]> git.meshlink.io Git - meshlink/blobdiff - src/meshlink.c
Fix race condition when calling meshlink_stop() immediately after meshlink_start().
[meshlink] / src / meshlink.c
index cbe88a3ed256e06f25a72663fc60991ff4189af3..b3b18074b171c9d93b3217fcbde533b7a4a80326 100644 (file)
@@ -1,6 +1,6 @@
 /*
     meshlink.c -- Implementation of the MeshLink API.
-    Copyright (C) 2014 Guus Sliepen <guus@meshlink.io>
+    Copyright (C) 2014, 2017 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
@@ -39,6 +39,7 @@ typedef struct {
 #include "node.h"
 #include "protocol.h"
 #include "route.h"
+#include "sockaddr.h"
 #include "utils.h"
 #include "xalloc.h"
 #include "ed25519/sha512.h"
@@ -701,16 +702,74 @@ static bool ecdsa_keygen(meshlink_handle_t *mesh) {
 
 static struct timeval idle(event_loop_t *loop, void *data) {
        meshlink_handle_t *mesh = data;
-       int t, tmin = -1;
+       struct timeval t, tmin = {3600, 0};
        for splay_each(node_t, n, mesh->nodes) {
                if(!n->utcp)
                        continue;
                t = utcp_timeout(n->utcp);
-               if(t >= 0 && t < tmin)
+               if(timercmp(&t, &tmin, <))
                        tmin = t;
        }
-       struct timeval tv = {.tv_sec = t};
-       return tv;
+       return tmin;
+}
+
+// Find out what local address a socket would use if we connect to the given address.
+// We do this using connect() on a UDP socket, so the kernel has to resolve the address
+// of both endpoints, but this will actually not send any UDP packet.
+static bool getlocaladdrname(char *destaddr, char *host, socklen_t hostlen) {
+       struct addrinfo *rai = NULL;
+       const struct addrinfo hint = {
+               .ai_family = AF_UNSPEC,
+               .ai_socktype = SOCK_DGRAM,
+               .ai_protocol = IPPROTO_UDP,
+       };
+
+       if(getaddrinfo(destaddr, "80", &hint, &rai) || !rai)
+               return false;
+
+       int sock = socket(rai->ai_family, rai->ai_socktype, rai->ai_protocol);
+       if(sock == -1) {
+               freeaddrinfo(rai);
+               return false;
+       }
+
+       if(connect(sock, rai->ai_addr, rai->ai_addrlen) && !sockwouldblock(errno)) {
+               freeaddrinfo(rai);
+               return false;
+       }
+
+       freeaddrinfo(rai);
+
+       struct sockaddr_storage sn;
+       socklen_t sl = sizeof sn;
+
+       if(getsockname(sock, (struct sockaddr *)&sn, &sl))
+               return false;
+
+       if(getnameinfo((struct sockaddr *)&sn, sl, host, hostlen, NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV))
+               return false;
+
+       return true;
+}
+
+// Get our local address(es) by simulating connecting to an Internet host.
+static void add_local_addresses(meshlink_handle_t *mesh) {
+       char host[NI_MAXHOST];
+       char entry[MAX_STRING_SIZE];
+
+       // IPv4 example.org
+
+       if(getlocaladdrname("93.184.216.34", host, sizeof host)) {
+               snprintf(entry, sizeof entry, "%s %s", host, mesh->myport);
+               append_config_file(mesh, mesh->name, "Address", entry);
+       }
+
+       // IPv6 example.org
+
+       if(getlocaladdrname("2606:2800:220:1:248:1893:25c8:1946", host, sizeof host)) {
+               snprintf(entry, sizeof entry, "%s %s", host, mesh->myport);
+               append_config_file(mesh, mesh->name, "Address", entry);
+       }
 }
 
 static bool meshlink_setup(meshlink_handle_t *mesh) {
@@ -854,6 +913,8 @@ meshlink_handle_t *meshlink_open(const char *confbase, const char *name, const c
                return NULL;
        }
 
+       add_local_addresses(mesh);
+
        idle_set(&mesh->loop, idle, mesh);
 
        logger(NULL, MESHLINK_DEBUG, "meshlink_open returning\n");
@@ -884,6 +945,12 @@ bool meshlink_start(meshlink_handle_t *mesh) {
        
        logger(mesh, MESHLINK_DEBUG, "meshlink_start called\n");
 
+       if(mesh->listen_socket[0].tcp.fd < 0) {
+               logger(mesh, MESHLINK_ERROR, "Listening socket not open\n");
+               meshlink_errno = MESHLINK_ENETWORK;
+               return false;
+       }
+
        mesh->thedatalen = 0;
 
        // TODO: open listening sockets first
@@ -898,6 +965,8 @@ bool meshlink_start(meshlink_handle_t *mesh) {
 
        // Start the main thread
 
+       event_loop_start(&mesh->loop);
+
        if(pthread_create(&mesh->thread, NULL, meshlink_main_loop, mesh) != 0) {
                logger(mesh, MESHLINK_DEBUG, "Could not start thread: %s\n", strerror(errno));
                memset(&mesh->thread, 0, sizeof mesh->thread);
@@ -926,10 +995,16 @@ void meshlink_stop(meshlink_handle_t *mesh) {
        // Stop discovery
        discovery_stop(mesh);
 
-       // Shut down a listening socket to signal the main thread to shut down
+       pthread_mutex_lock(&(mesh->mesh_mutex));
+       logger(mesh, MESHLINK_DEBUG, "meshlink_stop called\n");
 
+       // Shut down the main thread
+       event_loop_stop(&mesh->loop);
+
+       // Send ourselves a UDP packet to kick the event loop
        listen_socket_t *s = &mesh->listen_socket[0];
-       shutdown(s->tcp.fd, SHUT_RDWR);
+       if(sendto(s->udp.fd, "", 1, MSG_NOSIGNAL, &s->sa.sa, SALEN(s->sa.sa)) == -1)
+               logger(mesh, MESHLINK_ERROR, "Could not send a UDP packet to ourself");
 
        // Wait for the main thread to finish
        pthread_mutex_unlock(&(mesh->mesh_mutex));
@@ -938,16 +1013,6 @@ void meshlink_stop(meshlink_handle_t *mesh) {
 
        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));
 }
 
@@ -957,6 +1022,9 @@ void meshlink_close(meshlink_handle_t *mesh) {
                return;
        }
 
+       // stop can be called even if mesh has not been started
+       meshlink_stop(mesh);
+
        // lock is not released after this
        pthread_mutex_lock(&(mesh->mesh_mutex));
 
@@ -986,6 +1054,49 @@ void meshlink_close(meshlink_handle_t *mesh) {
        free(mesh);
 }
 
+static void deltree(const char *dirname) {
+       DIR *d = opendir(dirname);
+       if(d) {
+               struct dirent *ent;
+               while((ent = readdir(d))) {
+                       if(ent->d_name[0] == '.')
+                               continue;
+                       char filename[PATH_MAX];
+                       snprintf(filename, sizeof filename, "%s" SLASH "%s", dirname, ent->d_name);
+                       if(unlink(filename))
+                               deltree(filename);
+               }
+               closedir(d);
+       }
+       rmdir(dirname);
+       return;
+}
+
+bool meshlink_destroy(const char *confbase) {
+       if(!confbase) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return false;
+       }
+
+       char filename[PATH_MAX];
+       snprintf(filename, sizeof filename, "%s" SLASH "meshlink.conf", confbase);
+
+       if(unlink(filename)) {
+               if(errno == ENOENT) {
+                       meshlink_errno = MESHLINK_ENOENT;
+                       return false;
+               } else {
+                       logger(NULL, MESHLINK_ERROR, "Cannot delete %s: %s\n", filename, strerror(errno));
+                       meshlink_errno = MESHLINK_ESTORAGE;
+                       return false;
+               }
+       }
+
+       deltree(confbase);
+
+       return true;
+}
+
 void meshlink_set_receive_cb(meshlink_handle_t *mesh, meshlink_receive_cb_t cb) {
        if(!mesh) {
                meshlink_errno = MESHLINK_EINVAL;
@@ -1127,6 +1238,15 @@ char *meshlink_get_fingerprint(meshlink_handle_t *mesh, meshlink_node_t *node) {
        return fingerprint;
 }
 
+meshlink_node_t *meshlink_get_self(meshlink_handle_t *mesh) {
+       if(!mesh) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return NULL;
+       }
+
+       return (meshlink_node_t *)mesh->self;
+}
+
 meshlink_node_t *meshlink_get_node(meshlink_handle_t *mesh, const char *name) {
        if(!mesh || !name) {
                meshlink_errno = MESHLINK_EINVAL;
@@ -1832,6 +1952,10 @@ void meshlink_set_default_blacklist(meshlink_handle_t *mesh, bool blacklist) {
 void meshlink_hint_address(meshlink_handle_t *mesh, meshlink_node_t *node, const struct sockaddr *addr) {
        if(!mesh || !node || !addr)
                return;
+
+       // Ignore hints about ourself.
+       if((node_t *)node == mesh->self)
+               return;
        
        pthread_mutex_lock(&(mesh->mesh_mutex));