#include "system.h"
#include <pthread.h>
-#include "adns.h"
#include "crypto.h"
#include "ecdsagen.h"
#include "logger.h"
#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
typedef bool (*search_node_by_condition_t)(const node_t *, const void *);
+#define BUFSIZE 128
+
static int rstrip(char *value) {
int len = strlen(value);
}
-// 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;
}
- uint8_t buf[4096];
+ uint8_t buf[BUFSIZE];
/* Write the main config file */
packmsg_output_t out = {buf, sizeof buf};
char *data;
size_t thedatalen;
size_t blen;
- char line[4096];
- char buffer[4096];
+ char line[BUFSIZE];
+ char buffer[BUFSIZE];
} join_state_t;
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;
}
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
}
}
- /* 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;
sptps_send_record(&state->sptps, 1, ecdsa_get_public_key(mesh->private_key), 32);
- logger(mesh, MESHLINK_DEBUG, "Configuration stored in: %s\n", mesh->confbase);
+ if(mesh->confbase) {
+ logger(mesh, MESHLINK_DEBUG, "Configuration stored in: %s\n", mesh->confbase);
+ }
return true;
}
}
static bool sendline(int fd, const char *format, ...) {
- char buffer[4096];
+ char buffer[BUFSIZE];
char *p = buffer;
int blen = 0;
va_list ap;
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;
- }
- }
+ (void)data;
- 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);
-
- // 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) {
return false;
}
- if(check_port(mesh) == 0) {
- meshlink_errno = MESHLINK_ENETWORK;
- return false;
- }
+ mesh->myport = xstrdup("0");
/* Create a node for ourself */
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));
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;
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;
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;
#endif // HAVE_SETNS
}
- if(mesh->discovery.enabled) {
- discovery_start(mesh);
- }
-
if(pthread_mutex_lock(&mesh->mutex) != 0) {
abort();
}
pthread_mutex_unlock(&mesh->mutex);
- // Stop discovery
- if(mesh->discovery.enabled) {
- discovery_stop(mesh);
- }
-
return NULL;
}
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");
}
init_outgoings(mesh);
- init_adns(mesh);
// Start the main thread
pthread_cond_wait(&mesh->cond, &mesh->mutex);
mesh->threadstarted = true;
- // Ensure we are considered reachable
- graph(mesh);
-
pthread_mutex_unlock(&mesh->mutex);
return true;
}
// 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
}
// 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
}
}
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);
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);
return true;
}
- if(!data) {
+ if(!data || len > MTU) {
meshlink_errno = MESHLINK_EINVAL;
return false;
}
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)) {
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;
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_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);
-}
-
-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;
-}
-
-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);
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()");
+bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) {
+ logger(mesh, MESHLINK_DEBUG, "meshlink_join(%s)", invitation ? invitation : "(null)");
- if(!mesh) {
+ if(!mesh || !invitation) {
meshlink_errno = MESHLINK_EINVAL;
return false;
}
- char *address = meshlink_get_external_address(mesh);
-
- if(!address) {
+ if(mesh->storage_policy == MESHLINK_STORAGE_DISABLED) {
+ meshlink_errno = MESHLINK_EINVAL;
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;
- }
+ join_state_t *state = xzalloc(sizeof * state);
+ state->mesh = mesh;
+ state->sock = -1;
- if(!mesh->myport) {
- meshlink_errno = MESHLINK_EINTERNAL;
- return -1;
- }
+ ecdsa_t *key = NULL;
+ ecdsa_t *hiskey = NULL;
- int port;
+ //TODO: think of a better name for this variable, or of a different way to tokenize the invitation URL.
+ char *copy = xstrdup(invitation);
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) {
+ //Before doing meshlink_join make sure we are not connected to another mesh
+ if(mesh->threadstarted) {
+ logger(mesh, MESHLINK_ERROR, "Cannot join while started\n");
meshlink_errno = MESHLINK_EINVAL;
- return false;
+ goto exit;
}
- if(mesh->myport && port == atoi(mesh->myport)) {
- return true;
+ // 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->peer) {
+ logger(mesh, MESHLINK_ERROR, "Already part of an existing mesh\n");
+ meshlink_errno = MESHLINK_EINVAL;
+ goto exit;
}
- 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)");
-
- if(!mesh || !invitation) {
- meshlink_errno = MESHLINK_EINVAL;
- return false;
- }
-
- if(mesh->storage_policy == MESHLINK_STORAGE_DISABLED) {
- meshlink_errno = MESHLINK_EINVAL;
- return false;
- }
-
- join_state_t state = {
- .mesh = mesh,
- .sock = -1,
- };
-
- ecdsa_t *key = NULL;
- ecdsa_t *hiskey = NULL;
-
- //TODO: think of a better name for this variable, or of a different way to tokenize the invitation URL.
- char copy[strlen(invitation) + 1];
-
- if(pthread_mutex_lock(&mesh->mutex) != 0) {
- abort();
- }
-
- //Before doing meshlink_join make sure we are not connected to another mesh
- if(mesh->threadstarted) {
- logger(mesh, MESHLINK_ERROR, "Cannot join while started\n");
- meshlink_errno = MESHLINK_EINVAL;
- goto exit;
- }
-
- // 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) {
- logger(mesh, MESHLINK_ERROR, "Already part of an existing mesh\n");
- meshlink_errno = MESHLINK_EINVAL;
- goto exit;
- }
-
- strcpy(copy, invitation);
-
// Split the invitation URL into a list of hostname/port tuples, a key hash and a cookie.
char *slash = strchr(copy, '/');
char *address = copy;
char *port = NULL;
- if(!b64decode(slash, state.hash, 18) || !b64decode(slash + 24, state.cookie, 18)) {
+ if(!b64decode(slash, state->hash, 18) || !b64decode(slash + 24, state->cookie, 18)) {
goto invalid;
}
+ logger(mesh, MESHLINK_DEBUG, "Done");
+
if(mesh->inviter_commits_first) {
- memcpy(state.cookie + 18, ecdsa_get_public_key(mesh->private_key), 32);
+ memcpy(state->cookie + 18, ecdsa_get_public_key(mesh->private_key), 32);
}
// Generate a throw-away key for the invitation.
key = ecdsa_generate();
+ logger(mesh, MESHLINK_DEBUG, "Done");
if(!key) {
meshlink_errno = MESHLINK_EINTERNAL;
}
// 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) {
- state.sock = socket_in_netns(aip->ai_family, SOCK_STREAM, IPPROTO_TCP, mesh->netns);
+ state->sock = socket_in_netns(aip->ai_family, SOCK_STREAM, IPPROTO_TCP, mesh->netns);
- if(state.sock == -1) {
+ if(state->sock == -1) {
logger(mesh, MESHLINK_DEBUG, "Could not open socket: %s\n", strerror(errno));
meshlink_errno = MESHLINK_ENETWORK;
continue;
#ifdef SO_NOSIGPIPE
int nosigpipe = 1;
- setsockopt(state.sock, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(nosigpipe));
+ setsockopt(state->sock, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(nosigpipe));
#endif
- set_timeout(state.sock, 5000);
+ set_timeout(state->sock, 5000);
- if(connect(state.sock, aip->ai_addr, aip->ai_addrlen)) {
+ if(connect(state->sock, aip->ai_addr, aip->ai_addrlen)) {
logger(mesh, MESHLINK_DEBUG, "Could not connect to %s port %s: %s\n", address, port, strerror(errno));
meshlink_errno = MESHLINK_ENETWORK;
- closesocket(state.sock);
- state.sock = -1;
+ closesocket(state->sock);
+ state->sock = -1;
continue;
}
meshlink_errno = MESHLINK_ERESOLV;
}
- if(state.sock != -1 || !comma) {
+ if(state->sock != -1 || !comma) {
break;
}
address = comma;
}
- if(state.sock == -1) {
+ if(state->sock == -1) {
goto exit;
}
// Tell him we have an invitation, and give him our throw-away key.
- state.blen = 0;
+ state->blen = 0;
- if(!sendline(state.sock, "0 ?%s %d.%d %s", b64key, PROT_MAJOR, PROT_MINOR, mesh->appname)) {
+ if(!sendline(state->sock, "0 ?%s %d.%d %s", b64key, PROT_MAJOR, PROT_MINOR, mesh->appname)) {
logger(mesh, MESHLINK_ERROR, "Error sending request to %s port %s: %s\n", address, port, strerror(errno));
meshlink_errno = MESHLINK_ENETWORK;
goto exit;
free(b64key);
- char hisname[4096] = "";
+ char hisname[BUFSIZE] = "";
int code, hismajor, hisminor = 0;
- if(!recvline(&state) || sscanf(state.line, "%d %s %d.%d", &code, hisname, &hismajor, &hisminor) < 3 || code != 0 || hismajor != PROT_MAJOR || !check_id(hisname) || !recvline(&state) || !rstrip(state.line) || sscanf(state.line, "%d ", &code) != 1 || code != ACK || strlen(state.line) < 3) {
+ if(!recvline(state) || sscanf(state->line, "%d %s %d.%d", &code, hisname, &hismajor, &hisminor) < 3 || code != 0 || hismajor != PROT_MAJOR || !check_id(hisname) || !recvline(state) || !rstrip(state->line) || sscanf(state->line, "%d ", &code) != 1 || code != ACK || strlen(state->line) < 3) {
logger(mesh, MESHLINK_ERROR, "Cannot read greeting from peer\n");
meshlink_errno = MESHLINK_ENETWORK;
goto exit;
}
// Check if the hash of the key he gave us matches the hash in the URL.
- char *fingerprint = state.line + 2;
+ char *fingerprint = state->line + 2;
char hishash[64];
if(sha512(fingerprint, strlen(fingerprint), hishash)) {
- logger(mesh, MESHLINK_ERROR, "Could not create hash\n%s\n", state.line + 2);
+ logger(mesh, MESHLINK_ERROR, "Could not create hash\n%s\n", state->line + 2);
meshlink_errno = MESHLINK_EINTERNAL;
goto exit;
}
- if(memcmp(hishash, state.hash, 18)) {
- logger(mesh, MESHLINK_ERROR, "Peer has an invalid key!\n%s\n", state.line + 2);
+ if(memcmp(hishash, state->hash, 18)) {
+ logger(mesh, MESHLINK_ERROR, "Peer has an invalid key!\n%s\n", state->line + 2);
meshlink_errno = MESHLINK_EPEER;
goto exit;
}
}
// Start an SPTPS session
- if(!sptps_start(&state.sptps, &state, true, false, key, hiskey, meshlink_invitation_label, sizeof(meshlink_invitation_label), invitation_send, invitation_receive)) {
+ if(!sptps_start(&state->sptps, state, true, false, key, hiskey, meshlink_invitation_label, sizeof(meshlink_invitation_label), invitation_send, invitation_receive)) {
meshlink_errno = MESHLINK_EINTERNAL;
goto exit;
}
// Feed rest of input buffer to SPTPS
- if(!sptps_receive_data(&state.sptps, state.buffer, state.blen)) {
+ if(!sptps_receive_data(&state->sptps, state->buffer, state->blen)) {
meshlink_errno = MESHLINK_EPEER;
goto exit;
}
ssize_t len;
- logger(mesh, MESHLINK_DEBUG, "Starting invitation recv loop: %d %zu\n", state.sock, sizeof(state.line));
+ logger(mesh, MESHLINK_DEBUG, "Starting invitation recv loop: %d %zu\n", state->sock, sizeof(state->line));
- while((len = recv(state.sock, state.line, sizeof(state.line), 0))) {
+ while((len = recv(state->sock, state->line, sizeof(state->line), 0))) {
if(len < 0) {
if(errno == EINTR) {
continue;
}
+ if(errno == ENOTCONN) {
+ break;
+ }
+
logger(mesh, MESHLINK_ERROR, "Error reading data from %s port %s: %s\n", address, port, strerror(errno));
meshlink_errno = MESHLINK_ENETWORK;
goto exit;
}
- if(!sptps_receive_data(&state.sptps, state.line, len)) {
+ if(!sptps_receive_data(&state->sptps, state->line, len)) {
meshlink_errno = MESHLINK_EPEER;
goto exit;
}
+
+ if(state->success) {
+ break;
+ }
}
- if(!state.success) {
+ if(!state->success) {
logger(mesh, MESHLINK_ERROR, "Connection closed by peer, invitation cancelled.\n");
meshlink_errno = MESHLINK_EPEER;
goto exit;
}
- sptps_stop(&state.sptps);
+ sptps_stop(&state->sptps);
ecdsa_free(hiskey);
ecdsa_free(key);
- closesocket(state.sock);
+ closesocket(state->sock);
pthread_mutex_unlock(&mesh->mutex);
return true;
logger(mesh, MESHLINK_ERROR, "Invalid invitation URL\n");
meshlink_errno = MESHLINK_EINVAL;
exit:
- sptps_stop(&state.sptps);
+ sptps_stop(&state->sptps);
ecdsa_free(hiskey);
ecdsa_free(key);
- if(state.sock != -1) {
- closesocket(state.sock);
+ if(state->sock != -1) {
+ closesocket(state->sock);
}
pthread_mutex_unlock(&mesh->mutex);
+ free(copy);
return false;
}
// Create a config file on the fly.
- uint8_t buf[4096];
+ uint8_t buf[BUFSIZE];
packmsg_output_t out = {buf, sizeof(buf)};
packmsg_add_uint32(&out, MESHLINK_CONFIG_VERSION);
packmsg_add_str(&out, mesh->name);
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);
return true;
}
-bool meshlink_forget_node(meshlink_handle_t *mesh, meshlink_node_t *node) {
- logger(mesh, MESHLINK_DEBUG, "meshlink_forget_node(%s)", node ? node->name : "(null)");
-
- if(!mesh || !node) {
- meshlink_errno = MESHLINK_EINVAL;
- return false;
- }
-
- 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);
-
- 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.
*/
// @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;
- }
+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);
}
-
- 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;
- }
+void handle_duplicate_node(meshlink_handle_t *mesh, node_t *n) {
+ if(!mesh->node_duplicate_cb || n->status.duplicate) {
+ return;
}
- return true;
+ n->status.duplicate = true;
+ mesh->node_duplicate_cb(mesh, (meshlink_node_t *)n);
}
-static ssize_t channel_recv(struct utcp_connection *connection, const void *data, size_t len) {
- meshlink_channel_t *channel = connection->priv;
+void meshlink_hint_network_change(struct meshlink_handle *mesh) {
+ logger(mesh, MESHLINK_DEBUG, "meshlink_hint_network_change()");
- if(!channel) {
- abort();
+ if(!mesh) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return;
}
- node_t *n = channel->node;
- meshlink_handle_t *mesh = n->mesh;
-
- if(n->status.destroyed) {
- meshlink_channel_close(mesh, channel);
- return len;
+ if(pthread_mutex_lock(&mesh->mutex) != 0) {
+ abort();
}
- 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;
- }
+ pthread_mutex_unlock(&mesh->mutex);
+}
- aio->done += todo;
- p += todo;
- left -= todo;
+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(aio->done == aio->len) {
- if(!aio_finish_one(mesh, channel, &channel->aio_receive)) {
- return len;
- }
- }
+ if(!mesh || devclass < 0 || devclass >= DEV_CLASS_COUNT) {
+ meshlink_errno = EINVAL;
+ return;
+ }
- if(!left) {
- return len;
- }
+ if(pinginterval < 1 || pingtimeout < 1 || pingtimeout > pinginterval) {
+ meshlink_errno = EINVAL;
+ return;
}
- if(channel->receive_cb) {
- channel->receive_cb(mesh, channel, p, left);
+ if(pthread_mutex_lock(&mesh->mutex) != 0) {
+ abort();
}
- return len;
+ mesh->dev_class_traits[devclass].pinginterval = pinginterval;
+ mesh->dev_class_traits[devclass].pingtimeout = pingtimeout;
+ pthread_mutex_unlock(&mesh->mutex);
}
-static void channel_accept(struct utcp_connection *utcp_connection, uint16_t port) {
- node_t *n = utcp_connection->utcp->priv;
+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) {
- abort();
+ if(!mesh || devclass < 0 || devclass >= DEV_CLASS_COUNT) {
+ meshlink_errno = EINVAL;
+ return;
}
- meshlink_handle_t *mesh = n->mesh;
-
- if(!mesh->channel_accept_cb) {
+ if(fast_retry_period < 0) {
+ meshlink_errno = EINVAL;
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);
+ if(pthread_mutex_lock(&mesh->mutex) != 0) {
+ abort();
}
-}
-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
- });
- }
+ mesh->dev_class_traits[devclass].fast_retry_period = fast_retry_period;
+ pthread_mutex_unlock(&mesh->mutex);
}
-static ssize_t channel_send(struct utcp *utcp, const void *data, size_t len) {
- node_t *n = utcp->priv;
+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(n->status.destroyed) {
- return -1;
+ if(!mesh || devclass < 0 || devclass >= DEV_CLASS_COUNT) {
+ meshlink_errno = EINVAL;
+ return;
}
- 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;
+ if(maxtimeout < 0) {
+ meshlink_errno = 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) {
+ if(pthread_mutex_lock(&mesh->mutex) != 0) {
abort();
}
- utcp_recv(n->utcp, data, len);
+ mesh->dev_class_traits[devclass].maxtimeout = maxtimeout;
+ pthread_mutex_unlock(&mesh->mutex);
}
-static void channel_poll(struct utcp_connection *connection, size_t len) {
- meshlink_channel_t *channel = connection->priv;
+void meshlink_reset_timers(struct meshlink_handle *mesh) {
+ logger(mesh, MESHLINK_DEBUG, "meshlink_reset_timers()");
+
+ if(!mesh) {
+ return;
+ }
- if(!channel) {
+ if(pthread_mutex_lock(&mesh->mutex) != 0) {
abort();
}
- node_t *n = channel->node;
- meshlink_handle_t *mesh = n->mesh;
+ handle_network_change(mesh, true);
- 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;
- }
+ pthread_mutex_unlock(&mesh->mutex);
+}
- break;
- }
+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);
- /* 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(!mesh) {
+ meshlink_errno = EINVAL;
+ return;
+ }
- 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();
- }
+ 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) {
logger(mesh, MESHLINK_DEBUG, "meshlink_set_storage_policy(%d)", policy);
void handle_network_change(meshlink_handle_t *mesh, bool online) {
(void)online;
- if(!mesh->connections || !mesh->loop.running) {
+ if(!mesh->loop.running) {
return;
}
static void __attribute__((constructor)) meshlink_init(void) {
crypto_init();
- utcp_set_clock_granularity(10000);
}
static void __attribute__((destructor)) meshlink_exit(void) {