X-Git-Url: http://git.meshlink.io/?a=blobdiff_plain;f=src%2Fmeshlink.c;h=75458fe6940ed221059e39b29035a2f84de6ccc8;hb=fe5563f92021618b4a8b41e412c73d8364fcaf6e;hp=e82ea4f11a384f639f3c6298a42bfa15aecf3e8a;hpb=03ab29c1e6e1b608311decb6630eb0a2177ce1dd;p=meshlink diff --git a/src/meshlink.c b/src/meshlink.c index e82ea4f1..75458fe6 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; } @@ -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,10 +2837,10 @@ bool meshlink_import(meshlink_handle_t *mesh, const char *data) { return true; } -void meshlink_blacklist(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); @@ -2807,25 +2852,23 @@ void meshlink_blacklist(meshlink_handle_t *mesh, meshlink_node_t *node) { logger(mesh, MESHLINK_ERROR, "%s blacklisting itself?\n", node->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; + 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,40 +2880,57 @@ 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); + if(!node_write_config(mesh, n)) { + pthread_mutex_unlock(&mesh->mutex); + return false; } + logger(mesh, MESHLINK_DEBUG, "Blacklisted %s.\n", node->name); + pthread_mutex_unlock(&mesh->mutex); + + return config_sync(mesh, "current"); } -void meshlink_whitelist(meshlink_handle_t *mesh, meshlink_node_t *node) { +bool meshlink_whitelist(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(n == mesh->self) { + logger(mesh, MESHLINK_ERROR, "%s whitelisting itself?\n", node->name); + meshlink_errno = MESHLINK_EINVAL; + pthread_mutex_unlock(&mesh->mutex); + return false; + } + if(!n->status.blacklisted) { logger(mesh, MESHLINK_DEBUG, "Node %s was already whitelisted\n", node->name); - meshlink_errno = MESHLINK_EINVAL; pthread_mutex_unlock(&mesh->mutex); - return; + return true; } n->status.blacklisted = false; - node_write_config(mesh, n); - config_sync(mesh, "current"); if(n->status.reachable) { update_node_status(mesh, n); } + if(!node_write_config(mesh, n)) { + pthread_mutex_unlock(&mesh->mutex); + return false; + } + + logger(mesh, MESHLINK_DEBUG, "Whitelisted %s.\n", node->name); + pthread_mutex_unlock(&mesh->mutex); - return; + + return config_sync(mesh, "current"); } void meshlink_set_default_blacklist(meshlink_handle_t *mesh, bool blacklist) { @@ -2891,7 +2951,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? @@ -3150,7 +3213,7 @@ void meshlink_set_channel_rcvbuf(meshlink_handle_t *mesh, meshlink_channel_t *ch } meshlink_channel_t *meshlink_channel_open_ex(meshlink_handle_t *mesh, meshlink_node_t *node, uint16_t port, meshlink_channel_receive_cb_t cb, const void *data, size_t len, uint32_t flags) { - if(data || len) { + if(data && len) { abort(); // TODO: handle non-NULL data } @@ -3176,6 +3239,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; } @@ -3183,6 +3247,11 @@ meshlink_channel_t *meshlink_channel_open_ex(meshlink_handle_t *mesh, meshlink_n meshlink_channel_t *channel = xzalloc(sizeof(*channel)); channel->node = n; channel->receive_cb = cb; + + if(data && !len) { + channel->priv = (void *)data; + } + channel->c = utcp_connect_ex(n->utcp, port, channel_recv, channel, flags); pthread_mutex_unlock(&mesh->mutex);