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.
- 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)

View File

@ -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

View File

@ -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.

View File

@ -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;