+ for(const char *p = hostname; *p; p++) {
+ if(!(isalnum(*p) || *p == '-' || *p == '.' || *p == ':')) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool is_valid_port(const char *port) {
+ if(!*port) {
+ return false;
+ }
+
+ if(isdigit(*port)) {
+ char *end;
+ unsigned long int result = strtoul(port, &end, 10);
+ return result && result < 65536 && !*end;
+ }
+
+ for(const char *p = port; *p; p++) {
+ if(!(isalnum(*p) || *p == '-')) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void set_timeout(int sock, int timeout) {
+#ifdef _WIN32
+ DWORD tv = timeout;
+#else
+ struct timeval tv;
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = (timeout - tv.tv_sec * 1000) * 1000;
+#endif
+ setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+ setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
+}
+
+// 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 getlocaladdrname(char *destaddr, char *host, socklen_t hostlen) {
+ struct addrinfo *rai = NULL;
+ const struct addrinfo hint = {
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_DGRAM,
+ .ai_protocol = IPPROTO_UDP,
+ };
+
+ if(getaddrinfo(destaddr, "80", &hint, &rai) || !rai) {
+ return false;
+ }
+
+ int sock = socket(rai->ai_family, rai->ai_socktype, rai->ai_protocol);
+
+ if(sock == -1) {
+ freeaddrinfo(rai);
+ return false;
+ }
+
+ if(connect(sock, rai->ai_addr, rai->ai_addrlen) && !sockwouldblock(errno)) {
+ freeaddrinfo(rai);
+ return false;
+ }
+
+ freeaddrinfo(rai);
+
+ struct sockaddr_storage sn;
+ socklen_t sl = sizeof(sn);
+
+ if(getsockname(sock, (struct sockaddr *)&sn, &sl)) {
+ return false;
+ }
+
+ if(getnameinfo((struct sockaddr *)&sn, sl, 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) {
+ char *hostname = NULL;