X-Git-Url: http://git.meshlink.io/?a=blobdiff_plain;f=src%2Fmeshlink.c;h=e1cf7979631e0f94b9ebf3f8f820afeb5a66197d;hb=57f6341e91373198ad2a4099015f2988795d8a3f;hp=6fd89daded547d5ad2ad6045d55540fc2cfd4a17;hpb=b5aaabe0fcb104ef7801a68f4f9e6b77732dbb28;p=meshlink diff --git a/src/meshlink.c b/src/meshlink.c index 6fd89dad..e1cf7979 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. */ - if(result) { + int udp_fd = setup_udp_listen_socket(mesh, aip); + + 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; @@ -1098,7 +1161,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 +1533,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 +1796,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); @@ -1942,6 +2008,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 +2019,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 +2435,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 +2505,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 +2541,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 +2871,8 @@ bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) { state.sock = -1; continue; } + + break; } freeaddrinfo(ai); @@ -3965,6 +4088,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;