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.
This commit is contained in:
Erik Ekman 2014-02-05 22:36:53 +01:00
parent 8baad91156
commit a1d88c4f0a
8 changed files with 122 additions and 133 deletions

View File

@ -29,7 +29,6 @@
#ifdef WINDOWS32
#include "windows.h"
#include <winsock2.h>
#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");

View File

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

View File

@ -42,6 +42,8 @@
#include <netinet/in.h>
#include <arpa/inet.h>
#include <syslog.h>
#include <sys/socket.h>
#include <netdb.h>
#endif
#ifdef HAVE_SETCON
@ -112,20 +114,39 @@ check_superuser(void (*usage_fn)(void))
}
int
open_dns(int localport, in_addr_t listen_ip)
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)
{

View File

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

View File

@ -32,6 +32,7 @@
#else
#include <grp.h>
#include <pwd.h>
#include <netdb.h>
#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;

View File

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

View File

@ -29,9 +29,8 @@
#endif
#ifdef WINDOWS32
#include <winsock2.h>
#include <winioctl.h>
#include "windows.h"
#include <winioctl.h>
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;

View File

@ -17,11 +17,20 @@
#ifndef __FIX_WINDOWS_H__
#define __FIX_WINDOWS_H__
#include <w32api.h>
/* 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 <winsock2.h>
#include <windows.h>
#include <windns.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
@ -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