diff --git a/CHANGELOG b/CHANGELOG index 5fae2b5..b6ccad8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,6 +7,13 @@ iodine - IP over DNS is now easy CHANGES: +2006-09-11: 0.3.2 + - Support for NetBSD + - Fixed potential security problems + - Name parsing routines rewritten, added regression tests + - New encoding, 25% more peak upstream throughput + - New -l option to set local ip to listen to on server + 2006-07-11: 0.3.1 - Add Mac OSX support - Add setting device name diff --git a/Makefile b/Makefile index daf2d81..207b4e3 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,20 @@ CC = gcc CLIENT = iodine -CLIENTOBJS = iodine.o tun.o dns.o read.o +CLIENTOBJS = iodine.o tun.o dns.o read.o encoding.o SERVER = iodined -SERVEROBJS = iodined.o tun.o dns.o read.o +SERVEROBJS = iodined.o tun.o dns.o read.o encoding.o +TESTSUITE = tester +TESTOBJS = test.o dns.o read.o encoding.o OS = `uname | tr "a-z" "A-Z"` LDFLAGS = -lz CFLAGS = -c -g -Wall -D$(OS) -all: stateos $(CLIENT) $(SERVER) +all: stateos $(CLIENT) $(SERVER) $(TESTSUITE) + +test: $(TESTSUITE) + @./$(TESTSUITE) stateos: @echo OS is $(OS) @@ -22,11 +27,17 @@ $(SERVER): $(SERVEROBJS) @echo LD $@ @$(CC) $(SERVEROBJS) -o $(SERVER) $(LDFLAGS) +$(TESTSUITE): $(TESTOBJS) + @echo LD $@ + @$(CC) $(TESTOBJS) -o $(TESTSUITE) $(LDFLAGS) + @echo Running tests... + @./$(TESTSUITE) + .c.o: @echo CC $< @$(CC) $(CFLAGS) $< -o $@ clean: @echo "Cleaning..." - @rm -f $(CLIENT) $(SERVER) *~ *.o *.core + @rm -f $(CLIENT) $(SERVER) $(TESTSUITE) *~ *.o *.core diff --git a/README b/README index 077c511..716b553 100644 --- a/README +++ b/README @@ -10,6 +10,20 @@ server. This can be usable in different situations where internet access is firewalled, but DNS queries are allowed. +QUICKSTART: + +Try it out within your own LAN! Follow these simple steps: +- 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) +- On the client, run: ./iodine -f 192.168.0.1 test.asdf + (Replace 192.168.0.1 with the server's ip address) +- Now the client has the tunnel ip 10.0.0.2 and the server has 10.0.0.1 +- Try pinging each other through the tunnel +- Done! :) +To actually use it through a relaying nameserver, see below. + + HOW TO USE: Server side: @@ -61,10 +75,11 @@ possible to allow maximum throughput. PORTABILITY: -iodine has been tested on Linux (x86 and SPARC64), FreeBSD (x86), OpenBSD (x86) -and MacOS X (10.3, ppc, with http://www-user.rhrk.uni-kl.de/~nissler/tuntap/). -It should work on other unix-like systems as well that has TUN/TAP tunneling -support. Let us know if you get it to run on other platforms. +iodine has been tested on Linux (x86 and SPARC64), FreeBSD (x86), OpenBSD (x86), +NetBSD (x86) and MacOS X (10.3, ppc, with +http://www-user.rhrk.uni-kl.de/~nissler/tuntap/). It should work on other +unix-like systems as well that has TUN/TAP tunneling support. Let us know if you +get it to run on other platforms. THE NAME: @@ -73,6 +88,11 @@ The name iodine was chosen since it starts with IOD (IP Over DNS) and since iodine has atomic number 53, which happens to be the DNS port number. +THANKS: + +- To kuxien for FreeBSD and OS X testing + + AUTHORS & LICENSE: Copyright (c) 2006 Bjorn Andersson , Erik Ekman diff --git a/TODO b/TODO new file mode 100644 index 0000000..31467fd --- /dev/null +++ b/TODO @@ -0,0 +1,19 @@ + +iodine - IP over DNS is now easy + + http://code.kryo.se/iodine + +******************************** + +STUFF TO DO: + +- Handle multiple concurrent users on one server + +- Some kind of authentication? + +- Detect if EDNS0 can be used, probe MTU size + +- Port to more platforms (Solaris? Windows?) + +- More/better documentation (as always) + diff --git a/dns.c b/dns.c index 2cf04ab..2692850 100644 --- a/dns.c +++ b/dns.c @@ -32,17 +32,20 @@ #include #include -#include "read.h" #include "structs.h" #include "dns.h" +#include "encoding.h" +#include "read.h" // For FreeBSD #ifndef MIN #define MIN(a,b) ((a)<(b)?(a):(b)) #endif + static int host2dns(const char *, char *, int); static int dns_write(int, int, char *, int, char); +static void dns_query(int, int, char *, int); struct sockaddr_in peer; char topdomain[256]; @@ -58,7 +61,7 @@ uint16_t pingid; int -open_dns(const char *domain, int localport) +open_dns(const char *domain, int localport, in_addr_t listen_ip) { int fd; int flag; @@ -67,9 +70,10 @@ open_dns(const char *domain, int localport) bzero(&addr, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(localport); - addr.sin_addr.s_addr = htonl(INADDR_ANY); + /* listen_ip already in network byte order from inet_addr, or 0 */ + addr.sin_addr.s_addr = listen_ip; - fd = socket(AF_INET, SOCK_DGRAM, 0); + fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if(fd < 0) { warn("socket"); return -1; @@ -87,10 +91,11 @@ open_dns(const char *domain, int localport) } // Save top domain used - strncpy(topdomain, domain, sizeof(topdomain) - 2); - topdomain[sizeof(topdomain) - 1] = 0; + strncpy(topdomain, domain, sizeof(topdomain) - 1); + topdomain[sizeof(topdomain) - 1] = '\0'; printf("Opened UDP socket\n"); + return fd; } @@ -176,7 +181,7 @@ dns_handshake(int dns_fd) dns_write(dns_fd, ++pingid, data, 2, 'H'); } -void +static void dns_query(int fd, int id, char *host, int type) { char *p; @@ -204,16 +209,16 @@ dns_query(int fd, int id, char *host, int type) p = buf + sizeof(HEADER); p += host2dns(host, p, strlen(host)); - PUTSHORT(type, p); - PUTSHORT(C_IN, p); + putshort(&p, type); + putshort(&p, C_IN); // EDNS0 - *p++ = 0x00; //Root - PUTSHORT(0x0029, p); // OPT - PUTSHORT(0x1000, p); // Payload size: 4096 - PUTSHORT(0x0000, p); // Higher bits/ edns version - PUTSHORT(0x8000, p); // Z - PUTSHORT(0x0000, p); // Data length + putbyte(&p, 0x00); //Root + putshort(&p, 0x0029); // OPT + putshort(&p, 0x1000); // Payload size: 4096 + putshort(&p, 0x0000); // Higher bits/edns version + putshort(&p, 0x8000); // Z + putshort(&p, 0x0000); // Data length peerlen = sizeof(peer); @@ -221,72 +226,67 @@ dns_query(int fd, int id, char *host, int type) sendto(fd, buf, len, 0, (struct sockaddr*)&peer, peerlen); } -static void -put_hex(char *p, char h) -{ - int t; - static const char to_hex[] = "0123456789ABCDEF"; - - t = (h & 0xF0) >> 4; - p[0] = to_hex[t]; - t = h & 0x0F; - p[1] = to_hex[t]; -} - static int dns_write(int fd, int id, char *buf, int len, char flag) { int avail; - int i; - int final; + int written; + int encoded; char data[257]; char *d; -#define CHUNK 31 -// 31 bytes expands to 62 chars in domain -// We just use hex as encoding right now - avail = 0xFF - strlen(topdomain) - 2; - - avail /= 2; // use two chars per byte in encoding - avail -= (avail/CHUNK); // make space for parts - - avail = MIN(avail, len); // do not use more bytes than is available; - final = (avail == len); // is this the last block? bzero(data, sizeof(data)); d = data; - - if (flag != 0) { - *d = flag; - } else { - // First byte is 0 for middle packet and 1 for last packet - *d = '0' + final; - } - d++; - - if (len > 0) { - for (i = 0; i < avail; i++) { - if (i > 0 && i % 31 == 0) { - *d = '.'; - d++; - } - put_hex(d, buf[i]); - d += 2; - } - } + written = encode_data(buf, len, avail, d, flag); + encoded = strlen(data); + d += encoded; if (*d != '.') { *d++ = '.'; } strncpy(d, topdomain, strlen(topdomain)+1); dns_query(fd, id, data, T_NULL); - return avail; + return written; } int dns_read(int fd, char *buf, int buflen) { int r; + socklen_t addrlen; + char packet[64*1024]; + struct sockaddr_in from; + HEADER *header; + + addrlen = sizeof(struct sockaddr); + r = recvfrom(fd, packet, sizeof(packet), 0, (struct sockaddr*)&from, &addrlen); + if(r == -1) { + perror("recvfrom"); + return 0; + } + + header = (HEADER*)packet; + if (dns_sending() && chunkid == ntohs(header->id)) { + /* Got ACK on sent packet */ + packetpos += lastlen; + if (packetpos == packetlen) { + /* Packet completed */ + packetpos = 0; + packetlen = 0; + lastlen = 0; + } else { + /* More to send */ + dns_send_chunk(fd); + } + } + return dns_parse_reply(buf, buflen, packet, r); +} + +int +dns_parse_reply(char *outbuf, int buflen, char *packet, int packetlen) +{ + int rv; long ttl; short rlen; short type; @@ -297,63 +297,39 @@ dns_read(int fd, char *buf, int buflen) char name[255]; char rdata[4*1024]; HEADER *header; - socklen_t addrlen; - char packet[64*1024]; - struct sockaddr_in from; - addrlen = sizeof(struct sockaddr); - r = recvfrom(fd, packet, sizeof(packet), 0, (struct sockaddr*)&from, &addrlen); + rv = 0; + header = (HEADER*)packet; + + data = packet + sizeof(HEADER); - if(r == -1) { - perror("recvfrom"); - } else { - header = (HEADER*)packet; - - data = packet + sizeof(HEADER); + if(header->qr) { /* qr=1 => response */ + qdcount = ntohs(header->qdcount); + ancount = ntohs(header->ancount); - if(header->qr) { /* qr=1 => response */ - qdcount = ntohs(header->qdcount); - ancount = ntohs(header->ancount); + rlen = 0; - rlen = 0; + if(qdcount == 1) { + readname(packet, &data, name, sizeof(name)); + readshort(packet, &data, &type); + readshort(packet, &data, &class); + } + if(ancount == 1) { + readname(packet, &data, name, sizeof(name)); + readshort(packet, &data, &type); + readshort(packet, &data, &class); + readlong(packet, &data, &ttl); + readshort(packet, &data, &rlen); + readdata(packet, &data, rdata, rlen); + } - if(qdcount == 1) { - READNAME(packet, name, data); - READSHORT(type, data); - READSHORT(class, data); - } - if(ancount == 1) { - READNAME(packet, name, data); - READSHORT(type, data); - READSHORT(class, data); - READLONG(ttl, data); - READSHORT(rlen, data); - READDATA(rdata, data, rlen); - } - if (dns_sending() && chunkid == ntohs(header->id)) { - // Got ACK on sent packet - packetpos += lastlen; - if (packetpos == packetlen) { - // Packet completed - packetpos = 0; - packetlen = 0; - lastlen = 0; - } else { - // More to send - dns_send_chunk(fd); - } - } - - if(type == T_NULL && rlen > 2) { - memcpy(buf, rdata, rlen); - return rlen; - } else { - return 0; - } + if(type == T_NULL && rlen > 2) { + rv = MIN(rlen, sizeof(rdata)); + memcpy(outbuf, rdata, rv); } } - return 0; + return rv; } static int @@ -412,19 +388,18 @@ dnsd_send(int fd, struct query *q, char *data, int datalen) name = 0xc000 | ((p - buf) & 0x3fff); p += host2dns(q->name, p, strlen(q->name)); - PUTSHORT(q->type, p); - PUTSHORT(C_IN, p); - - PUTSHORT(name, p); - PUTSHORT(q->type, p); - PUTSHORT(C_IN, p); - PUTLONG(0, p); + putshort(&p, q->type); + putshort(&p, C_IN); + + putshort(&p, name); + putshort(&p, q->type); + putshort(&p, C_IN); + putlong(&p, 0); q->id = 0; - PUTSHORT(datalen, p); - memcpy(p, data, datalen); - p += datalen; + putshort(&p, datalen); + putdata(&p, data, datalen); len = p - buf; sendto(fd, buf, len, 0, (struct sockaddr*)&q->from, q->fromlen); @@ -433,32 +408,12 @@ dnsd_send(int fd, struct query *q, char *data, int datalen) static int decodepacket(const char *name, char *buf, int buflen) { - int r; int len; - char *dp; char *domain; - const char *np; - len = 1; domain = strstr(name, topdomain); - buf[0] = name[0]; - - dp = buf + 1; - np = name + 1; - - while(len < buflen && np < domain) { - if(*np == '.') { - np++; - continue; - } - - sscanf(np, "%02X", &r); - *dp++ = (char)r; - np+=2; - len++; - } - + len = decode_data(buf, buflen, name, domain); if (len == buflen) return -1; return len; @@ -468,12 +423,13 @@ int dnsd_read(int fd, struct query *q, char *buf, int buflen) { int r; + int rv; short id; short type; short class; short qdcount; char *data; - char name[255]; + char name[257]; HEADER *header; socklen_t addrlen; char packet[64*1024]; @@ -493,23 +449,24 @@ dnsd_read(int fd, struct query *q, char *buf, int buflen) qdcount = ntohs(header->qdcount); if(qdcount == 1) { - bzero(name, sizeof(name)); - READNAME(packet, name, data); - READSHORT(type, data); - READSHORT(class, data); + readname(packet, &data, name, sizeof(name) -1); + name[256] = 0; + readshort(packet, &data, &type); + readshort(packet, &data, &class); - strncpy(q->name, name, 256); + strncpy(q->name, name, 257); q->type = type; q->id = id; q->fromlen = addrlen; memcpy((struct sockaddr*)&q->from, (struct sockaddr*)&from, addrlen); - return decodepacket(name, buf, buflen); + rv = decodepacket(name, buf, buflen); } } } else { perror("recvfrom"); + rv = 0; } - return 0; + return rv; } diff --git a/dns.h b/dns.h index ad27b6b..f3807a1 100644 --- a/dns.h +++ b/dns.h @@ -17,7 +17,7 @@ #ifndef _DNS_H_ #define _DNS_H_ -int open_dns(const char *, int); +int open_dns(const char *, int, in_addr_t); int dns_settarget(const char*); void close_dns(int); @@ -25,7 +25,6 @@ int dns_sending(); void dns_handle_tun(int, char *, int); void dns_ping(int); void dns_handshake(int); -void dns_query(int, int, char *, int); int dns_read(int, char *, int); extern struct sockaddr_in peer; @@ -38,5 +37,7 @@ int dnsd_hasack(); void dnsd_forceack(int); void dnsd_queuepacket(const char *, const int); +int dns_parse_reply(char *, int, char *, int); + #endif /* _DNS_H_ */ diff --git a/encoding.c b/encoding.c new file mode 100644 index 0000000..ef007fb --- /dev/null +++ b/encoding.c @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2006 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. + */ + +#include +#include +#include + +// For FreeBSD +#ifndef MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) +#endif + +#define SPACING 63 +#define ENC_CHUNK 8 +#define RAW_CHUNK 5 + +static const char base32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ98765-"; +static const char padder[] = " 1234"; +static char reverse32[128]; +static int reverse_init = 0; + +/* Eat 5 bytes from src, write 8 bytes to dest */ +static void +encode_chunk(char *dest, char *src) +{ + unsigned char c; + + *dest++ = base32[(*src & 0xF8) >> 3]; // 1111 1000 first byte + + c = (*src++ & 0x07) << 2; // 0000 0111 first byte + c |= ((*src & 0xC0) >> 6); // 1100 0000 second byte + *dest++ = base32[(int) c]; + + *dest++ = base32[(*src & 0x3E) >> 1]; // 0011 1110 second byte + + c = (*src++ & 0x01) << 4; // 0000 0001 second byte + c |= ((*src & 0xF0) >> 4); // 1111 0000 third byte + *dest++ = base32[(int) c]; + + c = (*src++ & 0x0F) << 1; // 0000 1111 third byte + c |= ((*src & 0x80) >> 7); // 1000 0000 fourth byte + *dest++ = base32[(int) c]; + + *dest++ = base32[(*src & 0x7C) >> 2]; // 0111 1100 fourth byte + + c = (*src++ & 0x03) << 3; // 0000 0011 fourth byte + c |= ((*src & 0xE0) >> 5); // 1110 0000 fifth byte + *dest++ = base32[(int) c]; + + *dest++ = base32[*src++ & 0x1F]; // 0001 1111 fifth byte +} + +/* Eat 8 bytes from src, write 5 bytes to dest */ +static void +decode_chunk(char *dest, char *src) +{ + unsigned char c; + int i; + + if (!reverse_init) { + for (i = 0; i < 32; i++) { + c = base32[i]; + reverse32[(int) c] = i; + } + reverse_init = 1; + } + + c = reverse32[(int) *src++] << 3; // Take bits 11111 from byte 1 + c |= (reverse32[(int) *src] & 0x1C) >> 2; // Take bits 11100 from byte 2 + *dest++ = c; + + c = (reverse32[(int) *src++] & 0x3) << 6; // Take bits 00011 from byte 2 + c |= reverse32[(int) *src++] << 1; // Take bits 11111 from byte 3 + c |= (reverse32[(int) *src] & 0x10) >> 4; // Take bits 10000 from byte 4 + *dest++ = c; + + c = (reverse32[(int) *src++] & 0xF) << 4; // Take bits 01111 from byte 4 + c |= (reverse32[(int) *src] & 0x1E) >> 1; // Take bits 11110 from byte 5 + *dest++ = c; + + c = reverse32[(int) *src++] << 7; // Take bits 00001 from byte 5 + c |= reverse32[(int) *src++] << 2; // Take bits 11111 from byte 6 + c |= (reverse32[(int) *src] & 0x18) >> 3; // Take bits 11000 from byte 7 + *dest++ = c; + + c = (reverse32[(int) *src++] & 0x7) << 5; // Take bits 00111 from byte 7 + c |= reverse32[(int) *src++]; // Take bits 11111 from byte 8 + *dest++ = c; +} + +int +encode_data(char *buf, int len, int space, char *dest, char flag) +{ + int final; + int write; + int realwrite; + int chunks; + int leftovers; + int i; + char encoded[255]; + char padding[5]; + char *pp; + char *dp; + char *ep; + + space -= space / SPACING; + chunks = (space - 1) / ENC_CHUNK; + while ((chunks + 1) * ENC_CHUNK + 1 > space) { + chunks--; + } + write = RAW_CHUNK * chunks; + write = MIN(write, len); // do not use more bytes than is available; + final = (write == len); // is this the last block? + chunks = write / RAW_CHUNK; + leftovers = write % RAW_CHUNK; + + if (flag != 0) { + *dest = flag; + } else { + // First byte is 0 for middle packet and 1 for last packet + *dest = '0' + final; + } + dest++; + + bzero(encoded, sizeof(encoded)); + ep = encoded; + dp = buf; + for (i = 0; i < chunks; i++) { + encode_chunk(ep, dp); + ep += ENC_CHUNK; + dp += RAW_CHUNK; + } + realwrite = ENC_CHUNK * chunks; + bzero(padding, sizeof(padding)); + pp = padding; + if (leftovers) { + pp += RAW_CHUNK - leftovers; + memcpy(pp, dp, leftovers); + + pp = padding; + *ep++ = padder[leftovers]; + encode_chunk(ep, pp); + + realwrite += ENC_CHUNK + 1; // plus padding character + } + ep = encoded; + if (len > 0) { + for (i = 1; i <= realwrite; i++) { + if (i % SPACING == 0) { + *dest++ = '.'; + } + *dest++ = *ep++; + } + } + + return write; +} + +int +decode_data(char *dest, int size, const char *src, char *srcend) +{ + int len; + int i; + int chunks; + int padded; + char encoded[255]; + char padding[5]; + char *pp; + char *ep; + + // Copy flag + len = 1; + *dest = *src; + dest++; + src++; + + bzero(encoded, sizeof(encoded)); + ep = encoded; + while(len < size && src < srcend) { + if(*src == '.') { + src++; + continue; + } + + *ep++ = *src++; + } + chunks = strlen(encoded) / 8; + padded = strlen(encoded) % 8; + + ep = encoded; + for (i = 0; i < chunks-1; i++) { + decode_chunk(dest, ep); + dest += RAW_CHUNK; + ep += ENC_CHUNK; + len += RAW_CHUNK; + } + // Read last chunk: + if (padded) { + pp = padding; + padded = *ep++ - '0'; + decode_chunk(pp, ep); + pp += RAW_CHUNK - padded; + memcpy(dest, pp, padded); + len += padded; + } else { + decode_chunk(dest, ep); + len += RAW_CHUNK; + } + + return len; +} + diff --git a/encoding.h b/encoding.h new file mode 100644 index 0000000..3d1ef34 --- /dev/null +++ b/encoding.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2006 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 _ENCODING_H_ +#define _ENCODING_H_ + +int encode_data(char *, int, int, char *, char); +int decode_data(char *, int, const char *, char *); + +#endif /* _ENCODING_H_ */ diff --git a/iodine.c b/iodine.c index a8241c3..b33c720 100644 --- a/iodine.c +++ b/iodine.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -39,9 +40,6 @@ int running = 1; -int tun_fd; -int dns_fd; - static void sighandler(int sig) { running = 0; @@ -50,13 +48,16 @@ sighandler(int sig) { static int tunnel(int tun_fd, int dns_fd) { - int i; - int read; - fd_set fds; - struct timeval tv; - char in[64*1024]; - long outlen; char out[64*1024]; + char in[64*1024]; + struct timeval tv; + long outlen; + fd_set fds; + int read; + int i; + int rv; + + rv = 0; while (running) { tv.tv_sec = 1; @@ -68,17 +69,17 @@ tunnel(int tun_fd, int dns_fd) FD_SET(dns_fd, &fds); i = select(MAX(tun_fd, dns_fd) + 1, &fds, NULL, NULL, &tv); - - if(i < 0) { - if (running) { - warn("select"); - } - return 1; + + if (!running) { + rv = 1; + break; } - if(i == 0) { - dns_ping(dns_fd); - } else { + if(i < 0) { + warn("select"); + rv = 1; + break; + } else if (i > 0) { if(FD_ISSET(tun_fd, &fds)) { read = read_tun(tun_fd, in, sizeof(in)); if(read <= 0) @@ -100,24 +101,26 @@ tunnel(int tun_fd, int dns_fd) if (!dns_sending()) dns_ping(dns_fd); } - } + } else + dns_ping(dns_fd); } - return 0; + return rv; } static int handshake(int dns_fd) { + struct timeval tv; + char server[128]; + char client[128]; + char in[4096]; + int timeout; + fd_set fds; + int read; + int mtu; int i; int r; - char *p; - int mtu; - int read; - fd_set fds; - int timeout; - char in[4096]; - struct timeval tv; timeout = 1; @@ -141,12 +144,9 @@ handshake(int dns_fd) } if (read > 0) { - p = strchr(in, '-'); - if (p) { - *p++ = '\0'; - mtu = atoi(p); - - if (tun_setip(in) == 0 && tun_setmtu(atoi(p)) == 0) + if (sscanf(in, "%[^-]-%[^-]-%d", + server, client, &mtu) == 3) { + if (tun_setip(client) == 0 && tun_setmtu(mtu) == 0) return 0; else warn("Received handshake but b0rk"); @@ -160,17 +160,19 @@ handshake(int dns_fd) return 1; } -extern char *__progname; - static void usage() { - printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device]" + extern char *__progname; + + printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] " "nameserver topdomain\n", __progname); exit(2); } 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] " "nameserver topdomain\n", __progname); @@ -188,24 +190,26 @@ help() { static void version() { printf("iodine IP over DNS tunneling client\n"); - printf("version: 0.3.1 from 2006-07-11\n"); + printf("version: 0.3.2 from 2006-09-11\n"); exit(0); } int main(int argc, char **argv) { - int choice; - char *newroot; - char *username; - char *device; - int foreground; struct passwd *pw; + char *username; + int foreground; + char *newroot; + char *device; + int choice; + int tun_fd; + int dns_fd; - newroot = NULL; username = NULL; - device = NULL; foreground = 0; + newroot = NULL; + device = NULL; while ((choice = getopt(argc, argv, "vfhu:t:d:")) != -1) { switch(choice) { @@ -254,7 +258,7 @@ main(int argc, char **argv) if ((tun_fd = open_tun(device)) == -1) goto cleanup1; - if ((dns_fd = open_dns(argv[1], 0)) == -1) + if ((dns_fd = open_dns(argv[1], 0, INADDR_ANY)) == -1) goto cleanup2; if (dns_settarget(argv[0]) == -1) goto cleanup2; diff --git a/iodined.c b/iodined.c index a4b0d41..88d3c67 100644 --- a/iodined.c +++ b/iodined.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -57,17 +58,18 @@ sigint(int sig) { static int tunnel(int tun_fd, int dns_fd) { - int i; + struct in_addr clientip; + struct in_addr myip; + struct timeval tv; + char out[64*1024]; + char in[64*1024]; + char *tmp[2]; + long outlen; + fd_set fds; int read; int code; - int ipadder; - struct in_addr nextip; - fd_set fds; - struct timeval tv; - char in[64*1024]; - long outlen; - char out[64*1024]; - + int i; + while (running) { if (q.id != 0) { tv.tv_sec = 0; @@ -113,17 +115,20 @@ tunnel(int tun_fd, int dns_fd) continue; if(in[0] == 'H' || in[0] == 'h') { - ipadder = htonl(my_ip); // To get the last byte last - if ((ipadder & 0xFF) == 0xFF) { - // IP ends with 255. - ipadder--; - } else { - ipadder++; - } - nextip.s_addr = ntohl(ipadder); - read = snprintf(out, sizeof(out), "%s-%d", inet_ntoa(nextip), my_mtu); + myip.s_addr = my_ip; + clientip.s_addr = my_ip + inet_addr("0.0.0.1"); + + tmp[0] = strdup(inet_ntoa(myip)); + tmp[1] = strdup(inet_ntoa(clientip)); + + read = snprintf(out, sizeof(out), "%s-%s-%d", + tmp[0], tmp[1], my_mtu); + dnsd_send(dns_fd, &q, out, read); q.id = 0; + + free(tmp[1]); + free(tmp[0]); } else if((in[0] >= '0' && in[0] <= '9') || (in[0] >= 'a' && in[0] <= 'f') || (in[0] >= 'A' && in[0] <= 'F')) { @@ -159,19 +164,21 @@ tunnel(int tun_fd, int dns_fd) return 0; } -extern char *__progname; - static void usage() { - printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] [-m mtu] " + extern char *__progname; + + printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] [-m mtu] [-l ip address to listen on] " "tunnel_ip topdomain\n", __progname); exit(2); } static void 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] [-f] [-u user] [-t chrootdir] [-d device] [-m mtu] [-l ip address to listen on] " "tunnel_ip topdomain\n", __progname); printf(" -v to print version info and exit\n"); printf(" -h to print this help and exit\n"); @@ -180,6 +187,7 @@ help() { 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 (default 0.0.0.0)\n"); printf("tunnel_ip is the IP number of the local tunnel interface.\n"); printf("topdomain is the FQDN that is delegated to this server.\n"); exit(0); @@ -188,7 +196,7 @@ help() { static void version() { printf("iodine IP over DNS tunneling server\n"); - printf("version: 0.3.1 from 2006-07-11\n"); + printf("version: 0.3.2 from 2006-09-11\n"); exit(0); } @@ -204,19 +212,21 @@ main(int argc, char **argv) int foreground; int mtu; struct passwd *pw; + in_addr_t listen_ip; username = NULL; newroot = NULL; device = NULL; foreground = 0; mtu = 1024; + listen_ip = INADDR_ANY; packetbuf.len = 0; packetbuf.offset = 0; outpacket.len = 0; q.id = 0; - while ((choice = getopt(argc, argv, "vfhu:t:d:m:")) != -1) { + while ((choice = getopt(argc, argv, "vfhu:t:d:m:l:")) != -1) { switch(choice) { case 'v': version(); @@ -239,6 +249,9 @@ main(int argc, char **argv) case 'm': mtu = atoi(optarg); break; + case 'l': + listen_ip = inet_addr(optarg); + break; default: usage(); break; @@ -269,11 +282,16 @@ main(int argc, char **argv) usage(); } + if (listen_ip == INADDR_NONE) { + printf("Bad IP address to listen on.\n"); + usage(); + } + if ((tun_fd = open_tun(device)) == -1) goto cleanup0; if (tun_setip(argv[0]) != 0 || tun_setmtu(mtu) != 0) goto cleanup1; - if ((dnsd_fd = open_dns(argv[1], 53)) == -1) + if ((dnsd_fd = open_dns(argv[1], 53, listen_ip)) == -1) goto cleanup2; my_ip = inet_addr(argv[0]); diff --git a/read.c b/read.c index 4007cf9..07e3643 100644 --- a/read.c +++ b/read.c @@ -16,39 +16,149 @@ #include -int -readname(char *packet, char *dst, char *src) +static int +readname_loop(char *packet, char **src, char *dst, size_t length, size_t loop) { - char l; + char *dummy; + char *s; + char *d; int len; - int offset; + char c; + + if (loop <= 0) + return 0; len = 0; + s = *src; + d = dst; + while(*s && len < length - 2) { + c = *s++; - while(*src) { - l = *src++; - len++; - - if(l & 0x80 && l & 0x40) { - offset = ((src[-1] & 0x3f) << 8) | src[0]; - readname(packet, dst, packet + offset); - dst += strlen(dst); - break; + /* is this a compressed label? */ + if((c & 0xc0) == 0xc0) { + dummy = packet + (((s[-1] & 0x3f) << 8) | s[0]); + len += readname_loop(packet, &dummy, d, length - len, loop - 1); + goto end; } - while(l) { - *dst++ = *src++; - l--; + while(c && len < length - 1) { + *d++ = *s++; + len++; + + c--; + } + + if (len >= length - 1) { + break; /* We used up all space */ + } + + if (*s != 0) { + *d++ = '.'; len++; } - - *dst++ = '.'; } + dst[len++] = '\0'; - *dst = '\0'; - src++; - len++; +end: + (*src) = s+1; + return len; +} + +int +readname(char *packet, char **src, char *dst, size_t length) +{ + return readname_loop(packet, src, dst, length, 10); +} + +int +readshort(char *packet, char **src, short *dst) +{ + unsigned char *p; + + p = *src; + *dst = (p[0] << 8) | p[1]; + + (*src) += sizeof(short); + return sizeof(short); +} + +int +readlong(char *packet, char **src, long *dst) +{ + unsigned char *p; + + p = *src; + + *dst = ((long)p[0] << 24) + | ((long)p[1] << 16) + | ((long)p[2] << 8) + | ((long)p[3]); + + (*src) += sizeof(long); + return sizeof(long); +} + +int +readdata(char *packet, char **src, char *dst, size_t len) +{ + if (len < 0) + return 0; + + memcpy(dst, *src, len); + + (*src) += len; return len; } +int +putbyte(char **dst, char value) +{ + **dst = value; + (*dst)++; + + return sizeof(char); +} + +int +putshort(char **dst, short value) +{ + unsigned char *p; + + p = *dst; + + *p++ = (value >> 8); + *p++ = value; + + (*dst) = p; + return sizeof(short); +} + +int +putlong(char **dst, long value) +{ + unsigned char *p; + + p = *dst; + + *p++ = (value >> 24); + *p++ = (value >> 16); + *p++ = (value >> 8); + *p++ = (value); + + (*dst) = p; + return sizeof(long); +} + +int +putdata(char **dst, char *data, size_t len) +{ + if (len < 0) + return 0; + + memcpy(*dst, data, len); + + (*dst) += len; + return len; +} + diff --git a/read.h b/read.h index a4075ec..6117b7b 100644 --- a/read.h +++ b/read.h @@ -17,19 +17,14 @@ #ifndef _READ_H_ #define _READ_H_ -int readname(char *, char *, char *); +int readname(char *, char **, char *, size_t); +int readshort(char *, char **, short *); +int readlong(char *, char **, long *); +int readdata(char *, char **, char *, size_t); -#define READNAME(packet, dst, src) (src) += readname((packet), (dst), (src)); - -#define READSHORT(dst, src) \ - memcpy(&dst, src, 2); \ - (dst) = ntohs(dst); (src)+=2; - -#define READLONG(dst, src) \ - memcpy(&dst, src, 2); \ - (dst) = ntohl(dst); (src)+=4; - -#define READDATA(dst, src, len) \ - memcpy((dst), (src), (len)); (src)+=(len); +int putbyte(char **, char); +int putshort(char **, short); +int putlong(char **, long); +int putdata(char **, char *, size_t); #endif diff --git a/structs.h b/structs.h index 2cec05c..534f59c 100644 --- a/structs.h +++ b/structs.h @@ -25,7 +25,7 @@ struct packet }; struct query { - char name[256]; + char name[258]; short type; short id; struct sockaddr from; diff --git a/test.c b/test.c new file mode 100644 index 0000000..4125ebe --- /dev/null +++ b/test.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2006 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. + */ + +#include +#include +#include +#include +#include +#ifdef DARWIN +#include +#endif +#include +#include +#include +#include + +#include "structs.h" +#include "dns.h" +#include "read.h" + +static void +test_readputshort() +{ + short tshort; + short putted; + short temps; + char buf[4]; + short *s; + char* p; + int i; + + printf(" * Testing read/putshort... "); + fflush(stdout); + + for (i = 0; i < 65536; i++) { + tshort = (unsigned short) i; + temps = htons(tshort); + p = buf; + putshort(&p, tshort); + s = &putted; + memcpy(s, buf, sizeof(short)); + if (putted != temps) { + printf("Bad value on putshort for %d\n", i); + exit(1); + } + s = &temps; + memcpy(buf, s, sizeof(short)); + p = buf; + readshort(NULL, &p, &temps); + if (temps != tshort) { + printf("Bad value on readshort for %d\n", i); + exit(1); + } + } + + printf("OK\n"); +} + +static void +test_readputlong() +{ + char buf[4]; + long putint; + long tempi; + long tint; + long *l; + char* p; + int i; + + printf(" * Testing read/putlong... "); + fflush(stdout); + + for (i = 0; i < 32; i++) { + tint = 0xF << i; + tempi = htonl(tint); + p = buf; + putlong(&p, tint); + l = &putint; + memcpy(l, buf, sizeof(int)); + if (putint != tempi) { + printf("Bad value on putlong for %d\n", i); + exit(2); + } + l = &tempi; + memcpy(buf, l, sizeof(int)); + p = buf; + readlong(NULL, &p, &tempi); + if (tempi != tint) { + printf("Bad value on readlong for %d\n", i); + exit(2); + } + } + + printf("OK\n"); +} + + +static void +test_readname() +{ + char emptyloop[] = { + 'A', 'A', 0x81, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01 }; + char infloop[] = { + 'A', 'A', 0x81, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 'A', 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01 }; + char longname[] = + "AA\x81\x80\x00\x01\x00\x00\x00\x00\x00\x00" + "\x3FzBCDEFGHIJKLMNOPQURSTUVXYZ0123456789abcdefghijklmnopqrstuvxyzAA" + "\x3FzBCDEFGHIJKLMNOPQURSTUVXYZ0123456789abcdefghijklmnopqrstuvxyzAA" + "\x3FzBCDEFGHIJKLMNOPQURSTUVXYZ0123456789abcdefghijklmnopqrstuvxyzAA" + "\x3FzBCDEFGHIJKLMNOPQURSTUVXYZ0123456789abcdefghijklmnopqrstuvxyzAA" + "\x3FzBCDEFGHIJKLMNOPQURSTUVXYZ0123456789abcdefghijklmnopqrstuvxyzAA" + "\x3FzBCDEFGHIJKLMNOPQURSTUVXYZ0123456789abcdefghijklmnopqrstuvxyzAA" + "\x00\x00\x01\x00\x01"; + 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; + + printf(" * Testing readname... "); + fflush(stdout); + + bzero(buf, sizeof(buf)); + data = emptyloop + sizeof(HEADER); + buf[1023] = 'A'; + rv = readname(emptyloop, &data, buf, 1023); + assert(buf[1023] == 'A'); + + bzero(buf, sizeof(buf)); + data = infloop + sizeof(HEADER); + buf[4] = '\a'; + rv = readname(infloop, &data, buf, 4); + assert(buf[4] == '\a'); + + bzero(buf, sizeof(buf)); + data = longname + sizeof(HEADER); + buf[256] = '\a'; + rv = readname(longname, &data, buf, 256); + assert(buf[256] == '\a'); + + bzero(buf, sizeof(buf)); + data = onejump + sizeof(HEADER); + rv = readname(onejump, &data, buf, 256); + assert(rv == 9); + + printf("OK\n"); +} + +int +main() +{ + printf("** iodine test suite\n"); + + test_readputshort(); + test_readputlong(); + test_readname(); + + printf("** All went well :)\n"); + return 0; +} diff --git a/tun.c b/tun.c index d22f950..d750362 100644 --- a/tun.c +++ b/tun.c @@ -142,7 +142,7 @@ close_tun(int tun_fd) int write_tun(int tun_fd, char *data, int len) { -#if defined (FREEBSD) || defined (DARWIN) +#if defined (FREEBSD) || defined (DARWIN) || defined(NETBSD) data += 4; len -= 4; #else /* !FREEBSD/DARWIN */ @@ -169,7 +169,7 @@ write_tun(int tun_fd, char *data, int len) int read_tun(int tun_fd, char *buf, int len) { -#if defined (FREEBSD) || defined (DARWIN) +#if defined (FREEBSD) || defined (DARWIN) || defined(NETBSD) // FreeBSD has no header return read(tun_fd, buf + 4, len - 4) + 4; #else /* !FREEBSD */