X-Git-Url: http://git.meshlink.io/?a=blobdiff_plain;f=src%2Fmeshlink.c;h=69ab8f244ef1e715e171b2b13fd8ff521fb27dab;hb=703197ca7614963ba9b831967352b6c90379af48;hp=f1b6a334c6a7bb90bd6be8018a90b861a65a2842;hpb=b0319f8d358c476e29d3c6daeef1cda616cfd447;p=meshlink diff --git a/src/meshlink.c b/src/meshlink.c index f1b6a334..69ab8f24 100644 --- a/src/meshlink.c +++ b/src/meshlink.c @@ -662,7 +662,7 @@ static bool finalize_join(meshlink_handle_t *mesh, const void *buf, uint16_t len } /* Ensure the configuration directory metadata is on disk */ - if(!config_sync(mesh, "current")) { + if(!config_sync(mesh, "current") || !sync_path(mesh->confbase)) { return false; } @@ -796,6 +796,7 @@ static const char *errstr[] = { [MESHLINK_EPEER] = "Error communicating with peer", [MESHLINK_ENOTSUP] = "Operation not supported", [MESHLINK_EBUSY] = "MeshLink instance already in use", + [MESHLINK_EBLACKLISTED] = "Node is blacklisted", }; const char *meshlink_strerror(meshlink_errno_t err) { @@ -867,6 +868,18 @@ static void add_local_addresses(meshlink_handle_t *mesh) { } static bool meshlink_setup(meshlink_handle_t *mesh) { + if(!config_destroy(mesh->confbase, "new")) { + logger(mesh, MESHLINK_ERROR, "Could not delete configuration in %s/new: %s\n", mesh->confbase, strerror(errno)); + meshlink_errno = MESHLINK_ESTORAGE; + return false; + } + + if(!config_destroy(mesh->confbase, "old")) { + logger(mesh, MESHLINK_ERROR, "Could not delete configuration in %s/old: %s\n", mesh->confbase, strerror(errno)); + meshlink_errno = MESHLINK_ESTORAGE; + return false; + } + if(!config_init(mesh, "current")) { logger(mesh, MESHLINK_ERROR, "Could not set up configuration in %s/current: %s\n", mesh->confbase, strerror(errno)); meshlink_errno = MESHLINK_ESTORAGE; @@ -889,6 +902,7 @@ static bool meshlink_setup(meshlink_handle_t *mesh) { mesh->self->name = xstrdup(mesh->name); mesh->self->devclass = mesh->devclass; mesh->self->ecdsa = ecdsa_set_public_key(ecdsa_get_public_key(mesh->private_key)); + mesh->self->session_id = mesh->session_id; if(!write_main_config_files(mesh)) { logger(mesh, MESHLINK_ERROR, "Could not write main config files into %s/current: %s\n", mesh->confbase, strerror(errno)); @@ -901,23 +915,10 @@ static bool meshlink_setup(meshlink_handle_t *mesh) { return false; } - if(!main_config_lock(mesh)) { - logger(NULL, MESHLINK_ERROR, "Cannot lock main config file\n"); - meshlink_errno = MESHLINK_ESTORAGE; - return false; - } - return true; } static bool meshlink_read_config(meshlink_handle_t *mesh) { - // Open the configuration file and lock it - if(!main_config_lock(mesh)) { - logger(NULL, MESHLINK_ERROR, "Cannot lock main config file\n"); - meshlink_errno = MESHLINK_ESTORAGE; - return false; - } - config_t config; if(!main_config_read(mesh, "current", &config, mesh->config_key)) { @@ -967,6 +968,7 @@ static bool meshlink_read_config(meshlink_handle_t *mesh) { mesh->self = new_node(); mesh->self->name = xstrdup(name); mesh->self->devclass = mesh->devclass; + mesh->self->session_id = mesh->session_id; if(!node_read_public_key(mesh, mesh->self)) { logger(NULL, MESHLINK_ERROR, "Could not read our host configuration file!"); @@ -1101,14 +1103,11 @@ bool meshlink_encrypted_key_rotate(meshlink_handle_t *mesh, const void *new_key, devtool_keyrotate_probe(1); - main_config_unlock(mesh); - // Rename confbase/current/ to confbase/old if(!config_rename(mesh, "current", "old")) { logger(mesh, MESHLINK_ERROR, "Cannot rename %s/current to %s/old\n", mesh->confbase, mesh->confbase); meshlink_errno = MESHLINK_ESTORAGE; - main_config_lock(mesh); pthread_mutex_unlock(&mesh->mutex); return false; } @@ -1120,18 +1119,12 @@ bool meshlink_encrypted_key_rotate(meshlink_handle_t *mesh, const void *new_key, if(!config_rename(mesh, "new", "current")) { logger(mesh, MESHLINK_ERROR, "Cannot rename %s/new to %s/current\n", mesh->confbase, mesh->confbase); meshlink_errno = MESHLINK_ESTORAGE; - main_config_lock(mesh); pthread_mutex_unlock(&mesh->mutex); return false; } devtool_keyrotate_probe(3); - if(!main_config_lock(mesh)) { - pthread_mutex_unlock(&mesh->mutex); - return false; - } - // Cleanup the "old" confbase sub-directory if(!config_destroy(mesh->confbase, "old")) { @@ -1288,6 +1281,10 @@ meshlink_handle_t *meshlink_open_ex(const meshlink_open_params_t *params) { randomize(&mesh->prng_state, sizeof(mesh->prng_state)); + do { + randomize(&mesh->session_id, sizeof(mesh->session_id)); + } while(mesh->session_id == 0); + memcpy(mesh->dev_class_traits, default_class_traits, sizeof(default_class_traits)); if(usingname) { @@ -1318,6 +1315,12 @@ meshlink_handle_t *meshlink_open_ex(const meshlink_open_params_t *params) { meshlink_queue_init(&mesh->outpacketqueue); + // Atomically lock the configuration directory. + if(!main_config_lock(mesh)) { + meshlink_close(mesh); + return NULL; + } + // If no configuration exists yet, create it. if(!meshlink_confbase_exists(mesh)) { @@ -1370,7 +1373,11 @@ meshlink_handle_t *meshlink_open_ex(const meshlink_open_params_t *params) { } add_local_addresses(mesh); - node_write_config(mesh, mesh->self); + + if(!node_write_config(mesh, mesh->self)) { + logger(NULL, MESHLINK_ERROR, "Cannot update configuration\n"); + return NULL; + } idle_set(&mesh->loop, idle, mesh); @@ -1506,6 +1513,8 @@ bool meshlink_start(meshlink_handle_t *mesh) { pthread_cond_wait(&mesh->cond, &mesh->mutex); mesh->threadstarted = true; + mesh->self->last_reachable = time(NULL); + mesh->self->status.dirty = true; pthread_mutex_unlock(&mesh->mutex); return true; @@ -1520,6 +1529,11 @@ void meshlink_stop(meshlink_handle_t *mesh) { pthread_mutex_lock(&mesh->mutex); logger(mesh, MESHLINK_DEBUG, "meshlink_stop called\n"); + if(mesh->self) { + mesh->self->last_unreachable = time(NULL); + mesh->self->status.dirty = true; + } + // Shut down the main thread event_loop_stop(&mesh->loop); @@ -1559,12 +1573,11 @@ void meshlink_stop(meshlink_handle_t *mesh) { exit_outgoings(mesh); - // Write out any changed node config files + // Try to write out any changed node config files, ignore errors at this point. if(mesh->nodes) { for splay_each(node_t, n, mesh->nodes) { if(n->status.dirty) { - node_write_config(mesh, n); - n->status.dirty = false; + n->status.dirty = !node_write_config(mesh, n); } } } @@ -1634,16 +1647,56 @@ bool meshlink_destroy(const char *confbase) { return false; } - if(!config_destroy(confbase, "current")) { - logger(NULL, MESHLINK_ERROR, "Cannot remove confbase sub-directories %s: %s\n", confbase, strerror(errno)); + /* Exit early if the confbase directory itself doesn't exist */ + if(access(confbase, F_OK) && errno == ENOENT) { + return true; + } + + /* Take the lock the same way meshlink_open() would. */ + char lockfilename[PATH_MAX]; + snprintf(lockfilename, sizeof(lockfilename), "%s" SLASH "meshlink.lock", confbase); + + FILE *lockfile = fopen(lockfilename, "w+"); + + if(!lockfile) { + logger(NULL, MESHLINK_ERROR, "Could not open lock file %s: %s", lockfilename, strerror(errno)); + meshlink_errno = MESHLINK_ESTORAGE; + return false; + } + +#ifdef FD_CLOEXEC + fcntl(fileno(lockfile), F_SETFD, FD_CLOEXEC); +#endif + +#ifdef HAVE_MINGW + // TODO: use _locking()? +#else + + if(flock(fileno(lockfile), LOCK_EX | LOCK_NB) != 0) { + logger(NULL, MESHLINK_ERROR, "Configuration directory %s still in use\n", lockfilename); + fclose(lockfile); + meshlink_errno = MESHLINK_EBUSY; + return false; + } + +#endif + + if(!config_destroy(confbase, "current") || !config_destroy(confbase, "new") || !config_destroy(confbase, "old")) { + logger(NULL, MESHLINK_ERROR, "Cannot remove sub-directories in %s: %s\n", confbase, strerror(errno)); + return false; + } + + if(unlink(lockfilename)) { + logger(NULL, MESHLINK_ERROR, "Cannot remove lock file %s: %s\n", lockfilename, strerror(errno)); + fclose(lockfile); + meshlink_errno = MESHLINK_ESTORAGE; return false; } - config_destroy(confbase, "new"); - config_destroy(confbase, "old"); + fclose(lockfile); - if(rmdir(confbase) && errno != ENOENT) { - logger(NULL, MESHLINK_ERROR, "Cannot remove directory %s: %s\n", confbase, strerror(errno)); + if(!sync_path(confbase)) { + logger(NULL, MESHLINK_ERROR, "Cannot sync directory %s: %s\n", confbase, strerror(errno)); meshlink_errno = MESHLINK_ESTORAGE; return false; } @@ -1751,6 +1804,7 @@ bool meshlink_send(meshlink_handle_t *mesh, meshlink_node_t *destination, const if(n->status.blacklisted) { logger(mesh, MESHLINK_ERROR, "Node %s blacklisted, dropping packet\n", n->name); + meshlink_errno = MESHLINK_EBLACKLISTED; return false; } @@ -1868,17 +1922,17 @@ meshlink_node_t *meshlink_get_node(meshlink_handle_t *mesh, const char *name) { return NULL; } - meshlink_node_t *node = NULL; + node_t *n = NULL; pthread_mutex_lock(&mesh->mutex); - node = (meshlink_node_t *)lookup_node(mesh, (char *)name); // TODO: make lookup_node() use const + n = lookup_node(mesh, (char *)name); // TODO: make lookup_node() use const pthread_mutex_unlock(&mesh->mutex); - if(!node) { + if(!n) { meshlink_errno = MESHLINK_ENOENT; } - return node; + return (meshlink_node_t *)n; } meshlink_submesh_t *meshlink_get_submesh(meshlink_handle_t *mesh, const char *name) { @@ -1939,8 +1993,8 @@ static meshlink_node_t **meshlink_get_all_nodes_by_condition(meshlink_handle_t * *nmemb = 0; for splay_each(node_t, n, mesh->nodes) { - if(true == search_node(n, condition)) { - *nmemb = *nmemb + 1; + if(search_node(n, condition)) { + ++*nmemb; } } @@ -1956,7 +2010,7 @@ static meshlink_node_t **meshlink_get_all_nodes_by_condition(meshlink_handle_t * meshlink_node_t **p = result; for splay_each(node_t, n, mesh->nodes) { - if(true == search_node(n, condition)) { + if(search_node(n, condition)) { *p++ = (meshlink_node_t *)n; } } @@ -1989,6 +2043,31 @@ static bool search_node_by_submesh(const node_t *node, const void *condition) { return false; } +struct time_range { + time_t start; + time_t end; +}; + +static bool search_node_by_last_reachable(const node_t *node, const void *condition) { + const struct time_range *range = condition; + time_t start = node->last_reachable; + time_t end = node->last_unreachable; + + if(end < start) { + end = time(NULL); + + if(end < start) { + start = end; + } + } + + if(range->end >= range->start) { + return start <= range->end && end >= range->start; + } else { + return start > range->start || end < range->end; + } +} + meshlink_node_t **meshlink_get_all_nodes_by_dev_class(meshlink_handle_t *mesh, dev_class_t devclass, meshlink_node_t **nodes, size_t *nmemb) { if(!mesh || devclass < 0 || devclass >= DEV_CLASS_COUNT || !nmemb) { meshlink_errno = MESHLINK_EINVAL; @@ -2007,6 +2086,17 @@ meshlink_node_t **meshlink_get_all_nodes_by_submesh(meshlink_handle_t *mesh, mes return meshlink_get_all_nodes_by_condition(mesh, submesh, nodes, nmemb, search_node_by_submesh); } +meshlink_node_t **meshlink_get_all_nodes_by_last_reachable(meshlink_handle_t *mesh, time_t start, time_t end, meshlink_node_t **nodes, size_t *nmemb) { + if(!mesh || !nmemb) { + meshlink_errno = MESHLINK_EINVAL; + return NULL; + } + + struct time_range range = {start, end}; + + return meshlink_get_all_nodes_by_condition(mesh, &range, nodes, nmemb, search_node_by_last_reachable); +} + dev_class_t meshlink_get_node_dev_class(meshlink_handle_t *mesh, meshlink_node_t *node) { if(!mesh || !node) { meshlink_errno = MESHLINK_EINVAL; @@ -2136,11 +2226,15 @@ bool meshlink_set_canonical_address(meshlink_handle_t *mesh, meshlink_node_t *no node_t *n = (node_t *)node; free(n->canonical_address); n->canonical_address = canonical_address; - node_write_config(mesh, n); + + if(!node_write_config(mesh, n)) { + pthread_mutex_unlock(&mesh->mutex); + return false; + } pthread_mutex_unlock(&mesh->mutex); - return true; + return config_sync(mesh, "current"); } bool meshlink_add_address(meshlink_handle_t *mesh, const char *address) { @@ -2221,6 +2315,7 @@ bool meshlink_set_port(meshlink_handle_t *mesh, int port) { mesh->self = new_node(); mesh->self->name = xstrdup(mesh->name); mesh->self->devclass = mesh->devclass; + mesh->self->session_id = mesh->session_id; xasprintf(&mesh->myport, "%d", port); if(!node_read_public_key(mesh, mesh->self)) { @@ -2659,7 +2754,7 @@ char *meshlink_export(meshlink_handle_t *mesh) { uint32_t count = 0; - for(uint32_t i = 0; i < 5; i++) { + for(uint32_t i = 0; i < MAX_RECENT; i++) { if(mesh->self->recent[i].sa.sa_family) { count++; } else { @@ -2673,6 +2768,9 @@ char *meshlink_export(meshlink_handle_t *mesh) { packmsg_add_sockaddr(&out, &mesh->self->recent[i]); } + packmsg_add_int64(&out, 0); + packmsg_add_int64(&out, 0); + pthread_mutex_unlock(&mesh->mutex); if(!packmsg_output_ok(&out)) { @@ -2771,7 +2869,11 @@ bool meshlink_import(meshlink_handle_t *mesh, const char *data) { break; } - config_write(mesh, "current", n->name, &config, mesh->config_key); + if(!config_write(mesh, "current", n->name, &config, mesh->config_key)) { + free_node(n); + return false; + } + node_add(mesh, n); } @@ -2792,40 +2894,26 @@ bool meshlink_import(meshlink_handle_t *mesh, const char *data) { return true; } -void meshlink_blacklist(meshlink_handle_t *mesh, meshlink_node_t *node) { - if(!mesh || !node) { - meshlink_errno = MESHLINK_EINVAL; - return; - } - - pthread_mutex_lock(&mesh->mutex); - - node_t *n; - n = (node_t *)node; - +static bool blacklist(meshlink_handle_t *mesh, node_t *n) { if(n == mesh->self) { - logger(mesh, MESHLINK_ERROR, "%s blacklisting itself?\n", node->name); + logger(mesh, MESHLINK_ERROR, "%s blacklisting itself?\n", n->name); meshlink_errno = MESHLINK_EINVAL; - pthread_mutex_unlock(&mesh->mutex); - return; + return false; } if(n->status.blacklisted) { - logger(mesh, MESHLINK_DEBUG, "Node %s already blacklisted\n", node->name); - pthread_mutex_unlock(&mesh->mutex); - return; + logger(mesh, MESHLINK_DEBUG, "Node %s already blacklisted\n", n->name); + return true; } n->status.blacklisted = true; - node_write_config(mesh, n); - config_sync(mesh, "current"); - logger(mesh, MESHLINK_DEBUG, "Blacklisted %s.\n", node->name); - - //Immediately terminate any connections we have with the blacklisted node + /* Immediately shut down any connections we have with the blacklisted node. + * We can't call terminate_connection(), because we might be called from a callback function. + */ for list_each(connection_t, c, mesh->connections) { if(c->node == n) { - terminate_connection(mesh, c, c->status.active); + shutdown(c->socket, SHUT_RDWR); } } @@ -2837,46 +2925,189 @@ void meshlink_blacklist(meshlink_handle_t *mesh, meshlink_node_t *node) { n->mtuprobes = 0; n->status.udp_confirmed = false; - if(n->status.reachable) { - update_node_status(mesh, n); + /* Graph updates will suppress status updates for blacklisted nodes, so we need to + * manually call the status callback if necessary. + */ + if(n->status.reachable && mesh->node_status_cb) { + mesh->node_status_cb(mesh, (meshlink_node_t *)n, false); } - pthread_mutex_unlock(&mesh->mutex); + return node_write_config(mesh, n) && config_sync(mesh, "current"); } -void meshlink_whitelist(meshlink_handle_t *mesh, meshlink_node_t *node) { +bool meshlink_blacklist(meshlink_handle_t *mesh, meshlink_node_t *node) { if(!mesh || !node) { meshlink_errno = MESHLINK_EINVAL; - return; + return false; } pthread_mutex_lock(&mesh->mutex); - node_t *n = (node_t *)node; + if(!blacklist(mesh, (node_t *)node)) { + pthread_mutex_unlock(&mesh->mutex); + return false; + } - if(!n->status.blacklisted) { - logger(mesh, MESHLINK_DEBUG, "Node %s was already whitelisted\n", node->name); + pthread_mutex_unlock(&mesh->mutex); + + logger(mesh, MESHLINK_DEBUG, "Blacklisted %s.\n", node->name); + return true; +} + +bool meshlink_blacklist_by_name(meshlink_handle_t *mesh, const char *name) { + if(!mesh || !name) { meshlink_errno = MESHLINK_EINVAL; + return false; + } + + pthread_mutex_lock(&mesh->mutex); + + node_t *n = lookup_node(mesh, (char *)name); + + if(!n) { + n = new_node(); + n->name = xstrdup(name); + node_add(mesh, n); + } + + if(!blacklist(mesh, (node_t *)n)) { pthread_mutex_unlock(&mesh->mutex); - return; + return false; + } + + pthread_mutex_unlock(&mesh->mutex); + + logger(mesh, MESHLINK_DEBUG, "Blacklisted %s.\n", name); + return true; +} + +static bool whitelist(meshlink_handle_t *mesh, node_t *n) { + if(n == mesh->self) { + logger(mesh, MESHLINK_ERROR, "%s whitelisting itself?\n", n->name); + meshlink_errno = MESHLINK_EINVAL; + return false; + } + + if(!n->status.blacklisted) { + logger(mesh, MESHLINK_DEBUG, "Node %s was already whitelisted\n", n->name); + return true; } n->status.blacklisted = false; - node_write_config(mesh, n); - config_sync(mesh, "current"); if(n->status.reachable) { update_node_status(mesh, n); } + return node_write_config(mesh, n) && config_sync(mesh, "current"); +} + +bool meshlink_whitelist(meshlink_handle_t *mesh, meshlink_node_t *node) { + if(!mesh || !node) { + meshlink_errno = MESHLINK_EINVAL; + return false; + } + + pthread_mutex_lock(&mesh->mutex); + + if(!whitelist(mesh, (node_t *)node)) { + pthread_mutex_unlock(&mesh->mutex); + return false; + } + pthread_mutex_unlock(&mesh->mutex); - return; + + logger(mesh, MESHLINK_DEBUG, "Whitelisted %s.\n", node->name); + return true; +} + +bool meshlink_whitelist_by_name(meshlink_handle_t *mesh, const char *name) { + if(!mesh || !name) { + meshlink_errno = MESHLINK_EINVAL; + return false; + } + + pthread_mutex_lock(&mesh->mutex); + + node_t *n = lookup_node(mesh, (char *)name); + + if(!n) { + n = new_node(); + n->name = xstrdup(name); + node_add(mesh, n); + } + + if(!whitelist(mesh, (node_t *)n)) { + pthread_mutex_unlock(&mesh->mutex); + return false; + } + + pthread_mutex_unlock(&mesh->mutex); + + logger(mesh, MESHLINK_DEBUG, "Whitelisted %s.\n", name); + return true; } void meshlink_set_default_blacklist(meshlink_handle_t *mesh, bool blacklist) { mesh->default_blacklist = blacklist; } +bool meshlink_forget_node(meshlink_handle_t *mesh, meshlink_node_t *node) { + if(!mesh || !node) { + meshlink_errno = MESHLINK_EINVAL; + return false; + } + + node_t *n = (node_t *)node; + + pthread_mutex_lock(&mesh->mutex); + + /* Check that the node is not reachable */ + if(n->status.reachable || n->connection) { + pthread_mutex_unlock(&mesh->mutex); + logger(mesh, MESHLINK_WARNING, "Could not forget %s: still reachable", n->name); + return false; + } + + /* Check that we don't have any active UTCP connections */ + if(n->utcp && utcp_is_active(n->utcp)) { + pthread_mutex_unlock(&mesh->mutex); + logger(mesh, MESHLINK_WARNING, "Could not forget %s: active UTCP connections", n->name); + return false; + } + + /* Check that we have no active connections to this node */ + for list_each(connection_t, c, mesh->connections) { + if(c->node == n) { + pthread_mutex_unlock(&mesh->mutex); + logger(mesh, MESHLINK_WARNING, "Could not forget %s: active connection", n->name); + return false; + } + } + + /* Remove any pending outgoings to this node */ + if(mesh->outgoings) { + for list_each(outgoing_t, outgoing, mesh->outgoings) { + if(outgoing->node == n) { + list_delete_node(mesh->outgoings, node); + } + } + } + + /* Delete the config file for this node */ + if(!config_delete(mesh, "current", n->name)) { + pthread_mutex_unlock(&mesh->mutex); + return false; + } + + /* Delete the node struct and any remaining edges referencing this node */ + node_del(mesh, n); + + pthread_mutex_unlock(&mesh->mutex); + + return config_sync(mesh, "current"); +} + /* Hint that a hostname may be found at an address * See header file for detailed comment. */ @@ -2889,9 +3120,12 @@ void meshlink_hint_address(meshlink_handle_t *mesh, meshlink_node_t *node, const pthread_mutex_lock(&mesh->mutex); node_t *n = (node_t *)node; - memmove(n->recent + 1, n->recent, 4 * sizeof(*n->recent)); - memcpy(n->recent, addr, SALEN(*addr)); - node_write_config(mesh, n); + + if(node_add_recent_address(mesh, n, (sockaddr_t *)addr)) { + if(!node_write_config(mesh, n)) { + logger(mesh, MESHLINK_DEBUG, "Could not update %s\n", n->name); + } + } pthread_mutex_unlock(&mesh->mutex); // @TODO do we want to fire off a connection attempt right away? @@ -3176,6 +3410,7 @@ meshlink_channel_t *meshlink_channel_open_ex(meshlink_handle_t *mesh, meshlink_n if(n->status.blacklisted) { logger(mesh, MESHLINK_ERROR, "Cannot open a channel with blacklisted node\n"); + meshlink_errno = MESHLINK_EBLACKLISTED; pthread_mutex_unlock(&mesh->mutex); return NULL; }