1
0
mirror of https://github.com/yarrick/iodine.git synced 2024-11-24 14:06:06 +03:00

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: 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 2006-07-11: 0.3.1
- Add Mac OSX support - Add Mac OSX support
- Add setting device name - Add setting device name

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

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. 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:
@ -61,10 +75,11 @@ possible to allow maximum throughput.
PORTABILITY: PORTABILITY:
iodine has been tested on Linux (x86 and SPARC64), FreeBSD (x86), OpenBSD (x86) 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/). NetBSD (x86) and MacOS X (10.3, ppc, with
It should work on other unix-like systems as well that has TUN/TAP tunneling http://www-user.rhrk.uni-kl.de/~nissler/tuntap/). It should work on other
support. Let us know if you get it to run on other platforms. 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: 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. iodine has atomic number 53, which happens to be the DNS port number.
THANKS:
- To kuxien for FreeBSD and OS X testing
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)

225
dns.c
View File

@ -32,17 +32,20 @@
#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 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];
@ -58,7 +61,7 @@ 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;
@ -67,9 +70,10 @@ open_dns(const char *domain, int localport)
bzero(&addr, sizeof(addr)); bzero(&addr, 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;
@ -87,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;
} }
@ -176,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;
@ -204,16 +209,16 @@ dns_query(int fd, int id, char *host, int type)
p = buf + sizeof(HEADER); p = buf + sizeof(HEADER);
p += host2dns(host, p, strlen(host)); p += host2dns(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);
@ -221,72 +226,67 @@ 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;
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)); 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;
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; long ttl;
short rlen; short rlen;
short type; short type;
@ -297,16 +297,8 @@ 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);
if(r == -1) {
perror("recvfrom");
} else {
header = (HEADER*)packet; header = (HEADER*)packet;
data = packet + sizeof(HEADER); data = packet + sizeof(HEADER);
@ -318,42 +310,26 @@ dns_read(int fd, char *buf, int buflen)
rlen = 0; rlen = 0;
if(qdcount == 1) { if(qdcount == 1) {
READNAME(packet, name, data); readname(packet, &data, name, sizeof(name));
READSHORT(type, data); readshort(packet, &data, &type);
READSHORT(class, data); readshort(packet, &data, &class);
} }
if(ancount == 1) { if(ancount == 1) {
READNAME(packet, name, data); readname(packet, &data, name, sizeof(name));
READSHORT(type, data); readshort(packet, &data, &type);
READSHORT(class, data); readshort(packet, &data, &class);
READLONG(ttl, data); readlong(packet, &data, &ttl);
READSHORT(rlen, data); readshort(packet, &data, &rlen);
READDATA(rdata, data, rlen); readdata(packet, &data, rdata, 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) { if(type == T_NULL && rlen > 2) {
memcpy(buf, rdata, rlen); rv = MIN(rlen, sizeof(rdata));
return rlen; memcpy(outbuf, rdata, rv);
} else {
return 0;
}
} }
} }
return 0; return rv;
} }
static int static int
@ -412,19 +388,18 @@ dnsd_send(int fd, struct query *q, char *data, int datalen)
name = 0xc000 | ((p - buf) & 0x3fff); name = 0xc000 | ((p - buf) & 0x3fff);
p += host2dns(q->name, p, strlen(q->name)); p += host2dns(q->name, p, strlen(q->name));
PUTSHORT(q->type, p); putshort(&p, q->type);
PUTSHORT(C_IN, p); putshort(&p, C_IN);
PUTSHORT(name, p); putshort(&p, name);
PUTSHORT(q->type, p); putshort(&p, q->type);
PUTSHORT(C_IN, p); putshort(&p, C_IN);
PUTLONG(0, p); putlong(&p, 0);
q->id = 0; q->id = 0;
PUTSHORT(datalen, p); putshort(&p, datalen);
memcpy(p, data, datalen); putdata(&p, data, datalen);
p += datalen;
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);
@ -433,32 +408,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;
@ -468,12 +423,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];
@ -493,23 +449,24 @@ 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, &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 { } else {
perror("recvfrom"); perror("recvfrom");
rv = 0;
} }
return 0; return rv;
} }

5
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,7 +25,6 @@ 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);
extern struct sockaddr_in peer; extern struct sockaddr_in peer;
@ -38,5 +37,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_ */

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 <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;
@ -69,16 +70,16 @@ tunnel(int tun_fd, int dns_fd)
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[128];
char client[128];
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,12 +144,9 @@ handshake(int dns_fd)
} }
if (read > 0) { if (read > 0) {
p = strchr(in, '-'); if (sscanf(in, "%[^-]-%[^-]-%d",
if (p) { server, client, &mtu) == 3) {
*p++ = '\0'; if (tun_setip(client) == 0 && tun_setmtu(mtu) == 0)
mtu = atoi(p);
if (tun_setip(in) == 0 && tun_setmtu(atoi(p)) == 0)
return 0; return 0;
else else
warn("Received handshake but b0rk"); warn("Received handshake but b0rk");
@ -160,17 +160,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);
@ -188,24 +190,26 @@ help() {
static void static void
version() { version() {
printf("iodine IP over DNS tunneling client\n"); 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); 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) {
@ -254,7 +258,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,16 +58,17 @@ 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) {
@ -113,17 +115,20 @@ tunnel(int tun_fd, int dns_fd)
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,19 +164,21 @@ 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;
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); "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] "
"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");
@ -180,6 +187,7 @@ 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("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);
@ -188,7 +196,7 @@ help() {
static void static void
version() { version() {
printf("iodine IP over DNS tunneling server\n"); 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); exit(0);
} }
@ -204,19 +212,21 @@ main(int argc, char **argv)
int foreground; int foreground;
int mtu; int mtu;
struct passwd *pw; struct passwd *pw;
in_addr_t listen_ip;
username = NULL; username = NULL;
newroot = NULL; newroot = NULL;
device = NULL; device = NULL;
foreground = 0; foreground = 0;
mtu = 1024; mtu = 1024;
listen_ip = INADDR_ANY;
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:")) != -1) {
switch(choice) { switch(choice) {
case 'v': case 'v':
version(); version();
@ -239,6 +249,9 @@ 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;
default: default:
usage(); usage();
break; break;
@ -269,11 +282,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], 53, listen_ip)) == -1)
goto cleanup2; goto cleanup2;
my_ip = inet_addr(argv[0]); my_ip = inet_addr(argv[0]);

148
read.c
View File

@ -16,39 +16,149 @@
#include <string.h> #include <string.h>
int static int
readname(char *packet, char *dst, char *src) 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 len;
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++; dummy = packet + (((s[-1] & 0x3f) << 8) | s[0]);
len += readname_loop(packet, &dummy, d, length - len, loop - 1);
if(l & 0x80 && l & 0x40) { goto end;
offset = ((src[-1] & 0x3f) << 8) | src[0];
readname(packet, dst, packet + offset);
dst += strlen(dst);
break;
} }
while(l) { while(c && len < length - 1) {
*dst++ = *src++; *d++ = *s++;
l--;
len++; len++;
c--;
} }
*dst++ = '.'; if (len >= length - 1) {
break; /* We used up all space */
} }
*dst = '\0'; if (*s != 0) {
src++; *d++ = '.';
len++; len++;
}
}
dst[len++] = '\0';
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; 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_ #ifndef _READ_H_
#define _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)); int putbyte(char **, char);
int putshort(char **, short);
#define READSHORT(dst, src) \ int putlong(char **, long);
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;

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 int
write_tun(int tun_fd, char *data, int len) write_tun(int tun_fd, char *data, int len)
{ {
#if defined (FREEBSD) || defined (DARWIN) #if defined (FREEBSD) || defined (DARWIN) || defined(NETBSD)
data += 4; data += 4;
len -= 4; len -= 4;
#else /* !FREEBSD/DARWIN */ #else /* !FREEBSD/DARWIN */
@ -169,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)
{ {
#if defined (FREEBSD) || defined (DARWIN) #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 */