X-Git-Url: http://git.meshlink.io/?a=blobdiff_plain;f=src%2Fmeshlink.c;h=575895125fc67ace16e61344907b1b54f5ba62b9;hb=193ada771893e1d794f30869a1ed6dd1c1d3f14d;hp=f1b6a334c6a7bb90bd6be8018a90b861a65a2842;hpb=b0319f8d358c476e29d3c6daeef1cda616cfd447;p=meshlink diff --git a/src/meshlink.c b/src/meshlink.c index f1b6a334..57589512 100644 --- a/src/meshlink.c +++ b/src/meshlink.c @@ -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) { @@ -889,6 +890,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 +903,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 +956,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 +1091,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 +1107,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 +1269,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 +1303,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 +1361,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); @@ -1559,12 +1554,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 +1628,57 @@ 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)); + /* TODO: do we need to remove confbase? Potential race condition? */ + 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 +1786,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 +1904,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 +1975,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 +1992,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; } } @@ -2136,11 +2172,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 +2261,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)) { @@ -2771,7 +2812,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 +2837,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 +2868,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. */ @@ -2891,7 +3065,10 @@ void meshlink_hint_address(meshlink_handle_t *mesh, meshlink_node_t *node, const 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_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 +3353,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; }