From e45bdece8eaea3e5023e52bb6460e4d0df07a4d5 Mon Sep 17 00:00:00 2001 From: Erik Ekman Date: Wed, 6 Aug 2008 20:26:36 +0000 Subject: [PATCH] Tagged iodine 0.4.2 --- CHANGELOG | 17 ++++ Makefile | 2 +- man/iodine.8 | 22 +++-- src/Makefile | 4 +- src/common.c | 58 ++++++++++++ src/common.h | 2 + src/dns.c | 10 +- src/iodine.c | 36 +++++-- src/iodined.c | 237 +++++++++++++++++++++++++++++++++-------------- src/osflags | 14 +++ src/user.h | 3 +- tests/Makefile | 2 +- tests/base32.c | 10 +- tests/base64.c | 14 +-- tests/dns.c | 16 ++-- tests/encoding.c | 8 +- tests/read.c | 52 +++++------ tests/test.c | 16 +--- 18 files changed, 362 insertions(+), 161 deletions(-) create mode 100644 src/osflags diff --git a/CHANGELOG b/CHANGELOG index 14ad313..15623dc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -5,6 +5,23 @@ iodine - http://code.kryo.se/iodine CHANGES: +2008-08-06: 0.4.2 "Opened Zone" + - Applied a few small patches from Maxim Bourmistrov and Gregor Herrmann + - Applied a patch for not creating and configuring the tun interface, + Debian bug #477692 by Vincent Bernat, controlled by -s switch + - Applied a security patch from Andrew Griffiths, use setgroups() to + limit the groups of the user + - Applied a patch to make iodine build on (Open)Solaris, from Albert Lee + Needs TUN/TAP driver: http://www.whiteboard.ne.jp/~admin2/tuntap/ + Still needs some more code in tun.c for opening/closing the device + - Added option in server (-c) to disable IP/port checking on each packet, + will hopefully help when server is behind NAT + - Fixed bug #21, now only IP address part of each packet is checked. + Should remove the need for the -c option and also work with + bugfixed DNS servers worldwide. + - Added -D option on server to enable debugging. Debug level 1 now prints + info about each RX/TX datagram. + 2007-11-30: 0.4.1 "Tea Online" - Introduced encoding API - Switched to new Base32 implementation diff --git a/Makefile b/Makefile index 7516942..2f2bd10 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ uninstall: test: all @echo "!! The check library is required for compiling and running the tests" @echo "!! Get it at http://check.sf.net" - @(cd tests; make all) + @(cd tests; $(MAKE) all) clean: @echo "Cleaning..." diff --git a/man/iodine.8 b/man/iodine.8 index 3720a50..fdb28fe 100644 --- a/man/iodine.8 +++ b/man/iodine.8 @@ -1,5 +1,5 @@ .\" groff -man -Tascii iodine.8 -.TH IODINE 8 "JUN 2007" "User Manuals" +.TH IODINE 8 "JUL 2008" "User Manuals" .SH NAME iodine, iodined \- tunnel IPv4 over DNS .SH SYNOPSIS @@ -25,7 +25,7 @@ iodine, iodined \- tunnel IPv4 over DNS .B iodined [-h] -.B iodined [-f] [-u +.B iodined [-c] [-s] [-f] [-D] [-u .I user .B ] [-P .I password @@ -78,6 +78,16 @@ Use the TUN device 'device' instead of the normal one, which is dnsX on Linux and otherwise tunX. .SS Server Options: .TP +.B -c +Disable checks on client IP on all incoming requests. +.TP +.B -s +Don't try to configure IP address or MTU. This should only be used if +you have already configured the device that will be used. +.TP +.B -D +Increase debug level. Level 1 prints info about each RX/TX packet. +.TP .B -m mtu Set 'mtu' as mtu size for the tunnel device. This will be sent to the client on connect, and the client will use the same mtu. @@ -124,13 +134,13 @@ the same on both the client and the server. .TP Try it out within your own LAN! Follow these simple steps: .TP -- On your server, run: ./iodined -f 10.0.0.1 test.asdf +- On your server, run: ./iodined \-f 10.0.0.1 test.asdf (If you already use the 10.0.0.0 network, use another internal net like 172.16.0.0) .TP - Enter a password .TP -- On the client, run: ./iodine -f 192.168.0.1 test.asdf +- On the client, run: ./iodine \-f 192.168.0.1 test.asdf (Replace 192.168.0.1 with the server's ip address) .TP - Enter the same password @@ -160,10 +170,10 @@ tunnel1 IN NS tunnel1host.mytunnel.com. Now any DNS querys for domains ending with tunnel1.mytunnnel.com will be sent to your server. Start iodined on the server. The first argument is the tunnel IP address (like 192.168.99.1) and the second is the assigned domain (in this -case tunnel1.mytunnel.com). The -f argument will keep iodined running in the +case tunnel1.mytunnel.com). The \-f argument will keep iodined running in the foreground, which helps when testing. iodined will start a virtual interface, and also start listening for DNS queries on UDP port 53. Either enter a -password on the commandline (-P pass) or after the server has started. Now +password on the commandline (\-P pass) or after the server has started. Now everything is ready for the client. .TP .B Client side: diff --git a/src/Makefile b/src/Makefile index 958aa70..c397f1d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -8,10 +8,10 @@ SERVER = ../bin/iodined OS = `uname | tr "a-z" "A-Z"` ARCH = `uname -m` -LDFLAGS = -lz +LDFLAGS = -lz `sh osflags link` CFLAGS = -c -g -Wall -D$(OS) -pedantic -all: stateos $(CLIENT) $(SERVER) $(TESTSUITE) +all: stateos $(CLIENT) $(SERVER) stateos: @echo OS is $(OS), arch is $(ARCH) diff --git a/src/common.c b/src/common.c index 73cf24d..f738ce5 100644 --- a/src/common.c +++ b/src/common.c @@ -1,4 +1,5 @@ /* Copyright (c) 2006-2007 Bjorn Andersson , Erik Ekman + * Copyright (c) 2007 Albert Lee . * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -21,6 +22,8 @@ #endif #include #include +#include +#include #include #include #include @@ -29,10 +32,48 @@ #include #include #include +#include #include #include "common.h" +/* daemon(3) exists only in 4.4BSD or later, and in GNU libc */ +#if !(defined(BSD) && (BSD >= 199306)) && !defined(__GLIBC__) +static int daemon(int nochdir, int noclose) +{ + int fd, i; + + switch (fork()) { + case 0: + break; + case -1: + return -1; + default: + _exit(0); + } + + if (!nochdir) { + chdir("/"); + } + + if (setsid() < 0) { + return -1; + } + + if (!noclose) { + if ((fd = open("/dev/null", O_RDWR)) >= 0) { + for (i = 0; i < 3; i++) { + dup2(fd, i); + } + if (fd > 2) { + close(fd); + } + } + } + return 0; +} +#endif + int open_dns(int localport, in_addr_t listen_ip) { @@ -111,3 +152,20 @@ read_password(char *buf, size_t len) strncpy(buf, pwd, len); buf[len-1] = '\0'; } + +int +check_topdomain(char *str) +{ + int i; + + if(str[0] == '.') /* special case */ + return 1; + + for( i = 0; i < strlen(str); i++) { + if( isalpha(str[i]) || isdigit(str[i]) || str[i] == '-' || str[i] == '.' ) + continue; + else + return 1; + } + return 0; +} diff --git a/src/common.h b/src/common.h index 811aa40..255ff24 100644 --- a/src/common.h +++ b/src/common.h @@ -54,4 +54,6 @@ void do_detach(); void read_password(char*, size_t); +int check_topdomain(char *); + #endif diff --git a/src/dns.c b/src/dns.c index 1b6dd60..5ad4c07 100644 --- a/src/dns.c +++ b/src/dns.c @@ -150,14 +150,13 @@ dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, siz warnx("Got NXDOMAIN as reply"); break; - case SERVFAIL: warnx("Got SERVFAIL as reply"); break; case NOERROR: default: - warnx("no query or answer in answer"); + warnx("no query or answer in reply packet"); break; } return -1; @@ -178,7 +177,7 @@ dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, siz rv = MIN(rlen, sizeof(rdata)); rv = readdata(packet, &data, rdata, rv); - if(type == T_NULL && rv > 2) { + if(type == T_NULL && rv > 2 && buf) { rv = MIN(rv, buflen); memcpy(buf, rdata, rv); } @@ -194,11 +193,6 @@ dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, siz readshort(packet, &data, &type); readshort(packet, &data, &class); - if(type != T_NULL) { - rv = 0; - break; - } - strncpy(q->name, name, sizeof(q->name)); q->name[sizeof(q->name) - 1] = '\0'; q->type = type; diff --git a/src/iodine.c b/src/iodine.c index 3b2cc61..5ec13bb 100644 --- a/src/iodine.c +++ b/src/iodine.c @@ -23,10 +23,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -76,6 +78,10 @@ static struct encoder *dataenc; /* result of case preservation check done after login */ static int case_preserved; +#if !defined(BSD) && !defined(__GLIBC__) +static char *__progname; +#endif + static void sighandler(int sig) { @@ -396,8 +402,11 @@ handshake(int dns_fd) if(r > 0) { read = read_dns(dns_fd, in, sizeof(in)); - if(read < 0) { - warn("handshake read"); + if(read <= 0) { + if (read == 0) { + warn("handshake read"); + } + /* if read < 0 then warning has been printed already */ continue; } @@ -602,7 +611,7 @@ static void version() { printf("iodine IP over DNS tunneling client\n"); - printf("version: 0.4.1 from 2007-11-30\n"); + printf("version: 0.4.2 from 2008-08-06\n"); exit(0); } @@ -630,6 +639,14 @@ main(int argc, char **argv) b32 = get_base32_encoder(); dataenc = get_base32_encoder(); +#if !defined(BSD) && !defined(__GLIBC__) + __progname = strrchr(argv[0], '/'); + if (__progname == NULL) + __progname = argv[0]; + else + __progname++; +#endif + while ((choice = getopt(argc, argv, "vfhu:t:d:P:")) != -1) { switch(choice) { case 'v': @@ -687,8 +704,13 @@ main(int argc, char **argv) set_nameserver(nameserv_addr); - if (strlen(topdomain) > 128 || topdomain[0] == '.') { - warnx("Use a topdomain max 128 chars long. Do not start it with a dot.\n"); + if(strlen(topdomain) <= 128) { + if(check_topdomain(topdomain)) { + warnx("Topdomain contains invalid characters.\n"); + usage(); + } + } else { + warnx("Use a topdomain max 128 chars long.\n"); usage(); } @@ -722,7 +744,9 @@ main(int argc, char **argv) do_chroot(newroot); if (username != NULL) { - if (setgid(pw->pw_gid) < 0 || setuid(pw->pw_uid) < 0) { + gid_t gids[1]; + gids[0] = pw->pw_gid; + if (setgroups(1, gids) < 0 || setgid(pw->pw_gid) < 0 || setuid(pw->pw_uid) < 0) { warnx("Could not switch to user %s!\n", username); usage(); } diff --git a/src/iodined.c b/src/iodined.c index 17f27cc..d780d19 100644 --- a/src/iodined.c +++ b/src/iodined.c @@ -21,10 +21,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -32,6 +34,10 @@ #include #include #include +#include +#ifdef DARWIN +#include +#endif #include "common.h" #include "dns.h" @@ -47,10 +53,17 @@ static char *topdomain; static char password[33]; static struct encoder *b32; +static int check_ip; static int my_mtu; static in_addr_t my_ip; -static int read_dns(int, struct query *, char *, int); +static int debug; + +#if !defined(BSD) && !defined(__GLIBC__) +static char *__progname; +#endif + +static int read_dns(int, struct query *); static void write_dns(int, struct query *, char *, int); static void @@ -59,6 +72,15 @@ sigint(int sig) running = 0; } +static int +ip_cmp(int userid, struct query *q) +{ + struct sockaddr_in *tempin; + + tempin = (struct sockaddr_in *) &(q->from); + return memcmp(&(users[userid].host), &(tempin->sin_addr), sizeof(struct in_addr)); +} + static int tunnel_tun(int tun_fd, int dns_fd) { @@ -118,34 +140,44 @@ send_version_response(int fd, version_ack_t ack, uint32_t payload, struct user * out[5] = ((payload >> 16) & 0xff); out[6] = ((payload >> 8) & 0xff); out[7] = ((payload) & 0xff); - out[8] = u->id; + if (u) { + out[8] = u->id; + } else { + out[8] = 0; + } write_dns(fd, &u->q, out, sizeof(out)); } -static int -tunnel_dns(int tun_fd, int dns_fd) +static void +handle_null_request(int tun_fd, int dns_fd, struct query *q) { struct in_addr tempip; - struct user dummy; struct ip *hdr; unsigned long outlen; + char in[64*1024]; char logindata[16]; char out[64*1024]; - char in[64*1024]; char unpacked[64*1024]; char *tmp[2]; + char *domain; int userid; int touser; int version; - int read; int code; + int read; userid = -1; - if ((read = read_dns(dns_fd, &(dummy.q), in, sizeof(in))) <= 0) - return 0; - + domain = strstr(q->name, topdomain); + if (!domain) { + /* Not for us, discard */ + return; + } + + read = (int) (domain - q->name); + memcpy(in, q->name, MIN(read, sizeof(in))); + if(in[0] == 'V' || in[0] == 'v') { read = unpack_data(unpacked, sizeof(unpacked), &(in[1]), read - 1, b32); /* Version greeting, compare and send ack/nak */ @@ -160,34 +192,37 @@ tunnel_dns(int tun_fd, int dns_fd) if (version == VERSION) { userid = find_available_user(); if (userid >= 0) { + struct sockaddr_in *tempin; + users[userid].seed = rand(); - memcpy(&(users[userid].host), &(dummy.q.from), dummy.q.fromlen); - memcpy(&(users[userid].q), &(dummy.q), sizeof(struct query)); - users[userid].addrlen = dummy.q.fromlen; + /* Store remote IP number */ + tempin = (struct sockaddr_in *) &(q->from); + memcpy(&(users[userid].host), &(tempin->sin_addr), sizeof(struct in_addr)); + + memcpy(&(users[userid].q), q, sizeof(struct query)); users[userid].encoder = get_base32_encoder(); send_version_response(dns_fd, VERSION_ACK, users[userid].seed, &users[userid]); users[userid].q.id = 0; } else { /* No space for another user */ - send_version_response(dns_fd, VERSION_FULL, USERS, &dummy); + send_version_response(dns_fd, VERSION_FULL, USERS, NULL); } } else { - send_version_response(dns_fd, VERSION_NACK, VERSION, &dummy); + send_version_response(dns_fd, VERSION_NACK, VERSION, NULL); } } else if(in[0] == 'L' || in[0] == 'l') { read = unpack_data(unpacked, sizeof(unpacked), &(in[1]), read - 1, b32); /* Login phase, handle auth */ userid = unpacked[0]; if (userid < 0 || userid >= USERS) { - write_dns(dns_fd, &(dummy.q), "BADIP", 5); - return 0; /* illegal id */ + write_dns(dns_fd, q, "BADIP", 5); + return; /* illegal id */ } users[userid].last_pkt = time(NULL); login_calculate(logindata, 16, password, users[userid].seed); - if (dummy.q.fromlen != users[userid].addrlen || - memcmp(&(users[userid].host), &(dummy.q.from), dummy.q.fromlen) != 0) { - write_dns(dns_fd, &(dummy.q), "BADIP", 5); + if (check_ip && ip_cmp(userid, q) != 0) { + write_dns(dns_fd, q, "BADIP", 5); } else { if (read >= 18 && (memcmp(logindata, unpacked+1, 16) == 0)) { /* Login ok, send ip/mtu info */ @@ -200,31 +235,31 @@ tunnel_dns(int tun_fd, int dns_fd) read = snprintf(out, sizeof(out), "%s-%s-%d", tmp[0], tmp[1], my_mtu); - write_dns(dns_fd, &(dummy.q), out, read); - dummy.q.id = 0; + write_dns(dns_fd, q, out, read); + q->id = 0; free(tmp[1]); free(tmp[0]); } else { - write_dns(dns_fd, &(dummy.q), "LNAK", 4); + write_dns(dns_fd, q, "LNAK", 4); } } } else if(in[0] == 'P' || in[0] == 'p') { read = unpack_data(unpacked, sizeof(unpacked), &(in[1]), read - 1, b32); /* Ping packet, store userid */ userid = unpacked[0]; - if (userid < 0 || userid >= USERS) { - write_dns(dns_fd, &(dummy.q), "BADIP", 5); - return 0; /* illegal id */ + if (userid < 0 || userid >= USERS || ip_cmp(userid, q) != 0) { + write_dns(dns_fd, q, "BADIP", 5); + return; /* illegal id */ } - memcpy(&(users[userid].q), &(dummy.q), sizeof(struct query)); + memcpy(&(users[userid].q), q, sizeof(struct query)); users[userid].last_pkt = time(NULL); } else if(in[0] == 'Z' || in[0] == 'z') { /* Case conservation check */ /* Reply with received hostname as data */ - write_dns(dns_fd, &(dummy.q), in, read); - return 0; + write_dns(dns_fd, q, in, read); + return; } else if((in[0] >= '0' && in[0] <= '9') || (in[0] >= 'a' && in[0] <= 'f') || (in[0] >= 'A' && in[0] <= 'F')) { @@ -237,22 +272,20 @@ tunnel_dns(int tun_fd, int dns_fd) userid = code >> 1; if (userid < 0 || userid >= USERS) { - write_dns(dns_fd, &(dummy.q), "BADIP", 5); - return 0; /* illegal id */ + write_dns(dns_fd, q, "BADIP", 5); + return; /* illegal id */ } /* Check sending ip number */ - if (dummy.q.fromlen != users[userid].addrlen || - memcmp(&(users[userid].host), &(dummy.q.from), dummy.q.fromlen) != 0) { - write_dns(dns_fd, &(dummy.q), "BADIP", 5); + if (check_ip && ip_cmp(userid, q) != 0) { + write_dns(dns_fd, q, "BADIP", 5); } else { /* decode with this users encoding */ read = unpack_data(unpacked, sizeof(unpacked), &(in[1]), read - 1, users[userid].encoder); users[userid].last_pkt = time(NULL); - memcpy(&(users[userid].q), &(dummy.q), sizeof(struct query)); - users[userid].addrlen = dummy.q.fromlen; + memcpy(&(users[userid].q), q, sizeof(struct query)); memcpy(users[userid].inpacket.data + users[userid].inpacket.offset, unpacked, read); users[userid].inpacket.len += read; users[userid].inpacket.offset += read; @@ -281,14 +314,35 @@ tunnel_dns(int tun_fd, int dns_fd) } } /* userid must be set for a reply to be sent */ - if (userid >= 0 && userid < USERS && dummy.q.fromlen == users[userid].addrlen && - memcmp(&(users[userid].host), &(dummy.q.from), dummy.q.fromlen) == 0 && - users[userid].outpacket.len > 0) { - - write_dns(dns_fd, &(dummy.q), users[userid].outpacket.data, users[userid].outpacket.len); + if (userid >= 0 && userid < USERS && ip_cmp(userid, q) == 0 && users[userid].outpacket.len > 0) { + write_dns(dns_fd, q, users[userid].outpacket.data, users[userid].outpacket.len); users[userid].outpacket.len = 0; users[userid].q.id = 0; } +} + +static int +tunnel_dns(int tun_fd, int dns_fd) +{ + struct query q; + int read; + + if ((read = read_dns(dns_fd, &q)) <= 0) + return 0; + + if (debug >= 1) { + struct sockaddr_in *tempin; + tempin = (struct sockaddr_in *) &(q.from); + printf("RX: client %s, type %d, name %s\n", inet_ntoa(tempin->sin_addr), q.type, q.name); + } + + switch (q.type) { + case T_NULL: + handle_null_request(tun_fd, dns_fd, &q); + break; + default: + break; + } return 0; } @@ -349,36 +403,28 @@ tunnel(int tun_fd, int dns_fd) } static int -read_dns(int fd, struct query *q, char *buf, int buflen) +read_dns(int fd, struct query *q) { struct sockaddr_in from; char packet[64*1024]; - char *domain; socklen_t addrlen; - int rv; int r; addrlen = sizeof(struct sockaddr); r = recvfrom(fd, packet, sizeof(packet), 0, (struct sockaddr*)&from, &addrlen); if (r > 0) { - dns_decode(buf, buflen, q, QR_QUERY, packet, r); - domain = strstr(q->name, topdomain); - if (domain) { - rv = (int) (domain - q->name); - memcpy(buf, q->name, MIN(rv, buflen)); - q->fromlen = addrlen; - memcpy((struct sockaddr*)&q->from, (struct sockaddr*)&from, addrlen); - } else { - rv = 0; - } + dns_decode(NULL, 0, q, QR_QUERY, packet, r); + memcpy((struct sockaddr*)&q->from, (struct sockaddr*)&from, addrlen); + q->fromlen = addrlen; + + return strlen(q->name); } else if (r < 0) { /* Error */ warn("read dns"); - rv = 0; } - return rv; + return 0; } static void @@ -389,6 +435,13 @@ write_dns(int fd, struct query *q, char *data, int datalen) len = dns_encode(buf, sizeof(buf), q, QR_ANSWER, data, datalen); + if (debug >= 1) { + struct sockaddr_in *tempin; + tempin = (struct sockaddr_in *) &(q->from); + printf("TX: client %s, type %d, name %s, %d bytes data\n", + inet_ntoa(tempin->sin_addr), q->type, q->name, datalen); + } + sendto(fd, buf, len, 0, (struct sockaddr*)&q->from, q->fromlen); } @@ -396,7 +449,7 @@ static void usage() { extern char *__progname; - printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] [-m mtu] " + printf("Usage: %s [-v] [-h] [-c] [-s] [-f] [-D] [-u user] [-t chrootdir] [-d device] [-m mtu] " "[-l ip address to listen on] [-p port] [-P password]" " tunnel_ip topdomain\n", __progname); exit(2); @@ -407,12 +460,15 @@ help() { extern char *__progname; printf("iodine IP over DNS tunneling server\n"); - printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] [-m mtu] " + printf("Usage: %s [-v] [-h] [-c] [-s] [-f] [-D] [-u user] [-t chrootdir] [-d device] [-m mtu] " "[-l ip address to listen on] [-p port] [-P password]" " tunnel_ip topdomain\n", __progname); printf(" -v to print version info and exit\n"); printf(" -h to print this help and exit\n"); + printf(" -c to disable check of client IP/port on each request\n"); + printf(" -s to skip creating and configuring the tun device which then has to be created manually\n"); printf(" -f to keep running in foreground\n"); + printf(" -D to increase debug level\n"); printf(" -u name to drop privileges and run as user 'name'\n"); printf(" -t dir to chroot to directory dir\n"); printf(" -d device to set tunnel device name\n"); @@ -428,7 +484,7 @@ help() { static void version() { printf("iodine IP over DNS tunneling server\n"); - printf("version: 0.4.1 from 2007-11-30\n"); + printf("version: 0.4.2 from 2008-08-06\n"); exit(0); } @@ -446,6 +502,7 @@ main(int argc, char **argv) int choice; int port; int mtu; + int skipipconfig; username = NULL; newroot = NULL; @@ -454,23 +511,43 @@ main(int argc, char **argv) mtu = 1024; listen_ip = INADDR_ANY; port = 53; + check_ip = 1; + skipipconfig = 0; + debug = 0; b32 = get_base32_encoder(); +#if !defined(BSD) && !defined(__GLIBC__) + __progname = strrchr(argv[0], '/'); + if (__progname == NULL) + __progname = argv[0]; + else + __progname++; +#endif + memset(password, 0, sizeof(password)); srand(time(NULL)); - while ((choice = getopt(argc, argv, "vfhu:t:d:m:l:p:P:")) != -1) { + while ((choice = getopt(argc, argv, "vcsfhDu:t:d:m:l:p:P:")) != -1) { switch(choice) { case 'v': version(); break; + case 'c': + check_ip = 0; + break; + case 's': + skipipconfig = 1; + break; case 'f': foreground = 1; break; case 'h': help(); break; + case 'D': + debug++; + break; case 'u': username = optarg; break; @@ -488,10 +565,6 @@ main(int argc, char **argv) break; case 'p': port = atoi(optarg); - if (port) { - printf("ALERT! Other dns servers expect you to run on port 53.\n"); - printf("You must manually forward port 53 to port %d for things to work.\n", port); - } break; case 'P': strncpy(password, optarg, sizeof(password)); @@ -518,8 +591,13 @@ main(int argc, char **argv) usage(); topdomain = strdup(argv[1]); - if (strlen(topdomain) > 128 || topdomain[0] == '.') { - warnx("Use a topdomain max 128 chars long. Do not start it with a dot.\n"); + if(strlen(topdomain) <= 128) { + if(check_topdomain(topdomain)) { + warnx("Topdomain contains invalid characters.\n"); + usage(); + } + } else { + warnx("Use a topdomain max 128 chars long.\n"); usage(); } @@ -530,10 +608,26 @@ main(int argc, char **argv) } } - if (mtu == 0) { + if (mtu <= 0) { warnx("Bad MTU given.\n"); usage(); } + + if(port < 1 || port > 65535) { + warnx("Bad port number given.\n"); + usage(); + } + + if (port != 53) { + printf("ALERT! Other dns servers expect you to run on port 53.\n"); + printf("You must manually forward port 53 to port %d for things to work.\n", port); + } + + if (debug) { + printf("Debug level %d enabled, will stay in foreground.\n", debug); + printf("Add more -D switches to set higher debug level.\n"); + foreground = 1; + } if (listen_ip == INADDR_NONE) { warnx("Bad IP address to listen on.\n"); @@ -545,8 +639,9 @@ main(int argc, char **argv) if ((tun_fd = open_tun(device)) == -1) goto cleanup0; - if (tun_setip(argv[0]) != 0 || tun_setmtu(mtu) != 0) - goto cleanup1; + if (!skipipconfig) + if (tun_setip(argv[0]) != 0 || tun_setmtu(mtu) != 0) + goto cleanup1; if ((dnsd_fd = open_dns(port, listen_ip)) == -1) goto cleanup2; @@ -564,7 +659,9 @@ main(int argc, char **argv) signal(SIGINT, sigint); if (username != NULL) { - if (setgid(pw->pw_gid) < 0 || setuid(pw->pw_uid) < 0) { + gid_t gids[1]; + gids[0] = pw->pw_gid; + if (setgroups(1, gids) < 0 || setgid(pw->pw_gid) < 0 || setuid(pw->pw_uid) < 0) { warnx("Could not switch to user %s!\n", username); usage(); } diff --git a/src/osflags b/src/osflags new file mode 100644 index 0000000..0f8665d --- /dev/null +++ b/src/osflags @@ -0,0 +1,14 @@ +#!/bin/sh + +case $1 in +link) + + case `uname` in + SunOS | solaris) + echo '-lsocket -lnsl'; + ;; + esac + ;; +*) +;; +esac diff --git a/src/user.h b/src/user.h index 2d0d2ad..b333bcb 100644 --- a/src/user.h +++ b/src/user.h @@ -25,8 +25,7 @@ struct user { time_t last_pkt; int seed; in_addr_t tun_ip; - struct sockaddr host; - int addrlen; + struct in_addr host; struct query q; struct packet inpacket; struct packet outpacket; diff --git a/tests/Makefile b/tests/Makefile index 94e808e..ef45783 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -21,6 +21,6 @@ $(TEST): $(OBJS) $(SRCOBJS) clean: - @echo "Cleaning..." + @echo "Cleaning tests/" @rm -f *~ *.core $(TEST) $(OBJS) diff --git a/tests/base32.c b/tests/base32.c index b020cda..849790c 100644 --- a/tests/base32.c +++ b/tests/base32.c @@ -31,7 +31,7 @@ static struct tuple } testpairs[] = { { "iodinetestingtesting", "nfxwi0lomv0gk21unfxgo3dfon0gs1th" }, { "abc123", "mfrggmjsgm" }, - { NULL, NULL } + { NULL, NULL } }; START_TEST(test_base32_encode) @@ -46,8 +46,8 @@ START_TEST(test_base32_encode) val = base32_encode(buf, &len, testpairs[i].a, strlen(testpairs[i].a)); fail_unless(val > 0, strerror(errno)); - fail_unless(strcmp(buf, testpairs[i].b) == 0, - va_str("'%s' != '%s'", buf, testpairs[i].b)); + fail_unless(strcmp(buf, testpairs[i].b) == 0, + "'%s' != '%s'", buf, testpairs[i].b); } } END_TEST @@ -65,8 +65,8 @@ START_TEST(test_base32_decode) fail_unless(val > 0, strerror(errno)); fail_unless(buf != NULL, "buf == NULL"); - fail_unless(strcmp(buf, testpairs[i].a) == 0, - va_str("'%s' != '%s'", buf, testpairs[i].a)); + fail_unless(strcmp(buf, testpairs[i].a) == 0, + "'%s' != '%s'", buf, testpairs[i].a); } } END_TEST diff --git a/tests/base64.c b/tests/base64.c index 7f12bf4..4516879 100644 --- a/tests/base64.c +++ b/tests/base64.c @@ -31,7 +31,7 @@ static struct tuple } testpairs[] = { { "iodinetestingtesting", "Aw8KAw4LDgvZDgLUz2rLC2rPBMC" }, { "abc123", "ywjJmtiZ" }, - { + { "\xFF\xEF\x7C\xEF\xAE\x78\xDF\x6D\x74\xCF\x2C\x70\xBE\xEB\x6C\xAE\xAA\x68" "\x9E\x69\x64\x8E\x28\x60\x7D\xE7\x5C\x6D\xA6\x58\x5D\x65\x54\x4D\x24\x50" "\x3C\xE3\x4C\x2C\xA2\x48\x1C\x61\x44\x0C\x20\x40\x3F\x3F\x3C\xEF\xAE\x78" @@ -45,7 +45,7 @@ static struct tuple "776543210-ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba9abba87654" "3210-ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfe999dcba" }, - { + { "\xFF\xEF\x7C\xEF\xAE\x78\xDF\x6D\x74\xCF\x2C\x70\xBE\xEB\x6C\xAE\xAA\x68" "\x9E\x69\x64\x8E\x28\x60\x7D\xE7\x5C\x6D\xA6\x58\x5D\x65\x54\x4D\x24\x50" "\x3C\xE3\x4C\x2C\xA2\x48\x1C\x61\x44\x0C\x20\x40\x3F\x3F\x3C\xEF\xAE\x78" @@ -59,7 +59,7 @@ static struct tuple "776543210-ZYXWVUTSRQfHKwfHGsHGFEDCBAzyxwvutsrqponmlkjihgfedcbaML87654321" "0-ZYXWVUTSRQfHKwfHGsHGFEDCBAzyxwvutsrqponmlkjihgfedcba" }, - { NULL, NULL } + { NULL, NULL } }; START_TEST(test_base64_encode) @@ -74,8 +74,8 @@ START_TEST(test_base64_encode) val = base64_encode(buf, &len, testpairs[i].a, strlen(testpairs[i].a)); fail_unless(val > 0, strerror(errno)); - fail_unless(strcmp(buf, testpairs[i].b) == 0, - va_str("'%s' != '%s'", buf, testpairs[i].b)); + fail_unless(strcmp(buf, testpairs[i].b) == 0, + "'%s' != '%s'", buf, testpairs[i].b); } } END_TEST @@ -93,8 +93,8 @@ START_TEST(test_base64_decode) fail_unless(val > 0, strerror(errno)); fail_unless(buf != NULL, "buf == NULL"); - fail_unless(strcmp(buf, testpairs[i].a) == 0, - va_str("'%s' != '%s'", buf, testpairs[i].a)); + fail_unless(strcmp(buf, testpairs[i].a) == 0, + "'%s' != '%s'", buf, testpairs[i].a); } } END_TEST diff --git a/tests/dns.c b/tests/dns.c index 1311d33..0ccf232 100644 --- a/tests/dns.c +++ b/tests/dns.c @@ -33,24 +33,24 @@ static void dump_packet(char *, size_t); -static char queryPacket[] = +static char queryPacket[] = "\x05\x39\x01\x00\x00\x01\x00\x00\x00\x00\x00\x01\x2D\x41\x6A\x62\x63" "\x75\x79\x74\x63\x70\x65\x62\x30\x67\x71\x30\x6C\x74\x65\x62\x75\x78" "\x67\x69\x64\x75\x6E\x62\x73\x73\x61\x33\x64\x66\x6F\x6E\x30\x63\x61" "\x7A\x64\x62\x6F\x72\x71\x71\x04\x6B\x72\x79\x6F\x02\x73\x65\x00\x00" "\x0A\x00\x01\x00\x00\x29\x10\x00\x00\x00\x80\x00\x00\x00"; -static char answerPacket[] = +static char answerPacket[] = "\x05\x39\x84\x00\x00\x01\x00\x01\x00\x00\x00\x00\x05\x73\x69\x6C\x6C" "\x79\x04\x68\x6F\x73\x74\x02\x6F\x66\x06\x69\x6F\x64\x69\x6E\x65\x04" "\x63\x6F\x64\x65\x04\x6B\x72\x79\x6F\x02\x73\x65\x00\x00\x0A\x00\x01" "\xC0\x0C\x00\x0A\x00\x01\x00\x00\x00\x00\x00\x23\x74\x68\x69\x73\x20" "\x69\x73\x20\x74\x68\x65\x20\x6D\x65\x73\x73\x61\x67\x65\x20\x74\x6F" "\x20\x62\x65\x20\x64\x65\x6C\x69\x76\x65\x72\x65\x64"; - + static char *msgData = "this is the message to be delivered"; static char *topdomain = "kryo.se"; - + static char *innerData = "HELLO this is the test data"; START_TEST(test_encode_query) @@ -88,7 +88,7 @@ START_TEST(test_encode_query) dump_packet(buf, ret); } fail_unless(strncmp(queryPacket, buf, sizeof(queryPacket)) == 0, "Did not compile expected packet"); - fail_unless(ret == len, va_str("Bad packet length: %d, expected %d", ret, len)); + fail_unless(ret == len, "Bad packet length: %d, expected %d", ret, len); } END_TEST @@ -112,7 +112,7 @@ START_TEST(test_decode_query) unpack_data(buf, len, &(q.name[1]), (int) (domain - q.name) - 1, enc); fail_unless(strncmp(buf, innerData, strlen(innerData)) == 0, "Did not extract expected host: '%s'", buf); - fail_unless(strlen(buf) == strlen(innerData), va_str("Bad host length: %d, expected %d: '%s'", strlen(buf), strlen(innerData), buf)); + fail_unless(strlen(buf) == strlen(innerData), "Bad host length: %d, expected %d: '%s'", strlen(buf), strlen(innerData), buf); } END_TEST @@ -135,7 +135,7 @@ START_TEST(test_encode_response) len = sizeof(answerPacket) - 1; /* Skip extra null character */ fail_unless(strncmp(answerPacket, buf, sizeof(answerPacket)) == 0, "Did not compile expected packet"); - fail_unless(ret == len, va_str("Bad packet length: %d, expected %d", ret, len)); + fail_unless(ret == len, "Bad packet length: %d, expected %d", ret, len); } END_TEST @@ -150,7 +150,7 @@ START_TEST(test_decode_response) ret = dns_decode(buf, len, NULL, QR_ANSWER, answerPacket, sizeof(answerPacket)-1); fail_unless(strncmp(msgData, buf, sizeof(msgData)) == 0, "Did not extract expected data"); - fail_unless(ret == strlen(msgData), va_str("Bad data length: %d, expected %d", ret, strlen(msgData))); + fail_unless(ret == strlen(msgData), "Bad data length: %d, expected %d", ret, strlen(msgData)); } END_TEST diff --git a/tests/encoding.c b/tests/encoding.c index fcfae49..767a697 100644 --- a/tests/encoding.c +++ b/tests/encoding.c @@ -27,9 +27,9 @@ struct tuple char *a; char *b; } dottests[] = { - { "aaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + { "aaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.a"}, - { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."}, { "abc123", "abc123" }, { NULL, NULL } @@ -49,7 +49,7 @@ START_TEST(test_inline_dotify) inline_dotify(b, sizeof(temp)); fail_unless(strcmp(dottests[i].b, temp) == 0, - va_str("'%s' != '%s'", temp, dottests[i].b)); + "'%s' != '%s'", temp, dottests[i].b); i++; } } @@ -69,7 +69,7 @@ START_TEST(test_inline_undotify) inline_undotify(b, sizeof(temp)); fail_unless(strcmp(dottests[i].a, temp) == 0, - va_str("'%s' != '%s'", temp, dottests[i].a)); + "'%s' != '%s'", temp, dottests[i].a); i++; } } diff --git a/tests/read.c b/tests/read.c index 14a139a..e82546a 100644 --- a/tests/read.c +++ b/tests/read.c @@ -33,7 +33,7 @@ #include "dns.h" #include "read.h" #include "test.h" - + START_TEST(test_read_putshort) { unsigned short k; @@ -44,15 +44,15 @@ START_TEST(test_read_putshort) for (i = 0; i < 65536; i++) { p = (char*)&k; putshort(&p, i); - fail_unless(ntohs(k) == i, - va_str("Bad value on putshort for %d: %d != %d", - i, ntohs(k), i)); + fail_unless(ntohs(k) == i, + "Bad value on putshort for %d: %d != %d", + i, ntohs(k), i); p = (char*)&k; readshort(NULL, &p, (short *) &l); fail_unless(l == i, - va_str("Bad value on readshort for %d: %d != %d", - i, l, i)); + "Bad value on readshort for %d: %d != %d", + i, l, i); } } END_TEST @@ -71,14 +71,14 @@ START_TEST(test_read_putlong) putlong(&p, j); - fail_unless(ntohl(k) == j, - va_str("Bad value on putlong for %d: %d != %d", i, ntohl(j), j)); - + fail_unless(ntohl(k) == j, + "Bad value on putlong for %d: %d != %d", i, ntohl(j), j); + p = (char*)&k; readlong(NULL, &p, &l); fail_unless(l == j, - va_str("Bad value on readlong for %d: %d != %d", i, l, j)); + "Bad value on readlong for %d: %d != %d", i, l, j); } } END_TEST @@ -87,11 +87,11 @@ START_TEST(test_read_name) { unsigned char emptyloop[] = { 'A', 'A', 0x81, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01 }; + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01 }; unsigned char infloop[] = { 'A', 'A', 0x81, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 'A', 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01 }; - unsigned char longname[] = + 0x01, 'A', 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01 }; + unsigned char longname[] = "AA\x81\x80\x00\x01\x00\x00\x00\x00\x00\x00" "\x3FzBCDEFGHIJKLMNOPQURSTUVXYZ0123456789abcdefghijklmnopqrstuvxyzAA" "\x3FzBCDEFGHIJKLMNOPQURSTUVXYZ0123456789abcdefghijklmnopqrstuvxyzAA" @@ -100,15 +100,15 @@ START_TEST(test_read_name) "\x3FzBCDEFGHIJKLMNOPQURSTUVXYZ0123456789abcdefghijklmnopqrstuvxyzAA" "\x3FzBCDEFGHIJKLMNOPQURSTUVXYZ0123456789abcdefghijklmnopqrstuvxyzAA" "\x00\x00\x01\x00\x01"; - unsigned char onejump[] = + unsigned char onejump[] = "AA\x81\x80\x00\x01\x00\x00\x00\x00\x00\x00" "\x02hh\xc0\x15\x00\x01\x00\x01\x05zBCDE\x00"; unsigned char badjump[] = { 'A', 'A', 0x81, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xfe, 0xcc, 0x00, 0x01, 0x00, 0x01 }; + 0xfe, 0xcc, 0x00, 0x01, 0x00, 0x01 }; unsigned char badjump2[] = { 'A', 'A', 0x81, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 'B', 'A', 0xfe, 0xcc, 0x00, 0x01, 0x00, 0x01 }; + 0x02, 'B', 'A', 0xfe, 0xcc, 0x00, 0x01, 0x00, 0x01 }; unsigned char *jumper; char buf[1024]; char *data; @@ -119,13 +119,13 @@ START_TEST(test_read_name) buf[1023] = 'A'; rv = readname((char *) emptyloop, sizeof(emptyloop), &data, buf, 1023); fail_unless(buf[1023] == 'A', NULL); - + memset(buf, 0, sizeof(buf)); data = (char*) infloop + sizeof(HEADER); buf[4] = '\a'; rv = readname((char*) infloop, sizeof(infloop), &data, buf, 4); fail_unless(buf[4] == '\a', NULL); - + memset(buf, 0, sizeof(buf)); data = (char*) longname + sizeof(HEADER); buf[256] = '\a'; @@ -136,7 +136,7 @@ START_TEST(test_read_name) data = (char*) onejump + sizeof(HEADER); rv = readname((char*) onejump, sizeof(onejump), &data, buf, 256); fail_unless(rv == 9, NULL); - + /* These two tests use malloc to cause segfault if jump is executed */ memset(buf, 0, sizeof(buf)); jumper = malloc(sizeof(badjump)); @@ -149,7 +149,7 @@ START_TEST(test_read_name) fail_unless(buf[0] == 0, NULL); } free(jumper); - + memset(buf, 0, sizeof(buf)); jumper = malloc(sizeof(badjump2)); if (jumper) { @@ -158,8 +158,8 @@ START_TEST(test_read_name) rv = readname((char*) jumper, sizeof(badjump2), &data, buf, 256); fail_unless(rv == 4, NULL); - fail_unless(strcmp("BA.", buf) == 0, - va_str("buf is not BA: %s", buf)); + fail_unless(strcmp("BA.", buf) == 0, + "buf is not BA: %s", buf); } free(jumper); } @@ -184,11 +184,11 @@ START_TEST(test_putname) fail_unless(strncmp(buf, out, ret) == 0, "Happy flow failed"); } END_TEST - + START_TEST(test_putname_nodot) { char buf[256]; - char *nodot = + char *nodot = "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"; char *b; @@ -205,11 +205,11 @@ START_TEST(test_putname_nodot) fail_unless(b == buf, NULL); } END_TEST - + START_TEST(test_putname_toolong) { char buf[256]; - char *toolong = + char *toolong = "ABCDEFGHIJKLMNOPQRSTUVWXYZ.ABCDEFGHIJKLMNOPQRSTUVWXYZ." "ABCDEFGHIJKLMNOPQRSTUVWXYZ.ABCDEFGHIJKLMNOPQRSTUVWXYZ." "ABCDEFGHIJKLMNOPQRSTUVWXYZ.ABCDEFGHIJKLMNOPQRSTUVWXYZ." diff --git a/tests/test.c b/tests/test.c index b01417f..df354a9 100644 --- a/tests/test.c +++ b/tests/test.c @@ -22,20 +22,6 @@ #include "test.h" -char * -va_str(const char *fmt, ...) -{ - static char buf[512]; - va_list ap; - - memset(buf, 0, sizeof(buf)); - va_start(ap, fmt); - vsnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); - - return buf; -} - int main() { @@ -68,7 +54,7 @@ main() suite_add_tcase(iodine, test); runner = srunner_create(iodine); - srunner_run_all(runner, CK_VERBOSE); + srunner_run_all(runner, CK_MINIMAL); failed = srunner_ntests_failed(runner); srunner_free(runner);