CHANGES:
+2009-01-23: 0.5.0 "iPassed"
+ - Fixed segfault in server when sending version reject.
+ - Applied patch to make iodine build on BeOS R5-BONE and Haiku,
+ from Francois Revol. Still work to do to get tun device working.
+ - Added capability to forward DNS queries outside tunnel domain to
+ a nameserver on localhost. Use -b port to enable, fixes #31.
+ - iodined now replies to NS request on its own domain, fixes issue #33.
+ The destination IP address is sent as reply. Use -n to specify
+ a specific IP address to return (if behind NAT etc).
+ - Upstream data is now Base64 encoded if relay server preserves case and
+ supports the plus (+) character in domain names, fixes #16.
+ - Fixed problem in client when DNS trans. ID has highest bit set (#37)
+ - IP addresses are now assigned within the netmask, so iodined can
+ use any address for itself, fixes #28.
+ - Netmask size is now adjustable. Setting a small net will reduce the
+ number of users. Use x.x.x.x/n notation on iodined tunnel ip.
+ This fixes #27.
+ - Downstream data is now fragmented, and the fragment size is auto-
+ probed after login. Fixes #7. It only took a few years :)
+ - Enhanced the checks that validates incoming packets
+ - Fixed endless loop in fragment size autodetection, #39.
+ - Fixed broken hostname dot placing with specific lengths, #40.
+
2008-08-06: 0.4.2 "Opened Zone"
- Applied a few small patches from Maxim Bourmistrov and Gregor Herrmann
- Applied a patch for not creating and configuring the tun interface,
- Applied a security patch from Andrew Griffiths, use setgroups() to
limit the groups of the user
- Applied a patch to make iodine build on (Open)Solaris, from Albert Lee
- Needs TUN/TAP driver: http://www.whiteboard.ne.jp/~admin2/tuntap/
- Still needs some more code in tun.c for opening/closing the device
- - Added option in server (-c) to disable IP/port checking on each packet,
+ Needs TUN/TAP driver http://www.whiteboard.ne.jp/~admin2/tuntap/
+ Still needs more code in tun.c for opening/closing the device
+ - Added option in server (-c) to disable IP/port checking on packets,
will hopefully help when server is behind NAT
- Fixed bug #21, now only IP address part of each packet is checked.
Should remove the need for the -c option and also work with
bugfixed DNS servers worldwide.
- - Added -D option on server to enable debugging. Debug level 1 now prints
- info about each RX/TX datagram.
+ - Added -D option on server to enable debugging. Debug level 1 now
+ prints info about each RX/TX datagram.
2007-11-30: 0.4.1 "Tea Online"
- Introduced encoding API
Server side:
To use this tunnel, you need control over a real domain (like mytunnel.com),
-and a server with a public IP number (not behind NAT) that does not yet run
-a DNS server. Then, delegate a subdomain (say, tunnel1.mytunnel.com) to the
-server. If you use BIND for the domain, add these lines to the zone file:
+and a server with a public IP number. If the server already runs a DNS
+server, change the listening port and then use the -b option to let
+iodined forward the DNS requests. Then, delegate a subdomain
+(say, tunnel1.mytunnel.com) to the server. If you use BIND for the domain,
+add these lines to the zone file:
tunnel1host IN A 10.15.213.99
tunnel1 IN NS tunnel1host.mytunnel.com.
everything is ready for the client.
Client side:
-All the setup is done, just start iodine. It also takes two
-arguments, the first is the local relaying DNS server and the second is the
-domain used (tunnel1.mytunnnel.com). If DNS queries are allowed to any
-computer, you can use the tunnel endpoint (example: 10.15.213.99 or
-tunnel1host.mytunnel.com) as the first argument. The tunnel interface will get
-an IP close to the servers (in this case 192.168.99.2) and a suitable MTU.
-Enter the same password as on the server either by argument or after the client
-has started. Now you should be able to ping the other end of the tunnel from
-either side.
+All the setup is done, just start iodine. It takes up to two arguments, the
+first is the local relaying DNS server (optional) and the second is the domain
+used (tunnel1.mytunnnel.com). If DNS queries are allowed to any computer, you
+can use the tunnel endpoint (example: 10.15.213.99 or tunnel1host.mytunnel.com)
+as the first argument. The tunnel interface will get an IP close to the servers
+(in this case 192.168.99.2) and a suitable MTU. Enter the same password as on
+the server either by argument or after the client has started. Now you should
+be able to ping the other end of the tunnel from either side.
MISC. INFO:
and make sure that the relaying DNS server has not cached the response. A
cached error message could mean that you started the client before the server.
-The upstream data is sent gzipped encoded with Base32. DNS protocol allows
-one query per packet, and one query can be max 256 chars. Each domain name part
-can be max 63 chars. So your domain name and subdomain should be as short as
-possible to allow maximum throughput.
+The upstream data is sent gzipped encoded with Base32, or Base64 if the relay
+server support '+' in domain names. DNS protocol allows one query per packet,
+and one query can be max 256 chars. Each domain name part can be max 63 chars.
+So your domain name and subdomain should be as short as possible to allow
+maximum upstream throughput.
TIPS & TRICKS:
PORTABILITY:
-iodine has been tested on Linux (arm, ia64, x86, AMD64 and SPARC64), FreeBSD
-(ia64, x86), OpenBSD (x86), NetBSD (x86) and MacOS X (ppc and x86, with
-http://www-user.rhrk.uni-kl.de/~nissler/tuntap/). It should work on other
-unix-like systems as well that has TUN/TAP tunneling support (after some
-patching). Let us know if you get it to run on other platforms.
+iodine has been tested on Linux (arm, ia64, x86, AMD64 and SPARC64), FreeBSD
+(ia64, x86), OpenBSD (x86), NetBSD (x86) and MacOS X (ppc and x86, with
+http://tuntaposx.sourceforge.net/). It should be easy to port to other
+unix-like systems that has TUN/TAP tunneling support. Let us know if you get it
+to run on other platforms.
THE NAME:
AUTHORS & LICENSE:
-Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+Copyright (c) 2006-2009 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
--- /dev/null
+Detailed specification of protocol in version 00000402
+======================================================
+
+CMC = 2 byte Cache Miss Counter, increased every time it is used
+
+Version:
+Client sends:
+ First byte v or V
+ Rest encoded with base32:
+ 4 bytes big endian protocol version
+ CMC
+Server replies:
+ 4 chars:
+ VACK (version ok), followed by login challenge
+ VNAK (version differs), followed by server protocol version
+ VFUL (server has no free slots), followed by max users
+ 4 byte value: means login challenge/server protocol version/max users
+ 1 byte userid of the new user, or any byte if not VACK
+
+Login:
+Client sends:
+ First byte l or L
+ Rest encoded with base32:
+ 1 byte userid
+ 16 bytes MD5 hash of: (first 32 bytes of password) xor (8 repetitions of login challenge)
+ CMC
+Server replies:
+ LNAK means not accepted
+ x.x.x.x-y.y.y.y-mtu means accepted (server ip, client ip, mtu)
+
+Case check:
+Client sends:
+ First byte z or Z
+ Lots of data that should not be decoded
+Server replies:
+ The requested domain copied raw
+
+Data:
+Data header:
+ 321 0
+ +---+-+
+ |UUU|L|
+ +---+-+
+
+UUU = Userid
+L = Last fragment in packet flag
+
+First byte is the header, 4 bits coded as hex in ASCII.
+Followed by data encoded with Base32.
+
+Ping:
+Client sends:
+ First byte p or P
+ Rest encoded with Base32:
+ 1 byte userid
+ CMC
+
+The server response to Ping and Data packets is a DNS NULL type response:
+If server has nothing to send, data length is 0 bytes.
+If server has a packet to send, data length is set and the data is a full raw
+unencoded ip packet, prefixed with 32 bits tun data.
--- /dev/null
+Detailed specification of protocol in version 00000500
+======================================================
+
+CMC = 2 byte Cache Miss Counter, increased every time it is used
+
+Version:
+Client sends:
+ First byte v or V
+ Rest encoded with base32:
+ 4 bytes big endian protocol version
+ CMC
+Server replies:
+ 4 chars:
+ VACK (version ok), followed by login challenge
+ VNAK (version differs), followed by server protocol version
+ VFUL (server has no free slots), followed by max users
+ 4 byte value: means login challenge/server protocol version/max users
+ 1 byte userid of the new user, or any byte if not VACK
+
+Login:
+Client sends:
+ First byte l or L
+ Rest encoded with base32:
+ 1 byte userid
+ 16 bytes MD5 hash of: (first 32 bytes of password) xor (8 repetitions of login challenge)
+ CMC
+Server replies:
+ LNAK means not accepted
+ x.x.x.x-y.y.y.y-mtu-netmask means accepted (server ip, client ip, mtu, netmask bits)
+
+Case check:
+Client sends:
+ First byte z or Z
+ Lots of data that should not be decoded
+Server replies:
+ The requested domain copied raw
+
+Switch codec:
+Client sends:
+ First byte s or S
+ 5 bits coded as Base32 char, meaning userid
+ 5 bits coded as Base32 char, with value 5 or 6, representing number of raw
+ bits per encoded byte
+Server sends:
+ Name of codec if accepted. After this all upstream data packets must
+ be encoded with the new codec.
+ BADCODEC if not accepted. Client must then revert to Base32
+
+Probe downstream fragment size:
+Client sends:
+ First byte r or R
+ 15 bits coded as 3 Base32 chars: UUUUF FFFFF FFFFF
+ meaning 4 bits userid, 11 bits fragment size
+ Then follows a long random query which contents does not matter
+Server sends:
+ Requested number of bytes as a response. The first two bytes contains
+ the requested length. Rest of message can be any data.
+ BADFRAG if requested length not accepted.
+
+Set downstream fragment size:
+Client sends:
+ First byte n or N
+ Rest encoded with base32:
+ 1 byte userid
+ 2 bytes new downstream fragment size
+ CMC
+Server sends:
+ 2 bytes new downstream fragment size. After this all downstream
+ payloads will be max (fragsize + 2) bytes long.
+ BADFRAG if not accepted.
+
+Data:
+Upstream data header:
+ 3210 432 10 43 210 4321 0
+ +----+---+--+--+---+----+-+
+ |UUUU|SSS|FF|FF|DDD|GGGG|L|
+ +----+---+--+--+---+----+-+
+
+Downstream data header:
+ 7 654 3210 765 4321 0
+ +-+---+----+---+----+-+
+ |C|SSS|FFFF|DDD|GGGG|L|
+ +-+---+----+---+----+-+
+
+UUUU = Userid
+L = Last fragment in packet flag
+SS = Upstream packet sequence number
+FFFF = Upstream fragment number
+DDD = Downstream packet sequence number
+GGGG = Downstream fragment number
+C = Compression enabled for downstream packet
+
+Upstream data packet starts with 1 byte ASCII hex coded user byte, then 3 bytes
+Base32 encoded header, then comes the payload data, encoded with chosen codec.
+
+Downstream data starts with 2 byte header. Then payload data, which may be
+compressed.
+
+Ping:
+Client sends:
+ First byte p or P
+ Rest encoded with Base32:
+ 1 byte with 4 bits userid
+ 1 byte with:
+ 3 bits downstream seqno
+ 4 bits downstream fragment
+ CMC
+
+The server response to Ping and Data packets is a DNS NULL type response:
+If server has nothing to send, data length is 0 bytes.
+If server has something to send, it will send a downstream data packet,
+prefixed with 2 bytes header as shown above.
.I user
.B ] [-P
.I password
+.B ] [-m
+.I fragsize
.B ] [-t
.I chrootdir
.B ] [-d
.B iodined [-c] [-s] [-f] [-D] [-u
.I user
-.B ] [-P
-.I password
.B ] [-t
.I chrootdir
+.B ] [-d
+.I device
.B ] [-m
.I mtu
.B ] [-l
.I listen_ip
-.B ] [-d
-.I device
-.B ]
+.B ] [-p
+.I port
+.B ] [-n
+.I external ip
+.B ] [-b
+.I dnsport
+.B ] [-P
+.I password
+.B ]
.I tunnel_ip
+.B [
+.I /netmask
+.B ]
.I topdomain
.SH DESCRIPTION
.B iodine
.B -t chrootdir
Chroot to 'chrootdir' after setting up tunnel.
.TP
+.B -d device
+Use the TUN device 'device' instead of the normal one, which is dnsX on Linux
+and otherwise tunX.
+.TP
.B -P password
Use 'password' to authenticate. If not used,
.B stdin
will be used as input. Only the first 32 characters will be used.
+.SS Client Options:
.TP
-.B -d device
-Use the TUN device 'device' instead of the normal one, which is dnsX on Linux
-and otherwise tunX.
+.B -m fragsize
+Maximum downstream fragsize. Not setting this will cause the client to probe
+the maximum accepted downstream packet size.
.SS Server Options:
.TP
.B -c
Make the server listen on 'port' instead of 53 for traffic.
.B Note:
You must make sure the dns requests are forwarded to this port yourself.
+.TP
+.B -n external ip
+The IP address to return in NS responses. Default is to return the address used
+as destination in the query.
+.TP
+.B -b dnsport
+If this port is specified, all incoming requests not inside the tunnel domain
+will be forwarded to this port on localhost, to be handled by a real dns.
.SS Client Arguments:
.TP
.B nameserver
must be the same on both the client and the server.
.SS Server Arguments:
.TP
-.B tunnel_ip
+.B tunnel_ip[/netmask]
This is the servers ip address on the tunnel interface. The client will be
given the next ip number in the range. It is recommended to use the
-10.0.0.0/8 or 172.16.0.0/12 ranges.
+10.0.0.0 or 172.16.0.0 ranges. The default netmask is /27, can be overriden
+by specifying it here. Using a smaller network will limit the number of
+concurrent users.
.TP
.B topdomain
The dns traffic will is expected to be sent as querys of type NULL for
.TP
.B Server side:
To use this tunnel, you need control over a real domain (like mytunnel.com),
-and a server with a static public IP number that does not yet run a DNS
-server. Then, delegate a subdomain (say, tunnel1.mytunnel.com) to the server.
-If you use BIND for the domain, add these lines to the zone file (replace
-10.15.213.99 with your server ip):
+and a server with a public IP number. If the server already runs a DNS
+server, change the listening port and then use the \-b option to let
+iodined forward the DNS requests. Then, delegate a subdomain
+(say, tunnel1.mytunnel.com) to the server. If you use BIND for the domain,
+add these lines to the zone file (replace 10.15.213.99 with your server ip):
.nf
tunnel1host IN A 10.15.213.99
and configure the server to do NAT.
.TP
.B MTU issues:
-Some relaying DNS servers enforce a 512 byte packet limit. All larger packets are
-simply dropped. If you can ping through the tunnel but not login via SSH, this is
-most likely the case. Set the MTU on the server to 220 to ensure that all packets
-are less than 512 bytes. This will however greatly affect performance.
+These issues should be solved now, with automatic fragmentation of downstream
+packets. There should be no need to set the MTU explicitly on the server.
.SH BUGS
File bugs at http://dev.kryo.se/iodine/
.SH AUTHORS
COMMONOBJS = tun.o dns.o read.o encoding.o login.o base32.o base64.o md5.o common.o
CLIENTOBJS = iodine.o
CLIENT = ../bin/iodine
-SERVEROBJS = iodined.o user.o
+SERVEROBJS = iodined.o user.o fw_query.o
SERVER = ../bin/iodined
OS = `uname | tr "a-z" "A-Z"`
ARCH = `uname -m`
LDFLAGS = -lz `sh osflags link`
-CFLAGS = -c -g -Wall -D$(OS) -pedantic
+CFLAGS = -c -g -Wall -D$(OS) -pedantic `sh osflags cflags`
all: stateos $(CLIENT) $(SERVER)
/*
- * Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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
#include "encoding.h"
#include "base32.h"
+#define BLKSIZE_RAW 5
+#define BLKSIZE_ENC 8
+
static const char cb32[] =
- "abcdefghijklmnopqrstuvwxyz0123456789";
+ "abcdefghijklmnopqrstuvwxyz012345";
static unsigned char rev32[128];
static int reverse_init = 0;
+static int base32_decode(void *, size_t *, const char *, size_t);
+static int base32_encode(char *, size_t *, const void *, size_t);
+static int base32_handles_dots();
+static int base32_blksize_raw();
+static int base32_blksize_enc();
+
static struct encoder base32_encoder =
{
- "BASE32",
+ "Base32",
base32_encode,
base32_decode,
base32_handles_dots,
- base32_handles_dots
+ base32_handles_dots,
+ base32_blksize_raw,
+ base32_blksize_enc
};
struct encoder
return &base32_encoder;
}
-int
+static int
base32_handles_dots()
{
return 0;
}
-int
+static int
+base32_blksize_raw()
+{
+ return BLKSIZE_RAW;
+}
+
+static int
+base32_blksize_enc()
+{
+ return BLKSIZE_ENC;
+}
+
+int
+b32_5to8(int in)
+{
+ return cb32[in & 31];
+}
+
+int
+b32_8to5(int in)
+{
+ int i;
+ int c;
+ if (!reverse_init) {
+ for (i = 0; i < 32; i++) {
+ c = cb32[i];
+ rev32[(int) c] = i;
+ }
+ reverse_init = 1;
+ }
+ return rev32[in];
+}
+
+static int
base32_encode(char *buf, size_t *buflen, const void *data, size_t size)
{
size_t newsize;
size_t maxsize;
- unsigned char *s;
unsigned char *p;
unsigned char *q;
int i;
memset(buf, 0, *buflen);
/* how many chars can we encode within the buf */
- maxsize = 5 * (*buflen / 8 - 1) - 1;
+ maxsize = BLKSIZE_RAW * (*buflen / BLKSIZE_ENC - 1) - 1;
/* how big will the encoded data be */
- newsize = 8 * (size / 5 + 1) + 1;
+ newsize = BLKSIZE_ENC * (size / BLKSIZE_RAW + 1) + 1;
/* if the buffer is too small, eat some of the data */
if (*buflen < newsize) {
size = maxsize;
}
- p = s = (unsigned char *) buf;
+ p = (unsigned char *) buf;
q = (unsigned char *)data;
- for(i=0;i<size;i+=5) {
+ for(i=0;i<size;i+=BLKSIZE_RAW) {
p[0] = cb32[((q[0] & 0xf8) >> 3)];
p[1] = cb32[(((q[0] & 0x07) << 2) | ((q[1] & 0xc0) >> 6))];
p[2] = (i+1 < size) ? cb32[((q[1] & 0x3e) >> 1)] : '\0';
p[6] = (i+3 < size) ? cb32[((q[3] & 0x03) << 3) | ((q[4] & 0xe0) >> 5)] : '\0';
p[7] = (i+4 < size) ? cb32[((q[4] & 0x1f))] : '\0';
- q += 5;
- p += 8;
+ q += BLKSIZE_RAW;
+ p += BLKSIZE_ENC;
}
*p = 0;
return 5;
}
-int
+static int
base32_decode(void *buf, size_t *buflen, const char *str, size_t slen)
{
unsigned char *q;
}
/* chars needed to decode slen */
- newsize = 5 * (slen / 8 + 1) + 1;
+ newsize = BLKSIZE_RAW * (slen / BLKSIZE_ENC + 1) + 1;
/* encoded chars that fit in buf */
- maxsize = 8 * (*buflen / 5 + 1) + 1;
+ maxsize = BLKSIZE_ENC * (*buflen / BLKSIZE_RAW + 1) + 1;
/* if the buffer is too small, eat some of the data */
if (*buflen < newsize) {
slen = maxsize;
}
q = buf;
- for (p = str; *p && strchr(cb32, *p); p += 8) {
+ for (p = str; *p && strchr(cb32, *p); p += BLKSIZE_ENC) {
len = decode_token((unsigned char *) p, (unsigned char *) q, slen);
q += len;
- slen -= 8;
+ slen -= BLKSIZE_ENC;
- if (len < 5)
+ if (len < BLKSIZE_RAW)
break;
}
*q = '\0';
/*
- * Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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
#define __BASE32_H__
struct encoder *get_base32_encoder(void);
-int base32_handles_dots();
-int base32_encode(char *, size_t *, const void *, size_t);
-int base32_decode(void *, size_t *, const char *, size_t);
+int b32_5to8(int);
+int b32_8to5(int);
#endif
/*
- * Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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
#include "common.h"
#include "base64.h"
+#define BLKSIZE_RAW 3
+#define BLKSIZE_ENC 4
+
static const char cb64[] =
- "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789.";
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789+";
static unsigned char rev64[128];
static int reverse_init = 0;
+static int base64_encode(char *, size_t *, const void *, size_t);
+static int base64_decode(void *, size_t *, const char *, size_t);
+static int base64_handles_dots();
+static int base64_blksize_raw();
+static int base64_blksize_enc();
+
#define REV64(x) rev64[(int) (x)]
-#define MODE (cb64[62])
-#define P62 (cb64[62])
-#define P63 (cb64[63])
static struct encoder base64_encoder =
{
- "BASE64",
+ "Base64",
base64_encode,
base64_decode,
base64_handles_dots,
- base64_handles_dots
+ base64_handles_dots,
+ base64_blksize_raw,
+ base64_blksize_enc
};
struct encoder
return &base64_encoder;
}
-int
+static int
base64_handles_dots()
{
return 0;
}
-static void
-findesc(int *count, unsigned char *esc, char c1, char c2, char c3, char c4)
+static int
+base64_blksize_raw()
{
- int min1 = 0;
- int min2 = 0;
-
- int num1 = 0xFF; /* a very big number */
- int num2 = 0xFE; /* a nearly as big number */
-
- int i;
-
- /* check if no more escapes needed */
- if (count[62] == 0 && count[63] == 0) {
- esc[0] = MODE;
- esc[1] = MODE;
- return;
- }
-
- for (i = 0; i < 62; i++) {
- if (i == c1 || i == c2 || i == c3 || i == c4) {
- continue;
- }
-
- if (count[i] < num1) {
- min2 = min1;
- num2 = num1;
- min1 = i;
- num1 = count[i];
- } else if (count[i] < num2) {
- min2 = i;
- num2 = count[i];
- }
- }
-
- esc[0] = cb64[min1];
- esc[1] = cb64[min2];
+ return BLKSIZE_RAW;
}
-
-static void
-escape_chars(char *buf, size_t buflen)
-{
- int counter[64];
- int escapes;
- int reset;
- int i;
- unsigned char temp[4096];
- unsigned char *r;
- unsigned char *w;
- unsigned char *e;
- unsigned char esc[2];
- memset(counter, 0, sizeof(counter));
- esc[0] = P62;
- esc[1] = P63;
-
- /* first, find the number of times each token is used */
- r = (unsigned char *) buf;
- w = temp;
- while (*r) {
- counter[REV64(*r)]++;
- *w++ = *r++;
- }
-
- /* check if work needed */
- if (counter[62] == 0 && counter[63] == 0)
- return;
-
- r = temp;
- w = (unsigned char *) buf;
- reset = 1;
- escapes = 0;
- /* check a block for esc chars */
- while (*r) {
- if (reset == 0 && escapes == 0 && (
- r[0] == esc[0] || r[1] == esc[0] ||r[2] == esc[0] ||r[2] == esc[0] ||
- r[0] == esc[1] || r[1] == esc[1] ||r[2] == esc[1] ||r[2] == esc[1])) {
- /* last set of escape chars were unused.
- * if we reset last escape switch then maybe we dont have to switch now */
-
- /* change the latest escape switch to 999 (RESET) */
- e[1] = MODE;
- e[2] = MODE;
-
- /* store default esc chars */
- esc[0] = P62;
- esc[1] = P63;
-
- reset = 1;
- }
- /* these two if blocks can not be combined because a block can contain both
- * char 9 and/or . and the current escape chars. */
- if (r[0] == esc[0] || r[1] == esc[0] ||r[2] == esc[0] ||r[2] == esc[0] ||
- r[0] == esc[1] || r[1] == esc[1] ||r[2] == esc[1] ||r[2] == esc[1]) {
- /* switch escape chars */
- escapes = 0;
- reset = 0;
-
- /* find 2 suitable escape chars */
- findesc(counter, esc, REV64(r[0]), REV64(r[1]), REV64(r[2]), REV64(r[3]));
-
- /* store escape switch position */
- e = w;
-
- /* write new escape chars */
- *w++ = MODE;
- *w++ = esc[0];
- *w++ = esc[1];
- }
-
- /* update counter on remaining chars */
- for (i = 0; i < 4; i++) {
- if (r[i])
- counter[REV64(r[i])]--;
- }
-
- /* do the escaping */
- for (i = 0; i < 4; i++) {
- if (r[i] == P62) {
- r[i] = esc[0];
- escapes++;
- } else if (r[i] == P63) {
- r[i] = esc[1];
- escapes++;
- }
- }
-
- /* copy back to buf */
- *w++ = *r++;
- *w++ = *r++;
- *w++ = *r++;
- *w++ = *r++;
- }
+static int
+base64_blksize_enc()
+{
+ return BLKSIZE_ENC;
}
-int
+static int
base64_encode(char *buf, size_t *buflen, const void *data, size_t size)
{
size_t newsize;
}
/* how many chars can we encode within the buf */
- maxsize = 3 * (*buflen / 4 - 1) - 1;
+ maxsize = BLKSIZE_RAW * (*buflen / BLKSIZE_ENC - 1) - 1;
/* how big will the encoded data be */
- newsize = 4 * (size / 3 + 1) + 1;
+ newsize = BLKSIZE_ENC * (size / BLKSIZE_RAW + 1) + 1;
/* if the buffer is too small, eat some of the data */
if (*buflen < newsize) {
size = maxsize;
p = s = (unsigned char *) buf;
q = (unsigned char *)data;
- for(i=0;i<size;i+=3) {
+ for(i=0;i<size;i+=BLKSIZE_RAW) {
p[0] = cb64[((q[0] & 0xfc) >> 2)];
p[1] = cb64[(((q[0] & 0x03) << 4) | ((q[1] & 0xf0) >> 4))];
p[2] = (i+1 < size) ? cb64[((q[1] & 0x0f) << 2 ) | ((q[2] & 0xc0) >> 6)] : '\0';
p[3] = (i+2 < size) ? cb64[(q[2] & 0x3f)] : '\0';
- q += 3;
- p += 4;
+ q += BLKSIZE_RAW;
+ p += BLKSIZE_ENC;
}
*p = 0;
- escape_chars(buf, *buflen);
-
/* store number of bytes from data that was used */
*buflen = size;
return 3;
}
-int
+static int
base64_decode(void *buf, size_t *buflen, const char *str, size_t slen)
{
unsigned char *q;
size_t maxsize;
const char *p;
unsigned char c;
- unsigned char block[4];
- unsigned char prot62;
- unsigned char prot63;
+ unsigned char block[BLKSIZE_ENC];
int len;
int i;
}
/* chars needed to decode slen */
- newsize = 3 * (slen / 4 + 1) + 1;
+ newsize = BLKSIZE_RAW * (slen / BLKSIZE_ENC + 1) + 1;
/* encoded chars that fit in buf */
- maxsize = 4 * (*buflen / 3 + 1) + 1;
+ maxsize = BLKSIZE_ENC * (*buflen / BLKSIZE_RAW + 1) + 1;
/* if the buffer is too small, eat some of the data */
if (*buflen < newsize) {
slen = maxsize;
}
- prot62 = P62;
- prot63 = P63;
q = buf;
- for (p = str; *p; p += 4) {
- /* handle escape instructions */
- if (*p == MODE) {
- p++;
- if (p[0] == MODE && p[1] == MODE) {
- /* reset escape chars */
- prot62 = P62;
- prot63 = P63;
-
- p += 2;
- } else {
- prot62 = *p++;
- prot63 = *p++;
- }
- }
+ for (p = str; *p; p += BLKSIZE_ENC) {
/* since the str is const, we unescape in another buf */
- for (i = 0; i < 4; i++) {
+ for (i = 0; i < BLKSIZE_ENC; i++) {
block[i] = p[i];
- if (prot62 == block[i]) {
- block[i] = P62;
- } else if (prot63 == block[i]) {
- block[i] = P63;
- }
}
len = decode_token(block, (unsigned char *) q, slen);
q += len;
- slen -= 4;
+ slen -= BLKSIZE_ENC;
- if (len < 3)
+ if (len < BLKSIZE_RAW)
break;
}
*q = '\0';
/*
- * Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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
#define __BASE64_H__
struct encoder *get_base64_encoder(void);
-int base64_handles_dots();
-int base64_encode(char *, size_t *, const void *, size_t);
-int base64_decode(void *, size_t *, const char *, size_t);
#endif
-/* Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+/* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2007 Albert Lee <trisk@acm.jhu.edu>.
*
* Permission to use, copy, modify, and distribute this software for any
}
#endif
+#if defined(__BEOS__) && !defined(__HAIKU__)
+int setgroups(int count, int *groups)
+{
+ /* errno = ENOSYS; */
+ return -1;
+}
+#endif
+
int
open_dns(int localport, in_addr_t listen_ip)
{
#endif
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
+ /* To get destination address from each UDP datagram, see iodined.c:read_dns() */
+ setsockopt(fd, IPPROTO_IP, DSTADDR_SOCKOPT, &flag, sizeof(flag));
+
if(bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0)
err(1, "bind");
void
do_chroot(char *newroot)
{
+#if !defined(__BEOS__) || defined(__HAIKU__)
if (chroot(newroot) != 0 || chdir("/") != 0)
err(1, "%s", newroot);
seteuid(geteuid());
setuid(getuid());
+#else
+ warnx("chroot not available");
+#endif
}
void
/*
- * Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
+#include <netinet/in.h>
#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#define QUERY_NAME_SIZE 256
+#if defined IP_RECVDSTADDR
+# define DSTADDR_SOCKOPT IP_RECVDSTADDR
+# define dstaddr(x) ((struct in_addr *) CMSG_DATA(x))
+#elif defined IP_PKTINFO
+# define DSTADDR_SOCKOPT IP_PKTINFO
+# define dstaddr(x) (&(((struct in_pktinfo *)(CMSG_DATA(x)))->ipi_addr))
+#endif
+
struct packet
{
int len; /* Total packet length */
int sentlen; /* Length of chunk currently transmitted */
int offset; /* Current offset */
char data[64*1024]; /* The data */
+ char seqno; /* The packet sequence number */
+ char fragment; /* Fragment index */
};
struct query {
char name[QUERY_NAME_SIZE];
- short type;
- short id;
+ unsigned short type;
+ unsigned short id;
+ struct in_addr destination;
struct sockaddr from;
int fromlen;
};
/*
- * Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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
return len;
}
+int
+dns_encode_ns_response(char *buf, size_t buflen, struct query *q, char *topdomain)
+{
+ HEADER *header;
+ int len;
+ short name;
+ short topname;
+ short nsname;
+ char *domain;
+ int domain_len;
+ char *p;
+
+ memset(buf, 0, buflen);
+
+ header = (HEADER*)buf;
+
+ header->id = htons(q->id);
+ header->qr = 1;
+ header->opcode = 0;
+ header->aa = 1;
+ header->tc = 0;
+ header->rd = 0;
+ header->ra = 0;
+
+ p = buf + sizeof(HEADER);
+
+ header->qdcount = htons(1);
+ header->ancount = htons(1);
+ header->arcount = htons(1);
+
+ /* pointer to start of name */
+ name = 0xc000 | ((p - buf) & 0x3fff);
+
+ domain = strstr(q->name, topdomain);
+ if (domain) {
+ domain_len = (int) (domain - q->name);
+ } else {
+ return -1;
+ }
+ /* pointer to start of topdomain */
+ topname = 0xc000 | ((p - buf + domain_len) & 0x3fff);
+
+ /* Query section */
+ putname(&p, sizeof(q->name), q->name); /* Name */
+ putshort(&p, q->type); /* Type */
+ putshort(&p, C_IN); /* Class */
+
+ /* Answer section */
+ putshort(&p, name); /* Name */
+ putshort(&p, q->type); /* Type */
+ putshort(&p, C_IN); /* Class */
+ putlong(&p, 0x3ea7d011); /* TTL */
+ putshort(&p, 5); /* Data length */
+
+ /* pointer to ns.topdomain */
+ nsname = 0xc000 | ((p - buf) & 0x3fff);
+ putbyte(&p, 2);
+ putbyte(&p, 'n');
+ putbyte(&p, 's');
+ putshort(&p, topname); /* Name Server */
+
+ /* Additional data (A-record of NS server) */
+ putshort(&p, nsname); /* Name Server */
+ putshort(&p, T_A); /* Type */
+ putshort(&p, C_IN); /* Class */
+ putlong(&p, 0x3ea7d011); /* TTL */
+ putshort(&p, 4); /* Data length */
+
+ /* ugly hack to output IP address */
+ domain = (char *) &q->destination;
+ putbyte(&p, *domain++);
+ putbyte(&p, *domain++);
+ putbyte(&p, *domain++);
+ putbyte(&p, *domain);
+
+ len = p - buf;
+ return len;
+}
+
+short
+dns_get_id(char *packet, size_t packetlen)
+{
+ HEADER *header;
+ header = (HEADER*)packet;
+
+ if (packetlen < sizeof(HEADER))
+ return 0;
+
+ return ntohs(header->id);
+}
+
int
dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, size_t packetlen)
{
ancount = ntohs(header->ancount);
id = ntohs(header->id);
+ id = id & 0xFFFF; /* Kill any sign extension */
rlen = 0;
rv = MIN(rlen, sizeof(rdata));
rv = readdata(packet, &data, rdata, rv);
- if(type == T_NULL && rv > 2 && buf) {
+ if(type == T_NULL && rv >= 2 && buf) {
rv = MIN(rv, buflen);
memcpy(buf, rdata, rv);
}
break;
case QR_QUERY:
if (qdcount != 1) {
- warnx("no query on query");
+ warnx("no question section in name query");
return -1;
}
/*
- * Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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
} qr_t;
int dns_encode(char *, size_t, struct query *, qr_t, char *, size_t);
+int dns_encode_ns_response(char *buf, size_t buflen, struct query *q, char *topdomain);
+short dns_get_id(char *packet, size_t packetlen);
int dns_decode(char *, size_t, struct query *, qr_t, char *, size_t);
#endif /* _DNS_H_ */
/*
- * Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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
char *reader, *writer;
total = strlen(buf);
- dots = total / 62;
+ dots = total / 57;
writer = buf;
writer += total;
pos = (unsigned) (reader - buf) + 1;
while (dots) {
- if (pos % 62 == 0) {
+ *writer-- = *reader--;
+ pos--;
+ if (pos % 57 == 0) {
*writer-- = '.';
dots--;
}
- *writer-- = *reader--;
- pos--;
}
/* return new length of string */
/*
- * Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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
int (*decode) (void *, size_t *, const char *, size_t);
int (*places_dots) (void);
int (*eats_dots) (void);
+ int (*blocksize_raw)(void);
+ int (*blocksize_encoded)(void);
};
int unpack_data(char *, size_t, char *, size_t, struct encoder *);
--- /dev/null
+/*
+ * Copyright (c) 2008 Erik Ekman <yarrick@kryo.se>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <string.h>
+#include "fw_query.h"
+
+static struct fw_query fwq[FW_QUERY_CACHE_SIZE];
+static int fwq_ix;
+
+void fw_query_init()
+{
+ memset(fwq, 0, sizeof(struct fw_query) * FW_QUERY_CACHE_SIZE);
+ fwq_ix = 0;
+}
+
+void fw_query_put(struct fw_query *fw_query)
+{
+ memcpy(&(fwq[fwq_ix]), fw_query, sizeof(struct fw_query));
+
+ ++fwq_ix;
+ if (fwq_ix >= FW_QUERY_CACHE_SIZE)
+ fwq_ix = 0;
+}
+
+void fw_query_get(short query_id, struct fw_query **fw_query)
+{
+ int i;
+
+ *fw_query = NULL;
+ for (i = 0; i < FW_QUERY_CACHE_SIZE; i++) {
+ if (fwq[i].id == query_id) {
+ *fw_query = &(fwq[i]);
+ return;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2008 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 __FW_QUERY_H__
+#define __FW_QUERY_H__
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#define FW_QUERY_CACHE_SIZE 16
+
+struct fw_query {
+ struct sockaddr addr;
+ int addrlen;
+ short id;
+};
+
+void fw_query_init();
+void fw_query_put(struct fw_query *fw_query);
+void fw_query_get(short query_id, struct fw_query **fw_query);
+
+#endif /*__FW_QUERY_H__*/
+
/*
- * Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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
#include "common.h"
#include "encoding.h"
#include "base32.h"
+#include "base64.h"
#include "dns.h"
#include "login.h"
#include "tun.h"
static char *topdomain;
static uint16_t rand_seed;
+static int downstream_seqno;
+static int downstream_fragment;
+static int down_ack_seqno;
+static int down_ack_fragment;
-/* Current IP packet */
-static struct packet packet;
+static int max_downstream_frag_size;
+static int autodetect_frag_size;
+
+/* Current up/downstream IP packet */
+static struct packet outpkt;
+static struct packet inpkt;
/* My userid at the server */
static char userid;
size_t space;
char *b;
-
- space = MIN(0xFF, buflen) - strlen(topdomain) - 2;
+ space = MIN(0xFF, buflen) - strlen(topdomain) - 5;
if (!encoder->places_dots())
- space -= (space / 62); /* space for dots */
+ space -= (space / 57); /* space for dots */
memset(buf, 0, buflen);
return space;
}
-int
+static int
is_sending()
{
- return (packet.len != 0);
+ return (outpkt.len != 0);
}
-int
+static int
read_dns(int fd, char *buf, int buflen)
{
struct sockaddr_in from;
rv = dns_decode(buf, buflen, &q, QR_ANSWER, data, r);
- if (is_sending() && chunkid == q.id) {
- /* Got ACK on sent packet */
- packet.offset += packet.sentlen;
- if (packet.offset == packet.len) {
- /* Packet completed */
- packet.offset = 0;
- packet.len = 0;
- packet.sentlen = 0;
- } else {
- /* More to send */
- send_chunk(fd);
+ /* decode the data header, update seqno and frag before next request */
+ if (rv >= 2) {
+ downstream_seqno = (buf[1] >> 5) & 7;
+ downstream_fragment = (buf[1] >> 1) & 15;
+ }
+
+
+ if (is_sending()) {
+ if (chunkid == q.id) {
+ /* Got ACK on sent packet */
+ outpkt.offset += outpkt.sentlen;
+ if (outpkt.offset == outpkt.len) {
+ /* Packet completed */
+ outpkt.offset = 0;
+ outpkt.len = 0;
+ outpkt.sentlen = 0;
+
+ /* If the ack contains unacked frag number but no data,
+ * send a ping to ack the frag number and get more data*/
+ if (rv == 2 && (
+ downstream_seqno != down_ack_seqno ||
+ downstream_fragment != down_ack_fragment
+ )) {
+
+ send_ping(fd);
+ }
+ } else {
+ /* More to send */
+ send_chunk(fd);
+ }
+
}
}
return rv;
inlen = read;
compress2((uint8_t*)out, &outlen, (uint8_t*)in, inlen, 9);
- memcpy(packet.data, out, MIN(outlen, sizeof(packet.data)));
- packet.sentlen = 0;
- packet.offset = 0;
- packet.len = outlen;
+ memcpy(outpkt.data, out, MIN(outlen, sizeof(outpkt.data)));
+ outpkt.sentlen = 0;
+ outpkt.offset = 0;
+ outpkt.len = outlen;
+ outpkt.seqno++;
+ outpkt.fragment = 0;
send_chunk(dns_fd);
static int
tunnel_dns(int tun_fd, int dns_fd)
{
- unsigned long outlen;
- unsigned long inlen;
- char out[64*1024];
- char in[64*1024];
+ unsigned long datalen;
+ char buf[64*1024];
size_t read;
- if ((read = read_dns(dns_fd, in, sizeof(in))) <= 0)
+ if ((read = read_dns(dns_fd, buf, sizeof(buf))) <= 2)
return -1;
-
- outlen = sizeof(out);
- inlen = read;
- if (uncompress((uint8_t*)out, &outlen, (uint8_t*)in, inlen) != Z_OK)
+
+ if (downstream_seqno != inpkt.seqno) {
+ /* New packet */
+ inpkt.seqno = downstream_seqno;
+ inpkt.fragment = downstream_fragment;
+ inpkt.len = 0;
+ } else if (downstream_fragment <= inpkt.fragment) {
+ /* Duplicate fragment */
return -1;
+ }
+ inpkt.fragment = downstream_fragment;
+
+ datalen = MIN(read - 2, sizeof(inpkt.data) - inpkt.len);
+
+ /* Skip 2 byte data header and append to packet */
+ memcpy(&inpkt.data[inpkt.len], &buf[2], datalen);
+ inpkt.len += datalen;
+
+ if (buf[1] & 1) { /* If last fragment flag is set */
+ /* Uncompress packet and send to tun */
+ datalen = sizeof(buf);
+ if (uncompress((uint8_t*)buf, &datalen, (uint8_t*) inpkt.data, inpkt.len) == Z_OK) {
+ write_tun(tun_fd, buf, datalen);
+ }
+ inpkt.len = 0;
+ }
- write_tun(tun_fd, out, outlen);
+ /* If we have nothing to send, send a ping to get more data */
if (!is_sending())
send_ping(dns_fd);
tv.tv_sec = 1;
tv.tv_usec = 0;
+
FD_ZERO(&fds);
- if (!is_sending())
+ if (!is_sending()) {
FD_SET(tun_fd, &fds);
+ }
FD_SET(dns_fd, &fds);
i = select(MAX(tun_fd, dns_fd) + 1, &fds, NULL, NULL, &tv);
int code;
char *p;
- p = packet.data;
- p += packet.offset;
- avail = packet.len - packet.offset;
+ p = outpkt.data;
+ p += outpkt.offset;
+ avail = outpkt.len - outpkt.offset;
- packet.sentlen = build_hostname(buf + 1, sizeof(buf) - 1, p, avail, topdomain, dataenc);
+ outpkt.sentlen = build_hostname(buf + 4, sizeof(buf) - 4, p, avail, topdomain, dataenc);
- if (packet.sentlen == avail)
- code = 1;
- else
- code = 0;
-
- code |= (userid << 1);
- buf[0] = hex[code];
+ /* Build upstream data header (see doc/proto_xxxxxxxx.txt) */
+
+ buf[0] = hex[userid & 15]; /* First byte is 4 bits userid */
+
+ code = ((outpkt.seqno & 7) << 2) | ((outpkt.fragment & 15) >> 2);
+ buf[1] = b32_5to8(code); /* Second byte is 3 bits seqno, 2 upper bits fragment count */
+
+ code = ((outpkt.fragment & 3) << 3) | (downstream_seqno & 7);
+ buf[2] = b32_5to8(code); /* Third byte is 2 bits lower fragment count, 3 bits downstream packet seqno */
+
+ code = ((downstream_fragment & 15) << 1) | (outpkt.sentlen == avail);
+ buf[3] = b32_5to8(code); /* Fourth byte is 4 bits downstream fragment count, 1 bit last frag flag */
+ down_ack_seqno = downstream_seqno;
+ down_ack_fragment = downstream_fragment;
+
+ outpkt.fragment++;
send_query(fd, buf);
}
-void
+static void
send_login(int fd, char *login, int len)
{
char data[19];
static void
send_ping(int fd)
{
- char data[3];
+ char data[4];
if (is_sending()) {
- packet.sentlen = 0;
- packet.offset = 0;
- packet.len = 0;
+ outpkt.sentlen = 0;
+ outpkt.offset = 0;
+ outpkt.len = 0;
}
data[0] = userid;
- data[1] = (rand_seed >> 8) & 0xff;
- data[2] = (rand_seed >> 0) & 0xff;
+ data[1] = ((downstream_seqno & 7) << 4) | (downstream_fragment & 15);
+ data[2] = (rand_seed >> 8) & 0xff;
+ data[3] = (rand_seed >> 0) & 0xff;
+
+ down_ack_seqno = downstream_seqno;
+ down_ack_fragment = downstream_fragment;
rand_seed++;
send_packet(fd, 'P', data, sizeof(data));
}
-void
+static void
+send_fragsize_probe(int fd, int fragsize)
+{
+ char probedata[256];
+ char buf[4096];
+
+ /* build a large query domain which is random and maximum size */
+ memset(probedata, MIN(1, rand_seed & 0xff), sizeof(probedata));
+ probedata[1] = MIN(1, (rand_seed >> 8) & 0xff);
+ rand_seed++;
+ build_hostname(buf + 4, sizeof(buf) - 4, probedata, sizeof(probedata), topdomain, dataenc);
+
+ fragsize &= 2047;
+
+ buf[0] = 'r'; /* Probe downstream fragsize packet */
+ buf[1] = b32_5to8((userid << 1) | (fragsize & 1024));
+ buf[2] = b32_5to8((fragsize >> 5) & 31);
+ buf[3] = b32_5to8(fragsize & 31);
+
+ send_query(fd, buf);
+}
+
+static void
+send_set_downstream_fragsize(int fd, int fragsize)
+{
+ char data[5];
+
+ data[0] = userid;
+ data[1] = (fragsize & 0xff00) >> 8;
+ data[2] = (fragsize & 0x00ff);
+ data[3] = (rand_seed >> 8) & 0xff;
+ data[4] = (rand_seed >> 0) & 0xff;
+
+ rand_seed++;
+
+ send_packet(fd, 'N', data, sizeof(data));
+}
+
+static void
send_version(int fd, uint32_t version)
{
char data[6];
send_packet(fd, 'V', data, sizeof(data));
}
-void
+static void
send_case_check(int fd)
{
- char buf[512] = "zZaAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyY123-4560789.";
+ /* The '+' plus character is not allowed according to RFC.
+ * Expect to get SERVFAIL or similar if it is rejected.
+ */
+ char buf[512] = "zZ+-aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyY1234.";
strncat(buf, topdomain, 512 - strlen(buf));
send_query(fd, buf);
}
+static void
+send_codec_switch(int fd, int userid, int bits)
+{
+ char buf[512] = "S__.";
+ buf[1] = b32_5to8(userid);
+ buf[2] = b32_5to8(bits);
+
+ strncat(buf, topdomain, 512 - strlen(buf));
+ send_query(fd, buf);
+}
+
static int
handshake(int dns_fd)
{
seed = payload;
userid = in[8];
- printf("Version ok, both running 0x%08x. You are user #%d\n", VERSION, userid);
+ printf("Version ok, both using protocol v 0x%08x. You are user #%d\n", VERSION, userid);
goto perform_login;
} else if (strncmp("VNAK", in, 4) == 0) {
- errx(1, "you run 0x%08x, server runs 0x%08x. giving up\n",
+ warnx("You use protocol v 0x%08x, server uses v 0x%08x. Giving up",
VERSION, payload);
- /* NOTREACHED */
+ return 1;
} else if (strncmp("VFUL", in, 4) == 0) {
- errx(1, "server full, all %d slots are taken. try again later\n", payload);
- /* NOTREACHED */
+ warnx("Server full, all %d slots are taken. Try again later", payload);
+ return 1;
}
} else
- warnx("did not receive proper login challenge\n");
+ warnx("did not receive proper login challenge");
}
printf("Retrying version check...\n");
}
if (read > 0) {
+ int netmask;
if (strncmp("LNAK", in, 4) == 0) {
printf("Bad password\n");
return 1;
- } else if (sscanf(in, "%64[^-]-%64[^-]-%d",
- server, client, &mtu) == 3) {
+ } else if (sscanf(in, "%64[^-]-%64[^-]-%d-%d",
+ server, client, &mtu, &netmask) == 4) {
server[64] = 0;
client[64] = 0;
- if (tun_setip(client) == 0 &&
+ if (tun_setip(client, netmask) == 0 &&
tun_setmtu(mtu) == 0) {
goto perform_case_check;
} else {
printf("Retrying login...\n");
}
- errx(1, "couldn't login to server");
- /* NOTREACHED */
+ warnx("couldn't login to server");
+ return 1;
perform_case_check:
case_preserved = 0;
if(r > 0) {
read = read_dns(dns_fd, in, sizeof(in));
- if(read <= 0) {
- warn("read");
- continue;
- }
-
if (read > 0) {
if (in[0] == 'z' || in[0] == 'Z') {
- if (read < (26 * 2)) {
- printf("Received short case reply...\n");
+ if (read < (27 * 2)) {
+ printf("Received short case check reply. Will use base32 encoder\n");
+ goto switch_codec;
} else {
int k;
+ /* TODO enhance this, base128 is probably also possible */
case_preserved = 1;
- for (k = 0; k < 26 && case_preserved; k += 2) {
+ for (k = 0; k < 27 && case_preserved; k += 2) {
if (in[k] == in[k+1]) {
- /* test string: zZaAbBcCdD... */
+ /* test string: zZ+-aAbBcCdDeE... */
case_preserved = 0;
}
}
- return 0;
+ goto switch_codec;
}
} else {
printf("Received bad case check reply\n");
}
+ } else {
+ printf("Got error on case check, will use base32\n");
+ goto switch_codec;
}
}
}
printf("No reply on case check, continuing\n");
+switch_codec:
+ if (!case_preserved)
+ goto autodetect_max_fragsize;
+
+ dataenc = get_base64_encoder();
+ printf("Switching to %s codec\n", dataenc->name);
+ /* Send to server that this user will use base64 from now on */
+ for (i=0; running && i<5 ;i++) {
+ int bits;
+ tv.tv_sec = i + 1;
+ tv.tv_usec = 0;
+
+ bits = 6; /* base64 = 6 bits per byte */
+
+ send_codec_switch(dns_fd, userid, bits);
+
+ FD_ZERO(&fds);
+ FD_SET(dns_fd, &fds);
+
+ r = select(dns_fd + 1, &fds, NULL, NULL, &tv);
+
+ if(r > 0) {
+ read = read_dns(dns_fd, in, sizeof(in));
+
+ if (read > 0) {
+ if (strncmp("BADLEN", in, 6) == 0) {
+ printf("Server got bad message length. ");
+ goto codec_revert;
+ } else if (strncmp("BADIP", in, 5) == 0) {
+ printf("Server rejected sender IP address. ");
+ goto codec_revert;
+ } else if (strncmp("BADCODEC", in, 8) == 0) {
+ printf("Server rejected the selected codec. ");
+ goto codec_revert;
+ }
+ in[read] = 0; /* zero terminate */
+ printf("Server switched to codec %s\n", in);
+ goto autodetect_max_fragsize;
+ }
+ }
+ printf("Retrying codec switch...\n");
+ }
+ printf("No reply from server on codec switch. ");
+codec_revert:
+ printf("Falling back to base32\n");
+ dataenc = get_base32_encoder();
+autodetect_max_fragsize:
+ if (autodetect_frag_size) {
+ int proposed_fragsize = 768;
+ int range = 768;
+ max_downstream_frag_size = 0;
+ printf("Autoprobing max downstream fragment size... (skip with -m fragsize)\n");
+ while (running && range > 0 && (range >= 8 || !max_downstream_frag_size)) {
+ for (i=0; running && i<3 ;i++) {
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ send_fragsize_probe(dns_fd, proposed_fragsize);
+
+ FD_ZERO(&fds);
+ FD_SET(dns_fd, &fds);
+
+ r = select(dns_fd + 1, &fds, NULL, NULL, &tv);
+
+ if(r > 0) {
+ read = read_dns(dns_fd, in, sizeof(in));
+
+ if (read > 0) {
+ /* We got a reply */
+ int acked_fragsize = ((in[0] & 0xff) << 8) | (in[1] & 0xff);
+ if (acked_fragsize == proposed_fragsize) {
+ printf("%d ok.. ", acked_fragsize);
+ fflush(stdout);
+ max_downstream_frag_size = acked_fragsize;
+ range >>= 1;
+ proposed_fragsize += range;
+ continue;
+ }
+ }
+ }
+ }
+ printf("%d not ok.. ", proposed_fragsize);
+ fflush(stdout);
+ range >>= 1;
+ proposed_fragsize -= range;
+ }
+ if (!running) {
+ printf("\n");
+ warnx("stopped while autodetecting fragment size (Try probing manually with -m)");
+ return 1;
+ }
+ if (range == 0) {
+ /* Tried all the way down to 2 and found no good size */
+ printf("\n");
+ warnx("found no accepted fragment size. (Try probing manually with -m)");
+ return 1;
+ }
+ printf("will use %d\n", max_downstream_frag_size);
+ }
+ printf("Setting downstream fragment size to max %d...\n", max_downstream_frag_size);
+ for (i=0; running && i<5 ;i++) {
+ tv.tv_sec = i + 1;
+ tv.tv_usec = 0;
+
+ send_set_downstream_fragsize(dns_fd, max_downstream_frag_size);
+
+ FD_ZERO(&fds);
+ FD_SET(dns_fd, &fds);
+
+ r = select(dns_fd + 1, &fds, NULL, NULL, &tv);
+
+ if(r > 0) {
+ read = read_dns(dns_fd, in, sizeof(in));
+
+ if (read > 0) {
+ int accepted_fragsize;
+
+ if (strncmp("BADFRAG", in, 7) == 0) {
+ printf("Server rejected fragsize. Keeping default.");
+ goto done;
+ } else if (strncmp("BADIP", in, 5) == 0) {
+ printf("Server rejected sender IP address.\n");
+ goto done;
+ }
+
+ accepted_fragsize = ((in[0] & 0xff) << 8) | (in[1] & 0xff);
+ goto done;
+ }
+ }
+ printf("Retrying set fragsize...\n");
+ }
+ printf("No reply from server when setting fragsize. Keeping default.\n");
+done:
return 0;
}
-
+
static char *
get_resolvconf_addr()
{
extern char *__progname;
printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] "
- "[nameserver] topdomain\n", __progname);
+ "[-P password] [-m maxfragsize] [nameserver] topdomain\n", __progname);
exit(2);
}
printf("iodine IP over DNS tunneling client\n");
printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] "
- "[-P password] [nameserver] topdomain\n", __progname);
+ "[-P password] [-m maxfragsize] [nameserver] topdomain\n", __progname);
printf(" -v to print version info and exit\n");
printf(" -h to print this help and exit\n");
printf(" -f to keep running in foreground\n");
printf(" -t dir to chroot to directory dir\n");
printf(" -d device to set tunnel device name\n");
printf(" -P password used for authentication (max 32 chars will be used)\n");
+ printf(" -m maxfragsize, to limit size of downstream packets\n");
printf("nameserver is the IP number of the relaying nameserver, if absent /etc/resolv.conf is used\n");
printf("topdomain is the FQDN that is delegated to the tunnel endpoint.\n");
static void
version() {
-
printf("iodine IP over DNS tunneling client\n");
- printf("version: 0.4.2 from 2008-08-06\n");
+ printf("version: 0.5.0 from 2009-01-23\n");
exit(0);
}
device = NULL;
chunkid = 0;
+ outpkt.seqno = 0;
+ inpkt.len = 0;
+
+ autodetect_frag_size = 1;
+ max_downstream_frag_size = 3072;
+
b32 = get_base32_encoder();
dataenc = get_base32_encoder();
__progname++;
#endif
- while ((choice = getopt(argc, argv, "vfhu:t:d:P:")) != -1) {
+ while ((choice = getopt(argc, argv, "vfhu:t:d:P:m:")) != -1) {
switch(choice) {
case 'v':
version();
+ /* NOTREACHED */
break;
case 'f':
foreground = 1;
break;
case 'h':
help();
+ /* NOTREACHED */
break;
case 'u':
username = optarg;
/* XXX: find better way of cleaning up ps(1) */
memset(optarg, 0, strlen(optarg));
break;
+ case 'm':
+ autodetect_frag_size = 0;
+ max_downstream_frag_size = atoi(optarg);
+ break;
default:
usage();
/* NOTREACHED */
if (geteuid() != 0) {
warnx("Run as root and you'll be happy.\n");
usage();
+ /* NOTREACHED */
}
argc -= optind;
/* NOTREACHED */
}
+ if (max_downstream_frag_size < 1 || max_downstream_frag_size > 0xffff) {
+ warnx("Use a max frag size between 1 and 65535 bytes.\n");
+ usage();
+ /* NOTREACHED */
+ }
+
set_nameserver(nameserv_addr);
if(strlen(topdomain) <= 128) {
if(check_topdomain(topdomain)) {
warnx("Topdomain contains invalid characters.\n");
usage();
+ /* NOTREACHED */
}
} else {
warnx("Use a topdomain max 128 chars long.\n");
usage();
+ /* NOTREACHED */
}
if (username != NULL) {
if ((pw = getpwnam(username)) == NULL) {
warnx("User %s does not exist!\n", username);
usage();
+ /* NOTREACHED */
}
}
if (setgroups(1, gids) < 0 || setgid(pw->pw_gid) < 0 || setuid(pw->pw_uid) < 0) {
warnx("Could not switch to user %s!\n", username);
usage();
+ /* NOTREACHED */
}
}
+ downstream_seqno = 0;
+ downstream_fragment = 0;
+ down_ack_seqno = 0;
+ down_ack_fragment = 0;
+
tunnel(tun_fd, dns_fd);
cleanup2:
/*
- * Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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
#include <sys/types.h>
#include <sys/param.h>
#include <sys/time.h>
+#define _XPG4_2
#include <sys/socket.h>
+#include <sys/uio.h>
#include <fcntl.h>
#include <err.h>
#include <grp.h>
#include "dns.h"
#include "encoding.h"
#include "base32.h"
+#include "base64.h"
#include "user.h"
#include "login.h"
#include "tun.h"
+#include "fw_query.h"
#include "version.h"
static int running = 1;
static char *topdomain;
static char password[33];
static struct encoder *b32;
+static int created_users;
static int check_ip;
static int my_mtu;
static in_addr_t my_ip;
+static int netmask;
+static in_addr_t ns_ip;
+
+static int bind_port;
static int debug;
#if !defined(BSD) && !defined(__GLIBC__)
}
static int
-ip_cmp(int userid, struct query *q)
+check_user_and_ip(int userid, struct query *q)
{
struct sockaddr_in *tempin;
+ if (userid < 0 || userid >= created_users ) {
+ return 1;
+ }
+ if (!users[userid].active) {
+ return 1;
+ }
+
+ /* return early if IP checking is disabled */
+ if (!check_ip) {
+ return 0;
+ }
+
tempin = (struct sockaddr_in *) &(q->from);
return memcmp(&(users[userid].host), &(tempin->sin_addr), sizeof(struct in_addr));
}
if (users[userid].outpacket.len == 0) {
memcpy(users[userid].outpacket.data, out, outlen);
users[userid].outpacket.len = outlen;
+ users[userid].outpacket.offset = 0;
+ users[userid].outpacket.sentlen = 0;
+ users[userid].outpacket.seqno = (++users[userid].outpacket.seqno & 7);
+ users[userid].outpacket.fragment = 0;
return outlen;
} else {
return 0;
} version_ack_t;
static void
-send_version_response(int fd, version_ack_t ack, uint32_t payload, struct user *u)
+send_version_response(int fd, version_ack_t ack, uint32_t payload, int userid, struct query *q)
{
char out[9];
out[5] = ((payload >> 16) & 0xff);
out[6] = ((payload >> 8) & 0xff);
out[7] = ((payload) & 0xff);
- if (u) {
- out[8] = u->id;
+ out[8] = userid & 0xff;
+
+ write_dns(fd, q, out, sizeof(out));
+}
+
+static void
+send_chunk(int dns_fd, int userid) {
+ char pkt[4096];
+ int datalen;
+ int last;
+
+ datalen = MIN(users[userid].fragsize, users[userid].outpacket.len - users[userid].outpacket.offset);
+
+ if (datalen && users[userid].outpacket.sentlen > 0 &&
+ (
+ users[userid].outpacket.seqno != users[userid].out_acked_seqno ||
+ users[userid].outpacket.fragment != users[userid].out_acked_fragment
+ )
+ ) {
+
+ /* Still waiting on latest ack, send nothing */
+ datalen = 0;
+ last = 0;
+ /* TODO : count down and discard packet if no acks arrive within X queries */
} else {
- out[8] = 0;
+ memcpy(&pkt[2], &users[userid].outpacket.data[users[userid].outpacket.offset], datalen);
+ users[userid].outpacket.sentlen = datalen;
+ last = (users[userid].outpacket.len == users[userid].outpacket.offset + users[userid].outpacket.sentlen);
+
+ /* Increase fragment# when sending data with offset */
+ if (users[userid].outpacket.offset && datalen)
+ users[userid].outpacket.fragment++;
+ }
+
+ /* Build downstream data header (see doc/proto_xxxxxxxx.txt) */
+
+ /* First byte is 1 bit compression flag, 3 bits upstream seqno, 4 bits upstream fragment */
+ pkt[0] = (1<<7) | ((users[userid].inpacket.seqno & 7) << 4) | (users[userid].inpacket.fragment & 15);
+ /* Second byte is 3 bits downstream seqno, 4 bits downstream fragment, 1 bit last flag */
+ pkt[1] = ((users[userid].outpacket.seqno & 7) << 5) |
+ ((users[userid].outpacket.fragment & 15) << 1) | (last & 1);
+
+ if (debug >= 1) {
+ printf("OUT pkt seq# %d, frag %d (last=%d), offset %d, fragsize %d, total %d, to user %d\n",
+ users[userid].outpacket.seqno & 7, users[userid].outpacket.fragment & 15,
+ last, users[userid].outpacket.offset, datalen, users[userid].outpacket.len, userid);
+ }
+ write_dns(dns_fd, &users[userid].q, pkt, datalen + 2);
+ users[userid].q.id = 0;
+
+ if (users[userid].outpacket.len > 0 &&
+ users[userid].outpacket.len == users[userid].outpacket.sentlen) {
+
+ /* Whole packet was sent in one chunk, dont wait for ack */
+ users[userid].outpacket.len = 0;
+ users[userid].outpacket.offset = 0;
+ users[userid].outpacket.sentlen = 0;
}
+}
+
+static void
+update_downstream_seqno(int dns_fd, int userid, int down_seq, int down_frag)
+{
+ /* If we just read a new packet from tun we have not sent a fragment of, just send it */
+ if (users[userid].outpacket.len > 0 && users[userid].outpacket.sentlen == 0) {
+ send_chunk(dns_fd, userid);
+ return;
+ }
+
+ /* otherwise, check if we received ack on a fragment and can send next */
+ if (users[userid].outpacket.len > 0 &&
+ users[userid].outpacket.seqno == down_seq && users[userid].outpacket.fragment == down_frag) {
+ if (down_seq != users[userid].out_acked_seqno || down_frag != users[userid].out_acked_fragment) {
+ /* Received ACK on downstream fragment */
+ users[userid].outpacket.offset += users[userid].outpacket.sentlen;
+ users[userid].outpacket.sentlen = 0;
- write_dns(fd, &u->q, out, sizeof(out));
+ /* Is packet done? */
+ if (users[userid].outpacket.offset == users[userid].outpacket.len) {
+ users[userid].outpacket.len = 0;
+ users[userid].outpacket.offset = 0;
+ users[userid].outpacket.sentlen = 0;
+ }
+
+ users[userid].out_acked_seqno = down_seq;
+ users[userid].out_acked_fragment = down_frag;
+
+ /* Send reply if waiting */
+ if (users[userid].outpacket.len > 0) {
+ send_chunk(dns_fd, userid);
+ }
+ }
+ }
}
static void
-handle_null_request(int tun_fd, int dns_fd, struct query *q)
+handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len)
{
struct in_addr tempip;
struct ip *hdr;
unsigned long outlen;
- char in[64*1024];
+ char in[512];
char logindata[16];
char out[64*1024];
char unpacked[64*1024];
char *tmp[2];
- char *domain;
int userid;
int touser;
int version;
int read;
userid = -1;
- domain = strstr(q->name, topdomain);
- if (!domain) {
- /* Not for us, discard */
- return;
- }
-
- read = (int) (domain - q->name);
- memcpy(in, q->name, MIN(read, sizeof(in)));
+
+ memcpy(in, q->name, MIN(domain_len, sizeof(in)));
if(in[0] == 'V' || in[0] == 'v') {
- read = unpack_data(unpacked, sizeof(unpacked), &(in[1]), read - 1, b32);
+ read = unpack_data(unpacked, sizeof(unpacked), &(in[1]), domain_len - 1, b32);
/* Version greeting, compare and send ack/nak */
if (read > 4) {
/* Received V + 32bits version */
memcpy(&(users[userid].q), q, sizeof(struct query));
users[userid].encoder = get_base32_encoder();
- send_version_response(dns_fd, VERSION_ACK, users[userid].seed, &users[userid]);
+ send_version_response(dns_fd, VERSION_ACK, users[userid].seed, userid, q);
users[userid].q.id = 0;
} else {
/* No space for another user */
- send_version_response(dns_fd, VERSION_FULL, USERS, NULL);
+ send_version_response(dns_fd, VERSION_FULL, created_users, 0, q);
}
} else {
- send_version_response(dns_fd, VERSION_NACK, VERSION, NULL);
+ send_version_response(dns_fd, VERSION_NACK, VERSION, 0, q);
}
+ return;
} else if(in[0] == 'L' || in[0] == 'l') {
- read = unpack_data(unpacked, sizeof(unpacked), &(in[1]), read - 1, b32);
+ read = unpack_data(unpacked, sizeof(unpacked), &(in[1]), domain_len - 1, b32);
/* Login phase, handle auth */
userid = unpacked[0];
- if (userid < 0 || userid >= USERS) {
- write_dns(dns_fd, q, "BADIP", 5);
- return; /* illegal id */
- }
- users[userid].last_pkt = time(NULL);
- login_calculate(logindata, 16, password, users[userid].seed);
- if (check_ip && ip_cmp(userid, q) != 0) {
+ if (check_user_and_ip(userid, q) != 0) {
write_dns(dns_fd, q, "BADIP", 5);
+ return;
} else {
+ users[userid].last_pkt = time(NULL);
+ login_calculate(logindata, 16, password, users[userid].seed);
+
if (read >= 18 && (memcmp(logindata, unpacked+1, 16) == 0)) {
- /* Login ok, send ip/mtu info */
+ /* Login ok, send ip/mtu/netmask info */
tempip.s_addr = my_ip;
tmp[0] = strdup(inet_ntoa(tempip));
tempip.s_addr = users[userid].tun_ip;
tmp[1] = strdup(inet_ntoa(tempip));
- read = snprintf(out, sizeof(out), "%s-%s-%d",
- tmp[0], tmp[1], my_mtu);
+ read = snprintf(out, sizeof(out), "%s-%s-%d-%d",
+ tmp[0], tmp[1], my_mtu, netmask);
write_dns(dns_fd, q, out, read);
q->id = 0;
write_dns(dns_fd, q, "LNAK", 4);
}
}
+ return;
+ } else if(in[0] == 'Z' || in[0] == 'z') {
+ /* Check for case conservation and chars not allowed according to RFC */
+
+ /* Reply with received hostname as data */
+ write_dns(dns_fd, q, in, domain_len);
+ return;
+ } else if(in[0] == 'S' || in[0] == 's') {
+ int codec;
+ struct encoder *enc;
+ if (domain_len != 4) { /* len = 4, example: "S15." */
+ write_dns(dns_fd, q, "BADLEN", 6);
+ return;
+ }
+
+ userid = b32_8to5(in[1]);
+
+ if (check_user_and_ip(userid, q) != 0) {
+ write_dns(dns_fd, q, "BADIP", 5);
+ return; /* illegal id */
+ }
+
+ codec = b32_8to5(in[2]);
+
+ switch (codec) {
+ case 5: /* 5 bits per byte = base32 */
+ enc = get_base32_encoder();
+ user_switch_codec(userid, enc);
+ write_dns(dns_fd, q, enc->name, strlen(enc->name));
+ break;
+ case 6: /* 6 bits per byte = base64 */
+ enc = get_base64_encoder();
+ user_switch_codec(userid, enc);
+ write_dns(dns_fd, q, enc->name, strlen(enc->name));
+ break;
+ default:
+ write_dns(dns_fd, q, "BADCODEC", 8);
+ break;
+ }
+ return;
+ } else if(in[0] == 'R' || in[0] == 'r') {
+ int req_frag_size;
+
+ /* Downstream fragsize probe packet */
+ userid = (b32_8to5(in[1]) >> 1) & 15;
+ if (check_user_and_ip(userid, q) != 0) {
+ write_dns(dns_fd, q, "BADIP", 5);
+ return; /* illegal id */
+ }
+
+ req_frag_size = ((b32_8to5(in[1]) & 1) << 10) | ((b32_8to5(in[2]) & 31) << 5) | (b32_8to5(in[3]) & 31);
+ if (req_frag_size < 2 || req_frag_size > 2047) {
+ write_dns(dns_fd, q, "BADFRAG", 7);
+ } else {
+ char buf[2048];
+
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (req_frag_size >> 8) & 0xff;
+ buf[1] = req_frag_size & 0xff;
+ write_dns(dns_fd, q, buf, req_frag_size);
+ }
+ return;
+ } else if(in[0] == 'N' || in[0] == 'n') {
+ int max_frag_size;
+
+ read = unpack_data(unpacked, sizeof(unpacked), &(in[1]), domain_len - 1, b32);
+ /* Downstream fragsize packet */
+ userid = unpacked[0];
+ if (check_user_and_ip(userid, q) != 0) {
+ write_dns(dns_fd, q, "BADIP", 5);
+ return; /* illegal id */
+ }
+
+ max_frag_size = ((unpacked[1] & 0xff) << 8) | (unpacked[2] & 0xff);
+ if (max_frag_size < 2) {
+ write_dns(dns_fd, q, "BADFRAG", 7);
+ } else {
+ users[userid].fragsize = max_frag_size;
+ write_dns(dns_fd, q, &unpacked[1], 2);
+ }
+ return;
} else if(in[0] == 'P' || in[0] == 'p') {
- read = unpack_data(unpacked, sizeof(unpacked), &(in[1]), read - 1, b32);
+ int dn_seq;
+ int dn_frag;
+
+ read = unpack_data(unpacked, sizeof(unpacked), &(in[1]), domain_len - 1, b32);
/* Ping packet, store userid */
userid = unpacked[0];
- if (userid < 0 || userid >= USERS || ip_cmp(userid, q) != 0) {
+ if (check_user_and_ip(userid, q) != 0) {
write_dns(dns_fd, q, "BADIP", 5);
return; /* illegal id */
}
+
+ if (debug >= 1) {
+ printf("PING pkt from user %d\n", userid);
+ }
+
+ if (users[userid].q.id != 0) {
+ /* Send reply on earlier query before overwriting */
+ send_chunk(dns_fd, userid);
+ }
+
+ dn_seq = unpacked[1] >> 4;
+ dn_frag = unpacked[1] & 15;
memcpy(&(users[userid].q), q, sizeof(struct query));
users[userid].last_pkt = time(NULL);
- } else if(in[0] == 'Z' || in[0] == 'z') {
- /* Case conservation check */
- /* Reply with received hostname as data */
- write_dns(dns_fd, q, in, read);
- return;
+ /* Update seqno and maybe send immediate response packet */
+ update_downstream_seqno(dns_fd, userid, dn_seq, dn_frag);
} else if((in[0] >= '0' && in[0] <= '9')
|| (in[0] >= 'a' && in[0] <= 'f')
|| (in[0] >= 'A' && in[0] <= 'F')) {
if ((in[0] >= 'A' && in[0] <= 'F'))
code = in[0] - 'A' + 10;
- userid = code >> 1;
- if (userid < 0 || userid >= USERS) {
- write_dns(dns_fd, q, "BADIP", 5);
- return; /* illegal id */
- }
-
- /* Check sending ip number */
- if (check_ip && ip_cmp(userid, q) != 0) {
+ userid = code;
+ /* Check user and sending ip number */
+ if (check_user_and_ip(userid, q) != 0) {
write_dns(dns_fd, q, "BADIP", 5);
} else {
- /* decode with this users encoding */
- read = unpack_data(unpacked, sizeof(unpacked), &(in[1]), read - 1,
- users[userid].encoder);
+ /* Decode data header */
+ int up_seq = (b32_8to5(in[1]) >> 2) & 7;
+ int up_frag = ((b32_8to5(in[1]) & 3) << 2) | ((b32_8to5(in[2]) >> 3) & 3);
+ int dn_seq = (b32_8to5(in[2]) & 7);
+ int dn_frag = b32_8to5(in[3]) >> 1;
+ int lastfrag = b32_8to5(in[3]) & 1;
+
+ if (users[userid].q.id != 0) {
+ /* Send reply on earlier query before overwriting */
+ send_chunk(dns_fd, userid);
+ }
+ /* Update query and time info for user */
users[userid].last_pkt = time(NULL);
memcpy(&(users[userid].q), q, sizeof(struct query));
+
+ if (up_seq == users[userid].inpacket.seqno &&
+ up_frag <= users[userid].inpacket.fragment) {
+ /* Got repeated old packet, skip it */
+ if (debug >= 1) {
+ printf("IN pkt seq# %d, frag %d, dropped duplicate\n",
+ up_seq, up_frag);
+ }
+ /* Update seqno and maybe send immediate response packet */
+ update_downstream_seqno(dns_fd, userid, dn_seq, dn_frag);
+ return;
+ }
+ if (up_seq != users[userid].inpacket.seqno) {
+ /* New packet has arrived */
+ users[userid].inpacket.seqno = up_seq;
+ users[userid].inpacket.len = 0;
+ users[userid].inpacket.offset = 0;
+ }
+ users[userid].inpacket.fragment = up_frag;
+
+ /* decode with this users encoding */
+ read = unpack_data(unpacked, sizeof(unpacked), &(in[4]), domain_len - 4,
+ users[userid].encoder);
+
+ /* copy to packet buffer, update length */
memcpy(users[userid].inpacket.data + users[userid].inpacket.offset, unpacked, read);
users[userid].inpacket.len += read;
users[userid].inpacket.offset += read;
- if (code & 1) {
- outlen = sizeof(out);
- uncompress((uint8_t*)out, &outlen,
- (uint8_t*)users[userid].inpacket.data, users[userid].inpacket.len);
-
- hdr = (struct ip*) (out + 4);
- touser = find_user_by_ip(hdr->ip_dst.s_addr);
+ if (debug >= 1) {
+ printf("IN pkt seq# %d, frag %d (last=%d), fragsize %d, total %d, from user %d\n",
+ up_seq, up_frag, lastfrag, read, users[userid].inpacket.len, userid);
+ }
- if (touser == -1) {
- /* send the uncompressed packet to tun device */
- write_tun(tun_fd, out, outlen);
- } else {
- /* send the compressed packet to other client
- * if another packet is queued, throw away this one. TODO build queue */
- if (users[touser].outpacket.len == 0) {
- memcpy(users[touser].outpacket.data, users[userid].inpacket.data, users[userid].inpacket.len);
- users[touser].outpacket.len = users[userid].inpacket.len;
+ if (lastfrag & 1) { /* packet is complete */
+ int ret;
+ outlen = sizeof(out);
+ ret = uncompress((uint8_t*)out, &outlen,
+ (uint8_t*)users[userid].inpacket.data, users[userid].inpacket.len);
+
+ if (ret == Z_OK) {
+ hdr = (struct ip*) (out + 4);
+ touser = find_user_by_ip(hdr->ip_dst.s_addr);
+
+ if (touser == -1) {
+ /* send the uncompressed packet to tun device */
+ write_tun(tun_fd, out, outlen);
+ } else {
+ /* send the compressed packet to other client
+ * if another packet is queued, throw away this one. TODO build queue */
+ if (users[touser].outpacket.len == 0) {
+ memcpy(users[touser].outpacket.data, users[userid].inpacket.data, users[userid].inpacket.len);
+ users[touser].outpacket.len = users[userid].inpacket.len;
+ }
}
+ } else {
+ printf("Discarded data, uncompress() result: %d\n", ret);
}
users[userid].inpacket.len = users[userid].inpacket.offset = 0;
}
+ /* Update seqno and maybe send immediate response packet */
+ update_downstream_seqno(dns_fd, userid, dn_seq, dn_frag);
}
}
- /* userid must be set for a reply to be sent */
- if (userid >= 0 && userid < USERS && ip_cmp(userid, q) == 0 && users[userid].outpacket.len > 0) {
- write_dns(dns_fd, q, users[userid].outpacket.data, users[userid].outpacket.len);
- users[userid].outpacket.len = 0;
- users[userid].q.id = 0;
+}
+
+static void
+handle_ns_request(int dns_fd, struct query *q)
+{
+ char buf[64*1024];
+ int len;
+
+ if (ns_ip != INADDR_ANY) {
+ memcpy(&q->destination.s_addr, &ns_ip, sizeof(in_addr_t));
+ }
+
+ len = dns_encode_ns_response(buf, sizeof(buf), q, topdomain);
+
+ if (debug >= 2) {
+ struct sockaddr_in *tempin;
+ tempin = (struct sockaddr_in *) &(q->from);
+ printf("TX: client %s, type %d, name %s, %d bytes NS reply\n",
+ inet_ntoa(tempin->sin_addr), q->type, q->name, len);
+ }
+ if (sendto(dns_fd, buf, len, 0, (struct sockaddr*)&q->from, q->fromlen) <= 0) {
+ warn("ns reply send error");
+ }
+}
+
+static void
+forward_query(int bind_fd, struct query *q)
+{
+ char buf[64*1024];
+ int len;
+ struct fw_query fwq;
+ struct sockaddr_in *myaddr;
+ in_addr_t newaddr;
+
+ len = dns_encode(buf, sizeof(buf), q, QR_QUERY, q->name, strlen(q->name));
+
+ /* Store sockaddr for q->id */
+ memcpy(&(fwq.addr), &(q->from), q->fromlen);
+ fwq.addrlen = q->fromlen;
+ fwq.id = q->id;
+ fw_query_put(&fwq);
+
+ newaddr = inet_addr("127.0.0.1");
+ myaddr = (struct sockaddr_in *) &(q->from);
+ memcpy(&(myaddr->sin_addr), &newaddr, sizeof(in_addr_t));
+ myaddr->sin_port = htons(bind_port);
+
+ if (debug >= 2) {
+ printf("TX: NS reply \n");
+ }
+
+ if (sendto(bind_fd, buf, len, 0, (struct sockaddr*)&q->from, q->fromlen) <= 0) {
+ warn("forward query error");
+ }
+}
+
+static int
+tunnel_bind(int bind_fd, int dns_fd)
+{
+ char packet[64*1024];
+ struct sockaddr_in from;
+ socklen_t fromlen;
+ struct fw_query *query;
+ short id;
+ int r;
+
+ fromlen = sizeof(struct sockaddr);
+ r = recvfrom(bind_fd, packet, sizeof(packet), 0,
+ (struct sockaddr*)&from, &fromlen);
+
+ if (r <= 0)
+ return 0;
+
+ id = dns_get_id(packet, r);
+
+ if (debug >= 2) {
+ printf("RX: Got response on query %u from DNS\n", (id & 0xFFFF));
+ }
+
+ /* Get sockaddr from id */
+ fw_query_get(id, &query);
+ if (!query && debug >= 2) {
+ printf("Lost sender of id %u, dropping reply\n", (id & 0xFFFF));
+ return 0;
+ }
+
+ if (debug >= 2) {
+ struct sockaddr_in *in;
+ in = (struct sockaddr_in *) &(query->addr);
+ printf("TX: client %s id %u, %d bytes\n",
+ inet_ntoa(in->sin_addr), (id & 0xffff), r);
+ }
+
+ if (sendto(dns_fd, packet, r, 0, (const struct sockaddr *) &(query->addr),
+ query->addrlen) <= 0) {
+ warn("forward reply error");
}
+
+ return 0;
}
static int
-tunnel_dns(int tun_fd, int dns_fd)
+tunnel_dns(int tun_fd, int dns_fd, int bind_fd)
{
struct query q;
int read;
+ char *domain;
+ int domain_len;
+ int inside_topdomain;
if ((read = read_dns(dns_fd, &q)) <= 0)
return 0;
- if (debug >= 1) {
+ if (debug >= 2) {
struct sockaddr_in *tempin;
tempin = (struct sockaddr_in *) &(q.from);
- printf("RX: client %s, type %d, name %s\n", inet_ntoa(tempin->sin_addr), q.type, q.name);
+ printf("RX: client %s, type %d, name %s\n",
+ inet_ntoa(tempin->sin_addr), q.type, q.name);
}
- switch (q.type) {
- case T_NULL:
- handle_null_request(tun_fd, dns_fd, &q);
- break;
- default:
- break;
+ domain = strstr(q.name, topdomain);
+ inside_topdomain = 0;
+ if (domain) {
+ domain_len = (int) (domain - q.name);
+ if (domain_len + strlen(topdomain) == strlen(q.name)) {
+ inside_topdomain = 1;
+ }
+ }
+
+ if (inside_topdomain) {
+ /* This is a query we can handle */
+ switch (q.type) {
+ case T_NULL:
+ handle_null_request(tun_fd, dns_fd, &q, domain_len);
+ break;
+ case T_NS:
+ handle_ns_request(dns_fd, &q);
+ break;
+ default:
+ break;
+ }
+ } else {
+ /* Forward query to other port ? */
+ if (bind_fd) {
+ forward_query(bind_fd, &q);
+ }
}
-
return 0;
}
static int
-tunnel(int tun_fd, int dns_fd)
+tunnel(int tun_fd, int dns_fd, int bind_fd)
{
struct timeval tv;
fd_set fds;
int i;
- int j;
while (running) {
- if (users_waiting_on_reply()) {
- tv.tv_sec = 0;
- tv.tv_usec = 5000;
- } else {
- tv.tv_sec = 1;
- tv.tv_usec = 0;
- }
+ int maxfd;
+ if (users_waiting_on_reply()) {
+ tv.tv_sec = 0;
+ tv.tv_usec = 15000;
+ } else {
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ }
FD_ZERO(&fds);
+
+ FD_SET(dns_fd, &fds);
+ maxfd = dns_fd;
+
+ if (bind_fd) {
+ /* wait for replies from real DNS */
+ FD_SET(bind_fd, &fds);
+ maxfd = MAX(bind_fd, maxfd);
+ }
+
/* TODO : use some kind of packet queue */
if(!all_users_waiting_to_send()) {
FD_SET(tun_fd, &fds);
+ maxfd = MAX(tun_fd, maxfd);
}
- FD_SET(dns_fd, &fds);
- i = select(MAX(tun_fd, dns_fd) + 1, &fds, NULL, NULL, &tv);
+ i = select(maxfd + 1, &fds, NULL, NULL, &tv);
if(i < 0) {
if (running)
warn("select");
return 1;
}
-
- if (i==0) {
- for (j = 0; j < USERS; j++) {
- if (users[j].q.id != 0) {
- write_dns(dns_fd, &(users[j].q), users[j].outpacket.data, users[j].outpacket.len);
- users[j].outpacket.len = 0;
- users[j].q.id = 0;
- }
- }
- } else {
- if(FD_ISSET(tun_fd, &fds)) {
- tunnel_tun(tun_fd, dns_fd);
+
+ if (i==0) {
+ int j;
+ for (j = 0; j < USERS; j++) {
+ if (users[j].q.id != 0) {
+ send_chunk(dns_fd, j);
+ }
+ }
+ } else {
+ if(FD_ISSET(tun_fd, &fds)) {
+ tunnel_tun(tun_fd, dns_fd);
+ continue;
+ }
+ if(FD_ISSET(dns_fd, &fds)) {
+ tunnel_dns(tun_fd, dns_fd, bind_fd);
+ continue;
+ }
+ if(FD_ISSET(bind_fd, &fds)) {
+ tunnel_bind(bind_fd, dns_fd);
continue;
}
- if(FD_ISSET(dns_fd, &fds)) {
- tunnel_dns(tun_fd, dns_fd);
- continue;
- }
}
}
read_dns(int fd, struct query *q)
{
struct sockaddr_in from;
- char packet[64*1024];
socklen_t addrlen;
+ char packet[64*1024];
+ char address[96];
+ struct msghdr msg;
+ struct iovec iov;
+ struct cmsghdr *cmsg;
int r;
addrlen = sizeof(struct sockaddr);
- r = recvfrom(fd, packet, sizeof(packet), 0, (struct sockaddr*)&from, &addrlen);
+ iov.iov_base = packet;
+ iov.iov_len = sizeof(packet);
+
+ msg.msg_name = (caddr_t) &from;
+ msg.msg_namelen = (unsigned) addrlen;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = address;
+ msg.msg_controllen = sizeof(address);
+ msg.msg_flags = 0;
+
+ r = recvmsg(fd, &msg, 0);
if (r > 0) {
dns_decode(NULL, 0, q, QR_QUERY, packet, r);
memcpy((struct sockaddr*)&q->from, (struct sockaddr*)&from, addrlen);
q->fromlen = addrlen;
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+
+ if (cmsg->cmsg_level == IPPROTO_IP &&
+ cmsg->cmsg_type == DSTADDR_SOCKOPT) {
+
+ q->destination = *dstaddr(cmsg);
+ break;
+ }
+ }
return strlen(q->name);
} else if (r < 0) {
len = dns_encode(buf, sizeof(buf), q, QR_ANSWER, data, datalen);
- if (debug >= 1) {
+ if (debug >= 2) {
struct sockaddr_in *tempin;
tempin = (struct sockaddr_in *) &(q->from);
printf("TX: client %s, type %d, name %s, %d bytes data\n",
usage() {
extern char *__progname;
- printf("Usage: %s [-v] [-h] [-c] [-s] [-f] [-D] [-u user] [-t chrootdir] [-d device] [-m mtu] "
- "[-l ip address to listen on] [-p port] [-P password]"
- " tunnel_ip topdomain\n", __progname);
+ printf("Usage: %s [-v] [-h] [-c] [-s] [-f] [-D] [-u user] "
+ "[-t chrootdir] [-d device] [-m mtu] "
+ "[-l ip address to listen on] [-p port] [-n external ip] [-b dnsport] [-P password]"
+ " tunnel_ip[/netmask] topdomain\n", __progname);
exit(2);
}
extern char *__progname;
printf("iodine IP over DNS tunneling server\n");
- printf("Usage: %s [-v] [-h] [-c] [-s] [-f] [-D] [-u user] [-t chrootdir] [-d device] [-m mtu] "
- "[-l ip address to listen on] [-p port] [-P password]"
- " tunnel_ip topdomain\n", __progname);
+ printf("Usage: %s [-v] [-h] [-c] [-s] [-f] [-D] [-u user] "
+ "[-t chrootdir] [-d device] [-m mtu] "
+ "[-l ip address to listen on] [-p port] [-n external ip] [-b dnsport] [-P password]"
+ " tunnel_ip[/netmask] topdomain\n", __progname);
printf(" -v to print version info and exit\n");
printf(" -h to print this help and exit\n");
printf(" -c to disable check of client IP/port on each request\n");
- printf(" -s to skip creating and configuring the tun device which then has to be created manually\n");
+ printf(" -s to skip creating and configuring the tun device, "
+ "which then has to be created manually\n");
printf(" -f to keep running in foreground\n");
printf(" -D to increase debug level\n");
printf(" -u name to drop privileges and run as user 'name'\n");
printf(" -t dir to chroot to directory dir\n");
printf(" -d device to set tunnel device name\n");
printf(" -m mtu to set tunnel device mtu\n");
- printf(" -l ip address to listen on for incoming dns traffic (default 0.0.0.0)\n");
+ printf(" -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(" -n ip to respond with to NS queries\n");
+ printf(" -b port to forward normal DNS queries to (on localhost)\n");
printf(" -P password used for authentication (max 32 chars will be used)\n");
printf("tunnel_ip is the IP number of the local tunnel interface.\n");
+ printf(" /netmask sets the size of the tunnel network.\n");
printf("topdomain is the FQDN that is delegated to this server.\n");
exit(0);
}
static void
version() {
printf("iodine IP over DNS tunneling server\n");
- printf("version: 0.4.2 from 2008-08-06\n");
+ printf("version: 0.5.0 from 2009-01-23\n");
exit(0);
}
char *device;
int dnsd_fd;
int tun_fd;
+
+ /* settings for forwarding normal DNS to
+ * local real DNS server */
+ int bind_fd;
+ int bind_enable;
+
int choice;
int port;
int mtu;
int skipipconfig;
+ char *netsize;
username = NULL;
newroot = NULL;
device = NULL;
foreground = 0;
+ bind_enable = 0;
+ bind_fd = 0;
mtu = 1024;
listen_ip = INADDR_ANY;
port = 53;
+ ns_ip = INADDR_ANY;
check_ip = 1;
skipipconfig = 0;
debug = 0;
+ netmask = 27;
b32 = get_base32_encoder();
memset(password, 0, sizeof(password));
srand(time(NULL));
+ fw_query_init();
- while ((choice = getopt(argc, argv, "vcsfhDu:t:d:m:l:p:P:")) != -1) {
+ while ((choice = getopt(argc, argv, "vcsfhDu:t:d:m:l:p:n:b:P:")) != -1) {
switch(choice) {
case 'v':
version();
case 'p':
port = atoi(optarg);
break;
+ case 'n':
+ ns_ip = inet_addr(optarg);
+ break;
+ case 'b':
+ bind_enable = 1;
+ bind_port = atoi(optarg);
+ break;
case 'P':
strncpy(password, optarg, sizeof(password));
password[sizeof(password)-1] = 0;
if (argc != 2)
usage();
+
+ netsize = strchr(argv[0], '/');
+ if (netsize) {
+ *netsize = 0;
+ netsize++;
+ netmask = atoi(netsize);
+ }
+
+ my_ip = inet_addr(argv[0]);
+
+ if (my_ip == INADDR_NONE) {
+ warnx("Bad IP address to use inside tunnel.\n");
+ usage();
+ }
topdomain = strdup(argv[1]);
if(strlen(topdomain) <= 128) {
usage();
}
+ if(bind_enable) {
+ if (bind_port < 1 || bind_port > 65535 || bind_port == port) {
+ warnx("Bad DNS server port number given.\n");
+ usage();
+ /* NOTREACHED */
+ }
+ printf("Requests for domains outside of %s will be forwarded to port %d\n",
+ topdomain, bind_port);
+ }
+
if (port != 53) {
printf("ALERT! Other dns servers expect you to run on port 53.\n");
printf("You must manually forward port 53 to port %d for things to work.\n", port);
warnx("Bad IP address to listen on.\n");
usage();
}
-
+
+ if (ns_ip == INADDR_NONE) {
+ warnx("Bad IP address to return as nameserver.\n");
+ usage();
+ }
+ if (netmask > 30 || netmask < 8) {
+ warnx("Bad netmask (%d bits). Use 8-30 bits.\n", netmask);
+ usage();
+ }
+
if (strlen(password) == 0)
read_password(password, sizeof(password));
if ((tun_fd = open_tun(device)) == -1)
goto cleanup0;
if (!skipipconfig)
- if (tun_setip(argv[0]) != 0 || tun_setmtu(mtu) != 0)
+ if (tun_setip(argv[0], netmask) != 0 || tun_setmtu(mtu) != 0)
goto cleanup1;
if ((dnsd_fd = open_dns(port, listen_ip)) == -1)
goto cleanup2;
+ if (bind_enable)
+ if ((bind_fd = open_dns(0, INADDR_ANY)) == -1)
+ goto cleanup3;
- my_ip = inet_addr(argv[0]);
my_mtu = mtu;
- init_users(my_ip);
+ created_users = init_users(my_ip, netmask);
+
+ if (created_users < USERS) {
+ printf("Limiting to %d simultaneous users because of netmask /%d\n",
+ created_users, netmask);
+ }
printf("Listening to dns for domain %s\n", topdomain);
if (foreground == 0)
}
}
- tunnel(tun_fd, dnsd_fd);
+ tunnel(tun_fd, dnsd_fd, bind_fd);
+cleanup3:
+ close_dns(bind_fd);
cleanup2:
close_dns(dnsd_fd);
cleanup1:
/*
- * Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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 (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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
SunOS | solaris)
echo '-lsocket -lnsl';
;;
+ BeOS)
+ echo '-lsocket -lbind -lbsd';
+ ;;
+ Haiku)
+ echo '-lnetwork';
+ ;;
esac
;;
+cflags)
+ case `uname` in
+ BeOS)
+ echo '-Dsocklen_t=int';
+ ;;
+ esac
+;;
*)
;;
esac
/*
- * Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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 (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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 (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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
}
int
-tun_setip(const char *ip)
+tun_setip(const char *ip, int netbits)
{
char cmdline[512];
+ int netmask;
+ struct in_addr net;
+ int i;
+
#ifndef LINUX
- int r;
+ int r;
#endif
+ netmask = 0;
+ for (i = 0; i < netbits; i++) {
+ netmask = (netmask << 1) | 1;
+ }
+ netmask <<= (32 - netbits);
+ net.s_addr = htonl(netmask);
if (inet_addr(ip) != INADDR_NONE) {
snprintf(cmdline, sizeof(cmdline),
- "/sbin/ifconfig %s %s %s netmask 255.255.255.0",
+ "/sbin/ifconfig %s %s %s netmask %s",
if_name,
ip,
- ip);
+ ip,
+ inet_ntoa(net));
printf("Setting IP of %s to %s\n", if_name, ip);
#ifndef LINUX
return r;
} else {
snprintf(cmdline, sizeof(cmdline),
- "/sbin/route add %s/24 %s",
- ip, ip);
+ "/sbin/route add %s/%d %s",
+ ip, netbits, ip);
}
- printf("Adding route %s/24 to %s\n", ip, ip);
+ printf("Adding route %s/%d to %s\n", ip, netbits, ip);
#endif
return system(cmdline);
} else {
}
int
-tun_setmtu(const size_t mtu)
+tun_setmtu(const unsigned mtu)
{
char cmdline[512];
/*
- * Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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
void close_tun(int);
int write_tun(int, char *, size_t);
ssize_t read_tun(int, char *, size_t);
-int tun_setip(const char *);
-int tun_setmtu(const size_t);
+int tun_setip(const char *, int);
+int tun_setmtu(const unsigned);
#endif /* _TUN_H_ */
/*
- * Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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
struct user users[USERS];
-void
-init_users(in_addr_t my_ip)
+int
+init_users(in_addr_t my_ip, int netbits)
{
int i;
+ int skip = 0;
char newip[16];
+ int created_users = 0;
+
+ int maxusers;
+
+ in_addr_t netmask = 0;
+ struct in_addr net;
+ struct in_addr ipstart;
+
+ for (i = 0; i < netbits; i++) {
+ netmask = (netmask << 1) | 1;
+ }
+ netmask <<= (32 - netbits);
+ net.s_addr = htonl(netmask);
+ ipstart.s_addr = my_ip & net.s_addr;
+
+ maxusers = (1 << (32-netbits)) - 3; /* 3: Net addr, broadcast addr, iodined addr */
memset(users, 0, USERS * sizeof(struct user));
for (i = 0; i < USERS; i++) {
+ in_addr_t ip;
users[i].id = i;
- snprintf(newip, sizeof(newip), "0.0.0.%d", i + 1);
- users[i].tun_ip = my_ip + inet_addr(newip);;
+ snprintf(newip, sizeof(newip), "0.0.0.%d", i + skip + 1);
+ ip = ipstart.s_addr + inet_addr(newip);
+ if (ip == my_ip && skip == 0) {
+ /* This IP was taken by iodined */
+ skip++;
+ snprintf(newip, sizeof(newip), "0.0.0.%d", i + skip + 1);
+ ip = ipstart.s_addr + inet_addr(newip);
+ }
+ users[i].tun_ip = ip;
+ net.s_addr = ip;
+ if (maxusers-- < 1) {
+ users[i].disabled = 1;
+ } else {
+ users[i].disabled = 0;
+ created_users++;
+ }
users[i].inpacket.len = 0;
users[i].inpacket.offset = 0;
users[i].outpacket.len = 0;
users[i].q.id = 0;
+ users[i].out_acked_seqno = 0;
+ users[i].out_acked_fragment = 0;
+ users[i].fragsize = 4096;
}
+
+ return created_users;
}
int
ret = 0;
for (i = 0; i < USERS; i++) {
- if (users[i].active && users[i].last_pkt + 60 > time(NULL) &&
+ if (users[i].active && !users[i].disabled &&
+ users[i].last_pkt + 60 > time(NULL) &&
users[i].q.id != 0) {
ret++;
}
ret = -1;
for (i = 0; i < USERS; i++) {
- if (users[i].active && users[i].last_pkt + 60 > time(NULL) &&
+ if (users[i].active && !users[i].disabled &&
+ users[i].last_pkt + 60 > time(NULL) &&
ip == users[i].tun_ip) {
ret = i;
break;
ret = 1;
now = time(NULL);
for (i = 0; i < USERS; i++) {
- if (users[i].active && users[i].last_pkt + 60 > now &&
+ if (users[i].active && !users[i].disabled &&
+ users[i].last_pkt + 60 > now &&
users[i].outpacket.len == 0) {
ret = 0;
break;
int i;
for (i = 0; i < USERS; i++) {
/* Not used at all or not used in one minute */
- if (!users[i].active || users[i].last_pkt + 60 < time(NULL)) {
+ if ((!users[i].active || users[i].last_pkt + 60 < time(NULL)) && !users[i].disabled) {
users[i].active = 1;
users[i].last_pkt = time(NULL);
ret = i;
return ret;
}
+void
+user_switch_codec(int userid, struct encoder *enc)
+{
+ if (userid < 0 || userid >= USERS)
+ return;
+
+ users[userid].encoder = enc;
+}
/*
- * Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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
#ifndef __USER_H__
#define __USER_H__
-#define USERS 8
+#define USERS 16
struct user {
char id;
int active;
+ int disabled;
time_t last_pkt;
int seed;
in_addr_t tun_ip;
struct packet inpacket;
struct packet outpacket;
struct encoder *encoder;
+ int out_acked_seqno;
+ int out_acked_fragment;
+ int fragsize;
};
extern struct user users[USERS];
-void init_users(in_addr_t);
+int init_users(in_addr_t, int);
int users_waiting_on_reply();
int find_user_by_ip(uint32_t);
int all_users_waiting_to_send();
int find_available_user();
+void user_switch_codec(int userid, struct encoder *enc);
#endif
/*
- * Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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
/* This is the version of the network protocol
It is usually equal to the latest iodine version number */
-#define VERSION 0x00000402
+#define VERSION 0x00000500
#endif /* _VERSION_H_ */
OS = `uname | tr "a-z" "A-Z"`
-LDFLAGS = -L/usr/local/lib -lcheck
-CFLAGS = -g -Wall -D$(OS) -I../src -I/usr/local/include -pedantic
+CHECK_PATH = /usr/local
+LDFLAGS = -L$(CHECK_PATH)/lib -lcheck `../src/osflags link`
+CFLAGS = -g -Wall -D$(OS) -I../src -I$(CHECK_PATH)/include -pedantic `../src/osflags cflags`
all: $(TEST)
- @./$(TEST)
+ @LD_LIBRARY_PATH=${CHECK_PATH}/lib ./$(TEST)
$(TEST): $(OBJS) $(SRCOBJS)
@echo LD $(TEST)
/*
- * Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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
{
size_t len;
char buf[4096];
+ struct encoder *b32;
int val;
int i;
+ b32 = get_base32_encoder();
+
for (i = 0; testpairs[i].a != NULL; i++) {
len = sizeof(buf);
- val = base32_encode(buf, &len, testpairs[i].a, strlen(testpairs[i].a));
+ val = b32->encode(buf, &len, testpairs[i].a, strlen(testpairs[i].a));
fail_unless(val > 0, strerror(errno));
fail_unless(strcmp(buf, testpairs[i].b) == 0,
{
size_t len;
char buf[4096];
+ struct encoder *b32;
int val;
int i;
+
+ b32 = get_base32_encoder();
for (i = 0; testpairs[i].a != NULL; i++) {
len = sizeof(buf);
- val = base32_decode(buf, &len, testpairs[i].b, strlen(testpairs[i].b));
+ val = b32->decode(buf, &len, testpairs[i].b, strlen(testpairs[i].b));
fail_unless(val > 0, strerror(errno));
fail_unless(buf != NULL, "buf == NULL");
}
END_TEST
+START_TEST(test_base32_5to8_8to5)
+{
+ int i;
+ int c;
+
+ for (i = 0; i < 32; i++) {
+ c = b32_5to8(i);
+ fail_unless(b32_8to5(c) == i);
+ }
+}
+END_TEST
+
TCase *
test_base32_create_tests()
{
tc = tcase_create("Base32");
tcase_add_test(tc, test_base32_encode);
tcase_add_test(tc, test_base32_decode);
+ tcase_add_test(tc, test_base32_5to8_8to5);
return tc;
}
/*
- * Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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
char *b;
} testpairs[] = {
{ "iodinetestingtesting", "Aw8KAw4LDgvZDgLUz2rLC2rPBMC" },
- { "abc123", "ywjJmtiZ" },
+ { "abc1231", "ywjJmtiZmq" },
{
"\xFF\xEF\x7C\xEF\xAE\x78\xDF\x6D\x74\xCF\x2C\x70\xBE\xEB\x6C\xAE\xAA\x68"
"\x9E\x69\x64\x8E\x28\x60\x7D\xE7\x5C\x6D\xA6\x58\x5D\x65\x54\x4D\x24\x50"
"\xBE\xEB\x6C\xAE\xAA\x68\x9E\x69\x64\x8E\x28\x60\x7D\xE7\x5C\x6D\xA6\x58"
"\x5D\x65\x54\x4D\x24\x50\x3C\xE3\x4C\x2C\xA2\x48\x1C\x61\x44\x0C\x20\x40",
- "9abba876543210-ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfe999dcbapZ"
- "776543210-ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba9abba87654"
- "3210-ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfe999dcba"
+ "+9876543210-ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcbapZ"
+ "776543210-ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba+987654"
+ "3210-ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba"
},
{
"\xFF\xEF\x7C\xEF\xAE\x78\xDF\x6D\x74\xCF\x2C\x70\xBE\xEB\x6C\xAE\xAA\x68"
"\xBE\xEB\x6C\xAE\xA1\x61\x91\x61\x61\x81\x28\x60\x7D\xE7\x5C\x6D\xA6\x58"
"\x5D\x65\x54\x4D\x24\x50\x3C\xE3\x4C\x2C\xA2\x48\x1C\x61\x44\x0C\x20\x40",
- "9IJJI876543210-ZYXWVUTSRQPONMLK9LMJIHGFEDCBAzyxwvutsrqponmlkjihgfedcbapZ"
- "776543210-ZYXWVUTSRQfHKwfHGsHGFEDCBAzyxwvutsrqponmlkjihgfedcbaML87654321"
+ "+9876543210-ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcbapZ"
+ "776543210-ZYXWVUTSRQfHKwfHGsHGFEDCBAzyxwvutsrqponmlkjihgfedcba+987654321"
"0-ZYXWVUTSRQfHKwfHGsHGFEDCBAzyxwvutsrqponmlkjihgfedcba"
},
{ NULL, NULL }
{
size_t len;
char buf[4096];
+ struct encoder *b64;
int val;
int i;
+ b64 = get_base64_encoder();
+
for (i = 0; testpairs[i].a != NULL; i++) {
len = sizeof(buf);
- val = base64_encode(buf, &len, testpairs[i].a, strlen(testpairs[i].a));
+ val = b64->encode(buf, &len, testpairs[i].a, strlen(testpairs[i].a));
fail_unless(val > 0, strerror(errno));
fail_unless(strcmp(buf, testpairs[i].b) == 0,
{
size_t len;
char buf[4096];
+ struct encoder *b64;
int val;
int i;
+ b64 = get_base64_encoder();
+
for (i = 0; testpairs[i].a != NULL; i++) {
len = sizeof(buf);
- val = base64_decode(buf, &len, testpairs[i].b, strlen(testpairs[i].b));
+ val = b64->decode(buf, &len, testpairs[i].b, strlen(testpairs[i].b));
fail_unless(val > 0, strerror(errno));
fail_unless(buf != NULL, "buf == NULL");
/*
- * Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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
"\x69\x73\x20\x74\x68\x65\x20\x6D\x65\x73\x73\x61\x67\x65\x20\x74\x6F"
"\x20\x62\x65\x20\x64\x65\x6C\x69\x76\x65\x72\x65\x64";
+static char answerPacketHighTransId[] =
+ "\x85\x39\x84\x00\x00\x01\x00\x01\x00\x00\x00\x00\x05\x73\x69\x6C\x6C"
+ "\x79\x04\x68\x6F\x73\x74\x02\x6F\x66\x06\x69\x6F\x64\x69\x6E\x65\x04"
+ "\x63\x6F\x64\x65\x04\x6B\x72\x79\x6F\x02\x73\x65\x00\x00\x0A\x00\x01"
+ "\xC0\x0C\x00\x0A\x00\x01\x00\x00\x00\x00\x00\x23\x74\x68\x69\x73\x20"
+ "\x69\x73\x20\x74\x68\x65\x20\x6D\x65\x73\x73\x61\x67\x65\x20\x74\x6F"
+ "\x20\x62\x65\x20\x64\x65\x6C\x69\x76\x65\x72\x65\x64";
static char *msgData = "this is the message to be delivered";
static char *topdomain = "kryo.se";
START_TEST(test_decode_response)
{
char buf[512];
+ struct query q;
int len;
int ret;
len = sizeof(buf);
memset(&buf, 0, sizeof(buf));
- ret = dns_decode(buf, len, NULL, QR_ANSWER, answerPacket, sizeof(answerPacket)-1);
+ ret = dns_decode(buf, len, &q, QR_ANSWER, answerPacket, sizeof(answerPacket)-1);
fail_unless(strncmp(msgData, buf, sizeof(msgData)) == 0, "Did not extract expected data");
fail_unless(ret == strlen(msgData), "Bad data length: %d, expected %d", ret, strlen(msgData));
+ fail_unless(q.id == 0x0539);
}
END_TEST
+START_TEST(test_decode_response_with_high_trans_id)
+{
+ char buf[512];
+ struct query q;
+ int len;
+ int ret;
+
+ len = sizeof(buf);
+ memset(&buf, 0, sizeof(buf));
+
+ ret = dns_decode(buf, len, &q, QR_ANSWER, answerPacketHighTransId, sizeof(answerPacketHighTransId)-1);
+ fail_unless(strncmp(msgData, buf, sizeof(msgData)) == 0, "Did not extract expected data");
+ fail_unless(ret == strlen(msgData), "Bad data length: %d, expected %d", ret, strlen(msgData));
+ fail_unless(q.id == 0x8539, "q.id was %08X instead of %08X!", q.id, 0x8539);
+}
+END_TEST
static void
dump_packet(char *buf, size_t len)
{
tcase_add_test(tc, test_decode_query);
tcase_add_test(tc, test_encode_response);
tcase_add_test(tc, test_decode_response);
+ tcase_add_test(tc, test_decode_response_with_high_trans_id);
return tc;
}
/*
- * Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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
char *b;
} dottests[] = {
{ "aaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- "aaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.a"},
- { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."},
+ "aaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaa"},
+ { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."},
+ { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
{ "abc123", "abc123" },
{ NULL, NULL }
};
/*
- * Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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 (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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 (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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
suite_add_tcase(iodine, test);
runner = srunner_create(iodine);
- srunner_run_all(runner, CK_MINIMAL);
+ srunner_run_all(runner, CK_NORMAL);
failed = srunner_ntests_failed(runner);
srunner_free(runner);
/*
- * Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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 (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Copyright (c) 2006-2009 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
int i;
ip = inet_addr("127.0.0.1");
- init_users(ip);
+ init_users(ip, 27);
for (i = 0; i < USERS; i++) {
fail_unless(users[i].id == i);
fail_unless(users[i].q.id == 0);
in_addr_t ip;
ip = inet_addr("127.0.0.1");
- init_users(ip);
+ init_users(ip, 27);
fail_unless(users_waiting_on_reply() == 0);
unsigned int testip;
ip = inet_addr("127.0.0.1");
- init_users(ip);
+ init_users(ip, 27);
testip = (unsigned int) inet_addr("10.0.0.1");
fail_unless(find_user_by_ip(testip) == -1);
in_addr_t ip;
ip = inet_addr("127.0.0.1");
- init_users(ip);
+ init_users(ip, 27);
fail_unless(all_users_waiting_to_send() == 1);
int i;
ip = inet_addr("127.0.0.1");
- init_users(ip);
+ init_users(ip, 27);
for (i = 0; i < USERS; i++) {
fail_unless(find_available_user() == i);
}
END_TEST
+START_TEST(test_find_available_user_small_net)
+{
+ in_addr_t ip;
+ int i;
+
+ ip = inet_addr("127.0.0.1");
+ init_users(ip, 29); /* this should result in 5 enabled users */
+
+ for (i = 0; i < 5; i++) {
+ fail_unless(find_available_user() == i);
+ }
+
+ for (i = 0; i < USERS; i++) {
+ fail_unless(find_available_user() == -1);
+ }
+
+ users[3].active = 0;
+
+ fail_unless(find_available_user() == 3);
+ fail_unless(find_available_user() == -1);
+
+ users[3].last_pkt = 55;
+
+ fail_unless(find_available_user() == 3);
+ fail_unless(find_available_user() == -1);
+}
+END_TEST
+
TCase *
test_user_create_tests()
{
tcase_add_test(tc, test_find_user_by_ip);
tcase_add_test(tc, test_all_users_waiting_to_send);
tcase_add_test(tc, test_find_available_user);
+ tcase_add_test(tc, test_find_available_user_small_net);
return tc;
}