mirror of
https://github.com/yarrick/iodine.git
synced 2024-11-28 12:35:13 +00:00
Applied patch from #88, thanks a lot!
This commit is contained in:
parent
1a26a91db3
commit
b177901d38
299
README
299
README
@ -11,12 +11,12 @@ firewalled, but DNS queries are allowed.
|
|||||||
QUICKSTART:
|
QUICKSTART:
|
||||||
|
|
||||||
Try it out within your own LAN! Follow these simple steps:
|
Try it out within your own LAN! Follow these simple steps:
|
||||||
- On your server, run: ./iodined -f 10.0.0.1 test.asdf
|
- 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
|
(If you already use the 10.0.0.0 network, use another internal net like
|
||||||
172.16.0.0)
|
172.16.0.0)
|
||||||
- Enter a password
|
- Enter a password
|
||||||
- On the client, run: ./iodine -f 192.168.0.1 test.asdf
|
- On the client, run: ./iodine -f -r 192.168.0.1 test.com
|
||||||
(Replace 192.168.0.1 with the server's ip address)
|
(Replace 192.168.0.1 with your server's ip address)
|
||||||
- Enter the same password
|
- Enter the same password
|
||||||
- Now the client has the tunnel ip 10.0.0.2 and the server has 10.0.0.1
|
- 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
|
- Try pinging each other through the tunnel
|
||||||
@ -26,113 +26,196 @@ To actually use it through a relaying nameserver, see below.
|
|||||||
|
|
||||||
HOW TO USE:
|
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:
|
Server side:
|
||||||
To use this tunnel, you need control over a real domain (like mytunnel.com),
|
To use this tunnel, you need control over a real domain (like mydomain.com),
|
||||||
and a server with a public IP number. If the server already runs a DNS
|
and a server with a public IP address to run iodined on. If this server
|
||||||
server, change the listening port and then use the -b option to let
|
already runs a DNS program, change its listening port and then use iodined's
|
||||||
iodined forward the DNS requests. Then, delegate a subdomain
|
-b option to let iodined forward the DNS requests. (Note that this procedure
|
||||||
(say, tunnel1.mytunnel.com) to the server. If you use BIND for the domain,
|
is not advised in production environments, because iodined's DNS forwarding
|
||||||
add these lines to the zone file:
|
is not completely transparent.)
|
||||||
|
|
||||||
tunnel1host IN A 10.15.213.99
|
Then, delegate a subdomain (say, t1.mydomain.com) to the iodined server.
|
||||||
tunnel1 IN NS tunnel1host.mytunnel.com.
|
If you use BIND for your domain, add two lines like these to the zone file:
|
||||||
|
|
||||||
Do not use CNAME instead of A above.
|
t1 IN NS t1ns.mydomain.com. ; note the dot!
|
||||||
If your server has a dynamic IP, use a dynamic dns provider:
|
t1ns IN A 10.15.213.99
|
||||||
|
|
||||||
tunnel1 IN NS tunnel1host.mydyndnsprovider.com
|
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).
|
||||||
|
|
||||||
Now any DNS querys for domains ending with tunnel1.mytunnnel.com will be sent
|
If your iodined server has a dynamic IP, use a dynamic dns provider. Simply
|
||||||
to your server. Start iodined on the server. The first argument is the tunnel
|
point the "NS" line to it, and leave the "A" line out:
|
||||||
IP address (like 192.168.99.1) and the second is the assigned domain (in this
|
|
||||||
case tunnel1.mytunnel.com). The -f argument will keep iodined running in the
|
t1 IN NS myname.mydyndnsprovider.com. ; note the dot!
|
||||||
foreground, which helps when testing. iodined will start a virtual interface,
|
|
||||||
and also start listening for DNS queries on UDP port 53. Either enter a
|
Then reload or restart your nameserver program. Now any DNS queries for
|
||||||
password on the commandline (-P pass) or after the server has started. Now
|
domains ending in t1.mydomain.com will be sent to your iodined server.
|
||||||
everything is ready for the client.
|
|
||||||
|
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:
|
Client side:
|
||||||
All the setup is done, just start iodine. It takes up to two arguments, the
|
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
|
first is the local relaying DNS server (optional) and the second is the domain
|
||||||
used (tunnel1.mytunnnel.com). If DNS queries are allowed to any computer, you
|
you used (t1.mydomain.com). If you don't specify the first argument, the
|
||||||
can use the tunnel endpoint (example: 10.15.213.99 or tunnel1host.mytunnel.com)
|
system's current DNS setting will be consulted.
|
||||||
as the first argument. The tunnel interface will get an IP close to the servers
|
|
||||||
(in this case 192.168.99.2) and a suitable MTU. Enter the same password as on
|
If DNS queries are allowed to any computer, you can directly give the iodined
|
||||||
the server either by argument or after the client has started. Now you should
|
server's address as first argument (in the example: t1ns.mydomain.com or
|
||||||
be able to ping the other end of the tunnel from either side.
|
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:
|
MISC. INFO:
|
||||||
|
|
||||||
Routing:
|
Routing:
|
||||||
The normal case is to route all traffic through the DNS tunnel. To do this, first
|
It is possible to route all traffic through the DNS tunnel. To do this, first
|
||||||
add a route to the nameserver you use with the default gateway as gateway. Then
|
add a host route to the nameserver used by iodine over the wired/wireless
|
||||||
replace the default gateway with the servers IP address within the DNS tunnel,
|
interface with the default gateway as gateway. Then replace the default
|
||||||
and configure the server to do NAT.
|
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.
|
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.
|
To force a specific value (and speed things up), use the -m option.
|
||||||
|
|
||||||
The iodined server replies to NS requests sent for subdomains of the tunnel
|
The DNS hostnames are normally used up to their maximum length, 255 characters.
|
||||||
domain. If your domain is tunnel.com, send a NS request for foo.tunnel.com
|
Some DNS relays have been found that answer full-length queries rather
|
||||||
to see if the delegation works. dig is a good tool for this:
|
unreliably, giving widely varying (and mostly very bad) results of the
|
||||||
dig -t NS foo123.tunnel.com
|
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
|
The upstream data is sent gzipped encoded with Base32; or Base64 if the relay
|
||||||
server support '+' in domain names. DNS protocol allows one query per packet,
|
server supports mixed case and '+' in domain names; or Base64u if '_' is
|
||||||
and one query can be max 256 chars. Each domain name part can be max 63 chars.
|
supported instead; or Base128 if high-byte-value characters are supported.
|
||||||
So your domain name and subdomain should be as short as possible to allow
|
This upstream encoding is autodetected. The DNS protocol allows one query per
|
||||||
maximum upstream throughput.
|
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.
|
||||||
|
|
||||||
The default is to use DNS NULL-type queries, as this provides the largest
|
Several DNS request types are supported, with the NULL type expected to provide
|
||||||
downstream bandwidth. If your DNS server blocks NULL requests, try TXT or
|
the largest downstream bandwidth. Other available types are TXT, SRV, MX,
|
||||||
CNAME queries via the -T option. Also supported are A (returning CNAME) and
|
CNAME and A (returning CNAME), in decreasing bandwidth order. Normally the
|
||||||
MX requests, but these may/will cause additional lookups by "smart" caching
|
"best" request type is autodetected and used. However, DNS relays may impose
|
||||||
nameservers to get an actual IP address, which may either slow down or fail
|
limits on for example NULL and TXT, making SRV or MX actually the best choice.
|
||||||
completely. DNS responses for non-NULL are Base32 encoded by default, which
|
This is not autodetected, but can be forced using the -T option. It is
|
||||||
should always work. For more bandwidth, try Base64 or Raw (TXT only) via the
|
advisable to try various alternatives especially when the autodetected request
|
||||||
-O option. If Base64/Raw doesn't work, you'll see many failures in the
|
type provides a downstream fragment size of less than 200 bytes.
|
||||||
fragment size autoprobe.
|
|
||||||
|
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
|
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
|
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.
|
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
|
This greatly improves (interactive) performance and latency, and allows to
|
||||||
slow down the quiescent ping requests to 4 second intervals by default.
|
slow down the quiescent ping requests to 4 second intervals by default, and
|
||||||
In fact, the main purpose of the pings now is to force a reply to the previous
|
possibly much slower. In fact, the main purpose of the pings now is to force
|
||||||
ping, and prevent DNS server timeouts (usually 5-10 seconds per RFC1035).
|
a reply to the previous ping, and prevent DNS server timeouts (usually at
|
||||||
In the unlikely case that you do experience DNS server timeouts (SERVFAIL),
|
least 5-10 seconds per RFC1035). Some DNS servers are more impatient and will
|
||||||
decrease the -I option to 1. If you are running on a local network without
|
give SERVFAIL errors (timeouts) in periods without tunneled data traffic. All
|
||||||
any DNS server in-between, try -I 50 (iodine and iodined time out after 60
|
data should still get through in these cases, but iodine will reduce the ping
|
||||||
seconds). The only time you'll notice a slowdown, is when DNS reply packets
|
interval to 1 second anyway (-I1) to reduce the number of error messages. This
|
||||||
go missing; the iodined server then has to wait for a new ping to re-send the
|
may not help for very impatient DNS relays like dnsadvantage.com (ultradns),
|
||||||
data. You can speed this up by generating some upstream traffic (keypress,
|
which time out in 1 second or even less. Yet data will still get trough, and
|
||||||
ping). If this happens often, check your network for bottlenecks and/or run
|
you can ignore the SERVFAIL errors.
|
||||||
with -I1 .
|
|
||||||
|
|
||||||
Some DNS servers appear to be quite impatient and start retrying DNS requests
|
If you are running on a local network without any DNS server in-between, try
|
||||||
(with _different_ DNS ids!) when an answer does not appear within a few
|
-I 50 (iodine and iodined close the connection after 60 seconds of silence).
|
||||||
milliseconds. Usually they scale back retries when iodined's lazy mode
|
The only time you'll notice a slowdown, is when DNS reply packets go missing;
|
||||||
repeatedly takes several seconds to answer; and they scale up retries again
|
the iodined server then has to wait for a new ping to re-send the data. You can
|
||||||
when iodined answers fast during heavy data transfer. Some commercial DNS
|
speed this up by generating some upstream traffic (keypress, ping). If this
|
||||||
servers advertise this as "carrier-grade adaptive retransmission techniques".
|
happens often, check your network for bottlenecks and/or run with -I1.
|
||||||
The effect will only be visible in the network traffic at the iodined server,
|
|
||||||
and will not affect the client's connection. Iodined has rather elaborate
|
|
||||||
logic to deal with (i.e., ignore) these unwanted duplicates.
|
|
||||||
|
|
||||||
Other DNS servers, notably the opendns.com network, seem to regard iodined's
|
The delayed answering in lazy mode will cause some "carrier grade" commercial
|
||||||
lazyness as incompetency, and will start shuffling requests around, possibly
|
DNS relays to repeatedly re-send the same DNS query to the iodined server.
|
||||||
in an attempt to reduce iodined's workload. The resulting out-of-sequence DNS
|
If the DNS relay is actually implemented as a pool of parallel servers,
|
||||||
traffic works quite badly for lazy mode. The iodine client will detect this,
|
duplicate requests may even arrive from multiple sources. This effect will
|
||||||
and switch back to legacy mode ("immediate ping-pong") automatically. In these
|
only be visible in the network traffic at the iodined server, and will not
|
||||||
cases, start the iodine client with -L0 to prevent it from operating in lazy
|
affect the client's connection. Iodined will notice these duplicates, and send
|
||||||
mode altogether. Note that this will negatively affect interactive performance
|
the same answer (when its time has come) to both the original query and the
|
||||||
and latency, especially in the downstream direction.
|
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
|
If you have problems, try inspecting the traffic with network monitoring tools
|
||||||
and make sure that the relaying DNS server has not cached the response. A
|
like tcpdump or ethereal/wireshark, and make sure that the relaying DNS server
|
||||||
cached error message could mean that you started the client before the server.
|
has not cached the response. A cached error message could mean that you
|
||||||
The -D (and -DD) option on the server can also show received and sent queries.
|
started the client before the server. The -D (and -DD) option on the server
|
||||||
|
can also show received and sent queries.
|
||||||
|
|
||||||
|
|
||||||
TIPS & TRICKS:
|
TIPS & TRICKS:
|
||||||
@ -165,13 +248,16 @@ PERFORMANCE:
|
|||||||
This section tabulates some performance measurements. To view properly, use
|
This section tabulates some performance measurements. To view properly, use
|
||||||
a fixed-width font like Courier.
|
a fixed-width font like Courier.
|
||||||
|
|
||||||
Measurements were done in protocol 00000500 with lazy mode unless indicated
|
Measurements were done in protocol 00000502 in lazy mode; upstream encoding
|
||||||
otherwise. Upstream encoding always Base64.
|
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
|
Upstream/downstream throughput was measured by scp'ing a file previously
|
||||||
read from /dev/urandom (i.e. incompressible), and measuring size with
|
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
|
"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
|
large scp block size of 16 kB, this gives a resolution of 4.3 kbit/s, which
|
||||||
explains why many values are exactly equal.
|
explains why some values are exactly equal.
|
||||||
Ping round-trip times measured with "ping -c100", presented are average rtt
|
Ping round-trip times measured with "ping -c100", presented are average rtt
|
||||||
and mean deviation (indicating spread around the average), in milliseconds.
|
and mean deviation (indicating spread around the average), in milliseconds.
|
||||||
|
|
||||||
@ -185,43 +271,28 @@ Laptop -> Wifi AP -> Home server -> DSL provider -> Datacenter
|
|||||||
------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
iodine -> Wifi AP :53
|
iodine -> Wifi AP :53
|
||||||
-Tnull (= -Oraw) 982 39.3 148.5 26.7 3.1 26.6 3.0
|
-Tnull (= -Oraw) 982 43.6 131.0 28.0 4.6 26.8 3.4
|
||||||
|
|
||||||
iodine -> Home server :53
|
iodine -> Home server :53
|
||||||
-Tnull (= -Oraw) 1174 43.6 174.7 25.2 4.0 25.5 3.4
|
-Tnull (= -Oraw) 1174 48.0 305.8 26.6 5.0 26.9 8.4
|
||||||
|
|
||||||
iodine -> DSL provider :53
|
iodine -> DSL provider :53
|
||||||
-Tnull (= -Oraw) 1174 52.4 200.9 20.3 3.2 20.3 2.7
|
-Tnull (= -Oraw) 1174 56.7 367.0 20.6 3.1 21.2 4.4
|
||||||
-Ttxt -Obase32 730 52.4 192.2*
|
-Ttxt -Obase32 730 56.7 174.7*
|
||||||
-Ttxt -Obase64 874 52.4 192.2
|
-Ttxt -Obase64 874 56.7 174.7
|
||||||
-Ttxt -Oraw 1162 52.4 192.2
|
-Ttxt -Obase128 1018 56.7 174.7
|
||||||
-Tcname -Obase32 148 52.4 48.0
|
-Ttxt -Oraw 1162 56.7 358.2
|
||||||
-Tcname -Obase64 181 52.4 61.1
|
-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
|
iodine -> DSL provider :53
|
||||||
wired (no Wifi) -Tnull 1174 65.5 244.6 17.7 1.9 17.8 1.6
|
wired (no Wifi) -Tnull 1174 74.2 585.4 20.2 5.6 19.6 3.4
|
||||||
|
|
||||||
[192.2* : nice, because still 2frag/packet]
|
[174.7* : these all have 2frag/packet]
|
||||||
|
|
||||||
|
|
||||||
Situation 2:
|
Situation 2:
|
||||||
Laptop -> (wire) -> (Home server) -> (DSL) -> opendns.com -> Datacenter
|
|
||||||
iodine DNS cache iodined
|
|
||||||
|
|
||||||
downstr. upstream downstr. ping-up ping-down
|
|
||||||
fragsize kbit/s kbit/s avg +/-mdev avg +/-mdev
|
|
||||||
------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
iodine -> opendns.com :53
|
|
||||||
-Tnull -L1 (lazy mode) 230 - - 404.4 196.2 663.8 679.6
|
|
||||||
(20% lost) (2% lost)
|
|
||||||
|
|
||||||
-Tnull -L0 (legacy mode) 230 5.6 7.4 197.3 4.7 610.8 323.5
|
|
||||||
|
|
||||||
[Note: Throughput measured over 300 seconds to get better resolution]
|
|
||||||
|
|
||||||
|
|
||||||
Situation 3:
|
|
||||||
Laptop -> Wifi+vpn / wired -> Home server
|
Laptop -> Wifi+vpn / wired -> Home server
|
||||||
iodine iodined
|
iodine iodined
|
||||||
|
|
||||||
@ -229,9 +300,9 @@ Laptop -> Wifi+vpn / wired -> Home server
|
|||||||
fragsize kbit/s kbit/s avg +/-mdev avg +/-mdev
|
fragsize kbit/s kbit/s avg +/-mdev avg +/-mdev
|
||||||
------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
wifi + openvpn -Tnull 1186 183.5 611.6 5.7 1.4 7.0 2.7
|
wifi + openvpn -Tnull 1186 166.0 1022.3 6.3 1.3 6.6 1.6
|
||||||
|
|
||||||
wired -Tnull 1186 685.9 2350.5 1.3 0.1 1.4 0.4
|
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
|
Performance is strongly coupled to low ping times, as iodine requires
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
Detailed specification of protocol in version 00000501
|
Detailed specification of protocol in version 00000502
|
||||||
======================================================
|
======================================================
|
||||||
|
|
||||||
Note: work in progress!!
|
Note: work in progress!!
|
||||||
@ -7,6 +7,22 @@ Note: work in progress!!
|
|||||||
1. DNS protocol
|
1. DNS protocol
|
||||||
======================================================
|
======================================================
|
||||||
|
|
||||||
|
Quick alphabetical index / register:
|
||||||
|
0-9 Data packet
|
||||||
|
A-F Data packet
|
||||||
|
I IP address
|
||||||
|
L Login
|
||||||
|
N Downstream fragsize (NS.topdomain A-type reply)
|
||||||
|
O Options
|
||||||
|
P Ping
|
||||||
|
R Downstream fragsize probe
|
||||||
|
S Switch upstream codec
|
||||||
|
V Version
|
||||||
|
W (WWW.topdomain A-type reply)
|
||||||
|
Y Downstream codec check
|
||||||
|
Z Upstream codec check
|
||||||
|
|
||||||
|
|
||||||
CMC = 2 byte Cache Miss Counter, increased every time it is used
|
CMC = 2 byte Cache Miss Counter, increased every time it is used
|
||||||
|
|
||||||
Version:
|
Version:
|
||||||
@ -38,29 +54,55 @@ IP Request:
|
|||||||
Client sends:
|
Client sends:
|
||||||
First byte i or I
|
First byte i or I
|
||||||
5 bits coded as Base32 char, meaning userid
|
5 bits coded as Base32 char, meaning userid
|
||||||
CMC
|
CMC as 3 Base32 chars
|
||||||
Server replies
|
Server replies
|
||||||
BADIP if bad userid, or
|
BADIP if bad userid, or
|
||||||
I and then 4 bytes network order external IP address of iodined server
|
I and then 4 bytes network order external IP address of iodined server
|
||||||
|
|
||||||
Case check:
|
Upstream codec check / bounce:
|
||||||
Client sends:
|
Client sends:
|
||||||
First byte z or Z
|
First byte z or Z
|
||||||
Lots of data that should not be decoded
|
Lots of data that should not be decoded
|
||||||
Server replies:
|
Server replies:
|
||||||
The requested domain copied raw
|
The requested domain copied raw, in the lowest-grade downstream codec
|
||||||
|
available for the request type.
|
||||||
|
|
||||||
|
Downstream codec check:
|
||||||
|
Client sends:
|
||||||
|
First byte y or Y
|
||||||
|
1 char, meaning downstream codec to use
|
||||||
|
5 bits coded as Base32 char, meaning check variant
|
||||||
|
CMC as 3 Base32 chars
|
||||||
|
Possibly extra data, depending on check variant
|
||||||
|
Server sends:
|
||||||
|
Data encoded with requested downstream codec; data content depending
|
||||||
|
on check variant number.
|
||||||
|
BADCODEC if requested downstream codec not available.
|
||||||
|
BADLEN if check variant is not available, or problem with extra data.
|
||||||
|
|
||||||
|
Downstream codec chars are same as in 'O' Option request, below.
|
||||||
|
|
||||||
|
Check variants:
|
||||||
|
1: Send encoded DOWNCODECCHECK1 string as defined in encoding.h
|
||||||
|
|
||||||
|
(Other variants reserved; possibly variant that sends a decoded-encoded
|
||||||
|
copy of Base32-encoded extra data in the request)
|
||||||
|
|
||||||
Switch codec:
|
Switch codec:
|
||||||
Client sends:
|
Client sends:
|
||||||
First byte s or S
|
First byte s or S
|
||||||
5 bits coded as Base32 char, meaning userid
|
5 bits coded as Base32 char, meaning userid
|
||||||
5 bits coded as Base32 char, with value 5 or 6, representing number of raw
|
5 bits coded as Base32 char, representing number of raw bits per
|
||||||
bits per encoded byte
|
encoded byte:
|
||||||
CMC
|
5: Base32 (a-z0-5)
|
||||||
|
6: Base64 (a-zA-Z0-9+-)
|
||||||
|
26: Base64u (a-zA-Z0-9_-)
|
||||||
|
7: Base128 (a-zA-Z0-9\274-\375)
|
||||||
|
CMC as 3 Base32 chars
|
||||||
Server sends:
|
Server sends:
|
||||||
Name of codec if accepted. After this all upstream data packets must
|
Name of codec if accepted. After this all upstream data packets must
|
||||||
be encoded with the new codec.
|
be encoded with the new codec.
|
||||||
BADCODEC if not accepted. Client must then revert to Base32
|
BADCODEC if not accepted. Client must then revert to previous codec
|
||||||
BADLEN if length of query is too short
|
BADLEN if length of query is too short
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
@ -68,6 +110,7 @@ Client sends:
|
|||||||
First byte o or O
|
First byte o or O
|
||||||
5 bits coded as Base32 char, meaning userid
|
5 bits coded as Base32 char, meaning userid
|
||||||
1 char, meaning option
|
1 char, meaning option
|
||||||
|
CMC as 3 Base32 chars
|
||||||
Server sends:
|
Server sends:
|
||||||
Full name of option if accepted. After this, option immediately takes
|
Full name of option if accepted. After this, option immediately takes
|
||||||
effect in server.
|
effect in server.
|
||||||
@ -77,6 +120,8 @@ Server sends:
|
|||||||
Option chars:
|
Option chars:
|
||||||
t or T: Downstream encoding Base32, for TXT/CNAME/A/MX (default)
|
t or T: Downstream encoding Base32, for TXT/CNAME/A/MX (default)
|
||||||
s or S: Downstream encoding Base64, for TXT/CNAME/A/MX
|
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 TXT/NULL (default for NULL)
|
||||||
If codec unsupported for request type, server will use Base32; note
|
If codec unsupported for request type, server will use Base32; note
|
||||||
that server will answer any mix of request types that a client sends.
|
that server will answer any mix of request types that a client sends.
|
||||||
@ -96,8 +141,10 @@ Client sends:
|
|||||||
meaning 4 bits userid, 11 bits fragment size
|
meaning 4 bits userid, 11 bits fragment size
|
||||||
Then follows a long random query which contents does not matter
|
Then follows a long random query which contents does not matter
|
||||||
Server sends:
|
Server sends:
|
||||||
Requested number of bytes as a response. The first two bytes contains
|
Requested number of bytes as a response. The first two bytes contain
|
||||||
the requested length. Rest of message can be any data.
|
the requested length. The third byte is 107 (0x6B). The fourth byte
|
||||||
|
is a random value, and each following byte is incremented with 107.
|
||||||
|
This is checked by the client to determine corruption.
|
||||||
BADFRAG if requested length not accepted.
|
BADFRAG if requested length not accepted.
|
||||||
|
|
||||||
Set downstream fragment size:
|
Set downstream fragment size:
|
||||||
@ -114,10 +161,10 @@ Server sends:
|
|||||||
|
|
||||||
Data:
|
Data:
|
||||||
Upstream data header:
|
Upstream data header:
|
||||||
3210 432 10 43 210 4321 0
|
3210 432 10 43 210 4321 0 43210
|
||||||
+----+---+--+--+---+----+-+
|
+----+---+--+--+---+----+-+-----+
|
||||||
|UUUU|SSS|FF|FF|DDD|GGGG|L|
|
|UUUU|SSS|FF|FF|DDD|GGGG|L|UDCMC|
|
||||||
+----+---+--+--+---+----+-+
|
+----+---+--+--+---+----+-+-----+
|
||||||
|
|
||||||
Downstream data header:
|
Downstream data header:
|
||||||
7 654 3210 765 4321 0
|
7 654 3210 765 4321 0
|
||||||
@ -132,9 +179,11 @@ FFFF = Upstream fragment number
|
|||||||
DDD = Downstream packet sequence number
|
DDD = Downstream packet sequence number
|
||||||
GGGG = Downstream fragment number
|
GGGG = Downstream fragment number
|
||||||
C = Compression enabled for downstream packet
|
C = Compression enabled for downstream packet
|
||||||
|
UDCMC = Upstream Data CMC, 36 steps a-z0-9, case-insensitive
|
||||||
|
|
||||||
Upstream data packet starts with 1 byte ASCII hex coded user byte, then 3 bytes
|
Upstream data packet starts with 1 byte ASCII hex coded user byte; then 3 bytes
|
||||||
Base32 encoded header, then comes the payload data, encoded with chosen codec.
|
Base32 encoded header; then 1 char data-CMC; then comes the payload data,
|
||||||
|
encoded with the chosen upstream codec.
|
||||||
|
|
||||||
Downstream data starts with 2 byte header. Then payload data, which may be
|
Downstream data starts with 2 byte header. Then payload data, which may be
|
||||||
compressed.
|
compressed.
|
||||||
@ -147,10 +196,18 @@ TXT:
|
|||||||
<=255 bytes)
|
<=255 bytes)
|
||||||
t or T: Base32 encoded before chop, decoded after un-chop
|
t or T: Base32 encoded before chop, decoded after un-chop
|
||||||
s or S: Base64 encoded before chop, decoded after un-chop
|
s or S: Base64 encoded before chop, decoded after un-chop
|
||||||
|
u or U: Base64u encoded before chop, decoded after un-chop
|
||||||
|
v or V: Base128 encoded before chop, decoded after un-chop
|
||||||
r or R: Raw no encoding, only DNS-chop
|
r or R: Raw no encoding, only DNS-chop
|
||||||
CNAME/A/MX:
|
SRV/MX/CNAME/A:
|
||||||
h or H: Hostname encoded with Base32
|
h or H: Hostname encoded with Base32
|
||||||
i or I: Hostname encoded with Base64
|
i or I: Hostname encoded with Base64
|
||||||
|
j or J: Hostname encoded with Base64u
|
||||||
|
k or K: Hostname encoded with Base128
|
||||||
|
SRV and MX may reply with multiple hostnames, each encoded separately. Each
|
||||||
|
has a 10-multiple priority, and encoding/decoding is done in strictly
|
||||||
|
increasing priority sequence 10, 20, 30, etc. without gaps. Note that some DNS
|
||||||
|
relays will shuffle the answer records in the response.
|
||||||
|
|
||||||
Ping:
|
Ping:
|
||||||
Client sends:
|
Client sends:
|
||||||
@ -162,10 +219,11 @@ Client sends:
|
|||||||
4 bits downstream fragment
|
4 bits downstream fragment
|
||||||
CMC
|
CMC
|
||||||
|
|
||||||
The server response to Ping and Data packets is a DNS NULL type response:
|
The server response to Ping and Data packets is a DNS NULL/TXT/.. type response,
|
||||||
If server has nothing to send, data length is 0 bytes.
|
always starting with the 2 bytes downstream data header as shown above.
|
||||||
If server has something to send, it will send a downstream data packet,
|
If server has nothing to send, no data is added after the header.
|
||||||
prefixed with 2 bytes header as shown above.
|
If server has something to send, it will add the downstream data packet
|
||||||
|
(or some fragment of it) after the header.
|
||||||
|
|
||||||
|
|
||||||
"Lazy-mode" operation
|
"Lazy-mode" operation
|
185
man/iodine.8
185
man/iodine.8
@ -1,5 +1,5 @@
|
|||||||
.\" groff -man -Tascii iodine.8
|
.\" groff -man -Tascii iodine.8
|
||||||
.TH IODINE 8 "SEP 2009" "User Manuals"
|
.TH IODINE 8 "DEC 2009" "User Manuals"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
iodine, iodined \- tunnel IPv4 over DNS
|
iodine, iodined \- tunnel IPv4 over DNS
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
@ -19,6 +19,8 @@ iodine, iodined \- tunnel IPv4 over DNS
|
|||||||
.I device
|
.I device
|
||||||
.B ] [-m
|
.B ] [-m
|
||||||
.I fragsize
|
.I fragsize
|
||||||
|
.B ] [-M
|
||||||
|
.I namelen
|
||||||
.B ] [-z
|
.B ] [-z
|
||||||
.I context
|
.I context
|
||||||
.B ] [-F
|
.B ] [-F
|
||||||
@ -84,6 +86,10 @@ downstream.
|
|||||||
is the client application,
|
is the client application,
|
||||||
.B iodined
|
.B iodined
|
||||||
is the server.
|
is the server.
|
||||||
|
|
||||||
|
Note: server and client are required to speak the exact same protocol. In most
|
||||||
|
cases, this means running the same iodine version. Unfortunately, implementing
|
||||||
|
backward and forward protocol compatibility is usually not feasible.
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.SS Common Options:
|
.SS Common Options:
|
||||||
.TP
|
.TP
|
||||||
@ -127,49 +133,85 @@ will be sent to the server instead of the DNS relay.
|
|||||||
Force maximum downstream fragment size. Not setting this will cause the
|
Force maximum downstream fragment size. Not setting this will cause the
|
||||||
client to automatically probe the maximum accepted downstream fragment size.
|
client to automatically probe the maximum accepted downstream fragment size.
|
||||||
.TP
|
.TP
|
||||||
|
.B -M namelen
|
||||||
|
Maximum length of upstream hostnames, default 255.
|
||||||
|
Usable range ca. 100 to 255.
|
||||||
|
Use this option to scale back upstream bandwidth in favor of downstream
|
||||||
|
bandwidth.
|
||||||
|
Also useful for DNS servers that perform unreliably when using full-length
|
||||||
|
hostnames, noticable when fragment size autoprobe returns very
|
||||||
|
different results each time.
|
||||||
|
.TP
|
||||||
.B -T dnstype
|
.B -T dnstype
|
||||||
DNS request type.
|
DNS request type override.
|
||||||
.I NULL
|
By default, autodetection will probe for working DNS request types, and
|
||||||
is default. If this doesn't work, try
|
will select the request type that is expected to provide the most bandwidth.
|
||||||
.I TXT
|
However, it may turn out that a DNS relay imposes limits that skew the
|
||||||
(some less bandwidth) or
|
picture, which may lead to an "unexpected" DNS request type providing
|
||||||
|
more bandwidth.
|
||||||
|
In that case, use this option to override the autodetection.
|
||||||
|
In (expected) decreasing bandwidth order, the supported DNS request types are:
|
||||||
|
.IR NULL ,
|
||||||
|
.IR TXT ,
|
||||||
|
.IR SRV ,
|
||||||
|
.IR MX ,
|
||||||
.I CNAME
|
.I CNAME
|
||||||
(much less bandwidth). Also supported are
|
and
|
||||||
.I A
|
.I A
|
||||||
(returning CNAME) and
|
(returning CNAME).
|
||||||
|
Note that
|
||||||
|
.IR SRV ,
|
||||||
.I MX
|
.I MX
|
||||||
requests, but these may/will cause additional lookups by "smart" caching
|
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
|
nameservers to get an actual IP address, which may either slow down or fail
|
||||||
completely.
|
completely.
|
||||||
.TP
|
.TP
|
||||||
.B -O downenc
|
.B -O downenc
|
||||||
Downstream encoding for all query type responses except NULL.
|
Force downstream encoding type for all query type responses except NULL.
|
||||||
|
Default is autodetected, but may not spot all problems for the more advanced
|
||||||
|
codecs.
|
||||||
|
Use this option to override the autodetection.
|
||||||
.I Base32
|
.I Base32
|
||||||
is default and should always work.
|
is the lowest-grade codec and should always work; this is used when
|
||||||
|
autodetection fails.
|
||||||
.I Base64
|
.I Base64
|
||||||
provides more bandwidth, but may not work on all nameservers.
|
provides more bandwidth, but may not work on all nameservers.
|
||||||
|
.I Base64u
|
||||||
|
is equal to Base64 except in using underscore ('_')
|
||||||
|
instead of plus sign ('+'), possibly working where
|
||||||
|
.I Base64
|
||||||
|
does not.
|
||||||
|
.I Base128
|
||||||
|
uses high byte values (mostly accented letters in iso8859-1),
|
||||||
|
which might work with some nameservers.
|
||||||
For TXT queries,
|
For TXT queries,
|
||||||
.I Raw
|
.I Raw
|
||||||
will provide maximum performance. This will only work if the nameserver
|
will provide maximum performance, but this will only work if the nameserver
|
||||||
path is fully 8-bit-clean for responses that are assumed to be "legible text".
|
path is fully 8-bit-clean for responses that are assumed to be "legible text".
|
||||||
.TP
|
.TP
|
||||||
.B -L 0|1
|
.B -L 0|1
|
||||||
Lazy-mode switch.
|
Lazy-mode switch.
|
||||||
\-L1 (default): Use lazy mode if server supports it, for improved
|
\-L1 (default): Use lazy mode for improved performance and decreased latency.
|
||||||
performance and decreased latency.
|
A very small minority of DNS relays appears to be unable to handle the
|
||||||
Some DNS servers, notably the opendns.com network, appear unstable when
|
lazy mode traffic pattern, resulting in no or very little data coming through.
|
||||||
handling lazy mode DNS traffic and will re-order requests. If this occurs,
|
The iodine client will detect this and try to switch back to legacy mode,
|
||||||
you will notice fluctuating response speed in interactive sessions.
|
but this may not always work.
|
||||||
The iodine client will eventually detect this and switch back to legacy
|
In these situations use \-L0 to force running in legacy mode
|
||||||
mode automatically. Use \-L0 to force running in legacy mode
|
|
||||||
(implies \-I1).
|
(implies \-I1).
|
||||||
.TP
|
.TP
|
||||||
.B -I interval
|
.B -I interval
|
||||||
Maximum interval between requests (pings) so that intermediate DNS
|
Maximum interval between requests (pings) so that intermediate DNS
|
||||||
servers will not time out. Default is 4 in lazy mode, which will work
|
servers will not time out. Default is 4 in lazy mode, which will work
|
||||||
fine in almost all cases. Decrease if you get SERVFAIL errors in periods
|
fine in most cases. When too many SERVFAIL errors occur, iodine
|
||||||
without tunneled data traffic. To get absolute minimum DNS traffic,
|
will automatically reduce this to 1.
|
||||||
increase well above 4 until SERVFAIL errors start to occur.
|
To get absolute minimum DNS traffic,
|
||||||
|
increase well above 4, but not so high that SERVFAIL errors start to occur.
|
||||||
|
There are some DNS relays with very small timeouts,
|
||||||
|
notably dnsadvantage.com (ultradns), that will give
|
||||||
|
SERVFAIL errors even with \-I1; data will still get trough,
|
||||||
|
and these errors can be ignored.
|
||||||
Maximum useful value is 59, since iodined will close a client's
|
Maximum useful value is 59, since iodined will close a client's
|
||||||
connection after 60 seconds of inactivity.
|
connection after 60 seconds of inactivity.
|
||||||
.SS Server Options:
|
.SS Server Options:
|
||||||
@ -190,11 +232,16 @@ Increase debug level. Level 1 prints info about each RX/TX packet.
|
|||||||
Implies the
|
Implies the
|
||||||
.B -f
|
.B -f
|
||||||
option.
|
option.
|
||||||
|
On level 2 (-DD) or higher, DNS queries will be printed literally.
|
||||||
|
When using Base128 upstream encoding, this is best viewed as
|
||||||
|
ISO Latin-1 text instead of (illegal) UTF-8.
|
||||||
|
This is easily done with : "LC_ALL=C luit iodined -DD ..."
|
||||||
|
(see luit(1)).
|
||||||
.TP
|
.TP
|
||||||
.B -m mtu
|
.B -m mtu
|
||||||
Set 'mtu' as mtu size for the tun device.
|
Set 'mtu' as mtu size for the tun device.
|
||||||
This will be sent to the client on login, and the client will use the same mtu
|
This will be sent to the client on login, and the client will use the same mtu
|
||||||
for its tun device. Default 1200. Note that the DNS traffic will be
|
for its tun device. Default 1130. Note that the DNS traffic will be
|
||||||
automatically fragmented when needed.
|
automatically fragmented when needed.
|
||||||
.TP
|
.TP
|
||||||
.B -l listen_ip
|
.B -l listen_ip
|
||||||
@ -236,97 +283,22 @@ must be the same on both the client and the server.
|
|||||||
.SS Server Arguments:
|
.SS Server Arguments:
|
||||||
.TP
|
.TP
|
||||||
.B tunnel_ip[/netmask]
|
.B tunnel_ip[/netmask]
|
||||||
+This is the server's ip address on the tun interface. The client will be
|
This is the server's ip address on the tun interface. The client will be
|
||||||
given the next ip number in the range. It is recommended to use the
|
given the next ip number in the range. It is recommended to use the
|
||||||
10.0.0.0 or 172.16.0.0 ranges. The default netmask is /27, can be overriden
|
10.0.0.0 or 172.16.0.0 ranges. The default netmask is /27, can be overriden
|
||||||
by specifying it here. Using a smaller network will limit the number of
|
by specifying it here. Using a smaller network will limit the number of
|
||||||
concurrent users.
|
concurrent users.
|
||||||
.TP
|
.TP
|
||||||
.B topdomain
|
.B topdomain
|
||||||
+The dns traffic is expected to arrive as queries for
|
The dns traffic is expected to arrive as queries for
|
||||||
subdomains under 'topdomain'. This is normally a subdomain to a domain you
|
subdomains under 'topdomain'. This is normally a subdomain to a domain you
|
||||||
own. Use a short domain name to get better throughput. This argument must be
|
own. Use a short domain name to get better throughput. This argument must be
|
||||||
the same on both the client and the server. Queries for domains other
|
the same on both the client and the server. Queries for domains other
|
||||||
than 'topdomain' will be forwarded when the \-b option is given, otherwise
|
than 'topdomain' will be forwarded when the \-b option is given, otherwise
|
||||||
they will be dropped.
|
they will be dropped.
|
||||||
.SH EXAMPLES
|
.SH EXAMPLES
|
||||||
.SS Quickstart:
|
See the README file for both a quick test scenario, and a detailed description
|
||||||
.TP
|
of real-world deployment.
|
||||||
Try it out within your own LAN! Follow these simple steps:
|
|
||||||
.TP
|
|
||||||
- On your server, run: ./iodined \-f 10.0.0.1 test.asdf
|
|
||||||
(If you already use the 10.0.0.0 network, use another internal net like
|
|
||||||
172.16.0.0)
|
|
||||||
.TP
|
|
||||||
- Enter a password
|
|
||||||
.TP
|
|
||||||
- On the client, run: ./iodine \-f 192.168.0.1 test.asdf
|
|
||||||
(Replace 192.168.0.1 with the server's ip address)
|
|
||||||
.TP
|
|
||||||
- Enter the same password
|
|
||||||
.TP
|
|
||||||
- Now the client has the tunnel ip 10.0.0.2 and the server has 10.0.0.1
|
|
||||||
.TP
|
|
||||||
- Try pinging each other through the tunnel
|
|
||||||
.TP
|
|
||||||
- Done! :)
|
|
||||||
.TP
|
|
||||||
To actually use it through a relaying nameserver, see below.
|
|
||||||
.SS Full setup:
|
|
||||||
|
|
||||||
.TP
|
|
||||||
.B Server side:
|
|
||||||
To use this tunnel, you need control over a real domain (like mytunnel.com),
|
|
||||||
and a server with a public IP number. If the server already runs a DNS
|
|
||||||
server, change the listening port and then use the \-b option to let
|
|
||||||
iodined forward the DNS requests. Then, delegate a subdomain
|
|
||||||
(say, tunnel1.mytunnel.com) to the server. If you use BIND for the domain,
|
|
||||||
add these lines to the zone file (replace 10.15.213.99 with your server ip):
|
|
||||||
|
|
||||||
.nf
|
|
||||||
tunnel1host IN A 10.15.213.99
|
|
||||||
tunnel1 IN NS tunnel1host.mytunnel.com.
|
|
||||||
.fi
|
|
||||||
|
|
||||||
Now any DNS querys for domains ending with tunnel1.mytunnnel.com will be sent
|
|
||||||
to your server. Start iodined on the server. The first argument is the tunnel
|
|
||||||
IP address (like 192.168.99.1) and the second is the assigned domain (in this
|
|
||||||
case tunnel1.mytunnel.com). The \-f argument will keep iodined running in the
|
|
||||||
foreground, which helps when testing. iodined will start a virtual interface,
|
|
||||||
and 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.
|
|
||||||
.TP
|
|
||||||
.B Client side:
|
|
||||||
All the setup is done, just start iodine. It also takes two
|
|
||||||
arguments, the first is the local relaying DNS server and the second is the
|
|
||||||
domain used (tunnel1.mytunnnel.com). If DNS queries are allowed to any
|
|
||||||
computer, you can use the tunnel endpoint (example: 10.15.213.99 or
|
|
||||||
tunnel1host.mytunnel.com) as the first argument. The tunnel interface will get
|
|
||||||
an IP close to the servers (in this case 192.168.99.2) and a suitable MTU.
|
|
||||||
Enter the same password as on the server either by argument or after the client
|
|
||||||
has started. Now you should be able to ping the other end of the tunnel from
|
|
||||||
either side.
|
|
||||||
.TP
|
|
||||||
.B Routing:
|
|
||||||
The normal case is to route all traffic through the DNS tunnel. To do this, first
|
|
||||||
add a route to the nameserver you use with the default gateway as gateway. Then
|
|
||||||
replace the default gateway with the servers IP address within the DNS tunnel,
|
|
||||||
and configure the server to do NAT.
|
|
||||||
.TP
|
|
||||||
.B Troubleshooting:
|
|
||||||
Use the \-D option on the server to show received and sent queries, or use a
|
|
||||||
tool like Wireshark/tcpdump. The iodined server replies to NS requests sent for
|
|
||||||
subdomains of the tunnel domain. If your domain is tunnel.com, send a NS
|
|
||||||
request for foo.tunnel.com to see if the delegation works. dig is a good tool
|
|
||||||
for this:
|
|
||||||
.nf
|
|
||||||
dig \-t NS foo123.tunnel.com
|
|
||||||
.fi
|
|
||||||
.TP
|
|
||||||
.B MTU issues:
|
|
||||||
These issues should be solved now, with automatic fragmentation of downstream
|
|
||||||
packets. There should be no need to set the MTU explicitly on the server.
|
|
||||||
.SH SECURITY
|
.SH SECURITY
|
||||||
Login is a relatively secure challenge-response MD5 hash, with the
|
Login is a relatively secure challenge-response MD5 hash, with the
|
||||||
password never passing the wire.
|
password never passing the wire.
|
||||||
@ -336,10 +308,9 @@ encrypted in any way. The DNS traffic is also vulnerable to replay,
|
|||||||
injection and man-in-the-middle attacks, especially when iodined is used
|
injection and man-in-the-middle attacks, especially when iodined is used
|
||||||
with the \-c option. Use of ssh or vpn tunneling is strongly recommended.
|
with the \-c option. Use of ssh or vpn tunneling is strongly recommended.
|
||||||
On both server and client, use
|
On both server and client, use
|
||||||
.I iptables
|
.IR iptables ,
|
||||||
,
|
|
||||||
.I pf
|
.I pf
|
||||||
or other firewlls to block all traffic coming in from the tun interfaces,
|
or other firewalls to block all traffic coming in from the tun interfaces,
|
||||||
except to the used ssh or vpn ports.
|
except to the used ssh or vpn ports.
|
||||||
.SH ENVIRONMENT
|
.SH ENVIRONMENT
|
||||||
.SS IODINE_PASS
|
.SS IODINE_PASS
|
||||||
@ -348,14 +319,14 @@ If the environment variable
|
|||||||
is set, iodine will use the value it is set to as password instead of asking
|
is set, iodine will use the value it is set to as password instead of asking
|
||||||
for one. The
|
for one. The
|
||||||
.B -P
|
.B -P
|
||||||
option still has preference.
|
option still has precedence.
|
||||||
.SS IODINED_PASS
|
.SS IODINED_PASS
|
||||||
If the environment variable
|
If the environment variable
|
||||||
.B IODINED_PASS
|
.B IODINED_PASS
|
||||||
is set, iodined will use the value it is set to as password instead of asking
|
is set, iodined will use the value it is set to as password instead of asking
|
||||||
for one. The
|
for one. The
|
||||||
.B -P
|
.B -P
|
||||||
option still has preference.
|
option still has precedence.
|
||||||
.El
|
.El
|
||||||
.SH SEE ALSO
|
.SH SEE ALSO
|
||||||
The README file in the source distribution contains some more elaborate
|
The README file in the source distribution contains some more elaborate
|
||||||
|
14
src/Makefile
14
src/Makefile
@ -1,4 +1,4 @@
|
|||||||
COMMONOBJS = tun.o dns.o read.o encoding.o login.o base32.o base64.o md5.o common.o
|
COMMONOBJS = tun.o dns.o read.o encoding.o login.o base32.o base64.o base64u.o base128.o md5.o common.o
|
||||||
CLIENTOBJS = iodine.o client.o util.o
|
CLIENTOBJS = iodine.o client.o util.o
|
||||||
CLIENT = ../bin/iodine
|
CLIENT = ../bin/iodine
|
||||||
SERVEROBJS = iodined.o user.o fw_query.o
|
SERVEROBJS = iodined.o user.o fw_query.o
|
||||||
@ -30,7 +30,17 @@ $(SERVER): $(COMMONOBJS) $(SERVEROBJS)
|
|||||||
@echo CC $<
|
@echo CC $<
|
||||||
@$(CC) $(CFLAGS) $< -o $@
|
@$(CC) $(CFLAGS) $< -o $@
|
||||||
|
|
||||||
|
base64u.o client.o iodined.o: base64u.h
|
||||||
|
base64u.c: base64.c
|
||||||
|
@echo Making $@
|
||||||
|
@echo '/* No use in editing, produced by Makefile! */' > $@
|
||||||
|
@sed -e 's/\(base64\)/\1u/ig ; s/0123456789+/0123456789_/' < $< >> $@
|
||||||
|
base64u.h: base64.h
|
||||||
|
@echo Making $@
|
||||||
|
@echo '/* No use in editing, produced by Makefile! */' > $@
|
||||||
|
@sed -e 's/\(base64\)/\1u/ig ; s/0123456789+/0123456789_/' < $< >> $@
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@echo "Cleaning src/"
|
@echo "Cleaning src/"
|
||||||
@rm -f $(CLIENT){,.exe} $(SERVER){,.exe} *~ *.o *.core
|
@rm -f $(CLIENT){,.exe} $(SERVER){,.exe} *~ *.o *.core base64u.*
|
||||||
|
|
||||||
|
291
src/base128.c
Normal file
291
src/base128.c
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009 J.A.Bezemer@opensourcepartners.nl
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* raw 76543210 76543210 76543210 76543210 76543210 76543210 76543210
|
||||||
|
* enc 65432106 54321065 43210654 32106543 21065432 10654321 06543210
|
||||||
|
* ^ ^ ^ ^ ^ ^ ^ ^
|
||||||
|
*
|
||||||
|
* 0001 1 0001 1
|
||||||
|
* 0011 3 0011 3
|
||||||
|
* 0111 7 0111 7
|
||||||
|
* 1111 f 0110 6
|
||||||
|
* 1110 e 0100 4
|
||||||
|
* 1100 c
|
||||||
|
* 1000 8
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "encoding.h"
|
||||||
|
#include "base128.h"
|
||||||
|
|
||||||
|
#define BLKSIZE_RAW 7
|
||||||
|
#define BLKSIZE_ENC 8
|
||||||
|
|
||||||
|
/* Don't use '-' (restricted to middle of labels), prefer iso_8859-1
|
||||||
|
* accent chars since they might readily be entered in normal use,
|
||||||
|
* don't use 254-255 because of possible function overloading in DNS systems.
|
||||||
|
*/
|
||||||
|
static const unsigned char cb128[] =
|
||||||
|
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||||
|
"\274\275\276\277"
|
||||||
|
"\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317"
|
||||||
|
"\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337"
|
||||||
|
"\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357"
|
||||||
|
"\360\361\362\363\364\365\366\367\370\371\372\373\374\375";
|
||||||
|
static unsigned char rev128[256];
|
||||||
|
static int reverse_init = 0;
|
||||||
|
|
||||||
|
static int base128_encode(char *, size_t *, const void *, size_t);
|
||||||
|
static int base128_decode(void *, size_t *, const char *, size_t);
|
||||||
|
static int base128_handles_dots();
|
||||||
|
static int base128_blksize_raw();
|
||||||
|
static int base128_blksize_enc();
|
||||||
|
|
||||||
|
static struct encoder base128_encoder =
|
||||||
|
{
|
||||||
|
"Base128",
|
||||||
|
base128_encode,
|
||||||
|
base128_decode,
|
||||||
|
base128_handles_dots,
|
||||||
|
base128_handles_dots,
|
||||||
|
base128_blksize_raw,
|
||||||
|
base128_blksize_enc
|
||||||
|
};
|
||||||
|
|
||||||
|
struct encoder
|
||||||
|
*get_base128_encoder()
|
||||||
|
{
|
||||||
|
return &base128_encoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
base128_handles_dots()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
base128_blksize_raw()
|
||||||
|
{
|
||||||
|
return BLKSIZE_RAW;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
base128_blksize_enc()
|
||||||
|
{
|
||||||
|
return BLKSIZE_ENC;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static void
|
||||||
|
base128_reverse_init()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned char c;
|
||||||
|
|
||||||
|
if (!reverse_init) {
|
||||||
|
memset (rev128, 0, 256);
|
||||||
|
for (i = 0; i < 128; i++) {
|
||||||
|
c = cb128[i];
|
||||||
|
rev128[(int) c] = i;
|
||||||
|
}
|
||||||
|
reverse_init = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
base128_encode(char *buf, size_t *buflen, const void *data, size_t size)
|
||||||
|
/*
|
||||||
|
* Fills *buf with max. *buflen characters, encoding size bytes of *data.
|
||||||
|
*
|
||||||
|
* NOTE: *buf space should be at least 1 byte _more_ than *buflen
|
||||||
|
* to hold the trailing '\0'.
|
||||||
|
*
|
||||||
|
* return value : #bytes filled in buf (excluding \0)
|
||||||
|
* sets *buflen to : #bytes encoded from data
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
unsigned char *ubuf = (unsigned char *) buf;
|
||||||
|
unsigned char *udata = (unsigned char *) data;
|
||||||
|
int iout = 0; /* to-be-filled output char */
|
||||||
|
int iin = 0; /* one more than last input byte that can be
|
||||||
|
successfully decoded */
|
||||||
|
|
||||||
|
/* Note: Don't bother to optimize manually. GCC optimizes
|
||||||
|
better(!) when using simplistic array indexing. */
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (iout >= *buflen || iin >= size)
|
||||||
|
break;
|
||||||
|
ubuf[iout] = cb128[((udata[iin] & 0xfe) >> 1)];
|
||||||
|
iout++;
|
||||||
|
|
||||||
|
if (iout >= *buflen || iin >= size) {
|
||||||
|
iout--; /* previous char is useless */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ubuf[iout] = cb128[((udata[iin] & 0x01) << 6) |
|
||||||
|
((iin + 1 < size) ?
|
||||||
|
((udata[iin + 1] & 0xfc) >> 2) : 0)];
|
||||||
|
iin++; /* 0 complete, iin=1 */
|
||||||
|
iout++;
|
||||||
|
|
||||||
|
if (iout >= *buflen || iin >= size)
|
||||||
|
break;
|
||||||
|
ubuf[iout] = cb128[((udata[iin] & 0x03) << 5) |
|
||||||
|
((iin + 1 < size) ?
|
||||||
|
((udata[iin + 1] & 0xf8) >> 3) : 0)];
|
||||||
|
iin++; /* 1 complete, iin=2 */
|
||||||
|
iout++;
|
||||||
|
|
||||||
|
if (iout >= *buflen || iin >= size)
|
||||||
|
break;
|
||||||
|
ubuf[iout] = cb128[((udata[iin] & 0x07) << 4) |
|
||||||
|
((iin + 1 < size) ?
|
||||||
|
((udata[iin + 1] & 0xf0) >> 4) : 0)];
|
||||||
|
iin++; /* 2 complete, iin=3 */
|
||||||
|
iout++;
|
||||||
|
|
||||||
|
if (iout >= *buflen || iin >= size)
|
||||||
|
break;
|
||||||
|
ubuf[iout] = cb128[((udata[iin] & 0x0f) << 3) |
|
||||||
|
((iin + 1 < size) ?
|
||||||
|
((udata[iin + 1] & 0xe0) >> 5) : 0)];
|
||||||
|
iin++; /* 3 complete, iin=4 */
|
||||||
|
iout++;
|
||||||
|
|
||||||
|
if (iout >= *buflen || iin >= size)
|
||||||
|
break;
|
||||||
|
ubuf[iout] = cb128[((udata[iin] & 0x1f) << 2) |
|
||||||
|
((iin + 1 < size) ?
|
||||||
|
((udata[iin + 1] & 0xc0) >> 6) : 0)];
|
||||||
|
iin++; /* 4 complete, iin=5 */
|
||||||
|
iout++;
|
||||||
|
|
||||||
|
if (iout >= *buflen || iin >= size)
|
||||||
|
break;
|
||||||
|
ubuf[iout] = cb128[((udata[iin] & 0x3f) << 1) |
|
||||||
|
((iin + 1 < size) ?
|
||||||
|
((udata[iin + 1] & 0x80) >> 7) : 0)];
|
||||||
|
iin++; /* 5 complete, iin=6 */
|
||||||
|
iout++;
|
||||||
|
|
||||||
|
if (iout >= *buflen || iin >= size)
|
||||||
|
break;
|
||||||
|
ubuf[iout] = cb128[(udata[iin] & 0x7f)];
|
||||||
|
iin++; /* 6 complete, iin=7 */
|
||||||
|
iout++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ubuf[iout] = '\0';
|
||||||
|
|
||||||
|
/* store number of bytes from data that was used */
|
||||||
|
*buflen = iin;
|
||||||
|
|
||||||
|
return iout;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define REV128(x) rev128[(int) (x)]
|
||||||
|
|
||||||
|
static int
|
||||||
|
base128_decode(void *buf, size_t *buflen, const char *str, size_t slen)
|
||||||
|
/*
|
||||||
|
* Fills *buf with max. *buflen bytes, decoded from slen chars in *str.
|
||||||
|
* Decoding stops early when *str contains \0.
|
||||||
|
* Illegal encoded chars are assumed to decode to zero.
|
||||||
|
*
|
||||||
|
* NOTE: *buf space should be at least 1 byte _more_ than *buflen
|
||||||
|
* to hold a trailing '\0' that is added (though *buf will usually
|
||||||
|
* contain full-binary data).
|
||||||
|
*
|
||||||
|
* return value : #bytes filled in buf (excluding \0)
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
unsigned char *ustr = (unsigned char *) str;
|
||||||
|
unsigned char *ubuf = (unsigned char *) buf;
|
||||||
|
int iout = 0; /* to-be-filled output byte */
|
||||||
|
int iin = 0; /* next input char to use in decoding */
|
||||||
|
|
||||||
|
base128_reverse_init ();
|
||||||
|
|
||||||
|
/* Note: Don't bother to optimize manually. GCC optimizes
|
||||||
|
better(!) when using simplistic array indexing. */
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (iout >= *buflen || iin + 1 >= slen ||
|
||||||
|
str[iin] == '\0' || str[iin + 1] == '\0')
|
||||||
|
break;
|
||||||
|
ubuf[iout] = ((REV128(ustr[iin]) & 0x7f) << 1) |
|
||||||
|
((REV128(ustr[iin + 1]) & 0x40) >> 6);
|
||||||
|
iin++; /* 0 used up, iin=1 */
|
||||||
|
iout++;
|
||||||
|
|
||||||
|
if (iout >= *buflen || iin + 1 >= slen ||
|
||||||
|
str[iin] == '\0' || str[iin + 1] == '\0')
|
||||||
|
break;
|
||||||
|
ubuf[iout] = ((REV128(ustr[iin]) & 0x3f) << 2) |
|
||||||
|
((REV128(ustr[iin + 1]) & 0x60) >> 5);
|
||||||
|
iin++; /* 1 used up, iin=2 */
|
||||||
|
iout++;
|
||||||
|
|
||||||
|
if (iout >= *buflen || iin + 1 >= slen ||
|
||||||
|
str[iin] == '\0' || str[iin + 1] == '\0')
|
||||||
|
break;
|
||||||
|
ubuf[iout] = ((REV128(ustr[iin]) & 0x1f) << 3) |
|
||||||
|
((REV128(ustr[iin + 1]) & 0x70) >> 4);
|
||||||
|
iin++; /* 2 used up, iin=3 */
|
||||||
|
iout++;
|
||||||
|
|
||||||
|
if (iout >= *buflen || iin + 1 >= slen ||
|
||||||
|
str[iin] == '\0' || str[iin + 1] == '\0')
|
||||||
|
break;
|
||||||
|
ubuf[iout] = ((REV128(ustr[iin]) & 0x0f) << 4) |
|
||||||
|
((REV128(ustr[iin + 1]) & 0x78) >> 3);
|
||||||
|
iin++; /* 3 used up, iin=4 */
|
||||||
|
iout++;
|
||||||
|
|
||||||
|
if (iout >= *buflen || iin + 1 >= slen ||
|
||||||
|
str[iin] == '\0' || str[iin + 1] == '\0')
|
||||||
|
break;
|
||||||
|
ubuf[iout] = ((REV128(ustr[iin]) & 0x07) << 5) |
|
||||||
|
((REV128(ustr[iin + 1]) & 0x7c) >> 2);
|
||||||
|
iin++; /* 4 used up, iin=5 */
|
||||||
|
iout++;
|
||||||
|
|
||||||
|
if (iout >= *buflen || iin + 1 >= slen ||
|
||||||
|
str[iin] == '\0' || str[iin + 1] == '\0')
|
||||||
|
break;
|
||||||
|
ubuf[iout] = ((REV128(ustr[iin]) & 0x03) << 6) |
|
||||||
|
((REV128(ustr[iin + 1]) & 0x7e) >> 1);
|
||||||
|
iin++; /* 5 used up, iin=6 */
|
||||||
|
iout++;
|
||||||
|
|
||||||
|
if (iout >= *buflen || iin + 1 >= slen ||
|
||||||
|
str[iin] == '\0' || str[iin + 1] == '\0')
|
||||||
|
break;
|
||||||
|
ubuf[iout] = ((REV128(ustr[iin]) & 0x01) << 7) |
|
||||||
|
((REV128(ustr[iin + 1]) & 0x7f));
|
||||||
|
iin += 2; /* 6,7 used up, iin=8 */
|
||||||
|
iout++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ubuf[iout] = '\0';
|
||||||
|
|
||||||
|
return iout;
|
||||||
|
}
|
22
src/base128.h
Normal file
22
src/base128.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009 J.A.Bezemer@opensourcepartners.nl
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __BASE128_H__
|
||||||
|
#define __BASE128_H__
|
||||||
|
|
||||||
|
struct encoder *get_base128_encoder(void);
|
||||||
|
|
||||||
|
#endif
|
244
src/base32.c
244
src/base32.c
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
|
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
|
||||||
|
* Mostly rewritten 2009 J.A.Bezemer@opensourcepartners.nl
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
@ -28,10 +29,11 @@ static const char cb32[] =
|
|||||||
"abcdefghijklmnopqrstuvwxyz012345";
|
"abcdefghijklmnopqrstuvwxyz012345";
|
||||||
static const char cb32_ucase[] =
|
static const char cb32_ucase[] =
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
|
||||||
static unsigned char rev32[128];
|
static unsigned char rev32[256];
|
||||||
|
static int reverse_init = 0;
|
||||||
|
|
||||||
static int base32_decode(void *, size_t *, const char *, size_t);
|
|
||||||
static int base32_encode(char *, size_t *, const void *, size_t);
|
static int base32_encode(char *, size_t *, const void *, size_t);
|
||||||
|
static int base32_decode(void *, size_t *, const char *, size_t);
|
||||||
static int base32_handles_dots();
|
static int base32_handles_dots();
|
||||||
static int base32_blksize_raw();
|
static int base32_blksize_raw();
|
||||||
static int base32_blksize_enc();
|
static int base32_blksize_enc();
|
||||||
@ -71,14 +73,14 @@ base32_blksize_enc()
|
|||||||
return BLKSIZE_ENC;
|
return BLKSIZE_ENC;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
inline static void
|
||||||
base32_reverse_init()
|
base32_reverse_init()
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
unsigned char c;
|
unsigned char c;
|
||||||
static int reverse_init = 0;
|
|
||||||
|
|
||||||
if (!reverse_init) {
|
if (!reverse_init) {
|
||||||
|
memset (rev32, 0, 256);
|
||||||
for (i = 0; i < 32; i++) {
|
for (i = 0; i < 32; i++) {
|
||||||
c = cb32[i];
|
c = cb32[i];
|
||||||
rev32[(int) c] = i;
|
rev32[(int) c] = i;
|
||||||
@ -104,123 +106,165 @@ b32_8to5(int in)
|
|||||||
|
|
||||||
static int
|
static int
|
||||||
base32_encode(char *buf, size_t *buflen, const void *data, size_t size)
|
base32_encode(char *buf, size_t *buflen, const void *data, size_t size)
|
||||||
|
/*
|
||||||
|
* Fills *buf with max. *buflen characters, encoding size bytes of *data.
|
||||||
|
*
|
||||||
|
* NOTE: *buf space should be at least 1 byte _more_ than *buflen
|
||||||
|
* to hold the trailing '\0'.
|
||||||
|
*
|
||||||
|
* return value : #bytes filled in buf (excluding \0)
|
||||||
|
* sets *buflen to : #bytes encoded from data
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
size_t newsize;
|
unsigned char *udata = (unsigned char *) data;
|
||||||
size_t maxsize;
|
int iout = 0; /* to-be-filled output char */
|
||||||
unsigned char *p;
|
int iin = 0; /* one more than last input byte that can be
|
||||||
unsigned char *q;
|
successfully decoded */
|
||||||
int i;
|
|
||||||
|
|
||||||
memset(buf, 0, *buflen);
|
/* Note: Don't bother to optimize manually. GCC optimizes
|
||||||
|
better(!) when using simplistic array indexing. */
|
||||||
|
|
||||||
/* how many chars can we encode within the buf */
|
while (1) {
|
||||||
maxsize = BLKSIZE_RAW * (*buflen / BLKSIZE_ENC);
|
if (iout >= *buflen || iin >= size)
|
||||||
/* how big will the encoded data be */
|
break;
|
||||||
newsize = BLKSIZE_ENC * (size / BLKSIZE_RAW);
|
buf[iout] = cb32[((udata[iin] & 0xf8) >> 3)];
|
||||||
if (size % BLKSIZE_RAW) {
|
iout++;
|
||||||
newsize += BLKSIZE_ENC;
|
|
||||||
|
if (iout >= *buflen || iin >= size) {
|
||||||
|
iout--; /* previous char is useless */
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
/* if the buffer is too small, eat some of the data */
|
buf[iout] = cb32[((udata[iin] & 0x07) << 2) |
|
||||||
if (*buflen < newsize) {
|
((iin + 1 < size) ?
|
||||||
size = maxsize;
|
((udata[iin + 1] & 0xc0) >> 6) : 0)];
|
||||||
|
iin++; /* 0 complete, iin=1 */
|
||||||
|
iout++;
|
||||||
|
|
||||||
|
if (iout >= *buflen || iin >= size)
|
||||||
|
break;
|
||||||
|
buf[iout] = cb32[((udata[iin] & 0x3e) >> 1)];
|
||||||
|
iout++;
|
||||||
|
|
||||||
|
if (iout >= *buflen || iin >= size) {
|
||||||
|
iout--; /* previous char is useless */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buf[iout] = cb32[((udata[iin] & 0x01) << 4) |
|
||||||
|
((iin + 1 < size) ?
|
||||||
|
((udata[iin + 1] & 0xf0) >> 4) : 0)];
|
||||||
|
iin++; /* 1 complete, iin=2 */
|
||||||
|
iout++;
|
||||||
|
|
||||||
|
if (iout >= *buflen || iin >= size)
|
||||||
|
break;
|
||||||
|
buf[iout] = cb32[((udata[iin] & 0x0f) << 1) |
|
||||||
|
((iin + 1 < size) ?
|
||||||
|
((udata[iin + 1] & 0x80) >> 7) : 0)];
|
||||||
|
iin++; /* 2 complete, iin=3 */
|
||||||
|
iout++;
|
||||||
|
|
||||||
|
if (iout >= *buflen || iin >= size)
|
||||||
|
break;
|
||||||
|
buf[iout] = cb32[((udata[iin] & 0x7c) >> 2)];
|
||||||
|
iout++;
|
||||||
|
|
||||||
|
if (iout >= *buflen || iin >= size) {
|
||||||
|
iout--; /* previous char is useless */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buf[iout] = cb32[((udata[iin] & 0x03) << 3) |
|
||||||
|
((iin + 1 < size) ?
|
||||||
|
((udata[iin + 1] & 0xe0) >> 5) : 0)];
|
||||||
|
iin++; /* 3 complete, iin=4 */
|
||||||
|
iout++;
|
||||||
|
|
||||||
|
if (iout >= *buflen || iin >= size)
|
||||||
|
break;
|
||||||
|
buf[iout] = cb32[((udata[iin] & 0x1f))];
|
||||||
|
iin++; /* 4 complete, iin=5 */
|
||||||
|
iout++;
|
||||||
}
|
}
|
||||||
|
|
||||||
p = (unsigned char *) buf;
|
buf[iout] = '\0';
|
||||||
q = (unsigned char *)data;
|
|
||||||
|
|
||||||
for(i=0;i<size;i+=BLKSIZE_RAW) {
|
|
||||||
p[0] = cb32[((q[0] & 0xf8) >> 3)];
|
|
||||||
p[1] = cb32[(((q[0] & 0x07) << 2) | ((q[1] & 0xc0) >> 6))];
|
|
||||||
p[2] = (i+1 < size) ? cb32[((q[1] & 0x3e) >> 1)] : '\0';
|
|
||||||
p[3] = (i+1 < size) ? cb32[((q[1] & 0x01) << 4) | ((q[2] & 0xf0) >> 4)] : '\0';
|
|
||||||
p[4] = (i+2 < size) ? cb32[((q[2] & 0x0f) << 1) | ((q[3] & 0x80) >> 7)] : '\0';
|
|
||||||
p[5] = (i+3 < size) ? cb32[((q[3] & 0x7c) >> 2)] : '\0';
|
|
||||||
p[6] = (i+3 < size) ? cb32[((q[3] & 0x03) << 3) | ((q[4] & 0xe0) >> 5)] : '\0';
|
|
||||||
p[7] = (i+4 < size) ? cb32[((q[4] & 0x1f))] : '\0';
|
|
||||||
|
|
||||||
q += BLKSIZE_RAW;
|
|
||||||
p += BLKSIZE_ENC;
|
|
||||||
}
|
|
||||||
*p = 0;
|
|
||||||
|
|
||||||
/* store number of bytes from data that was used */
|
/* store number of bytes from data that was used */
|
||||||
*buflen = size;
|
*buflen = iin;
|
||||||
|
|
||||||
return strlen(buf);
|
return iout;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DECODE_ERROR 0xffffffff
|
|
||||||
#define REV32(x) rev32[(int) (x)]
|
#define REV32(x) rev32[(int) (x)]
|
||||||
|
|
||||||
static int
|
|
||||||
decode_token(const unsigned char *t, unsigned char *data, size_t len)
|
|
||||||
{
|
|
||||||
if (len < 2)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
data[0] = ((REV32(t[0]) & 0x1f) << 3) |
|
|
||||||
((REV32(t[1]) & 0x1c) >> 2);
|
|
||||||
|
|
||||||
if (len < 4)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
data[1] = ((REV32(t[1]) & 0x03) << 6) |
|
|
||||||
((REV32(t[2]) & 0x1f) << 1) |
|
|
||||||
((REV32(t[3]) & 0x10) >> 4);
|
|
||||||
|
|
||||||
if (len < 5)
|
|
||||||
return 2;
|
|
||||||
|
|
||||||
data[2] = ((REV32(t[3]) & 0x0f) << 4) |
|
|
||||||
((REV32(t[4]) & 0x1e) >> 1);
|
|
||||||
|
|
||||||
if (len < 7)
|
|
||||||
return 3;
|
|
||||||
|
|
||||||
data[3] = ((REV32(t[4]) & 0x01) << 7) |
|
|
||||||
((REV32(t[5]) & 0x1f) << 2) |
|
|
||||||
((REV32(t[6]) & 0x18) >> 3);
|
|
||||||
|
|
||||||
if (len < 8)
|
|
||||||
return 4;
|
|
||||||
|
|
||||||
data[4] = ((REV32(t[6]) & 0x07) << 5) |
|
|
||||||
((REV32(t[7]) & 0x1f));
|
|
||||||
|
|
||||||
return 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
base32_decode(void *buf, size_t *buflen, const char *str, size_t slen)
|
base32_decode(void *buf, size_t *buflen, const char *str, size_t slen)
|
||||||
|
/*
|
||||||
|
* Fills *buf with max. *buflen bytes, decoded from slen chars in *str.
|
||||||
|
* Decoding stops early when *str contains \0.
|
||||||
|
* Illegal encoded chars are assumed to decode to zero.
|
||||||
|
*
|
||||||
|
* NOTE: *buf space should be at least 1 byte _more_ than *buflen
|
||||||
|
* to hold a trailing '\0' that is added (though *buf will usually
|
||||||
|
* contain full-binary data).
|
||||||
|
*
|
||||||
|
* return value : #bytes filled in buf (excluding \0)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
unsigned char *q;
|
unsigned char *ubuf = (unsigned char *) buf;
|
||||||
size_t newsize;
|
int iout = 0; /* to-be-filled output byte */
|
||||||
size_t maxsize;
|
int iin = 0; /* next input char to use in decoding */
|
||||||
const char *p;
|
|
||||||
int len;
|
|
||||||
|
|
||||||
base32_reverse_init ();
|
base32_reverse_init ();
|
||||||
|
|
||||||
/* chars needed to decode slen */
|
/* Note: Don't bother to optimize manually. GCC optimizes
|
||||||
newsize = BLKSIZE_RAW * (slen / BLKSIZE_ENC + 1) + 1;
|
better(!) when using simplistic array indexing. */
|
||||||
/* encoded chars that fit in buf */
|
|
||||||
maxsize = BLKSIZE_ENC * (*buflen / BLKSIZE_RAW + 1) + 1;
|
|
||||||
/* if the buffer is too small, eat some of the data */
|
|
||||||
if (*buflen < newsize) {
|
|
||||||
slen = maxsize;
|
|
||||||
}
|
|
||||||
|
|
||||||
q = buf;
|
while (1) {
|
||||||
for (p = str; *p && strchr(cb32, *p); p += BLKSIZE_ENC) {
|
if (iout >= *buflen || iin + 1 >= slen ||
|
||||||
len = decode_token((unsigned char *) p, (unsigned char *) q, slen);
|
str[iin] == '\0' || str[iin + 1] == '\0')
|
||||||
q += len;
|
|
||||||
slen -= BLKSIZE_ENC;
|
|
||||||
|
|
||||||
if (len < BLKSIZE_RAW)
|
|
||||||
break;
|
break;
|
||||||
}
|
ubuf[iout] = ((REV32(str[iin]) & 0x1f) << 3) |
|
||||||
*q = '\0';
|
((REV32(str[iin + 1]) & 0x1c) >> 2);
|
||||||
|
iin++; /* 0 used up, iin=1 */
|
||||||
|
iout++;
|
||||||
|
|
||||||
return q - (unsigned char *) buf;
|
if (iout >= *buflen || iin + 2 >= slen ||
|
||||||
|
str[iin] == '\0' || str[iin + 1] == '\0' ||
|
||||||
|
str[iin + 2] == '\0')
|
||||||
|
break;
|
||||||
|
ubuf[iout] = ((REV32(str[iin]) & 0x03) << 6) |
|
||||||
|
((REV32(str[iin + 1]) & 0x1f) << 1) |
|
||||||
|
((REV32(str[iin + 2]) & 0x10) >> 4);
|
||||||
|
iin += 2; /* 1,2 used up, iin=3 */
|
||||||
|
iout++;
|
||||||
|
|
||||||
|
if (iout >= *buflen || iin + 1 >= slen ||
|
||||||
|
str[iin] == '\0' || str[iin + 1] == '\0')
|
||||||
|
break;
|
||||||
|
ubuf[iout] = ((REV32(str[iin]) & 0x0f) << 4) |
|
||||||
|
((REV32(str[iin + 1]) & 0x1e) >> 1);
|
||||||
|
iin++; /* 3 used up, iin=4 */
|
||||||
|
iout++;
|
||||||
|
|
||||||
|
if (iout >= *buflen || iin + 2 >= slen ||
|
||||||
|
str[iin] == '\0' || str[iin + 1] == '\0' ||
|
||||||
|
str[iin + 2] == '\0')
|
||||||
|
break;
|
||||||
|
ubuf[iout] = ((REV32(str[iin]) & 0x01) << 7) |
|
||||||
|
((REV32(str[iin + 1]) & 0x1f) << 2) |
|
||||||
|
((REV32(str[iin + 2]) & 0x18) >> 3);
|
||||||
|
iin += 2; /* 4,5 used up, iin=6 */
|
||||||
|
iout++;
|
||||||
|
|
||||||
|
if (iout >= *buflen || iin + 1 >= slen ||
|
||||||
|
str[iin] == '\0' || str[iin + 1] == '\0')
|
||||||
|
break;
|
||||||
|
ubuf[iout] = ((REV32(str[iin]) & 0x07) << 5) |
|
||||||
|
((REV32(str[iin + 1]) & 0x1f));
|
||||||
|
iin += 2; /* 6,7 used up, iin=8 */
|
||||||
|
iout++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ubuf[iout] = '\0';
|
||||||
|
|
||||||
|
return iout;
|
||||||
|
}
|
||||||
|
217
src/base64.c
217
src/base64.c
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
|
* Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
|
||||||
|
* Mostly rewritten 2009 J.A.Bezemer@opensourcepartners.nl
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
@ -19,15 +20,16 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "encoding.h"
|
#include "encoding.h"
|
||||||
#include "common.h"
|
|
||||||
#include "base64.h"
|
#include "base64.h"
|
||||||
|
|
||||||
#define BLKSIZE_RAW 3
|
#define BLKSIZE_RAW 3
|
||||||
#define BLKSIZE_ENC 4
|
#define BLKSIZE_ENC 4
|
||||||
|
|
||||||
|
/* Note: the "unofficial" char is last here, which means that the \377 pattern
|
||||||
|
in DOWNCODECCHECK1 ('Y' request) will properly test it. */
|
||||||
static const char cb64[] =
|
static const char cb64[] =
|
||||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789+";
|
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789+";
|
||||||
static unsigned char rev64[128];
|
static unsigned char rev64[256];
|
||||||
static int reverse_init = 0;
|
static int reverse_init = 0;
|
||||||
|
|
||||||
static int base64_encode(char *, size_t *, const void *, size_t);
|
static int base64_encode(char *, size_t *, const void *, size_t);
|
||||||
@ -36,8 +38,6 @@ static int base64_handles_dots();
|
|||||||
static int base64_blksize_raw();
|
static int base64_blksize_raw();
|
||||||
static int base64_blksize_enc();
|
static int base64_blksize_enc();
|
||||||
|
|
||||||
#define REV64(x) rev64[(int) (x)]
|
|
||||||
|
|
||||||
static struct encoder base64_encoder =
|
static struct encoder base64_encoder =
|
||||||
{
|
{
|
||||||
"Base64",
|
"Base64",
|
||||||
@ -73,122 +73,133 @@ base64_blksize_enc()
|
|||||||
return BLKSIZE_ENC;
|
return BLKSIZE_ENC;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
inline static void
|
||||||
base64_encode(char *buf, size_t *buflen, const void *data, size_t size)
|
base64_reverse_init()
|
||||||
{
|
{
|
||||||
size_t newsize;
|
|
||||||
size_t maxsize;
|
|
||||||
unsigned char *s;
|
|
||||||
unsigned char *p;
|
|
||||||
unsigned char *q;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
memset(buf, 0, *buflen);
|
|
||||||
|
|
||||||
/* how many chars can we encode within the buf */
|
|
||||||
maxsize = BLKSIZE_RAW * (*buflen / BLKSIZE_ENC);
|
|
||||||
/* how big will the encoded data be */
|
|
||||||
newsize = BLKSIZE_ENC * (size / BLKSIZE_RAW);
|
|
||||||
if (size % BLKSIZE_RAW) {
|
|
||||||
newsize += BLKSIZE_ENC;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if the buffer is too small, eat some of the data */
|
|
||||||
if (*buflen < newsize) {
|
|
||||||
size = maxsize;
|
|
||||||
}
|
|
||||||
|
|
||||||
p = s = (unsigned char *) buf;
|
|
||||||
q = (unsigned char *)data;
|
|
||||||
|
|
||||||
for(i=0;i<size;i+=BLKSIZE_RAW) {
|
|
||||||
p[0] = cb64[((q[0] & 0xfc) >> 2)];
|
|
||||||
p[1] = cb64[(((q[0] & 0x03) << 4) | ((q[1] & 0xf0) >> 4))];
|
|
||||||
p[2] = (i+1 < size) ? cb64[((q[1] & 0x0f) << 2 ) | ((q[2] & 0xc0) >> 6)] : '\0';
|
|
||||||
p[3] = (i+2 < size) ? cb64[(q[2] & 0x3f)] : '\0';
|
|
||||||
|
|
||||||
q += BLKSIZE_RAW;
|
|
||||||
p += BLKSIZE_ENC;
|
|
||||||
}
|
|
||||||
*p = 0;
|
|
||||||
|
|
||||||
/* store number of bytes from data that was used */
|
|
||||||
*buflen = size;
|
|
||||||
|
|
||||||
return strlen(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define DECODE_ERROR 0xffffffff
|
|
||||||
|
|
||||||
static int
|
|
||||||
decode_token(const unsigned char *t, unsigned char *data, size_t len)
|
|
||||||
{
|
|
||||||
if (len < 2)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
data[0] = ((REV64(t[0]) & 0x3f) << 2) |
|
|
||||||
((REV64(t[1]) & 0x30) >> 4);
|
|
||||||
|
|
||||||
if (len < 3)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
data[1] = ((REV64(t[1]) & 0x0f) << 4) |
|
|
||||||
((REV64(t[2]) & 0x3c) >> 2);
|
|
||||||
|
|
||||||
if (len < 4)
|
|
||||||
return 2;
|
|
||||||
|
|
||||||
data[2] = ((REV64(t[2]) & 0x03) << 6) |
|
|
||||||
(REV64(t[3]) & 0x3f);
|
|
||||||
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
base64_decode(void *buf, size_t *buflen, const char *str, size_t slen)
|
|
||||||
{
|
|
||||||
unsigned char *q;
|
|
||||||
size_t newsize;
|
|
||||||
size_t maxsize;
|
|
||||||
const char *p;
|
|
||||||
unsigned char c;
|
unsigned char c;
|
||||||
unsigned char block[BLKSIZE_ENC];
|
|
||||||
int len;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (!reverse_init) {
|
if (!reverse_init) {
|
||||||
|
memset (rev64, 0, 256);
|
||||||
for (i = 0; i < 64; i++) {
|
for (i = 0; i < 64; i++) {
|
||||||
c = cb64[i];
|
c = cb64[i];
|
||||||
rev64[(int) c] = i;
|
rev64[(int) c] = i;
|
||||||
}
|
}
|
||||||
reverse_init = 1;
|
reverse_init = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* chars needed to decode slen */
|
|
||||||
newsize = BLKSIZE_RAW * (slen / BLKSIZE_ENC + 1) + 1;
|
|
||||||
/* encoded chars that fit in buf */
|
|
||||||
maxsize = BLKSIZE_ENC * (*buflen / BLKSIZE_RAW + 1) + 1;
|
|
||||||
/* if the buffer is too small, eat some of the data */
|
|
||||||
if (*buflen < newsize) {
|
|
||||||
slen = maxsize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
base64_encode(char *buf, size_t *buflen, const void *data, size_t size)
|
||||||
|
/*
|
||||||
|
* Fills *buf with max. *buflen characters, encoding size bytes of *data.
|
||||||
|
*
|
||||||
|
* NOTE: *buf space should be at least 1 byte _more_ than *buflen
|
||||||
|
* to hold the trailing '\0'.
|
||||||
|
*
|
||||||
|
* return value : #bytes filled in buf (excluding \0)
|
||||||
|
* sets *buflen to : #bytes encoded from data
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
unsigned char *udata = (unsigned char *) data;
|
||||||
|
int iout = 0; /* to-be-filled output char */
|
||||||
|
int iin = 0; /* one more than last input byte that can be
|
||||||
|
successfully decoded */
|
||||||
|
|
||||||
q = buf;
|
/* Note: Don't bother to optimize manually. GCC optimizes
|
||||||
for (p = str; *p; p += BLKSIZE_ENC) {
|
better(!) when using simplistic array indexing. */
|
||||||
/* since the str is const, we unescape in another buf */
|
|
||||||
for (i = 0; i < BLKSIZE_ENC; i++) {
|
|
||||||
block[i] = p[i];
|
|
||||||
}
|
|
||||||
len = decode_token(block, (unsigned char *) q, slen);
|
|
||||||
q += len;
|
|
||||||
slen -= BLKSIZE_ENC;
|
|
||||||
|
|
||||||
if (len < BLKSIZE_RAW)
|
while (1) {
|
||||||
|
if (iout >= *buflen || iin >= size)
|
||||||
|
break;
|
||||||
|
buf[iout] = cb64[((udata[iin] & 0xfc) >> 2)];
|
||||||
|
iout++;
|
||||||
|
|
||||||
|
if (iout >= *buflen || iin >= size) {
|
||||||
|
iout--; /* previous char is useless */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
*q = '\0';
|
buf[iout] = cb64[((udata[iin] & 0x03) << 4) |
|
||||||
|
((iin + 1 < size) ?
|
||||||
|
((udata[iin + 1] & 0xf0) >> 4) : 0)];
|
||||||
|
iin++; /* 0 complete, iin=1 */
|
||||||
|
iout++;
|
||||||
|
|
||||||
return q - (unsigned char *) buf;
|
if (iout >= *buflen || iin >= size)
|
||||||
|
break;
|
||||||
|
buf[iout] = cb64[((udata[iin] & 0x0f) << 2 ) |
|
||||||
|
((iin + 1 < size) ?
|
||||||
|
((udata[iin + 1] & 0xc0) >> 6) : 0)];
|
||||||
|
iin++; /* 1 complete, iin=2 */
|
||||||
|
iout++;
|
||||||
|
|
||||||
|
if (iout >= *buflen || iin >= size)
|
||||||
|
break;
|
||||||
|
buf[iout] = cb64[(udata[iin] & 0x3f)];
|
||||||
|
iin++; /* 2 complete, iin=3 */
|
||||||
|
iout++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buf[iout] = '\0';
|
||||||
|
|
||||||
|
/* store number of bytes from data that was used */
|
||||||
|
*buflen = iin;
|
||||||
|
|
||||||
|
return iout;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define REV64(x) rev64[(int) (x)]
|
||||||
|
|
||||||
|
static int
|
||||||
|
base64_decode(void *buf, size_t *buflen, const char *str, size_t slen)
|
||||||
|
/*
|
||||||
|
* Fills *buf with max. *buflen bytes, decoded from slen chars in *str.
|
||||||
|
* Decoding stops early when *str contains \0.
|
||||||
|
* Illegal encoded chars are assumed to decode to zero.
|
||||||
|
*
|
||||||
|
* NOTE: *buf space should be at least 1 byte _more_ than *buflen
|
||||||
|
* to hold a trailing '\0' that is added (though *buf will usually
|
||||||
|
* contain full-binary data).
|
||||||
|
*
|
||||||
|
* return value : #bytes filled in buf (excluding \0)
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
unsigned char *ubuf = (unsigned char *) buf;
|
||||||
|
int iout = 0; /* to-be-filled output byte */
|
||||||
|
int iin = 0; /* next input char to use in decoding */
|
||||||
|
|
||||||
|
base64_reverse_init ();
|
||||||
|
|
||||||
|
/* Note: Don't bother to optimize manually. GCC optimizes
|
||||||
|
better(!) when using simplistic array indexing. */
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (iout >= *buflen || iin + 1 >= slen ||
|
||||||
|
str[iin] == '\0' || str[iin + 1] == '\0')
|
||||||
|
break;
|
||||||
|
ubuf[iout] = ((REV64(str[iin]) & 0x3f) << 2) |
|
||||||
|
((REV64(str[iin + 1]) & 0x30) >> 4);
|
||||||
|
iin++; /* 0 used up, iin=1 */
|
||||||
|
iout++;
|
||||||
|
|
||||||
|
if (iout >= *buflen || iin + 1 >= slen ||
|
||||||
|
str[iin] == '\0' || str[iin + 1] == '\0')
|
||||||
|
break;
|
||||||
|
ubuf[iout] = ((REV64(str[iin]) & 0x0f) << 4) |
|
||||||
|
((REV64(str[iin + 1]) & 0x3c) >> 2);
|
||||||
|
iin++; /* 1 used up, iin=2 */
|
||||||
|
iout++;
|
||||||
|
|
||||||
|
if (iout >= *buflen || iin + 1 >= slen ||
|
||||||
|
str[iin] == '\0' || str[iin + 1] == '\0')
|
||||||
|
break;
|
||||||
|
ubuf[iout] = ((REV64(str[iin]) & 0x03) << 6) |
|
||||||
|
(REV64(str[iin + 1]) & 0x3f);
|
||||||
|
iin += 2; /* 2,3 used up, iin=4 */
|
||||||
|
iout++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ubuf[iout] = '\0';
|
||||||
|
|
||||||
|
return iout;
|
||||||
|
}
|
||||||
|
1319
src/client.c
1319
src/client.c
File diff suppressed because it is too large
Load Diff
@ -27,9 +27,11 @@ void client_set_nameserver(const char *cp, int port);
|
|||||||
void client_set_topdomain(const char *cp);
|
void client_set_topdomain(const char *cp);
|
||||||
void client_set_password(const char *cp);
|
void client_set_password(const char *cp);
|
||||||
void set_qtype(char *qtype);
|
void set_qtype(char *qtype);
|
||||||
|
char *get_qtype();
|
||||||
void set_downenc(char *encoding);
|
void set_downenc(char *encoding);
|
||||||
void client_set_selecttimeout(int select_timeout);
|
void client_set_selecttimeout(int select_timeout);
|
||||||
void client_set_lazymode(int lazy_mode);
|
void client_set_lazymode(int lazy_mode);
|
||||||
|
void client_set_hostname_maxlen(int i);
|
||||||
|
|
||||||
int client_handshake(int dns_fd, int raw_mode, int autodetect_frag_size, int fragsize);
|
int client_handshake(int dns_fd, int raw_mode, int autodetect_frag_size, int fragsize);
|
||||||
int client_tunnel(int tun_fd, int dns_fd);
|
int client_tunnel(int tun_fd, int dns_fd);
|
||||||
|
@ -74,6 +74,9 @@ extern const unsigned char raw_header[RAW_HDR_LEN];
|
|||||||
# define DONT_FRAG_VALUE 1
|
# define DONT_FRAG_VALUE 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define T_UNSET 65432
|
||||||
|
/* Unused RR type; "private use" range, see http://www.bind9.net/dns-parameters */
|
||||||
|
|
||||||
struct packet
|
struct packet
|
||||||
{
|
{
|
||||||
int len; /* Total packet length */
|
int len; /* Total packet length */
|
||||||
@ -89,10 +92,12 @@ struct query {
|
|||||||
unsigned short type;
|
unsigned short type;
|
||||||
unsigned short rcode;
|
unsigned short rcode;
|
||||||
unsigned short id;
|
unsigned short id;
|
||||||
unsigned short iddupe; /* only used for dupe checking */
|
|
||||||
struct in_addr destination;
|
struct in_addr destination;
|
||||||
struct sockaddr from;
|
struct sockaddr from;
|
||||||
int fromlen;
|
int fromlen;
|
||||||
|
unsigned short id2;
|
||||||
|
struct sockaddr from2;
|
||||||
|
int fromlen2;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum connection {
|
enum connection {
|
||||||
|
274
src/dns.c
274
src/dns.c
@ -39,6 +39,8 @@
|
|||||||
#include "encoding.h"
|
#include "encoding.h"
|
||||||
#include "read.h"
|
#include "read.h"
|
||||||
|
|
||||||
|
int dnsc_use_edns0 = 1;
|
||||||
|
|
||||||
#define CHECKLEN(x) if (buflen - (p-buf) < (x)) return 0
|
#define CHECKLEN(x) if (buflen - (p-buf) < (x)) return 0
|
||||||
|
|
||||||
int
|
int
|
||||||
@ -48,6 +50,7 @@ dns_encode(char *buf, size_t buflen, struct query *q, qr_t qr, char *data, size_
|
|||||||
short name;
|
short name;
|
||||||
char *p;
|
char *p;
|
||||||
int len;
|
int len;
|
||||||
|
int ancnt;
|
||||||
|
|
||||||
if (buflen < sizeof(HEADER))
|
if (buflen < sizeof(HEADER))
|
||||||
return 0;
|
return 0;
|
||||||
@ -68,7 +71,6 @@ dns_encode(char *buf, size_t buflen, struct query *q, qr_t qr, char *data, size_
|
|||||||
|
|
||||||
switch (qr) {
|
switch (qr) {
|
||||||
case QR_ANSWER:
|
case QR_ANSWER:
|
||||||
header->ancount = htons(1);
|
|
||||||
header->qdcount = htons(1);
|
header->qdcount = htons(1);
|
||||||
|
|
||||||
name = 0xc000 | ((p - buf) & 0x3fff);
|
name = 0xc000 | ((p - buf) & 0x3fff);
|
||||||
@ -81,56 +83,115 @@ dns_encode(char *buf, size_t buflen, struct query *q, qr_t qr, char *data, size_
|
|||||||
putshort(&p, C_IN);
|
putshort(&p, C_IN);
|
||||||
|
|
||||||
/* Answer section */
|
/* Answer section */
|
||||||
|
|
||||||
|
if (q->type == T_CNAME || q->type == T_A) {
|
||||||
|
/* data is expected to be like "Hblabla.host.name.com\0" */
|
||||||
|
|
||||||
|
char *startp;
|
||||||
|
int namelen;
|
||||||
|
|
||||||
CHECKLEN(10);
|
CHECKLEN(10);
|
||||||
putshort(&p, name);
|
putshort(&p, name);
|
||||||
if (q->type == T_A)
|
if (q->type == T_A)
|
||||||
putshort(&p, T_CNAME); /* answer CNAME to A question */
|
/* answer CNAME to A question */
|
||||||
|
putshort(&p, T_CNAME);
|
||||||
else
|
else
|
||||||
putshort(&p, q->type);
|
putshort(&p, q->type);
|
||||||
putshort(&p, C_IN);
|
putshort(&p, C_IN);
|
||||||
putlong(&p, 0); /* TTL */
|
putlong(&p, 0); /* TTL */
|
||||||
|
|
||||||
if (q->type == T_CNAME || q->type == T_A || q->type == T_MX) {
|
startp = p;
|
||||||
/* data is expected to be like "Hblabla.host.name.com\0" */
|
|
||||||
|
|
||||||
char *startp = p;
|
|
||||||
int namelen;
|
|
||||||
|
|
||||||
p += 2; /* skip 2 bytes length */
|
p += 2; /* skip 2 bytes length */
|
||||||
CHECKLEN(2);
|
|
||||||
if (q->type == T_MX)
|
|
||||||
putshort(&p, 10); /* preference */
|
|
||||||
putname(&p, buflen - (p - buf), data);
|
putname(&p, buflen - (p - buf), data);
|
||||||
CHECKLEN(0);
|
CHECKLEN(0);
|
||||||
namelen = p - startp;
|
namelen = p - startp;
|
||||||
namelen -= 2;
|
namelen -= 2;
|
||||||
putshort(&startp, namelen);
|
putshort(&startp, namelen);
|
||||||
|
ancnt = 1;
|
||||||
|
} else if (q->type == T_MX || q->type == T_SRV) {
|
||||||
|
/* Data is expected to be like
|
||||||
|
"Hblabla.host.name.com\0Hanother.com\0\0"
|
||||||
|
For SRV, see RFC2782.
|
||||||
|
*/
|
||||||
|
|
||||||
|
char *mxdata = data;
|
||||||
|
char *startp;
|
||||||
|
int namelen;
|
||||||
|
|
||||||
|
ancnt = 1;
|
||||||
|
while (1) {
|
||||||
|
CHECKLEN(10);
|
||||||
|
putshort(&p, name);
|
||||||
|
putshort(&p, q->type);
|
||||||
|
putshort(&p, C_IN);
|
||||||
|
putlong(&p, 0); /* TTL */
|
||||||
|
|
||||||
|
startp = p;
|
||||||
|
p += 2; /* skip 2 bytes length */
|
||||||
|
CHECKLEN(2);
|
||||||
|
putshort(&p, 10 * ancnt); /* preference */
|
||||||
|
|
||||||
|
if (q->type == T_SRV) {
|
||||||
|
/* weight, port (5060 = SIP) */
|
||||||
|
CHECKLEN(4);
|
||||||
|
putshort(&p, 10);
|
||||||
|
putshort(&p, 5060);
|
||||||
|
}
|
||||||
|
|
||||||
|
putname(&p, buflen - (p - buf), mxdata);
|
||||||
|
CHECKLEN(0);
|
||||||
|
namelen = p - startp;
|
||||||
|
namelen -= 2;
|
||||||
|
putshort(&startp, namelen);
|
||||||
|
|
||||||
|
mxdata = mxdata + strlen(mxdata) + 1;
|
||||||
|
if (*mxdata == '\0')
|
||||||
|
break;
|
||||||
|
|
||||||
|
ancnt++;
|
||||||
|
}
|
||||||
} else if (q->type == T_TXT) {
|
} else if (q->type == T_TXT) {
|
||||||
/* TXT has binary or base-X data */
|
/* TXT has binary or base-X data */
|
||||||
char *startp = p;
|
char *startp;
|
||||||
int txtlen;
|
int txtlen;
|
||||||
|
|
||||||
|
CHECKLEN(10);
|
||||||
|
putshort(&p, name);
|
||||||
|
putshort(&p, q->type);
|
||||||
|
putshort(&p, C_IN);
|
||||||
|
putlong(&p, 0); /* TTL */
|
||||||
|
|
||||||
|
startp = p;
|
||||||
p += 2; /* skip 2 bytes length */
|
p += 2; /* skip 2 bytes length */
|
||||||
puttxtbin(&p, buflen - (p - buf), data, datalen);
|
puttxtbin(&p, buflen - (p - buf), data, datalen);
|
||||||
CHECKLEN(0);
|
CHECKLEN(0);
|
||||||
txtlen = p - startp;
|
txtlen = p - startp;
|
||||||
txtlen -= 2;
|
txtlen -= 2;
|
||||||
putshort(&startp, txtlen);
|
putshort(&startp, txtlen);
|
||||||
|
ancnt = 1;
|
||||||
} else {
|
} else {
|
||||||
/* NULL has raw binary data */
|
/* NULL has raw binary data */
|
||||||
|
|
||||||
|
CHECKLEN(10);
|
||||||
|
putshort(&p, name);
|
||||||
|
putshort(&p, q->type);
|
||||||
|
putshort(&p, C_IN);
|
||||||
|
putlong(&p, 0); /* TTL */
|
||||||
|
|
||||||
datalen = MIN(datalen, buflen - (p - buf));
|
datalen = MIN(datalen, buflen - (p - buf));
|
||||||
CHECKLEN(2);
|
CHECKLEN(2);
|
||||||
putshort(&p, datalen);
|
putshort(&p, datalen);
|
||||||
CHECKLEN(datalen);
|
CHECKLEN(datalen);
|
||||||
putdata(&p, data, datalen);
|
putdata(&p, data, datalen);
|
||||||
CHECKLEN(0);
|
CHECKLEN(0);
|
||||||
|
ancnt = 1;
|
||||||
}
|
}
|
||||||
|
header->ancount = htons(ancnt);
|
||||||
break;
|
break;
|
||||||
case QR_QUERY:
|
case QR_QUERY:
|
||||||
/* Note that iodined also uses this for forward queries */
|
/* Note that iodined also uses this for forward queries */
|
||||||
|
|
||||||
header->qdcount = htons(1);
|
header->qdcount = htons(1);
|
||||||
header->arcount = htons(1);
|
|
||||||
|
|
||||||
datalen = MIN(datalen, buflen - (p - buf));
|
datalen = MIN(datalen, buflen - (p - buf));
|
||||||
putname(&p, datalen, data);
|
putname(&p, datalen, data);
|
||||||
@ -141,6 +202,9 @@ dns_encode(char *buf, size_t buflen, struct query *q, qr_t qr, char *data, size_
|
|||||||
|
|
||||||
/* EDNS0 to advertise maximum response length
|
/* EDNS0 to advertise maximum response length
|
||||||
(even CNAME/A/MX, 255+255+header would be >512) */
|
(even CNAME/A/MX, 255+255+header would be >512) */
|
||||||
|
if (dnsc_use_edns0) {
|
||||||
|
header->arcount = htons(1);
|
||||||
|
/*XXX START adjust indent 1 tab forward*/
|
||||||
CHECKLEN(11);
|
CHECKLEN(11);
|
||||||
putbyte(&p, 0x00); /* Root */
|
putbyte(&p, 0x00); /* Root */
|
||||||
putshort(&p, 0x0029); /* OPT */
|
putshort(&p, 0x0029); /* OPT */
|
||||||
@ -148,6 +212,9 @@ dns_encode(char *buf, size_t buflen, struct query *q, qr_t qr, char *data, size_
|
|||||||
putshort(&p, 0x0000); /* Higher bits/edns version */
|
putshort(&p, 0x0000); /* Higher bits/edns version */
|
||||||
putshort(&p, 0x8000); /* Z */
|
putshort(&p, 0x8000); /* Z */
|
||||||
putshort(&p, 0x0000); /* Data length */
|
putshort(&p, 0x0000); /* Data length */
|
||||||
|
/*XXX END adjust indent 1 tab forward*/
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,13 +226,14 @@ dns_encode(char *buf, size_t buflen, struct query *q, qr_t qr, char *data, size_
|
|||||||
int
|
int
|
||||||
dns_encode_ns_response(char *buf, size_t buflen, struct query *q, char *topdomain)
|
dns_encode_ns_response(char *buf, size_t buflen, struct query *q, char *topdomain)
|
||||||
/* Only used when iodined gets an NS type query */
|
/* Only used when iodined gets an NS type query */
|
||||||
|
/* Mostly same as dns_encode_a_response() below */
|
||||||
{
|
{
|
||||||
HEADER *header;
|
HEADER *header;
|
||||||
int len;
|
int len;
|
||||||
short name;
|
short name;
|
||||||
short topname;
|
short topname;
|
||||||
short nsname;
|
short nsname;
|
||||||
char *domain;
|
char *ipp;
|
||||||
int domain_len;
|
int domain_len;
|
||||||
char *p;
|
char *p;
|
||||||
|
|
||||||
@ -193,13 +261,16 @@ dns_encode_ns_response(char *buf, size_t buflen, struct query *q, char *topdomai
|
|||||||
/* pointer to start of name */
|
/* pointer to start of name */
|
||||||
name = 0xc000 | ((p - buf) & 0x3fff);
|
name = 0xc000 | ((p - buf) & 0x3fff);
|
||||||
|
|
||||||
domain = strstr(q->name, topdomain);
|
domain_len = strlen(q->name) - strlen(topdomain);
|
||||||
if (domain) {
|
if (domain_len < 0 || domain_len == 1)
|
||||||
domain_len = (int) (domain - q->name);
|
|
||||||
} else {
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
if (strcasecmp(q->name + domain_len, topdomain))
|
||||||
/* pointer to start of topdomain */
|
return -1;
|
||||||
|
if (domain_len >= 1 && q->name[domain_len - 1] != '.')
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* pointer to start of topdomain; instead of dots at the end
|
||||||
|
we have length-bytes in front, so total length is the same */
|
||||||
topname = 0xc000 | ((p - buf + domain_len) & 0x3fff);
|
topname = 0xc000 | ((p - buf + domain_len) & 0x3fff);
|
||||||
|
|
||||||
/* Query section */
|
/* Query section */
|
||||||
@ -233,12 +304,72 @@ dns_encode_ns_response(char *buf, size_t buflen, struct query *q, char *topdomai
|
|||||||
putshort(&p, 4); /* Data length */
|
putshort(&p, 4); /* Data length */
|
||||||
|
|
||||||
/* ugly hack to output IP address */
|
/* ugly hack to output IP address */
|
||||||
domain = (char *) &q->destination;
|
ipp = (char *) &q->destination;
|
||||||
CHECKLEN(4);
|
CHECKLEN(4);
|
||||||
putbyte(&p, *domain++);
|
putbyte(&p, *(ipp++));
|
||||||
putbyte(&p, *domain++);
|
putbyte(&p, *(ipp++));
|
||||||
putbyte(&p, *domain++);
|
putbyte(&p, *(ipp++));
|
||||||
putbyte(&p, *domain);
|
putbyte(&p, *ipp);
|
||||||
|
|
||||||
|
len = p - buf;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
dns_encode_a_response(char *buf, size_t buflen, struct query *q)
|
||||||
|
/* Only used when iodined gets an A type query for ns.topdomain or www.topdomain */
|
||||||
|
/* Mostly same as dns_encode_ns_response() above */
|
||||||
|
{
|
||||||
|
HEADER *header;
|
||||||
|
int len;
|
||||||
|
short name;
|
||||||
|
char *ipp;
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
if (buflen < sizeof(HEADER))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
memset(buf, 0, buflen);
|
||||||
|
|
||||||
|
header = (HEADER*)buf;
|
||||||
|
|
||||||
|
header->id = htons(q->id);
|
||||||
|
header->qr = 1;
|
||||||
|
header->opcode = 0;
|
||||||
|
header->aa = 1;
|
||||||
|
header->tc = 0;
|
||||||
|
header->rd = 0;
|
||||||
|
header->ra = 0;
|
||||||
|
|
||||||
|
p = buf + sizeof(HEADER);
|
||||||
|
|
||||||
|
header->qdcount = htons(1);
|
||||||
|
header->ancount = htons(1);
|
||||||
|
|
||||||
|
/* pointer to start of name */
|
||||||
|
name = 0xc000 | ((p - buf) & 0x3fff);
|
||||||
|
|
||||||
|
/* Query section */
|
||||||
|
putname(&p, buflen - (p - buf), q->name); /* Name */
|
||||||
|
CHECKLEN(4);
|
||||||
|
putshort(&p, q->type); /* Type */
|
||||||
|
putshort(&p, C_IN); /* Class */
|
||||||
|
|
||||||
|
/* Answer section */
|
||||||
|
CHECKLEN(12);
|
||||||
|
putshort(&p, name); /* Name */
|
||||||
|
putshort(&p, q->type); /* Type */
|
||||||
|
putshort(&p, C_IN); /* Class */
|
||||||
|
putlong(&p, 3600); /* TTL */
|
||||||
|
putshort(&p, 4); /* Data length */
|
||||||
|
|
||||||
|
/* ugly hack to output IP address */
|
||||||
|
ipp = (char *) &q->destination;
|
||||||
|
CHECKLEN(4);
|
||||||
|
putbyte(&p, *(ipp++));
|
||||||
|
putbyte(&p, *(ipp++));
|
||||||
|
putbyte(&p, *(ipp++));
|
||||||
|
putbyte(&p, *ipp);
|
||||||
|
|
||||||
len = p - buf;
|
len = p - buf;
|
||||||
return len;
|
return len;
|
||||||
@ -276,6 +407,7 @@ dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, siz
|
|||||||
int id;
|
int id;
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
|
q->id2 = 0;
|
||||||
rv = 0;
|
rv = 0;
|
||||||
header = (HEADER*)packet;
|
header = (HEADER*)packet;
|
||||||
|
|
||||||
@ -324,11 +456,15 @@ dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, siz
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ancount < 1) {
|
if (ancount < 1) {
|
||||||
/* We may get both CNAME and A, then ancount=2 */
|
/* DNS errors like NXDOMAIN have ancount=0 and
|
||||||
|
stop here. CNAME may also have A; MX/SRV may have
|
||||||
|
multiple results. */
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Assume that first answer is NULL/CNAME that we wanted */
|
/* Here type is still the question type */
|
||||||
|
if (type == T_NULL) {
|
||||||
|
/* Assume that first answer is what we wanted */
|
||||||
readname(packet, packetlen, &data, name, sizeof(name));
|
readname(packet, packetlen, &data, name, sizeof(name));
|
||||||
CHECKLEN(10);
|
CHECKLEN(10);
|
||||||
readshort(packet, &data, &type);
|
readshort(packet, &data, &type);
|
||||||
@ -336,7 +472,6 @@ dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, siz
|
|||||||
readlong(packet, &data, &ttl);
|
readlong(packet, &data, &ttl);
|
||||||
readshort(packet, &data, &rlen);
|
readshort(packet, &data, &rlen);
|
||||||
|
|
||||||
if (type == T_NULL) {
|
|
||||||
rv = MIN(rlen, sizeof(rdata));
|
rv = MIN(rlen, sizeof(rdata));
|
||||||
rv = readdata(packet, &data, rdata, rv);
|
rv = readdata(packet, &data, rdata, rv);
|
||||||
if (rv >= 2 && buf) {
|
if (rv >= 2 && buf) {
|
||||||
@ -346,9 +481,15 @@ dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, siz
|
|||||||
rv = 0;
|
rv = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((type == T_CNAME || type == T_MX) && buf) {
|
else if ((type == T_A || type == T_CNAME) && buf) {
|
||||||
if (type == T_MX)
|
/* Assume that first answer is what we wanted */
|
||||||
data += 2; /* skip preference */
|
readname(packet, packetlen, &data, name, sizeof(name));
|
||||||
|
CHECKLEN(10);
|
||||||
|
readshort(packet, &data, &type);
|
||||||
|
readshort(packet, &data, &class);
|
||||||
|
readlong(packet, &data, &ttl);
|
||||||
|
readshort(packet, &data, &rlen);
|
||||||
|
|
||||||
memset(name, 0, sizeof(name));
|
memset(name, 0, sizeof(name));
|
||||||
readname(packet, packetlen, &data, name, sizeof(name) - 1);
|
readname(packet, packetlen, &data, name, sizeof(name) - 1);
|
||||||
name[sizeof(name)-1] = '\0';
|
name[sizeof(name)-1] = '\0';
|
||||||
@ -356,7 +497,74 @@ dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, siz
|
|||||||
buf[buflen - 1] = '\0';
|
buf[buflen - 1] = '\0';
|
||||||
rv = strlen(buf);
|
rv = strlen(buf);
|
||||||
}
|
}
|
||||||
if (type == T_TXT && buf) {
|
else if ((type == T_MX || type == T_SRV) && buf) {
|
||||||
|
/* We support 250 records, 250*(255+header) ~= 64kB.
|
||||||
|
Only exact 10-multiples are accepted, and gaps in
|
||||||
|
numbering are not jumped over (->truncated).
|
||||||
|
Hopefully DNS servers won't mess around too much.
|
||||||
|
*/
|
||||||
|
char names[250][QUERY_NAME_SIZE];
|
||||||
|
char *rdatastart;
|
||||||
|
short pref;
|
||||||
|
int i;
|
||||||
|
int offset;
|
||||||
|
|
||||||
|
memset(names, 0, sizeof(names));
|
||||||
|
|
||||||
|
for (i=0; i < ancount; i++) {
|
||||||
|
readname(packet, packetlen, &data, name, sizeof(name));
|
||||||
|
CHECKLEN(12);
|
||||||
|
readshort(packet, &data, &type);
|
||||||
|
readshort(packet, &data, &class);
|
||||||
|
readlong(packet, &data, &ttl);
|
||||||
|
readshort(packet, &data, &rlen);
|
||||||
|
rdatastart = data;
|
||||||
|
readshort(packet, &data, &pref);
|
||||||
|
|
||||||
|
if (type == T_SRV) {
|
||||||
|
/* skip weight, port */
|
||||||
|
data += 4;
|
||||||
|
CHECKLEN(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pref % 10 == 0 && pref >= 10 &&
|
||||||
|
pref < 2500) {
|
||||||
|
readname(packet, packetlen, &data,
|
||||||
|
names[pref / 10 - 1],
|
||||||
|
QUERY_NAME_SIZE - 1);
|
||||||
|
names[pref / 10 - 1][QUERY_NAME_SIZE-1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* always trust rlen, not name encoding */
|
||||||
|
data = rdatastart + rlen;
|
||||||
|
CHECKLEN(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* output is like Hname10.com\0Hname20.com\0\0 */
|
||||||
|
offset = 0;
|
||||||
|
i = 0;
|
||||||
|
while (names[i][0] != '\0') {
|
||||||
|
int l = MIN(strlen(names[i]), buflen-offset-2);
|
||||||
|
if (l <= 0)
|
||||||
|
break;
|
||||||
|
memcpy(buf + offset, names[i], l);
|
||||||
|
offset += l;
|
||||||
|
*(buf + offset) = '\0';
|
||||||
|
offset++;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
*(buf + offset) = '\0';
|
||||||
|
rv = offset;
|
||||||
|
}
|
||||||
|
else if (type == T_TXT && buf) {
|
||||||
|
/* Assume that first answer is what we wanted */
|
||||||
|
readname(packet, packetlen, &data, name, sizeof(name));
|
||||||
|
CHECKLEN(10);
|
||||||
|
readshort(packet, &data, &type);
|
||||||
|
readshort(packet, &data, &class);
|
||||||
|
readlong(packet, &data, &ttl);
|
||||||
|
readshort(packet, &data, &rlen);
|
||||||
|
|
||||||
rv = readtxtbin(packet, &data, rlen, rdata, sizeof(rdata));
|
rv = readtxtbin(packet, &data, rlen, rdata, sizeof(rdata));
|
||||||
if (rv >= 1) {
|
if (rv >= 1) {
|
||||||
rv = MIN(rv, buflen);
|
rv = MIN(rv, buflen);
|
||||||
@ -365,6 +573,8 @@ dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, siz
|
|||||||
rv = 0;
|
rv = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Here type is the answer type (note A->CNAME) */
|
||||||
if (q != NULL)
|
if (q != NULL)
|
||||||
q->type = type;
|
q->type = type;
|
||||||
break;
|
break;
|
||||||
|
@ -24,8 +24,11 @@ typedef enum {
|
|||||||
QR_ANSWER = 1
|
QR_ANSWER = 1
|
||||||
} qr_t;
|
} qr_t;
|
||||||
|
|
||||||
|
extern int dnsc_use_edns0;
|
||||||
|
|
||||||
int dns_encode(char *, size_t, struct query *, qr_t, char *, size_t);
|
int dns_encode(char *, size_t, struct query *, qr_t, char *, size_t);
|
||||||
int dns_encode_ns_response(char *buf, size_t buflen, struct query *q, char *topdomain);
|
int dns_encode_ns_response(char *buf, size_t buflen, struct query *q, char *topdomain);
|
||||||
|
int dns_encode_a_response(char *buf, size_t buflen, struct query *q);
|
||||||
unsigned short dns_get_id(char *packet, size_t packetlen);
|
unsigned short dns_get_id(char *packet, size_t packetlen);
|
||||||
int dns_decode(char *, size_t, struct query *, qr_t, char *, size_t);
|
int dns_decode(char *, size_t, struct query *, qr_t, char *, size_t);
|
||||||
|
|
||||||
|
@ -21,13 +21,15 @@
|
|||||||
int
|
int
|
||||||
build_hostname(char *buf, size_t buflen,
|
build_hostname(char *buf, size_t buflen,
|
||||||
const char *data, const size_t datalen,
|
const char *data, const size_t datalen,
|
||||||
const char *topdomain, struct encoder *encoder)
|
const char *topdomain, struct encoder *encoder, int maxlen)
|
||||||
{
|
{
|
||||||
int encsize;
|
int encsize;
|
||||||
size_t space;
|
size_t space;
|
||||||
char *b;
|
char *b;
|
||||||
|
|
||||||
space = MIN(0xFF, buflen) - strlen(topdomain) - 7;
|
space = MIN(maxlen, buflen) - strlen(topdomain) - 8;
|
||||||
|
/* 8 = 5 max header length + 1 dot before topdomain + 2 safety */
|
||||||
|
|
||||||
if (!encoder->places_dots())
|
if (!encoder->places_dots())
|
||||||
space -= (space / 57); /* space for dots */
|
space -= (space / 57); /* space for dots */
|
||||||
|
|
||||||
|
@ -17,6 +17,13 @@
|
|||||||
#ifndef _ENCODING_H_
|
#ifndef _ENCODING_H_
|
||||||
#define _ENCODING_H_
|
#define _ENCODING_H_
|
||||||
|
|
||||||
|
/* All-0, all-1, 01010101, 10101010: each 4 times to make sure the pattern
|
||||||
|
spreads across multiple encoded chars -> 16 bytes total.
|
||||||
|
Followed by 32 bytes from my /dev/random; should be enough.
|
||||||
|
*/
|
||||||
|
#define DOWNCODECCHECK1 "\000\000\000\000\377\377\377\377\125\125\125\125\252\252\252\252\201\143\310\322\307\174\262\027\137\117\316\311\111\055\122\041\141\251\161\040\045\263\006\163\346\330\104\060\171\120\127\277"
|
||||||
|
#define DOWNCODECCHECK1_LEN 48
|
||||||
|
|
||||||
struct encoder {
|
struct encoder {
|
||||||
char name[8];
|
char name[8];
|
||||||
int (*encode) (char *, size_t *, const void *, size_t);
|
int (*encode) (char *, size_t *, const void *, size_t);
|
||||||
@ -27,7 +34,7 @@ struct encoder {
|
|||||||
int (*blocksize_encoded)(void);
|
int (*blocksize_encoded)(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
int build_hostname(char *, size_t, const char *, const size_t, const char *, struct encoder *);
|
int build_hostname(char *, size_t, const char *, const size_t, const char *, struct encoder *, int);
|
||||||
int unpack_data(char *, size_t, char *, size_t, struct encoder *);
|
int unpack_data(char *, size_t, char *, size_t, struct encoder *);
|
||||||
int inline_dotify(char *, size_t);
|
int inline_dotify(char *, size_t);
|
||||||
int inline_undotify(char *, size_t);
|
int inline_undotify(char *, size_t);
|
||||||
|
43
src/iodine.c
43
src/iodine.c
@ -61,7 +61,7 @@ usage() {
|
|||||||
extern char *__progname;
|
extern char *__progname;
|
||||||
|
|
||||||
fprintf(stderr, "Usage: %s [-v] [-h] [-f] [-r] [-u user] [-t chrootdir] [-d device] "
|
fprintf(stderr, "Usage: %s [-v] [-h] [-f] [-r] [-u user] [-t chrootdir] [-d device] "
|
||||||
"[-P password] [-m maxfragsize] [-T type] [-O enc] [-L 0|1] [-I sec] "
|
"[-P password] [-m maxfragsize] [-M maxlen] [-T type] [-O enc] [-L 0|1] [-I sec] "
|
||||||
"[-z context] [-F pidfile] [nameserver] topdomain\n", __progname);
|
"[-z context] [-F pidfile] [nameserver] topdomain\n", __progname);
|
||||||
exit(2);
|
exit(2);
|
||||||
}
|
}
|
||||||
@ -72,21 +72,25 @@ help() {
|
|||||||
|
|
||||||
fprintf(stderr, "iodine IP over DNS tunneling client\n");
|
fprintf(stderr, "iodine IP over DNS tunneling client\n");
|
||||||
fprintf(stderr, "Usage: %s [-v] [-h] [-f] [-r] [-u user] [-t chrootdir] [-d device] "
|
fprintf(stderr, "Usage: %s [-v] [-h] [-f] [-r] [-u user] [-t chrootdir] [-d device] "
|
||||||
"[-P password] [-m maxfragsize] [-T type] [-O enc] [-L 0|1] [-I sec] "
|
"[-P password] [-m maxfragsize] [-M maxlen] [-T type] [-O enc] [-L 0|1] [-I sec] "
|
||||||
"[-z context] [-F pidfile] [nameserver] topdomain\n", __progname);
|
"[-z context] [-F pidfile] [nameserver] topdomain\n", __progname);
|
||||||
|
fprintf(stderr, "Options to try if connection doesn't work:\n");
|
||||||
|
fprintf(stderr, " -T force dns type: NULL, TXT, SRV, MX, CNAME, A (default: autodetect)\n");
|
||||||
|
fprintf(stderr, " -O force downstream encoding for -T other than NULL: Base32, Base64, Base64u,\n");
|
||||||
|
fprintf(stderr, " Base128, or (only for TXT:) Raw (default: autodetect)\n");
|
||||||
|
fprintf(stderr, " -I max interval between requests (default 4 sec) to prevent DNS timeouts\n");
|
||||||
|
fprintf(stderr, " -L 1: use lazy mode for low-latency (default). 0: don't (implies -I1)\n");
|
||||||
|
fprintf(stderr, " -m max size of downstream fragments (default: autodetect)\n");
|
||||||
|
fprintf(stderr, " -M max size of upstream hostnames (~100-255, default: 255)\n");
|
||||||
|
fprintf(stderr, " -r to skip raw UDP mode attempt\n");
|
||||||
|
fprintf(stderr, " -P password used for authentication (max 32 chars will be used)\n");
|
||||||
|
fprintf(stderr, "Other options:\n");
|
||||||
fprintf(stderr, " -v to print version info and exit\n");
|
fprintf(stderr, " -v to print version info and exit\n");
|
||||||
fprintf(stderr, " -h to print this help and exit\n");
|
fprintf(stderr, " -h to print this help and exit\n");
|
||||||
fprintf(stderr, " -f to keep running in foreground\n");
|
fprintf(stderr, " -f to keep running in foreground\n");
|
||||||
fprintf(stderr, " -r to skip raw UDP mode attempt\n");
|
|
||||||
fprintf(stderr, " -u name to drop privileges and run as user 'name'\n");
|
fprintf(stderr, " -u name to drop privileges and run as user 'name'\n");
|
||||||
fprintf(stderr, " -t dir to chroot to directory dir\n");
|
fprintf(stderr, " -t dir to chroot to directory dir\n");
|
||||||
fprintf(stderr, " -d device to set tunnel device name\n");
|
fprintf(stderr, " -d device to set tunnel device name\n");
|
||||||
fprintf(stderr, " -P password used for authentication (max 32 chars will be used)\n");
|
|
||||||
fprintf(stderr, " -m maxfragsize, to limit size of downstream packets\n");
|
|
||||||
fprintf(stderr, " -T dns type: NULL (default, fastest), TXT, CNAME, A (CNAME answer), MX\n");
|
|
||||||
fprintf(stderr, " -O downstream encoding (!NULL): Base32(default), Base64, or Raw (only TXT)\n");
|
|
||||||
fprintf(stderr, " -L 1: try lazy mode for low-latency (default). 0: don't (implies -I1)\n");
|
|
||||||
fprintf(stderr, " -I max interval between requests (default 4 sec) to prevent server timeouts\n");
|
|
||||||
fprintf(stderr, " -z context, to apply specified SELinux context after initialization\n");
|
fprintf(stderr, " -z context, to apply specified SELinux context after initialization\n");
|
||||||
fprintf(stderr, " -F pidfile to write pid to a file\n");
|
fprintf(stderr, " -F pidfile to write pid to a file\n");
|
||||||
fprintf(stderr, "nameserver is the IP number/hostname of the relaying nameserver. if absent, /etc/resolv.conf is used\n");
|
fprintf(stderr, "nameserver is the IP number/hostname of the relaying nameserver. if absent, /etc/resolv.conf is used\n");
|
||||||
@ -131,6 +135,7 @@ main(int argc, char **argv)
|
|||||||
int raw_mode;
|
int raw_mode;
|
||||||
int lazymode;
|
int lazymode;
|
||||||
int selecttimeout;
|
int selecttimeout;
|
||||||
|
int hostname_maxlen;
|
||||||
|
|
||||||
nameserv_addr = NULL;
|
nameserv_addr = NULL;
|
||||||
topdomain = NULL;
|
topdomain = NULL;
|
||||||
@ -152,6 +157,7 @@ main(int argc, char **argv)
|
|||||||
raw_mode = 1;
|
raw_mode = 1;
|
||||||
lazymode = 1;
|
lazymode = 1;
|
||||||
selecttimeout = 4;
|
selecttimeout = 4;
|
||||||
|
hostname_maxlen = 0xFF;
|
||||||
|
|
||||||
#ifdef WINDOWS32
|
#ifdef WINDOWS32
|
||||||
WSAStartup(req_version, &wsa_data);
|
WSAStartup(req_version, &wsa_data);
|
||||||
@ -168,7 +174,7 @@ main(int argc, char **argv)
|
|||||||
__progname++;
|
__progname++;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
while ((choice = getopt(argc, argv, "vfhru:t:d:P:m:F:T:O:L:I:")) != -1) {
|
while ((choice = getopt(argc, argv, "vfhru:t:d:P:m:M:F:T:O:L:I:")) != -1) {
|
||||||
switch(choice) {
|
switch(choice) {
|
||||||
case 'v':
|
case 'v':
|
||||||
version();
|
version();
|
||||||
@ -203,6 +209,13 @@ main(int argc, char **argv)
|
|||||||
autodetect_frag_size = 0;
|
autodetect_frag_size = 0;
|
||||||
max_downstream_frag_size = atoi(optarg);
|
max_downstream_frag_size = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
|
case 'M':
|
||||||
|
hostname_maxlen = atoi(optarg);
|
||||||
|
if (hostname_maxlen > 255)
|
||||||
|
hostname_maxlen = 255;
|
||||||
|
if (hostname_maxlen < 10)
|
||||||
|
hostname_maxlen = 10;
|
||||||
|
break;
|
||||||
case 'z':
|
case 'z':
|
||||||
context = optarg;
|
context = optarg;
|
||||||
break;
|
break;
|
||||||
@ -283,6 +296,7 @@ main(int argc, char **argv)
|
|||||||
client_set_selecttimeout(selecttimeout);
|
client_set_selecttimeout(selecttimeout);
|
||||||
client_set_lazymode(lazymode);
|
client_set_lazymode(lazymode);
|
||||||
client_set_topdomain(topdomain);
|
client_set_topdomain(topdomain);
|
||||||
|
client_set_hostname_maxlen(hostname_maxlen);
|
||||||
|
|
||||||
if (username != NULL) {
|
if (username != NULL) {
|
||||||
#ifndef WINDOWS32
|
#ifndef WINDOWS32
|
||||||
@ -315,17 +329,20 @@ main(int argc, char **argv)
|
|||||||
signal(SIGINT, sighandler);
|
signal(SIGINT, sighandler);
|
||||||
signal(SIGTERM, sighandler);
|
signal(SIGTERM, sighandler);
|
||||||
|
|
||||||
|
fprintf(stderr, "Sending DNS queries for %s to %s\n",
|
||||||
|
topdomain, nameserv_addr);
|
||||||
|
|
||||||
if (client_handshake(dns_fd, raw_mode, autodetect_frag_size, max_downstream_frag_size)) {
|
if (client_handshake(dns_fd, raw_mode, autodetect_frag_size, max_downstream_frag_size)) {
|
||||||
retval = 1;
|
retval = 1;
|
||||||
goto cleanup2;
|
goto cleanup2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client_get_conn() == CONN_DNS_NULL) {
|
if (client_get_conn() == CONN_RAW_UDP) {
|
||||||
fprintf(stderr, "Sending queries for %s to %s\n", topdomain, nameserv_addr);
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Sending raw traffic directly to %s\n", client_get_raw_addr());
|
fprintf(stderr, "Sending raw traffic directly to %s\n", client_get_raw_addr());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "Connection setup complete, transmitting data.\n");
|
||||||
|
|
||||||
if (foreground == 0)
|
if (foreground == 0)
|
||||||
do_detach();
|
do_detach();
|
||||||
|
|
||||||
|
698
src/iodined.c
698
src/iodined.c
File diff suppressed because it is too large
Load Diff
16
src/user.h
16
src/user.h
@ -24,7 +24,14 @@
|
|||||||
lead to massive dropping in multi-user situations with high traffic. */
|
lead to massive dropping in multi-user situations with high traffic. */
|
||||||
|
|
||||||
#define DNSCACHE_LEN 4
|
#define DNSCACHE_LEN 4
|
||||||
/* Undefine to disable. MUST be less than 7; also see comments in iodined.c */
|
/* Undefine to disable. Should be less than 18; also see comments in iodined.c */
|
||||||
|
|
||||||
|
|
||||||
|
#define QMEMPING_LEN 30
|
||||||
|
/* Max advisable: 64k/2 = 32000. Total mem usage: QMEMPING_LEN * USERS * 6 bytes */
|
||||||
|
|
||||||
|
#define QMEMDATA_LEN 15
|
||||||
|
/* Max advisable: 36/2 = 18. Total mem usage: QMEMDATA_LEN * USERS * 6 bytes */
|
||||||
|
|
||||||
struct user {
|
struct user {
|
||||||
char id;
|
char id;
|
||||||
@ -35,7 +42,6 @@ struct user {
|
|||||||
in_addr_t tun_ip;
|
in_addr_t tun_ip;
|
||||||
struct in_addr host;
|
struct in_addr host;
|
||||||
struct query q;
|
struct query q;
|
||||||
struct query q_prev;
|
|
||||||
struct query q_sendrealsoon;
|
struct query q_sendrealsoon;
|
||||||
int q_sendrealsoon_new;
|
int q_sendrealsoon_new;
|
||||||
struct packet inpacket;
|
struct packet inpacket;
|
||||||
@ -48,6 +54,12 @@ struct user {
|
|||||||
int fragsize;
|
int fragsize;
|
||||||
enum connection conn;
|
enum connection conn;
|
||||||
int lazy;
|
int lazy;
|
||||||
|
unsigned char qmemping_cmc[QMEMPING_LEN * 4];
|
||||||
|
unsigned short qmemping_type[QMEMPING_LEN];
|
||||||
|
int qmemping_lastfilled;
|
||||||
|
unsigned char qmemdata_cmc[QMEMDATA_LEN * 4];
|
||||||
|
unsigned short qmemdata_type[QMEMDATA_LEN];
|
||||||
|
int qmemdata_lastfilled;
|
||||||
#ifdef OUTPACKETQ_LEN
|
#ifdef OUTPACKETQ_LEN
|
||||||
struct packet outpacketq[OUTPACKETQ_LEN];
|
struct packet outpacketq[OUTPACKETQ_LEN];
|
||||||
int outpacketq_nexttouse;
|
int outpacketq_nexttouse;
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
/* This is the version of the network protocol
|
/* This is the version of the network protocol
|
||||||
It is usually equal to the latest iodine version number */
|
It is usually equal to the latest iodine version number */
|
||||||
#define VERSION 0x00000501
|
#define VERSION 0x00000502
|
||||||
|
|
||||||
#endif /* _VERSION_H_ */
|
#endif /* _VERSION_H_ */
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user