+ if(!mesh->success) {
+ logger(mesh, MESHLINK_DEBUG, "Connection closed by peer, invitation cancelled.\n");
+ meshlink_errno = MESHLINK_EPEER;
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return false;
+ }
+
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return true;
+
+invalid:
+ logger(mesh, MESHLINK_DEBUG, "Invalid invitation URL\n");
+ meshlink_errno = MESHLINK_EINVAL;
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return false;
+}
+
+char *meshlink_export(meshlink_handle_t *mesh) {
+ if(!mesh) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return NULL;
+ }
+
+ // Create a config file on the fly.
+
+ uint8_t buf[4096];
+ packmsg_output_t out = {buf, sizeof(buf)};
+ packmsg_add_uint32(&out, MESHLINK_CONFIG_VERSION);
+ packmsg_add_str(&out, mesh->name);
+ packmsg_add_str(&out, CORE_MESH);
+
+ pthread_mutex_lock(&(mesh->mesh_mutex));
+
+ packmsg_add_int32(&out, mesh->self->devclass);
+ packmsg_add_bool(&out, mesh->self->status.blacklisted);
+ packmsg_add_bin(&out, ecdsa_get_public_key(mesh->private_key), 32);
+ packmsg_add_str(&out, mesh->self->canonical_address ? mesh->self->canonical_address : "");
+
+ uint32_t count = 0;
+
+ for(uint32_t i = 0; i < 5; i++) {
+ if(mesh->self->recent[i].sa.sa_family) {
+ count++;
+ } else {
+ break;
+ }
+ }
+
+ packmsg_add_array(&out, count);
+
+ for(uint32_t i = 0; i < count; i++) {
+ packmsg_add_sockaddr(&out, &mesh->self->recent[i]);
+ }
+
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+
+ if(!packmsg_output_ok(&out)) {
+ logger(mesh, MESHLINK_DEBUG, "Error creating export data\n");
+ meshlink_errno = MESHLINK_EINTERNAL;
+ return NULL;
+ }
+
+ // Prepare a base64-encoded packmsg array containing our config file
+
+ uint32_t len = packmsg_output_size(&out, buf);
+ uint32_t len2 = ((len + 4) * 4) / 3 + 4;
+ uint8_t *buf2 = xmalloc(len2);
+ packmsg_output_t out2 = {buf2, len2};
+ packmsg_add_array(&out2, 1);
+ packmsg_add_bin(&out2, buf, packmsg_output_size(&out, buf));
+
+ if(!packmsg_output_ok(&out2)) {
+ logger(mesh, MESHLINK_DEBUG, "Error creating export data\n");
+ meshlink_errno = MESHLINK_EINTERNAL;
+ free(buf2);
+ return NULL;
+ }
+
+ b64encode_urlsafe(buf2, (char *)buf2, packmsg_output_size(&out2, buf2));
+
+ return (char *)buf2;
+}
+
+bool meshlink_import(meshlink_handle_t *mesh, const char *data) {
+ if(!mesh || !data) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return false;
+ }
+
+ size_t datalen = strlen(data);
+ uint8_t *buf = xmalloc(datalen);
+ int buflen = b64decode(data, buf, datalen);
+
+ if(!buflen) {
+ logger(mesh, MESHLINK_DEBUG, "Invalid data\n");
+ meshlink_errno = MESHLINK_EPEER;
+ return false;
+ }
+
+ packmsg_input_t in = {buf, buflen};
+ uint32_t count = packmsg_get_array(&in);
+
+ if(!count) {
+ logger(mesh, MESHLINK_DEBUG, "Invalid data\n");
+ meshlink_errno = MESHLINK_EPEER;
+ return false;
+ }
+
+ pthread_mutex_lock(&(mesh->mesh_mutex));
+
+ while(count--) {
+ const void *data;
+ uint32_t len = packmsg_get_bin_raw(&in, &data);
+
+ if(!len) {
+ break;
+ }
+
+ packmsg_input_t in2 = {data, len};
+ uint32_t version = packmsg_get_uint32(&in2);
+ char *name = packmsg_get_str_dup(&in2);
+
+ if(!packmsg_input_ok(&in2) || version != MESHLINK_CONFIG_VERSION || !check_id(name)) {
+ free(name);
+ packmsg_input_invalidate(&in);
+ break;
+ }
+
+ if(!check_id(name)) {
+ free(name);
+ break;
+ }
+
+ node_t *n = lookup_node(mesh, name);
+
+ if(n) {
+ logger(mesh, MESHLINK_DEBUG, "Node %s already exists, not importing\n", name);
+ free(name);
+ continue;
+ }
+
+ n = new_node();
+ n->name = name;
+
+ config_t config = {data, len};
+
+ if(!node_read_from_config(mesh, n, &config)) {
+ free_node(n);
+ packmsg_input_invalidate(&in);
+ break;
+ }
+
+ config_write(mesh, "current", n->name, &config, mesh->config_key);
+ node_add(mesh, n);
+ }
+
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+
+ free(buf);
+
+ if(!packmsg_done(&in)) {
+ logger(mesh, MESHLINK_ERROR, "Invalid data\n");
+ meshlink_errno = MESHLINK_EPEER;
+ return false;
+ }
+
+ if(!config_sync(mesh, "current")) {
+ return false;
+ }
+
+ 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->mesh_mutex));
+
+ node_t *n;
+ n = (node_t *)node;
+
+ if(n == mesh->self) {
+ logger(mesh, MESHLINK_ERROR, "%s blacklisting itself?\n", node->name);
+ meshlink_errno = MESHLINK_EINVAL;
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return;
+ }
+
+ if(n->status.blacklisted) {
+ logger(mesh, MESHLINK_DEBUG, "Node %s already blacklisted\n", node->name);
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return;
+ }
+
+ 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
+ for list_each(connection_t, c, mesh->connections) {
+ if(c->node == n) {
+ terminate_connection(mesh, c, c->status.active);
+ }
+ }
+
+ utcp_abort_all_connections(n->utcp);
+
+ n->mtu = 0;
+ n->minmtu = 0;
+ n->maxmtu = MTU;
+ n->mtuprobes = 0;
+ n->status.udp_confirmed = false;
+
+ if(n->status.reachable) {
+ update_node_status(mesh, n);
+ }
+
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+}
+
+void meshlink_whitelist(meshlink_handle_t *mesh, meshlink_node_t *node) {
+ if(!mesh || !node) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return;
+ }
+
+ pthread_mutex_lock(&(mesh->mesh_mutex));
+
+ node_t *n = (node_t *)node;
+
+ if(!n->status.blacklisted) {
+ logger(mesh, MESHLINK_DEBUG, "Node %s was already whitelisted\n", node->name);
+ meshlink_errno = MESHLINK_EINVAL;
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return;
+ }
+
+ n->status.blacklisted = false;
+ node_write_config(mesh, n);
+ config_sync(mesh, "current");
+
+ if(n->status.reachable) {
+ update_node_status(mesh, n);
+ }
+
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return;
+}
+
+void meshlink_set_default_blacklist(meshlink_handle_t *mesh, bool blacklist) {
+ mesh->default_blacklist = blacklist;
+}
+
+/* Hint that a hostname may be found at an address
+ * See header file for detailed comment.
+ */
+void meshlink_hint_address(meshlink_handle_t *mesh, meshlink_node_t *node, const struct sockaddr *addr) {
+ if(!mesh || !node || !addr) {
+ meshlink_errno = EINVAL;
+ return;
+ }
+
+ pthread_mutex_lock(&(mesh->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);
+
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ // @TODO do we want to fire off a connection attempt right away?
+}
+
+static bool channel_pre_accept(struct utcp *utcp, uint16_t port) {
+ (void)port;
+ node_t *n = utcp->priv;
+ meshlink_handle_t *mesh = n->mesh;
+ return mesh->channel_accept_cb;
+}
+
+static void aio_signal(meshlink_handle_t *mesh, meshlink_channel_t *channel, meshlink_aio_buffer_t *aio) {
+ if(aio->data) {
+ if(aio->cb.buffer) {
+ aio->cb.buffer(mesh, channel, aio->data, aio->len, aio->priv);
+ }
+ } else {
+ if(aio->cb.fd) {
+ aio->cb.fd(mesh, channel, aio->fd, aio->done, aio->priv);
+ }
+ }
+}
+
+static ssize_t channel_recv(struct utcp_connection *connection, const void *data, size_t len) {
+ meshlink_channel_t *channel = connection->priv;
+
+ if(!channel) {
+ abort();
+ }
+
+ node_t *n = channel->node;
+ meshlink_handle_t *mesh = n->mesh;
+
+ if(n->status.destroyed) {
+ meshlink_channel_close(mesh, channel);
+ return len;
+ }
+
+ const char *p = data;
+ size_t left = len;
+
+ while(channel->aio_receive) {
+ meshlink_aio_buffer_t *aio = channel->aio_receive;
+ size_t todo = aio->len - aio->done;
+
+ if(todo > left) {
+ todo = left;
+ }
+
+ if(aio->data) {
+ memcpy((char *)aio->data + aio->done, p, todo);
+ } else {
+ ssize_t result = write(aio->fd, p, todo);
+
+ if(result > 0) {
+ todo = result;
+ }
+ }
+
+ aio->done += todo;
+
+ if(aio->done == aio->len) {
+ channel->aio_receive = aio->next;
+ aio_signal(mesh, channel, aio);
+ free(aio);
+ }
+
+ p += todo;
+ left -= todo;
+
+ if(!left && len) {
+ return len;
+ }
+ }
+
+ if(channel->receive_cb) {
+ channel->receive_cb(mesh, channel, p, left);
+ }
+
+ return len;
+}
+
+static void channel_accept(struct utcp_connection *utcp_connection, uint16_t port) {
+ node_t *n = utcp_connection->utcp->priv;
+
+ if(!n) {
+ abort();
+ }
+
+ meshlink_handle_t *mesh = n->mesh;
+
+ if(!mesh->channel_accept_cb) {
+ return;
+ }
+
+ meshlink_channel_t *channel = xzalloc(sizeof(*channel));
+ channel->node = n;
+ channel->c = utcp_connection;
+
+ if(mesh->channel_accept_cb(mesh, channel, port, NULL, 0)) {
+ utcp_accept(utcp_connection, channel_recv, channel);
+ } else {
+ free(channel);
+ }
+}
+
+static ssize_t channel_send(struct utcp *utcp, const void *data, size_t len) {
+ node_t *n = utcp->priv;
+
+ if(n->status.destroyed) {
+ return -1;
+ }
+
+ meshlink_handle_t *mesh = n->mesh;
+ return meshlink_send(mesh, (meshlink_node_t *)n, data, len) ? (ssize_t)len : -1;
+}
+
+void meshlink_set_channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, meshlink_channel_receive_cb_t cb) {
+ if(!mesh || !channel) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return;
+ }
+
+ channel->receive_cb = cb;
+}
+
+static void channel_receive(meshlink_handle_t *mesh, meshlink_node_t *source, const void *data, size_t len) {
+ (void)mesh;
+ node_t *n = (node_t *)source;
+
+ if(!n->utcp) {
+ abort();
+ }
+
+ utcp_recv(n->utcp, data, len);
+}
+
+static void channel_poll(struct utcp_connection *connection, size_t len) {
+ meshlink_channel_t *channel = connection->priv;
+
+ if(!channel) {
+ abort();
+ }
+
+ node_t *n = channel->node;
+ meshlink_handle_t *mesh = n->mesh;
+ meshlink_aio_buffer_t *aio = channel->aio_send;
+
+ if(aio) {
+ /* We at least one AIO buffer. Send as much as possible form the first buffer. */
+ size_t left = aio->len - aio->done;
+ ssize_t sent;
+
+ if(len > left) {
+ len = left;
+ }
+
+ if(aio->data) {
+ sent = utcp_send(connection, (char *)aio->data + aio->done, len);
+ } else {
+ char buf[65536];
+ size_t todo = utcp_get_sndbuf_free(connection);
+
+ if(todo > left) {
+ todo = left;
+ }
+
+ if(todo > sizeof(buf)) {
+ todo = sizeof(buf);
+ }
+
+ ssize_t result = read(aio->fd, buf, todo);
+
+ if(result > 0) {
+ sent = utcp_send(connection, buf, result);
+ } else {
+ sent = result;
+ }
+ }
+
+ if(sent >= 0) {
+ aio->done += sent;
+ }
+
+ /* If the buffer is now completely sent, call the callback and dispose of it. */
+ if(aio->done >= aio->len) {
+ channel->aio_send = aio->next;
+ aio_signal(mesh, channel, aio);
+ free(aio);
+ }
+ } else {
+ if(channel->poll_cb) {
+ channel->poll_cb(mesh, channel, len);
+ } else {
+ utcp_set_poll_cb(connection, NULL);
+ }
+ }
+}
+
+void meshlink_set_channel_poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, meshlink_channel_poll_cb_t cb) {
+ if(!mesh || !channel) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return;
+ }
+
+ pthread_mutex_lock(&mesh->mesh_mutex);
+ channel->poll_cb = cb;
+ utcp_set_poll_cb(channel->c, (cb || channel->aio_send) ? channel_poll : NULL);
+ pthread_mutex_unlock(&mesh->mesh_mutex);
+}
+
+void meshlink_set_channel_accept_cb(meshlink_handle_t *mesh, meshlink_channel_accept_cb_t cb) {
+ if(!mesh) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return;
+ }
+
+ pthread_mutex_lock(&mesh->mesh_mutex);
+ mesh->channel_accept_cb = cb;
+ mesh->receive_cb = channel_receive;
+
+ for splay_each(node_t, n, mesh->nodes) {
+ if(!n->utcp && n != mesh->self) {
+ n->utcp = utcp_init(channel_accept, channel_pre_accept, channel_send, n);
+ }
+ }
+
+ pthread_mutex_unlock(&mesh->mesh_mutex);
+}
+
+void meshlink_set_channel_sndbuf(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t size) {
+ (void)mesh;
+
+ if(!channel) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return;
+ }
+
+ pthread_mutex_lock(&mesh->mesh_mutex);
+ utcp_set_sndbuf(channel->c, size);
+ pthread_mutex_unlock(&mesh->mesh_mutex);
+}
+
+void meshlink_set_channel_rcvbuf(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t size) {
+ (void)mesh;
+
+ if(!channel) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return;
+ }
+
+ pthread_mutex_lock(&mesh->mesh_mutex);
+ utcp_set_rcvbuf(channel->c, size);
+ pthread_mutex_unlock(&mesh->mesh_mutex);
+}
+
+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) {
+ abort(); // TODO: handle non-NULL data
+ }
+
+ if(!mesh || !node) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return NULL;
+ }
+
+ pthread_mutex_lock(&mesh->mesh_mutex);
+
+ node_t *n = (node_t *)node;
+
+ if(!n->utcp) {
+ n->utcp = utcp_init(channel_accept, channel_pre_accept, channel_send, n);
+ mesh->receive_cb = channel_receive;
+
+ if(!n->utcp) {
+ meshlink_errno = errno == ENOMEM ? MESHLINK_ENOMEM : MESHLINK_EINTERNAL;
+ pthread_mutex_unlock(&mesh->mesh_mutex);
+ return NULL;
+ }
+ }
+
+ if(n->status.blacklisted) {
+ logger(mesh, MESHLINK_ERROR, "Cannot open a channel with blacklisted node\n");
+ pthread_mutex_unlock(&mesh->mesh_mutex);
+ return NULL;
+ }
+
+ meshlink_channel_t *channel = xzalloc(sizeof(*channel));
+ channel->node = n;
+ channel->receive_cb = cb;
+ channel->c = utcp_connect_ex(n->utcp, port, channel_recv, channel, flags);
+
+ pthread_mutex_unlock(&mesh->mesh_mutex);
+
+ if(!channel->c) {
+ meshlink_errno = errno == ENOMEM ? MESHLINK_ENOMEM : MESHLINK_EINTERNAL;
+ free(channel);
+ return NULL;
+ }
+
+ return channel;
+}
+
+meshlink_channel_t *meshlink_channel_open(meshlink_handle_t *mesh, meshlink_node_t *node, uint16_t port, meshlink_channel_receive_cb_t cb, const void *data, size_t len) {
+ return meshlink_channel_open_ex(mesh, node, port, cb, data, len, MESHLINK_CHANNEL_TCP);
+}
+
+void meshlink_channel_shutdown(meshlink_handle_t *mesh, meshlink_channel_t *channel, int direction) {
+ if(!mesh || !channel) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return;
+ }
+
+ pthread_mutex_lock(&mesh->mesh_mutex);
+ utcp_shutdown(channel->c, direction);
+ pthread_mutex_unlock(&mesh->mesh_mutex);
+}
+
+void meshlink_channel_close(meshlink_handle_t *mesh, meshlink_channel_t *channel) {
+ if(!mesh || !channel) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return;
+ }
+
+ pthread_mutex_lock(&mesh->mesh_mutex);
+
+ utcp_close(channel->c);
+
+ /* Clean up any outstanding AIO buffers. */
+ for(meshlink_aio_buffer_t *aio = channel->aio_send, *next; aio; aio = next) {
+ next = aio->next;
+ aio_signal(mesh, channel, aio);
+ free(aio);