From 2032b44949d7628480ea92463bee8d2c45e7bd2b Mon Sep 17 00:00:00 2001 From: Erik Ekman Date: Sun, 24 May 2020 16:05:55 +0200 Subject: [PATCH] Look up external IP via DNS instead of HTTP Use myip.opendns.com via their resolver. The code is now TCP free again :) --- CHANGELOG | 2 +- man/iodine.8 | 2 +- src/dns.c | 27 ++++++++++++---- src/iodined.c | 86 +++++++++++++++++++++++++-------------------------- 4 files changed, 66 insertions(+), 51 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index ce89938..1dd823c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -13,11 +13,11 @@ master: - README converted to markdown by Nicolas Braud-Santoni. - Linux: use pkg-config for systemd support flags. Patch by Jason A. Donenfeld. - - Change external IP webservice to ipify.org - Add support for IPv6 in the server. Raw mode will be with same protocol as used for login. Traffic inside tunnel is still IPv4. - Update android build to support 5.0 (Lollipop) and newer. + - Change external IP lookup to using myip.opendns.com via DNS. 2014-06-16: 0.7.0 "Kryoptonite" - Partial IPv6 support (#107) diff --git a/man/iodine.8 b/man/iodine.8 index 0eb9b9b..2122cee 100644 --- a/man/iodine.8 +++ b/man/iodine.8 @@ -286,7 +286,7 @@ You must make sure the dns requests are forwarded to this port yourself. .B -n auto|external_ip The IP address to return in NS responses. Default is to return the address used as destination in the query. -If external_ip is 'auto', iodined will use ipify.org web service to +If external_ip is 'auto', iodined will use the opendns.com DNS service to retrieve the external IP of the host and use that for NS responses. .TP .B -b dnsport diff --git a/src/dns.c b/src/dns.c index 9269278..f6aad55 100644 --- a/src/dns.c +++ b/src/dns.c @@ -492,12 +492,27 @@ int dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, readlong(packet, &data, &ttl); readshort(packet, &data, &rlen); - memset(name, 0, sizeof(name)); - readname(packet, packetlen, &data, name, sizeof(name) - 1); - name[sizeof(name)-1] = '\0'; - strncpy(buf, name, buflen); - buf[buflen - 1] = '\0'; - rv = strlen(buf); + if (type == T_CNAME) { + /* For tunnels, query type A has CNAME type answer */ + memset(name, 0, sizeof(name)); + readname(packet, packetlen, &data, name, sizeof(name) - 1); + name[sizeof(name)-1] = '\0'; + strncpy(buf, name, buflen); + buf[buflen - 1] = '\0'; + rv = strlen(buf); + } + if (type == T_A) { + /* Answer type A includes only 4 bytes. + Not used for tunneling. */ + rv = MIN(rlen, sizeof(rdata)); + rv = readdata(packet, &data, rdata, rv); + if (rv >= 2 && buf) { + rv = MIN(rv, buflen); + memcpy(buf, rdata, rv); + } else { + rv = 0; + } + } } else if ((type == T_MX || type == T_SRV) && buf) { /* We support 250 records, 250*(255+header) ~= 64kB. diff --git a/src/iodined.c b/src/iodined.c index 7fb53c0..647892d 100644 --- a/src/iodined.c +++ b/src/iodined.c @@ -123,57 +123,57 @@ get_dns_fd(struct dnsfd *fds, struct sockaddr_storage *addr) return fds->v4fd; } -/* Ask ipify.org webservice to get external ip */ +/* Ask opendns.com DNS service for external ip */ static int get_external_ip(struct in_addr *ip) { - int sock; - struct addrinfo *addr; + static const char target[] = "myip.opendns.com"; + struct query query; + int attempt; int res; - const char *getstr = "GET / HTTP/1.0\r\n" - /* HTTP 1.0 to avoid chunked transfer coding */ - "Host: api.ipify.org\r\n\r\n"; - char buf[512]; - char *b; - int len; + int fd; + int out_len = 0; + memset(&query, 0, sizeof(query)); - res = getaddrinfo("api.ipify.org", "80", NULL, &addr); + query.type = T_A; + res = get_addr("resolver1.opendns.com", 53, AF_INET, 0, &query.destination); if (res < 0) return 1; + query.dest_len = res; - sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); - if (sock < 0) { - freeaddrinfo(addr); - return 2; + fd = open_dns_from_host(NULL, 0, AF_INET, AI_PASSIVE); + if (fd < 0) return 2; + + for (attempt = 0; attempt < 3; attempt++) { + char buf[64*1024]; + int buflen; + fd_set fds; + struct timeval tv; + + if (attempt) fprintf(stderr, "Retrying external IP lookup\n"); + query.id = rand(); + buflen = sizeof(buf); + buflen = dns_encode(buf, buflen, &query, QR_QUERY, target, strlen(target)); + if (buflen < 0) continue; + + sendto(fd, buf, buflen, 0, (struct sockaddr*)&query.destination, query.dest_len); + + tv.tv_sec = 1 + attempt; + tv.tv_usec = 0; + FD_ZERO(&fds); + FD_SET(fd, &fds); + res = select(fd + 1, &fds, NULL, NULL, &tv); + if (res > 0) { + buflen = sizeof(buf); + buflen = recv(fd, buf, buflen, 0); + if (buflen > 0) { + out_len = dns_decode((char *)ip, sizeof(struct in_addr), &query, QR_ANSWER, buf, buflen); + if (out_len > 0) break; + } + } } - res = connect(sock, addr->ai_addr, addr->ai_addrlen); - freeaddrinfo(addr); - if (res < 0) return 3; - - res = write(sock, getstr, strlen(getstr)); - if (res != strlen(getstr)) return 4; - - /* Zero buf before receiving, leave at least one zero at the end */ - memset(buf, 0, sizeof(buf)); - res = read(sock, buf, sizeof(buf) - 1); - if (res < 0) return 5; - len = res; - - res = close(sock); - if (res < 0) return 6; - - b = buf; - while (len > 9) { - /* Look for split between headers and data */ - if (strncmp("\r\n\r\n", b, 4) == 0) break; - b++; - len--; - } - if (len < 10) return 7; - b += 4; - - res = inet_aton(b, ip); - return (res == 0); + close_dns(fd); + return (out_len != sizeof(struct in_addr)); } static void @@ -2616,7 +2616,7 @@ main(int argc, char **argv) struct in_addr extip; int res = get_external_ip(&extip); if (res) { - fprintf(stderr, "Failed to get external IP via web service.\n"); + fprintf(stderr, "Failed to get external IP via DNS query.\n"); exit(3); } ns_ip = extip.s_addr;