]> git.meshlink.io Git - meshlink/blobdiff - src/meshlink.c
Allow Catta to be disabled.
[meshlink] / src / meshlink.c
index 412976644cdb5d702647b406ab083d920ce3162b..6a2c99da68d8c416f79704a3dd8b615ba8906921 100644 (file)
@@ -165,9 +165,9 @@ static void scan_for_hostname(const char *filename, char **hostname, char **port
                p += strspn(p, "\t ");
                p[strcspn(p, "\t ")] = 0;
 
-               if(!*port && !strcasecmp(line, "Port")) {
+               if(!*port && !strcasecmp(line, "Port"))
                        *port = xstrdup(q);
-               else if(!*hostname && !strcasecmp(line, "Address")) {
+               else if(!*hostname && !strcasecmp(line, "Address")) {
                        *hostname = xstrdup(q);
                        if(*p) {
                                free(*port);
@@ -181,31 +181,41 @@ static void scan_for_hostname(const char *filename, char **hostname, char **port
 
        fclose(f);
 }
-static char *get_my_hostname(meshlink_handle_t* mesh) {
-       char *hostname = NULL;
-       char *port = NULL;
-       char *hostport = NULL;
-       char *name = mesh->self->name;
-       char filename[PATH_MAX] = "";
-       char line[4096];
-       FILE *f;
 
-       // Use first Address statement in own host config file
-       snprintf(filename, sizeof filename, "%s" SLASH "hosts" SLASH "%s", mesh->confbase, name);
-       scan_for_hostname(filename, &hostname, &port);
+static bool is_valid_hostname(const char *hostname) {
+       for(const char *p = hostname; *p; p++) {
+               if(!(isalnum(*p) || *p == '-' || *p == '.' || *p == ':'))
+                       return false;
+       }
 
-       if(hostname)
-               goto done;
+       return true;
+}
+
+static void set_timeout(int sock, int timeout) {
+#ifdef _WIN32
+       DWORD tv = timeout;
+#else
+       struct timeval tv;
+       tv.tv_sec = timeout / 1000;
+       tv.tv_usec = (timeout - tv.tv_sec * 1000) * 1000;
+#endif
+       setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
+       setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
+}
+
+char *meshlink_get_external_address(meshlink_handle_t *mesh) {
+       char *hostname = NULL;
 
-       // If that doesn't work, guess externally visible hostname
        logger(mesh, MESHLINK_DEBUG, "Trying to discover externally visible hostname...\n");
        struct addrinfo *ai = str2addrinfo("meshlink.io", "80", SOCK_STREAM);
        struct addrinfo *aip = ai;
        static const char request[] = "GET http://www.meshlink.io/host.cgi HTTP/1.0\r\n\r\n";
+       char line[256];
 
        while(aip) {
                int s = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol);
                if(s >= 0) {
+                       set_timeout(s, 5000);
                        if(connect(s, aip->ai_addr, aip->ai_addrlen)) {
                                closesocket(s);
                                s = -1;
@@ -234,17 +244,33 @@ static char *get_my_hostname(meshlink_handle_t* mesh) {
                freeaddrinfo(ai);
 
        // Check that the hostname is reasonable
-       if(hostname) {
-               for(char *p = hostname; *p; p++) {
-                       if(isalnum(*p) || *p == '-' || *p == '.' || *p == ':')
-                               continue;
-                       // If not, forget it.
-                       free(hostname);
-                       hostname = NULL;
-                       break;
-               }
+       if(hostname && !is_valid_hostname(hostname)) {
+               free(hostname);
+               hostname = NULL;
        }
 
+       if(!hostname)
+               meshlink_errno = MESHLINK_ERESOLV;
+
+       return hostname;
+}
+
+static char *get_my_hostname(meshlink_handle_t* mesh) {
+       char *hostname = NULL;
+       char *port = NULL;
+       char *hostport = NULL;
+       char *name = mesh->self->name;
+       char filename[PATH_MAX] = "";
+       FILE *f;
+
+       // Use first Address statement in own host config file
+       snprintf(filename, sizeof filename, "%s" SLASH "hosts" SLASH "%s", mesh->confbase, name);
+       scan_for_hostname(filename, &hostname, &port);
+
+       if(hostname)
+               goto done;
+
+       hostname = meshlink_get_external_address(mesh);
        if(!hostname)
                return NULL;
 
@@ -252,9 +278,8 @@ static char *get_my_hostname(meshlink_handle_t* mesh) {
        if(f) {
                fprintf(f, "\nAddress = %s\n", hostname);
                fclose(f);
-       } else {
+       } else
                logger(mesh, MESHLINK_DEBUG, "Could not append Address to %s: %s\n", filename, strerror(errno));
-       }
 
 done:
        if(port) {
@@ -505,7 +530,7 @@ static bool finalize_join(meshlink_handle_t *mesh) {
        if(!b64key) {
                fclose(fh);
                return false;
-               }
+       }
 
        fprintf(fh, "ECDSAPublicKey = %s\n", b64key);
        fprintf(fh, "Port = %s\n", mesh->myport);
@@ -544,28 +569,28 @@ static bool invitation_send(void *handle, uint8_t type, const void *data, size_t
 static bool invitation_receive(void *handle, uint8_t type, const void *msg, uint16_t len) {
        meshlink_handle_t* mesh = handle;
        switch(type) {
-               case SPTPS_HANDSHAKE:
-                       return sptps_send_record(&(mesh->sptps), 0, mesh->cookie, sizeof mesh->cookie);
-
-               case 0:
-                       mesh->data = xrealloc(mesh->data, mesh->thedatalen + len + 1);
-                       memcpy(mesh->data + mesh->thedatalen, msg, len);
-                       mesh->thedatalen += len;
-                       mesh->data[mesh->thedatalen] = 0;
-                       break;
-
-               case 1:
-                       mesh->thedatalen = 0;
-                       return finalize_join(mesh);
-
-               case 2:
-                       logger(mesh, MESHLINK_DEBUG, "Invitation succesfully accepted.\n");
-                       shutdown(mesh->sock, SHUT_RDWR);
-                       mesh->success = true;
-                       break;
-
-               default:
-                       return false;
+       case SPTPS_HANDSHAKE:
+               return sptps_send_record(&(mesh->sptps), 0, mesh->cookie, sizeof mesh->cookie);
+
+       case 0:
+               mesh->data = xrealloc(mesh->data, mesh->thedatalen + len + 1);
+               memcpy(mesh->data + mesh->thedatalen, msg, len);
+               mesh->thedatalen += len;
+               mesh->data[mesh->thedatalen] = 0;
+               break;
+
+       case 1:
+               mesh->thedatalen = 0;
+               return finalize_join(mesh);
+
+       case 2:
+               logger(mesh, MESHLINK_DEBUG, "Invitation succesfully accepted.\n");
+               shutdown(mesh->sock, SHUT_RDWR);
+               mesh->success = true;
+               break;
+
+       default:
+               return false;
        }
 
        return true;
@@ -819,7 +844,7 @@ static bool meshlink_setup(meshlink_handle_t *mesh) {
 meshlink_handle_t *meshlink_open(const char *confbase, const char *name, const char* appname, dev_class_t devclass) {
        // Validate arguments provided by the application
        bool usingname = false;
-       
+
        logger(NULL, MESHLINK_DEBUG, "meshlink_open called\n");
 
        if(!confbase || !*confbase) {
@@ -837,14 +862,14 @@ meshlink_handle_t *meshlink_open(const char *confbase, const char *name, const c
        if(!name || !*name) {
                logger(NULL, MESHLINK_ERROR, "No name given!\n");
                //return NULL;
-       }
-       else { //check name only if there is a name != NULL
+       } else { //check name only if there is a name != NULL
 
                if(!check_id(name)) {
                        logger(NULL, MESHLINK_ERROR, "Invalid name given!\n");
                        meshlink_errno = MESHLINK_EINVAL;
                        return NULL;
-               } else { usingname = true;}
+               } else
+                       usingname = true;
        }
 
        if(devclass < 0 || devclass > _DEV_CLASS_MAX) {
@@ -857,14 +882,14 @@ meshlink_handle_t *meshlink_open(const char *confbase, const char *name, const c
        mesh->confbase = xstrdup(confbase);
        mesh->appname = xstrdup(appname);
        mesh->devclass = devclass;
-       if (usingname) mesh->name = xstrdup(name);
+       if(usingname) mesh->name = xstrdup(name);
 
        // initialize mutex
        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
        pthread_mutex_init(&(mesh->mesh_mutex), &attr);
-       
+
        mesh->threadstarted = false;
        event_loop_init(&mesh->loop);
        mesh->loop.data = mesh;
@@ -941,10 +966,17 @@ bool meshlink_start(meshlink_handle_t *mesh) {
                meshlink_errno = MESHLINK_EINVAL;
                return false;
        }
-       pthread_mutex_lock(&(mesh->mesh_mutex));
-       
+
        logger(mesh, MESHLINK_DEBUG, "meshlink_start called\n");
 
+       pthread_mutex_lock(&(mesh->mesh_mutex));
+
+       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;
@@ -956,7 +988,7 @@ bool meshlink_start(meshlink_handle_t *mesh) {
        // TODO: open listening sockets first
 
        //Check that a valid name is set
-       if(!mesh->name ) {
+       if(!mesh->name) {
                logger(mesh, MESHLINK_DEBUG, "No name given!\n");
                meshlink_errno = MESHLINK_EINVAL;
                pthread_mutex_unlock(&(mesh->mesh_mutex));
@@ -965,6 +997,8 @@ bool meshlink_start(meshlink_handle_t *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);
@@ -975,7 +1009,8 @@ bool meshlink_start(meshlink_handle_t *mesh) {
 
        mesh->threadstarted=true;
 
-       discovery_start(mesh);
+       if(mesh->discovery)
+               discovery_start(mesh);
 
        pthread_mutex_unlock(&(mesh->mesh_mutex));
        return true;
@@ -991,19 +1026,16 @@ void meshlink_stop(meshlink_handle_t *mesh) {
        logger(mesh, MESHLINK_DEBUG, "meshlink_stop called\n");
 
        // Stop discovery
-       discovery_stop(mesh);
-
-       pthread_mutex_lock(&(mesh->mesh_mutex));
-       logger(mesh, MESHLINK_DEBUG, "meshlink_stop called\n");
+       if(mesh->discovery)
+               discovery_stop(mesh);
 
        // Shut down the main thread
        event_loop_stop(&mesh->loop);
 
-       // Fake a connection to kick the event loop
+       // Send ourselves a UDP packet to kick the event loop
        listen_socket_t *s = &mesh->listen_socket[0];
-       int fd = socket(s->sa.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
-       connect(fd, &s->sa.sa, SALEN(s->sa.sa));
-       close(fd);
+       if(sendto(s->udp.fd, "", 1, MSG_NOSIGNAL, &s->sa.sa, SALEN(s->sa.sa)) == -1)
+               logger(mesh, MESHLINK_ERROR, "Could not send a UDP packet to ourself");
 
        // Wait for the main thread to finish
        pthread_mutex_unlock(&(mesh->mesh_mutex));
@@ -1163,7 +1195,7 @@ bool meshlink_send(meshlink_handle_t *mesh, meshlink_node_t *destination, const
        // leave the last byte as 0 to make sure strings are always
        // null-terminated if they are longer than the buffer
        strncpy(hdr->destination, destination->name, (sizeof hdr->destination) - 1);
-       strncpy(hdr->source, mesh->self->name, (sizeof hdr->source) -1 );
+       strncpy(hdr->source, mesh->self->name, (sizeof hdr->source) -1);
 
        memcpy(packet->data + sizeof *hdr, data, len);
 
@@ -1176,7 +1208,7 @@ bool meshlink_send(meshlink_handle_t *mesh, meshlink_node_t *destination, const
 
        // Notify event loop
        signal_trigger(&(mesh->loop),&(mesh->datafromapp));
-       
+
        return true;
 }
 
@@ -1201,13 +1233,11 @@ ssize_t meshlink_get_pmtu(meshlink_handle_t *mesh, meshlink_node_t *destination)
        if(!n->status.reachable) {
                pthread_mutex_unlock(&(mesh->mesh_mutex));
                return 0;
-       
-       }
-       else if(n->mtuprobes > 30 && n->minmtu) {
+
+       } else if(n->mtuprobes > 30 && n->minmtu) {
                pthread_mutex_unlock(&(mesh->mesh_mutex));
                return n->minmtu;
-       }
-       else {
+       } else {
                pthread_mutex_unlock(&(mesh->mesh_mutex));
                return MTU;
        }
@@ -1333,16 +1363,15 @@ bool meshlink_verify(meshlink_handle_t *mesh, meshlink_node_t *source, const voi
        if(!n->ecdsa) {
                meshlink_errno = MESHLINK_EINTERNAL;
                rval = false;
-       } else {
+       } else
                rval = ecdsa_verify(((struct node_t *)source)->ecdsa, data, len, signature);
-       }
        pthread_mutex_unlock(&(mesh->mesh_mutex));
        return rval;
 }
 
 static bool refresh_invitation_key(meshlink_handle_t *mesh) {
        char filename[PATH_MAX];
-       
+
        pthread_mutex_lock(&(mesh->mesh_mutex));
 
        snprintf(filename, sizeof filename, "%s" SLASH "invitations", mesh->confbase);
@@ -1455,22 +1484,99 @@ bool meshlink_add_address(meshlink_handle_t *mesh, const char *address) {
                meshlink_errno = MESHLINK_EINVAL;
                return false;
        }
-       
+
+       if(!is_valid_hostname(address)) {
+               logger(mesh, MESHLINK_DEBUG, "Invalid character in address: %s\n", address);
+               meshlink_errno = MESHLINK_EINVAL;
+               return false;
+       }
+
        bool rval = false;
 
        pthread_mutex_lock(&(mesh->mesh_mutex));
+       rval = append_config_file(mesh, mesh->self->name, "Address", address);
+       pthread_mutex_unlock(&(mesh->mesh_mutex));
 
-       for(const char *p = address; *p; p++) {
-               if(isalnum(*p) || *p == '-' || *p == '.' || *p == ':')
-                       continue;
-               logger(mesh, MESHLINK_DEBUG, "Invalid character in address: %s\n", address);
+       return rval;
+}
+
+bool meshlink_add_external_address(meshlink_handle_t *mesh) {
+       if(!mesh) {
                meshlink_errno = MESHLINK_EINVAL;
-               pthread_mutex_unlock(&(mesh->mesh_mutex));
                return false;
        }
 
+       char *address = meshlink_get_external_address(mesh);
+       if(!address)
+               return false;
+
+       bool rval = false;
+
+       pthread_mutex_lock(&(mesh->mesh_mutex));
        rval = append_config_file(mesh, mesh->self->name, "Address", address);
        pthread_mutex_unlock(&(mesh->mesh_mutex));
+
+       free(address);
+       return rval;
+}
+
+int meshlink_get_port(meshlink_handle_t *mesh) {
+       if(!mesh) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return -1;
+       }
+
+       if(!mesh->myport) {
+               meshlink_errno = MESHLINK_EINTERNAL;
+               return -1;
+       }
+
+       return atoi(mesh->myport);
+}
+
+bool meshlink_set_port(meshlink_handle_t *mesh, int port) {
+       if(!mesh || port < 0 || port >= 65536 || mesh->threadstarted) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return false;
+       }
+
+       if(mesh->myport && port == atoi(mesh->myport))
+               return true;
+
+       if(!try_bind(port)) {
+               meshlink_errno = MESHLINK_ENETWORK;
+               return false;
+       }
+
+       bool rval = false;
+
+       pthread_mutex_lock(&(mesh->mesh_mutex));
+       if(mesh->threadstarted) {
+               meshlink_errno = MESHLINK_EINVAL;
+               goto done;
+       }
+
+       close_network_connections(mesh);
+       exit_configuration(&mesh->config);
+
+       char portstr[10];
+       snprintf(portstr, sizeof portstr, "%d", port);
+       portstr[sizeof portstr - 1] = 0;
+
+       modify_config_file(mesh, mesh->name, "Port", portstr, true);
+
+       init_configuration(&mesh->config);
+
+       if(!read_server_config(mesh))
+               meshlink_errno = MESHLINK_ESTORAGE;
+       else if(!setup_network(mesh))
+               meshlink_errno = MESHLINK_ENETWORK;
+       else
+               rval = true;
+
+done:
+       pthread_mutex_unlock(&(mesh->mesh_mutex));
+
        return rval;
 }
 
@@ -1479,7 +1585,7 @@ char *meshlink_invite(meshlink_handle_t *mesh, const char *name) {
                meshlink_errno = MESHLINK_EINVAL;
                return NULL;
        }
-       
+
        pthread_mutex_lock(&(mesh->mesh_mutex));
 
        // Check validity of the new node's name
@@ -1562,7 +1668,7 @@ char *meshlink_invite(meshlink_handle_t *mesh, const char *name) {
        // Fill in the details.
        fprintf(f, "Name = %s\n", name);
        //if(netname)
-       //      fprintf(f, "NetName = %s\n", netname);
+       //      fprintf(f, "NetName = %s\n", netname);
        fprintf(f, "ConnectTo = %s\n", mesh->self->name);
 
        // Copy Broadcast and Mode
@@ -1572,7 +1678,7 @@ char *meshlink_invite(meshlink_handle_t *mesh, const char *name) {
                char buf[1024];
                while(fgets(buf, sizeof buf, tc)) {
                        if((!strncasecmp(buf, "Mode", 4) && strchr(" \t=", buf[4]))
-                                       || (!strncasecmp(buf, "Broadcast", 9) && strchr(" \t=", buf[9]))) {
+                                       || (!strncasecmp(buf, "Broadcast", 9) && strchr(" \t=", buf[9]))) {
                                fputs(buf, f);
                                // Make sure there is a newline character.
                                if(!strchr(buf, '\n'))
@@ -1608,7 +1714,7 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) {
                meshlink_errno = MESHLINK_EINVAL;
                return false;
        }
-       
+
        pthread_mutex_lock(&(mesh->mesh_mutex));
 
        //TODO: think of a better name for this variable, or of a different way to tokenize the invitation URL.
@@ -1659,9 +1765,8 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) {
        char *b64key = ecdsa_get_base64_public_key(key);
 
        //Before doing meshlink_join make sure we are not connected to another mesh
-       if ( mesh->threadstarted ){
+       if(mesh->threadstarted)
                goto invalid;
-       }
 
        // Connect to the meshlink daemon mentioned in the URL.
        struct addrinfo *ai = str2addrinfo(address, port, SOCK_STREAM);
@@ -1680,6 +1785,8 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) {
                return false;
        }
 
+       set_timeout(mesh->sock, 5000);
+
        if(connect(mesh->sock, ai->ai_addr, ai->ai_addrlen)) {
                logger(mesh, MESHLINK_DEBUG, "Could not connect to %s port %s: %s\n", address, port, strerror(errno));
                closesocket(mesh->sock);
@@ -1743,7 +1850,7 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) {
        }
 
        // Start an SPTPS session
-       if(!sptps_start(&mesh->sptps, mesh, true, false, key, hiskey, "meshlink invitation", 15, invitation_send, invitation_receive)) {
+       if(!sptps_start(&mesh->sptps, mesh, true, false, key, hiskey, meshlink_invitation_label, sizeof meshlink_invitation_label, invitation_send, invitation_receive)) {
                meshlink_errno = MESHLINK_EINTERNAL;
                pthread_mutex_unlock(&(mesh->mesh_mutex));
                return false;
@@ -1804,7 +1911,7 @@ char *meshlink_export(meshlink_handle_t *mesh) {
        }
 
        pthread_mutex_lock(&(mesh->mesh_mutex));
-       
+
        char filename[PATH_MAX];
        snprintf(filename, sizeof filename, "%s" SLASH "hosts" SLASH "%s", mesh->confbase, mesh->self->name);
        FILE *f = fopen(filename, "r");
@@ -1832,7 +1939,7 @@ char *meshlink_export(meshlink_handle_t *mesh) {
 
        fclose(f);
        buf[len - 1] = 0;
-       
+
        pthread_mutex_unlock(&(mesh->mesh_mutex));
        return buf;
 }
@@ -1842,7 +1949,7 @@ bool meshlink_import(meshlink_handle_t *mesh, const char *data) {
                meshlink_errno = MESHLINK_EINVAL;
                return false;
        }
-       
+
        pthread_mutex_lock(&(mesh->mesh_mutex));
 
        if(strncmp(data, "Name = ", 7)) {
@@ -1911,7 +2018,7 @@ void meshlink_blacklist(meshlink_handle_t *mesh, meshlink_node_t *node) {
        }
 
        pthread_mutex_lock(&(mesh->mesh_mutex));
-       
+
        node_t *n;
        n = (node_t*)node;
        n->status.blacklisted=true;
@@ -1931,7 +2038,7 @@ void meshlink_whitelist(meshlink_handle_t *mesh, meshlink_node_t *node) {
        }
 
        pthread_mutex_lock(&(mesh->mesh_mutex));
-       
+
        node_t *n = (node_t *)node;
        n->status.blacklisted = false;
 
@@ -1955,15 +2062,15 @@ void meshlink_hint_address(meshlink_handle_t *mesh, meshlink_node_t *node, const
        // Ignore hints about ourself.
        if((node_t *)node == mesh->self)
                return;
-       
+
        pthread_mutex_lock(&(mesh->mesh_mutex));
-       
+
        char *host = NULL, *port = NULL, *str = NULL;
        sockaddr2str((const sockaddr_t *)addr, &host, &port);
 
        if(host && port) {
                xasprintf(&str, "%s %s", host, port);
-               if ( (strncmp ("fe80",host,4) != 0) && ( strncmp("127.",host,4) != 0 ) && ( strcmp("localhost",host) !=0 ) )
+               if((strncmp("fe80",host,4) != 0) && (strncmp("127.",host,4) != 0) && (strcmp("localhost",host) !=0))
                        append_config_file(mesh, node->name, "Address", str);
                else
                        logger(mesh, MESHLINK_DEBUG, "Not adding Link Local IPv6 Address to config\n");
@@ -1988,7 +2095,7 @@ meshlink_edge_t **meshlink_get_all_edges_state(meshlink_handle_t *mesh, meshlink
        }
 
        pthread_mutex_lock(&(mesh->mesh_mutex));
-       
+
        meshlink_edge_t **result = NULL;
        meshlink_edge_t *copy = NULL;
        int result_size = 0;
@@ -1996,11 +2103,10 @@ meshlink_edge_t **meshlink_get_all_edges_state(meshlink_handle_t *mesh, meshlink
        result_size = mesh->edges->count;
 
        // if result is smaller than edges, we have to dealloc all the excess meshlink_edge_t
-       if(result_size > *nmemb) {
-               result = realloc(edges, result_size * sizeof (meshlink_edge_t*));
-       } else {
+       if(result_size > *nmemb)
+               result = realloc(edges, result_size * sizeof(meshlink_edge_t*));
+       else
                result = edges;
-       }
 
        if(result) {
                meshlink_edge_t **p = result;
@@ -2013,12 +2119,10 @@ meshlink_edge_t **meshlink_get_all_edges_state(meshlink_handle_t *mesh, meshlink
                        }
                        n++;
                        // the first *nmemb members of result can be re-used
-                       if(n > *nmemb) {
+                       if(n > *nmemb)
                                copy = xzalloc(sizeof *copy);
-                       }
-                       else {
+                       else
                                copy = *p;
-                       }
                        copy->from = (meshlink_node_t*)e->from;
                        copy->to = (meshlink_node_t*)e->to;
                        copy->address = e->address.storage;
@@ -2027,14 +2131,12 @@ meshlink_edge_t **meshlink_get_all_edges_state(meshlink_handle_t *mesh, meshlink
                        *p++ = copy;
                }
                // shrink result to the actual amount of memory used
-               for(int i = *nmemb; i > result_size; i--) {
+               for(int i = *nmemb; i > result_size; i--)
                        free(result[i - 1]);
-               }
-               result = realloc(result, result_size * sizeof (meshlink_edge_t*));
+               result = realloc(result, result_size * sizeof(meshlink_edge_t*));
                *nmemb = result_size;
        } else {
                *nmemb = 0;
-               free(result);
                meshlink_errno = MESHLINK_ENOMEM;
        }
 
@@ -2054,12 +2156,9 @@ static ssize_t channel_recv(struct utcp_connection *connection, const void *data
                abort();
        node_t *n = channel->node;
        meshlink_handle_t *mesh = n->mesh;
-       if(!channel->receive_cb)
-               return -1;
-       else {
+       if(channel->receive_cb)
                channel->receive_cb(mesh, channel, data, len);
-               return len;
-       }
+       return len;
 }
 
 static void channel_accept(struct utcp_connection *utcp_connection, uint16_t port) {
@@ -2081,9 +2180,6 @@ static void channel_accept(struct utcp_connection *utcp_connection, uint16_t por
 static ssize_t channel_send(struct utcp *utcp, const void *data, size_t len) {
        node_t *n = utcp->priv;
        meshlink_handle_t *mesh = n->mesh;
-       char hex[len * 2 + 1];
-       bin2hex(data, hex, len);
-       logger(mesh, MESHLINK_WARNING, "channel_send(%p, %p, %zu): %s\n", utcp, data, len, hex);
        return meshlink_send(mesh, (meshlink_node_t *)n, data, len) ? len : -1;
 }
 
@@ -2100,9 +2196,6 @@ static void channel_receive(meshlink_handle_t *mesh, meshlink_node_t *source, co
        node_t *n = (node_t *)source;
        if(!n->utcp)
                abort();
-       char hex[len * 2 + 1];
-       bin2hex(data, hex, len);
-       logger(mesh, MESHLINK_WARNING, "channel_receive(%p, %p, %zu): %s\n", n->utcp, data, len, hex);
        utcp_recv(n->utcp, data, len);
 }
 
@@ -2131,21 +2224,18 @@ void meshlink_set_channel_accept_cb(meshlink_handle_t *mesh, meshlink_channel_ac
        mesh->channel_accept_cb = cb;
        mesh->receive_cb = channel_receive;
        for splay_each(node_t, n, mesh->nodes) {
-               if(!n->utcp && n != mesh->self) {
-                       logger(mesh, MESHLINK_WARNING, "utcp_init on node %s", n->name);
+               if(!n->utcp && n != mesh->self)
                        n->utcp = utcp_init(channel_accept, channel_pre_accept, channel_send, n);
-               }
        }
        pthread_mutex_unlock(&mesh->mesh_mutex);
 }
 
-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) {
+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(!mesh || !node) {
                meshlink_errno = MESHLINK_EINVAL;
                return NULL;
        }
 
-       logger(mesh, MESHLINK_WARNING, "meshlink_channel_open(%p, %s, %u, %p, %p, %zu)\n", mesh, node->name, port, cb, data, len);
        node_t *n = (node_t *)node;
        if(!n->utcp) {
                n->utcp = utcp_init(channel_accept, channel_pre_accept, channel_send, n);
@@ -2158,7 +2248,7 @@ meshlink_channel_t *meshlink_channel_open(meshlink_handle_t *mesh, meshlink_node
        meshlink_channel_t *channel = xzalloc(sizeof *channel);
        channel->node = n;
        channel->receive_cb = cb;
-       channel->c = utcp_connect(n->utcp, port, channel_recv, channel);
+       channel->c = utcp_connect_ex(n->utcp, port, channel_recv, channel, flags);
        if(!channel->c) {
                meshlink_errno = errno == ENOMEM ? MESHLINK_ENOMEM : MESHLINK_EINTERNAL;
                free(channel);
@@ -2167,6 +2257,10 @@ meshlink_channel_t *meshlink_channel_open(meshlink_handle_t *mesh, meshlink_node
        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;
@@ -2214,6 +2308,15 @@ ssize_t meshlink_channel_send(meshlink_handle_t *mesh, meshlink_channel_t *chann
        return retval;
 }
 
+uint32_t meshlink_channel_get_flags(meshlink_handle_t *mesh, meshlink_channel_t *channel) {
+       if(!mesh || !channel) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return -1;
+       }
+
+       return channel->c->flags;
+}
+
 void update_node_status(meshlink_handle_t *mesh, node_t *n) {
        if(n->status.reachable && mesh->channel_accept_cb && !n->utcp)
                n->utcp = utcp_init(channel_accept, channel_pre_accept, channel_send, n);
@@ -2221,8 +2324,35 @@ void update_node_status(meshlink_handle_t *mesh, node_t *n) {
                mesh->node_status_cb(mesh, (meshlink_node_t *)n, n->status.reachable);
 }
 
+void meshlink_enable_discovery(meshlink_handle_t *mesh, bool enable) {
+       if(!mesh) {
+               meshlink_errno = MESHLINK_EINVAL;
+               return;
+       }
+
+       pthread_mutex_lock(&mesh->mesh_mutex);
+
+       if(mesh->discovery == enable)
+               goto end;
+
+       if(mesh->threadstarted) {
+               if(enable)
+                       discovery_start(mesh);
+               else
+                       discovery_stop(mesh);
+       }
+
+       mesh->discovery = enable;
+
+end:
+       pthread_mutex_unlock(&mesh->mesh_mutex);
+}
+
 static void __attribute__((constructor)) meshlink_init(void) {
        crypto_init();
+       unsigned int seed;
+       randomize(&seed, sizeof seed);
+       srand(seed);
 }
 
 static void __attribute__((destructor)) meshlink_exit(void) {
@@ -2231,8 +2361,8 @@ static void __attribute__((destructor)) meshlink_exit(void) {
 
 /// Device class traits
 dev_class_traits_t dev_class_traits[_DEV_CLASS_MAX +1] = {
-       { .min_connects = 3, .max_connects = 10000, .edge_weight = 1 }, // DEV_CLASS_BACKBONE
-       { .min_connects = 3, .max_connects = 100, .edge_weight = 3 },   // DEV_CLASS_STATIONARY
-       { .min_connects = 3, .max_connects = 3, .edge_weight = 6 },             // DEV_CLASS_PORTABLE
-       { .min_connects = 1, .max_connects = 1, .edge_weight = 9 },             // DEV_CLASS_UNKNOWN
+       { .min_connects = 3, .max_connects = 10000, .edge_weight = 1 }, // DEV_CLASS_BACKBONE
+       { .min_connects = 3, .max_connects = 100, .edge_weight = 3 },   // DEV_CLASS_STATIONARY
+       { .min_connects = 3, .max_connects = 3, .edge_weight = 6 },             // DEV_CLASS_PORTABLE
+       { .min_connects = 1, .max_connects = 1, .edge_weight = 9 },             // DEV_CLASS_UNKNOWN
 };