diff --git a/CHANGELOG b/CHANGELOG index 6d5c4d0..731df84 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,9 +6,23 @@ iodine - http://code.kryo.se/iodine CHANGES: master: - - IPv6 support (in progress, #107) - Client can connect to iodined through an IPv6 nameserver. - Server only supports IPv4 for now. + - Mac OS X: Support native utun VPN devices. Patch by + Peter Sagerson, ported from OpenVPN by Catalin Patulea. + - Fix compilation failure on kFreeBSD and Hurd, by Gregor Herrmann + - Patch from Ryan Welton that fixes compilation warning. + - README converted to markdown by Nicolas Braud-Santoni. + - Linux: use pkg-config for systemd support flags. + Patch by Jason A. Donenfeld. + - Change external IP webservice to ipify.org + - Add support for IPv6 in the server. + Raw mode will be with same protocol as used for login. + Traffic inside tunnel is still IPv4. + - Update android build to support 5.0 (Lollipop) and newer. + +2014-06-16: 0.7.0 "Kryoptonite" + - Partial IPv6 support (#107) + Client can connect to iodined through an relaying IPv6 + nameserver. Server only supports IPv4 for now. Traffic inside tunnel is IPv4. - Add socket activation for systemd, by Michael Scherer. - Add automated lookup of external ip (via -n auto). @@ -20,6 +34,13 @@ master: Patch by laurent at gouloum fr, fixes #95. - Add android patches and makefile, from Marcel Bokhorst, fixes #105. - Added missing break in iodine.c, by Pavel Pergamenshchik, #108. + - A number of minor patches from Frank Denis, Gregor Herrmann and + Barak A. Pearlmutter. + - Testcase compilation fixes for OS X and FreeBSD + - Do not let sockets be inherited by sub-processes, fixes #99. + - Add unspecified RR type (called PRIVATE; id 65399, in private use + range). For servers with RFC3597 support. Fixes #97. + - Fix authentication bypass vulnerability; found by Oscar Reparaz. 2010-02-06: 0.6.0-rc1 "Hotspotify" - Fixed tunnel not working on Windows. diff --git a/README b/README deleted file mode 100644 index e46a14c..0000000 --- a/README +++ /dev/null @@ -1,379 +0,0 @@ - -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. - - -COMPILING: - -Iodine has no configure script. There are two optional features for Linux -(SELinux and systemd support) that will be enabled automatically if the -relevant header files are found in /usr/include. (See script at ./src/osflags) - -Run 'make' to compile the server and client binaries. -Run 'make install' to copy binaries and manpage to the destination directory. -Run 'make test' to compile and run the unit tests. (Requires the check library) - - -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: - -IPv6: -At the moment the iodined server only supports IPv4. The data inside the tunnel -is IPv4 only. - -The client can use IPv4 or IPv6 nameservers to connect to iodined. The relay -nameservers will translate between protocols automatically if needed. Use -options -4 or -6 to force the client to use a specific IP version for its DNS -queries. The client has to force IPv4 if it has dual-stack connectivity and -the hostname handling the tunnel domain has both A and AAAA records. - -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. - -For systemd support on Debian, compile with libsystemd-daemon-dev installed. - -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 , Erik Ekman -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/README-android.txt b/README-android.txt index f08c6cf..a096457 100644 --- a/README-android.txt +++ b/README-android.txt @@ -13,7 +13,7 @@ Extra README file for Android 2. Find/build a compatible tun.ko for your specific Android kernel 3. Copy tun.ko and the iodine binary to your device: - (Almost all devices need the armeabi binary. Only Intel powered + (Almost all devices need the armeabi binary. Only Intel powered ones need the x86 build.) adb push tun.ko /data/local/tmp @@ -35,11 +35,15 @@ For more information: http://blog.bokhorst.biz/5123 2. Download and unpack the iodine sources 3. Build: - cd src - make base64u.h base64u.c - ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk + cd src + make base64u.h base64u.c + ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk APP_PLATFORM=android-16 or run "make cross-android" in the iodine root directory. - To build for other archs, specify TARGET_ARCH_ABI: + To build for other archs, specify TARGET_ARCH_ABI: "make cross-android TARGET_ARCH_ABI=x86" + For older android versions (pre-kitkat), build with "make cross-android-old" in the + root directory, or manually like above but with APP_PLATFORM=android-3 + + The iodine binary ends up in src/libs//iodine diff --git a/README-win32.txt b/README-win32.txt index de9124b..56e52e6 100644 --- a/README-win32.txt +++ b/README-win32.txt @@ -11,10 +11,11 @@ Extra README file for Win32 related stuff 0. After iodine 0.6, you need Windows XP or newer to run. -1. Install the TAP32 driver +1. Install the TAP 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. + Download the OpenVPN TAP driver (under section Tap-windows) + Problems has been reported with the NDIS6 version (9.2x.y), use the + NDIS5 version for now if possible. 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 diff --git a/README.md b/README.md new file mode 100644 index 0000000..1c76829 --- /dev/null +++ b/README.md @@ -0,0 +1,408 @@ +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. + + +COMPILING +--------- + +Iodine has no configure script. There are two optional features for Linux +(SELinux and systemd support) that will be enabled automatically if the +relevant header files are found in `/usr/include`. +(See script at `./src/osflags`) + +Run `make` to compile the server and client binaries. +Run `make install` to copy binaries and manpage to the destination directory. +Run `make test` to compile and run the unit tests. (Requires the `check` library) + + +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, adding -r forces DNS tunneling +even if raw UDP tunneling would be possible: + + ./iodine -f -P secretpassword t1.mydomain.com + +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` from the iodine server. + + +### MISC. INFO + +#### IPv6 +The data inside the tunnel is IPv4 only. + +The server listens to both IPv4 and IPv6 for incoming requests by default. +Use options `-4` or `-6` to only listen on one protocol. Raw mode will be +attempted on the same protocol as used for the login. + +The client can use IPv4 or IPv6 nameservers to connect to iodined. The relay +nameservers will translate between protocols automatically if needed. Use +options `-4` or `-6` to force the client to use a specific IP version for its DNS +queries. + +#### 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 + ns.io.citronna.de. + +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. + +#### Mac OS X +On Mac OS X 10.6 and later, iodine supports the native utun devices built into +the OS - use `-d utunX`. + + +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` and `PRIVATE` types +expected to provide the largest downstream bandwidth. The `PRIVATE` type uses +value 65399 in the private-use range. 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/PRIVATE` 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 + + +### Notes + +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 +). and Windows (with OpenVPN TAP32 driver, see +win32 readme file). It should be easy to port to other unix-like systems that +have 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-2014 Erik Ekman , 2006-2009 Bjorn +Andersson . Also major contributions by Anne Bezemer. + +Permission to use, copy, modify, and/or 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/doc/iodine-server.socket b/doc/iodine-server.socket index d6c57b8..485181f 100644 --- a/doc/iodine-server.socket +++ b/doc/iodine-server.socket @@ -3,6 +3,8 @@ Description=Iodine socket [Socket] ListenDatagram=53 +ListenDatagram=0.0.0.0:53 +BindIPv6Only=ipv6-only [Install] WantedBy=sockets.target diff --git a/doc/proto_00000502.txt b/doc/proto_00000502.txt index 46cf2de..2edf49a 100644 --- a/doc/proto_00000502.txt +++ b/doc/proto_00000502.txt @@ -38,7 +38,7 @@ Server replies: 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 @@ -50,17 +50,19 @@ 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: +IP Request: (for where to try raw login) 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 + BADIP if bad userid + First byte I + Then comes external IP address of iodined server + as 4 bytes (IPv4) or 16 bytes (IPv6) Upstream codec check / bounce: -Client sends: +Client sends: First byte z or Z Lots of data that should not be decoded Server replies: @@ -100,7 +102,7 @@ Client sends: 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 + 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 @@ -122,7 +124,8 @@ Server sends: 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) + r or R: Downstream encoding Raw, for PRIVATE/TXT/NULL (default for + PRIVATE and 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 @@ -181,15 +184,15 @@ 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 +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). +In NULL and PRIVATE 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 @@ -224,8 +227,8 @@ 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 ===================== @@ -249,7 +252,7 @@ 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). diff --git a/man/iodine.8 b/man/iodine.8 index ca2d72c..0eb9b9b 100644 --- a/man/iodine.8 +++ b/man/iodine.8 @@ -45,7 +45,7 @@ iodine, iodined \- tunnel IPv4 over DNS .B iodined [-h] -.B iodined [-c] [-s] [-f] [-D] [-u +.B iodined [-4] [-6] [-c] [-s] [-f] [-D] [-u .I user .B ] [-t .I chrootdir @@ -54,7 +54,9 @@ iodine, iodined \- tunnel IPv4 over DNS .B ] [-m .I mtu .B ] [-l -.I listen_ip +.I listen_ip4 +.B ] [-L +.I listen_ip6 .B ] [-p .I port .B ] [-n @@ -81,9 +83,9 @@ iodine, iodined \- tunnel IPv4 over DNS .I topdomain .SH DESCRIPTION .B iodine -lets you tunnel IPv4 data through a DNS +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 +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. @@ -110,6 +112,12 @@ Print usage info and exit. .B -f Keep running in foreground. .TP +.B -4 +Force/allow only IPv4 DNS queries +.TP +.B -6 +Force/allow only IPv6 DNS queries +.TP .B -u user Drop privileges and run as user 'user' after setting up tunnel. .TP @@ -118,10 +126,11 @@ 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. +and otherwise tunX. On Mac OS X 10.6, this can also be utunX, which will attempt +to use an utun device built into the OS. .TP .B -P password -Use 'password' to authenticate. If not used, +Use 'password' to authenticate. If not used, .B stdin will be used as input. Only the first 32 characters will be used. .TP @@ -132,12 +141,6 @@ Apply SELinux 'context' after initialization. Create 'pidfile' and write process id in it. .SS Client Options: .TP -.B -4 -Force IPv4 DNS queries -.TP -.B -6 -Force IPv6 DNS queries -.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 @@ -169,6 +172,7 @@ 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 PRIVATE , .IR TXT , .IR SRV , .IR MX , @@ -183,7 +187,10 @@ 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. +completely. The +.IR PRIVATE +type uses value 65399 (in the 'private use' range) and requires servers +implementing RFC 3597. .TP .B -O downenc Force downstream encoding type for all query type responses except NULL. @@ -240,7 +247,7 @@ 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. +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 @@ -256,18 +263,22 @@ 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. +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. +.B -l listen_ip4 +Make the server listen only on 'listen_ip4' for incoming IPv4 requests. +By default, incoming requests are accepted from all interfaces (0.0.0.0). +.TP +.B -L listen_ip6 +Make the server listen only on 'listen_ip6' for incoming IPv6 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. -If 'listen_ip' does not include localhost, this 'port' can be the same +Make the server listen on 'port' instead of 53 for traffic. +If 'listen_ip4' does not include localhost, this 'port' can be the same as 'dnsport'. .B Note: You must make sure the dns requests are forwarded to this port yourself. @@ -275,7 +286,7 @@ You must make sure the dns requests are forwarded to this port yourself. .B -n auto|external_ip The IP address to return in NS responses. Default is to return the address used as destination in the query. -If external_ip is 'auto', iodined will use externalip.net web service to +If external_ip is 'auto', iodined will use ipify.org web service to retrieve the external IP of the host and use that for NS responses. .TP .B -b dnsport @@ -303,7 +314,7 @@ file. .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 +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. @@ -311,15 +322,15 @@ must be the same on both the client and the server. .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 +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 overridden 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 +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. @@ -344,7 +355,7 @@ except to the used ssh or vpn ports. 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 +for one. The .B -P option still has precedence. .SS IODINED_PASS diff --git a/src/android_dns.h b/src/android_dns.h index dafd8ec..5a86e71 100644 --- a/src/android_dns.h +++ b/src/android_dns.h @@ -1,7 +1,25 @@ +/* + * Copyright (c) 2009 Marcel Bokhorst + * + * Permission to use, copy, modify, and/or 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_ANDROID_H__ #define __FIX_ANDROID_H__ +/* Newer android platforms can have this data already */ +#ifndef C_IN + typedef struct { unsigned id :16; unsigned rd :1; @@ -36,4 +54,6 @@ typedef struct { #define T_TXT 16 #define T_SRV 33 +#endif /* !C_IN */ + #endif diff --git a/src/base128.c b/src/base128.c index 7ce6642..245a138 100644 --- a/src/base128.c +++ b/src/base128.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2009 J.A.Bezemer@opensourcepartners.nl * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * @@ -46,7 +46,7 @@ * 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[] = +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" @@ -79,19 +79,19 @@ struct encoder return &base128_encoder; } -static int +static int base128_handles_dots() { return 0; } -static int +static int base128_blksize_raw() { return BLKSIZE_RAW; } -static int +static int base128_blksize_enc() { return BLKSIZE_ENC; @@ -113,7 +113,7 @@ base128_reverse_init() } } -static int +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. @@ -235,7 +235,7 @@ base128_decode(void *buf, size_t *buflen, const char *str, size_t slen) if (iout >= *buflen || iin + 1 >= slen || str[iin] == '\0' || str[iin + 1] == '\0') break; - ubuf[iout] = ((REV128(ustr[iin]) & 0x7f) << 1) | + ubuf[iout] = ((REV128(ustr[iin]) & 0x7f) << 1) | ((REV128(ustr[iin + 1]) & 0x40) >> 6); iin++; /* 0 used up, iin=1 */ iout++; diff --git a/src/base128.h b/src/base128.h index 235b2f9..f4c55f6 100644 --- a/src/base128.h +++ b/src/base128.h @@ -1,7 +1,7 @@ /* * Copyright (C) 2009 J.A.Bezemer@opensourcepartners.nl * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * diff --git a/src/base32.c b/src/base32.c index c25e0a4..44c0175 100644 --- a/src/base32.c +++ b/src/base32.c @@ -1,8 +1,9 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * Mostly rewritten 2009 J.A.Bezemer@opensourcepartners.nl * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * @@ -29,9 +30,9 @@ #define BLKSIZE_RAW 5 #define BLKSIZE_ENC 8 -static const char cb32[] = +static const char cb32[] = "abcdefghijklmnopqrstuvwxyz012345"; -static const char cb32_ucase[] = +static const char cb32_ucase[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345"; static unsigned char rev32[256]; static int reverse_init = 0; @@ -59,19 +60,19 @@ struct encoder return &base32_encoder; } -static int +static int base32_handles_dots() { return 0; } -static int +static int base32_blksize_raw() { return BLKSIZE_RAW; } -static int +static int base32_blksize_enc() { return BLKSIZE_ENC; @@ -108,7 +109,7 @@ b32_8to5(int in) return rev32[in]; } -static int +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. @@ -226,7 +227,7 @@ base32_decode(void *buf, size_t *buflen, const char *str, size_t slen) if (iout >= *buflen || iin + 1 >= slen || str[iin] == '\0' || str[iin + 1] == '\0') break; - ubuf[iout] = ((REV32(str[iin]) & 0x1f) << 3) | + ubuf[iout] = ((REV32(str[iin]) & 0x1f) << 3) | ((REV32(str[iin + 1]) & 0x1c) >> 2); iin++; /* 0 used up, iin=1 */ iout++; @@ -235,8 +236,8 @@ base32_decode(void *buf, size_t *buflen, const char *str, size_t 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) | + 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++; diff --git a/src/base32.h b/src/base32.h index 497ca33..83ba784 100644 --- a/src/base32.h +++ b/src/base32.h @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * diff --git a/src/base64.c b/src/base64.c index a8e9e1c..3d9a465 100644 --- a/src/base64.c +++ b/src/base64.c @@ -1,8 +1,9 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * Mostly rewritten 2009 J.A.Bezemer@opensourcepartners.nl * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * @@ -31,7 +32,7 @@ /* 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[] = +static const char cb64[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789+"; static unsigned char rev64[256]; static int reverse_init = 0; @@ -59,19 +60,19 @@ struct encoder return &base64_encoder; } -static int +static int base64_handles_dots() { return 0; } -static int +static int base64_blksize_raw() { return BLKSIZE_RAW; } -static int +static int base64_blksize_enc() { return BLKSIZE_ENC; @@ -93,7 +94,7 @@ base64_reverse_init() } } -static int +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. @@ -181,7 +182,7 @@ base64_decode(void *buf, size_t *buflen, const char *str, size_t slen) if (iout >= *buflen || iin + 1 >= slen || str[iin] == '\0' || str[iin + 1] == '\0') break; - ubuf[iout] = ((REV64(str[iin]) & 0x3f) << 2) | + ubuf[iout] = ((REV64(str[iin]) & 0x3f) << 2) | ((REV64(str[iin + 1]) & 0x30) >> 4); iin++; /* 0 used up, iin=1 */ iout++; @@ -189,7 +190,7 @@ base64_decode(void *buf, size_t *buflen, const char *str, size_t slen) if (iout >= *buflen || iin + 1 >= slen || str[iin] == '\0' || str[iin + 1] == '\0') break; - ubuf[iout] = ((REV64(str[iin]) & 0x0f) << 4) | + ubuf[iout] = ((REV64(str[iin]) & 0x0f) << 4) | ((REV64(str[iin + 1]) & 0x3c) >> 2); iin++; /* 1 used up, iin=2 */ iout++; diff --git a/src/base64.h b/src/base64.h index d550cf3..8ce4742 100644 --- a/src/base64.h +++ b/src/base64.h @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * diff --git a/src/client.c b/src/client.c index f94f5e7..15e966b 100644 --- a/src/client.c +++ b/src/client.c @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * @@ -23,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -34,10 +36,10 @@ #ifdef WINDOWS32 #include "windows.h" #else +#include #ifdef ANDROID #include "android_dns.h" #endif -#include #ifdef DARWIN #define BIND_8_COMPAT #include @@ -65,7 +67,9 @@ static int running; static const char *password; static struct sockaddr_storage nameserv; -static struct sockaddr_in raw_serv; +static int nameserv_len; +static struct sockaddr_storage raw_serv; +static int raw_serv_len; static const char *topdomain; static uint16_t rand_seed; @@ -95,10 +99,10 @@ 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; @@ -155,6 +159,7 @@ void client_set_nameserver(struct sockaddr_storage *addr, int addrlen) { memcpy(&nameserv, addr, addrlen); + nameserv_len = addrlen; } void @@ -169,11 +174,13 @@ client_set_password(const char *cp) password = cp; } -void -set_qtype(char *qtype) +int +client_set_qtype(char *qtype) { if (!strcasecmp(qtype, "NULL")) do_qtype = T_NULL; + else if (!strcasecmp(qtype, "PRIVATE")) + do_qtype = T_PRIVATE; else if (!strcasecmp(qtype, "CNAME")) do_qtype = T_CNAME; else if (!strcasecmp(qtype, "A")) @@ -184,14 +191,16 @@ set_qtype(char *qtype) do_qtype = T_SRV; else if (!strcasecmp(qtype, "TXT")) do_qtype = T_TXT; + return (do_qtype == T_UNSET); } char * -get_qtype() +client_get_qtype() { char *c = "UNDEFINED"; if (do_qtype == T_NULL) c = "NULL"; + else if (do_qtype == T_PRIVATE) c = "PRIVATE"; else if (do_qtype == T_CNAME) c = "CNAME"; else if (do_qtype == T_A) c = "A"; else if (do_qtype == T_MX) c = "MX"; @@ -202,7 +211,7 @@ get_qtype() } void -set_downenc(char *encoding) +client_set_downenc(char *encoding) { if (!strcasecmp(encoding, "base32")) downenc = 'T'; @@ -216,7 +225,7 @@ set_downenc(char *encoding) downenc = 'R'; } -void +void client_set_selecttimeout(int select_timeout) { selecttimeout = select_timeout; @@ -238,7 +247,7 @@ client_set_hostname_maxlen(int i) const char * client_get_raw_addr() { - return inet_ntoa(raw_serv.sin_addr); + return format_addr(&raw_serv, raw_serv_len); } static void @@ -268,7 +277,7 @@ send_query(int fd, char *hostname) fprintf(stderr, " Sendquery: id %5d name[0] '%c'\n", q.id, hostname[0]); #endif - sendto(fd, packet, len, 0, (struct sockaddr*)&nameserv, sizeof(nameserv)); + sendto(fd, packet, len, 0, (struct sockaddr*)&nameserv, nameserv_len); /* There are DNS relays that time out quickly but don't send anything back on timeout. @@ -335,7 +344,7 @@ 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); @@ -368,7 +377,7 @@ send_chunk(int fd) /* 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 */ @@ -382,7 +391,7 @@ send_chunk(int fd) 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, @@ -397,12 +406,12 @@ 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 @@ -568,7 +577,7 @@ read_dns_withq(int dns_fd, int tun_fd, char *buf, int buflen, struct query *q) int r; addrlen = sizeof(from); - if ((r = recvfrom(dns_fd, data, sizeof(data), 0, + if ((r = recvfrom(dns_fd, data, sizeof(data), 0, (struct sockaddr*)&from, &addrlen)) < 0) { warn("recvfrom"); return -1; @@ -590,9 +599,9 @@ read_dns_withq(int dns_fd, int tun_fd, char *buf, int buflen, struct query *q) /* * 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 */ @@ -1133,11 +1142,11 @@ client_tunnel(int tun_fd, int dns_fd) warnx("No downstream data received in 60 seconds, shutting down."); running = 0; } - + if (running == 0) break; - if (i < 0) + if (i < 0) err(1, "select"); if (i == 0) { @@ -1180,7 +1189,7 @@ client_tunnel(int tun_fd, int dns_fd) if (FD_ISSET(dns_fd, &fds)) { if (tunnel_dns(tun_fd, dns_fd) <= 0) continue; - } + } } } @@ -1198,7 +1207,7 @@ send_login(int fd, char *login, int len) data[17] = (rand_seed >> 8) & 0xff; data[18] = (rand_seed >> 0) & 0xff; - + rand_seed++; send_packet(fd, 'l', data, sizeof(data)); @@ -1237,23 +1246,23 @@ 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 +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; @@ -1261,7 +1270,7 @@ send_version(int fd, uint32_t version) data[4] = (rand_seed >> 8) & 0xff; data[5] = (rand_seed >> 0) & 0xff; - + rand_seed++; send_packet(fd, 'v', data, sizeof(data)); @@ -1272,7 +1281,7 @@ 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); @@ -1296,7 +1305,7 @@ 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); @@ -1332,7 +1341,7 @@ 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); @@ -1411,7 +1420,7 @@ handshake_version(int dns_fd, int *seed) PROTOCOL_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", + warnx("You use protocol v 0x%08x, server uses v 0x%08x. Giving up", PROTOCOL_VERSION, payload); return 1; } else if (strncmp("VFUL", in, 4) == 0) { @@ -1420,7 +1429,7 @@ handshake_version(int dns_fd, int *seed) } } else if (read > 0) warnx("did not receive proper login challenge"); - + fprintf(stderr, "Retrying version check...\n"); } warnx("couldn't connect to server (maybe other -T options will work)"); @@ -1439,7 +1448,7 @@ handshake_login(int dns_fd, int seed) int read; login_calculate(login, 16, password, seed); - + for (i=0; running && i<5 ;i++) { send_login(dns_fd, login, 16); @@ -1484,8 +1493,10 @@ handshake_raw_udp(int dns_fd, int seed) int i; int r; int len; - unsigned remoteaddr = 0; - struct in_addr server; + int got_addr; + + memset(&raw_serv, 0, sizeof(raw_serv)); + got_addr = 0; fprintf(stderr, "Testing raw UDP data to the server (skip with -r)"); for (i=0; running && i<3 ;i++) { @@ -1495,15 +1506,23 @@ handshake_raw_udp(int dns_fd, int seed) len = handshake_waitdns(dns_fd, in, sizeof(in), 'i', 'I', i+1); 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); + /* Received IPv4 address */ + struct sockaddr_in *raw4_serv = (struct sockaddr_in *) &raw_serv; + raw4_serv->sin_family = AF_INET; + memcpy(&raw4_serv->sin_addr, &in[1], sizeof(struct in_addr)); + raw4_serv->sin_port = htons(53); + raw_serv_len = sizeof(struct sockaddr_in); + got_addr = 1; + break; + } + if (len == 17 && in[0] == 'I') { + /* Received IPv6 address */ + struct sockaddr_in6 *raw6_serv = (struct sockaddr_in6 *) &raw_serv; + raw6_serv->sin6_family = AF_INET6; + memcpy(&raw6_serv->sin6_addr, &in[1], sizeof(struct in6_addr)); + raw6_serv->sin6_port = htons(53); + raw_serv_len = sizeof(struct sockaddr_in6); + got_addr = 1; break; } @@ -1513,21 +1532,15 @@ handshake_raw_udp(int dns_fd, int seed) fprintf(stderr, "\n"); if (!running) return 0; - - if (!remoteaddr) { + + if (!got_addr) { 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)); + fprintf(stderr, "Server is at %s, trying raw login: ", format_addr(&raw_serv, raw_serv_len)); 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 + /* 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++) { @@ -1535,7 +1548,7 @@ handshake_raw_udp(int dns_fd, int seed) tv.tv_usec = 0; send_raw_udp_login(dns_fd, userid, seed); - + FD_ZERO(&fds); FD_SET(dns_fd, &fds); @@ -1548,7 +1561,7 @@ handshake_raw_udp(int dns_fd, int seed) 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 + && RAW_HDR_GET_CMD(in) == RAW_HDR_CMD_LOGIN && memcmp(&in[RAW_HDR_LEN], hash, sizeof(hash)) == 0) { fprintf(stderr, "OK\n"); @@ -1559,7 +1572,7 @@ handshake_raw_udp(int dns_fd, int seed) fprintf(stderr, "."); fflush(stderr); } - + fprintf(stderr, "failed\n"); return 0; } @@ -1788,7 +1801,7 @@ handshake_downenc_autodetect(int dns_fd) int base64uok = 0; int base128ok = 0; - if (do_qtype == T_NULL) { + if (do_qtype == T_NULL || do_qtype == T_PRIVATE) { /* no other choice than raw */ fprintf(stderr, "No alternative downstream codec available, using default (Raw)\n"); return ' '; @@ -1842,13 +1855,14 @@ handshake_qtypetest(int dns_fd, int timeout) int trycodec; int k; - if (do_qtype == T_NULL) + if (do_qtype == T_NULL || do_qtype == T_PRIVATE) 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. */ + byte values can be returned, which is needed for NULL/PRIVATE + to work. */ send_downenctest(dns_fd, trycodec, 1, NULL, 0); @@ -1873,11 +1887,12 @@ 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; + case 1: return T_PRIVATE; + case 2: return T_TXT; + case 3: return T_SRV; + case 4: return T_MX; + case 5: return T_CNAME; + case 6: return T_A; } return T_UNSET; } @@ -1920,7 +1935,7 @@ handshake_qtype_autodetect(int dns_fd) highestworking = qtypenum; #if 0 fprintf(stderr, " Type %s timeout %d works\n", - get_qtype(), timeout); + client_get_qtype(), timeout); #endif break; /* try others with longer timeout */ @@ -2027,7 +2042,7 @@ handshake_switch_codec(int dns_fd, int bits) 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); if (read > 0) { @@ -2054,7 +2069,7 @@ handshake_switch_codec(int dns_fd, int bits) fprintf(stderr, "No reply from server on codec switch. "); -codec_revert: +codec_revert: fprintf(stderr, "Falling back to upstream codec %s\n", dataenc->name); } @@ -2106,7 +2121,7 @@ handshake_switch_downenc(int dns_fd) fprintf(stderr, "No reply from server on codec switch. "); -codec_revert: +codec_revert: fprintf(stderr, "Falling back to downstream codec Base32\n"); } @@ -2148,7 +2163,7 @@ handshake_try_lazy(int dns_fd) fprintf(stderr, "No reply from server on lazy switch. "); -codec_revert: +codec_revert: fprintf(stderr, "Falling back to legacy mode\n"); lazymode = 0; selecttimeout = 1; @@ -2265,7 +2280,7 @@ handshake_autoprobe_fragsize(int dns_fd) int max_fragsize; max_fragsize = 0; - fprintf(stderr, "Autoprobing max downstream fragment size... (skip with -m fragsize)\n"); + 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++) { @@ -2273,7 +2288,7 @@ handshake_autoprobe_fragsize(int dns_fd) send_fragsize_probe(dns_fd, proposed_fragsize); read = handshake_waitdns(dns_fd, in, sizeof(in), 'r', 'R', 1); - + if (read > 0) { /* We got a reply */ if (fragsize_check(in, read, proposed_fragsize, &max_fragsize) == 1) @@ -2319,7 +2334,7 @@ handshake_autoprobe_fragsize(int dns_fd) 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_NULL || do_qtype == T_PRIVATE || 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"); @@ -2382,7 +2397,7 @@ client_handshake(int dns_fd, int raw_mode, int autodetect_frag_size, int fragsiz } } - fprintf(stderr, "Using DNS type %s queries\n", get_qtype()); + fprintf(stderr, "Using DNS type %s queries\n", client_get_qtype()); r = handshake_version(dns_fd, &seed); if (r) { diff --git a/src/client.h b/src/client.h index fc5625d..c2493f1 100644 --- a/src/client.h +++ b/src/client.h @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * @@ -26,9 +27,9 @@ const char *client_get_raw_addr(); void client_set_nameserver(struct sockaddr_storage *, int); 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); +int client_set_qtype(char *qtype); +char *client_get_qtype(); +void client_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); diff --git a/src/common.c b/src/common.c index 909d1f1..2bbb954 100644 --- a/src/common.c +++ b/src/common.c @@ -1,7 +1,8 @@ -/* Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman +/* Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * Copyright (c) 2007 Albert Lee . * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * @@ -64,7 +65,7 @@ const unsigned char raw_header[RAW_HDR_LEN] = { 0x10, 0xd1, 0x9e, 0x00 }; static int daemon(int nochdir, int noclose) { int fd, i; - + switch (fork()) { case 0: break; @@ -73,15 +74,15 @@ static int daemon(int nochdir, int noclose) 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++) { @@ -153,8 +154,8 @@ get_addr(char *host, int port, int addr_family, int flags, struct sockaddr_stora memset(&hints, 0, sizeof(hints)); hints.ai_family = addr_family; -#ifdef WINDOWS32 - /* AI_ADDRCONFIG misbehaves on windows */ +#if defined(WINDOWS32) || defined(OPENBSD) + /* AI_ADDRCONFIG misbehaves on windows, and does not exist in OpenBSD */ hints.ai_flags = flags; #else hints.ai_flags = AI_ADDRCONFIG | flags; @@ -173,10 +174,16 @@ get_addr(char *host, int port, int addr_family, int flags, struct sockaddr_stora return res; } -int +int open_dns(struct sockaddr_storage *sockaddr, size_t sockaddr_len) { - int flag = 1; + return open_dns_opt(sockaddr, sockaddr_len, -1); +} + +int +open_dns_opt(struct sockaddr_storage *sockaddr, size_t sockaddr_len, int v6only) +{ + int flag; int fd; if ((fd = socket(sockaddr->ss_family, SOCK_DGRAM, IPPROTO_UDP)) < 0) { @@ -190,10 +197,13 @@ open_dns(struct sockaddr_storage *sockaddr, size_t sockaddr_len) 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)); + fd_set_close_on_exec(fd); #endif + if (sockaddr->ss_family == AF_INET6 && v6only >= 0) { + setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (const void*) &v6only, sizeof(v6only)); + } + #ifdef IP_OPT_DONT_FRAG /* Set dont-fragment ip header flag */ flag = DONT_FRAG_VALUE; @@ -294,7 +304,7 @@ read_password(char *buf, size_t len) tcgetattr(0, &tp); old = tp; - + tp.c_lflag &= (~ECHO); tcsetattr(0, TCSANOW, &tp); #else @@ -320,7 +330,7 @@ read_password(char *buf, size_t len) fprintf(stderr, "\n"); #ifndef WINDOWS32 - tcsetattr(0, TCSANOW, &old); + tcsetattr(0, TCSANOW, &old); #endif strncpy(buf, pwd, len); @@ -328,20 +338,64 @@ read_password(char *buf, size_t len) } int -check_topdomain(char *str) +check_topdomain(char *str, char **errormsg) { - int i; + int i; + int dots = 0; + int chunklen = 0; - if(str[0] == '.') /* special case */ - return 1; + if (strlen(str) < 3) { + if (errormsg) *errormsg = "Too short (< 3)"; + return 1; + } + if (strlen(str) > 128) { + if (errormsg) *errormsg = "Too long (> 128)"; + 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 (str[0] == '.') { + if (errormsg) *errormsg = "Starts with a dot"; + return 1; + } + + for( i = 0; i < strlen(str); i++) { + if(str[i] == '.') { + dots++; + if (chunklen == 0) { + if (errormsg) *errormsg = "Consecutive dots"; + return 1; + } + if (chunklen > 63) { + if (errormsg) *errormsg = "Too long domain part (> 63)"; + return 1; + } + chunklen = 0; + } else { + chunklen++; + } + if( (str[i] >= 'a' && str[i] <= 'z') || (str[i] >= 'A' && str[i] <= 'Z') || + isdigit(str[i]) || str[i] == '-' || str[i] == '.' ) { + continue; + } else { + if (errormsg) *errormsg = "Contains illegal character (allowed: [a-zA-Z0-9-.])"; + return 1; + } + } + + if (dots == 0) { + if (errormsg) *errormsg = "No dots"; + return 1; + } + if (chunklen == 0) { + if (errormsg) *errormsg = "Ends with a dot"; + return 1; + } + if (chunklen > 63) { + if (errormsg) *errormsg = "Too long domain part (> 63)"; + return 1; + } + + return 0; } #if defined(WINDOWS32) || defined(ANDROID) @@ -354,31 +408,43 @@ inet_aton(const char *cp, struct in_addr *inp) } #endif +void +vwarn(const char *fmt, va_list list) +{ + if (fmt) vfprintf(stderr, fmt, list); +#ifndef ANDROID + if (errno == 0) { + fprintf(stderr, ": WSA error %d\n", WSAGetLastError()); + } else { + fprintf(stderr, ": %s\n", strerror(errno)); + } +#endif +} + void warn(const char *fmt, ...) { va_list list; va_start(list, fmt); - if (fmt) fprintf(stderr, fmt, list); -#ifndef ANDROID - if (errno == 0) { - fprintf(stderr, ": WSA error %d\n", WSAGetLastError()); - } else { - fprintf(stderr, ": %s\n", strerror(errno)); - } -#endif + vwarn(fmt, list); va_end(list); } +void +vwarnx(const char *fmt, va_list list) +{ + if (fmt) vfprintf(stderr, fmt, list); + fprintf(stderr, "\n"); +} + void warnx(const char *fmt, ...) { va_list list; va_start(list, fmt); - if (fmt) fprintf(stderr, fmt, list); - fprintf(stderr, "\n"); + vwarnx(fmt, list); va_end(list); } @@ -388,7 +454,7 @@ err(int eval, const char *fmt, ...) va_list list; va_start(list, fmt); - warn(fmt, list); + vwarn(fmt, list); va_end(list); exit(eval); } @@ -399,7 +465,7 @@ errx(int eval, const char *fmt, ...) va_list list; va_start(list, fmt); - warnx(fmt, list); + vwarnx(fmt, list); va_end(list); exit(eval); } @@ -420,3 +486,22 @@ int recent_seqno(int ourseqno, int gotseqno) } return 0; } + +#ifndef WINDOWS32 +/* Set FD_CLOEXEC flag on file descriptor. + * This stops it from being inherited by system() calls. + */ +void +fd_set_close_on_exec(int fd) +{ + int flags; + + flags = fcntl(fd, F_GETFD); + if (flags == -1) + err(4, "Failed to get fd flags"); + flags |= FD_CLOEXEC; + if (fcntl(fd, F_SETFD, flags) == -1) + err(4, "Failed to set fd flags"); +} +#endif + diff --git a/src/common.h b/src/common.h index bbadc9f..2de23e9 100644 --- a/src/common.h +++ b/src/common.h @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2015 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * @@ -52,14 +53,6 @@ extern const unsigned char raw_header[RAW_HDR_LEN]; #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 @@ -74,10 +67,12 @@ extern const unsigned char raw_header[RAW_HDR_LEN]; # define DONT_FRAG_VALUE 1 #endif +#define T_PRIVATE 65399 +/* Undefined RR type; "private use" range, see http://www.bind9.net/dns-parameters */ #define T_UNSET 65432 -/* Unused RR type; "private use" range, see http://www.bind9.net/dns-parameters */ +/* Unused RR type, never actually sent */ -struct packet +struct packet { int len; /* Total packet length */ int sentlen; /* Length of chunk currently transmitted */ @@ -92,16 +87,17 @@ struct query { unsigned short type; unsigned short rcode; unsigned short id; - struct in_addr destination; + struct sockaddr_storage destination; + socklen_t dest_len; struct sockaddr_storage from; - int fromlen; + socklen_t fromlen; unsigned short id2; - struct sockaddr from2; - int fromlen2; + struct sockaddr_storage from2; + socklen_t fromlen2; }; enum connection { - CONN_RAW_UDP, + CONN_RAW_UDP = 0, CONN_DNS_NULL, CONN_MAX }; @@ -110,6 +106,7 @@ void check_superuser(void (*usage_fn)(void)); char *format_addr(struct sockaddr_storage *sockaddr, int sockaddr_len); int get_addr(char *, int, int, int, struct sockaddr_storage *); int open_dns(struct sockaddr_storage *, size_t); +int open_dns_opt(struct sockaddr_storage *sockaddr, size_t sockaddr_len, int v6only); int open_dns_from_host(char *host, int port, int addr_family, int flags); void close_dns(int); @@ -120,7 +117,7 @@ void do_pidfile(char *); void read_password(char*, size_t); -int check_topdomain(char *); +int check_topdomain(char *, char **); #if defined(WINDOWS32) || defined(ANDROID) #ifndef ANDROID @@ -135,4 +132,8 @@ void warnx(const char *fmt, ...); int recent_seqno(int , int); +#ifndef WINDOWS32 +void fd_set_close_on_exec(int fd); +#endif + #endif diff --git a/src/dns.c b/src/dns.c index dccea3a..53ba27e 100644 --- a/src/dns.c +++ b/src/dns.c @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * @@ -24,14 +25,12 @@ #include #include #include +#include #include #ifdef WINDOWS32 #include "windows.h" #else -#ifdef ANDROID -#include "android_dns.h" -#endif #include #ifdef DARWIN #define BIND_8_COMPAT @@ -40,6 +39,9 @@ #include #include #include +#ifdef ANDROID +#include "android_dns.h" +#endif #endif @@ -64,9 +66,9 @@ dns_encode(char *buf, size_t buflen, struct query *q, qr_t qr, char *data, size_ return 0; memset(buf, 0, buflen); - + header = (HEADER*)buf; - + header->id = htons(q->id); header->qr = (qr == QR_ANSWER); header->opcode = 0; @@ -80,7 +82,7 @@ dns_encode(char *buf, size_t buflen, struct query *q, qr_t qr, char *data, size_ switch (qr) { case QR_ANSWER: header->qdcount = htons(1); - + name = 0xc000 | ((p - buf) & 0x3fff); /* Question section */ @@ -99,7 +101,7 @@ dns_encode(char *buf, size_t buflen, struct query *q, qr_t qr, char *data, size_ int namelen; CHECKLEN(10); - putshort(&p, name); + putshort(&p, name); if (q->type == T_A) /* answer CNAME to A question */ putshort(&p, T_CNAME); @@ -129,7 +131,7 @@ dns_encode(char *buf, size_t buflen, struct query *q, qr_t qr, char *data, size_ ancnt = 1; while (1) { CHECKLEN(10); - putshort(&p, name); + putshort(&p, name); putshort(&p, q->type); putshort(&p, C_IN); putlong(&p, 0); /* TTL */ @@ -164,7 +166,7 @@ dns_encode(char *buf, size_t buflen, struct query *q, qr_t qr, char *data, size_ int txtlen; CHECKLEN(10); - putshort(&p, name); + putshort(&p, name); putshort(&p, q->type); putshort(&p, C_IN); putlong(&p, 0); /* TTL */ @@ -181,7 +183,7 @@ dns_encode(char *buf, size_t buflen, struct query *q, qr_t qr, char *data, size_ /* NULL has raw binary data */ CHECKLEN(10); - putshort(&p, name); + putshort(&p, name); putshort(&p, q->type); putshort(&p, C_IN); putlong(&p, 0); /* TTL */ @@ -200,7 +202,7 @@ dns_encode(char *buf, size_t buflen, struct query *q, qr_t qr, char *data, size_ /* Note that iodined also uses this for forward queries */ header->qdcount = htons(1); - + datalen = MIN(datalen, buflen - (p - buf)); putname(&p, datalen, data); @@ -223,7 +225,7 @@ dns_encode(char *buf, size_t buflen, struct query *q, qr_t qr, char *data, size_ break; } - + len = p - buf; return len; @@ -247,9 +249,9 @@ dns_encode_ns_response(char *buf, size_t buflen, struct query *q, char *topdomai return 0; memset(buf, 0, buflen); - + header = (HEADER*)buf; - + header->id = htons(q->id); header->qr = 1; header->opcode = 0; @@ -336,9 +338,9 @@ dns_encode_a_response(char *buf, size_t buflen, struct query *q) return 0; memset(buf, 0, buflen); - + header = (HEADER*)buf; - + header->id = htons(q->id); header->qr = 1; header->opcode = 0; @@ -406,11 +408,11 @@ dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, siz short qdcount; short ancount; uint32_t ttl; - short class; - short type; + unsigned short class; + unsigned short type; char *data; - short rlen; - int id; + unsigned short rlen; + int id; int rv; q->id2 = 0; @@ -418,9 +420,9 @@ dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, siz header = (HEADER*)packet; /* Reject short packets */ - if (packetlen < sizeof(HEADER)) + if (packetlen < sizeof(HEADER)) return 0; - + if (header->qr != qr) { warnx("header->qr does not match the requested qr"); return -1; @@ -429,13 +431,13 @@ dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, siz 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) + if (q != NULL) q->rcode = header->rcode; switch (qr) { @@ -445,7 +447,7 @@ dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, siz return -1; } - if (q != NULL) + if (q != NULL) q->id = id; /* Read name even if no answer, to give better error message */ @@ -453,14 +455,14 @@ dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, siz 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 @@ -469,7 +471,7 @@ dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, siz } /* Here type is still the question type */ - if (type == T_NULL) { + if (type == T_NULL || type == T_PRIVATE) { /* Assume that first answer is what we wanted */ readname(packet, packetlen, &data, name, sizeof(name)); CHECKLEN(10); @@ -511,7 +513,7 @@ dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, siz */ char names[250][QUERY_NAME_SIZE]; char *rdatastart; - short pref; + unsigned short pref; int i; int offset; @@ -541,7 +543,7 @@ dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, siz names[pref / 10 - 1][QUERY_NAME_SIZE-1] = '\0'; } - /* always trust rlen, not name encoding */ + /* always trust rlen, not name encoding */ data = rdatastart + rlen; CHECKLEN(0); } diff --git a/src/dns.h b/src/dns.h index 72d4fe9..b5d83b2 100644 --- a/src/dns.h +++ b/src/dns.h @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * diff --git a/src/encoding.c b/src/encoding.c index 5be4089..dc8ca30 100644 --- a/src/encoding.c +++ b/src/encoding.c @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * @@ -23,8 +24,8 @@ #include "encoding.h" int -build_hostname(char *buf, size_t buflen, - const char *data, const size_t datalen, +build_hostname(char *buf, size_t buflen, + const char *data, const size_t datalen, const char *topdomain, struct encoder *encoder, int maxlen) { size_t space; @@ -37,7 +38,7 @@ build_hostname(char *buf, size_t buflen, space -= (space / 57); /* space for dots */ memset(buf, 0, buflen); - + encoder->encode(buf, &space, data, datalen); if (!encoder->places_dots()) @@ -48,7 +49,7 @@ build_hostname(char *buf, size_t buflen, /* move b back one step to see if the dot is there */ b--; - if (*b != '.') + if (*b != '.') *++b = '.'; b++; /* move b ahead of the string so we can copy to it */ @@ -66,7 +67,7 @@ unpack_data(char *buf, size_t buflen, char *data, size_t datalen, struct encoder return enc->decode(buf, &buflen, data, datalen); } -int +int inline_dotify(char *buf, size_t buflen) { unsigned dots; @@ -104,7 +105,7 @@ inline_dotify(char *buf, size_t buflen) return total; } -int +int inline_undotify(char *buf, size_t len) { unsigned pos; @@ -127,7 +128,7 @@ inline_undotify(char *buf, size_t len) *writer++ = *reader++; pos++; } - + /* return new length of string */ return len - dots; } diff --git a/src/encoding.h b/src/encoding.h index 7ddf6e0..abb82da 100644 --- a/src/encoding.h +++ b/src/encoding.h @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * diff --git a/src/fw_query.c b/src/fw_query.c index 3aa12b4..aafa99e 100644 --- a/src/fw_query.c +++ b/src/fw_query.c @@ -1,7 +1,7 @@ /* - * Copyright (c) 2008 Erik Ekman + * Copyright (c) 2008-2014 Erik Ekman * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * @@ -33,9 +33,9 @@ void fw_query_init() 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) + if (fwq_ix >= FW_QUERY_CACHE_SIZE) fwq_ix = 0; } diff --git a/src/fw_query.h b/src/fw_query.h index f274a5a..0aa8778 100644 --- a/src/fw_query.h +++ b/src/fw_query.h @@ -1,7 +1,7 @@ /* - * Copyright (c) 2008 Erik Ekman + * Copyright (c) 2008-2014 Erik Ekman * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * diff --git a/src/iodine.c b/src/iodine.c index 5f0fd63..bb33bed 100644 --- a/src/iodine.c +++ b/src/iodine.c @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * @@ -56,11 +57,17 @@ static char *__progname; #define PASSWORD_ENV_VAR "IODINE_PASS" static void -sighandler(int sig) +sighandler(int sig) { client_stop(); } +#if defined(__GNUC__) || defined(__clang__) +/* mark as no return to help some compilers to avoid warnings + * about use of uninitialized variables */ +static void usage() __attribute__((noreturn)); +#endif + static void usage() { extern char *__progname; @@ -80,7 +87,7 @@ help() { "[-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, " -T force dns type: NULL, PRIVATE, 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"); @@ -118,6 +125,7 @@ main(int argc, char **argv) { char *nameserv_host; char *topdomain; + char *errormsg; #ifndef WINDOWS32 struct passwd *pw; #endif @@ -147,6 +155,7 @@ main(int argc, char **argv) nameserv_host = NULL; topdomain = NULL; + errormsg = NULL; #ifndef WINDOWS32 pw = NULL; #endif @@ -174,7 +183,7 @@ main(int argc, char **argv) srand((unsigned) time(NULL)); client_init(); - + #if !defined(BSD) && !defined(__GLIBC__) __progname = strrchr(argv[0], '/'); if (__progname == NULL) @@ -222,9 +231,9 @@ main(int argc, char **argv) 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)); + memset(optarg, 0, strlen(optarg)); break; case 'm': autodetect_frag_size = 0; @@ -242,12 +251,13 @@ main(int argc, char **argv) break; case 'F': pidfile = optarg; - break; + break; case 'T': - set_qtype(optarg); + if (client_set_qtype(optarg)) + errx(5, "Invalid query type '%s'", optarg); break; case 'O': /* not -D, is Debug in server */ - set_downenc(optarg); + client_set_downenc(optarg); break; case 'L': lazymode = atoi(optarg); @@ -268,7 +278,7 @@ main(int argc, char **argv) /* NOTREACHED */ } } - + check_superuser(usage); argc -= optind; @@ -305,16 +315,10 @@ main(int argc, char **argv) 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"); + if(check_topdomain(topdomain, &errormsg)) { + warnx("Invalid topdomain: %s", errormsg); usage(); /* NOTREACHED */ } @@ -323,7 +327,7 @@ main(int argc, char **argv) client_set_lazymode(lazymode); client_set_topdomain(topdomain); client_set_hostname_maxlen(hostname_maxlen); - + if (username != NULL) { #ifndef WINDOWS32 if ((pw = getpwnam(username)) == NULL) { @@ -333,21 +337,21 @@ main(int argc, char **argv) } #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_from_host(NULL, 53, nameservaddr.ss_family, AI_PASSIVE)) < 0) { + if ((dns_fd = open_dns_from_host(NULL, 0, nameservaddr.ss_family, AI_PASSIVE)) < 0) { retval = 1; goto cleanup2; } @@ -366,22 +370,22 @@ main(int argc, char **argv) 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) + 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]; diff --git a/src/iodined.c b/src/iodined.c index 62ab7b9..e635c41 100644 --- a/src/iodined.c +++ b/src/iodined.c @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2015 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * @@ -22,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -74,6 +76,18 @@ WSADATA wsa_data; #define PASSWORD_ENV_VAR "IODINED_PASS" +#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 + +#ifndef IPV6_RECVPKTINFO +#define IPV6_RECVPKTINFO IPV6_PKTINFO +#endif + static int running = 1; static char *topdomain; static char password[33]; @@ -97,24 +111,43 @@ static int debug; 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); +/* Struct with IPv4 and IPv6 file descriptors. + * Need to be passed on down to tunneling code since we can get a + * packet on one fd meant for a user on the other. + */ +struct dnsfd { + int v4fd; + int v6fd; +}; -/* Ask externalip.net webservice to get external ip */ -static int get_external_ip(struct in_addr *ip) +static int read_dns(int fd, struct dnsfd *dns_fds, int tun_fd, struct query *q); +static void write_dns(int fd, struct query *q, char *data, int datalen, char downenc); +static void handle_full_packet(int tun_fd, struct dnsfd *dns_fds, int userid); + +static int +get_dns_fd(struct dnsfd *fds, struct sockaddr_storage *addr) +{ + if (addr->ss_family == AF_INET6) { + return fds->v6fd; + } + return fds->v4fd; +} + +/* Ask ipify.org webservice to get external ip */ +static int +get_external_ip(struct in_addr *ip) { int sock; struct addrinfo *addr; int res; - const char *getstr = "GET /ip/ HTTP/1.0\r\n" + const char *getstr = "GET / HTTP/1.0\r\n" /* HTTP 1.0 to avoid chunked transfer coding */ - "Host: api.externalip.net\r\n\r\n"; + "Host: api.ipify.org\r\n\r\n"; char buf[512]; char *b; int len; - res = getaddrinfo("api.externalip.net", "80", NULL, &addr); + res = getaddrinfo("api.ipify.org", "80", NULL, &addr); if (res < 0) return 1; sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); @@ -154,7 +187,7 @@ static int get_external_ip(struct in_addr *ip) } static void -sigint(int sig) +sigint(int sig) { running = 0; } @@ -176,15 +209,14 @@ syslog(int a, const char *str, ...) } #endif +/* This will not check that user has passed login challenge */ 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; + return 1; } if (!users[userid].active || users[userid].disabled) { return 1; @@ -198,8 +230,41 @@ check_user_and_ip(int userid, struct query *q) return 0; } - tempin = (struct sockaddr_in *) &(q->from); - return memcmp(&(users[userid].host), &(tempin->sin_addr), sizeof(struct in_addr)); + if (q->from.ss_family != users[userid].host.ss_family) { + return 1; + } + /* Check IPv4 */ + if (q->from.ss_family == AF_INET) { + struct sockaddr_in *expected, *received; + + expected = (struct sockaddr_in *) &(users[userid].host); + received = (struct sockaddr_in *) &(q->from); + return memcmp(&(expected->sin_addr), &(received->sin_addr), sizeof(struct in_addr)); + } + /* Check IPv6 */ + if (q->from.ss_family == AF_INET6) { + struct sockaddr_in6 *expected, *received; + + expected = (struct sockaddr_in6 *) &(users[userid].host); + received = (struct sockaddr_in6 *) &(q->from); + return memcmp(&(expected->sin6_addr), &(received->sin6_addr), sizeof(struct in6_addr)); + } + /* Unknown address family */ + return 1; +} + +/* This checks that user has passed normal (non-raw) login challenge */ +static int +check_authenticated_user_and_ip(int userid, struct query *q) +{ + int res = check_user_and_ip(userid, q); + if (res) + return res; + + if (!users[userid].authenticated) + return 1; + + return 0; } static void @@ -219,7 +284,7 @@ send_raw(int fd, char *buf, int buflen, int user, int cmd, struct query *q) packet[RAW_HDR_CMD] = cmd | (user & 0x0F); if (debug >= 2) { - fprintf(stderr, "TX-raw: client %s, cmd %d, %d bytes\n", + fprintf(stderr, "TX-raw: client %s, cmd %d, %d bytes\n", format_addr(&q->from, q->fromlen), cmd, len); } @@ -554,12 +619,12 @@ send_chunk_or_dataless(int dns_fd, int userid, struct query *q) 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) | + 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, + 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); @@ -602,7 +667,7 @@ send_chunk_or_dataless(int dns_fd, int userid, struct query *q) } static int -tunnel_tun(int tun_fd, int dns_fd) +tunnel_tun(int tun_fd, struct dnsfd *dns_fds) { unsigned long outlen; struct ip *header; @@ -613,7 +678,7 @@ tunnel_tun(int tun_fd, int dns_fd) 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); @@ -637,13 +702,17 @@ tunnel_tun(int tun_fd, int dns_fd) start_new_outpacket(userid, out, outlen); /* Start sending immediately if query is waiting */ - if (users[userid].q_sendrealsoon.id != 0) + if (users[userid].q_sendrealsoon.id != 0) { + int dns_fd = get_dns_fd(dns_fds, &users[userid].q_sendrealsoon.from); send_chunk_or_dataless(dns_fd, userid, &users[userid].q_sendrealsoon); - else if (users[userid].q.id != 0) + } else if (users[userid].q.id != 0) { + int dns_fd = get_dns_fd(dns_fds, &users[userid].q.from); send_chunk_or_dataless(dns_fd, userid, &users[userid].q); + } return outlen; } else { /* CONN_RAW_UDP */ + int dns_fd = get_dns_fd(dns_fds, &users[userid].q.from); send_raw(dns_fd, out, outlen, userid, RAW_HDR_CMD_DATA, &users[userid].q); return outlen; } @@ -659,7 +728,7 @@ 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)); @@ -671,7 +740,7 @@ send_version_response(int fd, version_ack_t ack, uint32_t payload, int userid, s strncpy(out, "VFUL", sizeof(out)); break; } - + out[4] = ((payload >> 24) & 0xff); out[5] = ((payload >> 16) & 0xff); out[6] = ((payload >> 8) & 0xff); @@ -720,7 +789,7 @@ process_downstream_ack(int userid, int down_seq, int down_frag) } static void -handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) +handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query *q, int domain_len) { struct in_addr tempip; char in[512]; @@ -744,7 +813,7 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) read = unpack_data(unpacked, sizeof(unpacked), &(in[1]), domain_len - 1, b32); /* Version greeting, compare and send ack/nak */ - if (read > 4) { + if (read > 4) { /* Received V + 32bits version */ version = (((unpacked[0] & 0xff) << 24) | ((unpacked[1] & 0xff) << 16) | @@ -756,13 +825,12 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) 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].host), &(q->from), q->fromlen); + users[userid].hostlen = q->fromlen; + memcpy(&(users[userid].q), q, sizeof(struct query)); users[userid].encoder = get_base32_encoder(); users[userid].downenc = 'T'; @@ -809,12 +877,12 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) } 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", + syslog(LOG_INFO, "dropped user from %s, server full", format_addr(&q->from, q->fromlen)); } } else { send_version_response(dns_fd, VERSION_NACK, PROTOCOL_VERSION, 0, q); - syslog(LOG_INFO, "dropped user from %s, sent bad version %08X", + syslog(LOG_INFO, "dropped user from %s, sent bad version %08X", format_addr(&q->from, q->fromlen), version); } return; @@ -838,14 +906,16 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) login_calculate(logindata, 16, password, users[userid].seed); if (read >= 18 && (memcmp(logindata, unpacked+1, 16) == 0)) { - /* Login ok, send ip/mtu/netmask info */ + /* Store login ok */ + users[userid].authenticated = 1; + /* 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", + 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); @@ -863,31 +933,33 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) return; } else if(in[0] == 'I' || in[0] == 'i') { /* Request for IP number */ - in_addr_t replyaddr; - unsigned addr; - char reply[5]; - + char reply[17]; + int length; + userid = b32_8to5(in[1]); - if (check_user_and_ip(userid, q) != 0) { + if (check_authenticated_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; + reply[0] = 'I'; + if (q->from.ss_family == AF_INET) { + if (ns_ip != INADDR_ANY) { + /* If set, use assigned external ip (-n option) */ + memcpy(&reply[1], &ns_ip, sizeof(ns_ip)); + } else { + /* otherwise return destination ip from packet */ + struct sockaddr_in *addr = (struct sockaddr_in *) &q->destination; + memcpy(&reply[1], &addr->sin_addr, sizeof(struct in_addr)); + } + length = 1 + sizeof(struct in_addr); } else { - /* otherwise return destination ip from packet */ - memcpy(&replyaddr, &q->destination.s_addr, sizeof(in_addr_t)); + struct sockaddr_in6 *addr = (struct sockaddr_in6 *) &q->destination; + memcpy(&reply[1], &addr->sin6_addr, sizeof(struct in6_addr)); + length = 1 + sizeof(struct in6_addr); } - 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'); + write_dns(dns_fd, q, reply, length, 'T'); } else if(in[0] == 'Z' || in[0] == 'z') { /* Check for case conservation and chars not allowed according to RFC */ @@ -904,12 +976,12 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) } userid = b32_8to5(in[1]); - - if (check_user_and_ip(userid, q) != 0) { + + if (check_authenticated_user_and_ip(userid, q) != 0) { write_dns(dns_fd, q, "BADIP", 5, 'T'); return; /* illegal id */ } - + codec = b32_8to5(in[2]); switch (codec) { @@ -946,7 +1018,7 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) userid = b32_8to5(in[1]); - if (check_user_and_ip(userid, q) != 0) { + if (check_authenticated_user_and_ip(userid, q) != 0) { write_dns(dns_fd, q, "BADIP", 5, 'T'); return; /* illegal id */ } @@ -1074,13 +1146,13 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) /* Downstream fragsize probe packet */ userid = (b32_8to5(in[1]) >> 1) & 15; - if (check_user_and_ip(userid, q) != 0) { + if (check_authenticated_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) { + if (req_frag_size < 2 || req_frag_size > 2047) { write_dns(dns_fd, q, "BADFRAG", 7, users[userid].downenc); } else { char buf[2048]; @@ -1109,13 +1181,13 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) /* Downstream fragsize packet */ userid = unpacked[0]; - if (check_user_and_ip(userid, q) != 0) { + if (check_authenticated_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) { + if (max_frag_size < 2) { write_dns(dns_fd, q, "BADFRAG", 7, users[userid].downenc); } else { users[userid].fragsize = max_frag_size; @@ -1142,7 +1214,7 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) /* Ping packet, store userid */ userid = unpacked[0]; - if (check_user_and_ip(userid, q) != 0) { + if (check_authenticated_user_and_ip(userid, q) != 0) { write_dns(dns_fd, q, "BADIP", 5, 'T'); return; /* illegal id */ } @@ -1180,7 +1252,7 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) 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)) { @@ -1196,7 +1268,7 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) &(q->from), q->fromlen); return; } - + dn_seq = unpacked[1] >> 4; dn_frag = unpacked[1] & 15; @@ -1272,7 +1344,7 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) userid = code; /* Check user and sending ip number */ - if (check_user_and_ip(userid, q) != 0) { + if (check_authenticated_user_and_ip(userid, q) != 0) { write_dns(dns_fd, q, "BADIP", 5, 'T'); return; /* illegal id */ } @@ -1291,7 +1363,7 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) 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) && + !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 @@ -1308,7 +1380,7 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) 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)) { @@ -1324,7 +1396,7 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) &(q->from), q->fromlen); return; } - + /* Decode data header */ up_seq = (b32_8to5(in[1]) >> 2) & 7; @@ -1335,7 +1407,7 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) process_downstream_ack(userid, dn_seq, dn_frag); - if (up_seq == users[userid].inpacket.seqno && + 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 @@ -1397,7 +1469,7 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) } if (upstream_ok && lastfrag) { /* packet is complete */ - handle_full_packet(tun_fd, dns_fd, userid); + handle_full_packet(tun_fd, dns_fds, userid); } /* If there is a query that must be returned real soon, do it. @@ -1485,7 +1557,8 @@ handle_ns_request(int dns_fd, struct query *q) 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)); + struct sockaddr_in *addr = (struct sockaddr_in *) &q->destination; + memcpy(&addr->sin_addr, &ns_ip, sizeof(ns_ip)); } len = dns_encode_ns_response(buf, sizeof(buf), q, topdomain); @@ -1493,9 +1566,9 @@ handle_ns_request(int dns_fd, struct query *q) warnx("dns_encode_ns_response doesn't fit"); return; } - + if (debug >= 2) { - fprintf(stderr, "TX: client %s, type %d, name %s, %d bytes NS reply\n", + fprintf(stderr, "TX: client %s, type %d, name %s, %d bytes NS reply\n", format_addr(&q->from, q->fromlen), q->type, q->name, len); } if (sendto(dns_fd, buf, len, 0, (struct sockaddr*)&q->from, q->fromlen) <= 0) { @@ -1512,12 +1585,14 @@ handle_a_request(int dns_fd, struct query *q, int fakeip) if (fakeip) { in_addr_t ip = inet_addr("127.0.0.1"); - memcpy(&q->destination.s_addr, &ip, sizeof(in_addr_t)); + struct sockaddr_in *addr = (struct sockaddr_in *) &q->destination; + memcpy(&addr->sin_addr, &ip, sizeof(ip)); } 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)); + struct sockaddr_in *addr = (struct sockaddr_in *) &q->destination; + memcpy(&addr->sin_addr, &ns_ip, sizeof(ns_ip)); } len = dns_encode_a_response(buf, sizeof(buf), q); @@ -1525,7 +1600,7 @@ handle_a_request(int dns_fd, struct query *q, int fakeip) warnx("dns_encode_a_response doesn't fit"); return; } - + if (debug >= 2) { fprintf(stderr, "TX: client %s, type %d, name %s, %d bytes A reply\n", format_addr(&q->from, q->fromlen), q->type, q->name, len); @@ -1560,7 +1635,7 @@ forward_query(int bind_fd, struct query *q) 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"); } @@ -1569,26 +1644,27 @@ forward_query(int bind_fd, struct query *q) warn("forward query error"); } } - + static int -tunnel_bind(int bind_fd, int dns_fd) +tunnel_bind(int bind_fd, struct dnsfd *dns_fds) { char packet[64*1024]; struct sockaddr_storage from; socklen_t fromlen; struct fw_query *query; unsigned short id; + int dns_fd; int r; fromlen = sizeof(struct sockaddr); - r = recvfrom(bind_fd, packet, sizeof(packet), 0, + 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)); } @@ -1606,8 +1682,9 @@ tunnel_bind(int bind_fd, int dns_fd) fprintf(stderr, "TX: client %s id %u, %d bytes\n", format_addr(&query->addr, query->addrlen), (id & 0xffff), r); } - - if (sendto(dns_fd, packet, r, 0, (const struct sockaddr *) &(query->addr), + + dns_fd = get_dns_fd(dns_fds, &query->addr); + if (sendto(dns_fd, packet, r, 0, (const struct sockaddr *) &(query->addr), query->addrlen) <= 0) { warn("forward reply error"); } @@ -1616,18 +1693,18 @@ tunnel_bind(int bind_fd, int dns_fd) } static int -tunnel_dns(int tun_fd, int dns_fd, int bind_fd) +tunnel_dns(int tun_fd, int dns_fd, struct dnsfd *dns_fds, int bind_fd) { struct query q; int read; int domain_len; int inside_topdomain = 0; - if ((read = read_dns(dns_fd, tun_fd, &q)) <= 0) + if ((read = read_dns(dns_fd, dns_fds, tun_fd, &q)) <= 0) return 0; if (debug >= 2) { - fprintf(stderr, "RX: client %s, type %d, name %s\n", + fprintf(stderr, "RX: client %s, type %d, name %s\n", format_addr(&q.from, q.fromlen), q.type, q.name); } @@ -1664,13 +1741,14 @@ tunnel_dns(int tun_fd, int dns_fd, int bind_fd) switch (q.type) { case T_NULL: + case T_PRIVATE: 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); + handle_null_request(tun_fd, dns_fd, dns_fds, &q, domain_len); break; case T_NS: handle_ns_request(dns_fd, &q); @@ -1688,7 +1766,7 @@ tunnel_dns(int tun_fd, int dns_fd, int bind_fd) } static int -tunnel(int tun_fd, int dns_fd, int bind_fd, int max_idle_time) +tunnel(int tun_fd, struct dnsfd *dns_fds, int bind_fd, int max_idle_time) { struct timeval tv; fd_set fds; @@ -1720,9 +1798,16 @@ tunnel(int tun_fd, int dns_fd, int bind_fd, int max_idle_time) } FD_ZERO(&fds); + maxfd = 0; - FD_SET(dns_fd, &fds); - maxfd = dns_fd; + if (dns_fds->v4fd >= 0) { + FD_SET(dns_fds->v4fd, &fds); + maxfd = MAX(dns_fds->v4fd, maxfd); + } + if (dns_fds->v6fd >= 0) { + FD_SET(dns_fds->v6fd, &fds); + maxfd = MAX(dns_fds->v6fd, maxfd); + } if (bind_fd) { /* wait for replies from real DNS */ @@ -1738,9 +1823,9 @@ tunnel(int tun_fd, int dns_fd, int bind_fd, int max_idle_time) } i = select(maxfd + 1, &fds, NULL, NULL, &tv); - + if(i < 0) { - if (running) + if (running) warn("select"); return 1; } @@ -1759,15 +1844,18 @@ tunnel(int tun_fd, int dns_fd, int bind_fd, int max_idle_time) } } } - } 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); - } + } else { + if (FD_ISSET(tun_fd, &fds)) { + tunnel_tun(tun_fd, dns_fds); + } + if (dns_fds->v4fd >= 0 && FD_ISSET(dns_fds->v4fd, &fds)) { + tunnel_dns(tun_fd, dns_fds->v4fd, dns_fds, bind_fd); + } + if (dns_fds->v6fd >= 0 && FD_ISSET(dns_fds->v6fd, &fds)) { + tunnel_dns(tun_fd, dns_fds->v6fd, dns_fds, bind_fd); + } if (FD_ISSET(bind_fd, &fds)) { - tunnel_bind(bind_fd, dns_fd); + tunnel_bind(bind_fd, dns_fds); } } @@ -1777,15 +1865,17 @@ tunnel(int tun_fd, int dns_fd, int bind_fd, int max_idle_time) users[userid].last_pkt + 60 > time(NULL) && users[userid].q_sendrealsoon.id != 0 && users[userid].conn == CONN_DNS_NULL && - !users[userid].q_sendrealsoon_new) + !users[userid].q_sendrealsoon_new) { + int dns_fd = get_dns_fd(dns_fds, &users[userid].q_sendrealsoon.from); 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) +handle_full_packet(int tun_fd, struct dnsfd *dns_fds, int userid) { unsigned long outlen; char out[64*1024]; @@ -1793,7 +1883,7 @@ handle_full_packet(int tun_fd, int dns_fd, int userid) int ret; outlen = sizeof(out); - ret = uncompress((uint8_t*)out, &outlen, + ret = uncompress((uint8_t*)out, &outlen, (uint8_t*)users[userid].inpacket.data, users[userid].inpacket.len); if (ret == Z_OK) { @@ -1814,10 +1904,13 @@ handle_full_packet(int tun_fd, int dns_fd, int userid) users[userid].inpacket.len); /* Start sending immediately if query is waiting */ - if (users[touser].q_sendrealsoon.id != 0) + if (users[touser].q_sendrealsoon.id != 0) { + int dns_fd = get_dns_fd(dns_fds, &users[touser].q_sendrealsoon.from); send_chunk_or_dataless(dns_fd, touser, &users[touser].q_sendrealsoon); - else if (users[touser].q.id != 0) + } else if (users[touser].q.id != 0) { + int dns_fd = get_dns_fd(dns_fds, &users[touser].q.from); send_chunk_or_dataless(dns_fd, touser, &users[touser].q); + } #ifdef OUTPACKETQ_LEN } else { save_to_outpacketq(touser, @@ -1826,6 +1919,7 @@ handle_full_packet(int tun_fd, int dns_fd, int userid) #endif } } else{ /* CONN_RAW_UDP */ + int dns_fd = get_dns_fd(dns_fds, &users[touser].q.from); send_raw(dns_fd, users[userid].inpacket.data, users[userid].inpacket.len, touser, RAW_HDR_CMD_DATA, &users[touser].q); @@ -1845,13 +1939,14 @@ 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, + /* can't use check_authenticated_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].authenticated) return; if (users[userid].last_pkt + 60 < time(NULL)) return; if (debug >= 1) { @@ -1862,29 +1957,30 @@ handle_raw_login(char *packet, int len, struct query *q, int fd, int 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)); - + memcpy(&(users[userid].host), &(q->from), q->fromlen); + users[userid].hostlen = q->fromlen; + /* 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); + + users[userid].authenticated_raw = 1; } } static void -handle_raw_data(char *packet, int len, struct query *q, int dns_fd, int tun_fd, int userid) +handle_raw_data(char *packet, int len, struct query *q, struct dnsfd *dns_fds, int tun_fd, int userid) { - if (check_user_and_ip(userid, q) != 0) { + if (check_authenticated_user_and_ip(userid, q) != 0) { return; } + if (!users[userid].authenticated_raw) return; /* Update query and time info for user */ users[userid].last_pkt = time(NULL); @@ -1900,15 +1996,16 @@ handle_raw_data(char *packet, int len, struct query *q, int dns_fd, int tun_fd, users[userid].inpacket.len, userid); } - handle_full_packet(tun_fd, dns_fd, userid); + handle_full_packet(tun_fd, dns_fds, userid); } static void handle_raw_ping(struct query *q, int dns_fd, int userid) { - if (check_user_and_ip(userid, q) != 0) { + if (check_authenticated_user_and_ip(userid, q) != 0) { return; } + if (!users[userid].authenticated_raw) return; /* Update query and time info for user */ users[userid].last_pkt = time(NULL); @@ -1923,7 +2020,7 @@ handle_raw_ping(struct query *q, int dns_fd, int userid) } static int -raw_decode(char *packet, int len, struct query *q, int dns_fd, int tun_fd) +raw_decode(char *packet, int len, struct query *q, int dns_fd, struct dnsfd *dns_fds, int tun_fd) { int raw_user; @@ -1940,7 +2037,7 @@ raw_decode(char *packet, int len, struct query *q, int dns_fd, int tun_fd) 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); + handle_raw_data(&packet[RAW_HDR_LEN], len - RAW_HDR_LEN, q, dns_fds, tun_fd, raw_user); break; case RAW_HDR_CMD_PING: /* Keepalive packet */ @@ -1954,19 +2051,20 @@ raw_decode(char *packet, int len, struct query *q, int dns_fd, int tun_fd) } static int -read_dns(int fd, int tun_fd, struct query *q) /* FIXME: tun_fd is because of raw_decode() below */ +read_dns(int fd, struct dnsfd *dns_fds, int tun_fd, struct query *q) +/* FIXME: dns_fds and tun_fd are because of raw_decode() below */ { - struct sockaddr_in from; + struct sockaddr_storage from; socklen_t addrlen; char packet[64*1024]; int r; #ifndef WINDOWS32 - char address[96]; + char control[CMSG_SPACE(sizeof (struct in6_pktinfo))]; struct msghdr msg; struct iovec iov; struct cmsghdr *cmsg; - addrlen = sizeof(struct sockaddr); + addrlen = sizeof(struct sockaddr_storage); iov.iov_base = packet; iov.iov_len = sizeof(packet); @@ -1974,13 +2072,13 @@ read_dns(int fd, int tun_fd, struct query *q) /* FIXME: tun_fd is because of raw msg.msg_namelen = (unsigned) addrlen; msg.msg_iov = &iov; msg.msg_iovlen = 1; - msg.msg_control = address; - msg.msg_controllen = sizeof(address); + msg.msg_control = control; + msg.msg_controllen = sizeof(control); msg.msg_flags = 0; - + r = recvmsg(fd, &msg, 0); #else - addrlen = sizeof(struct sockaddr); + addrlen = sizeof(struct sockaddr_storage); r = recvfrom(fd, packet, sizeof(packet), 0, (struct sockaddr*)&from, &addrlen); #endif /* !WINDOWS32 */ @@ -1989,28 +2087,44 @@ read_dns(int fd, int tun_fd, struct query *q) /* FIXME: tun_fd is because of raw q->fromlen = addrlen; /* TODO do not handle raw packets here! */ - if (raw_decode(packet, r, q, fd, tun_fd)) { + if (raw_decode(packet, r, q, fd, dns_fds, 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); + memset(&q->destination, 0, sizeof(struct sockaddr_storage)); + /* Read destination IP address */ + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + + if (cmsg->cmsg_level == IPPROTO_IP && + cmsg->cmsg_type == DSTADDR_SOCKOPT) { + + struct sockaddr_in *addr = (struct sockaddr_in *) &q->destination; + addr->sin_family = AF_INET; + addr->sin_addr = *dstaddr(cmsg); + q->dest_len = sizeof(*addr); break; - } + } + if (cmsg->cmsg_level == IPPROTO_IPV6 && + cmsg->cmsg_type == IPV6_PKTINFO) { + + struct in6_pktinfo *pktinfo; + struct sockaddr_in6 *addr = (struct sockaddr_in6 *) &q->destination; + pktinfo = (struct in6_pktinfo *) CMSG_DATA(cmsg); + addr->sin6_family = AF_INET6; + memcpy(&addr->sin6_addr, &pktinfo->ipi6_addr, sizeof(struct in6_addr)); + q->dest_len = sizeof(*addr); + break; + } } #endif return strlen(q->name); - } else if (r < 0) { + } else if (r < 0) { /* Error */ warn("read dns"); } @@ -2075,7 +2189,7 @@ write_dns_nameenc(char *buf, size_t buflen, char *data, int datalen, char downen /* Add dot (if it wasn't there already) and topdomain */ b = buf; b += strlen(buf) - 1; - if (*b != '.') + if (*b != '.') *++b = '.'; b++; @@ -2167,9 +2281,9 @@ write_dns(int fd, struct query *q, char *data, int datalen, char downenc) warnx("dns_encode doesn't fit"); return; } - + if (debug >= 2) { - fprintf(stderr, "TX: client %s, type %d, name %s, %d bytes data\n", + fprintf(stderr, "TX: client %s, type %d, name %s, %d bytes data\n", format_addr(&q->from, q->fromlen), q->type, q->name, datalen); } @@ -2177,28 +2291,32 @@ write_dns(int fd, struct query *q, char *data, int datalen, char downenc) } static void -usage() { +print_usage() { extern char *__progname; - fprintf(stderr, "Usage: %s [-v] [-h] [-c] [-s] [-f] [-D] [-u user] " + fprintf(stderr, "Usage: %s [-v] [-h] " + "[-4] [-6] [-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] [-i max idle time] " + "[-l ipv4 listen address] [-L ipv6 listen address] " + "[-p port] [-n external ip] [-b dnsport] " + "[-P password] [-F pidfile] [-i max idle time] " "tunnel_ip[/netmask] topdomain\n", __progname); +} + +static void +usage() { + print_usage(); 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); + print_usage(); fprintf(stderr, " -v to print version info and exit\n"); fprintf(stderr, " -h to print this help and exit\n"); + fprintf(stderr, " -4 to listen only on IPv4\n"); + fprintf(stderr, " -6 to listen only on IPv6\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"); @@ -2210,8 +2328,10 @@ help() { 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 " + fprintf(stderr, " -l IPv4 address to listen on for incoming dns traffic " "(default 0.0.0.0)\n"); + fprintf(stderr, " -L IPv6 address to listen on for incoming dns traffic " + "(default ::)\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"); @@ -2231,11 +2351,31 @@ version() { exit(0); } +static void +prepare_dns_fd(int fd) +{ +#ifndef WINDOWS32 + int flag = 1; + + /* To get destination address from each UDP datagram, see read_dns() */ + setsockopt(fd, IPPROTO_IP, DSTADDR_SOCKOPT, (const void*) &flag, sizeof(flag)); +#ifdef IPV6_RECVPKTINFO + setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (const void*) &flag, sizeof(flag)); +#endif +#ifdef IPV6_PKTINFO + setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, (const void*) &flag, sizeof(flag)); +#endif + +#endif +} + int main(int argc, char **argv) { extern char *__progname; - char *listen_ip; + char *listen_ip4; + char *listen_ip6; + char *errormsg; #ifndef WINDOWS32 struct passwd *pw; #endif @@ -2245,14 +2385,15 @@ main(int argc, char **argv) char *context; char *device; char *pidfile; - int dnsd_fd; + int addrfamily; + struct dnsfd dns_fds; int tun_fd; - /* settings for forwarding normal DNS to + /* settings for forwarding normal DNS to * local real DNS server */ int bind_fd; int bind_enable; - + int choice; int port; int mtu; @@ -2261,8 +2402,10 @@ main(int argc, char **argv) int ns_get_externalip; int retval; int max_idle_time = 0; - struct sockaddr_storage dnsaddr; - int dnsaddr_len; + struct sockaddr_storage dns4addr; + int dns4addr_len; + struct sockaddr_storage dns6addr; + int dns6addr_len; #ifdef HAVE_SYSTEMD int nb_fds; #endif @@ -2270,6 +2413,7 @@ main(int argc, char **argv) #ifndef WINDOWS32 pw = NULL; #endif + errormsg = NULL; username = NULL; newroot = NULL; context = NULL; @@ -2279,10 +2423,12 @@ main(int argc, char **argv) bind_fd = 0; mtu = 1130; /* Very many relays give fragsize 1150 or slightly higher for NULL; tun/zlib adds ~17 bytes. */ - listen_ip = NULL; + listen_ip4 = NULL; + listen_ip6 = NULL; port = 53; ns_ip = INADDR_ANY; ns_get_externalip = 0; + addrfamily = AF_UNSPEC; check_ip = 1; skipipconfig = 0; debug = 0; @@ -2293,7 +2439,7 @@ main(int argc, char **argv) b64 = get_base64_encoder(); b64u = get_base64u_encoder(); b128 = get_base128_encoder(); - + retval = 0; #ifdef WINDOWS32 @@ -2311,9 +2457,15 @@ main(int argc, char **argv) memset(password, 0, sizeof(password)); srand(time(NULL)); fw_query_init(); - - while ((choice = getopt(argc, argv, "vcsfhDu:t:d:m:l:p:n:b:P:z:F:i:")) != -1) { + + while ((choice = getopt(argc, argv, "46vcsfhDu:t:d:m:l:L:p:n:b:P:z:F:i:")) != -1) { switch(choice) { + case '4': + addrfamily = AF_INET; + break; + case '6': + addrfamily = AF_INET6; + break; case 'v': version(); break; @@ -2345,7 +2497,10 @@ main(int argc, char **argv) mtu = atoi(optarg); break; case 'l': - listen_ip = optarg; + listen_ip4 = optarg; + break; + case 'L': + listen_ip6 = optarg; break; case 'p': port = atoi(optarg); @@ -2363,16 +2518,16 @@ main(int argc, char **argv) break; case 'F': pidfile = optarg; - break; + break; case 'i': max_idle_time = atoi(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)); + memset(optarg, 0, strlen(optarg)); break; case 'z': context = optarg; @@ -2388,9 +2543,9 @@ main(int argc, char **argv) check_superuser(usage); - if (argc != 2) + if (argc != 2) usage(); - + netsize = strchr(argv[0], '/'); if (netsize) { *netsize = 0; @@ -2399,21 +2554,17 @@ main(int argc, char **argv) } 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."); + if(check_topdomain(topdomain, &errormsg)) { + warnx("Invalid topdomain: %s", errormsg); usage(); + /* NOTREACHED */ } if (username != NULL) { @@ -2429,12 +2580,12 @@ main(int argc, char **argv) warnx("Bad MTU given."); usage(); } - + if(port < 1 || port > 65535) { warnx("Bad port number given."); usage(); } - + 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); @@ -2445,15 +2596,23 @@ main(int argc, char **argv) fprintf(stderr, "Add more -D switches to set higher debug level.\n"); foreground = 1; } - - dnsaddr_len = get_addr(listen_ip, port, AF_INET, AI_PASSIVE | AI_NUMERICHOST, &dnsaddr); - if (dnsaddr_len < 0) { - warnx("Bad IP address to listen on."); - usage(); + if (addrfamily == AF_UNSPEC || addrfamily == AF_INET) { + dns4addr_len = get_addr(listen_ip4, port, AF_INET, AI_PASSIVE | AI_NUMERICHOST, &dns4addr); + if (dns4addr_len < 0) { + warnx("Bad IPv4 address to listen on."); + usage(); + } + } + if (addrfamily == AF_UNSPEC || addrfamily == AF_INET6) { + dns6addr_len = get_addr(listen_ip6, port, AF_INET6, AI_PASSIVE | AI_NUMERICHOST, &dns6addr); + if (dns6addr_len < 0) { + warnx("Bad IPv6 address to listen on."); + usage(); + } } if(bind_enable) { - in_addr_t dns_ip = ((struct sockaddr_in *) &dnsaddr)->sin_addr.s_addr; + in_addr_t dns_ip = ((struct sockaddr_in *) &dns4addr)->sin_addr.s_addr; if (bind_port < 1 || bind_port > 65535) { warnx("Bad DNS server port number given."); usage(); @@ -2489,7 +2648,7 @@ main(int argc, char **argv) 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)); @@ -2497,56 +2656,94 @@ main(int argc, char **argv) read_password(password, sizeof(password)); } + /* Mark both file descriptors as unused */ + dns_fds.v4fd = -1; + dns_fds.v6fd = -1; + created_users = init_users(my_ip, netmask); if ((tun_fd = open_tun(device)) == -1) { - retval = 1; - goto cleanup0; + /* nothing to clean up, just return */ + return 1; } if (!skipipconfig) { const char *other_ip = users_get_first_ip(); if (tun_setip(argv[0], other_ip, netmask) != 0 || tun_setmtu(mtu) != 0) { retval = 1; free((void*) other_ip); - goto cleanup1; + goto cleanup; } free((void*) other_ip); } + #ifdef HAVE_SYSTEMD nb_fds = sd_listen_fds(0); - if (nb_fds > 1) { + if (nb_fds < 0) { + warnx("Failed to receive file descriptors from systemd: %s", strerror(-nb_fds)); retval = 1; - warnx("Too many file descriptors received!\n"); - goto cleanup1; - } else if (nb_fds == 1) { - dnsd_fd = SD_LISTEN_FDS_START; - } else { + goto cleanup; + } else if (nb_fds == 0) { #endif - if ((dnsd_fd = open_dns(&dnsaddr, dnsaddr_len)) < 0) { + if ((addrfamily == AF_UNSPEC || addrfamily == AF_INET) && + (dns_fds.v4fd = open_dns(&dns4addr, dns4addr_len)) < 0) { + retval = 1; - goto cleanup2; + goto cleanup; + } + if ((addrfamily == AF_UNSPEC || addrfamily == AF_INET6) && + /* Set IPv6 socket to V6ONLY */ + (dns_fds.v6fd = open_dns_opt(&dns6addr, dns6addr_len, 1)) < 0) { + + retval = 1; + goto cleanup; } #ifdef HAVE_SYSTEMD + } else if (nb_fds <= 2) { + /* systemd may pass up to two sockets, for ip4 and ip6, try to figure out + which is which */ + for (int i = 0; i < nb_fds; i++) { + int fd = SD_LISTEN_FDS_START + i; + if (sd_is_socket(fd, AF_INET, SOCK_DGRAM, -1)) { + dns_fds.v4fd = fd; + } else if (sd_is_socket(fd, AF_INET6, SOCK_DGRAM, -1)) { + dns_fds.v6fd = fd; + } else { + retval = 1; + warnx("Unknown socket %d passed to iodined!\n", fd); + goto cleanup; + } + } + } else { + retval = 1; + warnx("Too many file descriptors received!\n"); + goto cleanup; } #endif + + /* Setup dns file descriptors to get destination IP address */ + if (dns_fds.v4fd >= 0) + prepare_dns_fd(dns_fds.v4fd); + if (dns_fds.v6fd >= 0) + prepare_dns_fd(dns_fds.v6fd); + if (bind_enable) { if ((bind_fd = open_dns_from_host(NULL, 0, AF_INET, 0)) < 0) { retval = 1; - goto cleanup3; + goto cleanup; } } 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) + if (foreground == 0) do_detach(); - + if (pidfile != NULL) do_pidfile(pidfile); @@ -2576,17 +2773,17 @@ main(int argc, char **argv) do_setcon(context); syslog(LOG_INFO, "started, listening on port %d", port); - - tunnel(tun_fd, dnsd_fd, bind_fd, max_idle_time); + + tunnel(tun_fd, &dns_fds, bind_fd, max_idle_time); syslog(LOG_INFO, "stopping"); -cleanup3: close_dns(bind_fd); -cleanup2: - close_dns(dnsd_fd); -cleanup1: - close_tun(tun_fd); -cleanup0: +cleanup: + if (dns_fds.v6fd >= 0) + close_dns(dns_fds.v6fd); + if (dns_fds.v4fd >= 0) + close_dns(dns_fds.v4fd); + close_tun(tun_fd); return retval; } diff --git a/src/login.c b/src/login.c index 8641918..19e3f58 100644 --- a/src/login.c +++ b/src/login.c @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * @@ -30,11 +31,11 @@ #include "md5.h" -/* - * Needs a 16byte array for output, and 32 bytes password +/* + * Needs a 16byte array for output, and 32 bytes password */ -void -login_calculate(char *buf, int buflen, const char *pass, int seed) +void +login_calculate(char *buf, int buflen, const char *pass, int seed) { unsigned char temp[32]; md5_state_t ctx; @@ -42,7 +43,7 @@ login_calculate(char *buf, int buflen, const char *pass, int seed) int i; int k; - if (buflen < 16) + if (buflen < 16) return; memcpy(temp, pass, 32); diff --git a/src/login.h b/src/login.h index 840c920..4d6d293 100644 --- a/src/login.h +++ b/src/login.h @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * diff --git a/src/md5.h b/src/md5.h index 698c995..3baa4dc 100644 --- a/src/md5.h +++ b/src/md5.h @@ -71,7 +71,7 @@ typedef struct md5_state_s { } md5_state_t; #ifdef __cplusplus -extern "C" +extern "C" { #endif diff --git a/src/read.c b/src/read.c index 9fdd656..bb7192a 100644 --- a/src/read.c +++ b/src/read.c @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * @@ -64,7 +65,7 @@ readname_loop(char *packet, int packetlen, char **src, char *dst, size_t length, c--; } - + if (len >= length - 1) { break; /* We used up all space */ } @@ -88,15 +89,15 @@ readname(char *packet, int packetlen, char **src, char *dst, size_t length) } int -readshort(char *packet, char **src, short *dst) +readshort(char *packet, char **src, unsigned short *dst) { unsigned char *p; p = (unsigned char *) *src; *dst = (p[0] << 8) | p[1]; - (*src) += sizeof(short); - return sizeof(short); + (*src) += sizeof(unsigned short); + return sizeof(unsigned short); } int @@ -107,8 +108,8 @@ readlong(char *packet, char **src, uint32_t *dst) p = (unsigned char *) *src; - *dst = ((uint32_t)p[0] << 24) - | ((uint32_t)p[1] << 16) + *dst = ((uint32_t)p[0] << 24) + | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | ((uint32_t)p[3]); @@ -166,7 +167,7 @@ putname(char **buf, size_t buflen, const char *host) h = strdup(host); left = buflen; p = *buf; - + word = strtok(h, "."); while(word) { if (strlen(word) > 63 || strlen(word) > left) { @@ -234,7 +235,7 @@ int putdata(char **dst, char *data, size_t len) { memcpy(*dst, data, len); - + (*dst) += len; return len; } diff --git a/src/read.h b/src/read.h index b33f3bb..5da474d 100644 --- a/src/read.h +++ b/src/read.h @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * @@ -18,7 +19,7 @@ #define _READ_H_ int readname(char *, int, char **, char *, size_t); -int readshort(char *, char **, short *); +int readshort(char *, char **, unsigned short *); int readlong(char *, char **, uint32_t *); int readdata(char *, char **, char *, size_t); int readtxtbin(char *, char **, size_t, char *, size_t); diff --git a/src/tun.c b/src/tun.c index dd24b2d..1921898 100644 --- a/src/tun.c +++ b/src/tun.c @@ -1,7 +1,9 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson + * 2013 Peter Sagerson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * @@ -28,6 +30,15 @@ #include #include +#ifdef DARWIN +#include +#include +#include +#include +#include +#include +#endif + #ifndef IFCONFIGPATH #define IFCONFIGPATH "PATH=/sbin:/bin " #endif @@ -36,8 +47,8 @@ #include "windows.h" #include -HANDLE dev_handle; -struct tun_data data; +static HANDLE dev_handle; +static struct tun_data data; static void get_name(char *ifname, int namelen, char *dev_name); @@ -63,17 +74,16 @@ static void get_name(char *ifname, int namelen, char *dev_name); #include "tun.h" #include "common.h" -char if_name[250]; +static char if_name[250]; -#ifndef WINDOWS32 #ifdef LINUX #include #include #include -int -open_tun(const char *tun_device) +int +open_tun(const char *tun_device) { int i; int tun_fd; @@ -85,13 +95,13 @@ open_tun(const char *tun_device) #endif if ((tun_fd = open(tunnel, O_RDWR)) < 0) { - warn("open_tun: %s: %s", tunnel, strerror(errno)); + warn("open_tun: %s", tunnel); return -1; } memset(&ifreq, 0, sizeof(ifreq)); - ifreq.ifr_flags = IFF_TUN; + ifreq.ifr_flags = IFF_TUN; if (tun_device != NULL) { strncpy(ifreq.ifr_name, tun_device, IFNAMSIZ); @@ -101,11 +111,12 @@ open_tun(const char *tun_device) if (ioctl(tun_fd, TUNSETIFF, (void *) &ifreq) != -1) { fprintf(stderr, "Opened %s\n", ifreq.ifr_name); + fd_set_close_on_exec(tun_fd); return tun_fd; } if (errno != EBUSY) { - warn("open_tun: ioctl[TUNSETIFF]: %s", strerror(errno)); + warn("open_tun: ioctl[TUNSETIFF]"); return -1; } } else { @@ -115,11 +126,12 @@ open_tun(const char *tun_device) 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); + fd_set_close_on_exec(tun_fd); return tun_fd; } if (errno != EBUSY) { - warn("open_tun: ioctl[TUNSETIFF]: %s", strerror(errno)); + warn("open_tun: ioctl[TUNSETIFF]"); return -1; } } @@ -130,49 +142,8 @@ open_tun(const char *tun_device) return -1; } -#else /* BSD */ +#elif WINDOWS32 -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) { @@ -187,7 +158,7 @@ get_device(char *device, int device_len, const char *wanted_dev) warnx("Error opening registry key " TAP_ADAPTER_KEY ); return; } - + while (TRUE) { char name[256]; char unit[256]; @@ -225,7 +196,7 @@ get_device(char *device, int device_len, const char *wanted_dev) 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); @@ -291,7 +262,7 @@ DWORD WINAPI tun_reader(LPVOID arg) OVERLAPPED olpd; int sock; - sock = open_dns(0, INADDR_ANY); + sock = open_dns_from_host("127.0.0.1", 0, AF_INET, 0); olpd.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); @@ -302,7 +273,7 @@ DWORD WINAPI tun_reader(LPVOID arg) if (!res) { WaitForSingleObject(olpd.hEvent, INFINITE); res = GetOverlappedResult(dev_handle, &olpd, (LPDWORD) &len, FALSE); - res = sendto(sock, buf, len, 0, (struct sockaddr*) &(tun->addr), + res = sendto(sock, buf, len, 0, (struct sockaddr*) &(tun->addr), tun->addrlen); } } @@ -310,8 +281,8 @@ DWORD WINAPI tun_reader(LPVOID arg) return 0; } -int -open_tun(const char *tun_device) +int +open_tun(const char *tun_device) { char adapter[256]; char tapfile[512]; @@ -331,7 +302,7 @@ open_tun(const char *tun_device) } 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); @@ -342,9 +313,9 @@ open_tun(const char *tun_device) /* 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 + * A thread does blocking reads on tun device and * sends data as udp to this socket */ - + localsock_len = get_addr("127.0.0.1", 55353, AF_INET, 0, &localsock); tunfd = open_dns(&localsock, localsock_len); @@ -352,88 +323,259 @@ open_tun(const char *tun_device) memcpy(&(data.addr), &localsock, localsock_len); data.addrlen = localsock_len; CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)tun_reader, &data, 0, NULL); - + return tunfd; } -#endif -void -close_tun(int tun_fd) +#else /* BSD and friends */ + +#ifdef DARWIN + +/* Extract the device number from the name, if given. The value returned will + * be suitable for sockaddr_ctl.sc_unit, which means 0 for auto-assign, or + * (n + 1) for manual. + */ +static int +utun_unit(const char *dev) +{ + const char *unit_str = dev; + int unit = 0; + + while (*unit_str != '\0' && !isdigit(*unit_str)) + unit_str++; + + if (isdigit(*unit_str)) + unit = strtol(unit_str, NULL, 10) + 1; + + return unit; +} + +static int +open_utun(const char *dev) +{ + struct sockaddr_ctl addr; + struct ctl_info info; + char ifname[10]; + socklen_t ifname_len = sizeof(ifname); + int fd = -1; + int err = 0; + + fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL); + if (fd < 0) { + warn("open_utun: socket(PF_SYSTEM)"); + return -1; + } + + /* Look up the kernel controller ID for utun devices. */ + bzero(&info, sizeof(info)); + strncpy(info.ctl_name, UTUN_CONTROL_NAME, MAX_KCTL_NAME); + + err = ioctl(fd, CTLIOCGINFO, &info); + if (err != 0) { + warn("open_utun: ioctl(CTLIOCGINFO)"); + close(fd); + return -1; + } + + /* Connecting to the socket creates the utun device. */ + addr.sc_len = sizeof(addr); + addr.sc_family = AF_SYSTEM; + addr.ss_sysaddr = AF_SYS_CONTROL; + addr.sc_id = info.ctl_id; + addr.sc_unit = utun_unit(dev); + + err = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); + if (err != 0) { + warn("open_utun: connect"); + close(fd); + return -1; + } + + /* Retrieve the assigned interface name. */ + err = getsockopt(fd, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, ifname, &ifname_len); + if (err != 0) { + warn("open_utun: getsockopt(UTUN_OPT_IFNAME)"); + close(fd); + return -1; + } + + strncpy(if_name, ifname, sizeof(if_name)); + + fprintf(stderr, "Opened %s\n", ifname); + fd_set_close_on_exec(fd); + + return fd; +} + +#endif + +int +open_tun(const char *tun_device) +{ + int i; + int tun_fd; + char tun_name[50]; + +#ifdef DARWIN + if (!strncmp(tun_device, "utun", 4)) { + tun_fd = open_utun(tun_device); + if (tun_fd >= 0) { + return tun_fd; + } + } +#endif + + 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", tun_name); + return -1; + } + + fprintf(stderr, "Opened %s\n", tun_name); + fd_set_close_on_exec(tun_fd); + 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); + fd_set_close_on_exec(tun_fd); + return tun_fd; + } + + if (errno == ENOENT) + break; + } + + warn("open_tun: Failed to open tunneling device"); + } + + return -1; +} + +#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) +#ifdef WINDOWS32 +int +write_tun(int tun_fd, char *data, size_t len) { -#if defined (FREEBSD) || defined (DARWIN) || defined(NETBSD) || defined(WINDOWS32) + DWORD written; + DWORD res; + OVERLAPPED olpd; + 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; - } + 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) +read_tun(int tun_fd, char *buf, size_t len) { -#if defined (FREEBSD) || defined (DARWIN) || defined(NETBSD) || defined(WINDOWS32) - /* FreeBSD/Darwin/NetBSD 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 */ } +#else +int +write_tun(int tun_fd, char *data, size_t len) +{ +#if defined (FREEBSD) || defined (NETBSD) + /* FreeBSD/NetBSD has no header */ + int header = 0; +#elif defined (DARWIN) + /* Darwin tun has no header, Darwin utun does */ + int header = !strncmp(if_name, "utun", 4); +#else /* LINUX/OPENBSD */ + int header = 1; +#endif + + if (!header) { + data += 4; + len -= 4; + } else { +#ifdef LINUX + // Linux prefixes with 32 bits ethertype + // 0x0800 for IPv4, 0x86DD for IPv6 + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0x08; + data[3] = 0x00; +#else /* OPENBSD and DARWIN(utun) */ + // BSDs prefix with 32 bits address family + // AF_INET for IPv4, AF_INET6 for IPv6 + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x02; +#endif + } + + if (write(tun_fd, data, len) != len) { + warn("write_tun"); + return 1; + } + return 0; +} + +ssize_t +read_tun(int tun_fd, char *buf, size_t len) +{ +#if defined (FREEBSD) || defined (NETBSD) + /* FreeBSD/NetBSD has no header */ + int header = 0; +#elif defined (DARWIN) + /* Darwin tun has no header, Darwin utun does */ + int header = !strncmp(if_name, "utun", 4); +#else /* LINUX/OPENBSD */ + int header = 1; +#endif + + if (!header) { + int bytes; + memset(buf, 0, 4); + + bytes = read(tun_fd, buf + 4, len - 4); + if (bytes < 0) { + return bytes; + } else { + return bytes + 4; + } + } else { + return read(tun_fd, buf, len); + } +} +#endif int tun_setip(const char *ip, const char *other_ip, int netbits) @@ -450,8 +592,12 @@ tun_setip(const char *ip, const char *other_ip, int netbits) DWORD ipdata[3]; struct in_addr addr; DWORD len; -#endif +#else const char *display_ip; +#ifndef LINUX + struct in_addr netip; +#endif +#endif netmask = 0; for (i = 0; i < netbits; i++) { @@ -470,23 +616,22 @@ tun_setip(const char *ip, const char *other_ip, int netbits) # else display_ip = ip; # endif - snprintf(cmdline, sizeof(cmdline), + snprintf(cmdline, sizeof(cmdline), IFCONFIGPATH "ifconfig %s %s %s netmask %s", if_name, ip, display_ip, inet_ntoa(net)); - + fprintf(stderr, "Setting IP of %s to %s\n", if_name, ip); #ifndef LINUX - struct in_addr netip; netip.s_addr = inet_addr(ip); netip.s_addr = netip.s_addr & net.s_addr; r = system(cmdline); if(r != 0) { return r; } else { - + snprintf(cmdline, sizeof(cmdline), "/sbin/route add %s/%d %s", inet_ntoa(netip), netbits, ip); @@ -499,13 +644,13 @@ tun_setip(const char *ip, const char *other_ip, int netbits) /* Set device as connected */ fprintf(stderr, "Enabling interface '%s'\n", if_name); status = 1; - r = DeviceIoControl(dev_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, + 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 */ @@ -515,7 +660,7 @@ tun_setip(const char *ip, const char *other_ip, int netbits) } /* Tell ip/networkaddr/netmask to device for arp use */ - r = DeviceIoControl(dev_handle, TAP_IOCTL_CONFIG_TUN, &ipdata, + 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"); @@ -530,18 +675,18 @@ tun_setip(const char *ip, const char *other_ip, int netbits) #endif } -int +int tun_setmtu(const unsigned mtu) { #ifndef WINDOWS32 char cmdline[512]; if (mtu > 200 && mtu <= 1500) { - snprintf(cmdline, sizeof(cmdline), + snprintf(cmdline, sizeof(cmdline), IFCONFIGPATH "ifconfig %s mtu %u", if_name, mtu); - + fprintf(stderr, "Setting MTU of %s to %u\n", if_name, mtu); return system(cmdline); } else { diff --git a/src/tun.h b/src/tun.h index 89ffcfa..8982a9f 100644 --- a/src/tun.h +++ b/src/tun.h @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * diff --git a/src/user.c b/src/user.c index 2f711eb..8f6cdeb 100644 --- a/src/user.c +++ b/src/user.c @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * @@ -62,7 +63,7 @@ init_users(in_addr_t my_ip, int netbits) maxusers = (1 << (32-netbits)) - 3; /* 3: Net addr, broadcast addr, iodined addr */ usercount = MIN(maxusers, USERS); - + users = calloc(usercount, sizeof(struct tun_user)); for (i = 0; i < usercount; i++) { in_addr_t ip; @@ -78,6 +79,8 @@ init_users(in_addr_t my_ip, int netbits) users[i].tun_ip = ip; net.s_addr = ip; users[i].disabled = 0; + users[i].authenticated = 0; + users[i].authenticated_raw = 0; users[i].active = 0; /* Rest is reset on login ('V' packet) */ } @@ -93,24 +96,6 @@ users_get_first_ip() return strdup(inet_ntoa(ip)); } -int -users_waiting_on_reply() -{ - int ret; - int i; - - ret = 0; - for (i = 0; i < usercount; 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) { @@ -119,7 +104,9 @@ find_user_by_ip(uint32_t ip) ret = -1; for (i = 0; i < usercount; i++) { - if (users[i].active && !users[i].disabled && + if (users[i].active && + users[i].authenticated && + !users[i].disabled && users[i].last_pkt + 60 > time(NULL) && ip == users[i].tun_ip) { ret = i; @@ -146,8 +133,8 @@ all_users_waiting_to_send() for (i = 0; i < usercount; 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) + ((users[i].conn == CONN_RAW_UDP) || + ((users[i].conn == CONN_DNS_NULL) #ifdef OUTPACKETQ_LEN && users[i].outpacketq_filled < 1 #else @@ -171,6 +158,8 @@ find_available_user() /* 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].authenticated = 0; + users[i].authenticated_raw = 0; users[i].last_pkt = time(NULL); users[i].fragsize = 4096; users[i].conn = CONN_DNS_NULL; @@ -186,7 +175,7 @@ user_switch_codec(int userid, struct encoder *enc) { if (userid < 0 || userid >= usercount) return; - + users[userid].encoder = enc; } @@ -196,9 +185,9 @@ user_set_conn_type(int userid, enum connection c) if (userid < 0 || userid >= usercount) return; - if (c < 0 || c >= CONN_MAX) + if (c < CONN_RAW_UDP || c >= CONN_MAX) return; - + users[userid].conn = c; } - + diff --git a/src/user.h b/src/user.h index 7d02b65..7d2553a 100644 --- a/src/user.h +++ b/src/user.h @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * @@ -36,11 +37,14 @@ struct tun_user { char id; int active; + int authenticated; + int authenticated_raw; int disabled; time_t last_pkt; int seed; in_addr_t tun_ip; - struct in_addr host; + struct sockaddr_storage host; + socklen_t hostlen; struct query q; struct query q_sendrealsoon; int q_sendrealsoon_new; @@ -77,7 +81,6 @@ extern struct tun_user *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(); diff --git a/src/util.c b/src/util.c index 6f2a0bc..ce93879 100644 --- a/src/util.c +++ b/src/util.c @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * @@ -39,12 +40,12 @@ get_resolvconf_addr() rv = addr; pclose(fp); #else - + rv = NULL; - if ((fp = fopen("/etc/resolv.conf", "r")) == NULL) + if ((fp = fopen("/etc/resolv.conf", "r")) == NULL) err(1, "/etc/resolv.conf"); - + while (feof(fp) == 0) { fgets(buf, sizeof(buf), fp); @@ -53,7 +54,7 @@ get_resolvconf_addr() break; } } - + fclose(fp); #endif #else /* !WINDOWS32 */ diff --git a/src/util.h b/src/util.h index 6872077..a0ee03b 100644 --- a/src/util.h +++ b/src/util.h @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson + * + * Permission to use, copy, modify, and/or 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 __UTIL_H__ #define __UTIL_H__ diff --git a/src/version.h b/src/version.h index 69f1143..5843a7a 100644 --- a/src/version.h +++ b/src/version.h @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * diff --git a/src/windows.h b/src/windows.h index db26995..96288d1 100644 --- a/src/windows.h +++ b/src/windows.h @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * diff --git a/tests/base32.c b/tests/base32.c index 7b29fe9..b636a7c 100644 --- a/tests/base32.c +++ b/tests/base32.c @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * @@ -66,7 +67,7 @@ START_TEST(test_base32_decode) char buf[4096]; struct encoder *b32; int val; - + b32 = get_base32_encoder(); len = sizeof(buf); @@ -84,7 +85,7 @@ START_TEST(test_base32_5to8_8to5) int c; for (i = 0; i < 32; i++) { - c = b32_5to8(i); + c = b32_5to8(i); fail_unless(b32_8to5(c) == i); } } diff --git a/tests/base64.c b/tests/base64.c index c32c464..72dac19 100644 --- a/tests/base64.c +++ b/tests/base64.c @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * diff --git a/tests/common.c b/tests/common.c index 2d8122a..2214800 100644 --- a/tests/common.c +++ b/tests/common.c @@ -1,13 +1,113 @@ +/* + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson + * + * Permission to use, copy, modify, and/or 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. + */ + #ifdef HAVE_CONFIG_H #include "config.h" #endif - #include #include #include #include #include +START_TEST(test_topdomain_ok) +{ + char *error; + + fail_if(check_topdomain("foo.0123456789.qwertyuiop.asdfghjkl.zxcvbnm.com", &error)); + + /* Not allowed to start with dot */ + fail_unless(check_topdomain(".foo.0123456789.qwertyuiop.asdfghjkl.zxcvbnm.com", &error)); + fail_if(strcmp("Starts with a dot", error)); + + /* Test missing error msg ptr */ + fail_unless(check_topdomain(".foo", NULL)); +} +END_TEST + +START_TEST(test_topdomain_length) +{ + char *error; + + /* Test empty and too short */ + fail_unless(check_topdomain("", &error)); + fail_if(strcmp("Too short (< 3)", error)); + fail_unless(check_topdomain("a", &error)); + fail_if(strcmp("Too short (< 3)", error)); + fail_unless(check_topdomain(".a", &error)); + fail_if(strcmp("Too short (< 3)", error)); + fail_unless(check_topdomain("a.", &error)); + fail_if(strcmp("Too short (< 3)", error)); + fail_unless(check_topdomain("ab", &error)); + fail_if(strcmp("Too short (< 3)", error)); + fail_if(check_topdomain("a.b", &error)); + fail_if(strcmp("Too short (< 3)", error)); + + /* Test too long (over 128, need rest of space for data) */ + fail_unless(check_topdomain( + "abcd12345.abcd12345.abcd12345.abcd12345.abcd12345." + "abcd12345.abcd12345.abcd12345.abcd12345.abcd12345." + "abcd12345.abcd12345.foo129xxx", &error)); + fail_if(strcmp("Too long (> 128)", error)); + fail_if(check_topdomain( + "abcd12345.abcd12345.abcd12345.abcd12345.abcd12345." + "abcd12345.abcd12345.abcd12345.abcd12345.abcd12345." + "abcd12345.abcd12345.foo128xx", &error)); +} +END_TEST + +START_TEST(test_topdomain_chunks) +{ + char *error; + + /* Must have at least one dot */ + fail_if(check_topdomain("abcde.gh", &error)); + fail_unless(check_topdomain("abcdefgh", &error)); + fail_if(strcmp("No dots", error)); + + /* Not two consecutive dots */ + fail_unless(check_topdomain("abc..defgh", &error)); + fail_if(strcmp("Consecutive dots", error)); + + /* Not end with a dots */ + fail_unless(check_topdomain("abc.defgh.", &error)); + fail_if(strcmp("Ends with a dot", error)); + + /* No chunk longer than 63 chars */ + fail_if(check_topdomain("123456789012345678901234567890" + "123456789012345678901234567890333.com", &error)); + fail_unless(check_topdomain("123456789012345678901234567890" + "1234567890123456789012345678904444.com", &error)); + fail_if(strcmp("Too long domain part (> 63)", error)); + + fail_if(check_topdomain("abc.123456789012345678901234567890" + "123456789012345678901234567890333.com", &error)); + fail_unless(check_topdomain("abc.123456789012345678901234567890" + "1234567890123456789012345678904444.com", &error)); + fail_if(strcmp("Too long domain part (> 63)", error)); + + fail_if(check_topdomain("abc.123456789012345678901234567890" + "123456789012345678901234567890333", &error)); + fail_unless(check_topdomain("abc.123456789012345678901234567890" + "1234567890123456789012345678904444", &error)); + fail_if(strcmp("Too long domain part (> 63)", error)); +} +END_TEST + START_TEST(test_parse_format_ipv4) { char *host = "192.168.2.10"; @@ -106,6 +206,9 @@ test_common_create_tests() int sock; tc = tcase_create("Common"); + tcase_add_test(tc, test_topdomain_ok); + tcase_add_test(tc, test_topdomain_length); + tcase_add_test(tc, test_topdomain_chunks); tcase_add_test(tc, test_parse_format_ipv4); tcase_add_test(tc, test_parse_format_ipv4_listen_all); diff --git a/tests/dns.c b/tests/dns.c index 04ff190..4875a55 100644 --- a/tests/dns.c +++ b/tests/dns.c @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * diff --git a/tests/encoding.c b/tests/encoding.c index 5804e77..2af820e 100644 --- a/tests/encoding.c +++ b/tests/encoding.c @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * @@ -88,7 +89,7 @@ START_TEST(test_build_hostname) } buflen = sizeof(buf); - + for (i = 1; i < sizeof(data); i++) { int len = build_hostname(buf, buflen, data, i, topdomain, get_base32_encoder(), sizeof(buf)); diff --git a/tests/fw_query.c b/tests/fw_query.c index 0892b88..e5f46d7 100644 --- a/tests/fw_query.c +++ b/tests/fw_query.c @@ -1,7 +1,7 @@ /* - * Copyright (c) 2006-2009 Erik Ekman + * Copyright (c) 2009-2014 Erik Ekman * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * @@ -32,7 +32,7 @@ START_TEST(test_fw_query_simple) q.id = 0x848A; fw_query_init(); - + /* Test empty cache */ fw_query_get(0x848A, &qp); fail_unless(qp == NULL); @@ -53,7 +53,7 @@ START_TEST(test_fw_query_edge) int i; fw_query_init(); - + q.addrlen = 33; q.id = 0x848A; fw_query_put(&q); @@ -68,7 +68,7 @@ START_TEST(test_fw_query_edge) fw_query_get(0x848A, &qp); fail_unless(qp->addrlen == 33); fail_unless(qp->id == 0x848A); - + q.addrlen++; q.id++; fw_query_put(&q); diff --git a/tests/login.c b/tests/login.c index 15bd55d..17c2ff8 100644 --- a/tests/login.c +++ b/tests/login.c @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * diff --git a/tests/read.c b/tests/read.c index 387a223..c1d0ab6 100644 --- a/tests/read.c +++ b/tests/read.c @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * @@ -54,7 +55,7 @@ START_TEST(test_read_putshort) i, ntohs(k), i); p = (char*)&k; - readshort(NULL, &p, (short *) &l); + readshort(NULL, &p, &l); fail_unless(l == i, "Bad value on readshort for %d: %d != %d", i, l, i); diff --git a/tests/test.c b/tests/test.c index 04549bf..7857439 100644 --- a/tests/test.c +++ b/tests/test.c @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * diff --git a/tests/test.h b/tests/test.h index 0701886..d3f7985 100644 --- a/tests/test.h +++ b/tests/test.h @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * @@ -28,11 +29,11 @@ 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(...) +#define tcase_set_timeout(...) #endif #endif diff --git a/tests/user.c b/tests/user.c index 2095429..f5e11ae 100644 --- a/tests/user.c +++ b/tests/user.c @@ -1,7 +1,8 @@ /* - * Copyright (c) 2006-2009 Bjorn Andersson , Erik Ekman + * Copyright (c) 2006-2014 Erik Ekman , + * 2006-2009 Bjorn Andersson * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or 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. * @@ -51,30 +52,6 @@ START_TEST(test_init_users) } 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; @@ -86,17 +63,22 @@ START_TEST(test_find_user_by_ip) 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) == -1); + + users[0].authenticated = 1; + testip = (unsigned int) inet_addr("127.0.0.2"); fail_unless(find_user_by_ip(testip) == 0); } @@ -110,15 +92,15 @@ START_TEST(test_all_users_waiting_to_send) 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 @@ -126,7 +108,7 @@ START_TEST(test_all_users_waiting_to_send) #else users[0].outpacket.len = 44; #endif - + fail_unless(all_users_waiting_to_send() == 1); } END_TEST @@ -140,7 +122,11 @@ START_TEST(test_find_available_user) init_users(ip, 27); for (i = 0; i < USERS; i++) { + users[i].authenticated = 1; + users[i].authenticated_raw = 1; fail_unless(find_available_user() == i); + fail_if(users[i].authenticated); + fail_if(users[i].authenticated_raw); } for (i = 0; i < USERS; i++) { @@ -153,7 +139,7 @@ START_TEST(test_find_available_user) fail_unless(find_available_user() == -1); users[3].last_pkt = 55; - + fail_unless(find_available_user() == 3); fail_unless(find_available_user() == -1); } @@ -181,7 +167,7 @@ START_TEST(test_find_available_user_small_net) fail_unless(find_available_user() == -1); users[3].last_pkt = 55; - + fail_unless(find_available_user() == 3); fail_unless(find_available_user() == -1); } @@ -194,7 +180,6 @@ test_user_create_tests() 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);