X-Git-Url: http://git.meshlink.io/?a=blobdiff_plain;f=src%2Fmeshlink.c;h=b407e5b53dca8e7a263be76d08c6a1e8511ac661;hb=e99687f6b8c784e9a1805d1435e91a08b4ea6408;hp=fd9b8827dc32f338a9426021a0eedf949e73cf73;hpb=2bd4182c4d960596b54793027a0f42e991eb12dd;p=meshlink diff --git a/src/meshlink.c b/src/meshlink.c index fd9b8827..b407e5b5 100644 --- a/src/meshlink.c +++ b/src/meshlink.c @@ -1,6 +1,6 @@ /* meshlink.c -- Implementation of the MeshLink API. - Copyright (C) 2014, 2017 Guus Sliepen + Copyright (C) 2014-2018 Guus Sliepen 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) { @@ -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); @@ -1215,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; } @@ -1232,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); @@ -1318,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); @@ -1370,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)); @@ -2015,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, '/'); @@ -2030,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; @@ -2063,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++; @@ -2074,7 +2131,7 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) { *bracket++ = 0; - if(comma && bracket != comma) { + if(*bracket) { goto invalid; } } @@ -2126,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; @@ -2361,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) { @@ -2652,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; @@ -2676,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) {