+ if(mesh->threadstarted) {
+ logger(mesh, MESHLINK_DEBUG, "thread was already running\n");
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return true;
+ }
+
+ if(mesh->listen_socket[0].tcp.fd < 0) {
+ logger(mesh, MESHLINK_ERROR, "Listening socket not open\n");
+ meshlink_errno = MESHLINK_ENETWORK;
+ return false;
+ }
+
+ mesh->thedatalen = 0;
+
+ // TODO: open listening sockets first
+
+ //Check that a valid name is set
+ if(!mesh->name) {
+ logger(mesh, MESHLINK_DEBUG, "No name given!\n");
+ meshlink_errno = MESHLINK_EINVAL;
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return false;
+ }
+
+ init_outgoings(mesh);
+
+ // Start the main thread
+
+ event_loop_start(&mesh->loop);
+
+ if(pthread_create(&mesh->thread, NULL, meshlink_main_loop, mesh) != 0) {
+ logger(mesh, MESHLINK_DEBUG, "Could not start thread: %s\n", strerror(errno));
+ memset(&mesh->thread, 0, sizeof(mesh)->thread);
+ meshlink_errno = MESHLINK_EINTERNAL;
+ event_loop_stop(&mesh->loop);
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return false;
+ }
+
+ mesh->threadstarted = true;
+
+#if HAVE_CATTA
+
+ if(mesh->discovery) {
+ discovery_start(mesh);
+ }
+
+#endif
+
+ assert(mesh->self->ecdsa);
+ assert(!memcmp((uint8_t *)mesh->self->ecdsa + 64, (uint8_t *)mesh->private_key + 64, 32));
+
+
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return true;
+}
+
+void meshlink_stop(meshlink_handle_t *mesh) {
+ if(!mesh) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return;
+ }
+
+ 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);
+
+ // Send ourselves a UDP packet to kick the event loop
+ for(int i = 0; i < mesh->listen_sockets; i++) {
+ sockaddr_t sa;
+ socklen_t salen = sizeof(sa.sa);
+
+ if(getsockname(mesh->listen_socket[i].udp.fd, &sa.sa, &salen) == -1) {
+ logger(mesh, MESHLINK_ERROR, "System call `%s' failed: %s", "getsockname", sockstrerror(sockerrno));
+ continue;
+ }
+
+ if(sendto(mesh->listen_socket[i].udp.fd, "", 1, MSG_NOSIGNAL, &sa.sa, salen) == -1) {
+ logger(mesh, MESHLINK_ERROR, "Could not send a UDP packet to ourself: %s", sockstrerror(sockerrno));
+ }
+ }
+
+ if(mesh->threadstarted) {
+ // Wait for the main thread to finish
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ pthread_join(mesh->thread, NULL);
+ pthread_mutex_lock(&(mesh->mesh_mutex));
+
+ mesh->threadstarted = false;
+ }
+
+ // Close all metaconnections
+ if(mesh->connections) {
+ for(list_node_t *node = mesh->connections->head, *next; node; node = next) {
+ next = node->next;
+ connection_t *c = node->data;
+ c->outgoing = NULL;
+ terminate_connection(mesh, c, false);
+ }
+ }
+
+ exit_outgoings(mesh);
+
+ // Write out any changed node config files
+ if(mesh->nodes) {
+ for splay_each(node_t, n, mesh->nodes) {
+ if(n->status.dirty) {
+ node_write_config(mesh, n);
+ n->status.dirty = false;
+ }
+ }
+ }
+
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+}
+
+void meshlink_close(meshlink_handle_t *mesh) {
+ if(!mesh) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return;
+ }
+
+ // stop can be called even if mesh has not been started
+ meshlink_stop(mesh);
+
+ // lock is not released after this
+ pthread_mutex_lock(&(mesh->mesh_mutex));
+
+ // Close and free all resources used.
+
+ close_network_connections(mesh);
+
+ logger(mesh, MESHLINK_INFO, "Terminating");
+
+ event_loop_exit(&mesh->loop);
+
+#ifdef HAVE_MINGW
+
+ if(mesh->confbase) {
+ WSACleanup();
+ }
+
+#endif
+
+ ecdsa_free(mesh->invitation_key);
+
+ if(mesh->netns != -1) {
+ close(mesh->netns);
+ }
+
+ free(mesh->name);
+ free(mesh->appname);
+ free(mesh->confbase);
+ free(mesh->config_key);
+ ecdsa_free(mesh->private_key);
+ pthread_mutex_destroy(&(mesh->mesh_mutex));
+
+ main_config_unlock(mesh);
+
+ memset(mesh, 0, sizeof(*mesh));
+
+ free(mesh);
+}
+
+bool meshlink_destroy(const char *confbase) {
+ if(!confbase) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return false;
+ }
+
+ if(!config_destroy(confbase, "current")) {
+ logger(NULL, MESHLINK_ERROR, "Cannot remove confbase sub-directories %s: %s\n", confbase, strerror(errno));
+ return false;
+ }
+
+ config_destroy(confbase, "new");
+ config_destroy(confbase, "old");
+
+ if(rmdir(confbase) && errno != ENOENT) {
+ logger(NULL, MESHLINK_ERROR, "Cannot remove directory %s: %s\n", confbase, strerror(errno));
+ meshlink_errno = MESHLINK_ESTORAGE;
+ return false;
+ }
+
+ return true;
+}
+
+void meshlink_set_receive_cb(meshlink_handle_t *mesh, meshlink_receive_cb_t cb) {
+ if(!mesh) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return;
+ }
+
+ pthread_mutex_lock(&(mesh->mesh_mutex));
+ mesh->receive_cb = cb;
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+}
+
+void meshlink_set_connection_try_cb(meshlink_handle_t *mesh, meshlink_connection_try_cb_t cb) {
+ if(!mesh) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return;
+ }
+
+ pthread_mutex_lock(&(mesh->mesh_mutex));
+ mesh->connection_try_cb = cb;
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+}
+
+void meshlink_set_node_status_cb(meshlink_handle_t *mesh, meshlink_node_status_cb_t cb) {
+ if(!mesh) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return;
+ }
+
+ pthread_mutex_lock(&(mesh->mesh_mutex));
+ mesh->node_status_cb = cb;
+ 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));
+ mesh->log_cb = cb;
+ mesh->log_level = cb ? level : 0;
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ } else {
+ global_log_cb = cb;
+ global_log_level = cb ? level : 0;
+ }
+}
+
+bool meshlink_send(meshlink_handle_t *mesh, meshlink_node_t *destination, const void *data, size_t len) {
+ meshlink_packethdr_t *hdr;
+
+ // Validate arguments
+ if(!mesh || !destination || len >= MAXSIZE - sizeof(*hdr)) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return false;
+ }
+
+ if(!len) {
+ return true;
+ }
+
+ if(!data) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return false;
+ }
+
+ node_t *n = (node_t *)destination;
+
+ if(n->status.blacklisted) {
+ logger(mesh, MESHLINK_ERROR, "Node %s blacklisted, dropping packet\n", n->name);
+ return false;
+ }
+
+ // Prepare the packet
+ vpn_packet_t *packet = malloc(sizeof(*packet));
+
+ if(!packet) {
+ meshlink_errno = MESHLINK_ENOMEM;
+ return false;
+ }
+
+ packet->probe = false;
+ packet->tcp = false;
+ packet->len = len + sizeof(*hdr);
+
+ hdr = (meshlink_packethdr_t *)packet->data;
+ memset(hdr, 0, sizeof(*hdr));
+ // leave the last byte as 0 to make sure strings are always
+ // null-terminated if they are longer than the buffer
+ strncpy((char *)hdr->destination, destination->name, (sizeof(hdr)->destination) - 1);
+ strncpy((char *)hdr->source, mesh->self->name, (sizeof(hdr)->source) - 1);
+
+ memcpy(packet->data + sizeof(*hdr), data, len);
+
+ // Queue it
+ if(!meshlink_queue_push(&mesh->outpacketqueue, packet)) {
+ free(packet);
+ meshlink_errno = MESHLINK_ENOMEM;
+ return false;
+ }
+
+ // Notify event loop
+ signal_trigger(&(mesh->loop), &(mesh->datafromapp));
+
+ return true;
+}
+
+void meshlink_send_from_queue(event_loop_t *loop, meshlink_handle_t *mesh) {
+ (void)loop;
+ vpn_packet_t *packet = meshlink_queue_pop(&mesh->outpacketqueue);
+
+ if(!packet) {
+ return;
+ }
+
+ mesh->self->in_packets++;
+ mesh->self->in_bytes += packet->len;
+ route(mesh, mesh->self, packet);
+}
+
+ssize_t meshlink_get_pmtu(meshlink_handle_t *mesh, meshlink_node_t *destination) {
+ if(!mesh || !destination) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return -1;
+ }
+
+ pthread_mutex_lock(&(mesh->mesh_mutex));
+
+ node_t *n = (node_t *)destination;
+
+ if(!n->status.reachable) {
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return 0;
+
+ } else if(n->mtuprobes > 30 && n->minmtu) {
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return n->minmtu;
+ } else {
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return MTU;
+ }
+}
+
+char *meshlink_get_fingerprint(meshlink_handle_t *mesh, meshlink_node_t *node) {
+ if(!mesh || !node) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return NULL;
+ }
+
+ pthread_mutex_lock(&(mesh->mesh_mutex));
+
+ node_t *n = (node_t *)node;
+
+ if(!node_read_public_key(mesh, n) || !n->ecdsa) {
+ meshlink_errno = MESHLINK_EINTERNAL;
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return false;
+ }
+
+ char *fingerprint = ecdsa_get_base64_public_key(n->ecdsa);
+
+ if(!fingerprint) {
+ meshlink_errno = MESHLINK_EINTERNAL;
+ }
+
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return fingerprint;
+}
+
+meshlink_node_t *meshlink_get_self(meshlink_handle_t *mesh) {
+ if(!mesh) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return NULL;
+ }
+
+ return (meshlink_node_t *)mesh->self;
+}
+
+meshlink_node_t *meshlink_get_node(meshlink_handle_t *mesh, const char *name) {
+ if(!mesh || !name) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return NULL;
+ }
+
+ meshlink_node_t *node = NULL;
+
+ pthread_mutex_lock(&(mesh->mesh_mutex));
+ node = (meshlink_node_t *)lookup_node(mesh, (char *)name); // TODO: make lookup_node() use const
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return node;
+}
+
+meshlink_submesh_t *meshlink_get_submesh(meshlink_handle_t *mesh, const char *name) {
+ if(!mesh || !name) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return NULL;
+ }
+
+ meshlink_submesh_t *submesh = NULL;
+
+ pthread_mutex_lock(&(mesh->mesh_mutex));
+ submesh = (meshlink_submesh_t *)lookup_submesh(mesh, name);
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return submesh;
+}
+
+meshlink_node_t **meshlink_get_all_nodes(meshlink_handle_t *mesh, meshlink_node_t **nodes, size_t *nmemb) {
+ if(!mesh || !nmemb || (*nmemb && !nodes)) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return NULL;
+ }
+
+ meshlink_node_t **result;
+
+ //lock mesh->nodes
+ pthread_mutex_lock(&(mesh->mesh_mutex));
+
+ *nmemb = mesh->nodes->count;
+ result = realloc(nodes, *nmemb * sizeof(*nodes));
+
+ if(result) {
+ meshlink_node_t **p = result;
+
+ for splay_each(node_t, n, mesh->nodes) {
+ *p++ = (meshlink_node_t *)n;
+ }
+ } else {
+ *nmemb = 0;
+ free(nodes);
+ meshlink_errno = MESHLINK_ENOMEM;
+ }
+
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+
+ return result;
+}
+
+static meshlink_node_t **meshlink_get_all_nodes_by_condition(meshlink_handle_t *mesh, const void *condition, meshlink_node_t **nodes, size_t *nmemb, search_node_by_condition_t search_node) {
+ meshlink_node_t **result;
+
+ pthread_mutex_lock(&(mesh->mesh_mutex));
+
+ *nmemb = 0;
+
+ for splay_each(node_t, n, mesh->nodes) {
+ if(true == search_node(n, condition)) {
+ *nmemb = *nmemb + 1;
+ }
+ }
+
+ if(*nmemb == 0) {
+ free(nodes);
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return NULL;
+ }
+
+ result = realloc(nodes, *nmemb * sizeof(*nodes));
+
+ if(result) {
+ meshlink_node_t **p = result;
+
+ for splay_each(node_t, n, mesh->nodes) {
+ if(true == search_node(n, condition)) {
+ *p++ = (meshlink_node_t *)n;
+ }
+ }
+ } else {
+ *nmemb = 0;
+ free(nodes);
+ meshlink_errno = MESHLINK_ENOMEM;
+ }
+
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+
+ return result;
+}
+
+static bool search_node_by_dev_class(const node_t *node, const void *condition) {
+ dev_class_t *devclass = (dev_class_t *)condition;
+
+ if(*devclass == (dev_class_t)node->devclass) {
+ return true;
+ }
+
+ return false;
+}
+
+static bool search_node_by_submesh(const node_t *node, const void *condition) {
+ if(condition == node->submesh) {
+ return true;
+ }
+
+ return false;
+}
+
+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 || ((int)devclass < 0) || (devclass > _DEV_CLASS_MAX) || !nmemb) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return NULL;
+ }
+
+ return meshlink_get_all_nodes_by_condition(mesh, &devclass, nodes, nmemb, search_node_by_dev_class);
+}
+
+meshlink_node_t **meshlink_get_all_nodes_by_submesh(meshlink_handle_t *mesh, meshlink_submesh_t *submesh, meshlink_node_t **nodes, size_t *nmemb) {
+ if(!mesh || !submesh || !nmemb) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return NULL;
+ }
+
+ return meshlink_get_all_nodes_by_condition(mesh, submesh, nodes, nmemb, search_node_by_submesh);
+}
+
+dev_class_t meshlink_get_node_dev_class(meshlink_handle_t *mesh, meshlink_node_t *node) {
+ if(!mesh || !node) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return -1;
+ }