X-Git-Url: http://git.meshlink.io/?a=blobdiff_plain;f=src%2Fmeshlink.c;h=b407e5b53dca8e7a263be76d08c6a1e8511ac661;hb=e99687f6b8c784e9a1805d1435e91a08b4ea6408;hp=52a4389a9311e1e872091e9cccc335db94ca3b37;hpb=34a52e6e69be085ee99755c2890268d2ce11f9b4;p=meshlink diff --git a/src/meshlink.c b/src/meshlink.c index 52a4389a..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; } @@ -534,6 +535,31 @@ int check_port(meshlink_handle_t *mesh) { return 0; } +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); +} + static bool finalize_join(meshlink_handle_t *mesh) { char *name = xstrdup(get_value(mesh->data, "Name")); @@ -559,6 +585,19 @@ static bool finalize_join(meshlink_handle_t *mesh) { fprintf(f, "Name = %s\n", name); + // Wipe all old host config files and invitations + snprintf(filename, sizeof(filename), "%s" SLASH "hosts", mesh->confbase); + deltree(filename); + + if(mkdir(filename, 0777) && errno != EEXIST) { + logger(mesh, MESHLINK_DEBUG, "Could not create directory %s: %s\n", filename, strerror(errno)); + return false; + } + + snprintf(filename, sizeof(filename), "%s" SLASH "invitations", mesh->confbase); + deltree(filename); + + // Create a new host config file for ourself snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", mesh->confbase, name); FILE *fh = fopen(filename, "w"); @@ -690,8 +729,10 @@ static bool finalize_join(meshlink_handle_t *mesh) { sptps_send_record(&(mesh->sptps), 1, b64key, strlen(b64key)); free(b64key); + free(mesh->name); free(mesh->self->name); free(mesh->self->connection->name); + mesh->name = xstrdup(name); mesh->self->name = xstrdup(name); mesh->self->connection->name = name; @@ -830,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) { @@ -1008,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; @@ -1071,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); @@ -1169,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; } @@ -1186,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); @@ -1272,37 +1357,15 @@ 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); } -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; @@ -1350,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)); @@ -1995,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, '/'); @@ -2010,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; @@ -2043,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++; @@ -2054,7 +2131,7 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) { *bracket++ = 0; - if(comma && bracket != comma) { + if(*bracket) { goto invalid; } } @@ -2106,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; @@ -2341,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) { @@ -2632,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; @@ -2656,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) {