[svn-upgrade] Integrating new upstream version, iodine (0.5.0) upstream/0.5.0
authorgregor herrmann <gregoa@debian.org>
Sat, 24 Jan 2009 23:59:10 +0000 (23:59 -0000)
committergregor herrmann <gregoa@debian.org>
Sat, 24 Jan 2009 23:59:10 +0000 (23:59 -0000)
40 files changed:
CHANGELOG
README
doc/proto_00000402.txt [new file with mode: 0644]
doc/proto_00000500.txt [new file with mode: 0644]
man/iodine.8
src/Makefile
src/base32.c
src/base32.h
src/base64.c
src/base64.h
src/common.c
src/common.h
src/dns.c
src/dns.h
src/encoding.c
src/encoding.h
src/fw_query.c [new file with mode: 0644]
src/fw_query.h [new file with mode: 0644]
src/iodine.c
src/iodined.c
src/login.c
src/login.h
src/osflags
src/read.c
src/read.h
src/tun.c
src/tun.h
src/user.c
src/user.h
src/version.h
tests/Makefile
tests/base32.c
tests/base64.c
tests/dns.c
tests/encoding.c
tests/login.c
tests/read.c
tests/test.c
tests/test.h
tests/user.c

index 15623dcced6d6e9e6bbc147d9143192889585df3..6f7fc936ca0e6767769e1b9b783cfda737254031 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -5,6 +5,29 @@ iodine - http://code.kryo.se/iodine
 
 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,
@@ -12,15 +35,15 @@ CHANGES:
        - 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
diff --git a/README b/README
index 8e449937b5f9669974a17363bfbb838f6874a4f0..4978c6eba32f090cb06a34abc94aa7a76e816d6e 100644 (file)
--- a/README
+++ b/README
@@ -28,9 +28,11 @@ HOW TO USE:
 
 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.
@@ -50,15 +52,14 @@ password on the commandline (-P pass) or after the server has started. Now
 everything is ready for the client.
 
 Client side: 
-All the setup is done, just start iodine. It also takes two
-arguments, the first is the local relaying DNS server and the second is the
-domain used (tunnel1.mytunnnel.com). If DNS queries are allowed to any
-computer, you can use the tunnel endpoint (example: 10.15.213.99 or
-tunnel1host.mytunnel.com) as the first argument. The tunnel interface will get
-an IP close to the servers (in this case 192.168.99.2) and a suitable MTU. 
-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:
@@ -73,10 +74,11 @@ If you have problems, try inspecting the traffic with network monitoring tools
 and make sure that the relaying DNS server has not cached the response. A
 cached error message could mean that you started the client before the server.
 
-The upstream data is sent gzipped encoded with Base32. DNS protocol allows
-one query per packet, and one query can be max 256 chars. Each domain name part
-can be max 63 chars. So your domain name and subdomain should be as short as
-possible to allow maximum throughput.
+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:
@@ -90,11 +92,11 @@ iptables -t nat -A PREROUTING -i eth0 -p udp --dport 53 -j DNAT --to :5353
 
 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:
@@ -111,7 +113,7 @@ THANKS:
 
 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
diff --git a/doc/proto_00000402.txt b/doc/proto_00000402.txt
new file mode 100644 (file)
index 0000000..da36919
--- /dev/null
@@ -0,0 +1,61 @@
+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.
diff --git a/doc/proto_00000500.txt b/doc/proto_00000500.txt
new file mode 100644 (file)
index 0000000..05f100c
--- /dev/null
@@ -0,0 +1,112 @@
+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.
index fdb28fed53d2c0e1e4cdff03df4fadffb04bc67f..9eb88f01a7497e8fbfd38eb5fd0142dee14e7bf7 100644 (file)
@@ -11,6 +11,8 @@ iodine, iodined \- tunnel IPv4 over DNS
 .I user
 .B ] [-P
 .I password
+.B ] [-m
+.I fragsize
 .B ] [-t
 .I chrootdir
 .B ] [-d
@@ -27,18 +29,27 @@ iodine, iodined \- tunnel IPv4 over DNS
 
 .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
@@ -68,14 +79,19 @@ Drop privileges and run as user 'user' after setting up tunnel.
 .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
@@ -100,6 +116,14 @@ connections.
 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
@@ -119,10 +143,12 @@ is the iodined server, then the topdomain can be chosen freely. This argument
 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 
@@ -157,10 +183,11 @@ To actually use it through a relaying nameserver, see below.
 .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
@@ -194,10 +221,8 @@ replace the default gateway with the servers IP address within the DNS tunnel,
 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
index c397f1dac5882f48253075d9a071d932a04c120b..8197c4d12bad78ff54e74214732b8a33f97f7f4a 100644 (file)
@@ -2,14 +2,14 @@ CC = gcc
 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)
 
index d42c5e97a5602a7ec1698b0307cf44e26cd0a937..af6a2147ac1fa5cf8a3141f7d1327089801483c0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -41,18 +52,50 @@ 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;
@@ -60,18 +103,18 @@ base32_encode(char *buf, size_t *buflen, const void *data, size_t size)
        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';
@@ -81,8 +124,8 @@ base32_encode(char *buf, size_t *buflen, const void *data, size_t size)
                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;
 
@@ -133,7 +176,7 @@ decode_token(const unsigned char *t, unsigned char *data, size_t len)
        return 5;
 }
 
-int
+static int
 base32_decode(void *buf, size_t *buflen, const char *str, size_t slen)
 {
        unsigned char *q;
@@ -153,21 +196,21 @@ base32_decode(void *buf, size_t *buflen, const char *str, size_t slen)
        }
        
        /* 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';
index fa9415114ed10329a2ef98976cec1b2f20a6cda9..497ca33e1a47b1956523995de3c742ba1a9ca50f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -18,8 +18,7 @@
 #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
index 512a1997e713dc1a01c1fad7fe6a6af70ba5d563..803d938a6c85e157235ebf913c05b727df046db1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -47,147 +55,25 @@ 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;
@@ -209,9 +95,9 @@ base64_encode(char *buf, size_t *buflen, const void *data, size_t size)
        }
 
        /* 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;
@@ -220,19 +106,17 @@ base64_encode(char *buf, size_t *buflen, const void *data, size_t size)
        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;
 
@@ -265,7 +149,7 @@ decode_token(const unsigned char *t, unsigned char *data, size_t len)
        return 3;
 }
 
-int
+static int
 base64_decode(void *buf, size_t *buflen, const char *str, size_t slen)
 {
        unsigned char *q;
@@ -273,9 +157,7 @@ base64_decode(void *buf, size_t *buflen, const char *str, size_t slen)
        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;
 
@@ -288,47 +170,26 @@ base64_decode(void *buf, size_t *buflen, const char *str, size_t slen)
        }
        
        /* 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';
index 6ee06568a0c3b9986227ad6682a2f7bf16220464..d550cf3de5fbb3774367ae089aea8371d9eb4bc8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -18,8 +18,5 @@
 #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
index f738ce5e79d0c1c21b3a54d82312a960e5f779ce..9b32f844819d721951b2cbb1e52d1c488d504a02 100644 (file)
@@ -1,4 +1,4 @@
-/* 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
@@ -74,6 +74,14 @@ static int daemon(int nochdir, int noclose)
 }
 #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) 
 {
@@ -96,6 +104,9 @@ 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");
 
@@ -113,11 +124,15 @@ close_dns(int fd)
 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
index 255ff248b534fbc0c0efc72d5f71015a06337249..edc8e82f84e6fed57be223498c2c819c2ad6721f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -20,6 +20,7 @@
 #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;
 };
index 5ad4c07eadd4f7e8dcfd84b23b1beb60c96d57f5..df06fd173a4aceb30fac96d9102ed1cc439b813a 100644 (file)
--- a/src/dns.c
+++ b/src/dns.c
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -98,6 +98,97 @@ dns_encode(char *buf, size_t buflen, struct query *q, qr_t qr, char *data, size_
        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)
 {
@@ -131,6 +222,7 @@ dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, siz
        ancount = ntohs(header->ancount);
        
        id = ntohs(header->id);
+       id = id & 0xFFFF; /* Kill any sign extension */
                
        rlen = 0;
 
@@ -177,14 +269,14 @@ dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, siz
                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;
                }
 
index 0ced1f193bd126b48b9910cc334b43a7b945aca2..dc3e429c345e713d58702e7da43df3a582c9defd 100644 (file)
--- a/src/dns.h
+++ b/src/dns.h
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -25,6 +25,8 @@ typedef enum {
 } 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_ */
index 127e87190050b9697cf1c023855ae3b6af9d7fe6..6878e5effd95fec9a91394118febecd1e3b2677d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -35,7 +35,7 @@ inline_dotify(char *buf, size_t buflen)
        char *reader, *writer;
 
        total = strlen(buf);
-       dots = total / 62;
+       dots = total / 57;
 
        writer = buf;
        writer += total;
@@ -52,12 +52,12 @@ inline_dotify(char *buf, size_t buflen)
        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 */
index 4473b2ff973cdd86d3bb03fa61212b852247f58b..dda2c134f0cc41d1088bab234e543ab608efc1fb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -23,6 +23,8 @@ struct encoder {
        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 *);
diff --git a/src/fw_query.c b/src/fw_query.c
new file mode 100644 (file)
index 0000000..d2269bc
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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;
+               }
+       }
+}
diff --git a/src/fw_query.h b/src/fw_query.h
new file mode 100644 (file)
index 0000000..c47da41
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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__*/
+
index 5ec13bbb85f8fed9adc1d3ca1eef68cebb5e21dc..7b1a084307e034fcbd93bb4c3ed51224a3ff57ad 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -40,6 +40,7 @@
 #include "common.h"
 #include "encoding.h"
 #include "base32.h"
+#include "base64.h"
 #include "dns.h"
 #include "login.h"
 #include "tun.h"
@@ -58,9 +59,17 @@ static struct sockaddr_in nameserv;
 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;
@@ -123,10 +132,9 @@ build_hostname(char *buf, size_t buflen,
        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);
        
@@ -146,13 +154,13 @@ build_hostname(char *buf, size_t 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;
@@ -171,17 +179,37 @@ read_dns(int fd, char *buf, int buflen)
 
        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;
@@ -204,10 +232,12 @@ tunnel_tun(int tun_fd, int dns_fd)
        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);
 
@@ -217,21 +247,40 @@ tunnel_tun(int tun_fd, int 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);
        
@@ -252,9 +301,11 @@ tunnel(int tun_fd, int 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);
@@ -291,24 +342,33 @@ send_chunk(int fd)
        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];
@@ -328,24 +388,66 @@ send_login(int fd, char *login, int len)
 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];
@@ -363,15 +465,29 @@ send_version(int fd, uint32_t version)
        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)
 {
@@ -420,18 +536,18 @@ 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");
@@ -462,15 +578,16 @@ perform_login:
                        }
 
                        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 {
@@ -484,8 +601,8 @@ perform_login:
 
                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;
@@ -503,30 +620,30 @@ perform_case_check:
                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;
                        }
                }
 
@@ -534,9 +651,141 @@ perform_case_check:
        }
 
        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()
 {
@@ -583,7 +832,7 @@ usage() {
        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);
 }
 
@@ -593,7 +842,7 @@ help() {
 
        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");
@@ -601,6 +850,7 @@ help() {
        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");
 
@@ -609,9 +859,8 @@ help() {
 
 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);
 }
@@ -636,6 +885,12 @@ main(int argc, char **argv)
        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();
        
@@ -647,16 +902,18 @@ main(int argc, char **argv)
                __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;
@@ -674,6 +931,10 @@ main(int argc, char **argv)
                        /* 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 */
@@ -683,6 +944,7 @@ main(int argc, char **argv)
        if (geteuid() != 0) {
                warnx("Run as root and you'll be happy.\n");
                usage();
+               /* NOTREACHED */
        }
 
        argc -= optind;
@@ -702,22 +964,31 @@ main(int argc, char **argv)
                /* 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 */
                }
        }
        
@@ -749,9 +1020,15 @@ main(int argc, char **argv)
                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:
index d780d19c662603942ae3f03b2e11fb56a5036607..25bddc65f3ca9824bcc589c55ae5c01c16b6ff0e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -23,7 +23,9 @@
 #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__)
@@ -73,10 +82,22 @@ sigint(int sig)
 }
 
 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));
 }
@@ -107,6 +128,10 @@ tunnel_tun(int tun_fd, int dns_fd)
        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;
@@ -120,7 +145,7 @@ typedef enum {
 } 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];
        
@@ -140,28 +165,113 @@ send_version_response(int fd, version_ack_t ack, uint32_t payload, struct user *
        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;
@@ -169,17 +279,11 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q)
        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 */
@@ -201,39 +305,38 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q)
                                
                                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;
@@ -244,22 +347,115 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q)
                                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')) {
@@ -270,132 +466,302 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q)
                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;
-                       } 
                }
        }
 
@@ -406,17 +772,43 @@ static int
 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) { 
@@ -435,7 +827,7 @@ write_dns(int fd, struct query *q, char *data, int datalen)
 
        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", 
@@ -449,9 +841,10 @@ static void
 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);
 }
 
@@ -460,23 +853,29 @@ help() {
        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);
 }
@@ -484,7 +883,7 @@ help() {
 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);
 }
 
@@ -499,21 +898,32 @@ main(int argc, char **argv)
        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();
 
@@ -527,8 +937,9 @@ main(int argc, char **argv)
 
        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();
@@ -566,6 +977,13 @@ main(int argc, char **argv)
                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;
@@ -589,6 +1007,20 @@ main(int argc, char **argv)
 
        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) {
@@ -618,6 +1050,16 @@ main(int argc, char **argv)
                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);
@@ -633,22 +1075,38 @@ main(int argc, char **argv)
                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) 
@@ -667,8 +1125,10 @@ main(int argc, char **argv)
                }
        }
        
-       tunnel(tun_fd, dnsd_fd);
+       tunnel(tun_fd, dnsd_fd, bind_fd);
 
+cleanup3:
+       close_dns(bind_fd);
 cleanup2:
        close_dns(dnsd_fd);
 cleanup1:
index a5cde37e242683e8b1ffceb8a4110d615c827ee3..3aee250316db5c5d29a05ab97f97ee2bea2a430a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
index d0c335ff24438acf312f1ac7b255e2035d032851..df490744975d63a00074f7d0c38658a9326ac527 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
index 0f8665d39bf484608a010435168a59dd736a80e5..2e28ee886ee627b86aa7c596fc727cd259260ca7 100644 (file)
@@ -7,8 +7,21 @@ link)
                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
index 299bc3e3c5106338aa9a3dc7794823e5c15c2529..51accb8451199dce1dff86b76e2e526b4cee4f31 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
index 241447b882f837b773bafb9c27db23a892e14d4d..a66063e0554fe7534c09541f8ed6fbf250e641ee 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
index 144133d53bee4932acd86bff2379544b1e642f49..ba2be65f114255e7ea2c6a7a3603cc9bdd5f96f8 100644 (file)
--- a/src/tun.c
+++ b/src/tun.c
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -181,19 +181,30 @@ read_tun(int tun_fd, char *buf, size_t len)
 }
 
 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
@@ -202,10 +213,10 @@ tun_setip(const char *ip)
                        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 {
@@ -216,7 +227,7 @@ tun_setip(const char *ip)
 }
 
 int 
-tun_setmtu(const size_t mtu)
+tun_setmtu(const unsigned mtu)
 {
        char cmdline[512];
 
index 72773defdcd6df7907b17f0a0e2aabdb14cf43e9..3f99dc0a720793b20e5416b2458ecdec6de0865a 100644 (file)
--- a/src/tun.h
+++ b/src/tun.h
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -21,7 +21,7 @@ int open_tun(const char *);
 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_ */
index 6cbddfb8e1b00bc41b43c355b2656088a2df165f..01b402183572b77237053ec0795b049748f7db4e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -61,7 +98,8 @@ users_waiting_on_reply()
 
        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++;
                }
@@ -78,7 +116,8 @@ find_user_by_ip(uint32_t ip)
 
        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;
@@ -97,7 +136,8 @@ all_users_waiting_to_send()
        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;
@@ -113,7 +153,7 @@ find_available_user()
        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;
@@ -123,3 +163,11 @@ find_available_user()
        return ret;
 }
 
+void
+user_switch_codec(int userid, struct encoder *enc)
+{
+       if (userid < 0 || userid >= USERS)
+               return;
+       
+       users[userid].encoder = enc;
+}
index b333bcb2b7d6effa68047e8b9ebebabd1a7dd532..51fdfbcefbd78cf6ebeb2250a73b3b2f7fb7fa45 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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;
@@ -30,14 +31,18 @@ struct user {
        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
index d12c2f9d62de4985ff1d870d6cbad688ccec8e7d..7856fdee2b4a4d88cdfa93c4212b863f016fbb87 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -19,7 +19,7 @@
 
 /* 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_ */
 
index ef457830c47c283dfc13e9afd99b7307ff8dfad6..79f51f06ca43320e2f28254996bffbbf3d9e7457 100644 (file)
@@ -5,11 +5,12 @@ SRCOBJS = ../src/base32.o  ../src/base64.o ../src/read.o ../src/dns.o ../src/enc
 
 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)
index 849790c76c6c2de807d0d3b8892e997ccef18aa4..a3f95bbfd5fea2db7015818cea388adc8da5fe99 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -38,12 +38,15 @@ START_TEST(test_base32_encode)
 {
        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,
@@ -56,12 +59,15 @@ START_TEST(test_base32_decode)
 {
        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");
@@ -71,6 +77,18 @@ START_TEST(test_base32_decode)
 }
 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()
 {
@@ -79,6 +97,7 @@ 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;
 }
index 451687915a972c4c75ec4e8ec6b821f268b70957..280963ec153a78345b78ac8543c2977fa50cde3e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -30,7 +30,7 @@ static struct tuple
        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"
@@ -41,9 +41,9 @@ static struct tuple
          "\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"
@@ -55,8 +55,8 @@ static struct tuple
          "\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 }
@@ -66,12 +66,15 @@ START_TEST(test_base64_encode)
 {
        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,
@@ -84,12 +87,15 @@ START_TEST(test_base64_decode)
 {
        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");
index 0ccf2329d7dcac7624cbb0cb0749726607c427da..185f15834b0bd42313d41ed6858e7ffcb00b4739 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -48,6 +48,13 @@ static char answerPacket[] =
        "\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";
 
@@ -142,18 +149,36 @@ END_TEST
 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)
 {
@@ -183,6 +208,7 @@ test_dns_create_tests()
        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;
 }
index 767a6971d1d094a596c8b67c7cd9907daf6aa513..a5d04fa2674eaba50bfcfaae1163873944a261f2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -28,9 +28,11 @@ struct tuple
        char *b;
 } dottests[] = {
        { "aaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
-         "aaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.a"},
-       { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
-         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."},
+         "aaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaa"},
+       { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."},
+       { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
        { "abc123", "abc123" },
        { NULL, NULL }
 };
index b698b858d577b69e7655df44b7b7c3512be648fc..2745c92a5478b45ff96573c2d20e2f22c12b39b4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
index e82546a7c55a273d657bf153ad242eefd3610acd..ab55341ef1308187cda33541dd81bf40d4c24217 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
index df354a9c4fe756841eb145d57d58ac2365f74865..8e6359791f7222c427c082d48b29e3338c477b40 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -54,7 +54,7 @@ main()
        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);
index 413ae412b5a738ef361d7be861cf1edef5ee430a..54a3fffa027995bf523c761480866c9251b3628c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
index 7c3f87a25f861a998ff034a34c9d5524a8afb1af..5983717601378f4a612d30052c1f3973a482601e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -34,7 +34,7 @@ START_TEST(test_init_users)
        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);
@@ -51,7 +51,7 @@ START_TEST(test_users_waiting)
        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);
 
@@ -75,7 +75,7 @@ START_TEST(test_find_user_by_ip)
        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);
@@ -100,7 +100,7 @@ START_TEST(test_all_users_waiting_to_send)
        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);
        
@@ -124,7 +124,7 @@ START_TEST(test_find_available_user)
        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);
@@ -146,6 +146,34 @@ START_TEST(test_find_available_user)
 }
 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()
 {
@@ -157,6 +185,7 @@ 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;
 }