1
0
mirror of https://github.com/yarrick/iodine.git synced 2025-02-21 14:53:17 +03:00

Merge remote-tracking branch 'yarrick/master' into autotools

only non-trivial conflict is the CC option -std=c99, which I defer
This commit is contained in:
Barak A. Pearlmutter 2016-12-28 11:15:45 +01:00
commit 11b6da12ff
51 changed files with 1810 additions and 1149 deletions

View File

@ -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.

379
README
View File

@ -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 <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
Also major contributions by Anne Bezemer.
Permission to use, copy, modify, and distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
MD5 implementation by L. Peter Deutsch (license and source in src/md5.[ch])
Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.

View File

@ -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:
"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/<arch>/iodine

View File

@ -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

408
README.md Normal file
View File

@ -0,0 +1,408 @@
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, 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
<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
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 <yarrick@kryo.se>, 2006-2009 Bjorn
Andersson <flex@kryo.se>. 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.

View File

@ -3,6 +3,8 @@ Description=Iodine socket
[Socket]
ListenDatagram=53
ListenDatagram=0.0.0.0:53
BindIPv6Only=ipv6-only
[Install]
WantedBy=sockets.target

View File

@ -50,14 +50,16 @@ 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:
@ -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
@ -188,8 +191,8 @@ 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

View File

@ -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
@ -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,7 +126,8 @@ 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,
@ -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.
@ -261,13 +268,17 @@ 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
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

View File

@ -1,7 +1,25 @@
/*
* Copyright (c) 2009 Marcel Bokhorst <marcel@bokhorst.biz>
*
* 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

View File

@ -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.
*

View File

@ -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.
*

View File

@ -1,8 +1,9 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
* 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.
*

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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.
*

View File

@ -1,8 +1,9 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
* 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.
*

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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.
*

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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 <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <signal.h>
#include <unistd.h>
#include <sys/param.h>
@ -34,10 +36,10 @@
#ifdef WINDOWS32
#include "windows.h"
#else
#include <arpa/nameser.h>
#ifdef ANDROID
#include "android_dns.h"
#endif
#include <arpa/nameser.h>
#ifdef DARWIN
#define BIND_8_COMPAT
#include <arpa/nameser_compat.h>
@ -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;
@ -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';
@ -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.
@ -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;
}
@ -1514,19 +1533,13 @@ handshake_raw_udp(int dns_fd, int seed)
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
* based on the old seed. If reply received,
* switch to raw udp mode */
@ -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 */
@ -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) {

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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);

View File

@ -1,7 +1,8 @@
/* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
/* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
* Copyright (c) 2007 Albert Lee <trisk@acm.jhu.edu>.
*
* 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.
*
@ -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;
@ -176,7 +177,13 @@ get_addr(char *host, int port, int addr_family, int flags, struct sockaddr_stora
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;
@ -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)
@ -355,12 +409,9 @@ inet_aton(const char *cp, struct in_addr *inp)
#endif
void
warn(const char *fmt, ...)
vwarn(const char *fmt, va_list list)
{
va_list list;
va_start(list, fmt);
if (fmt) fprintf(stderr, fmt, list);
if (fmt) vfprintf(stderr, fmt, list);
#ifndef ANDROID
if (errno == 0) {
fprintf(stderr, ": WSA error %d\n", WSAGetLastError());
@ -368,17 +419,32 @@ warn(const char *fmt, ...)
fprintf(stderr, ": %s\n", strerror(errno));
}
#endif
}
void
warn(const char *fmt, ...)
{
va_list list;
va_start(list, fmt);
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

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2015 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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,8 +67,10 @@ 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
{
@ -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

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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 <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#ifdef WINDOWS32
#include "windows.h"
#else
#ifdef ANDROID
#include "android_dns.h"
#endif
#include <arpa/nameser.h>
#ifdef DARWIN
#define BIND_8_COMPAT
@ -40,6 +39,9 @@
#include <netinet/in.h>
#include <arpa/inet.h>
#include <err.h>
#ifdef ANDROID
#include "android_dns.h"
#endif
#endif
@ -406,10 +408,10 @@ 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;
unsigned short rlen;
int id;
int rv;
@ -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;

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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.
*

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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.
*

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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.
*

View File

@ -1,7 +1,7 @@
/*
* Copyright (c) 2008 Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2008-2014 Erik Ekman <yarrick@kryo.se>
*
* 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.
*

View File

@ -1,7 +1,7 @@
/*
* Copyright (c) 2008 Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2008-2014 Erik Ekman <yarrick@kryo.se>
*
* 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.
*

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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.
*
@ -61,6 +62,12 @@ 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
@ -244,10 +253,11 @@ main(int argc, char **argv)
pidfile = optarg;
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);
@ -307,14 +317,8 @@ main(int argc, char **argv)
/* 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 */
}
@ -347,7 +351,7 @@ main(int argc, char **argv)
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;
}

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2015 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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 <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <signal.h>
#include <unistd.h>
#include <sys/param.h>
@ -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);
@ -176,11 +209,10 @@ 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 ) {
@ -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
@ -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;
@ -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;
}
@ -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];
@ -756,12 +825,11 @@ 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();
@ -838,8 +906,10 @@ 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;
@ -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 */
@ -905,7 +977,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 */
}
@ -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,7 +1146,7 @@ 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 */
}
@ -1109,7 +1181,7 @@ 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 */
}
@ -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 */
}
@ -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 */
}
@ -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);
@ -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);
@ -1571,13 +1646,14 @@ forward_query(int bind_fd, struct query *q)
}
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);
@ -1607,6 +1683,7 @@ tunnel_bind(int bind_fd, int dns_fd)
format_addr(&query->addr, query->addrlen), (id & 0xffff), r);
}
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,14 +1693,14 @@ 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) {
@ -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 */
@ -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];
@ -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);
@ -1848,10 +1942,11 @@ handle_raw_login(char *packet, int len, struct query *q, int fd, int userid)
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,7 +2087,7 @@ 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) {
@ -1997,13 +2095,29 @@ read_dns(int fd, int tun_fd, struct query *q) /* FIXME: tun_fd is because of raw
}
#ifndef WINDOWS32
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) {
q->destination = *dstaddr(cmsg);
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;
}
}
@ -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,7 +2385,8 @@ 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
@ -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;
@ -2312,8 +2458,14 @@ main(int argc, char **argv)
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);
@ -2406,14 +2561,10 @@ main(int argc, char **argv)
}
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) {
@ -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();
@ -2497,42 +2656,80 @@ 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;
}
}
@ -2577,16 +2774,16 @@ main(int argc, char **argv)
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:
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);
cleanup0:
return retval;
}

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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.
*

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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.
*

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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,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

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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);

347
src/tun.c
View File

@ -1,7 +1,9 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
* 2013 Peter Sagerson <psagers.github@ignorare.net>
*
* 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 <sys/stat.h>
#include <fcntl.h>
#ifdef DARWIN
#include <ctype.h>
#include <sys/kern_control.h>
#include <sys/sys_domain.h>
#include <sys/ioctl.h>
#include <net/if_utun.h>
#include <netinet/ip.h>
#endif
#ifndef IFCONFIGPATH
#define IFCONFIGPATH "PATH=/sbin:/bin "
#endif
@ -36,8 +47,8 @@
#include "windows.h"
#include <winioctl.h>
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,9 +74,8 @@ 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 <sys/ioctl.h>
@ -85,7 +95,7 @@ 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;
}
@ -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)
{
@ -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);
@ -355,6 +326,139 @@ open_tun(const char *tun_device)
return tunfd;
}
#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
@ -364,76 +468,114 @@ close_tun(int tun_fd)
close(tun_fd);
}
#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)
{
#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++) {
@ -479,7 +625,6 @@ tun_setip(const char *ip, const char *other_ip, int netbits)
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);

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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.
*

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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.
*
@ -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;
@ -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;
@ -196,7 +185,7 @@ 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;

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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();

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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.
*

View File

@ -1,3 +1,20 @@
/*
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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__

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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.
*

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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.
*

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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.
*

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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.
*

View File

@ -1,13 +1,113 @@
/*
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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 <check.h>
#include <common.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netdb.h>
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);

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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.
*

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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.
*

View File

@ -1,7 +1,7 @@
/*
* Copyright (c) 2006-2009 Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2009-2014 Erik Ekman <yarrick@kryo.se>
*
* 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.
*

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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.
*

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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);

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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.
*

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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.
*

View File

@ -1,7 +1,8 @@
/*
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
* 2006-2009 Bjorn Andersson <flex@kryo.se>
*
* 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;
@ -97,6 +74,11 @@ START_TEST(test_find_user_by_ip)
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);
}
@ -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++) {
@ -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);