Look up external IP via DNS instead of HTTP

Use myip.opendns.com via their resolver.

The code is now TCP free again :)
This commit is contained in:
Erik Ekman 2020-05-24 16:05:55 +02:00
parent e8a4c66719
commit 2032b44949
4 changed files with 66 additions and 51 deletions

View File

@ -13,11 +13,11 @@ master:
- README converted to markdown by Nicolas Braud-Santoni. - README converted to markdown by Nicolas Braud-Santoni.
- Linux: use pkg-config for systemd support flags. - Linux: use pkg-config for systemd support flags.
Patch by Jason A. Donenfeld. Patch by Jason A. Donenfeld.
- Change external IP webservice to ipify.org
- Add support for IPv6 in the server. - Add support for IPv6 in the server.
Raw mode will be with same protocol as used for login. Raw mode will be with same protocol as used for login.
Traffic inside tunnel is still IPv4. Traffic inside tunnel is still IPv4.
- Update android build to support 5.0 (Lollipop) and newer. - 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" 2014-06-16: 0.7.0 "Kryoptonite"
- Partial IPv6 support (#107) - Partial IPv6 support (#107)

View File

@ -286,7 +286,7 @@ You must make sure the dns requests are forwarded to this port yourself.
.B -n auto|external_ip .B -n auto|external_ip
The IP address to return in NS responses. Default is to return the address used The IP address to return in NS responses. Default is to return the address used
as destination in the query. 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. retrieve the external IP of the host and use that for NS responses.
.TP .TP
.B -b dnsport .B -b dnsport

View File

@ -492,6 +492,8 @@ int dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet,
readlong(packet, &data, &ttl); readlong(packet, &data, &ttl);
readshort(packet, &data, &rlen); readshort(packet, &data, &rlen);
if (type == T_CNAME) {
/* For tunnels, query type A has CNAME type answer */
memset(name, 0, sizeof(name)); memset(name, 0, sizeof(name));
readname(packet, packetlen, &data, name, sizeof(name) - 1); readname(packet, packetlen, &data, name, sizeof(name) - 1);
name[sizeof(name)-1] = '\0'; name[sizeof(name)-1] = '\0';
@ -499,6 +501,19 @@ int dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet,
buf[buflen - 1] = '\0'; buf[buflen - 1] = '\0';
rv = strlen(buf); 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) { else if ((type == T_MX || type == T_SRV) && buf) {
/* We support 250 records, 250*(255+header) ~= 64kB. /* We support 250 records, 250*(255+header) ~= 64kB.
Only exact 10-multiples are accepted, and gaps in Only exact 10-multiples are accepted, and gaps in

View File

@ -123,57 +123,57 @@ get_dns_fd(struct dnsfd *fds, struct sockaddr_storage *addr)
return fds->v4fd; return fds->v4fd;
} }
/* Ask ipify.org webservice to get external ip */ /* Ask opendns.com DNS service for external ip */
static int static int
get_external_ip(struct in_addr *ip) get_external_ip(struct in_addr *ip)
{ {
int sock; static const char target[] = "myip.opendns.com";
struct addrinfo *addr; struct query query;
int attempt;
int res; int res;
const char *getstr = "GET / HTTP/1.0\r\n" int fd;
/* HTTP 1.0 to avoid chunked transfer coding */ int out_len = 0;
"Host: api.ipify.org\r\n\r\n"; memset(&query, 0, sizeof(query));
char buf[512];
char *b;
int len;
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; if (res < 0) return 1;
query.dest_len = res;
sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); fd = open_dns_from_host(NULL, 0, AF_INET, AI_PASSIVE);
if (sock < 0) { if (fd < 0) return 2;
freeaddrinfo(addr);
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); close_dns(fd);
freeaddrinfo(addr); return (out_len != sizeof(struct in_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);
} }
static void static void
@ -2616,7 +2616,7 @@ main(int argc, char **argv)
struct in_addr extip; struct in_addr extip;
int res = get_external_ip(&extip); int res = get_external_ip(&extip);
if (res) { 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); exit(3);
} }
ns_ip = extip.s_addr; ns_ip = extip.s_addr;