X-Git-Url: http://git.meshlink.io/?a=blobdiff_plain;f=src%2Fmeshlink.c;h=747bd3b5071a406310b4e4e7e8c7d8a463fcd736;hb=4c57e6902219ecca1872e18e34365d8e54a0f407;hp=6fd89daded547d5ad2ad6045d55540fc2cfd4a17;hpb=b5aaabe0fcb104ef7801a68f4f9e6b77732dbb28;p=meshlink diff --git a/src/meshlink.c b/src/meshlink.c index 6fd89dad..747bd3b5 100644 --- a/src/meshlink.c +++ b/src/meshlink.c @@ -221,12 +221,42 @@ char *meshlink_get_external_address(meshlink_handle_t *mesh) { } char *meshlink_get_external_address_for_family(meshlink_handle_t *mesh, int family) { - char *hostname = NULL; + 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 = str2addrinfo("meshlink.io", "80", SOCK_STREAM); - static const char request[] = "GET http://www.meshlink.io/host.cgi HTTP/1.0\r\n\r\n"; + struct addrinfo *ai = str2addrinfo(host, port ? port : "80", SOCK_STREAM); char line[256]; + char *hostname = NULL; for(struct addrinfo *aip = ai; aip; aip = aip->ai_next) { if(family != AF_UNSPEC && aip->ai_family != family) { @@ -245,7 +275,9 @@ char *meshlink_get_external_address_for_family(meshlink_handle_t *mesh, int fami } if(s >= 0) { - send(s, request, sizeof(request) - 1, 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) { @@ -389,10 +421,15 @@ void remove_duplicate_hostnames(char *host[], char *port[], int n) { // This gets the hostname part for use in invitation URLs static char *get_my_hostname(meshlink_handle_t *mesh, uint32_t flags) { - char *hostname[4] = {NULL}; - char *port[4] = {NULL}; + int count = 4 + (mesh->invitation_addresses ? mesh->invitation_addresses->count : 0); + int n = 0; + char *hostname[count]; + char *port[count]; char *hostport = NULL; + memset(hostname, 0, sizeof(hostname)); + memset(port, 0, sizeof(port)); + if(!(flags & (MESHLINK_INVITE_LOCAL | MESHLINK_INVITE_PUBLIC))) { flags |= MESHLINK_INVITE_LOCAL | MESHLINK_INVITE_PUBLIC; } @@ -401,44 +438,61 @@ static char *get_my_hostname(meshlink_handle_t *mesh, uint32_t flags) { flags |= MESHLINK_INVITE_IPV4 | MESHLINK_INVITE_IPV6; } + // Add all explicitly set invitation addresses + if(mesh->invitation_addresses) { + for list_each(char, combo, mesh->invitation_addresses) { + hostname[n] = xstrdup(combo); + char *slash = strrchr(hostname[n], '/'); + + if(slash) { + *slash = 0; + port[n] = xstrdup(slash + 1); + } + + n++; + } + } + // Add local addresses if requested if(flags & MESHLINK_INVITE_LOCAL) { if(flags & MESHLINK_INVITE_IPV4) { - hostname[0] = meshlink_get_local_address_for_family(mesh, AF_INET); + hostname[n++] = meshlink_get_local_address_for_family(mesh, AF_INET); } if(flags & MESHLINK_INVITE_IPV6) { - hostname[1] = meshlink_get_local_address_for_family(mesh, AF_INET6); + hostname[n++] = meshlink_get_local_address_for_family(mesh, AF_INET6); } } // Add public/canonical addresses if requested if(flags & MESHLINK_INVITE_PUBLIC) { // Try the CanonicalAddress first - get_canonical_address(mesh->self, &hostname[2], &port[2]); + get_canonical_address(mesh->self, &hostname[n], &port[n]); - if(!hostname[2]) { + if(!hostname[n] && count == 4) { if(flags & MESHLINK_INVITE_IPV4) { - hostname[2] = meshlink_get_external_address_for_family(mesh, AF_INET); + hostname[n++] = meshlink_get_external_address_for_family(mesh, AF_INET); } if(flags & MESHLINK_INVITE_IPV6) { - hostname[3] = meshlink_get_external_address_for_family(mesh, AF_INET6); + hostname[n++] = meshlink_get_external_address_for_family(mesh, AF_INET6); } + } else { + n++; } } - for(int i = 0; i < 4; i++) { + for(int i = 0; i < n; i++) { // Ensure we always have a port number if(hostname[i] && !port[i]) { port[i] = xstrdup(mesh->myport); } } - remove_duplicate_hostnames(hostname, port, 4); + remove_duplicate_hostnames(hostname, port, n); // Resolve the hostnames - for(int i = 0; i < 4; i++) { + for(int i = 0; i < n; i++) { if(!hostname[i]) { continue; } @@ -456,8 +510,10 @@ static char *get_my_hostname(meshlink_handle_t *mesh, uint32_t flags) { continue; } - // Remember the address - node_add_recent_address(mesh, mesh->self, (sockaddr_t *)ai_in->ai_addr); + // Remember the address(es) + for(struct addrinfo *aip = ai_in; aip; aip = aip->ai_next) { + node_add_recent_address(mesh, mesh->self, (sockaddr_t *)aip->ai_addr); + } if(flags & MESHLINK_INVITE_NUMERIC) { // We don't need to do any further conversion @@ -500,10 +556,10 @@ static char *get_my_hostname(meshlink_handle_t *mesh, uint32_t flags) { } // Remove duplicates again, since IPv4 and IPv6 addresses might map to the same hostname - remove_duplicate_hostnames(hostname, port, 4); + remove_duplicate_hostnames(hostname, port, n); // Concatenate all unique address to the hostport string - for(int i = 0; i < 4; i++) { + for(int i = 0; i < n; i++) { if(!hostname[i]) { continue; } @@ -521,7 +577,7 @@ static char *get_my_hostname(meshlink_handle_t *mesh, uint32_t flags) { return hostport; } -static bool try_bind(int port) { +static bool try_bind(meshlink_handle_t *mesh, int port) { struct addrinfo *ai = NULL; struct addrinfo hint = { .ai_flags = AI_PASSIVE, @@ -540,37 +596,44 @@ static bool try_bind(int port) { bool success = false; for(struct addrinfo *aip = ai; aip; aip = aip->ai_next) { - if(!(aip->ai_family == AF_INET || aip->ai_family == AF_INET6)) { - continue; - } + /* Try to bind to TCP. */ - int fd = socket(aip->ai_family, SOCK_STREAM, IPPROTO_TCP); + int tcp_fd = setup_tcp_listen_socket(mesh, aip); - if(fd == -1) { - success = false; - break; + if(tcp_fd == -1) { + if(errno == EADDRINUSE) { + /* If this port is in use for any address family, avoid it. */ + success = false; + break; + } else { + continue; + } } - int result = bind(fd, aip->ai_addr, aip->ai_addrlen); - closesocket(fd); + /* If TCP worked, then we require that UDP works as well. */ + + int udp_fd = setup_udp_listen_socket(mesh, aip); - if(result) { + if(udp_fd == -1) { + closesocket(tcp_fd); success = false; break; - } else { - success = true; } + + closesocket(tcp_fd); + closesocket(udp_fd); + success = true; } freeaddrinfo(ai); return success; } -static int check_port(meshlink_handle_t *mesh) { +int check_port(meshlink_handle_t *mesh) { for(int i = 0; i < 1000; i++) { int port = 0x1000 + prng(mesh, 0x8000); - if(try_bind(port)) { + if(try_bind(mesh, port)) { free(mesh->myport); xasprintf(&mesh->myport, "%d", port); return port; @@ -932,10 +995,18 @@ static bool ecdsa_keygen(meshlink_handle_t *mesh) { return true; } -static struct timeval idle(event_loop_t *loop, void *data) { +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 timeval t, tmin = {3600, 0}; + struct timespec t, tmin = {3600, 0}; for splay_each(node_t, n, mesh->nodes) { if(!n->utcp) { @@ -944,7 +1015,7 @@ static struct timeval idle(event_loop_t *loop, void *data) { t = utcp_timeout(n->utcp); - if(timercmp(&t, &tmin, <)) { + if(timespec_lt(&t, &tmin)) { tmin = t; } } @@ -1098,7 +1169,6 @@ static void *setup_network_in_netns_thread(void *arg) { } bool success = setup_network(mesh); - add_local_addresses(mesh); return success ? arg : NULL; } #endif // HAVE_SETNS @@ -1471,7 +1541,6 @@ meshlink_handle_t *meshlink_open_ex(const meshlink_open_params_t *params) { #endif // HAVE_SETNS } else { success = setup_network(mesh); - add_local_addresses(mesh); } if(!success) { @@ -1735,8 +1804,13 @@ void meshlink_close(meshlink_handle_t *mesh) { free(mesh->appname); free(mesh->confbase); free(mesh->config_key); + free(mesh->external_address_url); ecdsa_free(mesh->private_key); + if(mesh->invitation_addresses) { + list_delete_list(mesh->invitation_addresses); + } + main_config_unlock(mesh); pthread_mutex_unlock(&mesh->mutex); @@ -1888,22 +1962,12 @@ void meshlink_set_error_cb(struct meshlink_handle *mesh, meshlink_error_cb_t cb) pthread_mutex_unlock(&mesh->mutex); } -bool meshlink_send(meshlink_handle_t *mesh, meshlink_node_t *destination, const void *data, size_t len) { +static vpn_packet_t *prepare_packet(meshlink_handle_t *mesh, meshlink_node_t *destination, const void *data, size_t len) { meshlink_packethdr_t *hdr; - // Validate arguments - if(!mesh || !destination || len >= MAXSIZE - sizeof(*hdr)) { + if(len >= MAXSIZE - sizeof(*hdr)) { meshlink_errno = MESHLINK_EINVAL; - return false; - } - - if(!len) { - return true; - } - - if(!data) { - meshlink_errno = MESHLINK_EINVAL; - return false; + return NULL; } node_t *n = (node_t *)destination; @@ -1911,7 +1975,7 @@ bool meshlink_send(meshlink_handle_t *mesh, meshlink_node_t *destination, const if(n->status.blacklisted) { logger(mesh, MESHLINK_ERROR, "Node %s blacklisted, dropping packet\n", n->name); meshlink_errno = MESHLINK_EBLACKLISTED; - return false; + return NULL; } // Prepare the packet @@ -1919,7 +1983,7 @@ bool meshlink_send(meshlink_handle_t *mesh, meshlink_node_t *destination, const if(!packet) { meshlink_errno = MESHLINK_ENOMEM; - return false; + return NULL; } packet->probe = false; @@ -1935,6 +1999,52 @@ bool meshlink_send(meshlink_handle_t *mesh, meshlink_node_t *destination, const memcpy(packet->data + sizeof(*hdr), data, len); + return packet; +} + +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 + vpn_packet_t *packet = prepare_packet(mesh, destination, data, len); + + if(!packet) { + return false; + } + + // Send it immediately + route(mesh, mesh->self, packet); + free(packet); + + return true; +} + +bool meshlink_send(meshlink_handle_t *mesh, meshlink_node_t *destination, const void *data, size_t len) { + // Validate arguments + if(!mesh || !destination) { + meshlink_errno = MESHLINK_EINVAL; + return false; + } + + if(!len) { + return true; + } + + if(!data) { + meshlink_errno = MESHLINK_EINVAL; + return false; + } + + // Prepare the packet + vpn_packet_t *packet = prepare_packet(mesh, destination, data, len); + + if(!packet) { + return false; + } + // Queue it if(!meshlink_queue_push(&mesh->outpacketqueue, packet)) { free(packet); @@ -1942,6 +2052,8 @@ bool meshlink_send(meshlink_handle_t *mesh, meshlink_node_t *destination, const return false; } + logger(mesh, MESHLINK_DEBUG, "Adding packet of %zu bytes to packet queue", len); + // Notify event loop signal_trigger(&mesh->loop, &mesh->datafromapp); @@ -1951,17 +2063,16 @@ bool meshlink_send(meshlink_handle_t *mesh, meshlink_node_t *destination, const void meshlink_send_from_queue(event_loop_t *loop, void *data) { (void)loop; meshlink_handle_t *mesh = data; - vpn_packet_t *packet = meshlink_queue_pop(&mesh->outpacketqueue); - if(!packet) { - return; - } + logger(mesh, MESHLINK_DEBUG, "Flushing the packet queue"); - mesh->self->in_packets++; - mesh->self->in_bytes += packet->len; - route(mesh, mesh->self, packet); - - free(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); + free(packet); + } } ssize_t meshlink_get_pmtu(meshlink_handle_t *mesh, meshlink_node_t *destination) { @@ -2368,6 +2479,60 @@ bool meshlink_set_canonical_address(meshlink_handle_t *mesh, meshlink_node_t *no return config_sync(mesh, "current"); } +bool meshlink_add_invitation_address(struct meshlink_handle *mesh, const char *address, const char *port) { + if(!mesh || !address) { + meshlink_errno = MESHLINK_EINVAL; + return false; + } + + if(!is_valid_hostname(address)) { + logger(mesh, MESHLINK_DEBUG, "Invalid character in address: %s\n", address); + meshlink_errno = MESHLINK_EINVAL; + return false; + } + + if(port && !is_valid_port(port)) { + logger(mesh, MESHLINK_DEBUG, "Invalid character in port: %s\n", address); + meshlink_errno = MESHLINK_EINVAL; + return false; + } + + char *combo; + + if(port) { + xasprintf(&combo, "%s/%s", address, port); + } else { + combo = xstrdup(address); + } + + pthread_mutex_lock(&mesh->mutex); + + if(!mesh->invitation_addresses) { + mesh->invitation_addresses = list_alloc((list_action_t)free); + } + + list_insert_tail(mesh->invitation_addresses, combo); + pthread_mutex_unlock(&mesh->mutex); + + return true; +} + +void meshlink_clear_invitation_addresses(struct meshlink_handle *mesh) { + if(!mesh) { + meshlink_errno = MESHLINK_EINVAL; + return; + } + + pthread_mutex_lock(&mesh->mutex); + + if(mesh->invitation_addresses) { + list_delete_list(mesh->invitation_addresses); + mesh->invitation_addresses = NULL; + } + + pthread_mutex_unlock(&mesh->mutex); +} + bool meshlink_add_address(meshlink_handle_t *mesh, const char *address) { return meshlink_set_canonical_address(mesh, (meshlink_node_t *)mesh->self, address, NULL); } @@ -2384,7 +2549,7 @@ bool meshlink_add_external_address(meshlink_handle_t *mesh) { return false; } - bool rval = meshlink_add_address(mesh, address); + bool rval = meshlink_set_canonical_address(mesh, (meshlink_node_t *)mesh->self, address, NULL); free(address); return rval; @@ -2420,7 +2585,7 @@ bool meshlink_set_port(meshlink_handle_t *mesh, int port) { return true; } - if(!try_bind(port)) { + if(!try_bind(mesh, port)) { meshlink_errno = MESHLINK_ENETWORK; return false; } @@ -2750,6 +2915,8 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) { state.sock = -1; continue; } + + break; } freeaddrinfo(ai); @@ -3075,7 +3242,7 @@ static bool blacklist(meshlink_handle_t *mesh, node_t *n) { n->status.udp_confirmed = false; if(n->status.reachable) { - n->last_unreachable = mesh->loop.now.tv_sec; + n->last_unreachable = time(NULL); } /* Graph updates will suppress status updates for blacklisted nodes, so we need to @@ -3149,7 +3316,7 @@ static bool whitelist(meshlink_handle_t *mesh, node_t *n) { n->status.blacklisted = false; if(n->status.reachable) { - n->last_reachable = mesh->loop.now.tv_sec; + n->last_reachable = time(NULL); update_node_status(mesh, n); } @@ -3395,7 +3562,7 @@ static ssize_t channel_send(struct utcp *utcp, const void *data, size_t len) { } meshlink_handle_t *mesh = n->mesh; - return meshlink_send(mesh, (meshlink_node_t *)n, data, len) ? (ssize_t)len : -1; + 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) { @@ -3965,6 +4132,23 @@ extern void meshlink_set_inviter_commits_first(struct meshlink_handle *mesh, boo pthread_mutex_unlock(&mesh->mutex); } +void meshlink_set_external_address_discovery_url(struct meshlink_handle *mesh, const char *url) { + if(!mesh) { + meshlink_errno = EINVAL; + return; + } + + if(url && (strncmp(url, "http://", 7) || strchr(url, ' '))) { + meshlink_errno = EINVAL; + return; + } + + pthread_mutex_lock(&mesh->mutex); + free(mesh->external_address_url); + mesh->external_address_url = url ? xstrdup(url) : NULL; + pthread_mutex_unlock(&mesh->mutex); +} + void handle_network_change(meshlink_handle_t *mesh, bool online) { (void)online;