2 meshlink.c -- Implementation of the MeshLink API.
3 Copyright (C) 2014-2018 Guus Sliepen <guus@meshlink.io>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #define VAR_SERVER 1 /* Should be in meshlink.conf */
20 #define VAR_HOST 2 /* Can be in host config file */
21 #define VAR_MULTIPLE 4 /* Multiple statements allowed */
22 #define VAR_OBSOLETE 8 /* Should not be used anymore */
23 #define VAR_SAFE 16 /* Variable is safe when accepting invitations */
24 #define MAX_ADDRESS_LENGTH 45 /* Max length of an (IPv6) address */
25 #define MAX_PORT_LENGTH 5 /* 0-65535 */
37 #include "meshlink_internal.h"
45 #include "ed25519/sha512.h"
46 #include "discovery.h"
49 #define MSG_NOSIGNAL 0
52 __thread meshlink_errno_t meshlink_errno;
53 meshlink_log_cb_t global_log_cb;
54 meshlink_log_level_t global_log_level;
56 //TODO: this can go away completely
57 const var_t variables[] = {
58 /* Server configuration */
59 {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
61 /* Host configuration */
62 {"CanonicalAddress", VAR_HOST},
63 {"Address", VAR_HOST | VAR_MULTIPLE},
64 {"ECDSAPublicKey", VAR_HOST},
69 static bool fcopy(FILE *out, const char *filename) {
70 FILE *in = fopen(filename, "r");
73 logger(NULL, MESHLINK_ERROR, "Could not open %s: %s\n", filename, strerror(errno));
80 while((len = fread(buf, 1, sizeof(buf), in))) {
81 fwrite(buf, len, 1, out);
88 static int rstrip(char *value) {
89 int len = strlen(value);
91 while(len && strchr("\t\r\n ", value[len - 1])) {
98 static void scan_for_canonical_address(const char *filename, char **hostname, char **port) {
101 if(!filename || (*hostname && *port)) {
105 FILE *f = fopen(filename, "r");
111 while(fgets(line, sizeof(line), f)) {
117 p += strcspn(p, "\t =");
123 q = p + strspn(p, "\t ");
126 q += 1 + strspn(q + 1, "\t ");
129 // q is now pointing to the hostname
131 p = q + strcspn(q, "\t ");
137 p += strspn(p, "\t ");
138 p[strcspn(p, "\t ")] = 0;
139 // p is now pointing to the port, if present
141 if(!*port && !strcasecmp(line, "Port")) {
143 } else if(!strcasecmp(line, "CanonicalAddress")) {
144 *hostname = xstrdup(q);
152 if(*hostname && *port) {
160 static bool is_valid_hostname(const char *hostname) {
165 for(const char *p = hostname; *p; p++) {
166 if(!(isalnum(*p) || *p == '-' || *p == '.' || *p == ':')) {
174 static bool is_valid_port(const char *port) {
181 unsigned long int result = strtoul(port, &end, 10);
182 return result && result < 65536 && !*end;
185 for(const char *p = port; *p; p++) {
186 if(!(isalnum(*p) || *p == '-')) {
194 static void set_timeout(int sock, int timeout) {
199 tv.tv_sec = timeout / 1000;
200 tv.tv_usec = (timeout - tv.tv_sec * 1000) * 1000;
202 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
203 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
206 // Find out what local address a socket would use if we connect to the given address.
207 // We do this using connect() on a UDP socket, so the kernel has to resolve the address
208 // of both endpoints, but this will actually not send any UDP packet.
209 static bool getlocaladdrname(char *destaddr, char *host, socklen_t hostlen) {
210 struct addrinfo *rai = NULL;
211 const struct addrinfo hint = {
212 .ai_family = AF_UNSPEC,
213 .ai_socktype = SOCK_DGRAM,
214 .ai_protocol = IPPROTO_UDP,
217 if(getaddrinfo(destaddr, "80", &hint, &rai) || !rai) {
221 int sock = socket(rai->ai_family, rai->ai_socktype, rai->ai_protocol);
228 if(connect(sock, rai->ai_addr, rai->ai_addrlen) && !sockwouldblock(errno)) {
235 struct sockaddr_storage sn;
236 socklen_t sl = sizeof(sn);
238 if(getsockname(sock, (struct sockaddr *)&sn, &sl)) {
242 if(getnameinfo((struct sockaddr *)&sn, sl, host, hostlen, NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV)) {
249 char *meshlink_get_external_address(meshlink_handle_t *mesh) {
250 return meshlink_get_external_address_for_family(mesh, AF_UNSPEC);
253 char *meshlink_get_external_address_for_family(meshlink_handle_t *mesh, int family) {
254 char *hostname = NULL;
256 logger(mesh, MESHLINK_DEBUG, "Trying to discover externally visible hostname...\n");
257 struct addrinfo *ai = str2addrinfo("meshlink.io", "80", SOCK_STREAM);
258 static const char request[] = "GET http://www.meshlink.io/host.cgi HTTP/1.0\r\n\r\n";
261 for(struct addrinfo *aip = ai; aip; aip = aip->ai_next) {
262 if(family != AF_UNSPEC && aip->ai_family != family) {
266 int s = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol);
269 set_timeout(s, 5000);
271 if(connect(s, aip->ai_addr, aip->ai_addrlen)) {
278 send(s, request, sizeof(request) - 1, 0);
279 int len = recv(s, line, sizeof(line) - 1, MSG_WAITALL);
284 if(line[len - 1] == '\n') {
288 char *p = strrchr(line, '\n');
291 hostname = xstrdup(p + 1);
307 // Check that the hostname is reasonable
308 if(hostname && !is_valid_hostname(hostname)) {
314 meshlink_errno = MESHLINK_ERESOLV;
320 char *meshlink_get_local_address_for_family(meshlink_handle_t *mesh, int family) {
323 // Determine address of the local interface used for outgoing connections.
324 char localaddr[NI_MAXHOST];
325 bool success = false;
327 if(family == AF_INET) {
328 success = getlocaladdrname("93.184.216.34", localaddr, sizeof(localaddr));
329 } else if(family == AF_INET6) {
330 success = getlocaladdrname("2606:2800:220:1:248:1893:25c8:1946", localaddr, sizeof(localaddr));
334 meshlink_errno = MESHLINK_ENETWORK;
338 return xstrdup(localaddr);
341 void remove_duplicate_hostnames(char *host[], char *port[], int n) {
342 for(int i = 0; i < n; i++) {
347 // Ignore duplicate hostnames
350 for(int j = 0; j < i; j++) {
355 if(strcmp(host[i], host[j])) {
359 if(strcmp(port[i], port[j])) {
377 // This gets the hostname part for use in invitation URLs
378 static char *get_my_hostname(meshlink_handle_t *mesh, uint32_t flags) {
379 char *hostname[4] = {NULL};
380 char *port[4] = {NULL};
381 char *hostport = NULL;
383 if(!(flags & (MESHLINK_INVITE_LOCAL | MESHLINK_INVITE_PUBLIC))) {
384 flags |= MESHLINK_INVITE_LOCAL | MESHLINK_INVITE_PUBLIC;
387 if(!(flags & (MESHLINK_INVITE_IPV4 | MESHLINK_INVITE_IPV6))) {
388 flags |= MESHLINK_INVITE_IPV4 | MESHLINK_INVITE_IPV6;
391 fprintf(stderr, "flags = %u\n", flags);
393 // Add local addresses if requested
394 if(flags & MESHLINK_INVITE_LOCAL) {
395 if(flags & MESHLINK_INVITE_IPV4) {
396 hostname[0] = meshlink_get_local_address_for_family(mesh, AF_INET);
399 if(flags & MESHLINK_INVITE_IPV6) {
400 hostname[1] = meshlink_get_local_address_for_family(mesh, AF_INET6);
404 // Add public/canonical addresses if requested
405 if(flags & MESHLINK_INVITE_PUBLIC) {
406 // Try the CanonicalAddress first
407 char filename[PATH_MAX] = "";
408 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", mesh->confbase, mesh->self->name);
409 scan_for_canonical_address(filename, &hostname[2], &port[2]);
412 if(flags & MESHLINK_INVITE_IPV4) {
413 hostname[2] = meshlink_get_external_address_for_family(mesh, AF_INET);
416 if(flags & MESHLINK_INVITE_IPV6) {
417 hostname[3] = meshlink_get_external_address_for_family(mesh, AF_INET6);
422 for(int i = 0; i < 4; i++) {
423 // Ensure we always have a port number
424 if(hostname[i] && !port[i]) {
425 port[i] = xstrdup(mesh->myport);
429 remove_duplicate_hostnames(hostname, port, 4);
431 if(!(flags & MESHLINK_INVITE_NUMERIC)) {
432 for(int i = 0; i < 4; i++) {
437 // Convert what we have to a sockaddr
438 struct addrinfo *ai_in, *ai_out;
439 struct addrinfo hint = {
440 .ai_family = AF_UNSPEC,
441 .ai_flags = AI_NUMERICSERV,
442 .ai_socktype = SOCK_STREAM,
444 int err = getaddrinfo(hostname[i], port[i], &hint, &ai_in);
450 // Convert it to a hostname
451 char resolved_host[NI_MAXHOST];
452 char resolved_port[NI_MAXSERV];
453 err = getnameinfo(ai_in->ai_addr, ai_in->ai_addrlen, resolved_host, sizeof resolved_host, resolved_port, sizeof resolved_port, NI_NUMERICSERV);
460 // Convert the hostname back to a sockaddr
461 hint.ai_family = ai_in->ai_family;
462 err = getaddrinfo(resolved_host, resolved_port, &hint, &ai_out);
469 // Check if it's still the same sockaddr
470 if(ai_in->ai_addrlen != ai_out->ai_addrlen || memcmp(ai_in->ai_addr, ai_out->ai_addr, ai_in->ai_addrlen)) {
472 freeaddrinfo(ai_out);
476 // Yes: replace the hostname with the resolved one
478 hostname[i] = xstrdup(resolved_host);
481 freeaddrinfo(ai_out);
485 // Remove duplicates again, since IPv4 and IPv6 addresses might map to the same hostname
486 remove_duplicate_hostnames(hostname, port, 4);
488 // Concatenate all unique address to the hostport string
489 for(int i = 0; i < 4; i++) {
494 // Ensure we have the same addresses in our own host config file.
496 xasprintf(&tmphostport, "%s %s", hostname[i], port[i]);
497 append_config_file(mesh, mesh->self->name, "Address", tmphostport);
500 // Append the address to the hostport string
502 xasprintf(&newhostport, (strchr(hostname[i], ':') ? "%s%s[%s]:%s" : "%s%s%s:%s"), hostport ? hostport : "", hostport ? "," : "", hostname[i], port[i]);
504 hostport = newhostport;
513 static char *get_line(const char **data) {
514 if(!data || !*data) {
523 static char line[1024];
524 const char *end = strchr(*data, '\n');
525 size_t len = end ? (size_t)(end - *data) : strlen(*data);
527 if(len >= sizeof(line)) {
528 logger(NULL, MESHLINK_ERROR, "Maximum line length exceeded!\n");
532 if(len && !isprint(**data)) {
536 memcpy(line, *data, len);
548 static char *get_value(const char *data, const char *var) {
549 char *line = get_line(&data);
555 char *sep = line + strcspn(line, " \t=");
556 char *val = sep + strspn(sep, " \t");
559 val += 1 + strspn(val + 1, " \t");
564 if(strcasecmp(line, var)) {
571 static bool try_bind(int port) {
572 struct addrinfo *ai = NULL;
573 struct addrinfo hint = {
574 .ai_flags = AI_PASSIVE,
575 .ai_family = AF_UNSPEC,
576 .ai_socktype = SOCK_STREAM,
577 .ai_protocol = IPPROTO_TCP,
581 snprintf(portstr, sizeof(portstr), "%d", port);
583 if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai) {
588 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
595 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
610 int check_port(meshlink_handle_t *mesh) {
611 for(int i = 0; i < 1000; i++) {
612 int port = 0x1000 + (rand() & 0x7fff);
615 char filename[PATH_MAX];
616 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", mesh->confbase, mesh->name);
617 FILE *f = fopen(filename, "a");
620 meshlink_errno = MESHLINK_ESTORAGE;
621 logger(mesh, MESHLINK_DEBUG, "Could not store Port.\n");
625 fprintf(f, "Port = %d\n", port);
631 meshlink_errno = MESHLINK_ENETWORK;
632 logger(mesh, MESHLINK_DEBUG, "Could not find any available network port.\n");
636 static void deltree(const char *dirname) {
637 DIR *d = opendir(dirname);
642 while((ent = readdir(d))) {
643 if(ent->d_name[0] == '.') {
647 char filename[PATH_MAX];
648 snprintf(filename, sizeof(filename), "%s" SLASH "%s", dirname, ent->d_name);
650 if(unlink(filename)) {
661 static bool finalize_join(meshlink_handle_t *mesh) {
662 char *name = xstrdup(get_value(mesh->data, "Name"));
665 logger(mesh, MESHLINK_DEBUG, "No Name found in invitation!\n");
669 if(!check_id(name)) {
670 logger(mesh, MESHLINK_DEBUG, "Invalid Name found in invitation: %s!\n", name);
674 char filename[PATH_MAX];
675 snprintf(filename, sizeof(filename), "%s" SLASH "meshlink.conf", mesh->confbase);
677 FILE *f = fopen(filename, "w");
680 logger(mesh, MESHLINK_DEBUG, "Could not create file %s: %s\n", filename, strerror(errno));
684 fprintf(f, "Name = %s\n", name);
686 // Wipe all old host config files and invitations
687 snprintf(filename, sizeof(filename), "%s" SLASH "hosts", mesh->confbase);
690 if(mkdir(filename, 0777) && errno != EEXIST) {
691 logger(mesh, MESHLINK_DEBUG, "Could not create directory %s: %s\n", filename, strerror(errno));
695 snprintf(filename, sizeof(filename), "%s" SLASH "invitations", mesh->confbase);
698 // Create a new host config file for ourself
699 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", mesh->confbase, name);
700 FILE *fh = fopen(filename, "w");
703 logger(mesh, MESHLINK_DEBUG, "Could not create file %s: %s\n", filename, strerror(errno));
708 // Filter first chunk on approved keywords, split between meshlink.conf and hosts/Name
709 // Other chunks go unfiltered to their respective host config files
710 const char *p = mesh->data;
713 while((l = get_line(&p))) {
719 // Split line into variable and value
720 int len = strcspn(l, "\t =");
722 value += strspn(value, "\t ");
726 value += strspn(value, "\t ");
732 if(!strcasecmp(l, "Name"))
733 if(strcmp(value, name)) {
737 } else if(!strcasecmp(l, "NetName")) {
741 // Check the list of known variables
745 for(i = 0; variables[i].name; i++) {
746 if(strcasecmp(l, variables[i].name)) {
754 // Ignore unknown and unsafe variables
756 logger(mesh, MESHLINK_DEBUG, "Ignoring unknown variable '%s' in invitation.\n", l);
758 } else if(!(variables[i].type & VAR_SAFE)) {
759 logger(mesh, MESHLINK_DEBUG, "Ignoring unsafe variable '%s' in invitation.\n", l);
763 // Copy the safe variable to the right config file
764 fprintf(variables[i].type & VAR_HOST ? fh : f, "%s = %s\n", l, value);
769 while(l && !strcasecmp(l, "Name")) {
770 if(!check_id(value)) {
771 logger(mesh, MESHLINK_DEBUG, "Invalid Name found in invitation.\n");
775 if(!strcmp(value, name)) {
776 logger(mesh, MESHLINK_DEBUG, "Secondary chunk would overwrite our own host config file.\n");
780 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", mesh->confbase, value);
781 f = fopen(filename, "w");
784 logger(mesh, MESHLINK_DEBUG, "Could not create file %s: %s\n", filename, strerror(errno));
788 while((l = get_line(&p))) {
789 if(!strcmp(l, "#---------------------------------------------------------------#")) {
793 int len = strcspn(l, "\t =");
795 if(len == 4 && !strncasecmp(l, "Name", 4)) {
797 value += strspn(value, "\t ");
801 value += strspn(value, "\t ");
815 char *b64key = ecdsa_get_base64_public_key(mesh->self->connection->ecdsa);
822 fprintf(fh, "ECDSAPublicKey = %s\n", b64key);
823 fprintf(fh, "Port = %s\n", mesh->myport);
827 sptps_send_record(&(mesh->sptps), 1, b64key, strlen(b64key));
831 free(mesh->self->name);
832 free(mesh->self->connection->name);
833 mesh->name = xstrdup(name);
834 mesh->self->name = xstrdup(name);
835 mesh->self->connection->name = name;
837 logger(mesh, MESHLINK_DEBUG, "Configuration stored in: %s\n", mesh->confbase);
839 load_all_nodes(mesh);
844 static bool invitation_send(void *handle, uint8_t type, const void *data, size_t len) {
846 meshlink_handle_t *mesh = handle;
847 const char *ptr = data;
850 int result = send(mesh->sock, ptr, len, 0);
852 if(result == -1 && errno == EINTR) {
854 } else if(result <= 0) {
865 static bool invitation_receive(void *handle, uint8_t type, const void *msg, uint16_t len) {
866 meshlink_handle_t *mesh = handle;
869 case SPTPS_HANDSHAKE:
870 return sptps_send_record(&(mesh->sptps), 0, mesh->cookie, sizeof(mesh)->cookie);
873 mesh->data = xrealloc(mesh->data, mesh->thedatalen + len + 1);
874 memcpy(mesh->data + mesh->thedatalen, msg, len);
875 mesh->thedatalen += len;
876 mesh->data[mesh->thedatalen] = 0;
880 mesh->thedatalen = 0;
881 return finalize_join(mesh);
884 logger(mesh, MESHLINK_DEBUG, "Invitation succesfully accepted.\n");
885 shutdown(mesh->sock, SHUT_RDWR);
886 mesh->success = true;
896 static bool recvline(meshlink_handle_t *mesh, size_t len) {
897 char *newline = NULL;
903 while(!(newline = memchr(mesh->buffer, '\n', mesh->blen))) {
904 int result = recv(mesh->sock, mesh->buffer + mesh->blen, sizeof(mesh)->buffer - mesh->blen, 0);
906 if(result == -1 && errno == EINTR) {
908 } else if(result <= 0) {
912 mesh->blen += result;
915 if((size_t)(newline - mesh->buffer) >= len) {
919 len = newline - mesh->buffer;
921 memcpy(mesh->line, mesh->buffer, len);
923 memmove(mesh->buffer, newline + 1, mesh->blen - len - 1);
924 mesh->blen -= len + 1;
928 static bool sendline(int fd, char *format, ...) {
929 static char buffer[4096];
934 va_start(ap, format);
935 blen = vsnprintf(buffer, sizeof(buffer), format, ap);
938 if(blen < 1 || (size_t)blen >= sizeof(buffer)) {
946 int result = send(fd, p, blen, MSG_NOSIGNAL);
948 if(result == -1 && errno == EINTR) {
950 } else if(result <= 0) {
961 static const char *errstr[] = {
962 [MESHLINK_OK] = "No error",
963 [MESHLINK_EINVAL] = "Invalid argument",
964 [MESHLINK_ENOMEM] = "Out of memory",
965 [MESHLINK_ENOENT] = "No such node",
966 [MESHLINK_EEXIST] = "Node already exists",
967 [MESHLINK_EINTERNAL] = "Internal error",
968 [MESHLINK_ERESOLV] = "Could not resolve hostname",
969 [MESHLINK_ESTORAGE] = "Storage error",
970 [MESHLINK_ENETWORK] = "Network error",
971 [MESHLINK_EPEER] = "Error communicating with peer",
972 [MESHLINK_ENOTSUP] = "Operation not supported",
973 [MESHLINK_EBUSY] = "MeshLink instance already in use",
976 const char *meshlink_strerror(meshlink_errno_t err) {
977 if((int)err < 0 || err >= sizeof(errstr) / sizeof(*errstr)) {
978 return "Invalid error code";
984 static bool ecdsa_keygen(meshlink_handle_t *mesh) {
987 char pubname[PATH_MAX], privname[PATH_MAX];
989 logger(mesh, MESHLINK_DEBUG, "Generating ECDSA keypair:\n");
991 if(!(key = ecdsa_generate())) {
992 logger(mesh, MESHLINK_DEBUG, "Error during key generation!\n");
993 meshlink_errno = MESHLINK_EINTERNAL;
996 logger(mesh, MESHLINK_DEBUG, "Done.\n");
999 if(snprintf(privname, sizeof(privname), "%s" SLASH "ecdsa_key.priv", mesh->confbase) >= PATH_MAX) {
1000 logger(mesh, MESHLINK_DEBUG, "Filename too long: %s" SLASH "ecdsa_key.priv\n", mesh->confbase);
1001 meshlink_errno = MESHLINK_ESTORAGE;
1005 f = fopen(privname, "wb");
1008 meshlink_errno = MESHLINK_ESTORAGE;
1013 fchmod(fileno(f), 0600);
1016 if(!ecdsa_write_pem_private_key(key, f)) {
1017 logger(mesh, MESHLINK_DEBUG, "Error writing private key!\n");
1020 meshlink_errno = MESHLINK_EINTERNAL;
1026 snprintf(pubname, sizeof(pubname), "%s" SLASH "hosts" SLASH "%s", mesh->confbase, mesh->name);
1027 f = fopen(pubname, "a");
1030 meshlink_errno = MESHLINK_ESTORAGE;
1034 char *pubkey = ecdsa_get_base64_public_key(key);
1035 fprintf(f, "ECDSAPublicKey = %s\n", pubkey);
1044 static struct timeval idle(event_loop_t *loop, void *data) {
1046 meshlink_handle_t *mesh = data;
1047 struct timeval t, tmin = {3600, 0};
1049 for splay_each(node_t, n, mesh->nodes) {
1054 t = utcp_timeout(n->utcp);
1056 if(timercmp(&t, &tmin, <)) {
1064 // Get our local address(es) by simulating connecting to an Internet host.
1065 static void add_local_addresses(meshlink_handle_t *mesh) {
1066 char host[NI_MAXHOST];
1067 char entry[MAX_STRING_SIZE];
1071 if(getlocaladdrname("93.184.216.34", host, sizeof(host))) {
1072 snprintf(entry, sizeof(entry), "%s %s", host, mesh->myport);
1073 append_config_file(mesh, mesh->name, "Address", entry);
1078 if(getlocaladdrname("2606:2800:220:1:248:1893:25c8:1946", host, sizeof(host))) {
1079 snprintf(entry, sizeof(entry), "%s %s", host, mesh->myport);
1080 append_config_file(mesh, mesh->name, "Address", entry);
1084 static bool meshlink_setup(meshlink_handle_t *mesh) {
1085 if(mkdir(mesh->confbase, 0777) && errno != EEXIST) {
1086 logger(mesh, MESHLINK_DEBUG, "Could not create directory %s: %s\n", mesh->confbase, strerror(errno));
1087 meshlink_errno = MESHLINK_ESTORAGE;
1091 char filename[PATH_MAX];
1092 snprintf(filename, sizeof(filename), "%s" SLASH "hosts", mesh->confbase);
1094 if(mkdir(filename, 0777) && errno != EEXIST) {
1095 logger(mesh, MESHLINK_DEBUG, "Could not create directory %s: %s\n", filename, strerror(errno));
1096 meshlink_errno = MESHLINK_ESTORAGE;
1100 snprintf(filename, sizeof(filename), "%s" SLASH "meshlink.conf", mesh->confbase);
1102 if(!access(filename, F_OK)) {
1103 logger(mesh, MESHLINK_DEBUG, "Configuration file %s already exists!\n", filename);
1104 meshlink_errno = MESHLINK_EEXIST;
1108 FILE *f = fopen(filename, "w");
1111 logger(mesh, MESHLINK_DEBUG, "Could not create file %s: %s\n", filename, strerror(errno));
1112 meshlink_errno = MESHLINK_ESTORAGE;
1116 fprintf(f, "Name = %s\n", mesh->name);
1119 if(!ecdsa_keygen(mesh)) {
1120 meshlink_errno = MESHLINK_EINTERNAL;
1125 if(check_port(mesh) == 0) {
1126 meshlink_errno = MESHLINK_ENETWORK;
1134 meshlink_handle_t *meshlink_open(const char *confbase, const char *name, const char *appname, dev_class_t devclass) {
1135 // Validate arguments provided by the application
1136 bool usingname = false;
1138 logger(NULL, MESHLINK_DEBUG, "meshlink_open called\n");
1140 if(!confbase || !*confbase) {
1141 logger(NULL, MESHLINK_ERROR, "No confbase given!\n");
1142 meshlink_errno = MESHLINK_EINVAL;
1146 if(!appname || !*appname) {
1147 logger(NULL, MESHLINK_ERROR, "No appname given!\n");
1148 meshlink_errno = MESHLINK_EINVAL;
1152 if(strchr(appname, ' ')) {
1153 logger(NULL, MESHLINK_ERROR, "Invalid appname given!\n");
1154 meshlink_errno = MESHLINK_EINVAL;
1158 if(!name || !*name) {
1159 logger(NULL, MESHLINK_ERROR, "No name given!\n");
1161 } else { //check name only if there is a name != NULL
1163 if(!check_id(name)) {
1164 logger(NULL, MESHLINK_ERROR, "Invalid name given!\n");
1165 meshlink_errno = MESHLINK_EINVAL;
1172 if((int)devclass < 0 || devclass > _DEV_CLASS_MAX) {
1173 logger(NULL, MESHLINK_ERROR, "Invalid devclass given!\n");
1174 meshlink_errno = MESHLINK_EINVAL;
1178 meshlink_handle_t *mesh = xzalloc(sizeof(meshlink_handle_t));
1179 mesh->confbase = xstrdup(confbase);
1180 mesh->appname = xstrdup(appname);
1181 mesh->devclass = devclass;
1182 mesh->discovery = true;
1183 mesh->invitation_timeout = 604800; // 1 week
1186 mesh->name = xstrdup(name);
1190 pthread_mutexattr_t attr;
1191 pthread_mutexattr_init(&attr);
1192 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
1193 pthread_mutex_init(&(mesh->mesh_mutex), &attr);
1195 mesh->threadstarted = false;
1196 event_loop_init(&mesh->loop);
1197 mesh->loop.data = mesh;
1199 meshlink_queue_init(&mesh->outpacketqueue);
1201 // Check whether meshlink.conf already exists
1203 char filename[PATH_MAX];
1204 snprintf(filename, sizeof(filename), "%s" SLASH "meshlink.conf", confbase);
1206 if(access(filename, R_OK)) {
1207 if(errno == ENOENT) {
1208 // If not, create it
1209 if(!meshlink_setup(mesh)) {
1210 // meshlink_errno is set by meshlink_setup()
1214 logger(NULL, MESHLINK_ERROR, "Cannot not read from %s: %s\n", filename, strerror(errno));
1215 meshlink_close(mesh);
1216 meshlink_errno = MESHLINK_ESTORAGE;
1221 // Open the configuration file and lock it
1223 mesh->conffile = fopen(filename, "r");
1225 if(!mesh->conffile) {
1226 logger(NULL, MESHLINK_ERROR, "Cannot not open %s: %s\n", filename, strerror(errno));
1227 meshlink_close(mesh);
1228 meshlink_errno = MESHLINK_ESTORAGE;
1233 fcntl(fileno(mesh->conffile), F_SETFD, FD_CLOEXEC);
1237 // TODO: use _locking()?
1240 if(flock(fileno(mesh->conffile), LOCK_EX | LOCK_NB) != 0) {
1241 logger(NULL, MESHLINK_ERROR, "Cannot lock %s: %s\n", filename, strerror(errno));
1242 meshlink_close(mesh);
1243 meshlink_errno = MESHLINK_EBUSY;
1249 // Read the configuration
1251 init_configuration(&mesh->config);
1253 if(!read_server_config(mesh)) {
1254 meshlink_close(mesh);
1255 meshlink_errno = MESHLINK_ESTORAGE;
1260 struct WSAData wsa_state;
1262 WSAStartup(MAKEWORD(2, 2), &wsa_state);
1266 // Setup up everything
1267 // TODO: we should not open listening sockets yet
1269 if(!setup_network(mesh)) {
1270 meshlink_close(mesh);
1271 meshlink_errno = MESHLINK_ENETWORK;
1275 add_local_addresses(mesh);
1277 idle_set(&mesh->loop, idle, mesh);
1279 logger(NULL, MESHLINK_DEBUG, "meshlink_open returning\n");
1283 static void *meshlink_main_loop(void *arg) {
1284 meshlink_handle_t *mesh = arg;
1286 pthread_mutex_lock(&(mesh->mesh_mutex));
1288 try_outgoing_connections(mesh);
1290 logger(mesh, MESHLINK_DEBUG, "Starting main_loop...\n");
1292 logger(mesh, MESHLINK_DEBUG, "main_loop returned.\n");
1294 pthread_mutex_unlock(&(mesh->mesh_mutex));
1298 bool meshlink_start(meshlink_handle_t *mesh) {
1300 meshlink_errno = MESHLINK_EINVAL;
1304 logger(mesh, MESHLINK_DEBUG, "meshlink_start called\n");
1306 pthread_mutex_lock(&(mesh->mesh_mutex));
1308 if(mesh->threadstarted) {
1309 logger(mesh, MESHLINK_DEBUG, "thread was already running\n");
1310 pthread_mutex_unlock(&(mesh->mesh_mutex));
1314 if(mesh->listen_socket[0].tcp.fd < 0) {
1315 logger(mesh, MESHLINK_ERROR, "Listening socket not open\n");
1316 meshlink_errno = MESHLINK_ENETWORK;
1320 mesh->thedatalen = 0;
1322 // TODO: open listening sockets first
1324 //Check that a valid name is set
1326 logger(mesh, MESHLINK_DEBUG, "No name given!\n");
1327 meshlink_errno = MESHLINK_EINVAL;
1328 pthread_mutex_unlock(&(mesh->mesh_mutex));
1332 // Start the main thread
1334 event_loop_start(&mesh->loop);
1336 if(pthread_create(&mesh->thread, NULL, meshlink_main_loop, mesh) != 0) {
1337 logger(mesh, MESHLINK_DEBUG, "Could not start thread: %s\n", strerror(errno));
1338 memset(&mesh->thread, 0, sizeof(mesh)->thread);
1339 meshlink_errno = MESHLINK_EINTERNAL;
1340 event_loop_stop(&mesh->loop);
1341 pthread_mutex_unlock(&(mesh->mesh_mutex));
1345 mesh->threadstarted = true;
1349 if(mesh->discovery) {
1350 discovery_start(mesh);
1355 pthread_mutex_unlock(&(mesh->mesh_mutex));
1359 void meshlink_stop(meshlink_handle_t *mesh) {
1361 meshlink_errno = MESHLINK_EINVAL;
1365 pthread_mutex_lock(&(mesh->mesh_mutex));
1366 logger(mesh, MESHLINK_DEBUG, "meshlink_stop called\n");
1371 if(mesh->discovery) {
1372 discovery_stop(mesh);
1377 // Shut down the main thread
1378 event_loop_stop(&mesh->loop);
1380 // Send ourselves a UDP packet to kick the event loop
1381 for(int i = 0; i < mesh->listen_sockets; i++) {
1383 socklen_t salen = sizeof(sa.sa);
1385 if(getsockname(mesh->listen_socket[i].udp.fd, &sa.sa, &salen) == -1) {
1386 logger(mesh, MESHLINK_ERROR, "System call `%s' failed: %s", "getsockname", sockstrerror(sockerrno));
1390 if(sendto(mesh->listen_socket[i].udp.fd, "", 1, MSG_NOSIGNAL, &sa.sa, salen) == -1) {
1391 logger(mesh, MESHLINK_ERROR, "Could not send a UDP packet to ourself: %s", sockstrerror(sockerrno));
1395 if(mesh->threadstarted) {
1396 // Wait for the main thread to finish
1397 pthread_mutex_unlock(&(mesh->mesh_mutex));
1398 pthread_join(mesh->thread, NULL);
1399 pthread_mutex_lock(&(mesh->mesh_mutex));
1401 mesh->threadstarted = false;
1404 // Close all metaconnections
1405 if(mesh->connections) {
1406 for(list_node_t *node = mesh->connections->head, *next; node; node = next) {
1408 connection_t *c = node->data;
1410 terminate_connection(mesh, c, false);
1414 if(mesh->outgoings) {
1415 list_delete_list(mesh->outgoings);
1416 mesh->outgoings = NULL;
1419 pthread_mutex_unlock(&(mesh->mesh_mutex));
1422 void meshlink_close(meshlink_handle_t *mesh) {
1423 if(!mesh || !mesh->confbase) {
1424 meshlink_errno = MESHLINK_EINVAL;
1428 // stop can be called even if mesh has not been started
1429 meshlink_stop(mesh);
1431 // lock is not released after this
1432 pthread_mutex_lock(&(mesh->mesh_mutex));
1434 // Close and free all resources used.
1436 close_network_connections(mesh);
1438 logger(mesh, MESHLINK_INFO, "Terminating");
1440 exit_configuration(&mesh->config);
1441 event_loop_exit(&mesh->loop);
1445 if(mesh->confbase) {
1451 ecdsa_free(mesh->invitation_key);
1454 free(mesh->appname);
1455 free(mesh->confbase);
1456 pthread_mutex_destroy(&(mesh->mesh_mutex));
1458 if(mesh->conffile) {
1459 fclose(mesh->conffile);
1462 memset(mesh, 0, sizeof(*mesh));
1467 bool meshlink_destroy(const char *confbase) {
1469 meshlink_errno = MESHLINK_EINVAL;
1473 char filename[PATH_MAX];
1474 snprintf(filename, sizeof(filename), "%s" SLASH "meshlink.conf", confbase);
1476 if(unlink(filename)) {
1477 if(errno == ENOENT) {
1478 meshlink_errno = MESHLINK_ENOENT;
1481 logger(NULL, MESHLINK_ERROR, "Cannot delete %s: %s\n", filename, strerror(errno));
1482 meshlink_errno = MESHLINK_ESTORAGE;
1492 void meshlink_set_receive_cb(meshlink_handle_t *mesh, meshlink_receive_cb_t cb) {
1494 meshlink_errno = MESHLINK_EINVAL;
1498 pthread_mutex_lock(&(mesh->mesh_mutex));
1499 mesh->receive_cb = cb;
1500 pthread_mutex_unlock(&(mesh->mesh_mutex));
1503 void meshlink_set_node_status_cb(meshlink_handle_t *mesh, meshlink_node_status_cb_t cb) {
1505 meshlink_errno = MESHLINK_EINVAL;
1509 pthread_mutex_lock(&(mesh->mesh_mutex));
1510 mesh->node_status_cb = cb;
1511 pthread_mutex_unlock(&(mesh->mesh_mutex));
1514 void meshlink_set_node_duplicate_cb(meshlink_handle_t *mesh, meshlink_node_duplicate_cb_t cb) {
1516 meshlink_errno = MESHLINK_EINVAL;
1520 pthread_mutex_lock(&(mesh->mesh_mutex));
1521 mesh->node_duplicate_cb = cb;
1522 pthread_mutex_unlock(&(mesh->mesh_mutex));
1525 void meshlink_set_log_cb(meshlink_handle_t *mesh, meshlink_log_level_t level, meshlink_log_cb_t cb) {
1527 pthread_mutex_lock(&(mesh->mesh_mutex));
1529 mesh->log_level = cb ? level : 0;
1530 pthread_mutex_unlock(&(mesh->mesh_mutex));
1533 global_log_level = cb ? level : 0;
1537 bool meshlink_send(meshlink_handle_t *mesh, meshlink_node_t *destination, const void *data, size_t len) {
1538 meshlink_packethdr_t *hdr;
1540 // Validate arguments
1541 if(!mesh || !destination || len >= MAXSIZE - sizeof(*hdr)) {
1542 meshlink_errno = MESHLINK_EINVAL;
1551 meshlink_errno = MESHLINK_EINVAL;
1555 // Prepare the packet
1556 vpn_packet_t *packet = malloc(sizeof(*packet));
1559 meshlink_errno = MESHLINK_ENOMEM;
1563 packet->probe = false;
1564 packet->tcp = false;
1565 packet->len = len + sizeof(*hdr);
1567 hdr = (meshlink_packethdr_t *)packet->data;
1568 memset(hdr, 0, sizeof(*hdr));
1569 // leave the last byte as 0 to make sure strings are always
1570 // null-terminated if they are longer than the buffer
1571 strncpy((char *)hdr->destination, destination->name, (sizeof(hdr)->destination) - 1);
1572 strncpy((char *)hdr->source, mesh->self->name, (sizeof(hdr)->source) - 1);
1574 memcpy(packet->data + sizeof(*hdr), data, len);
1577 if(!meshlink_queue_push(&mesh->outpacketqueue, packet)) {
1579 meshlink_errno = MESHLINK_ENOMEM;
1583 // Notify event loop
1584 signal_trigger(&(mesh->loop), &(mesh->datafromapp));
1589 void meshlink_send_from_queue(event_loop_t *loop, meshlink_handle_t *mesh) {
1591 vpn_packet_t *packet = meshlink_queue_pop(&mesh->outpacketqueue);
1597 mesh->self->in_packets++;
1598 mesh->self->in_bytes += packet->len;
1599 route(mesh, mesh->self, packet);
1602 ssize_t meshlink_get_pmtu(meshlink_handle_t *mesh, meshlink_node_t *destination) {
1603 if(!mesh || !destination) {
1604 meshlink_errno = MESHLINK_EINVAL;
1608 pthread_mutex_lock(&(mesh->mesh_mutex));
1610 node_t *n = (node_t *)destination;
1612 if(!n->status.reachable) {
1613 pthread_mutex_unlock(&(mesh->mesh_mutex));
1616 } else if(n->mtuprobes > 30 && n->minmtu) {
1617 pthread_mutex_unlock(&(mesh->mesh_mutex));
1620 pthread_mutex_unlock(&(mesh->mesh_mutex));
1625 char *meshlink_get_fingerprint(meshlink_handle_t *mesh, meshlink_node_t *node) {
1626 if(!mesh || !node) {
1627 meshlink_errno = MESHLINK_EINVAL;
1631 pthread_mutex_lock(&(mesh->mesh_mutex));
1633 node_t *n = (node_t *)node;
1635 if(!node_read_ecdsa_public_key(mesh, n) || !n->ecdsa) {
1636 meshlink_errno = MESHLINK_EINTERNAL;
1637 pthread_mutex_unlock(&(mesh->mesh_mutex));
1641 char *fingerprint = ecdsa_get_base64_public_key(n->ecdsa);
1644 meshlink_errno = MESHLINK_EINTERNAL;
1647 pthread_mutex_unlock(&(mesh->mesh_mutex));
1651 meshlink_node_t *meshlink_get_self(meshlink_handle_t *mesh) {
1653 meshlink_errno = MESHLINK_EINVAL;
1657 return (meshlink_node_t *)mesh->self;
1660 meshlink_node_t *meshlink_get_node(meshlink_handle_t *mesh, const char *name) {
1661 if(!mesh || !name) {
1662 meshlink_errno = MESHLINK_EINVAL;
1666 meshlink_node_t *node = NULL;
1668 pthread_mutex_lock(&(mesh->mesh_mutex));
1669 node = (meshlink_node_t *)lookup_node(mesh, (char *)name); // TODO: make lookup_node() use const
1670 pthread_mutex_unlock(&(mesh->mesh_mutex));
1674 meshlink_node_t **meshlink_get_all_nodes(meshlink_handle_t *mesh, meshlink_node_t **nodes, size_t *nmemb) {
1675 if(!mesh || !nmemb || (*nmemb && !nodes)) {
1676 meshlink_errno = MESHLINK_EINVAL;
1680 meshlink_node_t **result;
1683 pthread_mutex_lock(&(mesh->mesh_mutex));
1685 *nmemb = mesh->nodes->count;
1686 result = realloc(nodes, *nmemb * sizeof(*nodes));
1689 meshlink_node_t **p = result;
1691 for splay_each(node_t, n, mesh->nodes) {
1692 *p++ = (meshlink_node_t *)n;
1697 meshlink_errno = MESHLINK_ENOMEM;
1700 pthread_mutex_unlock(&(mesh->mesh_mutex));
1705 bool meshlink_sign(meshlink_handle_t *mesh, const void *data, size_t len, void *signature, size_t *siglen) {
1706 if(!mesh || !data || !len || !signature || !siglen) {
1707 meshlink_errno = MESHLINK_EINVAL;
1711 if(*siglen < MESHLINK_SIGLEN) {
1712 meshlink_errno = MESHLINK_EINVAL;
1716 pthread_mutex_lock(&(mesh->mesh_mutex));
1718 if(!ecdsa_sign(mesh->self->connection->ecdsa, data, len, signature)) {
1719 meshlink_errno = MESHLINK_EINTERNAL;
1720 pthread_mutex_unlock(&(mesh->mesh_mutex));
1724 *siglen = MESHLINK_SIGLEN;
1725 pthread_mutex_unlock(&(mesh->mesh_mutex));
1729 bool meshlink_verify(meshlink_handle_t *mesh, meshlink_node_t *source, const void *data, size_t len, const void *signature, size_t siglen) {
1730 if(!mesh || !data || !len || !signature) {
1731 meshlink_errno = MESHLINK_EINVAL;
1735 if(siglen != MESHLINK_SIGLEN) {
1736 meshlink_errno = MESHLINK_EINVAL;
1740 pthread_mutex_lock(&(mesh->mesh_mutex));
1744 struct node_t *n = (struct node_t *)source;
1745 node_read_ecdsa_public_key(mesh, n);
1748 meshlink_errno = MESHLINK_EINTERNAL;
1751 rval = ecdsa_verify(((struct node_t *)source)->ecdsa, data, len, signature);
1754 pthread_mutex_unlock(&(mesh->mesh_mutex));
1758 static bool refresh_invitation_key(meshlink_handle_t *mesh) {
1759 char filename[PATH_MAX];
1761 pthread_mutex_lock(&(mesh->mesh_mutex));
1763 snprintf(filename, sizeof(filename), "%s" SLASH "invitations", mesh->confbase);
1765 if(mkdir(filename, 0700) && errno != EEXIST) {
1766 logger(mesh, MESHLINK_DEBUG, "Could not create directory %s: %s\n", filename, strerror(errno));
1767 meshlink_errno = MESHLINK_ESTORAGE;
1768 pthread_mutex_unlock(&(mesh->mesh_mutex));
1772 // Count the number of valid invitations, clean up old ones
1773 DIR *dir = opendir(filename);
1776 logger(mesh, MESHLINK_DEBUG, "Could not read directory %s: %s\n", filename, strerror(errno));
1777 meshlink_errno = MESHLINK_ESTORAGE;
1778 pthread_mutex_unlock(&(mesh->mesh_mutex));
1785 time_t deadline = time(NULL) - 604800; // 1 week in the past
1787 while((ent = readdir(dir))) {
1788 if(strlen(ent->d_name) != 24) {
1792 char invname[PATH_MAX];
1795 if(snprintf(invname, sizeof(invname), "%s" SLASH "%s", filename, ent->d_name) >= PATH_MAX) {
1796 logger(mesh, MESHLINK_DEBUG, "Filename too long: %s" SLASH "%s", filename, ent->d_name);
1800 if(!stat(invname, &st)) {
1801 if(mesh->invitation_key && deadline < st.st_mtime) {
1807 logger(mesh, MESHLINK_DEBUG, "Could not stat %s: %s\n", invname, strerror(errno));
1813 logger(mesh, MESHLINK_DEBUG, "Error while reading directory %s: %s\n", filename, strerror(errno));
1815 meshlink_errno = MESHLINK_ESTORAGE;
1816 pthread_mutex_unlock(&(mesh->mesh_mutex));
1822 snprintf(filename, sizeof(filename), "%s" SLASH "invitations" SLASH "ecdsa_key.priv", mesh->confbase);
1824 // Remove the key if there are no outstanding invitations.
1828 if(mesh->invitation_key) {
1829 ecdsa_free(mesh->invitation_key);
1830 mesh->invitation_key = NULL;
1834 if(mesh->invitation_key) {
1835 pthread_mutex_unlock(&(mesh->mesh_mutex));
1839 // Create a new key if necessary.
1840 FILE *f = fopen(filename, "rb");
1843 if(errno != ENOENT) {
1844 logger(mesh, MESHLINK_DEBUG, "Could not read %s: %s\n", filename, strerror(errno));
1845 meshlink_errno = MESHLINK_ESTORAGE;
1846 pthread_mutex_unlock(&(mesh->mesh_mutex));
1850 mesh->invitation_key = ecdsa_generate();
1852 if(!mesh->invitation_key) {
1853 logger(mesh, MESHLINK_DEBUG, "Could not generate a new key!\n");
1854 meshlink_errno = MESHLINK_EINTERNAL;
1855 pthread_mutex_unlock(&(mesh->mesh_mutex));
1859 f = fopen(filename, "wb");
1862 logger(mesh, MESHLINK_DEBUG, "Could not write %s: %s\n", filename, strerror(errno));
1863 meshlink_errno = MESHLINK_ESTORAGE;
1864 pthread_mutex_unlock(&(mesh->mesh_mutex));
1868 chmod(filename, 0600);
1869 ecdsa_write_pem_private_key(mesh->invitation_key, f);
1872 mesh->invitation_key = ecdsa_read_pem_private_key(f);
1875 if(!mesh->invitation_key) {
1876 logger(mesh, MESHLINK_DEBUG, "Could not read private key from %s\n", filename);
1877 meshlink_errno = MESHLINK_ESTORAGE;
1881 pthread_mutex_unlock(&(mesh->mesh_mutex));
1882 return mesh->invitation_key;
1885 bool meshlink_set_canonical_address(meshlink_handle_t *mesh, meshlink_node_t *node, const char *address, const char *port) {
1886 if(!mesh || !node || !address) {
1887 meshlink_errno = MESHLINK_EINVAL;
1891 if(!is_valid_hostname(address)) {
1892 logger(mesh, MESHLINK_DEBUG, "Invalid character in address: %s\n", address);
1893 meshlink_errno = MESHLINK_EINVAL;
1897 if(port && !is_valid_port(port)) {
1898 logger(mesh, MESHLINK_DEBUG, "Invalid character in port: %s\n", address);
1899 meshlink_errno = MESHLINK_EINVAL;
1903 char *canonical_address;
1906 xasprintf(&canonical_address, "%s %s", address, port);
1908 canonical_address = xstrdup(address);
1911 pthread_mutex_lock(&(mesh->mesh_mutex));
1912 bool rval = modify_config_file(mesh, node->name, "CanonicalAddress", canonical_address, 1);
1913 pthread_mutex_unlock(&(mesh->mesh_mutex));
1915 free(canonical_address);
1919 bool meshlink_add_address(meshlink_handle_t *mesh, const char *address) {
1920 return meshlink_set_canonical_address(mesh, (meshlink_node_t *)mesh->self, address, NULL);
1923 bool meshlink_add_external_address(meshlink_handle_t *mesh) {
1925 meshlink_errno = MESHLINK_EINVAL;
1929 char *address = meshlink_get_external_address(mesh);
1937 pthread_mutex_lock(&(mesh->mesh_mutex));
1938 rval = append_config_file(mesh, mesh->self->name, "Address", address);
1939 pthread_mutex_unlock(&(mesh->mesh_mutex));
1945 int meshlink_get_port(meshlink_handle_t *mesh) {
1947 meshlink_errno = MESHLINK_EINVAL;
1952 meshlink_errno = MESHLINK_EINTERNAL;
1956 return atoi(mesh->myport);
1959 bool meshlink_set_port(meshlink_handle_t *mesh, int port) {
1960 if(!mesh || port < 0 || port >= 65536 || mesh->threadstarted) {
1961 meshlink_errno = MESHLINK_EINVAL;
1965 if(mesh->myport && port == atoi(mesh->myport)) {
1969 if(!try_bind(port)) {
1970 meshlink_errno = MESHLINK_ENETWORK;
1976 pthread_mutex_lock(&(mesh->mesh_mutex));
1978 if(mesh->threadstarted) {
1979 meshlink_errno = MESHLINK_EINVAL;
1983 close_network_connections(mesh);
1984 exit_configuration(&mesh->config);
1987 snprintf(portstr, sizeof(portstr), "%d", port);
1988 portstr[sizeof(portstr) - 1] = 0;
1990 modify_config_file(mesh, mesh->name, "Port", portstr, true);
1992 init_configuration(&mesh->config);
1994 if(!read_server_config(mesh)) {
1995 meshlink_errno = MESHLINK_ESTORAGE;
1996 } else if(!setup_network(mesh)) {
1997 meshlink_errno = MESHLINK_ENETWORK;
2003 pthread_mutex_unlock(&(mesh->mesh_mutex));
2008 void meshlink_set_invitation_timeout(meshlink_handle_t *mesh, int timeout) {
2009 mesh->invitation_timeout = timeout;
2012 char *meshlink_invite_ex(meshlink_handle_t *mesh, const char *name, uint32_t flags) {
2014 meshlink_errno = MESHLINK_EINVAL;
2018 pthread_mutex_lock(&(mesh->mesh_mutex));
2020 // Check validity of the new node's name
2021 if(!check_id(name)) {
2022 logger(mesh, MESHLINK_DEBUG, "Invalid name for node.\n");
2023 meshlink_errno = MESHLINK_EINVAL;
2024 pthread_mutex_unlock(&(mesh->mesh_mutex));
2028 // Ensure no host configuration file with that name exists
2029 char filename[PATH_MAX];
2030 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", mesh->confbase, name);
2032 if(!access(filename, F_OK)) {
2033 logger(mesh, MESHLINK_DEBUG, "A host config file for %s already exists!\n", name);
2034 meshlink_errno = MESHLINK_EEXIST;
2035 pthread_mutex_unlock(&(mesh->mesh_mutex));
2039 // Ensure no other nodes know about this name
2040 if(meshlink_get_node(mesh, name)) {
2041 logger(mesh, MESHLINK_DEBUG, "A node with name %s is already known!\n", name);
2042 meshlink_errno = MESHLINK_EEXIST;
2043 pthread_mutex_unlock(&(mesh->mesh_mutex));
2047 // Get the local address
2048 char *address = get_my_hostname(mesh, flags);
2051 logger(mesh, MESHLINK_DEBUG, "No Address known for ourselves!\n");
2052 meshlink_errno = MESHLINK_ERESOLV;
2053 pthread_mutex_unlock(&(mesh->mesh_mutex));
2057 if(!refresh_invitation_key(mesh)) {
2058 meshlink_errno = MESHLINK_EINTERNAL;
2059 pthread_mutex_unlock(&(mesh->mesh_mutex));
2065 // Create a hash of the key.
2066 char *fingerprint = ecdsa_get_base64_public_key(mesh->invitation_key);
2067 sha512(fingerprint, strlen(fingerprint), hash);
2068 b64encode_urlsafe(hash, hash, 18);
2070 // Create a random cookie for this invitation.
2072 randomize(cookie, 18);
2074 // Create a filename that doesn't reveal the cookie itself
2075 char buf[18 + strlen(fingerprint)];
2076 char cookiehash[64];
2077 memcpy(buf, cookie, 18);
2078 memcpy(buf + 18, fingerprint, sizeof(buf) - 18);
2079 sha512(buf, sizeof(buf), cookiehash);
2080 b64encode_urlsafe(cookiehash, cookiehash, 18);
2082 b64encode_urlsafe(cookie, cookie, 18);
2086 // Create a file containing the details of the invitation.
2087 snprintf(filename, sizeof(filename), "%s" SLASH "invitations" SLASH "%s", mesh->confbase, cookiehash);
2088 int ifd = open(filename, O_RDWR | O_CREAT | O_EXCL, 0600);
2091 logger(mesh, MESHLINK_DEBUG, "Could not create invitation file %s: %s\n", filename, strerror(errno));
2092 meshlink_errno = MESHLINK_ESTORAGE;
2093 pthread_mutex_unlock(&(mesh->mesh_mutex));
2097 FILE *f = fdopen(ifd, "w");
2103 // Fill in the details.
2104 fprintf(f, "Name = %s\n", name);
2105 fprintf(f, "ConnectTo = %s\n", mesh->self->name);
2107 // Copy Broadcast and Mode
2108 snprintf(filename, sizeof(filename), "%s" SLASH "meshlink.conf", mesh->confbase);
2109 FILE *tc = fopen(filename, "r");
2114 while(fgets(buf, sizeof(buf), tc)) {
2115 if((!strncasecmp(buf, "Mode", 4) && strchr(" \t=", buf[4]))
2116 || (!strncasecmp(buf, "Broadcast", 9) && strchr(" \t=", buf[9]))) {
2119 // Make sure there is a newline character.
2120 if(!strchr(buf, '\n')) {
2128 logger(mesh, MESHLINK_DEBUG, "Could not create %s: %s\n", filename, strerror(errno));
2129 meshlink_errno = MESHLINK_ESTORAGE;
2130 pthread_mutex_unlock(&(mesh->mesh_mutex));
2134 fprintf(f, "#---------------------------------------------------------------#\n");
2135 fprintf(f, "Name = %s\n", mesh->self->name);
2137 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", mesh->confbase, mesh->self->name);
2141 // Create an URL from the local address, key hash and cookie
2143 xasprintf(&url, "%s/%s%s", address, hash, cookie);
2146 pthread_mutex_unlock(&(mesh->mesh_mutex));
2150 char *meshlink_invite(meshlink_handle_t *mesh, const char *name) {
2151 return meshlink_invite_ex(mesh, name, 0);
2154 bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) {
2155 if(!mesh || !invitation) {
2156 meshlink_errno = MESHLINK_EINVAL;
2160 pthread_mutex_lock(&(mesh->mesh_mutex));
2162 //Before doing meshlink_join make sure we are not connected to another mesh
2163 if(mesh->threadstarted) {
2164 logger(mesh, MESHLINK_DEBUG, "Already connected to a mesh\n");
2165 meshlink_errno = MESHLINK_EINVAL;
2166 pthread_mutex_unlock(&(mesh->mesh_mutex));
2170 //TODO: think of a better name for this variable, or of a different way to tokenize the invitation URL.
2171 char copy[strlen(invitation) + 1];
2172 strcpy(copy, invitation);
2174 // Split the invitation URL into a list of hostname/port tuples, a key hash and a cookie.
2176 char *slash = strchr(copy, '/');
2184 if(strlen(slash) != 48) {
2188 char *address = copy;
2191 if(!b64decode(slash, mesh->hash, 18) || !b64decode(slash + 24, mesh->cookie, 18)) {
2195 // Generate a throw-away key for the invitation.
2196 ecdsa_t *key = ecdsa_generate();
2199 meshlink_errno = MESHLINK_EINTERNAL;
2200 pthread_mutex_unlock(&(mesh->mesh_mutex));
2204 char *b64key = ecdsa_get_base64_public_key(key);
2208 while(address && *address) {
2209 // We allow commas in the address part to support multiple addresses in one invitation URL.
2210 comma = strchr(address, ',');
2216 // Split of the port
2217 port = strrchr(address, ':');
2225 // IPv6 address are enclosed in brackets, per RFC 3986
2226 if(*address == '[') {
2228 char *bracket = strchr(address, ']');
2241 // Connect to the meshlink daemon mentioned in the URL.
2242 struct addrinfo *ai = str2addrinfo(address, port, SOCK_STREAM);
2245 for(struct addrinfo *aip = ai; aip; aip = aip->ai_next) {
2246 mesh->sock = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol);
2248 if(mesh->sock == -1) {
2249 logger(mesh, MESHLINK_DEBUG, "Could not open socket: %s\n", strerror(errno));
2250 meshlink_errno = MESHLINK_ENETWORK;
2254 set_timeout(mesh->sock, 5000);
2256 if(connect(mesh->sock, aip->ai_addr, aip->ai_addrlen)) {
2257 logger(mesh, MESHLINK_DEBUG, "Could not connect to %s port %s: %s\n", address, port, strerror(errno));
2258 meshlink_errno = MESHLINK_ENETWORK;
2259 closesocket(mesh->sock);
2267 meshlink_errno = MESHLINK_ERESOLV;
2270 if(mesh->sock != -1 || !comma) {
2277 if(mesh->sock == -1) {
2278 pthread_mutex_unlock(&mesh->mesh_mutex);
2282 logger(mesh, MESHLINK_DEBUG, "Connected to %s port %s...\n", address, port);
2284 // Tell him we have an invitation, and give him our throw-away key.
2288 if(!sendline(mesh->sock, "0 ?%s %d.%d %s", b64key, PROT_MAJOR, 1, mesh->appname)) {
2289 logger(mesh, MESHLINK_DEBUG, "Error sending request to %s port %s: %s\n", address, port, strerror(errno));
2290 closesocket(mesh->sock);
2291 meshlink_errno = MESHLINK_ENETWORK;
2292 pthread_mutex_unlock(&(mesh->mesh_mutex));
2298 char hisname[4096] = "";
2299 int code, hismajor, hisminor = 0;
2301 if(!recvline(mesh, sizeof(mesh)->line) || sscanf(mesh->line, "%d %s %d.%d", &code, hisname, &hismajor, &hisminor) < 3 || code != 0 || hismajor != PROT_MAJOR || !check_id(hisname) || !recvline(mesh, sizeof(mesh)->line) || !rstrip(mesh->line) || sscanf(mesh->line, "%d ", &code) != 1 || code != ACK || strlen(mesh->line) < 3) {
2302 logger(mesh, MESHLINK_DEBUG, "Cannot read greeting from peer\n");
2303 closesocket(mesh->sock);
2304 meshlink_errno = MESHLINK_ENETWORK;
2305 pthread_mutex_unlock(&(mesh->mesh_mutex));
2309 // Check if the hash of the key he gave us matches the hash in the URL.
2310 char *fingerprint = mesh->line + 2;
2313 if(sha512(fingerprint, strlen(fingerprint), hishash)) {
2314 logger(mesh, MESHLINK_DEBUG, "Could not create hash\n%s\n", mesh->line + 2);
2315 meshlink_errno = MESHLINK_EINTERNAL;
2316 pthread_mutex_unlock(&(mesh->mesh_mutex));
2320 if(memcmp(hishash, mesh->hash, 18)) {
2321 logger(mesh, MESHLINK_DEBUG, "Peer has an invalid key!\n%s\n", mesh->line + 2);
2322 meshlink_errno = MESHLINK_EPEER;
2323 pthread_mutex_unlock(&(mesh->mesh_mutex));
2328 ecdsa_t *hiskey = ecdsa_set_base64_public_key(fingerprint);
2331 meshlink_errno = MESHLINK_EINTERNAL;
2332 pthread_mutex_unlock(&(mesh->mesh_mutex));
2336 // Start an SPTPS session
2337 if(!sptps_start(&mesh->sptps, mesh, true, false, key, hiskey, meshlink_invitation_label, sizeof(meshlink_invitation_label), invitation_send, invitation_receive)) {
2338 meshlink_errno = MESHLINK_EINTERNAL;
2339 pthread_mutex_unlock(&(mesh->mesh_mutex));
2343 // Feed rest of input buffer to SPTPS
2344 if(!sptps_receive_data(&mesh->sptps, mesh->buffer, mesh->blen)) {
2345 meshlink_errno = MESHLINK_EPEER;
2346 pthread_mutex_unlock(&(mesh->mesh_mutex));
2352 while((len = recv(mesh->sock, mesh->line, sizeof(mesh)->line, 0))) {
2354 if(errno == EINTR) {
2358 logger(mesh, MESHLINK_DEBUG, "Error reading data from %s port %s: %s\n", address, port, strerror(errno));
2359 meshlink_errno = MESHLINK_ENETWORK;
2360 pthread_mutex_unlock(&(mesh->mesh_mutex));
2364 if(!sptps_receive_data(&mesh->sptps, mesh->line, len)) {
2365 meshlink_errno = MESHLINK_EPEER;
2366 pthread_mutex_unlock(&(mesh->mesh_mutex));
2371 sptps_stop(&mesh->sptps);
2374 closesocket(mesh->sock);
2376 if(!mesh->success) {
2377 logger(mesh, MESHLINK_DEBUG, "Connection closed by peer, invitation cancelled.\n");
2378 meshlink_errno = MESHLINK_EPEER;
2379 pthread_mutex_unlock(&(mesh->mesh_mutex));
2383 pthread_mutex_unlock(&(mesh->mesh_mutex));
2387 logger(mesh, MESHLINK_DEBUG, "Invalid invitation URL\n");
2388 meshlink_errno = MESHLINK_EINVAL;
2389 pthread_mutex_unlock(&(mesh->mesh_mutex));
2393 char *meshlink_export(meshlink_handle_t *mesh) {
2395 meshlink_errno = MESHLINK_EINVAL;
2399 pthread_mutex_lock(&(mesh->mesh_mutex));
2401 char filename[PATH_MAX];
2402 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", mesh->confbase, mesh->self->name);
2403 FILE *f = fopen(filename, "r");
2406 logger(mesh, MESHLINK_DEBUG, "Could not open %s: %s\n", filename, strerror(errno));
2407 meshlink_errno = MESHLINK_ESTORAGE;
2408 pthread_mutex_unlock(&(mesh->mesh_mutex));
2412 fseek(f, 0, SEEK_END);
2413 int fsize = ftell(f);
2416 size_t len = fsize + 9 + strlen(mesh->self->name);
2417 char *buf = xmalloc(len);
2418 snprintf(buf, len, "Name = %s\n", mesh->self->name);
2420 if(fread(buf + len - fsize - 1, fsize, 1, f) != 1) {
2421 logger(mesh, MESHLINK_DEBUG, "Error reading from %s: %s\n", filename, strerror(errno));
2424 meshlink_errno = MESHLINK_ESTORAGE;
2425 pthread_mutex_unlock(&(mesh->mesh_mutex));
2432 pthread_mutex_unlock(&(mesh->mesh_mutex));
2436 bool meshlink_import(meshlink_handle_t *mesh, const char *data) {
2437 if(!mesh || !data) {
2438 meshlink_errno = MESHLINK_EINVAL;
2442 pthread_mutex_lock(&(mesh->mesh_mutex));
2444 if(strncmp(data, "Name = ", 7)) {
2445 logger(mesh, MESHLINK_DEBUG, "Invalid data\n");
2446 meshlink_errno = MESHLINK_EPEER;
2447 pthread_mutex_unlock(&(mesh->mesh_mutex));
2451 char *end = strchr(data + 7, '\n');
2454 logger(mesh, MESHLINK_DEBUG, "Invalid data\n");
2455 meshlink_errno = MESHLINK_EPEER;
2456 pthread_mutex_unlock(&(mesh->mesh_mutex));
2460 int len = end - (data + 7);
2462 memcpy(name, data + 7, len);
2465 if(!check_id(name)) {
2466 logger(mesh, MESHLINK_DEBUG, "Invalid Name\n");
2467 meshlink_errno = MESHLINK_EPEER;
2468 pthread_mutex_unlock(&(mesh->mesh_mutex));
2472 char filename[PATH_MAX];
2473 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", mesh->confbase, name);
2475 if(!access(filename, F_OK)) {
2476 logger(mesh, MESHLINK_DEBUG, "File %s already exists, not importing\n", filename);
2477 meshlink_errno = MESHLINK_EEXIST;
2478 pthread_mutex_unlock(&(mesh->mesh_mutex));
2482 if(errno != ENOENT) {
2483 logger(mesh, MESHLINK_DEBUG, "Error accessing %s: %s\n", filename, strerror(errno));
2484 meshlink_errno = MESHLINK_ESTORAGE;
2485 pthread_mutex_unlock(&(mesh->mesh_mutex));
2489 FILE *f = fopen(filename, "w");
2492 logger(mesh, MESHLINK_DEBUG, "Could not create %s: %s\n", filename, strerror(errno));
2493 meshlink_errno = MESHLINK_ESTORAGE;
2494 pthread_mutex_unlock(&(mesh->mesh_mutex));
2498 fwrite(end + 1, strlen(end + 1), 1, f);
2501 load_all_nodes(mesh);
2503 pthread_mutex_unlock(&(mesh->mesh_mutex));
2507 void meshlink_blacklist(meshlink_handle_t *mesh, meshlink_node_t *node) {
2508 if(!mesh || !node) {
2509 meshlink_errno = MESHLINK_EINVAL;
2513 pthread_mutex_lock(&(mesh->mesh_mutex));
2517 n->status.blacklisted = true;
2518 logger(mesh, MESHLINK_DEBUG, "Blacklisted %s.\n", node->name);
2520 //Make blacklisting persistent in the config file
2521 append_config_file(mesh, n->name, "blacklisted", "yes");
2523 //Immediately terminate any connections we have with the blacklisted node
2524 for list_each(connection_t, c, mesh->connections) {
2526 terminate_connection(mesh, c, c->status.active);
2530 pthread_mutex_unlock(&(mesh->mesh_mutex));
2533 void meshlink_whitelist(meshlink_handle_t *mesh, meshlink_node_t *node) {
2534 if(!mesh || !node) {
2535 meshlink_errno = MESHLINK_EINVAL;
2539 pthread_mutex_lock(&(mesh->mesh_mutex));
2541 node_t *n = (node_t *)node;
2542 n->status.blacklisted = false;
2544 //TODO: remove blacklisted = yes from the config file
2546 pthread_mutex_unlock(&(mesh->mesh_mutex));
2550 void meshlink_set_default_blacklist(meshlink_handle_t *mesh, bool blacklist) {
2551 mesh->default_blacklist = blacklist;
2554 /* Hint that a hostname may be found at an address
2555 * See header file for detailed comment.
2557 void meshlink_hint_address(meshlink_handle_t *mesh, meshlink_node_t *node, const struct sockaddr *addr) {
2558 if(!mesh || !node || !addr) {
2562 // Ignore hints about ourself.
2563 if((node_t *)node == mesh->self) {
2567 pthread_mutex_lock(&(mesh->mesh_mutex));
2569 char *host = NULL, *port = NULL, *str = NULL;
2570 sockaddr2str((const sockaddr_t *)addr, &host, &port);
2573 xasprintf(&str, "%s %s", host, port);
2575 if((strncmp("fe80", host, 4) != 0) && (strncmp("127.", host, 4) != 0) && (strcmp("localhost", host) != 0)) {
2576 modify_config_file(mesh, node->name, "Address", str, 5);
2578 logger(mesh, MESHLINK_DEBUG, "Not adding Link Local IPv6 Address to config\n");
2586 pthread_mutex_unlock(&(mesh->mesh_mutex));
2587 // @TODO do we want to fire off a connection attempt right away?
2590 static bool channel_pre_accept(struct utcp *utcp, uint16_t port) {
2592 node_t *n = utcp->priv;
2593 meshlink_handle_t *mesh = n->mesh;
2594 return mesh->channel_accept_cb;
2597 static ssize_t channel_recv(struct utcp_connection *connection, const void *data, size_t len) {
2598 meshlink_channel_t *channel = connection->priv;
2604 node_t *n = channel->node;
2605 meshlink_handle_t *mesh = n->mesh;
2607 if(n->status.destroyed) {
2608 meshlink_channel_close(mesh, channel);
2609 } else if(channel->receive_cb) {
2610 channel->receive_cb(mesh, channel, data, len);
2616 static void channel_accept(struct utcp_connection *utcp_connection, uint16_t port) {
2617 node_t *n = utcp_connection->utcp->priv;
2623 meshlink_handle_t *mesh = n->mesh;
2625 if(!mesh->channel_accept_cb) {
2629 meshlink_channel_t *channel = xzalloc(sizeof(*channel));
2631 channel->c = utcp_connection;
2633 if(mesh->channel_accept_cb(mesh, channel, port, NULL, 0)) {
2634 utcp_accept(utcp_connection, channel_recv, channel);
2640 static ssize_t channel_send(struct utcp *utcp, const void *data, size_t len) {
2641 node_t *n = utcp->priv;
2643 if(n->status.destroyed) {
2647 meshlink_handle_t *mesh = n->mesh;
2648 return meshlink_send(mesh, (meshlink_node_t *)n, data, len) ? (ssize_t)len : -1;
2651 void meshlink_set_channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, meshlink_channel_receive_cb_t cb) {
2652 if(!mesh || !channel) {
2653 meshlink_errno = MESHLINK_EINVAL;
2657 channel->receive_cb = cb;
2660 static void channel_receive(meshlink_handle_t *mesh, meshlink_node_t *source, const void *data, size_t len) {
2662 node_t *n = (node_t *)source;
2668 utcp_recv(n->utcp, data, len);
2671 static void channel_poll(struct utcp_connection *connection, size_t len) {
2672 meshlink_channel_t *channel = connection->priv;
2678 node_t *n = channel->node;
2679 meshlink_handle_t *mesh = n->mesh;
2681 if(channel->poll_cb) {
2682 channel->poll_cb(mesh, channel, len);
2686 void meshlink_set_channel_poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, meshlink_channel_poll_cb_t cb) {
2688 channel->poll_cb = cb;
2689 utcp_set_poll_cb(channel->c, cb ? channel_poll : NULL);
2692 void meshlink_set_channel_accept_cb(meshlink_handle_t *mesh, meshlink_channel_accept_cb_t cb) {
2694 meshlink_errno = MESHLINK_EINVAL;
2698 pthread_mutex_lock(&mesh->mesh_mutex);
2699 mesh->channel_accept_cb = cb;
2700 mesh->receive_cb = channel_receive;
2702 for splay_each(node_t, n, mesh->nodes) {
2703 if(!n->utcp && n != mesh->self) {
2704 n->utcp = utcp_init(channel_accept, channel_pre_accept, channel_send, n);
2708 pthread_mutex_unlock(&mesh->mesh_mutex);
2711 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) {
2713 abort(); // TODO: handle non-NULL data
2716 if(!mesh || !node) {
2717 meshlink_errno = MESHLINK_EINVAL;
2721 node_t *n = (node_t *)node;
2724 n->utcp = utcp_init(channel_accept, channel_pre_accept, channel_send, n);
2725 mesh->receive_cb = channel_receive;
2728 meshlink_errno = errno == ENOMEM ? MESHLINK_ENOMEM : MESHLINK_EINTERNAL;
2733 meshlink_channel_t *channel = xzalloc(sizeof(*channel));
2735 channel->receive_cb = cb;
2736 channel->c = utcp_connect_ex(n->utcp, port, channel_recv, channel, flags);
2739 meshlink_errno = errno == ENOMEM ? MESHLINK_ENOMEM : MESHLINK_EINTERNAL;
2747 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) {
2748 return meshlink_channel_open_ex(mesh, node, port, cb, data, len, MESHLINK_CHANNEL_TCP);
2751 void meshlink_channel_shutdown(meshlink_handle_t *mesh, meshlink_channel_t *channel, int direction) {
2752 if(!mesh || !channel) {
2753 meshlink_errno = MESHLINK_EINVAL;
2757 utcp_shutdown(channel->c, direction);
2760 void meshlink_channel_close(meshlink_handle_t *mesh, meshlink_channel_t *channel) {
2761 if(!mesh || !channel) {
2762 meshlink_errno = MESHLINK_EINVAL;
2766 utcp_close(channel->c);
2770 ssize_t meshlink_channel_send(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *data, size_t len) {
2771 if(!mesh || !channel) {
2772 meshlink_errno = MESHLINK_EINVAL;
2781 meshlink_errno = MESHLINK_EINVAL;
2785 // TODO: more finegrained locking.
2786 // Ideally we want to put the data into the UTCP connection's send buffer.
2787 // Then, preferrably only if there is room in the receiver window,
2788 // kick the meshlink thread to go send packets.
2790 pthread_mutex_lock(&mesh->mesh_mutex);
2791 ssize_t retval = utcp_send(channel->c, data, len);
2792 pthread_mutex_unlock(&mesh->mesh_mutex);
2795 meshlink_errno = MESHLINK_ENETWORK;
2801 uint32_t meshlink_channel_get_flags(meshlink_handle_t *mesh, meshlink_channel_t *channel) {
2802 if(!mesh || !channel) {
2803 meshlink_errno = MESHLINK_EINVAL;
2807 return channel->c->flags;
2810 void update_node_status(meshlink_handle_t *mesh, node_t *n) {
2811 if(n->status.reachable && mesh->channel_accept_cb && !n->utcp) {
2812 n->utcp = utcp_init(channel_accept, channel_pre_accept, channel_send, n);
2815 if(mesh->node_status_cb) {
2816 mesh->node_status_cb(mesh, (meshlink_node_t *)n, n->status.reachable);
2820 void handle_duplicate_node(meshlink_handle_t *mesh, node_t *n) {
2821 if(!mesh->node_duplicate_cb || n->status.duplicate) {
2825 n->status.duplicate = true;
2826 mesh->node_duplicate_cb(mesh, (meshlink_node_t *)n);
2829 void meshlink_enable_discovery(meshlink_handle_t *mesh, bool enable) {
2833 meshlink_errno = MESHLINK_EINVAL;
2837 pthread_mutex_lock(&mesh->mesh_mutex);
2839 if(mesh->discovery == enable) {
2843 if(mesh->threadstarted) {
2845 discovery_start(mesh);
2847 discovery_stop(mesh);
2851 mesh->discovery = enable;
2854 pthread_mutex_unlock(&mesh->mesh_mutex);
2858 meshlink_errno = MESHLINK_ENOTSUP;
2862 static void __attribute__((constructor)) meshlink_init(void) {
2865 randomize(&seed, sizeof(seed));
2869 static void __attribute__((destructor)) meshlink_exit(void) {
2873 /// Device class traits
2874 dev_class_traits_t dev_class_traits[_DEV_CLASS_MAX + 1] = {
2875 { .min_connects = 3, .max_connects = 10000, .edge_weight = 1 }, // DEV_CLASS_BACKBONE
2876 { .min_connects = 3, .max_connects = 100, .edge_weight = 3 }, // DEV_CLASS_STATIONARY
2877 { .min_connects = 3, .max_connects = 3, .edge_weight = 6 }, // DEV_CLASS_PORTABLE
2878 { .min_connects = 1, .max_connects = 1, .edge_weight = 9 }, // DEV_CLASS_UNKNOWN