From 6536fc7c5d614cce3bdf86062411d7b7385bd322 Mon Sep 17 00:00:00 2001 From: Erik Ekman Date: Sat, 21 Mar 2009 13:07:49 +0000 Subject: [PATCH] Release 0.5.1 --- CHANGELOG | 15 ++ Makefile | 23 ++- README-win32.txt | 62 ++++++++ src/Makefile | 9 +- src/base32.c | 46 +++--- src/base64.c | 19 +-- src/common.c | 150 +++++++++++++++--- src/common.h | 33 +++- src/dns.c | 20 ++- src/dns.h | 2 +- src/fw_query.c | 2 +- src/fw_query.h | 9 +- src/iodine.c | 390 +++++++++++++++++++++++++++++++---------------- src/iodined.c | 176 ++++++++++++++------- src/login.c | 7 +- src/osflags | 9 +- src/tun.c | 303 +++++++++++++++++++++++++++++++----- src/user.c | 11 +- src/windows.h | 85 +++++++++++ tests/Makefile | 4 +- tests/base32.c | 81 +++++++--- tests/base64.c | 77 +++++++--- tests/dns.c | 62 ++++++-- tests/fw_query.c | 88 +++++++++++ tests/login.c | 23 ++- tests/read.c | 135 ++++++++++------ tests/test.c | 3 + tests/test.h | 1 + 28 files changed, 1444 insertions(+), 401 deletions(-) create mode 100644 README-win32.txt create mode 100644 src/windows.h create mode 100644 tests/fw_query.c diff --git a/CHANGELOG b/CHANGELOG index 6f7fc93..1b792b8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -5,6 +5,21 @@ iodine - http://code.kryo.se/iodine CHANGES: +2009-03-21: 0.5.1 "Boringo" + - Added initial Windows support, fixes #43. + - Added length check of autoprobe responses + - Refactored and added unit tests + - Added syslog logging for iodined on version and login packets + - Fixed segfault when encoding just one block, fixes #51. + The normal code was never affected by this. + - Added win32 code to read DNS server from system, fixes #45. + - Disabled password echo on win32, fixes #44. + - Fix encoding error making all autoprobing > 1024 bytes fail, #52. + - Increase default interface MTU to 1200. + - Fix autoprobing error making every third probe fail, set IP flag + Dont-Fragment where supported. Fixes #54. + - Added TAP32 version 0901 as accepted (#53). + 2009-01-23: 0.5.0 "iPassed" - Fixed segfault in server when sending version reject. - Applied patch to make iodine build on BeOS R5-BONE and Haiku, diff --git a/Makefile b/Makefile index 2f2bd10..01c2325 100644 --- a/Makefile +++ b/Makefile @@ -14,8 +14,25 @@ MKDIR_FLAGS=-p RM=rm RM_FLAGS=-f +TARGETOS = `uname` + all: - @(cd src; $(MAKE) all) + @(cd src; $(MAKE) TARGETOS=$(TARGETOS) all) + +cross-mingw: + @(cd src; $(MAKE) TARGETOS=windows32 CC=i686-mingw32-gcc all) + +cross-mingw-dist: cross-mingw + @rm -rf iodine-latest-win32* + @mkdir -p iodine-latest-win32/bin + @for i in `ls bin`; do cp bin/$$i iodine-latest-win32/bin/$$i.exe; done + @cp /usr/i686-mingw32/usr/bin/zlib1.dll iodine-latest-win32/bin + @cp README* CH* TO* iodine-latest-win32 + @echo "Create date: " > iodine-latest-win32/VERSION + @date >> iodine-latest-win32/VERSION + @echo "SVN version: " >> iodine-latest-win32/VERSION + @svnversion >> iodine-latest-win32/VERSION + @zip -r iodine-latest-win32.zip iodine-latest-win32 install: all $(MKDIR) $(MKDIR_FLAGS) $(DESTDIR)$(sbindir) @@ -35,11 +52,11 @@ 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) TARGETOS=$(TARGETOS) all) clean: @echo "Cleaning..." @(cd src; $(MAKE) clean) @(cd tests; $(MAKE) clean) - @rm -rf bin + @rm -rf bin iodine-latest-win32* diff --git a/README-win32.txt b/README-win32.txt new file mode 100644 index 0000000..1df7f64 --- /dev/null +++ b/README-win32.txt @@ -0,0 +1,62 @@ + + +iodine - http://code.kryo.se/iodine + +*********************************** + +Extra README file for Win32 related stuff + + +== Running iodine on Windows: +1. Install the TAP32 driver + http://openvpn.net/index.php/downloads.html + choose OpenVPN 2.0.9 Windows Installer, when installing you can + select to install only the TAP driver. + +2. Have one TAP32 interface installed + +3. Name the interface "dns" + +4. Make sure the interface does not have a default gateway set + +5. Run iodine/iodined as normal. + +6. Enjoy! + + +== Building on Windows: +You need: + MinGW, MSYS, GCC, zlib + +Then just run make + + +== Cross-compiling for MinGW: +You need: + MinGW crosscompiler, crosscompiled zlib + +Then run "make cross-mingw" +Note that the binaries will not get a .exe suffix + + +== Zlib download +You can get zlib for MinGW here (both for native and crosscompile): +http://code.kryo.se/iodine/deps/zlib.zip +Unzip it in your MinGW directory on Windows or in $ROOT/usr for +cross-compile. + + +== Results of crappy Win32 API: +The following fixable limitations apply: +- Exactly one TAP32 interface must be installed +- The TAP32 interface must be named "dns" and be version 0801 or 0901 +- Server cannot read packet destination address + +The following (probably) un-fixable limitations apply: +- A password entered as -P argument can be shown in process list +- Priviligies cannot be dropped +- chroot() cannot be used +- Detaching from terminal not possible +- Server on windows must be run with /30 netmask +- Client can only talk to server, not other clients + diff --git a/src/Makefile b/src/Makefile index 8197c4d..fe15d93 100644 --- a/src/Makefile +++ b/src/Makefile @@ -5,11 +5,12 @@ CLIENT = ../bin/iodine SERVEROBJS = iodined.o user.o fw_query.o SERVER = ../bin/iodined -OS = `uname | tr "a-z" "A-Z"` +OS = `echo $(TARGETOS) | tr "a-z" "A-Z"` ARCH = `uname -m` -LDFLAGS = -lz `sh osflags link` -CFLAGS = -c -g -Wall -D$(OS) -pedantic `sh osflags cflags` +LIBPATH = -L. +LDFLAGS = -lz `sh osflags $(TARGETOS) link` $(LIBPATH) +CFLAGS = -c -g -Wall -D$(OS) -pedantic `sh osflags $(TARGETOS) cflags` all: stateos $(CLIENT) $(SERVER) @@ -32,5 +33,5 @@ $(SERVER): $(COMMONOBJS) $(SERVEROBJS) clean: @echo "Cleaning src/" - @rm -f $(CLIENT) $(SERVER) *~ *.o *.core + @rm -f $(CLIENT){,.exe} $(SERVER){,.exe} *~ *.o *.core diff --git a/src/base32.c b/src/base32.c index af6a214..5ee0664 100644 --- a/src/base32.c +++ b/src/base32.c @@ -27,7 +27,6 @@ static const char cb32[] = "abcdefghijklmnopqrstuvwxyz012345"; static unsigned char rev32[128]; -static int reverse_init = 0; static int base32_decode(void *, size_t *, const char *, size_t); static int base32_encode(char *, size_t *, const void *, size_t); @@ -70,6 +69,22 @@ base32_blksize_enc() return BLKSIZE_ENC; } +static void +base32_reverse_init() +{ + int i; + unsigned char c; + static int reverse_init = 0; + + if (!reverse_init) { + for (i = 0; i < 32; i++) { + c = cb32[i]; + rev32[(int) c] = i; + } + reverse_init = 1; + } +} + int b32_5to8(int in) { @@ -79,15 +94,7 @@ b32_5to8(int in) int b32_8to5(int in) { - int i; - int c; - if (!reverse_init) { - for (i = 0; i < 32; i++) { - c = cb32[i]; - rev32[(int) c] = i; - } - reverse_init = 1; - } + base32_reverse_init(); return rev32[in]; } @@ -103,9 +110,12 @@ base32_encode(char *buf, size_t *buflen, const void *data, size_t size) memset(buf, 0, *buflen); /* how many chars can we encode within the buf */ - maxsize = BLKSIZE_RAW * (*buflen / BLKSIZE_ENC - 1) - 1; + maxsize = BLKSIZE_RAW * (*buflen / BLKSIZE_ENC); /* how big will the encoded data be */ - newsize = BLKSIZE_ENC * (size / BLKSIZE_RAW + 1) + 1; + newsize = BLKSIZE_ENC * (size / BLKSIZE_RAW); + if (size % BLKSIZE_RAW) { + newsize += BLKSIZE_ENC; + } /* if the buffer is too small, eat some of the data */ if (*buflen < newsize) { size = maxsize; @@ -132,7 +142,7 @@ base32_encode(char *buf, size_t *buflen, const void *data, size_t size) /* store number of bytes from data that was used */ *buflen = size; - return strlen(buf) - 1; + return strlen(buf); } #define DECODE_ERROR 0xffffffff @@ -183,17 +193,9 @@ base32_decode(void *buf, size_t *buflen, const char *str, size_t slen) size_t newsize; size_t maxsize; const char *p; - unsigned char c; int len; - int i; - if (!reverse_init) { - for (i = 0; i < 32; i++) { - c = cb32[i]; - rev32[(int) c] = i; - } - reverse_init = 1; - } + base32_reverse_init(); /* chars needed to decode slen */ newsize = BLKSIZE_RAW * (slen / BLKSIZE_ENC + 1) + 1; diff --git a/src/base64.c b/src/base64.c index 803d938..7da2d2a 100644 --- a/src/base64.c +++ b/src/base64.c @@ -78,7 +78,6 @@ base64_encode(char *buf, size_t *buflen, const void *data, size_t size) { size_t newsize; size_t maxsize; - unsigned char c; unsigned char *s; unsigned char *p; unsigned char *q; @@ -86,18 +85,14 @@ base64_encode(char *buf, size_t *buflen, const void *data, size_t size) memset(buf, 0, *buflen); - if (!reverse_init) { - for (i = 0; i < 64; i++) { - c = cb64[i]; - rev64[(int) c] = i; - } - reverse_init = 1; + /* how many chars can we encode within the buf */ + maxsize = BLKSIZE_RAW * (*buflen / BLKSIZE_ENC); + /* how big will the encoded data be */ + newsize = BLKSIZE_ENC * (size / BLKSIZE_RAW); + if (size % BLKSIZE_RAW) { + newsize += BLKSIZE_ENC; } - /* how many chars can we encode within the buf */ - maxsize = BLKSIZE_RAW * (*buflen / BLKSIZE_ENC - 1) - 1; - /* how big will the encoded data be */ - newsize = BLKSIZE_ENC * (size / BLKSIZE_RAW + 1) + 1; /* if the buffer is too small, eat some of the data */ if (*buflen < newsize) { size = maxsize; @@ -120,7 +115,7 @@ base64_encode(char *buf, size_t *buflen, const void *data, size_t size) /* store number of bytes from data that was used */ *buflen = size; - return strlen(buf) - 1; + return strlen(buf); } #define DECODE_ERROR 0xffffffff diff --git a/src/common.c b/src/common.c index 9b32f84..8511498 100644 --- a/src/common.c +++ b/src/common.c @@ -14,14 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include -#include -#ifdef DARWIN -#include -#endif #include -#include #include #include #include @@ -33,12 +26,25 @@ #include #include #include + +#ifdef WINDOWS32 +#include +#include +#else +#include +#ifdef DARWIN +#include +#endif #include +#include +#include +#include +#endif #include "common.h" /* daemon(3) exists only in 4.4BSD or later, and in GNU libc */ -#if !(defined(BSD) && (BSD >= 199306)) && !defined(__GLIBC__) +#if !defined(WINDOWS32) && !(defined(BSD) && (BSD >= 199306)) && !defined(__GLIBC__) static int daemon(int nochdir, int noclose) { int fd, i; @@ -82,11 +88,24 @@ int setgroups(int count, int *groups) } #endif + +void +check_superuser(void (*usage_fn)(void)) +{ +#ifndef WINDOWS32 + if (geteuid() != 0) { + warnx("Run as root and you'll be happy.\n"); + usage_fn(); + /* NOTREACHED */ + } +#endif +} + int open_dns(int localport, in_addr_t listen_ip) { struct sockaddr_in addr; - int flag; + int flag = 1; int fd; memset(&addr, 0, sizeof(addr)); @@ -95,22 +114,32 @@ open_dns(int localport, in_addr_t listen_ip) /* 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) + if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + fprintf(stderr, "got fd %d\n", fd); err(1, "socket"); + } flag = 1; #ifdef SO_REUSEPORT - setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &flag, sizeof(flag)); + setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (const void*) &flag, sizeof(flag)); #endif - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)); + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void*) &flag, sizeof(flag)); +#ifndef WINDOWS32 /* To get destination address from each UDP datagram, see iodined.c:read_dns() */ - setsockopt(fd, IPPROTO_IP, DSTADDR_SOCKOPT, &flag, sizeof(flag)); + setsockopt(fd, IPPROTO_IP, DSTADDR_SOCKOPT, (const void*) &flag, sizeof(flag)); +#endif + +#ifdef IP_OPT_DONT_FRAG + /* Set dont-fragment ip header flag */ + flag = DONT_FRAG_VALUE; + setsockopt(fd, IPPROTO_IP, IP_OPT_DONT_FRAG, (const void*) &flag, sizeof(flag)); +#endif if(bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) err(1, "bind"); - printf("Opened UDP socket\n"); + fprintf(stderr, "Opened UDP socket\n"); return fd; } @@ -124,7 +153,7 @@ close_dns(int fd) void do_chroot(char *newroot) { -#if !defined(__BEOS__) || defined(__HAIKU__) +#if !(defined(WINDOWS32) || defined(__BEOS__) || defined(__HAIKU__)) if (chroot(newroot) != 0 || chdir("/") != 0) err(1, "%s", newroot); @@ -138,31 +167,54 @@ do_chroot(char *newroot) void do_detach() { - printf("Detaching from terminal...\n"); +#ifndef WINDOWS32 + fprintf(stderr, "Detaching from terminal...\n"); daemon(0, 0); umask(0); alarm(0); +#else + fprintf(stderr, "Windows version does not support detaching\n"); +#endif } void read_password(char *buf, size_t len) { + char pwd[80]; +#ifndef WINDOWS32 struct termios old; struct termios tp; - char pwd[80]; tcgetattr(0, &tp); old = tp; tp.c_lflag &= (~ECHO); tcsetattr(0, TCSANOW, &tp); +#else + int i; +#endif - printf("Enter password: "); - fflush(stdout); + fprintf(stderr, "Enter password: "); + fflush(stderr); +#ifndef WINDOWS32 scanf("%79s", pwd); - printf("\n"); +#else + for (i = 0; i < sizeof(pwd); i++) { + pwd[i] = getch(); + if (pwd[i] == '\r' || pwd[i] == '\n') { + pwd[i] = 0; + break; + } else if (pwd[i] == '\b') { + i--; /* Remove the \b char */ + if (i >=0) i--; /* If not first char, remove one more */ + } + } +#endif + fprintf(stderr, "\n"); +#ifndef WINDOWS32 tcsetattr(0, TCSANOW, &old); +#endif strncpy(buf, pwd, len); buf[len-1] = '\0'; @@ -184,3 +236,61 @@ check_topdomain(char *str) } return 0; } + +#ifdef WINDOWS32 +int +inet_aton(const char *cp, struct in_addr *inp) +{ + inp->s_addr = inet_addr(cp); + return inp->s_addr != INADDR_ANY; +} + +void +warn(const char *fmt, ...) +{ + va_list list; + + va_start(list, fmt); + if (fmt) fprintf(stderr, fmt, list); + if (errno == 0) { + fprintf(stderr, ": WSA error %d\n", WSAGetLastError()); + } else { + fprintf(stderr, ": %s\n", strerror(errno)); + } + va_end(list); +} + +void +warnx(const char *fmt, ...) +{ + va_list list; + + va_start(list, fmt); + if (fmt) fprintf(stderr, fmt, list); + fprintf(stderr, "\n"); + va_end(list); +} + +void +err(int eval, const char *fmt, ...) +{ + va_list list; + + va_start(list, fmt); + warn(fmt, list); + va_end(list); + exit(eval); +} + +void +errx(int eval, const char *fmt, ...) +{ + va_list list; + + va_start(list, fmt); + warnx(fmt, list); + va_end(list); + exit(eval); +} +#endif + diff --git a/src/common.h b/src/common.h index edc8e82..f39461a 100644 --- a/src/common.h +++ b/src/common.h @@ -17,10 +17,15 @@ #ifndef __COMMON_H__ #define __COMMON_H__ -#include -#include +#ifdef WINDOWS32 +#include "windows.h" +#else #include +#include +#include #include +#endif + #ifndef MIN #define MIN(a,b) ((a)<(b)?(a):(b)) @@ -39,6 +44,20 @@ # define dstaddr(x) (&(((struct in_pktinfo *)(CMSG_DATA(x)))->ipi_addr)) #endif +#if defined IP_MTU_DISCOVER + /* Linux */ +# define IP_OPT_DONT_FRAG IP_MTU_DISCOVER +# define DONT_FRAG_VALUE IP_PMTUDISC_DO +#elif defined IP_DONTFRAG + /* FreeBSD */ +# define IP_OPT_DONT_FRAG IP_DONTFRAG +# define DONT_FRAG_VALUE 1 +#elif defined IP_DONTFRAGMENT + /* Winsock2 */ +# define IP_OPT_DONT_FRAG IP_DONTFRAGMENT +# define DONT_FRAG_VALUE 1 +#endif + struct packet { int len; /* Total packet length */ @@ -58,6 +77,7 @@ struct query { int fromlen; }; +void check_superuser(void (*usage_fn)(void)); int open_dns(int, in_addr_t); void close_dns(int); @@ -68,4 +88,13 @@ void read_password(char*, size_t); int check_topdomain(char *); +#ifdef WINDOWS32 +int inet_aton(const char *cp, struct in_addr *inp); + +void err(int eval, const char *fmt, ...); +void warn(const char *fmt, ...); +void errx(int eval, const char *fmt, ...); +void warnx(const char *fmt, ...); +#endif + #endif diff --git a/src/dns.c b/src/dns.c index df06fd1..4b8199b 100644 --- a/src/dns.c +++ b/src/dns.c @@ -14,13 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include -#ifdef DARWIN -#include -#endif #include -#include #include #include #include @@ -28,6 +22,18 @@ #include #include +#ifdef WINDOWS32 +#include "windows.h" +#else +#include +#ifdef DARWIN +#include +#endif +#include +#include +#endif + + #include "dns.h" #include "encoding.h" #include "read.h" @@ -177,7 +183,7 @@ dns_encode_ns_response(char *buf, size_t buflen, struct query *q, char *topdomai return len; } -short +unsigned short dns_get_id(char *packet, size_t packetlen) { HEADER *header; diff --git a/src/dns.h b/src/dns.h index dc3e429..b5c4cc5 100644 --- a/src/dns.h +++ b/src/dns.h @@ -26,7 +26,7 @@ typedef enum { int dns_encode(char *, size_t, struct query *, qr_t, char *, size_t); int dns_encode_ns_response(char *buf, size_t buflen, struct query *q, char *topdomain); -short dns_get_id(char *packet, size_t packetlen); +unsigned short dns_get_id(char *packet, size_t packetlen); int dns_decode(char *, size_t, struct query *, qr_t, char *, size_t); #endif /* _DNS_H_ */ diff --git a/src/fw_query.c b/src/fw_query.c index d2269bc..3727f08 100644 --- a/src/fw_query.c +++ b/src/fw_query.c @@ -35,7 +35,7 @@ void fw_query_put(struct fw_query *fw_query) fwq_ix = 0; } -void fw_query_get(short query_id, struct fw_query **fw_query) +void fw_query_get(unsigned short query_id, struct fw_query **fw_query) { int i; diff --git a/src/fw_query.h b/src/fw_query.h index c47da41..7568a5f 100644 --- a/src/fw_query.h +++ b/src/fw_query.h @@ -18,19 +18,24 @@ #define __FW_QUERY_H__ #include +#ifdef WINDOWS32 +#include "windows.h" +#include +#else #include +#endif #define FW_QUERY_CACHE_SIZE 16 struct fw_query { struct sockaddr addr; int addrlen; - short id; + unsigned short id; }; void fw_query_init(); void fw_query_put(struct fw_query *fw_query); -void fw_query_get(short query_id, struct fw_query **fw_query); +void fw_query_get(unsigned short query_id, struct fw_query **fw_query); #endif /*__FW_QUERY_H__*/ diff --git a/src/iodine.c b/src/iodine.c index 7b1a084..65e47bc 100644 --- a/src/iodine.c +++ b/src/iodine.c @@ -20,22 +20,28 @@ #include #include #include -#include -#include #include #include #include -#include #include -#include -#include -#include -#include #include + +#ifdef WINDOWS32 +#include "windows.h" +#include +#else #include #ifdef DARWIN #include #endif +#include +#include +#include +#include +#include +#include +#include +#endif #include "common.h" #include "encoding.h" @@ -46,6 +52,11 @@ #include "tun.h" #include "version.h" +#ifdef WINDOWS32 +WORD req_version = MAKEWORD(2, 2); +WSADATA wsa_data; +#endif + static void send_ping(int fd); static void send_chunk(int fd); static int build_hostname(char *buf, size_t buflen, @@ -64,9 +75,6 @@ static int downstream_fragment; static int down_ack_seqno; static int down_ack_fragment; -static int max_downstream_frag_size; -static int autodetect_frag_size; - /* Current up/downstream IP packet */ static struct packet outpkt; static struct packet inpkt; @@ -132,7 +140,7 @@ build_hostname(char *buf, size_t buflen, size_t space; char *b; - space = MIN(0xFF, buflen) - strlen(topdomain) - 5; + space = MIN(0xFF, buflen) - strlen(topdomain) - 7; if (!encoder->places_dots()) space -= (space / 57); /* space for dots */ @@ -424,7 +432,7 @@ send_fragsize_probe(int fd, int fragsize) fragsize &= 2047; buf[0] = 'r'; /* Probe downstream fragsize packet */ - buf[1] = b32_5to8((userid << 1) | (fragsize & 1024)); + buf[1] = b32_5to8((userid << 1) | ((fragsize >> 10) & 1)); buf[2] = b32_5to8((fragsize >> 5) & 31); buf[3] = b32_5to8(fragsize & 31); @@ -487,22 +495,17 @@ send_codec_switch(int fd, int userid, int bits) strncat(buf, topdomain, 512 - strlen(buf)); send_query(fd, buf); } - + static int -handshake(int dns_fd) +handshake_version(int dns_fd, int *seed) { struct timeval tv; - uint32_t payload; - char server[65]; - char client[65]; - char login[16]; char in[4096]; fd_set fds; - int read; - int mtu; - int seed; + uint32_t payload; int i; int r; + int read; for (i = 0; running && i < 5; i++) { tv.tv_sec = i + 1; @@ -533,11 +536,11 @@ handshake(int dns_fd) ((in[7] & 0xff))); if (strncmp("VACK", in, 4) == 0) { - seed = payload; + *seed = payload; userid = in[8]; - printf("Version ok, both using protocol v 0x%08x. You are user #%d\n", VERSION, userid); - goto perform_login; + fprintf(stderr, "Version ok, both using protocol v 0x%08x. You are user #%d\n", VERSION, userid); + return 0; } else if (strncmp("VNAK", in, 4) == 0) { warnx("You use protocol v 0x%08x, server uses v 0x%08x. Giving up", VERSION, payload); @@ -550,12 +553,26 @@ handshake(int dns_fd) warnx("did not receive proper login challenge"); } - printf("Retrying version check...\n"); + fprintf(stderr, "Retrying version check...\n"); } - errx(1, "couldn't connect to server"); - /* NOTREACHED */ - -perform_login: + warnx("couldn't connect to server"); + return 1; +} + +static int +handshake_login(int dns_fd, int seed) +{ + struct timeval tv; + char in[4096]; + char login[16]; + char server[65]; + char client[65]; + int mtu; + fd_set fds; + int i; + int r; + int read; + login_calculate(login, 16, password, seed); for (i=0; running && i<5 ;i++) { @@ -580,7 +597,7 @@ perform_login: if (read > 0) { int netmask; if (strncmp("LNAK", in, 4) == 0) { - printf("Bad password\n"); + fprintf(stderr, "Bad password\n"); return 1; } else if (sscanf(in, "%64[^-]-%64[^-]-%d-%d", server, client, &mtu, &netmask) == 4) { @@ -589,22 +606,32 @@ perform_login: client[64] = 0; if (tun_setip(client, netmask) == 0 && tun_setmtu(mtu) == 0) { - goto perform_case_check; + return 0; } else { warnx("Received handshake with bad data"); } } else { - printf("Received bad handshake\n"); + fprintf(stderr, "Received bad handshake\n"); } } } - printf("Retrying login...\n"); + fprintf(stderr, "Retrying login...\n"); } warnx("couldn't login to server"); return 1; +} + +static void +handshake_case_check(int dns_fd) +{ + struct timeval tv; + char in[4096]; + fd_set fds; + int i; + int r; + int read; -perform_case_check: case_preserved = 0; for (i=0; running && i<5 ;i++) { tv.tv_sec = i + 1; @@ -623,8 +650,8 @@ perform_case_check: if (read > 0) { if (in[0] == 'z' || in[0] == 'Z') { if (read < (27 * 2)) { - printf("Received short case check reply. Will use base32 encoder\n"); - goto switch_codec; + fprintf(stderr, "Received short case check reply. Will use base32 encoder\n"); + return; } else { int k; @@ -636,27 +663,35 @@ perform_case_check: case_preserved = 0; } } - goto switch_codec; + return; } } else { - printf("Received bad case check reply\n"); + fprintf(stderr, "Received bad case check reply\n"); } } else { - printf("Got error on case check, will use base32\n"); - goto switch_codec; + fprintf(stderr, "Got error on case check, will use base32\n"); + return; } } - printf("Retrying case check...\n"); + fprintf(stderr, "Retrying case check...\n"); } - printf("No reply on case check, continuing\n"); -switch_codec: - if (!case_preserved) - goto autodetect_max_fragsize; + fprintf(stderr, "No reply on case check, continuing\n"); +} + +static void +handshake_switch_codec(int dns_fd) +{ + struct timeval tv; + char in[4096]; + fd_set fds; + int i; + int r; + int read; dataenc = get_base64_encoder(); - printf("Switching to %s codec\n", dataenc->name); + fprintf(stderr, "Switching to %s codec\n", dataenc->name); /* Send to server that this user will use base64 from now on */ for (i=0; running && i<5 ;i++) { int bits; @@ -677,84 +712,120 @@ switch_codec: if (read > 0) { if (strncmp("BADLEN", in, 6) == 0) { - printf("Server got bad message length. "); + fprintf(stderr, "Server got bad message length. "); goto codec_revert; } else if (strncmp("BADIP", in, 5) == 0) { - printf("Server rejected sender IP address. "); + fprintf(stderr, "Server rejected sender IP address. "); goto codec_revert; } else if (strncmp("BADCODEC", in, 8) == 0) { - printf("Server rejected the selected codec. "); + fprintf(stderr, "Server rejected the selected codec. "); goto codec_revert; } in[read] = 0; /* zero terminate */ - printf("Server switched to codec %s\n", in); - goto autodetect_max_fragsize; + fprintf(stderr, "Server switched to codec %s\n", in); + return; } } - printf("Retrying codec switch...\n"); + fprintf(stderr, "Retrying codec switch...\n"); } - printf("No reply from server on codec switch. "); + fprintf(stderr, "No reply from server on codec switch. "); + codec_revert: - printf("Falling back to base32\n"); + fprintf(stderr, "Falling back to base32\n"); dataenc = get_base32_encoder(); -autodetect_max_fragsize: - if (autodetect_frag_size) { - int proposed_fragsize = 768; - int range = 768; - max_downstream_frag_size = 0; - printf("Autoprobing max downstream fragment size... (skip with -m fragsize)\n"); - while (running && range > 0 && (range >= 8 || !max_downstream_frag_size)) { - for (i=0; running && i<3 ;i++) { - tv.tv_sec = 1; - tv.tv_usec = 0; - send_fragsize_probe(dns_fd, proposed_fragsize); +} - FD_ZERO(&fds); - FD_SET(dns_fd, &fds); +static int +handshake_autoprobe_fragsize(int dns_fd) +{ + struct timeval tv; + char in[4096]; + fd_set fds; + int i; + int r; + int read; + int proposed_fragsize = 768; + int range = 768; + int max_fragsize = 0; - r = select(dns_fd + 1, &fds, NULL, NULL, &tv); + max_fragsize = 0; + fprintf(stderr, "Autoprobing max downstream fragment size... (skip with -m fragsize)\n"); + while (running && range > 0 && (range >= 8 || !max_fragsize)) { + for (i=0; running && i<3 ;i++) { + tv.tv_sec = 1; + tv.tv_usec = 0; + send_fragsize_probe(dns_fd, proposed_fragsize); - if(r > 0) { - read = read_dns(dns_fd, in, sizeof(in)); - - if (read > 0) { - /* We got a reply */ - int acked_fragsize = ((in[0] & 0xff) << 8) | (in[1] & 0xff); - if (acked_fragsize == proposed_fragsize) { - printf("%d ok.. ", acked_fragsize); - fflush(stdout); - max_downstream_frag_size = acked_fragsize; - range >>= 1; - proposed_fragsize += range; - continue; + FD_ZERO(&fds); + FD_SET(dns_fd, &fds); + + r = select(dns_fd + 1, &fds, NULL, NULL, &tv); + + if(r > 0) { + read = read_dns(dns_fd, in, sizeof(in)); + + if (read > 0) { + /* We got a reply */ + int acked_fragsize = ((in[0] & 0xff) << 8) | (in[1] & 0xff); + if (acked_fragsize == proposed_fragsize) { + if (read == proposed_fragsize) { + fprintf(stderr, "%d ok.. ", acked_fragsize); + fflush(stderr); + max_fragsize = acked_fragsize; } } + if (strncmp("BADIP", in, 5) == 0) { + fprintf(stderr, "got BADIP.. "); + fflush(stderr); + } + break; } } - printf("%d not ok.. ", proposed_fragsize); - fflush(stdout); - range >>= 1; + fprintf(stderr, "."); + fflush(stderr); + } + range >>= 1; + if (max_fragsize == proposed_fragsize) { + /* Try bigger */ + proposed_fragsize += range; + } else { + /* Try smaller */ + fprintf(stderr, "%d not ok.. ", proposed_fragsize); + fflush(stderr); proposed_fragsize -= range; } - if (!running) { - printf("\n"); - warnx("stopped while autodetecting fragment size (Try probing manually with -m)"); - return 1; - } - if (range == 0) { - /* Tried all the way down to 2 and found no good size */ - printf("\n"); - warnx("found no accepted fragment size. (Try probing manually with -m)"); - return 1; - } - printf("will use %d\n", max_downstream_frag_size); } - printf("Setting downstream fragment size to max %d...\n", max_downstream_frag_size); + if (!running) { + fprintf(stderr, "\n"); + warnx("stopped while autodetecting fragment size (Try probing manually with -m)"); + return 0; + } + if (range == 0) { + /* Tried all the way down to 2 and found no good size */ + fprintf(stderr, "\n"); + warnx("found no accepted fragment size. (Try probing manually with -m)"); + return 0; + } + fprintf(stderr, "will use %d\n", max_fragsize); + return max_fragsize; +} + +static void +handshake_set_fragsize(int dns_fd, int fragsize) +{ + struct timeval tv; + char in[4096]; + fd_set fds; + int i; + int r; + int read; + + fprintf(stderr, "Setting downstream fragment size to max %d...\n", fragsize); for (i=0; running && i<5 ;i++) { tv.tv_sec = i + 1; tv.tv_usec = 0; - send_set_downstream_fragsize(dns_fd, max_downstream_frag_size); + send_set_downstream_fragsize(dns_fd, fragsize); FD_ZERO(&fds); FD_SET(dns_fd, &fds); @@ -768,21 +839,53 @@ autodetect_max_fragsize: int accepted_fragsize; if (strncmp("BADFRAG", in, 7) == 0) { - printf("Server rejected fragsize. Keeping default."); - goto done; + fprintf(stderr, "Server rejected fragsize. Keeping default."); + return; } else if (strncmp("BADIP", in, 5) == 0) { - printf("Server rejected sender IP address.\n"); - goto done; + fprintf(stderr, "Server rejected sender IP address.\n"); + return; } accepted_fragsize = ((in[0] & 0xff) << 8) | (in[1] & 0xff); - goto done; + return; } } - printf("Retrying set fragsize...\n"); + fprintf(stderr, "Retrying set fragsize...\n"); } - printf("No reply from server when setting fragsize. Keeping default.\n"); -done: + fprintf(stderr, "No reply from server when setting fragsize. Keeping default.\n"); +} + +static int +handshake(int dns_fd, int autodetect_frag_size, int fragsize) +{ + int seed; + int r; + + r = handshake_version(dns_fd, &seed); + if (r) { + return r; + } + + r = handshake_login(dns_fd, seed); + if (r) { + return r; + } + + handshake_case_check(dns_fd); + + if (case_preserved) { + handshake_switch_codec(dns_fd); + } + + if (autodetect_frag_size) { + fragsize = handshake_autoprobe_fragsize(dns_fd); + if (!fragsize) { + return 1; + } + } + + handshake_set_fragsize(dns_fd, fragsize); + return 0; } @@ -790,8 +893,9 @@ static char * get_resolvconf_addr() { static char addr[16]; - char buf[80]; char *rv; +#ifndef WINDOWS32 + char buf[80]; FILE *fp; rv = NULL; @@ -809,7 +913,29 @@ get_resolvconf_addr() } fclose(fp); +#else /* !WINDOWS32 */ + FIXED_INFO *fixed_info; + ULONG buflen; + DWORD ret; + rv = NULL; + fixed_info = malloc(sizeof(FIXED_INFO)); + buflen = sizeof(FIXED_INFO); + + if (GetNetworkParams(fixed_info, &buflen) == ERROR_BUFFER_OVERFLOW) { + /* official ugly api workaround */ + free(fixed_info); + fixed_info = malloc(buflen); + } + + ret = GetNetworkParams(fixed_info, &buflen); + if (ret == NO_ERROR) { + strncpy(addr, fixed_info->DnsServerList.IpAddress.String, sizeof(addr)); + addr[15] = 0; + rv = addr; + } + free(fixed_info); +#endif return rv; } @@ -831,7 +957,7 @@ static void usage() { extern char *__progname; - printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] " + fprintf(stderr, "Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] " "[-P password] [-m maxfragsize] [nameserver] topdomain\n", __progname); exit(2); } @@ -840,19 +966,19 @@ static void help() { extern char *__progname; - printf("iodine IP over DNS tunneling client\n"); - printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] " + fprintf(stderr, "iodine IP over DNS tunneling client\n"); + fprintf(stderr, "Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] " "[-P password] [-m maxfragsize] [nameserver] topdomain\n", __progname); - printf(" -v to print version info and exit\n"); - printf(" -h to print this help and exit\n"); - printf(" -f to keep running in foreground\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"); - printf(" -P password used for authentication (max 32 chars will be used)\n"); - printf(" -m maxfragsize, to limit size of downstream packets\n"); - printf("nameserver is the IP number of the relaying nameserver, if absent /etc/resolv.conf is used\n"); - printf("topdomain is the FQDN that is delegated to the tunnel endpoint.\n"); + fprintf(stderr, " -v to print version info and exit\n"); + fprintf(stderr, " -h to print this help and exit\n"); + fprintf(stderr, " -f to keep running in foreground\n"); + fprintf(stderr, " -u name to drop privileges and run as user 'name'\n"); + fprintf(stderr, " -t dir to chroot to directory dir\n"); + fprintf(stderr, " -d device to set tunnel device name\n"); + fprintf(stderr, " -P password used for authentication (max 32 chars will be used)\n"); + fprintf(stderr, " -m maxfragsize, to limit size of downstream packets\n"); + fprintf(stderr, "nameserver is the IP number of the relaying nameserver, if absent /etc/resolv.conf is used\n"); + fprintf(stderr, "topdomain is the FQDN that is delegated to the tunnel endpoint.\n"); exit(0); } @@ -860,7 +986,7 @@ help() { static void version() { printf("iodine IP over DNS tunneling client\n"); - printf("version: 0.5.0 from 2009-01-23\n"); + printf("version: 0.5.1 from 2009-03-21\n"); exit(0); } @@ -869,7 +995,9 @@ int main(int argc, char **argv) { char *nameserv_addr; +#ifndef WINDOWS32 struct passwd *pw; +#endif char *username; int foreground; char *newroot; @@ -877,6 +1005,8 @@ main(int argc, char **argv) int choice; int tun_fd; int dns_fd; + int max_downstream_frag_size; + int autodetect_frag_size; memset(password, 0, 33); username = NULL; @@ -893,6 +1023,10 @@ main(int argc, char **argv) b32 = get_base32_encoder(); dataenc = get_base32_encoder(); + +#ifdef WINDOWS32 + WSAStartup(req_version, &wsa_data); +#endif #if !defined(BSD) && !defined(__GLIBC__) __progname = strrchr(argv[0], '/'); @@ -941,11 +1075,7 @@ main(int argc, char **argv) } } - if (geteuid() != 0) { - warnx("Run as root and you'll be happy.\n"); - usage(); - /* NOTREACHED */ - } + check_superuser(usage); argc -= optind; argv += optind; @@ -985,11 +1115,13 @@ main(int argc, char **argv) } if (username != NULL) { +#ifndef WINDOWS32 if ((pw = getpwnam(username)) == NULL) { warnx("User %s does not exist!\n", username); usage(); /* NOTREACHED */ } +#endif } if (strlen(password) == 0) @@ -1003,10 +1135,10 @@ main(int argc, char **argv) signal(SIGINT, sighandler); signal(SIGTERM, sighandler); - if(handshake(dns_fd)) + if(handshake(dns_fd, autodetect_frag_size, max_downstream_frag_size)) goto cleanup2; - printf("Sending queries for %s to %s\n", topdomain, nameserv_addr); + fprintf(stderr, "Sending queries for %s to %s\n", topdomain, nameserv_addr); if (foreground == 0) do_detach(); @@ -1015,6 +1147,7 @@ main(int argc, char **argv) do_chroot(newroot); if (username != NULL) { +#ifndef WINDOWS32 gid_t gids[1]; gids[0] = pw->pw_gid; if (setgroups(1, gids) < 0 || setgid(pw->pw_gid) < 0 || setuid(pw->pw_uid) < 0) { @@ -1022,6 +1155,7 @@ main(int argc, char **argv) usage(); /* NOTREACHED */ } +#endif } downstream_seqno = 0; diff --git a/src/iodined.c b/src/iodined.c index 25bddc6..bf35c70 100644 --- a/src/iodined.c +++ b/src/iodined.c @@ -23,22 +23,30 @@ #include #include #include +#include +#include +#include + +#ifdef WINDOWS32 +#include "windows.h" +#include +#else +#include +#ifdef DARWIN +#include +#endif #define _XPG4_2 #include -#include -#include #include -#include -#include -#include #include #include #include #include -#include -#include -#ifdef DARWIN -#include +#include +#include +#include +#include +#include #endif #include "common.h" @@ -52,6 +60,11 @@ #include "fw_query.h" #include "version.h" +#ifdef WINDOWS32 +WORD req_version = MAKEWORD(2, 2); +WSADATA wsa_data; +#endif + static int running = 1; static char *topdomain; static char password[33]; @@ -81,6 +94,23 @@ sigint(int sig) running = 0; } +#ifdef WINDOWS32 +#define LOG_EMERG 0 +#define LOG_ALERT 1 +#define LOG_CRIT 2 +#define LOG_ERR 3 +#define LOG_WARNING 4 +#define LOG_NOTICE 5 +#define LOG_INFO 6 +#define LOG_DEBUG 7 +static void +syslog(int a, const char *str, ...) +{ + /* TODO: implement (add to event log), move to common.c */ + ; +} +#endif + static int check_user_and_ip(int userid, struct query *q) { @@ -208,7 +238,7 @@ send_chunk(int dns_fd, int userid) { ((users[userid].outpacket.fragment & 15) << 1) | (last & 1); if (debug >= 1) { - printf("OUT pkt seq# %d, frag %d (last=%d), offset %d, fragsize %d, total %d, to user %d\n", + fprintf(stderr, "OUT pkt seq# %d, frag %d (last=%d), offset %d, fragsize %d, total %d, to user %d\n", users[userid].outpacket.seqno & 7, users[userid].outpacket.fragment & 15, last, users[userid].outpacket.offset, datalen, users[userid].outpacket.len, userid); } @@ -306,13 +336,19 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) memcpy(&(users[userid].q), q, sizeof(struct query)); users[userid].encoder = get_base32_encoder(); send_version_response(dns_fd, VERSION_ACK, users[userid].seed, userid, q); + syslog(LOG_INFO, "accepted version for user #%d from %s", + userid, inet_ntoa(tempin->sin_addr)); users[userid].q.id = 0; } else { /* No space for another user */ send_version_response(dns_fd, VERSION_FULL, created_users, 0, q); + syslog(LOG_INFO, "dropped user from %s, server full", + inet_ntoa(((struct sockaddr_in *) &q->from)->sin_addr)); } } else { send_version_response(dns_fd, VERSION_NACK, VERSION, 0, q); + syslog(LOG_INFO, "dropped user from %s, sent bad version %08X", + inet_ntoa(((struct sockaddr_in *) &q->from)->sin_addr), version); } return; } else if(in[0] == 'L' || in[0] == 'l') { @@ -322,6 +358,8 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) if (check_user_and_ip(userid, q) != 0) { write_dns(dns_fd, q, "BADIP", 5); + syslog(LOG_WARNING, "dropped login request from user #%d from unexpected source %s", + userid, inet_ntoa(((struct sockaddr_in *) &q->from)->sin_addr)); return; } else { users[userid].last_pkt = time(NULL); @@ -340,11 +378,14 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) write_dns(dns_fd, q, out, read); q->id = 0; + syslog(LOG_NOTICE, "accepted password from user #%d, given IP %s", userid, tmp[1]); free(tmp[1]); free(tmp[0]); } else { write_dns(dns_fd, q, "LNAK", 4); + syslog(LOG_WARNING, "rejected login request from user #%d from %s, bad password", + userid, inet_ntoa(((struct sockaddr_in *) &q->from)->sin_addr)); } } return; @@ -441,7 +482,7 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) } if (debug >= 1) { - printf("PING pkt from user %d\n", userid); + fprintf(stderr, "PING pkt from user %d\n", userid); } if (users[userid].q.id != 0) { @@ -491,7 +532,7 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) up_frag <= users[userid].inpacket.fragment) { /* Got repeated old packet, skip it */ if (debug >= 1) { - printf("IN pkt seq# %d, frag %d, dropped duplicate\n", + fprintf(stderr, "IN pkt seq# %d, frag %d, dropped duplicate\n", up_seq, up_frag); } /* Update seqno and maybe send immediate response packet */ @@ -516,7 +557,7 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) users[userid].inpacket.offset += read; if (debug >= 1) { - printf("IN pkt seq# %d, frag %d (last=%d), fragsize %d, total %d, from user %d\n", + fprintf(stderr, "IN pkt seq# %d, frag %d (last=%d), fragsize %d, total %d, from user %d\n", up_seq, up_frag, lastfrag, read, users[userid].inpacket.len, userid); } @@ -542,7 +583,7 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) } } } else { - printf("Discarded data, uncompress() result: %d\n", ret); + fprintf(stderr, "Discarded data, uncompress() result: %d\n", ret); } users[userid].inpacket.len = users[userid].inpacket.offset = 0; } @@ -567,7 +608,7 @@ handle_ns_request(int dns_fd, struct query *q) if (debug >= 2) { struct sockaddr_in *tempin; tempin = (struct sockaddr_in *) &(q->from); - printf("TX: client %s, type %d, name %s, %d bytes NS reply\n", + fprintf(stderr, "TX: client %s, type %d, name %s, %d bytes NS reply\n", inet_ntoa(tempin->sin_addr), q->type, q->name, len); } if (sendto(dns_fd, buf, len, 0, (struct sockaddr*)&q->from, q->fromlen) <= 0) { @@ -598,7 +639,7 @@ forward_query(int bind_fd, struct query *q) myaddr->sin_port = htons(bind_port); if (debug >= 2) { - printf("TX: NS reply \n"); + fprintf(stderr, "TX: NS reply \n"); } if (sendto(bind_fd, buf, len, 0, (struct sockaddr*)&q->from, q->fromlen) <= 0) { @@ -613,7 +654,7 @@ tunnel_bind(int bind_fd, int dns_fd) struct sockaddr_in from; socklen_t fromlen; struct fw_query *query; - short id; + unsigned short id; int r; fromlen = sizeof(struct sockaddr); @@ -626,20 +667,20 @@ tunnel_bind(int bind_fd, int dns_fd) id = dns_get_id(packet, r); if (debug >= 2) { - printf("RX: Got response on query %u from DNS\n", (id & 0xFFFF)); + fprintf(stderr, "RX: Got response on query %u from DNS\n", (id & 0xFFFF)); } /* Get sockaddr from id */ fw_query_get(id, &query); if (!query && debug >= 2) { - printf("Lost sender of id %u, dropping reply\n", (id & 0xFFFF)); + fprintf(stderr, "Lost sender of id %u, dropping reply\n", (id & 0xFFFF)); return 0; } if (debug >= 2) { struct sockaddr_in *in; in = (struct sockaddr_in *) &(query->addr); - printf("TX: client %s id %u, %d bytes\n", + fprintf(stderr, "TX: client %s id %u, %d bytes\n", inet_ntoa(in->sin_addr), (id & 0xffff), r); } @@ -666,7 +707,7 @@ tunnel_dns(int tun_fd, int dns_fd, int bind_fd) if (debug >= 2) { struct sockaddr_in *tempin; tempin = (struct sockaddr_in *) &(q.from); - printf("RX: client %s, type %d, name %s\n", + fprintf(stderr, "RX: client %s, type %d, name %s\n", inet_ntoa(tempin->sin_addr), q.type, q.name); } @@ -774,11 +815,12 @@ read_dns(int fd, struct query *q) struct sockaddr_in from; socklen_t addrlen; char packet[64*1024]; + int r; +#ifndef WINDOWS32 char address[96]; struct msghdr msg; struct iovec iov; struct cmsghdr *cmsg; - int r; addrlen = sizeof(struct sockaddr); iov.iov_base = packet; @@ -793,12 +835,17 @@ read_dns(int fd, struct query *q) msg.msg_flags = 0; r = recvmsg(fd, &msg, 0); +#else + addrlen = sizeof(struct sockaddr); + r = recvfrom(fd, packet, sizeof(packet), 0, (struct sockaddr*)&from, &addrlen); +#endif /* !WINDOWS32 */ if (r > 0) { dns_decode(NULL, 0, q, QR_QUERY, packet, r); memcpy((struct sockaddr*)&q->from, (struct sockaddr*)&from, addrlen); q->fromlen = addrlen; +#ifndef WINDOWS32 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { @@ -809,6 +856,7 @@ read_dns(int fd, struct query *q) break; } } +#endif return strlen(q->name); } else if (r < 0) { @@ -830,7 +878,7 @@ write_dns(int fd, struct query *q, char *data, int datalen) if (debug >= 2) { struct sockaddr_in *tempin; tempin = (struct sockaddr_in *) &(q->from); - printf("TX: client %s, type %d, name %s, %d bytes data\n", + fprintf(stderr, "TX: client %s, type %d, name %s, %d bytes data\n", inet_ntoa(tempin->sin_addr), q->type, q->name, datalen); } @@ -841,7 +889,7 @@ static void usage() { extern char *__progname; - printf("Usage: %s [-v] [-h] [-c] [-s] [-f] [-D] [-u user] " + fprintf(stderr, "Usage: %s [-v] [-h] [-c] [-s] [-f] [-D] [-u user] " "[-t chrootdir] [-d device] [-m mtu] " "[-l ip address to listen on] [-p port] [-n external ip] [-b dnsport] [-P password]" " tunnel_ip[/netmask] topdomain\n", __progname); @@ -852,46 +900,49 @@ static void help() { extern char *__progname; - printf("iodine IP over DNS tunneling server\n"); - printf("Usage: %s [-v] [-h] [-c] [-s] [-f] [-D] [-u user] " + fprintf(stderr, "iodine IP over DNS tunneling server\n"); + fprintf(stderr, "Usage: %s [-v] [-h] [-c] [-s] [-f] [-D] [-u user] " "[-t chrootdir] [-d device] [-m mtu] " "[-l ip address to listen on] [-p port] [-n external ip] [-b dnsport] [-P password]" " tunnel_ip[/netmask] 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, " + fprintf(stderr, " -v to print version info and exit\n"); + fprintf(stderr, " -h to print this help and exit\n"); + fprintf(stderr, " -c to disable check of client IP/port on each request\n"); + fprintf(stderr, " -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"); - printf(" -m mtu to set tunnel device mtu\n"); - printf(" -l ip address to listen on for incoming dns traffic " + fprintf(stderr, " -f to keep running in foreground\n"); + fprintf(stderr, " -D to increase debug level\n"); + fprintf(stderr, " -u name to drop privileges and run as user 'name'\n"); + fprintf(stderr, " -t dir to chroot to directory dir\n"); + fprintf(stderr, " -d device to set tunnel device name\n"); + fprintf(stderr, " -m mtu to set tunnel device mtu\n"); + fprintf(stderr, " -l ip address to listen on for incoming dns traffic " "(default 0.0.0.0)\n"); - printf(" -p port to listen on for incoming dns traffic (default 53)\n"); - printf(" -n ip to respond with to NS queries\n"); - printf(" -b port to forward normal DNS queries to (on localhost)\n"); - printf(" -P password used for authentication (max 32 chars will be used)\n"); - printf("tunnel_ip is the IP number of the local tunnel interface.\n"); - printf(" /netmask sets the size of the tunnel network.\n"); - printf("topdomain is the FQDN that is delegated to this server.\n"); + fprintf(stderr, " -p port to listen on for incoming dns traffic (default 53)\n"); + fprintf(stderr, " -n ip to respond with to NS queries\n"); + fprintf(stderr, " -b port to forward normal DNS queries to (on localhost)\n"); + fprintf(stderr, " -P password used for authentication (max 32 chars will be used)\n"); + fprintf(stderr, "tunnel_ip is the IP number of the local tunnel interface.\n"); + fprintf(stderr, " /netmask sets the size of the tunnel network.\n"); + fprintf(stderr, "topdomain is the FQDN that is delegated to this server.\n"); exit(0); } static void version() { printf("iodine IP over DNS tunneling server\n"); - printf("version: 0.5.0 from 2009-01-23\n"); + printf("version: 0.5.1 from 2009-03-21\n"); exit(0); } int main(int argc, char **argv) { + extern char *__progname; in_addr_t listen_ip; +#ifndef WINDOWS32 struct passwd *pw; +#endif int foreground; char *username; char *newroot; @@ -916,7 +967,7 @@ main(int argc, char **argv) foreground = 0; bind_enable = 0; bind_fd = 0; - mtu = 1024; + mtu = 1200; listen_ip = INADDR_ANY; port = 53; ns_ip = INADDR_ANY; @@ -926,6 +977,10 @@ main(int argc, char **argv) netmask = 27; b32 = get_base32_encoder(); + +#ifdef WINDOWS32 + WSAStartup(req_version, &wsa_data); +#endif #if !defined(BSD) && !defined(__GLIBC__) __progname = strrchr(argv[0], '/'); @@ -1000,10 +1055,7 @@ main(int argc, char **argv) argc -= optind; argv += optind; - if (geteuid() != 0) { - warnx("Run as root and you'll be happy.\n"); - usage(); - } + check_superuser(usage); if (argc != 2) usage(); @@ -1034,10 +1086,12 @@ main(int argc, char **argv) } if (username != NULL) { +#ifndef WINDOWS32 if ((pw = getpwnam(username)) == NULL) { warnx("User %s does not exist!\n", username); usage(); } +#endif } if (mtu <= 0) { @@ -1056,18 +1110,18 @@ main(int argc, char **argv) usage(); /* NOTREACHED */ } - printf("Requests for domains outside of %s will be forwarded to port %d\n", + fprintf(stderr, "Requests for domains outside of %s will be forwarded to port %d\n", topdomain, bind_port); } 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); + 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); } if (debug) { - printf("Debug level %d enabled, will stay in foreground.\n", debug); - printf("Add more -D switches to set higher debug level.\n"); + fprintf(stderr, "Debug level %d enabled, will stay in foreground.\n", debug); + fprintf(stderr, "Add more -D switches to set higher debug level.\n"); foreground = 1; } @@ -1104,10 +1158,10 @@ main(int argc, char **argv) created_users = init_users(my_ip, netmask); if (created_users < USERS) { - printf("Limiting to %d simultaneous users because of netmask /%d\n", + fprintf(stderr, "Limiting to %d simultaneous users because of netmask /%d\n", created_users, netmask); } - printf("Listening to dns for domain %s\n", topdomain); + fprintf(stderr, "Listening to dns for domain %s\n", topdomain); if (foreground == 0) do_detach(); @@ -1117,16 +1171,24 @@ main(int argc, char **argv) signal(SIGINT, sigint); if (username != NULL) { +#ifndef WINDOWS32 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(); } +#endif } + +#ifndef WINDOWS32 + openlog(__progname, LOG_NOWAIT, LOG_DAEMON); +#endif + syslog(LOG_INFO, "started, listening on port %d", port); tunnel(tun_fd, dnsd_fd, bind_fd); + syslog(LOG_INFO, "stopping"); cleanup3: close_dns(bind_fd); cleanup2: diff --git a/src/login.c b/src/login.c index 3aee250..cce0f31 100644 --- a/src/login.c +++ b/src/login.c @@ -15,9 +15,14 @@ */ #include -#include #include +#ifdef WINDOWS32 +#include "windows.h" +#else +#include +#endif + #include "md5.h" /* diff --git a/src/osflags b/src/osflags index 2e28ee8..fc6da1c 100755 --- a/src/osflags +++ b/src/osflags @@ -1,9 +1,9 @@ #!/bin/sh -case $1 in +case $2 in link) - case `uname` in + case $1 in SunOS | solaris) echo '-lsocket -lnsl'; ;; @@ -13,10 +13,13 @@ link) Haiku) echo '-lnetwork'; ;; + windows32) + echo '-lws2_32 -liphlpapi'; + ;; esac ;; cflags) - case `uname` in + case $1 in BeOS) echo '-Dsocklen_t=int'; ;; diff --git a/src/tun.c b/src/tun.c index ba2be65..57b369d 100644 --- a/src/tun.c +++ b/src/tun.c @@ -23,16 +23,39 @@ #include #include #include + +#ifdef WINDOWS32 +#include +#include +#include "windows.h" + +HANDLE dev_handle; +struct tun_data data; + +#define TAP_CONTROL_CODE(request,method) CTL_CODE(FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS) +#define TAP_IOCTL_CONFIG_TUN TAP_CONTROL_CODE(10, METHOD_BUFFERED) +#define TAP_IOCTL_SET_MEDIA_STATUS TAP_CONTROL_CODE(6, METHOD_BUFFERED) + +#define TAP_ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}" +#define TAP_DEVICE_SPACE "\\\\.\\Global\\" +#define TAP_VERSION_ID_0801 "tap0801" +#define TAP_VERSION_ID_0901 "tap0901" +#define KEY_COMPONENT_ID "ComponentId" +#define NET_CFG_INST_ID "NetCfgInstanceId" +#else #include #include #include -#include "tun.h" - #define TUN_MAX_TRY 50 +#endif + +#include "tun.h" +#include "common.h" char if_name[50]; +#ifndef WINDOWS32 #ifdef LINUX #include @@ -63,7 +86,7 @@ open_tun(const char *tun_device) if_name[sizeof(if_name)-1] = '\0'; if (ioctl(tun_fd, TUNSETIFF, (void *) &ifreq) != -1) { - printf("Opened %s\n", ifreq.ifr_name); + fprintf(stderr, "Opened %s\n", ifreq.ifr_name); return tun_fd; } @@ -76,7 +99,7 @@ open_tun(const char *tun_device) snprintf(ifreq.ifr_name, IFNAMSIZ, "dns%d", i); if (ioctl(tun_fd, TUNSETIFF, (void *) &ifreq) != -1) { - printf("Opened %s\n", ifreq.ifr_name); + fprintf(stderr, "Opened %s\n", ifreq.ifr_name); snprintf(if_name, sizeof(if_name), "dns%d", i); return tun_fd; } @@ -111,14 +134,14 @@ open_tun(const char *tun_device) return -1; } - printf("Opened %s\n", tun_name); + fprintf(stderr, "Opened %s\n", tun_name); return tun_fd; } else { for (i = 0; i < TUN_MAX_TRY; i++) { snprintf(tun_name, sizeof(tun_name), "/dev/tun%d", i); if ((tun_fd = open(tun_name, O_RDWR)) >= 0) { - printf("Opened %s\n", tun_name); + fprintf(stderr, "Opened %s\n", tun_name); snprintf(if_name, sizeof(if_name), "tun%d", i); return tun_fd; } @@ -134,6 +157,148 @@ open_tun(const char *tun_device) } #endif /* !LINUX */ +#else /* WINDOWS32 */ +static void +get_device(char *device, int device_len) +{ + LONG status; + HKEY adapter_key; + int index; + + index = 0; + status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TAP_ADAPTER_KEY, 0, KEY_READ, &adapter_key); + + if (status != ERROR_SUCCESS) { + warnx("Error opening registry key " TAP_ADAPTER_KEY ); + return; + } + + while (TRUE) { + char name[256]; + char unit[256]; + char component[256]; + + char cid_string[256] = KEY_COMPONENT_ID; + HKEY device_key; + DWORD datatype; + DWORD len; + + /* Iterate through all adapter of this kind */ + len = sizeof(name); + status = RegEnumKeyEx(adapter_key, index, name, &len, NULL, NULL, NULL, NULL); + if (status == ERROR_NO_MORE_ITEMS) { + break; + } else if (status != ERROR_SUCCESS) { + warnx("Error enumerating subkeys of registry key " TAP_ADAPTER_KEY ); + break; + } + + snprintf(unit, sizeof(unit), TAP_ADAPTER_KEY "\\%s", name); + status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, unit, 0, KEY_READ, &device_key); + if (status != ERROR_SUCCESS) { + warnx("Error opening registry key %s", unit); + goto next; + } + + /* Check component id */ + len = sizeof(component); + status = RegQueryValueEx(device_key, cid_string, NULL, &datatype, (LPBYTE)component, &len); + if (status != ERROR_SUCCESS || datatype != REG_SZ) { + goto next; + } + if (strncmp(TAP_VERSION_ID_0801, component, strlen(TAP_VERSION_ID_0801)) == 0 || + strncmp(TAP_VERSION_ID_0901, component, strlen(TAP_VERSION_ID_0901)) == 0) { + /* We found a TAP32 device, get its NetCfgInstanceId */ + char iid_string[256] = NET_CFG_INST_ID; + + status = RegQueryValueEx(device_key, iid_string, NULL, &datatype, (LPBYTE) device, (DWORD *) &device_len); + if (status != ERROR_SUCCESS || datatype != REG_SZ) { + warnx("Error reading registry key %s\\%s on TAP device", unit, iid_string); + } else { + /* Done getting name of TAP device */ + RegCloseKey(device_key); + return; + } + } +next: + RegCloseKey(device_key); + index++; + } + RegCloseKey(adapter_key); +} + +DWORD WINAPI tun_reader(LPVOID arg) +{ + struct tun_data *tun = arg; + char buf[64*1024]; + int len; + int res; + OVERLAPPED olpd; + int sock; + + sock = open_dns(0, INADDR_ANY); + + olpd.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + + while(TRUE) { + olpd.Offset = 0; + olpd.OffsetHigh = 0; + res = ReadFile(tun->tun, buf, sizeof(buf), (LPDWORD) &len, &olpd); + if (!res) { + 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)); + } + } + + return 0; +} + +int +open_tun(const char *tun_device) +{ + char adapter[256]; + char tapfile[512]; + int tunfd; + in_addr_t local; + + memset(adapter, 0, sizeof(adapter)); + get_device(adapter, sizeof(adapter)); + + if (strlen(adapter) == 0) { + warnx("No TAP adapters found. See README-win32.txt for help.\n"); + return -1; + } + + snprintf(tapfile, sizeof(tapfile), "%s%s.tap", TAP_DEVICE_SPACE, adapter); + fprintf(stderr, "Opening device %s\n", tapfile); + dev_handle = CreateFile(tapfile, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, NULL); + if (dev_handle == INVALID_HANDLE_VALUE) { + return -1; + } + + /* TODO get name of interface */ + strncpy(if_name, "dns", MIN(4, sizeof(if_name))); + + /* Use a UDP connection to forward packets from tun, + * so we can still use select() in main code. + * 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); + + 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; + CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)tun_reader, &data, 0, NULL); + + return tunfd; +} +#endif void close_tun(int tun_fd) @@ -145,7 +310,7 @@ close_tun(int tun_fd) int write_tun(int tun_fd, char *data, size_t len) { -#if defined (FREEBSD) || defined (DARWIN) || defined(NETBSD) +#if defined (FREEBSD) || defined (DARWIN) || defined(NETBSD) || defined(WINDOWS32) data += 4; len -= 4; #else /* !FREEBSD/DARWIN */ @@ -162,19 +327,45 @@ write_tun(int tun_fd, char *data, size_t len) #endif /* !LINUX */ #endif /* FREEBSD */ +#ifndef WINDOWS32 if (write(tun_fd, data, len) != len) { warn("write_tun"); return 1; } +#else /* WINDOWS32 */ + { + DWORD written; + DWORD res; + OVERLAPPED olpd; + + olpd.Offset = 0; + olpd.OffsetHigh = 0; + olpd.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + res = WriteFile(dev_handle, data, len, &written, &olpd); + if (!res && GetLastError() == ERROR_IO_PENDING) { + WaitForSingleObject(olpd.hEvent, INFINITE); + res = GetOverlappedResult(dev_handle, &olpd, &written, FALSE); + if (written != len) { + return -1; + } + } + } +#endif return 0; } ssize_t read_tun(int tun_fd, char *buf, size_t len) { -#if defined (FREEBSD) || defined (DARWIN) || defined(NETBSD) +#if defined (FREEBSD) || defined (DARWIN) || defined(NETBSD) || defined(WINDOWS32) /* FreeBSD/Darwin/NetBSD has no header */ - return read(tun_fd, buf + 4, len - 4) + 4; + int bytes; + bytes = recv(tun_fd, buf + 4, len, 0); + if (bytes < 0) { + return bytes; + } else { + return bytes + 4; + } #else /* !FREEBSD */ return read(tun_fd, buf, len); #endif /* !FREEBSD */ @@ -187,10 +378,16 @@ tun_setip(const char *ip, int netbits) int netmask; struct in_addr net; int i; - #ifndef LINUX int r; #endif +#ifdef WINDOWS32 + DWORD status; + DWORD ipdata[3]; + struct in_addr addr; + DWORD len; +#endif + netmask = 0; for (i = 0; i < netbits; i++) { netmask = (netmask << 1) | 1; @@ -198,51 +395,89 @@ tun_setip(const char *ip, int netbits) netmask <<= (32 - netbits); net.s_addr = htonl(netmask); - if (inet_addr(ip) != INADDR_NONE) { - snprintf(cmdline, sizeof(cmdline), - "/sbin/ifconfig %s %s %s netmask %s", - if_name, - ip, - ip, - inet_ntoa(net)); - - printf("Setting IP of %s to %s\n", if_name, ip); + if (inet_addr(ip) == INADDR_NONE) { + fprintf(stderr, "Invalid IP: %s!\n", ip); + return 1; + } +#ifndef WINDOWS32 + snprintf(cmdline, sizeof(cmdline), + "/sbin/ifconfig %s %s %s netmask %s", + if_name, + ip, + ip, + inet_ntoa(net)); + + fprintf(stderr, "Setting IP of %s to %s\n", if_name, ip); #ifndef LINUX - r = system(cmdline); - if(r != 0) { - return r; - } else { - snprintf(cmdline, sizeof(cmdline), - "/sbin/route add %s/%d %s", - ip, netbits, ip); - } - printf("Adding route %s/%d to %s\n", ip, netbits, ip); -#endif - return system(cmdline); + r = system(cmdline); + if(r != 0) { + return r; } else { - printf("Invalid IP: %s!\n", ip); + snprintf(cmdline, sizeof(cmdline), + "/sbin/route add %s/%d %s", + ip, netbits, ip); + } + fprintf(stderr, "Adding route %s/%d to %s\n", ip, netbits, ip); +#endif + return system(cmdline); +#else /* WINDOWS32 */ + + /* Set device as connected */ + fprintf(stderr, "Enabling interface '%s'\n", if_name); + status = 1; + r = DeviceIoControl(dev_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, + sizeof(status), &status, sizeof(status), &len, NULL); + if (!r) { + fprintf(stderr, "Failed to enable interface\n"); + return -1; + } + + if (inet_aton(ip, &addr)) { + ipdata[0] = (DWORD) addr.s_addr; /* local ip addr */ + ipdata[1] = net.s_addr & ipdata[0]; /* network addr */ + ipdata[2] = (DWORD) net.s_addr; /* netmask */ + } else { + return -1; } - return 1; + /* Tell ip/networkaddr/netmask to device for arp use */ + r = DeviceIoControl(dev_handle, TAP_IOCTL_CONFIG_TUN, &ipdata, + sizeof(ipdata), &ipdata, sizeof(ipdata), &len, NULL); + if (!r) { + fprintf(stderr, "Failed to set interface in TUN mode\n"); + return -1; + } + + /* use netsh to set ip address */ + fprintf(stderr, "Setting IP of interface '%s' to %s (can take a few seconds)...\n", if_name, ip); + snprintf(cmdline, sizeof(cmdline), "netsh interface ip set address \"%s\" static %s %s", + if_name, ip, inet_ntoa(net)); + return system(cmdline); +#endif } int tun_setmtu(const unsigned mtu) { +#ifndef WINDOWS32 char cmdline[512]; - if (mtu > 200 && mtu < 1500) { + if (mtu > 200 && mtu <= 1500) { snprintf(cmdline, sizeof(cmdline), "/sbin/ifconfig %s mtu %u", if_name, mtu); - printf("Setting MTU of %s to %u\n", if_name, mtu); + fprintf(stderr, "Setting MTU of %s to %u\n", if_name, mtu); return system(cmdline); } else { warn("MTU out of range: %u\n", mtu); } return 1; +#else /* WINDOWS32 */ + + return 0; +#endif } diff --git a/src/user.c b/src/user.c index 01b4021..b15665b 100644 --- a/src/user.c +++ b/src/user.c @@ -21,13 +21,18 @@ #include #include #include -#include -#include #include -#include #include + +#ifdef WINDOWS32 +#include +#else #include #include +#include +#include +#include +#endif #include "common.h" #include "encoding.h" diff --git a/src/windows.h b/src/windows.h new file mode 100644 index 0000000..f930855 --- /dev/null +++ b/src/windows.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __FIX_WINDOWS_H__ +#define __FIX_WINDOWS_H__ + +typedef unsigned int in_addr_t; + +#include +#include +#include +#include +#include + +#define T_A DNS_TYPE_A +#define T_NS DNS_TYPE_NS +#define T_NULL DNS_TYPE_NULL + +#define C_IN 1 + +#define SERVFAIL 2 +#define NXDOMAIN 3 +#define NOTIMP 4 +#define REFUSED 5 + +typedef struct { + unsigned id :16; /* query identification number */ + /* fields in third byte */ + unsigned rd :1; /* recursion desired */ + unsigned tc :1; /* truncated message */ + unsigned aa :1; /* authoritive answer */ + unsigned opcode :4; /* purpose of message */ + unsigned qr :1; /* response flag */ + /* fields in fourth byte */ + unsigned rcode :4; /* response code */ + unsigned cd: 1; /* checking disabled by resolver */ + unsigned ad: 1; /* authentic data from named */ + unsigned unused :1; /* unused bits (MBZ as of 4.9.3a3) */ + unsigned ra :1; /* recursion available */ + /* remaining bytes */ + unsigned qdcount :16; /* number of question entries */ + unsigned ancount :16; /* number of answer entries */ + unsigned nscount :16; /* number of authority entries */ + unsigned arcount :16; /* number of resource entries */ +} HEADER; + +struct ip + { + unsigned int ip_hl:4; /* header length */ + unsigned int ip_v:4; /* version */ + u_char ip_tos; /* type of service */ + u_short ip_len; /* total length */ + u_short ip_id; /* identification */ + u_short ip_off; /* fragment offset field */ +#define IP_RF 0x8000 /* reserved fragment flag */ +#define IP_DF 0x4000 /* dont fragment flag */ +#define IP_MF 0x2000 /* more fragments flag */ +#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ + u_char ip_ttl; /* time to live */ + u_char ip_p; /* protocol */ + u_short ip_sum; /* checksum */ + struct in_addr ip_src, ip_dst; /* source and dest address */ + }; + +DWORD WINAPI tun_reader(LPVOID arg); +struct tun_data { + HANDLE tun; + int sock; + struct sockaddr_in addr; +}; + +#endif diff --git a/tests/Makefile b/tests/Makefile index 79f51f0..3a7ac01 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,7 +1,7 @@ CC = gcc TEST = test -OBJS = test.o base32.o base64.o read.o dns.o encoding.o login.o user.o -SRCOBJS = ../src/base32.o ../src/base64.o ../src/read.o ../src/dns.o ../src/encoding.o ../src/login.o ../src/md5.o ../src/user.o +OBJS = test.o base32.o base64.o read.o dns.o encoding.o login.o user.o fw_query.o +SRCOBJS = ../src/base32.o ../src/base64.o ../src/read.o ../src/dns.o ../src/encoding.o ../src/login.o ../src/md5.o ../src/user.o ../src/fw_query.o OS = `uname | tr "a-z" "A-Z"` diff --git a/tests/base32.c b/tests/base32.c index a3f95bb..9ff0cf7 100644 --- a/tests/base32.c +++ b/tests/base32.c @@ -24,14 +24,18 @@ #include "base32.h" #include "test.h" +#define TUPLES 5 + static struct tuple { char *a; char *b; -} testpairs[] = { +} testpairs[TUPLES] = { { "iodinetestingtesting", "nfxwi0lomv0gk21unfxgo3dfon0gs1th" }, { "abc123", "mfrggmjsgm" }, - { NULL, NULL } + { "test", "orsxg3a" }, + { "tst", "orzxi" }, + { "", "" }, }; START_TEST(test_base32_encode) @@ -40,18 +44,14 @@ START_TEST(test_base32_encode) char buf[4096]; struct encoder *b32; int val; - int i; b32 = get_base32_encoder(); - for (i = 0; testpairs[i].a != NULL; i++) { - len = sizeof(buf); - val = b32->encode(buf, &len, testpairs[i].a, strlen(testpairs[i].a)); + len = sizeof(buf); + val = b32->encode(buf, &len, testpairs[_i].a, strlen(testpairs[_i].a)); - fail_unless(val > 0, strerror(errno)); - fail_unless(strcmp(buf, testpairs[i].b) == 0, - "'%s' != '%s'", buf, testpairs[i].b); - } + fail_unless(strcmp(buf, testpairs[_i].b) == 0, + "'%s' != '%s'", buf, testpairs[_i].b); } END_TEST @@ -61,19 +61,15 @@ START_TEST(test_base32_decode) char buf[4096]; struct encoder *b32; int val; - int i; b32 = get_base32_encoder(); - for (i = 0; testpairs[i].a != NULL; i++) { - len = sizeof(buf); - val = b32->decode(buf, &len, testpairs[i].b, strlen(testpairs[i].b)); + len = sizeof(buf); + val = b32->decode(buf, &len, testpairs[_i].b, strlen(testpairs[_i].b)); - fail_unless(val > 0, strerror(errno)); - fail_unless(buf != NULL, "buf == NULL"); - fail_unless(strcmp(buf, testpairs[i].a) == 0, - "'%s' != '%s'", buf, testpairs[i].a); - } + fail_unless(buf != NULL, "buf == NULL"); + fail_unless(strcmp(buf, testpairs[_i].a) == 0, + "'%s' != '%s'", buf, testpairs[_i].a); } END_TEST @@ -89,15 +85,58 @@ START_TEST(test_base32_5to8_8to5) } END_TEST +START_TEST(test_base32_blksize) +{ + size_t rawlen; + size_t enclen; + char *rawbuf; + char *encbuf; + struct encoder *b32; + int i; + int val; + + b32 = get_base32_encoder(); + + rawlen = b32->blocksize_raw(); + enclen = b32->blocksize_encoded(); + + rawbuf = malloc(rawlen + 16); + encbuf = malloc(enclen + 16); + + for (i = 0; i < rawlen; i++) { + rawbuf[i] = 'A'; + } + rawbuf[i] = 0; + + val = b32->encode(encbuf, &enclen, rawbuf, rawlen); + + fail_unless(rawlen == 5, "raw length was %d not 5", rawlen); + fail_unless(enclen == 5, "encoded %d bytes, not 5", enclen); + fail_unless(val == 8, "encoded string %s was length %d", encbuf, val); + + memset(rawbuf, 0, rawlen + 16); + + enclen = val; + val = b32->decode(rawbuf, &rawlen, encbuf, enclen); + + fail_unless(rawlen == 5, "raw length was %d not 5", rawlen); + fail_unless(val == 5, "val was not 5 but %d", val); + for (i = 0; i < rawlen; i++) { + fail_unless(rawbuf[i] == 'A'); + } +} +END_TEST + TCase * test_base32_create_tests() { TCase *tc; tc = tcase_create("Base32"); - tcase_add_test(tc, test_base32_encode); - tcase_add_test(tc, test_base32_decode); + tcase_add_loop_test(tc, test_base32_encode, 0, TUPLES); + tcase_add_loop_test(tc, test_base32_decode, 0, TUPLES); tcase_add_test(tc, test_base32_5to8_8to5); + tcase_add_test(tc, test_base32_blksize); return tc; } diff --git a/tests/base64.c b/tests/base64.c index 280963e..bd0e9ce 100644 --- a/tests/base64.c +++ b/tests/base64.c @@ -24,11 +24,13 @@ #include "base64.h" #include "test.h" +#define TUPLES 5 + static struct tuple { char *a; char *b; -} testpairs[] = { +} testpairs[TUPLES] = { { "iodinetestingtesting", "Aw8KAw4LDgvZDgLUz2rLC2rPBMC" }, { "abc1231", "ywjJmtiZmq" }, { @@ -59,7 +61,7 @@ static struct tuple "776543210-ZYXWVUTSRQfHKwfHGsHGFEDCBAzyxwvutsrqponmlkjihgfedcba+987654321" "0-ZYXWVUTSRQfHKwfHGsHGFEDCBAzyxwvutsrqponmlkjihgfedcba" }, - { NULL, NULL } + { "", "" } }; START_TEST(test_base64_encode) @@ -68,18 +70,14 @@ START_TEST(test_base64_encode) char buf[4096]; struct encoder *b64; int val; - int i; b64 = get_base64_encoder(); - for (i = 0; testpairs[i].a != NULL; i++) { - len = sizeof(buf); - val = b64->encode(buf, &len, testpairs[i].a, strlen(testpairs[i].a)); + len = sizeof(buf); + val = b64->encode(buf, &len, testpairs[_i].a, strlen(testpairs[_i].a)); - fail_unless(val > 0, strerror(errno)); - fail_unless(strcmp(buf, testpairs[i].b) == 0, - "'%s' != '%s'", buf, testpairs[i].b); - } + fail_unless(strcmp(buf, testpairs[_i].b) == 0, + "'%s' != '%s'", buf, testpairs[_i].b); } END_TEST @@ -89,18 +87,56 @@ START_TEST(test_base64_decode) char buf[4096]; struct encoder *b64; int val; - int i; b64 = get_base64_encoder(); - for (i = 0; testpairs[i].a != NULL; i++) { - len = sizeof(buf); - val = b64->decode(buf, &len, testpairs[i].b, strlen(testpairs[i].b)); + len = sizeof(buf); + val = b64->decode(buf, &len, testpairs[_i].b, strlen(testpairs[_i].b)); - fail_unless(val > 0, strerror(errno)); - fail_unless(buf != NULL, "buf == NULL"); - fail_unless(strcmp(buf, testpairs[i].a) == 0, - "'%s' != '%s'", buf, testpairs[i].a); + fail_unless(buf != NULL, "buf == NULL"); + fail_unless(strcmp(buf, testpairs[_i].a) == 0, + "'%s' != '%s'", buf, testpairs[_i].a); +} +END_TEST + +START_TEST(test_base64_blksize) +{ + size_t rawlen; + size_t enclen; + char *rawbuf; + char *encbuf; + struct encoder *b64; + int i; + int val; + + b64 = get_base64_encoder(); + + rawlen = b64->blocksize_raw(); + enclen = b64->blocksize_encoded(); + + rawbuf = malloc(rawlen + 16); + encbuf = malloc(enclen + 16); + + for (i = 0; i < rawlen; i++) { + rawbuf[i] = 'A'; + } + rawbuf[i] = 0; + + val = b64->encode(encbuf, &enclen, rawbuf, rawlen); + + fail_unless(rawlen == 3, "raw length was %d not 3", rawlen); + fail_unless(enclen == 3, "encoded %d bytes, not 3", enclen); + fail_unless(val == 4, "encoded string %s was length %d", encbuf, val); + + memset(rawbuf, 0, rawlen + 16); + + enclen = val; + val = b64->decode(rawbuf, &rawlen, encbuf, enclen); + + fail_unless(rawlen == 3, "raw length was %d not 3", rawlen); + fail_unless(val == 3); + for (i = 0; i < rawlen; i++) { + fail_unless(rawbuf[i] == 'A'); } } END_TEST @@ -111,8 +147,9 @@ test_base64_create_tests() TCase *tc; tc = tcase_create("Base64"); - tcase_add_test(tc, test_base64_encode); - tcase_add_test(tc, test_base64_decode); + tcase_add_loop_test(tc, test_base64_encode, 0, TUPLES); + tcase_add_loop_test(tc, test_base64_decode, 0, TUPLES); + tcase_add_test(tc, test_base64_blksize); return tc; } diff --git a/tests/dns.c b/tests/dns.c index 185f158..55e753b 100644 --- a/tests/dns.c +++ b/tests/dns.c @@ -33,14 +33,14 @@ static void dump_packet(char *, size_t); -static char queryPacket[] = +static char query_packet[] = "\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 answer_packet[] = "\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" @@ -48,7 +48,7 @@ static char answerPacket[] = "\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 answerPacketHighTransId[] = +static char answer_packet_high_trans_id[] = "\x85\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" @@ -87,14 +87,14 @@ START_TEST(test_encode_query) } strcpy(d, topdomain); ret = dns_encode(buf, len, &q, QR_QUERY, resolv, strlen(resolv)); - len = sizeof(queryPacket) - 1; /* Skip extra null character */ + len = sizeof(query_packet) - 1; /* Skip extra null character */ - if (strncmp(queryPacket, buf, sizeof(queryPacket)) || ret != len) { + if (strncmp(query_packet, buf, sizeof(query_packet)) || ret != len) { printf("\n"); - dump_packet(queryPacket, len); + dump_packet(query_packet, len); dump_packet(buf, ret); } - fail_unless(strncmp(queryPacket, buf, sizeof(queryPacket)) == 0, "Did not compile expected packet"); + fail_unless(strncmp(query_packet, buf, sizeof(query_packet)) == 0, "Did not compile expected packet"); fail_unless(ret == len, "Bad packet length: %d, expected %d", ret, len); } END_TEST @@ -110,10 +110,10 @@ START_TEST(test_decode_query) memset(&q, 0, sizeof(struct query)); memset(&buf, 0, sizeof(buf)); q.id = 0; - len = sizeof(queryPacket) - 1; + len = sizeof(query_packet) - 1; enc = get_base32_encoder(); - dns_decode(buf, sizeof(buf), &q, QR_QUERY, queryPacket, len); + dns_decode(buf, sizeof(buf), &q, QR_QUERY, query_packet, len); domain = strstr(q.name, topdomain); len = sizeof(buf); unpack_data(buf, len, &(q.name[1]), (int) (domain - q.name) - 1, enc); @@ -139,9 +139,9 @@ START_TEST(test_encode_response) q.id = 1337; ret = dns_encode(buf, len, &q, QR_ANSWER, msgData, strlen(msgData)); - len = sizeof(answerPacket) - 1; /* Skip extra null character */ + len = sizeof(answer_packet) - 1; /* Skip extra null character */ - fail_unless(strncmp(answerPacket, buf, sizeof(answerPacket)) == 0, "Did not compile expected packet"); + fail_unless(strncmp(answer_packet, buf, sizeof(answer_packet)) == 0, "Did not compile expected packet"); fail_unless(ret == len, "Bad packet length: %d, expected %d", ret, len); } END_TEST @@ -156,7 +156,7 @@ START_TEST(test_decode_response) len = sizeof(buf); memset(&buf, 0, sizeof(buf)); - ret = dns_decode(buf, len, &q, QR_ANSWER, answerPacket, sizeof(answerPacket)-1); + ret = dns_decode(buf, len, &q, QR_ANSWER, answer_packet, sizeof(answer_packet)-1); fail_unless(strncmp(msgData, buf, sizeof(msgData)) == 0, "Did not extract expected data"); fail_unless(ret == strlen(msgData), "Bad data length: %d, expected %d", ret, strlen(msgData)); fail_unless(q.id == 0x0539); @@ -173,12 +173,45 @@ START_TEST(test_decode_response_with_high_trans_id) len = sizeof(buf); memset(&buf, 0, sizeof(buf)); - ret = dns_decode(buf, len, &q, QR_ANSWER, answerPacketHighTransId, sizeof(answerPacketHighTransId)-1); + ret = dns_decode(buf, len, &q, QR_ANSWER, answer_packet_high_trans_id, sizeof(answer_packet_high_trans_id)-1); fail_unless(strncmp(msgData, buf, sizeof(msgData)) == 0, "Did not extract expected data"); fail_unless(ret == strlen(msgData), "Bad data length: %d, expected %d", ret, strlen(msgData)); fail_unless(q.id == 0x8539, "q.id was %08X instead of %08X!", q.id, 0x8539); } END_TEST + +START_TEST(test_get_id_short_packet) +{ + char buf[5]; + int len; + unsigned short id; + + len = sizeof(buf); + memset(&buf, 5, sizeof(buf)); + + id = dns_get_id(buf, len); + fail_unless(id == 0); +} +END_TEST + +START_TEST(test_get_id_low) +{ + unsigned short id; + + id = dns_get_id(answer_packet, sizeof(answer_packet)); + fail_unless(id == 1337); +} +END_TEST + +START_TEST(test_get_id_high) +{ + unsigned short id; + + id = dns_get_id(answer_packet_high_trans_id, sizeof(answer_packet_high_trans_id)); + fail_unless(id == 0x8539); +} +END_TEST + static void dump_packet(char *buf, size_t len) { @@ -209,6 +242,9 @@ test_dns_create_tests() tcase_add_test(tc, test_encode_response); tcase_add_test(tc, test_decode_response); tcase_add_test(tc, test_decode_response_with_high_trans_id); + tcase_add_test(tc, test_get_id_short_packet); + tcase_add_test(tc, test_get_id_low); + tcase_add_test(tc, test_get_id_high); return tc; } diff --git a/tests/fw_query.c b/tests/fw_query.c new file mode 100644 index 0000000..6d23924 --- /dev/null +++ b/tests/fw_query.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2006-2009 Erik Ekman + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "fw_query.h" +#include "test.h" + +START_TEST(test_fw_query_simple) +{ + struct fw_query q; + struct fw_query *qp; + + q.addrlen = 33; + q.id = 0x848A; + + fw_query_init(); + + /* Test empty cache */ + fw_query_get(0x848A, &qp); + fail_unless(qp == NULL); + + fw_query_put(&q); + + /* Test cache with one entry */ + fw_query_get(0x848A, &qp); + fail_unless(qp->addrlen == q.addrlen); + fail_unless(qp->id == q.id); +} +END_TEST + +START_TEST(test_fw_query_edge) +{ + struct fw_query q; + struct fw_query *qp; + int i; + + fw_query_init(); + + q.addrlen = 33; + q.id = 0x848A; + fw_query_put(&q); + + for (i = 1; i < FW_QUERY_CACHE_SIZE; i++) { + q.addrlen++; + q.id++; + fw_query_put(&q); + } + + /* The query should still be cached */ + fw_query_get(0x848A, &qp); + fail_unless(qp->addrlen == 33); + fail_unless(qp->id == 0x848A); + + q.addrlen++; + q.id++; + fw_query_put(&q); + + /* but now it is overwritten */ + fw_query_get(0x848A, &qp); + fail_unless(qp == NULL); +} +END_TEST + +TCase * +test_fw_query_create_tests() +{ + TCase *tc; + + tc = tcase_create("Forwarded query"); + tcase_add_test(tc, test_fw_query_simple); + tcase_add_test(tc, test_fw_query_edge); + + return tc; +} diff --git a/tests/login.c b/tests/login.c index 2745c92..1ef23f4 100644 --- a/tests/login.c +++ b/tests/login.c @@ -28,7 +28,7 @@ START_TEST(test_login_hash) int len; int seed; - len = 16; + len = sizeof(ans); seed = 15; memset(ans, 0, sizeof(ans)); @@ -37,6 +37,26 @@ START_TEST(test_login_hash) } END_TEST +START_TEST(test_login_hash_short) +{ + char ans[8]; + char check[sizeof(ans)]; + char pass[32] = "iodine is the shit"; + int len; + int seed; + + len = sizeof(ans); + seed = 15; + + memset(ans, 0, sizeof(ans)); + memset(check, 0, sizeof(check)); + + /* If len < 16, it should do nothing */ + login_calculate(ans, len, pass, seed); + fail_if(memcmp(ans, check, sizeof(ans))); +} +END_TEST + TCase * test_login_create_tests() { @@ -44,6 +64,7 @@ test_login_create_tests() tc = tcase_create("Login"); tcase_add_test(tc, test_login_hash); + tcase_add_test(tc, test_login_hash_short); return tc; } diff --git a/tests/read.c b/tests/read.c index ab55341..18cc29c 100644 --- a/tests/read.c +++ b/tests/read.c @@ -83,14 +83,42 @@ START_TEST(test_read_putlong) } END_TEST -START_TEST(test_read_name) +START_TEST(test_read_name_empty_loop) { unsigned char emptyloop[] = { 'A', 'A', 0x81, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01 }; + char buf[1024]; + char *data; + int rv; + + memset(buf, 0, sizeof(buf)); + data = (char*) emptyloop + sizeof(HEADER); + buf[1023] = 'A'; + rv = readname((char *) emptyloop, sizeof(emptyloop), &data, buf, 1023); + fail_unless(buf[1023] == 'A'); +} +END_TEST + +START_TEST(test_read_name_inf_loop) +{ unsigned char infloop[] = { 'A', 'A', 0x81, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 'A', 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01 }; + char buf[1024]; + char *data; + int rv; + + 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'); +} +END_TEST + +START_TEST(test_read_name_longname) +{ unsigned char longname[] = "AA\x81\x80\x00\x01\x00\x00\x00\x00\x00\x00" "\x3FzBCDEFGHIJKLMNOPQURSTUVXYZ0123456789abcdefghijklmnopqrstuvxyzAA" @@ -100,12 +128,61 @@ START_TEST(test_read_name) "\x3FzBCDEFGHIJKLMNOPQURSTUVXYZ0123456789abcdefghijklmnopqrstuvxyzAA" "\x3FzBCDEFGHIJKLMNOPQURSTUVXYZ0123456789abcdefghijklmnopqrstuvxyzAA" "\x00\x00\x01\x00\x01"; + char buf[1024]; + char *data; + int rv; + + memset(buf, 0, sizeof(buf)); + data = (char*) longname + sizeof(HEADER); + buf[256] = '\a'; + rv = readname((char*) longname, sizeof(longname), &data, buf, 256); + fail_unless(buf[256] == '\a'); +} +END_TEST + +START_TEST(test_read_name_onejump) +{ unsigned char onejump[] = "AA\x81\x80\x00\x01\x00\x00\x00\x00\x00\x00" "\x02hh\xc0\x15\x00\x01\x00\x01\x05zBCDE\x00"; + char buf[1024]; + char *data; + int rv; + + memset(buf, 0, sizeof(buf)); + data = (char*) onejump + sizeof(HEADER); + rv = readname((char*) onejump, sizeof(onejump), &data, buf, 256); + fail_unless(rv == 9); +} +END_TEST + +START_TEST(test_read_name_badjump_start) +{ unsigned char badjump[] = { 'A', 'A', 0x81, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x00, 0x01, 0x00, 0x01 }; + unsigned char *jumper; + char buf[1024]; + char *data; + int rv; + + /* This test uses malloc to cause segfault if jump is executed */ + memset(buf, 0, sizeof(buf)); + jumper = malloc(sizeof(badjump)); + if (jumper) { + memcpy(jumper, badjump, sizeof(badjump)); + data = (char*) jumper + sizeof(HEADER); + rv = readname((char*) jumper, sizeof(badjump), &data, buf, 256); + + fail_unless(rv == 0); + fail_unless(buf[0] == 0); + } + free(jumper); +} +END_TEST + +START_TEST(test_read_name_badjump_second) +{ unsigned char badjump2[] = { 'A', 'A', 0x81, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 'B', 'A', 0xfe, 0xcc, 0x00, 0x01, 0x00, 0x01 }; @@ -114,42 +191,7 @@ START_TEST(test_read_name) char *data; int rv; - memset(buf, 0, sizeof(buf)); - data = (char*) emptyloop + sizeof(HEADER); - 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'; - rv = readname((char*) longname, sizeof(longname), &data, buf, 256); - fail_unless(buf[256] == '\a', NULL); - - memset(buf, 0, sizeof(buf)); - 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)); - if (jumper) { - memcpy(jumper, badjump, sizeof(badjump)); - data = (char*) jumper + sizeof(HEADER); - rv = readname((char*) jumper, sizeof(badjump), &data, buf, 256); - - fail_unless(rv == 0, NULL); - fail_unless(buf[0] == 0, NULL); - } - free(jumper); - + /* This test uses malloc to cause segfault if jump is executed */ memset(buf, 0, sizeof(buf)); jumper = malloc(sizeof(badjump2)); if (jumper) { @@ -157,7 +199,7 @@ START_TEST(test_read_name) data = (char*) jumper + sizeof(HEADER); rv = readname((char*) jumper, sizeof(badjump2), &data, buf, 256); - fail_unless(rv == 4, NULL); + fail_unless(rv == 4); fail_unless(strcmp("BA.", buf) == 0, "buf is not BA: %s", buf); } @@ -180,7 +222,7 @@ START_TEST(test_putname) b = buf; ret = putname(&b, 256, domain); - fail_unless(ret == strlen(domain) + 1, NULL); + fail_unless(ret == strlen(domain) + 1); fail_unless(strncmp(buf, out, ret) == 0, "Happy flow failed"); } END_TEST @@ -201,8 +243,8 @@ START_TEST(test_putname_nodot) b = buf; ret = putname(&b, 256, nodot); - fail_unless(ret == -1, NULL); - fail_unless(b == buf, NULL); + fail_unless(ret == -1); + fail_unless(b == buf); } END_TEST @@ -226,8 +268,8 @@ START_TEST(test_putname_toolong) b = buf; ret = putname(&b, 256, toolong); - fail_unless(ret == -1, NULL); - fail_unless(b == buf, NULL); + fail_unless(ret == -1); + fail_unless(b == buf); } END_TEST @@ -241,7 +283,12 @@ test_read_create_tests() tcase_set_timeout(tc, 60); tcase_add_test(tc, test_read_putshort); tcase_add_test(tc, test_read_putlong); - tcase_add_test(tc, test_read_name); + tcase_add_test(tc, test_read_name_empty_loop); + tcase_add_test(tc, test_read_name_inf_loop); + tcase_add_test(tc, test_read_name_longname); + tcase_add_test(tc, test_read_name_onejump); + tcase_add_test(tc, test_read_name_badjump_start); + tcase_add_test(tc, test_read_name_badjump_second); tcase_add_test(tc, test_putname); tcase_add_test(tc, test_putname_nodot); tcase_add_test(tc, test_putname_toolong); diff --git a/tests/test.c b/tests/test.c index 8e63597..5bee9d2 100644 --- a/tests/test.c +++ b/tests/test.c @@ -53,6 +53,9 @@ main() test = test_user_create_tests(); suite_add_tcase(iodine, test); + test = test_fw_query_create_tests(); + suite_add_tcase(iodine, test); + runner = srunner_create(iodine); srunner_run_all(runner, CK_NORMAL); failed = srunner_ntests_failed(runner); diff --git a/tests/test.h b/tests/test.h index 54a3fff..1022a9e 100644 --- a/tests/test.h +++ b/tests/test.h @@ -24,6 +24,7 @@ TCase *test_encoding_create_tests(); TCase *test_read_create_tests(); TCase *test_login_create_tests(); TCase *test_user_create_tests(); +TCase *test_fw_query_create_tests(); char *va_str(const char *, ...);