]> 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>
Mon, 17 Dec 2018 02:24:33 +0000 (03:24 +0100)
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.

34 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/blackbox/common/test_step.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 e3574ea5442888b3fa3edee6886040264f2378c5..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 occured 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 9b272dc88741879e0e291405be05db0694e1717b..32f97c70e1ea558f51909a224e2c3fe872fd3cbf 100644 (file)
@@ -82,8 +82,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 7994cf3edb295686a0b7955ea6c4faa8b7784323..a3e3eb7cee99fc8a38ab640389b616564e6468ec 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>
@@ -37,6 +26,8 @@ typedef struct {
 #include "meshlink_internal.h"
 #include "netutl.h"
 #include "node.h"
+#include "packmsg.h"
+#include "prf.h"
 #include "protocol.h"
 #include "route.h"
 #include "sockaddr.h"
@@ -53,38 +44,6 @@ __thread meshlink_errno_t meshlink_errno;
 meshlink_log_cb_t global_log_cb;
 meshlink_log_level_t global_log_level;
 
-//TODO: this can go away completely
-const var_t variables[] = {
-       /* Server configuration */
-       {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
-       {"Name", VAR_SERVER},
-       /* Host configuration */
-       {"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);
 
@@ -95,66 +54,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");
-
-       if(!f) {
-               return;
-       }
-
-       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);
-                       }
-               }
+       *hostname = xstrdup(n->canonical_address);
+       char *space = strchr(*hostname, ' ');
 
-               if(*hostname && *port) {
-                       break;
-               }
+       if(space) {
+               *space++ = 0;
+               *port = xstrdup(space);
        }
-
-       fclose(f);
 }
 
 static bool is_valid_hostname(const char *hostname) {
@@ -206,7 +117,7 @@ static void set_timeout(int sock, int timeout) {
 // 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) {
+static bool getlocaladdr(char *destaddr, struct sockaddr *sn, socklen_t *sl) {
        struct addrinfo *rai = NULL;
        const struct addrinfo hint = {
                .ai_family = AF_UNSPEC,
@@ -233,15 +144,22 @@ static bool getlocaladdrname(char *destaddr, char *host, socklen_t hostlen) {
 
        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) {
+       struct sockaddr_storage sn;
+       socklen_t sl = sizeof(sn);
+
+       if(!getlocaladdr(destaddr, (struct sockaddr *)&sn, &sl)) {
+               return false;
+       }
 
        if(getnameinfo((struct sockaddr *)&sn, sl, host, hostlen, NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV)) {
                return false;
@@ -406,9 +324,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) {
@@ -496,7 +412,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
@@ -512,64 +429,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 = {
@@ -614,18 +473,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;
                }
        }
@@ -635,33 +484,18 @@ int check_port(meshlink_handle_t *mesh) {
        return 0;
 }
 
-static void deltree(const char *dirname) {
-       DIR *d = opendir(dirname);
+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);
 
-       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);
-                       }
-               }
-
-               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);
+       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");
@@ -673,168 +507,95 @@ 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;
-
-               // Is it a Name?
-               if(!strcasecmp(l, "Name"))
-                       if(strcmp(value, name)) {
-                               break;
-                       } else {
-                               continue;
-                       } else if(!strcasecmp(l, "NetName")) {
-                       continue;
-               }
+       // 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_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);
 
-               // Check the list of known variables
-               bool found = false;
-               int i;
+       config.len = packmsg_output_size(&out, outbuf);
 
-               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;
-               }
-
-               // 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);
 
@@ -872,17 +633,9 @@ 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 succesfully accepted.\n");
                shutdown(mesh->sock, SHUT_RDWR);
                mesh->success = true;
@@ -984,61 +737,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;
 }
@@ -1065,75 +775,174 @@ 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))) {
-               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)) {
+               ((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))) {
-               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)) {
+               ((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;
        }
 
-       FILE *f = fopen(filename, "w");
+       // 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;
+       }
+
+       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;
        }
 
        return true;
 }
 
-meshlink_handle_t *meshlink_open(const char *confbase, const char *name, const char *appname, dev_class_t devclass) {
+
+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) {
        // Validate arguments provided by the application
        bool usingname = false;
 
@@ -1161,7 +970,6 @@ meshlink_handle_t *meshlink_open(const char *confbase, const char *name, const c
                logger(NULL, MESHLINK_ERROR, "No name given!\n");
                //return NULL;
        } else { //check name only if there is a name != NULL
-
                if(!check_id(name)) {
                        logger(NULL, MESHLINK_ERROR, "Invalid name given!\n");
                        meshlink_errno = MESHLINK_EINVAL;
@@ -1177,6 +985,12 @@ meshlink_handle_t *meshlink_open(const char *confbase, const char *name, const c
                return NULL;
        }
 
+       if((key && !keylen) || (!key && 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(confbase);
        mesh->appname = xstrdup(appname);
@@ -1188,6 +1002,17 @@ meshlink_handle_t *meshlink_open(const char *confbase, const char *name, const c
                mesh->name = xstrdup(name);
        }
 
+       // Hash the key
+       if(key) {
+               mesh->config_key = xmalloc(CHACHA_POLY1305_KEYLEN);
+
+               if(!prf(key, 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);
@@ -1200,69 +1025,25 @@ meshlink_handle_t *meshlink_open(const char *confbase, const char *name, const c
 
        meshlink_queue_init(&mesh->outpacketqueue);
 
-       // Check whether meshlink.conf already exists
-
-       char filename[PATH_MAX];
-       snprintf(filename, sizeof(filename), "%s" SLASH "meshlink.conf", 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
@@ -1275,6 +1056,7 @@ meshlink_handle_t *meshlink_open(const char *confbase, const char *name, const c
        }
 
        add_local_addresses(mesh);
+       node_write_config(mesh, mesh->self);
 
        idle_set(&mesh->loop, idle, mesh);
 
@@ -1282,13 +1064,15 @@ meshlink_handle_t *meshlink_open(const char *confbase, const char *name, const c
        return mesh;
 }
 
+meshlink_handle_t *meshlink_open(const char *confbase, const char *name, const char *appname, dev_class_t devclass) {
+       return meshlink_open_encrypted(confbase, name, appname, devclass, NULL, 0);
+}
+
 static void *meshlink_main_loop(void *arg) {
        meshlink_handle_t *mesh = 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");
@@ -1298,6 +1082,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;
@@ -1331,6 +1118,8 @@ bool meshlink_start(meshlink_handle_t *mesh) {
                return false;
        }
 
+       init_outgoings(mesh);
+
        // Start the main thread
 
        event_loop_start(&mesh->loop);
@@ -1354,6 +1143,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;
 }
@@ -1413,10 +1206,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));
 }
@@ -1439,7 +1229,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
@@ -1457,9 +1246,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));
 
@@ -1472,23 +1259,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) {
@@ -1634,7 +1405,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;
@@ -1717,7 +1488,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;
@@ -1744,9 +1515,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 {
@@ -1758,129 +1528,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;
-               }
-
-               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;
-               }
-       }
+       size_t count = invitation_purge_old(mesh, time(NULL) - mesh->invitation_timeout);
 
-       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;
 }
 
@@ -1911,11 +1568,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) {
@@ -1934,13 +1595,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;
 }
 
@@ -1983,19 +1640,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;
+       // TODO: write meshlink.conf again
 
-       modify_config_file(mesh, mesh->name, "Port", portstr, true);
-
-       init_configuration(&mesh->config);
-
-       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;
@@ -2028,10 +1676,7 @@ char *meshlink_invite_ex(meshlink_handle_t *mesh, const char *name, uint32_t fla
        }
 
        // 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));
@@ -2085,61 +1730,39 @@ char *meshlink_invite_ex(meshlink_handle_t *mesh, const char *name, uint32_t fla
 
        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_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);
-       fprintf(f, "ConnectTo = %s\n", mesh->self->name);
-
-       // Copy Broadcast and Mode
-       snprintf(filename, sizeof(filename), "%s" SLASH "meshlink.conf", mesh->confbase);
-       FILE *tc = fopen(filename,  "r");
+       /* Append host config files to the invitation file */
+       packmsg_add_array(&inv, count);
 
-       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);
+       for(int i = 0; i < count; i++) {
+               packmsg_add_bin(&inv, configs[i].buf, configs[i].len);
+               config_free(&configs[i]);
+       }
 
-                               // Make sure there is a newline character.
-                               if(!strchr(buf, '\n')) {
-                                       fputc('\n', f);
-                               }
-                       }
-               }
+       config_t config = {outbuf, packmsg_output_size(&inv, outbuf)};
 
-               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);
@@ -2398,111 +2021,143 @@ char *meshlink_export(meshlink_handle_t *mesh) {
                return NULL;
        }
 
-       pthread_mutex_lock(&(mesh->mesh_mutex));
+       config_t config;
 
-       char filename[PATH_MAX];
-       snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", mesh->confbase, mesh->self->name);
-       FILE *f = fopen(filename, "r");
+       // Get our config file
 
-       if(!f) {
-               logger(mesh, MESHLINK_DEBUG, "Could not open %s: %s\n", filename, strerror(errno));
+       pthread_mutex_lock(&(mesh->mesh_mutex));
+
+       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));
+
+       // Prepare a base64-encoded packmsg array containing our config file
 
-       size_t len = fsize + 9 + strlen(mesh->self->name);
-       char *buf = xmalloc(len);
-       snprintf(buf, len, "Name = %s\n", mesh->self->name);
+       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(fread(buf + len - fsize - 1, fsize, 1, f) != 1) {
-               logger(mesh, MESHLINK_DEBUG, "Error reading from %s: %s\n", filename, strerror(errno));
-               fclose(f);
+       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) {
        if(!mesh || !data) {
+               abort();
                meshlink_errno = MESHLINK_EINVAL;
                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) {
+               abort();
                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) {
+               abort();
                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);
 
-       fwrite(end + 1, strlen(end + 1), 1, f);
-       fclose(f);
+               if(n) {
+                       free(name);
+                       logger(mesh, MESHLINK_DEBUG, "Node %s already exists, not importing\n", name);
+                       continue;
+               }
 
-       load_all_nodes(mesh);
+               n = new_node();
+               n->name = 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) {
+                       abort();
+                       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)) {
+               abort();
+               logger(mesh, MESHLINK_ERROR, "Invalid data\n");
+               meshlink_errno = MESHLINK_EPEER;
+               return false;
+       }
+
        return true;
 }
 
@@ -2517,11 +2172,9 @@ void meshlink_blacklist(meshlink_handle_t *mesh, meshlink_node_t *node) {
        node_t *n;
        n = (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) {
@@ -2542,8 +2195,7 @@ void meshlink_whitelist(meshlink_handle_t *mesh, meshlink_node_t *node) {
 
        node_t *n = (node_t *)node;
        n->status.blacklisted = false;
-
-       //TODO: remove blacklisted = yes from the config file
+       n->status.dirty = true;
 
        pthread_mutex_unlock(&(mesh->mesh_mutex));
        return;
@@ -2558,32 +2210,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 74ebae5d71e4eb5771312f57bbe5a08e663a2130..b5062bea1ddeb7e504fb53d31892a6e45f087912 100644 (file)
@@ -158,6 +158,37 @@ extern const char *meshlink_strerror(meshlink_errno_t err);
  */
 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);
+
 /// Start MeshLink.
 /** This function causes MeshLink to open network sockets, make outgoing connections, and
  *  create a new thread, which will handle all network I/O.
index 92774c49baf3c9481d4aac62273256156c77d629..4410505300a928da3427981f653fe777386b3113 100644 (file)
@@ -37,6 +37,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;
@@ -63,7 +66,7 @@ struct meshlink_handle {
        void *priv;
 
        char *appname;
-       dev_class_t devclass;
+       int32_t devclass;
 
        char *confbase;
        FILE *conffile;
@@ -86,7 +89,6 @@ struct meshlink_handle {
 
        struct node_t *self;
 
-       struct splay_tree_t *config;
        struct splay_tree_t *edges;
        struct splay_tree_t *nodes;
 
@@ -121,6 +123,7 @@ struct meshlink_handle {
 
        hash_t *node_udp_cache;
        struct connection_t *everyone;
+       struct ecdsa *private_key;
        struct ecdsa *invitation_key;
        int invitation_timeout;
 
@@ -145,6 +148,8 @@ struct meshlink_handle {
        struct CattaSimplePoll *catta_poll;
        struct CattaSEntryGroup *catta_group;
        char *catta_servicetype;
+
+       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 10fb37b0005c5df5ae73b35bf33a66a9591796ac..0cb79bf25c8cacf3f07f7997bf0cf7789ec40849 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"
 
-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;
-       }
-
-       char *p;
-
-       if(!c->config_tree) {
-               init_configuration(&c->config_tree);
+/// Read just the devclass from a host config file. Used at startup when reading all host config files.
+bool node_read_devclass(meshlink_handle_t *mesh, node_t *n) {
+       config_t config;
+       packmsg_input_t in;
 
-               if(!read_host_config(mesh, c->config_tree, c->name)) {
-                       return false;
-               }
+       if(!node_get_config(mesh, n, &config, &in)) {
+               return false;
        }
 
-       /* First, check for simple ECDSAPublicKey statement */
+       int32_t devclass = packmsg_get_int32(&in);
+       bool blacklisted = packmsg_get_bool(&in);
+       config_free(&config);
 
-       if(get_config_string(lookup_config(c->config_tree, "ECDSAPublicKey"), &p)) {
-               c->ecdsa = ecdsa_set_base64_public_key(p);
-               free(p);
-               return c->ecdsa;
+       if(!packmsg_input_ok(&in) || devclass < 0 || devclass > _DEV_CLASS_MAX) {
+               return false;
        }
 
-       return false;
+       n->devclass = devclass;
+       n->status.blacklisted = blacklisted;
+       return true;
 }
 
-bool read_ecdsa_private_key(meshlink_handle_t *mesh) {
-       FILE *fp;
-       char filename[PATH_MAX];
+/// 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;
+       }
 
-       snprintf(filename, PATH_MAX, "%s" SLASH "ecdsa_key.priv", mesh->confbase);
-       fp = fopen(filename, "rb");
+       config_t config;
+       packmsg_input_t in;
 
-       if(!fp) {
-               logger(mesh, MESHLINK_ERROR, "Error reading ECDSA private key file: %s", strerror(errno));
+       if(!node_get_config(mesh, n, &config, &in)) {
                return false;
        }
 
-       mesh->self->connection->ecdsa = ecdsa_read_pem_private_key(fp);
-       fclose(fp);
+       packmsg_get_int32(&in); /* devclass */
+       packmsg_get_bool(&in); /* blacklisted */
+
+       const void *key;
+       uint32_t len = packmsg_get_bin_raw(&in, &key);
 
-       if(!mesh->self->connection->ecdsa) {
-               logger(mesh, MESHLINK_ERROR, "Reading ECDSA private key file failed: %s", strerror(errno));
+       if(len != 32) {
+               config_free(&config);
+               return false;
        }
 
-       return mesh->self->connection->ecdsa;
+       n->ecdsa = ecdsa_set_public_key(key);
+       config_free(&config);
+       return true;
 }
 
-static bool read_invitation_key(meshlink_handle_t *mesh) {
-       FILE *fp;
-       char filename[PATH_MAX];
-
-       if(mesh->invitation_key) {
-               ecdsa_free(mesh->invitation_key);
-               mesh->invitation_key = NULL;
+/// 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;
        }
 
-       snprintf(filename, PATH_MAX, "%s" SLASH "invitations" SLASH "ecdsa_key.priv", mesh->confbase);
+       config_t config;
+       packmsg_input_t in;
 
-       fp = fopen(filename, "rb");
+       if(!node_get_config(mesh, n, &config, &in)) {
+               return false;
+       }
 
-       if(fp) {
-               mesh->invitation_key = ecdsa_read_pem_private_key(fp);
-               fclose(fp);
+       packmsg_get_int32(&in); /* devclass */
+       packmsg_get_bool(&in); /* blacklisted */
+       const void *key;
+       uint32_t len = packmsg_get_bin_raw(&in, &key); /* public key */
 
-               if(!mesh->invitation_key) {
-                       logger(mesh, MESHLINK_ERROR, "Reading ECDSA private key file `%s' failed: %s", filename, strerror(errno));
-               }
+       if(len != 32) {
+               return false;
        }
 
-       return mesh->invitation_key;
-}
-
-bool node_read_devclass(meshlink_handle_t *mesh, node_t *n) {
+       if(!ecdsa_active(n->ecdsa)) {
+               n->ecdsa = ecdsa_set_public_key(key);
+       }
 
-       splay_tree_t *config_tree;
-       char *p;
+       n->canonical_address = packmsg_get_str_dup(&in);
 
-       init_configuration(&config_tree);
+       uint32_t count = packmsg_get_array(&in);
 
-       if(!read_host_config(mesh, config_tree, n->name)) {
-               goto exit;
+       if(count > 5) {
+               count = 5;
        }
 
-       if(get_config_string(lookup_config(config_tree, "DeviceClass"), &p)) {
-               n->devclass = atoi(p);
-               free(p);
+       for(uint32_t i = 0; i < count; i++) {
+               n->recent[i] = packmsg_get_sockaddr(&in);
        }
 
-       if((int)n->devclass < 0 || n->devclass > _DEV_CLASS_MAX) {
-               n->devclass = _DEV_CLASS_MAX;
-       }
+       config_free(&config);
 
-exit:
-       exit_configuration(&config_tree);
-       return n->devclass != 0;
+       return packmsg_done(&in);
 }
 
-bool node_write_devclass(meshlink_handle_t *mesh, node_t *n) {
-
-       if((int)n->devclass < 0 || n->devclass > _DEV_CLASS_MAX) {
-               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_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, "DeviceClass");
+       uint32_t count = 0;
 
-       if(!cnf) {
-               cnf = new_config();
-               cnf->variable = xstrdup("DeviceClass");
-               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_int(cnf, n->devclass);
+       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) {
@@ -224,36 +229,6 @@ void load_all_nodes(meshlink_handle_t *mesh) {
        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.
 */
@@ -348,67 +323,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);
-
-               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;
+       /* Set some defaults */
 
-       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 */
 
@@ -417,7 +337,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);
@@ -428,7 +347,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);
@@ -438,7 +357,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 {
@@ -491,15 +410,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 eaba79d0b01ae8bb959c773947d046688540cf81..3eef1987c9c19bf66b414cd1e4697ddc65d5d409 100644 (file)
@@ -38,7 +38,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 {
@@ -46,7 +47,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 */
 
@@ -68,7 +69,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;
@@ -86,16 +87,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 487f34c96ebef3f640ea2339540dc7607100fe62..c171c531c943e9bba3f18827102518c3b58af645 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,51 +144,34 @@ 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, "%d %s %d.%d %s", ID, mesh->self->connection->name, mesh->self->connection->protocol_major, minor, mesh->appname);
+       return send_request(mesh, c, "%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);
-       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->status.dirty = true;
+       node_add(mesh, n);
+       // TODO: immediately write the config file?
 
        logger(mesh, MESHLINK_INFO, "Key succesfully 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;
 }
@@ -219,86 +203,21 @@ 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;
+       config_t config;
 
-       if(fstat(fileno(f), &st)) {
-               logger(mesh, MESHLINK_ERROR, "Could not stat invitation file %s\n", usedname);
-               fclose(f);
-               unlink(usedname);
-               return false;
-       }
-
-       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);
 
        // 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;
 
@@ -372,37 +291,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);
                }
@@ -427,7 +341,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) {
@@ -453,7 +371,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;
@@ -498,7 +415,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);
 
@@ -530,9 +447,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 6788109847371e0afb7e6dd7ff754cf11661a0cb..717dbb9634ca04d2c81fbdd6d32757d62e901a2c 100644 (file)
@@ -33,8 +33,6 @@
 #include "utils.h"
 #include "xalloc.h"
 
-extern bool node_write_devclass(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;
@@ -100,8 +98,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;
                node_add(mesh, from);
        }
 
@@ -110,18 +110,16 @@ bool add_edge_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
        }
 
        from->devclass = from_devclass;
-       node_write_devclass(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;
                node_add(mesh, to);
        }
 
-       to->devclass = to_devclass;
-       node_write_devclass(mesh, to);
-
        /* Convert addresses */
 
        address = str2sockaddr(to_address, to_port);
index cae95047220341def5d777f2e01f58ed309d6c53..dc349b8fad8fde1104e6853e036058b827ef2c60 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, "%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,14 +107,14 @@ 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);
                send_request(mesh, from->nexthop->connection, "%d %s %s %d %s", REQ_KEY, mesh->self->name, from->name, ANS_PUBKEY, pubkey);
                free(pubkey);
                return true;
        }
 
        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;
                }
@@ -127,12 +127,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, "%d %s %s %d", REQ_KEY, mesh->self->name, from->name, REQ_PUBKEY);
                        return true;
@@ -161,7 +161,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 727881bd1080476d2c5d9303b76d315f702cc2a0..c6a6b024319385db4beb92019097abb6a535ff06 100644 (file)
@@ -91,9 +91,10 @@ bool pong_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
 
        /* Succesful 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;
index 4a2bf853896886c03d7f9fdeb13dbd91aa359eed..7681c82ee78527223c72550b0fe82c5e7bb5ca14 100644 (file)
@@ -39,6 +39,7 @@ meshlink_handle_t *execute_open(char *node_name, char *dev_class) {
        /* Create meshlink instance */
        mesh_handle = meshlink_open("testconf", node_name, "node_sim", atoi(dev_class));
        fprintf(stderr, "meshlink_open status: %s\n", meshlink_strerror(meshlink_errno));
+       meshlink_enable_discovery(mesh_handle, false);
        PRINT_TEST_CASE_MSG("meshlink_open status: %s\n", meshlink_strerror(meshlink_errno));
        assert(mesh_handle);
 
@@ -51,7 +52,7 @@ meshlink_handle_t *execute_open(char *node_name, char *dev_class) {
 }
 
 char *execute_invite(char *invitee) {
-       char *invite_url = meshlink_invite(mesh_handle, invitee);
+       char *invite_url = meshlink_invite_ex(mesh_handle, invitee, MESHLINK_INVITE_LOCAL | MESHLINK_INVITE_NUMERIC);
 
        PRINT_TEST_CASE_MSG("meshlink_invite status: %s\n", meshlink_strerror(meshlink_errno));
        assert(invite_url);
@@ -62,15 +63,7 @@ char *execute_invite(char *invitee) {
 void execute_join(char *invite_url) {
        bool join_status;
 
-       /* The inviting node may take a moment to open its listening port
-           This sleep() prevents meshlink_join() from failing when the listening port is not open */
-       /* TO DO: Replace this with code that actually checks for the port being open, if possible */
-       PRINT_TEST_CASE_MSG("Sleeping 1 sec to allow inviting node to start listening...\n");
-       sleep(1);
-
-       PRINT_TEST_CASE_MSG("About to join with mesh_handle = %p, invite_url = %s\n", mesh_handle, invite_url);
        join_status = meshlink_join(mesh_handle, invite_url);
-       PRINT_TEST_CASE_MSG("meshlink_join status: %s\n", meshlink_strerror(meshlink_errno));
        assert(join_status);
 }
 
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 03e169176ee2e52e771b1641daa6443ed6ef4788..2c14688d4980ab360afe1f02f109041702c8f232 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);