X-Git-Url: http://git.meshlink.io/?a=blobdiff_plain;f=src%2Fmeshlink.c;h=b69c1619e498beec8a288c3960e4abb9983ddcac;hb=f3014d25c8b6f8cc1cf8ab48cabf6fbc8e8311d0;hp=3941ff0fc8a32a762f0675c350cf92c62f98d114;hpb=7a9d30b60af9d5e71f1bfee4dd976d5807402a15;p=meshlink-tiny diff --git a/src/meshlink.c b/src/meshlink.c index 3941ff0..b69c161 100644 --- a/src/meshlink.c +++ b/src/meshlink.c @@ -20,7 +20,6 @@ #include "system.h" #include -#include "adns.h" #include "crypto.h" #include "ecdsagen.h" #include "logger.h" @@ -28,18 +27,14 @@ #include "net.h" #include "netutl.h" #include "node.h" -#include "submesh.h" #include "packmsg.h" #include "prf.h" #include "protocol.h" -#include "route.h" #include "sockaddr.h" #include "utils.h" #include "xalloc.h" #include "ed25519/sha512.h" -#include "discovery.h" #include "devtools.h" -#include "graph.h" #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 @@ -152,353 +147,6 @@ static int socket_in_netns(int domain, int type, int protocol, int netns) { } -// Find out what local address a socket would use if we connect to the given address. -// We do this using connect() on a UDP socket, so the kernel has to resolve the address -// of both endpoints, but this will actually not send any UDP packet. -static bool getlocaladdr(const char *destaddr, sockaddr_t *sa, socklen_t *salen, int netns) { - struct addrinfo *rai = NULL; - const struct addrinfo hint = { - .ai_family = AF_UNSPEC, - .ai_socktype = SOCK_DGRAM, - .ai_protocol = IPPROTO_UDP, - .ai_flags = AI_NUMERICHOST | AI_NUMERICSERV, - }; - - if(getaddrinfo(destaddr, "80", &hint, &rai) || !rai) { - return false; - } - - int sock = socket_in_netns(rai->ai_family, rai->ai_socktype, rai->ai_protocol, netns); - - if(sock == -1) { - freeaddrinfo(rai); - return false; - } - - if(connect(sock, rai->ai_addr, rai->ai_addrlen) && !sockwouldblock(errno)) { - closesocket(sock); - freeaddrinfo(rai); - return false; - } - - freeaddrinfo(rai); - - if(getsockname(sock, &sa->sa, salen)) { - closesocket(sock); - return false; - } - - closesocket(sock); - return true; -} - -static bool getlocaladdrname(const char *destaddr, char *host, socklen_t hostlen, int netns) { - sockaddr_t sa; - socklen_t salen = sizeof(sa); - - if(!getlocaladdr(destaddr, &sa, &salen, netns)) { - return false; - } - - if(getnameinfo(&sa.sa, salen, host, hostlen, NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV)) { - return false; - } - - return true; -} - -char *meshlink_get_external_address(meshlink_handle_t *mesh) { - return meshlink_get_external_address_for_family(mesh, AF_UNSPEC); -} - -char *meshlink_get_external_address_for_family(meshlink_handle_t *mesh, int family) { - logger(mesh, MESHLINK_DEBUG, "meshlink_get_external_address_for_family(%d)", family); - const char *url = mesh->external_address_url; - - if(!url) { - url = "http://meshlink.io/host.cgi"; - } - - /* Find the hostname part between the slashes */ - if(strncmp(url, "http://", 7)) { - abort(); - meshlink_errno = MESHLINK_EINTERNAL; - return NULL; - } - - const char *begin = url + 7; - - const char *end = strchr(begin, '/'); - - if(!end) { - end = begin + strlen(begin); - } - - /* Make a copy */ - char host[end - begin + 1]; - strncpy(host, begin, end - begin); - host[end - begin] = 0; - - char *port = strchr(host, ':'); - - if(port) { - *port++ = 0; - } - - logger(mesh, MESHLINK_DEBUG, "Trying to discover externally visible hostname...\n"); - struct addrinfo *ai = adns_blocking_request(mesh, xstrdup(host), xstrdup(port ? port : "80"), SOCK_STREAM, 5); - char line[256]; - char *hostname = NULL; - - for(struct addrinfo *aip = ai; aip; aip = aip->ai_next) { - if(family != AF_UNSPEC && aip->ai_family != family) { - continue; - } - - int s = socket_in_netns(aip->ai_family, aip->ai_socktype, aip->ai_protocol, mesh->netns); - -#ifdef SO_NOSIGPIPE - int nosigpipe = 1; - setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(nosigpipe)); -#endif - - if(s >= 0) { - set_timeout(s, 5000); - - if(connect(s, aip->ai_addr, aip->ai_addrlen)) { - closesocket(s); - s = -1; - } - } - - if(s >= 0) { - send(s, "GET ", 4, 0); - send(s, url, strlen(url), 0); - send(s, " HTTP/1.0\r\n\r\n", 13, 0); - int len = recv(s, line, sizeof(line) - 1, MSG_WAITALL); - - if(len > 0) { - line[len] = 0; - - if(line[len - 1] == '\n') { - line[--len] = 0; - } - - char *p = strrchr(line, '\n'); - - if(p && p[1]) { - hostname = xstrdup(p + 1); - } - } - - closesocket(s); - - if(hostname) { - break; - } - } - } - - if(ai) { - freeaddrinfo(ai); - } - - // Check that the hostname is reasonable - if(hostname && !is_valid_hostname(hostname)) { - free(hostname); - hostname = NULL; - } - - if(!hostname) { - meshlink_errno = MESHLINK_ERESOLV; - } - - return hostname; -} - -static bool is_localaddr(sockaddr_t *sa) { - switch(sa->sa.sa_family) { - case AF_INET: - return *(uint8_t *)(&sa->in.sin_addr.s_addr) == 127; - - case AF_INET6: { - uint16_t first = sa->in6.sin6_addr.s6_addr[0] << 8 | sa->in6.sin6_addr.s6_addr[1]; - return first == 0 || (first & 0xffc0) == 0xfe80; - } - - default: - return false; - } -} - -#ifdef HAVE_GETIFADDRS -struct getifaddrs_in_netns_params { - struct ifaddrs **ifa; - int netns; -}; - -#ifdef HAVE_SETNS -static void *getifaddrs_in_netns_thread(void *arg) { - struct getifaddrs_in_netns_params *params = arg; - - if(setns(params->netns, CLONE_NEWNET) == -1) { - meshlink_errno = MESHLINK_EINVAL; - return NULL; - } - - if(getifaddrs(params->ifa) != 0) { - *params->ifa = NULL; - } - - return NULL; -} -#endif // HAVE_SETNS - -static int getifaddrs_in_netns(struct ifaddrs **ifa, int netns) { - if(netns == -1) { - return getifaddrs(ifa); - } - -#ifdef HAVE_SETNS - struct getifaddrs_in_netns_params params = {ifa, netns}; - pthread_t thr; - - if(pthread_create(&thr, NULL, getifaddrs_in_netns_thread, ¶ms) == 0) { - if(pthread_join(thr, NULL) != 0) { - abort(); - } - } - - return *params.ifa ? 0 : -1; -#else - return -1; -#endif // HAVE_SETNS - -} -#endif - -char *meshlink_get_local_address_for_family(meshlink_handle_t *mesh, int family) { - logger(mesh, MESHLINK_DEBUG, "meshlink_get_local_address_for_family(%d)", family); - - if(!mesh) { - meshlink_errno = MESHLINK_EINVAL; - return NULL; - } - - // Determine address of the local interface used for outgoing connections. - char localaddr[NI_MAXHOST]; - bool success = false; - - if(family == AF_INET) { - success = getlocaladdrname("93.184.216.34", localaddr, sizeof(localaddr), mesh->netns); - } else if(family == AF_INET6) { - success = getlocaladdrname("2606:2800:220:1:248:1893:25c8:1946", localaddr, sizeof(localaddr), mesh->netns); - } - -#ifdef HAVE_GETIFADDRS - - if(!success) { - struct ifaddrs *ifa = NULL; - getifaddrs_in_netns(&ifa, mesh->netns); - - for(struct ifaddrs *ifap = ifa; ifap; ifap = ifap->ifa_next) { - sockaddr_t *sa = (sockaddr_t *)ifap->ifa_addr; - - if(!sa || sa->sa.sa_family != family) { - continue; - } - - if(is_localaddr(sa)) { - continue; - } - - if(!getnameinfo(&sa->sa, SALEN(sa->sa), localaddr, sizeof(localaddr), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV)) { - success = true; - break; - } - } - - freeifaddrs(ifa); - } - -#endif - - if(!success) { - meshlink_errno = MESHLINK_ENETWORK; - return NULL; - } - - return xstrdup(localaddr); -} - -static bool try_bind(meshlink_handle_t *mesh, int port) { - struct addrinfo *ai = NULL; - struct addrinfo hint = { - .ai_flags = AI_PASSIVE, - .ai_family = AF_UNSPEC, - .ai_socktype = SOCK_STREAM, - .ai_protocol = IPPROTO_TCP, - }; - - char portstr[16]; - snprintf(portstr, sizeof(portstr), "%d", port); - - if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai) { - return false; - } - - bool success = false; - - for(struct addrinfo *aip = ai; aip; aip = aip->ai_next) { - /* Try to bind to TCP. */ - - int tcp_fd = setup_tcp_listen_socket(mesh, aip); - - if(tcp_fd == -1) { - if(errno == EADDRINUSE) { - /* If this port is in use for any address family, avoid it. */ - success = false; - break; - } else { - continue; - } - } - - /* If TCP worked, then we require that UDP works as well. */ - - int udp_fd = setup_udp_listen_socket(mesh, aip); - - if(udp_fd == -1) { - closesocket(tcp_fd); - success = false; - break; - } - - closesocket(tcp_fd); - closesocket(udp_fd); - success = true; - } - - freeaddrinfo(ai); - return success; -} - -int check_port(meshlink_handle_t *mesh) { - for(int i = 0; i < 1000; i++) { - int port = 0x1000 + prng(mesh, 0x8000); - - if(try_bind(mesh, port)) { - free(mesh->myport); - xasprintf(&mesh->myport, "%d", port); - return port; - } - } - - meshlink_errno = MESHLINK_ENETWORK; - logger(mesh, MESHLINK_DEBUG, "Could not find any available network port.\n"); - return 0; -} - static bool write_main_config_files(meshlink_handle_t *mesh) { if(!mesh->confbase) { return true; @@ -558,28 +206,19 @@ static bool finalize_join(join_state_t *state, const void *buf, uint16_t len) { } char *name = packmsg_get_str_dup(&in); - char *submesh_name = packmsg_get_str_dup(&in); + packmsg_skip_element(&in); // submesh_name dev_class_t devclass = packmsg_get_int32(&in); uint32_t count = packmsg_get_array(&in); if(!name || !check_id(name)) { logger(mesh, MESHLINK_DEBUG, "No valid Name found in invitation!\n"); free(name); - free(submesh_name); - return false; - } - - if(!submesh_name || (strcmp(submesh_name, CORE_MESH) && !check_id(submesh_name))) { - logger(mesh, MESHLINK_DEBUG, "No valid Submesh found in invitation!\n"); - free(name); - free(submesh_name); return false; } if(!count) { logger(mesh, MESHLINK_ERROR, "Incomplete invitation file!\n"); free(name); - free(submesh_name); return false; } @@ -587,8 +226,6 @@ static bool finalize_join(join_state_t *state, const void *buf, uint16_t len) { free(mesh->self->name); mesh->name = name; mesh->self->name = xstrdup(name); - mesh->self->submesh = strcmp(submesh_name, CORE_MESH) ? lookup_or_create_submesh(mesh, submesh_name) : NULL; - free(submesh_name); mesh->self->devclass = devclass == DEV_CLASS_UNKNOWN ? mesh->devclass : devclass; // Initialize configuration directory @@ -656,10 +293,6 @@ static bool finalize_join(join_state_t *state, const void *buf, uint16_t len) { } } - /* Clear the reachability times, since we ourself have never seen these nodes yet */ - n->last_reachable = 0; - n->last_unreachable = 0; - if(!node_write_config(mesh, n, true)) { free_node(n); return false; @@ -854,55 +487,13 @@ static bool ecdsa_keygen(meshlink_handle_t *mesh) { return true; } -static bool timespec_lt(const struct timespec *a, const struct timespec *b) { - if(a->tv_sec == b->tv_sec) { - return a->tv_nsec < b->tv_nsec; - } else { - return a->tv_sec < b->tv_sec; - } -} - static struct timespec idle(event_loop_t *loop, void *data) { (void)loop; - meshlink_handle_t *mesh = data; - struct timespec t, tmin = {3600, 0}; - - for splay_each(node_t, n, mesh->nodes) { - if(!n->utcp) { - continue; - } - - t = utcp_timeout(n->utcp); - - if(timespec_lt(&t, &tmin)) { - tmin = t; - } - } - - return tmin; -} - -// Get our local address(es) by simulating connecting to an Internet host. -static void add_local_addresses(meshlink_handle_t *mesh) { - sockaddr_t sa; - sa.storage.ss_family = AF_UNKNOWN; - socklen_t salen = sizeof(sa); + (void)data; - // IPv4 example.org - - if(getlocaladdr("93.184.216.34", &sa, &salen, mesh->netns)) { - sa.in.sin_port = ntohs(atoi(mesh->myport)); - node_add_recent_address(mesh, mesh->self, &sa); - } - - // IPv6 example.org - - salen = sizeof(sa); - - if(getlocaladdr("2606:2800:220:1:248:1893:25c8:1946", &sa, &salen, mesh->netns)) { - sa.in6.sin6_port = ntohs(atoi(mesh->myport)); - node_add_recent_address(mesh, mesh->self, &sa); - } + return (struct timespec) { + 3600, 0 + }; } static bool meshlink_setup(meshlink_handle_t *mesh) { @@ -929,10 +520,7 @@ static bool meshlink_setup(meshlink_handle_t *mesh) { return false; } - if(check_port(mesh) == 0) { - meshlink_errno = MESHLINK_ENETWORK; - return false; - } + mesh->myport = xstrdup("0"); /* Create a node for ourself */ @@ -1369,9 +957,7 @@ meshlink_handle_t *meshlink_open_ex(const meshlink_open_params_t *params) { mesh->appname = xstrdup(params->appname); mesh->devclass = params->devclass; - mesh->discovery.enabled = true; mesh->netns = params->netns; - mesh->submeshes = NULL; mesh->log_cb = global_log_cb; mesh->log_level = global_log_level; mesh->packet = xmalloc(sizeof(vpn_packet_t)); @@ -1409,8 +995,6 @@ meshlink_handle_t *meshlink_open_ex(const meshlink_open_params_t *params) { pthread_mutex_init(&mesh->mutex, &attr); pthread_cond_init(&mesh->cond, NULL); - pthread_cond_init(&mesh->adns_cond, NULL); - mesh->threadstarted = false; event_loop_init(&mesh->loop); mesh->loop.data = mesh; @@ -1486,8 +1070,6 @@ meshlink_handle_t *meshlink_open_ex(const meshlink_open_params_t *params) { return NULL; } - add_local_addresses(mesh); - if(!node_write_config(mesh, mesh->self, new_configuration)) { logger(NULL, MESHLINK_ERROR, "Cannot update configuration\n"); return NULL; @@ -1499,35 +1081,6 @@ meshlink_handle_t *meshlink_open_ex(const meshlink_open_params_t *params) { return mesh; } -meshlink_submesh_t *meshlink_submesh_open(meshlink_handle_t *mesh, const char *submesh) { - logger(NULL, MESHLINK_DEBUG, "meshlink_submesh_open(%s)", submesh); - - meshlink_submesh_t *s = NULL; - - if(!mesh) { - logger(NULL, MESHLINK_ERROR, "No mesh handle given!\n"); - meshlink_errno = MESHLINK_EINVAL; - return NULL; - } - - if(!submesh || !*submesh) { - logger(NULL, MESHLINK_ERROR, "No submesh name given!\n"); - meshlink_errno = MESHLINK_EINVAL; - return NULL; - } - - //lock mesh->nodes - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - s = (meshlink_submesh_t *)create_submesh(mesh, submesh); - - pthread_mutex_unlock(&mesh->mutex); - - return s; -} - static void *meshlink_main_loop(void *arg) { meshlink_handle_t *mesh = arg; @@ -1545,10 +1098,6 @@ static void *meshlink_main_loop(void *arg) { #endif // HAVE_SETNS } - if(mesh->discovery.enabled) { - discovery_start(mesh); - } - if(pthread_mutex_lock(&mesh->mutex) != 0) { abort(); } @@ -1560,11 +1109,6 @@ static void *meshlink_main_loop(void *arg) { pthread_mutex_unlock(&mesh->mutex); - // Stop discovery - if(mesh->discovery.enabled) { - discovery_stop(mesh); - } - return NULL; } @@ -1591,19 +1135,11 @@ bool meshlink_start(meshlink_handle_t *mesh) { 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; - } - // Reset node connection timers - for splay_each(node_t, n, mesh->nodes) { - n->last_connect_try = 0; + if(mesh->peer) { + mesh->peer->last_connect_try = 0; } - // TODO: open listening sockets first - //Check that a valid name is set if(!mesh->name) { logger(mesh, MESHLINK_ERROR, "No name given!\n"); @@ -1613,7 +1149,6 @@ bool meshlink_start(meshlink_handle_t *mesh) { } init_outgoings(mesh); - init_adns(mesh); // Start the main thread @@ -1636,9 +1171,6 @@ bool meshlink_start(meshlink_handle_t *mesh) { pthread_cond_wait(&mesh->cond, &mesh->mutex); mesh->threadstarted = true; - // Ensure we are considered reachable - graph(mesh); - pthread_mutex_unlock(&mesh->mutex); return true; } @@ -1658,20 +1190,7 @@ void meshlink_stop(meshlink_handle_t *mesh) { // 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); - - 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)); - } - } + // TODO: send something to a local socket to kick the event loop if(mesh->threadstarted) { // Wait for the main thread to finish @@ -1689,31 +1208,17 @@ void meshlink_stop(meshlink_handle_t *mesh) { } // 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); - } + if(mesh->connection) { + mesh->connection->outgoing = NULL; + terminate_connection(mesh, mesh->connection, false); } - exit_adns(mesh); exit_outgoings(mesh); - // Ensure we are considered unreachable - if(mesh->nodes) { - graph(mesh); - } - // Try to write out any changed node config files, ignore errors at this point. - if(mesh->nodes) { - for splay_each(node_t, n, mesh->nodes) { - if(n->status.dirty) { - if(!node_write_config(mesh, n, false)) { - // ignore - } - } + if(mesh->peer && mesh->peer->status.dirty) { + if(!node_write_config(mesh, mesh->peer, false)) { + // ignore } } @@ -1909,22 +1414,6 @@ void meshlink_set_node_status_cb(meshlink_handle_t *mesh, meshlink_node_status_c pthread_mutex_unlock(&mesh->mutex); } -void meshlink_set_node_pmtu_cb(meshlink_handle_t *mesh, meshlink_node_pmtu_cb_t cb) { - logger(mesh, MESHLINK_DEBUG, "meshlink_set_node_pmtu_cb(%p)", (void *)(intptr_t)cb); - - if(!mesh) { - meshlink_errno = MESHLINK_EINVAL; - return; - } - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - mesh->node_pmtu_cb = cb; - pthread_mutex_unlock(&mesh->mutex); -} - void meshlink_set_node_duplicate_cb(meshlink_handle_t *mesh, meshlink_node_duplicate_cb_t cb) { logger(mesh, MESHLINK_DEBUG, "meshlink_set_node_duplicate_cb(%p)", (void *)(intptr_t)cb); @@ -1974,72 +1463,6 @@ void meshlink_set_error_cb(struct meshlink_handle *mesh, meshlink_error_cb_t cb) pthread_mutex_unlock(&mesh->mutex); } -void meshlink_set_blacklisted_cb(struct meshlink_handle *mesh, meshlink_blacklisted_cb_t cb) { - logger(mesh, MESHLINK_DEBUG, "meshlink_set_blacklisted_cb(%p)", (void *)(intptr_t)cb); - - if(!mesh) { - meshlink_errno = MESHLINK_EINVAL; - return; - } - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - mesh->blacklisted_cb = cb; - pthread_mutex_unlock(&mesh->mutex); -} - -static bool prepare_packet(meshlink_handle_t *mesh, meshlink_node_t *destination, const void *data, size_t len, vpn_packet_t *packet) { - meshlink_packethdr_t *hdr; - - if(len > MAXSIZE - sizeof(*hdr)) { - 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); - meshlink_errno = MESHLINK_EBLACKLISTED; - return false; - } - - // Prepare the packet - 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); - - return true; -} - -static bool meshlink_send_immediate(meshlink_handle_t *mesh, meshlink_node_t *destination, const void *data, size_t len) { - assert(mesh); - assert(destination); - assert(data); - assert(len); - - // Prepare the packet - if(!prepare_packet(mesh, destination, data, len, mesh->packet)) { - return false; - } - - // Send it immediately - route(mesh, mesh->self, mesh->packet); - - return true; -} - bool meshlink_send(meshlink_handle_t *mesh, meshlink_node_t *destination, const void *data, size_t len) { logger(mesh, MESHLINK_DEBUG, "meshlink_send(%s, %p, %zu)", destination ? destination->name : "(null)", data, len); @@ -2053,7 +1476,7 @@ bool meshlink_send(meshlink_handle_t *mesh, meshlink_node_t *destination, const return true; } - if(!data) { + if(!data || len > MTU) { meshlink_errno = MESHLINK_EINVAL; return false; } @@ -2066,10 +1489,8 @@ bool meshlink_send(meshlink_handle_t *mesh, meshlink_node_t *destination, const return false; } - if(!prepare_packet(mesh, destination, data, len, packet)) { - free(packet); - return false; - } + packet->len = len; + memcpy(packet->data, data, len); // Queue it if(!meshlink_queue_push(&mesh->outpacketqueue, packet)) { @@ -2094,38 +1515,11 @@ void meshlink_send_from_queue(event_loop_t *loop, void *data) { for(vpn_packet_t *packet; (packet = meshlink_queue_pop(&mesh->outpacketqueue));) { logger(mesh, MESHLINK_DEBUG, "Removing packet of %d bytes from packet queue", packet->len); - mesh->self->in_packets++; - mesh->self->in_bytes += packet->len; - route(mesh, mesh->self, packet); + send_raw_packet(mesh, mesh->peer->connection, packet); free(packet); } } -ssize_t meshlink_get_pmtu(meshlink_handle_t *mesh, meshlink_node_t *destination) { - if(!mesh || !destination) { - meshlink_errno = MESHLINK_EINVAL; - return -1; - } - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - node_t *n = (node_t *)destination; - - if(!n->status.reachable) { - pthread_mutex_unlock(&mesh->mutex); - return 0; - - } else if(n->mtuprobes > 30 && n->minmtu) { - pthread_mutex_unlock(&mesh->mutex); - return n->minmtu; - } else { - pthread_mutex_unlock(&mesh->mutex); - return MTU; - } -} - char *meshlink_get_fingerprint(meshlink_handle_t *mesh, meshlink_node_t *node) { if(!mesh || !node) { meshlink_errno = MESHLINK_EINVAL; @@ -2185,272 +1579,6 @@ meshlink_node_t *meshlink_get_node(meshlink_handle_t *mesh, const char *name) { return (meshlink_node_t *)n; } -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; - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - submesh = (meshlink_submesh_t *)lookup_submesh(mesh, name); - pthread_mutex_unlock(&mesh->mutex); - - if(!submesh) { - meshlink_errno = MESHLINK_ENOENT; - } - - 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 - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - *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->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; - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - *nmemb = 0; - - for splay_each(node_t, n, mesh->nodes) { - if(search_node(n, condition)) { - ++*nmemb; - } - } - - if(*nmemb == 0) { - free(nodes); - pthread_mutex_unlock(&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(search_node(n, condition)) { - *p++ = (meshlink_node_t *)n; - } - } - } else { - *nmemb = 0; - free(nodes); - meshlink_errno = MESHLINK_ENOMEM; - } - - pthread_mutex_unlock(&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_blacklisted(const node_t *node, const void *condition) { - return *(bool *)condition == node->status.blacklisted; -} - -static bool search_node_by_submesh(const node_t *node, const void *condition) { - if(condition == node->submesh) { - return true; - } - - return false; -} - -struct time_range { - time_t start; - time_t end; -}; - -static bool search_node_by_last_reachable(const node_t *node, const void *condition) { - const struct time_range *range = condition; - time_t start = node->last_reachable; - time_t end = node->last_unreachable; - - if(end < start) { - end = time(NULL); - - if(end < start) { - start = end; - } - } - - if(range->end >= range->start) { - return start <= range->end && end >= range->start; - } else { - return start > range->start || end < range->end; - } -} - -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 || devclass < 0 || devclass >= DEV_CLASS_COUNT || !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); -} - -meshlink_node_t **meshlink_get_all_nodes_by_last_reachable(meshlink_handle_t *mesh, time_t start, time_t end, meshlink_node_t **nodes, size_t *nmemb) { - if(!mesh || !nmemb) { - meshlink_errno = MESHLINK_EINVAL; - return NULL; - } - - struct time_range range = {start, end}; - - return meshlink_get_all_nodes_by_condition(mesh, &range, nodes, nmemb, search_node_by_last_reachable); -} - -meshlink_node_t **meshlink_get_all_nodes_by_blacklisted(meshlink_handle_t *mesh, bool blacklisted, meshlink_node_t **nodes, size_t *nmemb) { - if(!mesh || !nmemb) { - meshlink_errno = MESHLINK_EINVAL; - return NULL; - } - - return meshlink_get_all_nodes_by_condition(mesh, &blacklisted, nodes, nmemb, search_node_by_blacklisted); -} - -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; - } - - dev_class_t devclass; - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - devclass = ((node_t *)node)->devclass; - - pthread_mutex_unlock(&mesh->mutex); - - return devclass; -} - -bool meshlink_get_node_blacklisted(meshlink_handle_t *mesh, meshlink_node_t *node) { - if(!mesh) { - meshlink_errno = MESHLINK_EINVAL; - } - - if(!node) { - return mesh->default_blacklist; - } - - bool blacklisted; - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - blacklisted = ((node_t *)node)->status.blacklisted; - - pthread_mutex_unlock(&mesh->mutex); - - return blacklisted; -} - -meshlink_submesh_t *meshlink_get_node_submesh(meshlink_handle_t *mesh, meshlink_node_t *node) { - if(!mesh || !node) { - meshlink_errno = MESHLINK_EINVAL; - return NULL; - } - - node_t *n = (node_t *)node; - - meshlink_submesh_t *s; - - s = (meshlink_submesh_t *)n->submesh; - - return s; -} - -bool meshlink_get_node_reachability(struct meshlink_handle *mesh, struct meshlink_node *node, time_t *last_reachable, time_t *last_unreachable) { - if(!mesh || !node) { - meshlink_errno = MESHLINK_EINVAL; - return NULL; - } - - node_t *n = (node_t *)node; - bool reachable; - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - reachable = n->status.reachable && !n->status.blacklisted; - - if(last_reachable) { - *last_reachable = n->last_reachable; - } - - if(last_unreachable) { - *last_unreachable = n->last_unreachable; - } - - pthread_mutex_unlock(&mesh->mutex); - - return reachable; -} - bool meshlink_sign(meshlink_handle_t *mesh, const void *data, size_t len, void *signature, size_t *siglen) { logger(mesh, MESHLINK_DEBUG, "meshlink_sign(%p, %zu, %p, %p)", data, len, signature, (void *)siglen); @@ -2586,126 +1714,8 @@ bool meshlink_clear_canonical_address(meshlink_handle_t *mesh, meshlink_node_t * return config_sync(mesh, "current"); } -bool meshlink_add_address(meshlink_handle_t *mesh, const char *address) { - logger(mesh, MESHLINK_DEBUG, "meshlink_add_address(%s)", address ? address : "(null)"); - - return meshlink_set_canonical_address(mesh, (meshlink_node_t *)mesh->self, address, NULL); -} - -bool meshlink_add_external_address(meshlink_handle_t *mesh) { - logger(mesh, MESHLINK_DEBUG, "meshlink_add_external_address()"); - - if(!mesh) { - meshlink_errno = MESHLINK_EINVAL; - return false; - } - - char *address = meshlink_get_external_address(mesh); - - if(!address) { - return false; - } - - bool rval = meshlink_set_canonical_address(mesh, (meshlink_node_t *)mesh->self, address, NULL); - 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; - } - - int port; - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - port = atoi(mesh->myport); - pthread_mutex_unlock(&mesh->mutex); - - return port; -} - -bool meshlink_set_port(meshlink_handle_t *mesh, int port) { - logger(mesh, MESHLINK_DEBUG, "meshlink_set_port(%d)", 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(mesh, port)) { - meshlink_errno = MESHLINK_ENETWORK; - return false; - } - - devtool_trybind_probe(); - - bool rval = false; - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - if(mesh->threadstarted) { - meshlink_errno = MESHLINK_EINVAL; - goto done; - } - - free(mesh->myport); - xasprintf(&mesh->myport, "%d", port); - - /* Close down the network. This also deletes mesh->self. */ - close_network_connections(mesh); - - /* Recreate mesh->self. */ - mesh->self = new_node(); - mesh->self->name = xstrdup(mesh->name); - mesh->self->devclass = mesh->devclass; - mesh->self->session_id = mesh->session_id; - xasprintf(&mesh->myport, "%d", port); - - if(!node_read_public_key(mesh, mesh->self)) { - logger(NULL, MESHLINK_ERROR, "Could not read our host configuration file!"); - meshlink_errno = MESHLINK_ESTORAGE; - free_node(mesh->self); - mesh->self = NULL; - goto done; - } else if(!setup_network(mesh)) { - meshlink_errno = MESHLINK_ENETWORK; - goto done; - } - - /* Rebuild our own list of recent addresses */ - memset(mesh->self->recent, 0, sizeof(mesh->self->recent)); - add_local_addresses(mesh); - - /* Write meshlink.conf with the updated port number */ - write_main_config_files(mesh); - - rval = config_sync(mesh, "current"); - -done: - pthread_mutex_unlock(&mesh->mutex); - - return rval && meshlink_get_port(mesh) == port; -} - -bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) { - logger(mesh, MESHLINK_DEBUG, "meshlink_join(%s)", invitation ? invitation : "(null)"); +bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) { + logger(mesh, MESHLINK_DEBUG, "meshlink_join(%s)", invitation ? invitation : "(null)"); if(!mesh || !invitation) { meshlink_errno = MESHLINK_EINVAL; @@ -2740,7 +1750,7 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) { } // Refuse to join a mesh if we are already part of one. We are part of one if we know at least one other node. - if(mesh->nodes->count > 1) { + if(mesh->peer) { logger(mesh, MESHLINK_ERROR, "Already part of an existing mesh\n"); meshlink_errno = MESHLINK_EINVAL; goto exit; @@ -2818,7 +1828,7 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) { } // Connect to the meshlink daemon mentioned in the URL. - struct addrinfo *ai = adns_blocking_request(mesh, xstrdup(address), xstrdup(port), SOCK_STREAM, 30); + struct addrinfo *ai = str2addrinfo(address, port, SOCK_STREAM); if(ai) { for(struct addrinfo *aip = ai; aip; aip = aip->ai_next) { @@ -3126,10 +2136,6 @@ bool meshlink_import(meshlink_handle_t *mesh, const char *data) { break; } - /* Clear the reachability times, since we ourself have never seen these nodes yet */ - n->last_reachable = 0; - n->last_unreachable = 0; - if(!node_write_config(mesh, n, true)) { free_node(n); free(buf); @@ -3156,256 +2162,147 @@ bool meshlink_import(meshlink_handle_t *mesh, const char *data) { return true; } -static bool blacklist(meshlink_handle_t *mesh, node_t *n) { - if(n == mesh->self) { - logger(mesh, MESHLINK_ERROR, "%s blacklisting itself?\n", n->name); - meshlink_errno = MESHLINK_EINVAL; - return false; - } +/* Hint that a hostname may be found at an address + * See header file for detailed comment. + */ +void meshlink_hint_address(meshlink_handle_t *mesh, meshlink_node_t *node, const struct sockaddr *addr) { + logger(mesh, MESHLINK_DEBUG, "meshlink_hint_address(%s, %p)", node ? node->name : "(null)", (const void *)addr); - if(n->status.blacklisted) { - logger(mesh, MESHLINK_DEBUG, "Node %s already blacklisted\n", n->name); - return true; + if(!mesh || !node || !addr) { + meshlink_errno = EINVAL; + return; } - n->status.blacklisted = true; + if(pthread_mutex_lock(&mesh->mutex) != 0) { + abort(); + } - /* Immediately shut down any connections we have with the blacklisted node. - * We can't call terminate_connection(), because we might be called from a callback function. - */ - for list_each(connection_t, c, mesh->connections) { - if(c->node == n) { - if(c->status.active) { - send_error(mesh, c, BLACKLISTED, "blacklisted"); - } + node_t *n = (node_t *)node; - shutdown(c->socket, SHUT_RDWR); + if(node_add_recent_address(mesh, n, (sockaddr_t *)addr)) { + if(!node_write_config(mesh, n, false)) { + logger(mesh, MESHLINK_DEBUG, "Could not update %s\n", n->name); } } - utcp_reset_all_connections(n->utcp); - - n->mtu = 0; - n->minmtu = 0; - n->maxmtu = MTU; - n->mtuprobes = 0; - n->status.udp_confirmed = false; + pthread_mutex_unlock(&mesh->mutex); + // @TODO do we want to fire off a connection attempt right away? +} - if(n->status.reachable) { - n->last_unreachable = time(NULL); +void update_node_status(meshlink_handle_t *mesh, node_t *n) { + if(mesh->node_status_cb) { + mesh->node_status_cb(mesh, (meshlink_node_t *)n, n->status.reachable && !n->status.blacklisted); } +} - /* Graph updates will suppress status updates for blacklisted nodes, so we need to - * manually call the status callback if necessary. - */ - if(n->status.reachable && mesh->node_status_cb) { - mesh->node_status_cb(mesh, (meshlink_node_t *)n, false); +void handle_duplicate_node(meshlink_handle_t *mesh, node_t *n) { + if(!mesh->node_duplicate_cb || n->status.duplicate) { + return; } - return node_write_config(mesh, n, true) && config_sync(mesh, "current"); + n->status.duplicate = true; + mesh->node_duplicate_cb(mesh, (meshlink_node_t *)n); } -bool meshlink_blacklist(meshlink_handle_t *mesh, meshlink_node_t *node) { - logger(mesh, MESHLINK_DEBUG, "meshlink_blacklist(%s)", node ? node->name : "(null)"); +void meshlink_hint_network_change(struct meshlink_handle *mesh) { + logger(mesh, MESHLINK_DEBUG, "meshlink_hint_network_change()"); - if(!mesh || !node) { + if(!mesh) { meshlink_errno = MESHLINK_EINVAL; - return false; + return; } if(pthread_mutex_lock(&mesh->mutex) != 0) { abort(); } - if(!blacklist(mesh, (node_t *)node)) { - pthread_mutex_unlock(&mesh->mutex); - return false; - } - pthread_mutex_unlock(&mesh->mutex); - - logger(mesh, MESHLINK_DEBUG, "Blacklisted %s.\n", node->name); - return true; } -bool meshlink_blacklist_by_name(meshlink_handle_t *mesh, const char *name) { - logger(mesh, MESHLINK_DEBUG, "meshlink_blacklist_by_name(%s)", name ? name : "(null)"); - - if(!mesh || !name) { - meshlink_errno = MESHLINK_EINVAL; - return false; - } +void meshlink_set_dev_class_timeouts(meshlink_handle_t *mesh, dev_class_t devclass, int pinginterval, int pingtimeout) { + logger(mesh, MESHLINK_DEBUG, "meshlink_set_dev_class_timeouts(%d, %d, %d)", devclass, pinginterval, pingtimeout); - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); + if(!mesh || devclass < 0 || devclass >= DEV_CLASS_COUNT) { + meshlink_errno = EINVAL; + return; } - node_t *n = lookup_node(mesh, (char *)name); - - if(!n) { - n = new_node(); - n->name = xstrdup(name); - node_add(mesh, n); + if(pinginterval < 1 || pingtimeout < 1 || pingtimeout > pinginterval) { + meshlink_errno = EINVAL; + return; } - if(!blacklist(mesh, (node_t *)n)) { - pthread_mutex_unlock(&mesh->mutex); - return false; + if(pthread_mutex_lock(&mesh->mutex) != 0) { + abort(); } + mesh->dev_class_traits[devclass].pinginterval = pinginterval; + mesh->dev_class_traits[devclass].pingtimeout = pingtimeout; pthread_mutex_unlock(&mesh->mutex); - - logger(mesh, MESHLINK_DEBUG, "Blacklisted %s.\n", name); - return true; } -static bool whitelist(meshlink_handle_t *mesh, node_t *n) { - if(n == mesh->self) { - logger(mesh, MESHLINK_ERROR, "%s whitelisting itself?\n", n->name); - meshlink_errno = MESHLINK_EINVAL; - return false; - } - - if(!n->status.blacklisted) { - logger(mesh, MESHLINK_DEBUG, "Node %s was already whitelisted\n", n->name); - return true; - } - - n->status.blacklisted = false; +void meshlink_set_dev_class_fast_retry_period(meshlink_handle_t *mesh, dev_class_t devclass, int fast_retry_period) { + logger(mesh, MESHLINK_DEBUG, "meshlink_set_dev_class_fast_retry_period(%d, %d)", devclass, fast_retry_period); - if(n->status.reachable) { - n->last_reachable = time(NULL); - update_node_status(mesh, n); + if(!mesh || devclass < 0 || devclass >= DEV_CLASS_COUNT) { + meshlink_errno = EINVAL; + return; } - return node_write_config(mesh, n, true) && config_sync(mesh, "current"); -} - -bool meshlink_whitelist(meshlink_handle_t *mesh, meshlink_node_t *node) { - logger(mesh, MESHLINK_DEBUG, "meshlink_whitelist(%s)", node ? node->name : "(null)"); - - if(!mesh || !node) { - meshlink_errno = MESHLINK_EINVAL; - return false; + if(fast_retry_period < 0) { + meshlink_errno = EINVAL; + return; } if(pthread_mutex_lock(&mesh->mutex) != 0) { abort(); } - if(!whitelist(mesh, (node_t *)node)) { - pthread_mutex_unlock(&mesh->mutex); - return false; - } - + mesh->dev_class_traits[devclass].fast_retry_period = fast_retry_period; pthread_mutex_unlock(&mesh->mutex); - - logger(mesh, MESHLINK_DEBUG, "Whitelisted %s.\n", node->name); - return true; } -bool meshlink_whitelist_by_name(meshlink_handle_t *mesh, const char *name) { - logger(mesh, MESHLINK_DEBUG, "meshlink_whitelist_by_name(%s)", name ? name : "(null)"); - - if(!mesh || !name) { - meshlink_errno = MESHLINK_EINVAL; - return false; - } +void meshlink_set_dev_class_maxtimeout(struct meshlink_handle *mesh, dev_class_t devclass, int maxtimeout) { + logger(mesh, MESHLINK_DEBUG, "meshlink_set_dev_class_fast_maxtimeout(%d, %d)", devclass, maxtimeout); - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); + if(!mesh || devclass < 0 || devclass >= DEV_CLASS_COUNT) { + meshlink_errno = EINVAL; + return; } - node_t *n = lookup_node(mesh, (char *)name); - - if(!n) { - n = new_node(); - n->name = xstrdup(name); - node_add(mesh, n); + if(maxtimeout < 0) { + meshlink_errno = EINVAL; + return; } - if(!whitelist(mesh, (node_t *)n)) { - pthread_mutex_unlock(&mesh->mutex); - return false; + if(pthread_mutex_lock(&mesh->mutex) != 0) { + abort(); } + mesh->dev_class_traits[devclass].maxtimeout = maxtimeout; pthread_mutex_unlock(&mesh->mutex); - - logger(mesh, MESHLINK_DEBUG, "Whitelisted %s.\n", name); - return true; } -void meshlink_set_default_blacklist(meshlink_handle_t *mesh, bool blacklist) { - mesh->default_blacklist = blacklist; -} - -bool meshlink_forget_node(meshlink_handle_t *mesh, meshlink_node_t *node) { - logger(mesh, MESHLINK_DEBUG, "meshlink_forget_node(%s)", node ? node->name : "(null)"); +void meshlink_reset_timers(struct meshlink_handle *mesh) { + logger(mesh, MESHLINK_DEBUG, "meshlink_reset_timers()"); - if(!mesh || !node) { - meshlink_errno = MESHLINK_EINVAL; - return false; + if(!mesh) { + return; } - node_t *n = (node_t *)node; - if(pthread_mutex_lock(&mesh->mutex) != 0) { abort(); } - /* Check that the node is not reachable */ - if(n->status.reachable || n->connection) { - pthread_mutex_unlock(&mesh->mutex); - logger(mesh, MESHLINK_WARNING, "Could not forget %s: still reachable", n->name); - return false; - } - - /* Check that we don't have any active UTCP connections */ - if(n->utcp && utcp_is_active(n->utcp)) { - pthread_mutex_unlock(&mesh->mutex); - logger(mesh, MESHLINK_WARNING, "Could not forget %s: active UTCP connections", n->name); - return false; - } - - /* Check that we have no active connections to this node */ - for list_each(connection_t, c, mesh->connections) { - if(c->node == n) { - pthread_mutex_unlock(&mesh->mutex); - logger(mesh, MESHLINK_WARNING, "Could not forget %s: active connection", n->name); - return false; - } - } - - /* Remove any pending outgoings to this node */ - if(mesh->outgoings) { - for list_each(outgoing_t, outgoing, mesh->outgoings) { - if(outgoing->node == n) { - list_delete_node(mesh->outgoings, list_node); - } - } - } - - /* Delete the config file for this node */ - if(!config_delete(mesh, "current", n->name)) { - pthread_mutex_unlock(&mesh->mutex); - return false; - } - - /* Delete the node struct and any remaining edges referencing this node */ - node_del(mesh, n); + handle_network_change(mesh, true); pthread_mutex_unlock(&mesh->mutex); - - return config_sync(mesh, "current"); } -/* Hint that a hostname may be found at an address - * See header file for detailed comment. - */ -void meshlink_hint_address(meshlink_handle_t *mesh, meshlink_node_t *node, const struct sockaddr *addr) { - logger(mesh, MESHLINK_DEBUG, "meshlink_hint_address(%s, %p)", node ? node->name : "(null)", (const void *)addr); +void meshlink_set_inviter_commits_first(struct meshlink_handle *mesh, bool inviter_commits_first) { + logger(mesh, MESHLINK_DEBUG, "meshlink_set_inviter_commits_first(%d)", inviter_commits_first); - if(!mesh || !node || !addr) { + if(!mesh) { meshlink_errno = EINVAL; return; } @@ -3414,1060 +2311,8 @@ void meshlink_hint_address(meshlink_handle_t *mesh, meshlink_node_t *node, const abort(); } - node_t *n = (node_t *)node; - - if(node_add_recent_address(mesh, n, (sockaddr_t *)addr)) { - if(!node_write_config(mesh, n, false)) { - logger(mesh, MESHLINK_DEBUG, "Could not update %s\n", n->name); - } - } - + mesh->inviter_commits_first = inviter_commits_first; pthread_mutex_unlock(&mesh->mutex); - // @TODO do we want to fire off a connection attempt right away? -} - -static bool channel_pre_accept(struct utcp *utcp, uint16_t port) { - (void)port; - node_t *n = utcp->priv; - meshlink_handle_t *mesh = n->mesh; - - if(mesh->channel_accept_cb && mesh->channel_listen_cb) { - return mesh->channel_listen_cb(mesh, (meshlink_node_t *)n, port); - } else { - return mesh->channel_accept_cb; - } -} - -/* Finish one AIO buffer, return true if the channel is still open. */ -static bool aio_finish_one(meshlink_handle_t *mesh, meshlink_channel_t *channel, meshlink_aio_buffer_t **head) { - meshlink_aio_buffer_t *aio = *head; - *head = aio->next; - - if(channel->c) { - channel->in_callback = true; - - if(aio->data) { - if(aio->cb.buffer) { - aio->cb.buffer(mesh, channel, aio->data, aio->done, aio->priv); - } - } else { - if(aio->cb.fd) { - aio->cb.fd(mesh, channel, aio->fd, aio->done, aio->priv); - } - } - - channel->in_callback = false; - - if(!channel->c) { - free(aio); - free(channel); - return false; - } - } - - free(aio); - return true; -} - -/* Finish all AIO buffers, return true if the channel is still open. */ -static bool aio_abort(meshlink_handle_t *mesh, meshlink_channel_t *channel, meshlink_aio_buffer_t **head) { - while(*head) { - if(!aio_finish_one(mesh, channel, head)) { - return false; - } - } - - return true; -} - -static ssize_t channel_recv(struct utcp_connection *connection, const void *data, size_t len) { - meshlink_channel_t *channel = connection->priv; - - if(!channel) { - abort(); - } - - node_t *n = channel->node; - meshlink_handle_t *mesh = n->mesh; - - if(n->status.destroyed) { - meshlink_channel_close(mesh, channel); - return len; - } - - const char *p = data; - size_t left = len; - - while(channel->aio_receive) { - if(!len) { - /* This receive callback signalled an error, abort all outstanding AIO buffers. */ - if(!aio_abort(mesh, channel, &channel->aio_receive)) { - return len; - } - - break; - } - - meshlink_aio_buffer_t *aio = channel->aio_receive; - size_t todo = aio->len - aio->done; - - if(todo > left) { - todo = left; - } - - if(aio->data) { - memcpy((char *)aio->data + aio->done, p, todo); - } else { - ssize_t result = write(aio->fd, p, todo); - - if(result <= 0) { - if(result < 0 && errno == EINTR) { - continue; - } - - /* Writing to fd failed, cancel just this AIO buffer. */ - logger(mesh, MESHLINK_ERROR, "Writing to AIO fd %d failed: %s", aio->fd, strerror(errno)); - - if(!aio_finish_one(mesh, channel, &channel->aio_receive)) { - return len; - } - - continue; - } - - todo = result; - } - - aio->done += todo; - p += todo; - left -= todo; - - if(aio->done == aio->len) { - if(!aio_finish_one(mesh, channel, &channel->aio_receive)) { - return len; - } - } - - if(!left) { - return len; - } - } - - if(channel->receive_cb) { - channel->receive_cb(mesh, channel, p, left); - } - - return len; -} - -static void channel_accept(struct utcp_connection *utcp_connection, uint16_t port) { - node_t *n = utcp_connection->utcp->priv; - - if(!n) { - abort(); - } - - meshlink_handle_t *mesh = n->mesh; - - if(!mesh->channel_accept_cb) { - return; - } - - meshlink_channel_t *channel = xzalloc(sizeof(*channel)); - channel->node = n; - channel->c = utcp_connection; - - if(mesh->channel_accept_cb(mesh, channel, port, NULL, 0)) { - utcp_accept(utcp_connection, channel_recv, channel); - } else { - free(channel); - } -} - -static void channel_retransmit(struct utcp_connection *utcp_connection) { - node_t *n = utcp_connection->utcp->priv; - meshlink_handle_t *mesh = n->mesh; - - if(n->mtuprobes == 31 && n->mtutimeout.cb) { - timeout_set(&mesh->loop, &n->mtutimeout, &(struct timespec) { - 0, 0 - }); - } -} - -static ssize_t channel_send(struct utcp *utcp, const void *data, size_t len) { - node_t *n = utcp->priv; - - if(n->status.destroyed) { - return -1; - } - - meshlink_handle_t *mesh = n->mesh; - return meshlink_send_immediate(mesh, (meshlink_node_t *)n, data, len) ? (ssize_t)len : -1; -} - -void meshlink_set_channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, meshlink_channel_receive_cb_t cb) { - logger(mesh, MESHLINK_DEBUG, "meshlink_set_channel_receive_cb(%p, %p)", (void *)channel, (void *)(intptr_t)cb); - - if(!mesh || !channel) { - meshlink_errno = MESHLINK_EINVAL; - return; - } - - channel->receive_cb = cb; -} - -static void channel_receive(meshlink_handle_t *mesh, meshlink_node_t *source, const void *data, size_t len) { - (void)mesh; - node_t *n = (node_t *)source; - - if(!n->utcp) { - abort(); - } - - utcp_recv(n->utcp, data, len); -} - -static void channel_poll(struct utcp_connection *connection, size_t len) { - meshlink_channel_t *channel = connection->priv; - - if(!channel) { - abort(); - } - - node_t *n = channel->node; - meshlink_handle_t *mesh = n->mesh; - - while(channel->aio_send) { - if(!len) { - /* This poll callback signalled an error, abort all outstanding AIO buffers. */ - if(!aio_abort(mesh, channel, &channel->aio_send)) { - return; - } - - break; - } - - /* We have at least one AIO buffer. Send as much as possible from the buffers. */ - meshlink_aio_buffer_t *aio = channel->aio_send; - size_t todo = aio->len - aio->done; - ssize_t sent; - - if(todo > len) { - todo = len; - } - - if(aio->data) { - sent = utcp_send(connection, (char *)aio->data + aio->done, todo); - } else { - /* Limit the amount we read at once to avoid stack overflows */ - if(todo > 65536) { - todo = 65536; - } - - char buf[todo]; - ssize_t result = read(aio->fd, buf, todo); - - if(result > 0) { - todo = result; - sent = utcp_send(connection, buf, todo); - } else { - if(result < 0 && errno == EINTR) { - continue; - } - - /* Reading from fd failed, cancel just this AIO buffer. */ - if(result != 0) { - logger(mesh, MESHLINK_ERROR, "Reading from AIO fd %d failed: %s", aio->fd, strerror(errno)); - } - - if(!aio_finish_one(mesh, channel, &channel->aio_send)) { - return; - } - - continue; - } - } - - if(sent != (ssize_t)todo) { - /* Sending failed, abort all outstanding AIO buffers and send a poll callback. */ - if(!aio_abort(mesh, channel, &channel->aio_send)) { - return; - } - - len = 0; - break; - } - - aio->done += sent; - len -= sent; - - /* If we didn't finish this buffer, exit early. */ - if(aio->done < aio->len) { - return; - } - - /* Signal completion of this buffer, and go to the next one. */ - if(!aio_finish_one(mesh, channel, &channel->aio_send)) { - return; - } - - if(!len) { - return; - } - } - - if(channel->poll_cb) { - channel->poll_cb(mesh, channel, len); - } else { - utcp_set_poll_cb(connection, NULL); - } -} - -void meshlink_set_channel_poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, meshlink_channel_poll_cb_t cb) { - logger(mesh, MESHLINK_DEBUG, "meshlink_set_channel_poll_cb(%p, %p)", (void *)channel, (void *)(intptr_t)cb); - - if(!mesh || !channel) { - meshlink_errno = MESHLINK_EINVAL; - return; - } - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - channel->poll_cb = cb; - utcp_set_poll_cb(channel->c, (cb || channel->aio_send) ? channel_poll : NULL); - pthread_mutex_unlock(&mesh->mutex); -} - -void meshlink_set_channel_listen_cb(meshlink_handle_t *mesh, meshlink_channel_listen_cb_t cb) { - logger(mesh, MESHLINK_DEBUG, "meshlink_set_channel_listen_cb(%p)", (void *)(intptr_t)cb); - - if(!mesh) { - meshlink_errno = MESHLINK_EINVAL; - return; - } - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - mesh->channel_listen_cb = cb; - - pthread_mutex_unlock(&mesh->mutex); -} - -void meshlink_set_channel_accept_cb(meshlink_handle_t *mesh, meshlink_channel_accept_cb_t cb) { - logger(mesh, MESHLINK_DEBUG, "meshlink_set_channel_accept_cb(%p)", (void *)(intptr_t)cb); - - if(!mesh) { - meshlink_errno = MESHLINK_EINVAL; - return; - } - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - mesh->channel_accept_cb = cb; - mesh->receive_cb = channel_receive; - - for splay_each(node_t, n, mesh->nodes) { - if(!n->utcp && n != mesh->self) { - n->utcp = utcp_init(channel_accept, channel_pre_accept, channel_send, n); - utcp_set_mtu(n->utcp, n->mtu - sizeof(meshlink_packethdr_t)); - utcp_set_retransmit_cb(n->utcp, channel_retransmit); - } - } - - pthread_mutex_unlock(&mesh->mutex); -} - -void meshlink_set_channel_sndbuf(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t size) { - logger(mesh, MESHLINK_DEBUG, "meshlink_set_channel_sndbuf(%p, %zu)", (void *)channel, size); - - meshlink_set_channel_sndbuf_storage(mesh, channel, NULL, size); -} - -void meshlink_set_channel_rcvbuf(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t size) { - logger(mesh, MESHLINK_DEBUG, "meshlink_set_channel_rcvbuf(%p, %zu)", (void *)channel, size); - - meshlink_set_channel_rcvbuf_storage(mesh, channel, NULL, size); -} - -void meshlink_set_channel_sndbuf_storage(meshlink_handle_t *mesh, meshlink_channel_t *channel, void *buf, size_t size) { - logger(mesh, MESHLINK_DEBUG, "meshlink_set_channel_sndbuf_storage(%p, %p, %zu)", (void *)channel, buf, size); - - if(!mesh || !channel) { - meshlink_errno = MESHLINK_EINVAL; - return; - } - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - utcp_set_sndbuf(channel->c, buf, size); - pthread_mutex_unlock(&mesh->mutex); -} - -void meshlink_set_channel_rcvbuf_storage(meshlink_handle_t *mesh, meshlink_channel_t *channel, void *buf, size_t size) { - logger(mesh, MESHLINK_DEBUG, "meshlink_set_channel_rcvbuf_storage(%p, %p, %zu)", (void *)channel, buf, size); - - if(!mesh || !channel) { - meshlink_errno = MESHLINK_EINVAL; - return; - } - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - utcp_set_rcvbuf(channel->c, buf, size); - pthread_mutex_unlock(&mesh->mutex); -} - -void meshlink_set_channel_flags(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint32_t flags) { - logger(mesh, MESHLINK_DEBUG, "meshlink_set_channel_flags(%p, %u)", (void *)channel, flags); - - if(!mesh || !channel) { - meshlink_errno = MESHLINK_EINVAL; - return; - } - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - utcp_set_flags(channel->c, flags); - pthread_mutex_unlock(&mesh->mutex); -} - -meshlink_channel_t *meshlink_channel_open_ex(meshlink_handle_t *mesh, meshlink_node_t *node, uint16_t port, meshlink_channel_receive_cb_t cb, const void *data, size_t len, uint32_t flags) { - logger(mesh, MESHLINK_DEBUG, "meshlink_channel_open_ex(%s, %u, %p, %p, %zu, %u)", node ? node->name : "(null)", port, (void *)(intptr_t)cb, data, len, flags); - - if(data && len) { - abort(); // TODO: handle non-NULL data - } - - if(!mesh || !node) { - meshlink_errno = MESHLINK_EINVAL; - return NULL; - } - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - node_t *n = (node_t *)node; - - if(!n->utcp) { - n->utcp = utcp_init(channel_accept, channel_pre_accept, channel_send, n); - utcp_set_mtu(n->utcp, n->mtu - sizeof(meshlink_packethdr_t)); - utcp_set_retransmit_cb(n->utcp, channel_retransmit); - mesh->receive_cb = channel_receive; - - if(!n->utcp) { - meshlink_errno = errno == ENOMEM ? MESHLINK_ENOMEM : MESHLINK_EINTERNAL; - pthread_mutex_unlock(&mesh->mutex); - return NULL; - } - } - - if(n->status.blacklisted) { - logger(mesh, MESHLINK_ERROR, "Cannot open a channel with blacklisted node\n"); - meshlink_errno = MESHLINK_EBLACKLISTED; - pthread_mutex_unlock(&mesh->mutex); - return NULL; - } - - meshlink_channel_t *channel = xzalloc(sizeof(*channel)); - channel->node = n; - channel->receive_cb = cb; - - if(data && !len) { - channel->priv = (void *)data; - } - - channel->c = utcp_connect_ex(n->utcp, port, channel_recv, channel, flags); - - pthread_mutex_unlock(&mesh->mutex); - - if(!channel->c) { - meshlink_errno = errno == ENOMEM ? MESHLINK_ENOMEM : MESHLINK_EINTERNAL; - free(channel); - return NULL; - } - - return channel; -} - -meshlink_channel_t *meshlink_channel_open(meshlink_handle_t *mesh, meshlink_node_t *node, uint16_t port, meshlink_channel_receive_cb_t cb, const void *data, size_t len) { - logger(mesh, MESHLINK_DEBUG, "meshlink_channel_open_ex(%s, %u, %p, %p, %zu)", node ? node->name : "(null)", port, (void *)(intptr_t)cb, data, 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) { - logger(mesh, MESHLINK_DEBUG, "meshlink_channel_shutdown(%p, %d)", (void *)channel, direction); - - if(!mesh || !channel) { - meshlink_errno = MESHLINK_EINVAL; - return; - } - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - utcp_shutdown(channel->c, direction); - pthread_mutex_unlock(&mesh->mutex); -} - -void meshlink_channel_close(meshlink_handle_t *mesh, meshlink_channel_t *channel) { - logger(mesh, MESHLINK_DEBUG, "meshlink_channel_close(%p)", (void *)channel); - - if(!mesh || !channel) { - meshlink_errno = MESHLINK_EINVAL; - return; - } - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - if(channel->c) { - utcp_close(channel->c); - channel->c = NULL; - - /* Clean up any outstanding AIO buffers. */ - aio_abort(mesh, channel, &channel->aio_send); - aio_abort(mesh, channel, &channel->aio_receive); - } - - if(!channel->in_callback) { - free(channel); - } - - pthread_mutex_unlock(&mesh->mutex); -} - -void meshlink_channel_abort(meshlink_handle_t *mesh, meshlink_channel_t *channel) { - logger(mesh, MESHLINK_DEBUG, "meshlink_channel_abort(%p)", (void *)channel); - - if(!mesh || !channel) { - meshlink_errno = MESHLINK_EINVAL; - return; - } - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - if(channel->c) { - utcp_abort(channel->c); - channel->c = NULL; - - /* Clean up any outstanding AIO buffers. */ - aio_abort(mesh, channel, &channel->aio_send); - aio_abort(mesh, channel, &channel->aio_receive); - } - - if(!channel->in_callback) { - free(channel); - } - - pthread_mutex_unlock(&mesh->mutex); -} - -ssize_t meshlink_channel_send(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *data, size_t len) { - logger(mesh, MESHLINK_DEBUG, "meshlink_channel_send(%p, %p, %zu)", (void *)channel, data, len); - - if(!mesh || !channel) { - meshlink_errno = MESHLINK_EINVAL; - return -1; - } - - if(!len) { - return 0; - } - - if(!data) { - meshlink_errno = MESHLINK_EINVAL; - return -1; - } - - // TODO: more finegrained locking. - // Ideally we want to put the data into the UTCP connection's send buffer. - // Then, preferably only if there is room in the receiver window, - // kick the meshlink thread to go send packets. - - ssize_t retval; - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - /* Disallow direct calls to utcp_send() while we still have AIO active. */ - if(channel->aio_send) { - retval = 0; - } else { - retval = utcp_send(channel->c, data, len); - } - - pthread_mutex_unlock(&mesh->mutex); - - if(retval < 0) { - meshlink_errno = MESHLINK_ENETWORK; - } - - return retval; -} - -bool meshlink_channel_aio_send(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *data, size_t len, meshlink_aio_cb_t cb, void *priv) { - logger(mesh, MESHLINK_DEBUG, "meshlink_channel_aio_send(%p, %p, %zu, %p, %p)", (void *)channel, data, len, (void *)(intptr_t)cb, priv); - - if(!mesh || !channel) { - meshlink_errno = MESHLINK_EINVAL; - return false; - } - - if(!len || !data) { - meshlink_errno = MESHLINK_EINVAL; - return false; - } - - meshlink_aio_buffer_t *aio = xzalloc(sizeof(*aio)); - aio->data = data; - aio->len = len; - aio->cb.buffer = cb; - aio->priv = priv; - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - /* Append the AIO buffer descriptor to the end of the chain */ - meshlink_aio_buffer_t **p = &channel->aio_send; - - while(*p) { - p = &(*p)->next; - } - - *p = aio; - - /* Ensure the poll callback is set, and call it right now to push data if possible */ - utcp_set_poll_cb(channel->c, channel_poll); - size_t todo = MIN(len, utcp_get_rcvbuf_free(channel->c)); - - if(todo) { - channel_poll(channel->c, todo); - } - - pthread_mutex_unlock(&mesh->mutex); - - return true; -} - -bool meshlink_channel_aio_fd_send(meshlink_handle_t *mesh, meshlink_channel_t *channel, int fd, size_t len, meshlink_aio_fd_cb_t cb, void *priv) { - logger(mesh, MESHLINK_DEBUG, "meshlink_channel_aio_fd_send(%p, %d, %zu, %p, %p)", (void *)channel, fd, len, (void *)(intptr_t)cb, priv); - - if(!mesh || !channel) { - meshlink_errno = MESHLINK_EINVAL; - return false; - } - - if(!len || fd == -1) { - meshlink_errno = MESHLINK_EINVAL; - return false; - } - - meshlink_aio_buffer_t *aio = xzalloc(sizeof(*aio)); - aio->fd = fd; - aio->len = len; - aio->cb.fd = cb; - aio->priv = priv; - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - /* Append the AIO buffer descriptor to the end of the chain */ - meshlink_aio_buffer_t **p = &channel->aio_send; - - while(*p) { - p = &(*p)->next; - } - - *p = aio; - - /* Ensure the poll callback is set, and call it right now to push data if possible */ - utcp_set_poll_cb(channel->c, channel_poll); - size_t left = utcp_get_rcvbuf_free(channel->c); - - if(left) { - channel_poll(channel->c, left); - } - - pthread_mutex_unlock(&mesh->mutex); - - return true; -} - -bool meshlink_channel_aio_receive(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *data, size_t len, meshlink_aio_cb_t cb, void *priv) { - logger(mesh, MESHLINK_DEBUG, "meshlink_channel_aio_receive(%p, %p, %zu, %p, %p)", (void *)channel, data, len, (void *)(intptr_t)cb, priv); - - if(!mesh || !channel) { - meshlink_errno = MESHLINK_EINVAL; - return false; - } - - if(!len || !data) { - meshlink_errno = MESHLINK_EINVAL; - return false; - } - - meshlink_aio_buffer_t *aio = xzalloc(sizeof(*aio)); - aio->data = data; - aio->len = len; - aio->cb.buffer = cb; - aio->priv = priv; - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - /* Append the AIO buffer descriptor to the end of the chain */ - meshlink_aio_buffer_t **p = &channel->aio_receive; - - while(*p) { - p = &(*p)->next; - } - - *p = aio; - - pthread_mutex_unlock(&mesh->mutex); - - return true; -} - -bool meshlink_channel_aio_fd_receive(meshlink_handle_t *mesh, meshlink_channel_t *channel, int fd, size_t len, meshlink_aio_fd_cb_t cb, void *priv) { - logger(mesh, MESHLINK_DEBUG, "meshlink_channel_aio_fd_receive(%p, %d, %zu, %p, %p)", (void *)channel, fd, len, (void *)(intptr_t)cb, priv); - - if(!mesh || !channel) { - meshlink_errno = MESHLINK_EINVAL; - return false; - } - - if(!len || fd == -1) { - meshlink_errno = MESHLINK_EINVAL; - return false; - } - - meshlink_aio_buffer_t *aio = xzalloc(sizeof(*aio)); - aio->fd = fd; - aio->len = len; - aio->cb.fd = cb; - aio->priv = priv; - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - /* Append the AIO buffer descriptor to the end of the chain */ - meshlink_aio_buffer_t **p = &channel->aio_receive; - - while(*p) { - p = &(*p)->next; - } - - *p = aio; - - pthread_mutex_unlock(&mesh->mutex); - - return true; -} - -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; -} - -size_t meshlink_channel_get_sendq(meshlink_handle_t *mesh, meshlink_channel_t *channel) { - if(!mesh || !channel) { - meshlink_errno = MESHLINK_EINVAL; - return -1; - } - - return utcp_get_sendq(channel->c); -} - -size_t meshlink_channel_get_recvq(meshlink_handle_t *mesh, meshlink_channel_t *channel) { - if(!mesh || !channel) { - meshlink_errno = MESHLINK_EINVAL; - return -1; - } - - return utcp_get_recvq(channel->c); -} - -size_t meshlink_channel_get_mss(meshlink_handle_t *mesh, meshlink_channel_t *channel) { - if(!mesh || !channel) { - meshlink_errno = MESHLINK_EINVAL; - return -1; - } - - return utcp_get_mss(channel->node->utcp); -} - -void meshlink_set_node_channel_timeout(meshlink_handle_t *mesh, meshlink_node_t *node, int timeout) { - logger(mesh, MESHLINK_DEBUG, "meshlink_set_node_channel_timeout(%s, %d)", node ? node->name : "(null)", timeout); - - if(!mesh || !node) { - meshlink_errno = MESHLINK_EINVAL; - return; - } - - node_t *n = (node_t *)node; - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - if(!n->utcp) { - n->utcp = utcp_init(channel_accept, channel_pre_accept, channel_send, n); - utcp_set_mtu(n->utcp, n->mtu - sizeof(meshlink_packethdr_t)); - utcp_set_retransmit_cb(n->utcp, channel_retransmit); - } - - utcp_set_user_timeout(n->utcp, timeout); - - pthread_mutex_unlock(&mesh->mutex); -} - -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); - utcp_set_mtu(n->utcp, n->mtu - sizeof(meshlink_packethdr_t)); - utcp_set_retransmit_cb(n->utcp, channel_retransmit); - } - - if(mesh->node_status_cb) { - mesh->node_status_cb(mesh, (meshlink_node_t *)n, n->status.reachable && !n->status.blacklisted); - } - - if(mesh->node_pmtu_cb) { - mesh->node_pmtu_cb(mesh, (meshlink_node_t *)n, n->minmtu); - } -} - -void update_node_pmtu(meshlink_handle_t *mesh, node_t *n) { - utcp_set_mtu(n->utcp, (n->minmtu > MINMTU ? n->minmtu : MINMTU) - sizeof(meshlink_packethdr_t)); - - if(mesh->node_pmtu_cb && !n->status.blacklisted) { - mesh->node_pmtu_cb(mesh, (meshlink_node_t *)n, n->minmtu); - } -} - -void handle_duplicate_node(meshlink_handle_t *mesh, node_t *n) { - if(!mesh->node_duplicate_cb || n->status.duplicate) { - return; - } - - n->status.duplicate = true; - mesh->node_duplicate_cb(mesh, (meshlink_node_t *)n); -} - -void meshlink_enable_discovery(meshlink_handle_t *mesh, bool enable) { - logger(mesh, MESHLINK_DEBUG, "meshlink_enable_discovery(%d)", enable); - - if(!mesh) { - meshlink_errno = MESHLINK_EINVAL; - return; - } - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - if(mesh->discovery.enabled == enable) { - goto end; - } - - if(mesh->threadstarted) { - if(enable) { - discovery_start(mesh); - } else { - discovery_stop(mesh); - } - } - - mesh->discovery.enabled = enable; - -end: - pthread_mutex_unlock(&mesh->mutex); -} - -void meshlink_hint_network_change(struct meshlink_handle *mesh) { - logger(mesh, MESHLINK_DEBUG, "meshlink_hint_network_change()"); - - if(!mesh) { - meshlink_errno = MESHLINK_EINVAL; - return; - } - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - if(mesh->discovery.enabled) { - scan_ifaddrs(mesh); - } - - if(mesh->loop.now.tv_sec > mesh->discovery.last_update + 5) { - mesh->discovery.last_update = mesh->loop.now.tv_sec; - handle_network_change(mesh, 1); - } - - pthread_mutex_unlock(&mesh->mutex); -} - -void meshlink_set_dev_class_timeouts(meshlink_handle_t *mesh, dev_class_t devclass, int pinginterval, int pingtimeout) { - logger(mesh, MESHLINK_DEBUG, "meshlink_set_dev_class_timeouts(%d, %d, %d)", devclass, pinginterval, pingtimeout); - - if(!mesh || devclass < 0 || devclass >= DEV_CLASS_COUNT) { - meshlink_errno = EINVAL; - return; - } - - if(pinginterval < 1 || pingtimeout < 1 || pingtimeout > pinginterval) { - meshlink_errno = EINVAL; - return; - } - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - mesh->dev_class_traits[devclass].pinginterval = pinginterval; - mesh->dev_class_traits[devclass].pingtimeout = pingtimeout; - pthread_mutex_unlock(&mesh->mutex); -} - -void meshlink_set_dev_class_fast_retry_period(meshlink_handle_t *mesh, dev_class_t devclass, int fast_retry_period) { - logger(mesh, MESHLINK_DEBUG, "meshlink_set_dev_class_fast_retry_period(%d, %d)", devclass, fast_retry_period); - - if(!mesh || devclass < 0 || devclass >= DEV_CLASS_COUNT) { - meshlink_errno = EINVAL; - return; - } - - if(fast_retry_period < 0) { - meshlink_errno = EINVAL; - return; - } - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - mesh->dev_class_traits[devclass].fast_retry_period = fast_retry_period; - pthread_mutex_unlock(&mesh->mutex); -} - -void meshlink_set_dev_class_maxtimeout(struct meshlink_handle *mesh, dev_class_t devclass, int maxtimeout) { - logger(mesh, MESHLINK_DEBUG, "meshlink_set_dev_class_fast_maxtimeout(%d, %d)", devclass, maxtimeout); - - if(!mesh || devclass < 0 || devclass >= DEV_CLASS_COUNT) { - meshlink_errno = EINVAL; - return; - } - - if(maxtimeout < 0) { - meshlink_errno = EINVAL; - return; - } - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - mesh->dev_class_traits[devclass].maxtimeout = maxtimeout; - pthread_mutex_unlock(&mesh->mutex); -} - -void meshlink_reset_timers(struct meshlink_handle *mesh) { - logger(mesh, MESHLINK_DEBUG, "meshlink_reset_timers()"); - - if(!mesh) { - return; - } - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - handle_network_change(mesh, true); - - if(mesh->discovery.enabled) { - discovery_refresh(mesh); - } - - pthread_mutex_unlock(&mesh->mutex); -} - -void meshlink_set_inviter_commits_first(struct meshlink_handle *mesh, bool inviter_commits_first) { - logger(mesh, MESHLINK_DEBUG, "meshlink_set_inviter_commits_first(%d)", inviter_commits_first); - - if(!mesh) { - meshlink_errno = EINVAL; - return; - } - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - mesh->inviter_commits_first = inviter_commits_first; - pthread_mutex_unlock(&mesh->mutex); -} - -void meshlink_set_external_address_discovery_url(struct meshlink_handle *mesh, const char *url) { - logger(mesh, MESHLINK_DEBUG, "meshlink_set_external_address_discovery_url(%s)", url ? url : "(null)"); - - if(!mesh) { - meshlink_errno = EINVAL; - return; - } - - if(url && (strncmp(url, "http://", 7) || strchr(url, ' '))) { - meshlink_errno = EINVAL; - return; - } - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - free(mesh->external_address_url); - mesh->external_address_url = url ? xstrdup(url) : NULL; - pthread_mutex_unlock(&mesh->mutex); -} - -void meshlink_set_scheduling_granularity(struct meshlink_handle *mesh, long granularity) { - logger(mesh, MESHLINK_DEBUG, "meshlink_set_scheduling_granularity(%ld)", granularity); - - if(!mesh || granularity < 0) { - meshlink_errno = EINVAL; - return; - } - - utcp_set_clock_granularity(granularity); } void meshlink_set_storage_policy(struct meshlink_handle *mesh, meshlink_storage_policy_t policy) { @@ -4489,7 +2334,7 @@ void meshlink_set_storage_policy(struct meshlink_handle *mesh, meshlink_storage_ void handle_network_change(meshlink_handle_t *mesh, bool online) { (void)online; - if(!mesh->connections || !mesh->loop.running) { + if(!mesh->loop.running) { return; } @@ -4514,7 +2359,6 @@ void call_error_cb(meshlink_handle_t *mesh, meshlink_errno_t cb_errno) { static void __attribute__((constructor)) meshlink_init(void) { crypto_init(); - utcp_set_clock_granularity(10000); } static void __attribute__((destructor)) meshlink_exit(void) {