summaryrefslogtreecommitdiff
path: root/jni/iodine
diff options
context:
space:
mode:
authorYves Fischer <yvesf-git@xapek.org>2014-01-11 18:44:50 +0100
committerYves Fischer <yvesf-git@xapek.org>2014-01-11 18:48:48 +0100
commit002a2c3e1d0f091a48f8cc3eb7dce519870debaf (patch)
tree64140ef20603bcf66dc33b8f2c5416d006547cb1 /jni/iodine
downloadandiodine-002a2c3e1d0f091a48f8cc3eb7dce519870debaf.tar.gz
andiodine-002a2c3e1d0f091a48f8cc3eb7dce519870debaf.zip
import code
Diffstat (limited to 'jni/iodine')
-rw-r--r--jni/iodine/CHANGELOG142
-rw-r--r--jni/iodine/Makefile62
-rw-r--r--jni/iodine/README357
-rw-r--r--jni/iodine/README-win32.txt62
-rw-r--r--jni/iodine/TODO12
-rw-r--r--jni/iodine/doc/iodine.te25
-rw-r--r--jni/iodine/doc/proto_00000402.txt61
-rw-r--r--jni/iodine/doc/proto_00000500.txt112
-rw-r--r--jni/iodine/doc/proto_00000502.txt287
-rw-r--r--jni/iodine/man/iodine.8338
-rw-r--r--jni/iodine/src/Makefile46
-rw-r--r--jni/iodine/src/base128.c291
-rw-r--r--jni/iodine/src/base128.h22
-rw-r--r--jni/iodine/src/base32.c270
-rw-r--r--jni/iodine/src/base32.h24
-rw-r--r--jni/iodine/src/base64.c205
-rw-r--r--jni/iodine/src/base64.h22
-rw-r--r--jni/iodine/src/client.c2527
-rw-r--r--jni/iodine/src/client.h40
-rw-r--r--jni/iodine/src/common.c381
-rw-r--r--jni/iodine/src/common.h147
-rw-r--r--jni/iodine/src/dns.c610
-rw-r--r--jni/iodine/src/dns.h39
-rw-r--r--jni/iodine/src/dns_android.h625
-rw-r--r--jni/iodine/src/encoding.c130
-rw-r--r--jni/iodine/src/encoding.h43
-rw-r--r--jni/iodine/src/fw_query.c49
-rw-r--r--jni/iodine/src/fw_query.h41
-rw-r--r--jni/iodine/src/iodine.c375
-rw-r--r--jni/iodine/src/iodined.c2486
-rw-r--r--jni/iodine/src/login.c57
-rw-r--r--jni/iodine/src/login.h23
-rw-r--r--jni/iodine/src/md5.c384
-rw-r--r--jni/iodine/src/md5.h91
-rwxr-xr-xjni/iodine/src/osflags36
-rw-r--r--jni/iodine/src/read.c275
-rw-r--r--jni/iodine/src/read.h33
-rw-r--r--jni/iodine/src/tun.c555
-rw-r--r--jni/iodine/src/tun.h37
-rw-r--r--jni/iodine/src/user.c204
-rw-r--r--jni/iodine/src/user.h87
-rw-r--r--jni/iodine/src/util.c69
-rw-r--r--jni/iodine/src/util.h6
-rw-r--r--jni/iodine/src/version.h25
-rw-r--r--jni/iodine/src/windows.h100
-rw-r--r--jni/iodine/tests/Makefile27
-rw-r--r--jni/iodine/tests/base32.c142
-rw-r--r--jni/iodine/tests/base64.c155
-rw-r--r--jni/iodine/tests/dns.c252
-rw-r--r--jni/iodine/tests/encoding.c108
-rw-r--r--jni/iodine/tests/fw_query.c88
-rw-r--r--jni/iodine/tests/login.c70
-rw-r--r--jni/iodine/tests/read.c297
-rw-r--r--jni/iodine/tests/test.c66
-rw-r--r--jni/iodine/tests/test.h37
-rw-r--r--jni/iodine/tests/user.c199
56 files changed, 13254 insertions, 0 deletions
diff --git a/jni/iodine/CHANGELOG b/jni/iodine/CHANGELOG
new file mode 100644
index 0000000..888beac
--- /dev/null
+++ b/jni/iodine/CHANGELOG
@@ -0,0 +1,142 @@
+
+iodine - http://code.kryo.se/iodine
+
+***********************************
+
+CHANGES:
+
+2010-02-13: 0.6.0-rc1 "Hotspotify"
+ - Fixed tunnel not working on Windows.
+ - Any device name is now supported on Windows, fixes #47.
+ - Multiple installed TAP32 interfaces are now supported, fixes #46.
+ - Return nonzero if tunnel fails to open, fixes #62.
+ - Support for setting a SELinux context, based on patch by
+ Sebastien Raveau. Sample context file in doc/iodine.te
+ - Allow listen port and DNS forward port to be the same if listen IP
+ does not include localhost.
+ - The client will now exit if configuring IP or MTU fails.
+ - The starting cache miss value is randomized at startup, fixes #65.
+ - Raw UDP mode added. If the iodined server is reachable directly,
+ packets can be sent to it without DNS encoding. Fixes #36.
+ - Do not overwrite users CC/CFLAGS/LDFLAGS, only add to them.
+ - Added -F option to write pidfile, based on patch from
+ misc at mandriva.org. Fixes #70.
+ - Allow password to be set via environment variable, fixes #77.
+ Based on patch by logix.
+ - Client now prints server tunnel IP, fixes #78. Patch by logix.
+ - Fix build error on Mac OS X 10.6, patch by G. Rischard. #79.
+ - Added support for CNAME/TXT/A/MX query types, fixes #75.
+ Patch by Anne Bezemer, merge help by logix.
+ - Merged low-latency patch from Anne Bezemer, fixes #76.
+ - Resolve client nameserver argument if given as hostname, fixes #82.
+ - Open log before chroot, fixes #86: logging on FreeBSD.
+
+2009-06-01: 0.5.2 "WifiFree"
+ - Fixed client segfault on OS X, #57
+ - Added check that nameserver lookup was successful
+ - Fixed ENOTSOCK error on OS X and FreeBSD, #58.
+
+2009-03-21: 0.5.1 "Boringo"
+ - Added initial Windows support, fixes #43.
+ - Added length check of autoprobe responses
+ - Refactored and added unit tests
+ - Added syslog logging for iodined on version and login packets
+ - Fixed segfault when encoding just one block, fixes #51.
+ The normal code was never affected by this.
+ - Added win32 code to read DNS server from system, fixes #45.
+ - Disabled password echo on win32, fixes #44.
+ - Fix encoding error making all autoprobing > 1024 bytes fail, #52.
+ - Increase default interface MTU to 1200.
+ - Fix autoprobing error making every third probe fail, set IP flag
+ Dont-Fragment where supported. Fixes #54.
+ - Added TAP32 version 0901 as accepted (#53).
+
+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,
+ Debian bug #477692 by Vincent Bernat, controlled by -s switch
+ - 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 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.
+
+2007-11-30: 0.4.1 "Tea Online"
+ - Introduced encoding API
+ - Switched to new Base32 implementation
+ - Added Base64 implementation that only uses 63 chars (not used yet)
+ - Refined 'install' make target and use $(MAKE) for recursive calls
+ - All received error messages (RCODE field) are echoed
+ - Top domain limited to 128 chars
+ - Case preservation check sent after login to decide codec
+ - Fixed crash on incoming NULL query in server with bad top domain
+ - /etc/resolv.conf is consulted if no nameserver is given on commandline
+ - Applied patch from Matthew W. S. Bell (Detach before chroot/dropping priv)
+
+2007-03-25: 0.4.0 "Run Home"
+ - Added multiuser support (up to 8 users simultaneously)
+ - Added authentication (password entered as argument or on stdin)
+ - Added manpage
+ - Added install/uninstall make target
+ - Cleanup of dns code, more test cases, use check library
+ - Changed directory structure
+
+2006-11-08: 0.3.4
+ - Fixed handshake() buffer overflow
+ (Found by poplix, Secunia: SA22674 / FrSIRT/ADV-2006-4333)
+ - Added more tests
+ - More name parsing enhancements
+ - Now runs on Linux/AMD64
+ - Added setting to change server port
+
+2006-11-05: 0.3.3
+ - Fixed possible buffer overflow
+ (Found by poplix, Bugtraq ID: 20883)
+ - Reworked dns hostname encoding
+
+2006-09-11: 0.3.2
+ - Support for NetBSD
+ - Fixed potential security problems
+ - Name parsing routines rewritten, added regression tests
+ - New encoding, 25% more peak upstream throughput
+ - New -l option to set local ip to listen to on server
+
+2006-07-11: 0.3.1
+ - Add Mac OSX support
+ - Add setting device name
+ - Use compression of domain name in reply (should allow setting MTU
+ approx 200 bytes higher)
+
+2006-06-24: 0.3.0
+ - First public release
+ - Support for Linux, FreeBSD, OpenBSD
diff --git a/jni/iodine/Makefile b/jni/iodine/Makefile
new file mode 100644
index 0000000..01c2325
--- /dev/null
+++ b/jni/iodine/Makefile
@@ -0,0 +1,62 @@
+prefix=/usr/local
+sbindir=$(prefix)/sbin
+datadir=$(prefix)/share
+mandir=$(datadir)/man
+
+DESTDIR=
+
+INSTALL=install
+INSTALL_FLAGS=
+
+MKDIR=mkdir
+MKDIR_FLAGS=-p
+
+RM=rm
+RM_FLAGS=-f
+
+TARGETOS = `uname`
+
+all:
+ @(cd src; $(MAKE) TARGETOS=$(TARGETOS) all)
+
+cross-mingw:
+ @(cd src; $(MAKE) TARGETOS=windows32 CC=i686-mingw32-gcc all)
+
+cross-mingw-dist: cross-mingw
+ @rm -rf iodine-latest-win32*
+ @mkdir -p iodine-latest-win32/bin
+ @for i in `ls bin`; do cp bin/$$i iodine-latest-win32/bin/$$i.exe; done
+ @cp /usr/i686-mingw32/usr/bin/zlib1.dll iodine-latest-win32/bin
+ @cp README* CH* TO* iodine-latest-win32
+ @echo "Create date: " > iodine-latest-win32/VERSION
+ @date >> iodine-latest-win32/VERSION
+ @echo "SVN version: " >> iodine-latest-win32/VERSION
+ @svnversion >> iodine-latest-win32/VERSION
+ @zip -r iodine-latest-win32.zip iodine-latest-win32
+
+install: all
+ $(MKDIR) $(MKDIR_FLAGS) $(DESTDIR)$(sbindir)
+ $(INSTALL) $(INSTALL_FLAGS) bin/iodine $(DESTDIR)$(sbindir)/iodine
+ chmod 755 $(DESTDIR)$(sbindir)/iodine
+ $(INSTALL) $(INSTALL_FLAGS) bin/iodined $(DESTDIR)$(sbindir)/iodined
+ chmod 755 $(DESTDIR)$(sbindir)/iodined
+ $(MKDIR) $(MKDIR_FLAGS) $(DESTDIR)$(mandir)/man8
+ $(INSTALL) $(INSTALL_FLAGS) man/iodine.8 $(DESTDIR)$(mandir)/man8/iodine.8
+ chmod 644 $(DESTDIR)$(mandir)/man8/iodine.8
+
+uninstall:
+ $(RM) $(RM_FLAGS) $(DESTDIR)$(sbindir)/iodine
+ $(RM) $(RM_FLAGS) $(DESTDIR)$(sbindir)/iodined
+ $(RM) $(RM_FLAGS) $(DESTDIR)$(mandir)/man8/iodine.8
+
+test: all
+ @echo "!! The check library is required for compiling and running the tests"
+ @echo "!! Get it at http://check.sf.net"
+ @(cd tests; $(MAKE) TARGETOS=$(TARGETOS) all)
+
+clean:
+ @echo "Cleaning..."
+ @(cd src; $(MAKE) clean)
+ @(cd tests; $(MAKE) clean)
+ @rm -rf bin iodine-latest-win32*
+
diff --git a/jni/iodine/README b/jni/iodine/README
new file mode 100644
index 0000000..d9e3c29
--- /dev/null
+++ b/jni/iodine/README
@@ -0,0 +1,357 @@
+
+iodine - http://code.kryo.se/iodine
+
+***********************************
+
+This is a piece of software that lets you tunnel IPv4 data through a DNS
+server. This can be usable in different situations where internet access is
+firewalled, but DNS queries are allowed.
+
+
+QUICKSTART:
+
+Try it out within your own LAN! Follow these simple steps:
+- On your server, run: ./iodined -f 10.0.0.1 test.com
+ (If you already use the 10.0.0.0 network, use another internal net like
+ 172.16.0.0)
+- Enter a password
+- On the client, run: ./iodine -f -r 192.168.0.1 test.com
+ (Replace 192.168.0.1 with your server's ip address)
+- Enter the same password
+- Now the client has the tunnel ip 10.0.0.2 and the server has 10.0.0.1
+- Try pinging each other through the tunnel
+- Done! :)
+To actually use it through a relaying nameserver, see below.
+
+
+HOW TO USE:
+
+Note: server and client are required to speak the exact same protocol. In most
+cases, this means running the same iodine version. Unfortunately, implementing
+backward and forward protocol compatibility is usually not feasible.
+
+Server side:
+To use this tunnel, you need control over a real domain (like mydomain.com),
+and a server with a public IP address to run iodined on. If this server
+already runs a DNS program, change its listening port and then use iodined's
+-b option to let iodined forward the DNS requests. (Note that this procedure
+is not advised in production environments, because iodined's DNS forwarding
+is not completely transparent.)
+
+Then, delegate a subdomain (say, t1.mydomain.com) to the iodined server.
+If you use BIND for your domain, add two lines like these to the zone file:
+
+t1 IN NS t1ns.mydomain.com. ; note the dot!
+t1ns IN A 10.15.213.99
+
+The "NS" line is all that's needed to route queries for the "t1" subdomain
+to the "t1ns" server. We use a short name for the subdomain, to keep as much
+space as possible available for the data traffic. At the end of the "NS" line
+is the name of your iodined server. This can be any name, pointing anywhere,
+but in this case it's easily kept in the same zone file. It must be a name
+(not an IP address), and that name itself must have an A record (not a CNAME).
+
+If your iodined server has a dynamic IP, use a dynamic dns provider. Simply
+point the "NS" line to it, and leave the "A" line out:
+
+t1 IN NS myname.mydyndnsprovider.com. ; note the dot!
+
+Then reload or restart your nameserver program. Now any DNS queries for
+domains ending in t1.mydomain.com will be sent to your iodined server.
+
+Finally start iodined on your server. The first argument is the IP address
+inside the tunnel, which can be from any range that you don't use yet (for
+example 192.168.99.1), and the second argument is the assigned domain (in this
+case t1.mydomain.com). Using the -f option will keep iodined running in the
+foreground, which helps when testing. iodined will open a virtual interface
+("tun device"), and will also start listening for DNS queries on UDP port 53.
+Either enter a password on the commandline (-P pass) or after the server has
+started. Now everything is ready for the client.
+
+If there is a chance you'll be using an iodine tunnel from unexpected
+environments, start iodined with a -c option.
+
+Resulting commandline in this example situation:
+./iodined -f -c -P secretpassword 192.168.99.1 t1.mydomain.com
+
+Client side:
+All the setup is done, just start iodine. It takes one or two arguments, the
+first is the local relaying DNS server (optional) and the second is the domain
+you used (t1.mydomain.com). If you don't specify the first argument, the
+system's current DNS setting will be consulted.
+
+If DNS queries are allowed to any computer, you can directly give the iodined
+server's address as first argument (in the example: t1ns.mydomain.com or
+10.15.213.99). In that case, it may also happen that _any_ traffic is allowed
+to the DNS port (53 UDP) of any computer. Iodine will detect this, and switch
+to raw UDP tunneling if possible. To force DNS tunneling in any case, use the
+-r option (especially useful when testing within your own network).
+
+The client's tunnel interface will get an IP close to the server's (in this
+case 192.168.99.2 or .3 etc.) and a suitable MTU. Enter the same password as
+on the server either as commandline option or after the client has started.
+Using the -f option will keep the iodine client running in the foreground.
+
+Resulting commandline in this example situation:
+./iodine -f -P secretpassword t1.mydomain.com
+(add -r to force DNS tunneling even if raw UDP tunneling would be possible)
+
+From either side, you should now be able to ping the IP address on the other
+end of the tunnel. In this case, ping 192.168.99.1 from the iodine client, and
+192.168.99.2 or .3 etc. from the iodine server.
+
+
+MISC. INFO:
+
+Routing:
+It is possible to route all traffic through the DNS tunnel. To do this, first
+add a host route to the nameserver used by iodine over the wired/wireless
+interface with the default gateway as gateway. Then replace the default
+gateway with the iodined server's IP address inside the DNS tunnel, and
+configure the server to do NAT.
+
+However, note that the tunneled data traffic is not encrypted at all, and can
+be read and changed by external parties relatively easily. For maximum
+security, run a VPN through the DNS tunnel (=double tunneling), or use secure
+shell (SSH) access, possibly with port forwarding. The latter can also be used
+for web browsing, when you run a web proxy (for example Privoxy) on your
+server.
+
+Testing:
+The iodined server replies to NS requests sent for subdomains of the tunnel
+domain. If your iodined subdomain is t1.mydomain.com, send a NS request for
+foo123.t1.mydomain.com to see if the delegation works. dig is a good tool
+for this:
+dig -t NS foo123.t1.mydomain.com
+
+Also, the iodined server will answer requests starting with 'z' for any of the
+supported request types, for example:
+dig -t TXT z456.t1.mydomain.com
+dig -t SRV z456.t1.mydomain.com
+dig -t CNAME z456.t1.mydomain.com
+The reply should look like garbled text in all these cases.
+
+Operational info:
+The DNS-response fragment size is normally autoprobed to get maximum bandwidth.
+To force a specific value (and speed things up), use the -m option.
+
+The DNS hostnames are normally used up to their maximum length, 255 characters.
+Some DNS relays have been found that answer full-length queries rather
+unreliably, giving widely varying (and mostly very bad) results of the
+fragment size autoprobe on repeated tries. In these cases, use the -M switch
+to reduce the DNS hostname length to for example 200 characters, which makes
+these DNS relays much more stable. This is also useful on some "de-optimizing"
+DNS relays that stuff the response with two full copies of the query, leaving
+very little space for downstream data (also not capable of EDNS0). The -M
+switch can trade some upstream bandwidth for downstream bandwidth. Note that
+the minimum -M value is about 100, since the protocol can split packets (1200
+bytes max) in only 16 fragments, requiring at least 75 real data bytes per
+fragment.
+
+The upstream data is sent gzipped encoded with Base32; or Base64 if the relay
+server supports mixed case and '+' in domain names; or Base64u if '_' is
+supported instead; or Base128 if high-byte-value characters are supported.
+This upstream encoding is autodetected. The 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.
+
+Several DNS request types are supported, with the NULL type expected to provide
+the largest downstream bandwidth. Other available types are TXT, SRV, MX,
+CNAME and A (returning CNAME), in decreasing bandwidth order. Normally the
+"best" request type is autodetected and used. However, DNS relays may impose
+limits on for example NULL and TXT, making SRV or MX actually the best choice.
+This is not autodetected, but can be forced using the -T option. It is
+advisable to try various alternatives especially when the autodetected request
+type provides a downstream fragment size of less than 200 bytes.
+
+Note that SRV, MX and A (returning CNAME) queries may/will cause additional
+lookups by "smart" caching nameservers to get an actual IP address, which may
+either slow down or fail completely.
+
+DNS responses for non-NULL queries can be encoded with the same set of codecs
+as upstream data. This is normally also autodetected, but no fully exhaustive
+tests are done, so some problems may not be noticed when selecting more
+advanced codecs. In that case, you'll see failures/corruption in the fragment
+size autoprobe. In particular, several DNS relays have been found that change
+replies returning hostnames (SRV, MX, CNAME, A) to lowercase only when that
+hostname exceeds ca. 180 characters. In these and similar cases, use the -O
+option to try other downstream codecs; Base32 should always work.
+
+Normal operation now is for the server to _not_ answer a DNS request until
+the next DNS request has come in, a.k.a. being "lazy". This way, the server
+will always have a DNS request handy when new downstream data has to be sent.
+This greatly improves (interactive) performance and latency, and allows to
+slow down the quiescent ping requests to 4 second intervals by default, and
+possibly much slower. In fact, the main purpose of the pings now is to force
+a reply to the previous ping, and prevent DNS server timeouts (usually at
+least 5-10 seconds per RFC1035). Some DNS servers are more impatient and will
+give SERVFAIL errors (timeouts) in periods without tunneled data traffic. All
+data should still get through in these cases, but iodine will reduce the ping
+interval to 1 second anyway (-I1) to reduce the number of error messages. This
+may not help for very impatient DNS relays like dnsadvantage.com (ultradns),
+which time out in 1 second or even less. Yet data will still get trough, and
+you can ignore the SERVFAIL errors.
+
+If you are running on a local network without any DNS server in-between, try
+-I 50 (iodine and iodined close the connection after 60 seconds of silence).
+The only time you'll notice a slowdown, is when DNS reply packets go missing;
+the iodined server then has to wait for a new ping to re-send the data. You can
+speed this up by generating some upstream traffic (keypress, ping). If this
+happens often, check your network for bottlenecks and/or run with -I1.
+
+The delayed answering in lazy mode will cause some "carrier grade" commercial
+DNS relays to repeatedly re-send the same DNS query to the iodined server.
+If the DNS relay is actually implemented as a pool of parallel servers,
+duplicate requests may even arrive from multiple sources. This effect will
+only be visible in the network traffic at the iodined server, and will not
+affect the client's connection. Iodined will notice these duplicates, and send
+the same answer (when its time has come) to both the original query and the
+latest duplicate. After that, the full answer is cached for a short while.
+Delayed duplicates that arrive at the server even later, get a reply that the
+iodine client will ignore (if it ever arrives there).
+
+If you have problems, try inspecting the traffic with network monitoring tools
+like tcpdump or ethereal/wireshark, 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 -D (and -DD) option on the server
+can also show received and sent queries.
+
+
+TIPS & TRICKS:
+
+If your port 53 is taken on a specific interface by an application that does
+not use it, use -p on iodined to specify an alternate port (like -p 5353) and
+use for instance iptables (on Linux) to forward the traffic:
+iptables -t nat -A PREROUTING -i eth0 -p udp --dport 53 -j DNAT --to :5353
+(Sent in by Tom Schouten)
+
+Iodined will reject data from clients that have not been active (data/pings)
+for more than 60 seconds. Similarly, iodine will exit when no downstream
+data has been received for 60 seconds. In case of a long network outage or
+similar, just restart iodine (re-login), possibly multiple times until you get
+your old IP address back. Once that's done, just wait a while, and you'll
+eventually see the tunneled TCP traffic continue to flow from where it left
+off before the outage.
+
+With the introduction of the downstream packet queue in the server, its memory
+usage has increased with several megabytes in the default configuration.
+For use in low-memory environments (e.g. running on your DSL router), you can
+decrease USERS and undefine OUTPACKETQ_LEN in user.h without any ill conse-
+quence, assuming at most one client will be connected at any time. A small
+DNSCACHE_LEN is still advised, preferably 2 or higher, however you can also
+undefine it to save a few more kilobytes.
+
+
+PERFORMANCE:
+
+This section tabulates some performance measurements. To view properly, use
+a fixed-width font like Courier.
+
+Measurements were done in protocol 00000502 in lazy mode; upstream encoding
+always Base128; iodine -M255; iodined -m1130. Network conditions were not
+extremely favorable; results are not benchmarks but a realistic indication of
+real-world performance that can be expected in similar situations.
+
+Upstream/downstream throughput was measured by scp'ing a file previously
+read from /dev/urandom (i.e. incompressible), and measuring size with
+"ls -l ; sleep 30 ; ls -l" on a separate non-tunneled connection. Given the
+large scp block size of 16 kB, this gives a resolution of 4.3 kbit/s, which
+explains why some values are exactly equal.
+Ping round-trip times measured with "ping -c100", presented are average rtt
+and mean deviation (indicating spread around the average), in milliseconds.
+
+
+Situation 1:
+Laptop -> Wifi AP -> Home server -> DSL provider -> Datacenter
+ iodine DNS "relay" bind9 DNS cache iodined
+
+ downstr. upstream downstr. ping-up ping-down
+ fragsize kbit/s kbit/s avg +/-mdev avg +/-mdev
+------------------------------------------------------------------------------
+
+iodine -> Wifi AP :53
+ -Tnull (= -Oraw) 982 43.6 131.0 28.0 4.6 26.8 3.4
+
+iodine -> Home server :53
+ -Tnull (= -Oraw) 1174 48.0 305.8 26.6 5.0 26.9 8.4
+
+iodine -> DSL provider :53
+ -Tnull (= -Oraw) 1174 56.7 367.0 20.6 3.1 21.2 4.4
+ -Ttxt -Obase32 730 56.7 174.7*
+ -Ttxt -Obase64 874 56.7 174.7
+ -Ttxt -Obase128 1018 56.7 174.7
+ -Ttxt -Oraw 1162 56.7 358.2
+ -Tsrv -Obase128 910 56.7 174.7
+ -Tcname -Obase32 151 56.7 43.6
+ -Tcname -Obase128 212 56.7 52.4
+
+iodine -> DSL provider :53
+ wired (no Wifi) -Tnull 1174 74.2 585.4 20.2 5.6 19.6 3.4
+
+ [174.7* : these all have 2frag/packet]
+
+
+Situation 2:
+Laptop -> Wifi+vpn / wired -> Home server
+ iodine iodined
+
+ downstr. upstream downstr. ping-up ping-down
+ fragsize kbit/s kbit/s avg +/-mdev avg +/-mdev
+------------------------------------------------------------------------------
+
+wifi + openvpn -Tnull 1186 166.0 1022.3 6.3 1.3 6.6 1.6
+
+wired -Tnull 1186 677.2 2464.1 1.3 0.2 1.3 0.1
+
+
+Performance is strongly coupled to low ping times, as iodine requires
+confirmation for every data fragment before moving on to the next. Allowing
+multiple fragments in-flight like TCP could possibly increase performance,
+but it would likely cause serious overload for the intermediary DNS servers.
+The current protocol scales performance with DNS responsivity, since the
+DNS servers are on average handling at most one DNS request per client.
+
+
+PORTABILITY:
+
+iodine has been tested on Linux (arm, ia64, x86, AMD64 and SPARC64), FreeBSD
+(ia64, x86), OpenBSD (x86), NetBSD (x86), MacOS X (ppc and x86, with
+http://tuntaposx.sourceforge.net/). and Windows (with OpenVPN TAP32 driver, see
+win32 readme file). 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:
+
+The name iodine was chosen since it starts with IOD (IP Over DNS) and since
+iodine has atomic number 53, which happens to be the DNS port number.
+
+
+THANKS:
+
+- To kuxien for FreeBSD and OS X testing
+- To poplix for code audit
+
+
+AUTHORS & LICENSE:
+
+Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+Also major contributions by Anne Bezemer.
+
+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.
+
+
+MD5 implementation by L. Peter Deutsch (license and source in src/md5.[ch])
+Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
diff --git a/jni/iodine/README-win32.txt b/jni/iodine/README-win32.txt
new file mode 100644
index 0000000..eec675e
--- /dev/null
+++ b/jni/iodine/README-win32.txt
@@ -0,0 +1,62 @@
+
+
+iodine - http://code.kryo.se/iodine
+
+***********************************
+
+Extra README file for Win32 related stuff
+
+
+== Running iodine on Windows:
+1. Install the TAP32 driver
+ http://openvpn.net/index.php/open-source/downloads.html
+ Choose OpenVPN 2.0.9 Windows Installer, when installing you can
+ select to install only the TAP driver.
+
+2. Have at least one TAP32 interface installed. There are scripts for adding
+ and removing in the OpenVPN bin directory. If you have more than one
+ installed, use -d to specify which. Use double quotes if you have spaces,
+ example: iodine.exe -d "Local Area Connection 4" abc.ab
+
+3. Make sure the interface you want to use does not have a default gateway set.
+
+4. Run iodine/iodined as normal (see the main README file).
+ You may have to run it as administrator depending on user privileges.
+
+5. Enjoy!
+
+
+== Building on Windows:
+You need:
+ MinGW, MSYS, GCC, zlib
+
+Then just run make
+
+
+== Cross-compiling for MinGW:
+You need:
+ MinGW crosscompiler, crosscompiled zlib
+
+Then run "make cross-mingw"
+Note that the binaries will not get a .exe suffix
+
+
+== Zlib download
+You can get zlib for MinGW here (both for native and crosscompile):
+http://code.kryo.se/iodine/deps/zlib.zip
+Unzip it in your MinGW directory on Windows or in $ROOT/usr for
+cross-compile.
+
+
+== Results of crappy Win32 API:
+The following fixable limitations apply:
+- Server cannot read packet destination address
+
+The following (probably) un-fixable limitations apply:
+- A password entered as -P argument can be shown in process list
+- Priviligies cannot be dropped
+- chroot() cannot be used
+- Detaching from terminal not possible
+- Server on windows must be run with /30 netmask
+- Client can only talk to server, not other clients
+
diff --git a/jni/iodine/TODO b/jni/iodine/TODO
new file mode 100644
index 0000000..e852d32
--- /dev/null
+++ b/jni/iodine/TODO
@@ -0,0 +1,12 @@
+
+iodine - http://code.kryo.se/iodine
+
+***********************************
+
+The TODO list is now located at
+
+http://dev.kryo.se/iodine/
+
+The list is under the "View tickets" page
+
+Feel free to add your own wishes and bug reports
diff --git a/jni/iodine/doc/iodine.te b/jni/iodine/doc/iodine.te
new file mode 100644
index 0000000..9749f03
--- /dev/null
+++ b/jni/iodine/doc/iodine.te
@@ -0,0 +1,25 @@
+# Sample post-initialization SELinux policy for Iodine
+policy_module(iodine, 1.1)
+
+require {
+ type init_t;
+ type initrc_t;
+ type unconfined_t;
+ type unlabeled_t;
+ class udp_socket { read write };
+ class rawip_socket { write read };
+ class association recvfrom;
+ class unix_dgram_socket { create connect };
+}
+
+type iodine_t;
+domain_type(iodine_t)
+domain_dyntrans_type(initrc_t)
+allow initrc_t iodine_t:process dyntransition;
+
+allow iodine_t unconfined_t:udp_socket { read write };
+allow iodine_t unconfined_t:rawip_socket { write read };
+allow iodine_t unlabeled_t:association recvfrom;
+allow iodine_t self:unix_dgram_socket { create connect };
+corenet_raw_receive_generic_node(iodine_t)
+corenet_rw_tun_tap_dev(iodine_t)
diff --git a/jni/iodine/doc/proto_00000402.txt b/jni/iodine/doc/proto_00000402.txt
new file mode 100644
index 0000000..da36919
--- /dev/null
+++ b/jni/iodine/doc/proto_00000402.txt
@@ -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/jni/iodine/doc/proto_00000500.txt b/jni/iodine/doc/proto_00000500.txt
new file mode 100644
index 0000000..05f100c
--- /dev/null
+++ b/jni/iodine/doc/proto_00000500.txt
@@ -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.
diff --git a/jni/iodine/doc/proto_00000502.txt b/jni/iodine/doc/proto_00000502.txt
new file mode 100644
index 0000000..46cf2de
--- /dev/null
+++ b/jni/iodine/doc/proto_00000502.txt
@@ -0,0 +1,287 @@
+Detailed specification of protocol in version 00000502
+======================================================
+
+Note: work in progress!!
+
+======================================================
+1. DNS protocol
+======================================================
+
+Quick alphabetical index / register:
+ 0-9 Data packet
+ A-F Data packet
+ I IP address
+ L Login
+ N Downstream fragsize (NS.topdomain A-type reply)
+ O Options
+ P Ping
+ R Downstream fragsize probe
+ S Switch upstream codec
+ V Version
+ W (WWW.topdomain A-type reply)
+ Y Downstream codec check
+ Z Upstream codec check
+
+
+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)
+
+IP Request:
+Client sends:
+ First byte i or I
+ 5 bits coded as Base32 char, meaning userid
+ CMC as 3 Base32 chars
+Server replies
+ BADIP if bad userid, or
+ I and then 4 bytes network order external IP address of iodined server
+
+Upstream codec check / bounce:
+Client sends:
+ First byte z or Z
+ Lots of data that should not be decoded
+Server replies:
+ The requested domain copied raw, in the lowest-grade downstream codec
+ available for the request type.
+
+Downstream codec check:
+Client sends:
+ First byte y or Y
+ 1 char, meaning downstream codec to use
+ 5 bits coded as Base32 char, meaning check variant
+ CMC as 3 Base32 chars
+ Possibly extra data, depending on check variant
+Server sends:
+ Data encoded with requested downstream codec; data content depending
+ on check variant number.
+ BADCODEC if requested downstream codec not available.
+ BADLEN if check variant is not available, or problem with extra data.
+
+ Downstream codec chars are same as in 'O' Option request, below.
+
+ Check variants:
+ 1: Send encoded DOWNCODECCHECK1 string as defined in encoding.h
+
+ (Other variants reserved; possibly variant that sends a decoded-encoded
+ copy of Base32-encoded extra data in the request)
+
+Switch codec:
+Client sends:
+ First byte s or S
+ 5 bits coded as Base32 char, meaning userid
+ 5 bits coded as Base32 char, representing number of raw bits per
+ encoded byte:
+ 5: Base32 (a-z0-5)
+ 6: Base64 (a-zA-Z0-9+-)
+ 26: Base64u (a-zA-Z0-9_-)
+ 7: Base128 (a-zA-Z0-9\274-\375)
+ CMC as 3 Base32 chars
+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 previous codec
+ BADLEN if length of query is too short
+
+Options:
+Client sends:
+ First byte o or O
+ 5 bits coded as Base32 char, meaning userid
+ 1 char, meaning option
+ CMC as 3 Base32 chars
+Server sends:
+ Full name of option if accepted. After this, option immediately takes
+ effect in server.
+ BADCODEC if not accepted. Previous situation remains.
+ All options affect only the requesting client.
+
+ Option chars:
+ t or T: Downstream encoding Base32, for TXT/CNAME/A/MX (default)
+ s or S: Downstream encoding Base64, for TXT/CNAME/A/MX
+ u or U: Downstream encoding Base64u, for TXT/CNAME/A/MX
+ v or V: Downstream encoding Base128, for TXT/CNAME/A/MX
+ r or R: Downstream encoding Raw, for TXT/NULL (default for NULL)
+ If codec unsupported for request type, server will use Base32; note
+ that server will answer any mix of request types that a client sends.
+ Server may disregard this option; client must always use the downstream
+ encoding type indicated in every downstream DNS packet.
+
+ l or L: Lazy mode, server will keep one request unanswered until the
+ next one comes in. Applies only to data transfer; handshake is always
+ answered immediately.
+ i or I: Immediate (non-lazy) mode, server will answer all requests
+ (nearly) immediately.
+
+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 contain
+ the requested length. The third byte is 107 (0x6B). The fourth byte
+ is a random value, and each following byte is incremented with 107.
+ This is checked by the client to determine corruption.
+ 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 43210
+ +----+---+--+--+---+----+-+-----+
+ |UUUU|SSS|FF|FF|DDD|GGGG|L|UDCMC|
+ +----+---+--+--+---+----+-+-----+
+
+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
+UDCMC = Upstream Data CMC, 36 steps a-z0-9, case-insensitive
+
+Upstream data packet starts with 1 byte ASCII hex coded user byte; then 3 bytes
+Base32 encoded header; then 1 char data-CMC; then comes the payload data,
+encoded with the chosen upstream codec.
+
+Downstream data starts with 2 byte header. Then payload data, which may be
+compressed.
+
+In NULL responses, downstream data is always raw. In all other response types,
+downstream data is encoded (see Options above).
+Encoding type is indicated by 1 prefix char:
+TXT:
+ End result is always DNS-chopped (series of len-prefixed strings
+ <=255 bytes)
+ t or T: Base32 encoded before chop, decoded after un-chop
+ s or S: Base64 encoded before chop, decoded after un-chop
+ u or U: Base64u encoded before chop, decoded after un-chop
+ v or V: Base128 encoded before chop, decoded after un-chop
+ r or R: Raw no encoding, only DNS-chop
+SRV/MX/CNAME/A:
+ h or H: Hostname encoded with Base32
+ i or I: Hostname encoded with Base64
+ j or J: Hostname encoded with Base64u
+ k or K: Hostname encoded with Base128
+SRV and MX may reply with multiple hostnames, each encoded separately. Each
+has a 10-multiple priority, and encoding/decoding is done in strictly
+increasing priority sequence 10, 20, 30, etc. without gaps. Note that some DNS
+relays will shuffle the answer records in the response.
+
+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/TXT/.. type response,
+always starting with the 2 bytes downstream data header as shown above.
+If server has nothing to send, no data is added after the header.
+If server has something to send, it will add the downstream data packet
+(or some fragment of it) after the header.
+
+
+"Lazy-mode" operation
+=====================
+
+Client-server DNS traffic sequence has been reordered to provide increased
+(interactive) performance and greatly reduced latency.
+
+Idea taken from Lucas Nussbaum's slides (24th IFIP International Security
+Conference, 2009) at http://www.loria.fr/~lnussbau/tuns.html. Current
+implementation is original to iodine, no code or documentation from any other
+project was consulted during development.
+
+Server:
+Upstream data is acked immediately*, to keep the slow upstream data flowing
+as fast as possible (client waits for ack to send next frag).
+
+Upstream pings are answered _only_ when 1) downstream data arrives from tun,
+OR 2) new upstream ping/data arrives from client.
+In most cases, this means we answer the previous DNS query instead of the
+current one. The current query is kept in queue and used as soon as
+downstream data has to be sent.
+
+*: upstream data ack is usually done as reply on the previous ping packet,
+and the upstream-data packet itself is kept in queue.
+
+Client:
+Downstream data is acked immediately, to keep it flowing fast (includes a
+ping after last downstream frag).
+
+Also, after all available upstream data is sent & acked by the server (which
+in some cases uses up the last query), send an additional ping to prime the
+server for the next downstream data.
+
+
+======================================================
+2. Raw UDP protocol
+======================================================
+
+All Raw UDP protcol messages start with a 3 byte header: 0x10d19e
+This is not the start of a valid DNS message so it is easy to identify.
+The fourth byte contains the command and the user id.
+
+ 7654 3210
+ +----+----+
+ |CCCC|UUUU|
+ +----+----+
+
+Login message (command = 1):
+The header is followed by a MD5 hash with the same password as in the DNS
+login. The client starts the raw mode by sending this message, and uses
+the login challenge +1, and the server responds using the login challenge -1.
+After the login message has been exchanged, both the server and the client
+switch to raw udp mode for the rest of the connection.
+
+Data message (command = 2):
+After the header comes the payload data, which may be compressed.
+
+Ping message (command = 3):
+Sent from client to server and back to keep session open. Has no payload.
+
diff --git a/jni/iodine/man/iodine.8 b/jni/iodine/man/iodine.8
new file mode 100644
index 0000000..6eee603
--- /dev/null
+++ b/jni/iodine/man/iodine.8
@@ -0,0 +1,338 @@
+.\" groff -man -Tascii iodine.8
+.TH IODINE 8 "DEC 2009" "User Manuals"
+.SH NAME
+iodine, iodined \- tunnel IPv4 over DNS
+.SH SYNOPSIS
+.B iodine [-v]
+
+.B iodine [-h]
+
+.B iodine [-f] [-r] [-u
+.I user
+.B ] [-P
+.I password
+.B ] [-m
+.I fragsize
+.B ] [-t
+.I chrootdir
+.B ] [-d
+.I device
+.B ] [-m
+.I fragsize
+.B ] [-M
+.I namelen
+.B ] [-z
+.I context
+.B ] [-F
+.I pidfile
+.B ] [-T
+.I dnstype
+.B ] [-O
+.I downenc
+.B ] [-L
+.I 0|1
+.B ] [-I
+.I interval
+.B ]
+.B [
+.I nameserver
+.B ]
+.I topdomain
+
+.B iodined [-v]
+
+.B iodined [-h]
+
+.B iodined [-c] [-s] [-f] [-D] [-u
+.I user
+.B ] [-t
+.I chrootdir
+.B ] [-d
+.I device
+.B ] [-m
+.I mtu
+.B ] [-l
+.I listen_ip
+.B ] [-p
+.I port
+.B ] [-n
+.I external_ip
+.B ] [-b
+.I dnsport
+.B ] [-P
+.I password
+.B ] [-z
+.I context
+.B ] [-F
+.I pidfile
+.B ]
+.I tunnel_ip
+.B [
+.I /netmask
+.B ]
+.I topdomain
+.SH DESCRIPTION
+.B iodine
+lets you tunnel IPv4 data through a DNS
+server. This can be useful in situations where Internet access is firewalled,
+but DNS queries are allowed. It needs a TUN/TAP device to operate. The
+bandwidth is asymmetrical,
+with a measured maximum of 680 kbit/s upstream and 2.3 Mbit/s
+downstream in a wired LAN test network.
+Realistic sustained throughput on a Wifi network using a carrier-grade
+DNS cache has been measured at some 50 kbit/s upstream and over 200 kbit/s
+downstream.
+.B iodine
+is the client application,
+.B iodined
+is the server.
+
+Note: server and client are required to speak the exact same protocol. In most
+cases, this means running the same iodine version. Unfortunately, implementing
+backward and forward protocol compatibility is usually not feasible.
+.SH OPTIONS
+.SS Common Options:
+.TP
+.B -v
+Print version info and exit.
+.TP
+.B -h
+Print usage info and exit.
+.TP
+.B -f
+Keep running in foreground.
+.TP
+.B -u user
+Drop privileges and run as user 'user' after setting up tunnel.
+.TP
+.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.
+.TP
+.B -z context
+Apply SELinux 'context' after initialization.
+.TP
+.B -F pidfile
+Create 'pidfile' and write process id in it.
+.SS Client Options:
+.TP
+.B -r
+Skip raw UDP mode. If not used, iodine will try getting the public IP address
+of the iodined host and test if it is reachable directly. If it is, traffic
+will be sent to the server instead of the DNS relay.
+.TP
+.B -m fragsize
+Force maximum downstream fragment size. Not setting this will cause the
+client to automatically probe the maximum accepted downstream fragment size.
+.TP
+.B -M namelen
+Maximum length of upstream hostnames, default 255.
+Usable range ca. 100 to 255.
+Use this option to scale back upstream bandwidth in favor of downstream
+bandwidth.
+Also useful for DNS servers that perform unreliably when using full-length
+hostnames, noticable when fragment size autoprobe returns very
+different results each time.
+.TP
+.B -T dnstype
+DNS request type override.
+By default, autodetection will probe for working DNS request types, and
+will select the request type that is expected to provide the most bandwidth.
+However, it may turn out that a DNS relay imposes limits that skew the
+picture, which may lead to an "unexpected" DNS request type providing
+more bandwidth.
+In that case, use this option to override the autodetection.
+In (expected) decreasing bandwidth order, the supported DNS request types are:
+.IR NULL ,
+.IR TXT ,
+.IR SRV ,
+.IR MX ,
+.I CNAME
+and
+.I A
+(returning CNAME).
+Note that
+.IR SRV ,
+.I MX
+and
+.I A
+may/will cause additional lookups by "smart" caching
+nameservers to get an actual IP address, which may either slow down or fail
+completely.
+.TP
+.B -O downenc
+Force downstream encoding type for all query type responses except NULL.
+Default is autodetected, but may not spot all problems for the more advanced
+codecs.
+Use this option to override the autodetection.
+.I Base32
+is the lowest-grade codec and should always work; this is used when
+autodetection fails.
+.I Base64
+provides more bandwidth, but may not work on all nameservers.
+.I Base64u
+is equal to Base64 except in using underscore ('_')
+instead of plus sign ('+'), possibly working where
+.I Base64
+does not.
+.I Base128
+uses high byte values (mostly accented letters in iso8859-1),
+which might work with some nameservers.
+For TXT queries,
+.I Raw
+will provide maximum performance, but this will only work if the nameserver
+path is fully 8-bit-clean for responses that are assumed to be "legible text".
+.TP
+.B -L 0|1
+Lazy-mode switch.
+\-L1 (default): Use lazy mode for improved performance and decreased latency.
+A very small minority of DNS relays appears to be unable to handle the
+lazy mode traffic pattern, resulting in no or very little data coming through.
+The iodine client will detect this and try to switch back to legacy mode,
+but this may not always work.
+In these situations use \-L0 to force running in legacy mode
+(implies \-I1).
+.TP
+.B -I interval
+Maximum interval between requests (pings) so that intermediate DNS
+servers will not time out. Default is 4 in lazy mode, which will work
+fine in most cases. When too many SERVFAIL errors occur, iodine
+will automatically reduce this to 1.
+To get absolute minimum DNS traffic,
+increase well above 4, but not so high that SERVFAIL errors start to occur.
+There are some DNS relays with very small timeouts,
+notably dnsadvantage.com (ultradns), that will give
+SERVFAIL errors even with \-I1; data will still get trough,
+and these errors can be ignored.
+Maximum useful value is 59, since iodined will close a client's
+connection after 60 seconds of inactivity.
+.SS Server Options:
+.TP
+.B -c
+Disable checking the client IP address on all incoming requests.
+By default, requests originating from non-matching IP adresses will be
+rejected, however this will cause problems when requests are routed
+via a cluster of DNS servers.
+.TP
+.B -s
+Don't try to configure IP address or MTU.
+This should only be used if you have already configured the device that will be
+used.
+.TP
+.B -D
+Increase debug level. Level 1 prints info about each RX/TX packet.
+Implies the
+.B -f
+option.
+On level 2 (-DD) or higher, DNS queries will be printed literally.
+When using Base128 upstream encoding, this is best viewed as
+ISO Latin-1 text instead of (illegal) UTF-8.
+This is easily done with : "LC_ALL=C luit iodined -DD ..."
+(see luit(1)).
+.TP
+.B -m mtu
+Set 'mtu' as mtu size for the tun device.
+This will be sent to the client on login, and the client will use the same mtu
+for its tun device. Default 1130. Note that the DNS traffic will be
+automatically fragmented when needed.
+.TP
+.B -l listen_ip
+Make the server listen only on 'listen_ip' for incoming requests.
+By default, incoming requests are accepted from all interfaces.
+.TP
+.B -p port
+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.
+.B Note:
+The forwarding is not fully transparent, and not advised for use
+in production environments.
+.SS Client Arguments:
+.TP
+.B nameserver
+The nameserver to use to relay the dns traffic. This can be any relaying
+nameserver or the server running iodined if reachable. This field can be
+given as an IP address, or as a hostname. This argument is optional, and
+if not specified a nameserver will be read from the
+.I /etc/resolv.conf
+file.
+.TP
+.B topdomain
+The dns traffic will be sent as queries for subdomains under
+\'topdomain'. This is normally a subdomain to a domain you own. Use a short
+domain name to get better throughput. If
+.B nameserver
+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[/netmask]
+This is the server's ip address on the tun interface. The client will be
+given the next ip number in the range. It is recommended to use the
+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 is expected to arrive as queries for
+subdomains under 'topdomain'. This is normally a subdomain to a domain you
+own. Use a short domain name to get better throughput. This argument must be
+the same on both the client and the server. Queries for domains other
+than 'topdomain' will be forwarded when the \-b option is given, otherwise
+they will be dropped.
+.SH EXAMPLES
+See the README file for both a quick test scenario, and a detailed description
+of real-world deployment.
+.SH SECURITY
+Login is a relatively secure challenge-response MD5 hash, with the
+password never passing the wire.
+However, all other data is
+.B NOT
+encrypted in any way. The DNS traffic is also vulnerable to replay,
+injection and man-in-the-middle attacks, especially when iodined is used
+with the \-c option. Use of ssh or vpn tunneling is strongly recommended.
+On both server and client, use
+.IR iptables ,
+.I pf
+or other firewalls to block all traffic coming in from the tun interfaces,
+except to the used ssh or vpn ports.
+.SH ENVIRONMENT
+.SS IODINE_PASS
+If the environment variable
+.B IODINE_PASS
+is set, iodine will use the value it is set to as password instead of asking
+for one. The
+.B -P
+option still has precedence.
+.SS IODINED_PASS
+If the environment variable
+.B IODINED_PASS
+is set, iodined will use the value it is set to as password instead of asking
+for one. The
+.B -P
+option still has precedence.
+.El
+.SH SEE ALSO
+The README file in the source distribution contains some more elaborate
+information.
+.SH BUGS
+File bugs at http://dev.kryo.se/iodine/
+.SH AUTHORS
+Erik Ekman <yarrick@kryo.se> and Bjorn Andersson <flex@kryo.se>. Major
+contributions by Anne Bezemer.
diff --git a/jni/iodine/src/Makefile b/jni/iodine/src/Makefile
new file mode 100644
index 0000000..a5b9838
--- /dev/null
+++ b/jni/iodine/src/Makefile
@@ -0,0 +1,46 @@
+COMMONOBJS = tun.o dns.o read.o encoding.o login.o base32.o base64.o base64u.o base128.o md5.o common.o
+CLIENTOBJS = iodine.o client.o util.o
+CLIENT = ../bin/iodine
+SERVEROBJS = iodined.o user.o fw_query.o
+SERVER = ../bin/iodined
+
+OS = `echo $(TARGETOS) | tr "a-z" "A-Z"`
+ARCH = `uname -m`
+
+LIBPATH = -L.
+LDFLAGS += -lz `sh osflags $(TARGETOS) link` $(LIBPATH)
+CFLAGS += -c -g -Wall -D$(OS) -pedantic `sh osflags $(TARGETOS) cflags`
+
+all: stateos $(CLIENT) $(SERVER)
+
+stateos:
+ @echo OS is $(OS), arch is $(ARCH)
+
+$(CLIENT): $(COMMONOBJS) $(CLIENTOBJS)
+ @echo LD $@
+ @mkdir -p ../bin
+ @$(CC) $(COMMONOBJS) $(CLIENTOBJS) -o $(CLIENT) $(LDFLAGS)
+
+$(SERVER): $(COMMONOBJS) $(SERVEROBJS)
+ @echo LD $@
+ @mkdir -p ../bin
+ @$(CC) $(COMMONOBJS) $(SERVEROBJS) -o $(SERVER) $(LDFLAGS)
+
+.c.o:
+ @echo CC $<
+ @$(CC) $(CFLAGS) $< -o $@
+
+base64u.o client.o iodined.o: base64u.h
+base64u.c: base64.c
+ @echo Making $@
+ @echo '/* No use in editing, produced by Makefile! */' > $@
+ @sed -e 's/\([Bb][Aa][Ss][Ee]64\)/\1u/g ; s/0123456789+/0123456789_/' < base64.c >> $@
+base64u.h: base64.h
+ @echo Making $@
+ @echo '/* No use in editing, produced by Makefile! */' > $@
+ @sed -e 's/\([Bb][Aa][Ss][Ee]64\)/\1u/g ; s/0123456789+/0123456789_/' < base64.h >> $@
+
+clean:
+ @echo "Cleaning src/"
+ @rm -f $(CLIENT){,.exe} $(SERVER){,.exe} *~ *.o *.core base64u.*
+
diff --git a/jni/iodine/src/base128.c b/jni/iodine/src/base128.c
new file mode 100644
index 0000000..32a29f8
--- /dev/null
+++ b/jni/iodine/src/base128.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2009 J.A.Bezemer@opensourcepartners.nl
+ *
+ * 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.
+ */
+
+/*
+ * raw 76543210 76543210 76543210 76543210 76543210 76543210 76543210
+ * enc 65432106 54321065 43210654 32106543 21065432 10654321 06543210
+ * ^ ^ ^ ^ ^ ^ ^ ^
+ *
+ * 0001 1 0001 1
+ * 0011 3 0011 3
+ * 0111 7 0111 7
+ * 1111 f 0110 6
+ * 1110 e 0100 4
+ * 1100 c
+ * 1000 8
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "encoding.h"
+#include "base128.h"
+
+#define BLKSIZE_RAW 7
+#define BLKSIZE_ENC 8
+
+/* Don't use '-' (restricted to middle of labels), prefer iso_8859-1
+ * accent chars since they might readily be entered in normal use,
+ * don't use 254-255 because of possible function overloading in DNS systems.
+ */
+static const unsigned char cb128[] =
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+ "\274\275\276\277"
+ "\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317"
+ "\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337"
+ "\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357"
+ "\360\361\362\363\364\365\366\367\370\371\372\373\374\375";
+static unsigned char rev128[256];
+static int reverse_init = 0;
+
+static int base128_encode(char *, size_t *, const void *, size_t);
+static int base128_decode(void *, size_t *, const char *, size_t);
+static int base128_handles_dots();
+static int base128_blksize_raw();
+static int base128_blksize_enc();
+
+static struct encoder base128_encoder =
+{
+ "Base128",
+ base128_encode,
+ base128_decode,
+ base128_handles_dots,
+ base128_handles_dots,
+ base128_blksize_raw,
+ base128_blksize_enc
+};
+
+struct encoder
+*get_base128_encoder()
+{
+ return &base128_encoder;
+}
+
+static int
+base128_handles_dots()
+{
+ return 0;
+}
+
+static int
+base128_blksize_raw()
+{
+ return BLKSIZE_RAW;
+}
+
+static int
+base128_blksize_enc()
+{
+ return BLKSIZE_ENC;
+}
+
+inline static void
+base128_reverse_init()
+{
+ int i;
+ unsigned char c;
+
+ if (!reverse_init) {
+ memset (rev128, 0, 256);
+ for (i = 0; i < 128; i++) {
+ c = cb128[i];
+ rev128[(int) c] = i;
+ }
+ reverse_init = 1;
+ }
+}
+
+static int
+base128_encode(char *buf, size_t *buflen, const void *data, size_t size)
+/*
+ * Fills *buf with max. *buflen characters, encoding size bytes of *data.
+ *
+ * NOTE: *buf space should be at least 1 byte _more_ than *buflen
+ * to hold the trailing '\0'.
+ *
+ * return value : #bytes filled in buf (excluding \0)
+ * sets *buflen to : #bytes encoded from data
+ */
+{
+ unsigned char *ubuf = (unsigned char *) buf;
+ unsigned char *udata = (unsigned char *) data;
+ int iout = 0; /* to-be-filled output char */
+ int iin = 0; /* one more than last input byte that can be
+ successfully decoded */
+
+ /* Note: Don't bother to optimize manually. GCC optimizes
+ better(!) when using simplistic array indexing. */
+
+ while (1) {
+ if (iout >= *buflen || iin >= size)
+ break;
+ ubuf[iout] = cb128[((udata[iin] & 0xfe) >> 1)];
+ iout++;
+
+ if (iout >= *buflen || iin >= size) {
+ iout--; /* previous char is useless */
+ break;
+ }
+ ubuf[iout] = cb128[((udata[iin] & 0x01) << 6) |
+ ((iin + 1 < size) ?
+ ((udata[iin + 1] & 0xfc) >> 2) : 0)];
+ iin++; /* 0 complete, iin=1 */
+ iout++;
+
+ if (iout >= *buflen || iin >= size)
+ break;
+ ubuf[iout] = cb128[((udata[iin] & 0x03) << 5) |
+ ((iin + 1 < size) ?
+ ((udata[iin + 1] & 0xf8) >> 3) : 0)];
+ iin++; /* 1 complete, iin=2 */
+ iout++;
+
+ if (iout >= *buflen || iin >= size)
+ break;
+ ubuf[iout] = cb128[((udata[iin] & 0x07) << 4) |
+ ((iin + 1 < size) ?
+ ((udata[iin + 1] & 0xf0) >> 4) : 0)];
+ iin++; /* 2 complete, iin=3 */
+ iout++;
+
+ if (iout >= *buflen || iin >= size)
+ break;
+ ubuf[iout] = cb128[((udata[iin] & 0x0f) << 3) |
+ ((iin + 1 < size) ?
+ ((udata[iin + 1] & 0xe0) >> 5) : 0)];
+ iin++; /* 3 complete, iin=4 */
+ iout++;
+
+ if (iout >= *buflen || iin >= size)
+ break;
+ ubuf[iout] = cb128[((udata[iin] & 0x1f) << 2) |
+ ((iin + 1 < size) ?
+ ((udata[iin + 1] & 0xc0) >> 6) : 0)];
+ iin++; /* 4 complete, iin=5 */
+ iout++;
+
+ if (iout >= *buflen || iin >= size)
+ break;
+ ubuf[iout] = cb128[((udata[iin] & 0x3f) << 1) |
+ ((iin + 1 < size) ?
+ ((udata[iin + 1] & 0x80) >> 7) : 0)];
+ iin++; /* 5 complete, iin=6 */
+ iout++;
+
+ if (iout >= *buflen || iin >= size)
+ break;
+ ubuf[iout] = cb128[(udata[iin] & 0x7f)];
+ iin++; /* 6 complete, iin=7 */
+ iout++;
+ }
+
+ ubuf[iout] = '\0';
+
+ /* store number of bytes from data that was used */
+ *buflen = iin;
+
+ return iout;
+}
+
+#define REV128(x) rev128[(int) (x)]
+
+static int
+base128_decode(void *buf, size_t *buflen, const char *str, size_t slen)
+/*
+ * Fills *buf with max. *buflen bytes, decoded from slen chars in *str.
+ * Decoding stops early when *str contains \0.
+ * Illegal encoded chars are assumed to decode to zero.
+ *
+ * NOTE: *buf space should be at least 1 byte _more_ than *buflen
+ * to hold a trailing '\0' that is added (though *buf will usually
+ * contain full-binary data).
+ *
+ * return value : #bytes filled in buf (excluding \0)
+ */
+{
+ unsigned char *ustr = (unsigned char *) str;
+ unsigned char *ubuf = (unsigned char *) buf;
+ int iout = 0; /* to-be-filled output byte */
+ int iin = 0; /* next input char to use in decoding */
+
+ base128_reverse_init ();
+
+ /* Note: Don't bother to optimize manually. GCC optimizes
+ better(!) when using simplistic array indexing. */
+
+ while (1) {
+ if (iout >= *buflen || iin + 1 >= slen ||
+ str[iin] == '\0' || str[iin + 1] == '\0')
+ break;
+ ubuf[iout] = ((REV128(ustr[iin]) & 0x7f) << 1) |
+ ((REV128(ustr[iin + 1]) & 0x40) >> 6);
+ iin++; /* 0 used up, iin=1 */
+ iout++;
+
+ if (iout >= *buflen || iin + 1 >= slen ||
+ str[iin] == '\0' || str[iin + 1] == '\0')
+ break;
+ ubuf[iout] = ((REV128(ustr[iin]) & 0x3f) << 2) |
+ ((REV128(ustr[iin + 1]) & 0x60) >> 5);
+ iin++; /* 1 used up, iin=2 */
+ iout++;
+
+ if (iout >= *buflen || iin + 1 >= slen ||
+ str[iin] == '\0' || str[iin + 1] == '\0')
+ break;
+ ubuf[iout] = ((REV128(ustr[iin]) & 0x1f) << 3) |
+ ((REV128(ustr[iin + 1]) & 0x70) >> 4);
+ iin++; /* 2 used up, iin=3 */
+ iout++;
+
+ if (iout >= *buflen || iin + 1 >= slen ||
+ str[iin] == '\0' || str[iin + 1] == '\0')
+ break;
+ ubuf[iout] = ((REV128(ustr[iin]) & 0x0f) << 4) |
+ ((REV128(ustr[iin + 1]) & 0x78) >> 3);
+ iin++; /* 3 used up, iin=4 */
+ iout++;
+
+ if (iout >= *buflen || iin + 1 >= slen ||
+ str[iin] == '\0' || str[iin + 1] == '\0')
+ break;
+ ubuf[iout] = ((REV128(ustr[iin]) & 0x07) << 5) |
+ ((REV128(ustr[iin + 1]) & 0x7c) >> 2);
+ iin++; /* 4 used up, iin=5 */
+ iout++;
+
+ if (iout >= *buflen || iin + 1 >= slen ||
+ str[iin] == '\0' || str[iin + 1] == '\0')
+ break;
+ ubuf[iout] = ((REV128(ustr[iin]) & 0x03) << 6) |
+ ((REV128(ustr[iin + 1]) & 0x7e) >> 1);
+ iin++; /* 5 used up, iin=6 */
+ iout++;
+
+ if (iout >= *buflen || iin + 1 >= slen ||
+ str[iin] == '\0' || str[iin + 1] == '\0')
+ break;
+ ubuf[iout] = ((REV128(ustr[iin]) & 0x01) << 7) |
+ ((REV128(ustr[iin + 1]) & 0x7f));
+ iin += 2; /* 6,7 used up, iin=8 */
+ iout++;
+ }
+
+ ubuf[iout] = '\0';
+
+ return iout;
+}
diff --git a/jni/iodine/src/base128.h b/jni/iodine/src/base128.h
new file mode 100644
index 0000000..235b2f9
--- /dev/null
+++ b/jni/iodine/src/base128.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2009 J.A.Bezemer@opensourcepartners.nl
+ *
+ * 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 __BASE128_H__
+#define __BASE128_H__
+
+struct encoder *get_base128_encoder(void);
+
+#endif
diff --git a/jni/iodine/src/base32.c b/jni/iodine/src/base32.c
new file mode 100644
index 0000000..8731a92
--- /dev/null
+++ b/jni/iodine/src/base32.c
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Mostly rewritten 2009 J.A.Bezemer@opensourcepartners.nl
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "encoding.h"
+#include "base32.h"
+
+#define BLKSIZE_RAW 5
+#define BLKSIZE_ENC 8
+
+static const char cb32[] =
+ "abcdefghijklmnopqrstuvwxyz012345";
+static const char cb32_ucase[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
+static unsigned char rev32[256];
+static int reverse_init = 0;
+
+static int base32_encode(char *, size_t *, const void *, size_t);
+static int base32_decode(void *, size_t *, const char *, size_t);
+static int base32_handles_dots();
+static int base32_blksize_raw();
+static int base32_blksize_enc();
+
+static struct encoder base32_encoder =
+{
+ "Base32",
+ base32_encode,
+ base32_decode,
+ base32_handles_dots,
+ base32_handles_dots,
+ base32_blksize_raw,
+ base32_blksize_enc
+};
+
+struct encoder
+*get_base32_encoder()
+{
+ return &base32_encoder;
+}
+
+static int
+base32_handles_dots()
+{
+ return 0;
+}
+
+static int
+base32_blksize_raw()
+{
+ return BLKSIZE_RAW;
+}
+
+static int
+base32_blksize_enc()
+{
+ return BLKSIZE_ENC;
+}
+
+inline static void
+base32_reverse_init()
+{
+ int i;
+ unsigned char c;
+
+ if (!reverse_init) {
+ memset (rev32, 0, 256);
+ for (i = 0; i < 32; i++) {
+ c = cb32[i];
+ rev32[(int) c] = i;
+ c = cb32_ucase[i];
+ rev32[(int) c] = i;
+ }
+ reverse_init = 1;
+ }
+}
+
+int
+b32_5to8(int in)
+{
+ return cb32[in & 31];
+}
+
+int
+b32_8to5(int in)
+{
+ base32_reverse_init();
+ return rev32[in];
+}
+
+static int
+base32_encode(char *buf, size_t *buflen, const void *data, size_t size)
+/*
+ * Fills *buf with max. *buflen characters, encoding size bytes of *data.
+ *
+ * NOTE: *buf space should be at least 1 byte _more_ than *buflen
+ * to hold the trailing '\0'.
+ *
+ * return value : #bytes filled in buf (excluding \0)
+ * sets *buflen to : #bytes encoded from data
+ */
+{
+ unsigned char *udata = (unsigned char *) data;
+ int iout = 0; /* to-be-filled output char */
+ int iin = 0; /* one more than last input byte that can be
+ successfully decoded */
+
+ /* Note: Don't bother to optimize manually. GCC optimizes
+ better(!) when using simplistic array indexing. */
+
+ while (1) {
+ if (iout >= *buflen || iin >= size)
+ break;
+ buf[iout] = cb32[((udata[iin] & 0xf8) >> 3)];
+ iout++;
+
+ if (iout >= *buflen || iin >= size) {
+ iout--; /* previous char is useless */
+ break;
+ }
+ buf[iout] = cb32[((udata[iin] & 0x07) << 2) |
+ ((iin + 1 < size) ?
+ ((udata[iin + 1] & 0xc0) >> 6) : 0)];
+ iin++; /* 0 complete, iin=1 */
+ iout++;
+
+ if (iout >= *buflen || iin >= size)
+ break;
+ buf[iout] = cb32[((udata[iin] & 0x3e) >> 1)];
+ iout++;
+
+ if (iout >= *buflen || iin >= size) {
+ iout--; /* previous char is useless */
+ break;
+ }
+ buf[iout] = cb32[((udata[iin] & 0x01) << 4) |
+ ((iin + 1 < size) ?
+ ((udata[iin + 1] & 0xf0) >> 4) : 0)];
+ iin++; /* 1 complete, iin=2 */
+ iout++;
+
+ if (iout >= *buflen || iin >= size)
+ break;
+ buf[iout] = cb32[((udata[iin] & 0x0f) << 1) |
+ ((iin + 1 < size) ?
+ ((udata[iin + 1] & 0x80) >> 7) : 0)];
+ iin++; /* 2 complete, iin=3 */
+ iout++;
+
+ if (iout >= *buflen || iin >= size)
+ break;
+ buf[iout] = cb32[((udata[iin] & 0x7c) >> 2)];
+ iout++;
+
+ if (iout >= *buflen || iin >= size) {
+ iout--; /* previous char is useless */
+ break;
+ }
+ buf[iout] = cb32[((udata[iin] & 0x03) << 3) |
+ ((iin + 1 < size) ?
+ ((udata[iin + 1] & 0xe0) >> 5) : 0)];
+ iin++; /* 3 complete, iin=4 */
+ iout++;
+
+ if (iout >= *buflen || iin >= size)
+ break;
+ buf[iout] = cb32[((udata[iin] & 0x1f))];
+ iin++; /* 4 complete, iin=5 */
+ iout++;
+ }
+
+ buf[iout] = '\0';
+
+ /* store number of bytes from data that was used */
+ *buflen = iin;
+
+ return iout;
+}
+
+#define REV32(x) rev32[(int) (x)]
+
+static int
+base32_decode(void *buf, size_t *buflen, const char *str, size_t slen)
+/*
+ * Fills *buf with max. *buflen bytes, decoded from slen chars in *str.
+ * Decoding stops early when *str contains \0.
+ * Illegal encoded chars are assumed to decode to zero.
+ *
+ * NOTE: *buf space should be at least 1 byte _more_ than *buflen
+ * to hold a trailing '\0' that is added (though *buf will usually
+ * contain full-binary data).
+ *
+ * return value : #bytes filled in buf (excluding \0)
+ */
+{
+ unsigned char *ubuf = (unsigned char *) buf;
+ int iout = 0; /* to-be-filled output byte */
+ int iin = 0; /* next input char to use in decoding */
+
+ base32_reverse_init ();
+
+ /* Note: Don't bother to optimize manually. GCC optimizes
+ better(!) when using simplistic array indexing. */
+
+ while (1) {
+ if (iout >= *buflen || iin + 1 >= slen ||
+ str[iin] == '\0' || str[iin + 1] == '\0')
+ break;
+ ubuf[iout] = ((REV32(str[iin]) & 0x1f) << 3) |
+ ((REV32(str[iin + 1]) & 0x1c) >> 2);
+ iin++; /* 0 used up, iin=1 */
+ iout++;
+
+ if (iout >= *buflen || iin + 2 >= slen ||
+ str[iin] == '\0' || str[iin + 1] == '\0' ||
+ str[iin + 2] == '\0')
+ break;
+ ubuf[iout] = ((REV32(str[iin]) & 0x03) << 6) |
+ ((REV32(str[iin + 1]) & 0x1f) << 1) |
+ ((REV32(str[iin + 2]) & 0x10) >> 4);
+ iin += 2; /* 1,2 used up, iin=3 */
+ iout++;
+
+ if (iout >= *buflen || iin + 1 >= slen ||
+ str[iin] == '\0' || str[iin + 1] == '\0')
+ break;
+ ubuf[iout] = ((REV32(str[iin]) & 0x0f) << 4) |
+ ((REV32(str[iin + 1]) & 0x1e) >> 1);
+ iin++; /* 3 used up, iin=4 */
+ iout++;
+
+ if (iout >= *buflen || iin + 2 >= slen ||
+ str[iin] == '\0' || str[iin + 1] == '\0' ||
+ str[iin + 2] == '\0')
+ break;
+ ubuf[iout] = ((REV32(str[iin]) & 0x01) << 7) |
+ ((REV32(str[iin + 1]) & 0x1f) << 2) |
+ ((REV32(str[iin + 2]) & 0x18) >> 3);
+ iin += 2; /* 4,5 used up, iin=6 */
+ iout++;
+
+ if (iout >= *buflen || iin + 1 >= slen ||
+ str[iin] == '\0' || str[iin + 1] == '\0')
+ break;
+ ubuf[iout] = ((REV32(str[iin]) & 0x07) << 5) |
+ ((REV32(str[iin + 1]) & 0x1f));
+ iin += 2; /* 6,7 used up, iin=8 */
+ iout++;
+ }
+
+ ubuf[iout] = '\0';
+
+ return iout;
+}
diff --git a/jni/iodine/src/base32.h b/jni/iodine/src/base32.h
new file mode 100644
index 0000000..497ca33
--- /dev/null
+++ b/jni/iodine/src/base32.h
@@ -0,0 +1,24 @@
+/*
+ * 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 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 __BASE32_H__
+#define __BASE32_H__
+
+struct encoder *get_base32_encoder(void);
+
+int b32_5to8(int);
+int b32_8to5(int);
+#endif
diff --git a/jni/iodine/src/base64.c b/jni/iodine/src/base64.c
new file mode 100644
index 0000000..5218c09
--- /dev/null
+++ b/jni/iodine/src/base64.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
+ * Mostly rewritten 2009 J.A.Bezemer@opensourcepartners.nl
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "encoding.h"
+#include "base64.h"
+
+#define BLKSIZE_RAW 3
+#define BLKSIZE_ENC 4
+
+/* Note: the "unofficial" char is last here, which means that the \377 pattern
+ in DOWNCODECCHECK1 ('Y' request) will properly test it. */
+static const char cb64[] =
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789+";
+static unsigned char rev64[256];
+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();
+
+static struct encoder base64_encoder =
+{
+ "Base64",
+ base64_encode,
+ base64_decode,
+ base64_handles_dots,
+ base64_handles_dots,
+ base64_blksize_raw,
+ base64_blksize_enc
+};
+
+struct encoder
+*get_base64_encoder()
+{
+ return &base64_encoder;
+}
+
+static int
+base64_handles_dots()
+{
+ return 0;
+}
+
+static int
+base64_blksize_raw()
+{
+ return BLKSIZE_RAW;
+}
+
+static int
+base64_blksize_enc()
+{
+ return BLKSIZE_ENC;
+}
+
+inline static void
+base64_reverse_init()
+{
+ int i;
+ unsigned char c;
+
+ if (!reverse_init) {
+ memset (rev64, 0, 256);
+ for (i = 0; i < 64; i++) {
+ c = cb64[i];
+ rev64[(int) c] = i;
+ }
+ reverse_init = 1;
+ }
+}
+
+static int
+base64_encode(char *buf, size_t *buflen, const void *data, size_t size)
+/*
+ * Fills *buf with max. *buflen characters, encoding size bytes of *data.
+ *
+ * NOTE: *buf space should be at least 1 byte _more_ than *buflen
+ * to hold the trailing '\0'.
+ *
+ * return value : #bytes filled in buf (excluding \0)
+ * sets *buflen to : #bytes encoded from data
+ */
+{
+ unsigned char *udata = (unsigned char *) data;
+ int iout = 0; /* to-be-filled output char */
+ int iin = 0; /* one more than last input byte that can be
+ successfully decoded */
+
+ /* Note: Don't bother to optimize manually. GCC optimizes
+ better(!) when using simplistic array indexing. */
+
+ while (1) {
+ if (iout >= *buflen || iin >= size)
+ break;
+ buf[iout] = cb64[((udata[iin] & 0xfc) >> 2)];
+ iout++;
+
+ if (iout >= *buflen || iin >= size) {
+ iout--; /* previous char is useless */
+ break;
+ }
+ buf[iout] = cb64[((udata[iin] & 0x03) << 4) |
+ ((iin + 1 < size) ?
+ ((udata[iin + 1] & 0xf0) >> 4) : 0)];
+ iin++; /* 0 complete, iin=1 */
+ iout++;
+
+ if (iout >= *buflen || iin >= size)
+ break;
+ buf[iout] = cb64[((udata[iin] & 0x0f) << 2 ) |
+ ((iin + 1 < size) ?
+ ((udata[iin + 1] & 0xc0) >> 6) : 0)];
+ iin++; /* 1 complete, iin=2 */
+ iout++;
+
+ if (iout >= *buflen || iin >= size)
+ break;
+ buf[iout] = cb64[(udata[iin] & 0x3f)];
+ iin++; /* 2 complete, iin=3 */
+ iout++;
+ }
+
+ buf[iout] = '\0';
+
+ /* store number of bytes from data that was used */
+ *buflen = iin;
+
+ return iout;
+}
+
+#define REV64(x) rev64[(int) (x)]
+
+static int
+base64_decode(void *buf, size_t *buflen, const char *str, size_t slen)
+/*
+ * Fills *buf with max. *buflen bytes, decoded from slen chars in *str.
+ * Decoding stops early when *str contains \0.
+ * Illegal encoded chars are assumed to decode to zero.
+ *
+ * NOTE: *buf space should be at least 1 byte _more_ than *buflen
+ * to hold a trailing '\0' that is added (though *buf will usually
+ * contain full-binary data).
+ *
+ * return value : #bytes filled in buf (excluding \0)
+ */
+{
+ unsigned char *ubuf = (unsigned char *) buf;
+ int iout = 0; /* to-be-filled output byte */
+ int iin = 0; /* next input char to use in decoding */
+
+ base64_reverse_init ();
+
+ /* Note: Don't bother to optimize manually. GCC optimizes
+ better(!) when using simplistic array indexing. */
+
+ while (1) {
+ if (iout >= *buflen || iin + 1 >= slen ||
+ str[iin] == '\0' || str[iin + 1] == '\0')
+ break;
+ ubuf[iout] = ((REV64(str[iin]) & 0x3f) << 2) |
+ ((REV64(str[iin + 1]) & 0x30) >> 4);
+ iin++; /* 0 used up, iin=1 */
+ iout++;
+
+ if (iout >= *buflen || iin + 1 >= slen ||
+ str[iin] == '\0' || str[iin + 1] == '\0')
+ break;
+ ubuf[iout] = ((REV64(str[iin]) & 0x0f) << 4) |
+ ((REV64(str[iin + 1]) & 0x3c) >> 2);
+ iin++; /* 1 used up, iin=2 */
+ iout++;
+
+ if (iout >= *buflen || iin + 1 >= slen ||
+ str[iin] == '\0' || str[iin + 1] == '\0')
+ break;
+ ubuf[iout] = ((REV64(str[iin]) & 0x03) << 6) |
+ (REV64(str[iin + 1]) & 0x3f);
+ iin += 2; /* 2,3 used up, iin=4 */
+ iout++;
+ }
+
+ ubuf[iout] = '\0';
+
+ return iout;
+}
diff --git a/jni/iodine/src/base64.h b/jni/iodine/src/base64.h
new file mode 100644
index 0000000..d550cf3
--- /dev/null
+++ b/jni/iodine/src/base64.h
@@ -0,0 +1,22 @@
+/*
+ * 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 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 __BASE64_H__
+#define __BASE64_H__
+
+struct encoder *get_base64_encoder(void);
+
+#endif
diff --git a/jni/iodine/src/client.c b/jni/iodine/src/client.c
new file mode 100644
index 0000000..552344b
--- /dev/null
+++ b/jni/iodine/src/client.c
@@ -0,0 +1,2527 @@
+/*
+ * 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 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 <ctype.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <zlib.h>
+#include <time.h>
+
+#ifdef WINDOWS32
+#include "windows.h"
+#include <winsock2.h>
+#else
+#include <arpa/nameser.h>
+#ifdef DARWIN
+#define BIND_8_COMPAT
+#include <arpa/nameser_compat.h>
+#endif
+#include <grp.h>
+#include <pwd.h>
+#include <netdb.h>
+#endif
+
+#include "common.h"
+#include "encoding.h"
+#include "base32.h"
+#include "base64.h"
+#include "base64u.h"
+#include "base128.h"
+#include "dns.h"
+#include "login.h"
+#include "tun.h"
+#include "version.h"
+#include "client.h"
+
+static void handshake_lazyoff(int dns_fd);
+
+static int running;
+static const char *password;
+
+static struct sockaddr_in nameserv;
+static struct sockaddr_in raw_serv;
+static const char *topdomain;
+
+static uint16_t rand_seed;
+
+/* Current up/downstream IP packet */
+static struct packet outpkt;
+static struct packet inpkt;
+int outchunkresent = 0;
+
+/* My userid at the server */
+static char userid;
+static char userid_char; /* used when sending (lowercase) */
+static char userid_char2; /* also accepted when receiving (uppercase) */
+
+/* DNS id for next packet */
+static uint16_t chunkid;
+static uint16_t chunkid_prev;
+static uint16_t chunkid_prev2;
+
+/* Base32 encoder used for non-data packets and replies */
+static struct encoder *b32;
+/* Base64 etc encoders for replies */
+static struct encoder *b64;
+static struct encoder *b64u;
+static struct encoder *b128;
+
+/* The encoder used for data packets
+ * Defaults to Base32, can be changed after handshake */
+static struct encoder *dataenc;
+
+/* The encoder to use for downstream data */
+static char downenc = ' ';
+
+/* set query type to send */
+static unsigned short do_qtype = T_UNSET;
+
+/* My connection mode */
+static enum connection conn;
+
+static int selecttimeout; /* RFC says timeout minimum 5sec */
+static int lazymode;
+static long send_ping_soon;
+static time_t lastdownstreamtime;
+static long send_query_sendcnt = -1;
+static long send_query_recvcnt = 0;
+static int hostname_maxlen = 0xFF;
+
+void
+client_init()
+{
+ running = 1;
+ b32 = get_base32_encoder();
+ b64 = get_base64_encoder();
+ b64u = get_base64u_encoder();
+ b128 = get_base128_encoder();
+ dataenc = get_base32_encoder();
+ rand_seed = ((unsigned int) rand()) & 0xFFFF;
+ send_ping_soon = 1; /* send ping immediately after startup */
+ conn = CONN_DNS_NULL;
+
+ chunkid = ((unsigned int) rand()) & 0xFFFF;
+ chunkid_prev = 0;
+ chunkid_prev2 = 0;
+
+ outpkt.len = 0;
+ outpkt.seqno = 0;
+ outpkt.fragment = 0;
+ outchunkresent = 0;
+ inpkt.len = 0;
+ inpkt.seqno = 0;
+ inpkt.fragment = 0;
+}
+
+void
+client_stop()
+{
+ running = 0;
+}
+
+enum connection
+client_get_conn()
+{
+ return conn;
+}
+
+void
+client_set_nameserver(const char *cp, int port)
+{
+ struct in_addr addr;
+
+ if (inet_aton(cp, &addr) != 1) {
+ /* try resolving if a domain is given */
+ struct hostent *host;
+ const char *err;
+ host = gethostbyname(cp);
+ if (host != NULL && h_errno > 0) {
+ int i = 0;
+ while (host->h_addr_list[i] != 0) {
+ addr = *(struct in_addr *) host->h_addr_list[i++];
+ fprintf(stderr, "Resolved %s to %s\n", cp, inet_ntoa(addr));
+ goto setaddr;
+ }
+ }
+#ifndef WINDOWS32
+ err = hstrerror(h_errno);
+#else
+ {
+ DWORD wserr = WSAGetLastError();
+ switch (wserr) {
+ case WSAHOST_NOT_FOUND:
+ err = "Host not found";
+ break;
+ case WSANO_DATA:
+ err = "No data record found";
+ break;
+ default:
+ err = "Unknown error";
+ break;
+ }
+ }
+#endif /* !WINDOWS32 */
+ errx(1, "error resolving nameserver '%s': %s", cp, err);
+ }
+
+setaddr:
+ memset(&nameserv, 0, sizeof(nameserv));
+ nameserv.sin_family = AF_INET;
+ nameserv.sin_port = htons(port);
+ nameserv.sin_addr = addr;
+}
+
+void
+client_set_topdomain(const char *cp)
+{
+ topdomain = cp;
+}
+
+void
+client_set_password(const char *cp)
+{
+ password = cp;
+}
+
+void
+set_qtype(char *qtype)
+{
+ if (!strcasecmp(qtype, "NULL"))
+ do_qtype = T_NULL;
+ else if (!strcasecmp(qtype, "CNAME"))
+ do_qtype = T_CNAME;
+ else if (!strcasecmp(qtype, "A"))
+ do_qtype = T_A;
+ else if (!strcasecmp(qtype, "MX"))
+ do_qtype = T_MX;
+ else if (!strcasecmp(qtype, "SRV"))
+ do_qtype = T_SRV;
+ else if (!strcasecmp(qtype, "TXT"))
+ do_qtype = T_TXT;
+}
+
+char *
+get_qtype()
+{
+ char *c = "UNDEFINED";
+
+ if (do_qtype == T_NULL) c = "NULL";
+ else if (do_qtype == T_CNAME) c = "CNAME";
+ else if (do_qtype == T_A) c = "A";
+ else if (do_qtype == T_MX) c = "MX";
+ else if (do_qtype == T_SRV) c = "SRV";
+ else if (do_qtype == T_TXT) c = "TXT";
+
+ return c;
+}
+
+void
+set_downenc(char *encoding)
+{
+ if (!strcasecmp(encoding, "base32"))
+ downenc = 'T';
+ else if (!strcasecmp(encoding, "base64"))
+ downenc = 'S';
+ else if (!strcasecmp(encoding, "base64u"))
+ downenc = 'U';
+ else if (!strcasecmp(encoding, "base128"))
+ downenc = 'V';
+ else if (!strcasecmp(encoding, "raw"))
+ downenc = 'R';
+}
+
+void
+client_set_selecttimeout(int select_timeout)
+{
+ selecttimeout = select_timeout;
+}
+
+void
+client_set_lazymode(int lazy_mode)
+{
+ lazymode = lazy_mode;
+}
+
+void
+client_set_hostname_maxlen(int i)
+{
+ if (i <= 0xFF)
+ hostname_maxlen = i;
+}
+
+const char *
+client_get_raw_addr()
+{
+ return inet_ntoa(raw_serv.sin_addr);
+}
+
+static void
+send_query(int fd, char *hostname)
+{
+ char packet[4096];
+ struct query q;
+ size_t len;
+
+ chunkid_prev2 = chunkid_prev;
+ chunkid_prev = chunkid;
+ chunkid += 7727;
+ if (chunkid == 0)
+ /* 0 is used as "no-query" in iodined.c */
+ chunkid = 7727;
+
+ q.id = chunkid;
+ q.type = do_qtype;
+
+ len = dns_encode(packet, sizeof(packet), &q, QR_QUERY, hostname, strlen(hostname));
+ if (len < 1) {
+ warnx("dns_encode doesn't fit");
+ return;
+ }
+
+#if 0
+ fprintf(stderr, " Sendquery: id %5d name[0] '%c'\n", q.id, hostname[0]);
+#endif
+
+ sendto(fd, packet, len, 0, (struct sockaddr*)&nameserv, sizeof(nameserv));
+
+ /* There are DNS relays that time out quickly but don't send anything
+ back on timeout.
+ And there are relays where, in lazy mode, our new query apparently
+ _replaces_ our previous query, and we get no answers at all in
+ lazy mode while legacy immediate-ping-pong works just fine.
+ Here we detect and fix these situations.
+ (Can't very well do this anywhere else; this is the only place
+ we'll reliably get to in such situations.)
+ */
+
+ if (send_query_sendcnt >= 0 && send_query_sendcnt < 100 && lazymode) {
+ send_query_sendcnt++;
+
+ if ((send_query_sendcnt > 6 && send_query_recvcnt <= 0) ||
+ (send_query_sendcnt > 10 &&
+ 4 * send_query_recvcnt < send_query_sendcnt)) {
+ if (selecttimeout > 1) {
+ warnx("Receiving too few answers. Setting interval to 1 (-I1)");
+ selecttimeout = 1;
+ /* restart counting */
+ send_query_sendcnt = 0;
+ send_query_recvcnt = 0;
+ } else if (lazymode) {
+ warnx("Receiving too few answers. Will try to switch lazy mode off, but that may not always work any more. Start with -L0 next time on this network.");
+ lazymode = 0;
+ selecttimeout = 1;
+ handshake_lazyoff(fd);
+ }
+ }
+ }
+}
+
+static void
+send_raw(int fd, char *buf, int buflen, int user, int cmd)
+{
+ char packet[4096];
+ int len;
+
+ len = MIN(sizeof(packet) - RAW_HDR_LEN, buflen);
+
+ memcpy(packet, raw_header, RAW_HDR_LEN);
+ if (len) {
+ memcpy(&packet[RAW_HDR_LEN], buf, len);
+ }
+
+ len += RAW_HDR_LEN;
+ packet[RAW_HDR_CMD] = cmd | (user & 0x0F);
+
+ sendto(fd, packet, len, 0, (struct sockaddr*)&raw_serv, sizeof(raw_serv));
+}
+
+static void
+send_raw_data(int dns_fd)
+{
+ send_raw(dns_fd, outpkt.data, outpkt.len, userid, RAW_HDR_CMD_DATA);
+ outpkt.len = 0;
+}
+
+
+static void
+send_packet(int fd, char cmd, const char *data, const size_t datalen)
+{
+ char buf[4096];
+
+ buf[0] = cmd;
+
+ build_hostname(buf + 1, sizeof(buf) - 1, data, datalen, topdomain,
+ b32, hostname_maxlen);
+ send_query(fd, buf);
+}
+
+static inline int
+is_sending()
+{
+ return (outpkt.len != 0);
+}
+
+static void
+send_chunk(int fd)
+{
+ char buf[4096];
+ int avail;
+ int code;
+ char *p;
+ static int datacmc = 0;
+ char *datacmcchars = "abcdefghijklmnopqrstuvwxyz0123456789";
+
+ p = outpkt.data;
+ p += outpkt.offset;
+ avail = outpkt.len - outpkt.offset;
+
+ /* Note: must be same, or smaller than send_fragsize_probe() */
+ outpkt.sentlen = build_hostname(buf + 5, sizeof(buf) - 5, p, avail,
+ topdomain, dataenc, hostname_maxlen);
+
+ /* Build upstream data header (see doc/proto_xxxxxxxx.txt) */
+
+ buf[0] = userid_char; /* First byte is hex 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) | (inpkt.seqno & 7);
+ buf[2] = b32_5to8(code); /* Third byte is 2 bits lower fragment count, 3 bits downstream packet seqno */
+
+ code = ((inpkt.fragment & 15) << 1) | (outpkt.sentlen == avail);
+ buf[3] = b32_5to8(code); /* Fourth byte is 4 bits downstream fragment count, 1 bit last frag flag */
+
+ buf[4] = datacmcchars[datacmc]; /* Fifth byte is data-CMC */
+ datacmc++;
+ if (datacmc >= 36)
+ datacmc = 0;
+
+#if 0
+ fprintf(stderr, " Send: down %d/%d up %d/%d, %d bytes\n",
+ inpkt.seqno, inpkt.fragment, outpkt.seqno, outpkt.fragment,
+ outpkt.sentlen);
+#endif
+
+ send_query(fd, buf);
+}
+
+static void
+send_ping(int fd)
+{
+ if (conn == CONN_DNS_NULL) {
+ char data[4];
+
+ data[0] = userid;
+ data[1] = ((inpkt.seqno & 7) << 4) | (inpkt.fragment & 15);
+ data[2] = (rand_seed >> 8) & 0xff;
+ data[3] = (rand_seed >> 0) & 0xff;
+
+ rand_seed++;
+
+#if 0
+ fprintf(stderr, " Send: down %d/%d (ping)\n",
+ inpkt.seqno, inpkt.fragment);
+#endif
+
+ send_packet(fd, 'p', data, sizeof(data));
+ } else {
+ send_raw(fd, NULL, 0, userid, RAW_HDR_CMD_PING);
+ }
+}
+
+static void
+write_dns_error(struct query *q, int ignore_some_errors)
+/* This is called from:
+ 1. handshake_waitdns() when already checked that reply fits to our
+ latest query.
+ 2. tunnel_dns() when already checked that reply is for our ping or data
+ packet, but not necessarily the most recent (SERVFAIL mostly comes
+ after long delay).
+ So ignorable errors are never printed.
+*/
+{
+ if (!q) return;
+
+ switch (q->rcode) {
+ case NOERROR: /* 0 */
+ if (!ignore_some_errors)
+ warnx("Got reply without error, but also without question and/or answer");
+ break;
+ case FORMERR: /* 1 */
+ warnx("Got FORMERR as reply: server does not understand our request");
+ break;
+ case SERVFAIL: /* 2 */
+ if (!ignore_some_errors)
+ warnx("Got SERVFAIL as reply: server failed or recursion timeout");
+ break;
+ case NXDOMAIN: /* 3 */
+ warnx("Got NXDOMAIN as reply: domain does not exist");
+ break;
+ case NOTIMP: /* 4 */
+ warnx("Got NOTIMP as reply: server does not support our request");
+ break;
+ case REFUSED: /* 5 */
+ warnx("Got REFUSED as reply");
+ break;
+ default:
+ warnx("Got RCODE %u as reply", q->rcode);
+ break;
+ }
+}
+
+static int
+dns_namedec(char *outdata, int outdatalen, char *buf, int buflen)
+/* Decodes *buf to *outdata.
+ * *buf WILL be changed by undotify.
+ * Note: buflen must be _exactly_ strlen(buf) before undotifying.
+ * (undotify of reduced-len won't copy \0, base-X decode will decode too much.)
+ * Returns #bytes usefully filled in outdata.
+ */
+{
+ size_t outdatalenu = outdatalen;
+
+ switch (buf[0]) {
+ case 'h': /* Hostname with base32 */
+ case 'H':
+ /* Need 1 byte H, 3 bytes ".xy", >=1 byte data */
+ if (buflen < 5)
+ return 0;
+
+ /* this also does undotify */
+ return unpack_data(outdata, outdatalen, buf + 1, buflen - 4,
+ b32);
+
+ case 'i': /* Hostname++ with base64 */
+ case 'I':
+ /* Need 1 byte I, 3 bytes ".xy", >=1 byte data */
+ if (buflen < 5)
+ return 0;
+
+ /* this also does undotify */
+ return unpack_data(outdata, outdatalen, buf + 1, buflen - 4,
+ b64);
+
+ case 'j': /* Hostname++ with base64u */
+ case 'J':
+ /* Need 1 byte J, 3 bytes ".xy", >=1 byte data */
+ if (buflen < 5)
+ return 0;
+
+ /* this also does undotify */
+ return unpack_data(outdata, outdatalen, buf + 1, buflen - 4,
+ b64u);
+
+ case 'k': /* Hostname++ with base128 */
+ case 'K':
+ /* Need 1 byte J, 3 bytes ".xy", >=1 byte data */
+ if (buflen < 5)
+ return 0;
+
+ /* this also does undotify */
+ return unpack_data(outdata, outdatalen, buf + 1, buflen - 4,
+ b128);
+
+ case 't': /* plain base32(Thirty-two) from TXT */
+ case 'T':
+ if (buflen < 2)
+ return 0;
+
+ return b32->decode(outdata, &outdatalenu, buf + 1, buflen - 1);
+
+ case 's': /* plain base64(Sixty-four) from TXT */
+ case 'S':
+ if (buflen < 2)
+ return 0;
+
+ return b64->decode(outdata, &outdatalenu, buf + 1, buflen - 1);
+
+ case 'u': /* plain base64u (Underscore) from TXT */
+ case 'U':
+ if (buflen < 2)
+ return 0;
+
+ return b64u->decode(outdata, &outdatalenu, buf + 1, buflen - 1);
+
+ case 'v': /* plain base128 from TXT */
+ case 'V':
+ if (buflen < 2)
+ return 0;
+
+ return b128->decode(outdata, &outdatalenu, buf + 1, buflen - 1);
+
+ case 'r': /* Raw binary from TXT */
+ case 'R':
+ /* buflen>=1 already checked */
+ buflen--;
+ buflen = MIN(buflen, outdatalen);
+ memcpy(outdata, buf + 1, buflen);
+ return buflen;
+
+ default:
+ warnx("Received unsupported encoding");
+ return 0;
+ }
+
+ /* notreached */
+ return 0;
+}
+
+static int
+read_dns_withq(int dns_fd, int tun_fd, char *buf, int buflen, struct query *q)
+/* FIXME: tun_fd needed for raw handling */
+/* Returns -1 on receive error or decode error, including DNS error replies.
+ Returns 0 on replies that could be correct but are useless, and are not
+ DNS error replies.
+ Returns >0 on correct replies; value is #valid bytes in *buf.
+*/
+{
+ struct sockaddr_in from;
+ char data[64*1024];
+ socklen_t addrlen;
+ int r;
+
+ addrlen = sizeof(struct sockaddr);
+ if ((r = recvfrom(dns_fd, data, sizeof(data), 0,
+ (struct sockaddr*)&from, &addrlen)) < 0) {
+ warn("recvfrom");
+ return -1;
+ }
+
+ if (conn == CONN_DNS_NULL) {
+ int rv;
+ if (r <= 0)
+ /* useless packet */
+ return 0;
+
+ rv = dns_decode(buf, buflen, q, QR_ANSWER, data, r);
+ if (rv <= 0)
+ return rv;
+
+ if (q->type == T_CNAME || q->type == T_TXT)
+ /* CNAME can also be returned from an A question */
+ {
+ /*
+ * buf is a hostname or txt stream that we still need to
+ * decode to binary
+ *
+ * also update rv with the number of valid bytes
+ *
+ * data is unused here, and will certainly hold the smaller binary
+ */
+
+ rv = dns_namedec(data, sizeof(data), buf, rv);
+
+ rv = MIN(rv, buflen);
+ if (rv > 0)
+ memcpy(buf, data, rv);
+
+ } else if (q->type == T_MX || q->type == T_SRV) {
+ /* buf is like "Hname.com\0Hanother.com\0\0" */
+ int buftotal = rv; /* idx of last \0 */
+ int bufoffset = 0;
+ int dataoffset = 0;
+ int thispartlen, dataspace, datanew;
+
+ while (1) {
+ thispartlen = strlen(buf);
+ thispartlen = MIN(thispartlen, buftotal-bufoffset);
+ dataspace = sizeof(data) - dataoffset;
+ if (thispartlen <= 0 || dataspace <= 0)
+ break;
+
+ datanew = dns_namedec(data + dataoffset, dataspace,
+ buf + bufoffset, thispartlen);
+ if (datanew <= 0)
+ break;
+
+ bufoffset += thispartlen + 1;
+ dataoffset += datanew;
+ }
+ rv = dataoffset;
+ rv = MIN(rv, buflen);
+ if (rv > 0)
+ memcpy(buf, data, rv);
+ }
+
+ return rv;
+ } else { /* CONN_RAW_UDP */
+ unsigned long datalen;
+ char buf[64*1024];
+
+ /* minimum length */
+ if (r < RAW_HDR_LEN) return 0;
+ /* should start with header */
+ if (memcmp(data, raw_header, RAW_HDR_IDENT_LEN)) return 0;
+ /* should be my user id */
+ if (RAW_HDR_GET_USR(data) != userid) return 0;
+
+ if (RAW_HDR_GET_CMD(data) == RAW_HDR_CMD_DATA ||
+ RAW_HDR_GET_CMD(data) == RAW_HDR_CMD_PING)
+ lastdownstreamtime = time(NULL);
+
+ /* should be data packet */
+ if (RAW_HDR_GET_CMD(data) != RAW_HDR_CMD_DATA) return 0;
+
+ r -= RAW_HDR_LEN;
+ datalen = sizeof(buf);
+ if (uncompress((uint8_t*)buf, &datalen, (uint8_t*) &data[RAW_HDR_LEN], r) == Z_OK) {
+ write_tun(tun_fd, buf, datalen);
+ }
+
+ /* don't process any further */
+ return 0;
+ }
+}
+
+static int
+handshake_waitdns(int dns_fd, char *buf, int buflen, char c1, char c2, int timeout)
+/* Wait for DNS reply fitting to our latest query and returns it.
+ Returns length of reply = #bytes used in buf.
+ Returns 0 if fitting reply happens to be useless.
+ Returns -2 on (at least) DNS error that fits to our latest query,
+ error message already printed.
+ Returns -3 on timeout (given in seconds).
+ Returns -1 on other errors.
+
+ Timeout is restarted when "wrong" (previous/delayed) replies are received,
+ so effective timeout may be longer than specified.
+*/
+{
+ struct query q;
+ int r, rv;
+ fd_set fds;
+ struct timeval tv;
+
+ while (1) {
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ FD_ZERO(&fds);
+ FD_SET(dns_fd, &fds);
+ r = select(dns_fd + 1, &fds, NULL, NULL, &tv);
+
+ if (r < 0)
+ return -1; /* select error */
+ if (r == 0)
+ return -3; /* select timeout */
+
+ q.id = 0;
+ q.name[0] = '\0';
+ rv = read_dns_withq(dns_fd, 0, buf, buflen, &q);
+
+ if (q.id != chunkid || (q.name[0] != c1 && q.name[0] != c2)) {
+#if 0
+ fprintf(stderr, "Ignoring unfitting reply id %d starting with '%c'\n", q.id, q.name[0]);
+#endif
+ continue;
+ }
+
+ /* if still here: reply matches our latest query */
+
+ /* Non-recursive DNS servers (such as [a-m].root-servers.net)
+ return no answer, but only additional and authority records.
+ Can't explicitly test for that here, just assume that
+ NOERROR is such situation. Only trigger on the very first
+ requests (Y or V, depending if -T given).
+ */
+ if (rv < 0 && q.rcode == NOERROR &&
+ (q.name[0] == 'Y' || q.name[0] == 'y' ||
+ q.name[0] == 'V' || q.name[0] == 'v')) {
+ fprintf(stderr, "Got empty reply. This nameserver may not be resolving recursively, use another.\n");
+ fprintf(stderr, "Try \"iodine [options] ns.%s %s\" first, it might just work.\n",
+ topdomain, topdomain);
+ return -2;
+ }
+
+ /* If we get an immediate SERVFAIL on the handshake query
+ we're waiting for, wait a while before sending the next.
+ SERVFAIL reliably happens during fragsize autoprobe, but
+ mostly long after we've moved along to some other queries.
+ However, some DNS relays, once they throw a SERVFAIL, will
+ for several seconds apply it immediately to _any_ new query
+ for the same topdomain. When this happens, waiting a while
+ is the only option that works.
+ */
+ if (rv < 0 && q.rcode == SERVFAIL)
+ sleep(1);
+
+ if (rv < 0) {
+ write_dns_error(&q, 1);
+ return -2;
+ }
+ /* rv either 0 or >0, return it as is. */
+ return rv;
+ }
+
+ /* not reached */
+ return -1;
+}
+
+static int
+tunnel_tun(int tun_fd, int dns_fd)
+{
+ unsigned long outlen;
+ unsigned long inlen;
+ char out[64*1024];
+ char in[64*1024];
+ ssize_t read;
+
+ if ((read = read_tun(tun_fd, in, sizeof(in))) <= 0)
+ return -1;
+
+ /* We may be here only to empty the tun device; then return -1
+ to force continue in select loop. */
+ if (is_sending())
+ return -1;
+
+ outlen = sizeof(out);
+ inlen = read;
+ compress2((uint8_t*)out, &outlen, (uint8_t*)in, inlen, 9);
+
+ memcpy(outpkt.data, out, MIN(outlen, sizeof(outpkt.data)));
+ outpkt.sentlen = 0;
+ outpkt.offset = 0;
+ outpkt.seqno = (outpkt.seqno + 1) & 7;
+ outpkt.len = outlen;
+ outpkt.fragment = 0;
+ outchunkresent = 0;
+
+ if (conn == CONN_DNS_NULL) {
+ send_chunk(dns_fd);
+
+ send_ping_soon = 0;
+ } else {
+ send_raw_data(dns_fd);
+ }
+
+ return read;
+}
+
+static int
+tunnel_dns(int tun_fd, int dns_fd)
+{
+ static long packrecv = 0;
+ static long packrecv_oos = 0;
+ static long packrecv_servfail = 0;
+ int up_ack_seqno;
+ int up_ack_fragment;
+ int new_down_seqno;
+ int new_down_fragment;
+ struct query q;
+ unsigned long datalen;
+ char buf[64*1024];
+ int read;
+ int send_something_now = 0;
+
+ memset(q.name, 0, sizeof(q.name));
+ read = read_dns_withq(dns_fd, tun_fd, buf, sizeof(buf), &q);
+
+ if (conn != CONN_DNS_NULL)
+ return 1; /* everything already done */
+
+#if 0
+ fprintf(stderr, " Recv: id %5d name[0]='%c'\n",
+ q.id, q.name[0]);
+#endif
+
+ /* Don't process anything that isn't data for us; usually error
+ replies from fragsize probes etc. However a sequence of those,
+ mostly 1 sec apart, will continuously break the >=2-second select
+ timeout, which means we won't send a proper ping for a while.
+ So make select a bit faster, <1sec. */
+ if (q.name[0] != 'P' && q.name[0] != 'p' &&
+ q.name[0] != userid_char && q.name[0] != userid_char2) {
+ send_ping_soon = 700;
+ return -1; /* nothing done */
+ }
+
+ if (read < 2) {
+ /* Maybe SERVFAIL etc. Send ping to get things back in order,
+ but wait a bit to prevent fast ping-pong loops. */
+
+ if (read < 0)
+ write_dns_error(&q, 0);
+
+ if (read < 0 && q.rcode == SERVFAIL && lazymode &&
+ selecttimeout > 1) {
+ if (packrecv < 500 && packrecv_servfail < 4) {
+ packrecv_servfail++;
+ warnx("Hmm, that's %ld. Your data should still go through...", packrecv_servfail);
+ } else if (packrecv < 500 && packrecv_servfail == 4) {
+ packrecv_servfail++;
+ warnx("I think %ld is too many. Setting interval to 1 to hopefully reduce SERVFAILs. But just ignore them if data still comes through. (Use -I1 next time on this network.)", packrecv_servfail);
+ selecttimeout = 1;
+ send_query_sendcnt = 0;
+ send_query_recvcnt = 0;
+ } else if (packrecv >= 500 && packrecv_servfail > 0) {
+ warnx("(Sorry, stopped counting; try -I1 if you experience hiccups.)");
+ packrecv_servfail = 0;
+ }
+ }
+
+ /* read==1 happens with "QMEM" illegal replies, caused by
+ heavy reordering, or after short disconnections when
+ data-CMC has looped around into the "duplicate" values.
+ All these cases are helped by faster pinging. */
+#if 0
+ if (read == 1)
+ fprintf(stderr, " q=%c id %5d 1-byte illegal \"QMEM\" reply\n", q.name[0], q.id);
+#endif
+
+ send_ping_soon = 900;
+ return -1; /* nothing done */
+ }
+
+ if (read == 5 && !strncmp("BADIP", buf, 5)) {
+ warnx("BADIP: Server rejected sender IP address (maybe iodined -c will help), or server kicked us due to timeout. Will exit if no downstream data is received in 60 seconds.");
+ return -1; /* nothing done */
+ }
+
+ if (send_ping_soon) {
+ send_something_now = 1;
+ send_ping_soon = 0;
+ }
+
+ /* Decode the data header, update seqno and frag;
+ already checked read>=2
+ Note that buf[] gets overwritten when down-pkt complete */
+ new_down_seqno = (buf[1] >> 5) & 7;
+ new_down_fragment = (buf[1] >> 1) & 15;
+ up_ack_seqno = (buf[0] >> 4) & 7;
+ up_ack_fragment = buf[0] & 15;
+
+#if 0
+ fprintf(stderr, " Recv: id %5d down %d/%d up %d/%d, %d bytes\n",
+ q.id, new_down_seqno, new_down_fragment, up_ack_seqno,
+ up_ack_fragment, read);
+#endif
+
+ /* Downstream data traffic */
+
+ if (read > 2 && new_down_seqno != inpkt.seqno &&
+ recent_seqno(inpkt.seqno, new_down_seqno)) {
+ /* This is the previous seqno, or a bit earlier.
+ Probably out-of-sequence dupe due to unreliable
+ intermediary DNS. Don't get distracted, but send
+ ping quickly to get things back in order.
+ Ping will send our current seqno idea.
+ If it's really a new packet that skipped multiple seqnos
+ (why??), server will re-send and drop a few times and
+ eventually everything will work again. */
+ read = 2;
+ send_ping_soon = 500;
+ /* Still process upstream ack, if any */
+ }
+
+ if (!(packrecv & 0x1000000))
+ packrecv++;
+ send_query_recvcnt++; /* overflow doesn't matter */
+
+ /* Don't process any non-recent stuff any further.
+ No need to remember more than 3 ids: in practice any older replies
+ arrive after new/current replies, and whatever data the old replies
+ have, it has become useless in the mean time.
+ Actually, ever since iodined is replying to both the original query
+ and the last dupe, this hardly triggers any more.
+ */
+ if (q.id != chunkid && q.id != chunkid_prev && q.id != chunkid_prev2) {
+ packrecv_oos++;
+#if 0
+ fprintf(stderr, " q=%c Packs received = %8ld Out-of-sequence = %8ld\n", q.name[0], packrecv, packrecv_oos);
+#endif
+ if (lazymode && packrecv < 1000 && packrecv_oos == 5) {
+ if (selecttimeout > 1)
+ warnx("Hmm, getting some out-of-sequence DNS replies. Setting interval to 1 (use -I1 next time on this network). If data traffic still has large hiccups, try if -L0 works better.");
+ else
+ warnx("Hmm, getting some out-of-sequence DNS replies. If data traffic often has large hiccups, try running with -L0 .");
+ selecttimeout = 1;
+ send_query_sendcnt = 0;
+ send_query_recvcnt = 0;
+ }
+
+ if (send_something_now) {
+ send_ping(dns_fd);
+ send_ping_soon = 0;
+ }
+ return -1; /* nothing done */
+ }
+#if 0
+ fprintf(stderr, " q=%c Packs received = %8ld Out-of-sequence = %8ld\n", q.name[0], packrecv, packrecv_oos);
+#endif
+
+ /* Okay, we have a recent downstream packet */
+ lastdownstreamtime = time(NULL);
+
+ /* In lazy mode, we shouldn't get much replies to our most-recent
+ query, only during heavy data transfer. Since this means the server
+ doesn't have any packets left, send one relatively fast (but not
+ too fast, to avoid runaway ping-pong loops..) */
+ if (q.id == chunkid && lazymode) {
+ if (!send_ping_soon || send_ping_soon > 900)
+ send_ping_soon = 900;
+ }
+
+ if (read == 2 && new_down_seqno != inpkt.seqno &&
+ !recent_seqno(inpkt.seqno, new_down_seqno)) {
+ /* This is a seqno that we didn't see yet, but it has
+ no data any more. Possible since iodined will send
+ fitting packs just once and not wait for ack.
+ Real data got lost, or will arrive shortly.
+ Update our idea of the seqno, and drop any waiting
+ old pack. Send ping to get things back on track. */
+ inpkt.seqno = new_down_seqno;
+ inpkt.fragment = new_down_fragment;
+ inpkt.len = 0;
+ send_ping_soon = 500;
+ }
+
+ while (read > 2) {
+ /* "if" with easy exit */
+
+ if (new_down_seqno != inpkt.seqno) {
+ /* New packet (and not dupe of recent; checked above) */
+ /* Forget any old packet, even if incomplete */
+ inpkt.seqno = new_down_seqno;
+ inpkt.fragment = new_down_fragment; /* hopefully 0 */
+ inpkt.len = 0;
+ } else if (inpkt.fragment == 0 && new_down_fragment == 0 &&
+ inpkt.len == 0) {
+ /* Weird situation: we probably got a no-data reply
+ for this seqno (see above), and the actual data
+ is following now. */
+ /* okay, nothing to do here, just so that next else-if
+ doesn't trigger */
+ } else if (new_down_fragment <= inpkt.fragment) {
+ /* Same packet but duplicate fragment, ignore.
+ If the server didn't get our ack for it, the next
+ ping or chunk will do that. */
+ send_ping_soon = 500;
+ break;
+ } else if (new_down_fragment > inpkt.fragment + 1) {
+ /* Quite impossible. We missed a fragment, but the
+ server got our ack for it and is sending the next
+ fragment already. Don't handle it but let server
+ re-send and drop. */
+ send_ping_soon = 500;
+ break;
+ }
+ inpkt.fragment = new_down_fragment;
+
+ datalen = MIN(read - 2, sizeof(inpkt.data) - inpkt.len);
+
+ /* we are here only when read > 2, so datalen "always" >=1 */
+
+ /* 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 */
+ /* RE-USES buf[] */
+ 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;
+ /* Keep .seqno and .fragment as is, so that we won't
+ reassemble from duplicate fragments */
+ }
+
+ /* Send anything to ack the received seqno/frag, and get more */
+ if (inpkt.len == 0) {
+ /* was last frag; wait just a trifle because our
+ tun will probably return TCP-ack immediately.
+ 5msec = 200 DNSreq/sec */
+ send_ping_soon = 5;
+ } else {
+ /* server certainly has more data */
+ send_something_now = 1;
+ }
+
+ break;
+ }
+
+ /* NOTE: buf[] was overwritten when down-packet complete */
+
+
+ /* Upstream data traffic */
+
+ if (is_sending()) {
+ /* already checked read>=2 */
+#if 0
+ fprintf(stderr, "Got ack for %d,%d - expecting %d,%d - id=%d cur=%d prev=%d prev2=%d\n",
+ up_ack_seqno, up_ack_fragment, outpkt.seqno, outpkt.fragment,
+ q.id, chunkid, chunkid_prev, chunkid_prev2);
+#endif
+
+ if (up_ack_seqno == outpkt.seqno &&
+ up_ack_fragment == outpkt.fragment) {
+ /* Okay, previously sent fragment has arrived */
+
+ outpkt.offset += outpkt.sentlen;
+ if (outpkt.offset >= outpkt.len) {
+ /* Packet completed */
+ outpkt.offset = 0;
+ outpkt.len = 0;
+ outpkt.sentlen = 0;
+ outchunkresent = 0;
+
+ /* Normally, server still has a query in queue,
+ but sometimes not. So send a ping.
+ (Comment this out and you'll see occasional
+ hiccups.)
+ But since the server often still has a
+ query and we can expect a TCP-ack returned
+ from our tun device quickly in many cases,
+ don't be too fast.
+ 20msec still is 50 DNSreq/second... */
+ if (!send_ping_soon || send_ping_soon > 20)
+ send_ping_soon = 20;
+ } else {
+ /* More to send */
+ outpkt.fragment++;
+ outchunkresent = 0;
+ send_chunk(dns_fd);
+ send_ping_soon = 0;
+ send_something_now = 0;
+ }
+ }
+ /* else: Some wrong fragment has arrived, or old fragment is
+ acked again, mostly by ping responses.
+ Don't resend chunk, usually not needed; select loop will
+ re-send on timeout (1sec if is_sending()). */
+ }
+
+
+ /* Send ping if we didn't send anything yet */
+ if (send_something_now) {
+ send_ping(dns_fd);
+ send_ping_soon = 0;
+ }
+
+ return read;
+}
+
+static int
+always_1()
+{
+ return 1;
+}
+
+int
+client_tunnel(int tun_fd, int dns_fd)
+{
+ return client_tunnel_cb(tun_fd, dns_fd, &always_1);
+}
+
+int
+client_tunnel_cb(int tun_fd, int dns_fd, int
+ (*cb_ask_continue)())
+{
+ struct timeval tv;
+ fd_set fds;
+ int rv;
+ int i;
+
+ rv = 0;
+ lastdownstreamtime = time(NULL);
+ send_query_sendcnt = 0; /* start counting now */
+
+ while (running && cb_ask_continue()) {
+ tv.tv_sec = selecttimeout;
+ tv.tv_usec = 0;
+
+ if (is_sending()) {
+ /* fast timeout for retransmits */
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ }
+
+ if (send_ping_soon) {
+ tv.tv_sec = 0;
+ tv.tv_usec = send_ping_soon * 1000;
+ }
+
+ FD_ZERO(&fds);
+ if (!is_sending() || outchunkresent >= 2) {
+ /* If re-sending upstream data, chances are that
+ we're several seconds behind already and TCP
+ will start filling tun buffer with (useless)
+ retransmits.
+ Get up-to-date fast by simply dropping stuff,
+ that's what TCP is designed to handle. */
+ FD_SET(tun_fd, &fds);
+ }
+ FD_SET(dns_fd, &fds);
+
+ i = select(MAX(tun_fd, dns_fd) + 1, &fds, NULL, NULL, &tv);
+
+ if (lastdownstreamtime + 60 < time(NULL)) {
+ warnx("No downstream data received in 60 seconds, shutting down.");
+ running = 0;
+ }
+
+ if (running == 0)
+ break;
+
+ if (i < 0)
+ err(1, "select");
+
+ if (i == 0) {
+ /* timeout */
+ if (is_sending()) {
+ /* Re-send current fragment; either frag
+ or ack probably dropped somewhere.
+ But problem: no cache-miss-counter,
+ so hostname will be identical.
+ Just drop whole packet after 3 retries,
+ and TCP retransmit will solve it.
+ NOTE: tun dropping above should be
+ >=(value_here - 1) */
+ if (outchunkresent < 3) {
+ outchunkresent++;
+ send_chunk(dns_fd);
+ } else {
+ outpkt.offset = 0;
+ outpkt.len = 0;
+ outpkt.sentlen = 0;
+ outchunkresent = 0;
+
+ send_ping(dns_fd);
+ }
+ } else {
+ send_ping(dns_fd);
+ }
+ send_ping_soon = 0;
+
+ } else {
+
+ if (FD_ISSET(tun_fd, &fds)) {
+ if (tunnel_tun(tun_fd, dns_fd) <= 0)
+ continue;
+ /* Returns -1 on error OR when quickly
+ dropping data in case of DNS congestion;
+ we need to _not_ do tunnel_dns() then.
+ If chunk sent, sets send_ping_soon=0. */
+ }
+ if (FD_ISSET(dns_fd, &fds)) {
+ if (tunnel_dns(tun_fd, dns_fd) <= 0)
+ continue;
+ }
+ }
+ }
+
+ return rv;
+}
+
+static void
+send_login(int fd, char *login, int len)
+{
+ char data[19];
+
+ memset(data, 0, sizeof(data));
+ data[0] = userid;
+ memcpy(&data[1], login, MIN(len, 16));
+
+ data[17] = (rand_seed >> 8) & 0xff;
+ data[18] = (rand_seed >> 0) & 0xff;
+
+ rand_seed++;
+
+ send_packet(fd, 'l', data, sizeof(data));
+}
+
+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,
+ * will also take up maximal space in the return packet
+ */
+ memset(probedata, MAX(1, rand_seed & 0xff), sizeof(probedata));
+ probedata[1] = MAX(1, (rand_seed >> 8) & 0xff);
+ rand_seed++;
+
+ /* Note: must either be same, or larger, than send_chunk() */
+ build_hostname(buf + 5, sizeof(buf) - 5, probedata, sizeof(probedata),
+ topdomain, dataenc, hostname_maxlen);
+
+ fragsize &= 2047;
+
+ buf[0] = 'r'; /* Probe downstream fragsize packet */
+ buf[1] = b32_5to8((userid << 1) | ((fragsize >> 10) & 1));
+ buf[2] = b32_5to8((fragsize >> 5) & 31);
+ buf[3] = b32_5to8(fragsize & 31);
+ buf[4] = 'd'; /* dummy to match send_chunk() */
+
+ 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];
+
+ data[0] = (version >> 24) & 0xff;
+ data[1] = (version >> 16) & 0xff;
+ data[2] = (version >> 8) & 0xff;
+ data[3] = (version >> 0) & 0xff;
+
+ data[4] = (rand_seed >> 8) & 0xff;
+ data[5] = (rand_seed >> 0) & 0xff;
+
+ rand_seed++;
+
+ send_packet(fd, 'v', data, sizeof(data));
+}
+
+static void
+send_ip_request(int fd, int userid)
+{
+ char buf[512] = "i____.";
+ buf[1] = b32_5to8(userid);
+
+ buf[2] = b32_5to8((rand_seed >> 10) & 0x1f);
+ buf[3] = b32_5to8((rand_seed >> 5) & 0x1f);
+ buf[4] = b32_5to8((rand_seed ) & 0x1f);
+ rand_seed++;
+
+ strncat(buf, topdomain, 512 - strlen(buf));
+ send_query(fd, buf);
+}
+
+static void
+send_raw_udp_login(int dns_fd, int userid, int seed)
+{
+ char buf[16];
+ login_calculate(buf, 16, password, seed + 1);
+
+ send_raw(dns_fd, buf, sizeof(buf), userid, RAW_HDR_CMD_LOGIN);
+}
+
+static void
+send_upenctest(int fd, char *s)
+/* NOTE: String may be at most 63-4=59 chars to fit in 1 dns chunk. */
+{
+ char buf[512] = "z___";
+
+ buf[1] = b32_5to8((rand_seed >> 10) & 0x1f);
+ buf[2] = b32_5to8((rand_seed >> 5) & 0x1f);
+ buf[3] = b32_5to8((rand_seed ) & 0x1f);
+ rand_seed++;
+
+ strncat(buf, s, 512);
+ strncat(buf, ".", 512);
+ strncat(buf, topdomain, 512 - strlen(buf));
+ send_query(fd, buf);
+}
+
+static void
+send_downenctest(int fd, char downenc, int variant, char *s, int slen)
+/* Note: content/handling of s is not defined yet. */
+{
+ char buf[512] = "y_____.";
+
+ buf[1] = tolower(downenc);
+ buf[2] = b32_5to8(variant);
+
+ buf[3] = b32_5to8((rand_seed >> 10) & 0x1f);
+ buf[4] = b32_5to8((rand_seed >> 5) & 0x1f);
+ buf[5] = b32_5to8((rand_seed ) & 0x1f);
+ rand_seed++;
+
+ 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);
+
+ buf[3] = b32_5to8((rand_seed >> 10) & 0x1f);
+ buf[4] = b32_5to8((rand_seed >> 5) & 0x1f);
+ buf[5] = b32_5to8((rand_seed ) & 0x1f);
+ rand_seed++;
+
+ strncat(buf, topdomain, 512 - strlen(buf));
+ send_query(fd, buf);
+}
+
+
+static void
+send_downenc_switch(int fd, int userid)
+{
+ char buf[512] = "o_____.";
+ buf[1] = b32_5to8(userid);
+ buf[2] = tolower(downenc);
+
+ buf[3] = b32_5to8((rand_seed >> 10) & 0x1f);
+ buf[4] = b32_5to8((rand_seed >> 5) & 0x1f);
+ buf[5] = b32_5to8((rand_seed ) & 0x1f);
+ rand_seed++;
+
+ strncat(buf, topdomain, 512 - strlen(buf));
+ send_query(fd, buf);
+}
+
+static void
+send_lazy_switch(int fd, int userid)
+{
+ char buf[512] = "o_____.";
+ buf[1] = b32_5to8(userid);
+
+ if (lazymode)
+ buf[2] = 'l';
+ else
+ buf[2] = 'i';
+
+ buf[3] = b32_5to8((rand_seed >> 10) & 0x1f);
+ buf[4] = b32_5to8((rand_seed >> 5) & 0x1f);
+ buf[5] = b32_5to8((rand_seed ) & 0x1f);
+ rand_seed++;
+
+ strncat(buf, topdomain, 512 - strlen(buf));
+ send_query(fd, buf);
+}
+
+static int
+handshake_version(int dns_fd, int *seed)
+{
+ char hex[] = "0123456789abcdef";
+ char hex2[] = "0123456789ABCDEF";
+ char in[4096];
+ uint32_t payload;
+ int i;
+ int read;
+
+ for (i = 0; running && i < 5; i++) {
+
+ send_version(dns_fd, VERSION);
+
+ read = handshake_waitdns(dns_fd, in, sizeof(in), 'v', 'V', i+1);
+
+ /*XXX START adjust indent 1 tab back*/
+ if (read >= 9) {
+ payload = (((in[4] & 0xff) << 24) |
+ ((in[5] & 0xff) << 16) |
+ ((in[6] & 0xff) << 8) |
+ ((in[7] & 0xff)));
+
+ if (strncmp("VACK", in, 4) == 0) {
+ *seed = payload;
+ userid = in[8];
+ userid_char = hex[userid & 15];
+ userid_char2 = hex2[userid & 15];
+
+ fprintf(stderr, "Version ok, both using protocol v 0x%08x. You are user #%d\n", VERSION, userid);
+ return 0;
+ } else if (strncmp("VNAK", in, 4) == 0) {
+ warnx("You use protocol v 0x%08x, server uses v 0x%08x. Giving up",
+ VERSION, payload);
+ return 1;
+ } else if (strncmp("VFUL", in, 4) == 0) {
+ warnx("Server full, all %d slots are taken. Try again later", payload);
+ return 1;
+ }
+ } else if (read > 0)
+ warnx("did not receive proper login challenge");
+ /*XXX END adjust indent 1 tab back*/
+
+ fprintf(stderr, "Retrying version check...\n");
+ }
+ warnx("couldn't connect to server (maybe other -T options will work)");
+ return 1;
+}
+
+static int
+handshake_login(int dns_fd, int seed)
+{
+ char in[4096];
+ char login[16];
+ char server[65];
+ char client[65];
+ int mtu;
+ int i;
+ int read;
+
+ login_calculate(login, 16, password, seed);
+
+ for (i=0; running && i<5 ;i++) {
+
+ send_login(dns_fd, login, 16);
+
+ read = handshake_waitdns(dns_fd, in, sizeof(in), 'l', 'L', i+1);
+
+ /*XXX START adjust indent 1 tab back*/
+ if (read > 0) {
+ int netmask;
+ if (strncmp("LNAK", in, 4) == 0) {
+ fprintf(stderr, "Bad password\n");
+ return 1;
+ } else if (sscanf(in, "%64[^-]-%64[^-]-%d-%d",
+ server, client, &mtu, &netmask) == 4) {
+
+ server[64] = 0;
+ client[64] = 0;
+ if (tun_setip(client, server, netmask) == 0 &&
+ tun_setmtu(mtu) == 0) {
+
+ fprintf(stderr, "Server tunnel IP is %s\n", server);
+ return 0;
+ } else {
+ errx(4, "Failed to set IP and MTU");
+ }
+ } else {
+ fprintf(stderr, "Received bad handshake\n");
+ }
+ }
+ /*XXX END adjust indent 1 tab back*/
+
+ fprintf(stderr, "Retrying login...\n");
+ }
+ warnx("couldn't login to server");
+ return 1;
+}
+
+static int
+handshake_raw_udp(int dns_fd, int seed)
+{
+ struct timeval tv;
+ char in[4096];
+ fd_set fds;
+ int i;
+ int r;
+ int len;
+ unsigned remoteaddr = 0;
+ struct in_addr server;
+
+ fprintf(stderr, "Testing raw UDP data to the server (skip with -r)");
+ for (i=0; running && i<3 ;i++) {
+
+ send_ip_request(dns_fd, userid);
+
+ len = handshake_waitdns(dns_fd, in, sizeof(in), 'i', 'I', i+1);
+
+ /*XXX START adjust indent 1 tab back*/
+ if (len == 5 && in[0] == 'I') {
+ /* Received IP address */
+ remoteaddr = (in[1] & 0xff);
+ remoteaddr <<= 8;
+ remoteaddr |= (in[2] & 0xff);
+ remoteaddr <<= 8;
+ remoteaddr |= (in[3] & 0xff);
+ remoteaddr <<= 8;
+ remoteaddr |= (in[4] & 0xff);
+ server.s_addr = ntohl(remoteaddr);
+ break;
+ }
+ /*XXX END adjust indent 1 tab back*/
+
+ fprintf(stderr, ".");
+ fflush(stderr);
+ }
+ fprintf(stderr, "\n");
+ if (!running)
+ return 0;
+
+ if (!remoteaddr) {
+ fprintf(stderr, "Failed to get raw server IP, will use DNS mode.\n");
+ return 0;
+ }
+ fprintf(stderr, "Server is at %s, trying raw login: ", inet_ntoa(server));
+ fflush(stderr);
+
+ /* Store address to iodined server */
+ memset(&raw_serv, 0, sizeof(raw_serv));
+ raw_serv.sin_family = AF_INET;
+ raw_serv.sin_port = htons(53);
+ raw_serv.sin_addr = server;
+
+ /* do login against port 53 on remote server
+ * based on the old seed. If reply received,
+ * switch to raw udp mode */
+ for (i=0; running && i<4 ;i++) {
+ tv.tv_sec = i + 1;
+ tv.tv_usec = 0;
+
+ send_raw_udp_login(dns_fd, userid, seed);
+
+ FD_ZERO(&fds);
+ FD_SET(dns_fd, &fds);
+
+ r = select(dns_fd + 1, &fds, NULL, NULL, &tv);
+
+ if(r > 0) {
+ /* recv() needed for windows, dont change to read() */
+ len = recv(dns_fd, in, sizeof(in), 0);
+ if (len >= (16 + RAW_HDR_LEN)) {
+ char hash[16];
+ login_calculate(hash, 16, password, seed - 1);
+ if (memcmp(in, raw_header, RAW_HDR_IDENT_LEN) == 0
+ && RAW_HDR_GET_CMD(in) == RAW_HDR_CMD_LOGIN
+ && memcmp(&in[RAW_HDR_LEN], hash, sizeof(hash)) == 0) {
+
+ fprintf(stderr, "OK\n");
+ return 1;
+ }
+ }
+ }
+ fprintf(stderr, ".");
+ fflush(stderr);
+ }
+
+ fprintf(stderr, "failed\n");
+ return 0;
+}
+
+static int
+handshake_upenctest(int dns_fd, char *s)
+/* NOTE: *s may be max 59 chars; must start with "aA" for case-swap check
+ Returns:
+ -1: case swap, no need for any further test: error printed; or Ctrl-C
+ 0: not identical or error or timeout
+ 1: identical string returned
+*/
+{
+ char in[4096];
+ unsigned char *uin = (unsigned char *) in;
+ unsigned char *us = (unsigned char *) s;
+ int i;
+ int read;
+ int slen;
+
+ slen = strlen(s);
+ for (i=0; running && i<3 ;i++) {
+
+ send_upenctest(dns_fd, s);
+
+ read = handshake_waitdns(dns_fd, in, sizeof(in), 'z', 'Z', i+1);
+
+ if (read == -2)
+ return 0; /* hard error */
+
+ if (read > 0 && read < slen + 4)
+ return 0; /* reply too short (chars dropped) */
+
+ if (read > 0) {
+ int k;
+#if 0
+ /* in[56] = '@'; */
+ /* in[56] = '_'; */
+ /* if (in[29] == '\344') in[29] = 'a'; */
+ in[read] = '\0';
+ fprintf(stderr, "BounceReply: >%s<\n", in);
+#endif
+ /* quick check if case swapped, to give informative error msg */
+ if (in[4] == 'A') {
+ fprintf(stderr, "DNS queries get changed to uppercase, keeping upstream codec Base32\n");
+ return -1;
+ }
+ if (in[5] == 'a') {
+ fprintf(stderr, "DNS queries get changed to lowercase, keeping upstream codec Base32\n");
+ return -1;
+ }
+
+ for (k = 0; k < slen; k++) {
+ if (in[k+4] != s[k]) {
+ /* Definitely not reliable */
+ if (in[k+4] >= ' ' && in[k+4] <= '~' &&
+ s[k] >= ' ' && s[k] <= '~') {
+ fprintf(stderr, "DNS query char '%c' gets changed into '%c'\n",
+ s[k], in[k+4]);
+ } else {
+ fprintf(stderr, "DNS query char 0x%02X gets changed into 0x%02X\n",
+ (unsigned int) us[k],
+ (unsigned int) uin[k+4]);
+ }
+ return 0;
+ }
+ }
+ /* if still here, then all okay */
+ return 1;
+ }
+
+ fprintf(stderr, "Retrying upstream codec test...\n");
+ }
+
+ if (!running)
+ return -1;
+
+ /* timeout */
+ return 0;
+}
+
+static int
+handshake_upenc_autodetect(int dns_fd)
+/* Returns:
+ 0: keep Base32
+ 1: Base64 is okay
+ 2: Base64u is okay
+ 3: Base128 is okay
+*/
+{
+ /* Note: max 59 chars, must start with "aA".
+ pat64: If 0129 work, assume 3-8 are okay too.
+
+ RFC1035 par 2.3.1 states that [A-Z0-9-] allowed, but only
+ [A-Z] as first, and [A-Z0-9] as last char _per label_.
+ Test by having '-' as last char.
+ */
+ char *pat64="aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ+0129-";
+ char *pat64u="aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ_0129-";
+ char *pat128a="aA-Aaahhh-Drink-mal-ein-J\344germeister-";
+ char *pat128b="aA-La-fl\373te-na\357ve-fran\347aise-est-retir\351-\340-Cr\350te";
+ char *pat128c="aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ";
+ char *pat128d="aA0123456789\274\275\276\277"
+ "\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317";
+ char *pat128e="aA"
+ "\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337"
+ "\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357"
+ "\360\361\362\363\364\365\366\367\370\371\372\373\374\375";
+ int res;
+
+ /* Try Base128, starting very gently to not draw attention */
+ while (1) {
+ res = handshake_upenctest(dns_fd, pat128a);
+ if (res < 0) {
+ /* DNS swaps case, msg already printed; or Ctrl-C */
+ return 0;
+ } else if (res == 0) {
+ /* Probably not okay, skip Base128 entirely */
+ break;
+ }
+
+ res = handshake_upenctest(dns_fd, pat128b);
+ if (res < 0)
+ return 0;
+ else if (res == 0)
+ break;
+
+ /* if this works, we can test the real stuff */
+
+ res = handshake_upenctest(dns_fd, pat128c);
+ if (res < 0)
+ return 0;
+ else if (res == 0)
+ break;
+
+ res = handshake_upenctest(dns_fd, pat128d);
+ if (res < 0)
+ return 0;
+ else if (res == 0)
+ break;
+
+ res = handshake_upenctest(dns_fd, pat128e);
+ if (res < 0)
+ return 0;
+ else if (res == 0)
+ break;
+
+ /* if still here, then base128 works completely */
+ return 3;
+ }
+
+ /* Try Base64 (with plus sign) */
+ res = handshake_upenctest(dns_fd, pat64);
+ if (res < 0) {
+ /* DNS swaps case, msg already printed; or Ctrl-C */
+ return 0;
+ } else if (res > 0) {
+ /* All okay, Base64 msg will be printed later */
+ return 1;
+ }
+
+ /* Try Base64u (with _u_nderscore) */
+ res = handshake_upenctest(dns_fd, pat64u);
+ if (res < 0) {
+ /* DNS swaps case, msg already printed; or Ctrl-C */
+ return 0;
+ } else if (res > 0) {
+ /* All okay, Base64u msg will be printed later */
+ return 2;
+ }
+
+ /* if here, then nonthing worked */
+ fprintf(stderr, "Keeping upstream codec Base32\n");
+ return 0;
+}
+
+static int
+handshake_downenctest(int dns_fd, char trycodec)
+/* Returns:
+ 0: not identical or error or timeout
+ 1: identical string returned
+*/
+{
+ char in[4096];
+ int i;
+ int read;
+ char *s = DOWNCODECCHECK1;
+ int slen = DOWNCODECCHECK1_LEN;
+
+ for (i=0; running && i<3 ;i++) {
+
+ send_downenctest(dns_fd, trycodec, 1, NULL, 0);
+
+ read = handshake_waitdns(dns_fd, in, sizeof(in), 'y', 'Y', i+1);
+
+ if (read == -2)
+ return 0; /* hard error */
+
+ if (read > 0 && read != slen)
+ return 0; /* reply incorrect = unreliable */
+
+ if (read > 0) {
+ int k;
+ for (k = 0; k < slen; k++) {
+ if (in[k] != s[k]) {
+ /* Definitely not reliable */
+ return 0;
+ }
+ }
+ /* if still here, then all okay */
+ return 1;
+ }
+
+ fprintf(stderr, "Retrying downstream codec test...\n");
+ }
+
+ /* timeout */
+ return 0;
+}
+
+static char
+handshake_downenc_autodetect(int dns_fd)
+/* Returns codec char (or ' ' if no advanced codec works) */
+{
+ int base64ok = 0;
+ int base64uok = 0;
+ int base128ok = 0;
+
+ if (do_qtype == T_NULL) {
+ /* no other choice than raw */
+ fprintf(stderr, "No alternative downstream codec available, using default (Raw)\n");
+ return ' ';
+ }
+
+ fprintf(stderr, "Autodetecting downstream codec (use -O to override)\n");
+
+ /* Try Base64 */
+ if (handshake_downenctest(dns_fd, 'S'))
+ base64ok = 1;
+ else if (running && handshake_downenctest(dns_fd, 'U'))
+ base64uok = 1;
+
+ /* Try Base128 only if 64 gives us some perspective */
+ if (running && (base64ok || base64uok)) {
+ if (handshake_downenctest(dns_fd, 'V'))
+ base128ok = 1;
+ }
+
+ /* If 128 works, then TXT may give us Raw as well */
+ if (running && (base128ok && do_qtype == T_TXT)) {
+ if (handshake_downenctest(dns_fd, 'R'))
+ return 'R';
+ }
+
+ if (!running)
+ return ' ';
+
+ if (base128ok)
+ return 'V';
+ if (base64ok)
+ return 'S';
+ if (base64uok)
+ return 'U';
+
+ fprintf(stderr, "No advanced downstream codecs seem to work, using default (Base32)\n");
+ return ' ';
+}
+
+static int
+handshake_qtypetest(int dns_fd, int timeout)
+/* Returns:
+ 0: doesn't work with this timeout
+ 1: works properly
+*/
+{
+ char in[4096];
+ int read;
+ char *s = DOWNCODECCHECK1;
+ int slen = DOWNCODECCHECK1_LEN;
+ int trycodec;
+ int k;
+
+ if (do_qtype == T_NULL)
+ trycodec = 'R';
+ else
+ trycodec = 'T';
+
+ /* We could use 'Z' bouncing here, but 'Y' also tests that 0-255
+ byte values can be returned, which is needed for NULL to work. */
+
+ send_downenctest(dns_fd, trycodec, 1, NULL, 0);
+
+ read = handshake_waitdns(dns_fd, in, sizeof(in), 'y', 'Y', timeout);
+
+ if (read != slen)
+ return 0; /* incorrect */
+
+ for (k = 0; k < slen; k++) {
+ if (in[k] != s[k]) {
+ /* corrupted */
+ return 0;
+ }
+ }
+
+ /* if still here, then all okay */
+ return 1;
+}
+
+static int
+handshake_qtype_numcvt(int num)
+{
+ switch (num) {
+ case 0: return T_NULL;
+ case 1: return T_TXT;
+ case 2: return T_SRV;
+ case 3: return T_MX;
+ case 4: return T_CNAME;
+ case 5: return T_A;
+ }
+ return T_UNSET;
+}
+
+static int
+handshake_qtype_autodetect(int dns_fd)
+/* Returns:
+ 0: okay, do_qtype set
+ 1: problem, program exit
+*/
+{
+ int highestworking = 100;
+ int timeout;
+ int qtypenum;
+
+ fprintf(stderr, "Autodetecting DNS query type (use -T to override)");
+ fflush(stderr);
+
+ /* Method: try all "interesting" qtypes with a 1-sec timeout, then try
+ all "still-interesting" qtypes with a 2-sec timeout, etc.
+ "Interesting" means: qtypes that (are expected to) have higher
+ bandwidth than what we know is working already (highestworking).
+
+ Note that DNS relays may not immediately resolve the first (NULL)
+ query in 1 sec, due to long recursive lookups, so we keep trying
+ to see if things will start working after a while.
+ */
+
+ for (timeout = 1; running && timeout <= 3; timeout++) {
+ for (qtypenum = 0; running && qtypenum < highestworking; qtypenum++) {
+ do_qtype = handshake_qtype_numcvt(qtypenum);
+ if (do_qtype == T_UNSET)
+ break; /* this round finished */
+
+ fprintf(stderr, ".");
+ fflush(stderr);
+
+ if (handshake_qtypetest(dns_fd, timeout)) {
+ /* okay */
+ highestworking = qtypenum;
+#if 0
+ fprintf(stderr, " Type %s timeout %d works\n",
+ get_qtype(), timeout);
+#endif
+ break;
+ /* try others with longer timeout */
+ }
+ /* else: try next qtype with same timeout */
+ }
+ if (highestworking == 0)
+ /* good, we have NULL; abort immediately */
+ break;
+ }
+
+ fprintf(stderr, "\n");
+
+ if (!running) {
+ warnx("Stopped while autodetecting DNS query type (try setting manually with -T)");
+ return 1; /* problem */
+ }
+
+ /* finished */
+ do_qtype = handshake_qtype_numcvt(highestworking);
+
+ if (do_qtype == T_UNSET) {
+ /* also catches highestworking still 100 */
+ warnx("No suitable DNS query type found. Are you connected to a network?");
+ warnx("If you expect very long roundtrip delays, use -T explicitly.");
+ warnx("(Also, connecting to an \"ancient\" version of iodined won't work.)");
+ return 1; /* problem */
+ }
+
+ /* "using qtype" message printed in handshake function */
+ return 0; /* okay */
+}
+
+static int
+handshake_edns0_check(int dns_fd)
+/* Returns:
+ 0: EDNS0 not supported; or Ctrl-C
+ 1: EDNS0 works
+*/
+{
+ char in[4096];
+ int i;
+ int read;
+ char *s = DOWNCODECCHECK1;
+ int slen = DOWNCODECCHECK1_LEN;
+ char trycodec;
+
+ if (do_qtype == T_NULL)
+ trycodec = 'R';
+ else
+ trycodec = 'T';
+
+ for (i=0; running && i<3 ;i++) {
+
+ send_downenctest(dns_fd, trycodec, 1, NULL, 0);
+
+ read = handshake_waitdns(dns_fd, in, sizeof(in), 'y', 'Y', i+1);
+
+ if (read == -2)
+ return 0; /* hard error */
+
+ if (read > 0 && read != slen)
+ return 0; /* reply incorrect = unreliable */
+
+ if (read > 0) {
+ int k;
+ for (k = 0; k < slen; k++) {
+ if (in[k] != s[k]) {
+ /* Definitely not reliable */
+ return 0;
+ }
+ }
+ /* if still here, then all okay */
+ return 1;
+ }
+
+ fprintf(stderr, "Retrying EDNS0 support test...\n");
+ }
+
+ /* timeout or Ctrl-C */
+ return 0;
+}
+
+static void
+handshake_switch_codec(int dns_fd, int bits)
+{
+ char in[4096];
+ int i;
+ int read;
+ struct encoder *tempenc;
+
+ if (bits == 5)
+ tempenc = get_base32_encoder();
+ else if (bits == 6)
+ tempenc = get_base64_encoder();
+ else if (bits == 26) /* "2nd" 6 bits per byte, with underscore */
+ tempenc = get_base64u_encoder();
+ else if (bits == 7)
+ tempenc = get_base128_encoder();
+ else return;
+
+ fprintf(stderr, "Switching upstream to codec %s\n", tempenc->name);
+
+ for (i=0; running && i<5 ;i++) {
+
+ send_codec_switch(dns_fd, userid, bits);
+
+ read = handshake_waitdns(dns_fd, in, sizeof(in), 's', 'S', i+1);
+
+ /*XXX START adjust indent 1 tab back*/
+ if (read > 0) {
+ if (strncmp("BADLEN", in, 6) == 0) {
+ fprintf(stderr, "Server got bad message length. ");
+ goto codec_revert;
+ } else if (strncmp("BADIP", in, 5) == 0) {
+ fprintf(stderr, "Server rejected sender IP address. ");
+ goto codec_revert;
+ } else if (strncmp("BADCODEC", in, 8) == 0) {
+ fprintf(stderr, "Server rejected the selected codec. ");
+ goto codec_revert;
+ }
+ in[read] = 0; /* zero terminate */
+ fprintf(stderr, "Server switched upstream to codec %s\n", in);
+ dataenc = tempenc;
+ return;
+ }
+ /*XXX END adjust indent 1 tab back*/
+
+ fprintf(stderr, "Retrying codec switch...\n");
+ }
+ if (!running)
+ return;
+
+ fprintf(stderr, "No reply from server on codec switch. ");
+
+codec_revert:
+ fprintf(stderr, "Falling back to upstream codec %s\n", dataenc->name);
+}
+
+static void
+handshake_switch_downenc(int dns_fd)
+{
+ char in[4096];
+ int i;
+ int read;
+ char *dname;
+
+ dname = "Base32";
+ if (downenc == 'S')
+ dname = "Base64";
+ else if (downenc == 'U')
+ dname = "Base64u";
+ else if (downenc == 'V')
+ dname = "Base128";
+ else if (downenc == 'R')
+ dname = "Raw";
+
+ fprintf(stderr, "Switching downstream to codec %s\n", dname);
+ for (i=0; running && i<5 ;i++) {
+
+ send_downenc_switch(dns_fd, userid);
+
+ read = handshake_waitdns(dns_fd, in, sizeof(in), 'o', 'O', i+1);
+
+ /*XXX START adjust indent 1 tab back*/
+ if (read > 0) {
+ if (strncmp("BADLEN", in, 6) == 0) {
+ fprintf(stderr, "Server got bad message length. ");
+ goto codec_revert;
+ } else if (strncmp("BADIP", in, 5) == 0) {
+ fprintf(stderr, "Server rejected sender IP address. ");
+ goto codec_revert;
+ } else if (strncmp("BADCODEC", in, 8) == 0) {
+ fprintf(stderr, "Server rejected the selected codec. ");
+ goto codec_revert;
+ }
+ in[read] = 0; /* zero terminate */
+ fprintf(stderr, "Server switched downstream to codec %s\n", in);
+ return;
+ }
+ /*XXX END adjust indent 1 tab back*/
+
+ fprintf(stderr, "Retrying codec switch...\n");
+ }
+ if (!running)
+ return;
+
+ fprintf(stderr, "No reply from server on codec switch. ");
+
+codec_revert:
+ fprintf(stderr, "Falling back to downstream codec Base32\n");
+}
+
+static void
+handshake_try_lazy(int dns_fd)
+{
+ char in[4096];
+ int i;
+ int read;
+
+ fprintf(stderr, "Switching to lazy mode for low-latency\n");
+ for (i=0; running && i<5; i++) {
+
+ send_lazy_switch(dns_fd, userid);
+
+ read = handshake_waitdns(dns_fd, in, sizeof(in), 'o', 'O', i+1);
+
+ /*XXX START adjust indent 1 tab back*/
+ if (read > 0) {
+ if (strncmp("BADLEN", in, 6) == 0) {
+ fprintf(stderr, "Server got bad message length. ");
+ goto codec_revert;
+ } else if (strncmp("BADIP", in, 5) == 0) {
+ fprintf(stderr, "Server rejected sender IP address. ");
+ goto codec_revert;
+ } else if (strncmp("BADCODEC", in, 8) == 0) {
+ fprintf(stderr, "Server rejected lazy mode. ");
+ goto codec_revert;
+ } else if (strncmp("Lazy", in, 4) == 0) {
+ fprintf(stderr, "Server switched to lazy mode\n");
+ lazymode = 1;
+ return;
+ }
+ }
+ /*XXX END adjust indent 1 tab back*/
+
+ fprintf(stderr, "Retrying lazy mode switch...\n");
+ }
+ if (!running)
+ return;
+
+ fprintf(stderr, "No reply from server on lazy switch. ");
+
+codec_revert:
+ fprintf(stderr, "Falling back to legacy mode\n");
+ lazymode = 0;
+ selecttimeout = 1;
+}
+
+static void
+handshake_lazyoff(int dns_fd)
+/* Used in the middle of data transfer, timing is different and no error msgs */
+{
+ char in[4096];
+ int i;
+ int read;
+
+ for (i=0; running && i<5; i++) {
+
+ send_lazy_switch(dns_fd, userid);
+
+ read = handshake_waitdns(dns_fd, in, sizeof(in), 'o', 'O', 1);
+
+ /*XXX START adjust indent 2 tabs back*/
+ if (read == 9 && strncmp("Immediate", in, 9) == 0) {
+ warnx("Server switched back to legacy mode.\n");
+ lazymode = 0;
+ selecttimeout = 1;
+ return;
+ }
+ /*XXX END adjust indent 2 tabs back*/
+ }
+ if (!running)
+ return;
+
+ warnx("No reply from server on legacy mode switch.\n");
+}
+
+static int
+fragsize_check(char *in, int read, int proposed_fragsize, int *max_fragsize)
+/* Returns: 0: keep checking, 1: break loop (either okay or definitely wrong) */
+{
+ int acked_fragsize = ((in[0] & 0xff) << 8) | (in[1] & 0xff);
+ int okay;
+ int i;
+ unsigned int v;
+
+ if (read >= 5 && strncmp("BADIP", in, 5) == 0) {
+ fprintf(stderr, "got BADIP (Try iodined -c)..\n");
+ fflush(stderr);
+ return 0; /* maybe temporary error */
+ }
+
+ if (acked_fragsize != proposed_fragsize) {
+ /*
+ * got ack for wrong fragsize, maybe late response for
+ * earlier query, or ack corrupted
+ */
+ return 0;
+ }
+
+ if (read != proposed_fragsize) {
+ /*
+ * correctly acked fragsize but read too little (or too
+ * much): this fragsize is definitely not reliable
+ */
+ return 1;
+ }
+
+ /* here: read == proposed_fragsize == acked_fragsize */
+
+ /* test: */
+ /* in[123] = 123; */
+
+ if ((in[2] & 0xff) != 107) {
+ fprintf(stderr, "\n");
+ warnx("corruption at byte 2, this won't work. Try -O Base32, or other -T options.");
+ *max_fragsize = -1;
+ return 1;
+ }
+
+ /* Check for corruption */
+ okay = 1;
+ v = in[3] & 0xff;
+
+ /*XXX START adjust indent 1 tab back*/
+ for (i = 3; i < read; i++, v = (v + 107) & 0xff)
+ if ((in[i] & 0xff) != v) {
+ okay = 0;
+ break;
+ }
+
+ if (okay) {
+ fprintf(stderr, "%d ok.. ", acked_fragsize);
+ fflush(stderr);
+ *max_fragsize = acked_fragsize;
+ return 1;
+ } else {
+ if (downenc != ' ' && downenc != 'T') {
+ fprintf(stderr, "%d corrupted at %d.. (Try -O Base32)\n", acked_fragsize, i);
+ } else {
+ fprintf(stderr, "%d corrupted at %d.. ", acked_fragsize, i);
+ }
+ fflush(stderr);
+ return 1;
+ }
+ /*XXX END adjust indent 1 tab back*/
+
+ /* notreached */
+ return 1;
+}
+
+
+static int
+handshake_autoprobe_fragsize(int dns_fd)
+{
+ char in[4096];
+ int i;
+ int read;
+ int proposed_fragsize = 768;
+ int range = 768;
+ int max_fragsize;
+
+ max_fragsize = 0;
+ fprintf(stderr, "Autoprobing max downstream fragment size... (skip with -m fragsize)\n");
+ while (running && range > 0 && (range >= 8 || max_fragsize < 300)) {
+ /* stop the slow probing early when we have enough bytes anyway */
+ for (i=0; running && i<3 ;i++) {
+
+ send_fragsize_probe(dns_fd, proposed_fragsize);
+
+ read = handshake_waitdns(dns_fd, in, sizeof(in), 'r', 'R', 1);
+
+ /*XXX START adjust indent 1 tab back*/
+ if (read > 0) {
+ /* We got a reply */
+ if (fragsize_check(in, read, proposed_fragsize, &max_fragsize) == 1)
+ break;
+ }
+ /*XXX END adjust indent 1 tab back*/
+
+ fprintf(stderr, ".");
+ fflush(stderr);
+ }
+ if (max_fragsize < 0)
+ break;
+
+ range >>= 1;
+ if (max_fragsize == proposed_fragsize) {
+ /* Try bigger */
+ proposed_fragsize += range;
+ } else {
+ /* Try smaller */
+ fprintf(stderr, "%d not ok.. ", proposed_fragsize);
+ fflush(stderr);
+ proposed_fragsize -= range;
+ }
+ }
+ if (!running) {
+ fprintf(stderr, "\n");
+ warnx("stopped while autodetecting fragment size (Try setting manually with -m)");
+ return 0;
+ }
+ if (max_fragsize <= 2) {
+ /* Tried all the way down to 2 and found no good size.
+ But we _did_ do all handshake before this, so there must
+ be some workable connection. */
+ fprintf(stderr, "\n");
+ warnx("found no accepted fragment size.");
+ warnx("try setting -M to 200 or lower, or try other -T or -O options.");
+ return 0;
+ }
+ /* data header adds 2 bytes */
+ fprintf(stderr, "will use %d-2=%d\n", max_fragsize, max_fragsize - 2);
+
+ /* need 1200 / 16frags = 75 bytes fragsize */
+ if (max_fragsize < 82) {
+ fprintf(stderr, "Note: this probably won't work well.\n");
+ fprintf(stderr, "Try setting -M to 200 or lower, or try other DNS types (-T option).\n");
+ } else if (max_fragsize < 202 &&
+ (do_qtype == T_NULL || do_qtype == T_TXT ||
+ do_qtype == T_SRV || do_qtype == T_MX)) {
+ fprintf(stderr, "Note: this isn't very much.\n");
+ fprintf(stderr, "Try setting -M to 200 or lower, or try other DNS types (-T option).\n");
+ }
+
+ return max_fragsize - 2;
+}
+
+static void
+handshake_set_fragsize(int dns_fd, int fragsize)
+{
+ char in[4096];
+ int i;
+ int read;
+
+ fprintf(stderr, "Setting downstream fragment size to max %d...\n", fragsize);
+ for (i=0; running && i<5 ;i++) {
+
+ send_set_downstream_fragsize(dns_fd, fragsize);
+
+ read = handshake_waitdns(dns_fd, in, sizeof(in), 'n', 'N', i+1);
+
+ /*XXX START adjust indent 1 tab back*/
+ if (read > 0) {
+ int accepted_fragsize;
+
+ if (strncmp("BADFRAG", in, 7) == 0) {
+ fprintf(stderr, "Server rejected fragsize. Keeping default.");
+ return;
+ } else if (strncmp("BADIP", in, 5) == 0) {
+ fprintf(stderr, "Server rejected sender IP address.\n");
+ return;
+ }
+
+ accepted_fragsize = ((in[0] & 0xff) << 8) | (in[1] & 0xff);
+ return;
+ }
+ /*XXX END adjust indent 1 tab back*/
+
+ fprintf(stderr, "Retrying set fragsize...\n");
+ }
+ if (!running)
+ return;
+
+ fprintf(stderr, "No reply from server when setting fragsize. Keeping default.\n");
+}
+
+int
+client_handshake(int dns_fd, int raw_mode, int autodetect_frag_size, int fragsize)
+{
+ int seed;
+ int upcodec;
+ int r;
+
+ dnsc_use_edns0 = 0;
+
+ /* qtype message printed in handshake function */
+ if (do_qtype == T_UNSET) {
+ r = handshake_qtype_autodetect(dns_fd);
+ if (r) {
+ return r;
+ }
+ }
+
+ fprintf(stderr, "Using DNS type %s queries\n", get_qtype());
+
+ r = handshake_version(dns_fd, &seed);
+ if (r) {
+ return r;
+ }
+
+ r = handshake_login(dns_fd, seed);
+ if (r) {
+ return r;
+ }
+
+ if (raw_mode && handshake_raw_udp(dns_fd, seed)) {
+ conn = CONN_RAW_UDP;
+ selecttimeout = 20;
+ } else {
+ if (raw_mode == 0) {
+ fprintf(stderr, "Skipping raw mode\n");
+ }
+
+ dnsc_use_edns0 = 1;
+ if (handshake_edns0_check(dns_fd) && running) {
+ fprintf(stderr, "Using EDNS0 extension\n");
+ } else if (!running) {
+ return -1;
+ } else {
+ fprintf(stderr, "DNS relay does not support EDNS0 extension\n");
+ dnsc_use_edns0 = 0;
+ }
+
+ upcodec = handshake_upenc_autodetect(dns_fd);
+ if (!running)
+ return -1;
+
+ if (upcodec == 1) {
+ handshake_switch_codec(dns_fd, 6);
+ } else if (upcodec == 2) {
+ handshake_switch_codec(dns_fd, 26);
+ } else if (upcodec == 3) {
+ handshake_switch_codec(dns_fd, 7);
+ }
+ if (!running)
+ return -1;
+
+ if (downenc == ' ') {
+ downenc = handshake_downenc_autodetect(dns_fd);
+ }
+ if (!running)
+ return -1;
+
+ if (downenc != ' ') {
+ handshake_switch_downenc(dns_fd);
+ }
+ if (!running)
+ return -1;
+
+ if (lazymode) {
+ handshake_try_lazy(dns_fd);
+ }
+ if (!running)
+ return -1;
+
+ if (autodetect_frag_size) {
+ fragsize = handshake_autoprobe_fragsize(dns_fd);
+ if (!fragsize) {
+ return 1;
+ }
+ }
+
+ handshake_set_fragsize(dns_fd, fragsize);
+ if (!running)
+ return -1;
+ }
+
+ return 0;
+}
+
diff --git a/jni/iodine/src/client.h b/jni/iodine/src/client.h
new file mode 100644
index 0000000..e2d9501
--- /dev/null
+++ b/jni/iodine/src/client.h
@@ -0,0 +1,40 @@
+/*
+ * 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 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 __CLIENT_H__
+#define __CLIENT_H__
+
+void client_init();
+void client_stop();
+
+enum connection client_get_conn();
+const char *client_get_raw_addr();
+
+void client_set_nameserver(const char *cp, int port);
+void client_set_topdomain(const char *cp);
+void client_set_password(const char *cp);
+void set_qtype(char *qtype);
+char *get_qtype();
+void set_downenc(char *encoding);
+void client_set_selecttimeout(int select_timeout);
+void client_set_lazymode(int lazy_mode);
+void client_set_hostname_maxlen(int i);
+
+int client_handshake(int dns_fd, int raw_mode, int autodetect_frag_size, int fragsize);
+int client_tunnel(int tun_fd, int dns_fd);
+int client_tunnel_cb(int tun_fd, int dns_fd, int (*cb_ask_continue)());
+
+#endif
diff --git a/jni/iodine/src/common.c b/jni/iodine/src/common.c
new file mode 100644
index 0000000..7a57a8a
--- /dev/null
+++ b/jni/iodine/src/common.c
@@ -0,0 +1,381 @@
+/* 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
+ * 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 <time.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#ifdef WINDOWS32
+#include <winsock2.h>
+#include <conio.h>
+#else
+#include <arpa/nameser.h>
+#ifdef DARWIN
+#define BIND_8_COMPAT
+#include <arpa/nameser_compat.h>
+#endif
+#include <termios.h>
+#include <err.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <syslog.h>
+#endif
+
+#ifdef HAVE_SETCON
+# include <selinux/selinux.h>
+#endif
+
+#include "common.h"
+
+/* The raw header used when not using DNS protocol */
+const unsigned char raw_header[RAW_HDR_LEN] = { 0x10, 0xd1, 0x9e, 0x00 };
+
+/* daemon(3) exists only in 4.4BSD or later, and in GNU libc */
+#if !defined(WINDOWS32) && !(defined(BSD) && (BSD >= 199306)) && !defined(__GLIBC__) && !defined(__ANDROID__)
+static int daemon(int nochdir, int noclose)
+{
+ int fd, i;
+
+ switch (fork()) {
+ case 0:
+ break;
+ case -1:
+ return -1;
+ default:
+ _exit(0);
+ }
+
+ if (!nochdir) {
+ chdir("/");
+ }
+
+ if (setsid() < 0) {
+ return -1;
+ }
+
+ if (!noclose) {
+ if ((fd = open("/dev/null", O_RDWR)) >= 0) {
+ for (i = 0; i < 3; i++) {
+ dup2(fd, i);
+ }
+ if (fd > 2) {
+ close(fd);
+ }
+ }
+ }
+ return 0;
+}
+#endif
+
+#if defined(__BEOS__) && !defined(__HAIKU__)
+int setgroups(int count, int *groups)
+{
+ /* errno = ENOSYS; */
+ return -1;
+}
+#endif
+
+
+void
+check_superuser(void (*usage_fn)(void))
+{
+#ifndef WINDOWS32
+ if (geteuid() != 0) {
+ warnx("Run as root and you'll be happy.\n");
+ usage_fn();
+ /* NOTREACHED */
+ }
+#endif
+}
+
+int
+open_dns(int localport, in_addr_t listen_ip)
+{
+ struct sockaddr_in addr;
+ int flag = 1;
+ int fd;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(localport);
+ /* listen_ip already in network byte order from inet_addr, or 0 */
+ addr.sin_addr.s_addr = listen_ip;
+
+ if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+ fprintf(stderr, "got fd %d\n", fd);
+ err(1, "socket");
+ }
+
+ flag = 1;
+#ifdef SO_REUSEPORT
+ setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (const void*) &flag, sizeof(flag));
+#endif
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void*) &flag, sizeof(flag));
+
+#ifndef WINDOWS32
+ /* To get destination address from each UDP datagram, see iodined.c:read_dns() */
+ setsockopt(fd, IPPROTO_IP, DSTADDR_SOCKOPT, (const void*) &flag, sizeof(flag));
+#endif
+
+#ifdef IP_OPT_DONT_FRAG
+ /* Set dont-fragment ip header flag */
+ flag = DONT_FRAG_VALUE;
+ setsockopt(fd, IPPROTO_IP, IP_OPT_DONT_FRAG, (const void*) &flag, sizeof(flag));
+#endif
+
+ if(bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0)
+ err(1, "bind");
+
+ fprintf(stderr, "Opened UDP socket\n");
+
+ return fd;
+}
+
+void
+close_dns(int fd)
+{
+ close(fd);
+}
+
+void
+do_chroot(char *newroot)
+{
+#if !(defined(WINDOWS32) || 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
+do_setcon(char *context)
+{
+#ifdef HAVE_SETCON
+ if (-1 == setcon(context))
+ err(1, "%s", context);
+#else
+ warnx("No SELinux support built in");
+#endif
+}
+
+void
+do_pidfile(char *pidfile)
+{
+#ifndef WINDOWS32
+ FILE *file;
+
+ if ((file = fopen(pidfile, "w")) == NULL) {
+ syslog(LOG_ERR, "Cannot write pidfile to %s, exiting", pidfile);
+ err(1, "do_pidfile: Can not write pidfile to %s", pidfile);
+ } else {
+ fprintf(file, "%d\n", (int)getpid());
+ fclose(file);
+ }
+#else
+ fprintf(stderr, "Windows version does not support pid file\n");
+#endif
+}
+
+void
+do_detach()
+{
+#ifndef WINDOWS32
+ fprintf(stderr, "Detaching from terminal...\n");
+ daemon(0, 0);
+ umask(0);
+ alarm(0);
+#else
+ fprintf(stderr, "Windows version does not support detaching\n");
+#endif
+}
+
+void
+read_password(char *buf, size_t len)
+{
+ char pwd[80];
+#ifndef WINDOWS32
+ struct termios old;
+ struct termios tp;
+
+ tcgetattr(0, &tp);
+ old = tp;
+
+ tp.c_lflag &= (~ECHO);
+ tcsetattr(0, TCSANOW, &tp);
+#else
+ int i;
+#endif
+
+ fprintf(stderr, "Enter password: ");
+ fflush(stderr);
+#ifndef WINDOWS32
+ scanf("%79s", pwd);
+#else
+ for (i = 0; i < sizeof(pwd); i++) {
+ pwd[i] = getch();
+ if (pwd[i] == '\r' || pwd[i] == '\n') {
+ pwd[i] = 0;
+ break;
+ } else if (pwd[i] == '\b') {
+ i--; /* Remove the \b char */
+ if (i >=0) i--; /* If not first char, remove one more */
+ }
+ }
+#endif
+ fprintf(stderr, "\n");
+
+#ifndef WINDOWS32
+ tcsetattr(0, TCSANOW, &old);
+#endif
+
+ strncpy(buf, pwd, len);
+ buf[len-1] = '\0';
+}
+
+int
+check_topdomain(char *str)
+{
+ int i;
+
+ if(str[0] == '.') /* special case */
+ return 1;
+
+ for( i = 0; i < strlen(str); i++) {
+ if( isalpha(str[i]) || isdigit(str[i]) || str[i] == '-' || str[i] == '.' )
+ continue;
+ else
+ return 1;
+ }
+ return 0;
+}
+
+#if defined(WINDOWS32)
+int
+inet_aton(const char *cp, struct in_addr *inp)
+{
+ inp->s_addr = inet_addr(cp);
+ return inp->s_addr != INADDR_ANY;
+}
+
+void
+warn(const char *fmt, ...)
+{
+ va_list list;
+
+ va_start(list, fmt);
+ if (fmt) fprintf(stderr, fmt, list);
+ if (errno == 0) {
+ fprintf(stderr, ": WSA error %d\n", WSAGetLastError());
+ } else {
+ fprintf(stderr, ": %s\n", strerror(errno));
+ }
+ va_end(list);
+}
+#endif
+
+#ifdef __ANDROID__
+
+void android_printf(const char *fmt, ...)
+{
+ static char buf[1024];
+ va_list list;
+ va_start(list, fmt);
+
+ snprintf(buf,1024,fmt,list);
+
+ android_log_callback(buf);
+
+ va_end(list);
+}
+
+
+void
+warn(const char *fmt, ...)
+{
+ va_list list;
+
+ va_start(list, fmt);
+ if (fmt) fprintf(stderr, fmt, list);
+ if (errno != 0)
+ fprintf(stderr, ": %s\n", strerror(errno));
+ va_end(list);
+}
+#endif
+
+#if defined(WINDOWS32) || defined(__ANDROID__)
+void
+warnx(const char *fmt, ...)
+{
+ va_list list;
+
+ va_start(list, fmt);
+ if (fmt) fprintf(stderr, fmt, list);
+ fprintf(stderr, "\n");
+ va_end(list);
+}
+
+void
+err(int eval, const char *fmt, ...)
+{
+ va_list list;
+
+ va_start(list, fmt);
+ warn(fmt, list);
+ va_end(list);
+ exit(eval);
+}
+
+void
+errx(int eval, const char *fmt, ...)
+{
+ va_list list;
+
+ va_start(list, fmt);
+ warnx(fmt, list);
+ va_end(list);
+ exit(eval);
+}
+#endif
+
+
+int recent_seqno(int ourseqno, int gotseqno)
+/* Return 1 if we've seen gotseqno recently (current or up to 3 back).
+ Return 0 if gotseqno is new (or very old).
+*/
+{
+ int i;
+ for (i = 0; i < 4; i++, ourseqno--) {
+ if (ourseqno < 0)
+ ourseqno = 7;
+ if (gotseqno == ourseqno)
+ return 1;
+ }
+ return 0;
+}
diff --git a/jni/iodine/src/common.h b/jni/iodine/src/common.h
new file mode 100644
index 0000000..0b0efb8
--- /dev/null
+++ b/jni/iodine/src/common.h
@@ -0,0 +1,147 @@
+/*
+ * 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 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 __COMMON_H__
+#define __COMMON_H__
+
+/* Last byte of raw header is the command */
+#define RAW_HDR_LEN 4
+#define RAW_HDR_IDENT_LEN 3
+#define RAW_HDR_CMD 3
+#define RAW_HDR_CMD_LOGIN 0x10
+#define RAW_HDR_CMD_DATA 0x20
+#define RAW_HDR_CMD_PING 0x30
+
+#define RAW_HDR_CMD_MASK 0xF0
+#define RAW_HDR_USR_MASK 0x0F
+#define RAW_HDR_GET_CMD(x) ((x)[RAW_HDR_CMD] & RAW_HDR_CMD_MASK)
+#define RAW_HDR_GET_USR(x) ((x)[RAW_HDR_CMD] & RAW_HDR_USR_MASK)
+extern const unsigned char raw_header[RAW_HDR_LEN];
+
+#ifdef WINDOWS32
+#include "windows.h"
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <err.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#endif
+
+#define DNS_PORT 53
+
+#ifndef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+#ifndef MAX
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#endif
+
+#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
+
+#if defined IP_MTU_DISCOVER
+ /* Linux */
+# define IP_OPT_DONT_FRAG IP_MTU_DISCOVER
+# define DONT_FRAG_VALUE IP_PMTUDISC_DO
+#elif defined IP_DONTFRAG
+ /* FreeBSD */
+# define IP_OPT_DONT_FRAG IP_DONTFRAG
+# define DONT_FRAG_VALUE 1
+#elif defined IP_DONTFRAGMENT
+ /* Winsock2 */
+# define IP_OPT_DONT_FRAG IP_DONTFRAGMENT
+# define DONT_FRAG_VALUE 1
+#endif
+
+#define T_UNSET 65432
+/* Unused RR type; "private use" range, see http://www.bind9.net/dns-parameters */
+
+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];
+ unsigned short type;
+ unsigned short rcode;
+ unsigned short id;
+ struct in_addr destination;
+ struct sockaddr from;
+ int fromlen;
+ unsigned short id2;
+ struct sockaddr from2;
+ int fromlen2;
+};
+
+enum connection {
+ CONN_RAW_UDP,
+ CONN_DNS_NULL,
+ CONN_MAX
+};
+
+void check_superuser(void (*usage_fn)(void));
+int open_dns(int, in_addr_t);
+void close_dns(int);
+
+void do_chroot(char *);
+void do_setcon(char *);
+void do_detach();
+void do_pidfile(char *);
+
+void read_password(char*, size_t);
+
+int check_topdomain(char *);
+
+#ifdef __ANDROID__
+//#define printf(...) __android_log_print(ANDROID_LOG_DEBUG, "iodine", __VA_ARGS__)
+#define printf(...) android_printf(__VA_ARGS__)
+
+void android_printf(const char *fmt, ...);
+
+
+#define fprintf(__fd_unused, ...) printf(__VA_ARGS__)
+
+#include <android/log.h>
+void err(int eval, const char *fmt, ...);
+void warn(const char *fmt, ...);
+void errx(int eval, const char *fmt, ...);
+void warnx(const char *fmt, ...);
+#elif WINDOWS32
+int inet_aton(const char *cp, struct in_addr *inp);
+
+void err(int eval, const char *fmt, ...);
+void warn(const char *fmt, ...);
+void errx(int eval, const char *fmt, ...);
+void warnx(const char *fmt, ...);
+#endif
+
+int recent_seqno(int , int);
+
+#endif
diff --git a/jni/iodine/src/dns.c b/jni/iodine/src/dns.c
new file mode 100644
index 0000000..fb2bcaf
--- /dev/null
+++ b/jni/iodine/src/dns.c
@@ -0,0 +1,610 @@
+/*
+ * 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 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 <time.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifdef WINDOWS32
+#include "windows.h"
+#else
+#include <arpa/nameser.h>
+#ifdef DARWIN
+#define BIND_8_COMPAT
+#include <arpa/nameser_compat.h>
+#endif
+#include <arpa/inet.h>
+#include <err.h>
+#endif
+
+
+#include "dns.h"
+#include "encoding.h"
+#include "read.h"
+
+int dnsc_use_edns0 = 1;
+
+#define CHECKLEN(x) if (buflen - (p-buf) < (x)) return 0
+
+int
+dns_encode(char *buf, size_t buflen, struct query *q, qr_t qr, char *data, size_t datalen)
+{
+ HEADER *header;
+ short name;
+ char *p;
+ int len;
+ int ancnt;
+
+ if (buflen < sizeof(HEADER))
+ return 0;
+
+ memset(buf, 0, buflen);
+
+ header = (HEADER*)buf;
+
+ header->id = htons(q->id);
+ header->qr = (qr == QR_ANSWER);
+ header->opcode = 0;
+ header->aa = (qr == QR_ANSWER);
+ header->tc = 0;
+ header->rd = (qr == QR_QUERY);
+ header->ra = 0;
+
+ p = buf + sizeof(HEADER);
+
+ switch (qr) {
+ case QR_ANSWER:
+ header->qdcount = htons(1);
+
+ name = 0xc000 | ((p - buf) & 0x3fff);
+
+ /* Question section */
+ putname(&p, buflen - (p - buf), q->name);
+
+ CHECKLEN(4);
+ putshort(&p, q->type);
+ putshort(&p, C_IN);
+
+ /* Answer section */
+
+ if (q->type == T_CNAME || q->type == T_A) {
+ /* data is expected to be like "Hblabla.host.name.com\0" */
+
+ char *startp;
+ int namelen;
+
+ CHECKLEN(10);
+ putshort(&p, name);
+ if (q->type == T_A)
+ /* answer CNAME to A question */
+ putshort(&p, T_CNAME);
+ else
+ putshort(&p, q->type);
+ putshort(&p, C_IN);
+ putlong(&p, 0); /* TTL */
+
+ startp = p;
+ p += 2; /* skip 2 bytes length */
+ putname(&p, buflen - (p - buf), data);
+ CHECKLEN(0);
+ namelen = p - startp;
+ namelen -= 2;
+ putshort(&startp, namelen);
+ ancnt = 1;
+ } else if (q->type == T_MX || q->type == T_SRV) {
+ /* Data is expected to be like
+ "Hblabla.host.name.com\0Hanother.com\0\0"
+ For SRV, see RFC2782.
+ */
+
+ char *mxdata = data;
+ char *startp;
+ int namelen;
+
+ ancnt = 1;
+ while (1) {
+ CHECKLEN(10);
+ putshort(&p, name);
+ putshort(&p, q->type);
+ putshort(&p, C_IN);
+ putlong(&p, 0); /* TTL */
+
+ startp = p;
+ p += 2; /* skip 2 bytes length */
+ CHECKLEN(2);
+ putshort(&p, 10 * ancnt); /* preference */
+
+ if (q->type == T_SRV) {
+ /* weight, port (5060 = SIP) */
+ CHECKLEN(4);
+ putshort(&p, 10);
+ putshort(&p, 5060);
+ }
+
+ putname(&p, buflen - (p - buf), mxdata);
+ CHECKLEN(0);
+ namelen = p - startp;
+ namelen -= 2;
+ putshort(&startp, namelen);
+
+ mxdata = mxdata + strlen(mxdata) + 1;
+ if (*mxdata == '\0')
+ break;
+
+ ancnt++;
+ }
+ } else if (q->type == T_TXT) {
+ /* TXT has binary or base-X data */
+ char *startp;
+ int txtlen;
+
+ CHECKLEN(10);
+ putshort(&p, name);
+ putshort(&p, q->type);
+ putshort(&p, C_IN);
+ putlong(&p, 0); /* TTL */
+
+ startp = p;
+ p += 2; /* skip 2 bytes length */
+ puttxtbin(&p, buflen - (p - buf), data, datalen);
+ CHECKLEN(0);
+ txtlen = p - startp;
+ txtlen -= 2;
+ putshort(&startp, txtlen);
+ ancnt = 1;
+ } else {
+ /* NULL has raw binary data */
+
+ CHECKLEN(10);
+ putshort(&p, name);
+ putshort(&p, q->type);
+ putshort(&p, C_IN);
+ putlong(&p, 0); /* TTL */
+
+ datalen = MIN(datalen, buflen - (p - buf));
+ CHECKLEN(2);
+ putshort(&p, datalen);
+ CHECKLEN(datalen);
+ putdata(&p, data, datalen);
+ CHECKLEN(0);
+ ancnt = 1;
+ }
+ header->ancount = htons(ancnt);
+ break;
+ case QR_QUERY:
+ /* Note that iodined also uses this for forward queries */
+
+ header->qdcount = htons(1);
+
+ datalen = MIN(datalen, buflen - (p - buf));
+ putname(&p, datalen, data);
+
+ CHECKLEN(4);
+ putshort(&p, q->type);
+ putshort(&p, C_IN);
+
+ /* EDNS0 to advertise maximum response length
+ (even CNAME/A/MX, 255+255+header would be >512) */
+ if (dnsc_use_edns0) {
+ header->arcount = htons(1);
+ /*XXX START adjust indent 1 tab forward*/
+ CHECKLEN(11);
+ putbyte(&p, 0x00); /* Root */
+ putshort(&p, 0x0029); /* OPT */
+ putshort(&p, 0x1000); /* Payload size: 4096 */
+ putshort(&p, 0x0000); /* Higher bits/edns version */
+ putshort(&p, 0x8000); /* Z */
+ putshort(&p, 0x0000); /* Data length */
+ /*XXX END adjust indent 1 tab forward*/
+ }
+
+ break;
+ }
+
+ len = p - buf;
+
+ return len;
+}
+
+int
+dns_encode_ns_response(char *buf, size_t buflen, struct query *q, char *topdomain)
+/* Only used when iodined gets an NS type query */
+/* Mostly same as dns_encode_a_response() below */
+{
+ HEADER *header;
+ int len;
+ short name;
+ short topname;
+ short nsname;
+ char *ipp;
+ int domain_len;
+ char *p;
+
+ if (buflen < sizeof(HEADER))
+ return 0;
+
+ 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_len = strlen(q->name) - strlen(topdomain);
+ if (domain_len < 0 || domain_len == 1)
+ return -1;
+ if (strcasecmp(q->name + domain_len, topdomain))
+ return -1;
+ if (domain_len >= 1 && q->name[domain_len - 1] != '.')
+ return -1;
+
+ /* pointer to start of topdomain; instead of dots at the end
+ we have length-bytes in front, so total length is the same */
+ topname = 0xc000 | ((p - buf + domain_len) & 0x3fff);
+
+ /* Query section */
+ putname(&p, buflen - (p - buf), q->name); /* Name */
+ CHECKLEN(4);
+ putshort(&p, q->type); /* Type */
+ putshort(&p, C_IN); /* Class */
+
+ /* Answer section */
+ CHECKLEN(12);
+ putshort(&p, name); /* Name */
+ putshort(&p, q->type); /* Type */
+ putshort(&p, C_IN); /* Class */
+ putlong(&p, 3600); /* TTL */
+ putshort(&p, 5); /* Data length */
+
+ /* pointer to ns.topdomain */
+ nsname = 0xc000 | ((p - buf) & 0x3fff);
+ CHECKLEN(5);
+ putbyte(&p, 2);
+ putbyte(&p, 'n');
+ putbyte(&p, 's');
+ putshort(&p, topname); /* Name Server */
+
+ /* Additional data (A-record of NS server) */
+ CHECKLEN(12);
+ putshort(&p, nsname); /* Name Server */
+ putshort(&p, T_A); /* Type */
+ putshort(&p, C_IN); /* Class */
+ putlong(&p, 3600); /* TTL */
+ putshort(&p, 4); /* Data length */
+
+ /* ugly hack to output IP address */
+ ipp = (char *) &q->destination;
+ CHECKLEN(4);
+ putbyte(&p, *(ipp++));
+ putbyte(&p, *(ipp++));
+ putbyte(&p, *(ipp++));
+ putbyte(&p, *ipp);
+
+ len = p - buf;
+ return len;
+}
+
+int
+dns_encode_a_response(char *buf, size_t buflen, struct query *q)
+/* Only used when iodined gets an A type query for ns.topdomain or www.topdomain */
+/* Mostly same as dns_encode_ns_response() above */
+{
+ HEADER *header;
+ int len;
+ short name;
+ char *ipp;
+ char *p;
+
+ if (buflen < sizeof(HEADER))
+ return 0;
+
+ 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);
+
+ /* pointer to start of name */
+ name = 0xc000 | ((p - buf) & 0x3fff);
+
+ /* Query section */
+ putname(&p, buflen - (p - buf), q->name); /* Name */
+ CHECKLEN(4);
+ putshort(&p, q->type); /* Type */
+ putshort(&p, C_IN); /* Class */
+
+ /* Answer section */
+ CHECKLEN(12);
+ putshort(&p, name); /* Name */
+ putshort(&p, q->type); /* Type */
+ putshort(&p, C_IN); /* Class */
+ putlong(&p, 3600); /* TTL */
+ putshort(&p, 4); /* Data length */
+
+ /* ugly hack to output IP address */
+ ipp = (char *) &q->destination;
+ CHECKLEN(4);
+ putbyte(&p, *(ipp++));
+ putbyte(&p, *(ipp++));
+ putbyte(&p, *(ipp++));
+ putbyte(&p, *ipp);
+
+ len = p - buf;
+ return len;
+}
+
+#undef CHECKLEN
+
+unsigned short
+dns_get_id(char *packet, size_t packetlen)
+{
+ HEADER *header;
+ header = (HEADER*)packet;
+
+ if (packetlen < sizeof(HEADER))
+ return 0;
+
+ return ntohs(header->id);
+}
+
+#define CHECKLEN(x) if (packetlen - (data-packet) < (x)) return 0
+
+int
+dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, size_t packetlen)
+{
+ char name[QUERY_NAME_SIZE];
+ char rdata[4*1024];
+ HEADER *header;
+ short qdcount;
+ short ancount;
+ uint32_t ttl;
+ short class;
+ short type;
+ char *data;
+ short rlen;
+ int id;
+ int rv;
+
+ q->id2 = 0;
+ rv = 0;
+ header = (HEADER*)packet;
+
+ /* Reject short packets */
+ if (packetlen < sizeof(HEADER))
+ return 0;
+
+ if (header->qr != qr) {
+ warnx("header->qr does not match the requested qr");
+ return -1;
+ }
+
+ data = packet + sizeof(HEADER);
+ qdcount = ntohs(header->qdcount);
+ ancount = ntohs(header->ancount);
+
+ id = ntohs(header->id);
+ id = id & 0xFFFF; /* Kill any sign extension */
+
+ rlen = 0;
+
+ if (q != NULL)
+ q->rcode = header->rcode;
+
+ switch (qr) {
+ case QR_ANSWER:
+ if(qdcount < 1) {
+ /* We need a question */
+ return -1;
+ }
+
+ if (q != NULL)
+ q->id = id;
+
+ /* Read name even if no answer, to give better error message */
+ readname(packet, packetlen, &data, name, sizeof(name));
+ CHECKLEN(4);
+ readshort(packet, &data, &type);
+ readshort(packet, &data, &class);
+
+ /* if CHECKLEN okay, then we're sure to have a proper name */
+ if (q != NULL) {
+ /* We only need the first char to check it */
+ q->name[0] = name[0];
+ q->name[1] = '\0';
+ }
+
+ if (ancount < 1) {
+ /* DNS errors like NXDOMAIN have ancount=0 and
+ stop here. CNAME may also have A; MX/SRV may have
+ multiple results. */
+ return -1;
+ }
+
+ /* Here type is still the question type */
+ if (type == T_NULL) {
+ /* Assume that first answer is what we wanted */
+ readname(packet, packetlen, &data, name, sizeof(name));
+ CHECKLEN(10);
+ readshort(packet, &data, &type);
+ readshort(packet, &data, &class);
+ readlong(packet, &data, &ttl);
+ readshort(packet, &data, &rlen);
+
+ rv = MIN(rlen, sizeof(rdata));
+ rv = readdata(packet, &data, rdata, rv);
+ if (rv >= 2 && buf) {
+ rv = MIN(rv, buflen);
+ memcpy(buf, rdata, rv);
+ } else {
+ rv = 0;
+ }
+ }
+ else if ((type == T_A || type == T_CNAME) && buf) {
+ /* Assume that first answer is what we wanted */
+ readname(packet, packetlen, &data, name, sizeof(name));
+ CHECKLEN(10);
+ readshort(packet, &data, &type);
+ readshort(packet, &data, &class);
+ readlong(packet, &data, &ttl);
+ readshort(packet, &data, &rlen);
+
+ memset(name, 0, sizeof(name));
+ readname(packet, packetlen, &data, name, sizeof(name) - 1);
+ name[sizeof(name)-1] = '\0';
+ strncpy(buf, name, buflen);
+ buf[buflen - 1] = '\0';
+ rv = strlen(buf);
+ }
+ else if ((type == T_MX || type == T_SRV) && buf) {
+ /* We support 250 records, 250*(255+header) ~= 64kB.
+ Only exact 10-multiples are accepted, and gaps in
+ numbering are not jumped over (->truncated).
+ Hopefully DNS servers won't mess around too much.
+ */
+ char names[250][QUERY_NAME_SIZE];
+ char *rdatastart;
+ short pref;
+ int i;
+ int offset;
+
+ memset(names, 0, sizeof(names));
+
+ for (i=0; i < ancount; i++) {
+ readname(packet, packetlen, &data, name, sizeof(name));
+ CHECKLEN(12);
+ readshort(packet, &data, &type);
+ readshort(packet, &data, &class);
+ readlong(packet, &data, &ttl);
+ readshort(packet, &data, &rlen);
+ rdatastart = data;
+ readshort(packet, &data, &pref);
+
+ if (type == T_SRV) {
+ /* skip weight, port */
+ data += 4;
+ CHECKLEN(0);
+ }
+
+ if (pref % 10 == 0 && pref >= 10 &&
+ pref < 2500) {
+ readname(packet, packetlen, &data,
+ names[pref / 10 - 1],
+ QUERY_NAME_SIZE - 1);
+ names[pref / 10 - 1][QUERY_NAME_SIZE-1] = '\0';
+ }
+
+ /* always trust rlen, not name encoding */
+ data = rdatastart + rlen;
+ CHECKLEN(0);
+ }
+
+ /* output is like Hname10.com\0Hname20.com\0\0 */
+ offset = 0;
+ i = 0;
+ while (names[i][0] != '\0') {
+ int l = MIN(strlen(names[i]), buflen-offset-2);
+ if (l <= 0)
+ break;
+ memcpy(buf + offset, names[i], l);
+ offset += l;
+ *(buf + offset) = '\0';
+ offset++;
+ i++;
+ }
+ *(buf + offset) = '\0';
+ rv = offset;
+ }
+ else if (type == T_TXT && buf) {
+ /* Assume that first answer is what we wanted */
+ readname(packet, packetlen, &data, name, sizeof(name));
+ CHECKLEN(10);
+ readshort(packet, &data, &type);
+ readshort(packet, &data, &class);
+ readlong(packet, &data, &ttl);
+ readshort(packet, &data, &rlen);
+
+ rv = readtxtbin(packet, &data, rlen, rdata, sizeof(rdata));
+ if (rv >= 1) {
+ rv = MIN(rv, buflen);
+ memcpy(buf, rdata, rv);
+ } else {
+ rv = 0;
+ }
+ }
+
+ /* Here type is the answer type (note A->CNAME) */
+ if (q != NULL)
+ q->type = type;
+ break;
+ case QR_QUERY:
+ if (qdcount < 1) {
+ warnx("no question section in name query");
+ return -1;
+ }
+
+ memset(name, 0, sizeof(name));
+ readname(packet, packetlen, &data, name, sizeof(name) - 1);
+ name[sizeof(name)-1] = '\0';
+ CHECKLEN(4);
+ readshort(packet, &data, &type);
+ readshort(packet, &data, &class);
+
+ if (q == NULL) {
+ rv = 0;
+ break;
+ }
+
+ strncpy(q->name, name, sizeof(q->name));
+ q->name[sizeof(q->name) - 1] = '\0';
+ q->type = type;
+ q->id = id;
+
+ rv = strlen(q->name);
+ break;
+ }
+
+ return rv;
+}
+
diff --git a/jni/iodine/src/dns.h b/jni/iodine/src/dns.h
new file mode 100644
index 0000000..be8b940
--- /dev/null
+++ b/jni/iodine/src/dns.h
@@ -0,0 +1,39 @@
+/*
+ * 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 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 __DNS_H__
+#define __DNS_H__
+
+#include "common.h"
+
+typedef enum {
+ QR_QUERY = 0,
+ QR_ANSWER = 1
+} qr_t;
+
+extern int dnsc_use_edns0;
+
+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);
+int dns_encode_a_response(char *buf, size_t buflen, struct query *q);
+unsigned short dns_get_id(char *packet, size_t packetlen);
+int dns_decode(char *, size_t, struct query *, qr_t, char *, size_t);
+
+#ifdef __ANDROID__
+#include "dns_android.h"
+#endif
+
+#endif /* _DNS_H_ */
diff --git a/jni/iodine/src/dns_android.h b/jni/iodine/src/dns_android.h
new file mode 100644
index 0000000..f8addf7
--- /dev/null
+++ b/jni/iodine/src/dns_android.h
@@ -0,0 +1,625 @@
+/* This file is modified to serve minimal requirements of iodine */
+
+
+/*
+ * Copyright (c) 1983, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * 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 INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+ * CONSORTIUM 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.
+ */
+
+/*
+ * $BINDId: nameser.h,v 8.37 2000/03/30 21:16:49 vixie Exp $
+ */
+
+#ifndef _ARPA_NAMESER_H_
+#define _ARPA_NAMESER_H_
+
+/*%
+ * Revision information. This is the release date in YYYYMMDD format.
+ * It can change every day so the right thing to do with it is use it
+ * in preprocessor commands such as "#if (__NAMESER > 19931104)". Do not
+ * compare for equality; rather, use it to determine whether your libbind.a
+ * contains a new enough lib/nameser/ to support the feature you need.
+ */
+
+#define __NAMESER 19991006 /*%< New interface version stamp. */
+/*
+ * Define constants based on RFC 883, RFC 1034, RFC 1035
+ */
+#define NS_PACKETSZ 512 /*%< default UDP packet size */
+#define NS_MAXDNAME 1025 /*%< maximum domain name */
+#define NS_MAXMSG 65535 /*%< maximum message size */
+#define NS_MAXCDNAME 255 /*%< maximum compressed domain name */
+#define NS_MAXLABEL 63 /*%< maximum length of domain label */
+#define NS_HFIXEDSZ 12 /*%< #/bytes of fixed data in header */
+#define NS_QFIXEDSZ 4 /*%< #/bytes of fixed data in query */
+#define NS_RRFIXEDSZ 10 /*%< #/bytes of fixed data in r record */
+#define NS_INT32SZ 4 /*%< #/bytes of data in a u_int32_t */
+#define NS_INT16SZ 2 /*%< #/bytes of data in a u_int16_t */
+#define NS_INT8SZ 1 /*%< #/bytes of data in a u_int8_t */
+#define NS_INADDRSZ 4 /*%< IPv4 T_A */
+#define NS_IN6ADDRSZ 16 /*%< IPv6 T_AAAA */
+#define NS_CMPRSFLGS 0xc0 /*%< Flag bits indicating name compression. */
+#define NS_DEFAULTPORT 53 /*%< For both TCP and UDP. */
+/*
+ * These can be expanded with synonyms, just keep ns_parse.c:ns_parserecord()
+ * in synch with it.
+ */
+typedef enum __ns_sect {
+ ns_s_qd = 0, /*%< Query: Question. */
+ ns_s_zn = 0, /*%< Update: Zone. */
+ ns_s_an = 1, /*%< Query: Answer. */
+ ns_s_pr = 1, /*%< Update: Prerequisites. */
+ ns_s_ns = 2, /*%< Query: Name servers. */
+ ns_s_ud = 2, /*%< Update: Update. */
+ ns_s_ar = 3, /*%< Query|Update: Additional records. */
+ ns_s_max = 4
+} ns_sect;
+
+/*%
+ * This is a message handle. It is caller allocated and has no dynamic data.
+ * This structure is intended to be opaque to all but ns_parse.c, thus the
+ * leading _'s on the member names. Use the accessor functions, not the _'s.
+ */
+typedef struct __ns_msg {
+ const u_char *_msg, *_eom;
+ u_int16_t _id, _flags, _counts[ns_s_max];
+ const u_char *_sections[ns_s_max];
+ ns_sect _sect;
+ int _rrnum;
+ const u_char *_msg_ptr;
+} ns_msg;
+
+/* Private data structure - do not use from outside library. */
+struct _ns_flagdata { int mask, shift; };
+extern const struct _ns_flagdata _ns_flagdata[];
+
+/* Accessor macros - this is part of the public interface. */
+
+#define ns_msg_id(handle) ((handle)._id + 0)
+#define ns_msg_base(handle) ((handle)._msg + 0)
+#define ns_msg_end(handle) ((handle)._eom + 0)
+#define ns_msg_size(handle) ((handle)._eom - (handle)._msg)
+#define ns_msg_count(handle, section) ((handle)._counts[section] + 0)
+
+/*%
+ * This is a parsed record. It is caller allocated and has no dynamic data.
+ */
+typedef struct __ns_rr {
+ char name[NS_MAXDNAME];
+ u_int16_t type;
+ u_int16_t rr_class;
+ u_int32_t ttl;
+ u_int16_t rdlength;
+ const u_char * rdata;
+} ns_rr;
+
+/* Accessor macros - this is part of the public interface. */
+#define ns_rr_name(rr) (((rr).name[0] != '\0') ? (rr).name : ".")
+#define ns_rr_type(rr) ((ns_type)((rr).type + 0))
+#define ns_rr_class(rr) ((ns_class)((rr).rr_class + 0))
+#define ns_rr_ttl(rr) ((rr).ttl + 0)
+#define ns_rr_rdlen(rr) ((rr).rdlength + 0)
+#define ns_rr_rdata(rr) ((rr).rdata + 0)
+
+/*%
+ * These don't have to be in the same order as in the packet flags word,
+ * and they can even overlap in some cases, but they will need to be kept
+ * in synch with ns_parse.c:ns_flagdata[].
+ */
+typedef enum __ns_flag {
+ ns_f_qr, /*%< Question/Response. */
+ ns_f_opcode, /*%< Operation code. */
+ ns_f_aa, /*%< Authoritative Answer. */
+ ns_f_tc, /*%< Truncation occurred. */
+ ns_f_rd, /*%< Recursion Desired. */
+ ns_f_ra, /*%< Recursion Available. */
+ ns_f_z, /*%< MBZ. */
+ ns_f_ad, /*%< Authentic Data (DNSSEC). */
+ ns_f_cd, /*%< Checking Disabled (DNSSEC). */
+ ns_f_rcode, /*%< Response code. */
+ ns_f_max
+} ns_flag;
+
+/*%
+ * Currently defined opcodes.
+ */
+typedef enum __ns_opcode {
+ ns_o_query = 0, /*%< Standard query. */
+ ns_o_iquery = 1, /*%< Inverse query (deprecated/unsupported). */
+ ns_o_status = 2, /*%< Name server status query (unsupported). */
+ /* Opcode 3 is undefined/reserved. */
+ ns_o_notify = 4, /*%< Zone change notification. */
+ ns_o_update = 5, /*%< Zone update message. */
+ ns_o_max = 6
+} ns_opcode;
+
+/*%
+ * Currently defined response codes.
+ */
+typedef enum __ns_rcode {
+ ns_r_noerror = 0, /*%< No error occurred. */
+ ns_r_formerr = 1, /*%< Format error. */
+ ns_r_servfail = 2, /*%< Server failure. */
+ ns_r_nxdomain = 3, /*%< Name error. */
+ ns_r_notimpl = 4, /*%< Unimplemented. */
+ ns_r_refused = 5, /*%< Operation refused. */
+ /* these are for BIND_UPDATE */
+ ns_r_yxdomain = 6, /*%< Name exists */
+ ns_r_yxrrset = 7, /*%< RRset exists */
+ ns_r_nxrrset = 8, /*%< RRset does not exist */
+ ns_r_notauth = 9, /*%< Not authoritative for zone */
+ ns_r_notzone = 10, /*%< Zone of record different from zone section */
+ ns_r_max = 11,
+ /* The following are EDNS extended rcodes */
+ ns_r_badvers = 16,
+ /* The following are TSIG errors */
+ ns_r_badsig = 16,
+ ns_r_badkey = 17,
+ ns_r_badtime = 18
+} ns_rcode;
+
+/* BIND_UPDATE */
+typedef enum __ns_update_operation {
+ ns_uop_delete = 0,
+ ns_uop_add = 1,
+ ns_uop_max = 2
+} ns_update_operation;
+
+/*%
+ * This structure is used for TSIG authenticated messages
+ */
+struct ns_tsig_key {
+ char name[NS_MAXDNAME], alg[NS_MAXDNAME];
+ unsigned char *data;
+ int len;
+};
+typedef struct ns_tsig_key ns_tsig_key;
+
+/*%
+ * This structure is used for TSIG authenticated TCP messages
+ */
+struct ns_tcp_tsig_state {
+ int counter;
+ struct dst_key *key;
+ void *ctx;
+ unsigned char sig[NS_PACKETSZ];
+ int siglen;
+};
+typedef struct ns_tcp_tsig_state ns_tcp_tsig_state;
+
+#define NS_TSIG_FUDGE 300
+#define NS_TSIG_TCP_COUNT 100
+#define NS_TSIG_ALG_HMAC_MD5 "HMAC-MD5.SIG-ALG.REG.INT"
+
+#define NS_TSIG_ERROR_NO_TSIG -10
+#define NS_TSIG_ERROR_NO_SPACE -11
+#define NS_TSIG_ERROR_FORMERR -12
+
+/*%
+ * Currently defined type values for resources and queries.
+ */
+typedef enum __ns_type {
+ ns_t_invalid = 0, /*%< Cookie. */
+ ns_t_a = 1, /*%< Host address. */
+ ns_t_ns = 2, /*%< Authoritative server. */
+ ns_t_md = 3, /*%< Mail destination. */
+ ns_t_mf = 4, /*%< Mail forwarder. */
+ ns_t_cname = 5, /*%< Canonical name. */
+ ns_t_soa = 6, /*%< Start of authority zone. */
+ ns_t_mb = 7, /*%< Mailbox domain name. */
+ ns_t_mg = 8, /*%< Mail group member. */
+ ns_t_mr = 9, /*%< Mail rename name. */
+ ns_t_null = 10, /*%< Null resource record. */
+ ns_t_wks = 11, /*%< Well known service. */
+ ns_t_ptr = 12, /*%< Domain name pointer. */
+ ns_t_hinfo = 13, /*%< Host information. */
+ ns_t_minfo = 14, /*%< Mailbox information. */
+ ns_t_mx = 15, /*%< Mail routing information. */
+ ns_t_txt = 16, /*%< Text strings. */
+ ns_t_rp = 17, /*%< Responsible person. */
+ ns_t_afsdb = 18, /*%< AFS cell database. */
+ ns_t_x25 = 19, /*%< X_25 calling address. */
+ ns_t_isdn = 20, /*%< ISDN calling address. */
+ ns_t_rt = 21, /*%< Router. */
+ ns_t_nsap = 22, /*%< NSAP address. */
+ ns_t_nsap_ptr = 23, /*%< Reverse NSAP lookup (deprecated). */
+ ns_t_sig = 24, /*%< Security signature. */
+ ns_t_key = 25, /*%< Security key. */
+ ns_t_px = 26, /*%< X.400 mail mapping. */
+ ns_t_gpos = 27, /*%< Geographical position (withdrawn). */
+ ns_t_aaaa = 28, /*%< Ip6 Address. */
+ ns_t_loc = 29, /*%< Location Information. */
+ ns_t_nxt = 30, /*%< Next domain (security). */
+ ns_t_eid = 31, /*%< Endpoint identifier. */
+ ns_t_nimloc = 32, /*%< Nimrod Locator. */
+ ns_t_srv = 33, /*%< Server Selection. */
+ ns_t_atma = 34, /*%< ATM Address */
+ ns_t_naptr = 35, /*%< Naming Authority PoinTeR */
+ ns_t_kx = 36, /*%< Key Exchange */
+ ns_t_cert = 37, /*%< Certification record */
+ ns_t_a6 = 38, /*%< IPv6 address (deprecated, use ns_t_aaaa) */
+ ns_t_dname = 39, /*%< Non-terminal DNAME (for IPv6) */
+ ns_t_sink = 40, /*%< Kitchen sink (experimentatl) */
+ ns_t_opt = 41, /*%< EDNS0 option (meta-RR) */
+ ns_t_apl = 42, /*%< Address prefix list (RFC3123) */
+ ns_t_tkey = 249, /*%< Transaction key */
+ ns_t_tsig = 250, /*%< Transaction signature. */
+ ns_t_ixfr = 251, /*%< Incremental zone transfer. */
+ ns_t_axfr = 252, /*%< Transfer zone of authority. */
+ ns_t_mailb = 253, /*%< Transfer mailbox records. */
+ ns_t_maila = 254, /*%< Transfer mail agent records. */
+ ns_t_any = 255, /*%< Wildcard match. */
+ ns_t_zxfr = 256, /*%< BIND-specific, nonstandard. */
+ ns_t_max = 65536
+} ns_type;
+
+/* Exclusively a QTYPE? (not also an RTYPE) */
+#define ns_t_qt_p(t) (ns_t_xfr_p(t) || (t) == ns_t_any || \
+ (t) == ns_t_mailb || (t) == ns_t_maila)
+/* Some kind of meta-RR? (not a QTYPE, but also not an RTYPE) */
+#define ns_t_mrr_p(t) ((t) == ns_t_tsig || (t) == ns_t_opt)
+/* Exclusively an RTYPE? (not also a QTYPE or a meta-RR) */
+#define ns_t_rr_p(t) (!ns_t_qt_p(t) && !ns_t_mrr_p(t))
+#define ns_t_udp_p(t) ((t) != ns_t_axfr && (t) != ns_t_zxfr)
+#define ns_t_xfr_p(t) ((t) == ns_t_axfr || (t) == ns_t_ixfr || \
+ (t) == ns_t_zxfr)
+
+/*%
+ * Values for class field
+ */
+typedef enum __ns_class {
+ ns_c_invalid = 0, /*%< Cookie. */
+ ns_c_in = 1, /*%< Internet. */
+ ns_c_2 = 2, /*%< unallocated/unsupported. */
+ ns_c_chaos = 3, /*%< MIT Chaos-net. */
+ ns_c_hs = 4, /*%< MIT Hesiod. */
+ /* Query class values which do not appear in resource records */
+ ns_c_none = 254, /*%< for prereq. sections in update requests */
+ ns_c_any = 255, /*%< Wildcard match. */
+ ns_c_max = 65536
+} ns_class;
+
+/* DNSSEC constants. */
+
+typedef enum __ns_key_types {
+ ns_kt_rsa = 1, /*%< key type RSA/MD5 */
+ ns_kt_dh = 2, /*%< Diffie Hellman */
+ ns_kt_dsa = 3, /*%< Digital Signature Standard (MANDATORY) */
+ ns_kt_private = 254 /*%< Private key type starts with OID */
+} ns_key_types;
+
+typedef enum __ns_cert_types {
+ cert_t_pkix = 1, /*%< PKIX (X.509v3) */
+ cert_t_spki = 2, /*%< SPKI */
+ cert_t_pgp = 3, /*%< PGP */
+ cert_t_url = 253, /*%< URL private type */
+ cert_t_oid = 254 /*%< OID private type */
+} ns_cert_types;
+
+/* Flags field of the KEY RR rdata. */
+#define NS_KEY_TYPEMASK 0xC000 /*%< Mask for "type" bits */
+#define NS_KEY_TYPE_AUTH_CONF 0x0000 /*%< Key usable for both */
+#define NS_KEY_TYPE_CONF_ONLY 0x8000 /*%< Key usable for confidentiality */
+#define NS_KEY_TYPE_AUTH_ONLY 0x4000 /*%< Key usable for authentication */
+#define NS_KEY_TYPE_NO_KEY 0xC000 /*%< No key usable for either; no key */
+/* The type bits can also be interpreted independently, as single bits: */
+#define NS_KEY_NO_AUTH 0x8000 /*%< Key unusable for authentication */
+#define NS_KEY_NO_CONF 0x4000 /*%< Key unusable for confidentiality */
+#define NS_KEY_RESERVED2 0x2000 /* Security is *mandatory* if bit=0 */
+#define NS_KEY_EXTENDED_FLAGS 0x1000 /*%< reserved - must be zero */
+#define NS_KEY_RESERVED4 0x0800 /*%< reserved - must be zero */
+#define NS_KEY_RESERVED5 0x0400 /*%< reserved - must be zero */
+#define NS_KEY_NAME_TYPE 0x0300 /*%< these bits determine the type */
+#define NS_KEY_NAME_USER 0x0000 /*%< key is assoc. with user */
+#define NS_KEY_NAME_ENTITY 0x0200 /*%< key is assoc. with entity eg host */
+#define NS_KEY_NAME_ZONE 0x0100 /*%< key is zone key */
+#define NS_KEY_NAME_RESERVED 0x0300 /*%< reserved meaning */
+#define NS_KEY_RESERVED8 0x0080 /*%< reserved - must be zero */
+#define NS_KEY_RESERVED9 0x0040 /*%< reserved - must be zero */
+#define NS_KEY_RESERVED10 0x0020 /*%< reserved - must be zero */
+#define NS_KEY_RESERVED11 0x0010 /*%< reserved - must be zero */
+#define NS_KEY_SIGNATORYMASK 0x000F /*%< key can sign RR's of same name */
+#define NS_KEY_RESERVED_BITMASK ( NS_KEY_RESERVED2 | \
+ NS_KEY_RESERVED4 | \
+ NS_KEY_RESERVED5 | \
+ NS_KEY_RESERVED8 | \
+ NS_KEY_RESERVED9 | \
+ NS_KEY_RESERVED10 | \
+ NS_KEY_RESERVED11 )
+#define NS_KEY_RESERVED_BITMASK2 0xFFFF /*%< no bits defined here */
+/* The Algorithm field of the KEY and SIG RR's is an integer, {1..254} */
+#define NS_ALG_MD5RSA 1 /*%< MD5 with RSA */
+#define NS_ALG_DH 2 /*%< Diffie Hellman KEY */
+#define NS_ALG_DSA 3 /*%< DSA KEY */
+#define NS_ALG_DSS NS_ALG_DSA
+#define NS_ALG_EXPIRE_ONLY 253 /*%< No alg, no security */
+#define NS_ALG_PRIVATE_OID 254 /*%< Key begins with OID giving alg */
+/* Protocol values */
+/* value 0 is reserved */
+#define NS_KEY_PROT_TLS 1
+#define NS_KEY_PROT_EMAIL 2
+#define NS_KEY_PROT_DNSSEC 3
+#define NS_KEY_PROT_IPSEC 4
+#define NS_KEY_PROT_ANY 255
+
+/* Signatures */
+#define NS_MD5RSA_MIN_BITS 512 /*%< Size of a mod or exp in bits */
+#define NS_MD5RSA_MAX_BITS 4096
+ /* Total of binary mod and exp */
+#define NS_MD5RSA_MAX_BYTES ((NS_MD5RSA_MAX_BITS+7/8)*2+3)
+ /* Max length of text sig block */
+#define NS_MD5RSA_MAX_BASE64 (((NS_MD5RSA_MAX_BYTES+2)/3)*4)
+#define NS_MD5RSA_MIN_SIZE ((NS_MD5RSA_MIN_BITS+7)/8)
+#define NS_MD5RSA_MAX_SIZE ((NS_MD5RSA_MAX_BITS+7)/8)
+
+#define NS_DSA_SIG_SIZE 41
+#define NS_DSA_MIN_SIZE 213
+#define NS_DSA_MAX_BYTES 405
+
+/* Offsets into SIG record rdata to find various values */
+#define NS_SIG_TYPE 0 /*%< Type flags */
+#define NS_SIG_ALG 2 /*%< Algorithm */
+#define NS_SIG_LABELS 3 /*%< How many labels in name */
+#define NS_SIG_OTTL 4 /*%< Original TTL */
+#define NS_SIG_EXPIR 8 /*%< Expiration time */
+#define NS_SIG_SIGNED 12 /*%< Signature time */
+#define NS_SIG_FOOT 16 /*%< Key footprint */
+#define NS_SIG_SIGNER 18 /*%< Domain name of who signed it */
+/* How RR types are represented as bit-flags in NXT records */
+#define NS_NXT_BITS 8
+#define NS_NXT_BIT_SET( n,p) (p[(n)/NS_NXT_BITS] |= (0x80>>((n)%NS_NXT_BITS)))
+#define NS_NXT_BIT_CLEAR(n,p) (p[(n)/NS_NXT_BITS] &= ~(0x80>>((n)%NS_NXT_BITS)))
+#define NS_NXT_BIT_ISSET(n,p) (p[(n)/NS_NXT_BITS] & (0x80>>((n)%NS_NXT_BITS)))
+#define NS_NXT_MAX 127
+
+/*%
+ * EDNS0 extended flags and option codes, host order.
+ */
+#define NS_OPT_DNSSEC_OK 0x8000U
+#define NS_OPT_NSID 3
+
+/*%
+ * Inline versions of get/put short/long. Pointer is advanced.
+ */
+#define NS_GET16(s, cp) do { \
+ register const u_char *t_cp = (const u_char *)(cp); \
+ (s) = ((u_int16_t)t_cp[0] << 8) \
+ | ((u_int16_t)t_cp[1]) \
+ ; \
+ (cp) += NS_INT16SZ; \
+} while (0)
+
+#define NS_GET32(l, cp) do { \
+ register const u_char *t_cp = (const u_char *)(cp); \
+ (l) = ((u_int32_t)t_cp[0] << 24) \
+ | ((u_int32_t)t_cp[1] << 16) \
+ | ((u_int32_t)t_cp[2] << 8) \
+ | ((u_int32_t)t_cp[3]) \
+ ; \
+ (cp) += NS_INT32SZ; \
+} while (0)
+
+#define NS_PUT16(s, cp) do { \
+ register u_int16_t t_s = (u_int16_t)(s); \
+ register u_char *t_cp = (u_char *)(cp); \
+ *t_cp++ = t_s >> 8; \
+ *t_cp = t_s; \
+ (cp) += NS_INT16SZ; \
+} while (0)
+
+#define NS_PUT32(l, cp) do { \
+ register u_int32_t t_l = (u_int32_t)(l); \
+ register u_char *t_cp = (u_char *)(cp); \
+ *t_cp++ = t_l >> 24; \
+ *t_cp++ = t_l >> 16; \
+ *t_cp++ = t_l >> 8; \
+ *t_cp = t_l; \
+ (cp) += NS_INT32SZ; \
+} while (0)
+
+#endif /* !_ARPA_NAMESER_H_ */
+/*! \file */
+
+
+
+/*%
+ * from nameser.h 8.1 (Berkeley) 6/2/93
+ * $BINDId: nameser_compat.h,v 8.11 1999/01/02 08:00:58 vixie Exp $
+ */
+
+#ifndef _ARPA_NAMESER_COMPAT_
+#define _ARPA_NAMESER_COMPAT_
+
+#define __BIND 19950621 /*%< (DEAD) interface version stamp. */
+
+#include <endian.h>
+
+/*%
+ * Structure for query header. The order of the fields is machine- and
+ * compiler-dependent, depending on the byte/bit order and the layout
+ * of bit fields. We use bit fields only in int variables, as this
+ * is all ANSI requires. This requires a somewhat confusing rearrangement.
+ */
+
+typedef struct {
+ unsigned id :16; /*%< query identification number */
+#if BYTE_ORDER == BIG_ENDIAN
+ /* fields in third byte */
+ unsigned qr: 1; /*%< response flag */
+ unsigned opcode: 4; /*%< purpose of message */
+ unsigned aa: 1; /*%< authoritive answer */
+ unsigned tc: 1; /*%< truncated message */
+ unsigned rd: 1; /*%< recursion desired */
+ /* fields in fourth byte */
+ unsigned ra: 1; /*%< recursion available */
+ unsigned unused :1; /*%< unused bits (MBZ as of 4.9.3a3) */
+ unsigned ad: 1; /*%< authentic data from named */
+ unsigned cd: 1; /*%< checking disabled by resolver */
+ unsigned rcode :4; /*%< response code */
+#endif
+#if BYTE_ORDER == LITTLE_ENDIAN || BYTE_ORDER == PDP_ENDIAN
+ /* fields in third byte */
+ unsigned rd :1; /*%< recursion desired */
+ unsigned tc :1; /*%< truncated message */
+ unsigned aa :1; /*%< authoritive answer */
+ unsigned opcode :4; /*%< purpose of message */
+ unsigned qr :1; /*%< response flag */
+ /* fields in fourth byte */
+ unsigned rcode :4; /*%< response code */
+ unsigned cd: 1; /*%< checking disabled by resolver */
+ unsigned ad: 1; /*%< authentic data from named */
+ unsigned unused :1; /*%< unused bits (MBZ as of 4.9.3a3) */
+ unsigned ra :1; /*%< recursion available */
+#endif
+ /* remaining bytes */
+ unsigned qdcount :16; /*%< number of question entries */
+ unsigned ancount :16; /*%< number of answer entries */
+ unsigned nscount :16; /*%< number of authority entries */
+ unsigned arcount :16; /*%< number of resource entries */
+} HEADER;
+
+#define PACKETSZ NS_PACKETSZ
+#define MAXDNAME NS_MAXDNAME
+#define MAXCDNAME NS_MAXCDNAME
+#define MAXLABEL NS_MAXLABEL
+#define HFIXEDSZ NS_HFIXEDSZ
+#define QFIXEDSZ NS_QFIXEDSZ
+#define RRFIXEDSZ NS_RRFIXEDSZ
+#define INT32SZ NS_INT32SZ
+#define INT16SZ NS_INT16SZ
+#define INT8SZ NS_INT8SZ
+#define INADDRSZ NS_INADDRSZ
+#define IN6ADDRSZ NS_IN6ADDRSZ
+#define INDIR_MASK NS_CMPRSFLGS
+#define NAMESERVER_PORT NS_DEFAULTPORT
+
+#define S_ZONE ns_s_zn
+#define S_PREREQ ns_s_pr
+#define S_UPDATE ns_s_ud
+#define S_ADDT ns_s_ar
+
+#define QUERY ns_o_query
+#define IQUERY ns_o_iquery
+#define STATUS ns_o_status
+#define NS_NOTIFY_OP ns_o_notify
+#define NS_UPDATE_OP ns_o_update
+
+#define NOERROR ns_r_noerror
+#define FORMERR ns_r_formerr
+#define SERVFAIL ns_r_servfail
+#define NXDOMAIN ns_r_nxdomain
+#define NOTIMP ns_r_notimpl
+#define REFUSED ns_r_refused
+#define YXDOMAIN ns_r_yxdomain
+#define YXRRSET ns_r_yxrrset
+#define NXRRSET ns_r_nxrrset
+#define NOTAUTH ns_r_notauth
+#define NOTZONE ns_r_notzone
+/*#define BADSIG ns_r_badsig*/
+/*#define BADKEY ns_r_badkey*/
+/*#define BADTIME ns_r_badtime*/
+
+
+#define DELETE ns_uop_delete
+#define ADD ns_uop_add
+
+#define T_A ns_t_a
+#define T_NS ns_t_ns
+#define T_MD ns_t_md
+#define T_MF ns_t_mf
+#define T_CNAME ns_t_cname
+#define T_SOA ns_t_soa
+#define T_MB ns_t_mb
+#define T_MG ns_t_mg
+#define T_MR ns_t_mr
+#define T_NULL ns_t_null
+#define T_WKS ns_t_wks
+#define T_PTR ns_t_ptr
+#define T_HINFO ns_t_hinfo
+#define T_MINFO ns_t_minfo
+#define T_MX ns_t_mx
+#define T_TXT ns_t_txt
+#define T_RP ns_t_rp
+#define T_AFSDB ns_t_afsdb
+#define T_X25 ns_t_x25
+#define T_ISDN ns_t_isdn
+#define T_RT ns_t_rt
+#define T_NSAP ns_t_nsap
+#define T_NSAP_PTR ns_t_nsap_ptr
+#define T_SIG ns_t_sig
+#define T_KEY ns_t_key
+#define T_PX ns_t_px
+#define T_GPOS ns_t_gpos
+#define T_AAAA ns_t_aaaa
+#define T_LOC ns_t_loc
+#define T_NXT ns_t_nxt
+#define T_EID ns_t_eid
+#define T_NIMLOC ns_t_nimloc
+#define T_SRV ns_t_srv
+#define T_ATMA ns_t_atma
+#define T_NAPTR ns_t_naptr
+#define T_A6 ns_t_a6
+#define T_DNAME ns_t_dname
+#define T_TSIG ns_t_tsig
+#define T_IXFR ns_t_ixfr
+#define T_AXFR ns_t_axfr
+#define T_MAILB ns_t_mailb
+#define T_MAILA ns_t_maila
+#define T_ANY ns_t_any
+
+#define C_IN ns_c_in
+#define C_CHAOS ns_c_chaos
+#define C_HS ns_c_hs
+/* BIND_UPDATE */
+#define C_NONE ns_c_none
+#define C_ANY ns_c_any
+
+#define GETSHORT NS_GET16
+#define GETLONG NS_GET32
+#define PUTSHORT NS_PUT16
+#define PUTLONG NS_PUT32
+
+#endif /* _ARPA_NAMESER_COMPAT_ */
+/*! \file */
diff --git a/jni/iodine/src/encoding.c b/jni/iodine/src/encoding.c
new file mode 100644
index 0000000..896d67d
--- /dev/null
+++ b/jni/iodine/src/encoding.c
@@ -0,0 +1,130 @@
+/*
+ * 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 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 "common.h"
+#include "encoding.h"
+
+int
+build_hostname(char *buf, size_t buflen,
+ const char *data, const size_t datalen,
+ const char *topdomain, struct encoder *encoder, int maxlen)
+{
+ int encsize;
+ size_t space;
+ char *b;
+
+ space = MIN(maxlen, buflen) - strlen(topdomain) - 8;
+ /* 8 = 5 max header length + 1 dot before topdomain + 2 safety */
+
+ if (!encoder->places_dots())
+ space -= (space / 57); /* space for dots */
+
+ memset(buf, 0, buflen);
+
+ encsize = encoder->encode(buf, &space, data, datalen);
+
+ if (!encoder->places_dots())
+ inline_dotify(buf, buflen);
+
+ b = buf;
+ b += strlen(buf);
+
+ /* move b back one step to see if the dot is there */
+ b--;
+ if (*b != '.')
+ *++b = '.';
+ b++;
+ /* move b ahead of the string so we can copy to it */
+
+ strncpy(b, topdomain, strlen(topdomain)+1);
+
+ return space;
+}
+
+int
+unpack_data(char *buf, size_t buflen, char *data, size_t datalen, struct encoder *enc)
+{
+ if (!enc->eats_dots())
+ datalen = inline_undotify(data, datalen);
+ return enc->decode(buf, &buflen, data, datalen);
+}
+
+int
+inline_dotify(char *buf, size_t buflen)
+{
+ unsigned dots;
+ unsigned pos;
+ unsigned total;
+ char *reader, *writer;
+
+ total = strlen(buf);
+ dots = total / 57;
+
+ writer = buf;
+ writer += total;
+ writer += dots;
+
+ total += dots;
+ if (strlen(buf) + dots > buflen) {
+ writer = buf;
+ writer += buflen;
+ total = buflen;
+ }
+
+ reader = writer - dots;
+ pos = (unsigned) (reader - buf) + 1;
+
+ while (dots) {
+ *writer-- = *reader--;
+ pos--;
+ if (pos % 57 == 0) {
+ *writer-- = '.';
+ dots--;
+ }
+ }
+
+ /* return new length of string */
+ return total;
+}
+
+int
+inline_undotify(char *buf, size_t len)
+{
+ unsigned pos;
+ unsigned dots;
+ char *reader, *writer;
+
+ writer = buf;
+ reader = writer;
+
+ pos = 0;
+ dots = 0;
+
+ while (pos < len) {
+ if (*reader == '.') {
+ reader++;
+ pos++;
+ dots++;
+ continue;
+ }
+ *writer++ = *reader++;
+ pos++;
+ }
+
+ /* return new length of string */
+ return len - dots;
+}
diff --git a/jni/iodine/src/encoding.h b/jni/iodine/src/encoding.h
new file mode 100644
index 0000000..7ddf6e0
--- /dev/null
+++ b/jni/iodine/src/encoding.h
@@ -0,0 +1,43 @@
+/*
+ * 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 and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _ENCODING_H_
+#define _ENCODING_H_
+
+/* All-0, all-1, 01010101, 10101010: each 4 times to make sure the pattern
+ spreads across multiple encoded chars -> 16 bytes total.
+ Followed by 32 bytes from my /dev/random; should be enough.
+ */
+#define DOWNCODECCHECK1 "\000\000\000\000\377\377\377\377\125\125\125\125\252\252\252\252\201\143\310\322\307\174\262\027\137\117\316\311\111\055\122\041\141\251\161\040\045\263\006\163\346\330\104\060\171\120\127\277"
+#define DOWNCODECCHECK1_LEN 48
+
+struct encoder {
+ char name[8];
+ int (*encode) (char *, size_t *, const void *, size_t);
+ 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 build_hostname(char *, size_t, const char *, const size_t, const char *, struct encoder *, int);
+int unpack_data(char *, size_t, char *, size_t, struct encoder *);
+int inline_dotify(char *, size_t);
+int inline_undotify(char *, size_t);
+
+
+#endif /* _ENCODING_H_ */
diff --git a/jni/iodine/src/fw_query.c b/jni/iodine/src/fw_query.c
new file mode 100644
index 0000000..3727f08
--- /dev/null
+++ b/jni/iodine/src/fw_query.c
@@ -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(unsigned 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/jni/iodine/src/fw_query.h b/jni/iodine/src/fw_query.h
new file mode 100644
index 0000000..7568a5f
--- /dev/null
+++ b/jni/iodine/src/fw_query.h
@@ -0,0 +1,41 @@
+/*
+ * 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>
+#ifdef WINDOWS32
+#include "windows.h"
+#include <winsock2.h>
+#else
+#include <sys/socket.h>
+#endif
+
+#define FW_QUERY_CACHE_SIZE 16
+
+struct fw_query {
+ struct sockaddr addr;
+ int addrlen;
+ unsigned short id;
+};
+
+void fw_query_init();
+void fw_query_put(struct fw_query *fw_query);
+void fw_query_get(unsigned short query_id, struct fw_query **fw_query);
+
+#endif /*__FW_QUERY_H__*/
+
diff --git a/jni/iodine/src/iodine.c b/jni/iodine/src/iodine.c
new file mode 100644
index 0000000..03efb18
--- /dev/null
+++ b/jni/iodine/src/iodine.c
@@ -0,0 +1,375 @@
+/*
+ * 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 and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <time.h>
+
+#ifdef WINDOWS32
+#include "windows.h"
+#include <winsock2.h>
+#else
+#include <grp.h>
+#include <pwd.h>
+#endif
+
+#include "common.h"
+#include "tun.h"
+#include "client.h"
+#include "util.h"
+
+#ifdef WINDOWS32
+WORD req_version = MAKEWORD(2, 2);
+WSADATA wsa_data;
+#endif
+
+#if !defined(BSD) && !defined(__GLIBC__)
+static char *__progname;
+#endif
+
+#define PASSWORD_ENV_VAR "IODINE_PASS"
+
+static void
+sighandler(int sig)
+{
+ client_stop();
+}
+
+static void
+usage() {
+ extern char *__progname;
+
+ fprintf(stderr, "Usage: %s [-v] [-h] [-f] [-r] [-u user] [-t chrootdir] [-d device] "
+ "[-P password] [-m maxfragsize] [-M maxlen] [-T type] [-O enc] [-L 0|1] [-I sec] "
+ "[-z context] [-F pidfile] [nameserver] topdomain\n", __progname);
+ exit(2);
+}
+
+static void
+help() {
+ extern char *__progname;
+
+ fprintf(stderr, "iodine IP over DNS tunneling client\n");
+ fprintf(stderr, "Usage: %s [-v] [-h] [-f] [-r] [-u user] [-t chrootdir] [-d device] "
+ "[-P password] [-m maxfragsize] [-M maxlen] [-T type] [-O enc] [-L 0|1] [-I sec] "
+ "[-z context] [-F pidfile] [nameserver] topdomain\n", __progname);
+ fprintf(stderr, "Options to try if connection doesn't work:\n");
+ fprintf(stderr, " -T force dns type: NULL, TXT, SRV, MX, CNAME, A (default: autodetect)\n");
+ fprintf(stderr, " -O force downstream encoding for -T other than NULL: Base32, Base64, Base64u,\n");
+ fprintf(stderr, " Base128, or (only for TXT:) Raw (default: autodetect)\n");
+ fprintf(stderr, " -I max interval between requests (default 4 sec) to prevent DNS timeouts\n");
+ fprintf(stderr, " -L 1: use lazy mode for low-latency (default). 0: don't (implies -I1)\n");
+ fprintf(stderr, " -m max size of downstream fragments (default: autodetect)\n");
+ fprintf(stderr, " -M max size of upstream hostnames (~100-255, default: 255)\n");
+ fprintf(stderr, " -r to skip raw UDP mode attempt\n");
+ fprintf(stderr, " -P password used for authentication (max 32 chars will be used)\n");
+ fprintf(stderr, "Other options:\n");
+ fprintf(stderr, " -v to print version info and exit\n");
+ fprintf(stderr, " -h to print this help and exit\n");
+ fprintf(stderr, " -f to keep running in foreground\n");
+ fprintf(stderr, " -u name to drop privileges and run as user 'name'\n");
+ fprintf(stderr, " -t dir to chroot to directory dir\n");
+ fprintf(stderr, " -d device to set tunnel device name\n");
+ fprintf(stderr, " -z context, to apply specified SELinux context after initialization\n");
+ fprintf(stderr, " -F pidfile to write pid to a file\n");
+ fprintf(stderr, "nameserver is the IP number/hostname of the relaying nameserver. if absent, /etc/resolv.conf is used\n");
+ fprintf(stderr, "topdomain is the FQDN that is delegated to the tunnel endpoint.\n");
+
+ exit(0);
+}
+
+static void
+version() {
+ fprintf(stderr, "iodine IP over DNS tunneling client\n");
+ fprintf(stderr, "version: 0.6.0-rc1 from 2010-02-13\n");
+
+ exit(0);
+}
+
+int
+main(int argc, char **argv)
+{
+ char *nameserv_addr;
+ char *topdomain;
+#ifndef WINDOWS32
+ struct passwd *pw;
+#endif
+ char *username;
+ char password[33];
+ int foreground;
+ char *newroot;
+ char *context;
+ char *device;
+ char *pidfile;
+ int choice;
+ int tun_fd;
+ int dns_fd;
+ int max_downstream_frag_size;
+ int autodetect_frag_size;
+ int retval;
+ int raw_mode;
+ int lazymode;
+ int selecttimeout;
+ int hostname_maxlen;
+
+ nameserv_addr = NULL;
+ topdomain = NULL;
+#ifndef WINDOWS32
+ pw = NULL;
+#endif
+ username = NULL;
+ memset(password, 0, 33);
+ srand(time(NULL));
+ foreground = 0;
+ newroot = NULL;
+ context = NULL;
+ device = NULL;
+ pidfile = NULL;
+
+ autodetect_frag_size = 1;
+ max_downstream_frag_size = 3072;
+ retval = 0;
+ raw_mode = 1;
+ lazymode = 1;
+ selecttimeout = 4;
+ hostname_maxlen = 0xFF;
+
+#ifdef WINDOWS32
+ WSAStartup(req_version, &wsa_data);
+#endif
+
+ srand((unsigned) time(NULL));
+ client_init();
+
+#if !defined(BSD) && !defined(__GLIBC__)
+ __progname = strrchr(argv[0], '/');
+ if (__progname == NULL)
+ __progname = argv[0];
+ else
+ __progname++;
+#endif
+
+ while ((choice = getopt(argc, argv, "vfhru:t:d:P:m:M:F:T:O:L:I:")) != -1) {
+ switch(choice) {
+ case 'v':
+ version();
+ /* NOTREACHED */
+ break;
+ case 'f':
+ foreground = 1;
+ break;
+ case 'h':
+ help();
+ /* NOTREACHED */
+ break;
+ case 'r':
+ raw_mode = 0;
+ case 'u':
+ username = optarg;
+ break;
+ case 't':
+ newroot = optarg;
+ break;
+ case 'd':
+ device = optarg;
+ break;
+ case 'P':
+ strncpy(password, optarg, sizeof(password));
+ password[sizeof(password)-1] = 0;
+
+ /* 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;
+ case 'M':
+ hostname_maxlen = atoi(optarg);
+ if (hostname_maxlen > 255)
+ hostname_maxlen = 255;
+ if (hostname_maxlen < 10)
+ hostname_maxlen = 10;
+ break;
+ case 'z':
+ context = optarg;
+ break;
+ case 'F':
+ pidfile = optarg;
+ break;
+ case 'T':
+ set_qtype(optarg);
+ break;
+ case 'O': /* not -D, is Debug in server */
+ set_downenc(optarg);
+ break;
+ case 'L':
+ lazymode = atoi(optarg);
+ if (lazymode > 1)
+ lazymode = 1;
+ if (lazymode < 0)
+ lazymode = 0;
+ if (!lazymode)
+ selecttimeout = 1;
+ break;
+ case 'I':
+ selecttimeout = atoi(optarg);
+ if (selecttimeout < 1)
+ selecttimeout = 1;
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+
+ check_superuser(usage);
+
+ argc -= optind;
+ argv += optind;
+
+ switch (argc) {
+ case 1:
+ nameserv_addr = get_resolvconf_addr();
+ topdomain = strdup(argv[0]);
+ break;
+ case 2:
+ nameserv_addr = argv[0];
+ topdomain = strdup(argv[1]);
+ break;
+ default:
+ usage();
+ /* 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 */
+ }
+
+ if (nameserv_addr) {
+ client_set_nameserver(nameserv_addr, DNS_PORT);
+ } else {
+ warnx("No nameserver found - not connected to any network?\n");
+ usage();
+ /* NOTREACHED */
+ }
+
+ 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 */
+ }
+
+ client_set_selecttimeout(selecttimeout);
+ client_set_lazymode(lazymode);
+ client_set_topdomain(topdomain);
+ client_set_hostname_maxlen(hostname_maxlen);
+
+ if (username != NULL) {
+#ifndef WINDOWS32
+ if ((pw = getpwnam(username)) == NULL) {
+ warnx("User %s does not exist!\n", username);
+ usage();
+ /* NOTREACHED */
+ }
+#endif
+ }
+
+ if (strlen(password) == 0) {
+ if (NULL != getenv(PASSWORD_ENV_VAR))
+ snprintf(password, sizeof(password), "%s", getenv(PASSWORD_ENV_VAR));
+ else
+ read_password(password, sizeof(password));
+ }
+
+ client_set_password(password);
+
+ if ((tun_fd = open_tun(device)) == -1) {
+ retval = 1;
+ goto cleanup1;
+ }
+ if ((dns_fd = open_dns(0, INADDR_ANY)) == -1) {
+ retval = 1;
+ goto cleanup2;
+ }
+
+ signal(SIGINT, sighandler);
+ signal(SIGTERM, sighandler);
+
+ fprintf(stderr, "Sending DNS queries for %s to %s\n",
+ topdomain, nameserv_addr);
+
+ if (client_handshake(dns_fd, raw_mode, autodetect_frag_size, max_downstream_frag_size)) {
+ retval = 1;
+ goto cleanup2;
+ }
+
+ if (client_get_conn() == CONN_RAW_UDP) {
+ fprintf(stderr, "Sending raw traffic directly to %s\n", client_get_raw_addr());
+ }
+
+ fprintf(stderr, "Connection setup complete, transmitting data.\n");
+
+ if (foreground == 0)
+ do_detach();
+
+ if (pidfile != NULL)
+ do_pidfile(pidfile);
+
+ if (newroot != NULL)
+ do_chroot(newroot);
+
+ if (username != NULL) {
+#ifndef WINDOWS32
+ gid_t gids[1];
+ gids[0] = pw->pw_gid;
+ 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 */
+ }
+#endif
+ }
+
+ if (context != NULL)
+ do_setcon(context);
+
+ client_tunnel(tun_fd, dns_fd);
+
+cleanup2:
+ close_dns(dns_fd);
+ close_tun(tun_fd);
+cleanup1:
+
+ return retval;
+}
+
diff --git a/jni/iodine/src/iodined.c b/jni/iodine/src/iodined.c
new file mode 100644
index 0000000..3681084
--- /dev/null
+++ b/jni/iodine/src/iodined.c
@@ -0,0 +1,2486 @@
+/*
+ * 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 and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <time.h>
+#include <zlib.h>
+
+#include "common.h"
+
+#ifdef WINDOWS32
+#include "windows.h"
+#include <winsock2.h>
+#else
+#include <arpa/nameser.h>
+#ifdef DARWIN
+#define BIND_8_COMPAT
+#include <arpa/nameser_compat.h>
+#endif
+#define _XPG4_2
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <grp.h>
+#include <sys/uio.h>
+#include <pwd.h>
+#include <netdb.h>
+#include <syslog.h>
+#endif
+
+#include "dns.h"
+#include "encoding.h"
+#include "base32.h"
+#include "base64.h"
+#include "base64u.h"
+#include "base128.h"
+#include "user.h"
+#include "login.h"
+#include "tun.h"
+#include "fw_query.h"
+#include "version.h"
+
+#ifdef WINDOWS32
+WORD req_version = MAKEWORD(2, 2);
+WSADATA wsa_data;
+#endif
+
+#define PASSWORD_ENV_VAR "IODINED_PASS"
+
+static int running = 1;
+static char *topdomain;
+static char password[33];
+static struct encoder *b32;
+static struct encoder *b64;
+static struct encoder *b64u;
+static struct encoder *b128;
+static int created_users;
+
+static int check_ip;
+static int my_mtu;
+static in_addr_t my_ip;
+static int netmask;
+
+static in_addr_t ns_ip;
+
+static int bind_port;
+static int debug;
+
+#if !defined(BSD) && !defined(__GLIBC__)
+static char *__progname;
+#endif
+
+static int read_dns(int, int, struct query *);
+static void write_dns(int, struct query *, char *, int, char);
+static void handle_full_packet(int, int, int);
+
+static void
+sigint(int sig)
+{
+ running = 0;
+}
+
+#ifdef WINDOWS32
+#define LOG_EMERG 0
+#define LOG_ALERT 1
+#define LOG_CRIT 2
+#define LOG_ERR 3
+#define LOG_WARNING 4
+#define LOG_NOTICE 5
+#define LOG_INFO 6
+#define LOG_DEBUG 7
+static void
+syslog(int a, const char *str, ...)
+{
+ /* TODO: implement (add to event log), move to common.c */
+ ;
+}
+#endif
+
+static int
+check_user_and_ip(int userid, struct query *q)
+{
+ struct sockaddr_in *tempin;
+
+ /* Note: duplicate in handle_raw_login() except IP-address check */
+
+ if (userid < 0 || userid >= created_users ) {
+ return 1;
+ }
+ if (!users[userid].active || users[userid].disabled) {
+ return 1;
+ }
+ if (users[userid].last_pkt + 60 < time(NULL)) {
+ 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));
+}
+
+static void
+send_raw(int fd, char *buf, int buflen, int user, int cmd, struct query *q)
+{
+ char packet[4096];
+ int len;
+
+ len = MIN(sizeof(packet) - RAW_HDR_LEN, buflen);
+
+ memcpy(packet, raw_header, RAW_HDR_LEN);
+ if (len) {
+ memcpy(&packet[RAW_HDR_LEN], buf, len);
+ }
+
+ len += RAW_HDR_LEN;
+ packet[RAW_HDR_CMD] = cmd | (user & 0x0F);
+
+ if (debug >= 2) {
+ struct sockaddr_in *tempin;
+ tempin = (struct sockaddr_in *) &(q->from);
+ fprintf(stderr, "TX-raw: client %s, cmd %d, %d bytes\n",
+ inet_ntoa(tempin->sin_addr), cmd, len);
+ }
+
+ sendto(fd, packet, len, 0, &q->from, q->fromlen);
+}
+
+
+static void
+start_new_outpacket(int userid, char *data, int datalen)
+/* Copies data to .outpacket and resets all counters.
+ data is expected to be compressed already. */
+{
+ datalen = MIN(datalen, sizeof(users[userid].outpacket.data));
+ memcpy(users[userid].outpacket.data, data, datalen);
+ users[userid].outpacket.len = datalen;
+ users[userid].outpacket.offset = 0;
+ users[userid].outpacket.sentlen = 0;
+ users[userid].outpacket.seqno = (users[userid].outpacket.seqno + 1) & 7;
+ users[userid].outpacket.fragment = 0;
+ users[userid].outfragresent = 0;
+}
+
+#ifdef OUTPACKETQ_LEN
+
+static int
+save_to_outpacketq(int userid, char *data, int datalen)
+/* Find space in outpacket-queue and store data (expected compressed already).
+ Returns: 1 = okay, 0 = no space. */
+{
+ int fill;
+
+ if (users[userid].outpacketq_filled >= OUTPACKETQ_LEN)
+ /* no space */
+ return 0;
+
+ fill = users[userid].outpacketq_nexttouse +
+ users[userid].outpacketq_filled;
+ if (fill >= OUTPACKETQ_LEN)
+ fill -= OUTPACKETQ_LEN;
+
+ datalen = MIN(datalen, sizeof(users[userid].outpacketq[fill].data));
+ memcpy(users[userid].outpacketq[fill].data, data, datalen);
+ users[userid].outpacketq[fill].len = datalen;
+
+ users[userid].outpacketq_filled++;
+
+ if (debug >= 3)
+ fprintf(stderr, " Qstore, now %d\n",
+ users[userid].outpacketq_filled);
+
+ return 1;
+}
+
+static int
+get_from_outpacketq(int userid)
+/* Starts new outpacket from queue, if any.
+ Returns: 1 = okay, 0 = no packets were waiting. */
+{
+ int use;
+
+ if (users[userid].outpacketq_filled <= 0)
+ /* no packets */
+ return 0;
+
+ use = users[userid].outpacketq_nexttouse;
+
+ start_new_outpacket(userid, users[userid].outpacketq[use].data,
+ users[userid].outpacketq[use].len);
+
+ use++;
+ if (use >= OUTPACKETQ_LEN)
+ use = 0;
+ users[userid].outpacketq_nexttouse = use;
+ users[userid].outpacketq_filled--;
+
+ if (debug >= 3)
+ fprintf(stderr, " Qget, now %d\n",
+ users[userid].outpacketq_filled);
+
+ return 1;
+}
+
+#endif /* OUTPACKETQ_LEN */
+
+#ifdef DNSCACHE_LEN
+
+/* On the DNS cache:
+
+ This cache is implemented to better handle the aggressively impatient DNS
+ servers that very quickly re-send requests when we choose to not
+ immediately answer them in lazy mode. This cache works much better than
+ pruning(=dropping) the improper requests, since the DNS server will
+ actually get an answer instead of silence.
+
+ Because of the CMC in both ping and upstream data, unwanted cache hits
+ are prevented. Data-CMC is only 36 counts, so our cache length should
+ not exceed 36/2=18 packets. (This quick rule assumes all packets are
+ otherwise equal, which they arent: up/downstream seq/frag, tcp sequence
+ number, and of course data.)
+*/
+
+static void
+save_to_dnscache(int userid, struct query *q, char *answer, int answerlen)
+/* Store answer in our little DNS cache. */
+{
+ int fill;
+
+ if (answerlen > sizeof(users[userid].dnscache_answer[fill]))
+ return; /* can't store this */
+
+ fill = users[userid].dnscache_lastfilled + 1;
+ if (fill >= DNSCACHE_LEN)
+ fill = 0;
+
+ memcpy(&(users[userid].dnscache_q[fill]), q, sizeof(struct query));
+ memcpy(users[userid].dnscache_answer[fill], answer, answerlen);
+ users[userid].dnscache_answerlen[fill] = answerlen;
+
+ users[userid].dnscache_lastfilled = fill;
+}
+
+static int
+answer_from_dnscache(int dns_fd, int userid, struct query *q)
+/* Checks cache and sends repeated answer if we alreay saw this query recently.
+ Returns: 1 = answer sent, drop this query, 0 = no answer sent, this is
+ a new query. */
+{
+ int i;
+ int use;
+
+ for (i = 0; i < DNSCACHE_LEN ; i++) {
+ /* Try cache most-recent-first */
+ use = users[userid].dnscache_lastfilled - i;
+ if (use < 0)
+ use += DNSCACHE_LEN;
+
+ if (users[userid].dnscache_q[use].id == 0)
+ continue;
+ if (users[userid].dnscache_answerlen[use] <= 0)
+ continue;
+
+ if (users[userid].dnscache_q[use].type != q->type ||
+ strcmp(users[userid].dnscache_q[use].name, q->name))
+ continue;
+
+ /* okay, match */
+ if (debug >= 1)
+ fprintf(stderr, "OUT user %d %s from dnscache\n", userid, q->name);
+
+ write_dns(dns_fd, q, users[userid].dnscache_answer[use],
+ users[userid].dnscache_answerlen[use],
+ users[userid].downenc);
+
+ q->id = 0; /* this query was used */
+ return 1;
+ }
+
+ /* here only when no match found */
+ return 0;
+}
+
+#endif /* DNSCACHE_LEN */
+
+static inline void
+save_to_qmem(unsigned char *qmem_cmc, unsigned short *qmem_type, int qmem_len,
+ int *qmem_lastfilled, unsigned char *cmc_to_add,
+ unsigned short type_to_add)
+/* Remember query to check for duplicates */
+{
+ int fill;
+
+ fill = *qmem_lastfilled + 1;
+ if (fill >= qmem_len)
+ fill = 0;
+
+ memcpy(qmem_cmc + fill * 4, cmc_to_add, 4);
+ qmem_type[fill] = type_to_add;
+ *qmem_lastfilled = fill;
+}
+
+static inline void
+save_to_qmem_pingordata(int userid, struct query *q)
+{
+ /* Our CMC is a bit more than the "official" CMC; we store 4 bytes
+ just because we can, and because it may prevent some false matches.
+ For ping, we save the 4 decoded bytes: userid + seq/frag + CMC.
+ For data, we save the 4 _un_decoded chars in lowercase: seq/frag's
+ + 1 char CMC; that last char is non-Base32.
+ */
+
+ char cmc[8];
+ int i;
+
+ if (q->name[0] == 'P' || q->name[0] == 'p') {
+ /* Ping packet */
+
+ size_t cmcsize = sizeof(cmc);
+ char *cp = strchr(q->name, '.');
+
+ if (cp == NULL)
+ return; /* illegal hostname; shouldn't happen */
+
+ /* We already unpacked in handle_null_request(), but that's
+ lost now... Note: b32 directly, we want no undotify here! */
+ i = b32->decode(cmc, &cmcsize, q->name + 1, (cp - q->name) - 1);
+
+ if (i < 4)
+ return; /* illegal ping; shouldn't happen */
+
+ save_to_qmem(users[userid].qmemping_cmc,
+ users[userid].qmemping_type, QMEMPING_LEN,
+ &users[userid].qmemping_lastfilled,
+ (void *) cmc, q->type);
+ } else {
+ /* Data packet, hopefully not illegal */
+ if (strlen(q->name) < 5)
+ return;
+
+ /* We store CMC in lowercase; if routing via multiple parallel
+ DNS servers, one may do case-switch and another may not,
+ and we still want to detect duplicates.
+ Data-header is always base32, so case-swap won't hurt.
+ */
+ for (i = 0; i < 4; i++)
+ if (q->name[i+1] >= 'A' && q->name[i+1] <= 'Z')
+ cmc[i] = q->name[i+1] + ('a' - 'A');
+ else
+ cmc[i] = q->name[i+1];
+
+ save_to_qmem(users[userid].qmemdata_cmc,
+ users[userid].qmemdata_type, QMEMDATA_LEN,
+ &users[userid].qmemdata_lastfilled,
+ (void *) cmc, q->type);
+ }
+}
+
+static int
+answer_from_qmem(int dns_fd, struct query *q, unsigned char *qmem_cmc,
+ unsigned short *qmem_type, int qmem_len,
+ unsigned char *cmc_to_check)
+/* Checks query memory and sends an (illegal) answer if this is a duplicate.
+ Returns: 1 = answer sent, drop this query, 0 = no answer sent, this is
+ not a duplicate. */
+{
+ int i;
+
+ for (i = 0; i < qmem_len ; i++) {
+
+ if (qmem_type[i] == T_UNSET)
+ continue;
+ if (qmem_type[i] != q->type)
+ continue;
+ if (memcmp(qmem_cmc + i * 4, cmc_to_check, 4))
+ continue;
+
+ /* okay, match */
+ if (debug >= 1)
+ fprintf(stderr, "OUT from qmem for %s == duplicate, sending illegal reply\n", q->name);
+
+ write_dns(dns_fd, q, "x", 1, 'T');
+
+ q->id = 0; /* this query was used */
+ return 1;
+ }
+
+ /* here only when no match found */
+ return 0;
+}
+
+static inline int
+answer_from_qmem_data(int dns_fd, int userid, struct query *q)
+/* Quick helper function to keep handle_null_request() clean */
+{
+ char cmc[4];
+ int i;
+
+ for (i = 0; i < 4; i++)
+ if (q->name[i+1] >= 'A' && q->name[i+1] <= 'Z')
+ cmc[i] = q->name[i+1] + ('a' - 'A');
+ else
+ cmc[i] = q->name[i+1];
+
+ return answer_from_qmem(dns_fd, q, users[userid].qmemdata_cmc,
+ users[userid].qmemdata_type, QMEMDATA_LEN,
+ (void *) cmc);
+}
+
+static int
+send_chunk_or_dataless(int dns_fd, int userid, struct query *q)
+/* Sends current fragment to user, or dataless packet if there is no
+ current fragment available (-> normal "quiet" ping reply).
+ Does not update anything, except:
+ - discards q always (query is used)
+ - forgets entire users[userid].outpacket if it was sent in one go,
+ and then tries to get new packet from outpacket-queue
+ Returns: 1 = can call us again immediately, new packet from queue;
+ 0 = don't call us again for now.
+*/
+{
+ char pkt[4096];
+ int datalen = 0;
+ int last = 0;
+
+ /* If re-sent too many times, drop entire packet */
+ if (users[userid].outpacket.len > 0 &&
+ users[userid].outfragresent > 5) {
+ users[userid].outpacket.len = 0;
+ users[userid].outpacket.offset = 0;
+ users[userid].outpacket.sentlen = 0;
+ users[userid].outfragresent = 0;
+
+#ifdef OUTPACKETQ_LEN
+ /* Maybe more in queue, use immediately */
+ get_from_outpacketq(userid);
+#endif
+ }
+
+ if (users[userid].outpacket.len > 0) {
+ datalen = MIN(users[userid].fragsize, users[userid].outpacket.len - users[userid].outpacket.offset);
+ datalen = MIN(datalen, sizeof(pkt)-2);
+
+ 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 + datalen);
+
+ users[userid].outfragresent++;
+ }
+
+ /* 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) {
+ fprintf(stderr, "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, q, pkt, datalen + 2, users[userid].downenc);
+
+ if (q->id2 != 0) {
+ q->id = q->id2;
+ q->fromlen = q->fromlen2;
+ memcpy(&(q->from), &(q->from2), q->fromlen2);
+ if (debug >= 1)
+ fprintf(stderr, "OUT again to last duplicate\n");
+ write_dns(dns_fd, q, pkt, datalen + 2, users[userid].downenc);
+ }
+
+ save_to_qmem_pingordata(userid, q);
+
+#ifdef DNSCACHE_LEN
+ save_to_dnscache(userid, q, pkt, datalen + 2);
+#endif
+
+ q->id = 0; /* this query is used */
+
+ if (datalen > 0 && datalen == users[userid].outpacket.len) {
+ /* 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;
+ users[userid].outfragresent = 0;
+
+#ifdef OUTPACKETQ_LEN
+ /* Maybe more in queue, prepare for next time */
+ if (get_from_outpacketq(userid) == 1) {
+ if (debug >= 3)
+ fprintf(stderr, " Chunk & fromqueue: callagain\n");
+ return 1; /* call us again */
+ }
+#endif
+ }
+
+ return 0; /* don't call us again */
+}
+
+static int
+tunnel_tun(int tun_fd, int dns_fd)
+{
+ unsigned long outlen;
+ struct ip *header;
+ char out[64*1024];
+ char in[64*1024];
+ int userid;
+ int read;
+
+ if ((read = read_tun(tun_fd, in, sizeof(in))) <= 0)
+ return 0;
+
+ /* find target ip in packet, in is padded with 4 bytes TUN header */
+ header = (struct ip*) (in + 4);
+ userid = find_user_by_ip(header->ip_dst.s_addr);
+ if (userid < 0)
+ return 0;
+
+ outlen = sizeof(out);
+ compress2((uint8_t*)out, &outlen, (uint8_t*)in, read, 9);
+
+ if (users[userid].conn == CONN_DNS_NULL) {
+#ifdef OUTPACKETQ_LEN
+ /* If a packet is being sent, try storing the new one in the queue.
+ If the queue is full, drop the packet. TCP will hopefully notice
+ and reduce the packet rate. */
+ if (users[userid].outpacket.len > 0) {
+ save_to_outpacketq(userid, out, outlen);
+ return 0;
+ }
+#endif
+
+ start_new_outpacket(userid, out, outlen);
+
+ /* Start sending immediately if query is waiting */
+ if (users[userid].q_sendrealsoon.id != 0)
+ send_chunk_or_dataless(dns_fd, userid, &users[userid].q_sendrealsoon);
+ else if (users[userid].q.id != 0)
+ send_chunk_or_dataless(dns_fd, userid, &users[userid].q);
+
+ return outlen;
+ } else { /* CONN_RAW_UDP */
+ send_raw(dns_fd, out, outlen, userid, RAW_HDR_CMD_DATA, &users[userid].q);
+ return outlen;
+ }
+}
+
+typedef enum {
+ VERSION_ACK,
+ VERSION_NACK,
+ VERSION_FULL
+} version_ack_t;
+
+static void
+send_version_response(int fd, version_ack_t ack, uint32_t payload, int userid, struct query *q)
+{
+ char out[9];
+
+ switch (ack) {
+ case VERSION_ACK:
+ strncpy(out, "VACK", sizeof(out));
+ break;
+ case VERSION_NACK:
+ strncpy(out, "VNAK", sizeof(out));
+ break;
+ case VERSION_FULL:
+ strncpy(out, "VFUL", sizeof(out));
+ break;
+ }
+
+ out[4] = ((payload >> 24) & 0xff);
+ out[5] = ((payload >> 16) & 0xff);
+ out[6] = ((payload >> 8) & 0xff);
+ out[7] = ((payload) & 0xff);
+ out[8] = userid & 0xff;
+
+ write_dns(fd, q, out, sizeof(out), users[userid].downenc);
+}
+
+static void
+process_downstream_ack(int userid, int down_seq, int down_frag)
+/* Process acks from downstream fragments.
+ After this, .offset and .fragment are updated (if ack correct),
+ or .len is set to zero when all is done.
+*/
+{
+ if (users[userid].outpacket.len <= 0)
+ /* No packet to apply acks to */
+ return;
+
+ if (users[userid].outpacket.seqno != down_seq ||
+ users[userid].outpacket.fragment != down_frag)
+ /* Not the ack we're waiting for; probably duplicate of old
+ ack, happens a lot with ping packets */
+ return;
+
+ /* Received proper ack */
+ users[userid].outpacket.offset += users[userid].outpacket.sentlen;
+ users[userid].outpacket.sentlen = 0;
+ users[userid].outpacket.fragment++;
+ users[userid].outfragresent = 0;
+
+ /* 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.fragment--; /* unneeded ++ above */
+ /* ^keep last seqno/frag, are always returned on pings */
+ /* users[userid].outfragresent = 0; already above */
+
+#ifdef OUTPACKETQ_LEN
+ /* Possibly get new packet from queue */
+ get_from_outpacketq(userid);
+#endif
+ }
+}
+
+static void
+handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len)
+{
+ struct in_addr tempip;
+ char in[512];
+ char logindata[16];
+ char out[64*1024];
+ char unpacked[64*1024];
+ char *tmp[2];
+ int userid;
+ int read;
+
+ userid = -1;
+
+ /* Everything here needs at least two chars in the name */
+ if (domain_len < 2)
+ return;
+
+ memcpy(in, q->name, MIN(domain_len, sizeof(in)));
+
+ if(in[0] == 'V' || in[0] == 'v') {
+ int version = 0;
+
+ 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 */
+ version = (((unpacked[0] & 0xff) << 24) |
+ ((unpacked[1] & 0xff) << 16) |
+ ((unpacked[2] & 0xff) << 8) |
+ ((unpacked[3] & 0xff)));
+ }
+
+ if (version == VERSION) {
+ userid = find_available_user();
+ if (userid >= 0) {
+ int i;
+ struct sockaddr_in *tempin;
+
+ users[userid].seed = rand();
+ /* Store remote IP number */
+ tempin = (struct sockaddr_in *) &(q->from);
+ memcpy(&(users[userid].host), &(tempin->sin_addr), sizeof(struct in_addr));
+
+ memcpy(&(users[userid].q), q, sizeof(struct query));
+ users[userid].encoder = get_base32_encoder();
+ users[userid].downenc = 'T';
+ send_version_response(dns_fd, VERSION_ACK, users[userid].seed, userid, q);
+ syslog(LOG_INFO, "accepted version for user #%d from %s",
+ userid, inet_ntoa(tempin->sin_addr));
+ users[userid].q.id = 0;
+ users[userid].q.id2 = 0;
+ users[userid].q_sendrealsoon.id = 0;
+ users[userid].q_sendrealsoon.id2 = 0;
+ users[userid].q_sendrealsoon_new = 0;
+ users[userid].outpacket.len = 0;
+ users[userid].outpacket.offset = 0;
+ users[userid].outpacket.sentlen = 0;
+ users[userid].outpacket.seqno = 0;
+ users[userid].outpacket.fragment = 0;
+ users[userid].outfragresent = 0;
+ users[userid].inpacket.len = 0;
+ users[userid].inpacket.offset = 0;
+ users[userid].inpacket.seqno = 0;
+ users[userid].inpacket.fragment = 0;
+ users[userid].fragsize = 100; /* very safe */
+ users[userid].conn = CONN_DNS_NULL;
+ users[userid].lazy = 0;
+#ifdef OUTPACKETQ_LEN
+ users[userid].outpacketq_nexttouse = 0;
+ users[userid].outpacketq_filled = 0;
+#endif
+#ifdef DNSCACHE_LEN
+ {
+ for (i = 0; i < DNSCACHE_LEN; i++) {
+ users[userid].dnscache_q[i].id = 0;
+ users[userid].dnscache_answerlen[i] = 0;
+ }
+ }
+ users[userid].dnscache_lastfilled = 0;
+#endif
+ for (i = 0; i < QMEMPING_LEN; i++)
+ users[userid].qmemping_type[i] = T_UNSET;
+ users[userid].qmemping_lastfilled = 0;
+ for (i = 0; i < QMEMDATA_LEN; i++)
+ users[userid].qmemdata_type[i] = T_UNSET;
+ users[userid].qmemdata_lastfilled = 0;
+ } else {
+ /* No space for another user */
+ send_version_response(dns_fd, VERSION_FULL, created_users, 0, q);
+ syslog(LOG_INFO, "dropped user from %s, server full",
+ inet_ntoa(((struct sockaddr_in *) &q->from)->sin_addr));
+ }
+ } else {
+ send_version_response(dns_fd, VERSION_NACK, VERSION, 0, q);
+ syslog(LOG_INFO, "dropped user from %s, sent bad version %08X",
+ inet_ntoa(((struct sockaddr_in *) &q->from)->sin_addr), version);
+ }
+ return;
+ } else if(in[0] == 'L' || in[0] == 'l') {
+ read = unpack_data(unpacked, sizeof(unpacked), &(in[1]), domain_len - 1, b32);
+ if (read < 17) {
+ write_dns(dns_fd, q, "BADLEN", 6, 'T');
+ return;
+ }
+
+ /* Login phase, handle auth */
+ userid = unpacked[0];
+
+ if (check_user_and_ip(userid, q) != 0) {
+ write_dns(dns_fd, q, "BADIP", 5, 'T');
+ syslog(LOG_WARNING, "dropped login request from user #%d from unexpected source %s",
+ userid, inet_ntoa(((struct sockaddr_in *) &q->from)->sin_addr));
+ 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/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-%d",
+ tmp[0], tmp[1], my_mtu, netmask);
+
+ write_dns(dns_fd, q, out, read, users[userid].downenc);
+ q->id = 0;
+ syslog(LOG_NOTICE, "accepted password from user #%d, given IP %s", userid, tmp[1]);
+
+ free(tmp[1]);
+ free(tmp[0]);
+ } else {
+ write_dns(dns_fd, q, "LNAK", 4, 'T');
+ syslog(LOG_WARNING, "rejected login request from user #%d from %s, bad password",
+ userid, inet_ntoa(((struct sockaddr_in *) &q->from)->sin_addr));
+ }
+ }
+ return;
+ } else if(in[0] == 'I' || in[0] == 'i') {
+ /* Request for IP number */
+ in_addr_t replyaddr;
+ unsigned addr;
+ char reply[5];
+
+ userid = b32_8to5(in[1]);
+ if (check_user_and_ip(userid, q) != 0) {
+ write_dns(dns_fd, q, "BADIP", 5, 'T');
+ return; /* illegal id */
+ }
+
+ if (ns_ip != INADDR_ANY) {
+ /* If set, use assigned external ip (-n option) */
+ replyaddr = ns_ip;
+ } else {
+ /* otherwise return destination ip from packet */
+ memcpy(&replyaddr, &q->destination.s_addr, sizeof(in_addr_t));
+ }
+
+ addr = htonl(replyaddr);
+ reply[0] = 'I';
+ reply[1] = (addr >> 24) & 0xFF;
+ reply[2] = (addr >> 16) & 0xFF;
+ reply[3] = (addr >> 8) & 0xFF;
+ reply[4] = (addr >> 0) & 0xFF;
+ write_dns(dns_fd, q, reply, sizeof(reply), 'T');
+ } 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 */
+ /* No userid here, reply with lowest-grade downenc */
+ write_dns(dns_fd, q, in, domain_len, 'T');
+ return;
+ } else if(in[0] == 'S' || in[0] == 's') {
+ int codec;
+ struct encoder *enc;
+ if (domain_len < 3) { /* len at least 3, example: "S15" */
+ write_dns(dns_fd, q, "BADLEN", 6, 'T');
+ return;
+ }
+
+ userid = b32_8to5(in[1]);
+
+ if (check_user_and_ip(userid, q) != 0) {
+ write_dns(dns_fd, q, "BADIP", 5, 'T');
+ 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), users[userid].downenc);
+ 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), users[userid].downenc);
+ break;
+ case 26: /* "2nd" 6 bits per byte = base64u, with underscore */
+ enc = get_base64u_encoder();
+ user_switch_codec(userid, enc);
+ write_dns(dns_fd, q, enc->name, strlen(enc->name), users[userid].downenc);
+ break;
+ case 7: /* 7 bits per byte = base128 */
+ enc = get_base128_encoder();
+ user_switch_codec(userid, enc);
+ write_dns(dns_fd, q, enc->name, strlen(enc->name), users[userid].downenc);
+ break;
+ default:
+ write_dns(dns_fd, q, "BADCODEC", 8, users[userid].downenc);
+ break;
+ }
+ return;
+ } else if(in[0] == 'O' || in[0] == 'o') {
+ if (domain_len < 3) { /* len at least 3, example: "O1T" */
+ write_dns(dns_fd, q, "BADLEN", 6, 'T');
+ return;
+ }
+
+ userid = b32_8to5(in[1]);
+
+ if (check_user_and_ip(userid, q) != 0) {
+ write_dns(dns_fd, q, "BADIP", 5, 'T');
+ return; /* illegal id */
+ }
+
+ switch (in[2]) {
+ case 'T':
+ case 't':
+ users[userid].downenc = 'T';
+ write_dns(dns_fd, q, "Base32", 6, users[userid].downenc);
+ break;
+ case 'S':
+ case 's':
+ users[userid].downenc = 'S';
+ write_dns(dns_fd, q, "Base64", 6, users[userid].downenc);
+ break;
+ case 'U':
+ case 'u':
+ users[userid].downenc = 'U';
+ write_dns(dns_fd, q, "Base64u", 7, users[userid].downenc);
+ break;
+ case 'V':
+ case 'v':
+ users[userid].downenc = 'V';
+ write_dns(dns_fd, q, "Base128", 7, users[userid].downenc);
+ break;
+ case 'R':
+ case 'r':
+ users[userid].downenc = 'R';
+ write_dns(dns_fd, q, "Raw", 3, users[userid].downenc);
+ break;
+ case 'L':
+ case 'l':
+ users[userid].lazy = 1;
+ write_dns(dns_fd, q, "Lazy", 4, users[userid].downenc);
+ break;
+ case 'I':
+ case 'i':
+ users[userid].lazy = 0;
+ write_dns(dns_fd, q, "Immediate", 9, users[userid].downenc);
+ break;
+ default:
+ write_dns(dns_fd, q, "BADCODEC", 8, users[userid].downenc);
+ break;
+ }
+ return;
+ } else if(in[0] == 'Y' || in[0] == 'y') {
+ int i;
+ char *datap;
+ int datalen;
+
+ if (domain_len < 6) { /* len at least 6, example: "YTxCMC" */
+ write_dns(dns_fd, q, "BADLEN", 6, 'T');
+ return;
+ }
+
+ i = b32_8to5(in[2]); /* check variant */
+
+ switch (i) {
+ case 1:
+ datap = DOWNCODECCHECK1;
+ datalen = DOWNCODECCHECK1_LEN;
+ break;
+ default:
+ write_dns(dns_fd, q, "BADLEN", 6, 'T');
+ return;
+ }
+
+ switch (in[1]) {
+ case 'T':
+ case 't':
+ if (q->type == T_TXT ||
+ q->type == T_SRV || q->type == T_MX ||
+ q->type == T_CNAME || q->type == T_A) {
+ write_dns(dns_fd, q, datap, datalen, 'T');
+ return;
+ }
+ break;
+ case 'S':
+ case 's':
+ if (q->type == T_TXT ||
+ q->type == T_SRV || q->type == T_MX ||
+ q->type == T_CNAME || q->type == T_A) {
+ write_dns(dns_fd, q, datap, datalen, 'S');
+ return;
+ }
+ break;
+ case 'U':
+ case 'u':
+ if (q->type == T_TXT ||
+ q->type == T_SRV || q->type == T_MX ||
+ q->type == T_CNAME || q->type == T_A) {
+ write_dns(dns_fd, q, datap, datalen, 'U');
+ return;
+ }
+ break;
+ case 'V':
+ case 'v':
+ if (q->type == T_TXT ||
+ q->type == T_SRV || q->type == T_MX ||
+ q->type == T_CNAME || q->type == T_A) {
+ write_dns(dns_fd, q, datap, datalen, 'V');
+ return;
+ }
+ break;
+ case 'R':
+ case 'r':
+ if (q->type == T_NULL || q->type == T_TXT) {
+ write_dns(dns_fd, q, datap, datalen, 'R');
+ return;
+ }
+ break;
+ }
+
+ /* if still here, then codec not available */
+ write_dns(dns_fd, q, "BADCODEC", 8, 'T');
+ return;
+
+ } else if(in[0] == 'R' || in[0] == 'r') {
+ int req_frag_size;
+
+ if (domain_len < 16) { /* we'd better have some chars for data... */
+ write_dns(dns_fd, q, "BADLEN", 6, 'T');
+ return;
+ }
+
+ /* 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, 'T');
+ 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, users[userid].downenc);
+ } else {
+ char buf[2048];
+ int i;
+ unsigned int v = ((unsigned int) rand()) & 0xff ;
+
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (req_frag_size >> 8) & 0xff;
+ buf[1] = req_frag_size & 0xff;
+ /* make checkable pseudo-random sequence */
+ buf[2] = 107;
+ for (i = 3; i < 2048; i++, v = (v + 107) & 0xff)
+ buf[i] = v;
+ write_dns(dns_fd, q, buf, req_frag_size, users[userid].downenc);
+ }
+ 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);
+
+ if (read < 3) {
+ write_dns(dns_fd, q, "BADLEN", 6, 'T');
+ return;
+ }
+
+ /* Downstream fragsize packet */
+ userid = unpacked[0];
+ if (check_user_and_ip(userid, q) != 0) {
+ write_dns(dns_fd, q, "BADIP", 5, 'T');
+ 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, users[userid].downenc);
+ } else {
+ users[userid].fragsize = max_frag_size;
+ write_dns(dns_fd, q, &unpacked[1], 2, users[userid].downenc);
+ }
+ return;
+ } else if(in[0] == 'P' || in[0] == 'p') {
+ int dn_seq;
+ int dn_frag;
+ int didsend = 0;
+
+ /* We can't handle id=0, that's "no packet" to us. So drop
+ request completely. Note that DNS servers rewrite the id.
+ We'll drop 1 in 64k times. If DNS server retransmits with
+ different id, then all okay.
+ Else client won't retransmit, and we'll just keep the
+ previous ping in cache, no problem either. */
+ if (q->id == 0)
+ return;
+
+ read = unpack_data(unpacked, sizeof(unpacked), &(in[1]), domain_len - 1, b32);
+ if (read < 4)
+ return;
+
+ /* Ping packet, store userid */
+ userid = unpacked[0];
+ if (check_user_and_ip(userid, q) != 0) {
+ write_dns(dns_fd, q, "BADIP", 5, 'T');
+ return; /* illegal id */
+ }
+
+#ifdef DNSCACHE_LEN
+ /* Check if cached */
+ if (answer_from_dnscache(dns_fd, userid, q))
+ return;
+#endif
+
+ /* Check if duplicate (and not in full dnscache any more) */
+ if (answer_from_qmem(dns_fd, q, users[userid].qmemping_cmc,
+ users[userid].qmemping_type, QMEMPING_LEN,
+ (void *) unpacked))
+ return;
+
+ /* Check if duplicate of waiting queries; impatient DNS relays
+ like to re-try early and often (with _different_ .id!) */
+ if (users[userid].q.id != 0 &&
+ q->type == users[userid].q.type &&
+ !strcmp(q->name, users[userid].q.name) &&
+ users[userid].lazy) {
+ /* We have this ping already, and it's waiting to be
+ answered. Always keep the last duplicate, since the
+ relay may have forgotten its first version already.
+ Our answer will go to both.
+ (If we already sent an answer, qmem/cache will
+ have triggered.) */
+ if (debug >= 2) {
+ fprintf(stderr, "PING pkt from user %d = dupe from impatient DNS server, remembering\n",
+ userid);
+ }
+ users[userid].q.id2 = q->id;
+ users[userid].q.fromlen2 = q->fromlen;
+ memcpy(&(users[userid].q.from2), &(q->from), q->fromlen);
+ return;
+ }
+
+ if (users[userid].q_sendrealsoon.id != 0 &&
+ q->type == users[userid].q_sendrealsoon.type &&
+ !strcmp(q->name, users[userid].q_sendrealsoon.name)) {
+ /* Outer select loop will send answer immediately,
+ to both queries. */
+ if (debug >= 2) {
+ fprintf(stderr, "PING pkt from user %d = dupe from impatient DNS server, remembering\n",
+ userid);
+ }
+ users[userid].q_sendrealsoon.id2 = q->id;
+ users[userid].q_sendrealsoon.fromlen2 = q->fromlen;
+ memcpy(&(users[userid].q_sendrealsoon.from2),
+ &(q->from), q->fromlen);
+ return;
+ }
+
+ dn_seq = unpacked[1] >> 4;
+ dn_frag = unpacked[1] & 15;
+
+ if (debug >= 1) {
+ fprintf(stderr, "PING pkt from user %d, ack for downstream %d/%d\n",
+ userid, dn_seq, dn_frag);
+ }
+
+ process_downstream_ack(userid, dn_seq, dn_frag);
+
+ if (debug >= 3) {
+ fprintf(stderr, "PINGret (if any) will ack upstream %d/%d\n",
+ users[userid].inpacket.seqno, users[userid].inpacket.fragment);
+ }
+
+ /* If there is a query that must be returned real soon, do it.
+ May contain new downstream data if the ping had a new ack.
+ Otherwise, may also be re-sending old data. */
+ if (users[userid].q_sendrealsoon.id != 0) {
+ send_chunk_or_dataless(dns_fd, userid, &users[userid].q_sendrealsoon);
+ }
+
+ /* We need to store a new query, so if there still is an
+ earlier query waiting, always send a reply to finish it.
+ May contain new downstream data if the ping had a new ack.
+ Otherwise, may also be re-sending old data.
+ (This is duplicate data if we had q_sendrealsoon above.) */
+ if (users[userid].q.id != 0) {
+ didsend = 1;
+ if (send_chunk_or_dataless(dns_fd, userid, &users[userid].q) == 1)
+ /* new packet from queue, send immediately */
+ didsend = 0;
+ }
+
+ /* Save new query and time info */
+ memcpy(&(users[userid].q), q, sizeof(struct query));
+ users[userid].last_pkt = time(NULL);
+
+ /* If anything waiting and we didn't already send above, send
+ it now. And always send immediately if we're not lazy
+ (then above won't have sent at all). */
+ if ((!didsend && users[userid].outpacket.len > 0) ||
+ !users[userid].lazy)
+ send_chunk_or_dataless(dns_fd, userid, &users[userid].q);
+
+ } else if((in[0] >= '0' && in[0] <= '9')
+ || (in[0] >= 'a' && in[0] <= 'f')
+ || (in[0] >= 'A' && in[0] <= 'F')) {
+ int up_seq, up_frag, dn_seq, dn_frag, lastfrag;
+ int upstream_ok = 1;
+ int didsend = 0;
+ int code = -1;
+
+ /* Need 5char header + >=1 char data */
+ if (domain_len < 6)
+ return;
+
+ /* We can't handle id=0, that's "no packet" to us. So drop
+ request completely. Note that DNS servers rewrite the id.
+ We'll drop 1 in 64k times. If DNS server retransmits with
+ different id, then all okay.
+ Else client doesn't get our ack, and will retransmit in
+ 1 second. */
+ if (q->id == 0)
+ return;
+
+ if ((in[0] >= '0' && in[0] <= '9'))
+ code = in[0] - '0';
+ if ((in[0] >= 'a' && in[0] <= 'f'))
+ code = in[0] - 'a' + 10;
+ if ((in[0] >= 'A' && in[0] <= 'F'))
+ code = in[0] - 'A' + 10;
+
+ userid = code;
+ /* Check user and sending ip number */
+ if (check_user_and_ip(userid, q) != 0) {
+ write_dns(dns_fd, q, "BADIP", 5, 'T');
+ return; /* illegal id */
+ }
+
+#ifdef DNSCACHE_LEN
+ /* Check if cached */
+ if (answer_from_dnscache(dns_fd, userid, q))
+ return;
+#endif
+
+ /* Check if duplicate (and not in full dnscache any more) */
+ if (answer_from_qmem_data(dns_fd, userid, q))
+ return;
+
+ /* Check if duplicate of waiting queries; impatient DNS relays
+ like to re-try early and often (with _different_ .id!) */
+ if (users[userid].q.id != 0 &&
+ q->type == users[userid].q.type &&
+ !strcmp(q->name, users[userid].q.name) &&
+ users[userid].lazy) {
+ /* We have this packet already, and it's waiting to be
+ answered. Always keep the last duplicate, since the
+ relay may have forgotten its first version already.
+ Our answer will go to both.
+ (If we already sent an answer, qmem/cache will
+ have triggered.) */
+ if (debug >= 2) {
+ fprintf(stderr, "IN pkt from user %d = dupe from impatient DNS server, remembering\n",
+ userid);
+ }
+ users[userid].q.id2 = q->id;
+ users[userid].q.fromlen2 = q->fromlen;
+ memcpy(&(users[userid].q.from2), &(q->from), q->fromlen);
+ return;
+ }
+
+ if (users[userid].q_sendrealsoon.id != 0 &&
+ q->type == users[userid].q_sendrealsoon.type &&
+ !strcmp(q->name, users[userid].q_sendrealsoon.name)) {
+ /* Outer select loop will send answer immediately,
+ to both queries. */
+ if (debug >= 2) {
+ fprintf(stderr, "IN pkt from user %d = dupe from impatient DNS server, remembering\n",
+ userid);
+ }
+ users[userid].q_sendrealsoon.id2 = q->id;
+ users[userid].q_sendrealsoon.fromlen2 = q->fromlen;
+ memcpy(&(users[userid].q_sendrealsoon.from2),
+ &(q->from), q->fromlen);
+ return;
+ }
+
+
+ /* Decode data header */
+ up_seq = (b32_8to5(in[1]) >> 2) & 7;
+ up_frag = ((b32_8to5(in[1]) & 3) << 2) | ((b32_8to5(in[2]) >> 3) & 3);
+ dn_seq = (b32_8to5(in[2]) & 7);
+ dn_frag = b32_8to5(in[3]) >> 1;
+ lastfrag = b32_8to5(in[3]) & 1;
+
+ process_downstream_ack(userid, dn_seq, dn_frag);
+
+ if (up_seq == users[userid].inpacket.seqno &&
+ up_frag <= users[userid].inpacket.fragment) {
+ /* Got repeated old packet _with data_, probably
+ because client didn't receive our ack. So re-send
+ our ack(+data) immediately to keep things flowing
+ fast.
+ If it's a _really_ old frag, it's a nameserver
+ that tries again, and sending our current (non-
+ matching) fragno won't be a problem. */
+ if (debug >= 1) {
+ fprintf(stderr, "IN pkt seq# %d, frag %d, dropped duplicate frag\n",
+ up_seq, up_frag);
+ }
+ upstream_ok = 0;
+ }
+ else if (up_seq != users[userid].inpacket.seqno &&
+ recent_seqno(users[userid].inpacket.seqno, up_seq)) {
+ /* Duplicate of recent upstream data packet; probably
+ need to answer this to keep DNS server happy */
+ if (debug >= 1) {
+ fprintf(stderr, "IN pkt seq# %d, frag %d, dropped duplicate recent seqno\n",
+ up_seq, up_frag);
+ }
+ upstream_ok = 0;
+ }
+ else if (up_seq != users[userid].inpacket.seqno) {
+ /* Really new packet has arrived, no recent duplicate */
+ /* Forget any old packet, even if incomplete */
+ users[userid].inpacket.seqno = up_seq;
+ users[userid].inpacket.fragment = up_frag;
+ users[userid].inpacket.len = 0;
+ users[userid].inpacket.offset = 0;
+ } else {
+ /* seq is same, frag is higher; don't care about
+ missing fragments, TCP checksum will fail */
+ users[userid].inpacket.fragment = up_frag;
+ }
+
+ if (debug >= 3) {
+ fprintf(stderr, "INpack with upstream %d/%d, we are going to ack upstream %d/%d\n",
+ up_seq, up_frag,
+ users[userid].inpacket.seqno, users[userid].inpacket.fragment);
+ }
+
+ if (upstream_ok) {
+ /* decode with this user's encoding */
+ read = unpack_data(unpacked, sizeof(unpacked), &(in[5]), domain_len - 5,
+ users[userid].encoder);
+
+ /* copy to packet buffer, update length */
+ read = MIN(read, sizeof(users[userid].inpacket.data) - users[userid].inpacket.offset);
+ memcpy(users[userid].inpacket.data + users[userid].inpacket.offset, unpacked, read);
+ users[userid].inpacket.len += read;
+ users[userid].inpacket.offset += read;
+
+ if (debug >= 1) {
+ fprintf(stderr, "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 (upstream_ok && lastfrag) { /* packet is complete */
+ handle_full_packet(tun_fd, dns_fd, userid);
+ }
+
+ /* If there is a query that must be returned real soon, do it.
+ Includes an ack of the just received upstream fragment,
+ may contain new data. */
+ if (users[userid].q_sendrealsoon.id != 0) {
+ didsend = 1;
+ if (send_chunk_or_dataless(dns_fd, userid, &users[userid].q_sendrealsoon) == 1)
+ /* new packet from queue, send immediately */
+ didsend = 0;
+ }
+
+ /* If we already have an earlier query waiting, we need to
+ get rid of it to store the new query.
+ - If we have new data waiting and not yet sent above,
+ send immediately.
+ - If this wasn't the last upstream fragment, then we expect
+ more, so ack immediately if we didn't already.
+ - If we are in non-lazy mode, there should be no query
+ waiting, but if there is, send immediately.
+ - In all other cases (mostly the last-fragment cases),
+ we can afford to wait just a tiny little while for the
+ TCP ack to arrive from our tun. Note that this works best
+ when there is only one client.
+ */
+ if (users[userid].q.id != 0) {
+ if ((users[userid].outpacket.len > 0 && !didsend) ||
+ (upstream_ok && !lastfrag && !didsend) ||
+ (!upstream_ok && !didsend) ||
+ !users[userid].lazy) {
+ didsend = 1;
+ if (send_chunk_or_dataless(dns_fd, userid, &users[userid].q) == 1)
+ /* new packet from queue, send immediately */
+ didsend = 0;
+ } else {
+ memcpy(&(users[userid].q_sendrealsoon),
+ &(users[userid].q),
+ sizeof(struct query));
+ users[userid].q_sendrealsoon_new = 1;
+ users[userid].q.id = 0; /* used */
+ didsend = 1;
+ }
+ }
+
+ /* Save new query and time info */
+ memcpy(&(users[userid].q), q, sizeof(struct query));
+ users[userid].last_pkt = time(NULL);
+
+ /* If we still need to ack this upstream frag, do it to keep
+ upstream flowing.
+ - If we have new data waiting and not yet sent above,
+ send immediately.
+ - If this wasn't the last upstream fragment, then we expect
+ more, so ack immediately if we didn't already or are
+ in non-lazy mode.
+ - If this was the last fragment, and we didn't ack already
+ or are in non-lazy mode, send the ack after just a tiny
+ little while so that the TCP ack may have arrived from
+ our tun device.
+ - In all other cases, don't send anything now.
+ */
+ if (users[userid].outpacket.len > 0 && !didsend)
+ send_chunk_or_dataless(dns_fd, userid, &users[userid].q);
+ else if (!didsend || !users[userid].lazy) {
+ if (upstream_ok && lastfrag) {
+ memcpy(&(users[userid].q_sendrealsoon),
+ &(users[userid].q),
+ sizeof(struct query));
+ users[userid].q_sendrealsoon_new = 1;
+ users[userid].q.id = 0; /* used */
+ } else {
+ send_chunk_or_dataless(dns_fd, userid, &users[userid].q);
+ }
+ }
+ }
+}
+
+static void
+handle_ns_request(int dns_fd, struct query *q)
+/* Mostly identical to handle_a_request() below */
+{
+ char buf[64*1024];
+ int len;
+
+ if (ns_ip != INADDR_ANY) {
+ /* If ns_ip set, overwrite destination addr with it.
+ * Destination addr will be sent as additional record (A, IN) */
+ memcpy(&q->destination.s_addr, &ns_ip, sizeof(in_addr_t));
+ }
+
+ len = dns_encode_ns_response(buf, sizeof(buf), q, topdomain);
+ if (len < 1) {
+ warnx("dns_encode_ns_response doesn't fit");
+ return;
+ }
+
+ if (debug >= 2) {
+ struct sockaddr_in *tempin;
+ tempin = (struct sockaddr_in *) &(q->from);
+ fprintf(stderr, "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
+handle_a_request(int dns_fd, struct query *q, int fakeip)
+/* Mostly identical to handle_ns_request() above */
+{
+ char buf[64*1024];
+ int len;
+
+ if (fakeip) {
+ in_addr_t ip = inet_addr("127.0.0.1");
+ memcpy(&q->destination.s_addr, &ip, sizeof(in_addr_t));
+
+ } else if (ns_ip != INADDR_ANY) {
+ /* If ns_ip set, overwrite destination addr with it.
+ * Destination addr will be sent as additional record (A, IN) */
+ memcpy(&q->destination.s_addr, &ns_ip, sizeof(in_addr_t));
+ }
+
+ len = dns_encode_a_response(buf, sizeof(buf), q);
+ if (len < 1) {
+ warnx("dns_encode_a_response doesn't fit");
+ return;
+ }
+
+ if (debug >= 2) {
+ struct sockaddr_in *tempin;
+ tempin = (struct sockaddr_in *) &(q->from);
+ fprintf(stderr, "TX: client %s, type %d, name %s, %d bytes A 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("a 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));
+ if (len < 1) {
+ warnx("dns_encode doesn't fit");
+ return;
+ }
+
+ /* 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) {
+ fprintf(stderr, "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;
+ unsigned 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) {
+ fprintf(stderr, "RX: Got response on query %u from DNS\n", (id & 0xFFFF));
+ }
+
+ /* Get sockaddr from id */
+ fw_query_get(id, &query);
+ if (!query && debug >= 2) {
+ fprintf(stderr, "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);
+ fprintf(stderr, "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, int bind_fd)
+{
+ struct query q;
+ int read;
+ int domain_len;
+ int inside_topdomain;
+
+ if ((read = read_dns(dns_fd, tun_fd, &q)) <= 0)
+ return 0;
+
+ if (debug >= 2) {
+ struct sockaddr_in *tempin;
+ tempin = (struct sockaddr_in *) &(q.from);
+ fprintf(stderr, "RX: client %s, type %d, name %s\n",
+ inet_ntoa(tempin->sin_addr), q.type, q.name);
+ }
+
+ domain_len = strlen(q.name) - strlen(topdomain);
+ if (domain_len >= 0 && !strcasecmp(q.name + domain_len, topdomain))
+ inside_topdomain = 1;
+ /* require dot before topdomain */
+ if (domain_len >= 1 && q.name[domain_len - 1] != '.')
+ inside_topdomain = 0;
+
+ if (inside_topdomain) {
+ /* This is a query we can handle */
+
+ /* Handle A-type query for ns.topdomain, possibly caused
+ by our proper response to any NS request */
+ if (domain_len == 3 && q.type == T_A &&
+ (q.name[0] == 'n' || q.name[0] == 'N') &&
+ (q.name[1] == 's' || q.name[1] == 'S') &&
+ q.name[2] == '.') {
+ handle_a_request(dns_fd, &q, 0);
+ return 0;
+ }
+
+ /* Handle A-type query for www.topdomain, for anyone that's
+ poking around */
+ if (domain_len == 4 && q.type == T_A &&
+ (q.name[0] == 'w' || q.name[0] == 'W') &&
+ (q.name[1] == 'w' || q.name[1] == 'W') &&
+ (q.name[2] == 'w' || q.name[2] == 'W') &&
+ q.name[3] == '.') {
+ handle_a_request(dns_fd, &q, 1);
+ return 0;
+ }
+
+ switch (q.type) {
+ case T_NULL:
+ case T_CNAME:
+ case T_A:
+ case T_MX:
+ case T_SRV:
+ case T_TXT:
+ /* encoding is "transparent" here */
+ 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, int bind_fd)
+{
+ struct timeval tv;
+ fd_set fds;
+ int i;
+ int userid;
+
+ while (running) {
+ int maxfd;
+ tv.tv_sec = 10; /* doesn't really matter */
+ tv.tv_usec = 0;
+
+ /* Adjust timeout if there is anything to send realsoon.
+ Clients won't be sending new data until we send our ack,
+ so don't keep them waiting long. This only triggers at
+ final upstream fragments, which is about once per eight
+ requests during heavy upstream traffic.
+ 20msec: ~8 packs every 1/50sec = ~400 DNSreq/sec,
+ or ~1200bytes every 1/50sec = ~0.5 Mbit/sec upstream */
+ for (userid = 0; userid < USERS; userid++) {
+ if (users[userid].active && !users[userid].disabled &&
+ users[userid].last_pkt + 60 > time(NULL)) {
+ users[userid].q_sendrealsoon_new = 0;
+ if (users[userid].q_sendrealsoon.id != 0) {
+ tv.tv_sec = 0;
+ tv.tv_usec = 20000;
+ }
+ }
+ }
+
+ 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);
+ }
+
+ /* Don't read from tun if no users can accept data anyway;
+ tun queue/TCP buffers are larger than our outpacket-queues */
+ if(!all_users_waiting_to_send()) {
+ FD_SET(tun_fd, &fds);
+ maxfd = MAX(tun_fd, maxfd);
+ }
+
+ i = select(maxfd + 1, &fds, NULL, NULL, &tv);
+
+ if(i < 0) {
+ if (running)
+ warn("select");
+ return 1;
+ }
+
+ if (i==0) {
+ /* timeout; whatever; doesn't matter anymore */
+ } else {
+ if (FD_ISSET(tun_fd, &fds)) {
+ tunnel_tun(tun_fd, dns_fd);
+ }
+ if (FD_ISSET(dns_fd, &fds)) {
+ tunnel_dns(tun_fd, dns_fd, bind_fd);
+ }
+ if (FD_ISSET(bind_fd, &fds)) {
+ tunnel_bind(bind_fd, dns_fd);
+ }
+ }
+
+ /* Send realsoon's if tun or dns didn't already */
+ for (userid = 0; userid < USERS; userid++)
+ if (users[userid].active && !users[userid].disabled &&
+ users[userid].last_pkt + 60 > time(NULL) &&
+ users[userid].q_sendrealsoon.id != 0 &&
+ users[userid].conn == CONN_DNS_NULL &&
+ !users[userid].q_sendrealsoon_new)
+ send_chunk_or_dataless(dns_fd, userid, &users[userid].q_sendrealsoon);
+ }
+
+ return 0;
+}
+
+static void
+handle_full_packet(int tun_fd, int dns_fd, int userid)
+{
+ unsigned long outlen;
+ char out[64*1024];
+ int touser;
+ 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) {
+ struct ip *hdr;
+
+ 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 */
+ /*XXX START adjust indent 1 tab forward*/
+ if (users[touser].conn == CONN_DNS_NULL) {
+ if (users[touser].outpacket.len == 0) {
+ start_new_outpacket(touser,
+ users[userid].inpacket.data,
+ users[userid].inpacket.len);
+
+ /* Start sending immediately if query is waiting */
+ if (users[touser].q_sendrealsoon.id != 0)
+ send_chunk_or_dataless(dns_fd, touser, &users[touser].q_sendrealsoon);
+ else if (users[touser].q.id != 0)
+ send_chunk_or_dataless(dns_fd, touser, &users[touser].q);
+#ifdef OUTPACKETQ_LEN
+ } else {
+ save_to_outpacketq(touser,
+ users[userid].inpacket.data,
+ users[userid].inpacket.len);
+#endif
+ }
+ } else{ /* CONN_RAW_UDP */
+ send_raw(dns_fd, users[userid].inpacket.data,
+ users[userid].inpacket.len, touser,
+ RAW_HDR_CMD_DATA, &users[touser].q);
+ }
+ /*XXX END adjust indent 1 tab forward*/
+ }
+ } else {
+ if (debug >= 1)
+ fprintf(stderr, "Discarded data, uncompress() result: %d\n", ret);
+ }
+
+ /* This packet is done */
+ users[userid].inpacket.len = 0;
+ users[userid].inpacket.offset = 0;
+}
+
+static void
+handle_raw_login(char *packet, int len, struct query *q, int fd, int userid)
+{
+ char myhash[16];
+
+ if (len < 16) return;
+
+ /* can't use check_user_and_ip() since IP address will be different,
+ so duplicate here except IP address */
+ if (userid < 0 || userid >= created_users) return;
+ if (!users[userid].active || users[userid].disabled) return;
+ if (users[userid].last_pkt + 60 < time(NULL)) return;
+
+ if (debug >= 1) {
+ fprintf(stderr, "IN login raw, len %d, from user %d\n",
+ len, userid);
+ }
+
+ /* User sends hash of seed + 1 */
+ login_calculate(myhash, 16, password, users[userid].seed + 1);
+ if (memcmp(packet, myhash, 16) == 0) {
+ struct sockaddr_in *tempin;
+
+ /* Update query and time info for user */
+ users[userid].last_pkt = time(NULL);
+ memcpy(&(users[userid].q), q, sizeof(struct query));
+
+ /* Store remote IP number */
+ tempin = (struct sockaddr_in *) &(q->from);
+ memcpy(&(users[userid].host), &(tempin->sin_addr), sizeof(struct in_addr));
+
+ /* Correct hash, reply with hash of seed - 1 */
+ user_set_conn_type(userid, CONN_RAW_UDP);
+ login_calculate(myhash, 16, password, users[userid].seed - 1);
+ send_raw(fd, myhash, 16, userid, RAW_HDR_CMD_LOGIN, q);
+ }
+}
+
+static void
+handle_raw_data(char *packet, int len, struct query *q, int dns_fd, int tun_fd, int userid)
+{
+ if (check_user_and_ip(userid, q) != 0) {
+ return;
+ }
+
+ /* Update query and time info for user */
+ users[userid].last_pkt = time(NULL);
+ memcpy(&(users[userid].q), q, sizeof(struct query));
+
+ /* copy to packet buffer, update length */
+ users[userid].inpacket.offset = 0;
+ memcpy(users[userid].inpacket.data, packet, len);
+ users[userid].inpacket.len = len;
+
+ if (debug >= 1) {
+ fprintf(stderr, "IN pkt raw, total %d, from user %d\n",
+ users[userid].inpacket.len, userid);
+ }
+
+ handle_full_packet(tun_fd, dns_fd, userid);
+}
+
+static void
+handle_raw_ping(struct query *q, int dns_fd, int userid)
+{
+ if (check_user_and_ip(userid, q) != 0) {
+ return;
+ }
+
+ /* Update query and time info for user */
+ users[userid].last_pkt = time(NULL);
+ memcpy(&(users[userid].q), q, sizeof(struct query));
+
+ if (debug >= 1) {
+ fprintf(stderr, "IN ping raw, from user %d\n", userid);
+ }
+
+ /* Send ping reply */
+ send_raw(dns_fd, NULL, 0, userid, RAW_HDR_CMD_PING, q);
+}
+
+static int
+raw_decode(char *packet, int len, struct query *q, int dns_fd, int tun_fd)
+{
+ int raw_user;
+
+ /* minimum length */
+ if (len < RAW_HDR_LEN) return 0;
+ /* should start with header */
+ if (memcmp(packet, raw_header, RAW_HDR_IDENT_LEN)) return 0;
+
+ raw_user = RAW_HDR_GET_USR(packet);
+ switch (RAW_HDR_GET_CMD(packet)) {
+ case RAW_HDR_CMD_LOGIN:
+ /* Login challenge */
+ handle_raw_login(&packet[RAW_HDR_LEN], len - RAW_HDR_LEN, q, dns_fd, raw_user);
+ break;
+ case RAW_HDR_CMD_DATA:
+ /* Data packet */
+ handle_raw_data(&packet[RAW_HDR_LEN], len - RAW_HDR_LEN, q, dns_fd, tun_fd, raw_user);
+ break;
+ case RAW_HDR_CMD_PING:
+ /* Keepalive packet */
+ handle_raw_ping(q, dns_fd, raw_user);
+ break;
+ default:
+ warnx("Unhandled raw command %02X from user %d", RAW_HDR_GET_CMD(packet), raw_user);
+ break;
+ }
+ return 1;
+}
+
+static int
+read_dns(int fd, int tun_fd, struct query *q) /* FIXME: tun_fd is because of raw_decode() below */
+{
+ struct sockaddr_in from;
+ socklen_t addrlen;
+ char packet[64*1024];
+ int r;
+#ifndef WINDOWS32
+ char address[96];
+ struct msghdr msg;
+ struct iovec iov;
+ struct cmsghdr *cmsg;
+
+ addrlen = sizeof(struct sockaddr);
+ 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);
+#else
+ addrlen = sizeof(struct sockaddr);
+ r = recvfrom(fd, packet, sizeof(packet), 0, (struct sockaddr*)&from, &addrlen);
+#endif /* !WINDOWS32 */
+
+ if (r > 0) {
+ memcpy((struct sockaddr*)&q->from, (struct sockaddr*)&from, addrlen);
+ q->fromlen = addrlen;
+
+ /* TODO do not handle raw packets here! */
+ if (raw_decode(packet, r, q, fd, tun_fd)) {
+ return 0;
+ }
+ if (dns_decode(NULL, 0, q, QR_QUERY, packet, r) < 0) {
+ return 0;
+ }
+
+#ifndef WINDOWS32
+ 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;
+ }
+ }
+#endif
+
+ return strlen(q->name);
+ } else if (r < 0) {
+ /* Error */
+ warn("read dns");
+ }
+
+ return 0;
+}
+
+static size_t
+write_dns_nameenc(char *buf, size_t buflen, char *data, int datalen, char downenc)
+/* Returns #bytes of data that were encoded */
+{
+ static int td1 = 0;
+ static int td2 = 0;
+ size_t space;
+ char *b;
+
+ /* Make a rotating topdomain to prevent filtering */
+ td1+=3;
+ td2+=7;
+ if (td1>=26) td1-=26;
+ if (td2>=25) td2-=25;
+
+ /* encode data,datalen to CNAME/MX answer
+ (adapted from build_hostname() in encoding.c)
+ */
+
+ space = MIN(0xFF, buflen) - 4 - 2;
+ /* -1 encoding type, -3 ".xy", -2 for safety */
+
+ memset(buf, 0, sizeof(buf));
+
+ if (downenc == 'S') {
+ buf[0] = 'i';
+ if (!b64->places_dots())
+ space -= (space / 57); /* space for dots */
+ b64->encode(buf+1, &space, data, datalen);
+ if (!b64->places_dots())
+ inline_dotify(buf, buflen);
+ } else if (downenc == 'U') {
+ buf[0] = 'j';
+ if (!b64u->places_dots())
+ space -= (space / 57); /* space for dots */
+ b64u->encode(buf+1, &space, data, datalen);
+ if (!b64u->places_dots())
+ inline_dotify(buf, buflen);
+ } else if (downenc == 'V') {
+ buf[0] = 'k';
+ if (!b128->places_dots())
+ space -= (space / 57); /* space for dots */
+ b128->encode(buf+1, &space, data, datalen);
+ if (!b128->places_dots())
+ inline_dotify(buf, buflen);
+ } else {
+ buf[0] = 'h';
+ if (!b32->places_dots())
+ space -= (space / 57); /* space for dots */
+ b32->encode(buf+1, &space, data, datalen);
+ if (!b32->places_dots())
+ inline_dotify(buf, buflen);
+ }
+
+ /* Add dot (if it wasn't there already) and topdomain */
+ b = buf;
+ b += strlen(buf) - 1;
+ if (*b != '.')
+ *++b = '.';
+ b++;
+
+ *b = 'a' + td1;
+ b++;
+ *b = 'a' + td2;
+ b++;
+ *b = '\0';
+
+ return space;
+}
+
+static void
+write_dns(int fd, struct query *q, char *data, int datalen, char downenc)
+{
+ char buf[64*1024];
+ int len = 0;
+
+ if (q->type == T_CNAME || q->type == T_A) {
+ char cnamebuf[1024]; /* max 255 */
+
+ write_dns_nameenc(cnamebuf, sizeof(cnamebuf),
+ data, datalen, downenc);
+
+ len = dns_encode(buf, sizeof(buf), q, QR_ANSWER, cnamebuf,
+ sizeof(cnamebuf));
+ } else if (q->type == T_MX || q->type == T_SRV) {
+ char mxbuf[64*1024];
+ char *b = mxbuf;
+ int offset = 0;
+ int res;
+
+ while (1) {
+ res = write_dns_nameenc(b, sizeof(mxbuf) - (b - mxbuf),
+ data + offset,
+ datalen - offset, downenc);
+ if (res < 1) {
+ /* nothing encoded */
+ b++; /* for final \0 */
+ break;
+ }
+
+ b = b + strlen(b) + 1;
+
+ offset += res;
+ if (offset >= datalen)
+ break;
+ }
+
+ /* Add final \0 */
+ *b = '\0';
+
+ len = dns_encode(buf, sizeof(buf), q, QR_ANSWER, mxbuf,
+ sizeof(mxbuf));
+ } else if (q->type == T_TXT) {
+ /* TXT with base32 */
+ char txtbuf[64*1024];
+ size_t space = sizeof(txtbuf) - 1;;
+
+ memset(txtbuf, 0, sizeof(txtbuf));
+
+ if (downenc == 'S') {
+ txtbuf[0] = 's'; /* plain base64(Sixty-four) */
+ len = b64->encode(txtbuf+1, &space, data, datalen);
+ }
+ else if (downenc == 'U') {
+ txtbuf[0] = 'u'; /* Base64 with Underscore */
+ len = b64u->encode(txtbuf+1, &space, data, datalen);
+ }
+ else if (downenc == 'V') {
+ txtbuf[0] = 'v'; /* Base128 */
+ len = b128->encode(txtbuf+1, &space, data, datalen);
+ }
+ else if (downenc == 'R') {
+ txtbuf[0] = 'r'; /* Raw binary data */
+ len = MIN(datalen, sizeof(txtbuf) - 1);
+ memcpy(txtbuf + 1, data, len);
+ } else {
+ txtbuf[0] = 't'; /* plain base32(Thirty-two) */
+ len = b32->encode(txtbuf+1, &space, data, datalen);
+ }
+ len = dns_encode(buf, sizeof(buf), q, QR_ANSWER, txtbuf, len+1);
+ } else {
+ /* Normal NULL-record encode */
+ len = dns_encode(buf, sizeof(buf), q, QR_ANSWER, data, datalen);
+ }
+
+ if (len < 1) {
+ warnx("dns_encode doesn't fit");
+ return;
+ }
+
+ if (debug >= 2) {
+ struct sockaddr_in *tempin;
+ tempin = (struct sockaddr_in *) &(q->from);
+ fprintf(stderr, "TX: client %s, type %d, name %s, %d bytes data\n",
+ inet_ntoa(tempin->sin_addr), q->type, q->name, datalen);
+ }
+
+ sendto(fd, buf, len, 0, (struct sockaddr*)&q->from, q->fromlen);
+}
+
+static void
+usage() {
+ extern char *__progname;
+
+ fprintf(stderr, "Usage: %s [-v] [-h] [-c] [-s] [-f] [-D] [-u user] "
+ "[-t chrootdir] [-d device] [-m mtu] [-z context] "
+ "[-l ip address to listen on] [-p port] [-n external ip] "
+ "[-b dnsport] [-P password] [-F pidfile] "
+ "tunnel_ip[/netmask] topdomain\n", __progname);
+ exit(2);
+}
+
+static void
+help() {
+ extern char *__progname;
+
+ fprintf(stderr, "iodine IP over DNS tunneling server\n");
+ fprintf(stderr, "Usage: %s [-v] [-h] [-c] [-s] [-f] [-D] [-u user] "
+ "[-t chrootdir] [-d device] [-m mtu] [-z context] "
+ "[-l ip address to listen on] [-p port] [-n external ip] [-b dnsport] [-P password] "
+ "[-F pidfile] tunnel_ip[/netmask] topdomain\n", __progname);
+ fprintf(stderr, " -v to print version info and exit\n");
+ fprintf(stderr, " -h to print this help and exit\n");
+ fprintf(stderr, " -c to disable check of client IP/port on each request\n");
+ fprintf(stderr, " -s to skip creating and configuring the tun device, "
+ "which then has to be created manually\n");
+ fprintf(stderr, " -f to keep running in foreground\n");
+ fprintf(stderr, " -D to increase debug level\n");
+ fprintf(stderr, " (using -DD in UTF-8 terminal: \"LC_ALL=C luit iodined -DD ...\")\n");
+ fprintf(stderr, " -u name to drop privileges and run as user 'name'\n");
+ fprintf(stderr, " -t dir to chroot to directory dir\n");
+ fprintf(stderr, " -d device to set tunnel device name\n");
+ fprintf(stderr, " -m mtu to set tunnel device mtu\n");
+ fprintf(stderr, " -z context to apply SELinux context after initialization\n");
+ fprintf(stderr, " -l ip address to listen on for incoming dns traffic "
+ "(default 0.0.0.0)\n");
+ fprintf(stderr, " -p port to listen on for incoming dns traffic (default 53)\n");
+ fprintf(stderr, " -n ip to respond with to NS queries\n");
+ fprintf(stderr, " -b port to forward normal DNS queries to (on localhost)\n");
+ fprintf(stderr, " -P password used for authentication (max 32 chars will be used)\n");
+ fprintf(stderr, " -F pidfile to write pid to a file\n");
+ fprintf(stderr, "tunnel_ip is the IP number of the local tunnel interface.\n");
+ fprintf(stderr, " /netmask sets the size of the tunnel network.\n");
+ fprintf(stderr, "topdomain is the FQDN that is delegated to this server.\n");
+ exit(0);
+}
+
+static void
+version() {
+ fprintf(stderr, "iodine IP over DNS tunneling server\n");
+ fprintf(stderr, "version: 0.6.0-rc1 from 2010-02-13\n");
+ exit(0);
+}
+
+int
+main(int argc, char **argv)
+{
+ extern char *__progname;
+ in_addr_t listen_ip;
+#ifndef WINDOWS32
+ struct passwd *pw;
+#endif
+ int foreground;
+ char *username;
+ char *newroot;
+ char *context;
+ char *device;
+ char *pidfile;
+ 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;
+ int retval;
+
+#ifndef WINDOWS32
+ pw = NULL;
+#endif
+ username = NULL;
+ newroot = NULL;
+ context = NULL;
+ device = NULL;
+ foreground = 0;
+ bind_enable = 0;
+ bind_fd = 0;
+ mtu = 1130; /* Very many relays give fragsize 1150 or slightly
+ higher for NULL; tun/zlib adds ~17 bytes. */
+ listen_ip = INADDR_ANY;
+ port = 53;
+ ns_ip = INADDR_ANY;
+ check_ip = 1;
+ skipipconfig = 0;
+ debug = 0;
+ netmask = 27;
+ pidfile = NULL;
+
+ b32 = get_base32_encoder();
+ b64 = get_base64_encoder();
+ b64u = get_base64u_encoder();
+ b128 = get_base128_encoder();
+
+ retval = 0;
+
+#ifdef WINDOWS32
+ WSAStartup(req_version, &wsa_data);
+#endif
+
+#if !defined(BSD) && !defined(__GLIBC__)
+ __progname = strrchr(argv[0], '/');
+ if (__progname == NULL)
+ __progname = argv[0];
+ else
+ __progname++;
+#endif
+
+ memset(password, 0, sizeof(password));
+ srand(time(NULL));
+ fw_query_init();
+
+ while ((choice = getopt(argc, argv, "vcsfhDu:t:d:m:l:p:n:b:P:z:F:")) != -1) {
+ switch(choice) {
+ case 'v':
+ version();
+ break;
+ case 'c':
+ check_ip = 0;
+ break;
+ case 's':
+ skipipconfig = 1;
+ break;
+ case 'f':
+ foreground = 1;
+ break;
+ case 'h':
+ help();
+ break;
+ case 'D':
+ debug++;
+ break;
+ case 'u':
+ username = optarg;
+ break;
+ case 't':
+ newroot = optarg;
+ break;
+ case 'd':
+ device = optarg;
+ break;
+ case 'm':
+ mtu = atoi(optarg);
+ break;
+ case 'l':
+ listen_ip = inet_addr(optarg);
+ break;
+ 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 'F':
+ pidfile = optarg;
+ break;
+ case 'P':
+ strncpy(password, optarg, sizeof(password));
+ password[sizeof(password)-1] = 0;
+
+ /* XXX: find better way of cleaning up ps(1) */
+ memset(optarg, 0, strlen(optarg));
+ break;
+ case 'z':
+ context = optarg;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ check_superuser(usage);
+
+ 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.");
+ usage();
+ }
+
+ topdomain = strdup(argv[1]);
+ if (strlen(topdomain) <= 128) {
+ if(check_topdomain(topdomain)) {
+ warnx("Topdomain contains invalid characters.");
+ usage();
+ }
+ } else {
+ warnx("Use a topdomain max 128 chars long.");
+ usage();
+ }
+
+ if (username != NULL) {
+#ifndef WINDOWS32
+ if ((pw = getpwnam(username)) == NULL) {
+ warnx("User %s does not exist!", username);
+ usage();
+ }
+#endif
+ }
+
+ if (mtu <= 0) {
+ warnx("Bad MTU given.");
+ usage();
+ }
+
+ if(port < 1 || port > 65535) {
+ warnx("Bad port number given.");
+ usage();
+ }
+
+ if(bind_enable) {
+ if (bind_port < 1 || bind_port > 65535) {
+ warnx("Bad DNS server port number given.");
+ usage();
+ /* NOTREACHED */
+ }
+ /* Avoid forwarding loops */
+ if (bind_port == port && (listen_ip == INADDR_ANY || listen_ip == htonl(0x7f000001L))) {
+ warnx("Forward port is same as listen port (%d), will create a loop!", bind_port);
+ fprintf(stderr, "Use -l to set listen ip to avoid this.\n");
+ usage();
+ /* NOTREACHED */
+ }
+ fprintf(stderr, "Requests for domains outside of %s will be forwarded to port %d\n",
+ topdomain, bind_port);
+ }
+
+ if (port != 53) {
+ fprintf(stderr, "ALERT! Other dns servers expect you to run on port 53.\n");
+ fprintf(stderr, "You must manually forward port 53 to port %d for things to work.\n", port);
+ }
+
+ if (debug) {
+ fprintf(stderr, "Debug level %d enabled, will stay in foreground.\n", debug);
+ fprintf(stderr, "Add more -D switches to set higher debug level.\n");
+ foreground = 1;
+ }
+
+ if (listen_ip == INADDR_NONE) {
+ warnx("Bad IP address to listen on.");
+ usage();
+ }
+
+ if (ns_ip == INADDR_NONE) {
+ warnx("Bad IP address to return as nameserver.");
+ usage();
+ }
+ if (netmask > 30 || netmask < 8) {
+ warnx("Bad netmask (%d bits). Use 8-30 bits.", netmask);
+ usage();
+ }
+
+ if (strlen(password) == 0) {
+ if (NULL != getenv(PASSWORD_ENV_VAR))
+ snprintf(password, sizeof(password), "%s", getenv(PASSWORD_ENV_VAR));
+ else
+ read_password(password, sizeof(password));
+ }
+
+ created_users = init_users(my_ip, netmask);
+
+ if ((tun_fd = open_tun(device)) == -1) {
+ retval = 1;
+ goto cleanup0;
+ }
+ if (!skipipconfig) {
+ if (tun_setip(argv[0], users_get_first_ip(), netmask) != 0 || tun_setmtu(mtu) != 0) {
+ retval = 1;
+ goto cleanup1;
+ }
+ }
+ if ((dnsd_fd = open_dns(port, listen_ip)) == -1) {
+ retval = 1;
+ goto cleanup2;
+ }
+ if (bind_enable) {
+ if ((bind_fd = open_dns(0, INADDR_ANY)) == -1) {
+ retval = 1;
+ goto cleanup3;
+ }
+ }
+
+ my_mtu = mtu;
+
+ if (created_users < USERS) {
+ fprintf(stderr, "Limiting to %d simultaneous users because of netmask /%d\n",
+ created_users, netmask);
+ }
+ fprintf(stderr, "Listening to dns for domain %s\n", topdomain);
+
+ if (foreground == 0)
+ do_detach();
+
+ if (pidfile != NULL)
+ do_pidfile(pidfile);
+
+#ifdef FREEBSD
+ tzsetwall();
+#endif
+#ifndef WINDOWS32
+ openlog( __progname, LOG_NDELAY, LOG_DAEMON );
+#endif
+
+ if (newroot != NULL)
+ do_chroot(newroot);
+
+ signal(SIGINT, sigint);
+ if (username != NULL) {
+#ifndef WINDOWS32
+ gid_t gids[1];
+ gids[0] = pw->pw_gid;
+ 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();
+ }
+#endif
+ }
+
+ if (context != NULL)
+ do_setcon(context);
+
+ syslog(LOG_INFO, "started, listening on port %d", port);
+
+ tunnel(tun_fd, dnsd_fd, bind_fd);
+
+ syslog(LOG_INFO, "stopping");
+cleanup3:
+ close_dns(bind_fd);
+cleanup2:
+ close_dns(dnsd_fd);
+cleanup1:
+ close_tun(tun_fd);
+cleanup0:
+
+ return retval;
+}
diff --git a/jni/iodine/src/login.c b/jni/iodine/src/login.c
new file mode 100644
index 0000000..c8827c3
--- /dev/null
+++ b/jni/iodine/src/login.c
@@ -0,0 +1,57 @@
+/*
+ * 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 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 <sys/types.h>
+
+#ifdef WINDOWS32
+#include "windows.h"
+#else
+#include <arpa/inet.h>
+#endif
+
+#include "md5.h"
+
+/*
+ * Needs a 16byte array for output, and 32 bytes password
+ */
+void
+login_calculate(char *buf, int buflen, const char *pass, int seed)
+{
+ unsigned char temp[32];
+ md5_state_t ctx;
+ int *ix;
+ int i;
+ int k;
+
+ if (buflen < 16)
+ return;
+
+ memcpy(temp, pass, 32);
+ ix = (int*) temp;
+
+ for (i = 0; i < 8; i++) {
+ k = ntohl(*ix);
+ k ^= seed;
+ *ix++ = htonl(k);
+ }
+
+ md5_init(&ctx);
+ md5_append(&ctx, temp, 32);
+ md5_finish(&ctx, (unsigned char *) buf);
+
+}
+
diff --git a/jni/iodine/src/login.h b/jni/iodine/src/login.h
new file mode 100644
index 0000000..840c920
--- /dev/null
+++ b/jni/iodine/src/login.h
@@ -0,0 +1,23 @@
+/*
+ * 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 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 __LOGIN_H__
+#define __LOGIN_H__
+
+void login_calculate(char *, int, const char *, int);
+
+#endif
+
diff --git a/jni/iodine/src/md5.c b/jni/iodine/src/md5.c
new file mode 100644
index 0000000..89d3d5e
--- /dev/null
+++ b/jni/iodine/src/md5.c
@@ -0,0 +1,384 @@
+/*
+ Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.c is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+ either statically or dynamically; added missing #include <string.h>
+ in library.
+ 2002-03-11 lpd Corrected argument list for main(), and added int return
+ type, in test program and T value program.
+ 2002-02-21 lpd Added missing #include <stdio.h> in test program.
+ 2000-07-03 lpd Patched to eliminate warnings about "constant is
+ unsigned in ANSI C, signed in traditional"; made test program
+ self-checking.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+ 1999-05-03 lpd Original version.
+ */
+
+#include "md5.h"
+#include <string.h>
+
+#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
+#ifdef ARCH_IS_BIG_ENDIAN
+# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
+#else
+# define BYTE_ORDER 0
+#endif
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3 0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6 0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9 0x698098d8
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13 0x6b901122
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16 0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19 0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22 0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25 0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28 0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31 0x676f02d9
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35 0x6d9d6122
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38 0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41 0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44 0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47 0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50 0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53 0x655b59c3
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57 0x6fa87e4f
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60 0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63 0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+ md5_word_t
+ a = pms->abcd[0], b = pms->abcd[1],
+ c = pms->abcd[2], d = pms->abcd[3];
+ md5_word_t t;
+#if BYTE_ORDER > 0
+ /* Define storage only for big-endian CPUs. */
+ md5_word_t X[16];
+#else
+ /* Define storage for little-endian or both types of CPUs. */
+ md5_word_t xbuf[16];
+ const md5_word_t *X;
+#endif
+
+ {
+#if BYTE_ORDER == 0
+ /*
+ * Determine dynamically whether this is a big-endian or
+ * little-endian machine, since we can use a more efficient
+ * algorithm on the latter.
+ */
+ static const int w = 1;
+
+ if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
+#endif
+#if BYTE_ORDER <= 0 /* little-endian */
+ {
+ /*
+ * On little-endian machines, we can process properly aligned
+ * data without copying it.
+ */
+ if (!((data - (const md5_byte_t *)0) & 3)) {
+ /* data are properly aligned */
+ X = (const md5_word_t *)data;
+ } else {
+ /* not aligned */
+ memcpy(xbuf, data, 64);
+ X = xbuf;
+ }
+ }
+#endif
+#if BYTE_ORDER == 0
+ else /* dynamic big-endian */
+#endif
+#if BYTE_ORDER >= 0 /* big-endian */
+ {
+ /*
+ * On big-endian machines, we must arrange the bytes in the
+ * right order.
+ */
+ const md5_byte_t *xp = data;
+ int i;
+
+# if BYTE_ORDER == 0
+ X = xbuf; /* (dynamic only) */
+# else
+# define xbuf X /* (static only) */
+# endif
+ for (i = 0; i < 16; ++i, xp += 4)
+ xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+ }
+#endif
+ }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+ /* Round 1. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + F(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 7, T1);
+ SET(d, a, b, c, 1, 12, T2);
+ SET(c, d, a, b, 2, 17, T3);
+ SET(b, c, d, a, 3, 22, T4);
+ SET(a, b, c, d, 4, 7, T5);
+ SET(d, a, b, c, 5, 12, T6);
+ SET(c, d, a, b, 6, 17, T7);
+ SET(b, c, d, a, 7, 22, T8);
+ SET(a, b, c, d, 8, 7, T9);
+ SET(d, a, b, c, 9, 12, T10);
+ SET(c, d, a, b, 10, 17, T11);
+ SET(b, c, d, a, 11, 22, T12);
+ SET(a, b, c, d, 12, 7, T13);
+ SET(d, a, b, c, 13, 12, T14);
+ SET(c, d, a, b, 14, 17, T15);
+ SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+ /* Round 2. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + G(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 1, 5, T17);
+ SET(d, a, b, c, 6, 9, T18);
+ SET(c, d, a, b, 11, 14, T19);
+ SET(b, c, d, a, 0, 20, T20);
+ SET(a, b, c, d, 5, 5, T21);
+ SET(d, a, b, c, 10, 9, T22);
+ SET(c, d, a, b, 15, 14, T23);
+ SET(b, c, d, a, 4, 20, T24);
+ SET(a, b, c, d, 9, 5, T25);
+ SET(d, a, b, c, 14, 9, T26);
+ SET(c, d, a, b, 3, 14, T27);
+ SET(b, c, d, a, 8, 20, T28);
+ SET(a, b, c, d, 13, 5, T29);
+ SET(d, a, b, c, 2, 9, T30);
+ SET(c, d, a, b, 7, 14, T31);
+ SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+ /* Round 3. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + H(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 5, 4, T33);
+ SET(d, a, b, c, 8, 11, T34);
+ SET(c, d, a, b, 11, 16, T35);
+ SET(b, c, d, a, 14, 23, T36);
+ SET(a, b, c, d, 1, 4, T37);
+ SET(d, a, b, c, 4, 11, T38);
+ SET(c, d, a, b, 7, 16, T39);
+ SET(b, c, d, a, 10, 23, T40);
+ SET(a, b, c, d, 13, 4, T41);
+ SET(d, a, b, c, 0, 11, T42);
+ SET(c, d, a, b, 3, 16, T43);
+ SET(b, c, d, a, 6, 23, T44);
+ SET(a, b, c, d, 9, 4, T45);
+ SET(d, a, b, c, 12, 11, T46);
+ SET(c, d, a, b, 15, 16, T47);
+ SET(b, c, d, a, 2, 23, T48);
+#undef SET
+
+ /* Round 4. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + I(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 6, T49);
+ SET(d, a, b, c, 7, 10, T50);
+ SET(c, d, a, b, 14, 15, T51);
+ SET(b, c, d, a, 5, 21, T52);
+ SET(a, b, c, d, 12, 6, T53);
+ SET(d, a, b, c, 3, 10, T54);
+ SET(c, d, a, b, 10, 15, T55);
+ SET(b, c, d, a, 1, 21, T56);
+ SET(a, b, c, d, 8, 6, T57);
+ SET(d, a, b, c, 15, 10, T58);
+ SET(c, d, a, b, 6, 15, T59);
+ SET(b, c, d, a, 13, 21, T60);
+ SET(a, b, c, d, 4, 6, T61);
+ SET(d, a, b, c, 11, 10, T62);
+ SET(c, d, a, b, 2, 15, T63);
+ SET(b, c, d, a, 9, 21, T64);
+#undef SET
+
+ /* Then perform the following additions. (That is increment each
+ of the four registers by the value it had before this block
+ was started.) */
+ pms->abcd[0] += a;
+ pms->abcd[1] += b;
+ pms->abcd[2] += c;
+ pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+ pms->count[0] = pms->count[1] = 0;
+ pms->abcd[0] = 0x67452301;
+ pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+ pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+ pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+ const md5_byte_t *p = data;
+ int left = nbytes;
+ int offset = (pms->count[0] >> 3) & 63;
+ md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+ if (nbytes <= 0)
+ return;
+
+ /* Update the message length. */
+ pms->count[1] += nbytes >> 29;
+ pms->count[0] += nbits;
+ if (pms->count[0] < nbits)
+ pms->count[1]++;
+
+ /* Process an initial partial block. */
+ if (offset) {
+ int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+ memcpy(pms->buf + offset, p, copy);
+ if (offset + copy < 64)
+ return;
+ p += copy;
+ left -= copy;
+ md5_process(pms, pms->buf);
+ }
+
+ /* Process full blocks. */
+ for (; left >= 64; p += 64, left -= 64)
+ md5_process(pms, p);
+
+ /* Process a final partial block. */
+ if (left)
+ memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+ static const md5_byte_t pad[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ md5_byte_t data[8];
+ int i;
+
+ /* Save the length before padding. */
+ for (i = 0; i < 8; ++i)
+ data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+ /* Pad to 56 bytes mod 64. */
+ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+ /* Append the length. */
+ md5_append(pms, data, 8);
+ for (i = 0; i < 16; ++i)
+ digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
+
+
+
diff --git a/jni/iodine/src/md5.h b/jni/iodine/src/md5.h
new file mode 100644
index 0000000..698c995
--- /dev/null
+++ b/jni/iodine/src/md5.h
@@ -0,0 +1,91 @@
+/*
+ Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.h is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Removed support for non-ANSI compilers; removed
+ references to Ghostscript; clarified derivation from RFC 1321;
+ now handles byte order either statically or dynamically.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+ added conditionalization for C++ compilation from Martin
+ Purschke <purschke@bnl.gov>.
+ 1999-05-03 lpd Original version.
+ */
+
+#ifndef md5_INCLUDED
+# define md5_INCLUDED
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+ md5_word_t count[2]; /* message length in bits, lsw first */
+ md5_word_t abcd[4]; /* digest buffer */
+ md5_byte_t buf[64]; /* accumulate block */
+} md5_state_t;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* Initialize the algorithm. */
+void md5_init(md5_state_t *pms);
+
+/* Append a string to the message. */
+void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+
+/* Finish the message and return the digest. */
+void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
+
+#endif /* md5_INCLUDED */
diff --git a/jni/iodine/src/osflags b/jni/iodine/src/osflags
new file mode 100755
index 0000000..787ffaa
--- /dev/null
+++ b/jni/iodine/src/osflags
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+case $2 in
+link)
+
+ case $1 in
+ SunOS | solaris)
+ echo '-lsocket -lnsl';
+ ;;
+ BeOS)
+ echo '-lsocket -lbind -lbsd';
+ ;;
+ Haiku)
+ echo '-lnetwork';
+ ;;
+ windows32)
+ echo '-lws2_32 -liphlpapi';
+ ;;
+ Linux)
+ [ -e /usr/include/selinux/selinux.h ] && echo '-lselinux';
+ ;;
+ esac
+ ;;
+cflags)
+ case $1 in
+ BeOS)
+ echo '-Dsocklen_t=int';
+ ;;
+ Linux)
+ [ -e /usr/include/selinux/selinux.h ] && echo '-DHAVE_SETCON';
+ ;;
+ esac
+;;
+*)
+;;
+esac
diff --git a/jni/iodine/src/read.c b/jni/iodine/src/read.c
new file mode 100644
index 0000000..ff40382
--- /dev/null
+++ b/jni/iodine/src/read.c
@@ -0,0 +1,275 @@
+/*
+ * 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 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 <stdint.h>
+#include <stdlib.h>
+
+static int
+readname_loop(char *packet, int packetlen, char **src, char *dst, size_t length, size_t loop)
+{
+ char *dummy;
+ char *s;
+ char *d;
+ int len;
+ int offset;
+ char c;
+
+ if (loop <= 0)
+ return 0;
+
+ len = 0;
+ s = *src;
+ d = dst;
+ while(*s && len < length - 2) {
+ c = *s++;
+
+ /* is this a compressed label? */
+ if((c & 0xc0) == 0xc0) {
+ offset = (((s[-1] & 0x3f) << 8) | (s[0] & 0xff));
+ if (offset > packetlen) {
+ if (len == 0) {
+ /* Bad jump first in packet */
+ return 0;
+ } else {
+ /* Bad jump after some data */
+ break;
+ }
+ }
+ dummy = packet + offset;
+ len += readname_loop(packet, packetlen, &dummy, d, length - len, loop - 1);
+ goto end;
+ }
+
+ while(c && len < length - 1) {
+ *d++ = *s++;
+ len++;
+
+ c--;
+ }
+
+ if (len >= length - 1) {
+ break; /* We used up all space */
+ }
+
+ if (*s != 0) {
+ *d++ = '.';
+ len++;
+ }
+ }
+ dst[len++] = '\0';
+
+end:
+ (*src) = s+1;
+ return len;
+}
+
+int
+readname(char *packet, int packetlen, char **src, char *dst, size_t length)
+{
+ return readname_loop(packet, packetlen, src, dst, length, 10);
+}
+
+int
+readshort(char *packet, char **src, short *dst)
+{
+ unsigned char *p;
+
+ p = (unsigned char *) *src;
+ *dst = (p[0] << 8) | p[1];
+
+ (*src) += sizeof(short);
+ return sizeof(short);
+}
+
+int
+readlong(char *packet, char **src, uint32_t *dst)
+{
+ /* A long as described in dns protocol is always 32 bits */
+ unsigned char *p;
+
+ p = (unsigned char *) *src;
+
+ *dst = ((uint32_t)p[0] << 24)
+ | ((uint32_t)p[1] << 16)
+ | ((uint32_t)p[2] << 8)
+ | ((uint32_t)p[3]);
+
+ (*src) += sizeof(uint32_t);
+ return sizeof(uint32_t);
+}
+
+int
+readdata(char *packet, char **src, char *dst, size_t len)
+{
+ if (len < 0)
+ return 0;
+
+ memcpy(dst, *src, len);
+
+ (*src) += len;
+
+ return len;
+}
+
+int
+readtxtbin(char *packet, char **src, size_t srcremain, char *dst, size_t dstremain)
+{
+ unsigned char *uc;
+ int tocopy;
+ int dstused = 0;
+
+ while (srcremain > 0)
+ {
+ uc = (unsigned char*) (*src);
+ tocopy = *uc;
+ (*src)++;
+ srcremain--;
+
+ if (tocopy > srcremain)
+ return 0; /* illegal, better have nothing */
+ if (tocopy > dstremain)
+ return 0; /* doesn't fit, better have nothing */
+
+ memcpy(dst, *src, tocopy);
+ dst += tocopy;
+ (*src) += tocopy;
+ srcremain -= tocopy;
+ dstremain -= tocopy;
+ dstused += tocopy;
+ }
+ return dstused;
+}
+
+int
+putname(char **buf, size_t buflen, const char *host)
+{
+ char *word;
+ int left;
+ char *h;
+ char *p;
+
+ h = strdup(host);
+ left = buflen;
+ p = *buf;
+
+ word = strtok(h, ".");
+ while(word) {
+ if (strlen(word) > 63 || strlen(word) > left) {
+ free(h);
+ return -1;
+ }
+
+ left -= (strlen(word) + 1);
+ *p++ = (char)strlen(word);
+ memcpy(p, word, strlen(word));
+ p += strlen(word);
+
+ word = strtok(NULL, ".");
+ }
+
+ *p++ = 0;
+
+ free(h);
+
+ *buf = p;
+ return buflen - left;
+}
+
+int
+putbyte(char **dst, unsigned char value)
+{
+ **dst = value;
+ (*dst)++;
+
+ return sizeof(char);
+}
+
+int
+putshort(char **dst, unsigned short value)
+{
+ unsigned char *p;
+
+ p = (unsigned char *) *dst;
+
+ *p++ = (value >> 8);
+ *p++ = value;
+
+ (*dst) = (char *) p;
+ return sizeof(short);
+}
+
+int
+putlong(char **dst, uint32_t value)
+{
+ /* A long as described in dns protocol is always 32 bits */
+ unsigned char *p;
+
+ p = (unsigned char *) *dst;
+
+ *p++ = (value >> 24);
+ *p++ = (value >> 16);
+ *p++ = (value >> 8);
+ *p++ = (value);
+
+ (*dst) = (char *) p;
+ return sizeof(uint32_t);
+}
+
+int
+putdata(char **dst, char *data, size_t len)
+{
+ if (len < 0)
+ return 0;
+
+ memcpy(*dst, data, len);
+
+ (*dst) += len;
+ return len;
+}
+
+int
+puttxtbin(char **buf, size_t bufremain, char *from, size_t fromremain)
+{
+ unsigned char uc;
+ unsigned char *ucp = &uc;
+ char *cp = (char *) ucp;
+ int tocopy;
+ int bufused = 0;
+
+ while (fromremain > 0)
+ {
+ tocopy = fromremain;
+ if (tocopy > 252)
+ tocopy = 252; /* allow off-by-1s in caches etc */
+ if (tocopy + 1 > bufremain)
+ return -1; /* doesn't fit, better have nothing */
+
+ uc = tocopy;
+ **buf = *cp;
+ (*buf)++;
+ bufremain--;
+ bufused++;
+
+ memcpy(*buf, from, tocopy);
+ (*buf) += tocopy;
+ from += tocopy;
+ bufremain -= tocopy;
+ fromremain -= tocopy;
+ bufused += tocopy;
+ }
+ return bufused;
+}
diff --git a/jni/iodine/src/read.h b/jni/iodine/src/read.h
new file mode 100644
index 0000000..b33f3bb
--- /dev/null
+++ b/jni/iodine/src/read.h
@@ -0,0 +1,33 @@
+/*
+ * 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 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 _READ_H_
+#define _READ_H_
+
+int readname(char *, int, char **, char *, size_t);
+int readshort(char *, char **, short *);
+int readlong(char *, char **, uint32_t *);
+int readdata(char *, char **, char *, size_t);
+int readtxtbin(char *, char **, size_t, char *, size_t);
+
+int putname(char **, size_t, const char *);
+int putbyte(char **, unsigned char);
+int putshort(char **, unsigned short);
+int putlong(char **, uint32_t);
+int putdata(char **, char *, size_t);
+int puttxtbin(char **, size_t, char *, size_t);
+
+#endif
diff --git a/jni/iodine/src/tun.c b/jni/iodine/src/tun.c
new file mode 100644
index 0000000..eb52959
--- /dev/null
+++ b/jni/iodine/src/tun.c
@@ -0,0 +1,555 @@
+/*
+ * 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 and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#ifdef WINDOWS32
+#include <winsock2.h>
+#include <winioctl.h>
+#include "windows.h"
+
+HANDLE dev_handle;
+struct tun_data data;
+
+static void get_name(char *ifname, int namelen, char *dev_name);
+
+#define TAP_CONTROL_CODE(request,method) CTL_CODE(FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
+#define TAP_IOCTL_CONFIG_TUN TAP_CONTROL_CODE(10, METHOD_BUFFERED)
+#define TAP_IOCTL_SET_MEDIA_STATUS TAP_CONTROL_CODE(6, METHOD_BUFFERED)
+
+#define TAP_ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
+#define NETWORK_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
+#define TAP_DEVICE_SPACE "\\\\.\\Global\\"
+#define TAP_VERSION_ID_0801 "tap0801"
+#define TAP_VERSION_ID_0901 "tap0901"
+#define KEY_COMPONENT_ID "ComponentId"
+#define NET_CFG_INST_ID "NetCfgInstanceId"
+#else
+#include <err.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#define TUN_MAX_TRY 50
+#endif
+
+#include "tun.h"
+#include "common.h"
+
+char if_name[250];
+
+#ifndef WINDOWS32
+#ifdef LINUX
+
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <linux/if_tun.h>
+
+int
+open_tun(const char *tun_device)
+{
+ int i;
+ int tun_fd;
+ struct ifreq ifreq;
+ char *tunnel = "/dev/net/tun";
+
+ if ((tun_fd = open(tunnel, O_RDWR)) < 0) {
+ warn("open_tun: %s: %s", tunnel, strerror(errno));
+ return -1;
+ }
+
+ memset(&ifreq, 0, sizeof(ifreq));
+
+ ifreq.ifr_flags = IFF_TUN;
+
+ if (tun_device != NULL) {
+ strncpy(ifreq.ifr_name, tun_device, IFNAMSIZ);
+ ifreq.ifr_name[IFNAMSIZ-1] = '\0';
+ strncpy(if_name, tun_device, sizeof(if_name));
+ if_name[sizeof(if_name)-1] = '\0';
+
+ if (ioctl(tun_fd, TUNSETIFF, (void *) &ifreq) != -1) {
+ fprintf(stderr, "Opened %s\n", ifreq.ifr_name);
+ return tun_fd;
+ }
+
+ if (errno != EBUSY) {
+ warn("open_tun: ioctl[TUNSETIFF]: %s", strerror(errno));
+ return -1;
+ }
+ } else {
+ for (i = 0; i < TUN_MAX_TRY; i++) {
+ snprintf(ifreq.ifr_name, IFNAMSIZ, "dns%d", i);
+
+ if (ioctl(tun_fd, TUNSETIFF, (void *) &ifreq) != -1) {
+ fprintf(stderr, "Opened %s\n", ifreq.ifr_name);
+ snprintf(if_name, sizeof(if_name), "dns%d", i);
+ return tun_fd;
+ }
+
+ if (errno != EBUSY) {
+ warn("open_tun: ioctl[TUNSETIFF]: %s", strerror(errno));
+ return -1;
+ }
+ }
+
+ warn("open_tun: Couldn't set interface name");
+ }
+ warn("error when opening tun");
+ return -1;
+}
+
+#else /* BSD */
+
+int
+open_tun(const char *tun_device)
+{
+ int i;
+ int tun_fd;
+ char tun_name[50];
+
+ if (tun_device != NULL) {
+ snprintf(tun_name, sizeof(tun_name), "/dev/%s", tun_device);
+ strncpy(if_name, tun_device, sizeof(if_name));
+ if_name[sizeof(if_name)-1] = '\0';
+
+ if ((tun_fd = open(tun_name, O_RDWR)) < 0) {
+ warn("open_tun: %s: %s", tun_name, strerror(errno));
+ return -1;
+ }
+
+ fprintf(stderr, "Opened %s\n", tun_name);
+ return tun_fd;
+ } else {
+ for (i = 0; i < TUN_MAX_TRY; i++) {
+ snprintf(tun_name, sizeof(tun_name), "/dev/tun%d", i);
+
+ if ((tun_fd = open(tun_name, O_RDWR)) >= 0) {
+ fprintf(stderr, "Opened %s\n", tun_name);
+ snprintf(if_name, sizeof(if_name), "tun%d", i);
+ return tun_fd;
+ }
+
+ if (errno == ENOENT)
+ break;
+ }
+
+ warn("open_tun: Failed to open tunneling device");
+ }
+
+ return -1;
+}
+
+#endif /* !LINUX */
+#else /* WINDOWS32 */
+static void
+get_device(char *device, int device_len, const char *wanted_dev)
+{
+ LONG status;
+ HKEY adapter_key;
+ int index;
+
+ index = 0;
+ status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TAP_ADAPTER_KEY, 0, KEY_READ, &adapter_key);
+
+ if (status != ERROR_SUCCESS) {
+ warnx("Error opening registry key " TAP_ADAPTER_KEY );
+ return;
+ }
+
+ while (TRUE) {
+ char name[256];
+ char unit[256];
+ char component[256];
+
+ char cid_string[256] = KEY_COMPONENT_ID;
+ HKEY device_key;
+ DWORD datatype;
+ DWORD len;
+
+ /* Iterate through all adapter of this kind */
+ len = sizeof(name);
+ status = RegEnumKeyEx(adapter_key, index, name, &len, NULL, NULL, NULL, NULL);
+ if (status == ERROR_NO_MORE_ITEMS) {
+ break;
+ } else if (status != ERROR_SUCCESS) {
+ warnx("Error enumerating subkeys of registry key " TAP_ADAPTER_KEY );
+ break;
+ }
+
+ snprintf(unit, sizeof(unit), TAP_ADAPTER_KEY "\\%s", name);
+ status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, unit, 0, KEY_READ, &device_key);
+ if (status != ERROR_SUCCESS) {
+ warnx("Error opening registry key %s", unit);
+ goto next;
+ }
+
+ /* Check component id */
+ len = sizeof(component);
+ status = RegQueryValueEx(device_key, cid_string, NULL, &datatype, (LPBYTE)component, &len);
+ if (status != ERROR_SUCCESS || datatype != REG_SZ) {
+ goto next;
+ }
+ if (strncmp(TAP_VERSION_ID_0801, component, strlen(TAP_VERSION_ID_0801)) == 0 ||
+ strncmp(TAP_VERSION_ID_0901, component, strlen(TAP_VERSION_ID_0901)) == 0) {
+ /* We found a TAP32 device, get its NetCfgInstanceId */
+ char iid_string[256] = NET_CFG_INST_ID;
+
+ status = RegQueryValueEx(device_key, iid_string, NULL, &datatype, (LPBYTE) device, (DWORD *) &device_len);
+ if (status != ERROR_SUCCESS || datatype != REG_SZ) {
+ warnx("Error reading registry key %s\\%s on TAP device", unit, iid_string);
+ } else {
+ /* Done getting GUID of TAP device,
+ * now check if the name is the requested one */
+ if (wanted_dev) {
+ char name[250];
+ get_name(name, sizeof(name), device);
+ if (strncmp(name, wanted_dev, strlen(wanted_dev))) {
+ /* Skip if name mismatch */
+ goto next;
+ }
+ }
+ /* Get the if name */
+ get_name(if_name, sizeof(if_name), device);
+ RegCloseKey(device_key);
+ return;
+ }
+ }
+next:
+ RegCloseKey(device_key);
+ index++;
+ }
+ RegCloseKey(adapter_key);
+}
+
+static void
+get_name(char *ifname, int namelen, char *dev_name)
+{
+ char path[256];
+ char name_str[256] = "Name";
+ LONG status;
+ HKEY conn_key;
+ DWORD len;
+ DWORD datatype;
+
+ memset(ifname, 0, namelen);
+
+ snprintf(path, sizeof(path), NETWORK_KEY "\\%s\\Connection", dev_name);
+ status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, path, 0, KEY_READ, &conn_key);
+ if (status != ERROR_SUCCESS) {
+ fprintf(stderr, "Could not look up name of interface %s: error opening key\n", dev_name);
+ RegCloseKey(conn_key);
+ return;
+ }
+ len = namelen;
+ status = RegQueryValueEx(conn_key, name_str, NULL, &datatype, (LPBYTE)ifname, &len);
+ if (status != ERROR_SUCCESS || datatype != REG_SZ) {
+ fprintf(stderr, "Could not look up name of interface %s: error reading value\n", dev_name);
+ RegCloseKey(conn_key);
+ return;
+ }
+ RegCloseKey(conn_key);
+}
+
+DWORD WINAPI tun_reader(LPVOID arg)
+{
+ struct tun_data *tun = arg;
+ char buf[64*1024];
+ int len;
+ int res;
+ OVERLAPPED olpd;
+ int sock;
+
+ sock = open_dns(0, INADDR_ANY);
+
+ olpd.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ while(TRUE) {
+ olpd.Offset = 0;
+ olpd.OffsetHigh = 0;
+ res = ReadFile(tun->tun, buf, sizeof(buf), (LPDWORD) &len, &olpd);
+ if (!res) {
+ WaitForSingleObject(olpd.hEvent, INFINITE);
+ res = GetOverlappedResult(dev_handle, &olpd, (LPDWORD) &len, FALSE);
+ res = sendto(sock, buf, len, 0, (struct sockaddr*) &(tun->addr),
+ sizeof(struct sockaddr_in));
+ }
+ }
+
+ return 0;
+}
+
+int
+open_tun(const char *tun_device)
+{
+ char adapter[256];
+ char tapfile[512];
+ int tunfd;
+ in_addr_t local;
+
+ memset(adapter, 0, sizeof(adapter));
+ memset(if_name, 0, sizeof(if_name));
+ get_device(adapter, sizeof(adapter), tun_device);
+
+ if (strlen(adapter) == 0 || strlen(if_name) == 0) {
+ if (tun_device) {
+ warnx("No TAP adapters found. Try without -d.");
+ } else {
+ warnx("No TAP adapters found. Version 0801 and 0901 are supported.");
+ }
+ return -1;
+ }
+
+ fprintf(stderr, "Opening device %s\n", if_name);
+ snprintf(tapfile, sizeof(tapfile), "%s%s.tap", TAP_DEVICE_SPACE, adapter);
+ dev_handle = CreateFile(tapfile, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, NULL);
+ if (dev_handle == INVALID_HANDLE_VALUE) {
+ warnx("Could not open device!");
+ return -1;
+ }
+
+ /* Use a UDP connection to forward packets from tun,
+ * so we can still use select() in main code.
+ * A thread does blocking reads on tun device and
+ * sends data as udp to this socket */
+
+ local = htonl(0x7f000001); /* 127.0.0.1 */
+ tunfd = open_dns(55353, local);
+
+ data.tun = dev_handle;
+ memset(&(data.addr), 0, sizeof(data.addr));
+ data.addr.sin_family = AF_INET;
+ data.addr.sin_port = htons(55353);
+ data.addr.sin_addr.s_addr = local;
+ CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)tun_reader, &data, 0, NULL);
+
+ return tunfd;
+}
+#endif
+
+void
+close_tun(int tun_fd)
+{
+ if (tun_fd >= 0)
+ close(tun_fd);
+}
+
+int
+write_tun(int tun_fd, char *data, size_t len)
+{
+#if defined (FREEBSD) || defined (DARWIN) || defined(NETBSD) || defined(WINDOWS32) || defined(__ANDROID__)
+ data += 4;
+ len -= 4;
+#else /* !FREEBSD/DARWIN */
+#ifdef LINUX
+ data[0] = 0x00;
+ data[1] = 0x00;
+ data[2] = 0x08;
+ data[3] = 0x00;
+#else /* OPENBSD */
+ data[0] = 0x00;
+ data[1] = 0x00;
+ data[2] = 0x00;
+ data[3] = 0x02;
+#endif /* !LINUX */
+#endif /* FREEBSD */
+
+#ifndef WINDOWS32
+ if (write(tun_fd, data, len) != len) {
+ warn("write_tun");
+ return 1;
+ }
+#else /* WINDOWS32 */
+ {
+ DWORD written;
+ DWORD res;
+ OVERLAPPED olpd;
+
+ olpd.Offset = 0;
+ olpd.OffsetHigh = 0;
+ olpd.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ res = WriteFile(dev_handle, data, len, &written, &olpd);
+ if (!res && GetLastError() == ERROR_IO_PENDING) {
+ WaitForSingleObject(olpd.hEvent, INFINITE);
+ res = GetOverlappedResult(dev_handle, &olpd, &written, FALSE);
+ if (written != len) {
+ return -1;
+ }
+ }
+ }
+#endif
+ return 0;
+}
+
+ssize_t
+read_tun(int tun_fd, char *buf, size_t len)
+{
+#if defined (FREEBSD) || defined (DARWIN) || defined(NETBSD) || defined(WINDOWS32) || defined(__ANDROID__)
+ /* FreeBSD/Darwin/NetBSD/Android-VPN has no header */
+ int bytes;
+ memset(buf, 0, 4);
+#ifdef WINDOWS32
+ /* Windows needs recv() since it is local UDP socket */
+ bytes = recv(tun_fd, buf + 4, len - 4, 0);
+#else
+ /* The other need read() because fd is not a socket */
+ bytes = read(tun_fd, buf + 4, len - 4);
+#endif /*WINDOWS32*/
+ if (bytes < 0) {
+ return bytes;
+ } else {
+ return bytes + 4;
+ }
+#else /* !FREEBSD */
+ return read(tun_fd, buf, len);
+#endif /* !FREEBSD */
+}
+
+int
+tun_setip(const char *ip, const char *remoteip, int netbits)
+{
+ char cmdline[512];
+ int netmask;
+ struct in_addr net;
+ int i;
+#ifndef LINUX
+ int r;
+#endif
+#ifdef WINDOWS32
+ DWORD status;
+ DWORD ipdata[3];
+ struct in_addr addr;
+ DWORD len;
+#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) {
+ fprintf(stderr, "Invalid IP: %s!\n", ip);
+ return 1;
+ }
+#if defined(__ANDROID__)
+ if (tun_config_android.ip) {
+ free(tun_config_android.ip);
+ }
+ if (tun_config_android.remoteip) {
+ free(tun_config_android.remoteip);
+ }
+ tun_config_android.ip = strdup(ip);
+ tun_config_android.remoteip = strdup(remoteip);
+ tun_config_android.netbits = netbits;
+ return 0;
+#elif !defined(WINDOWS32)
+ snprintf(cmdline, sizeof(cmdline),
+ "/sbin/ifconfig %s %s %s netmask %s",
+ if_name,
+ ip,
+#ifdef FREEBSD
+ remoteip, /* FreeBSD wants other IP as second IP */
+#else
+ ip,
+#endif
+ inet_ntoa(net));
+
+ fprintf(stderr, "Setting IP of %s to %s\n", if_name, ip);
+#ifndef LINUX
+ r = system(cmdline);
+ if(r != 0) {
+ return r;
+ } else {
+ snprintf(cmdline, sizeof(cmdline),
+ "/sbin/route add %s/%d %s",
+ ip, netbits, ip);
+ }
+ fprintf(stderr, "Adding route %s/%d to %s\n", ip, netbits, ip);
+#endif
+ return system(cmdline);
+#else /* WINDOWS32 */
+
+ /* Set device as connected */
+ fprintf(stderr, "Enabling interface '%s'\n", if_name);
+ status = 1;
+ r = DeviceIoControl(dev_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status,
+ sizeof(status), &status, sizeof(status), &len, NULL);
+ if (!r) {
+ fprintf(stderr, "Failed to enable interface\n");
+ return -1;
+ }
+
+ if (inet_aton(ip, &addr)) {
+ ipdata[0] = (DWORD) addr.s_addr; /* local ip addr */
+ ipdata[1] = net.s_addr & ipdata[0]; /* network addr */
+ ipdata[2] = (DWORD) net.s_addr; /* netmask */
+ } else {
+ return -1;
+ }
+
+ /* Tell ip/networkaddr/netmask to device for arp use */
+ r = DeviceIoControl(dev_handle, TAP_IOCTL_CONFIG_TUN, &ipdata,
+ sizeof(ipdata), &ipdata, sizeof(ipdata), &len, NULL);
+ if (!r) {
+ fprintf(stderr, "Failed to set interface in TUN mode\n");
+ return -1;
+ }
+
+ /* use netsh to set ip address */
+ fprintf(stderr, "Setting IP of interface '%s' to %s (can take a few seconds)...\n", if_name, ip);
+ snprintf(cmdline, sizeof(cmdline), "netsh interface ip set address \"%s\" static %s %s",
+ if_name, ip, inet_ntoa(net));
+ return system(cmdline);
+#endif
+}
+
+int
+tun_setmtu(const unsigned mtu)
+{
+#ifdef __ANDROID__
+ tun_config_android.mtu = mtu;
+ return 0;
+#elif !defined(WINDOWS32)
+ char cmdline[512];
+
+ if (mtu > 200 && mtu <= 1500) {
+ snprintf(cmdline, sizeof(cmdline),
+ "/sbin/ifconfig %s mtu %u",
+ if_name,
+ mtu);
+
+ fprintf(stderr, "Setting MTU of %s to %u\n", if_name, mtu);
+ return system(cmdline);
+ } else {
+ warn("MTU out of range: %u\n", mtu);
+ }
+
+ return 1;
+#else /* WINDOWS32 */
+
+ return 0;
+#endif
+}
+
diff --git a/jni/iodine/src/tun.h b/jni/iodine/src/tun.h
new file mode 100644
index 0000000..397e7a9
--- /dev/null
+++ b/jni/iodine/src/tun.h
@@ -0,0 +1,37 @@
+/*
+ * 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 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 _TUN_H_
+#define _TUN_H_
+
+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 *, const char *, int);
+int tun_setmtu(const unsigned);
+
+#ifdef __ANDROID__
+struct _tun_config_android {
+ char *ip;
+ char *remoteip;
+ int netbits;
+ unsigned mtu;
+ int request_disconnect;
+} tun_config_android;
+#endif /* __ANDROID__ */
+
+#endif /* _TUN_H_ */
diff --git a/jni/iodine/src/user.c b/jni/iodine/src/user.c
new file mode 100644
index 0000000..dfe9c36
--- /dev/null
+++ b/jni/iodine/src/user.c
@@ -0,0 +1,204 @@
+/*
+ * 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 and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <time.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#ifdef WINDOWS32
+#include <winsock2.h>
+#else
+#include <netdb.h>
+#endif
+
+#include "common.h"
+#include "encoding.h"
+#include "user.h"
+
+struct user users[USERS];
+
+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 + 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].active = 0;
+ /* Rest is reset on login ('V' packet) */
+ }
+
+ return created_users;
+}
+
+const char*
+users_get_first_ip()
+{
+ struct in_addr ip;
+ ip.s_addr = users[0].tun_ip;
+ return inet_ntoa(ip);
+}
+
+int
+users_waiting_on_reply()
+{
+ int ret;
+ int i;
+
+ ret = 0;
+ for (i = 0; i < USERS; i++) {
+ if (users[i].active && !users[i].disabled &&
+ users[i].last_pkt + 60 > time(NULL) &&
+ users[i].q.id != 0 && users[i].conn == CONN_DNS_NULL) {
+ ret++;
+ }
+ }
+
+ return ret;
+}
+
+int
+find_user_by_ip(uint32_t ip)
+{
+ int ret;
+ int i;
+
+ ret = -1;
+ for (i = 0; i < USERS; i++) {
+ if (users[i].active && !users[i].disabled &&
+ users[i].last_pkt + 60 > time(NULL) &&
+ ip == users[i].tun_ip) {
+ ret = i;
+ break;
+ }
+ }
+ return ret;
+}
+
+int
+all_users_waiting_to_send()
+/* If this returns true, then reading from tun device is blocked.
+ So only return true when all clients have at least one packet in
+ the outpacket-queue, so that sending back-to-back is possible
+ without going through another select loop.
+*/
+{
+ time_t now;
+ int ret;
+ int i;
+
+ ret = 1;
+ now = time(NULL);
+ for (i = 0; i < USERS; i++) {
+ if (users[i].active && !users[i].disabled &&
+ users[i].last_pkt + 60 > now &&
+ ((users[i].conn == CONN_RAW_UDP) ||
+ ((users[i].conn == CONN_DNS_NULL)
+#ifdef OUTPACKETQ_LEN
+ && users[i].outpacketq_filled < 1
+#else
+ && users[i].outpacket.len == 0
+#endif
+ ))) {
+
+ ret = 0;
+ break;
+ }
+ }
+ return ret;
+}
+
+int
+find_available_user()
+{
+ int ret = -1;
+ 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)) && !users[i].disabled) {
+ users[i].active = 1;
+ users[i].last_pkt = time(NULL);
+ users[i].fragsize = 4096;
+ users[i].conn = CONN_DNS_NULL;
+ ret = i;
+ break;
+ }
+ }
+ return ret;
+}
+
+void
+user_switch_codec(int userid, struct encoder *enc)
+{
+ if (userid < 0 || userid >= USERS)
+ return;
+
+ users[userid].encoder = enc;
+}
+
+void
+user_set_conn_type(int userid, enum connection c)
+{
+ if (userid < 0 || userid >= USERS)
+ return;
+
+ if (c < 0 || c >= CONN_MAX)
+ return;
+
+ users[userid].conn = c;
+}
+
diff --git a/jni/iodine/src/user.h b/jni/iodine/src/user.h
new file mode 100644
index 0000000..51a6092
--- /dev/null
+++ b/jni/iodine/src/user.h
@@ -0,0 +1,87 @@
+/*
+ * 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 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 __USER_H__
+#define __USER_H__
+
+#define USERS 16
+
+#define OUTPACKETQ_LEN 4 /* Note: 16 users * 1 packet = 1MB */
+/* Undefine to have no queue for packets coming in from tun device, which may
+ lead to massive dropping in multi-user situations with high traffic. */
+
+#define DNSCACHE_LEN 4
+/* Undefine to disable. Should be less than 18; also see comments in iodined.c */
+
+
+#define QMEMPING_LEN 30
+/* Max advisable: 64k/2 = 32000. Total mem usage: QMEMPING_LEN * USERS * 6 bytes */
+
+#define QMEMDATA_LEN 15
+/* Max advisable: 36/2 = 18. Total mem usage: QMEMDATA_LEN * USERS * 6 bytes */
+
+struct user {
+ char id;
+ int active;
+ int disabled;
+ time_t last_pkt;
+ int seed;
+ in_addr_t tun_ip;
+ struct in_addr host;
+ struct query q;
+ struct query q_sendrealsoon;
+ int q_sendrealsoon_new;
+ struct packet inpacket;
+ struct packet outpacket;
+ int outfragresent;
+ struct encoder *encoder;
+ char downenc;
+ int out_acked_seqno;
+ int out_acked_fragment;
+ int fragsize;
+ enum connection conn;
+ int lazy;
+ unsigned char qmemping_cmc[QMEMPING_LEN * 4];
+ unsigned short qmemping_type[QMEMPING_LEN];
+ int qmemping_lastfilled;
+ unsigned char qmemdata_cmc[QMEMDATA_LEN * 4];
+ unsigned short qmemdata_type[QMEMDATA_LEN];
+ int qmemdata_lastfilled;
+#ifdef OUTPACKETQ_LEN
+ struct packet outpacketq[OUTPACKETQ_LEN];
+ int outpacketq_nexttouse;
+ int outpacketq_filled;
+#endif
+#ifdef DNSCACHE_LEN
+ struct query dnscache_q[DNSCACHE_LEN];
+ char dnscache_answer[DNSCACHE_LEN][4096];
+ int dnscache_answerlen[DNSCACHE_LEN];
+ int dnscache_lastfilled;
+#endif
+};
+
+extern struct user users[USERS];
+
+int init_users(in_addr_t, int);
+const char* users_get_first_ip();
+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);
+void user_set_conn_type(int userid, enum connection c);
+
+#endif
diff --git a/jni/iodine/src/util.c b/jni/iodine/src/util.c
new file mode 100644
index 0000000..bc5fc8d
--- /dev/null
+++ b/jni/iodine/src/util.c
@@ -0,0 +1,69 @@
+/*
+ * 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 and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include "common.h"
+
+char *
+get_resolvconf_addr()
+{
+ static char addr[16];
+ char *rv;
+#ifndef WINDOWS32
+ char buf[80];
+ FILE *fp;
+
+ rv = NULL;
+
+ if ((fp = fopen("/etc/resolv.conf", "r")) == NULL)
+ err(1, "/etc/resolv.conf");
+
+ while (feof(fp) == 0) {
+ fgets(buf, sizeof(buf), fp);
+
+ if (sscanf(buf, "nameserver %15s", addr) == 1) {
+ rv = addr;
+ break;
+ }
+ }
+
+ fclose(fp);
+#else /* !WINDOWS32 */
+ FIXED_INFO *fixed_info;
+ ULONG buflen;
+ DWORD ret;
+
+ rv = NULL;
+ fixed_info = malloc(sizeof(FIXED_INFO));
+ buflen = sizeof(FIXED_INFO);
+
+ if (GetNetworkParams(fixed_info, &buflen) == ERROR_BUFFER_OVERFLOW) {
+ /* official ugly api workaround */
+ free(fixed_info);
+ fixed_info = malloc(buflen);
+ }
+
+ ret = GetNetworkParams(fixed_info, &buflen);
+ if (ret == NO_ERROR) {
+ strncpy(addr, fixed_info->DnsServerList.IpAddress.String, sizeof(addr));
+ addr[15] = 0;
+ rv = addr;
+ }
+ free(fixed_info);
+#endif
+ return rv;
+}
+
diff --git a/jni/iodine/src/util.h b/jni/iodine/src/util.h
new file mode 100644
index 0000000..f514139
--- /dev/null
+++ b/jni/iodine/src/util.h
@@ -0,0 +1,6 @@
+#ifndef __UTIL_H__
+#define __UTIL_H__
+
+char *get_resolvconf_addr();
+
+#endif
diff --git a/jni/iodine/src/version.h b/jni/iodine/src/version.h
new file mode 100644
index 0000000..1561b9e
--- /dev/null
+++ b/jni/iodine/src/version.h
@@ -0,0 +1,25 @@
+/*
+ * 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 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 _VERSION_H_
+#define _VERSION_H_
+
+/* This is the version of the network protocol
+ It is usually equal to the latest iodine version number */
+#define VERSION 0x00000502
+
+#endif /* _VERSION_H_ */
+
diff --git a/jni/iodine/src/windows.h b/jni/iodine/src/windows.h
new file mode 100644
index 0000000..7e0e16c
--- /dev/null
+++ b/jni/iodine/src/windows.h
@@ -0,0 +1,100 @@
+/*
+ * 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 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 __FIX_WINDOWS_H__
+#define __FIX_WINDOWS_H__
+
+typedef unsigned int in_addr_t;
+
+#include <windows.h>
+#include <windns.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <iphlpapi.h>
+
+/* Missing from the mingw headers */
+#ifndef DNS_TYPE_SRV
+# define DNS_TYPE_SRV 33
+#endif
+#ifndef DNS_TYPE_TXT
+# define DNS_TYPE_TXT 16
+#endif
+
+#define T_A DNS_TYPE_A
+#define T_NS DNS_TYPE_NS
+#define T_NULL DNS_TYPE_NULL
+#define T_CNAME DNS_TYPE_CNAME
+#define T_MX DNS_TYPE_MX
+#define T_TXT DNS_TYPE_TXT
+#define T_SRV DNS_TYPE_SRV
+
+#define C_IN 1
+
+#define FORMERR 1
+#define SERVFAIL 2
+#define NXDOMAIN 3
+#define NOTIMP 4
+#define REFUSED 5
+
+#define sleep(seconds) Sleep((seconds)*1000)
+
+typedef struct {
+ unsigned id :16; /* query identification number */
+ /* fields in third byte */
+ unsigned rd :1; /* recursion desired */
+ unsigned tc :1; /* truncated message */
+ unsigned aa :1; /* authoritive answer */
+ unsigned opcode :4; /* purpose of message */
+ unsigned qr :1; /* response flag */
+ /* fields in fourth byte */
+ unsigned rcode :4; /* response code */
+ unsigned cd: 1; /* checking disabled by resolver */
+ unsigned ad: 1; /* authentic data from named */
+ unsigned unused :1; /* unused bits (MBZ as of 4.9.3a3) */
+ unsigned ra :1; /* recursion available */
+ /* remaining bytes */
+ unsigned qdcount :16; /* number of question entries */
+ unsigned ancount :16; /* number of answer entries */
+ unsigned nscount :16; /* number of authority entries */
+ unsigned arcount :16; /* number of resource entries */
+} HEADER;
+
+struct ip
+ {
+ unsigned int ip_hl:4; /* header length */
+ unsigned int ip_v:4; /* version */
+ u_char ip_tos; /* type of service */
+ u_short ip_len; /* total length */
+ u_short ip_id; /* identification */
+ u_short ip_off; /* fragment offset field */
+#define IP_RF 0x8000 /* reserved fragment flag */
+#define IP_DF 0x4000 /* dont fragment flag */
+#define IP_MF 0x2000 /* more fragments flag */
+#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
+ u_char ip_ttl; /* time to live */
+ u_char ip_p; /* protocol */
+ u_short ip_sum; /* checksum */
+ struct in_addr ip_src, ip_dst; /* source and dest address */
+ };
+
+DWORD WINAPI tun_reader(LPVOID arg);
+struct tun_data {
+ HANDLE tun;
+ int sock;
+ struct sockaddr_in addr;
+};
+
+#endif
diff --git a/jni/iodine/tests/Makefile b/jni/iodine/tests/Makefile
new file mode 100644
index 0000000..3a7ac01
--- /dev/null
+++ b/jni/iodine/tests/Makefile
@@ -0,0 +1,27 @@
+CC = gcc
+TEST = test
+OBJS = test.o base32.o base64.o read.o dns.o encoding.o login.o user.o fw_query.o
+SRCOBJS = ../src/base32.o ../src/base64.o ../src/read.o ../src/dns.o ../src/encoding.o ../src/login.o ../src/md5.o ../src/user.o ../src/fw_query.o
+
+OS = `uname | tr "a-z" "A-Z"`
+
+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)
+ @LD_LIBRARY_PATH=${CHECK_PATH}/lib ./$(TEST)
+
+$(TEST): $(OBJS) $(SRCOBJS)
+ @echo LD $(TEST)
+ @$(CC) -o $@ $(SRCOBJS) $(OBJS) $(LDFLAGS)
+
+.c.o:
+ @echo CC $<
+ @$(CC) $(CFLAGS) -c $<
+
+
+clean:
+ @echo "Cleaning tests/"
+ @rm -f *~ *.core $(TEST) $(OBJS)
+
diff --git a/jni/iodine/tests/base32.c b/jni/iodine/tests/base32.c
new file mode 100644
index 0000000..9ff0cf7
--- /dev/null
+++ b/jni/iodine/tests/base32.c
@@ -0,0 +1,142 @@
+/*
+ * 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 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 <check.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "encoding.h"
+#include "base32.h"
+#include "test.h"
+
+#define TUPLES 5
+
+static struct tuple
+{
+ char *a;
+ char *b;
+} testpairs[TUPLES] = {
+ { "iodinetestingtesting", "nfxwi0lomv0gk21unfxgo3dfon0gs1th" },
+ { "abc123", "mfrggmjsgm" },
+ { "test", "orsxg3a" },
+ { "tst", "orzxi" },
+ { "", "" },
+};
+
+START_TEST(test_base32_encode)
+{
+ size_t len;
+ char buf[4096];
+ struct encoder *b32;
+ int val;
+
+ b32 = get_base32_encoder();
+
+ len = sizeof(buf);
+ val = b32->encode(buf, &len, testpairs[_i].a, strlen(testpairs[_i].a));
+
+ fail_unless(strcmp(buf, testpairs[_i].b) == 0,
+ "'%s' != '%s'", buf, testpairs[_i].b);
+}
+END_TEST
+
+START_TEST(test_base32_decode)
+{
+ size_t len;
+ char buf[4096];
+ struct encoder *b32;
+ int val;
+
+ b32 = get_base32_encoder();
+
+ len = sizeof(buf);
+ val = b32->decode(buf, &len, testpairs[_i].b, strlen(testpairs[_i].b));
+
+ fail_unless(buf != NULL, "buf == NULL");
+ fail_unless(strcmp(buf, testpairs[_i].a) == 0,
+ "'%s' != '%s'", buf, testpairs[_i].a);
+}
+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
+
+START_TEST(test_base32_blksize)
+{
+ size_t rawlen;
+ size_t enclen;
+ char *rawbuf;
+ char *encbuf;
+ struct encoder *b32;
+ int i;
+ int val;
+
+ b32 = get_base32_encoder();
+
+ rawlen = b32->blocksize_raw();
+ enclen = b32->blocksize_encoded();
+
+ rawbuf = malloc(rawlen + 16);
+ encbuf = malloc(enclen + 16);
+
+ for (i = 0; i < rawlen; i++) {
+ rawbuf[i] = 'A';
+ }
+ rawbuf[i] = 0;
+
+ val = b32->encode(encbuf, &enclen, rawbuf, rawlen);
+
+ fail_unless(rawlen == 5, "raw length was %d not 5", rawlen);
+ fail_unless(enclen == 5, "encoded %d bytes, not 5", enclen);
+ fail_unless(val == 8, "encoded string %s was length %d", encbuf, val);
+
+ memset(rawbuf, 0, rawlen + 16);
+
+ enclen = val;
+ val = b32->decode(rawbuf, &rawlen, encbuf, enclen);
+
+ fail_unless(rawlen == 5, "raw length was %d not 5", rawlen);
+ fail_unless(val == 5, "val was not 5 but %d", val);
+ for (i = 0; i < rawlen; i++) {
+ fail_unless(rawbuf[i] == 'A');
+ }
+}
+END_TEST
+
+TCase *
+test_base32_create_tests()
+{
+ TCase *tc;
+
+ tc = tcase_create("Base32");
+ tcase_add_loop_test(tc, test_base32_encode, 0, TUPLES);
+ tcase_add_loop_test(tc, test_base32_decode, 0, TUPLES);
+ tcase_add_test(tc, test_base32_5to8_8to5);
+ tcase_add_test(tc, test_base32_blksize);
+
+ return tc;
+}
diff --git a/jni/iodine/tests/base64.c b/jni/iodine/tests/base64.c
new file mode 100644
index 0000000..bd0e9ce
--- /dev/null
+++ b/jni/iodine/tests/base64.c
@@ -0,0 +1,155 @@
+/*
+ * 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 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 <check.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "encoding.h"
+#include "base64.h"
+#include "test.h"
+
+#define TUPLES 5
+
+static struct tuple
+{
+ char *a;
+ char *b;
+} testpairs[TUPLES] = {
+ { "iodinetestingtesting", "Aw8KAw4LDgvZDgLUz2rLC2rPBMC" },
+ { "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"
+ "\x3C\xE3\x4C\x2C\xA2\x48\x1C\x61\x44\x0C\x20\x40\x3F\x3F\x3C\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\x3C\xE3\x4C\x2C\xA2\x48"
+ "\x1C\x61\x44\x0C\x20\x40\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\x3C\xE3\x4C\x2C\xA2\x48\x1C\x61\x44\x0C\x20\x40",
+
+ "+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"
+ "\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\x3F\x3F\x3C\xEF\xAE\x78"
+ "\xDF\x6D\x74\xCF\x2C\x70\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\xFF\xEF\x7C\xEF\xAE\x78\xDF\x6D\x74\xCF\x2C\x70"
+ "\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",
+
+ "+9876543210-ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcbapZ"
+ "776543210-ZYXWVUTSRQfHKwfHGsHGFEDCBAzyxwvutsrqponmlkjihgfedcba+987654321"
+ "0-ZYXWVUTSRQfHKwfHGsHGFEDCBAzyxwvutsrqponmlkjihgfedcba"
+ },
+ { "", "" }
+};
+
+START_TEST(test_base64_encode)
+{
+ size_t len;
+ char buf[4096];
+ struct encoder *b64;
+ int val;
+
+ b64 = get_base64_encoder();
+
+ len = sizeof(buf);
+ val = b64->encode(buf, &len, testpairs[_i].a, strlen(testpairs[_i].a));
+
+ fail_unless(strcmp(buf, testpairs[_i].b) == 0,
+ "'%s' != '%s'", buf, testpairs[_i].b);
+}
+END_TEST
+
+START_TEST(test_base64_decode)
+{
+ size_t len;
+ char buf[4096];
+ struct encoder *b64;
+ int val;
+
+ b64 = get_base64_encoder();
+
+ len = sizeof(buf);
+ val = b64->decode(buf, &len, testpairs[_i].b, strlen(testpairs[_i].b));
+
+ fail_unless(buf != NULL, "buf == NULL");
+ fail_unless(strcmp(buf, testpairs[_i].a) == 0,
+ "'%s' != '%s'", buf, testpairs[_i].a);
+}
+END_TEST
+
+START_TEST(test_base64_blksize)
+{
+ size_t rawlen;
+ size_t enclen;
+ char *rawbuf;
+ char *encbuf;
+ struct encoder *b64;
+ int i;
+ int val;
+
+ b64 = get_base64_encoder();
+
+ rawlen = b64->blocksize_raw();
+ enclen = b64->blocksize_encoded();
+
+ rawbuf = malloc(rawlen + 16);
+ encbuf = malloc(enclen + 16);
+
+ for (i = 0; i < rawlen; i++) {
+ rawbuf[i] = 'A';
+ }
+ rawbuf[i] = 0;
+
+ val = b64->encode(encbuf, &enclen, rawbuf, rawlen);
+
+ fail_unless(rawlen == 3, "raw length was %d not 3", rawlen);
+ fail_unless(enclen == 3, "encoded %d bytes, not 3", enclen);
+ fail_unless(val == 4, "encoded string %s was length %d", encbuf, val);
+
+ memset(rawbuf, 0, rawlen + 16);
+
+ enclen = val;
+ val = b64->decode(rawbuf, &rawlen, encbuf, enclen);
+
+ fail_unless(rawlen == 3, "raw length was %d not 3", rawlen);
+ fail_unless(val == 3);
+ for (i = 0; i < rawlen; i++) {
+ fail_unless(rawbuf[i] == 'A');
+ }
+}
+END_TEST
+
+TCase *
+test_base64_create_tests()
+{
+ TCase *tc;
+
+ tc = tcase_create("Base64");
+ tcase_add_loop_test(tc, test_base64_encode, 0, TUPLES);
+ tcase_add_loop_test(tc, test_base64_decode, 0, TUPLES);
+ tcase_add_test(tc, test_base64_blksize);
+
+ return tc;
+}
diff --git a/jni/iodine/tests/dns.c b/jni/iodine/tests/dns.c
new file mode 100644
index 0000000..3d21e4c
--- /dev/null
+++ b/jni/iodine/tests/dns.c
@@ -0,0 +1,252 @@
+/*
+ * 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 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 <check.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/stat.h>
+#include <arpa/nameser.h>
+
+#include "common.h"
+#include "dns.h"
+#include "encoding.h"
+#include "base32.h"
+#include "test.h"
+
+static void dump_packet(char *, size_t);
+
+static char query_packet[] =
+ "\x05\x39\x01\x00\x00\x01\x00\x00\x00\x00\x00\x01\x2D\x41\x6A\x62\x63"
+ "\x75\x79\x74\x63\x70\x65\x62\x30\x67\x71\x30\x6C\x74\x65\x62\x75\x78"
+ "\x67\x69\x64\x75\x6E\x62\x73\x73\x61\x33\x64\x66\x6F\x6E\x30\x63\x61"
+ "\x7A\x64\x62\x6F\x72\x71\x71\x04\x6B\x72\x79\x6F\x02\x73\x65\x00\x00"
+ "\x0A\x00\x01\x00\x00\x29\x10\x00\x00\x00\x80\x00\x00\x00";
+
+static char answer_packet[] =
+ "\x05\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 answer_packet_high_trans_id[] =
+ "\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";
+
+static char *innerData = "HELLO this is the test data";
+
+START_TEST(test_encode_query)
+{
+ char buf[512];
+ char resolv[512];
+ struct query q;
+ struct encoder *enc;
+ char *d;
+ size_t len;
+ size_t enclen;
+ int ret;
+
+ enclen = sizeof(resolv);
+ memset(&buf, 0, sizeof(buf));
+ memset(&resolv, 0, sizeof(resolv));
+ memset(&q, 0, sizeof(struct query));
+ q.type = T_NULL;
+ q.id = 1337;
+ d = resolv;
+ enc = get_base32_encoder();
+
+ *d++ = 'A';
+ enc->encode(d, &enclen, innerData, strlen(innerData));
+ d = resolv + strlen(resolv);
+ if (*d != '.') {
+ *d++ = '.';
+ }
+ strcpy(d, topdomain);
+ len = sizeof(buf);
+ ret = dns_encode(buf, len, &q, QR_QUERY, resolv, strlen(resolv));
+ len = sizeof(query_packet) - 1; /* Skip extra null character */
+
+ if (strncmp(query_packet, buf, sizeof(query_packet)) || ret != len) {
+ printf("\n");
+ dump_packet(query_packet, len);
+ dump_packet(buf, ret);
+ }
+ fail_unless(strncmp(query_packet, buf, sizeof(query_packet)) == 0, "Did not compile expected packet");
+ fail_unless(ret == len, "Bad packet length: %d, expected %d", ret, len);
+}
+END_TEST
+
+START_TEST(test_decode_query)
+{
+ char buf[512];
+ char *domain;
+ struct query q;
+ struct encoder *enc;
+ size_t len;
+
+ memset(&q, 0, sizeof(struct query));
+ memset(&buf, 0, sizeof(buf));
+ q.id = 0;
+ len = sizeof(query_packet) - 1;
+ enc = get_base32_encoder();
+
+ dns_decode(buf, sizeof(buf), &q, QR_QUERY, query_packet, len);
+ domain = strstr(q.name, topdomain);
+ len = sizeof(buf);
+ unpack_data(buf, len, &(q.name[1]), (int) (domain - q.name) - 1, enc);
+
+ fail_unless(strncmp(buf, innerData, strlen(innerData)) == 0, "Did not extract expected host: '%s'", buf);
+ fail_unless(strlen(buf) == strlen(innerData), "Bad host length: %d, expected %d: '%s'", strlen(buf), strlen(innerData), buf);
+}
+END_TEST
+
+START_TEST(test_encode_response)
+{
+ char buf[512];
+ char *host = "silly.host.of.iodine.code.kryo.se";
+ struct query q;
+ int len;
+ int ret;
+
+ len = sizeof(buf);
+ memset(&buf, 0, sizeof(buf));
+ memset(&q, 0, sizeof(struct query));
+ strncpy(q.name, host, strlen(host));
+ q.type = T_NULL;
+ q.id = 1337;
+
+ ret = dns_encode(buf, len, &q, QR_ANSWER, msgData, strlen(msgData));
+ len = sizeof(answer_packet) - 1; /* Skip extra null character */
+
+ fail_unless(strncmp(answer_packet, buf, sizeof(answer_packet)) == 0, "Did not compile expected packet");
+ fail_unless(ret == len, "Bad packet length: %d, expected %d", ret, len);
+}
+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, &q, QR_ANSWER, answer_packet, sizeof(answer_packet)-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, answer_packet_high_trans_id, sizeof(answer_packet_high_trans_id)-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
+
+START_TEST(test_get_id_short_packet)
+{
+ char buf[5];
+ int len;
+ unsigned short id;
+
+ len = sizeof(buf);
+ memset(&buf, 5, sizeof(buf));
+
+ id = dns_get_id(buf, len);
+ fail_unless(id == 0);
+}
+END_TEST
+
+START_TEST(test_get_id_low)
+{
+ unsigned short id;
+
+ id = dns_get_id(answer_packet, sizeof(answer_packet));
+ fail_unless(id == 1337);
+}
+END_TEST
+
+START_TEST(test_get_id_high)
+{
+ unsigned short id;
+
+ id = dns_get_id(answer_packet_high_trans_id, sizeof(answer_packet_high_trans_id));
+ fail_unless(id == 0x8539);
+}
+END_TEST
+
+static void
+dump_packet(char *buf, size_t len)
+{
+ int pos;
+
+ for (pos = 0; pos < len; pos++) {
+ printf("\\x%02X", (unsigned char) buf[pos]);
+ }
+ printf("\n");
+ for (pos = 0; pos < len; pos++) {
+ if (isalnum((unsigned char) buf[pos])) {
+ printf(" %c ", (unsigned char) buf[pos]);
+ } else {
+ printf(" ");
+ }
+ }
+ printf("\n");
+}
+
+TCase *
+test_dns_create_tests()
+{
+ TCase *tc;
+
+ tc = tcase_create("Dns");
+ tcase_add_test(tc, test_encode_query);
+ 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);
+ tcase_add_test(tc, test_get_id_short_packet);
+ tcase_add_test(tc, test_get_id_low);
+ tcase_add_test(tc, test_get_id_high);
+
+ return tc;
+}
diff --git a/jni/iodine/tests/encoding.c b/jni/iodine/tests/encoding.c
new file mode 100644
index 0000000..ac22452
--- /dev/null
+++ b/jni/iodine/tests/encoding.c
@@ -0,0 +1,108 @@
+/*
+ * 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 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 <check.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "encoding.h"
+#include "test.h"
+#include "base32.h"
+#include "base64.h"
+
+#define TUPLES 4
+
+static struct tuple
+{
+ char *a;
+ char *b;
+} dottests[] = {
+ { "aaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaa"},
+ { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."},
+ { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
+ { "abc123", "abc123" },
+ { NULL, NULL }
+};
+
+START_TEST(test_inline_dotify)
+{
+ char temp[1024];
+ char *b;
+
+ memset(temp, 0, sizeof(temp));
+ strcpy(temp, dottests[_i].a);
+ b = temp;
+ inline_dotify(b, sizeof(temp));
+
+ fail_unless(strcmp(dottests[_i].b, temp) == 0,
+ "'%s' != '%s'", temp, dottests[_i].b);
+}
+END_TEST
+
+START_TEST(test_inline_undotify)
+{
+ char temp[1024];
+ char *b;
+
+ memset(temp, 0, sizeof(temp));
+ strcpy(temp, dottests[_i].b);
+ b = temp;
+ inline_undotify(b, sizeof(temp));
+
+ fail_unless(strcmp(dottests[_i].a, temp) == 0,
+ "'%s' != '%s'", temp, dottests[_i].a);
+}
+END_TEST
+
+START_TEST(test_build_hostname)
+{
+ char data[256];
+ char buf[1024];
+ char *topdomain = "a.c";
+ int buflen;
+ int i;
+
+ for (i = 0; i < sizeof(data); i++) {
+ data[i] = i & 0xFF;
+ }
+
+ buflen = sizeof(buf);
+
+ for (i = 1; i < sizeof(data); i++) {
+ int len = build_hostname(buf, buflen, data, i, topdomain, get_base32_encoder(), sizeof(buf));
+
+ fail_if(len > i);
+ fail_if(strstr(buf, ".."), "Found double dots when encoding data len %d! buf: %s", i, buf);
+ }
+}
+END_TEST
+
+TCase *
+test_encoding_create_tests()
+{
+ TCase *tc;
+
+ tc = tcase_create("Encoding");
+ tcase_add_loop_test(tc, test_inline_dotify, 0, TUPLES);
+ tcase_add_loop_test(tc, test_inline_undotify, 0, TUPLES);
+ tcase_add_test(tc, test_build_hostname);
+
+ return tc;
+}
diff --git a/jni/iodine/tests/fw_query.c b/jni/iodine/tests/fw_query.c
new file mode 100644
index 0000000..6d23924
--- /dev/null
+++ b/jni/iodine/tests/fw_query.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2006-2009 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 <check.h>
+
+#include "fw_query.h"
+#include "test.h"
+
+START_TEST(test_fw_query_simple)
+{
+ struct fw_query q;
+ struct fw_query *qp;
+
+ q.addrlen = 33;
+ q.id = 0x848A;
+
+ fw_query_init();
+
+ /* Test empty cache */
+ fw_query_get(0x848A, &qp);
+ fail_unless(qp == NULL);
+
+ fw_query_put(&q);
+
+ /* Test cache with one entry */
+ fw_query_get(0x848A, &qp);
+ fail_unless(qp->addrlen == q.addrlen);
+ fail_unless(qp->id == q.id);
+}
+END_TEST
+
+START_TEST(test_fw_query_edge)
+{
+ struct fw_query q;
+ struct fw_query *qp;
+ int i;
+
+ fw_query_init();
+
+ q.addrlen = 33;
+ q.id = 0x848A;
+ fw_query_put(&q);
+
+ for (i = 1; i < FW_QUERY_CACHE_SIZE; i++) {
+ q.addrlen++;
+ q.id++;
+ fw_query_put(&q);
+ }
+
+ /* The query should still be cached */
+ fw_query_get(0x848A, &qp);
+ fail_unless(qp->addrlen == 33);
+ fail_unless(qp->id == 0x848A);
+
+ q.addrlen++;
+ q.id++;
+ fw_query_put(&q);
+
+ /* but now it is overwritten */
+ fw_query_get(0x848A, &qp);
+ fail_unless(qp == NULL);
+}
+END_TEST
+
+TCase *
+test_fw_query_create_tests()
+{
+ TCase *tc;
+
+ tc = tcase_create("Forwarded query");
+ tcase_add_test(tc, test_fw_query_simple);
+ tcase_add_test(tc, test_fw_query_edge);
+
+ return tc;
+}
diff --git a/jni/iodine/tests/login.c b/jni/iodine/tests/login.c
new file mode 100644
index 0000000..1ef23f4
--- /dev/null
+++ b/jni/iodine/tests/login.c
@@ -0,0 +1,70 @@
+/*
+ * 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 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 <check.h>
+#include <string.h>
+
+#include "test.h"
+#include "login.h"
+
+START_TEST(test_login_hash)
+{
+ char ans[16];
+ char good[] = "\x2A\x8A\x12\xB4\xE0\x42\xEE\xAB\xD0\x19\x17\x1E\x44\xA0\x88\xCD";
+ char pass[32] = "iodine is the shit";
+ int len;
+ int seed;
+
+ len = sizeof(ans);
+ seed = 15;
+
+ memset(ans, 0, sizeof(ans));
+ login_calculate(ans, len, pass, seed);
+ fail_unless(strncmp(ans, good, len) == 0, NULL);
+}
+END_TEST
+
+START_TEST(test_login_hash_short)
+{
+ char ans[8];
+ char check[sizeof(ans)];
+ char pass[32] = "iodine is the shit";
+ int len;
+ int seed;
+
+ len = sizeof(ans);
+ seed = 15;
+
+ memset(ans, 0, sizeof(ans));
+ memset(check, 0, sizeof(check));
+
+ /* If len < 16, it should do nothing */
+ login_calculate(ans, len, pass, seed);
+ fail_if(memcmp(ans, check, sizeof(ans)));
+}
+END_TEST
+
+TCase *
+test_login_create_tests()
+{
+ TCase *tc;
+
+ tc = tcase_create("Login");
+ tcase_add_test(tc, test_login_hash);
+ tcase_add_test(tc, test_login_hash_short);
+
+ return tc;
+}
diff --git a/jni/iodine/tests/read.c b/jni/iodine/tests/read.c
new file mode 100644
index 0000000..18cc29c
--- /dev/null
+++ b/jni/iodine/tests/read.c
@@ -0,0 +1,297 @@
+/*
+ * 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 and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/stat.h>
+#include <arpa/nameser.h>
+#ifdef DARWIN
+#include <arpa/nameser8_compat.h>
+#endif
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <check.h>
+
+#include "common.h"
+#include "encoding.h"
+#include "dns.h"
+#include "read.h"
+#include "test.h"
+
+START_TEST(test_read_putshort)
+{
+ unsigned short k;
+ unsigned short l;
+ char* p;
+ int i;
+
+ for (i = 0; i < 65536; i++) {
+ p = (char*)&k;
+ putshort(&p, i);
+ fail_unless(ntohs(k) == i,
+ "Bad value on putshort for %d: %d != %d",
+ i, ntohs(k), i);
+
+ p = (char*)&k;
+ readshort(NULL, &p, (short *) &l);
+ fail_unless(l == i,
+ "Bad value on readshort for %d: %d != %d",
+ i, l, i);
+ }
+}
+END_TEST
+
+START_TEST(test_read_putlong)
+{
+ uint32_t k;
+ uint32_t l;
+ char* p;
+ int i;
+ int j;
+
+ for (i = 0; i < 32; i++) {
+ p = (char*)&k;
+ j = 0xf << i;
+
+ putlong(&p, j);
+
+ fail_unless(ntohl(k) == j,
+ "Bad value on putlong for %d: %d != %d", i, ntohl(j), j);
+
+ p = (char*)&k;
+ readlong(NULL, &p, &l);
+
+ fail_unless(l == j,
+ "Bad value on readlong for %d: %d != %d", i, l, j);
+ }
+}
+END_TEST
+
+START_TEST(test_read_name_empty_loop)
+{
+ unsigned char emptyloop[] = {
+ 'A', 'A', 0x81, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01 };
+ char buf[1024];
+ char *data;
+ int rv;
+
+ memset(buf, 0, sizeof(buf));
+ data = (char*) emptyloop + sizeof(HEADER);
+ buf[1023] = 'A';
+ rv = readname((char *) emptyloop, sizeof(emptyloop), &data, buf, 1023);
+ fail_unless(buf[1023] == 'A');
+}
+END_TEST
+
+START_TEST(test_read_name_inf_loop)
+{
+ unsigned char infloop[] = {
+ 'A', 'A', 0x81, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 'A', 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01 };
+ char buf[1024];
+ char *data;
+ int rv;
+
+ memset(buf, 0, sizeof(buf));
+ data = (char*) infloop + sizeof(HEADER);
+ buf[4] = '\a';
+ rv = readname((char*) infloop, sizeof(infloop), &data, buf, 4);
+ fail_unless(buf[4] == '\a');
+}
+END_TEST
+
+START_TEST(test_read_name_longname)
+{
+ unsigned char longname[] =
+ "AA\x81\x80\x00\x01\x00\x00\x00\x00\x00\x00"
+ "\x3FzBCDEFGHIJKLMNOPQURSTUVXYZ0123456789abcdefghijklmnopqrstuvxyzAA"
+ "\x3FzBCDEFGHIJKLMNOPQURSTUVXYZ0123456789abcdefghijklmnopqrstuvxyzAA"
+ "\x3FzBCDEFGHIJKLMNOPQURSTUVXYZ0123456789abcdefghijklmnopqrstuvxyzAA"
+ "\x3FzBCDEFGHIJKLMNOPQURSTUVXYZ0123456789abcdefghijklmnopqrstuvxyzAA"
+ "\x3FzBCDEFGHIJKLMNOPQURSTUVXYZ0123456789abcdefghijklmnopqrstuvxyzAA"
+ "\x3FzBCDEFGHIJKLMNOPQURSTUVXYZ0123456789abcdefghijklmnopqrstuvxyzAA"
+ "\x00\x00\x01\x00\x01";
+ char buf[1024];
+ char *data;
+ int rv;
+
+ memset(buf, 0, sizeof(buf));
+ data = (char*) longname + sizeof(HEADER);
+ buf[256] = '\a';
+ rv = readname((char*) longname, sizeof(longname), &data, buf, 256);
+ fail_unless(buf[256] == '\a');
+}
+END_TEST
+
+START_TEST(test_read_name_onejump)
+{
+ unsigned char onejump[] =
+ "AA\x81\x80\x00\x01\x00\x00\x00\x00\x00\x00"
+ "\x02hh\xc0\x15\x00\x01\x00\x01\x05zBCDE\x00";
+ char buf[1024];
+ char *data;
+ int rv;
+
+ memset(buf, 0, sizeof(buf));
+ data = (char*) onejump + sizeof(HEADER);
+ rv = readname((char*) onejump, sizeof(onejump), &data, buf, 256);
+ fail_unless(rv == 9);
+}
+END_TEST
+
+START_TEST(test_read_name_badjump_start)
+{
+ unsigned char badjump[] = {
+ 'A', 'A', 0x81, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xfe, 0xcc, 0x00, 0x01, 0x00, 0x01 };
+ unsigned char *jumper;
+ char buf[1024];
+ char *data;
+ int rv;
+
+ /* This test uses malloc to cause segfault if jump is executed */
+ memset(buf, 0, sizeof(buf));
+ jumper = malloc(sizeof(badjump));
+ if (jumper) {
+ memcpy(jumper, badjump, sizeof(badjump));
+ data = (char*) jumper + sizeof(HEADER);
+ rv = readname((char*) jumper, sizeof(badjump), &data, buf, 256);
+
+ fail_unless(rv == 0);
+ fail_unless(buf[0] == 0);
+ }
+ free(jumper);
+}
+END_TEST
+
+START_TEST(test_read_name_badjump_second)
+{
+ unsigned char badjump2[] = {
+ 'A', 'A', 0x81, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 'B', 'A', 0xfe, 0xcc, 0x00, 0x01, 0x00, 0x01 };
+ unsigned char *jumper;
+ char buf[1024];
+ char *data;
+ int rv;
+
+ /* This test uses malloc to cause segfault if jump is executed */
+ memset(buf, 0, sizeof(buf));
+ jumper = malloc(sizeof(badjump2));
+ if (jumper) {
+ memcpy(jumper, badjump2, sizeof(badjump2));
+ data = (char*) jumper + sizeof(HEADER);
+ rv = readname((char*) jumper, sizeof(badjump2), &data, buf, 256);
+
+ fail_unless(rv == 4);
+ fail_unless(strcmp("BA.", buf) == 0,
+ "buf is not BA: %s", buf);
+ }
+ free(jumper);
+}
+END_TEST
+
+START_TEST(test_putname)
+{
+ char out[] = "\x06" "BADGER\x06" "BADGER\x04" "KRYO\x02" "SE\x00";
+ char buf[256];
+ char *domain = "BADGER.BADGER.KRYO.SE";
+ char *b;
+ int len;
+ int ret;
+
+ len = 256;
+
+ memset(buf, 0, 256);
+ b = buf;
+ ret = putname(&b, 256, domain);
+
+ fail_unless(ret == strlen(domain) + 1);
+ fail_unless(strncmp(buf, out, ret) == 0, "Happy flow failed");
+}
+END_TEST
+
+START_TEST(test_putname_nodot)
+{
+ char buf[256];
+ char *nodot =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ char *b;
+ int len;
+ int ret;
+
+ len = 256;
+
+ memset(buf, 0, 256);
+ b = buf;
+ ret = putname(&b, 256, nodot);
+
+ fail_unless(ret == -1);
+ fail_unless(b == buf);
+}
+END_TEST
+
+START_TEST(test_putname_toolong)
+{
+ char buf[256];
+ char *toolong =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ.ABCDEFGHIJKLMNOPQRSTUVWXYZ."
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ.ABCDEFGHIJKLMNOPQRSTUVWXYZ."
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ.ABCDEFGHIJKLMNOPQRSTUVWXYZ."
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ.ABCDEFGHIJKLMNOPQRSTUVWXYZ."
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ.ABCDEFGHIJKLMNOPQRSTUVWXYZ."
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ.ABCDEFGHIJKLMNOPQRSTUVWXYZ.";
+ char *b;
+ int len;
+ int ret;
+
+ len = 256;
+
+ memset(buf, 0, 256);
+ b = buf;
+ ret = putname(&b, 256, toolong);
+
+ fail_unless(ret == -1);
+ fail_unless(b == buf);
+}
+END_TEST
+
+
+TCase *
+test_read_create_tests()
+{
+ TCase *tc;
+
+ tc = tcase_create("Read");
+ tcase_set_timeout(tc, 60);
+ tcase_add_test(tc, test_read_putshort);
+ tcase_add_test(tc, test_read_putlong);
+ tcase_add_test(tc, test_read_name_empty_loop);
+ tcase_add_test(tc, test_read_name_inf_loop);
+ tcase_add_test(tc, test_read_name_longname);
+ tcase_add_test(tc, test_read_name_onejump);
+ tcase_add_test(tc, test_read_name_badjump_start);
+ tcase_add_test(tc, test_read_name_badjump_second);
+ tcase_add_test(tc, test_putname);
+ tcase_add_test(tc, test_putname_nodot);
+ tcase_add_test(tc, test_putname_toolong);
+
+ return tc;
+}
diff --git a/jni/iodine/tests/test.c b/jni/iodine/tests/test.c
new file mode 100644
index 0000000..5bee9d2
--- /dev/null
+++ b/jni/iodine/tests/test.c
@@ -0,0 +1,66 @@
+/*
+ * 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 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 <check.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "test.h"
+
+int
+main()
+{
+ SRunner *runner;
+ Suite *iodine;
+ TCase *test;
+ int failed;
+
+ iodine = suite_create("iodine");
+
+ test = test_base32_create_tests();
+ suite_add_tcase(iodine, test);
+
+ test = test_base64_create_tests();
+ suite_add_tcase(iodine, test);
+
+ test = test_dns_create_tests();
+ suite_add_tcase(iodine, test);
+
+ test = test_encoding_create_tests();
+ suite_add_tcase(iodine, test);
+
+ test = test_read_create_tests();
+ suite_add_tcase(iodine, test);
+
+ test = test_login_create_tests();
+ suite_add_tcase(iodine, test);
+
+ test = test_user_create_tests();
+ suite_add_tcase(iodine, test);
+
+ test = test_fw_query_create_tests();
+ suite_add_tcase(iodine, test);
+
+ runner = srunner_create(iodine);
+ srunner_run_all(runner, CK_NORMAL);
+ failed = srunner_ntests_failed(runner);
+
+ srunner_free(runner);
+
+ return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/jni/iodine/tests/test.h b/jni/iodine/tests/test.h
new file mode 100644
index 0000000..1022a9e
--- /dev/null
+++ b/jni/iodine/tests/test.h
@@ -0,0 +1,37 @@
+/*
+ * 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 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 __TEST_H__
+#define __TEST_H__
+
+TCase *test_base32_create_tests();
+TCase *test_base64_create_tests();
+TCase *test_dns_create_tests();
+TCase *test_encoding_create_tests();
+TCase *test_read_create_tests();
+TCase *test_login_create_tests();
+TCase *test_user_create_tests();
+TCase *test_fw_query_create_tests();
+
+char *va_str(const char *, ...);
+
+#if (CHECK_MAJOR_VERSION == 0 && \
+ ((CHECK_MINOR_VERSION == 9 && CHECK_MICRO_VERSION < 2) || \
+ (CHECK_MINOR_VERSION < 9)))
+#define tcase_set_timeout(...)
+#endif
+
+#endif
diff --git a/jni/iodine/tests/user.c b/jni/iodine/tests/user.c
new file mode 100644
index 0000000..afd61ca
--- /dev/null
+++ b/jni/iodine/tests/user.c
@@ -0,0 +1,199 @@
+/*
+ * 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 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 <check.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include "common.h"
+#include "encoding.h"
+#include "user.h"
+#include "test.h"
+
+START_TEST(test_init_users)
+{
+ in_addr_t ip;
+ char givenip[16];
+ int i;
+
+ ip = inet_addr("127.0.0.1");
+ init_users(ip, 27);
+ for (i = 0; i < USERS; i++) {
+ fail_unless(users[i].id == i);
+ fail_unless(users[i].q.id == 0);
+ fail_unless(users[i].inpacket.len == 0);
+ fail_unless(users[i].outpacket.len == 0);
+ snprintf(givenip, sizeof(givenip), "127.0.0.%d", i + 2);
+ fail_unless(users[i].tun_ip == inet_addr(givenip));
+ }
+}
+END_TEST
+
+START_TEST(test_users_waiting)
+{
+ in_addr_t ip;
+
+ ip = inet_addr("127.0.0.1");
+ init_users(ip, 27);
+
+ fail_unless(users_waiting_on_reply() == 0);
+
+ users[3].active = 1;
+
+ fail_unless(users_waiting_on_reply() == 0);
+
+ users[3].last_pkt = time(NULL);
+
+ fail_unless(users_waiting_on_reply() == 0);
+
+ users[3].conn = CONN_DNS_NULL;
+ users[3].q.id = 1;
+
+ fail_unless(users_waiting_on_reply() == 1);
+}
+END_TEST
+
+START_TEST(test_find_user_by_ip)
+{
+ in_addr_t ip;
+ unsigned int testip;
+
+ ip = inet_addr("127.0.0.1");
+ init_users(ip, 27);
+ users[0].conn = CONN_DNS_NULL;
+
+ testip = (unsigned int) inet_addr("10.0.0.1");
+ fail_unless(find_user_by_ip(testip) == -1);
+
+ testip = (unsigned int) inet_addr("127.0.0.2");
+ fail_unless(find_user_by_ip(testip) == -1);
+
+ users[0].active = 1;
+
+ testip = (unsigned int) inet_addr("127.0.0.2");
+ fail_unless(find_user_by_ip(testip) == -1);
+
+ users[0].last_pkt = time(NULL);
+
+ testip = (unsigned int) inet_addr("127.0.0.2");
+ fail_unless(find_user_by_ip(testip) == 0);
+}
+END_TEST
+
+START_TEST(test_all_users_waiting_to_send)
+{
+ in_addr_t ip;
+
+ ip = inet_addr("127.0.0.1");
+ init_users(ip, 27);
+
+ fail_unless(all_users_waiting_to_send() == 1);
+
+ users[0].conn = CONN_DNS_NULL;
+ users[0].active = 1;
+
+ fail_unless(all_users_waiting_to_send() == 1);
+
+ users[0].last_pkt = time(NULL);
+ users[0].outpacket.len = 0;
+
+ fail_unless(all_users_waiting_to_send() == 0);
+
+#ifdef OUTPACKETQ_LEN
+ users[0].outpacketq_filled = 1;
+#else
+ users[0].outpacket.len = 44;
+#endif
+
+ fail_unless(all_users_waiting_to_send() == 1);
+}
+END_TEST
+
+START_TEST(test_find_available_user)
+{
+ in_addr_t ip;
+ int i;
+
+ ip = inet_addr("127.0.0.1");
+ init_users(ip, 27);
+
+ for (i = 0; i < USERS; 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
+
+START_TEST(test_find_available_user_small_net)
+{
+ in_addr_t ip;
+ int i;
+
+ ip = inet_addr("127.0.0.1");
+ init_users(ip, 29); /* this should result in 5 enabled users */
+
+ for (i = 0; i < 5; i++) {
+ fail_unless(find_available_user() == i);
+ }
+
+ for (i = 0; i < USERS; i++) {
+ fail_unless(find_available_user() == -1);
+ }
+
+ users[3].active = 0;
+
+ fail_unless(find_available_user() == 3);
+ fail_unless(find_available_user() == -1);
+
+ users[3].last_pkt = 55;
+
+ fail_unless(find_available_user() == 3);
+ fail_unless(find_available_user() == -1);
+}
+END_TEST
+
+TCase *
+test_user_create_tests()
+{
+ TCase *tc;
+
+ tc = tcase_create("User");
+ tcase_add_test(tc, test_init_users);
+ tcase_add_test(tc, test_users_waiting);
+ 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;
+}