From: Niklas Hofmann Date: Wed, 30 Jul 2014 23:51:55 +0000 (+0200) Subject: Merge branch 'address_hint_api' into discovery X-Git-Url: http://git.meshlink.io/?p=meshlink;a=commitdiff_plain;h=136acd9c81c813533015e566c23f55f4d51b069c;hp=77b943ba09731447a92485aa10d928e696de6ed7 Merge branch 'address_hint_api' into discovery --- diff --git a/examples/chat.c b/examples/chat.c index 3dbbb860..ace1e783 100644 --- a/examples/chat.c +++ b/examples/chat.c @@ -69,7 +69,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; } @@ -80,9 +80,9 @@ static void parse_command(meshlink_handle_t *mesh, char *buf) { if(!arg) { nodes = meshlink_get_all_nodes(mesh, nodes, &nnodes); if(!nnodes) { - fprintf(stderr, "No nodes known!\n"); + fprintf(stderr, "Could not get list of nodes: %s\n", meshlink_strerror(meshlink_errno)); } else { - printf("Known nodes:"); + printf("%zu known nodes:", nnodes); for(int i = 0; i < nnodes; i++) printf(" %s", nodes[i]->name); printf("\n"); @@ -90,7 +90,7 @@ static void parse_command(meshlink_handle_t *mesh, char *buf) { } 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 +153,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; } } @@ -184,7 +184,7 @@ int main(int argc, char *argv[]) { meshlink_handle_t *mesh = meshlink_open(confbase, nick); if(!mesh) { - fprintf(stderr, "Could not open MeshLink!\n"); + fprintf(stderr, "Could not open MeshLink: %s\n", meshlink_strerror(meshlink_errno)); return 1; } diff --git a/examples/chatpp.cc b/examples/chatpp.cc index 00863d50..f21e96de 100644 --- a/examples/chatpp.cc +++ b/examples/chatpp.cc @@ -69,7 +69,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; } @@ -79,10 +79,10 @@ static void parse_command(meshlink::mesh *mesh, char *buf) { } else if(!strcasecmp(buf, "who")) { if(!arg) { nodes = mesh->get_all_nodes(nodes, &nnodes); - if(!nnodes) { - fprintf(stderr, "No nodes known!\n"); + if(!nodes) { + fprintf(stderr, "Could not get list of nodes: %s\n", meshlink::strerror()); } else { - printf("Known nodes:"); + printf("%d known nodes:", nnodes); for(size_t i = 0; i < nnodes; i++) printf(" %s", nodes[i]->name); printf("\n"); @@ -90,7 +90,7 @@ static void parse_command(meshlink::mesh *mesh, char *buf) { } 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 +153,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; } } @@ -184,7 +184,7 @@ int main(int argc, char *argv[]) { meshlink::mesh *mesh = meshlink::open(confbase, nick); if(!mesh) { - fprintf(stderr, "Could not open MeshLink!\n"); + fprintf(stderr, "Could not open MeshLink: %s\n", meshlink::strerror()); return 1; } diff --git a/src/Makefile.am b/src/Makefile.am index 9a92c422..8b72c5b5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -65,6 +65,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 \ @@ -103,7 +104,7 @@ libmeshlink_la_SOURCES = \ libmeshlink_la_CFLAGS = -fPIC -libmeshlink_la_LIBADD = -lpthread +libmeshlink_la_LIBADD = -lpthread -lavahi-core -lavahi-common -lavahi-client libmeshlink_la_SOURCES += \ ed25519/ecdh.c \ diff --git a/src/conf.c b/src/conf.c index 36c10acb..5231a9df 100644 --- a/src/conf.c +++ b/src/conf.c @@ -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; } diff --git a/src/connection.c b/src/connection.c index 05a9d653..cbe7a9d6 100644 --- a/src/connection.c +++ b/src/connection.c @@ -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) { diff --git a/src/discovery.c b/src/discovery.c new file mode 100644 index 00000000..834822aa --- /dev/null +++ b/src/discovery.c @@ -0,0 +1,317 @@ + +#include "meshlink_internal.h" +#include "discovery.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define MESHLINK_MDNS_SERVICE_TYPE "_meshlink._tcp" +//#define MESHLINK_MDNS_SERVICE_NAME "Meshlink" +#define MESHLINK_MDNS_FINGERPRINT_KEY "fingerprint" + +static void discovery_resolve_callback( + AvahiSServiceResolver *resolver, + AVAHI_GCC_UNUSED AvahiIfIndex interface, + AVAHI_GCC_UNUSED 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, + AVAHI_GCC_UNUSED void* userdata) +{ + + meshlink_handle_t *mesh = userdata; + + /* Called whenever a service has been resolved successfully or timed out */ + + switch (event) + { + case AVAHI_RESOLVER_FAILURE: + fprintf(stderr, "(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: + { + char straddr[AVAHI_ADDRESS_STR_MAX], *strtxt; + + fprintf(stderr, "(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); + fprintf(stderr, + "\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 *fgli = avahi_string_list_find(txt, MESHLINK_MDNS_FINGERPRINT_KEY); + meshlink_node_t *node = meshlink_get_node(mesh, name); + + fprintf(stderr, "%p, %p, %s, %s\n", fgli, node, avahi_string_list_get_text(fgli), meshlink_get_fingerprint(mesh, node)); + + if( node && fgli && strcmp(avahi_string_list_get_text(fgli)+strlen(MESHLINK_MDNS_FINGERPRINT_KEY)+1, meshlink_get_fingerprint(mesh, node)) == 0 ) + { + fprintf(stderr, "Node %s is part of the mesh network - updating ip address.\n", node->name); + } + else + { + fprintf(stderr, "Node %s is not part of the mesh network - ignoring ip address.\n", node->name); + } + } + } + + avahi_s_service_resolver_free(resolver); +} + +static void discovery_entry_group_callback(AvahiServer *server, AvahiSEntryGroup *group, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) +{ + meshlink_handle_t *mesh = userdata; + + /* Called whenever the entry group state changes */ + switch (state) + { + case AVAHI_ENTRY_GROUP_ESTABLISHED: + /* The entry group has been established successfully */ + fprintf(stderr, "Service '%s' successfully established.\n", /*MESHLINK_MDNS_SERVICE_NAME*/ mesh->name); + break; + + case AVAHI_ENTRY_GROUP_COLLISION: + fprintf(stderr, "Service name collision '%s'\n", /*MESHLINK_MDNS_SERVICE_NAME*/ mesh->name); + break; + + case AVAHI_ENTRY_GROUP_FAILURE : + fprintf(stderr, "Entry group failure: %s\n", avahi_strerror(avahi_server_errno(mesh->avahi_server))); + + /* Some kind of failure happened while we were registering our services */ + avahi_simple_poll_quit(mesh->avahi_poll); + break; + + case AVAHI_ENTRY_GROUP_UNCOMMITED: + case AVAHI_ENTRY_GROUP_REGISTERING: + ; + } +} + + +static void discovery_create_services(meshlink_handle_t *mesh) +{ + fprintf(stderr, "Adding service '%s'\n", /*MESHLINK_MDNS_SERVICE_NAME*/ mesh->name); + + /* If this 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))) { + fprintf(stderr, "avahi_entry_group_new() failed: %s\n", avahi_strerror(avahi_server_errno(mesh->avahi_server))); + goto fail; + } + + /* Create some random TXT data */ + char fingerprint[1024] = ""; + snprintf(fingerprint, sizeof(fingerprint), "%s=%s", MESHLINK_MDNS_FINGERPRINT_KEY, meshlink_get_fingerprint(mesh, meshlink_get_node(mesh, mesh->name))); + + /* Add the service for IPP */ + int ret = 0; + if ((ret = avahi_server_add_service(mesh->avahi_server, mesh->avahi_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, /*MESHLINK_MDNS_SERVICE_NAME*/ mesh->name, MESHLINK_MDNS_SERVICE_TYPE, NULL, NULL, mesh->myport ? atoi(mesh->myport) : 655, fingerprint, NULL)) < 0) { + fprintf(stderr, "Failed to add _ipp._tcp 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) { + fprintf(stderr, "Failed to commit entry_group: %s\n", avahi_strerror(ret)); + goto fail; + } + + return; + +fail: + avahi_simple_poll_quit(mesh->avahi_poll); +} + +static void discovery_server_callback(AvahiServer *server, AvahiServerState state, AVAHI_GCC_UNUSED void * userdata) +{ + meshlink_handle_t *mesh = userdata; + + 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: + /* A host name collision happened. Let's do nothing */ + 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); + break; + + case AVAHI_SERVER_FAILURE: + /* Terminate on failure */ + fprintf(stderr, "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; + } +} + +static void discovery_browse_callback( + AvahiSServiceBrowser *browser, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name, + const char *type, + const char *domain, + AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, + void* userdata) +{ + meshlink_handle_t *mesh = userdata; + + /* Called whenever a new services becomes available on the LAN or is removed from the LAN */ + + switch (event) + { + case AVAHI_BROWSER_FAILURE: + fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_server_errno(mesh->avahi_server))); + avahi_simple_poll_quit(mesh->avahi_poll); + return; + + case AVAHI_BROWSER_NEW: + fprintf(stderr, "(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. If the 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))) + fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_server_errno(mesh->avahi_server))); + break; + + case AVAHI_BROWSER_REMOVE: + fprintf(stderr, "(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: + fprintf(stderr, "(Browser) %s\n", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW"); + break; + } +} + +static void *discovery_loop(void *arg) +{ + meshlink_handle_t *mesh = arg; + + avahi_simple_poll_loop(mesh->avahi_poll); + + return NULL; +} + +bool discovery_start(meshlink_handle_t *mesh) +{ + // Allocate discovery loop object + if (!(mesh->avahi_poll = avahi_simple_poll_new())) { + fprintf(stderr, "Failed to create discovery poll object.\n"); + goto fail; + } + + // Let's set the host name for this server. + AvahiServerConfig config; + avahi_server_config_init(&config); + config.host_name = avahi_strdup(mesh->name); + config.publish_workstation = 0; + + /* 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) { + fprintf(stderr, "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, MESHLINK_MDNS_SERVICE_TYPE, NULL, 0, discovery_browse_callback, mesh))) { + fprintf(stderr, "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) { + fprintf(stderr, "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) + avahi_s_service_browser_free(mesh->avahi_browser); + + if (mesh->avahi_server) + avahi_server_free(mesh->avahi_server); + + if (mesh->avahi_poll) + avahi_simple_poll_free(mesh->avahi_poll); + + return false; +} + +void discovery_stop(meshlink_handle_t *mesh) +{ + // @TODO: Shut down + avahi_simple_poll_quit(mesh->avahi_poll); + + // Wait for the discovery thread to finish + + pthread_join(mesh->discovery_thread, NULL); + + // Clean up resources + if (mesh->avahi_browser) + avahi_s_service_browser_free(mesh->avahi_browser); + + if (mesh->avahi_server) + avahi_server_free(mesh->avahi_server); + + if (mesh->avahi_poll) + avahi_simple_poll_free(mesh->avahi_poll); +} diff --git a/src/discovery.h b/src/discovery.h new file mode 100644 index 00000000..0455d6ed --- /dev/null +++ b/src/discovery.h @@ -0,0 +1,10 @@ + +#ifndef __MESHLINK_DISCOVERY_H__ +#define __MESHLINK_DISCOVERY_H__ + +#include + +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 index e69de29b..00000000 diff --git a/src/edge.c b/src/edge.c index 455b2301..efb90dfe 100644 --- a/src/edge.c +++ b/src/edge.c @@ -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 */ diff --git a/src/meshlink.c b/src/meshlink.c index bb17fcac..8d120813 100644 --- a/src/meshlink.c +++ b/src/meshlink.c @@ -41,6 +41,7 @@ typedef struct { #include "utils.h" #include "xalloc.h" #include "ed25519/sha512.h" +#include "discovery.h" #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 @@ -627,8 +628,15 @@ 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 err) { @@ -646,6 +654,7 @@ static bool ecdsa_keygen(meshlink_handle_t *mesh) { if(!(key = ecdsa_generate())) { fprintf(stderr, "Error during key generation!\n"); + meshlink_errno = MESHLINK_EINTERNAL; return false; } else fprintf(stderr, "Done.\n"); @@ -653,8 +662,10 @@ static bool ecdsa_keygen(meshlink_handle_t *mesh) { 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); @@ -664,17 +675,19 @@ static bool ecdsa_keygen(meshlink_handle_t *mesh) { fprintf(stderr, "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); @@ -689,6 +702,7 @@ 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)); + meshlink_errno = MESHLINK_ESTORAGE; return false; } @@ -697,6 +711,7 @@ static bool meshlink_setup(meshlink_handle_t *mesh) { if(mkdir(filename, 0777) && errno != EEXIST) { fprintf(stderr, "Could not create directory %s: %s\n", filename, strerror(errno)); + meshlink_errno = MESHLINK_ESTORAGE; return false; } @@ -704,20 +719,24 @@ static bool meshlink_setup(meshlink_handle_t *mesh) { if(!access(filename, F_OK)) { fprintf(stderr, "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; + 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); @@ -730,6 +749,7 @@ meshlink_handle_t *meshlink_open(const char *confbase, const char *name) { if(!confbase || !*confbase) { fprintf(stderr, "No confbase given!\n"); + meshlink_errno = MESHLINK_EINVAL; return NULL; } @@ -741,6 +761,7 @@ meshlink_handle_t *meshlink_open(const char *confbase, const char *name) { if(!check_id(name)) { fprintf(stderr, "Invalid name given!\n"); + meshlink_errno = MESHLINK_EINVAL; return NULL; } else { usingname = true;} } @@ -765,10 +786,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; + meshlink_close(mesh); + meshlink_errno = MESHLINK_ESTORAGE; + return NULL; } } @@ -776,8 +802,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; @@ -787,8 +816,11 @@ 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; + } return mesh; } @@ -804,14 +836,17 @@ static void *meshlink_main_loop(void *arg) { } bool meshlink_start(meshlink_handle_t *mesh) { - if(!mesh) + if(!mesh) { + meshlink_errno = MESHLINK_EINVAL; return false; + } // TODO: open listening sockets first //Check that a valid name is set if(!mesh->name ) { fprintf(stderr, "No name given!\n"); + meshlink_errno = MESHLINK_EINVAL; return false; } @@ -820,17 +855,27 @@ bool meshlink_start(meshlink_handle_t *mesh) { if(pthread_create(&mesh->thread, NULL, meshlink_main_loop, mesh) != 0) { fprintf(stderr, "Could not start thread: %s\n", strerror(errno)); memset(&mesh->thread, 0, sizeof mesh->thread); + meshlink_errno = MESHLINK_EINTERNAL; return false; } mesh->threadstarted=true; + // Start discovery + if(!discovery_start(mesh)) + return false; + return true; } void meshlink_stop(meshlink_handle_t *mesh) { - if(!mesh) + if(!mesh) { + meshlink_errno = MESHLINK_EINVAL; return; + } + + // Stop discovery + discovery_stop(mesh); // Shut down the listening sockets to signal the main thread to shut down @@ -845,8 +890,10 @@ void meshlink_stop(meshlink_handle_t *mesh) { } void meshlink_close(meshlink_handle_t *mesh) { - if(!mesh || !mesh->confbase) + if(!mesh || !mesh->confbase) { + meshlink_errno = MESHLINK_EINVAL; return; + } // Close and free all resources used. @@ -873,31 +920,46 @@ void meshlink_close(meshlink_handle_t *mesh) { } void meshlink_set_receive_cb(meshlink_handle_t *mesh, meshlink_receive_cb_t cb) { - if(!mesh) + if(!mesh) { + meshlink_errno = MESHLINK_EINVAL; return; + } + mesh->receive_cb = cb; } void meshlink_set_node_status_cb(meshlink_handle_t *mesh, meshlink_node_status_cb_t cb) { - if(!mesh) + if(!mesh) { + meshlink_errno = MESHLINK_EINVAL; return; + } + mesh->node_status_cb = cb; } void meshlink_set_log_cb(meshlink_handle_t *mesh, meshlink_log_level_t level, meshlink_log_cb_t cb) { - if(!mesh) + if(!mesh) { + meshlink_errno = MESHLINK_EINVAL; return; + } + mesh->log_cb = cb; mesh->log_level = level; } bool meshlink_send(meshlink_handle_t *mesh, meshlink_node_t *destination, const void *data, size_t len) { - if(!mesh || !destination) + if(!mesh || !destination) { + meshlink_errno = MESHLINK_EINVAL; return false; + } + if(!len) return true; - if(!data) + + if(!data) { + meshlink_errno = MESHLINK_EINVAL; return false; + } /* If there is no outgoing list yet, create one. */ @@ -947,8 +1009,10 @@ void meshlink_send_from_queue(event_loop_t* el,meshlink_handle_t *mesh) { } ssize_t meshlink_get_pmtu(meshlink_handle_t *mesh, meshlink_node_t *destination) { - if(!mesh || !destination) + if(!mesh || !destination) { + meshlink_errno = MESHLINK_EINVAL; return -1; + } node_t *n = (node_t *)destination; if(!n->status.reachable) @@ -959,17 +1023,43 @@ ssize_t meshlink_get_pmtu(meshlink_handle_t *mesh, meshlink_node_t *destination) return MTU; } +char *meshlink_get_fingerprint(meshlink_handle_t *mesh, meshlink_node_t *node) { + if(!mesh || !node) { + meshlink_errno = MESHLINK_EINVAL; + return NULL; + } + + node_t *n = (node_t *)node; + + if(!node_read_ecdsa_public_key(mesh, n) || !n->ecdsa) { + meshlink_errno = MESHLINK_EINTERNAL; + return false; + } + + char *fingerprint = ecdsa_get_base64_public_key(n->ecdsa); + + if(!fingerprint) + meshlink_errno = MESHLINK_EINTERNAL; + + return fingerprint; +} + meshlink_node_t *meshlink_get_node(meshlink_handle_t *mesh, const char *name) { - if(!mesh || !name) + if(!mesh || !name) { + meshlink_errno = MESHLINK_EINVAL; return NULL; + } + return (meshlink_node_t *)lookup_node(mesh, (char *)name); // TODO: make lookup_node() use const } meshlink_node_t **meshlink_get_all_nodes(meshlink_handle_t *mesh, meshlink_node_t **nodes, size_t *nmemb) { - if(!mesh || (nmemb && !nodes)) + if(!mesh || !nmemb || (*nmemb && !nodes)) { + meshlink_errno = MESHLINK_EINVAL; return NULL; + } - meshlink_node_t **result, **p; + meshlink_node_t **result; //lock mesh->nodes pthread_mutex_lock(&(mesh->nodes_mutex)); @@ -978,6 +1068,7 @@ meshlink_node_t **meshlink_get_all_nodes(meshlink_handle_t *mesh, meshlink_node_ result = realloc(nodes, *nmemb * sizeof *nodes); if(result) { + meshlink_node_t **p = result; for splay_each(node_t, n, mesh->nodes) *p++ = (meshlink_node_t *)n; } else { @@ -992,25 +1083,43 @@ meshlink_node_t **meshlink_get_all_nodes(meshlink_handle_t *mesh, meshlink_node_ } bool meshlink_sign(meshlink_handle_t *mesh, const void *data, size_t len, void *signature, size_t *siglen) { - if(!mesh || !data || !len || !signature || !siglen) + if(!mesh || !data || !len || !signature || !siglen) { + meshlink_errno = MESHLINK_EINVAL; return false; - if(*siglen < MESHLINK_SIGLEN) + } + + if(*siglen < MESHLINK_SIGLEN) { + meshlink_errno = MESHLINK_EINVAL; return false; - if(!ecdsa_sign(mesh->self->connection->ecdsa, data, len, signature)) + } + + if(!ecdsa_sign(mesh->self->connection->ecdsa, data, len, signature)) { + meshlink_errno = MESHLINK_EINTERNAL; return false; + } + *siglen = MESHLINK_SIGLEN; 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(!mesh || !data || !len || !signature) + if(!mesh || !data || !len || !signature) { + meshlink_errno = MESHLINK_EINVAL; return false; - if(siglen != MESHLINK_SIGLEN) + } + + if(siglen != MESHLINK_SIGLEN) { + meshlink_errno = MESHLINK_EINVAL; return false; + } + struct node_t *n = (struct node_t *)source; node_read_ecdsa_public_key(mesh, n); - if(!n->ecdsa) + if(!n->ecdsa) { + meshlink_errno = MESHLINK_EINTERNAL; return false; + } + return ecdsa_verify(((struct node_t *)source)->ecdsa, data, len, signature); } @@ -1020,6 +1129,7 @@ static bool refresh_invitation_key(meshlink_handle_t *mesh) { 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)); + meshlink_errno = MESHLINK_ESTORAGE; return false; } @@ -1027,6 +1137,7 @@ static bool refresh_invitation_key(meshlink_handle_t *mesh) { DIR *dir = opendir(filename); if(!dir) { fprintf(stderr, "Could not read directory %s: %s\n", filename, strerror(errno)); + meshlink_errno = MESHLINK_ESTORAGE; return false; } @@ -1055,6 +1166,7 @@ static bool refresh_invitation_key(meshlink_handle_t *mesh) { if(errno) { fprintf(stderr, "Error while reading directory %s: %s\n", filename, strerror(errno)); closedir(dir); + meshlink_errno = MESHLINK_ESTORAGE; return false; } @@ -1079,17 +1191,20 @@ static bool refresh_invitation_key(meshlink_handle_t *mesh) { if(!f) { if(errno != ENOENT) { fprintf(stderr, "Could not read %s: %s\n", filename, strerror(errno)); + meshlink_errno = MESHLINK_ESTORAGE; return false; } mesh->invitation_key = ecdsa_generate(); if(!mesh->invitation_key) { fprintf(stderr, "Could not generate a new key!\n"); + meshlink_errno = MESHLINK_EINTERNAL; return false; } f = fopen(filename, "w"); if(!f) { fprintf(stderr, "Could not write %s: %s\n", filename, strerror(errno)); + meshlink_errno = MESHLINK_ESTORAGE; return false; } chmod(filename, 0600); @@ -1098,21 +1213,26 @@ 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) + if(!mesh->invitation_key) { fprintf(stderr, "Could not read private key from %s\n", filename); + meshlink_errno = MESHLINK_ESTORAGE; + } } return mesh->invitation_key; } bool meshlink_add_address(meshlink_handle_t *mesh, const char *address) { - if(!mesh || !address) + if(!mesh || !address) { + meshlink_errno = MESHLINK_EINVAL; return false; + } for(const char *p = address; *p; p++) { if(isalnum(*p) || *p == '-' || *p == '.' || *p == ':') continue; fprintf(stderr, "Invalid character in address: %s\n", address); + meshlink_errno = MESHLINK_EINVAL; return false; } @@ -1120,12 +1240,15 @@ bool meshlink_add_address(meshlink_handle_t *mesh, const char *address) { } char *meshlink_invite(meshlink_handle_t *mesh, const char *name) { - if(!mesh) - return false; + if(!mesh) { + meshlink_errno = MESHLINK_EINVAL; + return NULL; + } // Check validity of the new node's name if(!check_id(name)) { fprintf(stderr, "Invalid name for node.\n"); + meshlink_errno = MESHLINK_EINVAL; return NULL; } @@ -1134,12 +1257,14 @@ char *meshlink_invite(meshlink_handle_t *mesh, const char *name) { 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); + meshlink_errno = MESHLINK_EEXIST; 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); + meshlink_errno = MESHLINK_EEXIST; return NULL; } @@ -1147,11 +1272,14 @@ char *meshlink_invite(meshlink_handle_t *mesh, const char *name) { char *address = get_my_hostname(mesh); if(!address) { fprintf(stderr, "No Address known for ourselves!\n"); + meshlink_errno = MESHLINK_ERESOLV; return NULL; } - if(!refresh_invitation_key(mesh)) + if(!refresh_invitation_key(mesh)) { + meshlink_errno = MESHLINK_EINTERNAL; return NULL; + } char hash[64]; @@ -1181,6 +1309,7 @@ char *meshlink_invite(meshlink_handle_t *mesh, const char *name) { 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)); + meshlink_errno = MESHLINK_ESTORAGE; return NULL; } FILE *f = fdopen(ifd, "w"); @@ -1210,6 +1339,7 @@ char *meshlink_invite(meshlink_handle_t *mesh, const char *name) { fclose(tc); } else { fprintf(stderr, "Could not create %s: %s\n", filename, strerror(errno)); + meshlink_errno = MESHLINK_ESTORAGE; return NULL; } @@ -1229,8 +1359,10 @@ char *meshlink_invite(meshlink_handle_t *mesh, const char *name) { } bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) { - if(!mesh || !invitation) + if(!mesh || !invitation) { + meshlink_errno = MESHLINK_EINVAL; return false; + } //TODO: think of a better name for this variable, or of a different way to tokenize the invitation URL. char copy[strlen(invitation) + 1]; @@ -1271,8 +1403,10 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) { // Generate a throw-away key for the invitation. ecdsa_t *key = ecdsa_generate(); - if(!key) + if(!key) { + meshlink_errno = MESHLINK_EINTERNAL; return false; + } char *b64key = ecdsa_get_base64_public_key(key); @@ -1283,13 +1417,16 @@ 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; 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)); freeaddrinfo(ai); + meshlink_errno = MESHLINK_ENETWORK; return false; } @@ -1297,6 +1434,7 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) { fprintf(stderr, "Could not connect to %s port %s: %s\n", address, port, strerror(errno)); closesocket(mesh->sock); freeaddrinfo(ai); + meshlink_errno = MESHLINK_ENETWORK; return false; } @@ -1311,6 +1449,7 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) { 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)); closesocket(mesh->sock); + meshlink_errno = MESHLINK_ENETWORK; return false; } @@ -1322,6 +1461,7 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) { 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"); closesocket(mesh->sock); + meshlink_errno = MESHLINK_ENETWORK; return false; } @@ -1330,25 +1470,33 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) { char hishash[64]; if(sha512(fingerprint, strlen(fingerprint), hishash)) { fprintf(stderr, "Could not create hash\n%s\n", mesh->line + 2); + meshlink_errno = MESHLINK_EINTERNAL; return false; } if(memcmp(hishash, mesh->hash, 18)) { fprintf(stderr, "Peer has an invalid key!\n%s\n", mesh->line + 2); + meshlink_errno = MESHLINK_EPEER; return false; } ecdsa_t *hiskey = ecdsa_set_base64_public_key(fingerprint); - if(!hiskey) + if(!hiskey) { + meshlink_errno = MESHLINK_EINTERNAL; 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; 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; return false; + } int len; @@ -1357,11 +1505,14 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) { if(errno == EINTR) continue; fprintf(stderr, "Error reading data from %s port %s: %s\n", address, port, strerror(errno)); + meshlink_errno = MESHLINK_ENETWORK; return false; } - if(!sptps_receive_data(&mesh->sptps, mesh->line, len)) + if(!sptps_receive_data(&mesh->sptps, mesh->line, len)) { + meshlink_errno = MESHLINK_EPEER; return false; + } } sptps_stop(&mesh->sptps); @@ -1371,6 +1522,7 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) { if(!mesh->success) { fprintf(stderr, "Connection closed by peer, invitation cancelled.\n"); + meshlink_errno = MESHLINK_EPEER; return false; } @@ -1378,18 +1530,22 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) { invalid: fprintf(stderr, "Invalid invitation URL or you are already connected to a Mesh ?\n"); + meshlink_errno = MESHLINK_EINVAL; return false; } char *meshlink_export(meshlink_handle_t *mesh) { - if(!mesh) + if(!mesh) { + meshlink_errno = MESHLINK_EINVAL; return NULL; + } 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)); + meshlink_errno = MESHLINK_ESTORAGE; return NULL; } @@ -1403,6 +1559,7 @@ char *meshlink_export(meshlink_handle_t *mesh) { if(fread(buf + len - fsize - 1, fsize, 1, f) != 1) { fprintf(stderr, "Error reading from %s: %s\n", filename, strerror(errno)); fclose(f); + meshlink_errno = MESHLINK_ESTORAGE; return NULL; } @@ -1412,17 +1569,21 @@ char *meshlink_export(meshlink_handle_t *mesh) { } bool meshlink_import(meshlink_handle_t *mesh, const char *data) { - if(!mesh || !data) + if(!mesh || !data) { + meshlink_errno = MESHLINK_EINVAL; return false; + } if(strncmp(data, "Name = ", 7)) { fprintf(stderr, "Invalid data\n"); + meshlink_errno = MESHLINK_EPEER; return false; } char *end = strchr(data + 7, '\n'); if(!end) { fprintf(stderr, "Invalid data\n"); + meshlink_errno = MESHLINK_EPEER; return false; } @@ -1432,6 +1593,7 @@ bool meshlink_import(meshlink_handle_t *mesh, const char *data) { name[len] = 0; if(!check_id(name)) { fprintf(stderr, "Invalid Name\n"); + meshlink_errno = MESHLINK_EPEER; return false; } @@ -1439,17 +1601,20 @@ bool meshlink_import(meshlink_handle_t *mesh, const char *data) { 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); + meshlink_errno = MESHLINK_EEXIST; return false; } if(errno != ENOENT) { fprintf(stderr, "Error accessing %s: %s\n", filename, strerror(errno)); + meshlink_errno = MESHLINK_ESTORAGE; return false; } FILE *f = fopen(filename, "w"); if(!f) { fprintf(stderr, "Could not create %s: %s\n", filename, strerror(errno)); + meshlink_errno = MESHLINK_ESTORAGE; return false; } @@ -1462,8 +1627,10 @@ bool meshlink_import(meshlink_handle_t *mesh, const char *data) { } void meshlink_blacklist(meshlink_handle_t *mesh, meshlink_node_t *node) { - if(!mesh || !node) + if(!mesh || !node) { + meshlink_errno = MESHLINK_EINVAL; return; + } node_t *n; n = (node_t*)node; @@ -1477,8 +1644,10 @@ void meshlink_blacklist(meshlink_handle_t *mesh, meshlink_node_t *node) { } void meshlink_whitelist(meshlink_handle_t *mesh, meshlink_node_t *node) { - if(!mesh || !node) + if(!mesh || !node) { + meshlink_errno = MESHLINK_EINVAL; return; + } node_t *n = (node_t *)node; n->status.blacklisted = false; diff --git a/src/meshlink.h b/src/meshlink.h index b3059c39..8a101741 100644 --- a/src/meshlink.h +++ b/src/meshlink.h @@ -44,8 +44,15 @@ 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; /// A variable holding the last encountered error from MeshLink. @@ -59,6 +66,8 @@ extern __thread meshlink_errno_t meshlink_errno; #ifndef MESHLINK_INTERNAL_H struct meshlink_handle { + const char *name; + void *priv; }; struct meshlink_node { @@ -268,6 +277,18 @@ extern ssize_t meshlink_get_pmtu(meshlink_handle_t *mesh, meshlink_node_t *desti */ 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. * diff --git a/src/meshlink_internal.h b/src/meshlink_internal.h index 7c3a7d4c..52137efb 100644 --- a/src/meshlink_internal.h +++ b/src/meshlink_internal.h @@ -33,6 +33,11 @@ #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,10 @@ typedef struct outpacketqueue { /// A handle for an instance of MeshLink. struct meshlink_handle { - char *confbase; char *name; + void *priv; + + char *confbase; meshlink_receive_cb_t receive_cb; meshlink_node_status_cb_t node_status_cb; @@ -124,6 +131,13 @@ 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; }; /// A handle for a MeshLink node. diff --git a/src/net_setup.c b/src/net_setup.c index 8b0fd8fd..ce9a59ad 100644 --- a/src/net_setup.c +++ b/src/net_setup.c @@ -377,11 +377,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) diff --git a/src/node.c b/src/node.c index 7fc476f4..b7b39927 100644 --- a/src/node.c +++ b/src/node.c @@ -42,8 +42,12 @@ void init_nodes(meshlink_handle_t *mesh) { void exit_nodes(meshlink_handle_t *mesh) { pthread_mutex_lock(&(mesh->nodes_mutex)); - hash_free(mesh->node_udp_cache); - splay_delete_tree(mesh->nodes); + 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; pthread_mutex_unlock(&(mesh->nodes_mutex)); } diff --git a/src/protocol.c b/src/protocol.c index 4cf35155..4b7d6016 100644 --- a/src/protocol.c +++ b/src/protocol.c @@ -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); }