]> git.meshlink.io Git - meshlink/blobdiff - src/meshlink.c
Add meshlink_get_self().
[meshlink] / src / meshlink.c
index 8b97be7913552d95f1537155e8ebb8b5fa8c45b3..1d682c40b53b2d8bb9911450f9ab61d41c18572e 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
@@ -660,7 +660,7 @@ static bool ecdsa_keygen(meshlink_handle_t *mesh) {
                logger(mesh, MESHLINK_DEBUG, "Done.\n");
 
        snprintf(privname, sizeof privname, "%s" SLASH "ecdsa_key.priv", mesh->confbase);
-       f = fopen(privname, "w");
+       f = fopen(privname, "wb");
 
        if(!f) {
                meshlink_errno = MESHLINK_ESTORAGE;
@@ -701,16 +701,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 +912,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");
@@ -957,6 +1017,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 +1049,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 +1233,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;
@@ -1292,7 +1407,7 @@ static bool refresh_invitation_key(meshlink_handle_t *mesh) {
        }
 
        // Create a new key if necessary.
-       FILE *f = fopen(filename, "r");
+       FILE *f = fopen(filename, "rb");
        if(!f) {
                if(errno != ENOENT) {
                        logger(mesh, MESHLINK_DEBUG, "Could not read %s: %s\n", filename, strerror(errno));
@@ -1308,7 +1423,7 @@ static bool refresh_invitation_key(meshlink_handle_t *mesh) {
                        pthread_mutex_unlock(&(mesh->mesh_mutex));
                        return false;
                }
-               f = fopen(filename, "w");
+               f = fopen(filename, "wb");
                if(!f) {
                        logger(mesh, MESHLINK_DEBUG, "Could not write %s: %s\n", filename, strerror(errno));
                        meshlink_errno = MESHLINK_ESTORAGE;
@@ -1832,6 +1947,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));