[svn-inject] Installing original source of iodine upstream/0.3.4
authorgregor herrmann <gregoa@debian.org>
Tue, 20 Mar 2007 21:31:09 +0000 (21:31 -0000)
committergregor herrmann <gregoa@debian.org>
Tue, 20 Mar 2007 21:31:09 +0000 (21:31 -0000)
16 files changed:
CHANGELOG [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
dns.c [new file with mode: 0644]
dns.h [new file with mode: 0644]
encoding.c [new file with mode: 0644]
encoding.h [new file with mode: 0644]
iodine.c [new file with mode: 0644]
iodined.c [new file with mode: 0644]
read.c [new file with mode: 0644]
read.h [new file with mode: 0644]
structs.h [new file with mode: 0644]
test.c [new file with mode: 0644]
tun.c [new file with mode: 0644]
tun.h [new file with mode: 0644]

diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644 (file)
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 (file)
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 (file)
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 <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+
+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 (file)
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 (file)
index 0000000..ade40de
--- /dev/null
+++ b/dns.c
@@ -0,0 +1,481 @@
+/*
+ * Copyright (c) 2006 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ *
+ * 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 <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#ifdef DARWIN
+#include <arpa/nameser8_compat.h>
+#endif
+#include <netdb.h>
+#include <time.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.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 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 (file)
index 0000000..f8da628
--- /dev/null
+++ b/dns.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2006 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ *
+ * 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 (file)
index 0000000..17092b0
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2006 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ *
+ * 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 <stdio.h>
+#include <strings.h>
+#include <string.h>
+
+// 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 (file)
index 0000000..3d1ef34
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2006 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ *
+ * 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 (file)
index 0000000..8dddc32
--- /dev/null
+++ b/iodine.c
@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) 2006 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ *
+ * 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 <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <err.h>
+#include <pwd.h>
+#include <arpa/inet.h>
+#include <zlib.h>
+
+#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 (file)
index 0000000..9321a53
--- /dev/null
+++ b/iodined.c
@@ -0,0 +1,339 @@
+/*
+ * Copyright (c) 2006 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ *
+ * 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 <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <err.h>
+#include <pwd.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <zlib.h>
+
+#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 (file)
index 0000000..1e40dba
--- /dev/null
+++ b/read.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2006 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ *
+ * 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 <string.h>
+#include <stdint.h>
+
+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 (file)
index 0000000..d695a7e
--- /dev/null
+++ b/read.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2006 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ *
+ * 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 (file)
index 0000000..534f59c
--- /dev/null
+++ b/structs.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2006 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ *
+ * 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 (file)
index 0000000..a2098cf
--- /dev/null
+++ b/test.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 2006 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ *
+ * 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 <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/stat.h>
+#include <arpa/nameser.h>
+#ifdef DARWIN
+#include <arpa/nameser8_compat.h>
+#endif
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#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 (file)
index 0000000..0f5cef0
--- /dev/null
+++ b/tun.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2006 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <err.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include "tun.h"
+
+#define TUN_MAX_TRY 50
+
+char if_name[50];
+
+#ifdef LINUX
+
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <linux/if_tun.h>
+
+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 (file)
index 0000000..0a32dcd
--- /dev/null
+++ b/tun.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2006 
+ * Bjorn Andersson <flex@kryo.se>,
+ * Erik Ekman <yarrick@kryo.se>
+ *
+ * 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_ */