]> git.meshlink.io Git - meshlink/commitdiff
Add support for encrypted storage.
authorGuus Sliepen <guus@meshlink.io>
Fri, 14 Dec 2018 21:21:17 +0000 (22:21 +0100)
committerGuus Sliepen <guus@meshlink.io>
Sun, 31 Mar 2019 09:35:08 +0000 (11:35 +0200)
This is a large overhaul of how configuration files are handled. All files
are now in PackMessage format, and are read from disk in to memory in one
go, and also saved to disk from memory in one go, using functions in conf.c.

33 files changed:
doc/Configuration.md [new file with mode: 0644]
doc/ENCRYPTED_STORAGE [new file with mode: 0644]
doc/FILES [deleted file]
src/chacha-poly1305/chacha-poly1305.c
src/chacha-poly1305/chacha-poly1305.h
src/chacha-poly1305/chacha.c
src/conf.c
src/conf.h
src/connection.c
src/connection.h
src/ecdsa.h
src/ed25519/ecdsa.c
src/meshlink.c
src/meshlink.h
src/meshlink_internal.h
src/net.c
src/net.h
src/net_setup.c
src/net_socket.c
src/netutl.c
src/netutl.h
src/node.h
src/protocol_auth.c
src/protocol_edge.c
src/protocol_key.c
src/protocol_misc.c
src/sockaddr.h
test/Makefile.am
test/basic.c
test/encrypted.c [new file with mode: 0644]
test/encrypted.test [new file with mode: 0755]
test/import-export.c
test/invite-join.c

diff --git a/doc/Configuration.md b/doc/Configuration.md
new file mode 100644 (file)
index 0000000..1520ee5
--- /dev/null
@@ -0,0 +1,95 @@
+# Configuration files
+
+There currently are three different types of configuration files in MeshLink:
+
+- the main configuration file
+- the host config files
+- pending invitation files
+
+All configuration files are in PackMessage format. The contents will be
+described below.
+
+There are three different ways envisioned to store configuration data:
+
+- unencrypted files
+- encrypted files
+- ephemeral, in-memory storage
+
+To keep things as simple and flexible as possible, there are some restrictions:
+
+- Configuration files are read from and written to memory in one go.
+  When a configuration files is changed, it is not modified in place,
+  but has to be written from scratch.
+- When in-memory, functions from `packmsg.h` can be used to parse and generate the configuration data.
+- Configuration files are read and written only via functions declared in `conf.h`.
+  This also includes creating and destroying the configuration directory.
+
+## Main configuration file
+
+When stored on disk, it's in `meshlink.conf`. The contents are:
+
+- uint32: configuration format version
+- str: name of the local node
+- bin: private Ed25519 key
+- bin: private Ed25519 key for invitations
+- uint16: port number of the local node
+
+More information about the local node is stored in its host config file.
+
+## Host configuration files
+
+When stored on disk, there is one file per node in the mesh, inside the directory `hosts/`.
+The contents of a host configuration are:
+
+- uint32: configuration format version
+- str: name of the node
+- int32: device class
+- bool: blacklisted
+- bin: public Ed25519 key
+- str: canonical address (may be zero length if unset)
+- arr[ext]: recent addresses
+
+## Invitation files
+
+When stored on disk, there is one file per pending invitation, inside the directory `invitations/`.
+The contents of an invitation file are:
+
+- uint32: invitation format version
+- str: name of the invitee
+- int32: device class of the invitee (may be unused)
+- arr[bin]: one or more host config files
+
+## Encryption
+
+When encryption is enabled, each file is individually encrypted using Chacha20-Poly1305.
+A unique counter stored at the start of the file. A (master) key must be provided by the application.
+
+## Exporting configuration
+
+Calling `meshlink_export()` will return an array of host config files:
+
+- arr[bin]: one or more host config files
+
+## Loading, saving and unloading configuration data to/from memory
+
+### Main configuration file
+
+Created during first setup.
+Loaded at start.
+Never unloaded.
+Might have to be saved again when port number or private key for invitations changes.
+
+### Host configuration files
+
+Can be loaded partially into memory:
+- devclass+blacklist status only, always required in memory, so loaded in `load_all_nodes()`.
+- public key, needed whenever SPTPS is required, or when the application wants a fingerprint or verify signed data from that node.
+- canonical and recent addresses, needed whenever we want to make outgoing connections to this node.
+
+Furthermore, in order to properly merge new data, we need to load the whole config file into memory when:
+- updating recent addresses
+- exporting this node's information (no API for this yet)
+
+Whenever a node's information is updated, we mark it dirty. It is written out at a convenient time.
+
+
diff --git a/doc/ENCRYPTED_STORAGE b/doc/ENCRYPTED_STORAGE
new file mode 100644 (file)
index 0000000..ea4b864
--- /dev/null
@@ -0,0 +1,6 @@
+Master key is a public/private keypair (RSA? We don't really care.)
+
+File format:
+<encrypted chacha-poly1305 key>
+<configuration-data>
+
diff --git a/doc/FILES b/doc/FILES
deleted file mode 100644 (file)
index 745b4e9..0000000
--- a/doc/FILES
+++ /dev/null
@@ -1,33 +0,0 @@
-MeshLink is based on tinc, and tinc had the following structure for its
-directory with configuration files:
-
-/tinc.conf       Main configuration file
-/tinc-up         Script to configure the virtual network interface
-/ecdsa_key.priv  This node's private key
-/hosts/node1     Host configuration file, containing the node's public key,
-                 address, and perhaps other information.
-/hosts/node2     Another host's configuration file,
-/hosts/...       et cetera.
-
-For MeshLink, we don't have any scripts, and neither the application nor the
-user is supposed to read or edit the configuration files in any way. Therefore,
-it might be a good idea to simplify them to the point that we are left
-with:
-
-/meshlink.conf    Contains only the local node's Name and Port.
-/ecdsa_key.priv   Contains the private key, in binary.
-/nodes/node1      Contains only the public key and a list of known addresses.
-/nodes/node2      Another node's key and addresses,
-/nodes/...        et cetera.
-
-Example /meshlink.conf:
-
-Name = foo
-Port = 12345
-
-Example /nodes/foo:
-
-PublicKey = 19fj193f12d1m02dj12089cn
-Address = foo.example.com 18529
-Address = 93.184.216.119 18529
-Address = 2606:2800:220:6d:26bf:1447:1097:aa7 18529
index 5810dbb508a3a988c2b997b7bfd421dfaa1462ff..2711abb29eb04455ac38a77f0c9a14b3cb66b5a7 100644 (file)
@@ -100,3 +100,58 @@ bool chacha_poly1305_decrypt(chacha_poly1305_ctx_t *ctx, uint64_t seqnr, const v
 
        return true;
 }
+
+bool chacha_poly1305_encrypt_iv96(chacha_poly1305_ctx_t *ctx, const uint8_t *seqbuf, const void *indata, size_t inlen, void *outdata, size_t *outlen) {
+       const uint8_t one[4] = { 1, 0, 0, 0 };  /* NB little-endian */
+       uint8_t poly_key[POLY1305_KEYLEN];
+
+       /*
+        * Run ChaCha20 once to generate the Poly1305 key. The IV is the
+        * packet sequence number.
+        */
+       memset(poly_key, 0, sizeof(poly_key));
+       chacha_ivsetup_96(&ctx->main_ctx, seqbuf, NULL);
+       chacha_encrypt_bytes(&ctx->main_ctx, poly_key, poly_key, sizeof(poly_key));
+
+       /* Set Chacha's block counter to 1 */
+       chacha_ivsetup_96(&ctx->main_ctx, seqbuf, one);
+
+       chacha_encrypt_bytes(&ctx->main_ctx, indata, outdata, inlen);
+       poly1305_auth((uint8_t *)outdata + inlen, outdata, inlen, poly_key);
+
+       if (outlen)
+               *outlen = inlen + POLY1305_TAGLEN;
+
+       return true;
+}
+
+bool chacha_poly1305_decrypt_iv96(chacha_poly1305_ctx_t *ctx, const uint8_t *seqbuf, const void *indata, size_t inlen, void *outdata, size_t *outlen) {
+       const uint8_t one[4] = { 1, 0, 0, 0 };  /* NB little-endian */
+       uint8_t expected_tag[POLY1305_TAGLEN], poly_key[POLY1305_KEYLEN];
+
+       /*
+        * Run ChaCha20 once to generate the Poly1305 key. The IV is the
+        * packet sequence number.
+        */
+       memset(poly_key, 0, sizeof(poly_key));
+       chacha_ivsetup_96(&ctx->main_ctx, seqbuf, NULL);
+       chacha_encrypt_bytes(&ctx->main_ctx, poly_key, poly_key, sizeof(poly_key));
+
+       /* Set Chacha's block counter to 1 */
+       chacha_ivsetup_96(&ctx->main_ctx, seqbuf, one);
+
+       /* Check tag before anything else */
+       inlen -= POLY1305_TAGLEN;
+       const uint8_t *tag = (const uint8_t *)indata + inlen;
+
+       poly1305_auth(expected_tag, indata, inlen, poly_key);
+       if (memcmp(expected_tag, tag, POLY1305_TAGLEN))
+               return false;
+
+       chacha_encrypt_bytes(&ctx->main_ctx, indata, outdata, inlen);
+
+       if (outlen)
+               *outlen = inlen;
+
+       return true;
+}
index af7eaf5efd4cde08b1b470092add23b11fd9ad5a..f6fbbb670d00e910bbdb3671123ba92baaa11a56 100644 (file)
@@ -12,4 +12,7 @@ extern bool chacha_poly1305_set_key(chacha_poly1305_ctx_t *ctx, const void *key)
 extern bool chacha_poly1305_encrypt(chacha_poly1305_ctx_t *ctx, uint64_t seqnr, const void *indata, size_t inlen, void *outdata, size_t *outlen);
 extern bool chacha_poly1305_decrypt(chacha_poly1305_ctx_t *ctx, uint64_t seqnr, const void *indata, size_t inlen, void *outdata, size_t *outlen);
 
+extern bool chacha_poly1305_encrypt_iv96(chacha_poly1305_ctx_t *ctx, const uint8_t *seqbuf, const void *indata, size_t inlen, void *outdata, size_t *outlen);
+extern bool chacha_poly1305_decrypt_iv96(chacha_poly1305_ctx_t *ctx, const uint8_t *seqbuf, const void *indata, size_t inlen, void *outdata, size_t *outlen);
+
 #endif //CHACHA_POLY1305_H
index 2d0b9183dde4258dd0d20a4492aa19b7aee72776..a158de5efafa6266cc46ee2473aba6aca0f7352f 100644 (file)
@@ -79,6 +79,14 @@ void chacha_ivsetup(chacha_ctx *x, const uint8_t *iv, const uint8_t *counter)
        x->input[15] = U8TO32_LITTLE(iv + 4);
 }
 
+void chacha_ivsetup_96(chacha_ctx *x, const uint8_t *iv, const uint8_t *counter)
+{
+       x->input[12] = counter == NULL ? 0 : U8TO32_LITTLE(counter + 0);
+       x->input[13] = U8TO32_LITTLE(iv + 0);
+       x->input[14] = U8TO32_LITTLE(iv + 4);
+       x->input[15] = U8TO32_LITTLE(iv + 8);
+}
+
 void
 chacha_encrypt_bytes(chacha_ctx *x, const uint8_t *m, uint8_t *c, uint32_t bytes)
 {
index 742e81d8e39cb1f6d7c21a02e48a416688127927..9e93c9a168a511b886fc2edbbddaaeddcfd5e362 100644 (file)
@@ -1,6 +1,6 @@
 /*
-    conf.c -- configuration code
-    Copyright (C) 2014 Guus Sliepen <guus@meshlink.io>
+    econf.c -- configuration code
+    Copyright (C) 2018 Guus Sliepen <guus@meshlink.io>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 #include "system.h"
 #include <assert.h>
 
-#include "splay_tree.h"
-#include "connection.h"
 #include "conf.h"
-#include "list.h"
+#include "crypto.h"
 #include "logger.h"
 #include "meshlink_internal.h"
-#include "netutl.h"             /* for str2address */
-#include "protocol.h"
-#include "utils.h"              /* for cp */
 #include "xalloc.h"
+#include "packmsg.h"
 
-static int config_compare(const config_t *a, const config_t *b) {
-       int result;
-
-       result = strcasecmp(a->variable, b->variable);
-
-       if(result) {
-               return result;
-       }
-
-       return result = a->line - b->line;
-}
-
-void init_configuration(splay_tree_t **config_tree) {
-       *config_tree = splay_alloc_tree((splay_compare_t) config_compare, (splay_action_t) free_config);
-}
-
-void exit_configuration(splay_tree_t **config_tree) {
-       if(*config_tree) {
-               splay_delete_tree(*config_tree);
-       }
-
-       *config_tree = NULL;
+/// Generate a path to the main configuration file.
+static void make_main_path(meshlink_handle_t *mesh, char *path, size_t len) {
+       snprintf(path, len, "%s" SLASH "meshlink.conf", mesh->confbase);
 }
 
-config_t *new_config(void) {
-       return xzalloc(sizeof(config_t));
+/// Generate a path to a host configuration file.
+static void make_host_path(meshlink_handle_t *mesh, const char *name, char *path, size_t len) {
+       snprintf(path, len, "%s" SLASH "hosts" SLASH "%s", mesh->confbase, name);
 }
 
-void free_config(config_t *cfg) {
-       free(cfg->variable);
-       free(cfg->value);
-       free(cfg);
+/// Generate a path to an unused invitation file.
+static void make_invitation_path(meshlink_handle_t *mesh, const char *name, char *path, size_t len) {
+       snprintf(path, len, "%s" SLASH "invitations" SLASH "%s", mesh->confbase, name);
 }
 
-void config_add(splay_tree_t *config_tree, config_t *cfg) {
-       splay_insert(config_tree, cfg);
+/// Generate a path to a used invitation file.
+static void make_used_invitation_path(meshlink_handle_t *mesh, const char *name, char *path, size_t len) {
+       snprintf(path, len, "%s" SLASH "invitations" SLASH "%s.used", mesh->confbase, name);
 }
 
-config_t *lookup_config(splay_tree_t *config_tree, char *variable) {
-       config_t cfg, *found;
+/// Remove a directory recursively
+static void deltree(const char *dirname) {
+       DIR *d = opendir(dirname);
 
-       cfg.variable = variable;
-       cfg.line = 0;
+       if(d) {
+               struct dirent *ent;
 
-       found = splay_search_closest_greater(config_tree, &cfg);
-
-       if(!found) {
-               return NULL;
-       }
-
-       if(strcasecmp(found->variable, variable)) {
-               return NULL;
-       }
-
-       return found;
-}
-
-config_t *lookup_config_next(splay_tree_t *config_tree, const config_t *cfg) {
-       splay_node_t *node;
-       config_t *found;
-
-       node = splay_search_node(config_tree, cfg);
+               while((ent = readdir(d))) {
+                       if(ent->d_name[0] == '.') {
+                               continue;
+                       }
 
-       if(node) {
-               if(node->next) {
-                       found = node->next->data;
+                       char filename[PATH_MAX];
+                       snprintf(filename, sizeof(filename), "%s" SLASH "%s", dirname, ent->d_name);
 
-                       if(!strcasecmp(found->variable, cfg->variable)) {
-                               return found;
+                       if(unlink(filename)) {
+                               deltree(filename);
                        }
                }
-       }
-
-       return NULL;
-}
-
-bool get_config_bool(const config_t *cfg, bool *result) {
-       if(!cfg) {
-               return false;
-       }
 
-       if(!strcasecmp(cfg->value, "yes")) {
-               *result = true;
-               return true;
-       } else if(!strcasecmp(cfg->value, "no")) {
-               *result = false;
-               return true;
+               closedir(d);
        }
 
-       logger(NULL, MESHLINK_ERROR, "\"yes\" or \"no\" expected for configuration variable %s in line %d",
-              cfg->variable, cfg->line);
-
-       return false;
+       rmdir(dirname);
 }
 
-bool get_config_int(const config_t *cfg, int *result) {
-       if(!cfg) {
+/// Create a fresh configuration directory
+bool config_init(meshlink_handle_t *mesh) {
+       if(mkdir(mesh->confbase, 0700) && errno != EEXIST) {
+               logger(mesh, MESHLINK_DEBUG, "Could not create directory %s: %s\n", mesh->confbase, strerror(errno));
                return false;
        }
 
-       if(sscanf(cfg->value, "%d", result) == 1) {
-               return true;
-       }
+       char path[PATH_MAX];
 
-       logger(NULL, MESHLINK_ERROR, "Integer expected for configuration variable %s in line %d",
-              cfg->variable, cfg->line);
+       // Remove meshlink.conf
+       snprintf(path, sizeof(path), "%s" SLASH "meshlink.conf", mesh->confbase);
+       unlink(path);
 
-       return false;
-}
+       // Remove any host config files
+       snprintf(path, sizeof(path), "%s" SLASH "hosts", mesh->confbase);
+       deltree(path);
 
-bool set_config_int(config_t *cfg, int val) {
-       if(!cfg) {
+       if(mkdir(path, 0700) && errno != EEXIST) {
+               logger(mesh, MESHLINK_DEBUG, "Could not create directory %s: %s\n", path, strerror(errno));
                return false;
        }
 
-       char val_str[1024];
-       snprintf(val_str, sizeof(val_str), "%d", val);
+       // Remove any invitation files
+       snprintf(path, sizeof(path), "%s" SLASH "invitations", mesh->confbase);
+       deltree(path);
 
-       if(cfg->value) {
-               free(cfg->value);
+       if(mkdir(path, 0700) && errno != EEXIST) {
+               logger(mesh, MESHLINK_DEBUG, "Could not create directory %s: %s\n", path, strerror(errno));
+               return false;
        }
 
-       cfg->value = xstrdup(val_str);
-
        return true;
 }
 
-bool get_config_string(const config_t *cfg, char **result) {
-       if(!cfg) {
-               return false;
+/// Wipe an existing configuration directory
+bool config_destroy(const char *confbase) {
+       char path[PATH_MAX];
+
+       // Remove meshlink.conf
+       snprintf(path, sizeof(path), "%s" SLASH "meshlink.conf", confbase);
+
+       if(unlink(path)) {
+               if(errno == ENOENT) {
+                       meshlink_errno = MESHLINK_ENOENT;
+                       return false;
+               } else {
+                       logger(NULL, MESHLINK_ERROR, "Cannot delete %s: %s\n", path, strerror(errno));
+                       meshlink_errno = MESHLINK_ESTORAGE;
+                       return false;
+               }
        }
 
-       *result = xstrdup(cfg->value);
-
+       deltree(confbase);
        return true;
 }
 
-bool set_config_string(config_t *cfg, const char *val) {
-       if(!cfg) {
-               return false;
-       }
-
-       if(cfg->value) {
-               free(cfg->value);
-       }
-
-       cfg->value = xstrdup(val);
+/// Check the presence of the main configuration file.
+bool main_config_exists(meshlink_handle_t *mesh) {
+       char path[PATH_MAX];
+       make_main_path(mesh, path, sizeof(path));
 
-       return true;
+       return access(path, F_OK) == 0;
 }
 
-bool get_config_address(const config_t *cfg, struct addrinfo **result) {
-       struct addrinfo *ai;
+/// Lock the main configuration file.
+bool main_config_lock(meshlink_handle_t *mesh) {
+       char path[PATH_MAX];
+       make_main_path(mesh, path, sizeof(path));
+
+       mesh->conffile = fopen(path, "r");
 
-       if(!cfg) {
+       if(!mesh->conffile) {
+               logger(NULL, MESHLINK_ERROR, "Cannot not open %s: %s\n", path, strerror(errno));
+               meshlink_errno = MESHLINK_ESTORAGE;
                return false;
        }
 
-       ai = str2addrinfo(cfg->value, NULL, 0);
+#ifdef FD_CLOEXEC
+       fcntl(fileno(mesh->conffile), F_SETFD, FD_CLOEXEC);
+#endif
+
+#ifdef HAVE_MINGW
+       // TODO: use _locking()?
+#else
 
-       if(ai) {
-               *result = ai;
-               return true;
+       if(flock(fileno(mesh->conffile), LOCK_EX | LOCK_NB) != 0) {
+               logger(NULL, MESHLINK_ERROR, "Cannot lock %s: %s\n", path, strerror(errno));
+               fclose(mesh->conffile);
+               mesh->conffile = NULL;
+               meshlink_errno = MESHLINK_EBUSY;
+               return false;
        }
 
-       logger(NULL, MESHLINK_ERROR, "Hostname or IP address expected for configuration variable %s in line %d",
-              cfg->variable, cfg->line);
+#endif
 
-       return false;
+       return true;
 }
 
-/*
-  Read exactly one line and strip the trailing newline if any.
-*/
-static char *readline(FILE *fp, char *buf, size_t buflen) {
-       char *newline = NULL;
-       char *p;
-
-       if(feof(fp)) {
-               return NULL;
+/// Unlock the main configuration file.
+void main_config_unlock(meshlink_handle_t *mesh) {
+       if(mesh->conffile) {
+               fclose(mesh->conffile);
+               mesh->conffile = NULL;
        }
+}
 
-       p = fgets(buf, buflen, fp);
+/// Read a configuration file from a FILE handle.
+bool config_read_file(meshlink_handle_t *mesh, FILE *f, config_t *config) {
+       (void)mesh;
+       long len;
 
-       if(!p) {
-               return NULL;
+       if(fseek(f, 0, SEEK_END) || !(len = ftell(f)) || fseek(f, 0, SEEK_SET)) {
+               logger(mesh, MESHLINK_ERROR, "Cannot get config file size: %s\n", strerror(errno));
+               meshlink_errno = MESHLINK_ESTORAGE;
+               fclose(f);
+               return false;
        }
 
-       newline = strchr(p, '\n');
+       uint8_t *buf = xmalloc(len);
 
-       if(!newline) {
-               return buf;
+       if(fread(buf, len, 1, f) != 1) {
+               logger(mesh, MESHLINK_ERROR, "Cannot read config file: %s\n", strerror(errno));
+               meshlink_errno = MESHLINK_ESTORAGE;
+               fclose(f);
+               return false;
        }
 
-       /* kill newline and carriage return if necessary */
-       *newline = '\0';
-
-       if(newline > p && newline[-1] == '\r') {
-               newline[-1] = '\0';
+       if(mesh->config_key) {
+               uint8_t *decrypted = xmalloc(len);
+               size_t decrypted_len = len;
+               chacha_poly1305_ctx_t *ctx = chacha_poly1305_init();
+               chacha_poly1305_set_key(ctx, mesh->config_key);
+
+               if(len > 12 && chacha_poly1305_decrypt_iv96(ctx, buf, buf + 12, len - 12, decrypted, &decrypted_len)) {
+                       free(buf);
+                       config->buf = decrypted;
+                       config->len = decrypted_len;
+                       return true;
+               } else {
+                       logger(mesh, MESHLINK_ERROR, "Cannot decrypt config file\n");
+                       meshlink_errno = MESHLINK_ESTORAGE;
+                       free(decrypted);
+                       free(buf);
+                       return false;
+               }
        }
 
-       return buf;
-}
-
-config_t *parse_config_line(char *line, const char *fname, int lineno) {
-       config_t *cfg;
-       int len;
-       char *variable, *value, *eol;
-       variable = value = line;
+       config->buf = buf;
+       config->len = len;
 
-       eol = line + strlen(line);
-
-       while(strchr("\t ", *--eol)) {
-               *eol = '\0';
-       }
+       return true;
+}
 
-       len = strcspn(value, "\t =");
-       value += len;
-       value += strspn(value, "\t ");
+/// Write a configuration file to a FILE handle.
+bool config_write_file(meshlink_handle_t *mesh, FILE *f, const config_t *config) {
+       if(mesh->config_key) {
+               uint8_t buf[config->len + 16];
+               size_t len = sizeof(buf);
+               uint8_t seqbuf[12];
+               randomize(&seqbuf, sizeof(seqbuf));
+               chacha_poly1305_ctx_t *ctx = chacha_poly1305_init();
+               chacha_poly1305_set_key(ctx, mesh->config_key);
+               bool success = false;
+
+               if(chacha_poly1305_encrypt_iv96(ctx, seqbuf, config->buf, config->len, buf, &len)) {
+                       success = fwrite(seqbuf, sizeof(seqbuf), 1, f) == 1 && fwrite(buf, len, 1, f) == 1;
+               } else {
+                       logger(mesh, MESHLINK_ERROR, "Cannot encrypt config file\n");
+                       meshlink_errno = MESHLINK_ESTORAGE;
+               }
 
-       if(*value == '=') {
-               value++;
-               value += strspn(value, "\t ");
+               chacha_poly1305_exit(ctx);
+               return success;
        }
 
-       variable[len] = '\0';
-
-       if(!*value) {
-               const char err[] = "No value for variable";
-               logger(NULL, MESHLINK_ERROR, "%s `%s' on line %d while reading config file %s",
-                      err, variable, lineno, fname);
-               return NULL;
+       if(fwrite(config->buf, config->len, 1, f) != 1) {
+               logger(mesh, MESHLINK_ERROR, "Cannot write config file: %s", strerror(errno));
+               meshlink_errno = MESHLINK_ESTORAGE;
+               return false;
        }
 
-       cfg = new_config();
-       cfg->variable = xstrdup(variable);
-       cfg->value = xstrdup(value);
-       cfg->line = lineno;
-
-       return cfg;
+       return true;
 }
 
-/*
-  Parse a configuration file and put the results in the configuration tree
-  starting at *base.
-*/
-bool read_config_file(splay_tree_t *config_tree, const char *fname) {
-       FILE *fp;
-       char buffer[MAX_STRING_SIZE];
-       char *line;
-       int lineno = 0;
-       bool ignore = false;
-       config_t *cfg;
-       bool result = false;
-
-       fp = fopen(fname, "r");
-
-       if(!fp) {
-               logger(NULL, MESHLINK_ERROR, "Cannot open config file %s: %s", fname, strerror(errno));
-               return false;
-       }
+/// Free resources of a loaded configuration file.
+void config_free(config_t *config) {
+       free((uint8_t *)config->buf);
+       config->buf = NULL;
+       config->len = 0;
+}
 
-       for(;;) {
-               line = readline(fp, buffer, sizeof(buffer));
+/// Check the presence of a host configuration file.
+bool config_exists(meshlink_handle_t *mesh, const char *name) {
+       char path[PATH_MAX];
+       make_host_path(mesh, name, path, sizeof(path));
 
-               if(!line) {
-                       if(feof(fp)) {
-                               result = true;
-                       }
-
-                       break;
-               }
+       return access(path, F_OK) == 0;
+}
 
-               lineno++;
+/// Read a host configuration file.
+bool config_read(meshlink_handle_t *mesh, const char *name, config_t *config) {
+       char path[PATH_MAX];
+       make_host_path(mesh, name, path, sizeof(path));
 
-               if(!*line || *line == '#') {
-                       continue;
-               }
+       FILE *f = fopen(path, "r");
 
-               if(ignore) {
-                       if(!strncmp(line, "-----END", 8)) {
-                               ignore = false;
-                       }
+       if(!f) {
+               logger(mesh, MESHLINK_ERROR, "Failed to open `%s': %s", path, strerror(errno));
+               return false;
+       }
 
-                       continue;
-               }
+       if(!config_read_file(mesh, f, config)) {
+               logger(mesh, MESHLINK_ERROR, "Failed to read `%s': %s", path, strerror(errno));
+               fclose(f);
+               return false;
+       }
 
-               if(!strncmp(line, "-----BEGIN", 10)) {
-                       ignore = true;
-                       continue;
-               }
+       fclose(f);
+       return true;
+}
 
-               cfg = parse_config_line(line, fname, lineno);
+/// Write a host configuration file.
+bool config_write(meshlink_handle_t *mesh, const char *name, const config_t *config) {
+       char path[PATH_MAX];
+       make_host_path(mesh, name, path, sizeof(path));
 
-               if(!cfg) {
-                       break;
-               }
+       FILE *f = fopen(path, "w");
 
-               config_add(config_tree, cfg);
+       if(!f) {
+               logger(mesh, MESHLINK_ERROR, "Failed to open `%s': %s", path, strerror(errno));
+               return false;
        }
 
-       fclose(fp);
+       if(!config_write_file(mesh, f, config)) {
+               logger(mesh, MESHLINK_ERROR, "Failed to write `%s': %s", path, strerror(errno));
+               fclose(f);
+               return false;
+       }
 
-       return result;
+       fclose(f);
+       return true;
 }
 
-bool write_config_file(const struct splay_tree_t *config_tree, const char *fname) {
-       FILE *fp;
+/// Read the main configuration file.
+bool main_config_read(meshlink_handle_t *mesh, config_t *config) {
+       char path[PATH_MAX];
+       make_main_path(mesh, path, sizeof(path));
 
-       fp = fopen(fname, "w+");
+       FILE *f = fopen(path, "r");
 
-       if(!fp) {
-               logger(NULL, MESHLINK_ERROR, "Cannot open config file %s: %s", fname, strerror(errno));
+       if(!f) {
+               logger(mesh, MESHLINK_ERROR, "Failed to open `%s': %s", path, strerror(errno));
                return false;
        }
 
-       for splay_each(config_t, cnf, config_tree) {
-               if(fwrite(cnf->variable, sizeof(char), strlen(cnf->variable), fp) < strlen(cnf->variable)) {
-                       goto error;
-               }
-
-               if(fwrite(" = ", sizeof(char), 3, fp) < 3) {
-                       goto error;
-               }
-
-               if(fwrite(cnf->value, sizeof(char), strlen(cnf->value), fp) < strlen(cnf->value)) {
-                       goto error;
-               }
-
-               if(fwrite("\n", sizeof(char), 1, fp) < 1) {
-                       goto error;
-               }
+       if(!config_read_file(mesh, f, config)) {
+               logger(mesh, MESHLINK_ERROR, "Failed to read `%s': %s", path, strerror(errno));
+               fclose(f);
+               return false;
        }
 
-       fclose(fp);
+       fclose(f);
        return true;
-
-error:
-       logger(NULL, MESHLINK_ERROR, "Cannot write to config file %s: %s", fname, strerror(errno));
-       fclose(fp);
-       return false;
 }
 
-bool read_server_config(meshlink_handle_t *mesh) {
-       char filename[PATH_MAX];
-       bool x;
+/// Write the main configuration file.
+bool main_config_write(meshlink_handle_t *mesh, const config_t *config) {
+       char path[PATH_MAX];
+       make_main_path(mesh, path, sizeof(path));
 
-       snprintf(filename, PATH_MAX, "%s" SLASH "meshlink.conf", mesh->confbase);
-       errno = 0;
-       x = read_config_file(mesh->config, filename);
+       FILE *f = fopen(path, "w");
 
-       if(!x && errno) {
-               logger(mesh, MESHLINK_ERROR, "Failed to read `%s': %s", filename, strerror(errno));
+       if(!f) {
+               logger(mesh, MESHLINK_ERROR, "Failed to open `%s': %s", path, strerror(errno));
+               return false;
        }
 
-       return x;
-}
-
-bool read_host_config(meshlink_handle_t *mesh, splay_tree_t *config_tree, const char *name) {
-       char filename[PATH_MAX];
-       bool x;
-
-       snprintf(filename, PATH_MAX, "%s" SLASH "hosts" SLASH "%s", mesh->confbase, name);
-       x = read_config_file(config_tree, filename);
+       if(!config_write_file(mesh, f, config)) {
+               logger(mesh, MESHLINK_ERROR, "Failed to write `%s': %s", path, strerror(errno));
+               fclose(f);
+               return false;
+       }
 
-       return x;
+       fclose(f);
+       return true;
 }
 
-bool write_host_config(struct meshlink_handle *mesh, const struct splay_tree_t *config_tree, const char *name) {
-       char filename[PATH_MAX];
-
-       snprintf(filename, PATH_MAX, "%s" SLASH "hosts" SLASH "%s", mesh->confbase, name);
-       return write_config_file(config_tree, filename);
-}
+/// Read an invitation file, and immediately delete it.
+bool invitation_read(meshlink_handle_t *mesh, const char *name, config_t *config) {
+       char path[PATH_MAX];
+       char used_path[PATH_MAX];
+       make_invitation_path(mesh, name, path, sizeof(path));
+       make_used_invitation_path(mesh, name, used_path, sizeof(used_path));
+
+       // Atomically rename the invitation file
+       if(rename(path, used_path)) {
+               if(errno == ENOENT) {
+                       logger(mesh, MESHLINK_ERROR, "Peer tried to use non-existing invitation %s\n", name);
+               } else {
+                       logger(mesh, MESHLINK_ERROR, "Error trying to rename invitation %s\n", name);
+               }
 
-bool modify_config_file(struct meshlink_handle *mesh, const char *name, const char *key, const char *value, int trim) {
-       assert(mesh && name && key);
+               return false;
+       }
 
-       char filename[PATH_MAX];
-       char tmpname[PATH_MAX];
-       bool error = false;
+       FILE *f = fopen(used_path, "r");
 
-       if(snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", mesh->confbase, name) >= PATH_MAX) {
-               logger(mesh, MESHLINK_ERROR, "Filename too long: %s" SLASH "hosts" SLASH "%s", mesh->confbase, name);
+       if(!f) {
+               logger(mesh, MESHLINK_ERROR, "Failed to open `%s': %s", path, strerror(errno));
                return false;
        }
 
-       if(snprintf(tmpname, sizeof(tmpname), "%s.tmp", filename) >= PATH_MAX) {
-               logger(mesh, MESHLINK_ERROR, "Filename too long: %s.tmp", filename);
+       // Check the timestamp
+       struct stat st;
+
+       if(fstat(fileno(f), &st)) {
+               logger(mesh, MESHLINK_ERROR, "Could not stat invitation file %s\n", name);
+               fclose(f);
+               unlink(used_path);
                return false;
        }
 
-       FILE *fr = fopen(filename, "r");
+       if(time(NULL) > st.st_mtime + mesh->invitation_timeout) {
+               logger(mesh, MESHLINK_ERROR, "Peer tried to use an outdated invitation file %s\n", name);
+               fclose(f);
+               unlink(used_path);
+               return false;
+       }
 
-       if(!fr) {
-               logger(mesh, MESHLINK_ERROR, "Cannot open config file %s: %s", filename, strerror(errno));
+       if(!config_read_file(mesh, f, config)) {
+               logger(mesh, MESHLINK_ERROR, "Failed to read `%s': %s", path, strerror(errno));
+               fclose(f);
+               unlink(used_path);
                return false;
        }
 
-       FILE *fw = fopen(tmpname, "w");
+       fclose(f);
+       unlink(used_path);
+       return true;
+}
+
+/// Write an invitation file.
+bool invitation_write(meshlink_handle_t *mesh, const char *name, const config_t *config) {
+       char path[PATH_MAX];
+       make_invitation_path(mesh, name, path, sizeof(path));
+
+       FILE *f = fopen(path, "w");
 
-       if(!fw) {
-               logger(mesh, MESHLINK_ERROR, "Cannot open temporary file %s: %s", tmpname, strerror(errno));
-               fclose(fr);
+       if(!f) {
+               logger(mesh, MESHLINK_ERROR, "Failed to open `%s': %s", path, strerror(errno));
                return false;
        }
 
-       char buf[4096];
-       char *sep;
-       int found = 0;
-
-       if(value) {
-               fprintf(fw, "%s = %s\n", key, value);
-               found++;
+       if(!config_write_file(mesh, f, config)) {
+               logger(mesh, MESHLINK_ERROR, "Failed to write `%s': %s", path, strerror(errno));
+               fclose(f);
+               return false;
        }
 
-       while(readline(fr, buf, sizeof(buf))) {
-               if(!*buf || *buf == '#') {
-                       goto copy;
-               }
+       fclose(f);
+       return true;
+}
 
-               sep = strchr(buf, ' ');
+/// Purge old invitation files
+size_t invitation_purge_old(meshlink_handle_t *mesh, time_t deadline) {
+       char path[PATH_MAX];
+       make_invitation_path(mesh, "", path, sizeof(path));
 
-               if(!sep) {
-                       goto copy;
-               }
+       DIR *dir = opendir(path);
 
-               *sep = 0;
+       if(!dir) {
+               logger(mesh, MESHLINK_DEBUG, "Could not read directory %s: %s\n", path, strerror(errno));
+               meshlink_errno = MESHLINK_ESTORAGE;
+               return 0;
+       }
 
-               if(strcmp(buf, key)) {
-                       *sep = ' ';
-                       goto copy;
-               }
+       errno = 0;
+       size_t count = 0;
+       struct dirent *ent;
 
-               // We found the key and the value. We already added it at the top, so ignore this one.
-               if(value && sep[1] == '=' && sep[2] == ' ' && !strcmp(sep + 3, value)) {
+       while((ent = readdir(dir))) {
+               if(strlen(ent->d_name) != 24) {
                        continue;
                }
 
-               // We found the key but with a different value, delete it if wanted.
-               found++;
+               char invname[PATH_MAX];
+               struct stat st;
 
-               if((!value || trim) && found > trim) {
+               if(snprintf(invname, sizeof(invname), "%s" SLASH "%s", path, ent->d_name) >= PATH_MAX) {
+                       logger(mesh, MESHLINK_DEBUG, "Filename too long: %s" SLASH "%s", path, ent->d_name);
                        continue;
                }
 
-               *sep = ' ';
-
-copy:
-               fprintf(fw, "%s\n", buf);
-       }
-
-       if(ferror(fr)) {
-               error = true;
-       }
-
-       fclose(fr);
-
-       if(ferror(fw)) {
-               error = true;
-       }
-
-       if(fclose(fw)) {
-               error = true;
+               if(!stat(invname, &st)) {
+                       if(mesh->invitation_key && deadline < st.st_mtime) {
+                               count++;
+                       } else {
+                               unlink(invname);
+                       }
+               } else {
+                       logger(mesh, MESHLINK_DEBUG, "Could not stat %s: %s\n", invname, strerror(errno));
+                       errno = 0;
+               }
        }
 
-       // If any error occurred during reading or writing, exit.
-       if(error) {
-               unlink(tmpname);
-               return false;
+       if(errno) {
+               logger(mesh, MESHLINK_DEBUG, "Error while reading directory %s: %s\n", path, strerror(errno));
+               closedir(dir);
+               meshlink_errno = MESHLINK_ESTORAGE;
+               return 0;
        }
 
-       // Try to atomically replace the old config file with the new one.
-#ifdef HAVE_MINGW
-       char bakname[PATH_MAX];
-       snprintf(bakname, sizeof(bakname), "%s.bak", filename);
-
-       if(rename(filename, bakname) || rename(tmpname, filename)) {
-               rename(bakname, filename);
-#else
-
-       if(rename(tmpname, filename)) {
-#endif
-               return false;
-       } else {
-#ifdef HAVE_MINGW
-               unlink(bakname);
-#endif
-               return true;
-       }
-}
+       closedir(dir);
 
-bool append_config_file(meshlink_handle_t *mesh, const char *name, const char *key, const char *value) {
-       return modify_config_file(mesh, name, key, value, 0);
+       return count;
 }
index fb29f02c829cdf27496dba533fc179ea4127d8d2..f2bb076201e7e21cc9089eced03ef908d6143093 100644 (file)
@@ -2,8 +2,8 @@
 #define MESHLINK_CONF_H
 
 /*
-    conf.h -- header for conf.c
-    Copyright (C) 2014, 2017 Guus Sliepen <guus@meshlink.io>
+    econf.h -- header for econf.c
+    Copyright (C) 2018 Guus Sliepen <guus@meshlink.io>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
-#include "list.h"
-#include "meshlink_internal.h"
-#include "splay_tree.h"
+struct meshlink_handle;
 
 typedef struct config_t {
-       char *variable;
-       char *value;
-       int line;
+       const uint8_t *buf;
+       size_t len;
 } config_t;
 
-extern void init_configuration(struct splay_tree_t **);
-extern void exit_configuration(struct splay_tree_t **);
-extern config_t *new_config(void) __attribute__((__malloc__));
-extern void free_config(config_t *);
-extern void config_add(struct splay_tree_t *, config_t *);
-extern config_t *lookup_config(struct splay_tree_t *, char *);
-extern config_t *lookup_config_next(struct splay_tree_t *, const config_t *);
-extern bool get_config_bool(const config_t *, bool *);
-extern bool get_config_int(const config_t *, int *);
-extern bool set_config_int(config_t *, int);
-extern bool get_config_string(const config_t *, char **);
-extern bool set_config_string(config_t *, const char *);
-extern bool get_config_address(const config_t *, struct addrinfo **);
-
-extern bool read_server_config(struct meshlink_handle *mesh);
-extern bool read_host_config(struct meshlink_handle *mesh, struct splay_tree_t *, const char *);
-extern bool write_host_config(struct meshlink_handle *mesh, const struct splay_tree_t *, const char *);
-extern bool modify_config_file(struct meshlink_handle *mesh, const char *name, const char *key, const char *value, int trim);
-extern bool append_config_file(struct meshlink_handle *mesh, const char *name, const char *key, const char *value);
+//extern bool config_read_file(struct meshlink_handle *mesh, FILE *f, struct config_t *);
+//extern bool config_write_file(struct meshlink_handle *mesh, FILE *f, const struct config_t *);
+extern void config_free(struct config_t *config);
+
+extern bool config_init(struct meshlink_handle *mesh);
+extern bool config_destroy(const char *confbase);
+
+extern bool main_config_exists(struct meshlink_handle *mesh);
+extern bool main_config_lock(struct meshlink_handle *mesh);
+extern void main_config_unlock(struct meshlink_handle *mesh);
+extern bool main_config_read(struct meshlink_handle *mesh, struct config_t *);
+extern bool main_config_write(struct meshlink_handle *mesh, const struct config_t *);
+
+extern bool config_exists(struct meshlink_handle *mesh, const char *name);
+extern bool config_read(struct meshlink_handle *mesh, const char *name, struct config_t *);
+extern bool config_write(struct meshlink_handle *mesh, const char *name, const struct config_t *);
+
+extern bool invitation_read(struct meshlink_handle *mesh, const char *name, struct config_t *);
+extern bool invitation_write(struct meshlink_handle *mesh, const char *name, const struct config_t *);
+extern size_t invitation_purge_old(struct meshlink_handle *mesh, time_t deadline);
 
 #endif
index 9b80db6b9e1c8fd3384e66d8b3cdae489dc54712..adecae8b82746d116c99727bd2ab21def9cdac44 100644 (file)
@@ -70,10 +70,6 @@ void free_connection(connection_t *c) {
 
        free(c->name);
 
-       if(c->config_tree) {
-               exit_configuration(&c->config_tree);
-       }
-
        free(c);
 }
 
index ca934a4976412eba97d346dc259cf50f76d559a6..d9e04f8c9942724e768e106281328ceea96ebd4a 100644 (file)
@@ -84,8 +84,6 @@ typedef struct connection_t {
        int allow_request;              /* defined if there's only one request possible */
 
        time_t last_ping_time;          /* last time we saw some activity from the other end or pinged them */
-
-       splay_tree_t *config_tree;      /* Pointer to configuration tree belonging to him */
 } connection_t;
 
 extern void init_connections(struct meshlink_handle *mesh);
index 364b2726f443ec9bf2f86a013aaa453f1eaba288..6da3d2f403ebec58d4ff10a5b9c3f3e3c751b39d 100644 (file)
 typedef struct ecdsa ecdsa_t;
 #endif
 
+extern ecdsa_t *ecdsa_set_private_key(const void *p) __attribute__((__malloc__));
 extern ecdsa_t *ecdsa_set_base64_public_key(const char *p) __attribute__((__malloc__));
+extern ecdsa_t *ecdsa_set_public_key(const void *p) __attribute__((__malloc__));
 extern char *ecdsa_get_base64_public_key(ecdsa_t *ecdsa);
+extern const void *ecdsa_get_public_key(ecdsa_t *ecdsa) __attribute__((__malloc__));
+extern const void *ecdsa_get_private_key(ecdsa_t *ecdsa) __attribute__((__malloc__));
 extern ecdsa_t *ecdsa_read_pem_public_key(FILE *fp) __attribute__((__malloc__));
 extern ecdsa_t *ecdsa_read_pem_private_key(FILE *fp) __attribute__((__malloc__));
 extern size_t ecdsa_size(ecdsa_t *ecdsa);
index ec53c070f67f792838e45c5cd1549c89193ae2b7..7cab5f2a46f7476f15e439da805832ae9b9fc93b 100644 (file)
@@ -54,6 +54,12 @@ ecdsa_t *ecdsa_set_base64_public_key(const char *p) {
        return ecdsa;
 }
 
+ecdsa_t *ecdsa_set_public_key(const void *p) {
+       ecdsa_t *ecdsa = xzalloc(sizeof(*ecdsa));
+       memcpy(ecdsa->public, p, sizeof ecdsa->public);
+       return ecdsa;
+}
+
 char *ecdsa_get_base64_public_key(ecdsa_t *ecdsa) {
        char *base64 = xmalloc(44);
        b64encode(ecdsa->public, base64, sizeof ecdsa->public);
@@ -61,10 +67,24 @@ char *ecdsa_get_base64_public_key(ecdsa_t *ecdsa) {
        return base64;
 }
 
+const void *ecdsa_get_public_key(ecdsa_t *ecdsa) {
+       return ecdsa->public;
+}
+
+ecdsa_t *ecdsa_set_private_key(const void *p) {
+       ecdsa_t *ecdsa = xzalloc(sizeof(*ecdsa));
+       memcpy(ecdsa->private, p, sizeof(*ecdsa));
+       return ecdsa;
+}
+
+const void *ecdsa_get_private_key(ecdsa_t *ecdsa) {
+       return ecdsa->private;
+}
+
 // Read PEM ECDSA keys
 
 ecdsa_t *ecdsa_read_pem_public_key(FILE *fp) {
-       ecdsa_t *ecdsa = xzalloc(sizeof * ecdsa);
+       ecdsa_t *ecdsa = xzalloc(sizeof(*ecdsa));
 
        if(fread(ecdsa->public, sizeof ecdsa->public, 1, fp) == 1) {
                return ecdsa;
index 8ec207842c6732bee32b25914db502c1174863d5..eb1a9c8426bfe1f0da196cb23f182096f5137a99 100644 (file)
     with this program; if not, write to the Free Software Foundation, Inc.,
     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
-#define VAR_SERVER 1    /* Should be in meshlink.conf */
-#define VAR_HOST 2      /* Can be in host config file */
-#define VAR_MULTIPLE 4  /* Multiple statements allowed */
-#define VAR_OBSOLETE 8  /* Should not be used anymore */
-#define VAR_SAFE 16     /* Variable is safe when accepting invitations */
-#define MAX_ADDRESS_LENGTH 45 /* Max length of an (IPv6) address */
-#define MAX_PORT_LENGTH 5 /* 0-65535 */
-typedef struct {
-       const char *name;
-       int type;
-} var_t;
 
 #include "system.h"
 #include <pthread.h>
@@ -38,6 +27,8 @@ typedef struct {
 #include "netutl.h"
 #include "node.h"
 #include "submesh.h"
+#include "packmsg.h"
+#include "prf.h"
 #include "protocol.h"
 #include "route.h"
 #include "sockaddr.h"
@@ -56,39 +47,6 @@ meshlink_log_level_t global_log_level;
 
 typedef bool (*search_node_by_condition_t)(const node_t *, const void *);
 
-//TODO: this can go away completely
-const var_t variables[] = {
-       /* Server configuration */
-       {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
-       {"Name", VAR_SERVER},
-       /* Host configuration */
-       {"SubMesh", VAR_HOST | VAR_SAFE},
-       {"CanonicalAddress", VAR_HOST},
-       {"Address", VAR_HOST | VAR_MULTIPLE},
-       {"ECDSAPublicKey", VAR_HOST},
-       {"Port", VAR_HOST},
-       {NULL, 0}
-};
-
-static bool fcopy(FILE *out, const char *filename) {
-       FILE *in = fopen(filename, "r");
-
-       if(!in) {
-               logger(NULL, MESHLINK_ERROR, "Could not open %s: %s\n", filename, strerror(errno));
-               return false;
-       }
-
-       char buf[1024];
-       size_t len;
-
-       while((len = fread(buf, 1, sizeof(buf), in))) {
-               fwrite(buf, len, 1, out);
-       }
-
-       fclose(in);
-       return true;
-}
-
 static int rstrip(char *value) {
        int len = strlen(value);
 
@@ -99,66 +57,18 @@ static int rstrip(char *value) {
        return len;
 }
 
-static void scan_for_canonical_address(const char *filename, char **hostname, char **port) {
-       char line[4096];
-
-       if(!filename || (*hostname && *port)) {
+static void get_canonical_address(node_t *n, char **hostname, char **port) {
+       if(!n->canonical_address) {
                return;
        }
 
-       FILE *f = fopen(filename, "r");
+       *hostname = xstrdup(n->canonical_address);
+       char *space = strchr(*hostname, ' ');
 
-       if(!f) {
-               return;
+       if(space) {
+               *space++ = 0;
+               *port = xstrdup(space);
        }
-
-       while(fgets(line, sizeof(line), f)) {
-               if(!rstrip(line)) {
-                       continue;
-               }
-
-               char *p = line, *q;
-               p += strcspn(p, "\t =");
-
-               if(!*p) {
-                       continue;
-               }
-
-               q = p + strspn(p, "\t ");
-
-               if(*q == '=') {
-                       q += 1 + strspn(q + 1, "\t ");
-               }
-
-               // q is now pointing to the hostname
-               *p = 0;
-               p = q + strcspn(q, "\t ");
-
-               if(*p) {
-                       *p++ = 0;
-               }
-
-               p += strspn(p, "\t ");
-               p[strcspn(p, "\t ")] = 0;
-               // p is now pointing to the port, if present
-
-               if(!*port && !strcasecmp(line, "Port")) {
-                       *port = xstrdup(q);
-               } else if(!strcasecmp(line, "CanonicalAddress")) {
-                       *hostname = xstrdup(q);
-
-                       if(*p) {
-                               free(*port);
-                               *port = xstrdup(p);
-                       }
-               }
-
-               if(*hostname && *port) {
-                       break;
-               }
-       }
-
-       fclose(f);
 }
 
 static bool is_valid_hostname(const char *hostname) {
@@ -246,7 +156,7 @@ static int socket_in_netns(int domain, int type, int protocol, int netns) {
 // Find out what local address a socket would use if we connect to the given address.
 // We do this using connect() on a UDP socket, so the kernel has to resolve the address
 // of both endpoints, but this will actually not send any UDP packet.
-static bool getlocaladdrname(char *destaddr, char *host, socklen_t hostlen, int netns) {
+static bool getlocaladdr(char *destaddr, struct sockaddr *sn, socklen_t *sl, int netns) {
        struct addrinfo *rai = NULL;
        const struct addrinfo hint = {
                .ai_family = AF_UNSPEC,
@@ -273,15 +183,22 @@ static bool getlocaladdrname(char *destaddr, char *host, socklen_t hostlen, int
 
        freeaddrinfo(rai);
 
-       struct sockaddr_storage sn;
-       socklen_t sl = sizeof(sn);
-
-       if(getsockname(sock, (struct sockaddr *)&sn, &sl)) {
+       if(getsockname(sock, sn, sl)) {
                closesocket(sock);
                return false;
        }
 
        closesocket(sock);
+       return true;
+}
+
+static bool getlocaladdrname(char *destaddr, char *host, socklen_t hostlen, int netns) {
+       struct sockaddr_storage sn;
+       socklen_t sl = sizeof(sn);
+
+       if(!getlocaladdr(destaddr, (struct sockaddr *)&sn, &sl, netns)) {
+               return false;
+       }
 
        if(getnameinfo((struct sockaddr *)&sn, sl, host, hostlen, NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV)) {
                return false;
@@ -446,9 +363,7 @@ static char *get_my_hostname(meshlink_handle_t *mesh, uint32_t flags) {
        // Add public/canonical addresses if requested
        if(flags & MESHLINK_INVITE_PUBLIC) {
                // Try the CanonicalAddress first
-               char filename[PATH_MAX] = "";
-               snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", mesh->confbase, mesh->self->name);
-               scan_for_canonical_address(filename, &hostname[2], &port[2]);
+               get_canonical_address(mesh->self, &hostname[2], &port[2]);
 
                if(!hostname[2]) {
                        if(flags & MESHLINK_INVITE_IPV4) {
@@ -536,7 +451,8 @@ static char *get_my_hostname(meshlink_handle_t *mesh, uint32_t flags) {
                // Ensure we have the same addresses in our own host config file.
                char *tmphostport;
                xasprintf(&tmphostport, "%s %s", hostname[i], port[i]);
-               append_config_file(mesh, mesh->self->name, "Address", tmphostport);
+               /// TODO: FIX
+               //config_add_string(&mesh->config, "Address", tmphostport);
                free(tmphostport);
 
                // Append the address to the hostport string
@@ -552,64 +468,6 @@ static char *get_my_hostname(meshlink_handle_t *mesh, uint32_t flags) {
        return hostport;
 }
 
-static char *get_line(const char **data) {
-       if(!data || !*data) {
-               return NULL;
-       }
-
-       if(! **data) {
-               *data = NULL;
-               return NULL;
-       }
-
-       static char line[1024];
-       const char *end = strchr(*data, '\n');
-       size_t len = end ? (size_t)(end - *data) : strlen(*data);
-
-       if(len >= sizeof(line)) {
-               logger(NULL, MESHLINK_ERROR, "Maximum line length exceeded!\n");
-               return NULL;
-       }
-
-       if(len && !isprint(**data)) {
-               abort();
-       }
-
-       memcpy(line, *data, len);
-       line[len] = 0;
-
-       if(end) {
-               *data = end + 1;
-       } else {
-               *data = NULL;
-       }
-
-       return line;
-}
-
-static char *get_value(const char *data, const char *var) {
-       char *line = get_line(&data);
-
-       if(!line) {
-               return NULL;
-       }
-
-       char *sep = line + strcspn(line, " \t=");
-       char *val = sep + strspn(sep, " \t");
-
-       if(*val == '=') {
-               val += 1 + strspn(val + 1, " \t");
-       }
-
-       *sep = 0;
-
-       if(strcasecmp(line, var)) {
-               return NULL;
-       }
-
-       return val;
-}
-
 static bool try_bind(int port) {
        struct addrinfo *ai = NULL;
        struct addrinfo hint = {
@@ -653,18 +511,8 @@ int check_port(meshlink_handle_t *mesh) {
                int port = 0x1000 + (rand() & 0x7fff);
 
                if(try_bind(port)) {
-                       char filename[PATH_MAX];
-                       snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", mesh->confbase, mesh->name);
-                       FILE *f = fopen(filename, "a");
-
-                       if(!f) {
-                               meshlink_errno = MESHLINK_ESTORAGE;
-                               logger(mesh, MESHLINK_DEBUG, "Could not store Port.\n");
-                               return 0;
-                       }
-
-                       fprintf(f, "Port = %d\n", port);
-                       fclose(f);
+                       free(mesh->myport);
+                       xasprintf(&mesh->myport, "%d", port);
                        return port;
                }
        }
@@ -674,33 +522,19 @@ int check_port(meshlink_handle_t *mesh) {
        return 0;
 }
 
-static void deltree(const char *dirname) {
-       DIR *d = opendir(dirname);
-
-       if(d) {
-               struct dirent *ent;
-
-               while((ent = readdir(d))) {
-                       if(ent->d_name[0] == '.') {
-                               continue;
-                       }
-
-                       char filename[PATH_MAX];
-                       snprintf(filename, sizeof(filename), "%s" SLASH "%s", dirname, ent->d_name);
-
-                       if(unlink(filename)) {
-                               deltree(filename);
-                       }
-               }
+static bool finalize_join(meshlink_handle_t *mesh, const void *buf, uint16_t len) {
+       packmsg_input_t in = {buf, len};
+       uint32_t version = packmsg_get_uint32(&in);
 
-               closedir(d);
+       if(version != MESHLINK_INVITATION_VERSION) {
+               logger(mesh, MESHLINK_ERROR, "Invalid invitation version!\n");
+               return false;
        }
 
-       rmdir(dirname);
-}
-
-static bool finalize_join(meshlink_handle_t *mesh) {
-       char *name = xstrdup(get_value(mesh->data, "Name"));
+       char *name = packmsg_get_str_dup(&in);
+       packmsg_skip_element(&in); /* submesh */
+       int32_t devclass = packmsg_get_int32(&in);
+       uint32_t count = packmsg_get_array(&in);
 
        if(!name) {
                logger(mesh, MESHLINK_DEBUG, "No Name found in invitation!\n");
@@ -712,168 +546,96 @@ static bool finalize_join(meshlink_handle_t *mesh) {
                return false;
        }
 
-       char filename[PATH_MAX];
-       snprintf(filename, sizeof(filename), "%s" SLASH "meshlink.conf", mesh->confbase);
-
-       FILE *f = fopen(filename, "w");
-
-       if(!f) {
-               logger(mesh, MESHLINK_DEBUG, "Could not create file %s: %s\n", filename, strerror(errno));
+       if(!count) {
+               logger(mesh, MESHLINK_ERROR, "Incomplete invitation file!\n");
                return false;
        }
 
-       fprintf(f, "Name = %s\n", name);
-
-       // Wipe all old host config files and invitations
-       snprintf(filename, sizeof(filename), "%s" SLASH "hosts", mesh->confbase);
-       deltree(filename);
-
-       if(mkdir(filename, 0777) && errno != EEXIST) {
-               logger(mesh, MESHLINK_DEBUG, "Could not create directory %s: %s\n", filename, strerror(errno));
+       // Initialize configuration directory
+       if(!config_init(mesh)) {
                return false;
        }
 
-       snprintf(filename, sizeof(filename), "%s" SLASH "invitations", mesh->confbase);
-       deltree(filename);
+       // Write main config file
+       uint8_t outbuf[4096];
+       packmsg_output_t out = {outbuf, sizeof(outbuf)};
+       packmsg_add_uint32(&out, MESHLINK_CONFIG_VERSION);
+       packmsg_add_str(&out, name);
+       packmsg_add_bin(&out, ecdsa_get_private_key(mesh->private_key), 96);
+       packmsg_add_uint16(&out, atoi(mesh->myport));
 
-       // Create a new host config file for ourself
-       snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", mesh->confbase, name);
-       FILE *fh = fopen(filename, "w");
+       config_t config = {outbuf, packmsg_output_size(&out, outbuf)};
 
-       if(!fh) {
-               logger(mesh, MESHLINK_DEBUG, "Could not create file %s: %s\n", filename, strerror(errno));
-               fclose(f);
+       if(!main_config_write(mesh, &config)) {
                return false;
        }
 
-       // Filter first chunk on approved keywords, split between meshlink.conf and hosts/Name
-       // Other chunks go unfiltered to their respective host config files
-       const char *p = mesh->data;
-       char *l, *value;
-
-       while((l = get_line(&p))) {
-               // Ignore comments
-               if(*l == '#') {
-                       continue;
-               }
-
-               // Split line into variable and value
-               int len = strcspn(l, "\t =");
-               value = l + len;
-               value += strspn(value, "\t ");
-
-               if(*value == '=') {
-                       value++;
-                       value += strspn(value, "\t ");
-               }
-
-               l[len] = 0;
+       // Write our own host config file
+       out.ptr = outbuf;
+       out.len = sizeof(outbuf);
+       packmsg_add_uint32(&out, MESHLINK_CONFIG_VERSION);
+       packmsg_add_str(&out, name);
+       packmsg_add_str(&out, CORE_MESH);
+       packmsg_add_int32(&out, devclass);
+       packmsg_add_bool(&out, false);
+       packmsg_add_bin(&out, ecdsa_get_public_key(mesh->private_key), 32);
+       packmsg_add_str(&out, ""); // TODO: copy existing canonical address, in case it was added before meshlink_join().
+       packmsg_add_array(&out, 0);
 
-               // Is it a Name?
-               if(!strcasecmp(l, "Name"))
-                       if(strcmp(value, name)) {
-                               break;
-                       } else {
-                               continue;
-                       } else if(!strcasecmp(l, "NetName")) {
-                       continue;
-               }
-
-               // Check the list of known variables
-               bool found = false;
-               int i;
-
-               for(i = 0; variables[i].name; i++) {
-                       if(strcasecmp(l, variables[i].name)) {
-                               continue;
-                       }
-
-                       found = true;
-                       break;
-               }
-
-               // Ignore unknown and unsafe variables
-               if(!found) {
-                       logger(mesh, MESHLINK_DEBUG, "Ignoring unknown variable '%s' in invitation.\n", l);
-                       continue;
-               } else if(!(variables[i].type & VAR_SAFE)) {
-                       logger(mesh, MESHLINK_DEBUG, "Ignoring unsafe variable '%s' in invitation.\n", l);
-                       continue;
-               }
+       config.len = packmsg_output_size(&out, outbuf);
 
-               // Copy the safe variable to the right config file
-               fprintf(variables[i].type & VAR_HOST ? fh : f, "%s = %s\n", l, value);
+       if(!config_write(mesh, name, &config)) {
+               return false;
        }
 
-       fclose(f);
+       // Write host config files
+       while(count--) {
+               const void *data;
+               uint32_t len = packmsg_get_bin_raw(&in, &data);
 
-       while(l && !strcasecmp(l, "Name")) {
-               if(!check_id(value)) {
-                       logger(mesh, MESHLINK_DEBUG, "Invalid Name found in invitation.\n");
+               if(!len) {
+                       logger(mesh, MESHLINK_ERROR, "Incomplete invitation file!\n");
                        return false;
                }
 
-               if(!strcmp(value, name)) {
-                       logger(mesh, MESHLINK_DEBUG, "Secondary chunk would overwrite our own host config file.\n");
+               packmsg_input_t in2 = {data, len};
+               uint32_t version = packmsg_get_uint32(&in2);
+
+               if(version != MESHLINK_CONFIG_VERSION) {
+                       logger(mesh, MESHLINK_ERROR, "Invalid host config file in invitation file!\n");
                        return false;
                }
 
-               snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", mesh->confbase, value);
-               f = fopen(filename, "w");
+               char *host = packmsg_get_str_dup(&in2);
 
-               if(!f) {
-                       logger(mesh, MESHLINK_DEBUG, "Could not create file %s: %s\n", filename, strerror(errno));
+               if(!check_id(host)) {
+                       logger(mesh, MESHLINK_ERROR, "Invalid node name in invitation file!\n");
+                       free(host);
                        return false;
                }
 
-               while((l = get_line(&p))) {
-                       if(!strcmp(l, "#---------------------------------------------------------------#")) {
-                               continue;
-                       }
-
-                       int len = strcspn(l, "\t =");
-
-                       if(len == 4 && !strncasecmp(l, "Name", 4)) {
-                               value = l + len;
-                               value += strspn(value, "\t ");
-
-                               if(*value == '=') {
-                                       value++;
-                                       value += strspn(value, "\t ");
-                               }
-
-                               l[len] = 0;
-                               break;
-                       }
-
-                       fputs(l, f);
-                       fputc('\n', f);
+               if(!strcmp(host, name)) {
+                       logger(mesh, MESHLINK_DEBUG, "Secondary chunk would overwrite our own host config file.\n");
+                       free(host);
+                       return false;
                }
 
-               fclose(f);
-       }
-
-       char *b64key = ecdsa_get_base64_public_key(mesh->self->connection->ecdsa);
+               config_t config = {data, len};
+               config_write(mesh, host, &config);
 
-       if(!b64key) {
-               fclose(fh);
-               return false;
+               node_t *n = new_node();
+               n->name = host;
+               node_read_full(mesh, n);
+               n->devclass = mesh->devclass;
+               node_add(mesh, n);
        }
 
-       fprintf(fh, "ECDSAPublicKey = %s\n", b64key);
-       fprintf(fh, "Port = %s\n", mesh->myport);
-
-       fclose(fh);
-
-       sptps_send_record(&(mesh->sptps), 1, b64key, strlen(b64key));
-       free(b64key);
+       sptps_send_record(&(mesh->sptps), 1, ecdsa_get_public_key(mesh->private_key), 32);
 
        free(mesh->name);
        free(mesh->self->name);
-       free(mesh->self->connection->name);
        mesh->name = xstrdup(name);
        mesh->self->name = xstrdup(name);
-       mesh->self->connection->name = name;
 
        logger(mesh, MESHLINK_DEBUG, "Configuration stored in: %s\n", mesh->confbase);
 
@@ -911,18 +673,10 @@ static bool invitation_receive(void *handle, uint8_t type, const void *msg, uint
                return sptps_send_record(&(mesh->sptps), 0, mesh->cookie, sizeof(mesh)->cookie);
 
        case 0:
-               mesh->data = xrealloc(mesh->data, mesh->thedatalen + len + 1);
-               memcpy(mesh->data + mesh->thedatalen, msg, len);
-               mesh->thedatalen += len;
-               mesh->data[mesh->thedatalen] = 0;
-               break;
+               return finalize_join(mesh, msg, len);
 
        case 1:
-               mesh->thedatalen = 0;
-               return finalize_join(mesh);
-
-       case 2:
-               logger(mesh, MESHLINK_DEBUG, "Invitation successfully accepted.\n");
+               logger(mesh, MESHLINK_DEBUG, "Invitation succesfully accepted.\n");
                shutdown(mesh->sock, SHUT_RDWR);
                mesh->success = true;
                break;
@@ -1023,61 +777,18 @@ const char *meshlink_strerror(meshlink_errno_t err) {
 }
 
 static bool ecdsa_keygen(meshlink_handle_t *mesh) {
-       ecdsa_t *key;
-       FILE *f;
-       char pubname[PATH_MAX], privname[PATH_MAX];
+       logger(mesh, MESHLINK_DEBUG, "Generating ECDSA keypairs:\n");
 
-       logger(mesh, MESHLINK_DEBUG, "Generating ECDSA keypair:\n");
+       mesh->private_key = ecdsa_generate();
+       mesh->invitation_key = ecdsa_generate();
 
-       if(!(key = ecdsa_generate())) {
+       if(!mesh->private_key || !mesh->invitation_key) {
                logger(mesh, MESHLINK_DEBUG, "Error during key generation!\n");
                meshlink_errno = MESHLINK_EINTERNAL;
                return false;
-       } else {
-               logger(mesh, MESHLINK_DEBUG, "Done.\n");
-       }
-
-       if(snprintf(privname, sizeof(privname), "%s" SLASH "ecdsa_key.priv", mesh->confbase) >= PATH_MAX) {
-               logger(mesh, MESHLINK_DEBUG, "Filename too long: %s" SLASH "ecdsa_key.priv\n", mesh->confbase);
-               meshlink_errno = MESHLINK_ESTORAGE;
-               return false;
        }
 
-       f = fopen(privname, "wb");
-
-       if(!f) {
-               meshlink_errno = MESHLINK_ESTORAGE;
-               return false;
-       }
-
-#ifdef HAVE_FCHMOD
-       fchmod(fileno(f), 0600);
-#endif
-
-       if(!ecdsa_write_pem_private_key(key, f)) {
-               logger(mesh, MESHLINK_DEBUG, "Error writing private key!\n");
-               ecdsa_free(key);
-               fclose(f);
-               meshlink_errno = MESHLINK_EINTERNAL;
-               return false;
-       }
-
-       fclose(f);
-
-       snprintf(pubname, sizeof(pubname), "%s" SLASH "hosts" SLASH "%s", mesh->confbase, mesh->name);
-       f = fopen(pubname, "a");
-
-       if(!f) {
-               meshlink_errno = MESHLINK_ESTORAGE;
-               return false;
-       }
-
-       char *pubkey = ecdsa_get_base64_public_key(key);
-       fprintf(f, "ECDSAPublicKey = %s\n", pubkey);
-       free(pubkey);
-
-       fclose(f);
-       ecdsa_free(key);
+       logger(mesh, MESHLINK_DEBUG, "Done.\n");
 
        return true;
 }
@@ -1104,68 +815,166 @@ static struct timeval idle(event_loop_t *loop, void *data) {
 
 // Get our local address(es) by simulating connecting to an Internet host.
 static void add_local_addresses(meshlink_handle_t *mesh) {
-       char host[NI_MAXHOST];
-       char entry[MAX_STRING_SIZE];
+       struct sockaddr_storage sn;
+       socklen_t sl = sizeof(sn);
 
        // IPv4 example.org
 
-       if(getlocaladdrname("93.184.216.34", host, sizeof(host), mesh->netns)) {
-               snprintf(entry, sizeof(entry), "%s %s", host, mesh->myport);
-               append_config_file(mesh, mesh->name, "Address", entry);
+       if(getlocaladdr("93.184.216.34", (struct sockaddr *)&sn, &sl, mesh->netns)) {
+               ((struct sockaddr_in *)&sn)->sin_port = ntohs(atoi(mesh->myport));
+               meshlink_hint_address(mesh, (meshlink_node_t *)mesh->self, (struct sockaddr *)&sn);
        }
 
        // IPv6 example.org
 
-       if(getlocaladdrname("2606:2800:220:1:248:1893:25c8:1946", host, sizeof(host), mesh->netns)) {
-               snprintf(entry, sizeof(entry), "%s %s", host, mesh->myport);
-               append_config_file(mesh, mesh->name, "Address", entry);
+       sl = sizeof(sn);
+
+       if(getlocaladdr("2606:2800:220:1:248:1893:25c8:1946", (struct sockaddr *)&sn, &sl, mesh->netns)) {
+               ((struct sockaddr_in6 *)&sn)->sin6_port = ntohs(atoi(mesh->myport));
+               meshlink_hint_address(mesh, (meshlink_node_t *)mesh->self, (struct sockaddr *)&sn);
        }
 }
 
+#if 0
+static bool meshlink_write_config(meshlink_handle_t *mesh) {
+       uint8_t buf[1024];
+       packmsg_output_t out = {buf, sizeof buf};
+       packmsg_add_str(&out, mesh->name);
+       packmsg_add_uint32(&out, mesh->devclass);
+       packmsg_add_uint16(&out, mesh->port);
+       packmsg_add_bin(&out, ecdsa, sizeof(ecdsa));
+       uint32_t len = packmsg_output_size(&out, buf);
+
+       if(!len) {
+               logger(mesh, MESHLINK_DEBUG, "Could not create configuration data\n",);
+               meshlink_errno = MESHLINK_EINTERNAL;
+               return false;
+       }
+}
+#endif
+
 static bool meshlink_setup(meshlink_handle_t *mesh) {
-       if(mkdir(mesh->confbase, 0777) && errno != EEXIST) {
-               logger(mesh, MESHLINK_DEBUG, "Could not create directory %s: %s\n", mesh->confbase, strerror(errno));
+       if(!config_init(mesh)) {
+               logger(mesh, MESHLINK_ERROR, "Could not set up configuration in %s: %s\n", mesh->confbase, strerror(errno));
                meshlink_errno = MESHLINK_ESTORAGE;
                return false;
        }
 
-       char filename[PATH_MAX];
-       snprintf(filename, sizeof(filename), "%s" SLASH "hosts", mesh->confbase);
+       if(!ecdsa_keygen(mesh)) {
+               meshlink_errno = MESHLINK_EINTERNAL;
+               return false;
+       }
 
-       if(mkdir(filename, 0777) && errno != EEXIST) {
-               logger(mesh, MESHLINK_DEBUG, "Could not create directory %s: %s\n", filename, strerror(errno));
-               meshlink_errno = MESHLINK_ESTORAGE;
+       if(check_port(mesh) == 0) {
+               meshlink_errno = MESHLINK_ENETWORK;
                return false;
        }
 
-       snprintf(filename, sizeof(filename), "%s" SLASH "meshlink.conf", mesh->confbase);
+       /* Create a node for ourself */
 
-       if(!access(filename, F_OK)) {
-               logger(mesh, MESHLINK_DEBUG, "Configuration file %s already exists!\n", filename);
-               meshlink_errno = MESHLINK_EEXIST;
+       mesh->self = new_node();
+       mesh->self->name = xstrdup(mesh->name);
+       mesh->self->devclass = mesh->devclass;
+       mesh->self->ecdsa = ecdsa_set_public_key(ecdsa_get_public_key(mesh->private_key));
+
+       // Write the main config file
+       uint8_t buf[4096];
+       packmsg_output_t out = {buf, sizeof(buf)};
+
+       packmsg_add_uint32(&out, MESHLINK_CONFIG_VERSION);
+       packmsg_add_str(&out, mesh->name);
+       packmsg_add_bin(&out, ecdsa_get_private_key(mesh->private_key), 96);
+       packmsg_add_bin(&out, ecdsa_get_private_key(mesh->invitation_key), 96);
+       packmsg_add_uint16(&out, atoi(mesh->myport));
+
+       config_t config = {buf, packmsg_output_size(&out, buf)};
+
+       if(!main_config_write(mesh, &config)) {
+               return false;
+       }
+
+       // Write our own host config file
+       out.ptr = buf;
+       out.len = sizeof(buf);
+       packmsg_add_uint32(&out, MESHLINK_CONFIG_VERSION);
+       packmsg_add_str(&out, mesh->name);
+       packmsg_add_int32(&out, mesh->devclass);
+       packmsg_add_bool(&out, false);
+       packmsg_add_bin(&out, ecdsa_get_public_key(mesh->private_key), 32);
+       packmsg_add_str(&out, ""); // TODO: copy existing canonical address, in case it was added before meshlink_join().
+       packmsg_add_array(&out, 0);
+
+       config.len = packmsg_output_size(&out, buf);
+
+       if(!config_write(mesh, mesh->name, &config)) {
                return false;
        }
 
-       FILE *f = fopen(filename, "w");
+       return true;
+}
 
-       if(!f) {
-               logger(mesh, MESHLINK_DEBUG, "Could not create file %s: %s\n", filename, strerror(errno));
+static bool meshlink_read_config(meshlink_handle_t *mesh) {
+       // Open the configuration file and lock it
+       if(!main_config_lock(mesh)) {
+               logger(NULL, MESHLINK_ERROR, "Cannot lock main config file\n");
                meshlink_errno = MESHLINK_ESTORAGE;
                return false;
        }
 
-       fprintf(f, "Name = %s\n", mesh->name);
-       fclose(f);
+       config_t config;
 
-       if(!ecdsa_keygen(mesh)) {
-               meshlink_errno = MESHLINK_EINTERNAL;
-               unlink(filename);
+       if(!main_config_read(mesh, &config)) {
+               logger(NULL, MESHLINK_ERROR, "Could not read main configuration file!");
                return false;
        }
 
-       if(check_port(mesh) == 0) {
-               meshlink_errno = MESHLINK_ENETWORK;
-               unlink(filename);
+       packmsg_input_t in = {config.buf, config.len};
+       const void *private_key;
+       const void *invitation_key;
+
+       uint32_t version = packmsg_get_uint32(&in);
+       char *name = packmsg_get_str_dup(&in);
+       uint32_t private_key_len = packmsg_get_bin_raw(&in, &private_key);
+       uint32_t invitation_key_len = packmsg_get_bin_raw(&in, &invitation_key);
+       uint16_t myport = packmsg_get_uint16(&in);
+
+       if(!packmsg_done(&in) || version != MESHLINK_CONFIG_VERSION || private_key_len != 96 || invitation_key_len != 96) {
+               logger(NULL, MESHLINK_ERROR, "Error parsing main configuration file!");
+               free(name);
+               config_free(&config);
+               return false;
+       }
+
+#if 0
+
+       // TODO: check this?
+       if(mesh->name && strcmp(mesh->name, name)) {
+               logger(NULL, MESHLINK_ERROR, "Configuration is for a different name (%s)!", name);
+               meshlink_errno = MESHLINK_ESTORAGE;
+               free(name);
+               config_free(&config);
+               return false;
+       }
+
+#endif
+
+       free(mesh->name);
+       mesh->name = name;
+       xasprintf(&mesh->myport, "%u", myport);
+       mesh->private_key = ecdsa_set_private_key(private_key);
+       mesh->invitation_key = ecdsa_set_private_key(invitation_key);
+       config_free(&config);
+
+       /* Create a node for ourself and read our host configuration file */
+
+       mesh->self = new_node();
+       mesh->self->name = xstrdup(name);
+       mesh->self->devclass = mesh->devclass;
+
+       if(!node_read_public_key(mesh, mesh->self)) {
+               logger(NULL, MESHLINK_ERROR, "Could not read our host configuration file!");
+               free_node(mesh->self);
+               mesh->self = NULL;
                return false;
        }
 
@@ -1231,6 +1040,35 @@ meshlink_open_params_t *meshlink_open_params_init(const char *confbase, const ch
        return params;
 }
 
+bool meshlink_open_params_set_netns(meshlink_open_params_t *params, int netns) {
+       if(!params) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return false;
+       }
+
+       params->netns = netns;
+
+       return true;
+}
+
+bool meshlink_open_params_set_storage_key(meshlink_open_params_t *params, const void *key, size_t keylen) {
+       if(!params) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return false;
+       }
+
+       if((!key && keylen) || (key && !keylen)) {
+               logger(NULL, MESHLINK_ERROR, "Invalid key length!\n");
+               meshlink_errno = MESHLINK_EINVAL;
+               return false;
+       }
+
+       params->key = key;
+       params->keylen = keylen;
+
+       return true;
+}
+
 void meshlink_open_params_free(meshlink_open_params_t *params) {
        if(!params) {
                meshlink_errno = MESHLINK_EINVAL;
@@ -1256,6 +1094,24 @@ meshlink_handle_t *meshlink_open(const char *confbase, const char *name, const c
 
        return meshlink_open_ex(&params);
 }
+
+meshlink_handle_t *meshlink_open_encrypted(const char *confbase, const char *name, const char *appname, dev_class_t devclass, const void *key, size_t keylen) {
+       /* Create a temporary struct on the stack, to avoid allocating and freeing one. */
+       meshlink_open_params_t params = {NULL};
+
+       params.confbase = (char *)confbase;
+       params.name = (char *)name;
+       params.appname = (char *)appname;
+       params.devclass = devclass;
+       params.netns = -1;
+
+       if(!meshlink_open_params_set_storage_key(&params, key, keylen)) {
+               return false;
+       }
+
+       return meshlink_open_ex(&params);
+}
+
 meshlink_handle_t *meshlink_open_ex(const meshlink_open_params_t *params) {
        // Validate arguments provided by the application
        bool usingname = false;
@@ -1300,6 +1156,12 @@ meshlink_handle_t *meshlink_open_ex(const meshlink_open_params_t *params) {
                return NULL;
        }
 
+       if((params->key && !params->keylen) || (!params->key && params->keylen)) {
+               logger(NULL, MESHLINK_ERROR, "Invalid key length!\n");
+               meshlink_errno = MESHLINK_EINVAL;
+               return NULL;
+       }
+
        meshlink_handle_t *mesh = xzalloc(sizeof(meshlink_handle_t));
        mesh->confbase = xstrdup(params->confbase);
        mesh->appname = xstrdup(params->appname);
@@ -1313,6 +1175,17 @@ meshlink_handle_t *meshlink_open_ex(const meshlink_open_params_t *params) {
                mesh->name = xstrdup(params->name);
        }
 
+       // Hash the key
+       if(params->key) {
+               mesh->config_key = xmalloc(CHACHA_POLY1305_KEYLEN);
+
+               if(!prf(params->key, params->keylen, "MeshLink configuration key", 26, mesh->config_key, CHACHA_POLY1305_KEYLEN)) {
+                       logger(NULL, MESHLINK_ERROR, "Error creating configuration key!\n");
+                       meshlink_errno = MESHLINK_EINTERNAL;
+                       return NULL;
+               }
+       }
+
        // initialize mutex
        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
@@ -1325,69 +1198,25 @@ meshlink_handle_t *meshlink_open_ex(const meshlink_open_params_t *params) {
 
        meshlink_queue_init(&mesh->outpacketqueue);
 
-       // Check whether meshlink.conf already exists
-
-       char filename[PATH_MAX];
-       snprintf(filename, sizeof(filename), "%s" SLASH "meshlink.conf", params->confbase);
+       // If no configuration exists yet, create it.
 
-       if(access(filename, R_OK)) {
-               if(errno == ENOENT) {
-                       // If not, create it
-                       if(!meshlink_setup(mesh)) {
-                               // meshlink_errno is set by meshlink_setup()
-                               return NULL;
-                       }
-               } else {
-                       logger(NULL, MESHLINK_ERROR, "Cannot not read from %s: %s\n", filename, strerror(errno));
+       if(!main_config_exists(mesh)) {
+               if(!meshlink_setup(mesh)) {
+                       logger(NULL, MESHLINK_ERROR, "Cannot create initial configuration\n");
+                       meshlink_close(mesh);
+                       return NULL;
+               }
+       } else {
+               if(!meshlink_read_config(mesh)) {
+                       logger(NULL, MESHLINK_ERROR, "Cannot read main configuration\n");
                        meshlink_close(mesh);
-                       meshlink_errno = MESHLINK_ESTORAGE;
                        return NULL;
                }
        }
 
-       // Open the configuration file and lock it
-
-       mesh->conffile = fopen(filename, "r");
-
-       if(!mesh->conffile) {
-               logger(NULL, MESHLINK_ERROR, "Cannot not open %s: %s\n", filename, strerror(errno));
-               meshlink_close(mesh);
-               meshlink_errno = MESHLINK_ESTORAGE;
-               return NULL;
-       }
-
-#ifdef FD_CLOEXEC
-       fcntl(fileno(mesh->conffile), F_SETFD, FD_CLOEXEC);
-#endif
-
-#ifdef HAVE_MINGW
-       // TODO: use _locking()?
-#else
-
-       if(flock(fileno(mesh->conffile), LOCK_EX | LOCK_NB) != 0) {
-               logger(NULL, MESHLINK_ERROR, "Cannot lock %s: %s\n", filename, strerror(errno));
-               meshlink_close(mesh);
-               meshlink_errno = MESHLINK_EBUSY;
-               return NULL;
-       }
-
-#endif
-
-       // Read the configuration
-
-       init_configuration(&mesh->config);
-
-       if(!read_server_config(mesh)) {
-               meshlink_close(mesh);
-               meshlink_errno = MESHLINK_ESTORAGE;
-               return NULL;
-       };
-
 #ifdef HAVE_MINGW
        struct WSAData wsa_state;
-
        WSAStartup(MAKEWORD(2, 2), &wsa_state);
-
 #endif
 
        // Setup up everything
@@ -1413,6 +1242,9 @@ meshlink_handle_t *meshlink_open_ex(const meshlink_open_params_t *params) {
                return NULL;
        }
 
+       add_local_addresses(mesh);
+       node_write_config(mesh, mesh->self);
+
        idle_set(&mesh->loop, idle, mesh);
 
        logger(NULL, MESHLINK_DEBUG, "meshlink_open returning\n");
@@ -1455,8 +1287,6 @@ static void *meshlink_main_loop(void *arg) {
 
        pthread_mutex_lock(&(mesh->mesh_mutex));
 
-       try_outgoing_connections(mesh);
-
        logger(mesh, MESHLINK_DEBUG, "Starting main_loop...\n");
        main_loop(mesh);
        logger(mesh, MESHLINK_DEBUG, "main_loop returned.\n");
@@ -1466,6 +1296,9 @@ static void *meshlink_main_loop(void *arg) {
 }
 
 bool meshlink_start(meshlink_handle_t *mesh) {
+       assert(mesh->self);
+       assert(mesh->private_key);
+
        if(!mesh) {
                meshlink_errno = MESHLINK_EINVAL;
                return false;
@@ -1499,6 +1332,8 @@ bool meshlink_start(meshlink_handle_t *mesh) {
                return false;
        }
 
+       init_outgoings(mesh);
+
        // Start the main thread
 
        event_loop_start(&mesh->loop);
@@ -1522,6 +1357,10 @@ bool meshlink_start(meshlink_handle_t *mesh) {
 
 #endif
 
+       assert(mesh->self->ecdsa);
+       assert(!memcmp((uint8_t *)mesh->self->ecdsa + 64, (uint8_t *)mesh->private_key + 64, 32));
+
+
        pthread_mutex_unlock(&(mesh->mesh_mutex));
        return true;
 }
@@ -1581,10 +1420,7 @@ void meshlink_stop(meshlink_handle_t *mesh) {
                }
        }
 
-       if(mesh->outgoings) {
-               list_delete_list(mesh->outgoings);
-               mesh->outgoings = NULL;
-       }
+       exit_outgoings(mesh);
 
        pthread_mutex_unlock(&(mesh->mesh_mutex));
 }
@@ -1607,7 +1443,6 @@ void meshlink_close(meshlink_handle_t *mesh) {
 
        logger(mesh, MESHLINK_INFO, "Terminating");
 
-       exit_configuration(&mesh->config);
        event_loop_exit(&mesh->loop);
 
 #ifdef HAVE_MINGW
@@ -1629,9 +1464,7 @@ void meshlink_close(meshlink_handle_t *mesh) {
        free(mesh->confbase);
        pthread_mutex_destroy(&(mesh->mesh_mutex));
 
-       if(mesh->conffile) {
-               fclose(mesh->conffile);
-       }
+       main_config_unlock(mesh);
 
        memset(mesh, 0, sizeof(*mesh));
 
@@ -1644,23 +1477,7 @@ bool meshlink_destroy(const char *confbase) {
                return false;
        }
 
-       char filename[PATH_MAX];
-       snprintf(filename, sizeof(filename), "%s" SLASH "meshlink.conf", confbase);
-
-       if(unlink(filename)) {
-               if(errno == ENOENT) {
-                       meshlink_errno = MESHLINK_ENOENT;
-                       return false;
-               } else {
-                       logger(NULL, MESHLINK_ERROR, "Cannot delete %s: %s\n", filename, strerror(errno));
-                       meshlink_errno = MESHLINK_ESTORAGE;
-                       return false;
-               }
-       }
-
-       deltree(confbase);
-
-       return true;
+       return config_destroy(confbase);
 }
 
 void meshlink_set_receive_cb(meshlink_handle_t *mesh, meshlink_receive_cb_t cb) {
@@ -1813,7 +1630,7 @@ char *meshlink_get_fingerprint(meshlink_handle_t *mesh, meshlink_node_t *node) {
 
        node_t *n = (node_t *)node;
 
-       if(!node_read_ecdsa_public_key(mesh, n) || !n->ecdsa) {
+       if(!node_read_public_key(mesh, n) || !n->ecdsa) {
                meshlink_errno = MESHLINK_EINTERNAL;
                pthread_mutex_unlock(&(mesh->mesh_mutex));
                return false;
@@ -2004,7 +1821,7 @@ bool meshlink_sign(meshlink_handle_t *mesh, const void *data, size_t len, void *
 
        pthread_mutex_lock(&(mesh->mesh_mutex));
 
-       if(!ecdsa_sign(mesh->self->connection->ecdsa, data, len, signature)) {
+       if(!ecdsa_sign(mesh->private_key, data, len, signature)) {
                meshlink_errno = MESHLINK_EINTERNAL;
                pthread_mutex_unlock(&(mesh->mesh_mutex));
                return false;
@@ -2031,9 +1848,8 @@ bool meshlink_verify(meshlink_handle_t *mesh, meshlink_node_t *source, const voi
        bool rval = false;
 
        struct node_t *n = (struct node_t *)source;
-       node_read_ecdsa_public_key(mesh, n);
 
-       if(!n->ecdsa) {
+       if(!node_read_public_key(mesh, n)) {
                meshlink_errno = MESHLINK_EINTERNAL;
                rval = false;
        } else {
@@ -2045,129 +1861,16 @@ bool meshlink_verify(meshlink_handle_t *mesh, meshlink_node_t *source, const voi
 }
 
 static bool refresh_invitation_key(meshlink_handle_t *mesh) {
-       char filename[PATH_MAX];
-
        pthread_mutex_lock(&(mesh->mesh_mutex));
 
-       snprintf(filename, sizeof(filename), "%s" SLASH "invitations", mesh->confbase);
-
-       if(mkdir(filename, 0700) && errno != EEXIST) {
-               logger(mesh, MESHLINK_DEBUG, "Could not create directory %s: %s\n", filename, strerror(errno));
-               meshlink_errno = MESHLINK_ESTORAGE;
-               pthread_mutex_unlock(&(mesh->mesh_mutex));
-               return false;
-       }
-
-       // Count the number of valid invitations, clean up old ones
-       DIR *dir = opendir(filename);
-
-       if(!dir) {
-               logger(mesh, MESHLINK_DEBUG, "Could not read directory %s: %s\n", filename, strerror(errno));
-               meshlink_errno = MESHLINK_ESTORAGE;
-               pthread_mutex_unlock(&(mesh->mesh_mutex));
-               return false;
-       }
-
-       errno = 0;
-       int count = 0;
-       struct dirent *ent;
-       time_t deadline = time(NULL) - 604800; // 1 week in the past
-
-       while((ent = readdir(dir))) {
-               if(strlen(ent->d_name) != 24) {
-                       continue;
-               }
-
-               char invname[PATH_MAX];
-               struct stat st;
-
-               if(snprintf(invname, sizeof(invname), "%s" SLASH "%s", filename, ent->d_name) >= PATH_MAX) {
-                       logger(mesh, MESHLINK_DEBUG, "Filename too long: %s" SLASH "%s", filename, ent->d_name);
-                       continue;
-               }
+       size_t count = invitation_purge_old(mesh, time(NULL) - mesh->invitation_timeout);
 
-               if(!stat(invname, &st)) {
-                       if(mesh->invitation_key && deadline < st.st_mtime) {
-                               count++;
-                       } else {
-                               unlink(invname);
-                       }
-               } else {
-                       logger(mesh, MESHLINK_DEBUG, "Could not stat %s: %s\n", invname, strerror(errno));
-                       errno = 0;
-               }
-       }
-
-       if(errno) {
-               logger(mesh, MESHLINK_DEBUG, "Error while reading directory %s: %s\n", filename, strerror(errno));
-               closedir(dir);
-               meshlink_errno = MESHLINK_ESTORAGE;
-               pthread_mutex_unlock(&(mesh->mesh_mutex));
-               return false;
-       }
-
-       closedir(dir);
-
-       snprintf(filename, sizeof(filename), "%s" SLASH "invitations" SLASH "ecdsa_key.priv", mesh->confbase);
-
-       // Remove the key if there are no outstanding invitations.
        if(!count) {
-               unlink(filename);
-
-               if(mesh->invitation_key) {
-                       ecdsa_free(mesh->invitation_key);
-                       mesh->invitation_key = NULL;
-               }
-       }
-
-       if(mesh->invitation_key) {
-               pthread_mutex_unlock(&(mesh->mesh_mutex));
-               return true;
-       }
-
-       // Create a new key if necessary.
-       FILE *f = fopen(filename, "rb");
-
-       if(!f) {
-               if(errno != ENOENT) {
-                       logger(mesh, MESHLINK_DEBUG, "Could not read %s: %s\n", filename, strerror(errno));
-                       meshlink_errno = MESHLINK_ESTORAGE;
-                       pthread_mutex_unlock(&(mesh->mesh_mutex));
-                       return false;
-               }
-
-               mesh->invitation_key = ecdsa_generate();
-
-               if(!mesh->invitation_key) {
-                       logger(mesh, MESHLINK_DEBUG, "Could not generate a new key!\n");
-                       meshlink_errno = MESHLINK_EINTERNAL;
-                       pthread_mutex_unlock(&(mesh->mesh_mutex));
-                       return false;
-               }
-
-               f = fopen(filename, "wb");
-
-               if(!f) {
-                       logger(mesh, MESHLINK_DEBUG, "Could not write %s: %s\n", filename, strerror(errno));
-                       meshlink_errno = MESHLINK_ESTORAGE;
-                       pthread_mutex_unlock(&(mesh->mesh_mutex));
-                       return false;
-               }
-
-               chmod(filename, 0600);
-               ecdsa_write_pem_private_key(mesh->invitation_key, f);
-               fclose(f);
-       } else {
-               mesh->invitation_key = ecdsa_read_pem_private_key(f);
-               fclose(f);
-
-               if(!mesh->invitation_key) {
-                       logger(mesh, MESHLINK_DEBUG, "Could not read private key from %s\n", filename);
-                       meshlink_errno = MESHLINK_ESTORAGE;
-               }
+               // TODO: Update invitation key if necessary?
        }
 
        pthread_mutex_unlock(&(mesh->mesh_mutex));
+
        return mesh->invitation_key;
 }
 
@@ -2198,11 +1901,15 @@ bool meshlink_set_canonical_address(meshlink_handle_t *mesh, meshlink_node_t *no
        }
 
        pthread_mutex_lock(&(mesh->mesh_mutex));
-       bool rval = modify_config_file(mesh, node->name, "CanonicalAddress", canonical_address, 1);
+
+       node_t *n = (node_t *)node;
+       free(n->canonical_address);
+       n->canonical_address = canonical_address;
+       n->status.dirty = true;
+
        pthread_mutex_unlock(&(mesh->mesh_mutex));
 
-       free(canonical_address);
-       return rval;
+       return true;
 }
 
 bool meshlink_add_address(meshlink_handle_t *mesh, const char *address) {
@@ -2221,13 +1928,9 @@ bool meshlink_add_external_address(meshlink_handle_t *mesh) {
                return false;
        }
 
-       bool rval = false;
-
-       pthread_mutex_lock(&(mesh->mesh_mutex));
-       rval = append_config_file(mesh, mesh->self->name, "Address", address);
-       pthread_mutex_unlock(&(mesh->mesh_mutex));
-
+       bool rval = meshlink_add_address(mesh, address);
        free(address);
+
        return rval;
 }
 
@@ -2270,19 +1973,10 @@ bool meshlink_set_port(meshlink_handle_t *mesh, int port) {
        }
 
        close_network_connections(mesh);
-       exit_configuration(&mesh->config);
-
-       char portstr[10];
-       snprintf(portstr, sizeof(portstr), "%d", port);
-       portstr[sizeof(portstr) - 1] = 0;
-
-       modify_config_file(mesh, mesh->name, "Port", portstr, true);
 
-       init_configuration(&mesh->config);
+       // TODO: write meshlink.conf again
 
-       if(!read_server_config(mesh)) {
-               meshlink_errno = MESHLINK_ESTORAGE;
-       } else if(!setup_network(mesh)) {
+       if(!setup_network(mesh)) {
                meshlink_errno = MESHLINK_ENETWORK;
        } else {
                rval = true;
@@ -2329,10 +2023,7 @@ char *meshlink_invite_ex(meshlink_handle_t *mesh, meshlink_submesh_t *submesh, c
        }
 
        // Ensure no host configuration file with that name exists
-       char filename[PATH_MAX];
-       snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", mesh->confbase, name);
-
-       if(!access(filename, F_OK)) {
+       if(config_exists(mesh, name)) {
                logger(mesh, MESHLINK_DEBUG, "A host config file for %s already exists!\n", name);
                meshlink_errno = MESHLINK_EEXIST;
                pthread_mutex_unlock(&(mesh->mesh_mutex));
@@ -2386,66 +2077,40 @@ char *meshlink_invite_ex(meshlink_handle_t *mesh, meshlink_submesh_t *submesh, c
 
        free(fingerprint);
 
-       // Create a file containing the details of the invitation.
-       snprintf(filename, sizeof(filename), "%s" SLASH "invitations" SLASH "%s", mesh->confbase, cookiehash);
-       int ifd = open(filename, O_RDWR | O_CREAT | O_EXCL, 0600);
+       /* Construct the invitation file */
+       uint8_t outbuf[4096];
+       packmsg_output_t inv = {outbuf, sizeof(outbuf)};
 
-       if(!ifd) {
-               logger(mesh, MESHLINK_DEBUG, "Could not create invitation file %s: %s\n", filename, strerror(errno));
-               meshlink_errno = MESHLINK_ESTORAGE;
-               pthread_mutex_unlock(&(mesh->mesh_mutex));
-               return NULL;
-       }
+       packmsg_add_uint32(&inv, MESHLINK_INVITATION_VERSION);
+       packmsg_add_str(&inv, name);
+       packmsg_add_str(&inv, s ? s->name : CORE_MESH);
+       packmsg_add_int32(&inv, DEV_CLASS_UNKNOWN); /* TODO: allow this to be set by inviter? */
 
-       FILE *f = fdopen(ifd, "w");
+       /* TODO: Add several host config files to bootstrap connections */
+       config_t configs[5] = {NULL};
+       int count = 0;
 
-       if(!f) {
-               abort();
+       if(config_read(mesh, mesh->self->name, &configs[count])) {
+               count++;
        }
 
-       // Fill in the details.
-       fprintf(f, "Name = %s\n", name);
+       /* Append host config files to the invitation file */
+       packmsg_add_array(&inv, count);
 
-       if(s) {
-               fprintf(f, "SubMesh = %s\n", s->name);
+       for(int i = 0; i < count; i++) {
+               packmsg_add_bin(&inv, configs[i].buf, configs[i].len);
+               config_free(&configs[i]);
        }
 
-       fprintf(f, "ConnectTo = %s\n", mesh->self->name);
+       config_t config = {outbuf, packmsg_output_size(&inv, outbuf)};
 
-       // Copy Broadcast and Mode
-       snprintf(filename, sizeof(filename), "%s" SLASH "meshlink.conf", mesh->confbase);
-       FILE *tc = fopen(filename,  "r");
-
-       if(tc) {
-               char buf[1024];
-
-               while(fgets(buf, sizeof(buf), tc)) {
-                       if((!strncasecmp(buf, "Mode", 4) && strchr(" \t=", buf[4]))
-                                       || (!strncasecmp(buf, "Broadcast", 9) && strchr(" \t=", buf[9]))) {
-                               fputs(buf, f);
-
-                               // Make sure there is a newline character.
-                               if(!strchr(buf, '\n')) {
-                                       fputc('\n', f);
-                               }
-                       }
-               }
-
-               fclose(tc);
-       } else {
-               logger(mesh, MESHLINK_DEBUG, "Could not create %s: %s\n", filename, strerror(errno));
+       if(!invitation_write(mesh, cookiehash, &config)) {
+               logger(mesh, MESHLINK_DEBUG, "Could not create invitation file %s: %s\n", cookiehash, strerror(errno));
                meshlink_errno = MESHLINK_ESTORAGE;
                pthread_mutex_unlock(&(mesh->mesh_mutex));
                return NULL;
        }
 
-       fprintf(f, "#---------------------------------------------------------------#\n");
-       fprintf(f, "Name = %s\n", mesh->self->name);
-
-       snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", mesh->confbase, mesh->self->name);
-       fcopy(f, filename);
-       fclose(f);
-
        // Create an URL from the local address, key hash and cookie
        char *url;
        xasprintf(&url, "%s/%s%s", address, hash, cookie);
@@ -2704,41 +2369,38 @@ char *meshlink_export(meshlink_handle_t *mesh) {
                return NULL;
        }
 
-       pthread_mutex_lock(&(mesh->mesh_mutex));
+       config_t config;
+
+       // Get our config file
 
-       char filename[PATH_MAX];
-       snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", mesh->confbase, mesh->self->name);
-       FILE *f = fopen(filename, "r");
+       pthread_mutex_lock(&(mesh->mesh_mutex));
 
-       if(!f) {
-               logger(mesh, MESHLINK_DEBUG, "Could not open %s: %s\n", filename, strerror(errno));
+       if(!config_read(mesh, mesh->self->name, &config)) {
                meshlink_errno = MESHLINK_ESTORAGE;
-               pthread_mutex_unlock(&(mesh->mesh_mutex));
+               pthread_mutex_unlock(&mesh->mesh_mutex);
                return NULL;
        }
 
-       fseek(f, 0, SEEK_END);
-       int fsize = ftell(f);
-       rewind(f);
+       pthread_mutex_unlock(&(mesh->mesh_mutex));
 
-       size_t len = fsize + 9 + strlen(mesh->self->name);
-       char *buf = xmalloc(len);
-       snprintf(buf, len, "Name = %s\n", mesh->self->name);
+       // Prepare a base64-encoded packmsg array containing our config file
 
-       if(fread(buf + len - fsize - 1, fsize, 1, f) != 1) {
-               logger(mesh, MESHLINK_DEBUG, "Error reading from %s: %s\n", filename, strerror(errno));
-               fclose(f);
+       uint8_t *buf = xmalloc(((config.len + 4) * 4) / 3 + 4);
+       packmsg_output_t out = {buf, config.len + 4};
+       packmsg_add_array(&out, 1);
+       packmsg_add_bin(&out, config.buf, config.len);
+       config_free(&config);
+
+       if(!packmsg_output_ok(&out)) {
+               logger(mesh, MESHLINK_DEBUG, "Error creating export data\n");
+               meshlink_errno = MESHLINK_EINTERNAL;
                free(buf);
-               meshlink_errno = MESHLINK_ESTORAGE;
-               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return NULL;
        }
 
-       fclose(f);
-       buf[len - 1] = 0;
+       b64encode_urlsafe(buf, (char *)buf, packmsg_output_size(&out, buf));
 
-       pthread_mutex_unlock(&(mesh->mesh_mutex));
-       return buf;
+       return (char *)buf;
 }
 
 bool meshlink_import(meshlink_handle_t *mesh, const char *data) {
@@ -2747,68 +2409,114 @@ bool meshlink_import(meshlink_handle_t *mesh, const char *data) {
                return false;
        }
 
-       pthread_mutex_lock(&(mesh->mesh_mutex));
+       size_t datalen = strlen(data);
+       uint8_t *buf = xmalloc(datalen);
+       int buflen = b64decode(data, buf, datalen);
 
-       if(strncmp(data, "Name = ", 7)) {
+       if(!buflen) {
                logger(mesh, MESHLINK_DEBUG, "Invalid data\n");
                meshlink_errno = MESHLINK_EPEER;
-               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return false;
        }
 
-       char *end = strchr(data + 7, '\n');
+       packmsg_input_t in = {buf, buflen};
+       uint32_t count = packmsg_get_array(&in);
 
-       if(!end) {
+       if(!count) {
                logger(mesh, MESHLINK_DEBUG, "Invalid data\n");
                meshlink_errno = MESHLINK_EPEER;
-               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return false;
        }
 
-       int len = end - (data + 7);
-       char name[len + 1];
-       memcpy(name, data + 7, len);
-       name[len] = 0;
+       pthread_mutex_lock(&(mesh->mesh_mutex));
 
-       if(!check_id(name)) {
-               logger(mesh, MESHLINK_DEBUG, "Invalid Name\n");
-               meshlink_errno = MESHLINK_EPEER;
-               pthread_mutex_unlock(&(mesh->mesh_mutex));
-               return false;
-       }
+       while(count--) {
+               const void *data;
+               uint32_t len = packmsg_get_bin_raw(&in, &data);
 
-       char filename[PATH_MAX];
-       snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", mesh->confbase, name);
+               if(!len) {
+                       break;
+               }
 
-       if(!access(filename, F_OK)) {
-               logger(mesh, MESHLINK_DEBUG, "File %s already exists, not importing\n", filename);
-               meshlink_errno = MESHLINK_EEXIST;
-               pthread_mutex_unlock(&(mesh->mesh_mutex));
-               return false;
-       }
+               packmsg_input_t in2 = {data, len};
+               uint32_t version = packmsg_get_uint32(&in2);
+               char *name = packmsg_get_str_dup(&in2);
 
-       if(errno != ENOENT) {
-               logger(mesh, MESHLINK_DEBUG, "Error accessing %s: %s\n", filename, strerror(errno));
-               meshlink_errno = MESHLINK_ESTORAGE;
-               pthread_mutex_unlock(&(mesh->mesh_mutex));
-               return false;
-       }
+               if(!packmsg_input_ok(&in2) || version != MESHLINK_CONFIG_VERSION || !check_id(name)) {
+                       free(name);
+                       packmsg_input_invalidate(&in2);
+                       break;
+               }
 
-       FILE *f = fopen(filename, "w");
+               if(!check_id(name)) {
+                       free(name);
+                       break;
+               }
 
-       if(!f) {
-               logger(mesh, MESHLINK_DEBUG, "Could not create %s: %s\n", filename, strerror(errno));
-               meshlink_errno = MESHLINK_ESTORAGE;
-               pthread_mutex_unlock(&(mesh->mesh_mutex));
-               return false;
-       }
+               node_t *n = lookup_node(mesh, name);
+
+               if(n) {
+                       free(name);
+                       logger(mesh, MESHLINK_DEBUG, "Node %s already exists, not importing\n", name);
+                       continue;
+               }
 
-       fwrite(end + 1, strlen(end + 1), 1, f);
-       fclose(f);
+               n = new_node();
+               n->name = name;
+               char *submesh_name = packmsg_get_str_dup(&in2);
 
-       load_all_nodes(mesh);
+               if(submesh_name) {
+                       if(!strcmp(submesh_name, CORE_MESH)) {
+                               n->submesh = NULL;
+                       } else {
+                               n->submesh = lookup_or_create_submesh(mesh, submesh_name);
+
+                               if(!n->submesh) {
+                                       packmsg_input_invalidate(&in2);
+                               }
+                       }
+
+                       free(submesh_name);
+               }
+
+               n->devclass = packmsg_get_int32(&in2);
+               n->status.blacklisted = packmsg_get_bool(&in2);
+               const void *key;
+               uint32_t keylen = packmsg_get_bin_raw(&in2, &key);
+
+               if(keylen == 32) {
+                       n->ecdsa = ecdsa_set_public_key(key);
+               }
+
+               n->canonical_address = packmsg_get_str_dup(&in2);
+               uint32_t count = packmsg_get_array(&in2);
+
+               if(count > 5) {
+                       count = 5;
+               }
+
+               for(uint32_t i = 0; i < count; i++) {
+                       n->recent[i] = packmsg_get_sockaddr(&in2);
+               }
+
+               if(!packmsg_done(&in2) || keylen != 32) {
+                       packmsg_input_invalidate(&in2);
+                       free_node(n);
+               } else {
+                       config_t config = {data, len};
+                       config_write(mesh, n->name, &config);
+                       node_add(mesh, n);
+               }
+       }
 
        pthread_mutex_unlock(&(mesh->mesh_mutex));
+
+       if(!packmsg_done(&in)) {
+               logger(mesh, MESHLINK_ERROR, "Invalid data\n");
+               meshlink_errno = MESHLINK_EPEER;
+               return false;
+       }
+
        return true;
 }
 
@@ -2837,11 +2545,9 @@ void meshlink_blacklist(meshlink_handle_t *mesh, meshlink_node_t *node) {
        }
 
        n->status.blacklisted = true;
+       n->status.dirty = true;
        logger(mesh, MESHLINK_DEBUG, "Blacklisted %s.\n", node->name);
 
-       //Make blacklisting persistent in the config file
-       append_config_file(mesh, n->name, "blacklisted", "yes");
-
        //Immediately terminate any connections we have with the blacklisted node
        for list_each(connection_t, c, mesh->connections) {
                if(c->node == n) {
@@ -2882,13 +2588,7 @@ void meshlink_whitelist(meshlink_handle_t *mesh, meshlink_node_t *node) {
        }
 
        n->status.blacklisted = false;
-
-       if(n->status.reachable) {
-               update_node_status(mesh, n);
-       }
-
-       //Remove blacklisting from the config file
-       append_config_file(mesh, n->name, "blacklisted", NULL);
+       n->status.dirty = true;
 
        pthread_mutex_unlock(&(mesh->mesh_mutex));
        return;
@@ -2903,32 +2603,16 @@ void meshlink_set_default_blacklist(meshlink_handle_t *mesh, bool blacklist) {
  */
 void meshlink_hint_address(meshlink_handle_t *mesh, meshlink_node_t *node, const struct sockaddr *addr) {
        if(!mesh || !node || !addr) {
-               return;
-       }
-
-       // Ignore hints about ourself.
-       if((node_t *)node == mesh->self) {
+               meshlink_errno = EINVAL;
                return;
        }
 
        pthread_mutex_lock(&(mesh->mesh_mutex));
 
-       char *host = NULL, *port = NULL, *str = NULL;
-       sockaddr2str((const sockaddr_t *)addr, &host, &port);
-
-       if(host && port) {
-               xasprintf(&str, "%s %s", host, port);
-
-               if((strncmp("fe80", host, 4) != 0) && (strncmp("127.", host, 4) != 0) && (strcmp("localhost", host) != 0)) {
-                       modify_config_file(mesh, node->name, "Address", str, 5);
-               } else {
-                       logger(mesh, MESHLINK_DEBUG, "Not adding Link Local IPv6 Address to config\n");
-               }
-       }
-
-       free(str);
-       free(host);
-       free(port);
+       node_t *n = (node_t *)node;
+       memmove(n->recent + 1, n->recent, 4 * sizeof(*n->recent));
+       memcpy(n->recent, addr, SALEN(*addr));
+       n->status.dirty = true;
 
        pthread_mutex_unlock(&(mesh->mesh_mutex));
        // @TODO do we want to fire off a connection attempt right away?
index 43d269cea76492134c08e4c8ffd9684a69502e52..61e59b6bd297e4745ab1956feae88a1d22ed94f4 100644 (file)
@@ -165,6 +165,27 @@ extern meshlink_open_params_t *meshlink_open_params_init(const char *confbase, c
  */
 extern void meshlink_open_params_free(meshlink_open_params_t *params);
 
+/// Set the network namespace MeshLink should use.
+/** This function changes the open parameters to use the given netns filedescriptor.
+ *
+ *  @param params   A pointer to a meshlink_open_params_t which must have been created earlier with meshlink_open_params_init().
+ *  @param netns    A filedescriptor that must point to a valid network namespace, or -1 to have MeshLink use the same namespace as the calling thread.
+ *
+ *  @return         This function will return true if the open parameters have been succesfully updated, false otherwise.
+ */
+extern bool meshlink_open_params_set_netns(meshlink_open_params_t *params, int netns);
+
+/// Set the encryption key MeshLink should use for local storage.
+/** This function changes the open parameters to use the given key for encrypting MeshLink's own configuration files.
+ *
+ *  @param params   A pointer to a meshlink_open_params_t which must have been created earlier with meshlink_open_params_init().
+ *  @param key      A pointer to a key, or NULL in case no encryption should be used.
+ *  @param keylen   The length of the given key, or 0 in case no encryption should be used.
+ *
+ *  @return         This function will return true if the open parameters have been succesfully updated, false otherwise.
+ */
+extern bool meshlink_open_params_set_storage_key(meshlink_open_params_t *params, const void *key, size_t keylen);
+
 /// Open or create a MeshLink instance.
 /** This function opens or creates a MeshLink instance.
  *  All parameters needed by MeshLink are passed via a meshlink_open_params_t struct,
@@ -214,6 +235,37 @@ extern meshlink_handle_t *meshlink_open_ex(const meshlink_open_params_t *params)
  */
 extern meshlink_handle_t *meshlink_open(const char *confbase, const char *name, const char *appname, dev_class_t devclass);
 
+/// Open or create a MeshLink instance that uses encrypted storage.
+/** This function opens or creates a MeshLink instance.
+ *  The state is stored in the configuration directory passed in the variable @a confbase @a.
+ *  If the configuration directory does not exist yet, for example when it is the first time
+ *  this instance is opened, the configuration directory will be automatically created and initialized.
+ *  However, the parent directory should already exist, otherwise an error will be returned.
+ *
+ *  The name given should be a unique identifier for this instance.
+ *
+ *  This function returns a pointer to a struct meshlink_handle that will be allocated by MeshLink.
+ *  When the application does no longer need to use this handle, it must call meshlink_close() to
+ *  free its resources.
+ *
+ *  This function does not start any network I/O yet. The application should
+ *  first set callbacks, and then call meshlink_start().
+ *
+ *  @param confbase The directory in which MeshLink will store its configuration files.
+ *                  After the function returns, the application is free to overwrite or free @a confbase @a.
+ *  @param name     The name which this instance of the application will use in the mesh.
+ *                  After the function returns, the application is free to overwrite or free @a name @a.
+ *  @param appname  The application name which will be used in the mesh.
+ *                  After the function returns, the application is free to overwrite or free @a name @a.
+ *  @param devclass The device class which will be used in the mesh.
+ *  @param key      A pointer to a key used to encrypt storage.
+ *  @param keylen   The length of the key in bytes.
+ *
+ *  @return         A pointer to a meshlink_handle_t which represents this instance of MeshLink, or NULL in case of an error.
+ *                  The pointer is valid until meshlink_close() is called.
+ */
+extern meshlink_handle_t *meshlink_open_encrypted(const char *confbase, const char *name, const char *appname, dev_class_t devclass, const void *key, size_t keylen);
+
 /// Create Sub-Mesh.
 /** This function causes MeshLink to open a new Sub-Mesh network
  *  create a new thread, which will handle all network I/O.
index a650083a337e14a177faac0beb6507cdcbafdb15..e649c7c560c3300b64624375cdf899ce38f31ba6 100644 (file)
@@ -41,6 +41,9 @@ static const char meshlink_invitation_label[] = "MeshLink invitation";
 static const char meshlink_tcp_label[] = "MeshLink TCP";
 static const char meshlink_udp_label[] = "MeshLink UDP";
 
+#define MESHLINK_CONFIG_VERSION 1
+#define MESHLINK_INVITATION_VERSION 1
+
 struct CattaServer;
 struct CattaSServiceBrowser;
 struct CattaSimplePoll;
@@ -68,6 +71,9 @@ struct meshlink_open_params {
        dev_class_t devclass;
 
        int netns;
+
+       const void *key;
+       size_t keylen;
 };
 
 /// A handle for an instance of MeshLink.
@@ -76,7 +82,7 @@ struct meshlink_handle {
        void *priv;
 
        char *appname;
-       dev_class_t devclass;
+       int32_t devclass;
 
        char *confbase;
        FILE *conffile;
@@ -99,7 +105,6 @@ struct meshlink_handle {
 
        struct node_t *self;
 
-       struct splay_tree_t *config;
        struct splay_tree_t *edges;
        struct splay_tree_t *nodes;
 
@@ -135,6 +140,7 @@ struct meshlink_handle {
 
        hash_t *node_udp_cache;
        struct connection_t *everyone;
+       struct ecdsa *private_key;
        struct ecdsa *invitation_key;
        int invitation_timeout;
 
@@ -161,6 +167,7 @@ struct meshlink_handle {
        char *catta_servicetype;
 
        int netns;
+       void *config_key;
 };
 
 /// A handle for a MeshLink node.
index 9d5e81719392d7027a970f9238fea35a1adfe229..660adf4ac1e47afcc85be08bf424c63ab94bbb4e 100644 (file)
--- a/src/net.c
+++ b/src/net.c
@@ -402,7 +402,7 @@ static void periodic_handler(event_loop_t *loop, void *data) {
                        splay_tree_t *nodes = splay_alloc_tree(node_compare_devclass_asc_lsc_desc, NULL);
 
                        for splay_each(node_t, n, mesh->nodes) {
-                               logger(mesh, MESHLINK_DEBUG, "* n->devclass = %d", n->devclass);
+                               logger(mesh, MESHLINK_DEBUG, "* %s->devclass = %d", n->name, n->devclass);
 
                                if(n != mesh->self && n->devclass <= mesh->devclass && !n->connection && (n->last_connect_try == 0 || (time(NULL) - n->last_connect_try) > retry_timeout)) {
                                        splay_insert(nodes, n);
@@ -410,10 +410,10 @@ static void periodic_handler(event_loop_t *loop, void *data) {
                        }
 
                        if(nodes->head) {
-                               logger(mesh, MESHLINK_DEBUG, "* found best one for initial connect");
-
                                //timeout = 0;
                                connect_to = (node_t *)nodes->head->data;
+
+                               logger(mesh, MESHLINK_DEBUG, "* found best one for initial connect: %s", connect_to->name);
                        } else {
                                logger(mesh, MESHLINK_DEBUG, "* could not find node for initial connect");
                        }
@@ -427,7 +427,7 @@ static void periodic_handler(event_loop_t *loop, void *data) {
                if(!connect_to && min_connects <= cur_connects && cur_connects < max_connects) {
                        unsigned int connects = 0;
 
-                       for(unsigned int devclass = 0; devclass <= mesh->devclass; ++devclass) {
+                       for(int32_t devclass = 0; devclass <= mesh->devclass; ++devclass) {
                                for list_each(connection_t, c, mesh->connections) {
                                        if(c->status.active && c->node && c->node->devclass == devclass) {
                                                connects += 1;
@@ -494,7 +494,7 @@ static void periodic_handler(event_loop_t *loop, void *data) {
                        bool found = false;
 
                        for list_each(outgoing_t, outgoing, mesh->outgoings) {
-                               if(!strcmp(outgoing->name, connect_to->name)) {
+                               if(outgoing->node == connect_to) {
                                        found = true;
                                        break;
                                }
@@ -503,8 +503,7 @@ static void periodic_handler(event_loop_t *loop, void *data) {
                        if(!found) {
                                logger(mesh, MESHLINK_DEBUG, "Autoconnecting to %s", connect_to->name);
                                outgoing_t *outgoing = xzalloc(sizeof(outgoing_t));
-                               outgoing->mesh = mesh;
-                               outgoing->name = xstrdup(connect_to->name);
+                               outgoing->node = connect_to;
                                list_insert_tail(mesh->outgoings, outgoing);
                                setup_outgoing_connection(mesh, outgoing);
                        } else {
@@ -518,7 +517,7 @@ static void periodic_handler(event_loop_t *loop, void *data) {
                if(min_connects < cur_connects /*&& cur_connects <= max_connects*/) {
                        unsigned int connects = 0;
 
-                       for(unsigned int devclass = 0; devclass <= mesh->devclass; ++devclass) {
+                       for(int32_t devclass = 0; devclass <= mesh->devclass; ++devclass) {
                                for list_each(connection_t, c, mesh->connections) {
                                        if(c->status.active && c->node && c->node->devclass == devclass) {
                                                connects += 1;
index 04bd55a50a0a5ff42fc50d2f314738f32d6b58a6..19a53d216e7bdd909cab80d173b8d42c619e15c2 100644 (file)
--- a/src/net.h
+++ b/src/net.h
@@ -59,9 +59,7 @@ typedef enum packet_type_t {
 #include "list.h"
 
 typedef struct outgoing_t {
-       char *name;
-       struct splay_tree_t *config_tree;
-       int timeout;
+       struct node_t *node;
        enum {
                OUTGOING_START,
                OUTGOING_CANONICAL,
@@ -70,11 +68,10 @@ typedef struct outgoing_t {
                OUTGOING_END,
                OUTGOING_NO_KNOWN_ADDRESSES,
        } state;
-       struct config_t *cfg;
+       int timeout;
+       timeout_t ev;
        struct addrinfo *ai;
        struct addrinfo *aip;
-       timeout_t ev;
-       struct meshlink_handle *mesh;
 } outgoing_t;
 
 extern int maxoutbufsize;
@@ -88,6 +85,9 @@ extern bool do_prune;
 #include "connection.h"
 #include "node.h"
 
+extern void init_outgoings(struct meshlink_handle *mesh);
+extern void exit_outgoings(struct meshlink_handle *mesh);
+
 extern void retry_outgoing(struct meshlink_handle *mesh, outgoing_t *);
 extern void handle_incoming_vpn_data(struct event_loop_t *loop, void *, int);
 extern void finish_connecting(struct meshlink_handle *mesh, struct connection_t *);
@@ -104,13 +104,14 @@ extern void load_all_nodes(struct meshlink_handle *mesh);
 extern bool setup_myself_reloadable(struct meshlink_handle *mesh);
 extern bool setup_network(struct meshlink_handle *mesh);
 extern void setup_outgoing_connection(struct meshlink_handle *mesh, struct outgoing_t *);
-extern void try_outgoing_connections(struct meshlink_handle *mesh);
 extern void close_network_connections(struct meshlink_handle *mesh);
 extern int main_loop(struct meshlink_handle *mesh);
 extern void terminate_connection(struct meshlink_handle *mesh, struct connection_t *, bool);
-extern bool node_read_ecdsa_public_key(struct meshlink_handle *mesh, struct node_t *);
+extern bool node_read_public_key(struct meshlink_handle *mesh, struct node_t *);
+extern bool node_read_full(struct meshlink_handle *mesh, struct node_t *);
 extern bool read_ecdsa_public_key(struct meshlink_handle *mesh, struct connection_t *);
 extern bool read_ecdsa_private_key(struct meshlink_handle *mesh);
+extern bool node_write_config(struct meshlink_handle *mesh, struct node_t *);
 extern void send_mtu_probe(struct meshlink_handle *mesh, struct node_t *);
 extern void handle_meta_connection_data(struct meshlink_handle *mesh, struct connection_t *);
 extern void retry(struct meshlink_handle *mesh);
index 70ed294e2862cc23bc8bdfa3184319a3f8cb303d..905e442d6aed296a27d74c01dcedd320f23e60e2 100644 (file)
 #include "meshlink_internal.h"
 #include "net.h"
 #include "netutl.h"
+#include "packmsg.h"
 #include "protocol.h"
 #include "route.h"
 #include "utils.h"
 #include "xalloc.h"
 #include "submesh.h"
 
-bool node_read_ecdsa_public_key(meshlink_handle_t *mesh, node_t *n) {
-       if(ecdsa_active(n->ecdsa)) {
-               return true;
+/// Helper function to start parsing a host config file
+static bool node_get_config(meshlink_handle_t *mesh, node_t *n, config_t *config, packmsg_input_t *in) {
+       if(!config_read(mesh, n->name, config)) {
+               return false;
        }
 
-       splay_tree_t *config_tree;
-       char *p;
+       in->ptr = config->buf;
+       in->len = config->len;
 
-       init_configuration(&config_tree);
+       uint32_t version = packmsg_get_uint32(in);
 
-       if(!read_host_config(mesh, config_tree, n->name)) {
-               goto exit;
+       if(version != MESHLINK_CONFIG_VERSION) {
+               config_free(config);
+               return false;
        }
 
-       /* First, check for simple ECDSAPublicKey statement */
+       const char *name;
+       uint32_t len = packmsg_get_str_raw(in, &name);
 
-       if(get_config_string(lookup_config(config_tree, "ECDSAPublicKey"), &p)) {
-               n->ecdsa = ecdsa_set_base64_public_key(p);
-               free(p);
+       if(len != strlen(n->name) || strncmp(name, n->name, len)) {
+               config_free(config);
+               return false;
        }
 
-exit:
-       exit_configuration(&config_tree);
-       return n->ecdsa;
+       return true;
 }
 
-bool read_ecdsa_public_key(meshlink_handle_t *mesh, connection_t *c) {
-       if(ecdsa_active(c->ecdsa)) {
-               return true;
+/// Read device class, blacklist status and submesh from a host config file. Used at startup when reading all host config files.
+bool node_read_partial(meshlink_handle_t *mesh, node_t *n) {
+       config_t config;
+       packmsg_input_t in;
+
+       if(!node_get_config(mesh, n, &config, &in)) {
+               return false;
        }
 
-       char *p;
+       char *submesh_name = packmsg_get_str_dup(&in);
 
-       if(!c->config_tree) {
-               init_configuration(&c->config_tree);
+       if(!strcmp(submesh_name, CORE_MESH)) {
+               free(submesh_name);
+               n->submesh = NULL;
+       } else {
+               n->submesh = lookup_or_create_submesh(mesh, submesh_name);
+               free(submesh_name);
 
-               if(!read_host_config(mesh, c->config_tree, c->name)) {
+               if(!n->submesh) {
+                       config_free(&config);
                        return false;
                }
        }
 
-       /* First, check for simple ECDSAPublicKey statement */
-
-       if(get_config_string(lookup_config(c->config_tree, "ECDSAPublicKey"), &p)) {
-               c->ecdsa = ecdsa_set_base64_public_key(p);
-               free(p);
-               return c->ecdsa;
-       }
-
-       return false;
-}
-
-bool read_ecdsa_private_key(meshlink_handle_t *mesh) {
-       FILE *fp;
-       char filename[PATH_MAX];
+       int32_t devclass = packmsg_get_int32(&in);
+       bool blacklisted = packmsg_get_bool(&in);
+       config_free(&config);
 
-       snprintf(filename, PATH_MAX, "%s" SLASH "ecdsa_key.priv", mesh->confbase);
-       fp = fopen(filename, "rb");
-
-       if(!fp) {
-               logger(mesh, MESHLINK_ERROR, "Error reading ECDSA private key file: %s", strerror(errno));
+       if(!packmsg_input_ok(&in) || devclass < 0 || devclass > _DEV_CLASS_MAX) {
                return false;
        }
 
-       mesh->self->connection->ecdsa = ecdsa_read_pem_private_key(fp);
-       fclose(fp);
+       n->devclass = devclass;
+       n->status.blacklisted = blacklisted;
+       return true;
+}
 
-       if(!mesh->self->connection->ecdsa) {
-               logger(mesh, MESHLINK_ERROR, "Reading ECDSA private key file failed: %s", strerror(errno));
+/// Read the public key from a host config file. Used whenever we need to start an SPTPS session.
+bool node_read_public_key(meshlink_handle_t *mesh, node_t *n) {
+       if(ecdsa_active(n->ecdsa)) {
+               return true;
        }
 
-       return mesh->self->connection->ecdsa;
-}
-
-static bool read_invitation_key(meshlink_handle_t *mesh) {
-       FILE *fp;
-       char filename[PATH_MAX];
+       config_t config;
+       packmsg_input_t in;
 
-       if(mesh->invitation_key) {
-               ecdsa_free(mesh->invitation_key);
-               mesh->invitation_key = NULL;
+       if(!node_get_config(mesh, n, &config, &in)) {
+               return false;
        }
 
-       snprintf(filename, PATH_MAX, "%s" SLASH "invitations" SLASH "ecdsa_key.priv", mesh->confbase);
+       packmsg_skip_element(&in); /* submesh */
+       packmsg_get_int32(&in); /* devclass */
+       packmsg_get_bool(&in); /* blacklisted */
 
-       fp = fopen(filename, "rb");
+       const void *key;
+       uint32_t len = packmsg_get_bin_raw(&in, &key);
 
-       if(fp) {
-               mesh->invitation_key = ecdsa_read_pem_private_key(fp);
-               fclose(fp);
-
-               if(!mesh->invitation_key) {
-                       logger(mesh, MESHLINK_ERROR, "Reading ECDSA private key file `%s' failed: %s", filename, strerror(errno));
-               }
+       if(len != 32) {
+               config_free(&config);
+               return false;
        }
 
-       return mesh->invitation_key;
+       n->ecdsa = ecdsa_set_public_key(key);
+       config_free(&config);
+       return true;
 }
 
-bool node_read_devclass(meshlink_handle_t *mesh, node_t *n) {
-
-       splay_tree_t *config_tree;
-       char *p;
-
-       init_configuration(&config_tree);
-
-       if(!read_host_config(mesh, config_tree, n->name)) {
-               goto exit;
+/// Read the full host config file. Used whenever we need to start an SPTPS session.
+bool node_read_full(meshlink_handle_t *mesh, node_t *n) {
+       if(n->canonical_address) {
+               return true;
        }
 
-       if(get_config_string(lookup_config(config_tree, "DeviceClass"), &p)) {
-               n->devclass = atoi(p);
-               free(p);
-       }
+       config_t config;
+       packmsg_input_t in;
 
-       if((int)n->devclass < 0 || n->devclass > _DEV_CLASS_MAX) {
-               n->devclass = _DEV_CLASS_MAX;
+       if(!node_get_config(mesh, n, &config, &in)) {
+               return false;
        }
 
-exit:
-       exit_configuration(&config_tree);
-       return n->devclass != 0;
-}
-
-bool node_read_submesh(meshlink_handle_t *mesh, node_t *n) {
-
-       splay_tree_t *config_tree;
-       char *p;
-
-       init_configuration(&config_tree);
-
-       if(!read_host_config(mesh, config_tree, n->name)) {
-               goto exit;
-       }
+       char *submesh_name = packmsg_get_str_dup(&in);
 
-       if(get_config_string(lookup_config(config_tree, "SubMesh"), &p)) {
+       if(!strcmp(submesh_name, CORE_MESH)) {
+               free(submesh_name);
                n->submesh = NULL;
-
-               for list_each(submesh_t, s, mesh->submeshes) {
-                       if(!strcmp(p, s->name)) {
-                               n->submesh = s;
-                               break;
-                       }
-               }
+       } else {
+               n->submesh = lookup_or_create_submesh(mesh, submesh_name);
+               free(submesh_name);
 
                if(!n->submesh) {
-                       n->submesh = (submesh_t *)meshlink_submesh_open(mesh, p);
+                       return false;
                }
-
-               free(p);
        }
 
-exit:
-       exit_configuration(&config_tree);
-       return n->submesh != NULL;
-}
-
-bool node_read_blacklist_status(meshlink_handle_t *mesh, node_t *n) {
+       packmsg_get_int32(&in); /* devclass */
+       packmsg_get_bool(&in); /* blacklisted */
+       const void *key;
+       uint32_t len = packmsg_get_bin_raw(&in, &key); /* public key */
 
-       splay_tree_t *config_tree;
-       bool blacklist_status;
-
-       init_configuration(&config_tree);
-
-       if(!read_host_config(mesh, config_tree, n->name)) {
-               goto exit;
-       }
-
-       if(get_config_bool(lookup_config(config_tree, "blacklisted"), &blacklist_status)) {
-               n->status.blacklisted = blacklist_status;
-       }
-
-exit:
-       exit_configuration(&config_tree);
-       return n->status.blacklisted;
-}
-
-bool node_write_devclass(meshlink_handle_t *mesh, node_t *n) {
-
-       if((int)n->devclass < 0 || n->devclass > _DEV_CLASS_MAX) {
+       if(len != 32) {
                return false;
        }
 
-       bool result = false;
-
-       splay_tree_t *config_tree;
-       init_configuration(&config_tree);
+       if(!ecdsa_active(n->ecdsa)) {
+               n->ecdsa = ecdsa_set_public_key(key);
+       }
 
-       // ignore read errors; in case the file does not exist we will create it
-       read_host_config(mesh, config_tree, n->name);
+       n->canonical_address = packmsg_get_str_dup(&in);
 
-       config_t *cnf = lookup_config(config_tree, "DeviceClass");
+       uint32_t count = packmsg_get_array(&in);
 
-       if(!cnf) {
-               cnf = new_config();
-               cnf->variable = xstrdup("DeviceClass");
-               config_add(config_tree, cnf);
+       if(count > 5) {
+               count = 5;
        }
 
-       set_config_int(cnf, n->devclass);
-
-       if(!write_host_config(mesh, config_tree, n->name)) {
-               goto fail;
+       for(uint32_t i = 0; i < count; i++) {
+               n->recent[i] = packmsg_get_sockaddr(&in);
        }
 
-       result = true;
+       config_free(&config);
 
-fail:
-       exit_configuration(&config_tree);
-       return result;
+       return packmsg_done(&in);
 }
 
-bool node_write_submesh(meshlink_handle_t *mesh, node_t *n) {
-
-       if(!n->submesh) {
-               return false;
-       }
+bool node_write_config(meshlink_handle_t *mesh, node_t *n) {
+       uint8_t buf[4096];
+       packmsg_output_t out = {buf, sizeof(buf)};
 
-       bool result = false;
+       packmsg_add_uint32(&out, MESHLINK_CONFIG_VERSION);
+       packmsg_add_str(&out, n->name);
+       packmsg_add_str(&out, n->submesh ? n->submesh->name : CORE_MESH);
+       packmsg_add_int32(&out, n->devclass);
+       assert(n->devclass != 3);
+       packmsg_add_bool(&out, n->status.blacklisted);
 
-       splay_tree_t *config_tree;
-       init_configuration(&config_tree);
+       if(ecdsa_active(n->ecdsa)) {
+               packmsg_add_bin(&out, ecdsa_get_public_key(n->ecdsa), 32);
+       } else {
+               packmsg_add_bin(&out, "", 0);
+       }
 
-       // ignore read errors; in case the file does not exist we will create it
-       read_host_config(mesh, config_tree, n->name);
+       packmsg_add_str(&out, n->canonical_address ? n->canonical_address : "");
 
-       config_t *cnf = lookup_config(config_tree, "SubMesh");
+       uint32_t count = 0;
 
-       if(!cnf) {
-               cnf = new_config();
-               cnf->variable = xstrdup("SubMesh");
-               config_add(config_tree, cnf);
+       for(uint32_t i = 0; i < 5; i++) {
+               if(n->recent[i].sa.sa_family) {
+                       count++;
+               } else {
+                       break;
+               }
        }
 
-       set_config_string(cnf, n->submesh->name);
+       packmsg_add_array(&out, count);
 
-       if(!write_host_config(mesh, config_tree, n->name)) {
-               goto fail;
+       for(uint32_t i = 0; i < count; i++) {
+               packmsg_add_sockaddr(&out, &n->recent[i]);
        }
 
-       result = true;
+       if(!packmsg_output_ok(&out)) {
+               return false;
+       }
 
-fail:
-       exit_configuration(&config_tree);
-       return result;
+       config_t config = {buf, packmsg_output_size(&out, buf)};
+       return config_write(mesh, n->name, &config);
 }
 
 void load_all_nodes(meshlink_handle_t *mesh) {
@@ -301,54 +249,18 @@ void load_all_nodes(meshlink_handle_t *mesh) {
                node_t *n = lookup_node(mesh, ent->d_name);
 
                if(n) {
-                       if(n == mesh->self && !n->submesh) {
-                               node_read_submesh(mesh, n);
-                       }
-
                        continue;
                }
 
                n = new_node();
                n->name = xstrdup(ent->d_name);
-               node_read_devclass(mesh, n);
-               node_read_submesh(mesh, n);
-               node_read_blacklist_status(mesh, n);
+               node_read_partial(mesh, n);
                node_add(mesh, n);
        }
 
        closedir(dir);
 }
 
-
-char *get_name(meshlink_handle_t *mesh) {
-       char *name = NULL;
-
-       get_config_string(lookup_config(mesh->config, "Name"), &name);
-
-       if(!name) {
-               return NULL;
-       }
-
-       if(!check_id(name)) {
-               logger(mesh, MESHLINK_ERROR, "Invalid name for mesh->self!");
-               free(name);
-               return NULL;
-       }
-
-       return name;
-}
-
-bool setup_myself_reloadable(meshlink_handle_t *mesh) {
-       mesh->localdiscovery = true;
-       keylifetime = 3600; // TODO: check if this can be removed as well
-       mesh->maxtimeout = 900;
-       mesh->self->options |= OPTION_PMTU_DISCOVERY;
-
-       read_invitation_key(mesh);
-
-       return true;
-}
-
 /*
   Add listening sockets.
 */
@@ -443,67 +355,12 @@ static bool add_listen_address(meshlink_handle_t *mesh, char *address, bool bind
   Configure node_t mesh->self and set up the local sockets (listen only)
 */
 bool setup_myself(meshlink_handle_t *mesh) {
-       char *name;
-       char *address = NULL;
-
-       if(!(name = get_name(mesh))) {
-               logger(mesh, MESHLINK_ERROR, "Name for MeshLink instance required!");
-               return false;
-       }
-
-       mesh->self = new_node();
-       mesh->self->connection = new_connection();
-       mesh->self->name = name;
-       mesh->self->devclass = mesh->devclass;
-       mesh->self->connection->name = xstrdup(name);
-       read_host_config(mesh, mesh->config, name);
-
-       if(!get_config_string(lookup_config(mesh->config, "Port"), &mesh->myport)) {
-               int port = check_port(mesh);
+       /* Set some defaults */
 
-               if(port == 0) {
-                       return false;
-               }
-
-               xasprintf(&mesh->myport, "%d", port);
-       }
-
-       mesh->self->connection->options = 0;
-       mesh->self->connection->protocol_major = PROT_MAJOR;
-       mesh->self->connection->protocol_minor = PROT_MINOR;
-
-       mesh->self->options |= PROT_MINOR << 24;
-
-       if(!read_ecdsa_private_key(mesh)) {
-               return false;
-       }
-
-       /* Ensure mesh->myport is numeric */
-
-       if(!atoi(mesh->myport)) {
-               struct addrinfo *ai = str2addrinfo("localhost", mesh->myport, SOCK_DGRAM);
-               sockaddr_t sa;
-
-               if(!ai || !ai->ai_addr) {
-                       return false;
-               }
-
-               free(mesh->myport);
-               memcpy(&sa, ai->ai_addr, ai->ai_addrlen);
-               sockaddr2str(&sa, NULL, &mesh->myport);
-       }
-
-       /* Check some options */
-
-       if(!setup_myself_reloadable(mesh)) {
-               return false;
-       }
-
-       /* Compression */
-
-       // TODO: drop compression in the packet layer?
-       mesh->self->incompression = 0;
-       mesh->self->connection->outcompression = 0;
+       mesh->localdiscovery = true;
+       keylifetime = 3600; // TODO: check if this can be removed as well
+       mesh->maxtimeout = 900;
+       mesh->self->options |= OPTION_PMTU_DISCOVERY;
 
        /* Done */
 
@@ -512,7 +369,6 @@ bool setup_myself(meshlink_handle_t *mesh) {
        mesh->self->status.reachable = true;
        mesh->self->last_state_change = mesh->loop.now.tv_sec;
 
-       node_write_devclass(mesh, mesh->self);
        node_add(mesh, mesh->self);
 
        graph(mesh);
@@ -523,7 +379,7 @@ bool setup_myself(meshlink_handle_t *mesh) {
 
        mesh->listen_sockets = 0;
 
-       if(!add_listen_address(mesh, address, NULL)) {
+       if(!add_listen_address(mesh, NULL, NULL)) {
                if(strcmp(mesh->myport, "0")) {
                        logger(mesh, MESHLINK_INFO, "Could not bind to port %s, asking OS to choose one for us", mesh->myport);
                        free(mesh->myport);
@@ -533,7 +389,7 @@ bool setup_myself(meshlink_handle_t *mesh) {
                                return false;
                        }
 
-                       if(!add_listen_address(mesh, address, NULL)) {
+                       if(!add_listen_address(mesh, NULL, NULL)) {
                                return false;
                        }
                } else {
@@ -587,15 +443,6 @@ void close_network_connections(meshlink_handle_t *mesh) {
                }
        }
 
-       if(mesh->outgoings) {
-               list_delete_list(mesh->outgoings);
-       }
-
-       if(mesh->self && mesh->self->connection) {
-               terminate_connection(mesh, mesh->self->connection, false);
-               free_connection(mesh->self->connection);
-       }
-
        for(int i = 0; i < mesh->listen_sockets; i++) {
                io_del(&mesh->loop, &mesh->listen_socket[i].tcp);
                io_del(&mesh->loop, &mesh->listen_socket[i].udp);
index d1a5df99a05f2e42b4ea9eaea335e1b3cf0dc3d4..31ebe5a4eae23feae3e7431eeac8d7b3af76c1a0 100644 (file)
@@ -381,6 +381,38 @@ static struct addrinfo *get_known_addresses(node_t *n) {
        return ai;
 }
 
+// Build a list of recently seen addresses.
+static struct addrinfo *get_recent_addresses(node_t *n) {
+       struct addrinfo *ai = NULL;
+       struct addrinfo *aip;
+
+       for(int i = 0; i < 5; i++) {
+               if(!n->recent[i].sa.sa_family) {
+                       break;
+               }
+
+               // Create a new struct addrinfo, and put it at the end of the list.
+               struct addrinfo *nai = xzalloc(sizeof(*nai) + SALEN(n->recent[i].sa));
+
+               if(!ai) {
+                       ai = nai;
+               } else {
+                       aip->ai_next = nai;
+               }
+
+               aip = nai;
+
+               nai->ai_family = n->recent[i].sa.sa_family;
+               nai->ai_socktype = SOCK_STREAM;
+               nai->ai_protocol = IPPROTO_TCP;
+               nai->ai_addrlen = SALEN(n->recent[i].sa);
+               nai->ai_addr = (struct sockaddr *)(nai + 1);
+               memcpy(nai->ai_addr, &n->recent[i], nai->ai_addrlen);
+       }
+
+       return ai;
+}
+
 // Free struct addrinfo list from get_known_addresses().
 static void free_known_addresses(struct addrinfo *ai) {
        for(struct addrinfo *aip = ai, *next; aip; aip = next) {
@@ -389,62 +421,30 @@ static void free_known_addresses(struct addrinfo *ai) {
        }
 }
 
-static bool get_recent(meshlink_handle_t *mesh, outgoing_t *outgoing) {
-       node_t *n = lookup_node(mesh, outgoing->name);
-
-       if(!n) {
+static struct addrinfo *get_canonical_address(node_t *n) {
+       if(!n->canonical_address) {
                return false;
        }
 
-       outgoing->ai = get_known_addresses(n);
-       outgoing->aip = outgoing->ai;
-       return outgoing->aip;
-}
-
-static bool get_next_ai(meshlink_handle_t *mesh, outgoing_t *outgoing) {
-       if(!outgoing->ai) {
-               char *address = NULL;
-
-               if(get_config_string(outgoing->cfg, &address)) {
-                       char *port;
-                       char *space = strchr(address, ' ');
-
-                       if(space) {
-                               port = xstrdup(space + 1);
-                               *space = 0;
-                       } else {
-                               if(!get_config_string(lookup_config(outgoing->config_tree, "Port"), &port)) {
-                                       logger(mesh, MESHLINK_ERROR, "No Port known for %s", outgoing->name);
-                                       return false;
-                               }
-                       }
-
-                       outgoing->ai = str2addrinfo(address, port, SOCK_STREAM);
-                       free(port);
-                       free(address);
-               }
+       char *address = xstrdup(n->canonical_address);
+       char *port = strchr(address, ' ');
 
-               outgoing->aip = outgoing->ai;
-       } else {
-               outgoing->aip = outgoing->aip->ai_next;
+       if(!port) {
+               free(address);
+               return false;
        }
 
-       return outgoing->aip;
-}
-
-static bool get_next_cfg(meshlink_handle_t *mesh, outgoing_t *outgoing, char *variable) {
-       (void)mesh;
+       *port++ = 0;
 
-       if(!outgoing->cfg) {
-               outgoing->cfg = lookup_config(outgoing->config_tree, variable);
-       } else {
-               outgoing->cfg = lookup_config_next(outgoing->config_tree, outgoing->cfg);
-       }
+       struct addrinfo *ai = str2addrinfo(address, port, SOCK_STREAM);
+       free(address);
 
-       return outgoing->cfg;
+       return ai;
 }
 
 static bool get_next_outgoing_address(meshlink_handle_t *mesh, outgoing_t *outgoing) {
+       (void)mesh;
+
        bool start = false;
 
        if(outgoing->state == OUTGOING_START) {
@@ -453,48 +453,56 @@ static bool get_next_outgoing_address(meshlink_handle_t *mesh, outgoing_t *outgo
        }
 
        if(outgoing->state == OUTGOING_CANONICAL) {
-               while(outgoing->aip || get_next_cfg(mesh, outgoing, "CanonicalAddress")) {
-                       if(get_next_ai(mesh, outgoing)) {
-                               return true;
-                       } else {
-                               freeaddrinfo(outgoing->ai);
-                               outgoing->ai = NULL;
-                               outgoing->aip = NULL;
-                       }
+               if(!outgoing->aip) {
+                       outgoing->ai = get_canonical_address(outgoing->node);
+                       outgoing->aip = outgoing->ai;
+               } else {
+                       outgoing->aip = outgoing->aip->ai_next;
+               }
+
+               if(outgoing->aip) {
+                       return true;
                }
 
+               freeaddrinfo(outgoing->ai);
+               outgoing->ai = NULL;
+               outgoing->aip = NULL;
                outgoing->state = OUTGOING_RECENT;
        }
 
        if(outgoing->state == OUTGOING_RECENT) {
-               while(outgoing->aip || get_next_cfg(mesh, outgoing, "Address")) {
-                       if(get_next_ai(mesh, outgoing)) {
-                               return true;
-                       } else {
-                               freeaddrinfo(outgoing->ai);
-                               outgoing->ai = NULL;
-                               outgoing->aip = NULL;
-                       }
+               if(!outgoing->aip) {
+                       outgoing->ai = get_recent_addresses(outgoing->node);
+                       outgoing->aip = outgoing->ai;
+               } else {
+                       outgoing->aip = outgoing->aip->ai_next;
+               }
+
+               if(outgoing->aip) {
+                       return true;
                }
 
+               free_known_addresses(outgoing->ai);
+               outgoing->ai = NULL;
+               outgoing->aip = NULL;
                outgoing->state = OUTGOING_KNOWN;
        }
 
        if(outgoing->state == OUTGOING_KNOWN) {
                if(!outgoing->aip) {
-                       get_recent(mesh, outgoing);
+                       outgoing->ai = get_known_addresses(outgoing->node);
+                       outgoing->aip = outgoing->ai;
                } else {
                        outgoing->aip = outgoing->aip->ai_next;
                }
 
                if(outgoing->aip) {
                        return true;
-               } else {
-                       free_known_addresses(outgoing->ai);
-                       outgoing->ai = NULL;
-                       outgoing->aip = NULL;
                }
 
+               free_known_addresses(outgoing->ai);
+               outgoing->ai = NULL;
+               outgoing->aip = NULL;
                outgoing->state = OUTGOING_END;
        }
 
@@ -513,9 +521,9 @@ begin:
 
        if(!get_next_outgoing_address(mesh, outgoing)) {
                if(outgoing->state == OUTGOING_NO_KNOWN_ADDRESSES) {
-                       logger(mesh, MESHLINK_ERROR, "No known addresses for %s", outgoing->name);
+                       logger(mesh, MESHLINK_ERROR, "No known addresses for %s", outgoing->node->name);
                } else {
-                       logger(mesh, MESHLINK_ERROR, "Could not set up a meta connection to %s", outgoing->name);
+                       logger(mesh, MESHLINK_ERROR, "Could not set up a meta connection to %s", outgoing->node->name);
                        retry_outgoing(mesh, outgoing);
                }
 
@@ -529,7 +537,7 @@ begin:
 
        char *hostname = sockaddr2hostname(&c->address);
 
-       logger(mesh, MESHLINK_INFO, "Trying to connect to %s at %s", outgoing->name, hostname);
+       logger(mesh, MESHLINK_INFO, "Trying to connect to %s at %s", outgoing->node->name, hostname);
 
        if(!mesh->proxytype) {
                c->socket = socket(c->address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
@@ -582,7 +590,7 @@ begin:
        }
 
        if(result == -1 && !sockinprogress(sockerrno)) {
-               logger(mesh, MESHLINK_ERROR, "Could not connect to %s: %s", outgoing->name, sockstrerror(sockerrno));
+               logger(mesh, MESHLINK_ERROR, "Could not connect to %s: %s", outgoing->node->name, sockstrerror(sockerrno));
                free_connection(c);
 
                goto begin;
@@ -591,8 +599,7 @@ begin:
        /* Now that there is a working socket, fill in the rest and register this connection. */
 
        c->status.connecting = true;
-       c->name = xstrdup(outgoing->name);
-       c->outcompression = mesh->self->connection->outcompression;
+       c->name = xstrdup(outgoing->node->name);
        c->last_ping_time = mesh->loop.now.tv_sec;
 
        connection_add(mesh, c);
@@ -603,37 +610,27 @@ begin:
 }
 
 void setup_outgoing_connection(meshlink_handle_t *mesh, outgoing_t *outgoing) {
-       bool blacklisted = false;
        timeout_del(&mesh->loop, &outgoing->ev);
 
-       node_t *n = lookup_node(mesh, outgoing->name);
+       if(outgoing->node->connection) {
+               logger(mesh, MESHLINK_INFO, "Already connected to %s", outgoing->node->name);
 
-       if(n && n->connection) {
-               logger(mesh, MESHLINK_INFO, "Already connected to %s", outgoing->name);
-
-               n->connection->outgoing = outgoing;
+               outgoing->node->connection->outgoing = outgoing;
                return;
        }
 
 
        if(outgoing->ai) {
-               if(outgoing->state == OUTGOING_KNOWN) {
+               if(outgoing->state == OUTGOING_RECENT || outgoing->state == OUTGOING_KNOWN) {
                        free_known_addresses(outgoing->ai);
                } else {
                        freeaddrinfo(outgoing->ai);
                }
        }
 
-       outgoing->cfg = NULL;
-
-       exit_configuration(&outgoing->config_tree); // discard old configuration if present
-       init_configuration(&outgoing->config_tree);
-       read_host_config(mesh, outgoing->config_tree, outgoing->name);
-       get_config_bool(lookup_config(outgoing->config_tree, "blacklisted"), &blacklisted);
-
        outgoing->state = OUTGOING_START;
 
-       if(blacklisted) {
+       if(outgoing->node->status.blacklisted) {
                return;
        }
 
@@ -704,7 +701,6 @@ void handle_new_meta_connection(event_loop_t *loop, void *data, int flags) {
 
        c = new_connection();
        c->name = xstrdup("<unknown>");
-       c->outcompression = mesh->self->connection->outcompression;
 
        c->address = sa;
        c->socket = fd;
@@ -725,88 +721,28 @@ void handle_new_meta_connection(event_loop_t *loop, void *data, int flags) {
 }
 
 static void free_outgoing(outgoing_t *outgoing) {
-       meshlink_handle_t *mesh = outgoing->mesh;
+       meshlink_handle_t *mesh = outgoing->node->mesh;
 
        timeout_del(&mesh->loop, &outgoing->ev);
 
        if(outgoing->ai) {
-               if(outgoing->state == OUTGOING_KNOWN) {
+               if(outgoing->state == OUTGOING_RECENT || outgoing->state == OUTGOING_KNOWN) {
                        free_known_addresses(outgoing->ai);
                } else {
                        freeaddrinfo(outgoing->ai);
                }
        }
 
-       if(outgoing->config_tree) {
-               exit_configuration(&outgoing->config_tree);
-       }
-
-       if(outgoing->name) {
-               free(outgoing->name);
-       }
-
        free(outgoing);
 }
 
-void try_outgoing_connections(meshlink_handle_t *mesh) {
-       /* If there is no outgoing list yet, create one. Otherwise, mark all outgoings as deleted. */
-
-       if(!mesh->outgoings) {
-               mesh->outgoings = list_alloc((list_action_t)free_outgoing);
-       } else {
-               for list_each(outgoing_t, outgoing, mesh->outgoings) {
-                       outgoing->timeout = -1;
-               }
-       }
-
-       /* Make sure there is one outgoing_t in the list for each ConnectTo. */
-
-       // TODO: Drop support for ConnectTo since AutoConnect is now always on?
-       for(config_t *cfg = lookup_config(mesh->config, "ConnectTo"); cfg; cfg = lookup_config_next(mesh->config, cfg)) {
-               char *name;
-               get_config_string(cfg, &name);
-
-               if(!check_id(name)) {
-                       logger(mesh, MESHLINK_ERROR,
-                              "Invalid name for outgoing connection in line %d",
-                              cfg->line);
-                       free(name);
-                       continue;
-               }
-
-               bool found = false;
-
-               for list_each(outgoing_t, outgoing, mesh->outgoings) {
-                       if(!strcmp(outgoing->name, name)) {
-                               found = true;
-                               outgoing->timeout = 0;
-                               break;
-                       }
-               }
-
-               if(!found) {
-                       outgoing_t *outgoing = xzalloc(sizeof(*outgoing));
-                       outgoing->mesh = mesh;
-                       outgoing->name = name;
-                       list_insert_tail(mesh->outgoings, outgoing);
-                       setup_outgoing_connection(mesh, outgoing);
-               }
-       }
-
-       /* Terminate any connections whose outgoing_t is to be deleted. */
+void init_outgoings(meshlink_handle_t *mesh) {
+       mesh->outgoings = list_alloc((list_action_t)free_outgoing);
+}
 
-       for list_each(connection_t, c, mesh->connections) {
-               if(c->outgoing && c->outgoing->timeout == -1) {
-                       c->outgoing = NULL;
-                       logger(mesh, MESHLINK_INFO, "No more outgoing connection to %s", c->name);
-                       terminate_connection(mesh, c, c->status.active);
-               }
+void exit_outgoings(meshlink_handle_t *mesh) {
+       if(mesh->outgoings) {
+               list_delete_list(mesh->outgoings);
+               mesh->outgoings = NULL;
        }
-
-       /* Delete outgoing_ts for which there is no ConnectTo. */
-
-       for list_each(outgoing_t, outgoing, mesh->outgoings)
-               if(outgoing->timeout == -1) {
-                       list_delete_node(mesh->outgoings, node);
-               }
 }
index 2a62c18a7ff64148eb15e5f5ea9be4aa5fc43b2a..895d9aa8116e82aea5fde1c9d3e73652a1cd72ec 100644 (file)
@@ -133,6 +133,7 @@ char *sockaddr2hostname(const sockaddr_t *sa) {
 
        if(err) {
                logger(NULL, MESHLINK_ERROR, "Error while looking up hostname: %s", err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
+               abort();
        }
 
        xasprintf(&str, "%s port %s", address, port);
@@ -226,6 +227,23 @@ void sockaddrcpy(sockaddr_t *a, const sockaddr_t *b) {
        }
 }
 
+void sockaddrcpy_setport(sockaddr_t *a, const sockaddr_t *b, uint16_t port) {
+       sockaddrcpy(a, b);
+
+       switch(b->sa.sa_family) {
+       case AF_INET:
+               a->in.sin_port = port;
+               break;
+
+       case AF_INET6:
+               a->in6.sin6_port = port;
+               break;
+
+       default:
+               break;
+       }
+}
+
 void sockaddrfree(sockaddr_t *a) {
        if(a->sa.sa_family == AF_UNKNOWN) {
                free(a->unknown.address);
@@ -239,3 +257,65 @@ void sockaddrunmap(sockaddr_t *sa) {
                sa->in.sin_family = AF_INET;
        }
 }
+
+void packmsg_add_sockaddr(packmsg_output_t *out, const sockaddr_t *sa) {
+       switch(sa->sa.sa_family) {
+       case AF_INET: {
+               uint8_t buf[6];
+               memcpy(buf + 0, &sa->in.sin_port, 2);
+               memcpy(buf + 2, &sa->in.sin_addr, 4);
+               packmsg_add_ext(out, 4, buf, sizeof(buf));
+               break;
+       }
+
+       case AF_INET6: {
+               uint8_t buf[18];
+               memcpy(buf + 0, &sa->in6.sin6_port, 2);
+               memcpy(buf + 2, &sa->in6.sin6_addr, 16);
+               packmsg_add_ext(out, 6, buf, sizeof(buf));
+               break;
+       }
+
+       default:
+               packmsg_output_invalidate(out);
+               break;
+       }
+}
+
+sockaddr_t packmsg_get_sockaddr(packmsg_input_t *in) {
+       sockaddr_t sa = {0};
+
+       int8_t type;
+       const void *data;
+       uint32_t len = packmsg_get_ext_raw(in, &type, &data);
+
+       switch(type) {
+       case 4:
+               if(len != 6) {
+                       packmsg_input_invalidate(in);
+                       return sa;
+               }
+
+               sa.sa.sa_family = AF_INET;
+               memcpy(&sa.in.sin_port, (uint8_t *)data + 0, 2);
+               memcpy(&sa.in.sin_addr, (uint8_t *)data + 2, 4);
+               break;
+
+       case 6:
+               if(len != 18) {
+                       packmsg_input_invalidate(in);
+                       return sa;
+               }
+
+               sa.sa.sa_family = AF_INET6;
+               memcpy(&sa.in6.sin6_port, (uint8_t *)data + 0, 2);
+               memcpy(&sa.in6.sin6_addr, (uint8_t *)data + 2, 16);
+               break;
+
+       default:
+               packmsg_input_invalidate(in);
+               return sa;
+       }
+
+       return sa;
+}
index 2f68e8325b35a579efce6c54e578a1e3e0de73ec..ce4c6a84daf70a3c0d5223c6e9eef014f19e39fc 100644 (file)
@@ -21,6 +21,7 @@
 */
 
 #include "net.h"
+#include "packmsg.h"
 
 extern bool hostnames;
 
@@ -33,5 +34,9 @@ extern int sockaddrcmp_noport(const sockaddr_t *, const sockaddr_t *);
 extern void sockaddrunmap(sockaddr_t *);
 extern void sockaddrfree(sockaddr_t *);
 extern void sockaddrcpy(sockaddr_t *, const sockaddr_t *);
+extern void sockaddrcpy_setport(sockaddr_t *, const sockaddr_t *, uint16_t port);
+
+extern void packmsg_add_sockaddr(struct packmsg_output *out, const sockaddr_t *);
+extern sockaddr_t packmsg_get_sockaddr(struct packmsg_input *in);
 
 #endif
index 9fad554550f0da2b3cc62da77dc0ce6c21637403..2568f182aaf17cca2ca1d9dcce52f16c2b8a3efa 100644 (file)
@@ -39,7 +39,8 @@ typedef struct node_status_t {
        unsigned int blacklisted: 1;            /* 1 if the node is blacklist so we never want to speak with him anymore */
        unsigned int destroyed: 1;              /* 1 if the node is being destroyed, deallocate channels when any callback is triggered */
        unsigned int duplicate: 1;              /* 1 if the node is duplicate, ie. multiple nodes using the same Name are online */
-       unsigned int unused: 20;
+       unsigned int dirty: 1;                  /* 1 if the configuration of the node is dirty and needs to be written out */
+       unsigned int unused: 19;
 } node_status_t;
 
 typedef struct node_t {
@@ -47,7 +48,7 @@ typedef struct node_t {
        void *priv;
 
        uint32_t options;                       /* options turned on for this node */
-       dev_class_t devclass;
+       int32_t devclass;
 
        struct meshlink_handle *mesh;           /* The mesh this node belongs to */
        struct submesh_t *submesh;              /* Nodes Sub-Mesh Handle*/
@@ -70,7 +71,7 @@ typedef struct node_t {
        struct edge_t *prevedge;                /* nearest node from him to us */
        struct node_t *via;                     /* next hop for UDP packets */
 
-       struct splay_tree_t *edge_tree;                /* Edges with this node as one of the endpoints */
+       struct splay_tree_t *edge_tree;         /* Edges with this node as one of the endpoints */
 
        struct connection_t *connection;        /* Connection associated with this node (if a direct connection exists) */
        time_t last_connect_try;
@@ -88,16 +89,19 @@ typedef struct node_t {
        uint64_t in_bytes;
        uint64_t out_packets;
        uint64_t out_bytes;
+
+       char *canonical_address;                /* The canonical address of this node, if known */
+       sockaddr_t recent[5];                   /* Recently seen addresses */
 } node_t;
 
 extern void init_nodes(struct meshlink_handle *mesh);
 extern void exit_nodes(struct meshlink_handle *mesh);
 extern node_t *new_node(void) __attribute__((__malloc__));
-extern void free_node(node_t *);
-extern void node_add(struct meshlink_handle *mesh, node_t *);
-extern void node_del(struct meshlink_handle *mesh, node_t *);
-extern node_t *lookup_node(struct meshlink_handle *mesh, const char *);
-extern node_t *lookup_node_udp(struct meshlink_handle *mesh, const sockaddr_t *);
-extern void update_node_udp(struct meshlink_handle *mesh, node_t *, const sockaddr_t *);
+extern void free_node(node_t *n);
+extern void node_add(struct meshlink_handle *mesh, node_t *n);
+extern void node_del(struct meshlink_handle *mesh, node_t *n);
+extern node_t *lookup_node(struct meshlink_handle *mesh, const char *name);
+extern node_t *lookup_node_udp(struct meshlink_handle *mesh, const sockaddr_t *sa);
+extern void update_node_udp(struct meshlink_handle *mesh, node_t *n, const sockaddr_t *sa);
 
 #endif
index f88aaf4b9e7bf430c08cffb02542fe1b9170dbc6..9fec1ffad737451a94d17f7b5d5132196a0ff7bc 100644 (file)
@@ -30,6 +30,7 @@
 #include "net.h"
 #include "netutl.h"
 #include "node.h"
+#include "packmsg.h"
 #include "prf.h"
 #include "protocol.h"
 #include "sptps.h"
@@ -143,56 +144,35 @@ static bool send_proxyrequest(meshlink_handle_t *mesh, connection_t *c) {
 }
 
 bool send_id(meshlink_handle_t *mesh, connection_t *c) {
-
-       int minor = mesh->self->connection->protocol_minor;
-
        if(mesh->proxytype && c->outgoing)
                if(!send_proxyrequest(mesh, c)) {
                        return false;
                }
 
-       return send_request(mesh, c, NULL, "%d %s %d.%d %s", ID, mesh->self->connection->name, mesh->self->connection->protocol_major, minor, mesh->appname);
+       return send_request(mesh, c, NULL, "%d %s %d.%d %s", ID, mesh->self->name, PROT_MAJOR, PROT_MINOR, mesh->appname);
 }
 
 static bool finalize_invitation(meshlink_handle_t *mesh, connection_t *c, const void *data, uint16_t len) {
-       (void)len;
-
-       if(strchr(data, '\n')) {
+       if(len != 32) {
                logger(mesh, MESHLINK_ERROR, "Received invalid key from invited node %s!\n", c->name);
                return false;
        }
 
-       // Create a new host config file
-       char filename[PATH_MAX];
-       snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", mesh->confbase, c->name);
-
-       if(!access(filename, F_OK)) {
-               logger(mesh, MESHLINK_ERROR, "Host config file for %s already exists!\n", c->name);
-               return false;
-       }
-
-       FILE *f = fopen(filename, "w");
-
-       if(!f) {
-               logger(mesh, MESHLINK_ERROR, "Error trying to create %s: %s\n", filename, strerror(errno));
-               return false;
-       }
-
-       fprintf(f, "ECDSAPublicKey = %s\n", (const char *)data);
-
-       if(c->submesh) {
-               fprintf(f, "SubMesh = %s\n", c->submesh->name);
-       }
-
-       fclose(f);
+       // Create a new node
+       node_t *n = new_node();
+       n->name = xstrdup(c->name);
+       n->devclass = DEV_CLASS_UNKNOWN;
+       n->ecdsa = ecdsa_set_public_key(data);
+       n->submesh = c->submesh;
+       n->status.dirty = true;
+       node_add(mesh, n);
+       // TODO: immediately write the config file?
 
        logger(mesh, MESHLINK_INFO, "Key successfully received from %s", c->name);
 
        //TODO: callback to application to inform of an accepted invitation
 
-       sptps_send_record(&c->sptps, 2, data, 0);
-
-       load_all_nodes(mesh);
+       sptps_send_record(&c->sptps, 1, "", 0);
 
        return true;
 }
@@ -224,100 +204,31 @@ static bool receive_invitation_sptps(void *handle, uint8_t type, const void *dat
        b64encode_urlsafe(hash, cookie, 18);
        free(fingerprint);
 
-       char filename[PATH_MAX], usedname[PATH_MAX];
-       snprintf(filename, sizeof(filename), "%s" SLASH "invitations" SLASH "%s", mesh->confbase, cookie);
-       snprintf(usedname, sizeof(usedname), "%s" SLASH "invitations" SLASH "%s.used", mesh->confbase, cookie);
-
-       // Atomically rename the invitation file
-       if(rename(filename, usedname)) {
-               if(errno == ENOENT) {
-                       logger(mesh, MESHLINK_ERROR, "Peer %s tried to use non-existing invitation %s\n", c->name, cookie);
-               } else {
-                       logger(mesh, MESHLINK_ERROR, "Error trying to rename invitation %s\n", cookie);
-               }
-
-               return false;
-       }
-
-       // Open the renamed file
-       FILE *f = fopen(usedname, "r");
-
-       if(!f) {
-               logger(mesh, MESHLINK_ERROR, "Error trying to open invitation %s\n", cookie);
-               unlink(usedname);
-               return false;
-       }
-
-       // Check the timestamp
-       struct stat st;
-
-       if(fstat(fileno(f), &st)) {
-               logger(mesh, MESHLINK_ERROR, "Could not stat invitation file %s\n", usedname);
-               fclose(f);
-               unlink(usedname);
-               return false;
-       }
+       config_t config;
 
-       if(time(NULL) > st.st_mtime + mesh->invitation_timeout) {
-               logger(mesh, MESHLINK_ERROR, "Peer %s tried to use an outdated invitation file %s\n", c->name, usedname);
-               fclose(f);
-               unlink(usedname);
+       if(!invitation_read(mesh, cookie, &config)) {
+               logger(mesh, MESHLINK_ERROR, "Error while trying to read invitation file\n");
                return false;
        }
 
        // Read the new node's Name from the file
-       char buf[1024];
-       fgets(buf, sizeof(buf), f);
-
-       if(*buf) {
-               buf[strlen(buf) - 1] = 0;
-       }
-
-       len = strcspn(buf, " \t=");
-       char *name = buf + len;
-       name += strspn(name, " \t");
-
-       if(*name == '=') {
-               name++;
-               name += strspn(name, " \t");
-       }
-
-       buf[len] = 0;
-
-       if(!*buf || !*name || strcasecmp(buf, "Name") || !check_id(name)) {
-               logger(mesh, MESHLINK_ERROR, "Invalid invitation file %s\n", cookie);
-               fclose(f);
-               return false;
-       }
-
+       packmsg_input_t in = {config.buf, config.len};
+       packmsg_get_uint32(&in); // skip version
        free(c->name);
-       c->name = xstrdup(name);
+       c->name = packmsg_get_str_dup(&in);
 
        // Check if the file contains Sub-Mesh information
-       buf[0] = 0;
-       fgets(buf, sizeof(buf), f);
-
-       if(*buf) {
-               buf[strlen(buf) - 1] = 0;
-       }
-
-       if(!strncmp(buf, "SubMesh", 7)) {
-               len = strcspn(buf, " \t=");
-               char *submesh_name = buf + len;
-               submesh_name += strspn(submesh_name, " \t");
-
-               if(*submesh_name == '=') {
-                       submesh_name++;
-                       submesh_name += strspn(submesh_name, " \t");
-               }
+       char *submesh_name = packmsg_get_str_dup(&in);
 
+       if(!strcmp(submesh_name, CORE_MESH)) {
+               c->submesh = NULL;
+       } else {
                if(!check_id(submesh_name)) {
                        logger(mesh, MESHLINK_ERROR, "Invalid invitation file %s\n", cookie);
-                       fclose(f);
+                       abort();
                        return false;
                }
 
-               c->submesh = NULL;
                c->submesh = lookup_or_create_submesh(mesh, submesh_name);
 
                if(!c->submesh) {
@@ -326,16 +237,7 @@ static bool receive_invitation_sptps(void *handle, uint8_t type, const void *dat
        }
 
        // Send the node the contents of the invitation file
-       rewind(f);
-       size_t result;
-
-       while((result = fread(buf, 1, sizeof(buf), f))) {
-               sptps_send_record(&c->sptps, 0, buf, result);
-       }
-
-       sptps_send_record(&c->sptps, 1, buf, 0);
-       fclose(f);
-       unlink(usedname);
+       sptps_send_record(&c->sptps, 0, config.buf, config.len);
 
        c->status.invitation_used = true;
 
@@ -409,37 +311,32 @@ bool id_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
 
        /* Check if version matches */
 
-       if(c->protocol_major != mesh->self->connection->protocol_major) {
+       if(c->protocol_major != PROT_MAJOR) {
                logger(mesh, MESHLINK_ERROR, "Peer %s uses incompatible version %d.%d",
                       c->name, c->protocol_major, c->protocol_minor);
                return false;
        }
 
-       if(!c->config_tree) {
-               init_configuration(&c->config_tree);
+       /* Check if we know this node */
 
-               if(!read_host_config(mesh, c->config_tree, c->name)) {
-                       logger(mesh, MESHLINK_ERROR, "Peer %s has unknown identity", c->name);
-                       return false;
-               }
-       }
+       node_t *n = lookup_node(mesh, c->name);
 
-       bool blacklisted = false;
-       get_config_bool(lookup_config(c->config_tree, "blacklisted"), &blacklisted);
+       if(!n) {
+               logger(mesh, MESHLINK_ERROR, "Peer %s has unknown identity", c->name);
+               return false;
+       }
 
-       if(blacklisted) {
+       if(n->status.blacklisted) {
                logger(mesh, MESHLINK_EPEER, "Peer %s is blacklisted", c->name);
                return false;
        }
 
-       read_ecdsa_public_key(mesh, c);
+       node_read_public_key(mesh, n);
 
-       if(!ecdsa_active(c->ecdsa)) {
+       if(!ecdsa_active(n->ecdsa)) {
                logger(mesh, MESHLINK_ERROR, "No key known for peer %s", c->name);
 
-               node_t *n = lookup_node(mesh, c->name);
-
-               if(n && n->status.reachable && !n->status.waitingforkey) {
+               if(n->status.reachable && !n->status.waitingforkey) {
                        logger(mesh, MESHLINK_INFO, "Requesting key from peer %s", c->name);
                        send_req_key(mesh, n);
                }
@@ -464,7 +361,11 @@ bool id_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
                snprintf(label, sizeof(label), "%s %s %s", meshlink_tcp_label, c->name, mesh->self->name);
        }
 
-       return sptps_start(&c->sptps, c, c->outgoing, false, mesh->self->connection->ecdsa, c->ecdsa, label, sizeof(label) - 1, send_meta_sptps, receive_meta_sptps);
+       char buf1[1024], buf2[1024];
+       bin2hex((uint8_t *)mesh->private_key + 64, buf1, 32);
+       bin2hex((uint8_t *)n->ecdsa + 64, buf2, 32);
+       logger(mesh, MESHLINK_DEBUG, "Connection to %s mykey %s hiskey %s", c->name, buf1, buf2);
+       return sptps_start(&c->sptps, c, c->outgoing, false, mesh->private_key, n->ecdsa, label, sizeof(label) - 1, send_meta_sptps, receive_meta_sptps);
 }
 
 bool send_ack(meshlink_handle_t *mesh, connection_t *c) {
@@ -490,7 +391,6 @@ static void send_everything(meshlink_handle_t *mesh, connection_t *c) {
 
 bool ack_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
        char hisport[MAX_STRING_SIZE];
-       char *hisaddress;
        int devclass;
        uint32_t options;
        node_t *n;
@@ -535,7 +435,7 @@ bool ack_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
        }
 
        n->devclass = devclass;
-       node_write_devclass(mesh, n);
+       n->status.dirty = true;
 
        n->last_successfull_connection = time(NULL);
 
@@ -567,9 +467,7 @@ bool ack_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
        c->edge = new_edge();
        c->edge->from = mesh->self;
        c->edge->to = n;
-       sockaddr2str(&c->address, &hisaddress, NULL);
-       c->edge->address = str2sockaddr(hisaddress, hisport);
-       free(hisaddress);
+       sockaddrcpy_setport(&c->edge->address, &c->address, atoi(hisport));
        c->edge->weight = dev_class_traits[devclass].edge_weight;
        c->edge->connection = c;
        c->edge->options = c->options;
index 5bfd6e21830d961bb51210a1b3be49dc87a0d5bd..b5de2821c8d28f7c303510fcce26840aa0ff6337 100644 (file)
@@ -34,9 +34,6 @@
 #include "xalloc.h"
 #include "submesh.h"
 
-extern bool node_write_devclass(meshlink_handle_t *mesh, node_t *n);
-extern bool node_write_submesh(meshlink_handle_t *mesh, node_t *n);
-
 bool send_add_edge(meshlink_handle_t *mesh, connection_t *c, const edge_t *e, int contradictions) {
        bool x;
        char *address, *port;
@@ -143,8 +140,10 @@ bool add_edge_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
 
        if(!from) {
                from = new_node();
+               from->status.dirty = true;
                from->status.blacklisted = mesh->default_blacklist;
                from->name = xstrdup(from_name);
+               from->devclass = from_devclass;
 
                from->submesh = NULL;
 
@@ -162,16 +161,13 @@ bool add_edge_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
        }
 
        from->devclass = from_devclass;
-       node_write_devclass(mesh, from);
-
-       if(from->submesh) {
-               node_write_submesh(mesh, from);
-       }
 
        if(!to) {
                to = new_node();
+               to->status.dirty = true;
                to->status.blacklisted = mesh->default_blacklist;
                to->name = xstrdup(to_name);
+               to->devclass = to_devclass;
 
                to->submesh = NULL;
 
@@ -185,13 +181,6 @@ bool add_edge_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
                node_add(mesh, to);
        }
 
-       to->devclass = to_devclass;
-       node_write_devclass(mesh, to);
-
-       if(to->submesh) {
-               node_write_submesh(mesh, to);
-       }
-
        /* Convert addresses */
 
        address = str2sockaddr(to_address, to_port);
index 43a227bbd65b459aaf7254a5069609ff2a1ea6f7..c1ffe322aa06ce24046494923ad4e8cdece13a73 100644 (file)
@@ -80,7 +80,7 @@ static bool send_initial_sptps_data(void *handle, uint8_t type, const void *data
 }
 
 bool send_req_key(meshlink_handle_t *mesh, node_t *to) {
-       if(!node_read_ecdsa_public_key(mesh, to)) {
+       if(!node_read_public_key(mesh, to)) {
                logger(mesh, MESHLINK_DEBUG, "No ECDSA key known for %s", to->name);
                send_request(mesh, to->nexthop->connection, NULL, "%d %s %s %d", REQ_KEY, mesh->self->name, to->name, REQ_PUBKEY);
                return true;
@@ -97,7 +97,7 @@ bool send_req_key(meshlink_handle_t *mesh, node_t *to) {
        to->status.waitingforkey = true;
        to->last_req_key = mesh->loop.now.tv_sec;
        to->incompression = mesh->self->incompression;
-       return sptps_start(&to->sptps, to, true, true, mesh->self->connection->ecdsa, to->ecdsa, label, sizeof(label) - 1, send_initial_sptps_data, receive_sptps_record);
+       return sptps_start(&to->sptps, to, true, true, mesh->private_key, to->ecdsa, label, sizeof(label) - 1, send_initial_sptps_data, receive_sptps_record);
 }
 
 /* REQ_KEY is overloaded to allow arbitrary requests to be routed between two nodes. */
@@ -107,7 +107,7 @@ static bool req_key_ext_h(meshlink_handle_t *mesh, connection_t *c, const char *
 
        switch(reqno) {
        case REQ_PUBKEY: {
-               char *pubkey = ecdsa_get_base64_public_key(mesh->self->connection->ecdsa);
+               char *pubkey = ecdsa_get_base64_public_key(mesh->private_key);
 
                if(!from->nexthop || !from->nexthop->connection) {
                        return false;
@@ -119,7 +119,7 @@ static bool req_key_ext_h(meshlink_handle_t *mesh, connection_t *c, const char *
        }
 
        case ANS_PUBKEY: {
-               if(node_read_ecdsa_public_key(mesh, from)) {
+               if(node_read_public_key(mesh, from)) {
                        logger(mesh, MESHLINK_WARNING, "Got ANS_PUBKEY from %s even though we already have his pubkey", from->name);
                        return true;
                }
@@ -132,12 +132,12 @@ static bool req_key_ext_h(meshlink_handle_t *mesh, connection_t *c, const char *
                }
 
                logger(mesh, MESHLINK_INFO, "Learned ECDSA public key from %s", from->name);
-               append_config_file(mesh, from->name, "ECDSAPublicKey", pubkey);
+               from->status.dirty = true;
                return true;
        }
 
        case REQ_KEY: {
-               if(!node_read_ecdsa_public_key(mesh, from)) {
+               if(!node_read_public_key(mesh, from)) {
                        logger(mesh, MESHLINK_DEBUG, "No ECDSA key known for %s", from->name);
                        send_request(mesh, from->nexthop->connection, NULL, "%d %s %s %d", REQ_KEY, mesh->self->name, from->name, REQ_PUBKEY);
                        return true;
@@ -166,7 +166,7 @@ static bool req_key_ext_h(meshlink_handle_t *mesh, connection_t *c, const char *
                from->status.validkey = false;
                from->status.waitingforkey = true;
                from->last_req_key = mesh->loop.now.tv_sec;
-               sptps_start(&from->sptps, from, false, true, mesh->self->connection->ecdsa, from->ecdsa, label, sizeof(label) - 1, send_sptps_data, receive_sptps_record);
+               sptps_start(&from->sptps, from, false, true, mesh->private_key, from->ecdsa, label, sizeof(label) - 1, send_sptps_data, receive_sptps_record);
                sptps_receive_data(&from->sptps, buf, len);
                return true;
        }
index 8855d20e22a6ada4967c6bd8df759103173ccf2e..dde4bbb4db377445cda34c3b6e028ebb8268237f 100644 (file)
@@ -91,9 +91,10 @@ bool pong_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
 
        /* Successful connection, reset timeout if this is an outgoing connection. */
 
+       // TODO: completely remove this outgoing, let the autoconnect algorithm handle it
        if(c->outgoing) {
                c->outgoing->timeout = 0;
-               c->outgoing->cfg = NULL;
+               c->outgoing->state = OUTGOING_START;
 
                if(c->outgoing->ai) {
                        freeaddrinfo(c->outgoing->ai);
index 1a98d47897f87f57e530115330a1fec17c377655..4a823aa9f6471697fcfe1c9c677483e9c73ef4c4 100644 (file)
@@ -4,9 +4,9 @@
 #define AF_UNKNOWN 255
 
 #ifdef SA_LEN
-#define SALEN(s) SA_LEN(&s)
+#define SALEN(s) SA_LEN(&(s))
 #else
-#define SALEN(s) (s.sa_family==AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6))
+#define SALEN(s) ((s).sa_family==AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6))
 #endif
 
 struct sockaddr_unknown {
index dce3747643128b3d2133f21f8cd6ead70b3d9841..e96213ed7026393d5f3f3564c06041dd0978d5f4 100644 (file)
@@ -5,6 +5,7 @@ TESTS = \
        channels-fork.test \
        channels-cornercases.test \
        duplicate.test \
+       encrypted.test \
        import-export.test \
        invite-join.test \
        sign-verify.test \
@@ -27,6 +28,7 @@ check_PROGRAMS = \
        channels-cornercases \
        duplicate \
        echo-fork \
+       encrypted \
        import-export \
        invite-join \
        sign-verify \
@@ -57,6 +59,9 @@ duplicate_LDADD = ../src/libmeshlink.la
 echo_fork_SOURCES = echo-fork.c
 echo_fork_LDADD = ../src/libmeshlink.la
 
+encrypted_SOURCES = encrypted.c
+encrypted_LDADD = ../src/libmeshlink.la
+
 import_export_SOURCES = import-export.c
 import_export_LDADD = ../src/libmeshlink.la
 
index 7b4f3cd71f5c510071e5b525fb5f0c87b74682ad..15bf7bf805ae0bade3cf59532aaeac77f9050710 100644 (file)
@@ -5,7 +5,27 @@
 
 #include "meshlink.h"
 
+void log_cb(meshlink_handle_t *mesh, meshlink_log_level_t level, const char *text) {
+       static struct timeval tv0;
+       struct timeval tv;
+
+       if(tv0.tv_sec == 0) {
+               gettimeofday(&tv0, NULL);
+       }
+
+       gettimeofday(&tv, NULL);
+       fprintf(stderr, "%u.%.03u ", (unsigned int)(tv.tv_sec - tv0.tv_sec), (unsigned int)tv.tv_usec / 1000);
+
+       if(mesh) {
+               fprintf(stderr, "(%s) ", mesh->name);
+       }
+
+       fprintf(stderr, "[%d] %s\n", level, text);
+}
+
 int main() {
+       meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb);
+
        // Open a new meshlink instance.
 
        meshlink_handle_t *mesh = meshlink_open("basic_conf", "foo", "basic", DEV_CLASS_BACKBONE);
@@ -15,6 +35,8 @@ int main() {
                return 1;
        }
 
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, log_cb);
+
        // Check that our own node exists.
 
        meshlink_node_t *self = meshlink_get_self(mesh);
@@ -60,6 +82,8 @@ int main() {
                return 1;
        }
 
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, log_cb);
+
        if(meshlink_get_node(mesh, "bar")) {
                fprintf(stderr, "Foo knows about bar, it shouldn't\n");
                return 1;
diff --git a/test/encrypted.c b/test/encrypted.c
new file mode 100644 (file)
index 0000000..e51debd
--- /dev/null
@@ -0,0 +1,76 @@
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/time.h>
+
+#include "meshlink.h"
+
+void log_cb(meshlink_handle_t *mesh, meshlink_log_level_t level, const char *text) {
+       static struct timeval tv0;
+       struct timeval tv;
+
+       if(tv0.tv_sec == 0) {
+               gettimeofday(&tv0, NULL);
+       }
+
+       gettimeofday(&tv, NULL);
+       fprintf(stderr, "%u.%.03u ", (unsigned int)(tv.tv_sec - tv0.tv_sec), (unsigned int)tv.tv_usec / 1000);
+
+       if(mesh) {
+               fprintf(stderr, "(%s) ", mesh->name);
+       }
+
+       fprintf(stderr, "[%d] %s\n", level, text);
+}
+
+int main() {
+       meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb);
+
+       // Open a new meshlink instance.
+
+       meshlink_handle_t *mesh = meshlink_open_encrypted("encrypted_conf", "foo", "encrypted", DEV_CLASS_BACKBONE, "right", 5);
+
+       if(!mesh) {
+               fprintf(stderr, "Could not initialize configuration for foo\n");
+               return 1;
+       }
+
+       // Close the mesh and open it again, now with a different key.
+
+       meshlink_close(mesh);
+
+       mesh = meshlink_open_encrypted("encrypted_conf", "foo", "encrypted", DEV_CLASS_BACKBONE, "wrong", 5);
+
+       if(mesh) {
+               fprintf(stderr, "Could open mesh with the wrong key\n");
+               return 1;
+       }
+
+       // Open it again, now with the right key.
+
+       mesh = meshlink_open_encrypted("encrypted_conf", "foo", "encrypted", DEV_CLASS_BACKBONE, "right", 5);
+
+       if(!mesh) {
+               fprintf(stderr, "Could not open mesh with the right key\n");
+               return 1;
+       }
+
+       // That's it.
+
+       meshlink_close(mesh);
+
+       // Destroy the mesh.
+
+       if(!meshlink_destroy("encrypted_conf")) {
+               fprintf(stderr, "Could not destroy configuration\n");
+               return 1;
+       }
+
+       if(!access("encrypted_conf", F_OK) || errno != ENOENT) {
+               fprintf(stderr, "Configuration not fully destroyed\n");
+               return 1;
+       }
+
+       return 0;
+}
diff --git a/test/encrypted.test b/test/encrypted.test
new file mode 100755 (executable)
index 0000000..f82e807
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+rm -Rf encrypted_conf
+./encrypted
index de2231e9298d2b673c4b808765e034c91b82a086..eaa94497ba918f86d7ef84e15901df849287652d 100644 (file)
@@ -7,6 +7,24 @@
 
 volatile bool bar_reachable = false;
 
+void log_cb(meshlink_handle_t *mesh, meshlink_log_level_t level, const char *text) {
+       static struct timeval tv0;
+       struct timeval tv;
+
+       if(tv0.tv_sec == 0) {
+               gettimeofday(&tv0, NULL);
+       }
+
+       gettimeofday(&tv, NULL);
+       fprintf(stderr, "%u.%.03u ", (unsigned int)(tv.tv_sec - tv0.tv_sec), (unsigned int)tv.tv_usec / 1000);
+
+       if(mesh) {
+               fprintf(stderr, "(%s) ", mesh->name);
+       }
+
+       fprintf(stderr, "[%d] %s\n", level, text);
+}
+
 void status_cb(meshlink_handle_t *mesh, meshlink_node_t *node, bool reachable) {
        (void)mesh;
 
@@ -16,12 +34,14 @@ void status_cb(meshlink_handle_t *mesh, meshlink_node_t *node, bool reachable) {
 }
 
 int main() {
+       meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb);
+
        // Open two new meshlink instance.
 
        meshlink_handle_t *mesh1 = meshlink_open("import_export_conf.1", "foo", "import-export", DEV_CLASS_BACKBONE);
 
        if(!mesh1) {
-               fprintf(stderr, "Could not initialize configuration for foo\n");
+               fprintf(stderr, "Could not initialize configuration for foo: %s\n", meshlink_strerror(meshlink_errno));
                return 1;
        }
 
@@ -32,6 +52,9 @@ int main() {
                return 1;
        }
 
+       meshlink_set_log_cb(mesh1, MESHLINK_DEBUG, log_cb);
+       meshlink_set_log_cb(mesh2, MESHLINK_DEBUG, log_cb);
+
        // Disable local discovery
 
        meshlink_enable_discovery(mesh1, false);
@@ -49,6 +72,8 @@ int main() {
                return 1;
        }
 
+       fprintf(stderr, "Foo export data:\n%s\n", data);
+
        if(!meshlink_import(mesh2, data)) {
                fprintf(stderr, "Bar could not import foo's configuration\n");
                return 1;
index eee5fa96b016e72e716b2652e19f88e27dd879ed..c9ebea6043d2a39b05f02c9e163711eec028a61b 100644 (file)
@@ -7,6 +7,24 @@
 
 volatile bool baz_reachable = false;
 
+void log_cb(meshlink_handle_t *mesh, meshlink_log_level_t level, const char *text) {
+       static struct timeval tv0;
+       struct timeval tv;
+
+       if(tv0.tv_sec == 0) {
+               gettimeofday(&tv0, NULL);
+       }
+
+       gettimeofday(&tv, NULL);
+       fprintf(stderr, "%u.%.03u ", (unsigned int)(tv.tv_sec - tv0.tv_sec), (unsigned int)tv.tv_usec / 1000);
+
+       if(mesh) {
+               fprintf(stderr, "(%s) ", mesh->name);
+       }
+
+       fprintf(stderr, "[%d] %s\n", level, text);
+}
+
 void status_cb(meshlink_handle_t *mesh, meshlink_node_t *node, bool reachable) {
        (void)mesh;
 
@@ -16,7 +34,9 @@ void status_cb(meshlink_handle_t *mesh, meshlink_node_t *node, bool reachable) {
 }
 
 int main() {
-       // Open two new meshlink instance.
+       meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb);
+
+       // Open thee new meshlink instance.
 
        meshlink_handle_t *mesh1 = meshlink_open("invite_join_conf.1", "foo", "invite-join", DEV_CLASS_BACKBONE);
 
@@ -25,6 +45,8 @@ int main() {
                return 1;
        }
 
+       meshlink_set_log_cb(mesh1, MESHLINK_DEBUG, log_cb);
+
        meshlink_handle_t *mesh2 = meshlink_open("invite_join_conf.2", "bar", "invite-join", DEV_CLASS_BACKBONE);
 
        if(!mesh2) {
@@ -32,6 +54,8 @@ int main() {
                return 1;
        }
 
+       meshlink_set_log_cb(mesh2, MESHLINK_DEBUG, log_cb);
+
        meshlink_handle_t *mesh3 = meshlink_open("invite_join_conf.3", "quux", "invite-join", DEV_CLASS_BACKBONE);
 
        if(!mesh3) {
@@ -39,6 +63,8 @@ int main() {
                return 1;
        }
 
+       meshlink_set_log_cb(mesh3, MESHLINK_DEBUG, log_cb);
+
        // Disable local discovery.
 
        meshlink_enable_discovery(mesh1, false);
@@ -69,6 +95,9 @@ int main() {
                return 1;
        }
 
+       fprintf(stderr, "Invitation URL for baz:  %s\n", baz_url);
+       fprintf(stderr, "Invitation URL for quux: %s\n", quux_url);
+
        // Have the second instance join the first.
 
        if(!meshlink_join(mesh2, baz_url)) {
@@ -123,8 +152,7 @@ int main() {
 
        // Clean up.
 
-       meshlink_stop(mesh2);
-       meshlink_stop(mesh1);
+       meshlink_close(mesh3);
        meshlink_close(mesh2);
        meshlink_close(mesh1);