From a1d88c4f0ad5c8405f30381319db14afa807719f Mon Sep 17 00:00:00 2001 From: Erik Ekman Date: Wed, 5 Feb 2014 22:36:53 +0100 Subject: [PATCH] IPv6 support for client (#107) The iodine client now supports both IPv4 and IPv6 nameservers for sending DNS queries to an IPv4 iodined. The nameserver will transparently handle translation between IP protocols. Windows port needs Vista or later to support IPv6. --- src/client.c | 77 ++++----------------------------------------------- src/client.h | 2 +- src/common.c | 60 ++++++++++++++++++++++++++++++--------- src/common.h | 6 ++-- src/iodine.c | 24 ++++++++++------ src/iodined.c | 54 +++++++++++++++++++----------------- src/tun.c | 18 ++++++------ src/windows.h | 14 ++++++++-- 8 files changed, 122 insertions(+), 133 deletions(-) diff --git a/src/client.c b/src/client.c index 31f594f..efb0f51 100644 --- a/src/client.c +++ b/src/client.c @@ -29,7 +29,6 @@ #ifdef WINDOWS32 #include "windows.h" -#include #else #ifdef ANDROID #include "android_dns.h" @@ -61,7 +60,7 @@ static void handshake_lazyoff(int dns_fd); static int running; static const char *password; -static struct sockaddr_in nameserv; +static struct sockaddr_storage nameserv; static struct sockaddr_in raw_serv; static const char *topdomain; @@ -149,75 +148,9 @@ client_get_conn() } void -client_set_nameserver(const char *cp, int port) +client_set_nameserver(struct sockaddr_storage *addr, int addrlen) { -#ifdef ANDROID - struct addrinfo hints, *result, *p; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_INET; - - char sport[10]; - sprintf(sport, "%d", port); - - getaddrinfo(cp, sport, &hints, &result); - if (result == NULL) - errx(1, "Cannot resolve %s:%s (no network?)", cp, sport); - else { - for (p = result;p != NULL; p = p->ai_next) { - if (p->ai_family == AF_INET) { /* IPv4 */ - struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; - memset(&nameserv, 0, sizeof(nameserv)); - nameserv.sin_family = AF_INET; - nameserv.sin_port = htons(port); - nameserv.sin_addr = ipv4->sin_addr; - freeaddrinfo(result); - return; - } - } - freeaddrinfo(result); - } -#endif - struct in_addr addr; - - if (inet_aton(cp, &addr) != 1) { - /* try resolving if a domain is given */ - struct hostent *host; - const char *err; - host = gethostbyname(cp); - if (host != NULL && h_errno > 0) { - int i = 0; - while (host->h_addr_list[i] != 0) { - addr = *(struct in_addr *) host->h_addr_list[i++]; - fprintf(stderr, "Resolved %s to %s\n", cp, inet_ntoa(addr)); - goto setaddr; - } - } -#ifndef WINDOWS32 - err = hstrerror(h_errno); -#else - { - DWORD wserr = WSAGetLastError(); - switch (wserr) { - case WSAHOST_NOT_FOUND: - err = "Host not found"; - break; - case WSANO_DATA: - err = "No data record found"; - break; - default: - err = "Unknown error"; - break; - } - } -#endif /* !WINDOWS32 */ - errx(1, "error resolving nameserver '%s': %s", cp, err); - } - -setaddr: - memset(&nameserv, 0, sizeof(nameserv)); - nameserv.sin_family = AF_INET; - nameserv.sin_port = htons(port); - nameserv.sin_addr = addr; + memcpy(&nameserv, addr, addrlen); } void @@ -625,12 +558,12 @@ read_dns_withq(int dns_fd, int tun_fd, char *buf, int buflen, struct query *q) Returns >0 on correct replies; value is #valid bytes in *buf. */ { - struct sockaddr_in from; + struct sockaddr_storage from; char data[64*1024]; socklen_t addrlen; int r; - addrlen = sizeof(struct sockaddr); + addrlen = sizeof(from); if ((r = recvfrom(dns_fd, data, sizeof(data), 0, (struct sockaddr*)&from, &addrlen)) < 0) { warn("recvfrom"); diff --git a/src/client.h b/src/client.h index 16ab0e4..fc5625d 100644 --- a/src/client.h +++ b/src/client.h @@ -23,7 +23,7 @@ void client_stop(); enum connection client_get_conn(); const char *client_get_raw_addr(); -void client_set_nameserver(const char *cp, int port); +void client_set_nameserver(struct sockaddr_storage *, int); void client_set_topdomain(const char *cp); void client_set_password(const char *cp); void set_qtype(char *qtype); diff --git a/src/common.c b/src/common.c index fe88112..19fdb95 100644 --- a/src/common.c +++ b/src/common.c @@ -42,6 +42,8 @@ #include #include #include +#include +#include #endif #ifdef HAVE_SETCON @@ -111,21 +113,40 @@ check_superuser(void (*usage_fn)(void)) #endif } -int -open_dns(int localport, in_addr_t listen_ip) +int +get_addr(char *host, int port, int addr_family, int flags, struct sockaddr_storage *out) +{ + struct addrinfo hints, *addr; + int res; + char portnum[8]; + + memset(portnum, 0, sizeof(portnum)); + snprintf(portnum, sizeof(portnum) - 1, "%d", port); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = addr_family; + hints.ai_flags = AI_ADDRCONFIG | flags; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + + res = getaddrinfo(host, portnum, &hints, &addr); + if (res == 0) { + int addrlen = addr->ai_addrlen; + /* Grab first result */ + memcpy(out, addr->ai_addr, addr->ai_addrlen); + freeaddrinfo(addr); + return addrlen; + } + return res; +} + +int +open_dns(struct sockaddr_storage *sockaddr, size_t sockaddr_len) { - struct sockaddr_in addr; int flag = 1; int fd; - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(localport); - /* listen_ip already in network byte order from inet_addr, or 0 */ - addr.sin_addr.s_addr = listen_ip; - - if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { - fprintf(stderr, "got fd %d\n", fd); + if ((fd = socket(sockaddr->ss_family, SOCK_DGRAM, IPPROTO_UDP)) < 0) { err(1, "socket"); } @@ -146,14 +167,27 @@ open_dns(int localport, in_addr_t listen_ip) setsockopt(fd, IPPROTO_IP, IP_OPT_DONT_FRAG, (const void*) &flag, sizeof(flag)); #endif - if(bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) + if(bind(fd, (struct sockaddr*) sockaddr, sockaddr_len) < 0) err(1, "bind"); - fprintf(stderr, "Opened UDP socket\n"); + fprintf(stderr, "Opened IPv%d UDP socket\n", sockaddr->ss_family == AF_INET6 ? 6 : 4); return fd; } +int +open_dns_from_host(char *host, int port, int addr_family, int flags) +{ + struct sockaddr_storage addr; + int addrlen; + + addrlen = get_addr(host, port, addr_family, flags, &addr); + if (addrlen < 0) + return addrlen; + + return open_dns(&addr, addrlen); +} + void close_dns(int fd) { diff --git a/src/common.h b/src/common.h index 4f8a6fc..9163834 100644 --- a/src/common.h +++ b/src/common.h @@ -93,7 +93,7 @@ struct query { unsigned short rcode; unsigned short id; struct in_addr destination; - struct sockaddr from; + struct sockaddr_storage from; int fromlen; unsigned short id2; struct sockaddr from2; @@ -107,7 +107,9 @@ enum connection { }; void check_superuser(void (*usage_fn)(void)); -int open_dns(int, in_addr_t); +int get_addr(char *, int, int, int, struct sockaddr_storage *); +int open_dns(struct sockaddr_storage *, size_t); +int open_dns_from_host(char *host, int port, int addr_family, int flags); void close_dns(int); void do_chroot(char *); diff --git a/src/iodine.c b/src/iodine.c index 93d55f0..eb4185e 100644 --- a/src/iodine.c +++ b/src/iodine.c @@ -32,6 +32,7 @@ #else #include #include +#include #endif #include "common.h" @@ -111,7 +112,7 @@ version() { int main(int argc, char **argv) { - char *nameserv_addr; + char *nameserv_host; char *topdomain; #ifndef WINDOWS32 struct passwd *pw; @@ -134,8 +135,10 @@ main(int argc, char **argv) int selecttimeout; int hostname_maxlen; int rtable = 0; + struct sockaddr_storage nameservaddr; + int nameservaddr_len; - nameserv_addr = NULL; + nameserv_host = NULL; topdomain = NULL; #ifndef WINDOWS32 pw = NULL; @@ -257,11 +260,11 @@ main(int argc, char **argv) switch (argc) { case 1: - nameserv_addr = get_resolvconf_addr(); + nameserv_host = get_resolvconf_addr(); topdomain = strdup(argv[0]); break; case 2: - nameserv_addr = argv[0]; + nameserv_host = argv[0]; topdomain = strdup(argv[1]); break; default: @@ -275,8 +278,13 @@ main(int argc, char **argv) /* NOTREACHED */ } - if (nameserv_addr) { - client_set_nameserver(nameserv_addr, DNS_PORT); + if (nameserv_host) { + nameservaddr_len = get_addr(nameserv_host, DNS_PORT, AF_UNSPEC, 0, &nameservaddr); + if (nameservaddr_len < 0) { + errx(1, "Cannot lookup nameserver '%s': %s ", + nameserv_host, gai_strerror(nameservaddr_len)); + } + client_set_nameserver(&nameservaddr, nameservaddr_len); } else { warnx("No nameserver found - not connected to any network?\n"); usage(); @@ -323,7 +331,7 @@ main(int argc, char **argv) retval = 1; goto cleanup1; } - if ((dns_fd = open_dns(0, INADDR_ANY)) == -1) { + if ((dns_fd = open_dns_from_host(NULL, 53, nameservaddr.ss_family, AI_PASSIVE)) < 0) { retval = 1; goto cleanup2; } @@ -338,7 +346,7 @@ main(int argc, char **argv) signal(SIGTERM, sighandler); fprintf(stderr, "Sending DNS queries for %s to %s\n", - topdomain, nameserv_addr); + topdomain, nameserv_host); if (client_handshake(dns_fd, raw_mode, autodetect_frag_size, max_downstream_frag_size)) { retval = 1; diff --git a/src/iodined.c b/src/iodined.c index b39c896..d83893c 100644 --- a/src/iodined.c +++ b/src/iodined.c @@ -221,7 +221,7 @@ send_raw(int fd, char *buf, int buflen, int user, int cmd, struct query *q) inet_ntoa(tempin->sin_addr), cmd, len); } - sendto(fd, packet, len, 0, &q->from, q->fromlen); + sendto(fd, packet, len, 0, (struct sockaddr *) &q->from, q->fromlen); } @@ -2241,7 +2241,7 @@ int main(int argc, char **argv) { extern char *__progname; - in_addr_t listen_ip; + char *listen_ip; #ifndef WINDOWS32 struct passwd *pw; #endif @@ -2267,6 +2267,8 @@ main(int argc, char **argv) int ns_get_externalip; int retval; int max_idle_time = 0; + struct sockaddr_storage dnsaddr; + int dnsaddr_len; #ifdef HAVE_SYSTEMD int nb_fds; #endif @@ -2283,7 +2285,7 @@ main(int argc, char **argv) bind_fd = 0; mtu = 1130; /* Very many relays give fragsize 1150 or slightly higher for NULL; tun/zlib adds ~17 bytes. */ - listen_ip = INADDR_ANY; + listen_ip = NULL; port = 53; ns_ip = INADDR_ANY; ns_get_externalip = 0; @@ -2349,7 +2351,7 @@ main(int argc, char **argv) mtu = atoi(optarg); break; case 'l': - listen_ip = inet_addr(optarg); + listen_ip = optarg; break; case 'p': port = atoi(optarg); @@ -2439,23 +2441,6 @@ main(int argc, char **argv) usage(); } - if(bind_enable) { - if (bind_port < 1 || bind_port > 65535) { - warnx("Bad DNS server port number given."); - usage(); - /* NOTREACHED */ - } - /* Avoid forwarding loops */ - if (bind_port == port && (listen_ip == INADDR_ANY || listen_ip == htonl(0x7f000001L))) { - warnx("Forward port is same as listen port (%d), will create a loop!", bind_port); - fprintf(stderr, "Use -l to set listen ip to avoid this.\n"); - usage(); - /* NOTREACHED */ - } - fprintf(stderr, "Requests for domains outside of %s will be forwarded to port %d\n", - topdomain, bind_port); - } - if (port != 53) { fprintf(stderr, "ALERT! Other dns servers expect you to run on port 53.\n"); fprintf(stderr, "You must manually forward port 53 to port %d for things to work.\n", port); @@ -2467,11 +2452,30 @@ main(int argc, char **argv) foreground = 1; } - if (listen_ip == INADDR_NONE) { + dnsaddr_len = get_addr(listen_ip, port, AF_INET, AI_PASSIVE | AI_NUMERICHOST, &dnsaddr); + if (dnsaddr_len < 0) { warnx("Bad IP address to listen on."); usage(); } - + + if(bind_enable) { + in_addr_t dns_ip = ((struct sockaddr_in *) &dnsaddr)->sin_addr.s_addr; + if (bind_port < 1 || bind_port > 65535) { + warnx("Bad DNS server port number given."); + usage(); + /* NOTREACHED */ + } + /* Avoid forwarding loops */ + if (bind_port == port && (dns_ip == INADDR_ANY || dns_ip == htonl(0x7f000001L))) { + warnx("Forward port is same as listen port (%d), will create a loop!", bind_port); + fprintf(stderr, "Use -l to set listen ip to avoid this.\n"); + usage(); + /* NOTREACHED */ + } + fprintf(stderr, "Requests for domains outside of %s will be forwarded to port %d\n", + topdomain, bind_port); + } + if (ns_get_externalip) { struct in_addr extip; int res = get_external_ip(&extip); @@ -2524,7 +2528,7 @@ main(int argc, char **argv) dnsd_fd = SD_LISTEN_FDS_START; } else { #endif - if ((dnsd_fd = open_dns(port, listen_ip)) == -1) { + if ((dnsd_fd = open_dns(&dnsaddr, dnsaddr_len)) < 0) { retval = 1; goto cleanup2; } @@ -2532,7 +2536,7 @@ main(int argc, char **argv) } #endif if (bind_enable) { - if ((bind_fd = open_dns(0, INADDR_ANY)) == -1) { + if ((bind_fd = open_dns_from_host(NULL, 0, AF_INET, 0)) < 0) { retval = 1; goto cleanup3; } diff --git a/src/tun.c b/src/tun.c index 734557b..301e08d 100644 --- a/src/tun.c +++ b/src/tun.c @@ -29,9 +29,8 @@ #endif #ifdef WINDOWS32 -#include -#include #include "windows.h" +#include HANDLE dev_handle; struct tun_data data; @@ -300,7 +299,7 @@ DWORD WINAPI tun_reader(LPVOID arg) WaitForSingleObject(olpd.hEvent, INFINITE); res = GetOverlappedResult(dev_handle, &olpd, (LPDWORD) &len, FALSE); res = sendto(sock, buf, len, 0, (struct sockaddr*) &(tun->addr), - sizeof(struct sockaddr_in)); + tun->addrlen); } } @@ -313,7 +312,8 @@ open_tun(const char *tun_device) char adapter[256]; char tapfile[512]; int tunfd; - in_addr_t local; + struct sockaddr_storage localsock; + int localsock_len; memset(adapter, 0, sizeof(adapter)); memset(if_name, 0, sizeof(if_name)); @@ -341,14 +341,12 @@ open_tun(const char *tun_device) * A thread does blocking reads on tun device and * sends data as udp to this socket */ - local = htonl(0x7f000001); /* 127.0.0.1 */ - tunfd = open_dns(55353, local); + localsock_len = get_addr("127.0.0.1", 55353, AF_INET, 0, &localsock); + tunfd = open_dns(&localsock, localsock_len); data.tun = dev_handle; - memset(&(data.addr), 0, sizeof(data.addr)); - data.addr.sin_family = AF_INET; - data.addr.sin_port = htons(55353); - data.addr.sin_addr.s_addr = local; + memcpy(&(data.addr), &localsock, localsock_len); + data.addrlen = localsock_len; CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)tun_reader, &data, 0, NULL); return tunfd; diff --git a/src/windows.h b/src/windows.h index 7e0e16c..a002b55 100644 --- a/src/windows.h +++ b/src/windows.h @@ -17,11 +17,20 @@ #ifndef __FIX_WINDOWS_H__ #define __FIX_WINDOWS_H__ +#include +/* Need Vista or Later to get IPv6 support */ +#undef WINVER +#undef _WIN32_WINDOWS +#undef _WIN32_WINNT +#define WINVER WindowsVista +#define _WIN32_WINDOWS WindowsVista +#define _WIN32_WINNT WindowsVista + typedef unsigned int in_addr_t; +#include #include #include -#include #include #include @@ -94,7 +103,8 @@ DWORD WINAPI tun_reader(LPVOID arg); struct tun_data { HANDLE tun; int sock; - struct sockaddr_in addr; + struct sockaddr_storage addr; + int addrlen; }; #endif