]> git.meshlink.io Git - meshlink/blobdiff - src/meshlink.c
Reformat the code.
[meshlink] / src / meshlink.c
index 9a0e914ac9be34a863103df10965889a29026705..b407e5b53dca8e7a263be76d08c6a1e8511ac661 100644 (file)
@@ -1,6 +1,6 @@
 /*
     meshlink.c -- Implementation of the MeshLink API.
-    Copyright (C) 2014, 2017 Guus Sliepen <guus@meshlink.io>
+    Copyright (C) 2014-2018 Guus Sliepen <guus@meshlink.io>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -347,63 +347,64 @@ char *meshlink_get_external_address_for_family(meshlink_handle_t *mesh, int fami
        return hostname;
 }
 
+// String comparison which handles NULL arguments
+static bool safe_streq(const char *a, const char *b) {
+       if(!a || !b) {
+               return a == b;
+       } else {
+               return !strcmp(a, b);
+       }
+}
+
 // This gets the hostname part for use in invitation URLs
 static char *get_my_hostname(meshlink_handle_t *mesh) {
-       char *hostname[2] = {NULL};
-       char *port = NULL;
+       char *hostname[3] = {NULL};
+       char *port[3] = {NULL};
        char *hostport = NULL;
-       char *name = mesh->self->name;
-       char filename[PATH_MAX] = "";
 
-       // Use first Address statement in own host config file
-       snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", mesh->confbase, name);
-       scan_for_hostname(filename, &hostname[0], &port);
-
-       if(hostname[0]) {
-               goto done;
-       }
-
-       hostname[0] = meshlink_get_external_address_for_family(mesh, AF_INET);
-       hostname[1] = meshlink_get_external_address_for_family(mesh, AF_INET6);
+       // Use the best Address statement in our own host config file
+       char filename[PATH_MAX] = "";
+       snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", mesh->confbase, mesh->self->name);
+       scan_for_hostname(filename, &hostname[0], &port[0]);
 
-       if(!hostname[0] && !hostname[1]) {
-               return NULL;
-       }
+       hostname[1] = meshlink_get_external_address_for_family(mesh, AF_INET);
+       hostname[2] = meshlink_get_external_address_for_family(mesh, AF_INET6);
 
-       if(hostname[0] && hostname[1] && !strcmp(hostname[0], hostname[1])) {
-               free(hostname[1]);
-               hostname[1] = NULL;
-       }
+       // Concatenate all unique address to the hostport string
+       for(int i = 0; i < 3; i++) {
+               if(!hostname[i]) {
+                       continue;
+               }
 
-       port = xstrdup(mesh->myport);
+               // Ignore duplicate hostnames
+               bool found = false;
 
-       for(int i = 0; i < 2; i++) {
-               if(hostname[i]) {
-                       char *tmphostport;
-                       xasprintf(&tmphostport, "%s %s", hostname[i], port);
-                       append_config_file(mesh, mesh->self->name, "Address", tmphostport);
-                       free(tmphostport);
+               for(int j = 0; i < j; j++) {
+                       if(safe_streq(hostname[i], hostname[j]) && safe_streq(port[i], port[j])) {
+                               found = true;
+                               break;
+                       }
                }
-       }
 
-done:
-
-       for(int i = 0; i < 2; i++) {
-               if(!hostname[i]) {
+               if(found) {
+                       free(hostname[i]);
+                       free(port[i]);
+                       hostname[i] = NULL;
+                       port[i] = NULL;
                        continue;
                }
 
-               char *newhostport;
-               xasprintf(&newhostport, (strchr(hostname[i], ':') ? "%s%s[%s]" : "%s%s%s"), hostport ? hostport : "", hostport ? "," : "", hostname[i]);
-               free(hostname[i]);
-               free(hostport);
-               hostport = newhostport;
-       }
+               // Ensure we have the same addresses in our own host config file.
+               char *tmphostport;
+               xasprintf(&tmphostport, "%s %s", hostname[i], port[i] ? port[i] : mesh->myport);
+               append_config_file(mesh, mesh->self->name, "Address", tmphostport);
+               free(tmphostport);
 
-       if(port) {
+               // Append the address to the hostport string
                char *newhostport;
-               xasprintf(&newhostport, "%s:%s", hostport, port);
-               free(port);
+               xasprintf(&newhostport, (strchr(hostname[i], ':') ? "%s%s[%s]:%s" : "%s%s%s:%s"), hostport ? hostport : "", hostport ? "," : "", hostname[i], port[i] ? port[i] : mesh->myport);
+               free(hostname[i]);
+               free(port[i]);
                free(hostport);
                hostport = newhostport;
        }
@@ -870,6 +871,8 @@ static const char *errstr[] = {
        [MESHLINK_ESTORAGE] = "Storage error",
        [MESHLINK_ENETWORK] = "Network error",
        [MESHLINK_EPEER] = "Error communicating with peer",
+       [MESHLINK_ENOTSUP] = "Operation not supported",
+       [MESHLINK_EBUSY] = "MeshLink instance already in use",
 };
 
 const char *meshlink_strerror(meshlink_errno_t err) {
@@ -1048,6 +1051,12 @@ meshlink_handle_t *meshlink_open(const char *confbase, const char *name, const c
                return NULL;
        }
 
+       if(strchr(appname, ' ')) {
+               logger(NULL, MESHLINK_ERROR, "Invalid appname given!\n");
+               meshlink_errno = MESHLINK_EINVAL;
+               return NULL;
+       }
+
        if(!name || !*name) {
                logger(NULL, MESHLINK_ERROR, "No name given!\n");
                //return NULL;
@@ -1111,6 +1120,34 @@ meshlink_handle_t *meshlink_open(const char *confbase, const char *name, const c
                }
        }
 
+       // Open the configuration file and lock it
+
+       mesh->conffile = fopen(filename, "r");
+
+       if(!mesh->conffile) {
+               logger(NULL, MESHLINK_ERROR, "Cannot not open %s: %s\n", filename, strerror(errno));
+               meshlink_close(mesh);
+               meshlink_errno = MESHLINK_ESTORAGE;
+               return NULL;
+       }
+
+#ifdef FD_CLOEXEC
+       fcntl(fileno(mesh->conffile), F_SETFD, FD_CLOEXEC);
+#endif
+
+#ifdef HAVE_MINGW
+       // TODO: use _locking()?
+#else
+
+       if(flock(fileno(mesh->conffile), LOCK_EX | LOCK_NB) != 0) {
+               logger(NULL, MESHLINK_ERROR, "Cannot lock %s: %s\n", filename, strerror(errno));
+               meshlink_close(mesh);
+               meshlink_errno = MESHLINK_EBUSY;
+               return NULL;
+       }
+
+#endif
+
        // Read the configuration
 
        init_configuration(&mesh->config);
@@ -1209,10 +1246,14 @@ bool meshlink_start(meshlink_handle_t *mesh) {
 
        mesh->threadstarted = true;
 
+#if HAVE_CATTA
+
        if(mesh->discovery) {
                discovery_start(mesh);
        }
 
+#endif
+
        pthread_mutex_unlock(&(mesh->mesh_mutex));
        return true;
 }
@@ -1226,11 +1267,15 @@ void meshlink_stop(meshlink_handle_t *mesh) {
        pthread_mutex_lock(&(mesh->mesh_mutex));
        logger(mesh, MESHLINK_DEBUG, "meshlink_stop called\n");
 
+#if HAVE_CATTA
+
        // Stop discovery
        if(mesh->discovery) {
                discovery_stop(mesh);
        }
 
+#endif
+
        // Shut down the main thread
        event_loop_stop(&mesh->loop);
 
@@ -1312,6 +1357,10 @@ void meshlink_close(meshlink_handle_t *mesh) {
        free(mesh->confbase);
        pthread_mutex_destroy(&(mesh->mesh_mutex));
 
+       if(mesh->conffile) {
+               fclose(mesh->conffile);
+        }
+
        memset(mesh, 0, sizeof(*mesh));
 
        free(mesh);
@@ -1364,6 +1413,17 @@ void meshlink_set_node_status_cb(meshlink_handle_t *mesh, meshlink_node_status_c
        pthread_mutex_unlock(&(mesh->mesh_mutex));
 }
 
+void meshlink_set_node_duplicate_cb(meshlink_handle_t *mesh, meshlink_node_duplicate_cb_t cb) {
+       if(!mesh) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return;
+       }
+
+       pthread_mutex_lock(&(mesh->mesh_mutex));
+       mesh->node_duplicate_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) {
        if(mesh) {
                pthread_mutex_lock(&(mesh->mesh_mutex));
@@ -2009,7 +2069,7 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) {
        char copy[strlen(invitation) + 1];
        strcpy(copy, invitation);
 
-       // Split the invitation URL into hostname, port, key hash and cookie.
+       // Split the invitation URL into a list of hostname/port tuples, a key hash and a cookie.
 
        char *slash = strchr(copy, '/');
 
@@ -2024,13 +2084,7 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) {
        }
 
        char *address = copy;
-       char *port = strrchr(address, ':');
-
-       if(!port) {
-               goto invalid;
-       }
-
-       *port++ = 0;
+       char *port = NULL;
 
        if(!b64decode(slash, mesh->hash, 18) || !b64decode(slash + 24, mesh->cookie, 18)) {
                goto invalid;
@@ -2057,6 +2111,15 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) {
                        *comma++ = 0;
                }
 
+               // Split of the port
+               port = strrchr(address, ':');
+
+               if(!port) {
+                       goto invalid;
+               }
+
+               *port++ = 0;
+
                // IPv6 address are enclosed in brackets, per RFC 3986
                if(*address == '[') {
                        address++;
@@ -2068,7 +2131,7 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) {
 
                        *bracket++ = 0;
 
-                       if(comma && bracket != comma) {
+                       if(*bracket) {
                                goto invalid;
                        }
                }
@@ -2120,7 +2183,7 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) {
 
        mesh->blen = 0;
 
-       if(!sendline(mesh->sock, "0 ?%s %d.%d", b64key, PROT_MAJOR, 1)) {
+       if(!sendline(mesh->sock, "0 ?%s %d.%d %s", b64key, PROT_MAJOR, 1, mesh->appname)) {
                logger(mesh, MESHLINK_DEBUG, "Error sending request to %s port %s: %s\n", address, port, strerror(errno));
                closesocket(mesh->sock);
                meshlink_errno = MESHLINK_ENETWORK;
@@ -2355,8 +2418,14 @@ void meshlink_blacklist(meshlink_handle_t *mesh, meshlink_node_t *node) {
        //Make blacklisting persistent in the config file
        append_config_file(mesh, n->name, "blacklisted", "yes");
 
+       //Immediately terminate any connections we have with the blacklisted node
+       for list_each(connection_t, c, mesh->connections) {
+               if(c->node == n) {
+                       terminate_connection(mesh, c, c->status.active);
+               }
+       }
+
        pthread_mutex_unlock(&(mesh->mesh_mutex));
-       return;
 }
 
 void meshlink_whitelist(meshlink_handle_t *mesh, meshlink_node_t *node) {
@@ -2646,7 +2715,18 @@ void update_node_status(meshlink_handle_t *mesh, node_t *n) {
        }
 }
 
+void handle_duplicate_node(meshlink_handle_t *mesh, node_t *n) {
+       if(!mesh->node_duplicate_cb || n->status.duplicate) {
+               return;
+       }
+
+       n->status.duplicate = true;
+       mesh->node_duplicate_cb(mesh, (meshlink_node_t *)n);
+}
+
 void meshlink_enable_discovery(meshlink_handle_t *mesh, bool enable) {
+#if HAVE_CATTA
+
        if(!mesh) {
                meshlink_errno = MESHLINK_EINVAL;
                return;
@@ -2670,6 +2750,11 @@ void meshlink_enable_discovery(meshlink_handle_t *mesh, bool enable) {
 
 end:
        pthread_mutex_unlock(&mesh->mesh_mutex);
+#else
+       (void)mesh;
+       (void)enable;
+       meshlink_errno = MESHLINK_ENOTSUP;
+#endif
 }
 
 static void __attribute__((constructor)) meshlink_init(void) {