]> git.meshlink.io Git - meshlink/commitdiff
Merge branch 'channels'
authorGuus Sliepen <guus@meshlink.io>
Fri, 22 Aug 2014 08:58:47 +0000 (10:58 +0200)
committerGuus Sliepen <guus@meshlink.io>
Fri, 22 Aug 2014 09:00:15 +0000 (11:00 +0200)
Conflicts:
.gitmodules
src/node.h

57 files changed:
.gitmodules
README.avahi [new file with mode: 0644]
avahi [new submodule]
configure.ac
examples/Makefile.am
examples/chat.c
examples/chatpp.cc
examples/manynodes.c [new file with mode: 0644]
examples/meshlinkapp.c
src/Makefile.am
src/conf.c
src/conf.h
src/connection.c
src/connection.h
src/devtools.c [new file with mode: 0644]
src/devtools.h [new file with mode: 0644]
src/discovery.c [new file with mode: 0644]
src/discovery.h [new file with mode: 0644]
src/ed25519/.dirstamp [deleted file]
src/ed25519/ecdsa.c
src/ed25519/seed.c
src/edge.c
src/event.c
src/event.h
src/graph.c
src/logger.c
src/logger.h
src/meshlink++.h
src/meshlink.c
src/meshlink.h
src/meshlink_internal.h
src/meshlink_queue.h [new file with mode: 0644]
src/meta.c
src/net.c
src/net.h
src/net_packet.c
src/net_setup.c
src/net_socket.c
src/netutl.c
src/node.c
src/node.h
src/protocol.c
src/protocol_auth.c
src/protocol_edge.c
src/protocol_key.c
src/protocol_misc.c
src/route.c
src/sockaddr.h
src/sptps.c
src/sptps_speed.c
src/sptps_test.c
test/basic.c
test/basicpp.cpp
test/channels.c
test/import-export.c
test/invite-join.c
test/sign-verify.c

index add1976b253c2319bc2b7113934679b1b4fb05d4..e938b09181fe4913bb679de84d167cd8917664c7 100644 (file)
@@ -1,3 +1,7 @@
+[submodule "avahi"]
+       path = avahi
+       url = git@chicago.everbase.net:meshlink/avahi-noptr.git
+       branch = noptr
 [submodule "src/utcp"]
        path = src/utcp
        url = git://meshlink.io/utcp
diff --git a/README.avahi b/README.avahi
new file mode 100644 (file)
index 0000000..81d545f
--- /dev/null
@@ -0,0 +1,5 @@
+
+cd avahi
+./bootstrap.sh
+./configure --disable-nls --disable-glib --disable-gobject --disable-qt3 --disable-qt4 --disable-gtk --disable-gtk3 --disable-dbus --disable-gdbm --disable-libdaemon --disable-python --disable-pygtk --disable-python-dbus --disable-mono --disable-monodoc --disable-autoipd --disable-doxygen-doc --disable-doxygen-dot --disable-doxygen-xml --disable-doxygen-html --disable-manpages --disable-xmltoman CFLAGS=-fPIC
+make
diff --git a/avahi b/avahi
new file mode 160000 (submodule)
index 0000000..7a5b2f6
--- /dev/null
+++ b/avahi
@@ -0,0 +1 @@
+Subproject commit 7a5b2f69af7d36d6cd4153142f125fa011784e03
index 2f1caa68fedc14e811c3fa324190749085253ade..6c6543cba57f148426253c5c5c976f2dc35308ea 100644 (file)
@@ -99,6 +99,10 @@ AC_CHECK_TYPES([socklen_t, struct addrinfo, struct sockaddr_in6], , ,
   [#include "src/have.h"]
 )
 
+AC_CHECK_TYPES([struct sockaddr_storage], ,AC_MSG_ERROR([System must support struct sockaddr_storage.]),
+  [#include "src/have.h"]
+)
+
 dnl Checks for library functions.
 AC_TYPE_SIGNAL
 AC_CHECK_FUNCS([asprintf fchmod fork get_current_dir_name gettimeofday random select strdup strerror time usleep],
index af8f0f45d22cb04319e0f36b4a4f5212c8bf076f..9be95b13a3380f0790d0e3733b30357f1f4c7902 100644 (file)
@@ -1,4 +1,4 @@
-bin_PROGRAMS = meshlinkapp chat chatpp
+bin_PROGRAMS = meshlinkapp chat chatpp manynodes
 
 AM_CPPFLAGS = -I../src
 
@@ -10,3 +10,6 @@ chat_LDADD = ../src/libmeshlink.la
 
 chatpp_SOURCES = chatpp.cc
 chatpp_LDADD = ../src/libmeshlink.la
+
+manynodes_SOURCES = manynodes.cc
+manynodes_LDADD = ../src/libmeshlink.la
index 70d17d7d917bf6515aaf98d1fb97bea2f969df22..c6d67fb5c1ba2c111d4c814fdc1971699dc85ccc 100644 (file)
@@ -5,8 +5,14 @@
 #include "../src/meshlink.h"
 
 static void log_message(meshlink_handle_t *mesh, meshlink_log_level_t level, const char *text) {
-       const char *levelstr[] = {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"};
-       fprintf(stderr, "%s: %s\n", levelstr[level], text);
+       const char *levelstr[] = {
+               [MESHLINK_DEBUG] = "\x1b[34mDEBUG",
+               [MESHLINK_INFO] = "\x1b[32mINFO",
+               [MESHLINK_WARNING] = "\x1b[33mWARNING",
+               [MESHLINK_ERROR] = "\x1b[31mERROR",
+               [MESHLINK_CRITICAL] = "\x1b[31mCRITICAL",
+       };
+       fprintf(stderr, "%s:\x1b[0m %s\n", levelstr[level], text);
 }
 
 static void receive(meshlink_handle_t *mesh, meshlink_node_t *source, const void *data, size_t len) {
@@ -27,6 +33,9 @@ static void node_status(meshlink_handle_t *mesh, meshlink_node_t *node, bool rea
                printf("%s left.\n", node->name);
 }
 
+static meshlink_node_t **nodes;
+static size_t nnodes;
+
 static void parse_command(meshlink_handle_t *mesh, char *buf) {
        char *arg = strchr(buf, ' ');
        if(arg)
@@ -42,7 +51,7 @@ static void parse_command(meshlink_handle_t *mesh, char *buf) {
 
                invitation = meshlink_invite(mesh, arg);
                if(!invitation) {
-                       fprintf(stderr, "Could not invite '%s': %s\n", arg, mesh->errstr);
+                       fprintf(stderr, "Could not invite '%s': %s\n", arg, meshlink_strerror(meshlink_errno));
                        return;
                }
 
@@ -53,11 +62,16 @@ static void parse_command(meshlink_handle_t *mesh, char *buf) {
                        fprintf(stderr, "/join requires an argument!\n");
                        return;
                }
-
+               meshlink_stop(mesh);
                if(!meshlink_join(mesh, arg))
-                       fprintf(stderr, "Could not join using invitation: %s\n", mesh->errstr);
-               else
+                       fprintf(stderr, "Could not join using invitation: %s\n", meshlink_strerror(meshlink_errno));
+               else {
                        fprintf(stderr, "Invitation accepted!\n");
+                       if(!meshlink_start(mesh)) {
+                               fprintf(stderr, "Could not start MeshLink: %s\n", meshlink_strerror(meshlink_errno));
+                       return;
+                       }
+               }
        } else if(!strcasecmp(buf, "kick")) {
                if(!arg) {
                        fprintf(stderr, "/kick requires an argument!\n");
@@ -66,7 +80,7 @@ static void parse_command(meshlink_handle_t *mesh, char *buf) {
 
                meshlink_node_t *node = meshlink_get_node(mesh, arg);
                if(!node) {
-                       fprintf(stderr, "Unknown node '%s'\n", arg);
+                       fprintf(stderr, "Error looking up '%s': %s\n", arg, meshlink_strerror(meshlink_errno));
                        return;
                }
 
@@ -75,22 +89,19 @@ static void parse_command(meshlink_handle_t *mesh, char *buf) {
                printf("Node '%s' blacklisted.\n", arg);
        } else if(!strcasecmp(buf, "who")) {
                if(!arg) {
-                       meshlink_node_t *nodes[100];
-                       size_t n = meshlink_get_all_nodes(mesh, nodes, 100);
-                       if(!n) {
-                               fprintf(stderr, "No nodes known!\n");
+                       nodes = meshlink_get_all_nodes(mesh, nodes, &nnodes);
+                       if(!nnodes) {
+                               fprintf(stderr, "Could not get list of nodes: %s\n", meshlink_strerror(meshlink_errno));
                        } else {
-                               printf("Known nodes:");
-                               for(int i = 0; i < n && i < 100; i++)
+                               printf("%zu known nodes:", nnodes);
+                               for(int i = 0; i < nnodes; i++)
                                        printf(" %s", nodes[i]->name);
-                               if(n > 100)
-                                       printf(" (and %zu more)", n - 100);
                                printf("\n");
                        }
                } else {
                        meshlink_node_t *node = meshlink_get_node(mesh, arg);
                        if(!node) {
-                               fprintf(stderr, "Unknown node '%s'\n", arg);
+                               fprintf(stderr, "Error looking up '%s': %s\n", arg, meshlink_strerror(meshlink_errno));
                        } else {
                                printf("Node %s found\n", arg);
                        }
@@ -153,7 +164,7 @@ static void parse_input(meshlink_handle_t *mesh, char *buf) {
 
                destination = meshlink_get_node(mesh, buf);
                if(!destination) {
-                       fprintf(stderr, "Unknown node '%s'\n", buf);
+                       fprintf(stderr, "Error looking up '%s': %s\n", buf, meshlink_strerror(meshlink_errno));
                        return;
                }
        }
@@ -164,7 +175,7 @@ static void parse_input(meshlink_handle_t *mesh, char *buf) {
        }
 
        if(!meshlink_send(mesh, destination, msg, strlen(msg) + 1)) {
-               fprintf(stderr, "Could not send message to '%s': %s\n", destination->name, mesh->errstr);
+               fprintf(stderr, "Could not send message to '%s': %s\n", destination->name, meshlink_strerror(meshlink_errno));
                return;
        }
 
@@ -182,9 +193,11 @@ int main(int argc, char *argv[]) {
        if(argc > 2)
                nick = argv[2];
 
-       meshlink_handle_t *mesh = meshlink_open(confbase, nick);
+       meshlink_set_log_cb(NULL, MESHLINK_INFO, log_message);
+
+       meshlink_handle_t *mesh = meshlink_open(confbase, nick, "chat", DEV_CLASS_STATIONARY);
        if(!mesh) {
-               fprintf(stderr, "Could not open MeshLink!\n");
+               fprintf(stderr, "Could not open MeshLink: %s\n", meshlink_strerror(meshlink_errno));
                return 1;
        }
 
@@ -193,7 +206,7 @@ int main(int argc, char *argv[]) {
        meshlink_set_log_cb(mesh, MESHLINK_INFO, log_message);
 
        if(!meshlink_start(mesh)) {
-               fprintf(stderr, "Could not start MeshLink: %s\n", mesh->errstr);
+               fprintf(stderr, "Could not start MeshLink: %s\n", meshlink_strerror(meshlink_errno));
                return 1;
        }
 
index 864a3e809b7258d20f8dcc7dcf561d3bb4c1d4aa..331f6584543b35ae236bc794e961f30611866ffe 100644 (file)
@@ -4,28 +4,35 @@
 #include <strings.h>
 #include "../src/meshlink++.h"
 
-static void log_message(meshlink::mesh *mesh, meshlink::log_level_t level, const char *text) {
-       const char *levelstr[] = {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"};
-       fprintf(stderr, "%s: %s\n", levelstr[level], text);
-}
-
-static void receive(meshlink::mesh *mesh, meshlink::node *source, const void *data, size_t len) {
-       const char *msg = (const char *)data;
+class ChatMesh : public meshlink::mesh
+{
+public:
+       void log(meshlink::log_level_t level, const char *text) {
+               const char *levelstr[] = {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"};
+               fprintf(stderr, "%s: %s\n", levelstr[level], text);
+               }
 
-       if(!len || msg[len - 1]) {
-               fprintf(stderr, "Received invalid data from %s\n", source->name);
-               return;
+       void receive(meshlink::node *source, const void *data, size_t len) {
+               const char *msg = (const char *)data;
+       
+               if(!len || msg[len - 1]) {
+                       fprintf(stderr, "Received invalid data from %s\n", source->name);
+                       return;
+               }
+               
+               printf("%s says: %s\n", source->name, msg);
        }
 
-       printf("%s says: %s\n", source->name, msg);
-}
+       void node_status(meshlink::node *node, bool reachable) {
+               if(reachable)
+                       printf("%s joined.\n", node->name);
+               else
+                       printf("%s left.\n", node->name);
+       }
+};
 
-static void node_status(meshlink::mesh *mesh, meshlink::node *node, bool reachable) {
-       if(reachable)
-               printf("%s joined.\n", node->name);
-       else
-               printf("%s left.\n", node->name);
-}
+static meshlink::node **nodes;
+static size_t nnodes;
 
 static void parse_command(meshlink::mesh *mesh, char *buf) {
        char *arg = strchr(buf, ' ');
@@ -42,7 +49,7 @@ static void parse_command(meshlink::mesh *mesh, char *buf) {
 
                invitation = mesh->invite(arg);
                if(!invitation) {
-                       fprintf(stderr, "Could not invite '%s': %s\n", arg, mesh->errstr);
+                       fprintf(stderr, "Could not invite '%s': %s\n", arg, meshlink::strerror());
                        return;
                }
 
@@ -55,7 +62,7 @@ static void parse_command(meshlink::mesh *mesh, char *buf) {
                }
 
                if(!mesh->join(arg))
-                       fprintf(stderr, "Could not join using invitation: %s\n", mesh->errstr);
+                       fprintf(stderr, "Could not join using invitation: %s\n", meshlink::strerror());
                else
                        fprintf(stderr, "Invitation accepted!\n");
        } else if(!strcasecmp(buf, "kick")) {
@@ -66,7 +73,7 @@ static void parse_command(meshlink::mesh *mesh, char *buf) {
 
                meshlink::node *node = mesh->get_node(arg);
                if(!node) {
-                       fprintf(stderr, "Unknown node '%s'\n", arg);
+                       fprintf(stderr, "Error looking up '%s': %s\n", arg, meshlink::strerror());
                        return;
                }
 
@@ -75,22 +82,19 @@ static void parse_command(meshlink::mesh *mesh, char *buf) {
                printf("Node '%s' blacklisted.\n", arg);
        } else if(!strcasecmp(buf, "who")) {
                if(!arg) {
-                       meshlink::node *nodes[100];
-                       size_t n = mesh->get_all_nodes(nodes, 100);
-                       if(!n) {
-                               fprintf(stderr, "No nodes known!\n");
+                       nodes = mesh->get_all_nodes(nodes, &nnodes);
+                       if(!nodes) {
+                               fprintf(stderr, "Could not get list of nodes: %s\n", meshlink::strerror());
                        } else {
-                               printf("Known nodes:");
-                               for(int i = 0; i < n && i < 100; i++)
+                               printf("%zu known nodes:", nnodes);
+                               for(size_t i = 0; i < nnodes; i++)
                                        printf(" %s", nodes[i]->name);
-                               if(n > 100)
-                                       printf(" (and %zu more)", n - 100);
                                printf("\n");
                        }
                } else {
                        meshlink::node *node = mesh->get_node(arg);
                        if(!node) {
-                               fprintf(stderr, "Unknown node '%s'\n", arg);
+                               fprintf(stderr, "Error looking up '%s': %s\n", arg, meshlink::strerror());
                        } else {
                                printf("Node %s found\n", arg);
                        }
@@ -153,7 +157,7 @@ static void parse_input(meshlink::mesh *mesh, char *buf) {
 
                destination = mesh->get_node(buf);
                if(!destination) {
-                       fprintf(stderr, "Unknown node '%s'\n", buf);
+                       fprintf(stderr, "Error looking up '%s': %s\n", buf, meshlink::strerror());
                        return;
                }
        }
@@ -164,13 +168,14 @@ static void parse_input(meshlink::mesh *mesh, char *buf) {
        }
 
        if(!mesh->send(destination, msg, strlen(msg) + 1)) {
-               fprintf(stderr, "Could not send message to '%s': %s\n", destination->name, mesh->errstr);
+               fprintf(stderr, "Could not send message to '%s': %s\n", destination->name, meshlink::strerror());
                return;
        }
 
        printf("Message sent to '%s'.\n", destination->name);
 }
 
+
 int main(int argc, char *argv[]) {
        const char *confbase = ".chat";
        const char *nick = NULL;
@@ -182,18 +187,15 @@ int main(int argc, char *argv[]) {
        if(argc > 2)
                nick = argv[2];
 
-       meshlink::mesh *mesh = meshlink::open(confbase, nick);
+       ChatMesh* mesh = meshlink::open<ChatMesh>(confbase, nick, "chatpp", DEV_CLASS_STATIONARY);
+
        if(!mesh) {
-               fprintf(stderr, "Could not open MeshLink!\n");
+               fprintf(stderr, "Could not open MeshLink: %s\n", meshlink::strerror());
                return 1;
        }
 
-       mesh->set_receive_cb(receive);
-       mesh->set_node_status_cb(node_status);
-       mesh->set_log_cb(MESHLINK_INFO, log_message);
-
        if(!mesh->start()) {
-               fprintf(stderr, "Could not start MeshLink: %s\n", mesh->errstr);
+               fprintf(stderr, "Could not start MeshLink: %s\n", meshlink::strerror());
                return 1;
        }
 
diff --git a/examples/manynodes.c b/examples/manynodes.c
new file mode 100644 (file)
index 0000000..7b23fc5
--- /dev/null
@@ -0,0 +1,415 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <linux/limits.h>
+
+#include "../src/meshlink.h"
+#include "../src/devtools.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#include <sys/time.h>
+#include <signal.h>
+
+static int n = 10;
+static meshlink_handle_t **mesh;
+
+static int nodeindex = 0;
+
+static meshlink_node_t **nodes;
+static size_t nnodes;
+
+static void log_message(meshlink_handle_t *mesh, meshlink_log_level_t level, const char *text) {
+       const char *levelstr[] = {
+               [MESHLINK_DEBUG] = "\x1b[34mDEBUG",
+               [MESHLINK_INFO] = "\x1b[32mINFO",
+               [MESHLINK_WARNING] = "\x1b[33mWARNING",
+               [MESHLINK_ERROR] = "\x1b[31mERROR",
+               [MESHLINK_CRITICAL] = "\x1b[31mCRITICAL",
+       };
+       fprintf(stderr, "%s\t%s:\x1b[0m %s\n", mesh ? mesh->name : "global",levelstr[level], text);
+}
+
+//Test mesh sending data
+static void testmesh () {
+
+       for(int nindex = 0; nindex < n; nindex++) {
+
+                       nodes = meshlink_get_all_nodes(mesh[nindex], nodes, &nnodes);
+                       if(!nodes) {
+                               fprintf(stderr, "Could not get list of nodes: %s\n", meshlink_strerror(meshlink_errno));
+                       } else {
+                               printf("%zu known nodes:\n", nnodes);
+                               for(int i = 0; i < nnodes; i++) {
+                                       //printf(" %s\n", nodes[i]->name);
+                                               if(!meshlink_send(mesh[nindex], nodes[i], "magic", strlen("magic") + 1)) {
+               fprintf(stderr, "Could not send message to '%s': %s\n", nodes[i]->name, meshlink_strerror(meshlink_errno));
+                                               }
+                               }
+
+                       }
+
+       }
+}
+// Make all nodes know about each other by importing each others public keys and addresses.
+static void linkmesh() {
+       for(int i = 0; i < n; i++) {
+               char *datai = meshlink_export(mesh[i]);
+
+               for(int j = i + 1; j < n; j++) {
+                       char *dataj = meshlink_export(mesh[j]);
+                       meshlink_import(mesh[i], dataj);
+                       meshlink_import(mesh[j], datai);
+                       free(dataj);
+               }
+
+               free(datai);
+       }
+}
+
+static bool exportmeshgraph(const char* path)
+{
+       struct stat ps;
+       int psr = stat(path, &ps);
+
+       if(psr == 0 || errno != ENOENT)
+       {
+               if(psr == -1)
+                       { perror("stat"); }
+               else
+                       { fprintf(stderr, "%s exists already\n", path); }
+
+               return false;
+       }
+
+       FILE* stream = fopen(path, "w");
+
+       if(!stream)
+       {
+               perror("stream");
+               return false;
+       }
+
+       if(!devtool_export_json_all_edges_state(mesh[nodeindex], stream))
+       {
+               fclose(stream);
+               fprintf(stderr, "could not export graph\n");
+               return false;
+       }
+
+       fclose(stream);
+       return true;
+}
+
+
+void exportmeshgraph_timer(int signum)
+{
+       struct timeval ts;
+       gettimeofday(&ts, NULL);
+
+       char name[1024];
+       snprintf(name, sizeof(name), "graph_%ld_%03ld.json", ts.tv_sec, ts.tv_usec/1000);
+
+       exportmeshgraph(name);
+}
+
+static bool exportmeshgraph_started = false;
+
+static bool exportmeshgraph_end(const char* none)
+{
+       if(!exportmeshgraph_started)
+               { return false; }
+
+       struct itimerval zero_timer = { 0 };
+       setitimer (ITIMER_REAL, &zero_timer, NULL);
+
+       exportmeshgraph_started = false;
+
+       return true;
+}
+
+static bool exportmeshgraph_begin(const char* timeout_str)
+{
+       if(!timeout_str)
+               return false;
+
+       if(exportmeshgraph_started)
+       {
+               if(!exportmeshgraph_end(NULL))
+                       return false;
+       }
+
+       // get timeout
+       int timeout = atoi(timeout_str);
+
+       if(timeout < 100)
+               { timeout = 100; }
+
+       int timeout_sec = timeout / 1000;
+       int timeout_msec = timeout % 1000;
+
+       /* Install timer_handler as the signal handler for SIGALRM. */
+       signal(SIGALRM, exportmeshgraph_timer);
+
+       /* Configure the timer to expire immediately... */
+       struct itimerval timer;
+       timer.it_value.tv_sec = 0;
+       timer.it_value.tv_usec = 1000;
+
+       /* ... and every X msec after that. */
+       timer.it_interval.tv_sec = timeout_sec;
+       timer.it_interval.tv_usec = timeout_msec * 1000;
+
+       /* Start a real timer. */
+       setitimer (ITIMER_REAL, &timer, NULL);
+
+       exportmeshgraph_started = true;
+
+       return true;
+}
+
+static void parse_command(char *buf) {
+       char *arg = strchr(buf, ' ');
+       if(arg)
+               *arg++ = 0;
+
+       if(!strcasecmp(buf, "invite")) {
+               char *invitation;
+
+               if(!arg) {
+                       fprintf(stderr, "/invite requires an argument!\n");
+                       return;
+               }
+
+               invitation = meshlink_invite(mesh[nodeindex], arg);
+               if(!invitation) {
+                       fprintf(stderr, "Could not invite '%s': %s\n", arg, meshlink_strerror(meshlink_errno));
+                       return;
+               }
+
+               printf("Invitation for %s: %s\n", arg, invitation);
+               free(invitation);
+       } else if(!strcasecmp(buf, "join")) {
+               if(!arg) {
+                       fprintf(stderr, "/join requires an argument!\n");
+                       return;
+               }
+               meshlink_stop(mesh[nodeindex]);
+               if(!meshlink_join(mesh[nodeindex], arg))
+                       fprintf(stderr, "Could not join using invitation: %s\n", meshlink_strerror(meshlink_errno));
+               else {
+                       fprintf(stderr, "Invitation accepted!\n");
+                       meshlink_start(mesh[nodeindex]);
+               }
+       } else if(!strcasecmp(buf, "kick")) {
+               if(!arg) {
+                       fprintf(stderr, "/kick requires an argument!\n");
+                       return;
+               }
+
+               meshlink_node_t *node = meshlink_get_node(mesh[nodeindex], arg);
+               if(!node) {
+                       fprintf(stderr, "Unknown node '%s'\n", arg);
+                       return;
+               }
+
+               meshlink_blacklist(mesh[nodeindex], node);
+
+               printf("Node '%s' blacklisted.\n", arg);
+       } else if(!strcasecmp(buf, "who")) {
+               if(!arg) {
+                       nodes = meshlink_get_all_nodes(mesh[nodeindex], nodes, &nnodes);
+                       if(!nodes) {
+                               fprintf(stderr, "Could not get list of nodes: %s\n", meshlink_strerror(meshlink_errno));
+                       } else {
+                               printf("%zu known nodes:", nnodes);
+                               for(int i = 0; i < nnodes; i++)
+                                       printf(" %s", nodes[i]->name);
+                               printf("\n");
+                       }
+               } else {
+                       meshlink_node_t *node = meshlink_get_node(mesh[nodeindex], arg);
+                       if(!node) {
+                               fprintf(stderr, "Unknown node '%s'\n", arg);
+                       } else {
+                               printf("Node %s found, pmtu %zd\n", arg, meshlink_get_pmtu(mesh[nodeindex], node));
+                       }
+               }
+       } else if(!strcasecmp(buf, "link")) {
+               linkmesh();
+       } else if(!strcasecmp(buf, "eg")) {
+               exportmeshgraph(arg);
+       } else if(!strcasecmp(buf, "egb")) {
+               exportmeshgraph_begin(arg);
+       } else if(!strcasecmp(buf, "ege")) {
+               exportmeshgraph_end(NULL);
+       } else if(!strcasecmp(buf, "test")) {
+               testmesh();
+       } else if(!strcasecmp(buf, "select")) {
+               if(!arg) {
+                       fprintf(stderr, "/select requires an argument!\n");
+                       return;
+               }
+               nodeindex = atoi(arg);
+               printf("Index is now %d\n",nodeindex);
+       
+       } else if(!strcasecmp(buf, "quit")) {
+               printf("Bye!\n");
+               fclose(stdin);
+       } else if(!strcasecmp(buf, "help")) {
+               printf(
+                       "<name>: <message>     Send a message to the given node.\n"
+                       "                      Subsequent messages don't need the <name>: prefix.\n"
+                       "/invite <name>        Create an invitation for a new node.\n"
+                       "/join <invitation>    Join an existing mesh using an invitation.\n"
+                       "/kick <name>          Blacklist the given node.\n"
+                       "/who [<name>]         List all nodes or show information about the given node.\n"
+                       "/link                 Link all nodes together.\n"
+                       "/eg <path>            Export graph as json file.\n"
+                       "/test                 Test functionality sending some data to all nodes\n"
+                       "/select <number>      Select the active node running the user commands\n"
+                       "/quit                 Exit this program.\n"
+                       );
+       } else {
+               fprintf(stderr, "Unknown command '/%s'\n", buf);
+       }
+}
+
+static void parse_input(char *buf) {
+       static meshlink_node_t *destination;
+       size_t len;
+
+       if(!buf)
+               return;
+
+       // Remove newline.
+
+       len = strlen(buf);
+
+       if(len && buf[len - 1] == '\n')
+               buf[--len] = 0;
+
+       if(len && buf[len - 1] == '\r')
+               buf[--len] = 0;
+
+       // Ignore empty lines.
+
+       if(!len)
+               return;
+
+       // Commands start with '/'
+
+       if(*buf == '/')
+               return parse_command(buf + 1);
+
+       // Lines in the form "name: message..." set the destination node.
+
+       char *msg = buf;
+       char *colon = strchr(buf, ':');
+
+       if(colon) {
+               *colon = 0;
+               msg = colon + 1;
+               if(*msg == ' ')
+                       msg++;
+
+               destination = meshlink_get_node(mesh[nodeindex], buf);
+               if(!destination) {
+                       fprintf(stderr, "Unknown node '%s'\n", buf);
+                       return;
+               }
+       }
+
+       if(!destination) {
+               fprintf(stderr, "Who are you talking to? Write 'name: message...'\n");
+               return;
+       }
+
+       if(!meshlink_send(mesh[nodeindex], destination, msg, strlen(msg) + 1)) {
+               fprintf(stderr, "Could not send message to '%s': %s\n", destination->name, meshlink_strerror(meshlink_errno));
+               return;
+       }
+
+       printf("Message sent to '%s'.\n", destination->name);
+}
+
+int main(int argc, char *argv[]) {
+       const char *basebase = ".manynodes";
+       const char *namesprefix = "machine1";
+       const char *graphexporttimeout = NULL;
+       char buf[1024];
+
+       if(argc > 1)
+               n = atoi(argv[1]);
+
+       if(n < 1) {
+               fprintf(stderr, "Usage: %s [number of local nodes] [confbase] [prefixnodenames] [graphexport timeout]\n", argv[0]);
+               return 1;
+       }
+
+       if(argc > 2)
+               basebase = argv[2];
+
+       if(argc > 3)
+               namesprefix = argv[3];
+
+       if(argc > 4)
+               graphexporttimeout = argv[4];
+
+       mesh = calloc(n, sizeof *mesh);
+
+       meshlink_set_log_cb(NULL, MESHLINK_WARNING, log_message);
+       mkdir(basebase, 0750);
+
+       char filename[PATH_MAX];
+       char nodename[100];
+       for(int i = 0; i < n; i++) {
+               snprintf(nodename, sizeof nodename, "%snode%d", namesprefix,i);
+               snprintf(filename, sizeof filename, "%s/%s", basebase, nodename);
+               bool itsnew = access(filename, R_OK);
+               mesh[i] = meshlink_open(filename, nodename, "manynodes", i%_DEV_CLASS_MAX);
+               meshlink_set_log_cb(mesh[i], MESHLINK_WARNING, log_message);
+               if(!mesh[i]) {
+                       fprintf(stderr, "errno is: %d\n", meshlink_errno);
+                       fprintf(stderr, "Could not open %s: %s\n", filename, meshlink_strerror(meshlink_errno));
+                       return 1;
+               }
+       }
+
+       int started = 0;
+
+       for(int i = 0; i < n; i++) {
+               if(!meshlink_start(mesh[i]))
+                       fprintf(stderr, "Could not start node %d: %s\n", i, meshlink_strerror(meshlink_errno));
+               else
+                       started++;
+       }
+
+       if(!started) {
+               fprintf(stderr, "Could not start any node!\n");
+               return 1;
+       }
+
+       if(graphexporttimeout)
+               { exportmeshgraph_begin(graphexporttimeout); }
+
+       printf("%d nodes started.\nType /help for a list of commands.\n", started);
+
+       // handle input
+       while(fgets(buf, sizeof buf, stdin))
+               parse_input(buf);
+
+       exportmeshgraph_end(NULL);
+
+       printf("Nodes stopping.\n");
+
+       for(int i = 0; i < n; i++)
+               meshlink_close(mesh[i]);
+
+       return 0;
+}
index f506c964162a52537bed63301f7988440fbd6127..d9e5b7e0ba4d6dfcb52632ff1f0a16852a1c71fe 100644 (file)
@@ -14,7 +14,7 @@ int main(int argc , char **argv){
 
        meshlink_handle_t* myhandle;
 
-       myhandle = meshlink_open(confbase, name);
+       myhandle = meshlink_open(confbase, name, "meshlinkapp", DEV_CLASS_STATIONARY);
 
        //Register callback function for incoming data
        meshlink_set_receive_cb(myhandle, (meshlink_receive_cb_t)handle_recv_data);
index c1d887e9759d2e0687bcda210aafd92c01a66bcd..a2a9305ac54e32f219be1439581d728b5fb66f13 100644 (file)
@@ -59,7 +59,7 @@ sptps_speed_SOURCES = \
 
 lib_LTLIBRARIES = libmeshlink.la
 
-libmeshlink_la_LDFLAGS = -export-symbols-regex '^meshlink_'
+libmeshlink_la_LDFLAGS = -export-symbols-regex '^(meshlink_|devtool_)'
 
 libmeshlink_la_SOURCES = \
        meshlink.c meshlink.h \
@@ -68,6 +68,7 @@ libmeshlink_la_SOURCES = \
        conf.c conf.h \
        connection.c connection.h \
        crypto.c crypto.h \
+       discovery.c discovery.h \
        dropin.c dropin.h \
        ecdh.h \
        ecdsa.h \
@@ -101,13 +102,14 @@ libmeshlink_la_SOURCES = \
        system.h \
        utils.c utils.h \
        xalloc.h \
+       devtools.c devtools.h \
        $(ed25519_SOURCES) \
        $(chacha_poly1305_SOURCES) \
        $(utcp_SOURCES)
 
-libmeshlink_la_CFLAGS = -fPIC
+libmeshlink_la_CFLAGS = -fPIC -I../avahi/
 
-libmeshlink_la_LIBADD = -lpthread
+libmeshlink_la_LIBADD = -lpthread -luuid ../avahi/avahi-core/.libs/libavahi-core.a ../avahi/avahi-common/.libs/libavahi-common.a
 
 libmeshlink_la_SOURCES += \
        ed25519/ecdh.c \
index aa22c47ff9a67807e2721ba7d7671a3bcbfe7a0e..ad4b28b89708e0db5378ac5ec8ef460295c04dce 100644 (file)
@@ -51,7 +51,8 @@ void init_configuration(splay_tree_t **config_tree) {
 }
 
 void exit_configuration(splay_tree_t **config_tree) {
-       splay_delete_tree(*config_tree);
+       if(*config_tree)
+               splay_delete_tree(*config_tree);
        *config_tree = NULL;
 }
 
@@ -124,7 +125,7 @@ bool get_config_bool(const config_t *cfg, bool *result) {
                return true;
        }
 
-       logger(DEBUG_ALWAYS, LOG_ERR, "\"yes\" or \"no\" expected for configuration variable %s in %s line %d",
+       logger(NULL, MESHLINK_ERROR, "\"yes\" or \"no\" expected for configuration variable %s in %s line %d",
                   cfg->variable, cfg->file, cfg->line);
 
        return false;
@@ -137,12 +138,28 @@ bool get_config_int(const config_t *cfg, int *result) {
        if(sscanf(cfg->value, "%d", result) == 1)
                return true;
 
-       logger(DEBUG_ALWAYS, LOG_ERR, "Integer expected for configuration variable %s in %s line %d",
+       logger(NULL, MESHLINK_ERROR, "Integer expected for configuration variable %s in %s line %d",
                   cfg->variable, cfg->file, cfg->line);
 
        return false;
 }
 
+bool set_config_int(config_t *cfg, int val)
+{
+       if(!cfg)
+               return false;
+
+       char val_str[1024];
+       snprintf(val_str, sizeof(val_str), "%d", val);
+
+       if(cfg->value)
+               free(cfg->value);
+
+       cfg->value = xstrdup(val_str);
+
+       return true;
+}
+
 bool get_config_string(const config_t *cfg, char **result) {
        if(!cfg)
                return false;
@@ -152,6 +169,19 @@ bool get_config_string(const config_t *cfg, char **result) {
        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);
+
+       return true;
+}
+
 bool get_config_address(const config_t *cfg, struct addrinfo **result) {
        struct addrinfo *ai;
 
@@ -165,7 +195,7 @@ bool get_config_address(const config_t *cfg, struct addrinfo **result) {
                return true;
        }
 
-       logger(DEBUG_ALWAYS, LOG_ERR, "Hostname or IP address expected for configuration variable %s in %s line %d",
+       logger(NULL, MESHLINK_ERROR, "Hostname or IP address expected for configuration variable %s in %s line %d",
                   cfg->variable, cfg->file, cfg->line);
 
        return false;
@@ -220,7 +250,7 @@ config_t *parse_config_line(char *line, const char *fname, int lineno) {
 
        if(!*value) {
                const char err[] = "No value for variable";
-               logger(DEBUG_ALWAYS, LOG_ERR, "%s `%s' on line %d while reading config file %s",
+               logger(NULL, MESHLINK_ERROR, "%s `%s' on line %d while reading config file %s",
                        err, variable, lineno, fname);
                return NULL;
        }
@@ -250,7 +280,7 @@ bool read_config_file(splay_tree_t *config_tree, const char *fname) {
        fp = fopen(fname, "r");
 
        if(!fp) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Cannot open config file %s: %s", fname, strerror(errno));
+               logger(NULL, MESHLINK_ERROR, "Cannot open config file %s: %s", fname, strerror(errno));
                return false;
        }
 
@@ -290,6 +320,45 @@ bool read_config_file(splay_tree_t *config_tree, const char *fname) {
        return result;
 }
 
+bool write_config_file(const struct splay_tree_t *config_tree, const char *fname)
+{
+       FILE *fp;
+
+       fp = fopen(fname, "w+");
+
+       if(!fp) {
+               logger(NULL, MESHLINK_ERROR, "Cannot open config file %s: %s", fname, 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)) {
+                       logger(NULL, MESHLINK_ERROR, "Cannot write to config file %s: %s", fname, strerror(errno));
+                       return false;
+               }
+
+               if(fwrite(" = ", sizeof(char), 3, fp) < 3) {
+                       logger(NULL, MESHLINK_ERROR, "Cannot write to config file %s: %s", fname, strerror(errno));
+                       return false;
+               }
+
+               if(fwrite(cnf->value, sizeof(char), strlen(cnf->value), fp) < strlen(cnf->value)) {
+                       logger(NULL, MESHLINK_ERROR, "Cannot write to config file %s: %s", fname, strerror(errno));
+                       return false;
+               }
+
+               if(fwrite("\n", sizeof(char), 1, fp) < 1) {
+                       logger(NULL, MESHLINK_ERROR, "Cannot write to config file %s: %s", fname, strerror(errno));
+                       return false;
+               }
+       }
+
+       fclose(fp);
+
+       return true;
+}
+
 bool read_server_config(meshlink_handle_t *mesh) {
        char filename[PATH_MAX];
        bool x;
@@ -299,7 +368,7 @@ bool read_server_config(meshlink_handle_t *mesh) {
        x = read_config_file(mesh->config, filename);
 
        if(!x && errno)
-               logger(DEBUG_ALWAYS, LOG_ERR, "Failed to read `%s': %s", filename, strerror(errno));
+               logger(mesh, MESHLINK_ERROR, "Failed to read `%s': %s", filename, strerror(errno));
 
        return x;
 }
@@ -314,6 +383,14 @@ bool read_host_config(meshlink_handle_t *mesh, splay_tree_t *config_tree, const
        return x;
 }
 
+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);
+}
+
 bool append_config_file(meshlink_handle_t *mesh, const char *name, const char *key, const char *value) {
        char filename[PATH_MAX];
        snprintf(filename,PATH_MAX, "%s" SLASH "hosts" SLASH "%s", mesh->confbase, name);
@@ -321,8 +398,9 @@ bool append_config_file(meshlink_handle_t *mesh, const char *name, const char *k
        FILE *fp = fopen(filename, "a");
 
        if(!fp) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Cannot open config file %s: %s", filename, strerror(errno));
+               logger(mesh, MESHLINK_ERROR, "Cannot open config file %s: %s", filename, strerror(errno));
        } else {
+               fprintf(fp, "%s = %s\n", key, value);
                fclose(fp);
        }
 
index 7c0335d78b2d6e7fa98fed28ad05d5bec5254fc8..c3cf6f7d642fee59748e4acf92518e95067d32f4 100644 (file)
@@ -40,14 +40,18 @@ 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 config_t *parse_config_line(char *, const char *, int);
 extern bool read_config_file(struct splay_tree_t *, const char *);
+extern bool write_config_file(const struct splay_tree_t *, const char *);
 
 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 append_config_file(struct meshlink_handle *mesh, const char *, const char *, const char *);
 
 #endif /* __MESHLINK_CONF_H__ */
index 05a9d653dc3bf377128b5eb73bd4017dfba2c2c5..cbe7a9d610277e0f9c1ef57910ced33f7b9a956c 100644 (file)
@@ -36,8 +36,13 @@ void init_connections(meshlink_handle_t *mesh) {
 }
 
 void exit_connections(meshlink_handle_t *mesh) {
-       list_delete_list(mesh->connections);
+       if(mesh->connections)
+               list_delete_list(mesh->connections);
+
        free_connection(mesh->everyone);
+
+       mesh->connections = NULL;
+       mesh->everyone = NULL;
 }
 
 connection_t *new_connection(void) {
index 5c11217593906d8dc3f1d93a00d56ad6b357777e..c3c219be4d0ae0bce1463453f0ca11c88e3da1a8 100644 (file)
@@ -64,8 +64,6 @@ typedef struct connection_t {
        int socket;                     /* socket used for this connection */
        uint32_t options;               /* options for this connection */
        connection_status_t status;     /* status info */
-       int estimated_weight;           /* estimation for the weight of the edge for this connection */
-       struct timeval start;           /* time this connection was started, used for above estimation */
        struct outgoing_t *outgoing;    /* used to keep track of outgoing connections */
 
        struct meshlink_handle *mesh;   /* the mesh this connection belongs to */
diff --git a/src/devtools.c b/src/devtools.c
new file mode 100644 (file)
index 0000000..d287f3c
--- /dev/null
@@ -0,0 +1,160 @@
+
+#include "system.h"
+
+#include "logger.h"
+#include "meshlink_internal.h"
+#include "node.h"
+#include "splay_tree.h"
+#include "netutl.h"
+#include "xalloc.h"
+
+#include "devtools.h"
+
+static int node_compare(const void *a, const void *b)
+{
+       if(a < b)
+               return -1;
+
+       if(a > b)
+               return 1;
+
+       return 0;
+}
+
+static bool fstrwrite(const char* str, FILE* stream)
+{
+       size_t len = strlen(str);
+
+       if(fwrite((void*)str, 1, len, stream) != len)
+               return false;
+
+       return true;
+}
+
+static const char* itoa(int value)
+{
+       static char buffer[sizeof(int) * 8 + 1];        // not thread safe
+
+       if(snprintf(buffer, sizeof(buffer), "%d", value) == -1)
+               return "";
+
+       return buffer;
+}
+
+bool devtool_export_json_all_edges_state(meshlink_handle_t *mesh, FILE* stream)
+{
+       bool result = true;
+
+       pthread_mutex_lock(&(mesh->mesh_mutex));
+
+       // export edges and nodes
+       size_t node_count = 0;
+       size_t edge_count = 0;
+
+       meshlink_node_t **nodes = meshlink_get_all_nodes(mesh, NULL, &node_count);
+       meshlink_edge_t **edges = meshlink_get_all_edges_state(mesh, NULL, &edge_count);
+
+       if((!nodes && node_count != 0) || (!edges && edge_count != 0))
+               { goto fail; }
+
+       // export begin
+       if(!fstrwrite("{\n", stream))
+               goto fail;
+
+       // export nodes
+       if(!fstrwrite("\t\"nodes\": {\n", stream))
+               goto fail;
+
+       for(size_t i = 0; i < node_count; ++i)
+       {
+               if(!fstrwrite("\t\t\"", stream) || !fstrwrite(((node_t*)nodes[i])->name, stream) || !fstrwrite("\": {\n", stream))
+                       goto fail;
+
+               if(!fstrwrite("\t\t\t\"name\": \"", stream) || !fstrwrite(((node_t*)nodes[i])->name, stream) || !fstrwrite("\",\n", stream))
+                       goto fail;
+
+               if(!fstrwrite("\t\t\t\"options\": ", stream) || !fstrwrite(itoa(((node_t*)nodes[i])->options), stream) || !fstrwrite(",\n", stream))
+                       goto fail;
+
+               if(!fstrwrite("\t\t\t\"devclass\": ", stream) || !fstrwrite(itoa(((node_t*)nodes[i])->devclass), stream) || !fstrwrite("\n", stream))
+                       goto fail;
+
+               if(!fstrwrite((i+1) != node_count ? "\t\t},\n" : "\t\t}\n", stream))
+                       goto fail;
+       }
+
+       if(!fstrwrite("\t},\n", stream))
+               goto fail;
+
+       // export edges
+
+       if(!fstrwrite("\t\"edges\": {\n", stream))
+               goto fail;
+
+       for(size_t i = 0; i < edge_count; ++i)
+       {
+               if(!fstrwrite("\t\t\"", stream) || !fstrwrite(edges[i]->from->name, stream) || !fstrwrite("_to_", stream) || !fstrwrite(edges[i]->to->name, stream) || !fstrwrite("\": {\n", stream))
+                       goto fail;
+
+               if(!fstrwrite("\t\t\t\"from\": \"", stream) || !fstrwrite(edges[i]->from->name, stream) || !fstrwrite("\",\n", stream))
+                       goto fail;
+
+               if(!fstrwrite("\t\t\t\"to\": \"", stream) || !fstrwrite(edges[i]->to->name, stream) || !fstrwrite("\",\n", stream))
+                       goto fail;
+
+               char *host = NULL, *port = NULL, *address = NULL;
+               sockaddr2str((const sockaddr_t *)&(edges[i]->address), &host, &port);
+
+               if(host && port) {
+                       xasprintf(&address, "{ \"host\": \"%s\", \"port\": %s }", host, port);
+               }
+
+               free(host);
+               free(port);
+
+               if(!fstrwrite("\t\t\t\"address\": ", stream) || !fstrwrite(address ? address : "null", stream) || !fstrwrite(",\n", stream))
+               {
+                       free(address);
+                       goto fail;
+               }
+
+               free(address);
+
+               if(!fstrwrite("\t\t\t\"options\": ", stream) || !fstrwrite(itoa(edges[i]->options), stream) || !fstrwrite(",\n", stream))
+                       goto fail;
+
+               if(!fstrwrite("\t\t\t\"weight\": ", stream) || !fstrwrite(itoa(edges[i]->weight), stream) || !fstrwrite("\n", stream))
+                       goto fail;
+
+               if(!fstrwrite((i+1) != edge_count ? "\t\t},\n" : "\t\t}\n", stream))
+                       goto fail;
+       }
+
+       if(!fstrwrite("\t}\n", stream))
+               goto fail;
+
+       // DONE!
+
+       if(!fstrwrite("}", stream))
+               goto fail;
+
+       goto done;
+
+fail:
+       result = false;
+
+done:
+
+       if(nodes)
+               { free(nodes); }
+
+       for(size_t i = 0; edges && i < edge_count; ++i)
+               { free(edges[i]); }
+
+       if(nodes)
+               { free(edges); }
+
+       pthread_mutex_unlock(&(mesh->mesh_mutex));
+
+       return result;
+}
diff --git a/src/devtools.h b/src/devtools.h
new file mode 100644 (file)
index 0000000..9ac58d5
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __MESHLINK_DEVTOOLS_H__
+#define __MESHLINK_DEVTOOLS_H__
+
+extern bool devtool_export_json_all_edges_state(meshlink_handle_t *mesh, FILE* stream);
+
+#endif
diff --git a/src/discovery.c b/src/discovery.c
new file mode 100644 (file)
index 0000000..fa4e13c
--- /dev/null
@@ -0,0 +1,613 @@
+
+#include "meshlink_internal.h"
+#include "discovery.h"
+#include "sockaddr.h"
+#include "logger.h"
+
+#include <pthread.h>
+
+#include <avahi-core/core.h>
+#include <avahi-core/lookup.h>
+#include <avahi-core/publish.h>
+#include <avahi-core/log.h>
+#include <avahi-common/simple-watch.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/alternative.h>
+#include <avahi-common/error.h>
+
+#include <netinet/in.h>
+
+#include <uuid/uuid.h>
+
+#define MESHLINK_MDNS_SERVICE_TYPE "_%s._tcp"
+#define MESHLINK_MDNS_NAME_KEY "name"
+#define MESHLINK_MDNS_FINGERPRINT_KEY "fingerprint"
+
+static void discovery_entry_group_callback(AvahiServer *server, AvahiSEntryGroup *group, AvahiEntryGroupState state, void *userdata)
+{
+    meshlink_handle_t *mesh = userdata;
+
+    // asserts
+    assert(mesh != NULL);
+    assert(mesh->avahi_server != NULL);
+    assert(mesh->avahi_poll != NULL);
+
+    pthread_mutex_lock(&(mesh->mesh_mutex));
+
+    /* Called whenever the entry group state changes */
+    switch(state)
+    {
+        case AVAHI_ENTRY_GROUP_ESTABLISHED:
+            /* The entry group has been established successfully */
+            logger(mesh, MESHLINK_DEBUG, "Avahi Service successfully established.\n");
+            break;
+
+        case AVAHI_ENTRY_GROUP_COLLISION:
+            logger(mesh, MESHLINK_WARNING, "Avahi Service collision.\n");
+            // @TODO can we just set a new name and retry?
+            break;
+
+        case AVAHI_ENTRY_GROUP_FAILURE :
+            /* Some kind of failure happened while we were registering our services */
+            logger(mesh, MESHLINK_ERROR, "Avahi Entry group failure: %s\n", avahi_strerror(avahi_server_errno(mesh->avahi_server)));
+            avahi_simple_poll_quit(mesh->avahi_poll);
+            break;
+
+        case AVAHI_ENTRY_GROUP_UNCOMMITED:
+        case AVAHI_ENTRY_GROUP_REGISTERING:
+            ;
+    }
+
+    pthread_mutex_unlock(&(mesh->mesh_mutex));
+}
+
+
+static void discovery_create_services(meshlink_handle_t *mesh)
+{
+    char *txt_name = NULL;
+
+    // asserts
+    assert(mesh != NULL);
+    assert(mesh->name != NULL);
+    assert(mesh->myport != NULL);
+    assert(mesh->avahi_server != NULL);
+    assert(mesh->avahi_poll != NULL);
+    assert(mesh->avahi_servicetype != NULL);
+    assert(mesh->self != NULL);
+
+    pthread_mutex_lock(&(mesh->mesh_mutex));
+
+    logger(mesh, MESHLINK_DEBUG, "Adding service\n");
+
+    /* Ifthis is the first time we're called, let's create a new entry group */
+    if(!mesh->avahi_group)
+    {
+        if(!(mesh->avahi_group = avahi_s_entry_group_new(mesh->avahi_server, discovery_entry_group_callback, mesh)))
+        {
+            logger(mesh, MESHLINK_ERROR, "avahi_entry_group_new() failed: %s\n", avahi_strerror(avahi_server_errno(mesh->avahi_server)));
+            goto fail;
+        }
+    }
+
+    /* Create txt records */
+    size_t txt_name_len = sizeof(MESHLINK_MDNS_NAME_KEY) + 1 + strlen(mesh->name) + 1;
+    txt_name = malloc(txt_name_len);
+
+    if(txt_name == NULL)
+    {
+        logger(mesh, MESHLINK_ERROR, "Could not allocate memory for TXT record\n");
+        goto fail;
+    }
+
+    snprintf(txt_name, txt_name_len, "%s=%s", MESHLINK_MDNS_NAME_KEY, mesh->name);
+
+    char txt_fingerprint[sizeof(MESHLINK_MDNS_FINGERPRINT_KEY) + 1 + MESHLINK_FINGERPRINTLEN + 1];
+    snprintf(txt_fingerprint, sizeof(txt_fingerprint), "%s=%s", MESHLINK_MDNS_FINGERPRINT_KEY, meshlink_get_fingerprint(mesh, (meshlink_node_t *)mesh->self));
+
+    /* Add the service */
+    int ret = 0;
+    if((ret = avahi_server_add_service(mesh->avahi_server, mesh->avahi_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, meshlink_get_fingerprint(mesh, (meshlink_node_t *)mesh->self), mesh->avahi_servicetype, NULL, NULL, atoi(mesh->myport), txt_name, txt_fingerprint, NULL)) < 0)
+    {
+        logger(mesh, MESHLINK_ERROR, "Failed to add service: %s\n", avahi_strerror(ret));
+        goto fail;
+    }
+
+    /* Tell the server to register the service */
+    if((ret = avahi_s_entry_group_commit(mesh->avahi_group)) < 0)
+    {
+        logger(mesh, MESHLINK_ERROR, "Failed to commit entry_group: %s\n", avahi_strerror(ret));
+        goto fail;
+    }
+
+    goto done;
+
+fail:
+    avahi_simple_poll_quit(mesh->avahi_poll);
+
+done:
+    if(txt_name)
+        { free(txt_name); }
+
+    pthread_mutex_unlock(&(mesh->mesh_mutex));
+}
+
+static void discovery_server_callback(AvahiServer *server, AvahiServerState state, void * userdata)
+{
+       meshlink_handle_t *mesh = userdata;
+
+    // asserts
+    assert(mesh != NULL);
+    
+    pthread_mutex_lock(&(mesh->mesh_mutex));
+
+    switch(state)
+    {
+        case AVAHI_SERVER_RUNNING:
+            {
+                /* The serve has startup successfully and registered its host
+                 * name on the network, so it's time to create our services */
+                if(!mesh->avahi_group)
+                {
+                    discovery_create_services(mesh);
+                }
+            }
+            break;
+
+        case AVAHI_SERVER_COLLISION:
+            {
+                // asserts
+                assert(mesh->avahi_server != NULL);
+                assert(mesh->avahi_poll != NULL);
+
+                /* A host name collision happened. Let's pick a new name for the server */
+                uuid_t hostname;
+                uuid_generate(hostname);
+
+                char hostnamestr[36+1];
+                uuid_unparse_lower(hostname, hostnamestr);
+
+                logger(mesh, MESHLINK_WARNING, "Avahi host name collision, retrying with '%s'\n", hostnamestr);
+                int result = avahi_server_set_host_name(mesh->avahi_server, hostnamestr);
+
+                if(result < 0)
+                {
+                    logger(mesh, MESHLINK_ERROR, "Avahi failed to set new host name: %s\n", avahi_strerror(result));
+                    avahi_simple_poll_quit(mesh->avahi_poll);
+                }
+            }
+            break;
+
+        case AVAHI_SERVER_REGISTERING:
+            {
+                /* Let's drop our registered services. When the server is back
+                 * in AVAHI_SERVER_RUNNING state we will register them
+                 * again with the new host name. */
+                if(mesh->avahi_group)
+                {
+                    avahi_s_entry_group_reset(mesh->avahi_group);
+                    mesh->avahi_group = NULL;
+                }
+            }
+            break;
+
+        case AVAHI_SERVER_FAILURE:
+            {
+                // asserts
+                assert(mesh->avahi_server != NULL);
+                assert(mesh->avahi_poll != NULL);
+
+                /* Terminate on failure */
+                logger(mesh, MESHLINK_ERROR, "Avahi server failure: %s\n", avahi_strerror(avahi_server_errno(mesh->avahi_server)));
+                avahi_simple_poll_quit(mesh->avahi_poll);
+            }
+            break;
+
+        case AVAHI_SERVER_INVALID:
+            break;
+    }
+
+    pthread_mutex_unlock(&(mesh->mesh_mutex));
+}
+
+static void discovery_resolve_callback(AvahiSServiceResolver *resolver, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void* userdata)
+{
+    meshlink_handle_t *mesh = userdata;
+
+    // asserts
+    assert(resolver != NULL);
+    assert(mesh != NULL);
+    assert(mesh->avahi_server != NULL);
+
+    pthread_mutex_lock(&(mesh->mesh_mutex));
+
+    /* Called whenever a service has been resolved successfully or timed out */
+    switch(event)
+    {
+        case AVAHI_RESOLVER_FAILURE:
+            {
+                // asserts
+                assert(name != NULL);
+                assert(type != NULL);
+                assert(domain != NULL);
+
+                logger(mesh, MESHLINK_WARNING, "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", name, type, domain, avahi_strerror(avahi_server_errno(mesh->avahi_server)));
+            }
+            break;
+
+        case AVAHI_RESOLVER_FOUND:
+            {
+                // asserts
+                assert(name != NULL);
+                assert(type != NULL);
+                assert(domain != NULL);
+                assert(host_name != NULL);
+                assert(address != NULL);
+                assert(txt != NULL);
+        
+                char straddr[AVAHI_ADDRESS_STR_MAX], *strtxt;
+
+                logger(mesh, MESHLINK_DEBUG, "(Resolver) Service '%s' of type '%s' in domain '%s':\n", name, type, domain);
+
+                avahi_address_snprint(straddr, sizeof(straddr), address);
+                strtxt = avahi_string_list_to_string(txt);
+                logger(mesh, MESHLINK_DEBUG,
+                        "\t%s:%u (%s)\n"
+                        "\tTXT=%s\n"
+                        "\tcookie is %u\n"
+                        "\tis_local: %i\n"
+                        "\twide_area: %i\n"
+                        "\tmulticast: %i\n"
+                        "\tcached: %i\n",
+                        host_name, port, straddr,
+                        strtxt,
+                        avahi_string_list_get_service_cookie(txt),
+                        !!(flags & AVAHI_LOOKUP_RESULT_LOCAL),
+                        !!(flags & AVAHI_LOOKUP_RESULT_WIDE_AREA),
+                        !!(flags & AVAHI_LOOKUP_RESULT_MULTICAST),
+                        !!(flags & AVAHI_LOOKUP_RESULT_CACHED));
+                avahi_free(strtxt);
+
+                // retrieve fingerprint
+                AvahiStringList *node_name_li = avahi_string_list_find(txt, MESHLINK_MDNS_NAME_KEY);
+                AvahiStringList *node_fp_li = avahi_string_list_find(txt, MESHLINK_MDNS_FINGERPRINT_KEY);
+
+                if(node_name_li != NULL && node_fp_li != NULL)
+                {
+                    char *node_name = (char*)avahi_string_list_get_text(node_name_li) + strlen(MESHLINK_MDNS_NAME_KEY);
+                    char *node_fp = (char*)avahi_string_list_get_text(node_fp_li) + strlen(MESHLINK_MDNS_FINGERPRINT_KEY);
+
+                    if(node_name[0] == '=' && node_fp[0] == '=')
+                    {
+                        node_name += 1;
+                        node_fp += 1;
+
+                        meshlink_node_t *node = meshlink_get_node(mesh, node_name);
+
+                        if(node != NULL)
+                        {
+                            logger(mesh, MESHLINK_INFO, "Node %s is part of the mesh network.\n", node->name);
+
+                            sockaddr_t naddress;
+                            memset(&naddress, 0, sizeof(naddress));
+
+                            switch(address->proto)
+                            {
+                                case AVAHI_PROTO_INET:
+                                    {
+                                        naddress.in.sin_family = AF_INET;
+                                        naddress.in.sin_port = htons(port);
+                                        naddress.in.sin_addr.s_addr = address->data.ipv4.address;
+                                    }
+                                    break;
+
+                                case AVAHI_PROTO_INET6:
+                                    {
+                                        naddress.in6.sin6_family = AF_INET6;
+                                        naddress.in6.sin6_port = htons(port);
+                                        memcpy(naddress.in6.sin6_addr.s6_addr, address->data.ipv6.address, sizeof(naddress.in6.sin6_addr.s6_addr));
+                                    }
+                                    break;
+
+                                default:
+                                    naddress.unknown.family = AF_UNKNOWN;
+                                    break;
+                            }
+
+                            if(naddress.unknown.family != AF_UNKNOWN)
+                            {
+                                meshlink_hint_address(mesh, (meshlink_node_t *)node, (struct sockaddr*)&naddress);
+                            }
+                            else
+                            {
+                                logger(mesh, MESHLINK_WARNING, "Could not resolve node %s to a known address family type.\n", node->name);
+                            }
+                        }
+                        else
+                        {
+                            logger(mesh, MESHLINK_WARNING, "Node %s is not part of the mesh network.\n", node_name);
+                        }
+                    }
+                    else
+                    {
+                        logger(mesh, MESHLINK_WARNING, "TXT records invalid.\n");
+                    }
+                }
+                else
+                {
+                    logger(mesh, MESHLINK_WARNING, "TXT records missing.\n");
+                }
+            }
+            break;
+    }
+
+    avahi_s_service_resolver_free(resolver);
+
+    pthread_mutex_unlock(&(mesh->mesh_mutex));
+}
+
+static void discovery_browse_callback(AvahiSServiceBrowser *browser, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AvahiLookupResultFlags flags, void* userdata)
+{
+       meshlink_handle_t *mesh = userdata;
+
+    // asserts
+    assert(mesh != NULL);
+    assert(mesh->avahi_server != NULL);
+    assert(mesh->avahi_poll != NULL);
+
+    pthread_mutex_lock(&(mesh->mesh_mutex));
+
+    /* Called whenever a new services becomes available on the LAN or is removed from the LAN */
+    switch (event)
+    {
+        case AVAHI_BROWSER_FAILURE:
+            {
+                logger(mesh, MESHLINK_ERROR, "(Browser) %s\n", avahi_strerror(avahi_server_errno(mesh->avahi_server)));
+                avahi_simple_poll_quit(mesh->avahi_poll);
+            }
+            break;
+
+        case AVAHI_BROWSER_NEW:
+            {
+                // asserts
+                assert(name != NULL);
+                assert(type != NULL);
+                assert(domain != NULL);
+
+                logger(mesh, MESHLINK_DEBUG, "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
+                /* We ignore the returned resolver object. In the callback
+                   function we free it. Ifthe server is terminated before
+                   the callback function is called the server will free
+                   the resolver for us. */
+                if(!(avahi_s_service_resolver_new(mesh->avahi_server, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, discovery_resolve_callback, mesh)))
+                {
+                    logger(mesh, MESHLINK_DEBUG, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_server_errno(mesh->avahi_server)));
+                }
+            }
+            break;
+
+        case AVAHI_BROWSER_REMOVE:
+            {
+                // asserts
+                assert(name != NULL);
+                assert(type != NULL);
+                assert(domain != NULL);
+
+                logger(mesh, MESHLINK_DEBUG, "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
+            }
+            break;
+
+        case AVAHI_BROWSER_ALL_FOR_NOW:
+        case AVAHI_BROWSER_CACHE_EXHAUSTED:
+            {
+                logger(mesh, MESHLINK_DEBUG, "(Browser) %s\n", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
+            }
+            break;
+    }
+    
+    pthread_mutex_unlock(&(mesh->mesh_mutex));
+}
+
+static void *discovery_loop(void *userdata)
+{
+       meshlink_handle_t *mesh = userdata;
+
+    // asserts
+    assert(mesh != NULL);
+    assert(mesh->avahi_poll != NULL);
+
+    avahi_simple_poll_loop(mesh->avahi_poll);
+       
+    return NULL;
+}
+
+static void discovery_log_cb(AvahiLogLevel level, const char *txt)
+{
+    meshlink_log_level_t mlevel = MESHLINK_CRITICAL;
+
+    switch(level)
+    {
+    case AVAHI_LOG_ERROR:
+        mlevel = MESHLINK_ERROR;
+        break;
+
+    case AVAHI_LOG_WARN:
+        mlevel = MESHLINK_WARNING;
+        break;
+
+    case AVAHI_LOG_NOTICE:
+    case AVAHI_LOG_INFO:
+        mlevel = MESHLINK_INFO;
+        break;
+
+    case AVAHI_LOG_DEBUG:
+        mlevel = MESHLINK_DEBUG;
+        break;
+    }
+
+    logger(NULL, mlevel, "%s\n", txt);
+}
+
+bool discovery_start(meshlink_handle_t *mesh)
+{
+    logger(mesh, MESHLINK_DEBUG, "discovery_start called\n");
+
+    // asserts
+    assert(mesh != NULL);
+    assert(mesh->avahi_poll == NULL);
+    assert(mesh->avahi_server == NULL);
+    assert(mesh->avahi_browser == NULL);
+    assert(mesh->discovery_threadstarted == false);
+    assert(mesh->avahi_servicetype == NULL);
+
+    // handle avahi logs
+    avahi_set_log_function(discovery_log_cb);
+    
+    // create service type string
+    size_t servicetype_strlen = sizeof(MESHLINK_MDNS_SERVICE_TYPE) + strlen(mesh->appname) + 1;
+    mesh->avahi_servicetype = malloc(servicetype_strlen);
+
+    if(mesh->avahi_servicetype == NULL)
+    {
+        logger(mesh, MESHLINK_ERROR, "Failed to allocate memory for service type string.\n");
+        goto fail;
+    }
+
+    snprintf(mesh->avahi_servicetype, servicetype_strlen, MESHLINK_MDNS_SERVICE_TYPE, mesh->appname);
+
+    // Allocate discovery loop object
+    if(!(mesh->avahi_poll = avahi_simple_poll_new()))
+    {
+        logger(mesh, MESHLINK_ERROR, "Failed to create discovery poll object.\n");
+               goto fail;
+    }
+
+    // generate some unique host name (we actually do not care about it)
+    uuid_t hostname;
+    uuid_generate(hostname);
+
+    char hostnamestr[36+1];
+    uuid_unparse_lower(hostname, hostnamestr);
+
+    // Let's set the host name for this server.
+    AvahiServerConfig config;
+    avahi_server_config_init(&config);
+    config.host_name = avahi_strdup(hostnamestr);
+    config.publish_workstation = 0;
+    config.disallow_other_stacks = 0;
+    config.publish_hinfo = 0;
+    config.publish_addresses = 1;
+    config.publish_no_reverse = 1;
+
+    /* Allocate a new server */
+    int error;
+    mesh->avahi_server = avahi_server_new(avahi_simple_poll_get(mesh->avahi_poll), &config, discovery_server_callback, mesh, &error);
+
+    /* Free the configuration data */
+    avahi_server_config_free(&config);
+
+    /* Check wether creating the server object succeeded */
+    if(!mesh->avahi_server)
+    {
+        logger(mesh, MESHLINK_ERROR, "Failed to create discovery server: %s\n", avahi_strerror(error));
+        goto fail;
+    }
+
+    // Create the service browser
+    if(!(mesh->avahi_browser = avahi_s_service_browser_new(mesh->avahi_server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, mesh->avahi_servicetype, NULL, 0, discovery_browse_callback, mesh)))
+    {
+        logger(mesh, MESHLINK_ERROR, "Failed to create discovery service browser: %s\n", avahi_strerror(avahi_server_errno(mesh->avahi_server)));
+        goto fail;
+    }
+
+       // Start the discovery thread
+       if(pthread_create(&mesh->discovery_thread, NULL, discovery_loop, mesh) != 0)
+    {
+               logger(mesh, MESHLINK_ERROR, "Could not start discovery thread: %s\n", strerror(errno));
+               memset(&mesh->discovery_thread, 0, sizeof mesh->discovery_thread);
+               goto fail;
+       }
+
+       mesh->discovery_threadstarted = true;
+
+       return true;
+
+fail:
+    if(mesh->avahi_browser != NULL)
+    {
+        avahi_s_service_browser_free(mesh->avahi_browser);
+        mesh->avahi_browser = NULL;
+    }
+
+    if(mesh->avahi_server != NULL)
+    {
+        avahi_server_free(mesh->avahi_server);
+        mesh->avahi_server = NULL;
+    }
+
+    if(mesh->avahi_poll != NULL)
+    {
+        avahi_simple_poll_free(mesh->avahi_poll);
+        mesh->avahi_poll = NULL;
+    }
+
+    if(mesh->avahi_servicetype != NULL)
+    {
+        free(mesh->avahi_servicetype);
+        mesh->avahi_servicetype = NULL;
+    }
+
+    return false;
+}
+
+void discovery_stop(meshlink_handle_t *mesh)
+{
+    logger(mesh, MESHLINK_DEBUG, "discovery_stop called\n");
+
+    // asserts
+    assert(mesh != NULL);
+
+       // Shut down
+    if(mesh->avahi_poll)
+    {
+        avahi_simple_poll_quit(mesh->avahi_poll);
+    }
+
+       // Wait for the discovery thread to finish
+    if(mesh->discovery_threadstarted == true)
+    {
+        pthread_join(mesh->discovery_thread, NULL);
+        mesh->discovery_threadstarted = false;
+    }
+
+       // Clean up resources
+    if(mesh->avahi_browser != NULL)
+    {
+        avahi_s_service_browser_free(mesh->avahi_browser);
+        mesh->avahi_browser = NULL;
+    }
+
+    if(mesh->avahi_group)
+    {
+        avahi_s_entry_group_reset(mesh->avahi_group);
+        avahi_s_entry_group_free(mesh->avahi_group);
+        mesh->avahi_group = NULL;
+    }
+
+    if(mesh->avahi_server != NULL)
+    {
+        avahi_server_free(mesh->avahi_server);
+        mesh->avahi_server = NULL;
+    }
+
+    if(mesh->avahi_poll != NULL)
+    {
+        avahi_simple_poll_free(mesh->avahi_poll);
+        mesh->avahi_poll = NULL;
+    }
+
+    if(mesh->avahi_servicetype != NULL)
+    {
+        free(mesh->avahi_servicetype);
+        mesh->avahi_servicetype = NULL;
+    }
+}
diff --git a/src/discovery.h b/src/discovery.h
new file mode 100644 (file)
index 0000000..0455d6e
--- /dev/null
@@ -0,0 +1,10 @@
+
+#ifndef __MESHLINK_DISCOVERY_H__
+#define __MESHLINK_DISCOVERY_H__
+
+#include <stdbool.h>
+
+extern bool discovery_start(meshlink_handle_t *mesh);
+extern void discovery_stop(meshlink_handle_t *mesh);
+
+#endif
diff --git a/src/ed25519/.dirstamp b/src/ed25519/.dirstamp
deleted file mode 100644 (file)
index e69de29..0000000
index cabec3a2d7e26a392d61d5501c44a4783d358f1a..a63dedab0597c16c59500fdc31a357af7c431b84 100644 (file)
@@ -38,14 +38,14 @@ ecdsa_t *ecdsa_set_base64_public_key(const char *p) {
        int len = strlen(p);
 
        if(len != 43) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Invalid size %d for public key!", len);
+               logger(NULL, MESHLINK_ERROR, "Invalid size %d for public key!", len);
                return 0;
        }
 
        ecdsa_t *ecdsa = xzalloc(sizeof *ecdsa);
        len = b64decode(p, ecdsa->public, len);
        if(len != 32) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Invalid format of public key! len = %d", len);
+               logger(NULL, MESHLINK_ERROR, "Invalid format of public key! len = %d", len);
                free(ecdsa);
                return 0;
        }
index 11a2e3ec460e22aa682b501fb171fbd3e9052891..dda3ccb2368af6afd77b1f3e5dc52fca47f9d812 100644 (file)
@@ -30,7 +30,9 @@ int ed25519_create_seed(unsigned char *seed) {
         return 1;
     }
 
-    fread(seed, 1, 32, f);
+    if(fread(seed, 32, 1, f) != 1)
+           return 1;
+
     fclose(f);
 #endif
 
index 455b2301e95333fc78d5ec68adfd8cf1c4c055fb..efb90dfeaaffd49b3f9262b8354384bafb1cd21a 100644 (file)
@@ -61,7 +61,9 @@ void free_edge_tree(splay_tree_t *edge_tree) {
 }
 
 void exit_edges(meshlink_handle_t *mesh) {
-       splay_delete_tree(mesh->edges);
+       if(mesh->edges)
+               splay_delete_tree(mesh->edges);
+       mesh->edges = NULL;
 }
 
 /* Creation and deletion of connection elements */
index d1a0e2568ff1e9a928b89d510b996482f1ae55cb..62daea6f13cf3a6138116e67773c1c60502bd22a 100644 (file)
@@ -81,6 +81,8 @@ void io_del(event_loop_t *loop, io_t *io) {
        if(!io->cb)
                return;
 
+       loop->deletion = true;
+
        io_set(loop, io, 0);
 
        splay_unlink_node(&loop->ios, &io->node);
@@ -115,6 +117,8 @@ void timeout_del(event_loop_t *loop, timeout_t *timeout) {
        if(!timeout->cb)
                return;
 
+       loop->deletion = true;
+
        splay_unlink_node(&loop->timeouts, &timeout->node);
        timeout->cb = 0;
        timeout->tv = (struct timeval){0, 0};
@@ -167,11 +171,13 @@ void signal_del(event_loop_t *loop, signal_t *sig) {
        if(!sig->cb)
                return;
 
+       loop->deletion = true;
+
        splay_unlink_node(&loop->signals, &sig->node);
        sig->cb = NULL;
 }
 
-bool event_loop_run(event_loop_t *loop) {
+bool event_loop_run(event_loop_t *loop, pthread_mutex_t *mutex) {
        loop->running = true;
 
        fd_set readable;
@@ -205,7 +211,12 @@ bool event_loop_run(event_loop_t *loop) {
                        fds = last->fd + 1;
                }
 
+               // release mesh mutex during select
+               if(mutex)
+                       pthread_mutex_unlock(mutex);
                int n = select(fds, &readable, &writable, NULL, tv);
+               if(mutex)
+                       pthread_mutex_lock(mutex);
 
                if(n < 0) {
                        if(sockwouldblock(errno))
@@ -217,11 +228,21 @@ bool event_loop_run(event_loop_t *loop) {
                if(!n)
                        continue;
 
+               // Normally, splay_each allows the current node to be deleted. However,
+               // it can be that one io callback triggers the deletion of another io,
+               // so we have to detect this and break the loop.
+
+               loop->deletion = false;
+
                for splay_each(io_t, io, &loop->ios) {
-                       if(FD_ISSET(io->fd, &writable))
+                       if(FD_ISSET(io->fd, &writable) && io->cb)
                                io->cb(loop, io->data, IO_WRITE);
-                       else if(FD_ISSET(io->fd, &readable))
+                       if(loop->deletion)
+                               break;
+                       if(FD_ISSET(io->fd, &readable) && io->cb)
                                io->cb(loop, io->data, IO_READ);
+                       if(loop->deletion)
+                               break;
                }
        }
 
index c90e50679a171971bde932e51ed7f866d9ceae9e..4eb182518b350ba57cb7c036e0340f7c70e4ba6b 100644 (file)
@@ -60,6 +60,7 @@ struct event_loop_t {
 
        volatile bool running;
        struct timeval now;
+       bool deletion;
        
        splay_tree_t ios;
        splay_tree_t timeouts;
@@ -85,7 +86,7 @@ extern void signal_del(event_loop_t *loop, signal_t *sig);
 
 extern void event_loop_init(event_loop_t *loop);
 extern void event_loop_exit(event_loop_t *loop);
-extern bool event_loop_run(event_loop_t *loop);
+extern bool event_loop_run(event_loop_t *loop, pthread_mutex_t *mutex);
 extern void event_loop_flush_output(event_loop_t *loop);
 extern void event_loop_stop(event_loop_t *loop);
 
index 34af237e0b228ed8547b6ee6de460bd854614cee..2453d6f1c21f52414be0f0d849f54b5130300354 100644 (file)
@@ -67,7 +67,7 @@ static void mst_kruskal(meshlink_handle_t *mesh) {
        for list_each(connection_t, c, mesh->connections)
                c->status.mst = false;
 
-       logger(DEBUG_SCARY_THINGS, LOG_DEBUG, "Running Kruskal's algorithm:");
+       logger(mesh, MESHLINK_DEBUG, "Running Kruskal's algorithm:");
 
        /* Clear visited status on nodes */
 
@@ -102,7 +102,7 @@ static void mst_kruskal(meshlink_handle_t *mesh) {
                if(e->reverse->connection)
                        e->reverse->connection->status.mst = true;
 
-               logger(DEBUG_SCARY_THINGS, LOG_DEBUG, " Adding edge %s - %s weight %d", e->from->name, e->to->name, e->weight);
+               logger(mesh, MESHLINK_DEBUG, " Adding edge %s - %s weight %d", e->from->name, e->to->name, e->weight);
 
                if(skipped) {
                        skipped = false;
@@ -139,7 +139,7 @@ static void sssp_bfs(meshlink_handle_t *mesh) {
        /* Loop while todo_list is filled */
 
        for list_each(node_t, n, todo_list) {                   /* "n" is the node from which we start */
-               logger(DEBUG_SCARY_THINGS, LOG_DEBUG, " Examining edges from %s", n->name);
+               logger(mesh, MESHLINK_DEBUG, " Examining edges from %s", n->name);
 
                if(n->distance < 0)
                        abort();
@@ -202,10 +202,10 @@ static void check_reachability(meshlink_handle_t *mesh) {
                        n->last_state_change = mesh->loop.now.tv_sec;
 
                        if(n->status.reachable) {
-                               logger(DEBUG_TRAFFIC, LOG_DEBUG, "Node %s (%s) became reachable",
+                               logger(mesh, MESHLINK_DEBUG, "Node %s (%s) became reachable",
                                           n->name, n->hostname);
                        } else {
-                               logger(DEBUG_TRAFFIC, LOG_DEBUG, "Node %s (%s) became unreachable",
+                               logger(mesh, MESHLINK_DEBUG, "Node %s (%s) became unreachable",
                                           n->name, n->hostname);
                        }
 
index ea29e3f69c3414e9d8b6278573bd2bc724cf614d..ecb55a534a631f7f3cf68b447b794cf97f3819d6 100644 (file)
 #include "sptps.h"
 
 // TODO: refactor logging code to use a meshlink_handle_t *.
-void logger(int level, int priority, const char *format, ...) {
-       //if(level > mesh->debug_level)
-       //      return;
+void logger(meshlink_handle_t *mesh, meshlink_log_level_t level, const char *format, ...) {
+       if(mesh) {
+               if(level < mesh->log_level || !mesh->log_cb)
+                       return;
+       } else {
+               if(level < global_log_level || !global_log_cb)
+                       return;
+       }
 
        va_list ap;
        char message[1024] = "";
@@ -38,5 +43,8 @@ void logger(int level, int priority, const char *format, ...) {
        if(len > 0 && len < sizeof message && message[len - 1] == '\n')
                message[len - 1] = 0;
 
-       fprintf(stderr, "%s\n", message);
+       if(mesh)
+               mesh->log_cb(mesh, level, message);
+       else
+               global_log_cb(NULL, level, message);
 }
index 4dca2a9565a6f13f3a838bea7e5b96022804377d..94482697449bcd3de758da8d59580751f5c095bd 100644 (file)
 #ifndef __MESHLINK_LOGGER_H__
 #define __MESHLINK_LOGGER_H__
 
-typedef enum debug_t {
-       DEBUG_NOTHING = 0,              /* Quiet mode, only show starting/stopping of the daemon */
-       DEBUG_ALWAYS = 0,
-       DEBUG_CONNECTIONS = 1,          /* Show (dis)connects of other nodes via TCP */
-       DEBUG_ERROR = 2,                /* Show error messages received from other hosts */
-       DEBUG_STATUS = 2,               /* Show status messages received from other hosts */
-       DEBUG_PROTOCOL = 3,             /* Show the requests that are sent/received */
-       DEBUG_META = 4,                 /* Show contents of every request that is sent/received */
-       DEBUG_TRAFFIC = 5,              /* Show network traffic information */
-       DEBUG_PACKET = 6,               /* Show contents of each packet that is being sent/received */
-       DEBUG_SCARY_THINGS = 10         /* You have been warned */
-} debug_t;
+#include "meshlink_internal.h"
 
-typedef enum logmode_t {
-       LOGMODE_NULL,
-       LOGMODE_STDERR,
-       LOGMODE_SYSLOG
-} logmode_t;
-
-#ifdef HAVE_MINGW
-#define LOG_EMERG EVENTLOG_ERROR_TYPE
-#define LOG_ALERT EVENTLOG_ERROR_TYPE
-#define LOG_CRIT EVENTLOG_ERROR_TYPE
-#define LOG_ERR EVENTLOG_ERROR_TYPE
-#define LOG_WARNING EVENTLOG_WARNING_TYPE
-#define LOG_NOTICE EVENTLOG_INFORMATION_TYPE
-#define LOG_INFO EVENTLOG_INFORMATION_TYPE
-#define LOG_DEBUG EVENTLOG_INFORMATION_TYPE
-#else
-#ifndef HAVE_SYSLOG_H
-enum {
-       LOG_EMERG,
-       LOG_ALERT,
-       LOG_CRIT,
-       LOG_ERR,
-       LOG_WARNING,
-       LOG_NOTICE,
-       LOG_INFO,
-       LOG_DEBUG,
-};
-#endif
-#endif
-
-extern void logger(int, int, const char *, ...) __attribute__ ((__format__(printf, 3, 4)));
+extern void logger(meshlink_handle_t *mesh, meshlink_log_level_t level, const char *format, ...) __attribute__ ((__format__(printf, 3, 4)));
 
 #endif /* __MESHLINK_LOGGER_H__ */
index a1911affbdb1d949049e343b5cae5a4d732f0391..ef578b113068cc8993bf3728d547f194c45bf06a 100644 (file)
@@ -21,6 +21,7 @@
 #define MESHLINKPP_H
 
 #include <meshlink.h>
+#include <new> // for 'placement new'
 
 namespace meshlink {
        class mesh;
@@ -86,9 +87,30 @@ namespace meshlink {
 
        /// A class describing a MeshLink mesh.
        class mesh: public meshlink_handle_t {
-               public:
-               // TODO: delete constructor, add a destructor.
-
+       public:
+               mesh() : meshlink_handle_t() {}
+       
+               virtual ~mesh() {
+                       meshlink_close(this);
+               }
+               
+               /** instead of registerin callbacks you derive your own class and overwrite the following abstract member functions.
+                *  These functions are run in MeshLink's own thread.
+                *  It is therefore important that these functions use apprioriate methods (queues, pipes, locking, etc.)
+                *  to hand the data over to the application's thread.
+                *  These functions should also not block itself and return as quickly as possible.
+                * The default member functions are no-ops, so you are not required to overwrite all these member functions
+                */
+               
+               /// This function is called whenever another node sends data to the local node.
+               virtual void receive(node* source, const void* data, size_t length) { /* do nothing */ }
+               
+               /// This functions is called  whenever another node's status changed.
+               virtual void node_status(node* peer, bool reachable)                { /* do nothing */ }
+               
+               /// This functions is called whenever MeshLink has some information to log.
+               virtual void log(log_level_t level, const char* message)            { /* do nothing */ }
+               
                /// 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.
@@ -96,6 +118,9 @@ namespace meshlink {
                 *  @return         This function will return true if MeshLink has succesfully started its thread, false otherwise.
                 */
                bool start() {
+                       meshlink_set_receive_cb    (this, &receive_trampoline);
+                       meshlink_set_node_status_cb(this, &node_status_trampoline);
+                       meshlink_set_log_cb        (this, MESHLINK_DEBUG, &log_trampoline);
                        return meshlink_start(this);
                }
 
@@ -107,46 +132,6 @@ namespace meshlink {
                        meshlink_stop(this);
                }
 
-               /// Set the receive callback.
-               /** This functions sets the callback that is called whenever another node sends data to the local node.
-                *  The callback is run in MeshLink's own thread.
-                *  It is therefore important that the callback uses apprioriate methods (queues, pipes, locking, etc.)
-                *  to hand the data over to the application's thread.
-                *  The callback should also not block itself and return as quickly as possible.
-                *
-                *  @param cb        A pointer to the function which will be called when another node sends data to the local node.
-                */
-               void set_receive_cb(receive_cb_t cb) {
-                       meshlink_set_receive_cb(this, (meshlink_receive_cb_t)cb);
-               }
-
-               /// Set the node status callback.
-               /** This functions sets the callback that is called whenever another node's status changed.
-                *  The callback is run in MeshLink's own thread.
-                *  It is therefore important that the callback uses apprioriate methods (queues, pipes, locking, etc.)
-                *  to hand the data over to the application's thread.
-                *  The callback should also not block itself and return as quickly as possible.
-                *
-                *  @param cb        A pointer to the function which will be called when another node's status changes.
-                */
-               void set_node_status_cb(node_status_cb_t cb) {
-                       meshlink_set_node_status_cb(this, (meshlink_node_status_cb_t)cb);
-               }
-
-               /// Set the log callback.
-               /** This functions sets the callback that is called whenever MeshLink has some information to log.
-                *  The callback is run in MeshLink's own thread.
-                *  It is important that the callback uses apprioriate methods (queues, pipes, locking, etc.)
-                *  to hand the data over to the application's thread.
-                *  The callback should also not block itself and return as quickly as possible.
-                *
-                *  @param level     An enum describing the minimum severity level. Debugging information with a lower level will not trigger the callback.
-                *  @param cb        A pointer to the function which will be called when another node sends data to the local node.
-                */
-               void set_log_cb(meshlink_log_level_t level, log_cb_t cb) {
-                       meshlink_set_log_cb(this, level, (meshlink_log_cb_t)cb);
-               }
-
                /// Send data to another node.
                /** This functions sends one packet of data to another node in the mesh.
                 *  The packet is sent using UDP semantics, which means that
@@ -182,10 +167,11 @@ namespace meshlink {
                 *  @param nodes        A pointer to an array of pointers to meshlink::node, which should be allocated by the application.
                 *  @param nmemb        The maximum number of pointers that can be stored in the nodes array.
                 *
-                *  @return             The number of known nodes. This can be larger than nmemb, in which case not all nodes were stored in the nodes array.
+                *  @return             The number of known nodes, or -1 in case of an error.
+                *                      This can be larger than nmemb, in which case not all nodes were stored in the nodes array.
                 */
-               size_t get_all_nodes(node **nodes, size_t nmemb) {
-                       return meshlink_get_all_nodes(this, (meshlink_node_t **)nodes, nmemb);
+               node **get_all_nodes(node **nodes, size_t *nmemb) {
+                       return (node **)meshlink_get_all_nodes(this, (meshlink_node_t **)nodes, nmemb);
                }
 
                /// Sign data using the local node's MeshLink key.
@@ -319,7 +305,7 @@ namespace meshlink {
                 *  @return             A handle for the channel, or NULL in case of an error.
                 */
                channel *channel_open(node *node, uint16_t port, channel_receive_cb_t cb, const void *data, size_t len) {
-                       return (channel *)meshlink_channel_open(this, node, port, (meshlink_channel_receive_cb_t)recv, data, len);
+                       return (channel *)meshlink_channel_open(this, node, port, (meshlink_channel_receive_cb_t)cb, data, len);
                }
 
                /// Partially close a reliable stream channel.
@@ -358,6 +344,29 @@ namespace meshlink {
                        return meshlink_channel_send(this, channel, data, len);
                }
 
+       private:
+               // non-copyable:
+               mesh(const mesh&) /* TODO: C++11: = delete */;
+               void operator=(const mesh&) /* TODO: C++11: = delete */ ;
+               
+               /// static callback trampolines:
+               static void receive_trampoline(meshlink_handle_t* handle, meshlink_node_t* source, const void* data, size_t length)
+               {
+                       mesh* that = static_cast<mesh*>(handle);
+                       that->receive(static_cast<node*>(source), data, length);
+               }
+               
+               static void node_status_trampoline(meshlink_handle_t* handle, meshlink_node_t* peer, bool reachable)
+               {
+                       mesh* that = static_cast<mesh*>(handle);
+                       that->node_status(static_cast<node*>(peer), reachable);
+               }
+
+               static void log_trampoline(meshlink_handle_t* handle, log_level_t level, const char* message)
+               {
+                       mesh* that = static_cast<mesh*>(handle);
+                       that->log(level, message);
+               }
        };
 
        /// Initialize MeshLink's configuration directory.
@@ -372,11 +381,15 @@ namespace meshlink {
         *
         *  @param confbase The directory in which MeshLink will store its configuration files.
         *  @param name     The name which this instance of the application will use in the mesh.
+        *  @param appname  The application name which will be used in the mesh.
+        *  @param dclass   The device class which will be used in the mesh.
         *
         *  @return         This function will return a pointer to a meshlink::mesh if MeshLink has succesfully set up its configuration files, NULL otherwise.
         */
-       static mesh *open(const char *confbase, const char *name) {
-               return (mesh *)meshlink_open(confbase, name);
+       template<class MESH>
+       static MESH* open(const char *confbase, const char *name, const char* appname, dev_class_t devclass) {
+               void* mp = (void *)meshlink_open_with_size(confbase, name, appname, devclass, sizeof(MESH));
+               return new (mp) MESH;
        }
 
        /// Close the MeshLink handle.
@@ -387,6 +400,11 @@ namespace meshlink {
        static void close(mesh *mesh) {
                meshlink_close(mesh);
        }
-};
+
+       static const char *strerror(errno_t err = meshlink_errno) {
+               return meshlink_strerror(err);
+       }
+
+}
 
 #endif // MESHLINKPP_H
index f44dba47b7a7dec9ec2ed7ec394c24e934b01ae3..ef02580ea93d641059eb203c7e3a8a17b14c1ab9 100644 (file)
@@ -21,6 +21,8 @@
 #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;
@@ -31,6 +33,7 @@ typedef struct {
 
 #include "crypto.h"
 #include "ecdsagen.h"
+#include "logger.h"
 #include "meshlink_internal.h"
 #include "netutl.h"
 #include "node.h"
@@ -39,11 +42,16 @@ typedef struct {
 #include "utils.h"
 #include "xalloc.h"
 #include "ed25519/sha512.h"
+#include "discovery.h"
 
 #ifndef MSG_NOSIGNAL
 #define MSG_NOSIGNAL 0
 #endif
 
+__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 */
@@ -111,7 +119,7 @@ const var_t variables[] = {
 static bool fcopy(FILE *out, const char *filename) {
        FILE *in = fopen(filename, "r");
        if(!in) {
-               fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
+               logger(NULL, MESHLINK_ERROR, "Could not open %s: %s\n", filename, strerror(errno));
                return false;
        }
 
@@ -189,10 +197,10 @@ static char *get_my_hostname(meshlink_handle_t* mesh) {
                goto done;
 
        // If that doesn't work, guess externally visible hostname
-       fprintf(stderr, "Trying to discover externally visible hostname...\n");
+       logger(mesh, MESHLINK_DEBUG, "Trying to discover externally visible hostname...\n");
        struct addrinfo *ai = str2addrinfo("meshlink.io", "80", SOCK_STREAM);
        struct addrinfo *aip = ai;
-       static const char request[] = "GET http://meshlink.io/host.cgi HTTP/1.0\r\n\r\n";
+       static const char request[] = "GET http://www.meshlink.io/host.cgi HTTP/1.0\r\n\r\n";
 
        while(aip) {
                int s = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol);
@@ -244,7 +252,7 @@ static char *get_my_hostname(meshlink_handle_t* mesh) {
                fprintf(f, "\nAddress = %s\n", hostname);
                fclose(f);
        } else {
-               fprintf(stderr, "Could not append Address to %s: %s\n", filename, strerror(errno));
+               logger(mesh, MESHLINK_DEBUG, "Could not append Address to %s: %s\n", filename, strerror(errno));
        }
 
 done:
@@ -278,7 +286,7 @@ static char *get_line(const char **data) {
        const char *end = strchr(*data, '\n');
        size_t len = end ? end - *data : strlen(*data);
        if(len >= sizeof line) {
-               fprintf(stderr, "Maximum line length exceeded!\n");
+               logger(NULL, MESHLINK_ERROR, "Maximum line length exceeded!\n");
                return NULL;
        }
        if(len && !isprint(**data))
@@ -327,55 +335,54 @@ static bool try_bind(int port) {
 
        while(ai) {
                int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
-               if(!fd)
+               if(!fd) {
+                       freeaddrinfo(ai);
                        return false;
+               }
                int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
                closesocket(fd);
-               if(result)
+               if(result) {
+                       freeaddrinfo(ai);
                        return false;
+               }
                ai = ai->ai_next;
        }
 
+       freeaddrinfo(ai);
        return true;
 }
 
 static int check_port(meshlink_handle_t *mesh) {
-       if(try_bind(655))
-               return 655;
-
-       fprintf(stderr, "Warning: could not bind to port 655.\n");
-
-       for(int i = 0; i < 100; i++) {
+       for(int i = 0; i < 1000; i++) {
                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) {
-                               fprintf(stderr, "Please change MeshLink's Port manually.\n");
+                               logger(mesh, MESHLINK_DEBUG, "Please change MeshLink's Port manually.\n");
                                return 0;
                        }
 
                        fprintf(f, "Port = %d\n", port);
                        fclose(f);
-                       fprintf(stderr, "MeshLink will instead listen on port %d.\n", port);
                        return port;
                }
        }
 
-       fprintf(stderr, "Please change MeshLink's Port manually.\n");
+       logger(mesh, MESHLINK_DEBUG, "Please change MeshLink's Port manually.\n");
        return 0;
 }
 
 static bool finalize_join(meshlink_handle_t *mesh) {
        char *name = xstrdup(get_value(mesh->data, "Name"));
        if(!name) {
-               fprintf(stderr, "No Name found in invitation!\n");
+               logger(mesh, MESHLINK_DEBUG, "No Name found in invitation!\n");
                return false;
        }
 
        if(!check_id(name)) {
-               fprintf(stderr, "Invalid Name found in invitation: %s!\n", name);
+               logger(mesh, MESHLINK_DEBUG, "Invalid Name found in invitation: %s!\n", name);
                return false;
        }
 
@@ -384,7 +391,7 @@ static bool finalize_join(meshlink_handle_t *mesh) {
 
        FILE *f = fopen(filename, "w");
        if(!f) {
-               fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
+               logger(mesh, MESHLINK_DEBUG, "Could not create file %s: %s\n", filename, strerror(errno));
                return false;
        }
 
@@ -393,7 +400,7 @@ static bool finalize_join(meshlink_handle_t *mesh) {
        snprintf(filename, sizeof filename, "%s" SLASH "hosts" SLASH "%s", mesh->confbase, name);
        FILE *fh = fopen(filename, "w");
        if(!fh) {
-               fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
+               logger(mesh, MESHLINK_DEBUG, "Could not create file %s: %s\n", filename, strerror(errno));
                fclose(f);
                return false;
        }
@@ -439,10 +446,10 @@ static bool finalize_join(meshlink_handle_t *mesh) {
 
                // Ignore unknown and unsafe variables
                if(!found) {
-                       fprintf(stderr, "Ignoring unknown variable '%s' in invitation.\n", l);
+                       logger(mesh, MESHLINK_DEBUG, "Ignoring unknown variable '%s' in invitation.\n", l);
                        continue;
                } else if(!(variables[i].type & VAR_SAFE)) {
-                       fprintf(stderr, "Ignoring unsafe variable '%s' in invitation.\n", l);
+                       logger(mesh, MESHLINK_DEBUG, "Ignoring unsafe variable '%s' in invitation.\n", l);
                        continue;
                }
 
@@ -454,12 +461,12 @@ static bool finalize_join(meshlink_handle_t *mesh) {
 
        while(l && !strcasecmp(l, "Name")) {
                if(!check_id(value)) {
-                       fprintf(stderr, "Invalid Name found in invitation.\n");
+                       logger(mesh, MESHLINK_DEBUG, "Invalid Name found in invitation.\n");
                        return false;
                }
 
                if(!strcmp(value, name)) {
-                       fprintf(stderr, "Secondary chunk would overwrite our own host config file.\n");
+                       logger(mesh, MESHLINK_DEBUG, "Secondary chunk would overwrite our own host config file.\n");
                        return false;
                }
 
@@ -467,7 +474,7 @@ static bool finalize_join(meshlink_handle_t *mesh) {
                f = fopen(filename, "w");
 
                if(!f) {
-                       fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
+                       logger(mesh, MESHLINK_DEBUG, "Could not create file %s: %s\n", filename, strerror(errno));
                        return false;
                }
 
@@ -494,8 +501,10 @@ static bool finalize_join(meshlink_handle_t *mesh) {
        }
 
        char *b64key = ecdsa_get_base64_public_key(mesh->self->connection->ecdsa);
-       if(!b64key)
+       if(!b64key) {
+               fclose(fh);
                return false;
+               }
 
        fprintf(fh, "ECDSAPublicKey = %s\n", b64key);
        fprintf(fh, "Port = %s\n", mesh->myport);
@@ -508,9 +517,9 @@ static bool finalize_join(meshlink_handle_t *mesh) {
        free(mesh->self->name);
        free(mesh->self->connection->name);
        mesh->self->name = xstrdup(name);
-       mesh->self->connection->name = xstrdup(name);
+       mesh->self->connection->name = name;
 
-       fprintf(stderr, "Configuration stored in: %s\n", mesh->confbase);
+       logger(mesh, MESHLINK_DEBUG, "Configuration stored in: %s\n", mesh->confbase);
 
        load_all_nodes(mesh);
 
@@ -545,10 +554,11 @@ static bool invitation_receive(void *handle, uint8_t type, const void *msg, uint
                        break;
 
                case 1:
+                       mesh->thedatalen = 0;
                        return finalize_join(mesh);
 
                case 2:
-                       fprintf(stderr, "Invitation succesfully accepted.\n");
+                       logger(mesh, MESHLINK_DEBUG, "Invitation succesfully accepted.\n");
                        shutdown(mesh->sock, SHUT_RDWR);
                        mesh->success = true;
                        break;
@@ -618,12 +628,21 @@ static bool sendline(int fd, char *format, ...) {
 
 static const char *errstr[] = {
        [MESHLINK_OK] = "No error",
+       [MESHLINK_EINVAL] = "Invalid argument",
        [MESHLINK_ENOMEM] = "Out of memory",
        [MESHLINK_ENOENT] = "No such node",
+       [MESHLINK_EEXIST] = "Node already exists",
+       [MESHLINK_EINTERNAL] = "Internal error",
+       [MESHLINK_ERESOLV] = "Could not resolve hostname",
+       [MESHLINK_ESTORAGE] = "Storage error",
+       [MESHLINK_ENETWORK] = "Network error",
+       [MESHLINK_EPEER] = "Error communicating with peer",
 };
 
-const char *meshlink_strerror(meshlink_errno_t errno) {
-       return errstr[errno];
+const char *meshlink_strerror(meshlink_errno_t err) {
+       if(err < 0 || err >= sizeof errstr / sizeof *errstr)
+               return "Invalid error code";
+       return errstr[err];
 }
 
 static bool ecdsa_keygen(meshlink_handle_t *mesh) {
@@ -631,39 +650,44 @@ static bool ecdsa_keygen(meshlink_handle_t *mesh) {
        FILE *f;
        char pubname[PATH_MAX], privname[PATH_MAX];
 
-       fprintf(stderr, "Generating ECDSA keypair:\n");
+       logger(mesh, MESHLINK_DEBUG, "Generating ECDSA keypair:\n");
 
        if(!(key = ecdsa_generate())) {
-               fprintf(stderr, "Error during key generation!\n");
+               logger(mesh, MESHLINK_DEBUG, "Error during key generation!\n");
+               meshlink_errno = MESHLINK_EINTERNAL;
                return false;
        } else
-               fprintf(stderr, "Done.\n");
+               logger(mesh, MESHLINK_DEBUG, "Done.\n");
 
        snprintf(privname, sizeof privname, "%s" SLASH "ecdsa_key.priv", mesh->confbase);
        f = fopen(privname, "w");
 
-       if(!f)
+       if(!f) {
+               meshlink_errno = MESHLINK_ESTORAGE;
                return false;
+       }
 
 #ifdef HAVE_FCHMOD
        fchmod(fileno(f), 0600);
 #endif
 
        if(!ecdsa_write_pem_private_key(key, f)) {
-               fprintf(stderr, "Error writing private key!\n");
+               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)
+       if(!f) {
+               meshlink_errno = MESHLINK_ESTORAGE;
                return false;
+       }
 
        char *pubkey = ecdsa_get_base64_public_key(key);
        fprintf(f, "ECDSAPublicKey = %s\n", pubkey);
@@ -677,7 +701,8 @@ static bool ecdsa_keygen(meshlink_handle_t *mesh) {
 
 static bool meshlink_setup(meshlink_handle_t *mesh) {
        if(mkdir(mesh->confbase, 0777) && errno != EEXIST) {
-               fprintf(stderr, "Could not create directory %s: %s\n", mesh->confbase, strerror(errno));
+               logger(mesh, MESHLINK_DEBUG, "Could not create directory %s: %s\n", mesh->confbase, strerror(errno));
+               meshlink_errno = MESHLINK_ESTORAGE;
                return false;
        }
 
@@ -685,67 +710,97 @@ static bool meshlink_setup(meshlink_handle_t *mesh) {
        snprintf(filename, sizeof filename, "%s" SLASH "hosts", mesh->confbase);
 
        if(mkdir(filename, 0777) && errno != EEXIST) {
-               fprintf(stderr, "Could not create directory %s: %s\n", filename, strerror(errno));
+               logger(mesh, MESHLINK_DEBUG, "Could not create directory %s: %s\n", filename, strerror(errno));
+               meshlink_errno = MESHLINK_ESTORAGE;
                return false;
        }
 
        snprintf(filename, sizeof filename, "%s" SLASH "meshlink.conf", mesh->confbase);
 
        if(!access(filename, F_OK)) {
-               fprintf(stderr, "Configuration file %s already exists!\n", filename);
+               logger(mesh, MESHLINK_DEBUG, "Configuration file %s already exists!\n", filename);
+               meshlink_errno = MESHLINK_EEXIST;
                return false;
        }
 
        FILE *f = fopen(filename, "w");
        if(!f) {
-               fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
-               return 1;
+               logger(mesh, MESHLINK_DEBUG, "Could not create file %s: %s\n", filename, strerror(errno));
+               meshlink_errno = MESHLINK_ESTORAGE;
+               return false;
        }
 
        fprintf(f, "Name = %s\n", mesh->name);
        fclose(f);
 
-       if(!ecdsa_keygen(mesh))
+       if(!ecdsa_keygen(mesh)) {
+               meshlink_errno = MESHLINK_EINTERNAL;
                return false;
+       }
 
        check_port(mesh);
 
        return true;
 }
 
-meshlink_handle_t *meshlink_open(const char *confbase, const char *name) {
+meshlink_handle_t *meshlink_open(const char *confbase, const char *name, const char* appname, dev_class_t devclass) {
+       return meshlink_open_with_size(confbase, name, appname, devclass, sizeof(meshlink_handle_t));
+}
+
+meshlink_handle_t *meshlink_open_with_size(const char *confbase, const char *name, const char* appname, dev_class_t devclass, size_t size) {
+
        // Validate arguments provided by the application
        bool usingname = false;
+       
+       logger(NULL, MESHLINK_DEBUG, "meshlink_open called\n");
 
        if(!confbase || !*confbase) {
-               fprintf(stderr, "No confbase given!\n");
+               logger(NULL, MESHLINK_ERROR, "No confbase given!\n");
+               meshlink_errno = MESHLINK_EINVAL;
+               return NULL;
+       }
+
+       if(!appname || !*appname) {
+               logger(NULL, MESHLINK_ERROR, "No appname given!\n");
+               meshlink_errno = MESHLINK_EINVAL;
                return NULL;
        }
 
        if(!name || !*name) {
-               fprintf(stderr, "No name given!\n");
+               logger(NULL, MESHLINK_ERROR, "No name given!\n");
                //return NULL;
        }
        else { //check name only if there is a name != NULL
 
                if(!check_id(name)) {
-                       fprintf(stderr, "Invalid name given!\n");
+                       logger(NULL, MESHLINK_ERROR, "Invalid name given!\n");
+                       meshlink_errno = MESHLINK_EINVAL;
                        return NULL;
                } else { usingname = true;}
        }
 
-       meshlink_handle_t *mesh = xzalloc(sizeof *mesh);
+       if(devclass < 0 || devclass > _DEV_CLASS_MAX) {
+               logger(NULL, MESHLINK_ERROR, "Invalid devclass given!\n");
+               meshlink_errno = MESHLINK_EINVAL;
+               return NULL;
+       }
+
+       meshlink_handle_t *mesh = xzalloc(size);
        mesh->confbase = xstrdup(confbase);
+       mesh->appname = xstrdup(appname);
+       mesh->devclass = devclass;
        if (usingname) mesh->name = xstrdup(name);
-       pthread_mutex_init ( &(mesh->outpacketqueue_mutex), NULL);
-       pthread_mutex_init ( &(mesh->nodes_mutex), NULL);
+
+       // initialize mutex
+       pthread_mutexattr_t attr;
+       pthread_mutexattr_init(&attr);
+       pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+       pthread_mutex_init(&(mesh->mesh_mutex), &attr);
+       
        mesh->threadstarted = false;
        event_loop_init(&mesh->loop);
        mesh->loop.data = mesh;
 
-       // TODO: should be set by a function.
-       mesh->debug_level = 5;
-
        // Check whether meshlink.conf already exists
 
        char filename[PATH_MAX];
@@ -754,10 +809,15 @@ meshlink_handle_t *meshlink_open(const char *confbase, const char *name) {
        if(access(filename, R_OK)) {
                if(errno == ENOENT) {
                        // If not, create it
-                       meshlink_setup(mesh);
+                       if(!meshlink_setup(mesh)) {
+                               // meshlink_errno is set by meshlink_setup()
+                               return NULL;
+                       }
                } else {
-                       fprintf(stderr, "Cannot not read from %s: %s\n", filename, strerror(errno));
-                       return meshlink_close(mesh), NULL;
+                       logger(NULL, MESHLINK_ERROR, "Cannot not read from %s: %s\n", filename, strerror(errno));
+                       meshlink_close(mesh);
+                       meshlink_errno = MESHLINK_ESTORAGE;
+                       return NULL;
                }
        }
 
@@ -765,8 +825,11 @@ meshlink_handle_t *meshlink_open(const char *confbase, const char *name) {
 
        init_configuration(&mesh->config);
 
-       if(!read_server_config(mesh))
-               return meshlink_close(mesh), NULL;
+       if(!read_server_config(mesh)) {
+               meshlink_close(mesh);
+               meshlink_errno = MESHLINK_ESTORAGE;
+               return NULL;
+       };
 
 #ifdef HAVE_MINGW
        struct WSAData wsa_state;
@@ -776,120 +839,227 @@ meshlink_handle_t *meshlink_open(const char *confbase, const char *name) {
        // Setup up everything
        // TODO: we should not open listening sockets yet
 
-       if(!setup_network(mesh))
-               return meshlink_close(mesh), NULL;
+       if(!setup_network(mesh)) {
+               meshlink_close(mesh);
+               meshlink_errno = MESHLINK_ENETWORK;
+               return NULL;
+       }
 
+       logger(NULL, MESHLINK_DEBUG, "meshlink_open returning\n");
        return mesh;
 }
 
-void *meshlink_main_loop(void *arg) {
+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");
 
+       pthread_mutex_unlock(&(mesh->mesh_mutex));
        return NULL;
 }
 
 bool meshlink_start(meshlink_handle_t *mesh) {
+       if(!mesh) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return false;
+       }
+       pthread_mutex_lock(&(mesh->mesh_mutex));
+       
+       logger(mesh, MESHLINK_DEBUG, "meshlink_start called\n");
+
+       mesh->thedatalen = 0;
+
        // TODO: open listening sockets first
 
        //Check that a valid name is set
        if(!mesh->name ) {
-               fprintf(stderr, "No name given!\n");
+               logger(mesh, MESHLINK_DEBUG, "No name given!\n");
+               meshlink_errno = MESHLINK_EINVAL;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return false;
        }
 
        // Start the main thread
 
        if(pthread_create(&mesh->thread, NULL, meshlink_main_loop, mesh) != 0) {
-               fprintf(stderr, "Could not start thread: %s\n", strerror(errno));
+               logger(mesh, MESHLINK_DEBUG, "Could not start thread: %s\n", strerror(errno));
                memset(&mesh->thread, 0, sizeof mesh->thread);
+               meshlink_errno = MESHLINK_EINTERNAL;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return false;
        }
 
        mesh->threadstarted=true;
 
+       discovery_start(mesh);
+
+       pthread_mutex_unlock(&(mesh->mesh_mutex));
        return true;
 }
 
 void meshlink_stop(meshlink_handle_t *mesh) {
-       // Shut down the listening sockets to signal the main thread to shut down
-
-       for(int i = 0; i < mesh->listen_sockets; i++) {
-               shutdown(mesh->listen_socket[i].tcp.fd, SHUT_RDWR);
-               shutdown(mesh->listen_socket[i].udp.fd, SHUT_RDWR);
+       if(!mesh) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return;
        }
 
-       // Wait for the main thread to finish
+       pthread_mutex_lock(&(mesh->mesh_mutex));
+       logger(mesh, MESHLINK_DEBUG, "meshlink_stop called\n");
+
+       // Stop discovery
+       discovery_stop(mesh);
 
+       // Shut down a listening socket to signal the main thread to shut down
+
+       listen_socket_t *s = &mesh->listen_socket[0];
+       shutdown(s->tcp.fd, SHUT_RDWR);
+
+       // Wait for the main thread to finish
+       pthread_mutex_unlock(&(mesh->mesh_mutex));
        pthread_join(mesh->thread, NULL);
+       pthread_mutex_lock(&(mesh->mesh_mutex));
+
+       mesh->threadstarted = false;
+
+       // Fix the socket
+       
+       closesocket(s->tcp.fd);
+       io_del(&mesh->loop, &s->tcp);
+       s->tcp.fd = setup_listen_socket(&s->sa);
+       if(s->tcp.fd < 0)
+               logger(mesh, MESHLINK_ERROR, "Could not repair listenen socket!");
+       else
+               io_add(&mesh->loop, &s->tcp, handle_new_meta_connection, s, s->tcp.fd, IO_READ);
+       
+       pthread_mutex_unlock(&(mesh->mesh_mutex));
 }
 
 void meshlink_close(meshlink_handle_t *mesh) {
+       if(!mesh || !mesh->confbase) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return;
+       }
+
+       // lock is not released after this
+       pthread_mutex_lock(&(mesh->mesh_mutex));
+
        // Close and free all resources used.
 
        close_network_connections(mesh);
 
-       logger(DEBUG_ALWAYS, LOG_NOTICE, "Terminating");
+       logger(mesh, MESHLINK_INFO, "Terminating");
 
        exit_configuration(&mesh->config);
        event_loop_exit(&mesh->loop);
 
-       free(mesh);
-
 #ifdef HAVE_MINGW
-       WSACleanup();
+       if(mesh->confbase)
+               WSACleanup();
 #endif
+
+       ecdsa_free(mesh->invitation_key);
+
+       free(mesh->name);
+       free(mesh->appname);
+       free(mesh->confbase);
+       pthread_mutex_destroy(&(mesh->mesh_mutex));
+
+       memset(mesh, 0, sizeof *mesh);
+
+       free(mesh);
 }
 
 void meshlink_set_receive_cb(meshlink_handle_t *mesh, meshlink_receive_cb_t cb) {
+       if(!mesh) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return;
+       }
+
+       pthread_mutex_lock(&(mesh->mesh_mutex));
        mesh->receive_cb = cb;
+       pthread_mutex_unlock(&(mesh->mesh_mutex));
 }
 
 void meshlink_set_node_status_cb(meshlink_handle_t *mesh, meshlink_node_status_cb_t cb) {
+       if(!mesh) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return;
+       }
+
+       pthread_mutex_lock(&(mesh->mesh_mutex));
        mesh->node_status_cb = cb;
+       pthread_mutex_unlock(&(mesh->mesh_mutex));
 }
 
 void meshlink_set_log_cb(meshlink_handle_t *mesh, meshlink_log_level_t level, meshlink_log_cb_t cb) {
-       mesh->log_cb = cb;
-       mesh->log_level = level;
+       if(mesh) {
+               pthread_mutex_lock(&(mesh->mesh_mutex));
+               mesh->log_cb = cb;
+               mesh->log_level = cb ? level : 0;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
+       } else {
+               global_log_cb = cb;
+               global_log_level = cb ? level : 0;
+       }
 }
 
-bool meshlink_send(meshlink_handle_t *mesh, meshlink_node_t *destination, const void *data, unsigned int len) {
+bool meshlink_send(meshlink_handle_t *mesh, meshlink_node_t *destination, const void *data, size_t len) {
+       if(!mesh || !destination) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return false;
+       }
 
-       /* If there is no outgoing list yet, create one. */
+       if(!len)
+               return true;
 
-       if(!mesh->outpacketqueue)
-               mesh->outpacketqueue = list_alloc(NULL);
+       if(!data) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return false;
+       }
+
+       pthread_mutex_lock(&(mesh->mesh_mutex));
 
        //add packet to the queue
        outpacketqueue_t *packet_in_queue = xzalloc(sizeof *packet_in_queue);
        packet_in_queue->destination=destination;
        packet_in_queue->data=data;
        packet_in_queue->len=len;
-       pthread_mutex_lock(&(mesh->outpacketqueue_mutex));
-       list_insert_head(mesh->outpacketqueue,packet_in_queue);
-       pthread_mutex_unlock(&(mesh->outpacketqueue_mutex));
+       if(!meshlink_queue_push(&mesh->outpacketqueue, packet_in_queue)) {
+               free(packet_in_queue);
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
+               return false;
+       }
 
        //notify event loop
        signal_trigger(&(mesh->loop),&(mesh->datafromapp));
+       
+       pthread_mutex_unlock(&(mesh->mesh_mutex));
        return true;
 }
 
 void meshlink_send_from_queue(event_loop_t* el,meshlink_handle_t *mesh) {
+       pthread_mutex_lock(&(mesh->mesh_mutex));
+       
        vpn_packet_t packet;
        meshlink_packethdr_t *hdr = (meshlink_packethdr_t *)packet.data;
 
-       outpacketqueue_t* p = list_get_tail(mesh->outpacketqueue);
-       if (p)
-       list_delete_tail(mesh->outpacketqueue);
-       else return ;
+       outpacketqueue_t* p = meshlink_queue_pop(&mesh->outpacketqueue);
+       if(!p)
+       {
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
+               return;
+       }
 
        if (sizeof(meshlink_packethdr_t) + p->len > MAXSIZE) {
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                //log something
-               return ;
+               return;
        }
 
        packet.probe = false;
@@ -903,62 +1073,171 @@ void meshlink_send_from_queue(event_loop_t* el,meshlink_handle_t *mesh) {
         mesh->self->in_packets++;
         mesh->self->in_bytes += packet.len;
         route(mesh, mesh->self, &packet);
+       
+       pthread_mutex_unlock(&(mesh->mesh_mutex));
        return ;
 }
 
+ssize_t meshlink_get_pmtu(meshlink_handle_t *mesh, meshlink_node_t *destination) {
+       if(!mesh || !destination) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return -1;
+       }
+       pthread_mutex_lock(&(mesh->mesh_mutex));
+
+       node_t *n = (node_t *)destination;
+       if(!n->status.reachable) {
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
+               return 0;
+       
+       }
+       else if(n->mtuprobes > 30 && n->minmtu) {
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
+               return n->minmtu;
+       }
+       else {
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
+               return MTU;
+       }
+}
+
+char *meshlink_get_fingerprint(meshlink_handle_t *mesh, meshlink_node_t *node) {
+       if(!mesh || !node) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return NULL;
+       }
+       pthread_mutex_lock(&(mesh->mesh_mutex));
+
+       node_t *n = (node_t *)node;
+
+       if(!node_read_ecdsa_public_key(mesh, n) || !n->ecdsa) {
+               meshlink_errno = MESHLINK_EINTERNAL;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
+               return false;
+       }
+
+       char *fingerprint = ecdsa_get_base64_public_key(n->ecdsa);
+
+       if(!fingerprint)
+               meshlink_errno = MESHLINK_EINTERNAL;
+
+       pthread_mutex_unlock(&(mesh->mesh_mutex));
+       return fingerprint;
+}
+
 meshlink_node_t *meshlink_get_node(meshlink_handle_t *mesh, const char *name) {
-       return (meshlink_node_t *)lookup_node(mesh, (char *)name); // TODO: make lookup_node() use const
+       if(!mesh || !name) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return NULL;
+       }
+
+       meshlink_node_t *node = NULL;
+
+       pthread_mutex_lock(&(mesh->mesh_mutex));
+       node = (meshlink_node_t *)lookup_node(mesh, (char *)name); // TODO: make lookup_node() use const
+       pthread_mutex_unlock(&(mesh->mesh_mutex));
+       return node;
 }
 
-size_t meshlink_get_all_nodes(meshlink_handle_t *mesh, meshlink_node_t **nodes, size_t nmemb) {
-       size_t i = 0;
+meshlink_node_t **meshlink_get_all_nodes(meshlink_handle_t *mesh, meshlink_node_t **nodes, size_t *nmemb) {
+       if(!mesh || !nmemb || (*nmemb && !nodes)) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return NULL;
+       }
+
+       meshlink_node_t **result;
 
        //lock mesh->nodes
-       pthread_mutex_lock(&(mesh->nodes_mutex));
+       pthread_mutex_lock(&(mesh->mesh_mutex));
+
+       *nmemb = mesh->nodes->count;
+       result = realloc(nodes, *nmemb * sizeof *nodes);
 
-       for splay_each(node_t, n, mesh->nodes) {
-               if(i < nmemb)
-                       nodes[i] = (meshlink_node_t *)n;
-               i++;
+       if(result) {
+               meshlink_node_t **p = result;
+               for splay_each(node_t, n, mesh->nodes)
+                       *p++ = (meshlink_node_t *)n;
+       } else {
+               *nmemb = 0;
+               free(nodes);
+               meshlink_errno = MESHLINK_ENOMEM;
        }
 
-       pthread_mutex_unlock(&(mesh->nodes_mutex));
+       pthread_mutex_unlock(&(mesh->mesh_mutex));
 
-       return i;
+       return result;
 }
 
 bool meshlink_sign(meshlink_handle_t *mesh, const void *data, size_t len, void *signature, size_t *siglen) {
-       if(*siglen < MESHLINK_SIGLEN)
+       if(!mesh || !data || !len || !signature || !siglen) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return false;
+       }
+
+       if(*siglen < MESHLINK_SIGLEN) {
+               meshlink_errno = MESHLINK_EINVAL;
                return false;
-       if(!ecdsa_sign(mesh->self->connection->ecdsa, data, len, signature))
+       }
+
+       pthread_mutex_lock(&(mesh->mesh_mutex));
+
+       if(!ecdsa_sign(mesh->self->connection->ecdsa, data, len, signature)) {
+               meshlink_errno = MESHLINK_EINTERNAL;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return false;
+       }
+
        *siglen = MESHLINK_SIGLEN;
+       pthread_mutex_unlock(&(mesh->mesh_mutex));
        return true;
 }
 
 bool meshlink_verify(meshlink_handle_t *mesh, meshlink_node_t *source, const void *data, size_t len, const void *signature, size_t siglen) {
-       if(siglen != MESHLINK_SIGLEN)
+       if(!mesh || !data || !len || !signature) {
+               meshlink_errno = MESHLINK_EINVAL;
                return false;
+       }
+
+       if(siglen != MESHLINK_SIGLEN) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return false;
+       }
+
+       pthread_mutex_lock(&(mesh->mesh_mutex));
+
+       bool rval = false;
+
        struct node_t *n = (struct node_t *)source;
        node_read_ecdsa_public_key(mesh, n);
-       if(!n->ecdsa)
-               return false;
-       return ecdsa_verify(((struct node_t *)source)->ecdsa, data, len, signature);
+       if(!n->ecdsa) {
+               meshlink_errno = MESHLINK_EINTERNAL;
+               rval = false;
+       } else {
+               rval = ecdsa_verify(((struct node_t *)source)->ecdsa, data, len, signature);
+       }
+       pthread_mutex_unlock(&(mesh->mesh_mutex));
+       return rval;
 }
 
 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) {
-               fprintf(stderr, "Could not create directory %s: %s\n", filename, strerror(errno));
+               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) {
-               fprintf(stderr, "Could not read directory %s: %s\n", filename, strerror(errno));
+               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;
        }
 
@@ -979,14 +1258,16 @@ static bool refresh_invitation_key(meshlink_handle_t *mesh) {
                        else
                                unlink(invname);
                } else {
-                       fprintf(stderr, "Could not stat %s: %s\n", invname, strerror(errno));
+                       logger(mesh, MESHLINK_DEBUG, "Could not stat %s: %s\n", invname, strerror(errno));
                        errno = 0;
                }
        }
 
        if(errno) {
-               fprintf(stderr, "Error while reading directory %s: %s\n", filename, strerror(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;
        }
 
@@ -1003,25 +1284,33 @@ static bool refresh_invitation_key(meshlink_handle_t *mesh) {
                }
        }
 
-       if(mesh->invitation_key)
+       if(mesh->invitation_key) {
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return true;
+       }
 
        // Create a new key if necessary.
        FILE *f = fopen(filename, "r");
        if(!f) {
                if(errno != ENOENT) {
-                       fprintf(stderr, "Could not read %s: %s\n", filename, strerror(errno));
+                       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) {
-                       fprintf(stderr, "Could not generate a new key!\n");
+                       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, "w");
                if(!f) {
-                       fprintf(stderr, "Could not write %s: %s\n", filename, strerror(errno));
+                       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);
@@ -1030,28 +1319,53 @@ static bool refresh_invitation_key(meshlink_handle_t *mesh) {
        } else {
                mesh->invitation_key = ecdsa_read_pem_private_key(f);
                fclose(f);
-               if(!mesh->invitation_key)
-                       fprintf(stderr, "Could not read private key from %s\n", filename);
+               if(!mesh->invitation_key) {
+                       logger(mesh, MESHLINK_DEBUG, "Could not read private key from %s\n", filename);
+                       meshlink_errno = MESHLINK_ESTORAGE;
+               }
        }
 
+       pthread_mutex_unlock(&(mesh->mesh_mutex));
        return mesh->invitation_key;
 }
 
 bool meshlink_add_address(meshlink_handle_t *mesh, const char *address) {
+       if(!mesh || !address) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return false;
+       }
+       
+       bool rval = false;
+
+       pthread_mutex_lock(&(mesh->mesh_mutex));
+
        for(const char *p = address; *p; p++) {
                if(isalnum(*p) || *p == '-' || *p == '.' || *p == ':')
                        continue;
-               fprintf(stderr, "Invalid character in address: %s\n", address);
+               logger(mesh, MESHLINK_DEBUG, "Invalid character in address: %s\n", address);
+               meshlink_errno = MESHLINK_EINVAL;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return false;
        }
 
-       return append_config_file(mesh, mesh->self->name, "Address", address);
+       rval = append_config_file(mesh, mesh->self->name, "Address", address);
+       pthread_mutex_unlock(&(mesh->mesh_mutex));
+       return rval;
 }
 
 char *meshlink_invite(meshlink_handle_t *mesh, const char *name) {
+       if(!mesh) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return NULL;
+       }
+       
+       pthread_mutex_lock(&(mesh->mesh_mutex));
+
        // Check validity of the new node's name
        if(!check_id(name)) {
-               fprintf(stderr, "Invalid name for node.\n");
+               logger(mesh, MESHLINK_DEBUG, "Invalid name for node.\n");
+               meshlink_errno = MESHLINK_EINVAL;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return NULL;
        }
 
@@ -1059,25 +1373,34 @@ char *meshlink_invite(meshlink_handle_t *mesh, const char *name) {
        char filename[PATH_MAX];
        snprintf(filename, sizeof filename, "%s" SLASH "hosts" SLASH "%s", mesh->confbase, name);
        if(!access(filename, F_OK)) {
-               fprintf(stderr, "A host config file for %s already exists!\n", 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));
                return NULL;
        }
 
        // Ensure no other nodes know about this name
        if(meshlink_get_node(mesh, name)) {
-               fprintf(stderr, "A node with name %s is already known!\n", name);
+               logger(mesh, MESHLINK_DEBUG, "A node with name %s is already known!\n", name);
+               meshlink_errno = MESHLINK_EEXIST;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return NULL;
        }
 
        // Get the local address
        char *address = get_my_hostname(mesh);
        if(!address) {
-               fprintf(stderr, "No Address known for ourselves!\n");
+               logger(mesh, MESHLINK_DEBUG, "No Address known for ourselves!\n");
+               meshlink_errno = MESHLINK_ERESOLV;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return NULL;
        }
 
-       if(!refresh_invitation_key(mesh))
+       if(!refresh_invitation_key(mesh)) {
+               meshlink_errno = MESHLINK_EINTERNAL;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return NULL;
+       }
 
        char hash[64];
 
@@ -1100,11 +1423,15 @@ char *meshlink_invite(meshlink_handle_t *mesh, const char *name) {
 
        b64encode_urlsafe(cookie, cookie, 18);
 
+       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);
        if(!ifd) {
-               fprintf(stderr, "Could not create invitation file %s: %s\n", filename, strerror(errno));
+               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;
        }
        FILE *f = fdopen(ifd, "w");
@@ -1133,7 +1460,9 @@ char *meshlink_invite(meshlink_handle_t *mesh, const char *name) {
                }
                fclose(tc);
        } else {
-               fprintf(stderr, "Could not create %s: %s\n", filename, strerror(errno));
+               logger(mesh, MESHLINK_DEBUG, "Could not create %s: %s\n", filename, strerror(errno));
+               meshlink_errno = MESHLINK_ESTORAGE;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return NULL;
        }
 
@@ -1147,11 +1476,20 @@ char *meshlink_invite(meshlink_handle_t *mesh, const char *name) {
        // Create an URL from the local address, key hash and cookie
        char *url;
        xasprintf(&url, "%s/%s%s", address, hash, cookie);
+       free(address);
 
+       pthread_mutex_unlock(&(mesh->mesh_mutex));
        return url;
 }
 
 bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) {
+       if(!mesh || !invitation) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return false;
+       }
+       
+       pthread_mutex_lock(&(mesh->mesh_mutex));
+
        //TODO: think of a better name for this variable, or of a different way to tokenize the invitation URL.
        char copy[strlen(invitation) + 1];
        strcpy(copy, invitation);
@@ -1184,15 +1522,18 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) {
        }
 
        if(!port)
-               port = "655";
+               goto invalid;
 
        if(!b64decode(slash, mesh->hash, 18) || !b64decode(slash + 24, mesh->cookie, 18))
                goto invalid;
 
        // Generate a throw-away key for the invitation.
        ecdsa_t *key = ecdsa_generate();
-       if(!key)
+       if(!key) {
+               meshlink_errno = MESHLINK_EINTERNAL;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return false;
+       }
 
        char *b64key = ecdsa_get_base64_public_key(key);
 
@@ -1203,39 +1544,56 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) {
 
        // Connect to the meshlink daemon mentioned in the URL.
        struct addrinfo *ai = str2addrinfo(address, port, SOCK_STREAM);
-       if(!ai)
+       if(!ai) {
+               meshlink_errno = MESHLINK_ERESOLV;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return false;
+       }
 
        mesh->sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
        if(mesh->sock <= 0) {
-               fprintf(stderr, "Could not open socket: %s\n", strerror(errno));
+               logger(mesh, MESHLINK_DEBUG, "Could not open socket: %s\n", strerror(errno));
+               freeaddrinfo(ai);
+               meshlink_errno = MESHLINK_ENETWORK;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return false;
        }
 
        if(connect(mesh->sock, ai->ai_addr, ai->ai_addrlen)) {
-               fprintf(stderr, "Could not connect to %s port %s: %s\n", address, port, strerror(errno));
+               logger(mesh, MESHLINK_DEBUG, "Could not connect to %s port %s: %s\n", address, port, strerror(errno));
                closesocket(mesh->sock);
+               freeaddrinfo(ai);
+               meshlink_errno = MESHLINK_ENETWORK;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return false;
        }
 
-       fprintf(stderr, "Connected to %s port %s...\n", address, port);
+       freeaddrinfo(ai);
+
+       logger(mesh, MESHLINK_DEBUG, "Connected to %s port %s...\n", address, port);
 
        // Tell him we have an invitation, and give him our throw-away key.
 
        mesh->blen = 0;
 
        if(!sendline(mesh->sock, "0 ?%s %d.%d", b64key, PROT_MAJOR, 1)) {
-               fprintf(stderr, "Error sending request to %s port %s: %s\n", address, port, strerror(errno));
+               logger(mesh, MESHLINK_DEBUG, "Error sending request to %s port %s: %s\n", address, port, strerror(errno));
                closesocket(mesh->sock);
+               meshlink_errno = MESHLINK_ENETWORK;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return false;
        }
 
+       free(b64key);
+
        char hisname[4096] = "";
        int code, hismajor, hisminor = 0;
 
        if(!recvline(mesh, sizeof mesh->line) || sscanf(mesh->line, "%d %s %d.%d", &code, hisname, &hismajor, &hisminor) < 3 || code != 0 || hismajor != PROT_MAJOR || !check_id(hisname) || !recvline(mesh, sizeof mesh->line) || !rstrip(mesh->line) || sscanf(mesh->line, "%d ", &code) != 1 || code != ACK || strlen(mesh->line) < 3) {
-               fprintf(stderr, "Cannot read greeting from peer\n");
+               logger(mesh, MESHLINK_DEBUG, "Cannot read greeting from peer\n");
                closesocket(mesh->sock);
+               meshlink_errno = MESHLINK_ENETWORK;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return false;
        }
 
@@ -1243,26 +1601,39 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) {
        char *fingerprint = mesh->line + 2;
        char hishash[64];
        if(sha512(fingerprint, strlen(fingerprint), hishash)) {
-               fprintf(stderr, "Could not create hash\n%s\n", mesh->line + 2);
+               logger(mesh, MESHLINK_DEBUG, "Could not create hash\n%s\n", mesh->line + 2);
+               meshlink_errno = MESHLINK_EINTERNAL;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return false;
        }
        if(memcmp(hishash, mesh->hash, 18)) {
-               fprintf(stderr, "Peer has an invalid key!\n%s\n", mesh->line + 2);
+               logger(mesh, MESHLINK_DEBUG, "Peer has an invalid key!\n%s\n", mesh->line + 2);
+               meshlink_errno = MESHLINK_EPEER;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return false;
 
        }
 
        ecdsa_t *hiskey = ecdsa_set_base64_public_key(fingerprint);
-       if(!hiskey)
+       if(!hiskey) {
+               meshlink_errno = MESHLINK_EINTERNAL;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return false;
+       }
 
        // Start an SPTPS session
-       if(!sptps_start(&mesh->sptps, mesh, true, false, key, hiskey, "meshlink invitation", 15, invitation_send, invitation_receive))
+       if(!sptps_start(&mesh->sptps, mesh, true, false, key, hiskey, "meshlink invitation", 15, invitation_send, invitation_receive)) {
+               meshlink_errno = MESHLINK_EINTERNAL;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return false;
+       }
 
        // Feed rest of input buffer to SPTPS
-       if(!sptps_receive_data(&mesh->sptps, mesh->buffer, mesh->blen))
+       if(!sptps_receive_data(&mesh->sptps, mesh->buffer, mesh->blen)) {
+               meshlink_errno = MESHLINK_EPEER;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return false;
+       }
 
        int len;
 
@@ -1270,12 +1641,17 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) {
                if(len < 0) {
                        if(errno == EINTR)
                                continue;
-                       fprintf(stderr, "Error reading data from %s port %s: %s\n", address, port, strerror(errno));
+                       logger(mesh, MESHLINK_DEBUG, "Error reading data from %s port %s: %s\n", address, port, strerror(errno));
+                       meshlink_errno = MESHLINK_ENETWORK;
+                       pthread_mutex_unlock(&(mesh->mesh_mutex));
                        return false;
                }
 
-               if(!sptps_receive_data(&mesh->sptps, mesh->line, len))
+               if(!sptps_receive_data(&mesh->sptps, mesh->line, len)) {
+                       meshlink_errno = MESHLINK_EPEER;
+                       pthread_mutex_unlock(&(mesh->mesh_mutex));
                        return false;
+               }
        }
 
        sptps_stop(&mesh->sptps);
@@ -1284,23 +1660,37 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) {
        closesocket(mesh->sock);
 
        if(!mesh->success) {
-               fprintf(stderr, "Connection closed by peer, invitation cancelled.\n");
+               logger(mesh, MESHLINK_DEBUG, "Connection closed by peer, invitation cancelled.\n");
+               meshlink_errno = MESHLINK_EPEER;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return false;
        }
 
+       pthread_mutex_unlock(&(mesh->mesh_mutex));
        return true;
 
 invalid:
-       fprintf(stderr, "Invalid invitation URL or you are already connected to a Mesh ?\n");
+       logger(mesh, MESHLINK_DEBUG, "Invalid invitation URL or you are already connected to a Mesh ?\n");
+       meshlink_errno = MESHLINK_EINVAL;
+       pthread_mutex_unlock(&(mesh->mesh_mutex));
        return false;
 }
 
 char *meshlink_export(meshlink_handle_t *mesh) {
+       if(!mesh) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return NULL;
+       }
+
+       pthread_mutex_lock(&(mesh->mesh_mutex));
+       
        char filename[PATH_MAX];
        snprintf(filename, sizeof filename, "%s" SLASH "hosts" SLASH "%s", mesh->confbase, mesh->self->name);
        FILE *f = fopen(filename, "r");
        if(!f) {
-               fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
+               logger(mesh, MESHLINK_DEBUG, "Could not open %s: %s\n", filename, strerror(errno));
+               meshlink_errno = MESHLINK_ESTORAGE;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return NULL;
        }
 
@@ -1312,25 +1702,40 @@ char *meshlink_export(meshlink_handle_t *mesh) {
        char *buf = xmalloc(len);
        snprintf(buf, len, "Name = %s\n", mesh->self->name);
        if(fread(buf + len - fsize - 1, fsize, 1, f) != 1) {
-               fprintf(stderr, "Error reading from %s: %s\n", filename, strerror(errno));
+               logger(mesh, MESHLINK_DEBUG, "Error reading from %s: %s\n", filename, strerror(errno));
                fclose(f);
+               meshlink_errno = MESHLINK_ESTORAGE;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return NULL;
        }
 
        fclose(f);
        buf[len - 1] = 0;
+       
+       pthread_mutex_unlock(&(mesh->mesh_mutex));
        return buf;
 }
 
 bool meshlink_import(meshlink_handle_t *mesh, const char *data) {
+       if(!mesh || !data) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return false;
+       }
+       
+       pthread_mutex_lock(&(mesh->mesh_mutex));
+
        if(strncmp(data, "Name = ", 7)) {
-               fprintf(stderr, "Invalid data\n");
+               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');
        if(!end) {
-               fprintf(stderr, "Invalid data\n");
+               logger(mesh, MESHLINK_DEBUG, "Invalid data\n");
+               meshlink_errno = MESHLINK_EPEER;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return false;
        }
 
@@ -1339,25 +1744,33 @@ bool meshlink_import(meshlink_handle_t *mesh, const char *data) {
        memcpy(name, data + 7, len);
        name[len] = 0;
        if(!check_id(name)) {
-               fprintf(stderr, "Invalid Name\n");
+               logger(mesh, MESHLINK_DEBUG, "Invalid Name\n");
+               meshlink_errno = MESHLINK_EPEER;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return false;
        }
 
        char filename[PATH_MAX];
        snprintf(filename, sizeof filename, "%s" SLASH "hosts" SLASH "%s", mesh->confbase, name);
        if(!access(filename, F_OK)) {
-               fprintf(stderr, "File %s already exists, not importing\n", filename);
+               logger(mesh, MESHLINK_DEBUG, "File %s already exists, not importing\n", filename);
+               meshlink_errno = MESHLINK_EEXIST;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return false;
        }
 
        if(errno != ENOENT) {
-               fprintf(stderr, "Error accessing %s: %s\n", filename, strerror(errno));
+               logger(mesh, MESHLINK_DEBUG, "Error accessing %s: %s\n", filename, strerror(errno));
+               meshlink_errno = MESHLINK_ESTORAGE;
+               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return false;
        }
 
        FILE *f = fopen(filename, "w");
        if(!f) {
-               fprintf(stderr, "Could not create %s: %s\n", filename, strerror(errno));
+               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;
        }
 
@@ -1366,19 +1779,139 @@ bool meshlink_import(meshlink_handle_t *mesh, const char *data) {
 
        load_all_nodes(mesh);
 
+       pthread_mutex_unlock(&(mesh->mesh_mutex));
        return true;
 }
 
 void meshlink_blacklist(meshlink_handle_t *mesh, meshlink_node_t *node) {
-    node_t *n;
-    n = (node_t*)node;
-    n->status.blacklisted=true;
-       fprintf(stderr, "Blacklisted %s.\n",node->name);
+       if(!mesh || !node) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return;
+       }
+
+       pthread_mutex_lock(&(mesh->mesh_mutex));
+       
+       node_t *n;
+       n = (node_t*)node;
+       n->status.blacklisted=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");
-    return;
 
+       pthread_mutex_unlock(&(mesh->mesh_mutex));
+       return;
+}
+
+void meshlink_whitelist(meshlink_handle_t *mesh, meshlink_node_t *node) {
+       if(!mesh || !node) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return;
+       }
+
+       pthread_mutex_lock(&(mesh->mesh_mutex));
+       
+       node_t *n = (node_t *)node;
+       n->status.blacklisted = false;
+
+       //TODO: remove blacklisted = yes from the config file
+
+       pthread_mutex_unlock(&(mesh->mesh_mutex));
+       return;
+}
+
+/* Hint that a hostname may be found at an address
+ * See header file for detailed comment.
+ */
+void meshlink_hint_address(meshlink_handle_t *mesh, meshlink_node_t *node, const struct sockaddr *addr) {
+       if(!mesh || !node || !addr)
+               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 ) )
+                       append_config_file(mesh, node->name, "Address", str);
+               else
+                       logger(mesh, MESHLINK_DEBUG, "Not adding Link Local IPv6 Address to config\n");
+       }
+
+       free(str);
+       free(host);
+       free(port);
+
+       pthread_mutex_unlock(&(mesh->mesh_mutex));
+       // @TODO do we want to fire off a connection attempt right away?
+}
+
+/* Return an array of edges in the current network graph.
+ * Data captures the current state and will not be updated.
+ * Caller must deallocate data when done.
+ */
+meshlink_edge_t **meshlink_get_all_edges_state(meshlink_handle_t *mesh, meshlink_edge_t **edges, size_t *nmemb) {
+       if(!mesh || !nmemb || (*nmemb && !edges)) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return NULL;
+       }
+
+       pthread_mutex_lock(&(mesh->mesh_mutex));
+       
+       meshlink_edge_t **result = NULL;
+       meshlink_edge_t *copy = NULL;
+       int result_size = 0;
+
+       result_size = mesh->edges->count;
+
+       // if result is smaller than edges, we have to dealloc all the excess meshlink_edge_t
+       if(result_size > *nmemb) {
+               result = realloc(edges, result_size * sizeof (meshlink_edge_t*));
+       } else {
+               result = edges;
+       }
+
+       if(result) {
+               meshlink_edge_t **p = result;
+               int n = 0;
+               for splay_each(edge_t, e, mesh->edges) {
+                       // skip edges that do not represent a two-directional connection
+                       if((!e->reverse) || (e->reverse->to != e->from)) {
+                               result_size--;
+                               continue;
+                       }
+                       n++;
+                       // the first *nmemb members of result can be re-used
+                       if(n > *nmemb) {
+                               copy = xzalloc(sizeof *copy);
+                       }
+                       else {
+                               copy = *p;
+                       }
+                       copy->from = (meshlink_node_t*)e->from;
+                       copy->to = (meshlink_node_t*)e->to;
+                       copy->address = e->address.storage;
+                       copy->options = e->options;
+                       copy->weight = e->weight;
+                       *p++ = copy;
+               }
+               // shrink result to the actual amount of memory used
+               for(int i = *nmemb; i > result_size; i--) {
+                       free(result[i - 1]);
+               }
+               result = realloc(result, result_size * sizeof (meshlink_edge_t*));
+               *nmemb = result_size;
+       } else {
+               *nmemb = 0;
+               free(result);
+               meshlink_errno = MESHLINK_ENOMEM;
+       }
+
+       pthread_mutex_unlock(&(mesh->mesh_mutex));
+
+       return result;
 }
 
 static bool channel_pre_accept(struct utcp *utcp, uint16_t port) {
@@ -1458,3 +1991,12 @@ static void __attribute__((constructor)) meshlink_init(void) {
 static void __attribute__((destructor)) meshlink_exit(void) {
        crypto_exit();
 }
+
+
+/// Device class traits
+dev_class_traits_t dev_class_traits[_DEV_CLASS_MAX +1] = {
+       { .min_connects = 3, .max_connects = 10000, .edge_weight = 1 }, // DEV_CLASS_BACKBONE
+       { .min_connects = 3, .max_connects = 100, .edge_weight = 3 },   // DEV_CLASS_STATIONARY
+       { .min_connects = 3, .max_connects = 3, .edge_weight = 6 },             // DEV_CLASS_PORTABLE
+       { .min_connects = 1, .max_connects = 1, .edge_weight = 9 },             // DEV_CLASS_UNKNOWN
+};
index b54e961e5c9adab0bf6b144e2a527ad34f558511..3d9290738791fb2a507ec0843b842965bceca455 100644 (file)
 #include <stddef.h>
 #include <unistd.h>
 
+#if defined(_WIN32)
+#include <Winsock2.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#endif
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 /// The length in bytes of a signature made with meshlink_sign()
-#define MESHLINK_SIGLEN 64
+#define MESHLINK_SIGLEN  (64)
+
+// The maximum length of fingerprints
+#define MESHLINK_FINGERPRINTLEN  (64)
 
 /// A handle for an instance of MeshLink.
 typedef struct meshlink_handle meshlink_handle_t;
@@ -38,26 +48,57 @@ typedef struct meshlink_handle meshlink_handle_t;
 /// A handle for a MeshLink node.
 typedef struct meshlink_node meshlink_node_t;
 
+/// A handle for a MeshLink edge.
+typedef struct meshlink_edge meshlink_edge_t;
+
 /// A handle for a MeshLink channel.
 typedef struct meshlink_channel meshlink_channel_t;
 
 /// Code of most recent error encountered.
 typedef enum {
        MESHLINK_OK,     ///< Everything is fine
+       MESHLINK_EINVAL, ///< Invalid parameter(s) to function call
        MESHLINK_ENOMEM, ///< Out of memory
        MESHLINK_ENOENT, ///< Node is not known
+       MESHLINK_EEXIST, ///< Node already exists
+       MESHLINK_EINTERNAL, ///< MeshLink internal error
+       MESHLINK_ERESOLV, ///< MeshLink could not resolve a hostname
+       MESHLINK_ESTORAGE, ///< MeshLink coud not load or write data from/to disk
+       MESHLINK_ENETWORK, ///< MeshLink encountered a network error
+       MESHLINK_EPEER, ///< A peer caused an error
 } meshlink_errno_t;
 
+/// Device class
+typedef enum {
+       DEV_CLASS_BACKBONE = 0,
+       DEV_CLASS_STATIONARY = 1,
+       DEV_CLASS_PORTABLE = 2,
+       DEV_CLASS_UNKNOWN = 3,
+       _DEV_CLASS_MAX = 3
+} dev_class_t;
+
+/// A variable holding the last encountered error from MeshLink.
+/** This is a thread local variable that contains the error code of the most recent error
+ *  encountered by a MeshLink API function called in the current thread.
+ *  The variable is only updated when an error is encountered, and is not reset to MESHLINK_OK
+ *  if a function returned succesfully.
+ */
+extern __thread meshlink_errno_t meshlink_errno;
+
 #ifndef MESHLINK_INTERNAL_H
 
 struct meshlink_handle {
-       meshlink_errno_t meshlink_errno; ///< Code of the last encountered error.
-       const char *errstr;              ///< Textual representation of most recent error encountered.
+       char *name;
+       char *appname;
+       dev_class_t devclass;
+       void *priv;
 };
 
 struct meshlink_node {
-       const char *name; ///< Textual name of this node.
-       void *priv;       ///< Private pointer which the application can set at will.
+       char *name;       ///< Textual name of this node. It is stored in a nul-terminated C string, which is allocated by MeshLink.
+       uint32_t options;
+       dev_class_t devclass;
+       void *priv;       ///< Private pointer which may be set freely by the application, and is never used or modified by MeshLink.
 };
 
 struct meshlink_channel {
@@ -65,39 +106,74 @@ struct meshlink_channel {
 
 #endif // MESHLINK_INTERNAL_H
 
+/// An edge in the meshlink network.
+struct meshlink_edge {
+       struct meshlink_node *from;     ///< Pointer to a node. Node memory is
+                                       //   owned by meshlink and should not be
+                                       //   deallocated. Node contents may be
+                                       //   changed by meshlink.
+       struct meshlink_node *to;       ///< Pointer to a node. Node memory is
+                                        //   owned by meshlink and should not be
+                                        //   deallocated. Node contents may be
+                                        //   changed by meshlink.
+       struct sockaddr_storage address;///< The address information associated
+                                       //   with this edge.
+       uint32_t options;               ///< Edge options. @TODO what are edge options?
+       int weight;                     ///< Weight assigned to this edge.
+};
+
 /// Get the text for the given MeshLink error code.
 /** This function returns a pointer to the string containing the description of the given error code.
  *
- *  @param errno    An error code returned by MeshLink.
+ *  @param err      An error code returned by MeshLink.
  *
  *  @return         A pointer to a string containing the description of the error code.
+ *                  The pointer is to static storage that is valid for the lifetime of the application.
+ *                  This function will always return a valid pointer, even if an invalid error code has been passed.
  */
-extern const char *meshlink_strerror(meshlink_errno_t errno);
+extern const char *meshlink_strerror(meshlink_errno_t err);
 
-/// Initialize MeshLink's configuration directory.
-/** This function causes MeshLink to initialize its configuration directory,
- *  if it hasn't already been initialized.
- *  It only has to be run the first time the application starts,
- *  but it is not a problem if it is run more than once, as long as
- *  the arguments given are the same.
+/// Open or create a MeshLink instance.
+/** 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.
  *
- *  @return         This function will return true if MeshLink has succesfully set up its configuration files, false otherwise.
+ *  @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(const char *confbase, const char *name);
+extern meshlink_handle_t *meshlink_open(const char *confbase, const char *name, const char* appname, dev_class_t devclass);
+
+/// is used by the C++ wrapper to allocate more memory behind the handle
+extern meshlink_handle_t *meshlink_open_with_size(const char *confbase, const char *name, const char* appname, dev_class_t devclass, size_t size);
 
 /// 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.
  *
+ *  It is allowed to call this function even if MeshLink is already started, in which case it will return true.
+ *
  *  @param mesh     A handle which represents an instance of MeshLink.
  *
- *  @return         This function will return true if MeshLink has succesfully started its thread, false otherwise.
+ *  @return         This function will return true if MeshLink has succesfully started, false otherwise.
  */
 extern bool meshlink_start(meshlink_handle_t *mesh);
 
@@ -105,14 +181,20 @@ extern bool meshlink_start(meshlink_handle_t *mesh);
 /** This function causes MeshLink to disconnect from all other nodes,
  *  close all sockets, and shut down its own thread.
  *
+ *  This function always succeeds. It is allowed to call meshlink_stop() even if MeshLink is already stopped or has never been started.
+ *
  *  @param mesh     A handle which represents an instance of MeshLink.
  */
 extern void meshlink_stop(meshlink_handle_t *mesh);
 
 /// Close the MeshLink handle.
 /** This function calls meshlink_stop() if necessary,
- *  and frees all memory allocated by MeshLink.
- *  Afterwards, the handle and any pointers to a struct meshlink_node are invalid.
+ *  and frees the struct meshlink_handle and all associacted memory allocated by MeshLink.
+ *  Afterwards, the handle and any pointers to a struct meshlink_node or struct meshlink_channel are invalid.
+ *
+ *  It is allowed to call this function at any time on a valid handle, except inside callback functions.
+ *  If called at a proper time with a valid handle, this function always succeeds.
+ *  If called within a callback or with an invalid handle, the result is undefined.
  *
  *  @param mesh     A handle which represents an instance of MeshLink.
  */
@@ -121,8 +203,10 @@ extern void meshlink_close(meshlink_handle_t *mesh);
 /// A callback for receiving data from the mesh.
 /** @param mesh      A handle which represents an instance of MeshLink.
  *  @param source    A pointer to a meshlink_node_t describing the source of the data.
- *  @param data      A pointer to a buffer containing the data sent by the source.
- *  @param len       The length of the received data.
+ *  @param data      A pointer to a buffer containing the data sent by the source, or NULL in case there is no data (an empty packet was received).
+ *                   The pointer is only valid during the lifetime of the callback.
+ *                   The callback should mempcy() the data if it needs to be available outside the callback.
+ *  @param len       The length of the received data, or 0 in case there is no data.
  */
 typedef void (*meshlink_receive_cb_t)(meshlink_handle_t *mesh, meshlink_node_t *source, const void *data, size_t len);
 
@@ -135,12 +219,14 @@ typedef void (*meshlink_receive_cb_t)(meshlink_handle_t *mesh, meshlink_node_t *
  *
  *  @param mesh      A handle which represents an instance of MeshLink.
  *  @param cb        A pointer to the function which will be called when another node sends data to the local node.
+ *                   If a NULL pointer is given, the callback will be disabled.
  */
 extern void meshlink_set_receive_cb(meshlink_handle_t *mesh, meshlink_receive_cb_t cb);
 
 /// A callback reporting node status changes.
 /** @param mesh       A handle which represents an instance of MeshLink.
  *  @param node       A pointer to a meshlink_node_t describing the node whose status changed.
+ *                    This pointer is valid until meshlink_close() is called.
  *  @param reachable  True if the node is reachable, false otherwise.
  */
 typedef void (*meshlink_node_status_cb_t)(meshlink_handle_t *mesh, meshlink_node_t *node, bool reachable);
@@ -154,6 +240,7 @@ typedef void (*meshlink_node_status_cb_t)(meshlink_handle_t *mesh, meshlink_node
  *
  *  @param mesh      A handle which represents an instance of MeshLink.
  *  @param cb        A pointer to the function which will be called when another node's status changes.
+ *                   If a NULL pointer is given, the callback will be disabled.
  */
 extern void meshlink_set_node_status_cb(meshlink_handle_t *mesh, meshlink_node_status_cb_t cb);
 
@@ -167,22 +254,35 @@ typedef enum {
 } meshlink_log_level_t;
 
 /// A callback for receiving log messages generated by MeshLink.
-/** @param mesh      A handle which represents an instance of MeshLink.
+/** @param mesh      A handle which represents an instance of MeshLink, or NULL.
  *  @param level     An enum describing the severity level of the message.
- *  @param text      A pointer to a string containing the textual log message.
+ *  @param text      A pointer to a nul-terminated C string containing the textual log message.
+ *                   This pointer is only valid for the duration of the callback.
+ *                   The application must not free() this pointer.
+ *                   The application should strdup() the text if it has to be available outside the callback.
  */
 typedef void (*meshlink_log_cb_t)(meshlink_handle_t *mesh, meshlink_log_level_t level, const char *text);
 
 /// Set the log callback.
 /** This functions sets the callback that is called whenever MeshLink has some information to log.
- *  The callback is run in MeshLink's own thread.
+ *
+ *  The @a mesh @a parameter can either be a valid MeshLink handle, or NULL.
+ *  In case it is NULL, the callback will be called for errors that happen outside the context of a valid mesh instance.
+ *  Otherwise, it will be called for errors that happen in the context of the given mesh instance.
+ *
+ *  If @a mesh @a is not NULL, then the callback is run in MeshLink's own thread.
  *  It is important that the callback uses apprioriate methods (queues, pipes, locking, etc.)
  *  to hand the data over to the application's thread.
  *  The callback should also not block itself and return as quickly as possible.
  *
- *  @param mesh      A handle which represents an instance of MeshLink.
+ *  The @a mesh @a parameter can either be a valid MeshLink handle, or NULL.
+ *  In case it is NULL, the callback will be called for errors that happen outside the context of a valid mesh instance.
+ *  Otherwise, it will be called for errors that happen in the context of the given mesh instance.
+ *
+ *  @param mesh      A handle which represents an instance of MeshLink, or NULL.
  *  @param level     An enum describing the minimum severity level. Debugging information with a lower level will not trigger the callback.
  *  @param cb        A pointer to the function which will be called when another node sends data to the local node.
+ *                   If a NULL pointer is given, the callback will be disabled.
  */
 extern void meshlink_set_log_cb(meshlink_handle_t *mesh, meshlink_log_level_t level, meshlink_log_cb_t cb);
 
@@ -191,38 +291,79 @@ extern void meshlink_set_log_cb(meshlink_handle_t *mesh, meshlink_log_level_t le
  *  The packet is sent using UDP semantics, which means that
  *  the packet is sent as one unit and is received as one unit,
  *  and that there is no guarantee that the packet will arrive at the destination.
+ *  Packets that are too big to be sent over the network as one unit might be dropped, and this function may return an error if this situation can be detected beforehand.
+ *  The application should not send packets that are larger than the path MTU, which can be queried with meshlink_get_pmtu().
  *  The application should take care of getting an acknowledgement and retransmission if necessary.
  *
  *  @param mesh         A handle which represents an instance of MeshLink.
  *  @param destination  A pointer to a meshlink_node_t describing the destination for the data.
  *  @param data         A pointer to a buffer containing the data to be sent to the source.
+ *                      After meshlink_send() returns, the application is free to overwrite or free this buffer.
+ *                      It is valid to specify a NULL pointer, but only if @a len @a is also 0.
  *  @param len          The length of the data.
  *  @return             This function will return true if MeshLink has queued the message for transmission, and false otherwise.
  *                      A return value of true does not guarantee that the message will actually arrive at the destination.
  */
-extern bool meshlink_send(meshlink_handle_t *mesh, meshlink_node_t *destination, const void *data, unsigned int len);
+extern bool meshlink_send(meshlink_handle_t *mesh, meshlink_node_t *destination, const void *data, size_t len);
+
+/// Query the maximum packet size that can be sent to a node.
+/** This functions returns the maximum size of packets (path MTU) that can be sent to a specific node with meshlink_send().
+ *  The path MTU is a property of the path packets will take to the destination node over the Internet.
+ *  It can be different for different destination nodes.
+ *  and the path MTU can change at any point in time due to changes in the Internet.
+ *  Therefore, although this should only occur rarely, it can still happen that packets that do not exceed this size get dropped.
+ *
+ *  @param mesh         A handle which represents an instance of MeshLink.
+ *  @param destination  A pointer to a meshlink_node_t describing the destination for the data.
+ *
+ *  @return             The recommended maximum size of packets that are to be sent to the destination node, 0 if the node is unreachable,
+ *                      or a negative value in case of an error.
+ */
+extern ssize_t meshlink_get_pmtu(meshlink_handle_t *mesh, meshlink_node_t *destination);
 
 /// Get a handle for a specific node.
 /** This function returns a handle for the node with the given name.
  *
  *  @param mesh         A handle which represents an instance of MeshLink.
  *  @param name         The name of the node for which a handle is requested.
+ *                      After this function returns, the application is free to overwrite or free @a name @a.
  *
  *  @return             A pointer to a meshlink_node_t which represents the requested node,
  *                      or NULL if the requested node does not exist.
+ *                      The pointer is guaranteed to be valid until meshlink_close() is called.
  */
 extern meshlink_node_t *meshlink_get_node(meshlink_handle_t *mesh, const char *name);
 
+/// Get the fingerprint of a node's public key.
+/** This function returns a fingerprint of the node's public key.
+ *  It should be treated as an opaque blob.
+ *
+ *  @param mesh         A handle which represents an instance of MeshLink.
+ *  @param node         A pointer to a meshlink_node_t describing the node.
+ *
+ *  @return            A nul-terminated C string containing the fingerprint of the node's public key in a printable ASCII format.
+ *                      The application should call free() after it is done using this string.
+ */
+extern char *meshlink_get_fingerprint(meshlink_handle_t *mesh, meshlink_node_t *node);
+
 /// Get a list of all nodes.
 /** This function returns a list with handles for all known nodes.
  *
  *  @param mesh         A handle which represents an instance of MeshLink.
- *  @param nodes        A pointer to an array of pointers to meshlink_node_t, which should be allocated by the application.
- *  @param nmemb        The maximum number of pointers that can be stored in the nodes array.
- *
- *  @return             The number of known nodes. This can be larger than nmemb, in which case not all nodes were stored in the nodes array.
+ *  @param nodes        A pointer to a previously allocated array of pointers to meshlink_node_t, or NULL in which case MeshLink will allocate a new array.
+ *                      The application can supply an array it allocated itself with malloc, or the return value from the previous call to this function (which is the preferred way).
+ *                      The application is allowed to call free() on the array whenever it wishes.
+ *                      The pointers in the array are valid until meshlink_close() is called.
+ *  @param nmemb        A pointer to a variable holding the number of nodes that are stored in the array.
+ *                      In case the @a nodes @a argument is not NULL, MeshLink might call realloc() on the array to change its size.
+ *                      The contents of this variable will be changed to reflect the new size of the array.
+ *
+ *  @return             A pointer to an array containing pointers to all known nodes, or NULL in case of an error.
+ *                      If the @a nodes @a argument was not NULL, then the return value can either be the same value or a different value.
+ *                      If it is a new value, the old value of @a nodes @a should not be used anymore.
+ *                      If the new value is NULL, then the old array will have been freed by MeshLink.
  */
-extern size_t meshlink_get_all_nodes(meshlink_handle_t *mesh, meshlink_node_t **nodes, size_t nmemb);
+extern meshlink_node_t **meshlink_get_all_nodes(meshlink_handle_t *mesh, meshlink_node_t **nodes, size_t *nmemb);
 
 /// Sign data using the local node's MeshLink key.
 /** This function signs data using the local node's MeshLink key.
@@ -232,6 +373,8 @@ extern size_t meshlink_get_all_nodes(meshlink_handle_t *mesh, meshlink_node_t **
  *  @param data         A pointer to a buffer containing the data to be signed.
  *  @param len          The length of the data to be signed.
  *  @param signature    A pointer to a buffer where the signature will be stored.
+ *                      The buffer must be allocated by the application, and should be at least MESHLINK_SIGLEN bytes big.
+ *                      The signature is a binary blob, and is not nul-terminated.
  *  @param siglen       The size of the signature buffer. Will be changed after the call to match the size of the signature itself.
  *
  *  @return             This function returns true if the signature was correctly generated, false otherwise.
@@ -245,8 +388,9 @@ extern bool meshlink_sign(meshlink_handle_t *mesh, const void *data, size_t len,
  *  @param source       A pointer to a meshlink_node_t describing the source of the signature.
  *  @param data         A pointer to a buffer containing the data to be verified.
  *  @param len          The length of the data to be verified.
- *  @param signature    A pointer to a string containing the signature.
- *  @param siglen       The size of the signature.
+ *  @param signature    A pointer to a buffer where the signature is stored.
+ *  @param siglen       A pointer to a variable holding the size of the signature buffer.
+ *                      The contents of the variable will be changed by meshlink_sign() to reflect the actual size of the signature.
  *
  *  @return             This function returns true if the signature is valid, false otherwise.
  */
@@ -256,7 +400,7 @@ extern bool meshlink_verify(meshlink_handle_t *mesh, meshlink_node_t *source, co
 /** This function adds an Address for the local node, which will be used for invitation URLs.
  *
  *  @param mesh         A handle which represents an instance of MeshLink.
- *  @param address      A string containing the address, which can be either in numeric format or a hostname.
+ *  @param address      A nul-terminated C string containing the address, which can be either in numeric format or a hostname.
  *
  *  @return             This function returns true if the address was added, false otherwise.
  */
@@ -269,9 +413,10 @@ extern bool meshlink_add_address(meshlink_handle_t *mesh, const char *address);
  *  The URL can only be used once, after the user has joined the mesh the URL is no longer valid.
  *
  *  @param mesh         A handle which represents an instance of MeshLink.
- *  @param name         The name that the invitee will use in the mesh.
+ *  @param name         A nul-terminated C string containing the name that the invitee will be allowed to use in the mesh.
+ *                      After this function returns, the application is free to overwrite or free @a name @a.
  *
- *  @return             This function returns a string that contains the invitation URL.
+ *  @return             This function returns a nul-terminated C string that contains the invitation URL, or NULL in case of an error.
  *                      The application should call free() after it has finished using the URL.
  */
 extern char *meshlink_invite(meshlink_handle_t *mesh, const char *name);
@@ -282,7 +427,8 @@ extern char *meshlink_invite(meshlink_handle_t *mesh, const char *name);
  *  After a succesfully accepted invitation, the name of the local node may have changed.
  *
  *  @param mesh         A handle which represents an instance of MeshLink.
- *  @param invitation   A string containing the invitation URL.
+ *  @param invitation   A nul-terminated C string containing the invitation URL.
+ *                      After this function returns, the application is free to overwrite or free @a invitation @a.
  *
  *  @return             This function returns true if the local node joined the mesh it was invited to, false otherwise.
  */
@@ -292,10 +438,13 @@ extern bool meshlink_join(meshlink_handle_t *mesh, const char *invitation);
 /** This function generates a string that contains the local node's public key and one or more IP addresses.
  *  The application can pass it in some way to another node, which can then import it,
  *  granting the local node access to the other node's mesh.
+ *  The exported data does not contain any secret keys, it is therefore safe to transmit this data unencrypted over public networks.
+ *
+ *  Note that to create a working connection between two nodes, both must call meshink_export() and both must meshlink_import() each other's data.
  *
  *  @param mesh         A handle which represents an instance of MeshLink.
  *
- *  @return             This function returns a string that contains the exported key and addresses.
+ *  @return             This function returns a nul-terminated C string that contains the exported key and addresses, or NULL in case of an error.
  *                      The application should call free() after it has finished using this string.
  */
 extern char *meshlink_export(meshlink_handle_t *mesh);
@@ -303,9 +452,13 @@ extern char *meshlink_export(meshlink_handle_t *mesh);
 /// Import another node's key and addresses.
 /** This function accepts a string containing the exported public key and addresses of another node.
  *  By importing this data, the local node grants the other node access to its mesh.
+ *  The application should make sure that the data it imports is really coming from the node it wants to import,
+ *
+ *  Note that to create a working connection between two nodes, both must call meshink_export() and both must meshlink_import() each other's data.
  *
  *  @param mesh         A handle which represents an instance of MeshLink.
- *  @param data         A string containing the other node's exported key and addresses.
+ *  @param data         A nul-terminated C string containing the other node's exported key and addresses.
+ *                      After this function returns, the application is free to overwrite or free @a data @a.
  *
  *  @return             This function returns true if the data was valid and the other node has been granted access to the mesh, false otherwise.
  */
@@ -321,16 +474,38 @@ extern bool meshlink_import(meshlink_handle_t *mesh, const char *data);
  */
 extern void meshlink_blacklist(meshlink_handle_t *mesh, meshlink_node_t *node);
 
+/// Whitelist a node on the mesh.
+/** This function causes the local node to whitelist a previously blacklisted node.
+ *  The local node will allow connections to and from that node,
+ *  and will send data to it and accept any data received from it.
+ *
+ *  @param mesh         A handle which represents an instance of MeshLink.
+ *  @param node         A pointer to a meshlink_node_t describing the node to be blacklisted.
+ */
+extern void meshlink_blacklist(meshlink_handle_t *mesh, meshlink_node_t *node);
+
 /// A callback for accepting incoming channels.
 /** This function is called whenever a remote node wants to open a channel to the local node.
  *  The application then has to decide whether to accept or reject this channel.
  *
+ *  The callback is run in MeshLink's own thread.
+ *  It is therefore important that the callback return quickly and uses apprioriate methods (queues, pipes, locking, etc.)
+ *  to hand any data over to the application's thread.
+ *
  *  @param mesh         A handle which represents an instance of MeshLink.
  *  @param channel      A handle for the incoming channel.
+ *                      If the application accepts the incoming channel by returning true,
+ *                      then this handle is valid until meshlink_channel_close() is called on it.
+ *                      If the application rejects the incoming channel by returning false,
+ *                      then this handle is invalid after the callback returns
+ *                      (the callback does not need to call meshlink_channel_close() itself in this case).
  *  @param node         The node from which this channel is being initiated.
+ *                      The pointer is guaranteed to be valid until meshlink_close() is called.
  *  @param port         The port number the peer wishes to connect to.
- *  @param data         A pointer to a buffer containing data already received. (Not yet used.)
- *  @param len          The length of the data. (Not yet used.)
+ *  @param data         A pointer to a buffer containing data already received, or NULL in case no data has been received yet. (Not yet used.)
+ *                      The pointer is only valid during the lifetime of the callback.
+ *                      The callback should mempcy() the data if it needs to be available outside the callback.
+ *  @param len          The length of the data, or 0 in case no data has been received yet. (Not yet used.)
  *
  *  @return             This function should return true if the application accepts the incoming channel, false otherwise.
  *                      If returning false, the channel is invalid and may not be used anymore.
@@ -338,13 +513,18 @@ extern void meshlink_blacklist(meshlink_handle_t *mesh, meshlink_node_t *node);
 typedef bool (*meshlink_channel_accept_cb_t)(meshlink_handle_t *mesh, meshlink_channel_t *channel, meshlink_node_t *node, uint16_t port, const void *data, size_t len);
 
 /// A callback for receiving data from a channel.
-/** This function is called whenever a remote node wants to open a channel to the local node.
- *  The application then has to decide whether to accept or reject this channel.
+/** This function is called whenever data is received from a remote node on a channel.
+ *
+ *  This function is also called in case the channel has been closed by the remote node, or when the channel is terminated abnormally.
+ *  In both cases, @a data @a will be NULL and @a len @a will be 0, and meshlink_errno will be set.
+ *  In any case, the @a channel @a handle will still be valid until the application calls meshlink_close().
  *
  *  @param mesh         A handle which represents an instance of MeshLink.
  *  @param channel      A handle for the channel.
- *  @param data         A pointer to a buffer containing data sent by the source.
- *  @param len          The length of the data.
+ *  @param data         A pointer to a buffer containing data sent by the source, or NULL in case of an error.
+ *                      The pointer is only valid during the lifetime of the callback.
+ *                      The callback should mempcy() the data if it needs to be available outside the callback.
+ *  @param len          The length of the data, or 0 in case of an error.
  */
 typedef void (*meshlink_channel_receive_cb_t)(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *data, size_t len);
 
@@ -355,8 +535,11 @@ typedef void (*meshlink_channel_receive_cb_t)(meshlink_handle_t *mesh, meshlink_
  *  to hand the data over to the application's thread.
  *  The callback should also not block itself and return as quickly as possible.
  *
+ *  If no accept callback is set, incoming channels are rejected.
+ *
  *  @param mesh      A handle which represents an instance of MeshLink.
  *  @param cb        A pointer to the function which will be called when another node sends data to the local node.
+ *                   If a NULL pointer is given, the callback will be disabled.
  */
 extern void meshlink_set_channel_accept_cb(meshlink_handle_t *mesh, meshlink_channel_accept_cb_t cb);
 
@@ -370,6 +553,7 @@ extern void meshlink_set_channel_accept_cb(meshlink_handle_t *mesh, meshlink_cha
  *  @param mesh      A handle which represents an instance of MeshLink.
  *  @param channel   A handle for the channel.
  *  @param cb        A pointer to the function which will be called when another node sends data to the local node.
+ *                   If a NULL pointer is given, the callback will be disabled and incoming data is ignored.
  */
 extern void meshlink_set_channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, meshlink_channel_receive_cb_t cb);
 
@@ -377,14 +561,21 @@ extern void meshlink_set_channel_receive_cb(meshlink_handle_t *mesh, meshlink_ch
 /** This function is called whenever a remote node wants to open a channel to the local node.
  *  The application then has to decide whether to accept or reject this channel.
  *
+ *  This function returns a pointer to a struct meshlink_channel that will be allocated by MeshLink.
+ *  When the application does no longer need to use this channel, it must call meshlink_close()
+ *  to free its resources.
+ *
  *  @param mesh         A handle which represents an instance of MeshLink.
  *  @param node         The node to which this channel is being initiated.
  *  @param port         The port number the peer wishes to connect to.
  *  @param cb           A pointer to the function which will be called when the remote node sends data to the local node.
- *  @param data         A pointer to a buffer containing data to already queue for sending.
- *  @param len          The length of the data.
+ *                      The pointer may be NULL, in which case incoming data is ignored.
+ *  @param data         A pointer to a buffer containing data to already queue for sending, or NULL if there is no data to send.
+ *                      After meshlink_send() returns, the application is free to overwrite or free this buffer.
+ *  @param len          The length of the data, or 0 if there is no data to send.
  *
  *  @return             A handle for the channel, or NULL in case of an error.
+ *                      The handle is valid until meshlink_channel_close() is called.
  */
 extern meshlink_channel_t *meshlink_channel_open(meshlink_handle_t *mesh, meshlink_node_t *node, uint16_t port, meshlink_channel_receive_cb_t cb, const void *data, size_t len);
 
@@ -393,17 +584,23 @@ extern meshlink_channel_t *meshlink_channel_open(meshlink_handle_t *mesh, meshli
  *  It can be used to inform the remote node that the local node has finished sending all data on the channel,
  *  but still allows waiting for incoming data from the remote node.
  *
+ *  Shutting down the receive direction is also possible, and is equivalent to setting the receive callback to NULL.
+ *
  *  @param mesh         A handle which represents an instance of MeshLink.
  *  @param channel      A handle for the channel.
- *  @param direction    Must be one of SHUT_RD, SHUT_WR or SHUT_RDWR.
+ *  @param direction    Must be one of SHUT_RD, SHUT_WR or SHUT_RDWR, otherwise this call will not have any affect.
  */
 extern void meshlink_channel_shutdown(meshlink_handle_t *mesh, meshlink_channel_t *channel, int direction);
 
 /// Close a reliable stream channel.
 /** This informs the remote node that the local node has finished sending all data on the channel.
  *  It also causes the local node to stop accepting incoming data from the remote node.
+ *  It will free the struct meshlink_channel and all associated resources.
  *  Afterwards, the channel handle is invalid and must not be used any more.
  *
+ *  It is allowed to call this function at any time on a valid handle, even inside callback functions.
+ *  If called with a valid handle, this function always succeeds, otherwise the result is undefined.
+ *
  *  @param mesh         A handle which represents an instance of MeshLink.
  *  @param channel      A handle for the channel.
  */
@@ -414,13 +611,65 @@ extern void meshlink_channel_close(meshlink_handle_t *mesh, meshlink_channel_t *
  *
  *  @param mesh         A handle which represents an instance of MeshLink.
  *  @param channel      A handle for the channel.
- *  @param data         A pointer to a buffer containing data sent by the source.
- *  @param len          The length of the data.
+ *  @param data         A pointer to a buffer containing data sent by the source, or NULL if there is no data to send.
+ *                      After meshlink_send() returns, the application is free to overwrite or free this buffer.
+ *  @param len          The length of the data, or 0 if there is no data to send.
  *
  *  @return             The amount of data that was queued, which can be less than len, or a negative value in case of an error.
  */
 extern ssize_t meshlink_channel_send(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *data, size_t len);
 
+/// Hint that a hostname may be found at an address
+/** This function indicates to meshlink that the given hostname is likely found
+ *  at the given IP address and port.
+ *
+ *  @param mesh                A handle which represents an instance of MeshLink.
+ *  @param hostname    The hostname which can be found at the given address.
+ *                     The caller is free to overwrite or free this string
+ *                     once meshlink returns.
+ *  @param addr                The IP address and port which should be tried for the
+ *                     given hostname. The caller is free to overwrite or free
+ *                     this memory once meshlink returns.
+ */
+extern void meshlink_hint_address(meshlink_handle_t *mesh, meshlink_node_t *node, const struct sockaddr *addr);
+
+/// Get a list of edges.
+/** This function returns an array with copies of all known bidirectional edges.
+ *  The edges are copied to capture the mesh state at call time, since edges
+ *  mutate frequently. The nodes pointed to within the meshlink_edge_t type
+ *  are not copies; these are the same pointers that one would get from a call
+ *  to meshlink_get_all_nodes().
+ *
+ *  @param mesh         A handle which represents an instance of MeshLink.
+ *  @param edges        A pointer to a previously allocated array of pointers to
+ *                      meshlink_edge_t, or NULL in which case MeshLink will
+ *                      allocate a new array. The application CANNOT supply an
+ *                      array it allocated itself with malloc, but CAN use
+ *                      the return value from the previous call to this function
+ *                      (which is the preferred way).
+ *                      The pointers in the array are valid until meshlink_close() is called.
+ *                      ATTENTION: The pointers and values should never be modified
+ *                      by the application!!!
+ *  @param nmemb        A pointer to a variable holding the number of nodes that
+ *                      are stored in the array. In case the @a nodes @a
+ *                      argument is not NULL, MeshLink might call realloc()
+ *                      on the array to change its size.
+ *                      The contents of this variable will be changed to reflect
+ *                      the new size of the array.
+ *  @return             A pointer to an array containing pointers to all known 
+ *                      edges, or NULL in case of an error.
+ *                      If the @a edges @a argument was not NULL, then the
+ *                      retun value can be either the same value or a different
+ *                      value. If the new values is NULL, then the old array
+ *                      will have been freed by Meshlink.
+ *                      The caller must call free() on each element of this
+ *                      array (but not the contents of said elements),
+ *                      as well as the array itself when it is finished.
+ *                      ATTENTION: The pointers and values should never be modified
+ *                      by the application!!!
+ */
+extern meshlink_edge_t **meshlink_get_all_edges_state(meshlink_handle_t *mesh, meshlink_edge_t **edges, size_t *nmemb);
+
 #ifdef __cplusplus
 }
 #endif
index 0e035920c08345c98fd72db4c6142ee177b21cc5..517dc1e01cb8dbee31824f040f7c0a6140015f9e 100644 (file)
@@ -24,8 +24,8 @@
 
 #include "event.h"
 #include "hash.h"
-#include "logger.h"
 #include "meshlink.h"
+#include "meshlink_queue.h"
 #include "sockaddr.h"
 #include "sptps.h"
 
 
 #define MAXSOCKETS 8    /* Probably overkill... */
 
+struct AvahiServer;
+struct AvahiSServiceBrowser;
+struct AvahiSimplePoll;
+struct AvahiSEntryGroup;
+
 typedef struct listen_socket_t {
        struct io_t tcp;
        struct io_t udp;
@@ -57,8 +62,12 @@ typedef struct outpacketqueue {
 
 /// A handle for an instance of MeshLink.
 struct meshlink_handle {
-       char *confbase;
        char *name;
+       char *appname;
+       dev_class_t devclass;
+       void *priv;
+
+       char *confbase;
 
        meshlink_receive_cb_t receive_cb;
        meshlink_node_status_cb_t node_status_cb;
@@ -70,7 +79,7 @@ struct meshlink_handle {
        pthread_t thread;
        bool threadstarted;
        pthread_mutex_t outpacketqueue_mutex;
-       pthread_mutex_t nodes_mutex;
+       pthread_mutex_t mesh_mutex;
        event_loop_t loop;
        listen_socket_t listen_socket[MAXSOCKETS];
        int listen_sockets;
@@ -85,7 +94,7 @@ struct meshlink_handle {
        struct list_t *connections;
        struct list_t *outgoings;
 
-       struct list_t *outpacketqueue;
+       meshlink_queue_t outpacketqueue;
 
        struct splay_tree_t *past_request_tree;
        timeout_t past_request_timeout;
@@ -112,7 +121,6 @@ struct meshlink_handle {
        struct connection_t *everyone;
        struct ecdsa *invitation_key;
 
-       debug_t debug_level;
        int pinginterval;       /* seconds between pings */
        int pingtimeout;        /* seconds to wait for response */
        int maxtimeout;
@@ -126,6 +134,14 @@ struct meshlink_handle {
        char line[4096];
        char buffer[4096];
        size_t blen;
+
+       pthread_t discovery_thread;
+       bool discovery_threadstarted;
+       struct AvahiServer *avahi_server;
+       struct AvahiSServiceBrowser *avahi_browser;
+       struct AvahiSimplePoll *avahi_poll;
+       struct AvahiSEntryGroup *avahi_group;
+       char* avahi_servicetype;
 };
 
 /// A handle for a MeshLink node.
@@ -148,6 +164,16 @@ typedef struct meshlink_packethdr {
 } __attribute__ ((__packed__)) meshlink_packethdr_t;
 
 extern void meshlink_send_from_queue(event_loop_t* el,meshlink_handle_t *mesh);
+extern meshlink_log_level_t global_log_level;
+extern meshlink_log_cb_t global_log_cb;
+
+/// Device class traits
+typedef struct {
+       unsigned int min_connects;
+       unsigned int max_connects;
+       int edge_weight;
+} dev_class_traits_t;
 
+extern dev_class_traits_t dev_class_traits[];
 
 #endif // MESHLINK_INTERNAL_H
diff --git a/src/meshlink_queue.h b/src/meshlink_queue.h
new file mode 100644 (file)
index 0000000..05b58d9
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+    queue.h -- Thread-safe queue
+    Copyright (C) 2014 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
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef MESHLINK_QUEUE_H
+#define MESHLINK_QUEUE_H
+
+#include <pthread.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <unistd.h>
+
+typedef struct meshlink_queue {
+       struct meshlink_queue_item *head;       
+       struct meshlink_queue_item *tail;       
+       pthread_mutex_t mutex;
+} meshlink_queue_t;
+
+typedef struct meshlink_queue_item {
+       void *data;
+       struct meshlink_queue_item *next;
+} meshlink_queue_item_t;
+
+static inline bool meshlink_queue_push(meshlink_queue_t *queue, void *data) {
+       meshlink_queue_item_t *item = malloc(sizeof *item);
+       if(!item)
+               return false;
+       item->data = data;
+       item->next = NULL;
+       pthread_mutex_lock(&queue->mutex);
+       if(!queue->tail)
+               queue->head = queue->tail = item;
+       else
+               queue->tail = queue->tail->next = item;
+       pthread_mutex_unlock(&queue->mutex);
+       return true;
+}
+
+static inline void *meshlink_queue_pop(meshlink_queue_t *queue) {
+       meshlink_queue_item_t *item;
+       void *data;
+       pthread_mutex_lock(&queue->mutex);
+       if((item = queue->head)) {
+               queue->head = item->next;
+               if(!queue->head)
+                       queue->tail = NULL;
+       }
+       pthread_mutex_unlock(&queue->mutex);
+       data = item ? item->data : NULL;
+       free(item);
+       return data;
+}
+
+#endif
index c402919b6bfd999b8a5f3d6833f37fae96041a4b..97cbed2a316959e6b3e697320be3ebcea62a37ad 100644 (file)
@@ -33,7 +33,7 @@ bool send_meta_sptps(void *handle, uint8_t type, const void *buffer, size_t leng
        meshlink_handle_t *mesh = c->mesh;
 
        if(!c) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "send_meta_sptps() called with NULL pointer!");
+               logger(mesh, MESHLINK_ERROR, "send_meta_sptps() called with NULL pointer!");
                abort();
        }
 
@@ -45,11 +45,11 @@ bool send_meta_sptps(void *handle, uint8_t type, const void *buffer, size_t leng
 
 bool send_meta(meshlink_handle_t *mesh, connection_t *c, const char *buffer, int length) {
        if(!c) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "send_meta() called with NULL pointer!");
+               logger(mesh, MESHLINK_ERROR, "send_meta() called with NULL pointer!");
                abort();
        }
 
-       logger(DEBUG_META, LOG_DEBUG, "Sending %d bytes of metadata to %s (%s)", length,
+       logger(mesh, MESHLINK_DEBUG, "Sending %d bytes of metadata to %s (%s)", length,
                           c->name, c->hostname);
 
        if(c->allow_request == ID) {
@@ -73,7 +73,7 @@ bool receive_meta_sptps(void *handle, uint8_t type, const void *data, uint16_t l
        char *request = (char *)data;
 
        if(!c) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "receive_meta_sptps() called with NULL pointer!");
+               logger(mesh, MESHLINK_ERROR, "receive_meta_sptps() called with NULL pointer!");
                abort();
        }
 
@@ -124,7 +124,7 @@ bool receive_meta(meshlink_handle_t *mesh, connection_t *c) {
        buffer_compact(&c->inbuf, MAXBUFSIZE);
 
        if(sizeof inbuf <= c->inbuf.len) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Input buffer full for %s (%s)", c->name, c->hostname);
+               logger(mesh, MESHLINK_ERROR, "Input buffer full for %s (%s)", c->name, c->hostname);
                return false;
        }
 
@@ -132,12 +132,12 @@ bool receive_meta(meshlink_handle_t *mesh, connection_t *c) {
 
        if(inlen <= 0) {
                if(!inlen || !errno) {
-                       logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Connection closed by %s (%s)",
+                       logger(mesh, MESHLINK_INFO, "Connection closed by %s (%s)",
                                           c->name, c->hostname);
                } else if(sockwouldblock(sockerrno))
                        return true;
                else
-                       logger(DEBUG_ALWAYS, LOG_ERR, "Metadata socket read error for %s (%s): %s",
+                       logger(mesh, MESHLINK_ERROR, "Metadata socket read error for %s (%s): %s",
                                   c->name, c->hostname, sockstrerror(sockerrno));
                return false;
        }
index 5f50531b9ff4fada055e90b255ecf9b9f03ea351..8400b936bd4f08d18ed8c5fe44a31e053098ed1c 100644 (file)
--- a/src/net.c
+++ b/src/net.c
 #include "protocol.h"
 #include "xalloc.h"
 
+#include <assert.h>
+
+static const int min(int a, int b) {
+       return a < b ? a : b;
+}
+
 /*
   Terminate a connection:
   - Mark it as inactive
@@ -39,7 +45,7 @@
   - Check if we need to retry making an outgoing connection
 */
 void terminate_connection(meshlink_handle_t *mesh, connection_t *c, bool report) {
-       logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Closing connection with %s (%s)", c->name, c->hostname);
+       logger(mesh, MESHLINK_INFO, "Closing connection with %s (%s)", c->name, c->hostname);
 
        c->status.active = false;
 
@@ -94,12 +100,18 @@ void terminate_connection(meshlink_handle_t *mesh, connection_t *c, bool report)
 */
 static void timeout_handler(event_loop_t *loop, void *data) {
        meshlink_handle_t *mesh = loop->data;
+       logger(mesh, MESHLINK_DEBUG, "timeout_handler()");
 
        for list_each(connection_t, c, mesh->connections) {
+               // Also make sure that if outstanding key requests for the UDP counterpart of a connection has timed out, we restart it.
+               if(c->node) {
+                       if(c->node->status.waitingforkey && c->node->last_req_key + mesh->pingtimeout <= mesh->loop.now.tv_sec)
+                               send_req_key(mesh, c->node);
+               }
                if(c->last_ping_time + mesh->pingtimeout <= mesh->loop.now.tv_sec) {
                        if(c->status.active) {
                                if(c->status.pinged) {
-                                       logger(DEBUG_CONNECTIONS, LOG_INFO, "%s (%s) didn't respond to PING in %ld seconds", c->name, c->hostname, (long)mesh->loop.now.tv_sec - c->last_ping_time);
+                                       logger(mesh, MESHLINK_INFO, "%s (%s) didn't respond to PING in %ld seconds", c->name, c->hostname, (long)mesh->loop.now.tv_sec - c->last_ping_time);
                                } else if(c->last_ping_time + mesh->pinginterval <= mesh->loop.now.tv_sec) {
                                        send_ping(mesh, c);
                                        continue;
@@ -108,9 +120,9 @@ static void timeout_handler(event_loop_t *loop, void *data) {
                                }
                        } else {
                                if(c->status.connecting)
-                                       logger(DEBUG_CONNECTIONS, LOG_WARNING, "Timeout while connecting to %s (%s)", c->name, c->hostname);
+                                       logger(mesh, MESHLINK_WARNING, "Timeout while connecting to %s (%s)", c->name, c->hostname);
                                else
-                                       logger(DEBUG_CONNECTIONS, LOG_WARNING, "Timeout from %s (%s) during authentication", c->name, c->hostname);
+                                       logger(mesh, MESHLINK_WARNING, "Timeout from %s (%s) during authentication", c->name, c->hostname);
                        }
                        terminate_connection(mesh, c, c->status.active);
                }
@@ -119,6 +131,173 @@ static void timeout_handler(event_loop_t *loop, void *data) {
        timeout_set(&mesh->loop, data, &(struct timeval){mesh->pingtimeout, rand() % 100000});
 }
 
+// devclass asc, last_successfull_connection desc
+static int node_compare_devclass_asc_lsc_desc(const void *a, const void *b)
+{
+       const node_t *na = a, *nb = b;
+
+       if(na->devclass < nb->devclass)
+               { return -1; }
+
+       if(na->devclass > nb->devclass)
+               { return 1; }
+
+       if(na->last_successfull_connection == nb->last_successfull_connection)
+               return 0;
+
+       if(na->last_successfull_connection == 0 || na->last_successfull_connection > nb->last_successfull_connection)
+               return -1;
+
+       if(nb->last_successfull_connection == 0 || na->last_successfull_connection < nb->last_successfull_connection)
+               return 1;
+
+       if(na < nb)
+               return -1;
+
+       if(na > nb)
+               return 1;
+
+       return 0;
+}
+
+// last_successfull_connection desc
+static int node_compare_lsc_desc(const void *a, const void *b)
+{
+       const node_t *na = a, *nb = b;
+
+       if(na->last_successfull_connection == nb->last_successfull_connection)
+               return 0;
+
+       if(na->last_successfull_connection == 0 || na->last_successfull_connection > nb->last_successfull_connection)
+               return -1;
+
+       if(nb->last_successfull_connection == 0 || na->last_successfull_connection < nb->last_successfull_connection)
+               return 1;
+
+       if(na < nb)
+               return -1;
+
+       if(na > nb)
+               return 1;
+
+       return 0;
+}
+
+// devclass desc
+static int node_compare_devclass_desc(const void *a, const void *b)
+{
+       const node_t *na = a, *nb = b;
+
+       if(na->devclass < nb->devclass)
+               { return -1; }
+
+       if(na->devclass > nb->devclass)
+               { return 1; }
+
+       if(na < nb)
+               return -1;
+
+       if(na > nb)
+               return 1;
+
+       return 0;
+}
+
+
+/*
+
+autoconnect()
+{
+       timeout = 5
+
+       // find the best one for initial connect
+
+       if cur < min
+               newcon =
+                       first from nodes
+                               where dclass <= my.dclass and !connection and (timestamp - last_retry) > retry_timeout
+                               order by dclass asc, last_connection desc
+               if newcon
+                       timeout = 0
+                       goto connect
+
+
+       // find better nodes to connect to: in case we have less than min connections within [BACKBONE, i] and there are nodes which we are not connected to within the range
+
+       if min <= cur < max
+               j = 0
+               for i = BACKBONE to my.dclass
+                       j += count(from connections where node.dclass = i)
+                       if j < min
+                               newcon =
+                                       first from nodes
+                                               where dclass = i and !connection and (timestamp - last_retry) > retry_timeout
+                                               order by last_connection desc
+                               if newcon
+                                       goto connect
+                       else
+                               break
+
+
+       // heal partitions
+
+       if min <= cur < max
+               newcon =
+                       first from nodes
+                               where dclass <= my.dclass and !reachable and (timestamp - last_retry) > retry_timeout
+                               order by dclass asc, last_connection desc
+               if newcon
+                       goto connect
+
+
+       // connect
+
+connect:
+       if newcon
+               connect newcon
+
+
+       // disconnect outgoing connections in case we have more than min connections within [BACKBONE, i] and there are nodes which we are connected to within the range [i, PORTABLE]
+
+       if min < cur <= max
+               j = 0
+               for i = BACKBONE to my.dclass
+                       j += count(from connections where node.dclass = i)
+                       if min < j
+                               delcon =
+                                       first from nodes
+                                               where dclass >= i and outgoing_connection
+                                               order by dclass desc
+                               if disconnect
+                                       goto disconnect
+                               else
+                                       break
+
+
+       // disconnect connections in case we have more than enough connections
+
+       if max < cur
+               delcon =
+                       first from nodes
+                               where outgoing_connection
+                               order by dclass desc
+               goto disconnect
+
+       // disconnect
+
+disconnect
+       if delcon
+               disconnect delcon
+
+
+       // next iteration
+       next (timeout, autoconnect)
+
+}
+
+*/
+
+
 static void periodic_handler(event_loop_t *loop, void *data) {
        meshlink_handle_t *mesh = loop->data;
 
@@ -128,7 +307,7 @@ static void periodic_handler(event_loop_t *loop, void *data) {
        */
 
        if(mesh->contradicting_del_edge > 100 && mesh->contradicting_add_edge > 100) {
-               logger(DEBUG_ALWAYS, LOG_WARNING, "Possible node with same Name as us! Sleeping %d seconds.", mesh->sleeptime);
+               logger(mesh, MESHLINK_WARNING, "Possible node with same Name as us! Sleeping %d seconds.", mesh->sleeptime);
                usleep(mesh->sleeptime * 1000000LL);
                mesh->sleeptime *= 2;
                if(mesh->sleeptime < 0)
@@ -142,99 +321,262 @@ static void periodic_handler(event_loop_t *loop, void *data) {
        mesh->contradicting_add_edge = 0;
        mesh->contradicting_del_edge = 0;
 
-       /* If AutoConnect is set, check if we need to make or break connections. */
+       int timeout = 5;
+
+       /* Check if we need to make or break connections. */
+
+       if(mesh->nodes->count > 1) {
+
+               logger(mesh, MESHLINK_INFO, "--- autoconnect begin ---");
+
+               int retry_timeout = min(mesh->nodes->count * 5, 60);
+
+               logger(mesh, MESHLINK_INFO, "* devclass = %d", mesh->devclass);
+               logger(mesh, MESHLINK_INFO, "* nodes = %d", mesh->nodes->count);
+               logger(mesh, MESHLINK_INFO, "* retry_timeout = %d", retry_timeout);
+
+
+               // connect disconnect nodes
 
-       if(autoconnect && mesh->nodes->count > 1) {
-               /* Count number of active connections */
-               int nc = 0;
-               for list_each(connection_t, c, mesh->connections) {
-                       if(c->status.active)
-                               nc++;
+               node_t* connect_to = NULL;
+               node_t* disconnect_from = NULL;
+
+
+               // get cur_connects
+
+               int cur_connects = 0;
+
+               for list_each(connection_t, c, mesh->connections)
+               {
+                       if(!c->status.remove_unused)
+                       {
+                               cur_connects += 1;
+                       }
                }
 
-               if(nc < autoconnect) {
-                       /* Not enough active connections, try to add one.
-                          Choose a random node, if we don't have a connection to it,
-                          and we are not already trying to make one, create an
-                          outgoing connection to this node.
-                       */
-                       int r = rand() % mesh->nodes->count;
-                       int i = 0;
-
-                       for splay_each(node_t, n, mesh->nodes) {
-                               if(i++ != r)
-                                       continue;
+               logger(mesh, MESHLINK_INFO, "* cur_connects = %d", cur_connects);
+               logger(mesh, MESHLINK_INFO, "* outgoings = %d", mesh->outgoings->count);
 
-                               if(n->connection)
-                                       break;
+               // get min_connects and max_connects
+
+               assert(mesh->devclass >= 0 && mesh->devclass <= _DEV_CLASS_MAX);
 
-                               bool found = false;
+               int min_connects = dev_class_traits[mesh->devclass].min_connects;
+               int max_connects = dev_class_traits[mesh->devclass].max_connects;
 
-                               for list_each(outgoing_t, outgoing, mesh->outgoings) {
-                                       if(!strcmp(outgoing->name, n->name)) {
-                                               found = true;
+               logger(mesh, MESHLINK_INFO, "* min_connects = %d", min_connects);
+               logger(mesh, MESHLINK_INFO, "* max_connects = %d", max_connects);
+
+
+               // find the best one for initial connect
+
+               if(cur_connects < min_connects)
+               {
+                       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_INFO, "* n->devclass = %d", 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); }
+                       }
+
+                       if(nodes->head)
+                       {
+                               logger(mesh, MESHLINK_INFO, "* found best one for initial connect");
+
+                               //timeout = 0;
+                               connect_to = (node_t*)nodes->head->data;
+                       }
+                       else
+                               { logger(mesh, MESHLINK_INFO, "* could not find node for initial connect"); }
+
+                       splay_free_tree(nodes);
+               }
+
+
+               // find better nodes to connect to
+
+               if(!connect_to && min_connects <= cur_connects && cur_connects < max_connects)
+               {
+                       unsigned int connects = 0;
+
+                       for(int devclass = 0; devclass <= mesh->devclass; ++devclass)
+                       {
+                               for list_each(connection_t, c, mesh->connections)
+                               {
+                                       if(!c->status.remove_unused && c->node && c->node->devclass == devclass)
+                                               { connects += 1; }
+                               }
+
+                               if( connects < min_connects )
+                               {
+                                       splay_tree_t *nodes = splay_alloc_tree(node_compare_lsc_desc, NULL);
+
+                                       for splay_each(node_t, n, mesh->nodes)
+                                       {
+                                               if(n != mesh->self && n->devclass == devclass && !n->connection && (n->last_connect_try == 0 || (time(NULL) - n->last_connect_try) > retry_timeout))
+                                                       { splay_insert(nodes, n); }
+                                       }
+
+                                       if(nodes->head)
+                                       {
+                                               logger(mesh, MESHLINK_INFO, "* found better node");
+                                               connect_to = (node_t*)nodes->head->data;
+
+                                               splay_free_tree(nodes);
                                                break;
                                        }
-                               }
 
-                               if(!found) {
-                                       //TODO: if the node is blacklisted the connection will not happen, but
-                                       //the user will read this debug message "Autoconnecting to %s" that is misleading
-                                       logger(DEBUG_CONNECTIONS, LOG_INFO, "Autoconnecting to %s", n->name);
-                                       outgoing_t *outgoing = xzalloc(sizeof *outgoing);
-                                       outgoing->name = xstrdup(n->name);
-                                       list_insert_tail(mesh->outgoings, outgoing);
-                                       setup_outgoing_connection(mesh, outgoing);
+                                       splay_free_tree(nodes);
                                }
-                               break;
+                               else
+                                       { break; }
                        }
-               } else if(nc > autoconnect) {
-                       /* Too many active connections, try to remove one.
-                          Choose a random outgoing connection to a node
-                          that has at least one other connection.
-                       */
-                       int r = rand() % nc;
-                       int i = 0;
-
-                       for list_each(connection_t, c, mesh->connections) {
-                               if(!c->status.active)
-                                       continue;
 
-                               if(i++ != r)
-                                       continue;
+                       if(!connect_to)
+                               { logger(mesh, MESHLINK_INFO, "* could not find better nodes"); }
+               }
+
+
+               // heal partitions
+
+               if(!connect_to && min_connects <= cur_connects && cur_connects < max_connects)
+               {
+                       splay_tree_t *nodes = splay_alloc_tree(node_compare_devclass_asc_lsc_desc, NULL);
+
+                       for splay_each(node_t, n, mesh->nodes)
+                       {
+                               if(n != mesh->self && n->devclass <= mesh->devclass && !n->status.reachable && (n->last_connect_try == 0 || (time(NULL) - n->last_connect_try) > retry_timeout))
+                                       { splay_insert(nodes, n); }
+                       }
+
+                       if(nodes->head)
+                       {
+                               logger(mesh, MESHLINK_INFO, "* try to heal partition");
+                               connect_to = (node_t*)nodes->head->data;
+                       }
+                       else
+                               { logger(mesh, MESHLINK_INFO, "* could not find nodes for partition healing"); }
+
+                       splay_free_tree(nodes);
+               }
+
 
-                               if(!c->outgoing || !c->node || c->node->edge_tree->count < 2)
+               // perform connect
+
+               if(connect_to && !connect_to->connection)
+               {
+                       connect_to->last_connect_try = time(NULL);
+
+                       /* check if there is already a connection attempt to this node */
+                       bool found = false;
+                       for list_each(outgoing_t, outgoing, mesh->outgoings)
+                       {
+                               if(!strcmp(outgoing->name, connect_to->name))
+                               {
+                                       found = true;
                                        break;
+                               }
+                       }
 
-                               logger(DEBUG_CONNECTIONS, LOG_INFO, "Autodisconnecting from %s", c->name);
-                               list_delete(mesh->outgoings, c->outgoing);
-                               c->outgoing = NULL;
-                               terminate_connection(mesh, c, c->status.active);
-                               break;
+                       if(!found)
+                       {
+                               logger(mesh, MESHLINK_INFO, "Autoconnecting to %s", connect_to->name);
+                               outgoing_t *outgoing = xzalloc(sizeof(outgoing_t));
+                               outgoing->mesh = mesh;
+                               outgoing->name = xstrdup(connect_to->name);
+                               list_insert_tail(mesh->outgoings, outgoing);
+                               setup_outgoing_connection(mesh, outgoing);
                        }
+                       else
+                               { logger(mesh, MESHLINK_INFO, "* skip autoconnect since it is an outgoing connection already"); }
                }
 
-               if(nc >= autoconnect) {
-                       /* If we have enough active connections,
-                          remove any pending outgoing connections.
-                       */
-                       for list_each(outgoing_t, o, mesh->outgoings) {
-                               bool found = false;
-                               for list_each(connection_t, c, mesh->connections) {
-                                       if(c->outgoing == o) {
-                                               found = true;
-                                               break;
-                                       }
+
+               // disconnect suboptimal outgoing connections
+
+               if(min_connects < cur_connects && cur_connects <= max_connects)
+               {
+                       unsigned int connects = 0;
+
+                       for(int devclass = 0; devclass <= mesh->devclass; ++devclass)
+                       {
+                               for list_each(connection_t, c, mesh->connections)
+                               {
+                                       if(!c->status.remove_unused && c->node && c->node->devclass == devclass)
+                                               { connects += 1; }
                                }
-                               if(!found) {
-                                       logger(DEBUG_CONNECTIONS, LOG_INFO, "Cancelled outgoing connection to %s", o->name);
-                                       list_delete_node(mesh->outgoings, node);
+
+                               if( min_connects < connects )
+                               {
+                                       splay_tree_t *nodes = splay_alloc_tree(node_compare_devclass_desc, NULL);
+
+                                       for list_each(connection_t, c, mesh->connections)
+                                       {
+                                               if(!c->status.remove_unused && c->outgoing && c->node && c->node->devclass >= devclass)
+                                                       { splay_insert(nodes, c->node); }
+                                       }
+
+                                       if(nodes->head)
+                                       {
+                                               logger(mesh, MESHLINK_INFO, "* disconnect suboptimal outgoing connection");
+                                               disconnect_from = (node_t*)nodes->head->data;
+                                       }
+
+                                       splay_free_tree(nodes);
+                                       break;
                                }
                        }
+
+                       if(!disconnect_from)
+                               { logger(mesh, MESHLINK_INFO, "* no suboptimal outgoing connections"); }
+               }
+
+
+               // disconnect connections (too many connections)
+
+               if(!disconnect_from && max_connects < cur_connects)
+               {
+                       splay_tree_t *nodes = splay_alloc_tree(node_compare_devclass_desc, NULL);
+
+                       for list_each(connection_t, c, mesh->connections)
+                       {
+                               if(!c->status.remove_unused && c->node)
+                                       { splay_insert(nodes, c->node); }
+                       }
+
+                       if(nodes->head)
+                       {
+                               logger(mesh, MESHLINK_INFO, "* disconnect connection (too many connections)");
+
+                               //timeout = 0;
+                               disconnect_from = (node_t*)nodes->head->data;
+                       }
+                       else
+                               { logger(mesh, MESHLINK_INFO, "* no node we want to disconnect, even though we have too many connections"); }
+
+                       splay_free_tree(nodes);
+               }
+
+
+               // perform disconnect
+
+               if(disconnect_from && disconnect_from->connection)
+               {
+                       logger(mesh, MESHLINK_INFO, "Autodisconnecting from %s", disconnect_from->connection->name);
+                       list_delete(mesh->outgoings, disconnect_from->connection->outgoing);
+                       disconnect_from->connection->outgoing = NULL;
+                       terminate_connection(mesh, disconnect_from->connection, disconnect_from->connection->status.active);
                }
+
+
+               // done!
+
+               logger(mesh, MESHLINK_INFO, "--- autoconnect end ---");
        }
 
-       timeout_set(&mesh->loop, data, &(struct timeval){5, rand() % 100000});
+       timeout_set(&mesh->loop, data, &(struct timeval){timeout, rand() % 100000});
 }
 
 void handle_meta_connection_data(meshlink_handle_t *mesh, connection_t *c) {
@@ -267,14 +609,14 @@ void retry(meshlink_handle_t *mesh) {
 */
 int main_loop(meshlink_handle_t *mesh) {
        timeout_add(&mesh->loop, &mesh->pingtimer, timeout_handler, &mesh->pingtimer, &(struct timeval){mesh->pingtimeout, rand() % 100000});
-       timeout_add(&mesh->loop, &mesh->periodictimer, periodic_handler, &mesh->periodictimer, &(struct timeval){mesh->pingtimeout, rand() % 100000});
+       timeout_add(&mesh->loop, &mesh->periodictimer, periodic_handler, &mesh->periodictimer, &(struct timeval){0, 0});
 
        //Add signal handler
        mesh->datafromapp.signum = 0;
        signal_add(&(mesh->loop),&(mesh->datafromapp), (signal_cb_t)meshlink_send_from_queue,mesh, mesh->datafromapp.signum);
 
-       if(!event_loop_run(&mesh->loop)) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Error while waiting for input: %s", strerror(errno));
+       if(!event_loop_run(&(mesh->loop), &(mesh->mesh_mutex))) {
+               logger(mesh, MESHLINK_ERROR, "Error while waiting for input: %s", strerror(errno));
                return 1;
        }
 
index 6014a3d6207881d15b4fbc8bbfe8aa8f43d48f2e..d2a60bcf685c858ef079f59b8d1b76305b609722 100644 (file)
--- a/src/net.h
+++ b/src/net.h
@@ -76,7 +76,6 @@ extern unsigned replaywin;
 extern int keylifetime;
 extern int max_connection_burst;
 extern bool do_prune;
-extern int autoconnect;
 
 /* Yes, very strange placement indeed, but otherwise the typedefs get all tangled up */
 #include "connection.h"
index 8367a9c5157d78c978d65ce9e1267daa1e6fc862..ca43d62c65b2f5a903af276229c564ed270068b8 100644 (file)
@@ -68,7 +68,7 @@ static void send_mtu_probe_handler(event_loop_t *loop, void *data) {
        n->mtuprobes++;
 
        if(!n->status.reachable || !n->status.validkey) {
-               logger(DEBUG_TRAFFIC, LOG_INFO, "Trying to send MTU probe to unreachable or rekeying node %s (%s)", n->name, n->hostname);
+               logger(mesh, MESHLINK_INFO, "Trying to send MTU probe to unreachable or rekeying node %s (%s)", n->name, n->hostname);
                n->mtuprobes = 0;
                return;
        }
@@ -80,7 +80,7 @@ static void send_mtu_probe_handler(event_loop_t *loop, void *data) {
                        goto end;
                }
 
-               logger(DEBUG_TRAFFIC, LOG_INFO, "%s (%s) did not respond to UDP ping, restarting PMTU discovery", n->name, n->hostname);
+               logger(mesh, MESHLINK_INFO, "%s (%s) did not respond to UDP ping, restarting PMTU discovery", n->name, n->hostname);
                n->status.udp_confirmed = false;
                n->mtuprobes = 1;
                n->minmtu = 0;
@@ -88,7 +88,7 @@ static void send_mtu_probe_handler(event_loop_t *loop, void *data) {
        }
 
        if(n->mtuprobes >= 10 && n->mtuprobes < 32 && !n->minmtu) {
-               logger(DEBUG_TRAFFIC, LOG_INFO, "No response to MTU probes from %s (%s)", n->name, n->hostname);
+               logger(mesh, MESHLINK_INFO, "No response to MTU probes from %s (%s)", n->name, n->hostname);
                n->mtuprobes = 31;
        }
 
@@ -98,7 +98,7 @@ static void send_mtu_probe_handler(event_loop_t *loop, void *data) {
                else
                        n->maxmtu = n->minmtu;
                n->mtu = n->minmtu;
-               logger(DEBUG_TRAFFIC, LOG_INFO, "Fixing MTU of %s (%s) to %d after %d probes", n->name, n->hostname, n->mtu, n->mtuprobes);
+               logger(mesh, MESHLINK_INFO, "Fixing MTU of %s (%s) to %d after %d probes", n->name, n->hostname, n->mtu, n->mtuprobes);
                n->mtuprobes = 31;
        }
 
@@ -132,7 +132,7 @@ static void send_mtu_probe_handler(event_loop_t *loop, void *data) {
                packet.len = len;
                n->status.broadcast = i >= 4 && n->mtuprobes <= 10 && n->prevedge;
 
-               logger(DEBUG_TRAFFIC, LOG_INFO, "Sending MTU probe length %d to %s (%s)", len, n->name, n->hostname);
+               logger(mesh, MESHLINK_DEBUG, "Sending MTU probe length %d to %s (%s)", len, n->name, n->hostname);
 
                send_udppacket(mesh, n, &packet);
        }
@@ -163,7 +163,7 @@ void send_mtu_probe(meshlink_handle_t *mesh, node_t *n) {
 }
 
 static void mtu_probe_h(meshlink_handle_t *mesh, node_t *n, vpn_packet_t *packet, uint16_t len) {
-       logger(DEBUG_TRAFFIC, LOG_INFO, "Got MTU probe length %d from %s (%s)", packet->len, n->name, n->hostname);
+       logger(mesh, MESHLINK_DEBUG, "Got MTU probe length %d from %s (%s)", packet->len, n->name, n->hostname);
 
        if(!packet->data[0]) {
                /* It's a probe request, send back a reply */
@@ -188,7 +188,7 @@ static void mtu_probe_h(meshlink_handle_t *mesh, node_t *n, vpn_packet_t *packet
 
                if(n->mtuprobes > 30) {
                        if (len == n->maxmtu + 8) {
-                               logger(DEBUG_TRAFFIC, LOG_INFO, "Increase in PMTU to %s (%s) detected, restarting PMTU discovery", n->name, n->hostname);
+                               logger(mesh, MESHLINK_INFO, "Increase in PMTU to %s (%s) detected, restarting PMTU discovery", n->name, n->hostname);
                                n->maxmtu = MTU;
                                n->mtuprobes = 10;
                                return;
@@ -224,7 +224,7 @@ static void mtu_probe_h(meshlink_handle_t *mesh, node_t *n, vpn_packet_t *packet
                        n->probe_time = now;
                } else if(n->probe_counter == 3) {
                        n->bandwidth = 2.0 * len / (diff.tv_sec + diff.tv_usec * 1e-6);
-                       logger(DEBUG_TRAFFIC, LOG_DEBUG, "%s (%s) RTT %.2f ms, burst bandwidth %.3f Mbit/s, rx packet loss %.2f %%", n->name, n->hostname, n->rtt * 1e3, n->bandwidth * 8e-6, n->packetloss * 1e2);
+                       logger(mesh, MESHLINK_DEBUG, "%s (%s) RTT %.2f ms, burst bandwidth %.3f Mbit/s, rx packet loss %.2f %%", n->name, n->hostname, n->rtt * 1e3, n->bandwidth * 8e-6, n->packetloss * 1e2);
                }
        }
 }
@@ -273,11 +273,11 @@ static uint16_t uncompress_packet(uint8_t *dest, const uint8_t *source, uint16_t
 /* VPN packet I/O */
 
 static void receive_packet(meshlink_handle_t *mesh, node_t *n, vpn_packet_t *packet) {
-       logger(DEBUG_TRAFFIC, LOG_DEBUG, "Received packet of %d bytes from %s (%s)",
+       logger(mesh, MESHLINK_DEBUG, "Received packet of %d bytes from %s (%s)",
                           packet->len, n->name, n->hostname);
 
     if (n->status.blacklisted) {
-        logger(DEBUG_PROTOCOL, LOG_WARNING, "Dropping packet from blacklisted node %s", n->name);
+        logger(mesh, MESHLINK_WARNING, "Dropping packet from blacklisted node %s", n->name);
     } else {
        n->in_packets++;
        n->in_bytes += packet->len;
@@ -293,10 +293,10 @@ static bool try_mac(meshlink_handle_t *mesh, node_t *n, const vpn_packet_t *inpk
 static void receive_udppacket(meshlink_handle_t *mesh, node_t *n, vpn_packet_t *inpkt) {
        if(!n->sptps.state) {
                if(!n->status.waitingforkey) {
-                       logger(DEBUG_TRAFFIC, LOG_DEBUG, "Got packet from %s (%s) but we haven't exchanged keys yet", n->name, n->hostname);
+                       logger(mesh, MESHLINK_DEBUG, "Got packet from %s (%s) but we haven't exchanged keys yet", n->name, n->hostname);
                        send_req_key(mesh, n);
                } else {
-                       logger(DEBUG_TRAFFIC, LOG_DEBUG, "Got packet from %s (%s) but he hasn't got our key yet", n->name, n->hostname);
+                       logger(mesh, MESHLINK_DEBUG, "Got packet from %s (%s) but he hasn't got our key yet", n->name, n->hostname);
                }
                return;
        }
@@ -318,11 +318,11 @@ void receive_tcppacket(meshlink_handle_t *mesh, connection_t *c, const char *buf
 
 static void send_sptps_packet(meshlink_handle_t *mesh, node_t *n, vpn_packet_t *origpkt) {
        if(!n->status.validkey) {
-               logger(DEBUG_TRAFFIC, LOG_INFO, "No valid key known yet for %s (%s)", n->name, n->hostname);
+               logger(mesh, MESHLINK_INFO, "No valid key known yet for %s (%s)", n->name, n->hostname);
                if(!n->status.waitingforkey)
                        send_req_key(mesh, n);
                else if(n->last_req_key + 10 < mesh->loop.now.tv_sec) {
-                       logger(DEBUG_ALWAYS, LOG_DEBUG, "No key from %s after 10 seconds, restarting SPTPS", n->name);
+                       logger(mesh, MESHLINK_DEBUG, "No key from %s after 10 seconds, restarting SPTPS", n->name);
                        sptps_stop(&n->sptps);
                        n->status.waitingforkey = false;
                        send_req_key(mesh, n);
@@ -343,7 +343,7 @@ static void send_sptps_packet(meshlink_handle_t *mesh, node_t *n, vpn_packet_t *
        if(n->outcompression) {
                int len = compress_packet(outpkt.data, origpkt->data, origpkt->len, n->outcompression);
                if(len < 0) {
-                       logger(DEBUG_TRAFFIC, LOG_ERR, "Error while compressing packet to %s (%s)", n->name, n->hostname);
+                       logger(mesh, MESHLINK_ERROR, "Error while compressing packet to %s (%s)", n->name, n->hostname);
                } else if(len < origpkt->len) {
                        outpkt.len = len;
                        origpkt = &outpkt;
@@ -445,7 +445,7 @@ static void choose_broadcast_address(meshlink_handle_t *mesh, const node_t *n, c
 
 static void send_udppacket(meshlink_handle_t *mesh, node_t *n, vpn_packet_t *origpkt) {
        if(!n->status.reachable) {
-               logger(DEBUG_TRAFFIC, LOG_INFO, "Trying to send UDP packet to unreachable node %s (%s)", n->name, n->hostname);
+               logger(mesh, MESHLINK_INFO, "Trying to send UDP packet to unreachable node %s (%s)", n->name, n->hostname);
                return;
        }
 
@@ -488,7 +488,7 @@ bool send_sptps_data(void *handle, uint8_t type, const void *data, size_t len) {
                        if(to->mtu >= len)
                                to->mtu = len - 1;
                } else {
-                       logger(DEBUG_TRAFFIC, LOG_WARNING, "Error sending UDP SPTPS packet to %s (%s): %s", to->name, to->hostname, sockstrerror(sockerrno));
+                       logger(mesh, MESHLINK_WARNING, "Error sending UDP SPTPS packet to %s (%s): %s", to->name, to->hostname, sockstrerror(sockerrno));
                        return false;
                }
        }
@@ -504,13 +504,13 @@ bool receive_sptps_record(void *handle, uint8_t type, const void *data, uint16_t
                if(!from->status.validkey) {
                        from->status.validkey = true;
                        from->status.waitingforkey = false;
-                       logger(DEBUG_META, LOG_INFO, "SPTPS key exchange with %s (%s) succesful", from->name, from->hostname);
+                       logger(mesh, MESHLINK_INFO, "SPTPS key exchange with %s (%s) succesful", from->name, from->hostname);
                }
                return true;
        }
 
        if(len > MTU) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Packet from %s (%s) larger than maximum supported size (%d > %d)", from->name, from->hostname, len, MTU);
+               logger(mesh, MESHLINK_ERROR, "Packet from %s (%s) larger than maximum supported size (%d > %d)", from->name, from->hostname, len, MTU);
                return false;
        }
 
@@ -527,7 +527,7 @@ bool receive_sptps_record(void *handle, uint8_t type, const void *data, uint16_t
        }
 
        if(type & ~(PKT_COMPRESSED)) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Unexpected SPTPS record type %d len %d from %s (%s)", type, len, from->name, from->hostname);
+               logger(mesh, MESHLINK_ERROR, "Unexpected SPTPS record type %d len %d from %s (%s)", type, len, from->name, from->hostname);
                return false;
        }
 
@@ -560,11 +560,11 @@ void send_packet(meshlink_handle_t *mesh, node_t *n, vpn_packet_t *packet) {
                return;
        }
 
-       logger(DEBUG_TRAFFIC, LOG_ERR, "Sending packet of %d bytes to %s (%s)",
+       logger(mesh, MESHLINK_DEBUG, "Sending packet of %d bytes to %s (%s)",
                           packet->len, n->name, n->hostname);
 
        if(!n->status.reachable) {
-               logger(DEBUG_TRAFFIC, LOG_INFO, "Node %s (%s) is not reachable",
+               logger(mesh, MESHLINK_WARNING, "Node %s (%s) is not reachable",
                                   n->name, n->hostname);
                return;
        }
@@ -583,7 +583,7 @@ void broadcast_packet(meshlink_handle_t *mesh, const node_t *from, vpn_packet_t
        if(from != mesh->self)
                send_packet(mesh, mesh->self, packet);
 
-       logger(DEBUG_TRAFFIC, LOG_INFO, "Broadcasting packet of %d bytes from %s (%s)",
+       logger(mesh, MESHLINK_INFO, "Broadcasting packet of %d bytes from %s (%s)",
                           packet->len, from->name, from->hostname);
 
        for list_each(connection_t, c, mesh->connections)
@@ -634,7 +634,7 @@ void handle_incoming_vpn_data(event_loop_t *loop, void *data, int flags) {
 
        if(len <= 0 || len > MAXSIZE) {
                if(!sockwouldblock(sockerrno))
-                       logger(DEBUG_ALWAYS, LOG_ERR, "Receiving packet failed: %s", sockstrerror(sockerrno));
+                       logger(mesh, MESHLINK_ERROR, "Receiving packet failed: %s", sockstrerror(sockerrno));
                return;
        }
 
@@ -648,9 +648,9 @@ void handle_incoming_vpn_data(event_loop_t *loop, void *data, int flags) {
                n = try_harder(mesh, &from, &pkt);
                if(n)
                        update_node_udp(mesh, n, &from);
-               else if(mesh->debug_level >= DEBUG_PROTOCOL) {
+               else if(mesh->log_level >= MESHLINK_WARNING) {
                        hostname = sockaddr2hostname(&from);
-                       logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet from unknown source %s", hostname);
+                       logger(mesh, MESHLINK_WARNING, "Received UDP packet from unknown source %s", hostname);
                        free(hostname);
                        return;
                }
@@ -659,7 +659,7 @@ void handle_incoming_vpn_data(event_loop_t *loop, void *data, int flags) {
        }
 
     if (n->status.blacklisted) {
-                       logger(DEBUG_PROTOCOL, LOG_WARNING, "Dropping packet from blacklisted node %s", n->name);
+                       logger(mesh, MESHLINK_WARNING, "Dropping packet from blacklisted node %s", n->name);
             return;
     }
        n->sock = ls - mesh->listen_socket;
index 8b0fd8fde443ced49bb80905bf7df3459ded601c..1ba98077046b3d709b7d7db09ecef73bdfc798ea 100644 (file)
@@ -32,8 +32,6 @@
 #include "utils.h"
 #include "xalloc.h"
 
-int autoconnect = 3;
-
 bool node_read_ecdsa_public_key(meshlink_handle_t *mesh, node_t *n) {
        if(ecdsa_active(n->ecdsa))
                return true;
@@ -88,7 +86,7 @@ bool read_ecdsa_private_key(meshlink_handle_t *mesh) {
        fp = fopen(filename, "r");
 
        if(!fp) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Error reading ECDSA private key file: %s", strerror(errno));
+               logger(mesh, MESHLINK_ERROR, "Error reading ECDSA private key file: %s", strerror(errno));
                return false;
        }
 
@@ -96,7 +94,7 @@ bool read_ecdsa_private_key(meshlink_handle_t *mesh) {
        fclose(fp);
 
        if(!mesh->self->connection->ecdsa)
-               logger(DEBUG_ALWAYS, LOG_ERR, "Reading ECDSA private key file failed: %s", strerror(errno));
+               logger(mesh, MESHLINK_ERROR, "Reading ECDSA private key file failed: %s", strerror(errno));
 
        return mesh->self->connection->ecdsa;
 }
@@ -118,12 +116,69 @@ static bool read_invitation_key(meshlink_handle_t *mesh) {
                mesh->invitation_key = ecdsa_read_pem_private_key(fp);
                fclose(fp);
                if(!mesh->invitation_key)
-                       logger(DEBUG_ALWAYS, LOG_ERR, "Reading ECDSA private key file `%s' failed: %s", filename, strerror(errno));
+                       logger(mesh, MESHLINK_ERROR, "Reading ECDSA private key file `%s' failed: %s", filename, strerror(errno));
        }
 
        return mesh->invitation_key;
 }
 
+bool node_read_devclass(meshlink_handle_t *mesh, node_t *n) {
+       
+       splay_tree_t *config_tree;
+       char *p;
+
+       init_configuration(&config_tree);
+       if(!read_host_config(mesh, config_tree, n->name))
+               goto exit;
+
+       if(get_config_string(lookup_config(config_tree, "DeviceClass"), &p))
+       {
+               n->devclass = atoi(p);
+               free(p);
+       }
+
+       if(n->devclass < 0 || n->devclass > _DEV_CLASS_MAX)
+               { n->devclass = _DEV_CLASS_MAX; }
+
+exit:
+       exit_configuration(&config_tree);
+       return n->devclass != 0;
+}
+
+bool node_write_devclass(meshlink_handle_t *mesh, node_t *n) {
+
+       if(n->devclass < 0 || n->devclass > _DEV_CLASS_MAX)
+               return false;
+
+       bool result = false;
+
+       splay_tree_t *config_tree;
+       init_configuration(&config_tree);
+
+       // ignore read errors; in case the file does not exist we will create it
+       read_host_config(mesh, config_tree, n->name);
+
+       config_t* cnf = lookup_config(config_tree, "DeviceClass");
+
+       if(!cnf)
+       {
+               cnf = new_config();
+               cnf->variable = xstrdup("DeviceClass");
+               config_add(config_tree, cnf);
+       }
+
+       set_config_int(cnf, n->devclass);
+
+       if(!write_host_config(mesh, config_tree, n->name))
+               goto fail;
+
+       result = true;
+
+fail:
+       exit_configuration(&config_tree);
+       return result;
+}
+
 void load_all_nodes(meshlink_handle_t *mesh) {
        DIR *dir;
        struct dirent *ent;
@@ -132,7 +187,7 @@ void load_all_nodes(meshlink_handle_t *mesh) {
        snprintf(dname,PATH_MAX, "%s" SLASH "hosts", mesh->confbase);
        dir = opendir(dname);
        if(!dir) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s", dname, strerror(errno));
+               logger(mesh, MESHLINK_ERROR, "Could not open %s: %s", dname, strerror(errno));
                return;
        }
 
@@ -146,6 +201,7 @@ void load_all_nodes(meshlink_handle_t *mesh) {
 
                n = new_node();
                n->name = xstrdup(ent->d_name);
+               node_read_devclass(mesh, n);
                node_add(mesh, n);
        }
 
@@ -162,7 +218,7 @@ char *get_name(meshlink_handle_t *mesh) {
                return NULL;
 
        if(!check_id(name)) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Invalid name for mesh->self!");
+               logger(mesh, MESHLINK_ERROR, "Invalid name for mesh->self!");
                free(name);
                return NULL;
        }
@@ -174,7 +230,6 @@ 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;
-       autoconnect = 3;
        mesh->self->options |= OPTION_PMTU_DISCOVERY;
 
        read_invitation_key(mesh);
@@ -209,7 +264,7 @@ static bool add_listen_address(meshlink_handle_t *mesh, char *address, bool bind
        free(address);
 
        if(err || !ai) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "getaddrinfo", err == EAI_SYSTEM ? strerror(err) : gai_strerror(err));
+               logger(mesh, MESHLINK_ERROR, "System call `%s' failed: %s", "getaddrinfo", err == EAI_SYSTEM ? strerror(err) : gai_strerror(err));
                return false;
        }
 
@@ -227,7 +282,7 @@ static bool add_listen_address(meshlink_handle_t *mesh, char *address, bool bind
                        continue;
 
                if(mesh->listen_sockets >= MAXSOCKETS) {
-                       logger(DEBUG_ALWAYS, LOG_ERR, "Too many listening sockets");
+                       logger(mesh, MESHLINK_ERROR, "Too many listening sockets");
                        return false;
                }
 
@@ -246,9 +301,9 @@ static bool add_listen_address(meshlink_handle_t *mesh, char *address, bool bind
                io_add(&mesh->loop, &mesh->listen_socket[mesh->listen_sockets].tcp, handle_new_meta_connection, &mesh->listen_socket[mesh->listen_sockets], tcp_fd, IO_READ);
                io_add(&mesh->loop, &mesh->listen_socket[mesh->listen_sockets].udp, handle_incoming_vpn_data, &mesh->listen_socket[mesh->listen_sockets], udp_fd, IO_READ);
 
-               if(mesh->debug_level >= DEBUG_CONNECTIONS) {
+               if(mesh->log_level >= MESHLINK_INFO) {
                        char *hostname = sockaddr2hostname((sockaddr_t *) aip->ai_addr);
-                       logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Listening on %s", hostname);
+                       logger(mesh, MESHLINK_INFO, "Listening on %s", hostname);
                        free(hostname);
                }
 
@@ -269,18 +324,21 @@ bool setup_myself(meshlink_handle_t *mesh) {
        char *address = NULL;
 
        if(!(name = get_name(mesh))) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Name for MeshLink instance required!");
+               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))
-               mesh->myport = xstrdup("655");
+       if(!get_config_string(lookup_config(mesh->config, "Port"), &mesh->myport)) {
+               logger(mesh, MESHLINK_ERROR, "Port for MeshLink instance required!");
+               return false;
+       }
 
        mesh->self->connection->options = 0;
        mesh->self->connection->protocol_major = PROT_MAJOR;
@@ -320,12 +378,13 @@ bool setup_myself(meshlink_handle_t *mesh) {
        mesh->self->via = mesh->self;
        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);
 
-       if(autoconnect)
-               load_all_nodes(mesh);
+       load_all_nodes(mesh);
 
        /* Open sockets */
 
@@ -335,15 +394,10 @@ bool setup_myself(meshlink_handle_t *mesh) {
                return false;
 
        if(!mesh->listen_sockets) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Unable to create any listening socket!");
+               logger(mesh, MESHLINK_ERROR, "Unable to create any listening socket!");
                return false;
        }
 
-       // TODO: require Port to be set? Or use "0" and use getsockname()?
-
-       if(!mesh->myport)
-               mesh->myport = xstrdup("655");
-
        xasprintf(&mesh->self->hostname, "MYSELF port %s", mesh->myport);
        mesh->self->connection->hostname = xstrdup(mesh->self->hostname);
 
@@ -377,11 +431,13 @@ bool setup_network(meshlink_handle_t *mesh) {
   close all open network connections
 */
 void close_network_connections(meshlink_handle_t *mesh) {
-       for(list_node_t *node = mesh->connections->head, *next; node; node = next) {
-               next = node->next;
-               connection_t *c = node->data;
-               c->outgoing = NULL;
-               terminate_connection(mesh, c, false);
+       if(mesh->connections) {
+               for(list_node_t *node = mesh->connections->head, *next; node; node = next) {
+                       next = node->next;
+                       connection_t *c = node->data;
+                       c->outgoing = NULL;
+                       terminate_connection(mesh, c, false);
+               }
        }
 
        if(mesh->outgoings)
index 85f51648dc81955f87b8c946f40e3bd6f059f2bc..d5786e5bc24c15be129f0abe508326ca90b0459a 100644 (file)
@@ -47,13 +47,13 @@ static void configure_tcp(connection_t *c) {
        int flags = fcntl(c->socket, F_GETFL);
 
        if(fcntl(c->socket, F_SETFL, flags | O_NONBLOCK) < 0) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "fcntl for %s: %s", c->hostname, strerror(errno));
+               logger(c->mesh, MESHLINK_ERROR, "fcntl for %s: %s", c->hostname, strerror(errno));
        }
 #elif defined(WIN32)
        unsigned long arg = 1;
 
        if(ioctlsocket(c->socket, FIONBIO, &arg) != 0) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "ioctlsocket for %s: %s", c->hostname, sockstrerror(sockerrno));
+               logger(c->mesh, MESHLINK_ERROR, "ioctlsocket for %s: %s", c->hostname, sockstrerror(sockerrno));
        }
 #endif
 
@@ -89,7 +89,7 @@ static bool bind_to_address(meshlink_handle_t *mesh, connection_t *c) {
                sa.in6.sin6_port = 0;
 
        if(bind(c->socket, &sa.sa, SALEN(sa.sa))) {
-               logger(DEBUG_CONNECTIONS, LOG_WARNING, "Can't bind outgoing socket: %s", strerror(errno));
+               logger(mesh, MESHLINK_WARNING, "Can't bind outgoing socket: %s", strerror(errno));
                return false;
        }
 
@@ -104,7 +104,7 @@ int setup_listen_socket(const sockaddr_t *sa) {
        nfd = socket(sa->sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
 
        if(nfd < 0) {
-               logger(DEBUG_STATUS, LOG_ERR, "Creating metasocket failed: %s", sockstrerror(sockerrno));
+               logger(NULL, MESHLINK_ERROR, "Creating metasocket failed: %s", sockstrerror(sockerrno));
                return -1;
        }
 
@@ -125,14 +125,14 @@ int setup_listen_socket(const sockaddr_t *sa) {
        if(bind(nfd, &sa->sa, SALEN(sa->sa))) {
                closesocket(nfd);
                addrstr = sockaddr2hostname(sa);
-               logger(DEBUG_ALWAYS, LOG_ERR, "Can't bind to %s/tcp: %s", addrstr, sockstrerror(sockerrno));
+               logger(NULL, MESHLINK_ERROR, "Can't bind to %s/tcp: %s", addrstr, sockstrerror(sockerrno));
                free(addrstr);
                return -1;
        }
 
        if(listen(nfd, 3)) {
                closesocket(nfd);
-               logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "listen", sockstrerror(sockerrno));
+               logger(NULL, MESHLINK_ERROR, "System call `%s' failed: %s", "listen", sockstrerror(sockerrno));
                return -1;
        }
 
@@ -147,7 +147,7 @@ int setup_vpn_in_socket(meshlink_handle_t *mesh, const sockaddr_t *sa) {
        nfd = socket(sa->sa.sa_family, SOCK_DGRAM, IPPROTO_UDP);
 
        if(nfd < 0) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Creating UDP socket failed: %s", sockstrerror(sockerrno));
+               logger(mesh, MESHLINK_ERROR, "Creating UDP socket failed: %s", sockstrerror(sockerrno));
                return -1;
        }
 
@@ -161,7 +161,7 @@ int setup_vpn_in_socket(meshlink_handle_t *mesh, const sockaddr_t *sa) {
 
                if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0) {
                        closesocket(nfd);
-                       logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "fcntl",
+                       logger(mesh, MESHLINK_ERROR, "System call `%s' failed: %s", "fcntl",
                                   strerror(errno));
                        return -1;
                }
@@ -171,7 +171,7 @@ int setup_vpn_in_socket(meshlink_handle_t *mesh, const sockaddr_t *sa) {
                unsigned long arg = 1;
                if(ioctlsocket(nfd, FIONBIO, &arg) != 0) {
                        closesocket(nfd);
-                       logger(DEBUG_ALWAYS, LOG_ERR, "Call to `%s' failed: %s", "ioctlsocket", sockstrerror(sockerrno));
+                       logger(mesh, MESHLINK_ERROR, "Call to `%s' failed: %s", "ioctlsocket", sockstrerror(sockerrno));
                        return -1;
                }
        }
@@ -221,7 +221,7 @@ int setup_vpn_in_socket(meshlink_handle_t *mesh, const sockaddr_t *sa) {
        if(bind(nfd, &sa->sa, SALEN(sa->sa))) {
                closesocket(nfd);
                addrstr = sockaddr2hostname(sa);
-               logger(DEBUG_ALWAYS, LOG_ERR, "Can't bind to %s/udp: %s", addrstr, sockstrerror(sockerrno));
+               logger(mesh, MESHLINK_ERROR, "Can't bind to %s/udp: %s", addrstr, sockstrerror(sockerrno));
                free(addrstr);
                return -1;
        }
@@ -243,11 +243,11 @@ void retry_outgoing(meshlink_handle_t *mesh, outgoing_t *outgoing) {
 
        timeout_add(&mesh->loop, &outgoing->ev, retry_outgoing_handler, outgoing, &(struct timeval){outgoing->timeout, rand() % 100000});
 
-       logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Trying to re-establish outgoing connection in %d seconds", outgoing->timeout);
+       logger(mesh, MESHLINK_INFO, "Trying to re-establish outgoing connection in %d seconds", outgoing->timeout);
 }
 
 void finish_connecting(meshlink_handle_t *mesh, connection_t *c) {
-       logger(DEBUG_CONNECTIONS, LOG_INFO, "Connected to %s (%s)", c->name, c->hostname);
+       logger(mesh, MESHLINK_INFO, "Connected to %s (%s)", c->name, c->hostname);
 
        c->last_ping_time = mesh->loop.now.tv_sec;
        c->status.connecting = false;
@@ -260,14 +260,14 @@ static void do_outgoing_pipe(meshlink_handle_t *mesh, connection_t *c, char *com
        int fd[2];
 
        if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd)) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Could not create socketpair: %s", strerror(errno));
+               logger(mesh, MESHLINK_ERROR, "Could not create socketpair: %s", strerror(errno));
                return;
        }
 
        if(fork()) {
                c->socket = fd[0];
                close(fd[1]);
-               logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Using proxy %s", command);
+               logger(mesh, MESHLINK_DEBUG, "Using proxy %s", command);
                return;
        }
 
@@ -291,12 +291,12 @@ static void do_outgoing_pipe(meshlink_handle_t *mesh, connection_t *c, char *com
 
        int result = system(command);
        if(result < 0)
-               logger(DEBUG_ALWAYS, LOG_ERR, "Could not execute %s: %s", command, strerror(errno));
+               logger(mesh, MESHLINK_ERROR, "Could not execute %s: %s", command, strerror(errno));
        else if(result)
-               logger(DEBUG_ALWAYS, LOG_ERR, "%s exited with non-zero status %d", command, result);
+               logger(mesh, MESHLINK_ERROR, "%s exited with non-zero status %d", command, result);
        exit(result);
 #else
-       logger(DEBUG_ALWAYS, LOG_ERR, "Proxy type exec not supported on this platform!");
+       logger(mesh, MESHLINK_ERROR, "Proxy type exec not supported on this platform!");
        return;
 #endif
 }
@@ -305,15 +305,15 @@ static void handle_meta_write(meshlink_handle_t *mesh, connection_t *c) {
        if(c->outbuf.len <= c->outbuf.offset)
                return;
 
-       ssize_t outlen = send(c->socket, c->outbuf.data + c->outbuf.offset, c->outbuf.len - c->outbuf.offset, 0);
+       ssize_t outlen = send(c->socket, c->outbuf.data + c->outbuf.offset, c->outbuf.len - c->outbuf.offset, MSG_NOSIGNAL);
        if(outlen <= 0) {
                if(!errno || errno == EPIPE) {
-                       logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Connection closed by %s (%s)", c->name, c->hostname);
+                       logger(mesh, MESHLINK_INFO, "Connection closed by %s (%s)", c->name, c->hostname);
                } else if(sockwouldblock(sockerrno)) {
-                       logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Sending %d bytes to %s (%s) would block", c->outbuf.len - c->outbuf.offset, c->name, c->hostname);
+                       logger(mesh, MESHLINK_DEBUG, "Sending %d bytes to %s (%s) would block", c->outbuf.len - c->outbuf.offset, c->name, c->hostname);
                        return;
                } else {
-                       logger(DEBUG_CONNECTIONS, LOG_ERR, "Could not send %d bytes of data to %s (%s): %s", c->outbuf.len - c->outbuf.offset, c->name, c->hostname, strerror(errno));
+                       logger(mesh, MESHLINK_ERROR, "Could not send %d bytes of data to %s (%s): %s", c->outbuf.len - c->outbuf.offset, c->name, c->hostname, strerror(errno));
                }
 
                terminate_connection(mesh, c, c->status.active);
@@ -339,7 +339,7 @@ static void handle_meta_io(event_loop_t *loop, void *data, int flags) {
                if(!result)
                        finish_connecting(mesh, c);
                else {
-                       logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Error while connecting to %s (%s): %s", c->name, c->hostname, sockstrerror(result));
+                       logger(mesh, MESHLINK_DEBUG, "Error while connecting to %s (%s): %s", c->name, c->hostname, sockstrerror(result));
                        terminate_connection(mesh, c, false);
                        return;
                }
@@ -359,7 +359,7 @@ bool do_outgoing_connection(meshlink_handle_t *mesh, outgoing_t *outgoing) {
 begin:
        if(!outgoing->ai) {
                if(!outgoing->cfg) {
-                       logger(DEBUG_CONNECTIONS, LOG_ERR, "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->name);
                        retry_outgoing(mesh, outgoing);
                        return false;
                }
@@ -372,8 +372,11 @@ begin:
                        *space = 0;
                } else {
                        // TODO: Only allow Address statements?
-                       if(!get_config_string(lookup_config(outgoing->config_tree, "Port"), &port))
-                               port = xstrdup("655");
+                       if(!get_config_string(lookup_config(outgoing->config_tree, "Port"), &port)) {
+                               logger(mesh, MESHLINK_ERROR, "No Port known for %s", outgoing->name);
+                               retry_outgoing(mesh, outgoing);
+                               return false;
+                       }
                }
 
                outgoing->ai = str2addrinfo(address, port, SOCK_STREAM);
@@ -399,7 +402,7 @@ begin:
 
        c->hostname = sockaddr2hostname(&c->address);
 
-       logger(DEBUG_CONNECTIONS, LOG_INFO, "Trying to connect to %s (%s)", outgoing->name, c->hostname);
+       logger(mesh, MESHLINK_INFO, "Trying to connect to %s (%s)", outgoing->name, c->hostname);
 
        if(!mesh->proxytype) {
                c->socket = socket(c->address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
@@ -412,13 +415,13 @@ begin:
                        free_connection(c);
                        goto begin;
                }
-               logger(DEBUG_CONNECTIONS, LOG_INFO, "Using proxy at %s port %s", mesh->proxyhost, mesh->proxyport);
+               logger(mesh, MESHLINK_INFO, "Using proxy at %s port %s", mesh->proxyhost, mesh->proxyport);
                c->socket = socket(proxyai->ai_family, SOCK_STREAM, IPPROTO_TCP);
                configure_tcp(c);
        }
 
        if(c->socket == -1) {
-               logger(DEBUG_CONNECTIONS, LOG_ERR, "Creating socket for %s failed: %s", c->hostname, sockstrerror(sockerrno));
+               logger(mesh, MESHLINK_ERROR, "Creating socket for %s failed: %s", c->hostname, sockstrerror(sockerrno));
                free_connection(c);
                goto begin;
        }
@@ -449,7 +452,7 @@ begin:
        }
 
        if(result == -1 && !sockinprogress(sockerrno)) {
-               logger(DEBUG_CONNECTIONS, LOG_ERR, "Could not connect to %s (%s): %s", outgoing->name, c->hostname, sockstrerror(sockerrno));
+               logger(mesh, MESHLINK_ERROR, "Could not connect to %s (%s): %s", outgoing->name, c->hostname, sockstrerror(sockerrno));
                free_connection(c);
 
                goto begin;
@@ -509,7 +512,7 @@ void setup_outgoing_connection(meshlink_handle_t *mesh, outgoing_t *outgoing) {
        node_t *n = lookup_node(mesh, outgoing->name);
 
        if(n && n->connection) {
-               logger(DEBUG_CONNECTIONS, LOG_INFO, "Already connected to %s", outgoing->name);
+               logger(mesh, MESHLINK_INFO, "Already connected to %s", outgoing->name);
 
                n->connection->outgoing = outgoing;
                return;
@@ -526,7 +529,7 @@ void setup_outgoing_connection(meshlink_handle_t *mesh, outgoing_t *outgoing) {
                if(n)
                        outgoing->aip = outgoing->ai = get_known_addresses(n);
                if(!outgoing->ai) {
-                       logger(DEBUG_ALWAYS, LOG_ERR, "No address known for %s", outgoing->name);
+                       logger(mesh, MESHLINK_ERROR, "No address known for %s", outgoing->name);
                        return;
                }
        }
@@ -554,7 +557,7 @@ void handle_new_meta_connection(event_loop_t *loop, void *data, int flags) {
                        return;
                }
 
-               logger(DEBUG_ALWAYS, LOG_ERR, "Accepting a new connection failed: %s", sockstrerror(sockerrno));
+               logger(mesh, MESHLINK_ERROR, "Accepting a new connection failed: %s", sockstrerror(sockerrno));
                return;
        }
 
@@ -620,7 +623,7 @@ void handle_new_meta_connection(event_loop_t *loop, void *data, int flags) {
        c->socket = fd;
        c->last_ping_time = mesh->loop.now.tv_sec;
 
-       logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Connection from %s", c->hostname);
+       logger(mesh, MESHLINK_INFO, "Connection from %s", c->hostname);
 
        io_add(&mesh->loop, &c->io, handle_meta_io, c, c->socket, IO_READ);
 
@@ -667,7 +670,7 @@ void try_outgoing_connections(meshlink_handle_t *mesh) {
                get_config_string(cfg, &name);
 
                if(!check_id(name)) {
-                       logger(DEBUG_ALWAYS, LOG_ERR,
+                       logger(mesh, MESHLINK_ERROR,
                                   "Invalid name for outgoing connection in %s line %d",
                                   cfg->file, cfg->line);
                        free(name);
@@ -698,7 +701,7 @@ void try_outgoing_connections(meshlink_handle_t *mesh) {
        for list_each(connection_t, c, mesh->connections) {
                if(c->outgoing && c->outgoing->timeout == -1) {
                        c->outgoing = NULL;
-                       logger(DEBUG_CONNECTIONS, LOG_INFO, "No more outgoing connection to %s", c->name);
+                       logger(mesh, MESHLINK_INFO, "No more outgoing connection to %s", c->name);
                        terminate_connection(mesh, c, c->status.active);
                }
        }
index aefde072eb5186fcb1ce0fb9b7ac4dd66bae90f2..11987afdaec917910f304f2e5c9059ef438d2d80 100644 (file)
@@ -41,7 +41,7 @@ struct addrinfo *str2addrinfo(const char *address, const char *service, int sock
        err = getaddrinfo(address, service, &hint, &ai);
 
        if(err) {
-               logger(DEBUG_ALWAYS, LOG_WARNING, "Error looking up %s port %s: %s", address, service, err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
+               logger(NULL, MESHLINK_WARNING, "Error looking up %s port %s: %s", address, service, err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
                return NULL;
        }
 
@@ -60,7 +60,7 @@ sockaddr_t str2sockaddr(const char *address, const char *port) {
        err = getaddrinfo(address, port, &hint, &ai);
 
        if(err || !ai) {
-               logger(DEBUG_SCARY_THINGS, LOG_DEBUG, "Unknown type address %s port %s", address, port);
+               logger(NULL, MESHLINK_DEBUG, "Unknown type address %s port %s", address, port);
                result.sa.sa_family = AF_UNKNOWN;
                result.unknown.address = xstrdup(address);
                result.unknown.port = xstrdup(port);
@@ -90,7 +90,7 @@ void sockaddr2str(const sockaddr_t *sa, char **addrstr, char **portstr) {
        err = getnameinfo(&sa->sa, SALEN(sa->sa), address, sizeof address, port, sizeof port, NI_NUMERICHOST | NI_NUMERICSERV);
 
        if(err) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Error while translating addresses: %s", err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
+               logger(NULL, MESHLINK_ERROR, "Error while translating addresses: %s", err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
                abort();
        }
 
@@ -119,7 +119,7 @@ char *sockaddr2hostname(const sockaddr_t *sa) {
        err = getnameinfo(&sa->sa, SALEN(sa->sa), address, sizeof address, port, sizeof port,
                                        hostnames ? 0 : (NI_NUMERICHOST | NI_NUMERICSERV));
        if(err) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Error while looking up hostname: %s", err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
+               logger(NULL, MESHLINK_ERROR, "Error while looking up hostname: %s", err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
        }
 
        xasprintf(&str, "%s port %s", address, port);
@@ -149,7 +149,7 @@ int sockaddrcmp_noport(const sockaddr_t *a, const sockaddr_t *b) {
                        return memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr));
 
                default:
-                       logger(DEBUG_ALWAYS, LOG_ERR, "sockaddrcmp() was called with unknown address family %d, exitting!",
+                       logger(NULL, MESHLINK_ERROR, "sockaddrcmp() was called with unknown address family %d, exitting!",
                                   a->sa.sa_family);
                        abort();
        }
@@ -192,7 +192,7 @@ int sockaddrcmp(const sockaddr_t *a, const sockaddr_t *b) {
                        return memcmp(&a->in6.sin6_port, &b->in6.sin6_port, sizeof a->in6.sin6_port);
 
                default:
-                       logger(DEBUG_ALWAYS, LOG_ERR, "sockaddrcmp() was called with unknown address family %d, exitting!",
+                       logger(NULL, MESHLINK_ERROR, "sockaddrcmp() was called with unknown address family %d, exitting!",
                                   a->sa.sa_family);
                        abort();
        }
index 7fc476f480aab645076dda3f856d3c0a2585fabb..c6213a17016b61d755973be24a0d0518cf8e7630 100644 (file)
@@ -34,17 +34,17 @@ static int node_compare(const node_t *a, const node_t *b) {
 }
 
 void init_nodes(meshlink_handle_t *mesh) {
-       pthread_mutex_lock(&(mesh->nodes_mutex));
        mesh->nodes = splay_alloc_tree((splay_compare_t) node_compare, (splay_action_t) free_node);
        mesh->node_udp_cache = hash_alloc(0x100, sizeof(sockaddr_t));
-       pthread_mutex_unlock(&(mesh->nodes_mutex));
 }
 
 void exit_nodes(meshlink_handle_t *mesh) {
-       pthread_mutex_lock(&(mesh->nodes_mutex));
-       hash_free(mesh->node_udp_cache);
-       splay_delete_tree(mesh->nodes);
-       pthread_mutex_unlock(&(mesh->nodes_mutex));
+       if(mesh->node_udp_cache)
+               hash_free(mesh->node_udp_cache);
+       if(mesh->nodes)
+               splay_delete_tree(mesh->nodes);
+       mesh->node_udp_cache = NULL;
+       mesh->nodes = NULL;
 }
 
 node_t *new_node(void) {
@@ -54,6 +54,7 @@ node_t *new_node(void) {
        n->edge_tree = new_edge_tree();
        n->mtu = MTU;
        n->maxmtu = MTU;
+       n->devclass = _DEV_CLASS_MAX;
 
        return n;
 }
@@ -83,32 +84,24 @@ void free_node(node_t *n) {
 }
 
 void node_add(meshlink_handle_t *mesh, node_t *n) {
-       pthread_mutex_lock(&(mesh->nodes_mutex));
        n->mesh = mesh;
        splay_insert(mesh->nodes, n);
-       pthread_mutex_unlock(&(mesh->nodes_mutex));
 }
 
 void node_del(meshlink_handle_t *mesh, node_t *n) {
-       pthread_mutex_lock(&(mesh->nodes_mutex));
        timeout_del(&mesh->loop, &n->mtutimeout);
 
        for splay_each(edge_t, e, n->edge_tree)
                edge_del(mesh, e);
 
        splay_delete(mesh->nodes, n);
-       pthread_mutex_unlock(&(mesh->nodes_mutex));
 }
 
-node_t *lookup_node(meshlink_handle_t *mesh, char *name) {
-       node_t n = {NULL};
-       node_tresult;
+node_t *lookup_node(meshlink_handle_t *mesh, const char *name) {
+       const node_t n = {.name = (char *)name};
+       node_t *result;
 
-       n.name = name;
-
-       pthread_mutex_lock(&(mesh->nodes_mutex));
        result = splay_search(mesh->nodes, &n);
-       pthread_mutex_unlock(&(mesh->nodes_mutex));
 
        return result;
 }
@@ -119,7 +112,7 @@ node_t *lookup_node_udp(meshlink_handle_t *mesh, const sockaddr_t *sa) {
 
 void update_node_udp(meshlink_handle_t *mesh, node_t *n, const sockaddr_t *sa) {
        if(n == mesh->self) {
-               logger(DEBUG_ALWAYS, LOG_WARNING, "Trying to update UDP address of mesh->self!");
+               logger(mesh, MESHLINK_WARNING, "Trying to update UDP address of mesh->self!");
                return;
        }
 
@@ -137,6 +130,6 @@ void update_node_udp(meshlink_handle_t *mesh, node_t *n, const sockaddr_t *sa) {
                hash_insert(mesh->node_udp_cache, sa, n);
                free(n->hostname);
                n->hostname = sockaddr2hostname(&n->address);
-               logger(DEBUG_PROTOCOL, LOG_DEBUG, "UDP address of %s set to %s", n->name, n->hostname);
+               logger(mesh, MESHLINK_DEBUG, "UDP address of %s set to %s", n->name, n->hostname);
        }
 }
index 427bc83a000907c6f8fb7e65602dd704ab651397..5761e4d747e5544c2d5189fdaed074eaa597ac12 100644 (file)
@@ -44,6 +44,7 @@ typedef struct node_t {
        void *priv;
 
        uint32_t options;                       /* options turned on for this node */
+       dev_class_t devclass;
 
        struct meshlink_handle *mesh;           /* The mesh this node belongs to */
 
@@ -69,6 +70,8 @@ typedef struct node_t {
        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;
+       time_t last_successfull_connection;
 
        uint32_t sent_seqno;                    /* Sequence number last sent to this node */
        uint32_t received_seqno;                /* Sequence number last received from this node */
@@ -102,7 +105,7 @@ 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, char *);
+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 *);
 
index 4cf351551820d4753206cbcb94ae5b7a9c8ca719..e22053c79148758fa2c4360f3397df284052ca14 100644 (file)
@@ -77,12 +77,12 @@ bool send_request(meshlink_handle_t *mesh, connection_t *c, const char *format,
        va_end(args);
 
        if(len < 0 || len > MAXBUFSIZE - 1) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Output buffer overflow while sending request to %s (%s)",
+               logger(mesh, MESHLINK_ERROR, "Output buffer overflow while sending request to %s (%s)",
                           c->name, c->hostname);
                return false;
        }
 
-       logger(DEBUG_META, LOG_DEBUG, "Sending %s to %s (%s): %s", request_name[atoi(request)], c->name, c->hostname, request);
+       logger(mesh, MESHLINK_DEBUG, "Sending %s to %s (%s): %s", request_name[atoi(request)], c->name, c->hostname, request);
 
        request[len++] = '\n';
 
@@ -94,7 +94,7 @@ bool send_request(meshlink_handle_t *mesh, connection_t *c, const char *format,
 }
 
 void forward_request(meshlink_handle_t *mesh, connection_t *from, const char *request) {
-       logger(DEBUG_META, LOG_DEBUG, "Forwarding %s from %s (%s): %s", request_name[atoi(request)], from->name, from->hostname, request);
+       logger(mesh, MESHLINK_DEBUG, "Forwarding %s from %s (%s): %s", request_name[atoi(request)], from->name, from->hostname, request);
 
        // Create a temporary newline-terminated copy of the request
        int len = strlen(request);
@@ -110,10 +110,10 @@ bool receive_request(meshlink_handle_t *mesh, connection_t *c, const char *reque
                        return true;
                if(!strncasecmp(request, "HTTP/1.1 ", 9)) {
                        if(!strncmp(request + 9, "200", 3)) {
-                               logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Proxy request granted");
+                               logger(mesh, MESHLINK_DEBUG, "Proxy request granted");
                                return true;
                        } else {
-                               logger(DEBUG_ALWAYS, LOG_DEBUG, "Proxy request rejected: %s", request + 9);
+                               logger(mesh, MESHLINK_DEBUG, "Proxy request rejected: %s", request + 9);
                                return false;
                        }
                }
@@ -123,25 +123,25 @@ bool receive_request(meshlink_handle_t *mesh, connection_t *c, const char *reque
 
        if(reqno || *request == '0') {
                if((reqno < 0) || (reqno >= LAST) || !request_handlers[reqno]) {
-                       logger(DEBUG_META, LOG_DEBUG, "Unknown request from %s (%s): %s", c->name, c->hostname, request);
+                       logger(mesh, MESHLINK_DEBUG, "Unknown request from %s (%s): %s", c->name, c->hostname, request);
                        return false;
                } else {
-                       logger(DEBUG_META, LOG_DEBUG, "Got %s from %s (%s): %s", request_name[reqno], c->name, c->hostname, request);
+                       logger(mesh, MESHLINK_DEBUG, "Got %s from %s (%s): %s", request_name[reqno], c->name, c->hostname, request);
                }
 
                if((c->allow_request != ALL) && (c->allow_request != reqno)) {
-                       logger(DEBUG_ALWAYS, LOG_ERR, "Unauthorized request from %s (%s)", c->name, c->hostname);
+                       logger(mesh, MESHLINK_ERROR, "Unauthorized request from %s (%s)", c->name, c->hostname);
                        return false;
                }
 
                if(!request_handlers[reqno](mesh, c, request)) {
                        /* Something went wrong. Probably scriptkiddies. Terminate. */
 
-                       logger(DEBUG_ALWAYS, LOG_ERR, "Error while processing %s from %s (%s)", request_name[reqno], c->name, c->hostname);
+                       logger(mesh, MESHLINK_ERROR, "Error while processing %s from %s (%s)", request_name[reqno], c->name, c->hostname);
                        return false;
                }
        } else {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Bogus data received from %s (%s)", c->name, c->hostname);
+               logger(mesh, MESHLINK_ERROR, "Bogus data received from %s (%s)", c->name, c->hostname);
                return false;
        }
 
@@ -171,7 +171,7 @@ static void age_past_requests(event_loop_t *loop, void *data) {
        }
 
        if(left || deleted)
-               logger(DEBUG_SCARY_THINGS, LOG_DEBUG, "Aging past requests: deleted %d, left %d", deleted, left);
+               logger(mesh, MESHLINK_DEBUG, "Aging past requests: deleted %d, left %d", deleted, left);
 
        if(left)
                timeout_set(&mesh->loop, &mesh->past_request_timeout, &(struct timeval){10, rand() % 100000});
@@ -183,7 +183,7 @@ bool seen_request(meshlink_handle_t *mesh, const char *request) {
        p.request = request;
 
        if(splay_search(mesh->past_request_tree, &p)) {
-               logger(DEBUG_SCARY_THINGS, LOG_DEBUG, "Already seen request");
+               logger(mesh, MESHLINK_DEBUG, "Already seen request");
                return true;
        } else {
                new = xmalloc(sizeof *new);
@@ -200,7 +200,9 @@ void init_requests(meshlink_handle_t *mesh) {
 }
 
 void exit_requests(meshlink_handle_t *mesh) {
-       splay_delete_tree(mesh->past_request_tree);
+       if(mesh->past_request_tree)
+               splay_delete_tree(mesh->past_request_tree);
+       mesh->past_request_tree = NULL;
 
        timeout_del(&mesh->loop, &mesh->past_request_timeout);
 }
index 9bd5ffca6dc007652af6a4b35e8d69f3247c2af0..404a81c88de8ec09fddc65f58d926a09f4935eda 100644 (file)
 #include "xalloc.h"
 #include "ed25519/sha512.h"
 
+#include <assert.h>
+
+extern bool node_write_devclass(meshlink_handle_t *mesh, node_t *n);
+
 static bool send_proxyrequest(meshlink_handle_t *mesh, connection_t *c) {
        switch(mesh->proxytype) {
                case PROXY_HTTP: {
@@ -51,7 +55,7 @@ static bool send_proxyrequest(meshlink_handle_t *mesh, connection_t *c) {
                }
                case PROXY_SOCKS4: {
                        if(c->address.sa.sa_family != AF_INET) {
-                               logger(DEBUG_ALWAYS, LOG_ERR, "Cannot connect to an IPv6 host through a SOCKS 4 proxy!");
+                               logger(mesh, MESHLINK_ERROR, "Cannot connect to an IPv6 host through a SOCKS 4 proxy!");
                                return false;
                        }
                        char s4req[9 + (mesh->proxyuser ? strlen(mesh->proxyuser) : 0)];
@@ -105,7 +109,7 @@ static bool send_proxyrequest(meshlink_handle_t *mesh, connection_t *c) {
                                i += 2;
                                c->tcplen += 22;
                        } else {
-                               logger(DEBUG_ALWAYS, LOG_ERR, "Address family %hx not supported for SOCKS 5 proxies!", c->address.sa.sa_family);
+                               logger(mesh, MESHLINK_ERROR, "Address family %hx not supported for SOCKS 5 proxies!", c->address.sa.sa_family);
                                return false;
                        }
                        if(i > len)
@@ -113,19 +117,18 @@ static bool send_proxyrequest(meshlink_handle_t *mesh, connection_t *c) {
                        return send_meta(mesh, c, s5req, sizeof s5req);
                }
                case PROXY_SOCKS4A:
-                       logger(DEBUG_ALWAYS, LOG_ERR, "Proxy type not implemented yet");
+                       logger(mesh, MESHLINK_ERROR, "Proxy type not implemented yet");
                        return false;
                case PROXY_EXEC:
                        return true;
                default:
-                       logger(DEBUG_ALWAYS, LOG_ERR, "Unknown proxy type");
+                       logger(mesh, MESHLINK_ERROR, "Unknown proxy type");
                        return false;
        }
 }
 
 bool send_id(meshlink_handle_t *mesh, connection_t *c) {
-       gettimeofday(&c->start, NULL);
-
+       
        int minor = mesh->self->connection->protocol_minor;
 
        if(mesh->proxytype && c->outgoing)
@@ -137,7 +140,7 @@ bool send_id(meshlink_handle_t *mesh, connection_t *c) {
 
 static bool finalize_invitation(meshlink_handle_t *mesh, connection_t *c, const void *data, uint16_t len) {
        if(strchr(data, '\n')) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Received invalid key from invited node %s (%s)!\n", c->name, c->hostname);
+               logger(mesh, MESHLINK_ERROR, "Received invalid key from invited node %s (%s)!\n", c->name, c->hostname);
                return false;
        }
 
@@ -145,20 +148,20 @@ static bool finalize_invitation(meshlink_handle_t *mesh, connection_t *c, const
        char filename[PATH_MAX];
        snprintf(filename, sizeof filename, "%s" SLASH "hosts" SLASH "%s", mesh->confbase, c->name);
        if(!access(filename, F_OK)) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Host config file for %s (%s) already exists!\n", c->name, c->hostname);
+               logger(mesh, MESHLINK_ERROR, "Host config file for %s (%s) already exists!\n", c->name, c->hostname);
                return false;
        }
 
        FILE *f = fopen(filename, "w");
        if(!f) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Error trying to create %s: %s\n", filename, strerror(errno));
+               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);
 
-       logger(DEBUG_CONNECTIONS, LOG_INFO, "Key succesfully received from %s (%s)", c->name, c->hostname);
+       logger(mesh, MESHLINK_INFO, "Key succesfully received from %s (%s)", c->name, c->hostname);
 
        //TODO: callback to application to inform of an accepted invitation
 
@@ -200,16 +203,16 @@ static bool receive_invitation_sptps(void *handle, uint8_t type, const void *dat
        // Atomically rename the invitation file
        if(rename(filename, usedname)) {
                if(errno == ENOENT)
-                       logger(DEBUG_ALWAYS, LOG_ERR, "Peer %s tried to use non-existing invitation %s\n", c->hostname, cookie);
+                       logger(mesh, MESHLINK_ERROR, "Peer %s tried to use non-existing invitation %s\n", c->hostname, cookie);
                else
-                       logger(DEBUG_ALWAYS, LOG_ERR, "Error trying to rename invitation %s\n", cookie);
+                       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(DEBUG_ALWAYS, LOG_ERR, "Error trying to open invitation %s\n", cookie);
+               logger(mesh, MESHLINK_ERROR, "Error trying to open invitation %s\n", cookie);
                return false;
        }
 
@@ -229,7 +232,7 @@ static bool receive_invitation_sptps(void *handle, uint8_t type, const void *dat
        buf[len] = 0;
 
        if(!*buf || !*name || strcasecmp(buf, "Name") || !check_id(name)) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Invalid invitation file %s\n", cookie);
+               logger(mesh, MESHLINK_ERROR, "Invalid invitation file %s\n", cookie);
                fclose(f);
                return false;
        }
@@ -248,7 +251,7 @@ static bool receive_invitation_sptps(void *handle, uint8_t type, const void *dat
 
        c->status.invitation_used = true;
 
-       logger(DEBUG_CONNECTIONS, LOG_INFO, "Invitation %s succesfully sent to %s (%s)", cookie, c->name, c->hostname);
+       logger(mesh, MESHLINK_INFO, "Invitation %s succesfully sent to %s (%s)", cookie, c->name, c->hostname);
        return true;
 }
 
@@ -256,7 +259,7 @@ bool id_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
        char name[MAX_STRING_SIZE];
 
        if(sscanf(request, "%*d " MAX_STRING " %d.%d", name, &c->protocol_major, &c->protocol_minor) < 2) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "ID", c->name,
+               logger(mesh, MESHLINK_ERROR, "Got bad %s from %s (%s)", "ID", c->name,
                           c->hostname);
                return false;
        }
@@ -265,13 +268,13 @@ bool id_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
 
        if(name[0] == '?') {
                if(!mesh->invitation_key) {
-                       logger(DEBUG_ALWAYS, LOG_ERR, "Got invitation from %s but we don't have an invitation key", c->hostname);
+                       logger(mesh, MESHLINK_ERROR, "Got invitation from %s but we don't have an invitation key", c->hostname);
                        return false;
                }
 
                c->ecdsa = ecdsa_set_base64_public_key(name + 1);
                if(!c->ecdsa) {
-                       logger(DEBUG_ALWAYS, LOG_ERR, "Got bad invitation from %s", c->hostname);
+                       logger(mesh, MESHLINK_ERROR, "Got bad invitation from %s", c->hostname);
                        return false;
                }
 
@@ -292,7 +295,7 @@ bool id_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
        /* Check if identity is a valid name */
 
        if(!check_id(name)) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "ID", c->name,
+               logger(mesh, MESHLINK_ERROR, "Got bad %s from %s (%s): %s", "ID", c->name,
                           c->hostname, "invalid name");
                return false;
        }
@@ -301,7 +304,7 @@ bool id_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
 
        if(c->outgoing) {
                if(strcmp(c->name, name)) {
-                       logger(DEBUG_ALWAYS, LOG_ERR, "Peer %s is %s instead of %s", c->hostname, name,
+                       logger(mesh, MESHLINK_ERROR, "Peer %s is %s instead of %s", c->hostname, name,
                                   c->name);
                        return false;
                }
@@ -314,7 +317,7 @@ 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) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Peer %s (%s) uses incompatible version %d.%d",
+               logger(mesh, MESHLINK_ERROR, "Peer %s (%s) uses incompatible version %d.%d",
                        c->name, c->hostname, c->protocol_major, c->protocol_minor);
                return false;
        }
@@ -323,20 +326,29 @@ bool id_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
                init_configuration(&c->config_tree);
 
                if(!read_host_config(mesh, c->config_tree, c->name)) {
-                       logger(DEBUG_ALWAYS, LOG_ERR, "Peer %s had unknown identity (%s)", c->hostname, c->name);
+                       logger(mesh, MESHLINK_ERROR, "Peer %s had unknown identity (%s)", c->hostname, c->name);
                        return false;
                }
+       }
 
-               read_ecdsa_public_key(mesh, c);
-       } else {
-               if(c->protocol_minor && !ecdsa_active(c->ecdsa))
-                       c->protocol_minor = 1;
+       read_ecdsa_public_key(mesh, c);
+
+       if(!ecdsa_active(c->ecdsa)) {
+               logger(mesh, MESHLINK_ERROR, "No key known for peer %s (%s)", c->name, c->hostname);
+
+               node_t *n = lookup_node(mesh, c->name);
+               if(n && !n->status.waitingforkey) {
+                       logger(mesh, MESHLINK_INFO, "Requesting key from peer %s (%s)", c->name, c->hostname);
+                       send_req_key(mesh, n);
+               }
+
+               return false;
        }
 
        /* Forbid version rollback for nodes whose ECDSA key we know */
 
        if(ecdsa_active(c->ecdsa) && c->protocol_minor < 2) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Peer %s (%s) tries to roll back protocol version to %d.%d",
+               logger(mesh, MESHLINK_ERROR, "Peer %s (%s) tries to roll back protocol version to %d.%d",
                        c->name, c->hostname, c->protocol_major, c->protocol_minor);
                return false;
        }
@@ -353,22 +365,13 @@ bool id_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
 }
 
 bool send_ack(meshlink_handle_t *mesh, connection_t *c) {
-       /* ACK message contains rest of the information the other end needs
-          to create node_t and edge_t structures. */
-
-       struct timeval now;
-
-       /* Estimate weight */
-
-       gettimeofday(&now, NULL);
-       c->estimated_weight = (now.tv_sec - c->start.tv_sec) * 1000 + (now.tv_usec - c->start.tv_usec) / 1000;
 
        /* Check some options */
 
        if(mesh->self->options & OPTION_PMTU_DISCOVERY)
                c->options |= OPTION_PMTU_DISCOVERY;
 
-       return send_request(mesh, c, "%d %s %d %x", ACK, mesh->myport, c->estimated_weight, (c->options & 0xffffff) | (PROT_MINOR << 24));
+       return send_request(mesh, c, "%d %s %d %x", ACK, mesh->myport, mesh->devclass, (c->options & 0xffffff) | (PROT_MINOR << 24));
 }
 
 static void send_everything(meshlink_handle_t *mesh, connection_t *c) {
@@ -383,16 +386,22 @@ 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 weight;
+       int devclass;
        uint32_t options;
        node_t *n;
 
-       if(sscanf(request, "%*d " MAX_STRING " %d %x", hisport, &weight, &options) != 3) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "ACK", c->name,
+       if(sscanf(request, "%*d " MAX_STRING " %d %x", hisport, &devclass, &options) != 3) {
+               logger(mesh, MESHLINK_ERROR, "Got bad %s from %s (%s)", "ACK", c->name,
                           c->hostname);
                return false;
        }
 
+       if(devclass < 0 || devclass > _DEV_CLASS_MAX) {
+               logger(mesh, MESHLINK_ERROR, "Got bad %s from %s (%s): %s", "ACK", c->name,
+                          c->hostname, "devclass invalid");
+               return false;
+       }
+
        /* Check if we already have a node_t for him */
 
        n = lookup_node(mesh, c->name);
@@ -404,11 +413,11 @@ bool ack_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
        } else {
                if(n->connection) {
                        /* Oh dear, we already have a connection to this node. */
-                       logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Established a second connection with %s (%s), closing old connection", n->connection->name, n->connection->hostname);
+                       logger(mesh, MESHLINK_DEBUG, "Established a second connection with %s (%s), closing old connection", n->connection->name, n->connection->hostname);
 
                        if(n->connection->outgoing) {
                                if(c->outgoing)
-                                       logger(DEBUG_ALWAYS, LOG_WARNING, "Two outgoing connections to the same node!");
+                                       logger(mesh, MESHLINK_WARNING, "Two outgoing connections to the same node!");
                                else
                                        c->outgoing = n->connection->outgoing;
 
@@ -421,6 +430,11 @@ bool ack_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
                }
        }
 
+       n->devclass = devclass;
+       node_write_devclass(mesh, n);
+
+       n->last_successfull_connection = time(NULL);
+
        n->connection = c;
        c->node = n;
        if(!(c->options & options & OPTION_PMTU_DISCOVERY)) {
@@ -434,7 +448,7 @@ bool ack_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
        c->allow_request = ALL;
        c->status.active = true;
 
-       logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Connection with %s (%s) activated", c->name,
+       logger(mesh, MESHLINK_INFO, "Connection with %s (%s) activated", c->name,
                           c->hostname);
 
        /* Send him everything we know */
@@ -443,13 +457,15 @@ bool ack_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
 
        /* Create an edge_t for this connection */
 
+       assert(devclass >= 0 && devclass <= _DEV_CLASS_MAX);
+
        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);
-       c->edge->weight = (weight + c->estimated_weight) / 2;
+       c->edge->weight = dev_class_traits[devclass].edge_weight;
        c->edge->connection = c;
        c->edge->options = c->options;
 
index 3b89cdec9bd438cf8169e5f310de91b796ca1714..89ed901d1979b0e04e5a865dc18deb78a2e2700f 100644 (file)
 #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) {
        bool x;
        char *address, *port;
 
        sockaddr2str(&e->address, &address, &port);
 
-       x = send_request(mesh, c, "%d %x %s %s %s %s %x %d", ADD_EDGE, rand(),
-                                        e->from->name, e->to->name, address, port,
+       x = send_request(mesh, c, "%d %x %s %d %s %s %s %d %x %d", ADD_EDGE, rand(),
+                                        e->from->name, e->from->devclass, e->to->name, address, port, e->to->devclass,
                                         e->options, e->weight);
        free(address);
        free(port);
@@ -52,16 +54,18 @@ bool add_edge_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
        edge_t *e;
        node_t *from, *to;
        char from_name[MAX_STRING_SIZE];
+       int from_devclass;
        char to_name[MAX_STRING_SIZE];
        char to_address[MAX_STRING_SIZE];
        char to_port[MAX_STRING_SIZE];
+       int to_devclass;
        sockaddr_t address;
        uint32_t options;
        int weight;
 
-       if(sscanf(request, "%*d %*x "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" %x %d",
-                         from_name, to_name, to_address, to_port, &options, &weight) != 6) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "ADD_EDGE", c->name,
+       if(sscanf(request, "%*d %*x "MAX_STRING" %d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %x %d",
+                         from_name, &from_devclass, to_name, to_address, to_port, &to_devclass, &options, &weight) != 8) {
+               logger(mesh, MESHLINK_ERROR, "Got bad %s from %s (%s)", "ADD_EDGE", c->name,
                           c->hostname);
                return false;
        }
@@ -69,11 +73,25 @@ bool add_edge_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
        /* Check if names are valid */
 
        if(!check_id(from_name) || !check_id(to_name)) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "ADD_EDGE", c->name,
+               logger(mesh, MESHLINK_ERROR, "Got bad %s from %s (%s): %s", "ADD_EDGE", c->name,
                           c->hostname, "invalid name");
                return false;
        }
 
+       // Check if devclasses are valid
+
+       if(from_devclass < 0 || from_devclass > _DEV_CLASS_MAX) {
+               logger(mesh, MESHLINK_ERROR, "Got bad %s from %s (%s): %s", "ADD_EDGE", c->name,
+                          c->hostname, "from devclass invalid");
+               return false;
+       }
+
+       if(to_devclass < 0 || to_devclass > _DEV_CLASS_MAX) {
+               logger(mesh, MESHLINK_ERROR, "Got bad %s from %s (%s): %s", "ADD_EDGE", c->name,
+                          c->hostname, "to devclass invalid");
+               return false;
+       }
+
        if(seen_request(mesh, request))
                return true;
 
@@ -88,12 +106,17 @@ bool add_edge_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
                node_add(mesh, from);
        }
 
+       from->devclass = from_devclass;
+       node_write_devclass(mesh, from);
+
        if(!to) {
                to = new_node();
                to->name = xstrdup(to_name);
                node_add(mesh, to);
        }
 
+       to->devclass = to_devclass;
+       node_write_devclass(mesh, to);
 
        /* Convert addresses */
 
@@ -106,12 +129,12 @@ bool add_edge_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
        if(e) {
                if(e->weight != weight || e->options != options || sockaddrcmp(&e->address, &address)) {
                        if(from == mesh->self) {
-                               logger(DEBUG_PROTOCOL, LOG_WARNING, "Got %s from %s (%s) for ourself which does not match existing entry",
+                               logger(mesh, MESHLINK_WARNING, "Got %s from %s (%s) for ourself which does not match existing entry",
                                                   "ADD_EDGE", c->name, c->hostname);
                                send_add_edge(mesh, c, e);
                                return true;
                        } else {
-                               logger(DEBUG_PROTOCOL, LOG_WARNING, "Got %s from %s (%s) which does not match existing entry",
+                               logger(mesh, MESHLINK_WARNING, "Got %s from %s (%s) which does not match existing entry",
                                                   "ADD_EDGE", c->name, c->hostname);
                                edge_del(mesh, e);
                                graph(mesh);
@@ -119,7 +142,7 @@ bool add_edge_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
                } else
                        return true;
        } else if(from == mesh->self) {
-               logger(DEBUG_PROTOCOL, LOG_WARNING, "Got %s from %s (%s) for ourself which does not exist",
+               logger(mesh, MESHLINK_WARNING, "Got %s from %s (%s) for ourself which does not exist",
                                   "ADD_EDGE", c->name, c->hostname);
                mesh->contradicting_add_edge++;
                e = new_edge();
@@ -161,7 +184,7 @@ bool del_edge_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
        node_t *from, *to;
 
        if(sscanf(request, "%*d %*x "MAX_STRING" "MAX_STRING, from_name, to_name) != 2) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "DEL_EDGE", c->name,
+               logger(mesh, MESHLINK_ERROR, "Got bad %s from %s (%s)", "DEL_EDGE", c->name,
                           c->hostname);
                return false;
        }
@@ -169,7 +192,7 @@ bool del_edge_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
        /* Check if names are valid */
 
        if(!check_id(from_name) || !check_id(to_name)) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "DEL_EDGE", c->name,
+               logger(mesh, MESHLINK_ERROR, "Got bad %s from %s (%s): %s", "DEL_EDGE", c->name,
                           c->hostname, "invalid name");
                return false;
        }
@@ -183,13 +206,13 @@ bool del_edge_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
        to = lookup_node(mesh, to_name);
 
        if(!from) {
-               logger(DEBUG_PROTOCOL, LOG_ERR, "Got %s from %s (%s) which does not appear in the edge tree",
+               logger(mesh, MESHLINK_ERROR, "Got %s from %s (%s) which does not appear in the edge tree",
                                   "DEL_EDGE", c->name, c->hostname);
                return true;
        }
 
        if(!to) {
-               logger(DEBUG_PROTOCOL, LOG_ERR, "Got %s from %s (%s) which does not appear in the edge tree",
+               logger(mesh, MESHLINK_ERROR, "Got %s from %s (%s) which does not appear in the edge tree",
                                   "DEL_EDGE", c->name, c->hostname);
                return true;
        }
@@ -199,13 +222,13 @@ bool del_edge_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
        e = lookup_edge(from, to);
 
        if(!e) {
-               logger(DEBUG_PROTOCOL, LOG_WARNING, "Got %s from %s (%s) which does not appear in the edge tree",
+               logger(mesh, MESHLINK_WARNING, "Got %s from %s (%s) which does not appear in the edge tree",
                                   "DEL_EDGE", c->name, c->hostname);
                return true;
        }
 
        if(e->from == mesh->self) {
-               logger(DEBUG_PROTOCOL, LOG_WARNING, "Got %s from %s (%s) for ourself",
+               logger(mesh, MESHLINK_WARNING, "Got %s from %s (%s) for ourself",
                                   "DEL_EDGE", c->name, c->hostname);
                mesh->contradicting_del_edge++;
                send_add_edge(mesh, c, e);    /* Send back a correction */
index 97e19610ce7164ad0e17fabdc6d59f3fe0a36a65..e138dd6bd4f2aeab6b6b390a8f585aaa19ac85e0 100644 (file)
@@ -46,7 +46,7 @@ bool key_changed_h(meshlink_handle_t *mesh, connection_t *c, const char *request
        node_t *n;
 
        if(sscanf(request, "%*d %*x " MAX_STRING, name) != 1) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "KEY_CHANGED",
+               logger(mesh, MESHLINK_ERROR, "Got bad %s from %s (%s)", "KEY_CHANGED",
                           c->name, c->hostname);
                return false;
        }
@@ -57,7 +57,7 @@ bool key_changed_h(meshlink_handle_t *mesh, connection_t *c, const char *request
        n = lookup_node(mesh, name);
 
        if(!n) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Got %s from %s (%s) origin %s which does not exist",
+               logger(mesh, MESHLINK_ERROR, "Got %s from %s (%s) origin %s which does not exist",
                           "KEY_CHANGED", c->name, c->hostname, name);
                return true;
        }
@@ -80,13 +80,13 @@ 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)) {
-               logger(DEBUG_PROTOCOL, LOG_DEBUG, "No ECDSA key known for %s (%s)", to->name, to->hostname);
+               logger(mesh, MESHLINK_DEBUG, "No ECDSA key known for %s (%s)", to->name, to->hostname);
                send_request(mesh, to->nexthop->connection, "%d %s %s %d", REQ_KEY, mesh->self->name, to->name, REQ_PUBKEY);
                return true;
        }
 
        if(to->sptps.label)
-               logger(DEBUG_ALWAYS, LOG_DEBUG, "send_req_key(%s) called while sptps->label != NULL!", to->name);
+               logger(mesh, MESHLINK_DEBUG, "send_req_key(%s) called while sptps->label != NULL!", to->name);
 
        char label[25 + strlen(mesh->self->name) + strlen(to->name)];
        snprintf(label, sizeof label, "MeshLink UDP key expansion %s %s", mesh->self->name, to->name);
@@ -111,36 +111,36 @@ static bool req_key_ext_h(meshlink_handle_t *mesh, connection_t *c, const char *
 
                case ANS_PUBKEY: {
                        if(node_read_ecdsa_public_key(mesh, from)) {
-                               logger(DEBUG_PROTOCOL, LOG_WARNING, "Got ANS_PUBKEY from %s (%s) even though we already have his pubkey", from->name, from->hostname);
+                               logger(mesh, MESHLINK_WARNING, "Got ANS_PUBKEY from %s (%s) even though we already have his pubkey", from->name, from->hostname);
                                return true;
                        }
 
                        char pubkey[MAX_STRING_SIZE];
                        if(sscanf(request, "%*d %*s %*s %*d " MAX_STRING, pubkey) != 1 || !(from->ecdsa = ecdsa_set_base64_public_key(pubkey))) {
-                               logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "ANS_PUBKEY", from->name, from->hostname, "invalid pubkey");
+                               logger(mesh, MESHLINK_ERROR, "Got bad %s from %s (%s): %s", "ANS_PUBKEY", from->name, from->hostname, "invalid pubkey");
                                return true;
                        }
 
-                       logger(DEBUG_PROTOCOL, LOG_INFO, "Learned ECDSA public key from %s (%s)", from->name, from->hostname);
+                       logger(mesh, MESHLINK_INFO, "Learned ECDSA public key from %s (%s)", from->name, from->hostname);
                        append_config_file(mesh, from->name, "ECDSAPublicKey", pubkey);
                        return true;
                }
 
                case REQ_KEY: {
                        if(!node_read_ecdsa_public_key(mesh, from)) {
-                               logger(DEBUG_PROTOCOL, LOG_DEBUG, "No ECDSA key known for %s (%s)", from->name, from->hostname);
+                               logger(mesh, MESHLINK_DEBUG, "No ECDSA key known for %s (%s)", from->name, from->hostname);
                                send_request(mesh, from->nexthop->connection, "%d %s %s %d", REQ_KEY, mesh->self->name, from->name, REQ_PUBKEY);
                                return true;
                        }
 
                        if(from->sptps.label)
-                               logger(DEBUG_ALWAYS, LOG_DEBUG, "Got REQ_KEY from %s while we already started a SPTPS session!", from->name);
+                               logger(mesh, MESHLINK_DEBUG, "Got REQ_KEY from %s while we already started a SPTPS session!", from->name);
 
                        char buf[MAX_STRING_SIZE];
                        int len;
 
                        if(sscanf(request, "%*d %*s %*s %*d " MAX_STRING, buf) != 1 || !(len = b64decode(buf, buf, strlen(buf)))) {
-                               logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "REQ_SPTPS_START", from->name, from->hostname, "invalid SPTPS data");
+                               logger(mesh, MESHLINK_ERROR, "Got bad %s from %s (%s): %s", "REQ_SPTPS_START", from->name, from->hostname, "invalid SPTPS data");
                                return true;
                        }
 
@@ -157,14 +157,14 @@ static bool req_key_ext_h(meshlink_handle_t *mesh, connection_t *c, const char *
 
                case REQ_SPTPS: {
                        if(!from->status.validkey) {
-                               logger(DEBUG_PROTOCOL, LOG_ERR, "Got REQ_SPTPS from %s (%s) but we don't have a valid key yet", from->name, from->hostname);
+                               logger(mesh, MESHLINK_ERROR, "Got REQ_SPTPS from %s (%s) but we don't have a valid key yet", from->name, from->hostname);
                                return true;
                        }
 
                        char buf[MAX_STRING_SIZE];
                        int len;
                        if(sscanf(request, "%*d %*s %*s %*d " MAX_STRING, buf) != 1 || !(len = b64decode(buf, buf, strlen(buf)))) {
-                               logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "REQ_SPTPS", from->name, from->hostname, "invalid SPTPS data");
+                               logger(mesh, MESHLINK_ERROR, "Got bad %s from %s (%s): %s", "REQ_SPTPS", from->name, from->hostname, "invalid SPTPS data");
                                return true;
                        }
                        sptps_receive_data(&from->sptps, buf, len);
@@ -172,7 +172,7 @@ static bool req_key_ext_h(meshlink_handle_t *mesh, connection_t *c, const char *
                }
 
                default:
-                       logger(DEBUG_ALWAYS, LOG_ERR, "Unknown extended REQ_KEY request from %s (%s): %s", from->name, from->hostname, request);
+                       logger(mesh, MESHLINK_ERROR, "Unknown extended REQ_KEY request from %s (%s): %s", from->name, from->hostname, request);
                        return true;
        }
 }
@@ -184,20 +184,20 @@ bool req_key_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
        int reqno = 0;
 
        if(sscanf(request, "%*d " MAX_STRING " " MAX_STRING " %d", from_name, to_name, &reqno) < 2) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "REQ_KEY", c->name,
+               logger(mesh, MESHLINK_ERROR, "Got bad %s from %s (%s)", "REQ_KEY", c->name,
                           c->hostname);
                return false;
        }
 
        if(!check_id(from_name) || !check_id(to_name)) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "REQ_KEY", c->name, c->hostname, "invalid name");
+               logger(mesh, MESHLINK_ERROR, "Got bad %s from %s (%s): %s", "REQ_KEY", c->name, c->hostname, "invalid name");
                return false;
        }
 
        from = lookup_node(mesh, from_name);
 
        if(!from) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Got %s from %s (%s) origin %s which does not exist in our connection list",
+               logger(mesh, MESHLINK_ERROR, "Got %s from %s (%s) origin %s which does not exist in our connection list",
                           "REQ_KEY", c->name, c->hostname, from_name);
                return true;
        }
@@ -205,7 +205,7 @@ bool req_key_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
        to = lookup_node(mesh, to_name);
 
        if(!to) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Got %s from %s (%s) destination %s which does not exist in our connection list",
+               logger(mesh, MESHLINK_ERROR, "Got %s from %s (%s) destination %s which does not exist in our connection list",
                           "REQ_KEY", c->name, c->hostname, to_name);
                return true;
        }
@@ -221,7 +221,7 @@ bool req_key_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
                send_ans_key(mesh, from);
        } else {
                if(!to->status.reachable) {
-                       logger(DEBUG_PROTOCOL, LOG_WARNING, "Got %s from %s (%s) destination %s which is not reachable",
+                       logger(mesh, MESHLINK_WARNING, "Got %s from %s (%s) destination %s which is not reachable",
                                "REQ_KEY", c->name, c->hostname, to_name);
                        return true;
                }
@@ -248,20 +248,20 @@ bool ans_key_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
        if(sscanf(request, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d "MAX_STRING" "MAX_STRING,
                from_name, to_name, key, &cipher, &digest, &maclength,
                &compression, address, port) < 7) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "ANS_KEY", c->name,
+               logger(mesh, MESHLINK_ERROR, "Got bad %s from %s (%s)", "ANS_KEY", c->name,
                           c->hostname);
                return false;
        }
 
        if(!check_id(from_name) || !check_id(to_name)) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "ANS_KEY", c->name, c->hostname, "invalid name");
+               logger(mesh, MESHLINK_ERROR, "Got bad %s from %s (%s): %s", "ANS_KEY", c->name, c->hostname, "invalid name");
                return false;
        }
 
        from = lookup_node(mesh, from_name);
 
        if(!from) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Got %s from %s (%s) origin %s which does not exist in our connection list",
+               logger(mesh, MESHLINK_ERROR, "Got %s from %s (%s) origin %s which does not exist in our connection list",
                           "ANS_KEY", c->name, c->hostname, from_name);
                return true;
        }
@@ -269,7 +269,7 @@ bool ans_key_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
        to = lookup_node(mesh, to_name);
 
        if(!to) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Got %s from %s (%s) destination %s which does not exist in our connection list",
+               logger(mesh, MESHLINK_ERROR, "Got %s from %s (%s) destination %s which does not exist in our connection list",
                           "ANS_KEY", c->name, c->hostname, to_name);
                return true;
        }
@@ -278,14 +278,14 @@ bool ans_key_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
 
        if(to != mesh->self) {
                if(!to->status.reachable) {
-                       logger(DEBUG_ALWAYS, LOG_WARNING, "Got %s from %s (%s) destination %s which is not reachable",
+                       logger(mesh, MESHLINK_WARNING, "Got %s from %s (%s) destination %s which is not reachable",
                                   "ANS_KEY", c->name, c->hostname, to_name);
                        return true;
                }
 
                if(!*address && from->address.sa.sa_family != AF_UNSPEC) {
                        char *address, *port;
-                       logger(DEBUG_PROTOCOL, LOG_DEBUG, "Appending reflexive UDP address to ANS_KEY from %s to %s", from->name, to->name);
+                       logger(mesh, MESHLINK_DEBUG, "Appending reflexive UDP address to ANS_KEY from %s to %s", from->name, to->name);
                        sockaddr2str(&from->address, &address, &port);
                        send_request(mesh, to->nexthop->connection, "%s %s %s", request, address, port);
                        free(address);
@@ -300,7 +300,7 @@ bool ans_key_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
        from->status.validkey = false;
 
        if(compression < 0 || compression > 11) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Node %s (%s) uses bogus compression level!", from->name, from->hostname);
+               logger(mesh, MESHLINK_ERROR, "Node %s (%s) uses bogus compression level!", from->name, from->hostname);
                return true;
        }
 
@@ -312,11 +312,11 @@ bool ans_key_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
        int len = b64decode(key, buf, strlen(key));
 
        if(!len || !sptps_receive_data(&from->sptps, buf, len))
-               logger(DEBUG_ALWAYS, LOG_ERR, "Error processing SPTPS data from %s (%s)", from->name, from->hostname);
+               logger(mesh, MESHLINK_ERROR, "Error processing SPTPS data from %s (%s)", from->name, from->hostname);
 
        if(from->status.validkey) {
                if(*address && *port) {
-                       logger(DEBUG_PROTOCOL, LOG_DEBUG, "Using reflexive UDP address from %s: %s port %s", from->name, address, port);
+                       logger(mesh, MESHLINK_DEBUG, "Using reflexive UDP address from %s: %s port %s", from->name, address, port);
                        sockaddr_t sa = str2sockaddr(address, port);
                        update_node_udp(mesh, from, &sa);
                }
index 84a2531c8fe6a9feab8892dfcfb450f923d4e3dd..7e9ebe5012b9155d6b5db050e7d762e147e48035 100644 (file)
@@ -45,12 +45,12 @@ bool status_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
        char statusstring[MAX_STRING_SIZE];
 
        if(sscanf(request, "%*d %d " MAX_STRING, &statusno, statusstring) != 2) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "STATUS",
+               logger(mesh, MESHLINK_ERROR, "Got bad %s from %s (%s)", "STATUS",
                           c->name, c->hostname);
                return false;
        }
 
-       logger(DEBUG_STATUS, LOG_NOTICE, "Status message from %s (%s): %d: %s",
+       logger(mesh, MESHLINK_INFO, "Status message from %s (%s): %d: %s",
                           c->name, c->hostname, statusno, statusstring);
 
        return true;
@@ -68,12 +68,12 @@ bool error_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
        char errorstring[MAX_STRING_SIZE];
 
        if(sscanf(request, "%*d %d " MAX_STRING, &err, errorstring) != 2) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "ERROR",
+               logger(mesh, MESHLINK_ERROR, "Got bad %s from %s (%s)", "ERROR",
                           c->name, c->hostname);
                return false;
        }
 
-       logger(DEBUG_ERROR, LOG_NOTICE, "Error message from %s (%s): %d: %s",
+       logger(mesh, MESHLINK_INFO, "Error message from %s (%s): %d: %s",
                           c->name, c->hostname, err, errorstring);
 
        return false;
@@ -138,7 +138,7 @@ bool tcppacket_h(meshlink_handle_t *mesh, connection_t *c, const char *request)
        short int len;
 
        if(sscanf(request, "%*d %hd", &len) != 1) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "PACKET", c->name,
+               logger(mesh, MESHLINK_ERROR, "Got bad %s from %s (%s)", "PACKET", c->name,
                           c->hostname);
                return false;
        }
index de81c2b68ea5de8f398114fb80c2482317a4eb39..f451d8e35ebb8229d987746b970b17de4d55049c 100644 (file)
@@ -29,9 +29,7 @@ bool decrement_ttl = false;
 
 static bool checklength(node_t *source, vpn_packet_t *packet, uint16_t length) {
        if(packet->len < length) {
-               logger(DEBUG_TRAFFIC, LOG_WARNING,
-                      "Got too short packet from %s (%s)", source->name,
-                      source->hostname);
+               logger(source->mesh, MESHLINK_WARNING, "Got too short packet from %s (%s)", source->name, source->hostname);
                return false;
        } else
                return true;
@@ -44,9 +42,7 @@ void route(meshlink_handle_t *mesh, node_t *source, vpn_packet_t *packet) {
        node_t *via = NULL;
        meshlink_packethdr_t *hdr = (meshlink_packethdr_t *) packet->data;
        owner = lookup_node(mesh, (char *)hdr->destination);
-       logger(DEBUG_TRAFFIC, LOG_WARNING,
-              "Routing packet from: %s . To: %s \n", hdr->source,
-              hdr->destination);
+       logger(mesh, MESHLINK_DEBUG, "Routing packet from \"%s\" to \"%s\"\n", hdr->source, hdr->destination);
 
        //Check Lenght
        if(!checklength(source, packet, (sizeof(meshlink_packethdr_t))))
@@ -54,13 +50,13 @@ void route(meshlink_handle_t *mesh, node_t *source, vpn_packet_t *packet) {
 
        if(owner == NULL) {
                //Lookup failed
-               logger(DEBUG_TRAFFIC, LOG_WARNING, "Cant lookup the owner of a packet in the route() function. This should never happen!\n");
-               logger(DEBUG_TRAFFIC, LOG_WARNING, "Destination was: %s\n", hdr->destination);
+               logger(mesh, MESHLINK_WARNING, "Cant lookup the owner of a packet in the route() function. This should never happen!\n");
+               logger(mesh, MESHLINK_WARNING, "Destination was: %s\n", hdr->destination);
                return;
        }
 
        if(owner == mesh->self) {
-               logger(DEBUG_TRAFFIC, LOG_WARNING, "I received a packet for me with payload: %s \n", packet->data + sizeof *hdr);
+               logger(mesh, MESHLINK_DEBUG, "I received a packet for me with payload: %s \n", packet->data + sizeof *hdr);
                if(mesh->receive_cb)
                        mesh->receive_cb(mesh, (meshlink_node_t *)source, packet->data + sizeof *hdr, packet->len - sizeof *hdr);
                return;
@@ -68,13 +64,13 @@ void route(meshlink_handle_t *mesh, node_t *source, vpn_packet_t *packet) {
 
        if(!owner->status.reachable) {
                //TODO: check what to do here, not just print a warning
-               logger(DEBUG_TRAFFIC, LOG_WARNING, "The owner of a packet in the route() function is unreachable. Dropping packet.\n");
+               logger(mesh, MESHLINK_WARNING, "The owner of a packet in the route() function is unreachable. Dropping packet.\n");
                return;
        }
 
        via = (owner->via == mesh->self) ? owner->nexthop : owner->via;
        if(via == source) {
-               logger(DEBUG_TRAFFIC, LOG_ERR, "Routing loop for packet from %s (%s)!", source->name, source->hostname);
+               logger(mesh, MESHLINK_ERROR, "Routing loop for packet from %s (%s)!", source->name, source->hostname);
                return;
        }
 
index 255dc83018f04a744062a714ac2e6485ab896ec1..775f81f6a36409dfcbee42a7183582885f4339be 100644 (file)
@@ -22,9 +22,7 @@ typedef union sockaddr_t {
        struct sockaddr_in in;
        struct sockaddr_in6 in6;
        struct sockaddr_unknown unknown;
-#ifdef HAVE_STRUCT_SOCKADDR_STORAGE
        struct sockaddr_storage storage;
-#endif
 } sockaddr_t;
 
 #endif // SOCKADDR_H
index e03b18a8b12e498182ee16b04deb37f337d90be1..2e9ac6fe63331917c320805fdac592fea91753f7 100644 (file)
@@ -371,12 +371,20 @@ static bool receive_handshake(sptps_t *s, const char *data, uint16_t len) {
 
 // Check datagram for valid HMAC
 bool sptps_verify_datagram(sptps_t *s, const void *data, size_t len) {
-       if(!s->instate || len < 21)
-               return error(s, EIO, "Received short packet");
+       if (!s->instate)
+               return error(s, EIO, "SPTPS state not ready to verify this datagram");
 
-       // TODO: just decrypt without updating the replay window
+       if(len < 21)
+               return error(s, EIO, "Received short packet in sptps_verify_datagram");
 
-       return true;
+       uint32_t seqno;
+       memcpy(&seqno, data, 4);
+       seqno = ntohl(seqno);
+       // TODO: check whether seqno makes sense, to avoid CPU intensive decrypt
+
+       char buffer[len];
+       size_t outlen;
+       return chacha_poly1305_decrypt(s->incipher, seqno, data + 4, len - 4, buffer, &outlen);
 }
 
 // Receive incoming data, datagram version.
@@ -384,7 +392,7 @@ static bool sptps_receive_data_datagram(sptps_t *s, const void *vdata, size_t le
        const char *data = vdata;
 
        if(len < (s->instate ? 21 : 5))
-               return error(s, EIO, "Received short packet");
+               return error(s, EIO, "Received short packet in sptps_receive_data_datagram");
 
        uint32_t seqno;
        memcpy(&seqno, data, 4);
@@ -557,6 +565,9 @@ bool sptps_receive_data(sptps_t *s, const void *data, size_t len) {
 
 // Start a SPTPS session.
 bool sptps_start(sptps_t *s, void *handle, bool initiator, bool datagram, ecdsa_t *mykey, ecdsa_t *hiskey, const char *label, size_t labellen, send_data_t send_data, receive_record_t receive_record) {
+       if(!s || !mykey || !hiskey || !label || !labellen || !send_data || !receive_record)
+               return error(s, EINVAL, "Invalid argument to sptps_start()");
+
        // Initialise struct sptps
        memset(s, 0, sizeof *s);
 
index 19ac6263ce2b85e1c7c420026ecad8590d91204f..c27683b57ab8e69d19bc9ae1d9116680c2cb355d 100644 (file)
@@ -30,6 +30,8 @@
 // Symbols necessary to link with logger.o
 bool send_request(void *c, const char *msg, ...) { return false; }
 void *mesh;
+void *global_log_cb;
+int global_log_level;
 bool send_meta(void *c, const char *msg , int len) { return false; }
 char *logfilename = NULL;
 struct timeval now;
index 550e0db2343a511760706f2402919f2b8790c482..b982f3878321f218786856cb744d195f62d1f7f4 100644 (file)
@@ -35,6 +35,8 @@
 // Symbols necessary to link with logger.o
 bool send_request(void *c, const char *msg, ...) { return false; }
 void *mesh;
+void *global_log_cb;
+int global_log_level;
 bool send_meta(void *c, const char *msg , int len) { return false; }
 char *logfilename = NULL;
 struct timeval now;
index 9b09455afb8c0c6e145c5ded0dc14039271d989f..002c5769df4f43288d6a1df0acc9193729a20dde 100644 (file)
@@ -6,7 +6,7 @@
 int main(int argc, char *argv[]) {
        // Open a new meshlink instance.
 
-       meshlink_handle_t *mesh = meshlink_open("basic_conf", "foo");
+       meshlink_handle_t *mesh = meshlink_open("basic_conf", "foo", "basic");
        if(!mesh) {
                fprintf(stderr, "Could not initialize configuration for foo\n");
                return 1;
@@ -46,7 +46,7 @@ int main(int argc, char *argv[]) {
 
        // Check that the name is ignored now, and that we still are "foo".
 
-       mesh = meshlink_open("basic_conf", "bar");
+       mesh = meshlink_open("basic_conf", "bar", "basic");
        if(!mesh) {
                fprintf(stderr, "Could not open configuration for foo a second time\n");
                return 1;
index d82610e3e5ce151f9dca14634200d3bf4b8d4db9..e1c103663b91b8e4903b39a20710cb5518e57561 100644 (file)
@@ -8,7 +8,7 @@ using namespace std;
 int main(int argc, char *argv[]) {
        // Open a new meshlink instance.
 
-       meshlink::mesh *mesh = meshlink::open("basicpp_conf", "foo");
+       meshlink::mesh *mesh = meshlink::open<meshlink::mesh>("basicpp_conf", "foo", "basicpp");
        if(!mesh) {
                cerr << "Could not initialize configuration for foo\n";
                return 1;
@@ -48,7 +48,7 @@ int main(int argc, char *argv[]) {
 
        // Check that the name is ignored now, and that we still are "foo".
 
-       mesh = meshlink::open("basic_conf", "bar");
+       mesh = meshlink::open<meshlink::mesh>("basic_conf", "bar", "basicpp");
        if(!mesh) {
                cerr << "Could not open configuration for foo a second time\n";
                return 1;
index 8327e8ed651a020ffdc53eb5672f3977990528b2..280902cbedd81dc76c68b02c386ca999a279cc27 100644 (file)
@@ -35,13 +35,13 @@ bool accept_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, meshlink_no
 int main(int argc, char *argv[]) {
        // Open two new meshlink instance.
 
-       meshlink_handle_t *mesh1 = meshlink_open("channels_conf.1", "foo");
+       meshlink_handle_t *mesh1 = meshlink_open("channels_conf.1", "foo", "channels");
        if(!mesh1) {
                fprintf(stderr, "Could not initialize configuration for foo\n");
                return 1;
        }
 
-       meshlink_handle_t *mesh2 = meshlink_open("channels_conf.2", "bar");
+       meshlink_handle_t *mesh2 = meshlink_open("channels_conf.2", "bar", "channels");
        if(!mesh2) {
                fprintf(stderr, "Could not initialize configuration for bar\n");
                return 1;
index 17beaaf0ea8e6a665088cb4de36baf8956d22ecd..f2229589ab5da907870561820df3fc5e8bff3814 100644 (file)
@@ -15,13 +15,13 @@ void status_cb(meshlink_handle_t *mesh, meshlink_node_t *node, bool reachable) {
 int main(int argc, char *argv[]) {
        // Open two new meshlink instance.
 
-       meshlink_handle_t *mesh1 = meshlink_open("import_export_conf.1", "foo");
+       meshlink_handle_t *mesh1 = meshlink_open("import_export_conf.1", "foo", "import-export");
        if(!mesh1) {
                fprintf(stderr, "Could not initialize configuration for foo\n");
                return 1;
        }
 
-       meshlink_handle_t *mesh2 = meshlink_open("import_export_conf.2", "bar");
+       meshlink_handle_t *mesh2 = meshlink_open("import_export_conf.2", "bar", "import-export");
        if(!mesh2) {
                fprintf(stderr, "Could not initialize configuration for bar\n");
                return 1;
@@ -86,6 +86,17 @@ int main(int argc, char *argv[]) {
                return 1;
        }
 
+       int pmtu = meshlink_get_pmtu(mesh2, meshlink_get_node(mesh2, "bar"));
+       for(int i = 0; i < 10 && !pmtu; i++) {
+               sleep(1);
+               pmtu = meshlink_get_pmtu(mesh2, meshlink_get_node(mesh2, "bar"));
+       }
+
+       if(!pmtu) {
+               fprintf(stderr, "UDP communication with bar not possible after 10 seconds\n");
+               return 1;
+       }
+
        // Clean up.
 
        meshlink_stop(mesh2);
index 4471091c2635c15162a514fa4940c71cbf5b6c02..a7bee95c0f9b9440812a4bc0745b8a38174e09fb 100644 (file)
@@ -15,13 +15,13 @@ void status_cb(meshlink_handle_t *mesh, meshlink_node_t *node, bool reachable) {
 int main(int argc, char *argv[]) {
        // Open two new meshlink instance.
 
-       meshlink_handle_t *mesh1 = meshlink_open("invite_join_conf.1", "foo");
+       meshlink_handle_t *mesh1 = meshlink_open("invite_join_conf.1", "foo", "invite-join");
        if(!mesh1) {
                fprintf(stderr, "Could not initialize configuration for foo\n");
                return 1;
        }
 
-       meshlink_handle_t *mesh2 = meshlink_open("invite_join_conf.2", "bar");
+       meshlink_handle_t *mesh2 = meshlink_open("invite_join_conf.2", "bar", "invite-join");
        if(!mesh2) {
                fprintf(stderr, "Could not initialize configuration for bar\n");
                return 1;
@@ -70,6 +70,17 @@ int main(int argc, char *argv[]) {
                return 1;
        }
 
+       int pmtu = meshlink_get_pmtu(mesh1, meshlink_get_node(mesh1, "baz"));
+       for(int i = 0; i < 10 && !pmtu; i++) {
+               sleep(1);
+               pmtu = meshlink_get_pmtu(mesh1, meshlink_get_node(mesh1, "baz"));
+       }
+
+       if(!pmtu) {
+               fprintf(stderr, "UDP communication with baz not possible after 10 seconds\n");
+               return 1;
+       }
+
        // Clean up.
 
        meshlink_stop(mesh2);
index a7f9a5af5b7e22bf55d9f085eac3a4cb1b4a3a0b..6c7ccfec545488e4a9a91bb12b1a5f4945fc7ae1 100644 (file)
@@ -8,13 +8,13 @@
 int main(int argc, char *argv[]) {
        // Open two new meshlink instance.
 
-       meshlink_handle_t *mesh1 = meshlink_open("sign_verify_conf.1", "foo");
+       meshlink_handle_t *mesh1 = meshlink_open("sign_verify_conf.1", "foo", "sign-verify");
        if(!mesh1) {
                fprintf(stderr, "Could not initialize configuration for foo\n");
                return 1;
        }
 
-       meshlink_handle_t *mesh2 = meshlink_open("sign_verify_conf.2", "bar");
+       meshlink_handle_t *mesh2 = meshlink_open("sign_verify_conf.2", "bar", "sign-verify");
        if(!mesh2) {
                fprintf(stderr, "Could not initialize configuration for bar\n");
                return 1;