Version 0.3.2

This commit is contained in:
Erik Ekman 2006-09-11 19:11:45 +00:00
parent 1feacfada3
commit 39d5049c44
15 changed files with 830 additions and 264 deletions

View File

@ -7,6 +7,13 @@ iodine - IP over DNS is now easy
CHANGES:
2006-09-11: 0.3.2
- Support for NetBSD
- Fixed potential security problems
- Name parsing routines rewritten, added regression tests
- New encoding, 25% more peak upstream throughput
- New -l option to set local ip to listen to on server
2006-07-11: 0.3.1
- Add Mac OSX support
- Add setting device name

View File

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

28
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.
QUICKSTART:
Try it out within your own LAN! Follow these simple steps:
- On your server, run: ./iodined -f 10.0.0.1 test.asdf
(If you already use the 10.0.0.0 network, use another internal net like
172.16.0.0)
- On the client, run: ./iodine -f 192.168.0.1 test.asdf
(Replace 192.168.0.1 with the server's ip address)
- Now the client has the tunnel ip 10.0.0.2 and the server has 10.0.0.1
- Try pinging each other through the tunnel
- Done! :)
To actually use it through a relaying nameserver, see below.
HOW TO USE:
Server side:
@ -61,10 +75,11 @@ possible to allow maximum throughput.
PORTABILITY:
iodine has been tested on Linux (x86 and SPARC64), FreeBSD (x86), OpenBSD (x86)
and MacOS X (10.3, ppc, with http://www-user.rhrk.uni-kl.de/~nissler/tuntap/).
It should work on other unix-like systems as well that has TUN/TAP tunneling
support. Let us know if you get it to run on other platforms.
iodine has been tested on Linux (x86 and SPARC64), FreeBSD (x86), OpenBSD (x86),
NetBSD (x86) and MacOS X (10.3, ppc, with
http://www-user.rhrk.uni-kl.de/~nissler/tuntap/). It should work on other
unix-like systems as well that has TUN/TAP tunneling support. Let us know if you
get it to run on other platforms.
THE NAME:
@ -73,6 +88,11 @@ The name iodine was chosen since it starts with IOD (IP Over DNS) and since
iodine has atomic number 53, which happens to be the DNS port number.
THANKS:
- To kuxien for FreeBSD and OS X testing
AUTHORS & LICENSE:
Copyright (c) 2006 Bjorn Andersson <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)

251
dns.c
View File

@ -32,17 +32,20 @@
#include <string.h>
#include <ctype.h>
#include "read.h"
#include "structs.h"
#include "dns.h"
#include "encoding.h"
#include "read.h"
// For FreeBSD
#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#endif
static int host2dns(const char *, char *, int);
static int dns_write(int, int, char *, int, char);
static void dns_query(int, int, char *, int);
struct sockaddr_in peer;
char topdomain[256];
@ -58,7 +61,7 @@ uint16_t pingid;
int
open_dns(const char *domain, int localport)
open_dns(const char *domain, int localport, in_addr_t listen_ip)
{
int fd;
int flag;
@ -67,9 +70,10 @@ open_dns(const char *domain, int localport)
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(localport);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
/* listen_ip already in network byte order from inet_addr, or 0 */
addr.sin_addr.s_addr = listen_ip;
fd = socket(AF_INET, SOCK_DGRAM, 0);
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(fd < 0) {
warn("socket");
return -1;
@ -87,10 +91,11 @@ open_dns(const char *domain, int localport)
}
// Save top domain used
strncpy(topdomain, domain, sizeof(topdomain) - 2);
topdomain[sizeof(topdomain) - 1] = 0;
strncpy(topdomain, domain, sizeof(topdomain) - 1);
topdomain[sizeof(topdomain) - 1] = '\0';
printf("Opened UDP socket\n");
return fd;
}
@ -176,7 +181,7 @@ dns_handshake(int dns_fd)
dns_write(dns_fd, ++pingid, data, 2, 'H');
}
void
static void
dns_query(int fd, int id, char *host, int type)
{
char *p;
@ -204,16 +209,16 @@ dns_query(int fd, int id, char *host, int type)
p = buf + sizeof(HEADER);
p += host2dns(host, p, strlen(host));
PUTSHORT(type, p);
PUTSHORT(C_IN, p);
putshort(&p, type);
putshort(&p, C_IN);
// EDNS0
*p++ = 0x00; //Root
PUTSHORT(0x0029, p); // OPT
PUTSHORT(0x1000, p); // Payload size: 4096
PUTSHORT(0x0000, p); // Higher bits/ edns version
PUTSHORT(0x8000, p); // Z
PUTSHORT(0x0000, p); // Data length
putbyte(&p, 0x00); //Root
putshort(&p, 0x0029); // OPT
putshort(&p, 0x1000); // Payload size: 4096
putshort(&p, 0x0000); // Higher bits/edns version
putshort(&p, 0x8000); // Z
putshort(&p, 0x0000); // Data length
peerlen = sizeof(peer);
@ -221,72 +226,67 @@ dns_query(int fd, int id, char *host, int type)
sendto(fd, buf, len, 0, (struct sockaddr*)&peer, peerlen);
}
static void
put_hex(char *p, char h)
{
int t;
static const char to_hex[] = "0123456789ABCDEF";
t = (h & 0xF0) >> 4;
p[0] = to_hex[t];
t = h & 0x0F;
p[1] = to_hex[t];
}
static int
dns_write(int fd, int id, char *buf, int len, char flag)
{
int avail;
int i;
int final;
int written;
int encoded;
char data[257];
char *d;
#define CHUNK 31
// 31 bytes expands to 62 chars in domain
// We just use hex as encoding right now
avail = 0xFF - strlen(topdomain) - 2;
avail /= 2; // use two chars per byte in encoding
avail -= (avail/CHUNK); // make space for parts
avail = MIN(avail, len); // do not use more bytes than is available;
final = (avail == len); // is this the last block?
bzero(data, sizeof(data));
d = data;
if (flag != 0) {
*d = flag;
} else {
// First byte is 0 for middle packet and 1 for last packet
*d = '0' + final;
}
d++;
if (len > 0) {
for (i = 0; i < avail; i++) {
if (i > 0 && i % 31 == 0) {
*d = '.';
d++;
}
put_hex(d, buf[i]);
d += 2;
}
}
written = encode_data(buf, len, avail, d, flag);
encoded = strlen(data);
d += encoded;
if (*d != '.') {
*d++ = '.';
}
strncpy(d, topdomain, strlen(topdomain)+1);
dns_query(fd, id, data, T_NULL);
return avail;
return written;
}
int
dns_read(int fd, char *buf, int buflen)
{
int r;
socklen_t addrlen;
char packet[64*1024];
struct sockaddr_in from;
HEADER *header;
addrlen = sizeof(struct sockaddr);
r = recvfrom(fd, packet, sizeof(packet), 0, (struct sockaddr*)&from, &addrlen);
if(r == -1) {
perror("recvfrom");
return 0;
}
header = (HEADER*)packet;
if (dns_sending() && chunkid == ntohs(header->id)) {
/* Got ACK on sent packet */
packetpos += lastlen;
if (packetpos == packetlen) {
/* Packet completed */
packetpos = 0;
packetlen = 0;
lastlen = 0;
} else {
/* More to send */
dns_send_chunk(fd);
}
}
return dns_parse_reply(buf, buflen, packet, r);
}
int
dns_parse_reply(char *outbuf, int buflen, char *packet, int packetlen)
{
int rv;
long ttl;
short rlen;
short type;
@ -297,63 +297,39 @@ dns_read(int fd, char *buf, int buflen)
char name[255];
char rdata[4*1024];
HEADER *header;
socklen_t addrlen;
char packet[64*1024];
struct sockaddr_in from;
addrlen = sizeof(struct sockaddr);
r = recvfrom(fd, packet, sizeof(packet), 0, (struct sockaddr*)&from, &addrlen);
rv = 0;
header = (HEADER*)packet;
data = packet + sizeof(HEADER);
if(r == -1) {
perror("recvfrom");
} else {
header = (HEADER*)packet;
data = packet + sizeof(HEADER);
if(header->qr) { /* qr=1 => response */
qdcount = ntohs(header->qdcount);
ancount = ntohs(header->ancount);
if(header->qr) { /* qr=1 => response */
qdcount = ntohs(header->qdcount);
ancount = ntohs(header->ancount);
rlen = 0;
rlen = 0;
if(qdcount == 1) {
readname(packet, &data, name, sizeof(name));
readshort(packet, &data, &type);
readshort(packet, &data, &class);
}
if(ancount == 1) {
readname(packet, &data, name, sizeof(name));
readshort(packet, &data, &type);
readshort(packet, &data, &class);
readlong(packet, &data, &ttl);
readshort(packet, &data, &rlen);
readdata(packet, &data, rdata, rlen);
}
if(qdcount == 1) {
READNAME(packet, name, data);
READSHORT(type, data);
READSHORT(class, data);
}
if(ancount == 1) {
READNAME(packet, name, data);
READSHORT(type, data);
READSHORT(class, data);
READLONG(ttl, data);
READSHORT(rlen, data);
READDATA(rdata, data, rlen);
}
if (dns_sending() && chunkid == ntohs(header->id)) {
// Got ACK on sent packet
packetpos += lastlen;
if (packetpos == packetlen) {
// Packet completed
packetpos = 0;
packetlen = 0;
lastlen = 0;
} else {
// More to send
dns_send_chunk(fd);
}
}
if(type == T_NULL && rlen > 2) {
memcpy(buf, rdata, rlen);
return rlen;
} else {
return 0;
}
if(type == T_NULL && rlen > 2) {
rv = MIN(rlen, sizeof(rdata));
memcpy(outbuf, rdata, rv);
}
}
return 0;
return rv;
}
static int
@ -412,19 +388,18 @@ dnsd_send(int fd, struct query *q, char *data, int datalen)
name = 0xc000 | ((p - buf) & 0x3fff);
p += host2dns(q->name, p, strlen(q->name));
PUTSHORT(q->type, p);
PUTSHORT(C_IN, p);
PUTSHORT(name, p);
PUTSHORT(q->type, p);
PUTSHORT(C_IN, p);
PUTLONG(0, p);
putshort(&p, q->type);
putshort(&p, C_IN);
putshort(&p, name);
putshort(&p, q->type);
putshort(&p, C_IN);
putlong(&p, 0);
q->id = 0;
PUTSHORT(datalen, p);
memcpy(p, data, datalen);
p += datalen;
putshort(&p, datalen);
putdata(&p, data, datalen);
len = p - buf;
sendto(fd, buf, len, 0, (struct sockaddr*)&q->from, q->fromlen);
@ -433,32 +408,12 @@ dnsd_send(int fd, struct query *q, char *data, int datalen)
static int
decodepacket(const char *name, char *buf, int buflen)
{
int r;
int len;
char *dp;
char *domain;
const char *np;
len = 1;
domain = strstr(name, topdomain);
buf[0] = name[0];
dp = buf + 1;
np = name + 1;
while(len < buflen && np < domain) {
if(*np == '.') {
np++;
continue;
}
sscanf(np, "%02X", &r);
*dp++ = (char)r;
np+=2;
len++;
}
len = decode_data(buf, buflen, name, domain);
if (len == buflen)
return -1;
return len;
@ -468,12 +423,13 @@ int
dnsd_read(int fd, struct query *q, char *buf, int buflen)
{
int r;
int rv;
short id;
short type;
short class;
short qdcount;
char *data;
char name[255];
char name[257];
HEADER *header;
socklen_t addrlen;
char packet[64*1024];
@ -493,23 +449,24 @@ dnsd_read(int fd, struct query *q, char *buf, int buflen)
qdcount = ntohs(header->qdcount);
if(qdcount == 1) {
bzero(name, sizeof(name));
READNAME(packet, name, data);
READSHORT(type, data);
READSHORT(class, data);
readname(packet, &data, name, sizeof(name) -1);
name[256] = 0;
readshort(packet, &data, &type);
readshort(packet, &data, &class);
strncpy(q->name, name, 256);
strncpy(q->name, name, 257);
q->type = type;
q->id = id;
q->fromlen = addrlen;
memcpy((struct sockaddr*)&q->from, (struct sockaddr*)&from, addrlen);
return decodepacket(name, buf, buflen);
rv = decodepacket(name, buf, buflen);
}
}
} else {
perror("recvfrom");
rv = 0;
}
return 0;
return rv;
}

5
dns.h
View File

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

225
encoding.c Normal file
View File

@ -0,0 +1,225 @@
/*
* 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;
if (flag != 0) {
*dest = flag;
} else {
// First byte is 0 for middle packet and 1 for last packet
*dest = '0' + final;
}
dest++;
bzero(encoded, sizeof(encoded));
ep = encoded;
dp = buf;
for (i = 0; i < chunks; i++) {
encode_chunk(ep, dp);
ep += ENC_CHUNK;
dp += RAW_CHUNK;
}
realwrite = ENC_CHUNK * chunks;
bzero(padding, sizeof(padding));
pp = padding;
if (leftovers) {
pp += RAW_CHUNK - leftovers;
memcpy(pp, dp, leftovers);
pp = padding;
*ep++ = padder[leftovers];
encode_chunk(ep, pp);
realwrite += ENC_CHUNK + 1; // plus padding character
}
ep = encoded;
if (len > 0) {
for (i = 1; i <= realwrite; i++) {
if (i % SPACING == 0) {
*dest++ = '.';
}
*dest++ = *ep++;
}
}
return write;
}
int
decode_data(char *dest, int size, const char *src, char *srcend)
{
int len;
int i;
int chunks;
int padded;
char encoded[255];
char padding[5];
char *pp;
char *ep;
// Copy flag
len = 1;
*dest = *src;
dest++;
src++;
bzero(encoded, sizeof(encoded));
ep = encoded;
while(len < size && src < srcend) {
if(*src == '.') {
src++;
continue;
}
*ep++ = *src++;
}
chunks = strlen(encoded) / 8;
padded = strlen(encoded) % 8;
ep = encoded;
for (i = 0; i < chunks-1; i++) {
decode_chunk(dest, ep);
dest += RAW_CHUNK;
ep += ENC_CHUNK;
len += RAW_CHUNK;
}
// Read last chunk:
if (padded) {
pp = padding;
padded = *ep++ - '0';
decode_chunk(pp, ep);
pp += RAW_CHUNK - padded;
memcpy(dest, pp, padded);
len += padded;
} else {
decode_chunk(dest, ep);
len += RAW_CHUNK;
}
return len;
}

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

View File

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

View File

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

152
read.c
View File

@ -16,39 +16,149 @@
#include <string.h>
int
readname(char *packet, char *dst, char *src)
static int
readname_loop(char *packet, char **src, char *dst, size_t length, size_t loop)
{
char l;
char *dummy;
char *s;
char *d;
int len;
int offset;
char c;
if (loop <= 0)
return 0;
len = 0;
s = *src;
d = dst;
while(*s && len < length - 2) {
c = *s++;
while(*src) {
l = *src++;
len++;
if(l & 0x80 && l & 0x40) {
offset = ((src[-1] & 0x3f) << 8) | src[0];
readname(packet, dst, packet + offset);
dst += strlen(dst);
break;
/* is this a compressed label? */
if((c & 0xc0) == 0xc0) {
dummy = packet + (((s[-1] & 0x3f) << 8) | s[0]);
len += readname_loop(packet, &dummy, d, length - len, loop - 1);
goto end;
}
while(l) {
*dst++ = *src++;
l--;
while(c && len < length - 1) {
*d++ = *s++;
len++;
c--;
}
if (len >= length - 1) {
break; /* We used up all space */
}
if (*s != 0) {
*d++ = '.';
len++;
}
*dst++ = '.';
}
dst[len++] = '\0';
*dst = '\0';
src++;
len++;
end:
(*src) = s+1;
return len;
}
int
readname(char *packet, char **src, char *dst, size_t length)
{
return readname_loop(packet, src, dst, length, 10);
}
int
readshort(char *packet, char **src, short *dst)
{
unsigned char *p;
p = *src;
*dst = (p[0] << 8) | p[1];
(*src) += sizeof(short);
return sizeof(short);
}
int
readlong(char *packet, char **src, long *dst)
{
unsigned char *p;
p = *src;
*dst = ((long)p[0] << 24)
| ((long)p[1] << 16)
| ((long)p[2] << 8)
| ((long)p[3]);
(*src) += sizeof(long);
return sizeof(long);
}
int
readdata(char *packet, char **src, char *dst, size_t len)
{
if (len < 0)
return 0;
memcpy(dst, *src, len);
(*src) += len;
return len;
}
int
putbyte(char **dst, char value)
{
**dst = value;
(*dst)++;
return sizeof(char);
}
int
putshort(char **dst, short value)
{
unsigned char *p;
p = *dst;
*p++ = (value >> 8);
*p++ = value;
(*dst) = p;
return sizeof(short);
}
int
putlong(char **dst, long value)
{
unsigned char *p;
p = *dst;
*p++ = (value >> 24);
*p++ = (value >> 16);
*p++ = (value >> 8);
*p++ = (value);
(*dst) = p;
return sizeof(long);
}
int
putdata(char **dst, char *data, size_t len)
{
if (len < 0)
return 0;
memcpy(*dst, data, len);
(*dst) += len;
return len;
}

21
read.h
View File

@ -17,19 +17,14 @@
#ifndef _READ_H_
#define _READ_H_
int readname(char *, char *, char *);
int readname(char *, char **, char *, size_t);
int readshort(char *, char **, short *);
int readlong(char *, char **, long *);
int readdata(char *, char **, char *, size_t);
#define READNAME(packet, dst, src) (src) += readname((packet), (dst), (src));
#define READSHORT(dst, src) \
memcpy(&dst, src, 2); \
(dst) = ntohs(dst); (src)+=2;
#define READLONG(dst, src) \
memcpy(&dst, src, 2); \
(dst) = ntohl(dst); (src)+=4;
#define READDATA(dst, src, len) \
memcpy((dst), (src), (len)); (src)+=(len);
int putbyte(char **, char);
int putshort(char **, short);
int putlong(char **, long);
int putdata(char **, char *, size_t);
#endif

View File

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

176
test.c Normal file
View File

@ -0,0 +1,176 @@
/*
* 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 <stdlib.h>
#include <string.h>
#include <assert.h>
#include "structs.h"
#include "dns.h"
#include "read.h"
static void
test_readputshort()
{
short tshort;
short putted;
short temps;
char buf[4];
short *s;
char* p;
int i;
printf(" * Testing read/putshort... ");
fflush(stdout);
for (i = 0; i < 65536; i++) {
tshort = (unsigned short) i;
temps = htons(tshort);
p = buf;
putshort(&p, tshort);
s = &putted;
memcpy(s, buf, sizeof(short));
if (putted != temps) {
printf("Bad value on putshort for %d\n", i);
exit(1);
}
s = &temps;
memcpy(buf, s, sizeof(short));
p = buf;
readshort(NULL, &p, &temps);
if (temps != tshort) {
printf("Bad value on readshort for %d\n", i);
exit(1);
}
}
printf("OK\n");
}
static void
test_readputlong()
{
char buf[4];
long putint;
long tempi;
long tint;
long *l;
char* p;
int i;
printf(" * Testing read/putlong... ");
fflush(stdout);
for (i = 0; i < 32; i++) {
tint = 0xF << i;
tempi = htonl(tint);
p = buf;
putlong(&p, tint);
l = &putint;
memcpy(l, buf, sizeof(int));
if (putint != tempi) {
printf("Bad value on putlong for %d\n", i);
exit(2);
}
l = &tempi;
memcpy(buf, l, sizeof(int));
p = buf;
readlong(NULL, &p, &tempi);
if (tempi != tint) {
printf("Bad value on readlong for %d\n", i);
exit(2);
}
}
printf("OK\n");
}
static void
test_readname()
{
char emptyloop[] = {
'A', 'A', 0x81, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01 };
char infloop[] = {
'A', 'A', 0x81, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 'A', 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01 };
char longname[] =
"AA\x81\x80\x00\x01\x00\x00\x00\x00\x00\x00"
"\x3FzBCDEFGHIJKLMNOPQURSTUVXYZ0123456789abcdefghijklmnopqrstuvxyzAA"
"\x3FzBCDEFGHIJKLMNOPQURSTUVXYZ0123456789abcdefghijklmnopqrstuvxyzAA"
"\x3FzBCDEFGHIJKLMNOPQURSTUVXYZ0123456789abcdefghijklmnopqrstuvxyzAA"
"\x3FzBCDEFGHIJKLMNOPQURSTUVXYZ0123456789abcdefghijklmnopqrstuvxyzAA"
"\x3FzBCDEFGHIJKLMNOPQURSTUVXYZ0123456789abcdefghijklmnopqrstuvxyzAA"
"\x3FzBCDEFGHIJKLMNOPQURSTUVXYZ0123456789abcdefghijklmnopqrstuvxyzAA"
"\x00\x00\x01\x00\x01";
char onejump[] =
"AA\x81\x80\x00\x01\x00\x00\x00\x00\x00\x00"
"\x02hh\xc0\x15\x00\x01\x00\x01\x05zBCDE\x00";
char buf[1024];
char *data;
int rv;
printf(" * Testing readname... ");
fflush(stdout);
bzero(buf, sizeof(buf));
data = emptyloop + sizeof(HEADER);
buf[1023] = 'A';
rv = readname(emptyloop, &data, buf, 1023);
assert(buf[1023] == 'A');
bzero(buf, sizeof(buf));
data = infloop + sizeof(HEADER);
buf[4] = '\a';
rv = readname(infloop, &data, buf, 4);
assert(buf[4] == '\a');
bzero(buf, sizeof(buf));
data = longname + sizeof(HEADER);
buf[256] = '\a';
rv = readname(longname, &data, buf, 256);
assert(buf[256] == '\a');
bzero(buf, sizeof(buf));
data = onejump + sizeof(HEADER);
rv = readname(onejump, &data, buf, 256);
assert(rv == 9);
printf("OK\n");
}
int
main()
{
printf("** iodine test suite\n");
test_readputshort();
test_readputlong();
test_readname();
printf("** All went well :)\n");
return 0;
}

4
tun.c
View File

@ -142,7 +142,7 @@ close_tun(int tun_fd)
int
write_tun(int tun_fd, char *data, int len)
{
#if defined (FREEBSD) || defined (DARWIN)
#if defined (FREEBSD) || defined (DARWIN) || defined(NETBSD)
data += 4;
len -= 4;
#else /* !FREEBSD/DARWIN */
@ -169,7 +169,7 @@ write_tun(int tun_fd, char *data, int len)
int
read_tun(int tun_fd, char *buf, int len)
{
#if defined (FREEBSD) || defined (DARWIN)
#if defined (FREEBSD) || defined (DARWIN) || defined(NETBSD)
// FreeBSD has no header
return read(tun_fd, buf + 4, len - 4) + 4;
#else /* !FREEBSD */