From eb4e6ec221cdbc5ad0e6ba5b6a7634064cb82d5c Mon Sep 17 00:00:00 2001 From: gregor herrmann Date: Tue, 20 Mar 2007 21:31:09 +0000 Subject: [PATCH 1/1] [svn-inject] Installing original source of iodine --- CHANGELOG | 38 +++++ Makefile | 43 +++++ README | 120 +++++++++++++ TODO | 19 +++ dns.c | 481 +++++++++++++++++++++++++++++++++++++++++++++++++++++ dns.h | 44 +++++ encoding.c | 226 +++++++++++++++++++++++++ encoding.h | 23 +++ iodine.c | 311 ++++++++++++++++++++++++++++++++++ iodined.c | 339 +++++++++++++++++++++++++++++++++++++ read.c | 178 ++++++++++++++++++++ read.h | 30 ++++ structs.h | 35 ++++ test.c | 262 +++++++++++++++++++++++++++++ tun.c | 233 ++++++++++++++++++++++++++ tun.h | 29 ++++ 16 files changed, 2411 insertions(+) create mode 100644 CHANGELOG create mode 100644 Makefile create mode 100644 README create mode 100644 TODO create mode 100644 dns.c create mode 100644 dns.h create mode 100644 encoding.c create mode 100644 encoding.h create mode 100644 iodine.c create mode 100644 iodined.c create mode 100644 read.c create mode 100644 read.h create mode 100644 structs.h create mode 100644 test.c create mode 100644 tun.c create mode 100644 tun.h diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..70dd54b --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,38 @@ + +iodine - IP over DNS is now easy + + http://code.kryo.se/iodine + +******************************** + +CHANGES: + +2006-11-08: 0.3.4 + - Fixed handshake() buffer overflow + (Found by poplix, Secunia: SA22674 / FrSIRT/ADV-2006-4333) + - Added more tests + - More name parsing enhancements + - Now runs on Linux/AMD64 + - Added setting to change server port + +2006-11-05: 0.3.3 + - Fixed possible buffer overflow + (Found by poplix, Bugtraq ID: 20883) + - Reworked dns hostname encoding + +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 + - Use compression of domain name in reply (should allow setting MTU + approx 200 bytes higher) + +2006-06-24: 0.3.0 + - First public release + - Support for Linux, FreeBSD, OpenBSD diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..207b4e3 --- /dev/null +++ b/Makefile @@ -0,0 +1,43 @@ +CC = gcc +CLIENT = iodine +CLIENTOBJS = iodine.o tun.o dns.o read.o encoding.o +SERVER = iodined +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) $(TESTSUITE) + +test: $(TESTSUITE) + @./$(TESTSUITE) + +stateos: + @echo OS is $(OS) + +$(CLIENT): $(CLIENTOBJS) + @echo LD $@ + @$(CC) $(CLIENTOBJS) -o $(CLIENT) $(LDFLAGS) + +$(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) $(TESTSUITE) *~ *.o *.core + diff --git a/README b/README new file mode 100644 index 0000000..0803fc1 --- /dev/null +++ b/README @@ -0,0 +1,120 @@ + +iodine - IP over DNS is now easy + + http://code.kryo.se/iodine + +******************************** + +This is a piece of software that lets you tunnel IPv4 data through a DNS +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: +To use this tunnel, you need control over a real domain (like mytunnel.com), +and a server with a static public IP number that does not yet run a DNS +server. Then, delegate a subdomain (say, tunnel1.mytunnel.com) to the server. +If you use BIND for the domain, add these lines to the zone file: + +tunnel1host IN A 10.15.213.99 +tunnel1 IN NS tunnel1host.mytunnel.com. + +Now any DNS querys for domains ending with tunnel1.mytunnnel.com will be sent +to your server. Start iodined on the server. The first argument is the tunnel +IP address (like 192.168.99.1) and the second is the assigned domain (in this +case tunnel1.mytunnel.com). The -f argument will keep iodined running in the +foreground, which helps when testing. iodined will start a virtual interface, +and also start listening for DNS queries on UDP port 53. Now everything is +ready for the client. + +Client side: +All the setup is done, just start iodine. It also takes two +arguments, the first is the local relaying DNS server and the second is the +domain used (tunnel1.mytunnnel.com). If DNS queries are allowed to any +computer, you can use the tunnel endpoint (example: 10.15.213.99 or +tunnel1host.mytunnel.com) as the first argument. The tunnel interface will get +an IP close to the servers (in this case 192.168.99.2) and a suitable MTU. Now +you should be able to ping the other end of the tunnel from either side. + + +MISC. INFO: + +Note that you can have only one client per server at the same time. This is +because of the fragmentation of big packets going upstream, and will be fixed +in future versions. + +Try experimenting with the MTU size (-m option) to get maximum bandwidth. It is +set to 1024 by default, which seems to work with most DNS servers. If you have +problems, try setting it to below 512. + +If you have problems, try inspecting the traffic with network monitoring tools +and make sure that the relaying DNS server has not cached the response. A +cached error message could mean that you started the client before the server. + +The upstream data is sent gzipped encoded with Base32. DNS protocol allows +one query per packet, and one query can be max 256 chars. Each domain name part +can be max 63 chars. So your domain name and subdomain should be as short as +possible to allow maximum throughput. + + +TIPS & TRICKS: + +If your port 53 is taken on a specific interface by an application that does +not use it, use -p on iodined to specify an alternate port (like -p 5353) and +use for instance iptables (on Linux) to forward the traffic: +iptables -t nat -A PREROUTING -i eth0 -p udp --dport 53 -j DNAT --to :5353 +(Sent in by Tom Schouten) + + +PORTABILITY: + +iodine has been tested on Linux (x86, AMD64 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 (after some +patching). Let us know if you get it to run on other platforms. + + +THE NAME: + +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 +- To poplix for code audit + + +AUTHORS & LICENSE: + +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. 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 new file mode 100644 index 0000000..ade40de --- /dev/null +++ b/dns.c @@ -0,0 +1,481 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#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 dns_write(int, int, char *, int, char); +static void dns_query(int, int, char *, int); + +struct sockaddr_in peer; +char topdomain[256]; + +// Current IP packet +char activepacket[4096]; +int lastlen; +int packetpos; +int packetlen; +uint16_t chunkid; + +uint16_t pingid; + + +int +open_dns(const char *domain, int localport, in_addr_t listen_ip) +{ + int fd; + int flag; + struct sockaddr_in addr; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(localport); + /* listen_ip already in network byte order from inet_addr, or 0 */ + addr.sin_addr.s_addr = listen_ip; + + fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if(fd < 0) { + warn("socket"); + return -1; + } + + flag = 1; +#ifdef SO_REUSEPORT + setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &flag, sizeof(flag)); +#endif + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)); + + if(bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { + warn("bind"); + return -1; + } + + // Save top domain used + strncpy(topdomain, domain, sizeof(topdomain) - 1); + topdomain[sizeof(topdomain) - 1] = '\0'; + + printf("Opened UDP socket\n"); + + return fd; +} + +int +dns_settarget(const char *host) +{ + struct hostent *h; + + // Init dns target struct + h = gethostbyname(host); + if (!h) { + printf("Could not resolve name %s, exiting\n", host); + return -1; + } + + memset(&peer, 0, sizeof(peer)); + peer.sin_family = AF_INET; + peer.sin_port = htons(53); + peer.sin_addr = *((struct in_addr *) h->h_addr); + + // Init chunk id + chunkid = 0; + pingid = 0; + + return 0; +} + +void +close_dns(int fd) +{ + close(fd); +} + +int +dns_sending() +{ + return (packetlen != 0); +} + +static void +dns_send_chunk(int fd) +{ + int avail; + char *p; + + p = activepacket; + p += packetpos; + avail = packetlen - packetpos; + lastlen = dns_write(fd, ++chunkid, p, avail, 0); +} + +void +dns_handle_tun(int fd, char *data, int len) +{ + memcpy(activepacket, data, MIN(len, sizeof(activepacket))); + lastlen = 0; + packetpos = 0; + packetlen = len; + + dns_send_chunk(fd); +} + +void +dns_ping(int dns_fd) +{ + char data[2]; + if (dns_sending()) { + lastlen = 0; + packetpos = 0; + packetlen = 0; + } + data[0] = (pingid & 0xFF00) >> 8; + data[1] = (pingid & 0xFF); + dns_write(dns_fd, ++pingid, data, 2, 'P'); +} + +void +dns_handshake(int dns_fd) +{ + char data[2]; + data[0] = (pingid & 0xFF00) >> 8; + data[1] = (pingid & 0xFF); + dns_write(dns_fd, ++pingid, data, 2, 'H'); +} + +static void +dns_query(int fd, int id, char *host, int type) +{ + char *p; + int len; + int peerlen; + char buf[1024]; + HEADER *header; + + len = 0; + memset(buf, 0, sizeof(buf)); + + header = (HEADER*)buf; + + header->id = htons(id); + header->qr = 0; + header->opcode = 0; + header->aa = 0; + header->tc = 0; + header->rd = 1; + header->ra = 0; + + header->qdcount = htons(1); + header->arcount = htons(1); + + p = buf + sizeof(HEADER); + p += dns_encode_hostname(host, p, strlen(host)); + + putshort(&p, type); + putshort(&p, C_IN); + + // EDNS0 + 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); + + len = p - buf; + sendto(fd, buf, len, 0, (struct sockaddr*)&peer, peerlen); +} + +static int +dns_write(int fd, int id, char *buf, int len, char flag) +{ + int avail; + int written; + int encoded; + char data[257]; + char *d; + + avail = 0xFF - strlen(topdomain) - 2; + memset(data, 0, sizeof(data)); + d = data; + 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 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; + uint32_t ttl; + short rlen; + short type; + short class; + short qdcount; + short ancount; + char *data; + char name[255]; + char rdata[4*1024]; + HEADER *header; + + rv = 0; + header = (HEADER*)packet; + + data = packet + sizeof(HEADER); + + if(header->qr) { /* qr=1 => response */ + qdcount = ntohs(header->qdcount); + ancount = ntohs(header->ancount); + + rlen = 0; + + if(qdcount == 1) { + readname(packet, packetlen, &data, name, sizeof(name)); + readshort(packet, &data, &type); + readshort(packet, &data, &class); + } + if(ancount == 1) { + readname(packet, packetlen, &data, name, sizeof(name)); + readshort(packet, &data, &type); + readshort(packet, &data, &class); + readlong(packet, &data, &ttl); + readshort(packet, &data, &rlen); + rv = MIN(rlen, sizeof(rdata)); + readdata(packet, &data, rdata, rv); + } + + if(type == T_NULL && rv > 2) { + rv = MIN(rv, buflen); + memcpy(outbuf, rdata, rv); + } + } + + return rv; +} + +int +dns_encode_hostname(const char *host, char *buffer, int size) +{ + char *h; + char *p; + char *word; + int left; + + h = strdup(host); + memset(buffer, 0, size); + p = buffer; + left = size; + + word = strtok(h, "."); + while(word) { + if (strlen(word) > 63 || strlen(word) > left) { + return -1; + } + left -= (strlen(word) + 1); + *p++ = (char)strlen(word); + memcpy(p, word, strlen(word)); + p += strlen(word); + + word = strtok(NULL, "."); + } + + *p++ = 0; + + free(h); + + return p - buffer; +} + +void +dnsd_send(int fd, struct query *q, char *data, int datalen) +{ + int len; + char *p; + char buf[64*1024]; + short name; + HEADER *header; + + memset(buf, 0, sizeof(buf)); + + len = 0; + header = (HEADER*)buf; + + header->id = htons(q->id); + header->qr = 1; + header->opcode = 0; + header->aa = 1; + header->tc = 0; + header->rd = 0; + header->ra = 0; + + header->qdcount = htons(1); + header->ancount = htons(1); + + p = buf + sizeof(HEADER); + + name = 0xc000 | ((p - buf) & 0x3fff); + p += dns_encode_hostname(q->name, p, strlen(q->name)); + 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(&p, datalen); + putdata(&p, data, datalen); + + len = p - buf; + sendto(fd, buf, len, 0, (struct sockaddr*)&q->from, q->fromlen); +} + +static int +decodepacket(const char *name, char *buf, int buflen) +{ + int len; + char *domain; + + domain = strstr(name, topdomain); + + len = decode_data(buf, buflen, name, domain); + if (len == buflen) + return -1; + return len; +} + +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[257]; + 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); + + if (r >= sizeof(HEADER)) { + header = (HEADER*)packet; + + id = ntohs(header->id); + + data = packet + sizeof(HEADER); + + if(!header->qr) { + qdcount = ntohs(header->qdcount); + + if(qdcount == 1) { + readname(packet, r, &data, name, sizeof(name) -1); + name[256] = 0; + readshort(packet, &data, &type); + readshort(packet, &data, &class); + + strncpy(q->name, name, 257); + q->type = type; + q->id = id; + q->fromlen = addrlen; + memcpy((struct sockaddr*)&q->from, (struct sockaddr*)&from, addrlen); + + rv = decodepacket(name, buf, buflen); + } + } + } else if (r < 0) { // Error + perror("recvfrom"); + rv = 0; + } else { // Packet too small to be dns protocol + rv = 0; + } + + return rv; +} diff --git a/dns.h b/dns.h new file mode 100644 index 0000000..f8da628 --- /dev/null +++ b/dns.h @@ -0,0 +1,44 @@ +/* + * 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 _DNS_H_ +#define _DNS_H_ + +int open_dns(const char *, int, in_addr_t); +int dns_settarget(const char*); +void close_dns(int); + +int dns_sending(); +void dns_handle_tun(int, char *, int); +void dns_ping(int); +void dns_handshake(int); +int dns_read(int, char *, int); +int dns_encode_hostname(const char *, char *, int); + +extern struct sockaddr_in peer; + +int dnsd_read(int, struct query*, char *, int); +void dnsd_send(int, struct query*, char *, int); + +int dnsd_haspacket(); +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..17092b0 --- /dev/null +++ b/encoding.c @@ -0,0 +1,226 @@ +/* + * 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; + + // flag is special character to be placed first in the encoded data + if (flag != 0) { + *dest = flag; + } else { + // First byte is 0 for middle packet and 1 for last packet + *dest = '0' + final; + } + dest++; + + memset(encoded, 0, 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; + memset(padding, 0, 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++; + + memset(encoded, 0, 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 new file mode 100644 index 0000000..8dddc32 --- /dev/null +++ b/iodine.c @@ -0,0 +1,311 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tun.h" +#include "structs.h" +#include "dns.h" + +#ifndef MAX +#define MAX(a,b) ((a)>(b)?(a):(b)) +#endif + +int running = 1; + +static void +sighandler(int sig) { + running = 0; +} + +static int +tunnel(int tun_fd, int dns_fd) +{ + 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; + tv.tv_usec = 0; + + FD_ZERO(&fds); + if (!dns_sending()) + FD_SET(tun_fd, &fds); + FD_SET(dns_fd, &fds); + + i = select(MAX(tun_fd, dns_fd) + 1, &fds, NULL, NULL, &tv); + + if (!running) { + rv = 1; + break; + } + + 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) + continue; + + outlen = sizeof(out); + compress2(out, &outlen, in, read, 9); + dns_handle_tun(dns_fd, out, outlen); + } + if(FD_ISSET(dns_fd, &fds)) { + read = dns_read(dns_fd, in, sizeof(in)); + if (read <= 0) + continue; + + outlen = sizeof(out); + uncompress(out, &outlen, in, read); + + write_tun(tun_fd, out, outlen); + if (!dns_sending()) + dns_ping(dns_fd); + } + } else + dns_ping(dns_fd); + } + + return rv; +} + +static int +handshake(int dns_fd) +{ + struct timeval tv; + char server[65]; + char client[65]; + char in[4096]; + int timeout; + fd_set fds; + int read; + int mtu; + int i; + int r; + + timeout = 1; + + for (i=0; running && i<5 ;i++) { + tv.tv_sec = timeout++; + tv.tv_usec = 0; + + dns_handshake(dns_fd); + + FD_ZERO(&fds); + FD_SET(dns_fd, &fds); + + r = select(dns_fd + 1, &fds, NULL, NULL, &tv); + + if(r > 0) { + read = dns_read(dns_fd, in, sizeof(in)); + + if(read < 0) { + perror("read"); + continue; + } + + if (read > 0) { + if (sscanf(in, "%64[^-]-%64[^-]-%d", + server, client, &mtu) == 3) { + + server[64] = 0; + client[64] = 0; + if (tun_setip(client) == 0 && + tun_setmtu(mtu) == 0) { + + return 0; + } else { + warn("Received handshake with bad data"); + } + } else { + printf("Received bad handshake\n"); + } + } + } + + printf("Retrying...\n"); + } + + return 1; +} + +static void +usage() { + 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); + 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("nameserver is the IP number of the relaying nameserver\n"); + printf("topdomain is the FQDN that is delegated to the tunnel endpoint.\n"); + exit(0); +} + +static void +version() { + printf("iodine IP over DNS tunneling client\n"); + printf("version: 0.3.4 from 2006-11-08\n"); + exit(0); +} + +int +main(int argc, char **argv) +{ + struct passwd *pw; + char *username; + int foreground; + char *newroot; + char *device; + int choice; + int tun_fd; + int dns_fd; + + username = NULL; + foreground = 0; + newroot = NULL; + device = NULL; + + while ((choice = getopt(argc, argv, "vfhu:t:d:")) != -1) { + switch(choice) { + case 'v': + version(); + break; + case 'f': + foreground = 1; + break; + case 'h': + help(); + break; + case 'u': + username = optarg; + break; + case 't': + newroot = optarg; + break; + case 'd': + device = optarg; + break; + default: + usage(); + break; + } + } + + if (geteuid() != 0) { + printf("Run as root and you'll be happy.\n"); + usage(); + } + + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + if(username) { + pw = getpwnam(username); + if (!pw) { + printf("User %s does not exist!\n", username); + usage(); + } + } + + if ((tun_fd = open_tun(device)) == -1) + goto cleanup1; + if ((dns_fd = open_dns(argv[1], 0, INADDR_ANY)) == -1) + goto cleanup2; + if (dns_settarget(argv[0]) == -1) + goto cleanup2; + + printf("Sending queries for %s to %s\n", argv[1], argv[0]); + + signal(SIGINT, sighandler); + signal(SIGTERM, sighandler); + + if(handshake(dns_fd)) + goto cleanup2; + + if (newroot) { + if (chroot(newroot) != 0 || chdir("/") != 0) + err(1, "%s", newroot); + seteuid(geteuid()); + setuid(getuid()); + } + + if (!foreground) { + printf("Detaching from terminal...\n"); + daemon(0, 0); + umask(0); + alarm(0); + } + + if (username) { + if (setgid(pw->pw_gid) < 0 || setuid(pw->pw_uid) < 0) { + printf("Could not switch to user %s!\n", username); + usage(); + } + } + + tunnel(tun_fd, dns_fd); + +cleanup2: + close_dns(dns_fd); + close_tun(tun_fd); +cleanup1: + + return 0; +} diff --git a/iodined.c b/iodined.c new file mode 100644 index 0000000..9321a53 --- /dev/null +++ b/iodined.c @@ -0,0 +1,339 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tun.h" +#include "structs.h" +#include "dns.h" + +#ifndef MAX +#define MAX(a,b) ((a)>(b)?(a):(b)) +#endif + +int running = 1; + +struct packet packetbuf; +struct packet outpacket; +int outid; + +struct query q; + +int my_mtu; +in_addr_t my_ip; + +static void +sigint(int sig) { + running = 0; +} + +static int +tunnel(int tun_fd, int dns_fd) +{ + 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 i; + + while (running) { + if (q.id != 0) { + tv.tv_sec = 0; + tv.tv_usec = 5000; + } else { + tv.tv_sec = 1; + tv.tv_usec = 0; + } + + FD_ZERO(&fds); + if(outpacket.len == 0) + FD_SET(tun_fd, &fds); + 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 (i==0) { + if (q.id != 0) { + dnsd_send(dns_fd, &q, outpacket.data, outpacket.len); + outpacket.len = 0; + q.id = 0; + } + } else { + if(FD_ISSET(tun_fd, &fds)) { + read = read_tun(tun_fd, in, sizeof(in)); + if (read <= 0) + continue; + + outlen = sizeof(out); + compress2(out, &outlen, in, read, 9); + memcpy(outpacket.data, out, outlen); + outpacket.len = outlen; + } + if(FD_ISSET(dns_fd, &fds)) { + read = dnsd_read(dns_fd, &q, in, sizeof(in)); + if (read <= 0) + continue; + + if(in[0] == 'H' || in[0] == 'h') { + 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')) { + if ((in[0] >= '0' && in[0] <= '9')) + code = in[0] - '0'; + if ((in[0] >= 'a' && in[0] <= 'f')) + code = in[0] - 'a' + 10; + if ((in[0] >= 'A' && in[0] <= 'F')) + code = in[0] - 'A' + 10; + + memcpy(packetbuf.data + packetbuf.offset, in + 1, read - 1); + packetbuf.len += read - 1; + packetbuf.offset += read - 1; + + if (code & 1) { + outlen = sizeof(out); + uncompress(out, &outlen, packetbuf.data, packetbuf.len); + + write_tun(tun_fd, out, outlen); + + packetbuf.len = packetbuf.offset = 0; + } + } + if (outpacket.len > 0) { + dnsd_send(dns_fd, &q, outpacket.data, outpacket.len); + outpacket.len = 0; + q.id = 0; + } + } + } + } + + return 0; +} + +static void +usage() { + extern char *__progname; + + printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] [-m mtu] [-l ip address to listen on] [-p port]" + " 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] [-l ip address to listen on] [-p port]" + " tunnel_ip 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(" -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(" -p port to listen on for incoming dns traffic (default 53)\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); +} + +static void +version() { + printf("iodine IP over DNS tunneling server\n"); + printf("version: 0.3.4 from 2006-11-08\n"); + exit(0); +} + +int +main(int argc, char **argv) +{ + int choice; + int tun_fd; + int dnsd_fd; + char *newroot; + char *username; + char *device; + int foreground; + int mtu; + struct passwd *pw; + in_addr_t listen_ip; + int port; + + username = NULL; + newroot = NULL; + device = NULL; + foreground = 0; + mtu = 1024; + listen_ip = INADDR_ANY; + port = 53; + + packetbuf.len = 0; + packetbuf.offset = 0; + outpacket.len = 0; + q.id = 0; + + while ((choice = getopt(argc, argv, "vfhu:t:d:m:l:p:")) != -1) { + switch(choice) { + case 'v': + version(); + break; + case 'f': + foreground = 1; + break; + case 'h': + help(); + break; + case 'u': + username = optarg; + break; + case 't': + newroot = optarg; + break; + case 'd': + device = optarg; + break; + case 'm': + mtu = atoi(optarg); + break; + case 'l': + listen_ip = inet_addr(optarg); + break; + case 'p': + port = atoi(optarg); + break; + default: + usage(); + break; + } + } + + argc -= optind; + argv += optind; + + if (geteuid() != 0) { + printf("Run as root and you'll be happy.\n"); + usage(); + } + + if (argc != 2) + usage(); + + if (username) { + pw = getpwnam(username); + if (!pw) { + printf("User %s does not exist!\n", username); + usage(); + } + } + + if (mtu == 0) { + printf("Bad MTU given.\n"); + 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], port, listen_ip)) == -1) + goto cleanup2; + + my_ip = inet_addr(argv[0]); + my_mtu = mtu; + + printf("Listening to dns for domain %s\n", argv[1]); + + if (newroot) { + if (chroot(newroot) != 0 || chdir("/") != 0) + err(1, "%s", newroot); + seteuid(geteuid()); + setuid(getuid()); + } + + if (!foreground) { + printf("Detaching from terminal...\n"); + daemon(0, 0); + umask(0); + alarm(0); + } + + signal(SIGINT, sigint); + if (username) { + if (setgid(pw->pw_gid) < 0 || setuid(pw->pw_uid) < 0) { + printf("Could not switch to user %s!\n", username); + usage(); + } + } + + tunnel(tun_fd, dnsd_fd); + +cleanup2: + close_dns(dnsd_fd); +cleanup1: + close_tun(tun_fd); +cleanup0: + + return 0; +} diff --git a/read.c b/read.c new file mode 100644 index 0000000..1e40dba --- /dev/null +++ b/read.c @@ -0,0 +1,178 @@ +/* + * 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 + +static int +readname_loop(char *packet, int packetlen, char **src, char *dst, size_t length, size_t loop) +{ + 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++; + + /* is this a compressed label? */ + if((c & 0xc0) == 0xc0) { + offset = (((s[-1] & 0x3f) << 8) | (s[0] & 0xff)); + if (offset > packetlen) { + if (len == 0) { + // Bad jump first in packet + return 0; + } else { + // Bad jump after some data + break; + } + } + dummy = packet + offset; + len += readname_loop(packet, packetlen, &dummy, d, length - len, loop - 1); + goto end; + } + + 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[len++] = '\0'; + +end: + (*src) = s+1; + return len; +} + +int +readname(char *packet, int packetlen, char **src, char *dst, size_t length) +{ + return readname_loop(packet, packetlen, 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, uint32_t *dst) +{ + // A long as described in dns protocol is always 32 bits + unsigned char *p; + + p = *src; + + *dst = ((uint32_t)p[0] << 24) + | ((uint32_t)p[1] << 16) + | ((uint32_t)p[2] << 8) + | ((uint32_t)p[3]); + + (*src) += sizeof(uint32_t); + return sizeof(uint32_t); +} + +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, uint32_t value) +{ + // A long as described in dns protocol is always 32 bits + unsigned char *p; + + p = *dst; + + *p++ = (value >> 24); + *p++ = (value >> 16); + *p++ = (value >> 8); + *p++ = (value); + + (*dst) = p; + return sizeof(uint32_t); +} + +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 new file mode 100644 index 0000000..d695a7e --- /dev/null +++ b/read.h @@ -0,0 +1,30 @@ +/* + * 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 _READ_H_ +#define _READ_H_ + +int readname(char *, int, char **, char *, size_t); +int readshort(char *, char **, short *); +int readlong(char *, char **, uint32_t *); +int readdata(char *, char **, char *, size_t); + +int putbyte(char **, char); +int putshort(char **, short); +int putlong(char **, uint32_t); +int putdata(char **, char *, size_t); + +#endif diff --git a/structs.h b/structs.h new file mode 100644 index 0000000..534f59c --- /dev/null +++ b/structs.h @@ -0,0 +1,35 @@ +/* + * 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 _STRUCTS_H_ +#define _STRUCTS_H_ + +struct packet +{ + int len; + int offset; + char data[64*1024]; +}; + +struct query { + char name[258]; + short type; + short id; + struct sockaddr from; + int fromlen; +}; + +#endif /* _STRUCTS_H_ */ diff --git a/test.c b/test.c new file mode 100644 index 0000000..a2098cf --- /dev/null +++ b/test.c @@ -0,0 +1,262 @@ +/* + * 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 + +#include "structs.h" +#include "encoding.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]; + uint32_t putint; + uint32_t tempi; + uint32_t tint; + uint32_t *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(uint32_t)); + if (putint != tempi) { + printf("Bad value on putlong for %d\n", i); + exit(2); + } + l = &tempi; + memcpy(buf, l, sizeof(uint32_t)); + 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 badjump[] = { + 'A', 'A', 0x81, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xcc, 0x00, 0x01, 0x00, 0x01 }; + char badjump2[] = { + 'A', 'A', 0x81, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 'B', 'A', 0xfe, 0xcc, 0x00, 0x01, 0x00, 0x01 }; + char *jumper; + char buf[1024]; + char *data; + int rv; + + printf(" * Testing readname... "); + fflush(stdout); + + memset(buf, 0, sizeof(buf)); + data = emptyloop + sizeof(HEADER); + buf[1023] = 'A'; + rv = readname(emptyloop, sizeof(emptyloop), &data, buf, 1023); + assert(buf[1023] == 'A'); + + memset(buf, 0, sizeof(buf)); + data = infloop + sizeof(HEADER); + buf[4] = '\a'; + rv = readname(infloop, sizeof(infloop), &data, buf, 4); + assert(buf[4] == '\a'); + + memset(buf, 0, sizeof(buf)); + data = longname + sizeof(HEADER); + buf[256] = '\a'; + rv = readname(longname, sizeof(longname), &data, buf, 256); + assert(buf[256] == '\a'); + + memset(buf, 0, sizeof(buf)); + data = onejump + sizeof(HEADER); + rv = readname(onejump, sizeof(onejump), &data, buf, 256); + assert(rv == 9); + + // 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 = jumper + sizeof(HEADER); + rv = readname(jumper, sizeof(badjump), &data, buf, 256); + assert(rv == 0); + } + free(jumper); + + memset(buf, 0, sizeof(buf)); + jumper = malloc(sizeof(badjump2)); + if (jumper) { + memcpy(jumper, badjump2, sizeof(badjump2)); + data = jumper + sizeof(HEADER); + rv = readname(jumper, sizeof(badjump2), &data, buf, 256); + assert(rv == 4); + assert(strcmp("BA.", buf) == 0); + } + free(jumper); + + printf("OK\n"); +} + +static void +test_encode_hostname() { + char buf[256]; + int len; + int ret; + + len = 256; + printf(" * Testing hostname encoding... "); + fflush(stdout); + + memset(buf, 0, 256); + ret = dns_encode_hostname( // More than 63 chars between dots + "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ" + , buf, len); + assert(ret == -1); + + memset(buf, 0, 256); + ret = dns_encode_hostname( // More chars than fits into array + "ABCDEFGHIJKLMNOPQRSTUVWXYZ.ABCDEFGHIJKLMNOPQRSTUVWXYZ.ABCDEFGHIJKLMNOPQRSTUVWXYZ.ABCDEFGHIJKLMNOPQRSTUVWXYZ." + "ABCDEFGHIJKLMNOPQRSTUVWXYZ.ABCDEFGHIJKLMNOPQRSTUVWXYZ.ABCDEFGHIJKLMNOPQRSTUVWXYZ.ABCDEFGHIJKLMNOPQRSTUVWXYZ." + "ABCDEFGHIJKLMNOPQRSTUVWXYZ.ABCDEFGHIJKLMNOPQRSTUVWXYZ.ABCDEFGHIJKLMNOPQRSTUVWXYZ.ABCDEFGHIJKLMNOPQRSTUVWXYZ." + , buf, len); + assert(ret == -1); + assert(strlen(buf) < len); + + printf("OK\n"); +} + +static void +test_base32() { + char temp[256]; + char *start = "HELLOTEST"; + char *out = "1HELLOTEST"; + char *end; + char *tempend; + int codedlength; + + printf(" * Testing base32 encoding... "); + fflush(stdout); + + memset(temp, 0, sizeof(temp)); + end = malloc(16); + memset(end, 0, 16); + + codedlength = encode_data(start, 9, 256, temp, 0); + tempend = temp + strlen(temp); + decode_data(end, 16, temp, tempend); + assert(strcmp(out, end) == 0); + free(end); + + printf("OK\n"); +} + +int +main() +{ + printf("** iodine test suite\n"); + + test_readputshort(); + test_readputlong(); + test_readname(); + test_encode_hostname(); + test_base32(); + + printf("** All went well :)\n"); + return 0; +} diff --git a/tun.c b/tun.c new file mode 100644 index 0000000..0f5cef0 --- /dev/null +++ b/tun.c @@ -0,0 +1,233 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "tun.h" + +#define TUN_MAX_TRY 50 + +char if_name[50]; + +#ifdef LINUX + +#include +#include +#include + +int +open_tun(const char *tun_device) +{ + int i; + int tun_fd; + struct ifreq ifreq; + char *tunnel = "/dev/net/tun"; + + if ((tun_fd = open(tunnel, O_RDWR)) < 0) { + warn("open_tun: %s: %s", tunnel, strerror(errno)); + return -1; + } + + memset(&ifreq, 0, sizeof(ifreq)); + + ifreq.ifr_flags = IFF_TUN; + + if (tun_device != NULL) { + strncpy(ifreq.ifr_name, tun_device, IFNAMSIZ); + strncpy(if_name, tun_device, sizeof(if_name)); + + if (ioctl(tun_fd, TUNSETIFF, (void *) &ifreq) != -1) { + printf("Opened %s\n", ifreq.ifr_name); + return tun_fd; + } + + if (errno != EBUSY) { + warn("open_tun: ioctl[TUNSETIFF]: %s", strerror(errno)); + return -1; + } + } else { + for (i = 0; i < TUN_MAX_TRY; i++) { + snprintf(ifreq.ifr_name, IFNAMSIZ, "dns%d", i); + + if (ioctl(tun_fd, TUNSETIFF, (void *) &ifreq) != -1) { + printf("Opened %s\n", ifreq.ifr_name); + snprintf(if_name, sizeof(if_name), "dns%d", i); + return tun_fd; + } + + if (errno != EBUSY) { + warn("open_tun: ioctl[TUNSETIFF]: %s", strerror(errno)); + return -1; + } + } + + warn("open_tun: Couldn't set interface name"); + } + return -1; +} + +#else /* BSD */ + +int +open_tun(const char *tun_device) +{ + int i; + int tun_fd; + char tun_name[50]; + + if (tun_device != NULL) { + snprintf(tun_name, sizeof(tun_name), "/dev/%s", tun_device); + strncpy(if_name, tun_device, sizeof(if_name)); + + if ((tun_fd = open(tun_name, O_RDWR)) < 0) { + warn("open_tun: %s: %s", tun_name, strerror(errno)); + return -1; + } + + printf("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); + snprintf(if_name, sizeof(if_name), "tun%d", i); + return tun_fd; + } + + if (errno == ENOENT) + break; + } + + warn("open_tun: Failed to open tunneling device"); + } + + return -1; +} + +#endif /* !LINUX */ + +void +close_tun(int tun_fd) +{ + if (tun_fd >= 0) + close(tun_fd); +} + +int +write_tun(int tun_fd, char *data, int len) +{ +#if defined (FREEBSD) || defined (DARWIN) || defined(NETBSD) + data += 4; + len -= 4; +#else /* !FREEBSD/DARWIN */ +#ifdef LINUX + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0x08; + data[3] = 0x00; +#else /* OPENBSD */ + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x02; +#endif /* !LINUX */ +#endif /* FREEBSD */ + + if (write(tun_fd, data, len) != len) { + warn("write_tun"); + return 1; + } + return 0; +} + +int +read_tun(int tun_fd, char *buf, int len) +{ +#if defined (FREEBSD) || defined (DARWIN) || defined(NETBSD) + // FreeBSD has no header + return read(tun_fd, buf + 4, len - 4) + 4; +#else /* !FREEBSD */ + return read(tun_fd, buf, len); +#endif /* !FREEBSD */ +} + +int +tun_setip(const char *ip) +{ + char cmdline[512]; + + if (inet_addr(ip) != INADDR_NONE) { + snprintf(cmdline, sizeof(cmdline), + "/sbin/ifconfig %s %s %s netmask 255.255.255.0", + if_name, + ip, + ip); + + printf("Setting IP of %s to %s\n", if_name, ip); +#ifndef LINUX + int r; + + r = system(cmdline); + if(r != 0) { + return r; + } else { + snprintf(cmdline, sizeof(cmdline), + "/sbin/route add %s/24 %s", + ip, ip); + } + printf("Adding route %s/24 to %s\n", ip, ip); +#endif + return system(cmdline); + } else { + printf("Invalid IP: %s!\n", ip); + } + + return 1; +} + +int +tun_setmtu(const int mtu) +{ + char cmdline[512]; + + if (mtu > 200 && mtu < 1500) { + snprintf(cmdline, sizeof(cmdline), + "/sbin/ifconfig %s mtu %d", + if_name, + mtu); + + printf("Setting MTU of %s to %d\n", if_name, mtu); + return system(cmdline); + } else { + warn("MTU out of range: %d\n", mtu); + } + + return 1; +} + diff --git a/tun.h b/tun.h new file mode 100644 index 0000000..0a32dcd --- /dev/null +++ b/tun.h @@ -0,0 +1,29 @@ +/* + * 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 _TUN_H_ +#define _TUN_H_ + +int open_tun(const char *); +void close_tun(int); +int write_tun(int, char *, int); +int read_tun(int, char *, int); +int tun_setip(const char *); +int tun_setmtu(const int); + +#endif /* _TUN_H_ */ -- 2.30.2