--- /dev/null
+
+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
--- /dev/null
+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
+
--- /dev/null
+
+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.
--- /dev/null
+
+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)
+
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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_ */