X-Git-Url: http://git.meshlink.io/?a=blobdiff_plain;f=src%2Fmeshlink.c;h=b407e5b53dca8e7a263be76d08c6a1e8511ac661;hb=e99687f6b8c784e9a1805d1435e91a08b4ea6408;hp=c720c766e48deda084ad42a03b22eddff4f410f8;hpb=f13d66f20a227a87075f6456f41b83ce269b67f4;p=meshlink diff --git a/src/meshlink.c b/src/meshlink.c index c720c766..b407e5b5 100644 --- a/src/meshlink.c +++ b/src/meshlink.c @@ -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) { @@ -1117,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); @@ -1216,9 +1247,11 @@ 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)); @@ -1235,10 +1268,12 @@ void meshlink_stop(meshlink_handle_t *mesh) { 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 @@ -1322,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); @@ -1374,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)); @@ -2019,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, '/'); @@ -2034,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; @@ -2067,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++; @@ -2078,7 +2131,7 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) { *bracket++ = 0; - if(comma && bracket != comma) { + if(*bracket) { goto invalid; } } @@ -2365,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) { @@ -2656,8 +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;