Compare commits

...

5 Commits

Author SHA1 Message Date
Erik Ekman 1defd22a20 Release 0.3.4 2006-11-08 21:50:15 +00:00
Erik Ekman 2b6054a939 Release 0.3.3 2006-11-05 11:52:20 +00:00
Erik Ekman 39d5049c44 Version 0.3.2 2006-09-11 19:11:45 +00:00
Erik Ekman 1feacfada3 Release 0.3.1 2006-07-11 08:54:05 +00:00
Erik Ekman fa06d5eea1 Branched first public release of iodine, 0.3 2006-06-24 12:21:54 +00:00
15 changed files with 1040 additions and 302 deletions

38
CHANGELOG Normal file
View File

@ -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

View File

@ -1,15 +1,20 @@
CC = gcc CC = gcc
CLIENT = iodine CLIENT = iodine
CLIENTOBJS = iodine.o tun.o dns.o read.o CLIENTOBJS = iodine.o tun.o dns.o read.o encoding.o
SERVER = iodined SERVER = iodined
SERVEROBJS = iodined.o tun.o dns.o read.o SERVEROBJS = iodined.o tun.o dns.o read.o encoding.o
TESTSUITE = tester
TESTOBJS = test.o dns.o read.o encoding.o
OS = `uname | tr "a-z" "A-Z"` OS = `uname | tr "a-z" "A-Z"`
LDFLAGS = -lz LDFLAGS = -lz
CFLAGS = -c -g -Wall -D$(OS) CFLAGS = -c -g -Wall -D$(OS)
all: stateos $(CLIENT) $(SERVER) all: stateos $(CLIENT) $(SERVER) $(TESTSUITE)
test: $(TESTSUITE)
@./$(TESTSUITE)
stateos: stateos:
@echo OS is $(OS) @echo OS is $(OS)
@ -22,11 +27,17 @@ $(SERVER): $(SERVEROBJS)
@echo LD $@ @echo LD $@
@$(CC) $(SERVEROBJS) -o $(SERVER) $(LDFLAGS) @$(CC) $(SERVEROBJS) -o $(SERVER) $(LDFLAGS)
$(TESTSUITE): $(TESTOBJS)
@echo LD $@
@$(CC) $(TESTOBJS) -o $(TESTSUITE) $(LDFLAGS)
@echo Running tests...
@./$(TESTSUITE)
.c.o: .c.o:
@echo CC $< @echo CC $<
@$(CC) $(CFLAGS) $< -o $@ @$(CC) $(CFLAGS) $< -o $@
clean: clean:
@echo "Cleaning..." @echo "Cleaning..."
@rm -f $(CLIENT) $(SERVER) *~ *.o *.core @rm -f $(CLIENT) $(SERVER) $(TESTSUITE) *~ *.o *.core

39
README
View File

@ -10,6 +10,20 @@ server. This can be usable in different situations where internet access is
firewalled, but DNS queries are allowed. 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: HOW TO USE:
Server side: Server side:
@ -53,17 +67,28 @@ 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 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. cached error message could mean that you started the client before the server.
The upstream data is sent gzipped encoded in hexadecimal. DNS protocol allows 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 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 can be max 63 chars. So your domain name and subdomain should be as short as
possible to allow maximum throughput. 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: PORTABILITY:
iodine has been tested on Linux (x86 and SPARC64), FreeBSD (x86) and iodine has been tested on Linux (x86, AMD64 and SPARC64), FreeBSD (x86),
OpenBSD (x86). It should work on other unix-like systems as well that has OpenBSD (x86), NetBSD (x86) and MacOS X (10.3, ppc, with
TUN/TAP tunneling support. Let us know if you get it to run on other platforms. 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:
@ -72,6 +97,12 @@ 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. 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: AUTHORS & LICENSE:
Copyright (c) 2006 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se> Copyright (c) 2006 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>

19
TODO Normal file
View File

@ -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)

295
dns.c
View File

@ -19,26 +19,33 @@
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <arpa/nameser.h> #include <arpa/nameser.h>
#ifdef DARWIN
#include <arpa/nameser8_compat.h>
#endif
#include <netdb.h> #include <netdb.h>
#include <time.h> #include <time.h>
#include <err.h> #include <err.h>
#include <stdio.h> #include <stdio.h>
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h>
#include <unistd.h> #include <unistd.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include "read.h"
#include "structs.h" #include "structs.h"
#include "dns.h" #include "dns.h"
#include "encoding.h"
#include "read.h"
// For FreeBSD // For FreeBSD
#ifndef MIN #ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b)) #define MIN(a,b) ((a)<(b)?(a):(b))
#endif #endif
static int host2dns(const char *, char *, int);
static int dns_write(int, int, char *, int, char); static int dns_write(int, int, char *, int, char);
static void dns_query(int, int, char *, int);
struct sockaddr_in peer; struct sockaddr_in peer;
char topdomain[256]; char topdomain[256];
@ -54,18 +61,19 @@ uint16_t pingid;
int int
open_dns(const char *domain, int localport) open_dns(const char *domain, int localport, in_addr_t listen_ip)
{ {
int fd; int fd;
int flag; int flag;
struct sockaddr_in addr; struct sockaddr_in addr;
bzero(&addr, sizeof(addr)); memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET; addr.sin_family = AF_INET;
addr.sin_port = htons(localport); addr.sin_port = htons(localport);
addr.sin_addr.s_addr = htonl(INADDR_ANY); /* listen_ip already in network byte order from inet_addr, or 0 */
addr.sin_addr.s_addr = listen_ip;
fd = socket(AF_INET, SOCK_DGRAM, 0); fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(fd < 0) { if(fd < 0) {
warn("socket"); warn("socket");
return -1; return -1;
@ -83,10 +91,11 @@ open_dns(const char *domain, int localport)
} }
// Save top domain used // Save top domain used
strncpy(topdomain, domain, sizeof(topdomain) - 2); strncpy(topdomain, domain, sizeof(topdomain) - 1);
topdomain[sizeof(topdomain) - 1] = 0; topdomain[sizeof(topdomain) - 1] = '\0';
printf("Opened UDP socket\n"); printf("Opened UDP socket\n");
return fd; return fd;
} }
@ -102,7 +111,7 @@ dns_settarget(const char *host)
return -1; return -1;
} }
bzero(&peer, sizeof(peer)); memset(&peer, 0, sizeof(peer));
peer.sin_family = AF_INET; peer.sin_family = AF_INET;
peer.sin_port = htons(53); peer.sin_port = htons(53);
peer.sin_addr = *((struct in_addr *) h->h_addr); peer.sin_addr = *((struct in_addr *) h->h_addr);
@ -172,7 +181,7 @@ dns_handshake(int dns_fd)
dns_write(dns_fd, ++pingid, data, 2, 'H'); dns_write(dns_fd, ++pingid, data, 2, 'H');
} }
void static void
dns_query(int fd, int id, char *host, int type) dns_query(int fd, int id, char *host, int type)
{ {
char *p; char *p;
@ -198,18 +207,18 @@ dns_query(int fd, int id, char *host, int type)
header->arcount = htons(1); header->arcount = htons(1);
p = buf + sizeof(HEADER); p = buf + sizeof(HEADER);
p += host2dns(host, p, strlen(host)); p += dns_encode_hostname(host, p, strlen(host));
PUTSHORT(type, p); putshort(&p, type);
PUTSHORT(C_IN, p); putshort(&p, C_IN);
// EDNS0 // EDNS0
*p++ = 0x00; //Root putbyte(&p, 0x00); //Root
PUTSHORT(0x0029, p); // OPT putshort(&p, 0x0029); // OPT
PUTSHORT(0x1000, p); // Payload size: 4096 putshort(&p, 0x1000); // Payload size: 4096
PUTSHORT(0x0000, p); // Higher bits/ edns version putshort(&p, 0x0000); // Higher bits/edns version
PUTSHORT(0x8000, p); // Z putshort(&p, 0x8000); // Z
PUTSHORT(0x0000, p); // Data length putshort(&p, 0x0000); // Data length
peerlen = sizeof(peer); peerlen = sizeof(peer);
@ -217,73 +226,68 @@ dns_query(int fd, int id, char *host, int type)
sendto(fd, buf, len, 0, (struct sockaddr*)&peer, peerlen); sendto(fd, buf, len, 0, (struct sockaddr*)&peer, peerlen);
} }
static void
put_hex(char *p, char h)
{
int t;
static const char to_hex[] = "0123456789ABCDEF";
t = (h & 0xF0) >> 4;
p[0] = to_hex[t];
t = h & 0x0F;
p[1] = to_hex[t];
}
static int static int
dns_write(int fd, int id, char *buf, int len, char flag) dns_write(int fd, int id, char *buf, int len, char flag)
{ {
int avail; int avail;
int i; int written;
int final; int encoded;
char data[257]; char data[257];
char *d; char *d;
#define CHUNK 31
// 31 bytes expands to 62 chars in domain
// We just use hex as encoding right now
avail = 0xFF - strlen(topdomain) - 2; avail = 0xFF - strlen(topdomain) - 2;
memset(data, 0, sizeof(data));
avail /= 2; // use two chars per byte in encoding
avail -= (avail/CHUNK); // make space for parts
avail = MIN(avail, len); // do not use more bytes than is available;
final = (avail == len); // is this the last block?
bzero(data, sizeof(data));
d = data; d = data;
written = encode_data(buf, len, avail, d, flag);
if (flag != 0) { encoded = strlen(data);
*d = flag; d += encoded;
} else {
// First byte is 0 for middle packet and 1 for last packet
*d = '0' + final;
}
d++;
if (len > 0) {
for (i = 0; i < avail; i++) {
if (i > 0 && i % 31 == 0) {
*d = '.';
d++;
}
put_hex(d, buf[i]);
d += 2;
}
}
if (*d != '.') { if (*d != '.') {
*d++ = '.'; *d++ = '.';
} }
strncpy(d, topdomain, strlen(topdomain)+1); strncpy(d, topdomain, strlen(topdomain)+1);
dns_query(fd, id, data, T_NULL); dns_query(fd, id, data, T_NULL);
return avail; return written;
} }
int int
dns_read(int fd, char *buf, int buflen) dns_read(int fd, char *buf, int buflen)
{ {
int r; int r;
long ttl; 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 rlen;
short type; short type;
short class; short class;
@ -293,79 +297,61 @@ dns_read(int fd, char *buf, int buflen)
char name[255]; char name[255];
char rdata[4*1024]; char rdata[4*1024];
HEADER *header; HEADER *header;
socklen_t addrlen;
char packet[64*1024];
struct sockaddr_in from;
addrlen = sizeof(struct sockaddr); rv = 0;
r = recvfrom(fd, packet, sizeof(packet), 0, (struct sockaddr*)&from, &addrlen); header = (HEADER*)packet;
data = packet + sizeof(HEADER);
if(r == -1) { if(header->qr) { /* qr=1 => response */
perror("recvfrom"); qdcount = ntohs(header->qdcount);
} else { ancount = ntohs(header->ancount);
header = (HEADER*)packet;
data = packet + sizeof(HEADER);
if(header->qr) { /* qr=1 => response */ rlen = 0;
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(qdcount == 1) { if(type == T_NULL && rv > 2) {
READNAME(packet, name, data); rv = MIN(rv, buflen);
READSHORT(type, data); memcpy(outbuf, rdata, rv);
READSHORT(class, data);
}
if(ancount == 1) {
READNAME(packet, name, data);
READSHORT(type, data);
READSHORT(class, data);
READLONG(ttl, data);
READSHORT(rlen, data);
READDATA(rdata, data, rlen);
}
if (dns_sending() && chunkid == ntohs(header->id)) {
// Got ACK on sent packet
packetpos += lastlen;
if (packetpos == packetlen) {
// Packet completed
packetpos = 0;
packetlen = 0;
lastlen = 0;
} else {
// More to send
dns_send_chunk(fd);
}
}
if(type == T_NULL && rlen > 2) {
memcpy(buf, rdata, rlen);
return rlen;
} else {
return 0;
}
} }
} }
return 0; return rv;
} }
int
static int dns_encode_hostname(const char *host, char *buffer, int size)
host2dns(const char *host, char *buffer, int size)
{ {
char *h; char *h;
char *p; char *p;
char *word; char *word;
int left;
h = strdup(host); h = strdup(host);
memset(buffer, 0, size); memset(buffer, 0, size);
p = buffer; p = buffer;
left = size;
word = strtok(h, "."); word = strtok(h, ".");
while(word) { while(word) {
if (strlen(word) > 63 || strlen(word) > left) {
return -1;
}
left -= (strlen(word) + 1);
*p++ = (char)strlen(word); *p++ = (char)strlen(word);
memcpy(p, word, strlen(word)); memcpy(p, word, strlen(word));
p += strlen(word); p += strlen(word);
@ -386,6 +372,7 @@ dnsd_send(int fd, struct query *q, char *data, int datalen)
int len; int len;
char *p; char *p;
char buf[64*1024]; char buf[64*1024];
short name;
HEADER *header; HEADER *header;
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
@ -406,24 +393,20 @@ dnsd_send(int fd, struct query *q, char *data, int datalen)
p = buf + sizeof(HEADER); p = buf + sizeof(HEADER);
p += host2dns(q->name, p, strlen(q->name)); name = 0xc000 | ((p - buf) & 0x3fff);
PUTSHORT(q->type, p); p += dns_encode_hostname(q->name, p, strlen(q->name));
PUTSHORT(C_IN, p); putshort(&p, q->type);
putshort(&p, C_IN);
p += host2dns(q->name, p, strlen(q->name));
PUTSHORT(q->type, p); putshort(&p, name);
PUTSHORT(C_IN, p); putshort(&p, q->type);
PUTLONG(0, p); putshort(&p, C_IN);
putlong(&p, 0);
q->id = 0; q->id = 0;
if(datalen > 0) { putshort(&p, datalen);
PUTSHORT(datalen, p); putdata(&p, data, datalen);
memcpy(p, data, datalen);
p += datalen;
} else {
PUTSHORT(0, p);
}
len = p - buf; len = p - buf;
sendto(fd, buf, len, 0, (struct sockaddr*)&q->from, q->fromlen); sendto(fd, buf, len, 0, (struct sockaddr*)&q->from, q->fromlen);
@ -432,32 +415,12 @@ dnsd_send(int fd, struct query *q, char *data, int datalen)
static int static int
decodepacket(const char *name, char *buf, int buflen) decodepacket(const char *name, char *buf, int buflen)
{ {
int r;
int len; int len;
char *dp;
char *domain; char *domain;
const char *np;
len = 1;
domain = strstr(name, topdomain); domain = strstr(name, topdomain);
buf[0] = name[0]; len = decode_data(buf, buflen, name, domain);
dp = buf + 1;
np = name + 1;
while(len < buflen && np < domain) {
if(*np == '.') {
np++;
continue;
}
sscanf(np, "%02X", &r);
*dp++ = (char)r;
np+=2;
len++;
}
if (len == buflen) if (len == buflen)
return -1; return -1;
return len; return len;
@ -467,12 +430,13 @@ int
dnsd_read(int fd, struct query *q, char *buf, int buflen) dnsd_read(int fd, struct query *q, char *buf, int buflen)
{ {
int r; int r;
int rv;
short id; short id;
short type; short type;
short class; short class;
short qdcount; short qdcount;
char *data; char *data;
char name[255]; char name[257];
HEADER *header; HEADER *header;
socklen_t addrlen; socklen_t addrlen;
char packet[64*1024]; char packet[64*1024];
@ -481,9 +445,7 @@ dnsd_read(int fd, struct query *q, char *buf, int buflen)
addrlen = sizeof(struct sockaddr); addrlen = sizeof(struct sockaddr);
r = recvfrom(fd, packet, sizeof(packet), 0, (struct sockaddr*)&from, &addrlen); r = recvfrom(fd, packet, sizeof(packet), 0, (struct sockaddr*)&from, &addrlen);
if(r == -1) { if (r >= sizeof(HEADER)) {
perror("recvfrom");
} else {
header = (HEADER*)packet; header = (HEADER*)packet;
id = ntohs(header->id); id = ntohs(header->id);
@ -494,21 +456,26 @@ dnsd_read(int fd, struct query *q, char *buf, int buflen)
qdcount = ntohs(header->qdcount); qdcount = ntohs(header->qdcount);
if(qdcount == 1) { if(qdcount == 1) {
bzero(name, sizeof(name)); readname(packet, r, &data, name, sizeof(name) -1);
READNAME(packet, name, data); name[256] = 0;
READSHORT(type, data); readshort(packet, &data, &type);
READSHORT(class, data); readshort(packet, &data, &class);
strncpy(q->name, name, 256); strncpy(q->name, name, 257);
q->type = type; q->type = type;
q->id = id; q->id = id;
q->fromlen = addrlen; q->fromlen = addrlen;
memcpy((struct sockaddr*)&q->from, (struct sockaddr*)&from, addrlen); memcpy((struct sockaddr*)&q->from, (struct sockaddr*)&from, addrlen);
return decodepacket(name, buf, buflen); 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 0; return rv;
} }

6
dns.h
View File

@ -17,7 +17,7 @@
#ifndef _DNS_H_ #ifndef _DNS_H_
#define _DNS_H_ #define _DNS_H_
int open_dns(const char *, int); int open_dns(const char *, int, in_addr_t);
int dns_settarget(const char*); int dns_settarget(const char*);
void close_dns(int); void close_dns(int);
@ -25,8 +25,8 @@ int dns_sending();
void dns_handle_tun(int, char *, int); void dns_handle_tun(int, char *, int);
void dns_ping(int); void dns_ping(int);
void dns_handshake(int); void dns_handshake(int);
void dns_query(int, int, char *, int);
int dns_read(int, char *, int); int dns_read(int, char *, int);
int dns_encode_hostname(const char *, char *, int);
extern struct sockaddr_in peer; extern struct sockaddr_in peer;
@ -38,5 +38,7 @@ int dnsd_hasack();
void dnsd_forceack(int); void dnsd_forceack(int);
void dnsd_queuepacket(const char *, const int); void dnsd_queuepacket(const char *, const int);
int dns_parse_reply(char *, int, char *, int);
#endif /* _DNS_H_ */ #endif /* _DNS_H_ */

226
encoding.c Normal file
View File

@ -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;
}

23
encoding.h Normal file
View File

@ -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_ */

105
iodine.c
View File

@ -20,6 +20,7 @@
#include <string.h> #include <string.h>
#include <signal.h> #include <signal.h>
#include <unistd.h> #include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/stat.h> #include <sys/stat.h>
@ -39,9 +40,6 @@
int running = 1; int running = 1;
int tun_fd;
int dns_fd;
static void static void
sighandler(int sig) { sighandler(int sig) {
running = 0; running = 0;
@ -50,13 +48,16 @@ sighandler(int sig) {
static int static int
tunnel(int tun_fd, int dns_fd) tunnel(int tun_fd, int dns_fd)
{ {
int i;
int read;
fd_set fds;
struct timeval tv;
char in[64*1024];
long outlen;
char out[64*1024]; char 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) { while (running) {
tv.tv_sec = 1; tv.tv_sec = 1;
@ -68,17 +69,17 @@ tunnel(int tun_fd, int dns_fd)
FD_SET(dns_fd, &fds); FD_SET(dns_fd, &fds);
i = select(MAX(tun_fd, dns_fd) + 1, &fds, NULL, NULL, &tv); i = select(MAX(tun_fd, dns_fd) + 1, &fds, NULL, NULL, &tv);
if(i < 0) { if (!running) {
if (running) { rv = 1;
warn("select"); break;
}
return 1;
} }
if(i == 0) { if(i < 0) {
dns_ping(dns_fd); warn("select");
} else { rv = 1;
break;
} else if (i > 0) {
if(FD_ISSET(tun_fd, &fds)) { if(FD_ISSET(tun_fd, &fds)) {
read = read_tun(tun_fd, in, sizeof(in)); read = read_tun(tun_fd, in, sizeof(in));
if(read <= 0) if(read <= 0)
@ -100,24 +101,26 @@ tunnel(int tun_fd, int dns_fd)
if (!dns_sending()) if (!dns_sending())
dns_ping(dns_fd); dns_ping(dns_fd);
} }
} } else
dns_ping(dns_fd);
} }
return 0; return rv;
} }
static int static int
handshake(int dns_fd) 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 i;
int r; int r;
char *p;
int mtu;
int read;
fd_set fds;
int timeout;
char in[4096];
struct timeval tv;
timeout = 1; timeout = 1;
@ -141,15 +144,20 @@ handshake(int dns_fd)
} }
if (read > 0) { if (read > 0) {
p = strchr(in, '-'); if (sscanf(in, "%64[^-]-%64[^-]-%d",
if (p) { server, client, &mtu) == 3) {
*p++ = '\0';
mtu = atoi(p); server[64] = 0;
client[64] = 0;
if (tun_setip(client) == 0 &&
tun_setmtu(mtu) == 0) {
if (tun_setip(in) == 0 && tun_setmtu(atoi(p)) == 0)
return 0; return 0;
else } else {
warn("Received handshake but b0rk"); warn("Received handshake with bad data");
}
} else {
printf("Received bad handshake\n");
} }
} }
} }
@ -160,17 +168,19 @@ handshake(int dns_fd)
return 1; return 1;
} }
extern char *__progname;
static void static void
usage() { usage() {
printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device]" extern char *__progname;
printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] "
"nameserver topdomain\n", __progname); "nameserver topdomain\n", __progname);
exit(2); exit(2);
} }
static void static void
help() { help() {
extern char *__progname;
printf("iodine IP over DNS tunneling client\n"); printf("iodine IP over DNS tunneling client\n");
printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] " printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] "
"nameserver topdomain\n", __progname); "nameserver topdomain\n", __progname);
@ -187,26 +197,27 @@ help() {
static void static void
version() { version() {
char *svnver = "$Rev$ from $Date$";
printf("iodine IP over DNS tunneling client\n"); printf("iodine IP over DNS tunneling client\n");
printf("SVN version: %s\n", svnver); printf("version: 0.3.4 from 2006-11-08\n");
exit(0); exit(0);
} }
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
int choice;
char *newroot;
char *username;
char *device;
int foreground;
struct passwd *pw; struct passwd *pw;
char *username;
int foreground;
char *newroot;
char *device;
int choice;
int tun_fd;
int dns_fd;
newroot = NULL;
username = NULL; username = NULL;
device = NULL;
foreground = 0; foreground = 0;
newroot = NULL;
device = NULL;
while ((choice = getopt(argc, argv, "vfhu:t:d:")) != -1) { while ((choice = getopt(argc, argv, "vfhu:t:d:")) != -1) {
switch(choice) { switch(choice) {
@ -255,7 +266,7 @@ main(int argc, char **argv)
if ((tun_fd = open_tun(device)) == -1) if ((tun_fd = open_tun(device)) == -1)
goto cleanup1; goto cleanup1;
if ((dns_fd = open_dns(argv[1], 0)) == -1) if ((dns_fd = open_dns(argv[1], 0, INADDR_ANY)) == -1)
goto cleanup2; goto cleanup2;
if (dns_settarget(argv[0]) == -1) if (dns_settarget(argv[0]) == -1)
goto cleanup2; goto cleanup2;

View File

@ -18,6 +18,7 @@
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <netinet/in.h>
#include <signal.h> #include <signal.h>
#include <unistd.h> #include <unistd.h>
#include <sys/types.h> #include <sys/types.h>
@ -57,17 +58,18 @@ sigint(int sig) {
static int static int
tunnel(int tun_fd, int dns_fd) tunnel(int tun_fd, int dns_fd)
{ {
int i; struct in_addr clientip;
struct in_addr myip;
struct timeval tv;
char out[64*1024];
char in[64*1024];
char *tmp[2];
long outlen;
fd_set fds;
int read; int read;
int code; int code;
int ipadder; int i;
struct in_addr nextip;
fd_set fds;
struct timeval tv;
char in[64*1024];
long outlen;
char out[64*1024];
while (running) { while (running) {
if (q.id != 0) { if (q.id != 0) {
tv.tv_sec = 0; tv.tv_sec = 0;
@ -109,21 +111,24 @@ tunnel(int tun_fd, int dns_fd)
} }
if(FD_ISSET(dns_fd, &fds)) { if(FD_ISSET(dns_fd, &fds)) {
read = dnsd_read(dns_fd, &q, in, sizeof(in)); read = dnsd_read(dns_fd, &q, in, sizeof(in));
if (read < 0) if (read <= 0)
continue; continue;
if(in[0] == 'H' || in[0] == 'h') { if(in[0] == 'H' || in[0] == 'h') {
ipadder = htonl(my_ip); // To get the last byte last myip.s_addr = my_ip;
if ((ipadder & 0xFF) == 0xFF) { clientip.s_addr = my_ip + inet_addr("0.0.0.1");
// IP ends with 255.
ipadder--; tmp[0] = strdup(inet_ntoa(myip));
} else { tmp[1] = strdup(inet_ntoa(clientip));
ipadder++;
} read = snprintf(out, sizeof(out), "%s-%s-%d",
nextip.s_addr = ntohl(ipadder); tmp[0], tmp[1], my_mtu);
read = snprintf(out, sizeof(out), "%s-%d", inet_ntoa(nextip), my_mtu);
dnsd_send(dns_fd, &q, out, read); dnsd_send(dns_fd, &q, out, read);
q.id = 0; q.id = 0;
free(tmp[1]);
free(tmp[0]);
} else if((in[0] >= '0' && in[0] <= '9') } else if((in[0] >= '0' && in[0] <= '9')
|| (in[0] >= 'a' && in[0] <= 'f') || (in[0] >= 'a' && in[0] <= 'f')
|| (in[0] >= 'A' && in[0] <= 'F')) { || (in[0] >= 'A' && in[0] <= 'F')) {
@ -159,20 +164,22 @@ tunnel(int tun_fd, int dns_fd)
return 0; return 0;
} }
extern char *__progname;
static void static void
usage() { usage() {
printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] [-m mtu] " extern char *__progname;
"tunnel_ip topdomain\n", __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); exit(2);
} }
static void static void
help() { help() {
extern char *__progname;
printf("iodine IP over DNS tunneling server\n"); printf("iodine IP over DNS tunneling server\n");
printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] [-m mtu] " printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] [-m mtu] [-l ip address to listen on] [-p port]"
"tunnel_ip topdomain\n", __progname); " tunnel_ip topdomain\n", __progname);
printf(" -v to print version info and exit\n"); printf(" -v to print version info and exit\n");
printf(" -h to print this help and exit\n"); printf(" -h to print this help and exit\n");
printf(" -f to keep running in foreground\n"); printf(" -f to keep running in foreground\n");
@ -180,6 +187,8 @@ help() {
printf(" -t dir to chroot to directory dir\n"); printf(" -t dir to chroot to directory dir\n");
printf(" -d device to set tunnel device name\n"); printf(" -d device to set tunnel device name\n");
printf(" -m mtu to set tunnel device mtu\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("tunnel_ip is the IP number of the local tunnel interface.\n");
printf("topdomain is the FQDN that is delegated to this server.\n"); printf("topdomain is the FQDN that is delegated to this server.\n");
exit(0); exit(0);
@ -187,9 +196,8 @@ help() {
static void static void
version() { version() {
char *svnver = "$Rev$ from $Date$";
printf("iodine IP over DNS tunneling server\n"); printf("iodine IP over DNS tunneling server\n");
printf("SVN version: %s\n", svnver); printf("version: 0.3.4 from 2006-11-08\n");
exit(0); exit(0);
} }
@ -205,19 +213,23 @@ main(int argc, char **argv)
int foreground; int foreground;
int mtu; int mtu;
struct passwd *pw; struct passwd *pw;
in_addr_t listen_ip;
int port;
username = NULL; username = NULL;
newroot = NULL; newroot = NULL;
device = NULL; device = NULL;
foreground = 0; foreground = 0;
mtu = 1024; mtu = 1024;
listen_ip = INADDR_ANY;
port = 53;
packetbuf.len = 0; packetbuf.len = 0;
packetbuf.offset = 0; packetbuf.offset = 0;
outpacket.len = 0; outpacket.len = 0;
q.id = 0; q.id = 0;
while ((choice = getopt(argc, argv, "vfhu:t:d:m:")) != -1) { while ((choice = getopt(argc, argv, "vfhu:t:d:m:l:p:")) != -1) {
switch(choice) { switch(choice) {
case 'v': case 'v':
version(); version();
@ -240,6 +252,12 @@ main(int argc, char **argv)
case 'm': case 'm':
mtu = atoi(optarg); mtu = atoi(optarg);
break; break;
case 'l':
listen_ip = inet_addr(optarg);
break;
case 'p':
port = atoi(optarg);
break;
default: default:
usage(); usage();
break; break;
@ -270,11 +288,16 @@ main(int argc, char **argv)
usage(); usage();
} }
if (listen_ip == INADDR_NONE) {
printf("Bad IP address to listen on.\n");
usage();
}
if ((tun_fd = open_tun(device)) == -1) if ((tun_fd = open_tun(device)) == -1)
goto cleanup0; goto cleanup0;
if (tun_setip(argv[0]) != 0 || tun_setmtu(mtu) != 0) if (tun_setip(argv[0]) != 0 || tun_setmtu(mtu) != 0)
goto cleanup1; goto cleanup1;
if ((dnsd_fd = open_dns(argv[1], 53)) == -1) if ((dnsd_fd = open_dns(argv[1], port, listen_ip)) == -1)
goto cleanup2; goto cleanup2;
my_ip = inet_addr(argv[0]); my_ip = inet_addr(argv[0]);

164
read.c
View File

@ -15,40 +15,164 @@
*/ */
#include <string.h> #include <string.h>
#include <stdint.h>
int static int
readname(char *packet, char *dst, char *src) readname_loop(char *packet, int packetlen, char **src, char *dst, size_t length, size_t loop)
{ {
char l; char *dummy;
char *s;
char *d;
int len; int len;
int offset; int offset;
char c;
if (loop <= 0)
return 0;
len = 0; len = 0;
s = *src;
d = dst;
while(*s && len < length - 2) {
c = *s++;
while(*src) { /* is this a compressed label? */
l = *src++; if((c & 0xc0) == 0xc0) {
len++; offset = (((s[-1] & 0x3f) << 8) | (s[0] & 0xff));
if (offset > packetlen) {
if(l & 0x80 && l & 0x40) { if (len == 0) {
offset = ((src[-1] & 0x3f) << 8) | src[0]; // Bad jump first in packet
readname(packet, dst, packet + offset); return 0;
dst += strlen(dst); } else {
break; // Bad jump after some data
break;
}
}
dummy = packet + offset;
len += readname_loop(packet, packetlen, &dummy, d, length - len, loop - 1);
goto end;
} }
while(l) { while(c && len < length - 1) {
*dst++ = *src++; *d++ = *s++;
l--; len++;
c--;
}
if (len >= length - 1) {
break; /* We used up all space */
}
if (*s != 0) {
*d++ = '.';
len++; len++;
} }
*dst++ = '.';
} }
dst[len++] = '\0';
*dst = '\0'; end:
src++; (*src) = s+1;
len++; 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; 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;
}

21
read.h
View File

@ -17,19 +17,14 @@
#ifndef _READ_H_ #ifndef _READ_H_
#define _READ_H_ #define _READ_H_
int readname(char *, char *, char *); 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);
#define READNAME(packet, dst, src) (src) += readname((packet), (dst), (src)); int putbyte(char **, char);
int putshort(char **, short);
#define READSHORT(dst, src) \ int putlong(char **, uint32_t);
memcpy(&dst, src, 2); \ int putdata(char **, char *, size_t);
(dst) = ntohs(dst); (src)+=2;
#define READLONG(dst, src) \
memcpy(&dst, src, 2); \
(dst) = ntohl(dst); (src)+=4;
#define READDATA(dst, src, len) \
memcpy((dst), (src), (len)); (src)+=(len);
#endif #endif

View File

@ -25,7 +25,7 @@ struct packet
}; };
struct query { struct query {
char name[256]; char name[258];
short type; short type;
short id; short id;
struct sockaddr from; struct sockaddr from;

262
test.c Normal file
View File

@ -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;
}

42
tun.c
View File

@ -52,23 +52,23 @@ open_tun(const char *tun_device)
return -1; return -1;
} }
bzero(&ifreq, sizeof(ifreq)); memset(&ifreq, 0, sizeof(ifreq));
ifreq.ifr_flags = IFF_TUN; ifreq.ifr_flags = IFF_TUN;
if (tun_device != NULL) { if (tun_device != NULL) {
strncpy(ifreq.ifr_name, tun_device, IFNAMSIZ); strncpy(ifreq.ifr_name, tun_device, IFNAMSIZ);
strncpy(if_name, tun_device, sizeof(if_name)); strncpy(if_name, tun_device, sizeof(if_name));
if (ioctl(tun_fd, TUNSETIFF, (void *) &ifreq) != -1) { if (ioctl(tun_fd, TUNSETIFF, (void *) &ifreq) != -1) {
printf("Opened %s\n", ifreq.ifr_name); printf("Opened %s\n", ifreq.ifr_name);
return tun_fd; return tun_fd;
} }
if (errno != EBUSY) { if (errno != EBUSY) {
warn("open_tun: ioctl[TUNSETIFF]: %s", strerror(errno)); warn("open_tun: ioctl[TUNSETIFF]: %s", strerror(errno));
return -1; return -1;
} }
} else { } else {
for (i = 0; i < TUN_MAX_TRY; i++) { for (i = 0; i < TUN_MAX_TRY; i++) {
snprintf(ifreq.ifr_name, IFNAMSIZ, "dns%d", i); snprintf(ifreq.ifr_name, IFNAMSIZ, "dns%d", i);
@ -85,7 +85,7 @@ open_tun(const char *tun_device)
} }
} }
warn("open_tun: Couldn't set interface name.\n"); warn("open_tun: Couldn't set interface name");
} }
return -1; return -1;
} }
@ -100,10 +100,16 @@ open_tun(const char *tun_device)
char tun_name[50]; char tun_name[50];
if (tun_device != NULL) { if (tun_device != NULL) {
if ((tun_fd = open(tun_device, O_RDWR)) < 0) { snprintf(tun_name, sizeof(tun_name), "/dev/%s", tun_device);
warn("open_tun: %s: %s", tun_device, strerror(errno)); 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; return -1;
} }
printf("Opened %s\n", tun_name);
return tun_fd;
} else { } else {
for (i = 0; i < TUN_MAX_TRY; i++) { for (i = 0; i < TUN_MAX_TRY; i++) {
snprintf(tun_name, sizeof(tun_name), "/dev/tun%d", i); snprintf(tun_name, sizeof(tun_name), "/dev/tun%d", i);
@ -118,7 +124,7 @@ open_tun(const char *tun_device)
break; break;
} }
warn("open_tun: Failed to open tunneling device."); warn("open_tun: Failed to open tunneling device");
} }
return -1; return -1;
@ -136,10 +142,10 @@ close_tun(int tun_fd)
int int
write_tun(int tun_fd, char *data, int len) write_tun(int tun_fd, char *data, int len)
{ {
#ifdef FREEBSD #if defined (FREEBSD) || defined (DARWIN) || defined(NETBSD)
data += 4; data += 4;
len -= 4; len -= 4;
#else /* !FREEBSD */ #else /* !FREEBSD/DARWIN */
#ifdef LINUX #ifdef LINUX
data[0] = 0x00; data[0] = 0x00;
data[1] = 0x00; data[1] = 0x00;
@ -163,7 +169,7 @@ write_tun(int tun_fd, char *data, int len)
int int
read_tun(int tun_fd, char *buf, int len) read_tun(int tun_fd, char *buf, int len)
{ {
#ifdef FREEBSD #if defined (FREEBSD) || defined (DARWIN) || defined(NETBSD)
// FreeBSD has no header // FreeBSD has no header
return read(tun_fd, buf + 4, len - 4) + 4; return read(tun_fd, buf + 4, len - 4) + 4;
#else /* !FREEBSD */ #else /* !FREEBSD */