Merge dbe9a10fc1
into 8e15a73a77
This commit is contained in:
commit
2a972feb79
|
@ -6,6 +6,11 @@ iodine - http://code.kryo.se/iodine
|
|||
CHANGES:
|
||||
|
||||
master:
|
||||
- Multiple fragments can be in transit at one time to improve
|
||||
throughput on high-latency connections
|
||||
Server can now store multiple pending queries per user.
|
||||
- Added bidirectional compression flags
|
||||
- Added statistics report every few seconds in iodine (-V)
|
||||
- Mac OS X: Support native utun VPN devices. Patch by
|
||||
Peter Sagerson, ported from OpenVPN by Catalin Patulea.
|
||||
- Fix compilation failure on kFreeBSD and Hurd, by Gregor Herrmann
|
||||
|
|
3
Makefile
3
Makefile
|
@ -18,6 +18,9 @@ TARGETOS = `uname`
|
|||
|
||||
all:
|
||||
@(cd src; $(MAKE) TARGETOS=$(TARGETOS) all)
|
||||
|
||||
debug:
|
||||
@(cd src; $(MAKE) TARGETOS=$(TARGETOS) debug)
|
||||
|
||||
install: all
|
||||
$(MKDIR) $(MKDIR_FLAGS) $(DESTDIR)$(sbindir)
|
||||
|
|
84
README.md
84
README.md
|
@ -16,6 +16,7 @@ relevant header files are found in `/usr/include`.
|
|||
(See script at `./src/osflags`)
|
||||
|
||||
Run `make` to compile the server and client binaries.
|
||||
Run `make debug` to compile the binaries with extra debugging enabled.
|
||||
Run `make install` to copy binaries and manpage to the destination directory.
|
||||
Run `make test` to compile and run the unit tests. (Requires the `check` library)
|
||||
|
||||
|
@ -24,11 +25,11 @@ QUICKSTART
|
|||
----------
|
||||
|
||||
Try it out within your own LAN! Follow these simple steps:
|
||||
- On your server, run: `./iodined -f 10.0.0.1 test.com`.
|
||||
If you already use the `10.0.0.0` network, use another internal net like
|
||||
`172.16.0.0`.
|
||||
- On your server, run: `./iodined -f test.com 10.0.0.1`.
|
||||
If you already use the `10.0.0.0/8` network, use another internal net like
|
||||
`172.16.0.0/12`.
|
||||
- Enter a password.
|
||||
- On the client, run: `./iodine -f -r 192.168.0.1 test.com`.
|
||||
- On the client, run: `./iodine -f -r test.com 192.168.0.1`.
|
||||
Replace `192.168.0.1` with your server's ip address.
|
||||
- Enter the same password.
|
||||
- Now the client has the tunnel ip `10.0.0.2` and the server has `10.0.0.1`.
|
||||
|
@ -91,13 +92,13 @@ Resulting commandline in this example situation:
|
|||
./iodined -f -c -P secretpassword 192.168.99.1 t1.mydomain.com
|
||||
|
||||
### Client side
|
||||
All the setup is done, just start `iodine`. It takes one or two arguments, the
|
||||
first is the local relaying DNS server (optional) and the second is the domain
|
||||
you used (`t1.mydomain.com`). If you don't specify the first argument, the
|
||||
system's current DNS setting will be consulted.
|
||||
All the setup is done, just start `iodine`. It takes one or more arguments, the
|
||||
first is the the domain you used (`t1.mydomain.com`) and the remaining options
|
||||
are a list of local relaying DNS server (optional). If you don't specify more
|
||||
than one argument, the system's current DNS setting will be consulted.
|
||||
|
||||
If DNS queries are allowed to any computer, you can directly give the `iodined`
|
||||
server's address as first argument (in the example: `t1ns.mydomain.com` or
|
||||
server's address as second argument (in the example: `t1ns.mydomain.com` or
|
||||
`10.15.213.99`). In that case, it may also happen that _any_ traffic is allowed
|
||||
to the DNS port (53 UDP) of any computer. Iodine will detect this, and switch
|
||||
to raw UDP tunneling if possible. To force DNS tunneling in any case, use the
|
||||
|
@ -184,15 +185,15 @@ 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 minimum `-M` value is about 20, since the first 10 bytes or so are the
|
||||
Base32 encoded data header and the remainder is the actual encoded data and
|
||||
packets can be split into up to 255 fragments.
|
||||
|
||||
The upstream data is sent gzipped encoded with Base32; or Base64 if the relay
|
||||
server supports mixed case and `+` in domain names; or Base64u if `_` is
|
||||
supported instead; or Base128 if high-byte-value characters are supported.
|
||||
This upstream encoding is autodetected. The DNS protocol allows one query per
|
||||
packet, and one query can be max 256 chars. Each domain name part can be max
|
||||
packet, and one query can be max 255 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.
|
||||
|
||||
|
@ -220,24 +221,24 @@ when that hostname exceeds ca. 180 characters. In these and similar cases, use
|
|||
the `-O` option to try other downstream codecs; Base32 should always work.
|
||||
|
||||
Normal operation now is for the server to _not_ answer a DNS request until
|
||||
the next DNS request has come in, a.k.a. being “lazy”. This way, the server
|
||||
it has timed out (see server timeout), a.k.a. being “lazy”. This way, the server
|
||||
will always have a DNS request handy when new downstream data has to be sent.
|
||||
This greatly improves (interactive) performance and latency, and allows to
|
||||
slow down the quiescent ping requests to 4 second intervals by default, and
|
||||
possibly much slower. In fact, the main purpose of the pings now is to force
|
||||
a reply to the previous ping, and prevent DNS server timeouts (usually at
|
||||
least 5-10 seconds per RFC1035). Some DNS servers are more impatient and will
|
||||
give SERVFAIL errors (timeouts) in periods without tunneled data traffic. All
|
||||
data should still get through in these cases, but `iodine` will reduce the ping
|
||||
interval to 1 second anyway (-I1) to reduce the number of error messages. This
|
||||
may not help for very impatient DNS relays like `dnsadvantage.com` (ultradns),
|
||||
which time out in 1 second or even less. Yet data will still get trough, and
|
||||
you can ignore the `SERVFAIL` errors.
|
||||
possibly much slower. Some DNS servers are more impatient and will give SERVFAIL
|
||||
errors (timeouts) randomly or consistently if the target timeout is too high. All
|
||||
data should still get through in these cases, but `iodine` will reduce the target
|
||||
interval slowly to reduce the number of SERVFAILS. In these scenarios, it is best
|
||||
to manually set the target interval to something which is definitely less than the
|
||||
most impatient DNS server timeout in the connection to ensure maximum reliability.
|
||||
Some very impatient DNS relays like `dnsadvantage.com` (ultradns), which time
|
||||
out in 1 second or even less can cause issues. Yet data will still get trough, and
|
||||
you can probably ignore the `SERVFAIL` errors.
|
||||
|
||||
If you are running on a local network without any DNS server in-between, try
|
||||
`-I 50` (iodine and iodined close the connection after 60 seconds of silence).
|
||||
The only time you'll notice a slowdown, is when DNS reply packets go missing;
|
||||
the `iodined` server then has to wait for a new ping to re-send the data. You can
|
||||
the `iodined` server fragment will have to time-out to be resent. You can
|
||||
speed this up by generating some upstream traffic (keypress, ping). If this
|
||||
happens often, check your network for bottlenecks and/or run with `-I1`.
|
||||
|
||||
|
@ -256,7 +257,8 @@ If you have problems, try inspecting the traffic with network monitoring tools
|
|||
like tcpdump or ethereal/wireshark, and make sure that the relaying DNS server
|
||||
has not cached the response. A cached error message could mean that you
|
||||
started the client before the server. The `-D` (and `-DD`) option on the server
|
||||
can also show received and sent queries.
|
||||
can also show received and sent queries. To assist in diagnosis, you may wish to
|
||||
recompile with `make debug` and use `-DDDDD` to see more debug output.
|
||||
|
||||
|
||||
TIPS & TRICKS
|
||||
|
@ -290,8 +292,12 @@ undefine it to save a few more kilobytes.
|
|||
PERFORMANCE
|
||||
-----------
|
||||
|
||||
This section tabulates some performance measurements. To view properly, use
|
||||
a fixed-width font like Courier.
|
||||
**Please note:** The following performance is outdated and does not apply to
|
||||
the current version of iodine. Since the protocol was updated to use a sliding
|
||||
window for sending fragments, the throughput was greatly increased and should
|
||||
be much higher even on high-latency connections.
|
||||
|
||||
This section tabulates some performance measurements.
|
||||
|
||||
Measurements were done in protocol 00000502 in lazy mode; upstream encoding
|
||||
always Base128; `iodine -M255`; `iodined -m1130`. Network conditions were not
|
||||
|
@ -306,9 +312,8 @@ explains why some values are exactly equal.
|
|||
Ping round-trip times measured with `ping -c100`, presented are average rtt
|
||||
and mean deviation (indicating spread around the average), in milliseconds.
|
||||
|
||||
|
||||
### Situation 1: `Laptop -> Wifi AP -> Home server -> DSL provider -> Datacenter`
|
||||
|
||||
```
|
||||
iodine DNS "relay" bind9 DNS cache iodined
|
||||
|
||||
downstr. upstream downstr. ping-up ping-down
|
||||
|
@ -336,9 +341,9 @@ and mean deviation (indicating spread around the average), in milliseconds.
|
|||
|
||||
[174.7* : these all have 2frag/packet]
|
||||
|
||||
|
||||
```
|
||||
### Situation 2: `Laptop -> Wifi+vpn / wired -> Home server`
|
||||
|
||||
```
|
||||
iodine iodined
|
||||
|
||||
downstr. upstream downstr. ping-up ping-down
|
||||
|
@ -348,16 +353,15 @@ and mean deviation (indicating spread around the average), in milliseconds.
|
|||
wifi + openvpn -Tnull 1186 166.0 1022.3 6.3 1.3 6.6 1.6
|
||||
|
||||
wired -Tnull 1186 677.2 2464.1 1.3 0.2 1.3 0.1
|
||||
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
Performance is strongly coupled to low ping times, as iodine requires
|
||||
confirmation for every data fragment before moving on to the next. Allowing
|
||||
multiple fragments in-flight like TCP could possibly increase performance,
|
||||
but it would likely cause serious overload for the intermediary DNS servers.
|
||||
The current protocol scales performance with DNS responsivity, since the
|
||||
DNS servers are on average handling at most one DNS request per client.
|
||||
Multiple fragments in-flight like TCP is allowed in iodine, which does increase
|
||||
performance, but it may overload some low-bandwidth intermediary DNS servers.
|
||||
Using carrier-grade DNS servers such as those provided by your ISP should be able
|
||||
to handle a high volume of DNS queries, and it is recommended to use as many DNS
|
||||
nameservers as possible to balance the load.
|
||||
|
||||
|
||||
PORTABILITY
|
||||
|
@ -388,8 +392,8 @@ THANKS
|
|||
AUTHORS & LICENSE
|
||||
-----------------
|
||||
|
||||
Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>, 2006-2009 Bjorn
|
||||
Andersson <flex@kryo.se>. Also major contributions by Anne Bezemer.
|
||||
Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>, 2015 Frekk van Blagh,
|
||||
2006-2009 Bjorn Andersson <flex@kryo.se>. Also major contributions by Anne Bezemer.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||
with or without fee is hereby granted, provided that the above copyright notice
|
||||
|
|
|
@ -0,0 +1,361 @@
|
|||
Detailed specification of protocol in version 00000800
|
||||
======================================================
|
||||
|
||||
Note: work in progress!!
|
||||
|
||||
======================================================
|
||||
1. DNS protocol
|
||||
======================================================
|
||||
|
||||
Quick alphabetical index / register:
|
||||
0-9 Data packet
|
||||
A-F Data packet
|
||||
I IP address
|
||||
L Login
|
||||
N Downstream fragsize (NS.topdomain A-type reply)
|
||||
O Options
|
||||
P Ping
|
||||
R Downstream fragsize probe
|
||||
S Switch upstream codec
|
||||
V Version
|
||||
W (WWW.topdomain A-type reply)
|
||||
Y Downstream codec check
|
||||
Z Upstream codec check
|
||||
|
||||
|
||||
CMC = Cache Miss Counter, increased every time it is used
|
||||
|
||||
Version:
|
||||
Client sends:
|
||||
First byte v or V
|
||||
Rest encoded with base32:
|
||||
4 bytes big endian protocol version
|
||||
CMC
|
||||
Server replies:
|
||||
4 chars:
|
||||
VACK (version ok), followed by login challenge
|
||||
VNAK (version differs), followed by server protocol version
|
||||
VFUL (server has no free slots), followed by max users
|
||||
4 byte value: means login challenge/server protocol version/max users
|
||||
1 byte userid of the new user, or any byte if not VACK
|
||||
|
||||
Login:
|
||||
Client sends:
|
||||
First byte l or L
|
||||
1 byte userid char (hex)
|
||||
Rest encoded with base32:
|
||||
1 byte flags: (least to most significant bits)
|
||||
0: connect to remote TCP port (data pipe/ProxyCommand mode)
|
||||
1: use non-localhost remote IP
|
||||
2: remote IP is IPv6
|
||||
3: use TCP-over-tun optimisation (drop extra packets)
|
||||
4: check forward connected status
|
||||
5-8: unused
|
||||
16 bytes MD5 hash of: (first 32 bytes of password) xor (8 repetitions of login challenge)
|
||||
2 bytes remote TCP port (big endian)
|
||||
(TCP port appears only when flags bit 0 is set)
|
||||
4-16 bytes remote IP address (4 bytes if IPv4 or 16 for IPv6)
|
||||
(IP address included only if flags bits 0, 1 are set)
|
||||
2 bytes CMC
|
||||
Server replies:
|
||||
LNAK means either auth or options not accepted
|
||||
flag [-x.x.x.x-y.y.y.y-mtu-netmask]|[error message] means accepted
|
||||
(server ip, client ip, mtu, netmask bits)
|
||||
flag can be one of:
|
||||
I: Login success (followed by IP addresses in [...])
|
||||
C: TCP forward connected
|
||||
W: TCP forward connection waiting
|
||||
E: TCP connection error - followed by human readable message string
|
||||
If the requested TCP forwarding options are not accepted by the server, the
|
||||
response is simply LNAK.
|
||||
If TCP forwarding is requested, server opens a connection to the specified host
|
||||
and IP in the background and responds with a flag corresponding to the current
|
||||
connection status.
|
||||
The client repeats login requests with the check flag (bit 4) set to poll TCP
|
||||
connection status, not resending the remote host address or setting any other
|
||||
flags. Once the server responds with 'C' or 'E', the client either continues
|
||||
the handshake or prints the error message and exits.
|
||||
|
||||
|
||||
IP Request: (for where to try raw login)
|
||||
Client sends:
|
||||
First byte i or I
|
||||
1 byte userid char (hex)
|
||||
CMC as 4 Base32 chars
|
||||
Server replies
|
||||
BADIP if bad userid
|
||||
First byte I
|
||||
Then comes external IP address of iodined server
|
||||
as 4 bytes (IPv4) or 16 bytes (IPv6)
|
||||
|
||||
Upstream codec check / bounce:
|
||||
Client sends:
|
||||
First byte z or Z
|
||||
Lots of data that should not be decoded
|
||||
Server replies:
|
||||
The requested domain copied raw, in the lowest-grade downstream codec
|
||||
available for the request type.
|
||||
|
||||
Downstream codec check:
|
||||
Client sends:
|
||||
First byte y or Y
|
||||
1 char, meaning downstream codec to use
|
||||
5 bits coded as Base32 char, meaning check variant
|
||||
CMC as 3 Base32 chars
|
||||
Possibly extra data, depending on check variant
|
||||
Server sends:
|
||||
Data encoded with requested downstream codec; data content depending
|
||||
on check variant number.
|
||||
BADCODEC if requested downstream codec not available.
|
||||
BADLEN if check variant is not available, or problem with extra data.
|
||||
|
||||
Downstream codec chars are same as in 'O' Option request, below.
|
||||
|
||||
Check variants:
|
||||
1: Send encoded DOWNCODECCHECK1 string as defined in encoding.h
|
||||
|
||||
(Other variants reserved; possibly variant that sends a decoded-encoded
|
||||
copy of Base32-encoded extra data in the request)
|
||||
|
||||
Switch codec:
|
||||
Client sends:
|
||||
First byte s or S
|
||||
1 byte userid char (hex)
|
||||
rest encoded in base32:
|
||||
1 byte meaning number of bits per encoded byte in new codec:
|
||||
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)
|
||||
2 bytes CMC
|
||||
Server sends:
|
||||
Name of codec if accepted. After this all upstream data packets must
|
||||
be encoded with the new codec.
|
||||
BADCODEC if not accepted. Client must then revert to previous codec
|
||||
BADLEN if length of query is too short
|
||||
|
||||
|
||||
Set Options:
|
||||
Client sends:
|
||||
First byte o or O
|
||||
1 byte userid char (hex)
|
||||
rest encoded in base32:
|
||||
1 byte option flags and 2 bytes CMC:
|
||||
0 1 - 3
|
||||
+76543210+---+
|
||||
|0TSUVRCL|CMC|
|
||||
+--------+---+
|
||||
Server sends:
|
||||
Full name of encoding type used if successful (case insensitive).
|
||||
BADCODEC if not accepted. Previous situation remains.
|
||||
BADLEN if number of options doesn't match length of query.
|
||||
All options affect only the requesting client.
|
||||
Option flags:
|
||||
T: Downstream encoding Base32, for TXT/CNAME/A/MX (default)
|
||||
S: Downstream encoding Base64, for TXT/CNAME/A/MX
|
||||
U: Downstream encoding Base64u, for TXT/CNAME/A/MX
|
||||
V: Downstream encoding Base128, for TXT/CNAME/A/MX
|
||||
R: Downstream encoding Raw, for PRIVATE/TXT/NULL (assumed for
|
||||
PRIVATE and NULL)
|
||||
C: Downstream compression enabled (compressed before encoding)
|
||||
L: Lazy mode enabled, server will keep a number of requests waiting until
|
||||
data becomes available to send downstream or the requests time out.
|
||||
The timeout value for requests is controlled by the client.
|
||||
Applies only to data transfer; handshake is always answered immediately.
|
||||
If codec unsupported for request type, server will use Base32; note
|
||||
that server will answer any mix of request types that a client sends.
|
||||
Server may disregard the encoding options; client must always use the
|
||||
downstream encoding type indicated in every downstream DNS packet.
|
||||
|
||||
|
||||
Probe downstream fragment size:
|
||||
Client sends:
|
||||
First byte r or R
|
||||
1 byte userid char (hex)
|
||||
2 bytes big-endian fragsize encoded as 4 bytes base32
|
||||
Then follows a long random query which contents does not matter.
|
||||
Server sends:
|
||||
Requested number of bytes as a response. The first two bytes contain
|
||||
the requested length. The third byte is 107 (0x6B). The fourth byte
|
||||
is a random value, and each following byte is incremented with 107.
|
||||
This is checked by the client to determine corruption.
|
||||
BADFRAG if requested length not accepted.
|
||||
|
||||
Set downstream fragment size:
|
||||
Client sends:
|
||||
First byte n or N
|
||||
1 byte userid char (hex)
|
||||
Rest encoded with base32:
|
||||
2 bytes new downstream fragment size (big-endian)
|
||||
CMC
|
||||
Server sends:
|
||||
2 bytes new downstream fragment size. After this all downstream
|
||||
payloads will be max (fragsize + 2) bytes long.
|
||||
BADFRAG if not accepted.
|
||||
|
||||
|
||||
Upstream data header:
|
||||
76543 21076 54321076 54321076 5432
|
||||
+!----+!----+!----!--+--!----!+----+
|
||||
|0UUUU|UDCMC| Seq ID | Dn ACK |ACFL|
|
||||
+-----+-----+--------+--------+----+
|
||||
|
||||
Downstream data header: |=> only if ping (P) flag set |
|
||||
0 1 2 3 4 5 6
|
||||
+--------+--------+76543210+--------+--------+--------+--------+
|
||||
| Seq ID | Up ACK |0EIPACFL|Dn Wsize|Up Wsize|DnWstart|UpWstart|
|
||||
+--------+--------+--------+--------+--------+--------+--------+
|
||||
|
||||
UUUU = Userid
|
||||
L = Last fragment flag
|
||||
A = ACK flag
|
||||
F = First fragment flag
|
||||
C = Compression enabled for downstream packet
|
||||
P = ping flag: extra header present
|
||||
I = responded to immediately (for RTT calculation) - downstream only
|
||||
E = TCP Forward error (data following is text string reason)
|
||||
UDCMC = Upstream Data CMC char (base36 [a-z0-9])
|
||||
|
||||
Up/Dn Wsize/Wstart = upstream/downstream window size/window start Seq ID
|
||||
|
||||
Upstream data packet starts with 1 byte ASCII hex coded user byte; then
|
||||
1 char data-CMC; then 4 bytes Base32 encoded header; then comes the payload
|
||||
data, encoded with the chosen upstream codec.
|
||||
|
||||
Downstream data starts with 3 byte header, followed by data, which may be
|
||||
compressed. If Ping flag is set, another 4 bytes are appended to the header,
|
||||
containing upstream and downstream window sizes and window start sequence IDs.
|
||||
The response does not need to contain data. If the server has no data to send,
|
||||
the response will always include the ping header and the ping flag will be set.
|
||||
|
||||
If the TCP forward error (E) flag is set, the TCP connection at the server is
|
||||
closed and the client sends EOF to stdout and exits.
|
||||
|
||||
In NULL and PRIVATE responses, downstream data is always raw. In all other
|
||||
response types, downstream data is encoded (see Options above).
|
||||
Encoding type is indicated by 1 prefix char (before the data header):
|
||||
TXT:
|
||||
End result is always DNS-chopped (series of len-prefixed strings
|
||||
<=255 bytes)
|
||||
t or T: Base32 encoded before chop, decoded after un-chop
|
||||
s or S: Base64 encoded before chop, decoded after un-chop
|
||||
u or U: Base64u encoded before chop, decoded after un-chop
|
||||
v or V: Base128 encoded before chop, decoded after un-chop
|
||||
r or R: Raw no encoding, only DNS-chop
|
||||
SRV/MX/CNAME/A:
|
||||
h or H: Hostname encoded with Base32
|
||||
i or I: Hostname encoded with Base64
|
||||
j or J: Hostname encoded with Base64u
|
||||
k or K: Hostname encoded with Base128
|
||||
SRV and MX may reply with multiple hostnames, each encoded separately. Each
|
||||
has a 10-multiple priority, and encoding/decoding is done in strictly
|
||||
increasing priority sequence 10, 20, 30, etc. without gaps. Note that some DNS
|
||||
relays will shuffle the answer records in the response.
|
||||
|
||||
|
||||
Ping:
|
||||
Client sends:
|
||||
First byte p or P
|
||||
Second byte CMC
|
||||
1 byte userid char (hex)
|
||||
Rest encoded with Base32:
|
||||
0 1...7 8 - 9
|
||||
+--------+---+76543210+---+
|
||||
|Dn SeqID|...|00DWTANR|CMC|
|
||||
+--------+---+--------+---+
|
||||
1 byte Downstream seq ID ACK
|
||||
1 byte window size (upstream)
|
||||
1 byte window size (downstream)
|
||||
1 byte window start (upstream)
|
||||
1 byte window start (downstream)
|
||||
2 bytes big-endian server timeout in ms
|
||||
2 bytes big-endian downstream fragment ACK timeout in ms
|
||||
|
||||
1 byte flags:
|
||||
D = disconnect remote TCP forward (client should then exit)
|
||||
W = update window frag timeout
|
||||
T = update server timeout
|
||||
A = is ACKing downstream frag
|
||||
N = is NACKing downstream frag (unused)
|
||||
R = response must contain ping header (data optional)
|
||||
2 bytes CMC
|
||||
|
||||
The server responses to Ping and Data packets are compatible, and are described
|
||||
above (refer to downstream data header).
|
||||
|
||||
If R (respond) bit is set, the server responds immediately with a ping header.
|
||||
The server must also adjust its window sizes to those provided by the ping.
|
||||
If the T but is set, the server sets the user's DNS timeout to the value spec-
|
||||
ified by the packet.
|
||||
|
||||
If the bit corresponding to changing a particular value (ie. window timeout) is
|
||||
not set, the value should be random. (note: this is disabled in debug mode).
|
||||
|
||||
In lazy mode, unless the R flag is set, the server will hold the ping until it
|
||||
times out or more data becomes available to send.
|
||||
|
||||
|
||||
"Lazy-mode" operation
|
||||
=====================
|
||||
|
||||
Client-server DNS traffic sequence has been reordered to provide increased
|
||||
(interactive) performance and greatly reduced latency.
|
||||
|
||||
Idea taken from Lucas Nussbaum's slides (24th IFIP International Security
|
||||
Conference, 2009) at http://www.loria.fr/~lnussbau/tuns.html. Current
|
||||
implementation is original to iodine, no code or documentation from any other
|
||||
project was consulted during development.
|
||||
|
||||
Server:
|
||||
In lazy mode, except where otherwise specified, responses are sent using the
|
||||
oldest pending query held in the server's buffer (QMEM). The server responds
|
||||
to a stored pending query when the query times out, an upstream ACK is pending
|
||||
(for that user), or the server has an excess of pending queries (more than the
|
||||
user's downstream window size).
|
||||
|
||||
Upstream data fragments are ACK'd immediately to keep data flowing.
|
||||
|
||||
Upstream pings are answered immediately only when the Respond flag is set (see
|
||||
ping header), in which case the response is to the same DNS query as the ping.
|
||||
Immediate responses (<10ms old) to either ping or data requests are marked
|
||||
and used to calculate the round-trip-time for the connection.
|
||||
|
||||
Client:
|
||||
The client keeps track of all queries it sends, and maintains a minimum of
|
||||
<downstream window size> pending queries to fill the server buffer.
|
||||
Downstream data is always ACK'd immediately with a new request (either a ping
|
||||
or data if available). The client sends excess requests (ie. already has enough
|
||||
pending queries) for ACKs or for new data.
|
||||
|
||||
|
||||
======================================================
|
||||
2. Raw UDP protocol
|
||||
======================================================
|
||||
|
||||
This protocol does not implement data windowing and does not guarantee data
|
||||
delivery, however it is faster since the data is not encoded and transferred
|
||||
on top of the DNS protocol. Full packets are compressed and sent when they
|
||||
arrive on the tun device, and are processed immediately on the other side.
|
||||
|
||||
All Raw UDP protcol messages start with a 3 byte header: 0x10d19e
|
||||
This is not the start of a valid DNS message so it is easy to identify.
|
||||
The fourth byte contains the command (C) and the user id (U).
|
||||
|
||||
7654 3210
|
||||
+----+----+
|
||||
|CCCC|UUUU|
|
||||
+----+----+
|
||||
|
||||
Login message (command = 1):
|
||||
The header is followed by a MD5 hash with the same password as in the DNS
|
||||
login. The client starts the raw mode by sending this message, and uses
|
||||
the login challenge +1, and the server responds using the login challenge -1.
|
||||
After the login message has been exchanged, both the server and the client
|
||||
switch to raw udp mode for the rest of the connection.
|
||||
|
||||
Data message (command = 2):
|
||||
After the header comes the payload data, which is always compressed.
|
||||
|
||||
Ping message (command = 3):
|
||||
Sent from client to server and back to keep session open. Has no payload.
|
||||
|
156
man/iodine.8
156
man/iodine.8
|
@ -1,5 +1,5 @@
|
|||
.\" groff -man -Tascii iodine.8
|
||||
.TH IODINE 8 "APR 2012" "User Manuals"
|
||||
.TH IODINE 8 "OCT 2015" "User Manuals"
|
||||
.SH NAME
|
||||
iodine, iodined \- tunnel IPv4 over DNS
|
||||
.SH SYNOPSIS
|
||||
|
@ -7,7 +7,9 @@ iodine, iodined \- tunnel IPv4 over DNS
|
|||
|
||||
.B iodine [-h]
|
||||
|
||||
.B iodine [-4] [-6] [-f] [-r] [-u
|
||||
.B iodine [-4] [-6] [-f] [-D] [-r] [-V
|
||||
.I sec
|
||||
.B [-u
|
||||
.I user
|
||||
.B ] [-P
|
||||
.I password
|
||||
|
@ -21,8 +23,22 @@ iodine, iodined \- tunnel IPv4 over DNS
|
|||
.I rdomain
|
||||
.B ] [-m
|
||||
.I fragsize
|
||||
.B ] [-w
|
||||
.I downfrags
|
||||
.B ] [-W
|
||||
.I upfrags
|
||||
.B ] [-i
|
||||
.I sec
|
||||
.B ] [-j
|
||||
.I sec
|
||||
.B ] [-c
|
||||
.I 0|1
|
||||
.B ] [-C
|
||||
.I 0|1
|
||||
.B ] [-s
|
||||
.I ms
|
||||
.B ] [-M
|
||||
.I namelen
|
||||
.I maxlen
|
||||
.B ] [-z
|
||||
.I context
|
||||
.B ] [-F
|
||||
|
@ -36,16 +52,19 @@ iodine, iodined \- tunnel IPv4 over DNS
|
|||
.B ] [-I
|
||||
.I interval
|
||||
.B ]
|
||||
.I topdomain
|
||||
.B [
|
||||
.I nameserver
|
||||
.B ]
|
||||
.I topdomain
|
||||
.B [
|
||||
.I nameserver2 ...
|
||||
.B ] ]
|
||||
|
||||
|
||||
.B iodined [-v]
|
||||
|
||||
.B iodined [-h]
|
||||
|
||||
.B iodined [-4] [-6] [-c] [-s] [-f] [-D] [-u
|
||||
.B iodined [-4] [-6] [-f] [-D] [-c] [-s] [-u
|
||||
.I user
|
||||
.B ] [-t
|
||||
.I chrootdir
|
||||
|
@ -81,17 +100,13 @@ iodine, iodined \- tunnel IPv4 over DNS
|
|||
.I /netmask
|
||||
.B ]
|
||||
.I topdomain
|
||||
|
||||
.SH DESCRIPTION
|
||||
.B iodine
|
||||
lets you tunnel IPv4 data through a DNS
|
||||
server. This can be useful in situations where Internet access is firewalled,
|
||||
but DNS queries are allowed. It needs a TUN/TAP device to operate. The
|
||||
bandwidth is asymmetrical,
|
||||
with a measured maximum of 680 kbit/s upstream and 2.3 Mbit/s
|
||||
downstream in a wired LAN test network.
|
||||
Realistic sustained throughput on a Wifi network using a carrier-grade
|
||||
DNS cache has been measured at some 50 kbit/s upstream and over 200 kbit/s
|
||||
downstream.
|
||||
but DNS queries are allowed. It needs a TUN/TAP device to operate.
|
||||
|
||||
.B iodine
|
||||
is the client application,
|
||||
.B iodined
|
||||
|
@ -100,6 +115,28 @@ 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 PERFORMANCE
|
||||
The bandwidth is asymmetrical,
|
||||
with a measured maximum of 13.7 Mbits/sec upstream and 53.4 Mbits/sec downstream
|
||||
with all data compression disabled in a wired LAN test network.
|
||||
In the same situation with compression enabled, the measured data throughput was
|
||||
approximately 46.1 Mbits/sec upstream and 37.4 Mbits/sec downstream.
|
||||
Compression is enabled by default, and can allow faster
|
||||
data transfer in both directions depending on the type of data being
|
||||
transferred.
|
||||
Realistic sustained throughput on a Wifi network using a carrier-grade
|
||||
DNS cache has been measured at some 600 Kbit/s upstream and over 2 Mbits/sec
|
||||
downstream once more with compression disabled, however that may be increased with
|
||||
compression enabled.
|
||||
|
||||
All of the above test scenarios used lazy mode with upstream/downstream windowsizes of
|
||||
8 fragments (default) and fixed fragment, DNS and server timeouts. These parameters
|
||||
were manually adjusted to best suit the environment, and can be specified using the
|
||||
.B iodine
|
||||
options described under
|
||||
.IR "Fine-tuning Options" .
|
||||
|
||||
.SH OPTIONS
|
||||
.SS Common Options:
|
||||
.TP
|
||||
|
@ -226,18 +263,67 @@ In these situations use \-L0 to force running in legacy mode
|
|||
(implies \-I1).
|
||||
.TP
|
||||
.B -I interval
|
||||
Maximum interval between requests (pings) so that intermediate DNS
|
||||
servers will not time out. Default is 4 in lazy mode, which will work
|
||||
fine in most cases. When too many SERVFAIL errors occur, iodine
|
||||
will automatically reduce this to 1.
|
||||
To get absolute minimum DNS traffic,
|
||||
increase well above 4, but not so high that SERVFAIL errors start to occur.
|
||||
Target timeout for queries. This should be less than the smallest timeout for
|
||||
any intermediate DNS servers to reduce SERVFAILS. If this is specified, the
|
||||
server timeout will be adjusted automatically based on the round-trip time
|
||||
so that queries remain pending for as long as possible (only in lazy mode).
|
||||
This value will be used as the polling interval in immediate mode.
|
||||
|
||||
When too many SERVFAIL errors occur, iodine will gradually reduce this until
|
||||
it reaches 0.5 seconds or below. If SERVFAILs continue to occur, lazy mode
|
||||
will be disabled and the server will respond to all queries immediately.
|
||||
|
||||
To reduce DNS traffic, set this interval to something large and disable lazy
|
||||
mode, or set the upstream and downstream window sizes to 1.
|
||||
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,
|
||||
SERVFAIL errors even with \-I1; data will still get through,
|
||||
and these errors can be ignored.
|
||||
Maximum useful value is 59, since iodined will close a client's
|
||||
connection after 60 seconds of inactivity.
|
||||
|
||||
.SS Fine-tuning Options (Client-side):
|
||||
.TP
|
||||
.B -s milliseconds
|
||||
Minimum query send interval. Increase this gradually if you notice that the
|
||||
nameserver(s) tend to fail more often with a high data load (and frequent
|
||||
queries) or drop excess DNS queries. This will affect throughput so use with
|
||||
caution.
|
||||
.B -w windowsize
|
||||
Size of downstream fragment sending window, or the number of fragments that
|
||||
can be in transit downstream at any point in time. The client will attempt to
|
||||
maintain at least this number of queries pending on the server in lazy mode,
|
||||
even when idle, so that the server can always send this number of fragments
|
||||
immediately if new data arrives on the tun device.
|
||||
The default value is 8 fragments. Increase this for high latency connections
|
||||
to improve throughput. The maximum usable value is probably around 128, however note
|
||||
that although higher values are possible there may be fragment overlaps and you may
|
||||
experience problems.
|
||||
.TP
|
||||
.B -W windowsize
|
||||
Number of fragments that can be in transit upstream at any point in time. The
|
||||
client will send a maximum of this number of queries immediately to the server
|
||||
when new data is received in addition to any already pending queries (such as
|
||||
those used to maintain the downstream windowsize). The server will respond to
|
||||
any excess queries using the oldest pending query first. The same limits apply
|
||||
as the downstream window size.
|
||||
.TP
|
||||
.B -i timeout
|
||||
The maximum amount of time in seconds the server should hold on to a pending
|
||||
query so as to not cause any intermediate DNS relays to time out. This should
|
||||
be less than the target timeout (set with
|
||||
.BR -I )
|
||||
by at least the round-trip time for the connection.
|
||||
If not set, this will be calculated automatically based on the round-trip time
|
||||
and the target timeout.
|
||||
.TP
|
||||
.B -c 0|1
|
||||
Enable or disable downstream data compression. Enabled by default. This may
|
||||
increase overall downstream throughput, or it may not depending on the type
|
||||
of data being transferred.
|
||||
.B -C 0|1
|
||||
Enable/disable upstream data compression, also enabled by default.
|
||||
|
||||
.SS Server Options:
|
||||
.TP
|
||||
.B -c
|
||||
|
@ -252,7 +338,9 @@ This should only be used if you have already configured the device that will be
|
|||
used.
|
||||
.TP
|
||||
.B -D
|
||||
Increase debug level. Level 1 prints info about each RX/TX packet.
|
||||
Increase debug level. Higher levels (>2) will spam the terminal with LOTS
|
||||
of debug messages. Recompile using `make debug` to enable extra debug output
|
||||
and debug timestamping.
|
||||
Implies the
|
||||
.B -f
|
||||
option.
|
||||
|
@ -303,29 +391,30 @@ Make the server stop itself after max_idle_time seconds if no traffic have been
|
|||
This should be combined with systemd or upstart on demand activation for being effective.
|
||||
.SS Client Arguments:
|
||||
.TP
|
||||
.B nameserver
|
||||
The nameserver to use to relay the dns traffic. This can be any relaying
|
||||
.B nameservers
|
||||
The nameservers to use to relay the dns traffic. This can be any relaying
|
||||
nameserver or the server running iodined if reachable. This field can be
|
||||
given as an IPv4/IPv6 address or as a hostname. This argument is optional,
|
||||
and if not specified a nameserver will be read from the
|
||||
.I /etc/resolv.conf
|
||||
file.
|
||||
Multiple nameservers can be specified, separated by spaces.
|
||||
.TP
|
||||
.B topdomain
|
||||
The dns traffic will be sent as queries for subdomains under
|
||||
\'topdomain'. This is normally a subdomain to a domain you own. Use a short
|
||||
domain name to get better throughput. If
|
||||
.B nameserver
|
||||
is the iodined server, then the topdomain can be chosen freely. This argument
|
||||
is the iodined server address, then the topdomain can be chosen freely. This argument
|
||||
must be the same on both the client and the server.
|
||||
.SS Server Arguments:
|
||||
.TP
|
||||
.B tunnel_ip[/netmask]
|
||||
This is the server's ip address on the tun interface. The client will be
|
||||
given the next ip number in the range. It is recommended to use the
|
||||
10.0.0.0 or 172.16.0.0 ranges. The default netmask is /27, can be overridden
|
||||
by specifying it here. Using a smaller network will limit the number of
|
||||
concurrent users.
|
||||
10.0.0.0/8, 172.16.0.0/12 or 192.168.0.0/16 ranges. The default netmask is /27,
|
||||
which can be overridden by specifying it here. Using a smaller network will
|
||||
limit the number of concurrent users.
|
||||
.TP
|
||||
.B topdomain
|
||||
The dns traffic is expected to arrive as queries for
|
||||
|
@ -333,10 +422,12 @@ subdomains under 'topdomain'. This is normally a subdomain to a domain you
|
|||
own. Use a short domain name to get better throughput. This argument must be
|
||||
the same on both the client and the server. Queries for domains other
|
||||
than 'topdomain' will be forwarded when the \-b option is given, otherwise
|
||||
they will be dropped.
|
||||
they will be ignored.
|
||||
|
||||
.SH EXAMPLES
|
||||
See the README file for both a quick test scenario, and a detailed description
|
||||
of real-world deployment.
|
||||
|
||||
.SH SECURITY
|
||||
Login is a relatively secure challenge-response MD5 hash, with the
|
||||
password never passing the wire.
|
||||
|
@ -365,11 +456,14 @@ is set, iodined will use the value it is set to as password instead of asking
|
|||
for one. The
|
||||
.B -P
|
||||
option still has precedence.
|
||||
|
||||
.SH SEE ALSO
|
||||
The README file in the source distribution contains some more elaborate
|
||||
The README.md file in the source distribution contains some more elaborate
|
||||
information.
|
||||
|
||||
.SH BUGS
|
||||
File bugs at http://dev.kryo.se/iodine/
|
||||
|
||||
.SH AUTHORS
|
||||
Erik Ekman <yarrick@kryo.se> and Bjorn Andersson <flex@kryo.se>. Major
|
||||
contributions by Anne Bezemer.
|
||||
Erik Ekman <yarrick@kryo.se>, Bjorn Andersson <flex@kryo.se> and Frekky.
|
||||
Major contributions by Anne Bezemer.
|
||||
|
|
|
@ -16,7 +16,7 @@ HEAD_COMMIT = `git rev-parse --short HEAD`
|
|||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := iodine
|
||||
LOCAL_SRC_FILES := tun.c dns.c read.c encoding.c login.c base32.c base64.c base64u.c base128.c md5.c common.c iodine.c client.c util.c
|
||||
LOCAL_SRC_FILES := tun.c dns.c read.c encoding.c login.c base32.c base64.c base64u.c base128.c md5.c common.c iodine.c client.c window.c util.c
|
||||
LOCAL_CFLAGS := -c -DANDROID -DLINUX -DIFCONFIGPATH=\"/system/bin/\" -Wall -DGITREVISION=\"$(HEAD_COMMIT)\"
|
||||
LOCAL_LDLIBS := -lz
|
||||
|
||||
|
|
23
src/Makefile
23
src/Makefile
|
@ -1,7 +1,7 @@
|
|||
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
|
||||
COMMONOBJS = tun.o dns.o read.o encoding.o login.o base32.o base64.o base64u.o base128.o md5.o window.o common.o util.o
|
||||
CLIENTOBJS = iodine.o client.o
|
||||
CLIENT = ../bin/iodine
|
||||
SERVEROBJS = iodined.o user.o fw_query.o
|
||||
SERVEROBJS = iodined.o user.o fw_query.o server.o
|
||||
SERVER = ../bin/iodined
|
||||
|
||||
OS = `echo $(TARGETOS) | tr "a-z" "A-Z"`
|
||||
|
@ -10,9 +10,18 @@ HEAD_COMMIT = `git rev-parse --short HEAD`
|
|||
|
||||
LIBPATH = -L.
|
||||
LDFLAGS += -lz `sh osflags $(TARGETOS) link` $(LIBPATH)
|
||||
CFLAGS += -std=c99 -c -g -Wall -D$(OS) -pedantic `sh osflags $(TARGETOS) cflags` -DGITREVISION=\"$(HEAD_COMMIT)\"
|
||||
CFLAGS += -std=c99 -c -Wall -D$(OS) -pedantic `sh osflags $(TARGETOS) cflags` -DGITREVISION=\"$(HEAD_COMMIT)\"
|
||||
|
||||
all: stateos $(CLIENT) $(SERVER)
|
||||
CFLAGS_RELEASE = -O3 -fno-strict-aliasing
|
||||
CFLAGS_DEBUG = -g -O0 -DDEBUG_BUILD
|
||||
|
||||
all: CFLAGS += $(CFLAGS_RELEASE)
|
||||
all: executables
|
||||
|
||||
debug: CFLAGS += $(CFLAGS_DEBUG)
|
||||
debug: executables
|
||||
|
||||
executables: stateos $(CLIENT) $(SERVER)
|
||||
|
||||
stateos:
|
||||
@echo OS is $(OS), arch is $(ARCH)
|
||||
|
@ -35,11 +44,11 @@ base64u.o client.o iodined.o: base64u.h
|
|||
base64u.c: base64.c
|
||||
@echo Making $@
|
||||
@echo '/* No use in editing, produced by Makefile! */' > $@
|
||||
@sed -e 's/\([Bb][Aa][Ss][Ee]64\)/\1u/g ; s/0123456789+/0123456789_/' < base64.c >> $@
|
||||
@sed -e 's/\([Bb][Aa][Ss][Ee]64\)/\1u/g ; s/\([Bb]64\)/\1u/g ; s/0123456789+/0123456789_/' < base64.c >> $@
|
||||
base64u.h: base64.h
|
||||
@echo Making $@
|
||||
@echo '/* No use in editing, produced by Makefile! */' > $@
|
||||
@sed -e 's/\([Bb][Aa][Ss][Ee]64\)/\1u/g ; s/0123456789+/0123456789_/' < base64.h >> $@
|
||||
@sed -e 's/\([Bb][Aa][Ss][Ee]64\)/\1u/g ; s/\([Bb]64\)/\1u/g ; s/0123456789+/0123456789_/' < base64.h >> $@
|
||||
|
||||
clean:
|
||||
@echo "Cleaning src/"
|
||||
|
|
|
@ -42,23 +42,25 @@
|
|||
* accent chars since they might readily be entered in normal use,
|
||||
* don't use 254-255 because of possible function overloading in DNS systems.
|
||||
*/
|
||||
static const unsigned char cb128[] =
|
||||
static const uint8_t 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 uint8_t 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 size_t base128_encode(uint8_t *, size_t *, const uint8_t *, size_t);
|
||||
static size_t base128_decode(uint8_t *, size_t *, const uint8_t *, size_t);
|
||||
static int base128_handles_dots();
|
||||
static int base128_blksize_raw();
|
||||
static int base128_blksize_enc();
|
||||
static size_t base128_blksize_raw();
|
||||
static size_t base128_blksize_enc();
|
||||
static size_t base128_encoded_length(size_t inputlen);
|
||||
static size_t base128_raw_length(size_t inputlen);
|
||||
|
||||
static struct encoder base128_encoder =
|
||||
struct encoder base128_encoder =
|
||||
{
|
||||
"Base128",
|
||||
base128_encode,
|
||||
|
@ -66,9 +68,13 @@ static struct encoder base128_encoder =
|
|||
base128_handles_dots,
|
||||
base128_handles_dots,
|
||||
base128_blksize_raw,
|
||||
base128_blksize_enc
|
||||
base128_blksize_enc,
|
||||
base128_encoded_length,
|
||||
base128_raw_length
|
||||
};
|
||||
|
||||
struct encoder *b128 = &base128_encoder;
|
||||
|
||||
struct encoder
|
||||
*get_base128_encoder()
|
||||
{
|
||||
|
@ -81,18 +87,30 @@ base128_handles_dots()
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static size_t
|
||||
base128_blksize_raw()
|
||||
{
|
||||
return BLKSIZE_RAW;
|
||||
}
|
||||
|
||||
static int
|
||||
static size_t
|
||||
base128_blksize_enc()
|
||||
{
|
||||
return BLKSIZE_ENC;
|
||||
}
|
||||
|
||||
static size_t
|
||||
base128_encoded_length(size_t inputlen)
|
||||
{
|
||||
return (BLKSIZE_ENC * inputlen) / BLKSIZE_RAW + (((BLKSIZE_ENC * inputlen) % BLKSIZE_RAW) ? 1 : 0);
|
||||
}
|
||||
|
||||
static size_t
|
||||
base128_raw_length(size_t inputlen)
|
||||
{
|
||||
return (BLKSIZE_RAW * inputlen) / BLKSIZE_ENC + (((BLKSIZE_RAW * inputlen) % BLKSIZE_ENC) ? 1 : 0);
|
||||
}
|
||||
|
||||
inline static void
|
||||
base128_reverse_init()
|
||||
{
|
||||
|
@ -109,8 +127,8 @@ base128_reverse_init()
|
|||
}
|
||||
}
|
||||
|
||||
static int
|
||||
base128_encode(char *buf, size_t *buflen, const void *data, size_t size)
|
||||
static size_t
|
||||
base128_encode(uint8_t *ubuf, size_t *buflen, const uint8_t *udata, size_t size)
|
||||
/*
|
||||
* Fills *buf with max. *buflen characters, encoding size bytes of *data.
|
||||
*
|
||||
|
@ -121,10 +139,8 @@ base128_encode(char *buf, size_t *buflen, const void *data, size_t size)
|
|||
* 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
|
||||
size_t iout = 0; /* to-be-filled output char */
|
||||
size_t iin = 0; /* one more than last input byte that can be
|
||||
successfully decoded */
|
||||
|
||||
/* Note: Don't bother to optimize manually. GCC optimizes
|
||||
|
@ -203,8 +219,8 @@ base128_encode(char *buf, size_t *buflen, const void *data, size_t size)
|
|||
|
||||
#define REV128(x) rev128[(int) (x)]
|
||||
|
||||
static int
|
||||
base128_decode(void *buf, size_t *buflen, const char *str, size_t slen)
|
||||
static size_t
|
||||
base128_decode(uint8_t *buf, size_t *buflen, const uint8_t *str, size_t slen)
|
||||
/*
|
||||
* Fills *buf with max. *buflen bytes, decoded from slen chars in *str.
|
||||
* Decoding stops early when *str contains \0.
|
||||
|
@ -217,8 +233,6 @@ base128_decode(void *buf, size_t *buflen, const char *str, size_t slen)
|
|||
* 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 */
|
||||
|
||||
|
@ -231,61 +245,61 @@ base128_decode(void *buf, size_t *buflen, const char *str, size_t slen)
|
|||
if (iout >= *buflen || iin + 1 >= slen ||
|
||||
str[iin] == '\0' || str[iin + 1] == '\0')
|
||||
break;
|
||||
ubuf[iout] = ((REV128(ustr[iin]) & 0x7f) << 1) |
|
||||
((REV128(ustr[iin + 1]) & 0x40) >> 6);
|
||||
buf[iout] = ((REV128(str[iin]) & 0x7f) << 1) |
|
||||
((REV128(str[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);
|
||||
buf[iout] = ((REV128(str[iin]) & 0x3f) << 2) |
|
||||
((REV128(str[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);
|
||||
buf[iout] = ((REV128(str[iin]) & 0x1f) << 3) |
|
||||
((REV128(str[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);
|
||||
buf[iout] = ((REV128(str[iin]) & 0x0f) << 4) |
|
||||
((REV128(str[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);
|
||||
buf[iout] = ((REV128(str[iin]) & 0x07) << 5) |
|
||||
((REV128(str[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);
|
||||
buf[iout] = ((REV128(str[iin]) & 0x03) << 6) |
|
||||
((REV128(str[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));
|
||||
buf[iout] = ((REV128(str[iin]) & 0x01) << 7) |
|
||||
((REV128(str[iin + 1]) & 0x7f));
|
||||
iin += 2; /* 6,7 used up, iin=8 */
|
||||
iout++;
|
||||
}
|
||||
|
||||
ubuf[iout] = '\0';
|
||||
buf[iout] = '\0';
|
||||
|
||||
return iout;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
#ifndef __BASE128_H__
|
||||
#define __BASE128_H__
|
||||
|
||||
extern struct encoder base128_encoder;
|
||||
extern struct encoder *b128;
|
||||
|
||||
struct encoder *get_base128_encoder(void);
|
||||
|
||||
#endif
|
||||
|
|
54
src/base32.c
54
src/base32.c
|
@ -33,13 +33,16 @@ static const char cb32_ucase[] =
|
|||
static unsigned char rev32[256];
|
||||
static int reverse_init = 0;
|
||||
|
||||
static int base32_encode(char *, size_t *, const void *, size_t);
|
||||
static int base32_decode(void *, size_t *, const char *, size_t);
|
||||
static size_t base32_encode(uint8_t *, size_t *, const uint8_t *, size_t);
|
||||
static size_t base32_decode(uint8_t *, size_t *, const uint8_t *, size_t);
|
||||
static int base32_handles_dots();
|
||||
static int base32_blksize_raw();
|
||||
static int base32_blksize_enc();
|
||||
static size_t base32_blksize_raw();
|
||||
static size_t base32_blksize_enc();
|
||||
static size_t base32_encoded_length(size_t inputlen);
|
||||
static size_t base32_raw_length(size_t inputlen);
|
||||
|
||||
static struct encoder base32_encoder =
|
||||
|
||||
struct encoder base32_encoder =
|
||||
{
|
||||
"Base32",
|
||||
base32_encode,
|
||||
|
@ -47,9 +50,13 @@ static struct encoder base32_encoder =
|
|||
base32_handles_dots,
|
||||
base32_handles_dots,
|
||||
base32_blksize_raw,
|
||||
base32_blksize_enc
|
||||
base32_blksize_enc,
|
||||
base32_encoded_length,
|
||||
base32_raw_length
|
||||
};
|
||||
|
||||
struct encoder *b32 = &base32_encoder;
|
||||
|
||||
struct encoder
|
||||
*get_base32_encoder()
|
||||
{
|
||||
|
@ -62,18 +69,31 @@ base32_handles_dots()
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static size_t
|
||||
base32_blksize_raw()
|
||||
{
|
||||
return BLKSIZE_RAW;
|
||||
}
|
||||
|
||||
static int
|
||||
static size_t
|
||||
base32_blksize_enc()
|
||||
{
|
||||
return BLKSIZE_ENC;
|
||||
}
|
||||
|
||||
static size_t
|
||||
base32_encoded_length(size_t inputlen)
|
||||
{
|
||||
return (BLKSIZE_ENC * inputlen) / BLKSIZE_RAW + (((BLKSIZE_ENC * inputlen) % BLKSIZE_RAW) ? 1 : 0);
|
||||
}
|
||||
|
||||
static size_t
|
||||
base32_raw_length(size_t inputlen)
|
||||
{
|
||||
return (BLKSIZE_RAW * inputlen) / BLKSIZE_ENC + (((BLKSIZE_RAW * inputlen) % BLKSIZE_ENC) ? 1 : 0);
|
||||
}
|
||||
|
||||
|
||||
inline static void
|
||||
base32_reverse_init()
|
||||
{
|
||||
|
@ -105,8 +125,8 @@ b32_8to5(int in)
|
|||
return rev32[in];
|
||||
}
|
||||
|
||||
static int
|
||||
base32_encode(char *buf, size_t *buflen, const void *data, size_t size)
|
||||
static size_t
|
||||
base32_encode(uint8_t *buf, size_t *buflen, const uint8_t *udata, size_t size)
|
||||
/*
|
||||
* Fills *buf with max. *buflen characters, encoding size bytes of *data.
|
||||
*
|
||||
|
@ -117,9 +137,8 @@ base32_encode(char *buf, size_t *buflen, const void *data, size_t size)
|
|||
* 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
|
||||
size_t iout = 0; /* to-be-filled output char */
|
||||
size_t iin = 0; /* one more than last input byte that can be
|
||||
successfully decoded */
|
||||
|
||||
/* Note: Don't bother to optimize manually. GCC optimizes
|
||||
|
@ -196,8 +215,8 @@ base32_encode(char *buf, size_t *buflen, const void *data, size_t size)
|
|||
|
||||
#define REV32(x) rev32[(int) (x)]
|
||||
|
||||
static int
|
||||
base32_decode(void *buf, size_t *buflen, const char *str, size_t slen)
|
||||
static size_t
|
||||
base32_decode(uint8_t *ubuf, size_t *buflen, const uint8_t *str, size_t slen)
|
||||
/*
|
||||
* Fills *buf with max. *buflen bytes, decoded from slen chars in *str.
|
||||
* Decoding stops early when *str contains \0.
|
||||
|
@ -210,9 +229,8 @@ base32_decode(void *buf, size_t *buflen, const char *str, size_t slen)
|
|||
* 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 */
|
||||
size_t iout = 0; /* to-be-filled output byte */
|
||||
size_t iin = 0; /* next input char to use in decoding */
|
||||
|
||||
base32_reverse_init ();
|
||||
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
#ifndef __BASE32_H__
|
||||
#define __BASE32_H__
|
||||
|
||||
extern struct encoder base32_encoder;
|
||||
extern struct encoder *b32;
|
||||
|
||||
struct encoder *get_base32_encoder(void);
|
||||
|
||||
int b32_5to8(int);
|
||||
|
|
52
src/base64.c
52
src/base64.c
|
@ -33,13 +33,15 @@ static const char cb64[] =
|
|||
static unsigned char rev64[256];
|
||||
static int reverse_init = 0;
|
||||
|
||||
static int base64_encode(char *, size_t *, const void *, size_t);
|
||||
static int base64_decode(void *, size_t *, const char *, size_t);
|
||||
static size_t base64_encode(uint8_t *, size_t *, const uint8_t *, size_t);
|
||||
static size_t base64_decode(uint8_t *, size_t *, const uint8_t *, size_t);
|
||||
static int base64_handles_dots();
|
||||
static int base64_blksize_raw();
|
||||
static int base64_blksize_enc();
|
||||
static size_t base64_blksize_raw();
|
||||
static size_t base64_blksize_enc();
|
||||
static size_t base64_encoded_length(size_t inputlen);
|
||||
static size_t base64_raw_length(size_t inputlen);
|
||||
|
||||
static struct encoder base64_encoder =
|
||||
struct encoder base64_encoder =
|
||||
{
|
||||
"Base64",
|
||||
base64_encode,
|
||||
|
@ -47,9 +49,13 @@ static struct encoder base64_encoder =
|
|||
base64_handles_dots,
|
||||
base64_handles_dots,
|
||||
base64_blksize_raw,
|
||||
base64_blksize_enc
|
||||
base64_blksize_enc,
|
||||
base64_encoded_length,
|
||||
base64_raw_length
|
||||
};
|
||||
|
||||
struct encoder *b64 = &base64_encoder;
|
||||
|
||||
struct encoder
|
||||
*get_base64_encoder()
|
||||
{
|
||||
|
@ -62,18 +68,30 @@ base64_handles_dots()
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static size_t
|
||||
base64_blksize_raw()
|
||||
{
|
||||
return BLKSIZE_RAW;
|
||||
}
|
||||
|
||||
static int
|
||||
static size_t
|
||||
base64_blksize_enc()
|
||||
{
|
||||
return BLKSIZE_ENC;
|
||||
}
|
||||
|
||||
static size_t
|
||||
base64_encoded_length(size_t inputlen)
|
||||
{
|
||||
return (BLKSIZE_ENC * inputlen) / BLKSIZE_RAW + (((BLKSIZE_ENC * inputlen) % BLKSIZE_RAW) ? 1 : 0);
|
||||
}
|
||||
|
||||
static size_t
|
||||
base64_raw_length(size_t inputlen)
|
||||
{
|
||||
return (BLKSIZE_RAW * inputlen) / BLKSIZE_ENC + (((BLKSIZE_RAW * inputlen) % BLKSIZE_ENC) ? 1 : 0);
|
||||
}
|
||||
|
||||
inline static void
|
||||
base64_reverse_init()
|
||||
{
|
||||
|
@ -90,8 +108,8 @@ base64_reverse_init()
|
|||
}
|
||||
}
|
||||
|
||||
static int
|
||||
base64_encode(char *buf, size_t *buflen, const void *data, size_t size)
|
||||
static size_t
|
||||
base64_encode(uint8_t *buf, size_t *buflen, const uint8_t *udata, size_t size)
|
||||
/*
|
||||
* Fills *buf with max. *buflen characters, encoding size bytes of *data.
|
||||
*
|
||||
|
@ -102,9 +120,8 @@ base64_encode(char *buf, size_t *buflen, const void *data, size_t size)
|
|||
* 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
|
||||
size_t iout = 0; /* to-be-filled output char */
|
||||
size_t iin = 0; /* one more than last input byte that can be
|
||||
successfully decoded */
|
||||
|
||||
/* Note: Don't bother to optimize manually. GCC optimizes
|
||||
|
@ -151,8 +168,8 @@ base64_encode(char *buf, size_t *buflen, const void *data, size_t size)
|
|||
|
||||
#define REV64(x) rev64[(int) (x)]
|
||||
|
||||
static int
|
||||
base64_decode(void *buf, size_t *buflen, const char *str, size_t slen)
|
||||
static size_t
|
||||
base64_decode(uint8_t *ubuf, size_t *buflen, const uint8_t *str, size_t slen)
|
||||
/*
|
||||
* Fills *buf with max. *buflen bytes, decoded from slen chars in *str.
|
||||
* Decoding stops early when *str contains \0.
|
||||
|
@ -165,9 +182,8 @@ base64_decode(void *buf, size_t *buflen, const char *str, size_t slen)
|
|||
* 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 */
|
||||
size_t iout = 0; /* to-be-filled output byte */
|
||||
size_t iin = 0; /* next input char to use in decoding */
|
||||
|
||||
base64_reverse_init ();
|
||||
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
#ifndef __BASE64_H__
|
||||
#define __BASE64_H__
|
||||
|
||||
extern struct encoder base64_encoder;
|
||||
extern struct encoder *b64;
|
||||
|
||||
struct encoder *get_base64_encoder(void);
|
||||
|
||||
#endif
|
||||
|
|
2409
src/client.c
2409
src/client.c
File diff suppressed because it is too large
Load Diff
149
src/client.h
149
src/client.h
|
@ -18,23 +18,152 @@
|
|||
#ifndef __CLIENT_H__
|
||||
#define __CLIENT_H__
|
||||
|
||||
#include "window.h"
|
||||
|
||||
extern int debug;
|
||||
extern int stats;
|
||||
|
||||
#define PENDING_QUERIES_LENGTH (MAX(this.windowsize_up, this.windowsize_down) * 4)
|
||||
#define INSTANCE this
|
||||
|
||||
struct client_instance {
|
||||
int max_downstream_frag_size;
|
||||
int autodetect_frag_size;
|
||||
int hostname_maxlen;
|
||||
int raw_mode;
|
||||
int foreground;
|
||||
char password[33];
|
||||
|
||||
/* DNS nameserver info */
|
||||
char **nameserv_hosts;
|
||||
size_t nameserv_hosts_len;
|
||||
struct sockaddr_storage *nameserv_addrs;
|
||||
size_t nameserv_addrs_len;
|
||||
int current_nameserver;
|
||||
struct sockaddr_storage raw_serv;
|
||||
int raw_serv_len;
|
||||
char *topdomain;
|
||||
|
||||
/* Remote TCP forwarding stuff (for -R) */
|
||||
struct sockaddr_storage remote_forward_addr;
|
||||
int use_remote_forward; /* 0 if no forwarding used */
|
||||
int remote_forward_connected;
|
||||
|
||||
int tun_fd;
|
||||
int dns_fd;
|
||||
|
||||
#ifdef OPENBSD
|
||||
int rtable;
|
||||
#endif
|
||||
int running;
|
||||
|
||||
/* Output flags for debug and time between stats update */
|
||||
int debug;
|
||||
int stats;
|
||||
|
||||
uint16_t rand_seed;
|
||||
|
||||
/* Current up/downstream window data */
|
||||
struct frag_buffer *outbuf;
|
||||
struct frag_buffer *inbuf;
|
||||
size_t windowsize_up;
|
||||
size_t windowsize_down;
|
||||
size_t maxfragsize_up;
|
||||
|
||||
/* Next downstream seqID to be ACK'd (-1 if none pending) */
|
||||
int next_downstream_ack;
|
||||
|
||||
/* Remembering queries we sent for tracking purposes */
|
||||
struct query_tuple *pending_queries;
|
||||
size_t num_pending;
|
||||
time_t max_timeout_ms;
|
||||
time_t send_interval_ms;
|
||||
time_t min_send_interval_ms;
|
||||
|
||||
/* Server response timeout in ms and downstream window timeout */
|
||||
time_t server_timeout_ms;
|
||||
time_t downstream_timeout_ms;
|
||||
int autodetect_server_timeout;
|
||||
|
||||
/* Cumulative Round-Trip-Time in ms */
|
||||
time_t rtt_total_ms;
|
||||
size_t num_immediate;
|
||||
|
||||
/* Connection statistics */
|
||||
size_t num_timeouts;
|
||||
size_t num_untracked;
|
||||
size_t num_servfail;
|
||||
size_t num_badip;
|
||||
size_t num_sent;
|
||||
size_t num_recv;
|
||||
size_t send_query_sendcnt;
|
||||
size_t send_query_recvcnt;
|
||||
size_t num_frags_sent;
|
||||
size_t num_frags_recv;
|
||||
size_t num_pings;
|
||||
|
||||
/* My userid at the server */
|
||||
char userid;
|
||||
char userid_char; /* used when sending (lowercase) */
|
||||
char userid_char2; /* also accepted when receiving (uppercase) */
|
||||
|
||||
uint16_t chunkid;
|
||||
|
||||
/* Base32 encoder used for non-data packets and replies */
|
||||
struct encoder *b32;
|
||||
/* Base64 etc encoders for replies */
|
||||
struct encoder *b64;
|
||||
struct encoder *b64u;
|
||||
struct encoder *b128;
|
||||
|
||||
/* The encoder used for data packets
|
||||
* Defaults to Base32, can be changed after handshake */
|
||||
struct encoder *dataenc;
|
||||
|
||||
/* Upstream/downstream compression flags */
|
||||
int compression_up;
|
||||
int compression_down;
|
||||
|
||||
/* The encoder to use for downstream data */
|
||||
char downenc;
|
||||
|
||||
/* set query type to send */
|
||||
uint16_t do_qtype;
|
||||
|
||||
/* My connection mode */
|
||||
enum connection conn;
|
||||
int connected;
|
||||
|
||||
int lazymode;
|
||||
long send_ping_soon;
|
||||
time_t lastdownstreamtime;
|
||||
};
|
||||
|
||||
struct query_tuple {
|
||||
int id; /* DNS query / response ID */
|
||||
struct timeval time; /* time sent or 0 if cleared */
|
||||
};
|
||||
|
||||
extern struct client_instance this;
|
||||
|
||||
void client_init();
|
||||
void client_stop();
|
||||
|
||||
enum connection client_get_conn();
|
||||
const char *client_get_raw_addr();
|
||||
|
||||
void client_set_nameserver(struct sockaddr_storage *, int);
|
||||
void client_set_topdomain(const char *cp);
|
||||
void client_set_password(const char *cp);
|
||||
void client_rotate_nameserver();
|
||||
int client_set_qtype(char *qtype);
|
||||
char *client_get_qtype();
|
||||
void client_set_downenc(char *encoding);
|
||||
void client_set_selecttimeout(int select_timeout);
|
||||
void client_set_lazymode(int lazy_mode);
|
||||
void client_set_hostname_maxlen(int i);
|
||||
char *format_qtype();
|
||||
char parse_encoding(char *encoding);
|
||||
void client_set_hostname_maxlen(size_t i);
|
||||
|
||||
int client_handshake(int dns_fd, int raw_mode, int autodetect_frag_size, int fragsize);
|
||||
int client_tunnel(int tun_fd, int dns_fd);
|
||||
int client_handshake();
|
||||
int client_tunnel();
|
||||
|
||||
int parse_data(uint8_t *data, size_t len, fragment *f, int *immediate, int*);
|
||||
int handshake_waitdns(char *buf, size_t buflen, char cmd, int timeout);
|
||||
void handshake_switch_options(int lazy, int compression, char denc);
|
||||
int send_ping(int ping_response, int ack, int timeout, int);
|
||||
|
||||
#endif
|
||||
|
|
102
src/common.c
102
src/common.c
|
@ -228,9 +228,15 @@ open_dns_from_host(char *host, int port, int addr_family, int flags)
|
|||
}
|
||||
|
||||
void
|
||||
close_dns(int fd)
|
||||
close_socket(int fd)
|
||||
{
|
||||
if (fd <= 0)
|
||||
return;
|
||||
#ifdef WINDOWS32
|
||||
closesocket(fd);
|
||||
#else
|
||||
close(fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -282,7 +288,9 @@ do_detach()
|
|||
{
|
||||
#ifndef WINDOWS32
|
||||
fprintf(stderr, "Detaching from terminal...\n");
|
||||
daemon(0, 0);
|
||||
if (daemon(0, 0) != 0) {
|
||||
err(1, "Failed to detach from terminal. Try running in foreground.");
|
||||
}
|
||||
umask(0);
|
||||
alarm(0);
|
||||
#else
|
||||
|
@ -310,7 +318,8 @@ read_password(char *buf, size_t len)
|
|||
fprintf(stderr, "Enter password: ");
|
||||
fflush(stderr);
|
||||
#ifndef WINDOWS32
|
||||
fscanf(stdin, "%79[^\n]", pwd);
|
||||
if (!fscanf(stdin, "%79[^\n]", pwd))
|
||||
err(1, "EOF while reading password!");
|
||||
#else
|
||||
for (i = 0; i < sizeof(pwd); i++) {
|
||||
pwd[i] = getch();
|
||||
|
@ -394,6 +403,77 @@ check_topdomain(char *str, char **errormsg)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
socket_set_blocking(int fd, int blocking)
|
||||
{
|
||||
/* Set non-blocking socket mode */
|
||||
#ifdef WINDOWS32
|
||||
if (ioctlsocket(fd, FIONBIO, &blocking) != 0) {
|
||||
return WSAGetLastError();
|
||||
}
|
||||
#else
|
||||
int flags;
|
||||
if ((flags = fcntl(fd, F_GETFL, 0)) < 0) {
|
||||
return flags;
|
||||
}
|
||||
|
||||
if (fcntl(fd, F_SETFL, !blocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK))) == -1)
|
||||
return errno;
|
||||
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
open_tcp_nonblocking(struct sockaddr_storage *addr, char **errormsg)
|
||||
/* Open TCP connection to given address without blocking */
|
||||
{
|
||||
int fd, ret;
|
||||
if ((fd = socket(addr->ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
|
||||
if (errormsg)
|
||||
*errormsg = strerror(errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((ret = socket_set_blocking(fd, 0)) != 0) {
|
||||
if (errormsg)
|
||||
*errormsg = strerror(ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((ret = connect(fd, (struct sockaddr *)addr, sizeof(struct sockaddr_storage)))
|
||||
== -1 && errno != EINPROGRESS) {
|
||||
if (errormsg)
|
||||
*errormsg = strerror(errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (errormsg)
|
||||
*errormsg = strerror(errno);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int
|
||||
check_tcp_error(int fd, char **error)
|
||||
/* checks connected status of given socket.
|
||||
* returns error code. 0 if connected or EINPROGRESS if connecting */
|
||||
{
|
||||
int errornum = 0;
|
||||
socklen_t len = sizeof(int);
|
||||
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errornum, &len) != 0) {
|
||||
if (error)
|
||||
*error = "getsockopt failed.";
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (error)
|
||||
*error = strerror(errornum);
|
||||
|
||||
return errornum;
|
||||
}
|
||||
|
||||
#if defined(WINDOWS32) || defined(ANDROID)
|
||||
#ifndef ANDROID
|
||||
int
|
||||
|
@ -455,22 +535,6 @@ errx(int eval, const char *fmt, ...)
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
int recent_seqno(int ourseqno, int gotseqno)
|
||||
/* Return 1 if we've seen gotseqno recently (current or up to 3 back).
|
||||
Return 0 if gotseqno is new (or very old).
|
||||
*/
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 4; i++, ourseqno--) {
|
||||
if (ourseqno < 0)
|
||||
ourseqno = 7;
|
||||
if (gotseqno == ourseqno)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef WINDOWS32
|
||||
/* Set FD_CLOEXEC flag on file descriptor.
|
||||
* This stops it from being inherited by system() calls.
|
||||
|
|
86
src/common.h
86
src/common.h
|
@ -40,10 +40,37 @@ extern const unsigned char raw_header[RAW_HDR_LEN];
|
|||
#include <err.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#define DNS_PORT 53
|
||||
|
||||
#if _WIN32 || _WIN64
|
||||
#if _WIN64
|
||||
#define BITS_64
|
||||
#else
|
||||
#define BITS_32
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if __GNUC__
|
||||
#if __x86_64__ || __ppc64__
|
||||
#define BITS_64 1
|
||||
#else
|
||||
#define BITS_32 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Determine appropriate format specifier for long int on 32/64 bit systems */
|
||||
#if BITS_64
|
||||
#define FMT_LONG "l"
|
||||
#else
|
||||
#define FMT_LONG ""
|
||||
#endif
|
||||
|
||||
/* For convenience and shortness */
|
||||
#define L FMT_LONG
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a,b) ((a)<(b)?(a):(b))
|
||||
#endif
|
||||
|
@ -67,33 +94,57 @@ extern const unsigned char raw_header[RAW_HDR_LEN];
|
|||
# define DONT_FRAG_VALUE 1
|
||||
#endif
|
||||
|
||||
#ifndef GITREVISION
|
||||
#define GITREVISION "GIT"
|
||||
#endif
|
||||
|
||||
#define T_PRIVATE 65399
|
||||
/* Undefined RR type; "private use" range, see http://www.bind9.net/dns-parameters */
|
||||
#define T_UNSET 65432
|
||||
/* Unused RR type, never actually sent */
|
||||
|
||||
struct packet
|
||||
{
|
||||
int len; /* Total packet length */
|
||||
int sentlen; /* Length of chunk currently transmitted */
|
||||
int offset; /* Current offset */
|
||||
char data[64*1024]; /* The data */
|
||||
char seqno; /* The packet sequence number */
|
||||
char fragment; /* Fragment index */
|
||||
};
|
||||
#define DOWNSTREAM_HDR 3
|
||||
#define DOWNSTREAM_PING_HDR 7
|
||||
#define UPSTREAM_HDR 6
|
||||
#define UPSTREAM_PING 11
|
||||
|
||||
/* handy debug printing macro */
|
||||
#ifdef DEBUG_BUILD
|
||||
#define TIMEPRINT(...) \
|
||||
struct timeval currenttime;\
|
||||
gettimeofday(¤ttime, NULL);\
|
||||
fprintf(stderr, "%03ld.%03ld ", (long) currenttime.tv_sec, (long) currenttime.tv_usec / 1000);\
|
||||
fprintf(stderr, __VA_ARGS__);
|
||||
|
||||
#define DEBUG(level, ...) \
|
||||
if (INSTANCE.debug >= level) {\
|
||||
TIMEPRINT("[D%d %s:%d] ", level, __FILE__, __LINE__); \
|
||||
fprintf(stderr, __VA_ARGS__);\
|
||||
fprintf(stderr, "\n");\
|
||||
}
|
||||
#else
|
||||
#define TIMEPRINT(...) \
|
||||
fprintf(stderr, __VA_ARGS__);
|
||||
|
||||
#define DEBUG(level, ...) \
|
||||
if (INSTANCE.debug >= level) {\
|
||||
fprintf(stderr, "[D%d] ", level); \
|
||||
fprintf(stderr, __VA_ARGS__);\
|
||||
fprintf(stderr, "\n");\
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
struct query {
|
||||
char name[QUERY_NAME_SIZE];
|
||||
unsigned short type;
|
||||
unsigned short rcode;
|
||||
unsigned short id;
|
||||
int id; /* id < 0: unusued */
|
||||
struct sockaddr_storage destination;
|
||||
socklen_t dest_len;
|
||||
struct sockaddr_storage from;
|
||||
socklen_t fromlen;
|
||||
unsigned short id2;
|
||||
struct sockaddr_storage from2;
|
||||
socklen_t fromlen2;
|
||||
struct timeval time_recv;
|
||||
};
|
||||
|
||||
enum connection {
|
||||
|
@ -108,7 +159,10 @@ int get_addr(char *, int, int, int, struct sockaddr_storage *);
|
|||
int open_dns(struct sockaddr_storage *, size_t);
|
||||
int open_dns_opt(struct sockaddr_storage *sockaddr, size_t sockaddr_len, int v6only);
|
||||
int open_dns_from_host(char *host, int port, int addr_family, int flags);
|
||||
void close_dns(int);
|
||||
void close_socket(int);
|
||||
|
||||
int open_tcp_nonblocking(struct sockaddr_storage *addr, char **error);
|
||||
int check_tcp_error(int fd, char **error);
|
||||
|
||||
void do_chroot(char *);
|
||||
void do_setcon(char *);
|
||||
|
@ -119,6 +173,8 @@ void read_password(char*, size_t);
|
|||
|
||||
int check_topdomain(char *, char **);
|
||||
|
||||
extern double difftime(time_t, time_t);
|
||||
|
||||
#if defined(WINDOWS32) || defined(ANDROID)
|
||||
#ifndef ANDROID
|
||||
int inet_aton(const char *cp, struct in_addr *inp);
|
||||
|
@ -130,8 +186,6 @@ void errx(int eval, const char *fmt, ...);
|
|||
void warnx(const char *fmt, ...);
|
||||
#endif
|
||||
|
||||
int recent_seqno(int , int);
|
||||
|
||||
#ifndef WINDOWS32
|
||||
void fd_set_close_on_exec(int fd);
|
||||
#endif
|
||||
|
|
|
@ -408,10 +408,9 @@ dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, siz
|
|||
unsigned short type;
|
||||
char *data;
|
||||
unsigned short rlen;
|
||||
int id;
|
||||
uint16_t id;
|
||||
int rv;
|
||||
|
||||
q->id2 = 0;
|
||||
rv = 0;
|
||||
header = (HEADER*)packet;
|
||||
|
||||
|
@ -429,7 +428,6 @@ dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, siz
|
|||
ancount = ntohs(header->ancount);
|
||||
|
||||
id = ntohs(header->id);
|
||||
id = id & 0xFFFF; /* Kill any sign extension */
|
||||
|
||||
rlen = 0;
|
||||
|
||||
|
|
|
@ -19,29 +19,58 @@
|
|||
#include "common.h"
|
||||
#include "encoding.h"
|
||||
|
||||
int
|
||||
build_hostname(char *buf, size_t buflen,
|
||||
const char *data, const size_t datalen,
|
||||
const char *topdomain, struct encoder *encoder, int maxlen)
|
||||
size_t
|
||||
get_raw_length_from_dns(size_t enc_bytes, struct encoder *enc, const char *topdomain)
|
||||
/* Returns the maximum length of raw data that can be encoded into enc_bytes */
|
||||
{
|
||||
size_t space;
|
||||
char *b;
|
||||
/* 2 byte for something - seems necessary */
|
||||
size_t enc_datalen = enc_bytes - strlen(topdomain) - 2;
|
||||
/* Number of dots in length of encoded data */
|
||||
size_t dots = 1;
|
||||
if (!enc->eats_dots()) /* Dots are not included in encoded data length */
|
||||
dots += enc_datalen / (DNS_MAXLABEL);
|
||||
enc_datalen -= dots;
|
||||
return enc->get_raw_length(enc_datalen);
|
||||
}
|
||||
|
||||
space = MIN((size_t)maxlen, buflen) - strlen(topdomain) - 8;
|
||||
/* 8 = 5 max header length + 1 dot before topdomain + 2 safety */
|
||||
size_t
|
||||
get_encoded_dns_length(size_t raw_bytes, struct encoder *enc, const char *topdomain)
|
||||
/* Returns length of encoded data from original data length orig_len; */
|
||||
{
|
||||
size_t dots = 1; /* dot before topdomain */
|
||||
size_t len = enc->get_encoded_length(raw_bytes);
|
||||
if (!enc->places_dots())
|
||||
dots += len / DNS_MAXLABEL; /* number of dots needed in data */
|
||||
return len + dots + strlen(topdomain);
|
||||
}
|
||||
|
||||
if (!encoder->places_dots())
|
||||
space -= (space / 57); /* space for dots */
|
||||
size_t
|
||||
build_hostname(uint8_t *buf, size_t buflen, const uint8_t *data, const size_t datalen,
|
||||
const char *topdomain, struct encoder *encoder, size_t maxlen, size_t header_len)
|
||||
/* Builds DNS-compatible hostname for data using specified encoder and topdomain
|
||||
* Encoded data is placed into buf. */
|
||||
{
|
||||
size_t space, enc;
|
||||
uint8_t *b;
|
||||
|
||||
buflen -= header_len;
|
||||
buf += header_len;
|
||||
maxlen -= header_len;
|
||||
memset(buf, 0, buflen);
|
||||
|
||||
encoder->encode(buf, &space, data, datalen);
|
||||
maxlen = MIN(maxlen, buflen);
|
||||
|
||||
/* 1 byte for dot before topdomain + 1 byte extra for something */
|
||||
space = maxlen - strlen(topdomain) - (maxlen / DNS_MAXLABEL) - 2;
|
||||
|
||||
enc = encoder->encode(buf, &space, data, datalen);
|
||||
// warnx("build_hostname: enc %lu, predicted %lu; maxlen %lu, header %lu, datalen %lu, space %lu",
|
||||
// encdata_len, encoder->get_encoded_length(datalen), maxlen, header_len, datalen, space);
|
||||
|
||||
if (!encoder->places_dots())
|
||||
inline_dotify(buf, buflen);
|
||||
enc = inline_dotify(buf - header_len, buflen + header_len) - header_len;
|
||||
|
||||
b = buf;
|
||||
b += strlen(buf);
|
||||
b = buf + enc;
|
||||
|
||||
/* move b back one step to see if the dot is there */
|
||||
b--;
|
||||
|
@ -50,48 +79,49 @@ build_hostname(char *buf, size_t buflen,
|
|||
b++;
|
||||
/* move b ahead of the string so we can copy to it */
|
||||
|
||||
strncpy(b, topdomain, strlen(topdomain)+1);
|
||||
strncpy((char *)b, topdomain, strlen(topdomain)+1);
|
||||
// warnx("build_hostname: host '%s' (sl %lu, actual %lu), topdomain '%s'",
|
||||
// buf - header_len, strlen(buf - header_len), encdata_len + header_len + strlen(topdomain)+1, b);
|
||||
|
||||
return space;
|
||||
}
|
||||
|
||||
int
|
||||
unpack_data(char *buf, size_t buflen, char *data, size_t datalen, struct encoder *enc)
|
||||
size_t
|
||||
unpack_data(uint8_t *buf, size_t buflen, uint8_t *data, size_t datalen, struct encoder *enc)
|
||||
{
|
||||
if (!enc->eats_dots())
|
||||
datalen = inline_undotify(data, datalen);
|
||||
return enc->decode(buf, &buflen, data, datalen);
|
||||
}
|
||||
|
||||
int
|
||||
inline_dotify(char *buf, size_t buflen)
|
||||
size_t
|
||||
inline_dotify(uint8_t *buf, size_t buflen)
|
||||
{
|
||||
unsigned dots;
|
||||
unsigned pos;
|
||||
unsigned total;
|
||||
char *reader, *writer;
|
||||
size_t pos, total;
|
||||
uint8_t *reader, *writer;
|
||||
|
||||
total = strlen(buf);
|
||||
dots = total / 57;
|
||||
total = strlen((char *)buf);
|
||||
dots = total / DNS_MAXLABEL;
|
||||
|
||||
writer = buf;
|
||||
writer += total;
|
||||
writer += dots;
|
||||
|
||||
total += dots;
|
||||
if (strlen(buf) + dots > buflen) {
|
||||
if (strlen((char *)buf) + dots > buflen) {
|
||||
writer = buf;
|
||||
writer += buflen;
|
||||
total = buflen;
|
||||
}
|
||||
|
||||
reader = writer - dots;
|
||||
pos = (unsigned) (reader - buf) + 1;
|
||||
pos = (reader - buf) + 1;
|
||||
|
||||
while (dots) {
|
||||
*writer-- = *reader--;
|
||||
pos--;
|
||||
if (pos % 57 == 0) {
|
||||
if (pos % DNS_MAXLABEL == 0) {
|
||||
*writer-- = '.';
|
||||
dots--;
|
||||
}
|
||||
|
@ -101,12 +131,12 @@ inline_dotify(char *buf, size_t buflen)
|
|||
return total;
|
||||
}
|
||||
|
||||
int
|
||||
inline_undotify(char *buf, size_t len)
|
||||
size_t
|
||||
inline_undotify(uint8_t *buf, size_t len)
|
||||
{
|
||||
unsigned pos;
|
||||
size_t pos;
|
||||
unsigned dots;
|
||||
char *reader, *writer;
|
||||
uint8_t *reader, *writer;
|
||||
|
||||
writer = buf;
|
||||
reader = writer;
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#ifndef _ENCODING_H_
|
||||
#define _ENCODING_H_
|
||||
|
||||
#include <stdint.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.
|
||||
|
@ -25,20 +27,29 @@
|
|||
#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
|
||||
|
||||
/* Don't push the limit with DNS servers: potentially unwanted behaviour
|
||||
* if labels are all 63 chars long (DNS standard max label length) */
|
||||
#define DNS_MAXLABEL 59
|
||||
|
||||
struct encoder {
|
||||
char name[8];
|
||||
int (*encode) (char *, size_t *, const void *, size_t);
|
||||
int (*decode) (void *, size_t *, const char *, size_t);
|
||||
size_t (*encode) (uint8_t *, size_t *, const uint8_t *, size_t);
|
||||
size_t (*decode) (uint8_t *, size_t *, const uint8_t *, size_t);
|
||||
int (*places_dots) (void);
|
||||
int (*eats_dots) (void);
|
||||
int (*blocksize_raw)(void);
|
||||
int (*blocksize_encoded)(void);
|
||||
size_t (*blocksize_raw)(void);
|
||||
size_t (*blocksize_encoded)(void);
|
||||
size_t (*get_encoded_length)(size_t);
|
||||
size_t (*get_raw_length)(size_t);
|
||||
};
|
||||
|
||||
int build_hostname(char *, size_t, const char *, const size_t, const char *, struct encoder *, int);
|
||||
int unpack_data(char *, size_t, char *, size_t, struct encoder *);
|
||||
int inline_dotify(char *, size_t);
|
||||
int inline_undotify(char *, size_t);
|
||||
size_t get_raw_length_from_dns(size_t enc_bytes, struct encoder *enc, const char *topdomain);
|
||||
size_t get_encoded_dns_length(size_t raw_bytes, struct encoder *enc, const char *topdomain);
|
||||
|
||||
size_t build_hostname(uint8_t *, size_t, const uint8_t *, const size_t, const char *, struct encoder *, size_t, size_t);
|
||||
size_t unpack_data(uint8_t *, size_t, uint8_t *, size_t, struct encoder *);
|
||||
size_t inline_dotify(uint8_t *, size_t);
|
||||
size_t inline_undotify(uint8_t *, size_t);
|
||||
|
||||
|
||||
#endif /* _ENCODING_H_ */
|
||||
|
|
707
src/iodine.c
707
src/iodine.c
|
@ -21,9 +21,10 @@
|
|||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/time.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
|
||||
|
@ -34,16 +35,33 @@
|
|||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
#include "version.h"
|
||||
#include "tun.h"
|
||||
#include "client.h"
|
||||
#include "util.h"
|
||||
#include "encoding.h"
|
||||
#include "base32.h"
|
||||
|
||||
#ifdef WINDOWS32
|
||||
#include "windows.h"
|
||||
WORD req_version = MAKEWORD(2, 2);
|
||||
WSADATA wsa_data;
|
||||
#else
|
||||
#include <arpa/nameser.h>
|
||||
#ifdef ANDROID
|
||||
#include "android_dns.h"
|
||||
#endif
|
||||
#ifdef DARWIN
|
||||
#define BIND_8_COMPAT
|
||||
#include <arpa/nameser_compat.h>
|
||||
#endif
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
|
||||
#if !defined(BSD) && !defined(__GLIBC__)
|
||||
|
@ -52,10 +70,139 @@ static char *__progname;
|
|||
|
||||
#define PASSWORD_ENV_VAR "IODINE_PASS"
|
||||
|
||||
struct client_instance this;
|
||||
|
||||
/* BEGIN PRESET DEFINITIONS */
|
||||
|
||||
/* static startup values - should not be changed in presets */
|
||||
#define PRESET_STATIC_VALUES \
|
||||
.conn = CONN_DNS_NULL, \
|
||||
.send_ping_soon = 1, \
|
||||
.maxfragsize_up = 100, \
|
||||
.next_downstream_ack = -1, \
|
||||
.num_immediate = 1, \
|
||||
.rtt_total_ms = 200, \
|
||||
.remote_forward_addr = {.ss_family = AF_UNSPEC}
|
||||
|
||||
static struct client_instance preset_default = {
|
||||
.raw_mode = 1,
|
||||
.lazymode = 1,
|
||||
.max_timeout_ms = 5000,
|
||||
.send_interval_ms = 0,
|
||||
.server_timeout_ms = 4000,
|
||||
.downstream_timeout_ms = 2000,
|
||||
.autodetect_server_timeout = 1,
|
||||
.dataenc = &base32_encoder,
|
||||
.autodetect_frag_size = 1,
|
||||
.max_downstream_frag_size = MAX_FRAGSIZE,
|
||||
.compression_up = 1,
|
||||
.compression_down = 1,
|
||||
.windowsize_up = 8,
|
||||
.windowsize_down = 8,
|
||||
.hostname_maxlen = 0xFF,
|
||||
.downenc = ' ',
|
||||
.do_qtype = T_UNSET,
|
||||
PRESET_STATIC_VALUES
|
||||
};
|
||||
|
||||
static struct client_instance preset_original = {
|
||||
.raw_mode = 0,
|
||||
.lazymode = 1,
|
||||
.max_timeout_ms = 4000,
|
||||
.send_interval_ms = 0,
|
||||
.server_timeout_ms = 3000,
|
||||
.autodetect_server_timeout = 1,
|
||||
.windowsize_down = 1,
|
||||
.windowsize_up = 1,
|
||||
.hostname_maxlen = 0xFF,
|
||||
.downstream_timeout_ms = 4000,
|
||||
.dataenc = &base32_encoder,
|
||||
.autodetect_frag_size = 1,
|
||||
.max_downstream_frag_size = MAX_FRAGSIZE,
|
||||
.compression_down = 1,
|
||||
.compression_up = 0,
|
||||
.downenc = ' ',
|
||||
.do_qtype = T_UNSET,
|
||||
PRESET_STATIC_VALUES
|
||||
};
|
||||
|
||||
static struct client_instance preset_fast = {
|
||||
.raw_mode = 0,
|
||||
.lazymode = 1,
|
||||
.max_timeout_ms = 3000,
|
||||
.send_interval_ms = 0,
|
||||
.server_timeout_ms = 2500,
|
||||
.downstream_timeout_ms = 100,
|
||||
.autodetect_server_timeout = 1,
|
||||
.dataenc = &base32_encoder,
|
||||
.autodetect_frag_size = 1,
|
||||
.max_downstream_frag_size = 1176,
|
||||
.compression_up = 1,
|
||||
.compression_down = 1,
|
||||
.windowsize_up = 30,
|
||||
.windowsize_down = 30,
|
||||
.hostname_maxlen = 0xFF,
|
||||
.downenc = ' ',
|
||||
.do_qtype = T_UNSET,
|
||||
PRESET_STATIC_VALUES
|
||||
};
|
||||
|
||||
static struct client_instance preset_fallback = {
|
||||
.raw_mode = 1,
|
||||
.lazymode = 1,
|
||||
.max_timeout_ms = 1000,
|
||||
.send_interval_ms = 20,
|
||||
.server_timeout_ms = 500,
|
||||
.downstream_timeout_ms = 1000,
|
||||
.autodetect_server_timeout = 1,
|
||||
.dataenc = &base32_encoder,
|
||||
.autodetect_frag_size = 1,
|
||||
.max_downstream_frag_size = 500,
|
||||
.compression_up = 1,
|
||||
.compression_down = 1,
|
||||
.windowsize_up = 1,
|
||||
.windowsize_down = 1,
|
||||
.hostname_maxlen = 100,
|
||||
.downenc = 'T',
|
||||
.do_qtype = T_CNAME,
|
||||
PRESET_STATIC_VALUES
|
||||
};
|
||||
|
||||
#define NUM_CLIENT_PRESETS 4
|
||||
|
||||
static struct {
|
||||
struct client_instance *preset_data;
|
||||
char short_name;
|
||||
char *desc;
|
||||
} client_presets[NUM_CLIENT_PRESETS] = {
|
||||
{
|
||||
.preset_data = &preset_default,
|
||||
.short_name = 'D',
|
||||
.desc = "Defaults"
|
||||
},
|
||||
{
|
||||
.preset_data = &preset_original,
|
||||
.short_name = '7',
|
||||
.desc = "Imitate iodine 0.7"
|
||||
},
|
||||
{
|
||||
.preset_data = &preset_fast,
|
||||
.short_name = 'F',
|
||||
.desc = "Fast and low latency"
|
||||
},
|
||||
{
|
||||
.preset_data = &preset_fallback,
|
||||
.short_name = 'M',
|
||||
.desc = "Minimal DNS queries and short DNS timeouts"
|
||||
}
|
||||
};
|
||||
|
||||
/* END PRESET DEFINITIONS */
|
||||
|
||||
static void
|
||||
sighandler(int sig)
|
||||
{
|
||||
client_stop();
|
||||
this.running = 0;
|
||||
}
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
|
@ -65,121 +212,181 @@ static void usage() __attribute__((noreturn));
|
|||
#endif
|
||||
|
||||
static void
|
||||
usage() {
|
||||
print_usage()
|
||||
{
|
||||
extern char *__progname;
|
||||
|
||||
fprintf(stderr, "Usage: %s [-v] [-h] [-f] [-r] [-u user] [-t chrootdir] [-d device] "
|
||||
"[-P password] [-m maxfragsize] [-M maxlen] [-T type] [-O enc] [-L 0|1] [-I sec] "
|
||||
"[-z context] [-F pidfile] [nameserver] topdomain\n", __progname);
|
||||
fprintf(stderr, "Usage: %s [-v] [-h] [-Y preset] [-V sec] [-X port] [-f] [-r] [-u user] [-t chrootdir] [-d device] "
|
||||
"[-w downfrags] [-W upfrags] [-i sec -j sec] [-I sec] [-c 0|1] [-C 0|1] [-s ms] "
|
||||
"[-P password] [-m maxfragsize] [-M maxlen] [-T type] [-O enc] [-L 0|1] [-R port[,host] ] "
|
||||
"[-z context] [-F pidfile] topdomain [nameserver1 [nameserver2 [...]]]\n", __progname);
|
||||
}
|
||||
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
print_usage();
|
||||
exit(2);
|
||||
}
|
||||
|
||||
static void
|
||||
help() {
|
||||
extern char *__progname;
|
||||
print_presets(int spaces)
|
||||
{
|
||||
#define INDENT fprintf(stderr, "%*s", spaces, "");
|
||||
INDENT fprintf(stderr, "Available presets: (use -Y <preset ID>)\n");
|
||||
spaces += 2;
|
||||
for (int i = 0; i < NUM_CLIENT_PRESETS; i++) {
|
||||
INDENT fprintf(stderr, "'%c': %s\n", client_presets[i].short_name, client_presets[i].desc);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
help()
|
||||
{
|
||||
fprintf(stderr, "iodine IP over DNS tunneling client\n");
|
||||
fprintf(stderr, "Usage: %s [-v] [-h] [-f] [-r] [-u user] [-t chrootdir] [-d device] "
|
||||
"[-P password] [-m maxfragsize] [-M maxlen] [-T type] [-O enc] [-L 0|1] [-I sec] "
|
||||
"[-z context] [-F pidfile] [nameserver] topdomain\n", __progname);
|
||||
fprintf(stderr, "Options to try if connection doesn't work:\n");
|
||||
fprintf(stderr, " -T force dns type: NULL, PRIVATE, TXT, SRV, MX, CNAME, A (default: autodetect)\n");
|
||||
fprintf(stderr, " -O force downstream encoding for -T other than NULL: Base32, Base64, Base64u,\n");
|
||||
fprintf(stderr, " Base128, or (only for TXT:) Raw (default: autodetect)\n");
|
||||
fprintf(stderr, " -I max interval between requests (default 4 sec) to prevent DNS timeouts\n");
|
||||
print_usage();
|
||||
fprintf(stderr, "\nOptions to try if connection doesn't work:\n");
|
||||
fprintf(stderr, " -T use DNS type: NULL, PRIVATE, TXT, SRV, MX, CNAME, A (default: autodetect)\n");
|
||||
fprintf(stderr, " -O use specific downstream encoding for queries: Base32, Base64, Base64u,\n");
|
||||
fprintf(stderr, " Base128, or (only for TXT:) Raw (default: autodetect)\n");
|
||||
fprintf(stderr, " -I target interval between sending and receiving requests (default: 4 secs)\n");
|
||||
fprintf(stderr, " or ping interval in immediate mode (default: 1 sec)\n");
|
||||
fprintf(stderr, " -s minimum interval between queries (default: 0ms)\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, " -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 skip raw UDP mode attempt\n");
|
||||
fprintf(stderr, " -P password used for authentication (max 32 chars will be used)\n\n");
|
||||
|
||||
fprintf(stderr, "Fine-tuning options:\n");
|
||||
fprintf(stderr, " -w downstream fragment window size (default: 8 frags)\n");
|
||||
fprintf(stderr, " -W upstream fragment window size (default: 8 frags)\n");
|
||||
fprintf(stderr, " -i server-side request timeout in lazy mode (default: auto)\n");
|
||||
fprintf(stderr, " -j downstream fragment ACK timeout, implies -i4 (default: 2 sec)\n");
|
||||
//fprintf(stderr, " --nodrop disable TCP packet-dropping optimisations\n");
|
||||
fprintf(stderr, " -c 1: use downstream compression (default), 0: disable\n");
|
||||
fprintf(stderr, " -C 1: use upstream compression (default), 0: disable\n\n");
|
||||
|
||||
fprintf(stderr, "Other options:\n");
|
||||
fprintf(stderr, " -v to print version info and exit\n");
|
||||
fprintf(stderr, " -h to print this help and exit\n");
|
||||
fprintf(stderr, " -f to keep running in foreground\n");
|
||||
fprintf(stderr, " -u name to drop privileges and run as user 'name'\n");
|
||||
fprintf(stderr, " -t dir to chroot to directory dir\n");
|
||||
fprintf(stderr, " -d device to set tunnel device name\n");
|
||||
fprintf(stderr, " -z context, to apply specified SELinux context after initialization\n");
|
||||
fprintf(stderr, " -F pidfile to write pid to a file\n");
|
||||
fprintf(stderr, "nameserver is the IP number/hostname of the relaying nameserver. if absent, /etc/resolv.conf is used\n");
|
||||
fprintf(stderr, " -v, --version print version info and exit\n");
|
||||
fprintf(stderr, " -h, --help print this help and exit\n");
|
||||
fprintf(stderr, " -V, --stats print connection statistics at given intervals (default: 5 sec)\n");
|
||||
fprintf(stderr, " -f keep running in foreground\n");
|
||||
fprintf(stderr, " -D enable debug mode (add more D's to increase debug level)\n");
|
||||
fprintf(stderr, " -d set tunnel device name\n");
|
||||
fprintf(stderr, " -u drop privileges and run as specified user\n");
|
||||
fprintf(stderr, " -F write PID to specified file\n");
|
||||
fprintf(stderr, " -Y, --preset use a set of predefined options for DNS tunnel (can be overridden manually)\n");
|
||||
print_presets(6);
|
||||
fprintf(stderr, " -R, --remote [host:]port skip tun device and forward data to/from\n");
|
||||
fprintf(stderr, " stdin/out, telling iodined to forward data to a remote port\n");
|
||||
fprintf(stderr, " locally or to a specific host (accessed by server). Implies --nodrop.\n");
|
||||
fprintf(stderr, " To specify an IPv6 address, host must be enclosed in square brackets.\n");
|
||||
fprintf(stderr, " Can be used with SSH ProxyCommand option. ('iodine -R 22 ...')\n");
|
||||
fprintf(stderr, " --chroot chroot to given directory\n");
|
||||
fprintf(stderr, " --context apply specified SELinux context after initialization\n");
|
||||
fprintf(stderr, " --rdomain use specified routing domain (OpenBSD only)\n\n");
|
||||
|
||||
fprintf(stderr, "nameserver is the IP/hostname of the relaying nameserver(s).\n");
|
||||
fprintf(stderr, " multiple nameservers can be specified (used in round-robin). \n");
|
||||
fprintf(stderr, " if absent, system default is used\n");
|
||||
fprintf(stderr, "topdomain is the FQDN that is delegated to the tunnel endpoint.\n");
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void
|
||||
version() {
|
||||
|
||||
version()
|
||||
{
|
||||
fprintf(stderr, "iodine IP over DNS tunneling client\n");
|
||||
fprintf(stderr, "Git version: %s\n", GITREVISION);
|
||||
|
||||
fprintf(stderr, "Git version: %s; protocol version %08X\n", GITREVISION, PROTOCOL_VERSION);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static int
|
||||
parse_tcp_forward_option(char *optstr)
|
||||
{
|
||||
char *remote_port_str, *remote_host_str;
|
||||
int retval;
|
||||
|
||||
if (strrchr(optstr, ':')) {
|
||||
remote_port_str = strrchr(optstr, ':') + 1;
|
||||
if (optstr[0] == '[') {
|
||||
/* IPv6 address enclosed in square brackets */
|
||||
remote_host_str = optstr + 1;
|
||||
/* replace closing bracket with null terminator */
|
||||
*strchr(remote_host_str, ']') = 0;
|
||||
this.remote_forward_addr.ss_family = AF_INET6;
|
||||
retval = inet_pton(AF_INET6, remote_host_str,
|
||||
&((struct sockaddr_in6 *) &this.remote_forward_addr)->sin6_addr);
|
||||
} else {
|
||||
remote_host_str = optstr;
|
||||
/* replace separator with null terminator */
|
||||
*strchr(remote_host_str, ':') = 0;
|
||||
this.remote_forward_addr.ss_family = AF_INET;
|
||||
retval = inet_aton(remote_host_str,
|
||||
&((struct sockaddr_in *) &this.remote_forward_addr)->sin_addr);
|
||||
}
|
||||
} else {
|
||||
/* no address specified (use server localhost IPv4), optstr is port */
|
||||
remote_port_str = optstr;
|
||||
this.remote_forward_addr.ss_family = AF_INET;
|
||||
((struct sockaddr_in *) &this.remote_forward_addr)->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
retval = 1;
|
||||
}
|
||||
|
||||
if (!retval) {
|
||||
warnx("Invalid remote forward address (-R)! Must be [host:]port,\n"
|
||||
"where IPv6 addresses are enclosed in literal square brackets [].");
|
||||
usage();
|
||||
/* not reached */
|
||||
}
|
||||
|
||||
/* parse port */
|
||||
int port = atoi(remote_port_str);
|
||||
if (port < 1 || port > 65535) {
|
||||
fprintf(stderr, "Remote forward (-R) TCP port must be between 1 and 65535.");
|
||||
usage();
|
||||
/* not reached */
|
||||
}
|
||||
|
||||
if (this.remote_forward_addr.ss_family == AF_INET) {
|
||||
/* set port as sockaddr_in (IPv4) */
|
||||
((struct sockaddr_in *) &this.remote_forward_addr)->sin_port = htons(port);
|
||||
} else {
|
||||
/* set port in IPv6 sockaddr */
|
||||
((struct sockaddr_in6 *) &this.remote_forward_addr)->sin6_port = htons(port);
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char *nameserv_host;
|
||||
char *topdomain;
|
||||
char *errormsg;
|
||||
char *errormsg = NULL;
|
||||
#ifndef WINDOWS32
|
||||
struct passwd *pw;
|
||||
#endif
|
||||
char *username;
|
||||
char password[33];
|
||||
int foreground;
|
||||
char *newroot;
|
||||
char *context;
|
||||
char *device;
|
||||
char *pidfile;
|
||||
int choice;
|
||||
int tun_fd;
|
||||
int dns_fd;
|
||||
int max_downstream_frag_size;
|
||||
int autodetect_frag_size;
|
||||
int retval;
|
||||
int raw_mode;
|
||||
int lazymode;
|
||||
int selecttimeout;
|
||||
int hostname_maxlen;
|
||||
#ifdef OPENBSD
|
||||
int rtable = 0;
|
||||
struct passwd *pw = NULL;
|
||||
#endif
|
||||
int choice = -1;
|
||||
int retval = 0;
|
||||
|
||||
char *username = NULL;
|
||||
char *newroot = NULL;
|
||||
char *context = NULL;
|
||||
char *device = NULL;
|
||||
char *pidfile = NULL;
|
||||
|
||||
int remote_forward_port = 0;
|
||||
|
||||
char *nameserv_host = NULL;
|
||||
struct sockaddr_storage nameservaddr;
|
||||
int nameservaddr_len;
|
||||
int nameserv_family;
|
||||
|
||||
nameserv_host = NULL;
|
||||
topdomain = NULL;
|
||||
errormsg = NULL;
|
||||
#ifndef WINDOWS32
|
||||
pw = NULL;
|
||||
#endif
|
||||
username = NULL;
|
||||
memset(password, 0, 33);
|
||||
srand(time(NULL));
|
||||
foreground = 0;
|
||||
newroot = NULL;
|
||||
context = NULL;
|
||||
device = NULL;
|
||||
pidfile = NULL;
|
||||
|
||||
autodetect_frag_size = 1;
|
||||
max_downstream_frag_size = 3072;
|
||||
retval = 0;
|
||||
raw_mode = 1;
|
||||
lazymode = 1;
|
||||
selecttimeout = 4;
|
||||
hostname_maxlen = 0xFF;
|
||||
nameserv_family = AF_UNSPEC;
|
||||
int nameservaddr_len = 0;
|
||||
int nameserv_family = AF_UNSPEC;
|
||||
|
||||
#ifdef WINDOWS32
|
||||
WSAStartup(req_version, &wsa_data);
|
||||
#endif
|
||||
|
||||
srand((unsigned) time(NULL));
|
||||
client_init();
|
||||
|
||||
#if !defined(BSD) && !defined(__GLIBC__)
|
||||
__progname = strrchr(argv[0], '/');
|
||||
if (__progname == NULL)
|
||||
|
@ -188,8 +395,76 @@ main(int argc, char **argv)
|
|||
__progname++;
|
||||
#endif
|
||||
|
||||
while ((choice = getopt(argc, argv, "46vfhru:t:d:R:P:m:M:F:T:O:L:I:")) != -1) {
|
||||
switch(choice) {
|
||||
#define OPT_RDOMAIN 0x80
|
||||
#define OPT_NODROP 0x81
|
||||
|
||||
/* each option has format:
|
||||
* char *name, int has_arg, int *flag, int val */
|
||||
static struct option iodine_args[] = {
|
||||
{"version", no_argument, 0, 'v'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"stats", optional_argument, 0, 'V'},
|
||||
{"context", required_argument, 0, 'z'},
|
||||
{"rdomain", required_argument, 0, OPT_RDOMAIN},
|
||||
{"chrootdir", required_argument, 0, 't'},
|
||||
{"preset", required_argument, 0, 'Y'},
|
||||
{"proxycommand", no_argument, 0, 'R'},
|
||||
// {"nodrop", no_argument, 0, OPT_NODROP},
|
||||
{"remote", required_argument, 0, 'R'},
|
||||
{NULL, 0, 0, 0}
|
||||
};
|
||||
|
||||
/* Pre-parse command line to get preset
|
||||
* This is so that all options override preset values regardless of order in command line */
|
||||
int optind_orig = optind, preset_id = -1;
|
||||
|
||||
static char *iodine_args_short = "46vfDhrY:s:V:c:C:i:j:u:t:d:R:P:w:W:m:M:F:T:O:L:I:";
|
||||
|
||||
while ((choice = getopt_long(argc, argv, iodine_args_short, iodine_args, NULL))) {
|
||||
/* Check if preset has been found yet so we don't process any other options */
|
||||
if (preset_id < 0) {
|
||||
if (choice == -1) {
|
||||
/* reached end of command line and no preset specified - use default */
|
||||
preset_id = 0;
|
||||
} else if (choice == 'Y') {
|
||||
/* find index of preset */
|
||||
if (optarg) {
|
||||
for (int i = 0; i < NUM_CLIENT_PRESETS; i++) {
|
||||
if (toupper(optarg[0]) == client_presets[i].short_name) {
|
||||
preset_id = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (choice == '?') {
|
||||
usage();
|
||||
/* Not reached */
|
||||
} else {
|
||||
/* skip all other options until we find preset */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (preset_id < 0) {
|
||||
/* invalid preset or none specified */
|
||||
fprintf(stderr, "Invalid preset or none specified with -Y or --preset!\n");
|
||||
print_presets(2);
|
||||
usage();
|
||||
/* not reached */
|
||||
}
|
||||
|
||||
memcpy(&this, client_presets[preset_id].preset_data, sizeof(struct client_instance));
|
||||
|
||||
/* Reset optind to reparse command line */
|
||||
optind = optind_orig;
|
||||
continue;
|
||||
} else if (choice == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Once a preset is used, it is copied into memory. This way other
|
||||
* options can override preset values regardless of order in command line */
|
||||
|
||||
switch (choice) {
|
||||
case '4':
|
||||
nameserv_family = AF_INET;
|
||||
break;
|
||||
|
@ -200,15 +475,23 @@ main(int argc, char **argv)
|
|||
version();
|
||||
/* NOTREACHED */
|
||||
break;
|
||||
case 'V':
|
||||
this.stats = atoi(optarg);
|
||||
if (this.stats < 0)
|
||||
this.stats = 0;
|
||||
break;
|
||||
case 'f':
|
||||
foreground = 1;
|
||||
this.foreground = 1;
|
||||
break;
|
||||
case 'D':
|
||||
this.debug++;
|
||||
break;
|
||||
case 'h':
|
||||
help();
|
||||
/* NOTREACHED */
|
||||
break;
|
||||
case 'r':
|
||||
raw_mode = 0;
|
||||
this.raw_mode = 0;
|
||||
break;
|
||||
case 'u':
|
||||
username = optarg;
|
||||
|
@ -220,27 +503,36 @@ main(int argc, char **argv)
|
|||
device = optarg;
|
||||
break;
|
||||
#ifdef OPENBSD
|
||||
case 'R':
|
||||
case OPT_RDOMAIN:
|
||||
rtable = atoi(optarg);
|
||||
break;
|
||||
#endif
|
||||
case 'R':
|
||||
/* Argument format: [host:]port */
|
||||
if (!optarg) break;
|
||||
this.use_remote_forward = 1;
|
||||
remote_forward_port = parse_tcp_forward_option(optarg);
|
||||
break;
|
||||
case OPT_NODROP:
|
||||
// TODO implement TCP-over-tun optimisations
|
||||
break;
|
||||
case 'P':
|
||||
strncpy(password, optarg, sizeof(password));
|
||||
password[sizeof(password)-1] = 0;
|
||||
strncpy(this.password, optarg, sizeof(this.password));
|
||||
this.password[sizeof(this.password)-1] = 0;
|
||||
|
||||
/* XXX: find better way of cleaning up ps(1) */
|
||||
memset(optarg, 0, strlen(optarg));
|
||||
break;
|
||||
case 'm':
|
||||
autodetect_frag_size = 0;
|
||||
max_downstream_frag_size = atoi(optarg);
|
||||
this.autodetect_frag_size = 0;
|
||||
this.max_downstream_frag_size = atoi(optarg);
|
||||
break;
|
||||
case 'M':
|
||||
hostname_maxlen = atoi(optarg);
|
||||
if (hostname_maxlen > 255)
|
||||
hostname_maxlen = 255;
|
||||
if (hostname_maxlen < 10)
|
||||
hostname_maxlen = 10;
|
||||
this.hostname_maxlen = atoi(optarg);
|
||||
if (this.hostname_maxlen > 255)
|
||||
this.hostname_maxlen = 255;
|
||||
if (this.hostname_maxlen < 10)
|
||||
this.hostname_maxlen = 10;
|
||||
break;
|
||||
case 'z':
|
||||
context = optarg;
|
||||
|
@ -252,104 +544,184 @@ main(int argc, char **argv)
|
|||
if (client_set_qtype(optarg))
|
||||
errx(5, "Invalid query type '%s'", optarg);
|
||||
break;
|
||||
case 'O': /* not -D, is Debug in server */
|
||||
client_set_downenc(optarg);
|
||||
case 'O':
|
||||
if ((this.downenc = parse_encoding(optarg)) == 0)
|
||||
errx(6, "Invalid encoding type '%s'", optarg);
|
||||
break;
|
||||
case 'L':
|
||||
lazymode = atoi(optarg);
|
||||
if (lazymode > 1)
|
||||
lazymode = 1;
|
||||
if (lazymode < 0)
|
||||
lazymode = 0;
|
||||
if (!lazymode)
|
||||
selecttimeout = 1;
|
||||
this.lazymode = atoi(optarg);
|
||||
if (this.lazymode > 1)
|
||||
this.lazymode = 1;
|
||||
if (this.lazymode < 0)
|
||||
this.lazymode = 0;
|
||||
break;
|
||||
case 'I':
|
||||
selecttimeout = atoi(optarg);
|
||||
if (selecttimeout < 1)
|
||||
selecttimeout = 1;
|
||||
this.max_timeout_ms = strtod(optarg, NULL) * 1000;
|
||||
if (this.autodetect_server_timeout) {
|
||||
this.server_timeout_ms = this.max_timeout_ms / 2;
|
||||
}
|
||||
break;
|
||||
case 'i':
|
||||
this.server_timeout_ms = strtod(optarg, NULL) * 1000;
|
||||
this.autodetect_server_timeout = 0;
|
||||
break;
|
||||
case 'j':
|
||||
this.downstream_timeout_ms = strtod(optarg, NULL) * 1000;
|
||||
if (this.autodetect_server_timeout) {
|
||||
this.autodetect_server_timeout = 0;
|
||||
this.server_timeout_ms = 4000;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
this.send_interval_ms = atoi(optarg);
|
||||
if (this.send_interval_ms < 0)
|
||||
this.send_interval_ms = 0;
|
||||
case 'w':
|
||||
this.windowsize_down = atoi(optarg);
|
||||
break;
|
||||
case 'W':
|
||||
this.windowsize_up = atoi(optarg);
|
||||
break;
|
||||
case 'c':
|
||||
this.compression_down = atoi(optarg) & 1;
|
||||
break;
|
||||
case 'C':
|
||||
this.compression_up = atoi(optarg) & 1;
|
||||
break;
|
||||
case 'Y':
|
||||
/* Already processed preset: ignore */
|
||||
continue;
|
||||
default:
|
||||
usage();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
}
|
||||
|
||||
srand((unsigned) time(NULL));
|
||||
this.rand_seed = (uint16_t) rand();
|
||||
this.chunkid = (uint16_t) rand();
|
||||
this.running = 1;
|
||||
|
||||
check_superuser(usage);
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
switch (argc) {
|
||||
case 1:
|
||||
nameserv_host = get_resolvconf_addr();
|
||||
topdomain = strdup(argv[0]);
|
||||
break;
|
||||
case 2:
|
||||
nameserv_host = argv[0];
|
||||
topdomain = strdup(argv[1]);
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
/* NOTREACHED */
|
||||
if (this.debug) {
|
||||
fprintf(stderr, "Debug level %d enabled, will stay in foreground.\n", this.debug);
|
||||
fprintf(stderr, "Add more -D switches to set higher debug level.\n");
|
||||
this.foreground = 1;
|
||||
}
|
||||
|
||||
if (max_downstream_frag_size < 1 || max_downstream_frag_size > 0xffff) {
|
||||
warnx("Use a max frag size between 1 and 65535 bytes.\n");
|
||||
usage();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
if (nameserv_host) {
|
||||
|
||||
this.nameserv_hosts_len = argc - 1;
|
||||
if (this.nameserv_hosts_len <= 0)
|
||||
/* if no hosts specified, use resolv.conf */
|
||||
this.nameserv_hosts_len = 1;
|
||||
|
||||
// Preallocate memory with expected number of hosts
|
||||
this.nameserv_hosts = malloc(sizeof(char *) * this.nameserv_hosts_len);
|
||||
this.nameserv_addrs = malloc(sizeof(struct sockaddr_storage) * this.nameserv_hosts_len);
|
||||
|
||||
if (argc == 0) {
|
||||
usage();
|
||||
/* NOT REACHED */
|
||||
} else if (argc == 1) {
|
||||
this.nameserv_hosts[0] = get_resolvconf_addr();
|
||||
} else if (argc > 1)
|
||||
for (int h = 0; h < this.nameserv_hosts_len; h++)
|
||||
this.nameserv_hosts[h] = strdup(argv[h + 1]);
|
||||
this.topdomain = strdup(argv[0]);
|
||||
|
||||
for (int n = 0; n < this.nameserv_hosts_len; n++) {
|
||||
nameserv_host = this.nameserv_hosts[n];
|
||||
if (!nameserv_host) {
|
||||
errx(1, "Error processing nameserver hostnames!");
|
||||
}
|
||||
nameservaddr_len = get_addr(nameserv_host, DNS_PORT, nameserv_family, 0, &nameservaddr);
|
||||
if (nameservaddr_len < 0) {
|
||||
errx(1, "Cannot lookup nameserver '%s': %s ",
|
||||
nameserv_host, gai_strerror(nameservaddr_len));
|
||||
nameserv_host, gai_strerror(nameservaddr_len));
|
||||
}
|
||||
client_set_nameserver(&nameservaddr, nameservaddr_len);
|
||||
} else {
|
||||
warnx("No nameserver found - not connected to any network?\n");
|
||||
memcpy(&this.nameserv_addrs[n], &nameservaddr, sizeof(struct sockaddr_storage));
|
||||
this.nameserv_addrs_len ++;
|
||||
nameserv_host = NULL;
|
||||
}
|
||||
|
||||
if (this.nameserv_addrs_len <= 0 || !this.nameserv_hosts[0]) {
|
||||
warnx("No nameservers found - not connected to any network?");
|
||||
usage();
|
||||
}
|
||||
|
||||
if (this.max_downstream_frag_size < 10 || this.max_downstream_frag_size > MAX_FRAGSIZE) {
|
||||
warnx("Use a max frag size between 10 and %d bytes.", MAX_FRAGSIZE);
|
||||
usage();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
if(check_topdomain(topdomain, &errormsg)) {
|
||||
if(check_topdomain(this.topdomain, &errormsg)) {
|
||||
warnx("Invalid topdomain: %s", errormsg);
|
||||
usage();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
client_set_selecttimeout(selecttimeout);
|
||||
client_set_lazymode(lazymode);
|
||||
client_set_topdomain(topdomain);
|
||||
client_set_hostname_maxlen(hostname_maxlen);
|
||||
int max_ws = MAX_SEQ_ID / 2;
|
||||
if (this.windowsize_up < 1 || this.windowsize_down < 1 ||
|
||||
this.windowsize_up > max_ws || this.windowsize_down > max_ws) {
|
||||
warnx("Window sizes (-w or -W) must be between 0 and %d!", max_ws);
|
||||
usage();
|
||||
}
|
||||
|
||||
if (this.max_timeout_ms < 100) {
|
||||
warnx("Target interval (-I) must be greater than 0.1 seconds!");
|
||||
usage();
|
||||
}
|
||||
|
||||
if ((this.server_timeout_ms < 100 || this.server_timeout_ms >= this.max_timeout_ms)
|
||||
&& !this.autodetect_server_timeout) {
|
||||
warnx("Server timeout (-i) must be greater than 0.1 sec and less than target interval!");
|
||||
usage();
|
||||
}
|
||||
|
||||
if (this.downstream_timeout_ms < 100) {
|
||||
warnx("Downstream fragment timeout must be more than 0.1 sec to prevent excessive retransmits.");
|
||||
usage();
|
||||
}
|
||||
|
||||
if (!this.lazymode && this.max_timeout_ms > 1000) {
|
||||
fprintf(stderr, "Warning: Target interval of >1 second in immediate mode will cause high latency.\n");
|
||||
}
|
||||
|
||||
if (username != NULL) {
|
||||
#ifndef WINDOWS32
|
||||
if ((pw = getpwnam(username)) == NULL) {
|
||||
warnx("User %s does not exist!\n", username);
|
||||
warnx("User %s does not exist!", username);
|
||||
usage();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
#else
|
||||
warnx("Warning: Cannot switch user on Windows systems.");
|
||||
#endif
|
||||
}
|
||||
|
||||
if (strlen(password) == 0) {
|
||||
if (strlen(this.password) == 0) {
|
||||
if (NULL != getenv(PASSWORD_ENV_VAR))
|
||||
snprintf(password, sizeof(password), "%s", getenv(PASSWORD_ENV_VAR));
|
||||
snprintf(this.password, sizeof(this.password), "%s", getenv(PASSWORD_ENV_VAR));
|
||||
else
|
||||
read_password(password, sizeof(password));
|
||||
read_password(this.password, sizeof(this.password));
|
||||
}
|
||||
|
||||
client_set_password(password);
|
||||
|
||||
if ((tun_fd = open_tun(device)) == -1) {
|
||||
retval = 1;
|
||||
goto cleanup1;
|
||||
if (!this.use_remote_forward) {
|
||||
if ((this.tun_fd = open_tun(device)) == -1) {
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
if ((dns_fd = open_dns_from_host(NULL, 0, nameservaddr.ss_family, AI_PASSIVE)) < 0) {
|
||||
|
||||
if ((this.dns_fd = open_dns_from_host(NULL, 0, nameservaddr.ss_family, AI_PASSIVE)) < 0) {
|
||||
retval = 1;
|
||||
goto cleanup2;
|
||||
goto cleanup;
|
||||
}
|
||||
#ifdef OPENBSD
|
||||
if (rtable > 0)
|
||||
|
@ -359,21 +731,28 @@ main(int argc, char **argv)
|
|||
signal(SIGINT, sighandler);
|
||||
signal(SIGTERM, sighandler);
|
||||
|
||||
fprintf(stderr, "Sending DNS queries for %s to %s\n",
|
||||
topdomain, format_addr(&nameservaddr, nameservaddr_len));
|
||||
fprintf(stderr, "Sending DNS queries for %s to ", this.topdomain);
|
||||
for (int a = 0; a < this.nameserv_addrs_len; a++)
|
||||
fprintf(stderr, "%s%s", format_addr(&this.nameserv_addrs[a], sizeof(struct sockaddr_storage)),
|
||||
(a != this.nameserv_addrs_len - 1) ? ", " : "");
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
if (client_handshake(dns_fd, raw_mode, autodetect_frag_size, max_downstream_frag_size)) {
|
||||
if (this.remote_forward_addr.ss_family != AF_UNSPEC)
|
||||
fprintf(stderr, "Requesting TCP data forwarding from server to %s:%d\n",
|
||||
format_addr(&this.remote_forward_addr, sizeof(struct sockaddr_storage)), remote_forward_port);
|
||||
|
||||
if (client_handshake()) {
|
||||
retval = 1;
|
||||
goto cleanup2;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (client_get_conn() == CONN_RAW_UDP) {
|
||||
fprintf(stderr, "Sending raw traffic directly to %s\n", client_get_raw_addr());
|
||||
if (this.conn == CONN_RAW_UDP) {
|
||||
fprintf(stderr, "Sending raw UDP traffic directly to %s\n", client_get_raw_addr());
|
||||
}
|
||||
|
||||
fprintf(stderr, "Connection setup complete, transmitting data.\n");
|
||||
|
||||
if (foreground == 0)
|
||||
if (this.foreground == 0)
|
||||
do_detach();
|
||||
|
||||
if (pidfile != NULL)
|
||||
|
@ -397,12 +776,16 @@ main(int argc, char **argv)
|
|||
if (context != NULL)
|
||||
do_setcon(context);
|
||||
|
||||
client_tunnel(tun_fd, dns_fd);
|
||||
client_tunnel();
|
||||
|
||||
cleanup2:
|
||||
close_dns(dns_fd);
|
||||
close_tun(tun_fd);
|
||||
cleanup1:
|
||||
cleanup:
|
||||
if (this.use_remote_forward)
|
||||
close(STDOUT_FILENO);
|
||||
close_socket(this.dns_fd);
|
||||
close_socket(this.tun_fd);
|
||||
#ifdef WINDOWS32
|
||||
WSACleanup();
|
||||
#endif
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
|
2484
src/iodined.c
2484
src/iodined.c
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2015 Erik Ekman <yarrick@kryo.se>,
|
||||
* 2006-2009 Bjorn Andersson <flex@kryo.se>,
|
||||
* 2015 Frekk van Blagh <frekk@frekkworks.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __SERVER_H__
|
||||
#define __SERVER_H__
|
||||
|
||||
#ifdef WINDOWS32
|
||||
#include "windows.h"
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <arpa/nameser.h>
|
||||
#ifdef DARWIN
|
||||
#define BIND_8_COMPAT
|
||||
#include <arpa/nameser_compat.h>
|
||||
#endif
|
||||
#define _XPG4_2
|
||||
#include <netinet/in_systm.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <grp.h>
|
||||
#include <sys/uio.h>
|
||||
#include <pwd.h>
|
||||
#include <netdb.h>
|
||||
#include <syslog.h>
|
||||
#endif
|
||||
|
||||
/* Max number of incoming queries to hold at one time (recommended to be same as windowsize)
|
||||
* Memory = USERS * (sizeof(struct query_buffer) + sizeof(query) * QMEM_LEN) */
|
||||
#define QMEM_LEN 32
|
||||
|
||||
#define USE_DNSCACHE
|
||||
/* QMEM entries contain additional space for DNS responses.
|
||||
* Undefine to disable. */
|
||||
|
||||
/* Number of fragments in outgoing buffer.
|
||||
* Mem usage: USERS * (MAX_FRAGLEN * OUTFRAGBUF_LEN + sizeof(struct window_buffer)) */
|
||||
#define OUTFRAGBUF_LEN 64
|
||||
|
||||
/* Number of fragments in incoming buffer; must be at least windowsize * 2
|
||||
* Minimum recommended = ((max packet size or MTU) / (max up fragsize)) * 2
|
||||
* ie. (1200 / 100) * 2 = 24 */
|
||||
#define INFRAGBUF_LEN 64
|
||||
|
||||
#define PASSWORD_ENV_VAR "IODINED_PASS"
|
||||
|
||||
#define INSTANCE server
|
||||
|
||||
#if defined IP_RECVDSTADDR
|
||||
# define DSTADDR_SOCKOPT IP_RECVDSTADDR
|
||||
# define dstaddr(x) ((struct in_addr *) CMSG_DATA(x))
|
||||
#elif defined IP_PKTINFO
|
||||
# define DSTADDR_SOCKOPT IP_PKTINFO
|
||||
# define dstaddr(x) (&(((struct in_pktinfo *)(CMSG_DATA(x)))->ipi_addr))
|
||||
#endif
|
||||
|
||||
#ifndef IPV6_RECVPKTINFO
|
||||
#define IPV6_RECVPKTINFO IPV6_PKTINFO
|
||||
#endif
|
||||
|
||||
#if !defined(BSD) && !defined(__GLIBC__)
|
||||
static char *__progname;
|
||||
#endif
|
||||
|
||||
/* Struct with IPv4 and IPv6 file descriptors.
|
||||
* Need to be passed on down to tunneling code since we can get a
|
||||
* packet on one fd meant for a user on the other.
|
||||
*/
|
||||
struct dnsfd {
|
||||
int v4fd;
|
||||
int v6fd;
|
||||
};
|
||||
|
||||
struct server_instance {
|
||||
/* Global server variables */
|
||||
int running;
|
||||
char *topdomain;
|
||||
char password[33];
|
||||
int check_ip;
|
||||
int my_mtu;
|
||||
in_addr_t my_ip;
|
||||
int netmask;
|
||||
in_addr_t ns_ip;
|
||||
int bind_port;
|
||||
int debug;
|
||||
|
||||
int addrfamily;
|
||||
struct dnsfd dns_fds;
|
||||
int tun_fd;
|
||||
int port;
|
||||
int mtu;
|
||||
int max_idle_time;
|
||||
struct sockaddr_storage dns4addr;
|
||||
int dns4addr_len;
|
||||
struct sockaddr_storage dns6addr;
|
||||
int dns6addr_len;
|
||||
|
||||
int allow_forward_local_port;
|
||||
int allow_forward_remote;
|
||||
|
||||
/* settings for forwarding normal DNS to
|
||||
* local real DNS server */
|
||||
int bind_fd;
|
||||
int bind_enable;
|
||||
};
|
||||
|
||||
extern struct server_instance server;
|
||||
|
||||
typedef enum {
|
||||
VERSION_ACK,
|
||||
VERSION_NACK,
|
||||
VERSION_FULL
|
||||
} version_ack_t;
|
||||
|
||||
struct query_answer {
|
||||
uint8_t data[4096];
|
||||
size_t len;
|
||||
};
|
||||
|
||||
struct qmem_query {
|
||||
struct query q;
|
||||
#ifdef USE_DNSCACHE
|
||||
struct query_answer a;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Struct used for QMEM + DNS cache */
|
||||
struct qmem_buffer {
|
||||
struct qmem_query queries[QMEM_LEN];
|
||||
size_t start_pending; /* index of first "pending" query (ie. no response yet) */
|
||||
size_t start; /* index of first stored/pending query */
|
||||
size_t end; /* index of space after last stored/pending query */
|
||||
size_t length; /* number of stored queries */
|
||||
size_t num_pending; /* number of pending queries */
|
||||
};
|
||||
|
||||
void server_init();
|
||||
void server_stop();
|
||||
int server_tunnel();
|
||||
|
||||
int read_dns(int fd, struct query *q);
|
||||
void write_dns(int fd, struct query *q, char *data, size_t datalen, char downenc);
|
||||
void handle_full_packet(int userid, uint8_t *data, size_t len, int);
|
||||
void handle_null_request(int dns_fd, struct query *q, int domain_len);
|
||||
void handle_ns_request(int dns_fd, struct query *q);
|
||||
void handle_a_request(int dns_fd, struct query *q, int fakeip);
|
||||
|
||||
void send_data_or_ping(int, struct query *, int, int, char*);
|
||||
|
||||
#endif /* __SERVER_H__ */
|
17
src/tun.c
17
src/tun.c
|
@ -457,16 +457,9 @@ open_tun(const char *tun_device)
|
|||
|
||||
#endif
|
||||
|
||||
void
|
||||
close_tun(int tun_fd)
|
||||
{
|
||||
if (tun_fd >= 0)
|
||||
close(tun_fd);
|
||||
}
|
||||
|
||||
#ifdef WINDOWS32
|
||||
int
|
||||
write_tun(int tun_fd, char *data, size_t len)
|
||||
write_tun(int tun_fd, uint8_t *data, size_t len)
|
||||
{
|
||||
DWORD written;
|
||||
DWORD res;
|
||||
|
@ -490,12 +483,12 @@ write_tun(int tun_fd, char *data, size_t len)
|
|||
}
|
||||
|
||||
ssize_t
|
||||
read_tun(int tun_fd, char *buf, size_t len)
|
||||
read_tun(int tun_fd, uint8_t *buf, size_t len)
|
||||
{
|
||||
int bytes;
|
||||
memset(buf, 0, 4);
|
||||
|
||||
bytes = recv(tun_fd, buf + 4, len - 4, 0);
|
||||
bytes = recv(tun_fd, (char *)buf + 4, len - 4, 0);
|
||||
if (bytes < 0) {
|
||||
return bytes;
|
||||
} else {
|
||||
|
@ -504,7 +497,7 @@ read_tun(int tun_fd, char *buf, size_t len)
|
|||
}
|
||||
#else
|
||||
int
|
||||
write_tun(int tun_fd, char *data, size_t len)
|
||||
write_tun(int tun_fd, uint8_t *data, size_t len)
|
||||
{
|
||||
#if defined (FREEBSD) || defined (NETBSD)
|
||||
/* FreeBSD/NetBSD has no header */
|
||||
|
@ -545,7 +538,7 @@ write_tun(int tun_fd, char *data, size_t len)
|
|||
}
|
||||
|
||||
ssize_t
|
||||
read_tun(int tun_fd, char *buf, size_t len)
|
||||
read_tun(int tun_fd, uint8_t *buf, size_t len)
|
||||
{
|
||||
#if defined (FREEBSD) || defined (NETBSD)
|
||||
/* FreeBSD/NetBSD has no header */
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
|
||||
int open_tun(const char *);
|
||||
void close_tun(int);
|
||||
int write_tun(int, char *, size_t);
|
||||
ssize_t read_tun(int, char *, size_t);
|
||||
int write_tun(int, uint8_t *, size_t);
|
||||
ssize_t read_tun(int, uint8_t *, size_t);
|
||||
int tun_setip(const char *, const char *, int);
|
||||
int tun_setmtu(const unsigned);
|
||||
|
||||
|
|
179
src/user.c
179
src/user.c
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
|
||||
* 2006-2009 Bjorn Andersson <flex@kryo.se>
|
||||
* 2006-2009 Bjorn Andersson <flex@kryo.se>,
|
||||
* 2015 Frekk van Blagh <frekk@frekkworks.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -33,9 +34,12 @@
|
|||
#include "common.h"
|
||||
#include "encoding.h"
|
||||
#include "user.h"
|
||||
#include "window.h"
|
||||
|
||||
struct tun_user *users;
|
||||
unsigned usercount;
|
||||
int created_users;
|
||||
|
||||
|
||||
int
|
||||
init_users(in_addr_t my_ip, int netbits)
|
||||
|
@ -60,6 +64,7 @@ init_users(in_addr_t my_ip, int netbits)
|
|||
maxusers = (1 << (32-netbits)) - 3; /* 3: Net addr, broadcast addr, iodined addr */
|
||||
usercount = MIN(maxusers, USERS);
|
||||
|
||||
if (users) free(users);
|
||||
users = calloc(usercount, sizeof(struct tun_user));
|
||||
for (i = 0; i < usercount; i++) {
|
||||
in_addr_t ip;
|
||||
|
@ -74,11 +79,10 @@ init_users(in_addr_t my_ip, int netbits)
|
|||
}
|
||||
users[i].tun_ip = ip;
|
||||
net.s_addr = ip;
|
||||
users[i].disabled = 0;
|
||||
users[i].authenticated = 0;
|
||||
users[i].authenticated_raw = 0;
|
||||
users[i].active = 0;
|
||||
/* Rest is reset on login ('V' packet) */
|
||||
|
||||
users[i].incoming = window_buffer_init(INFRAGBUF_LEN, 10, MAX_FRAGSIZE, WINDOW_RECVING);
|
||||
users[i].outgoing = window_buffer_init(OUTFRAGBUF_LEN, 10, 100, WINDOW_SENDING);
|
||||
/* Rest is reset on login ('V' packet) or already 0 */
|
||||
}
|
||||
|
||||
return usercount;
|
||||
|
@ -95,75 +99,67 @@ users_get_first_ip()
|
|||
int
|
||||
find_user_by_ip(uint32_t ip)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
ret = -1;
|
||||
for (i = 0; i < usercount; i++) {
|
||||
if (users[i].active &&
|
||||
users[i].authenticated &&
|
||||
!users[i].disabled &&
|
||||
users[i].last_pkt + 60 > time(NULL) &&
|
||||
ip == users[i].tun_ip) {
|
||||
ret = i;
|
||||
break;
|
||||
for (int i = 0; i < usercount; i++) {
|
||||
if (user_active(i) && users[i].authenticated && ip == users[i].tun_ip) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
user_sending(int user)
|
||||
{
|
||||
return users[user].outgoing->numitems > 0;
|
||||
}
|
||||
|
||||
int
|
||||
user_active(int i)
|
||||
{
|
||||
return users[i].active && difftime(time(NULL), users[i].last_pkt) < 60;
|
||||
}
|
||||
|
||||
int
|
||||
all_users_waiting_to_send()
|
||||
/* If this returns true, then reading from tun device is blocked.
|
||||
So only return true when all clients have at least one packet in
|
||||
the outpacket-queue, so that sending back-to-back is possible
|
||||
without going through another select loop.
|
||||
*/
|
||||
So only return true when all clients have insufficient space in
|
||||
outgoing buffer, so that sending back-to-back is possible
|
||||
without going through another select loop. */
|
||||
{
|
||||
time_t now;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
ret = 1;
|
||||
now = time(NULL);
|
||||
for (i = 0; i < usercount; i++) {
|
||||
if (users[i].active && !users[i].disabled &&
|
||||
users[i].last_pkt + 60 > now &&
|
||||
((users[i].conn == CONN_RAW_UDP) ||
|
||||
((users[i].conn == CONN_DNS_NULL)
|
||||
#ifdef OUTPACKETQ_LEN
|
||||
&& users[i].outpacketq_filled < 1
|
||||
#else
|
||||
&& users[i].outpacket.len == 0
|
||||
#endif
|
||||
))) {
|
||||
|
||||
ret = 0;
|
||||
break;
|
||||
int numactive = 0;
|
||||
for (int i = 0; i < usercount; i++) {
|
||||
if (user_active(i)) {
|
||||
if (users[i].outgoing->length - users[i].outgoing->numitems > 8)
|
||||
return 0;
|
||||
numactive ++;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
|
||||
/* no users waiting if there are no users */
|
||||
if (numactive == 0)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
find_available_user()
|
||||
{
|
||||
int ret = -1;
|
||||
int i;
|
||||
for (i = 0; i < usercount; i++) {
|
||||
for (int u = 0; u < usercount; u++) {
|
||||
/* Not used at all or not used in one minute */
|
||||
if ((!users[i].active || users[i].last_pkt + 60 < time(NULL)) && !users[i].disabled) {
|
||||
users[i].active = 1;
|
||||
users[i].authenticated = 0;
|
||||
users[i].authenticated_raw = 0;
|
||||
users[i].last_pkt = time(NULL);
|
||||
users[i].fragsize = 4096;
|
||||
users[i].conn = CONN_DNS_NULL;
|
||||
ret = i;
|
||||
break;
|
||||
if (!user_active(u)) {
|
||||
struct tun_user *user = &users[u];
|
||||
/* reset all stats */
|
||||
user->active = 1;
|
||||
user->authenticated = 0;
|
||||
user->authenticated_raw = 0;
|
||||
user->last_pkt = time(NULL);
|
||||
user->fragsize = MAX_FRAGSIZE;
|
||||
user->conn = CONN_DNS_NULL;
|
||||
return u;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -187,3 +183,72 @@ user_set_conn_type(int userid, enum connection c)
|
|||
users[userid].conn = c;
|
||||
}
|
||||
|
||||
/* This will not check that user has passed login challenge */
|
||||
int
|
||||
check_user_and_ip(int userid, struct query *q, int check_ip)
|
||||
{
|
||||
/* Note: duplicate in handle_raw_login() except IP-address check */
|
||||
|
||||
if (userid < 0 || userid >= created_users ) {
|
||||
return 1;
|
||||
}
|
||||
if (!user_active(userid)) return 1;
|
||||
|
||||
/* return early if IP checking is disabled */
|
||||
if (!check_ip) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (q->from.ss_family != users[userid].host.ss_family) {
|
||||
return 1;
|
||||
}
|
||||
/* Check IPv4 */
|
||||
if (q->from.ss_family == AF_INET) {
|
||||
struct sockaddr_in *expected, *received;
|
||||
|
||||
expected = (struct sockaddr_in *) &(users[userid].host);
|
||||
received = (struct sockaddr_in *) &(q->from);
|
||||
return memcmp(&(expected->sin_addr), &(received->sin_addr), sizeof(struct in_addr));
|
||||
}
|
||||
/* Check IPv6 */
|
||||
if (q->from.ss_family == AF_INET6) {
|
||||
struct sockaddr_in6 *expected, *received;
|
||||
|
||||
expected = (struct sockaddr_in6 *) &(users[userid].host);
|
||||
received = (struct sockaddr_in6 *) &(q->from);
|
||||
return memcmp(&(expected->sin6_addr), &(received->sin6_addr), sizeof(struct in6_addr));
|
||||
}
|
||||
/* Unknown address family */
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
check_authenticated_user_and_ip(int userid, struct query *q, int check_ip)
|
||||
/* This checks that user has passed normal (non-raw) login challenge
|
||||
* Returns 0 on success, 1 if user is not authenticated/IP is wrong */
|
||||
{
|
||||
int res = check_user_and_ip(userid, q, check_ip);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
if (!(users[userid].authenticated >= 1))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
set_user_tcp_fds(fd_set *fds, int conn_status)
|
||||
/* Add TCP forward FDs to fd_set for users with given connection status; returns largest FD added */
|
||||
{
|
||||
int max_fd = 0;
|
||||
for (int userid = 0; userid < created_users; userid ++) {
|
||||
if (user_active(userid) && users[userid].remoteforward_addr_len > 0
|
||||
&& users[userid].remote_forward_connected == conn_status) {
|
||||
FD_SET(users[userid].remote_tcp_fd, fds);
|
||||
max_fd = MAX(max_fd, users[userid].remote_tcp_fd);
|
||||
}
|
||||
}
|
||||
return max_fd;
|
||||
}
|
||||
|
||||
|
|
66
src/user.h
66
src/user.h
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2014 Erik Ekman <yarrick@kryo.se>,
|
||||
* 2006-2009 Bjorn Andersson <flex@kryo.se>
|
||||
* 2006-2009 Bjorn Andersson <flex@kryo.se>,
|
||||
* 2015 Frekk van Blagh <frekk@frekkworks.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -18,73 +19,54 @@
|
|||
#ifndef __USER_H__
|
||||
#define __USER_H__
|
||||
|
||||
#include "window.h"
|
||||
#include "server.h"
|
||||
|
||||
#define USERS 16
|
||||
|
||||
#define OUTPACKETQ_LEN 4 /* Note: 16 users * 1 packet = 1MB */
|
||||
/* Undefine to have no queue for packets coming in from tun device, which may
|
||||
lead to massive dropping in multi-user situations with high traffic. */
|
||||
|
||||
#define DNSCACHE_LEN 4
|
||||
/* Undefine to disable. Should be less than 18; also see comments in iodined.c */
|
||||
|
||||
|
||||
#define QMEMPING_LEN 30
|
||||
/* Max advisable: 64k/2 = 32000. Total mem usage: QMEMPING_LEN * USERS * 6 bytes */
|
||||
|
||||
#define QMEMDATA_LEN 15
|
||||
/* Max advisable: 36/2 = 18. Total mem usage: QMEMDATA_LEN * USERS * 6 bytes */
|
||||
|
||||
struct tun_user {
|
||||
char id;
|
||||
int active;
|
||||
int authenticated;
|
||||
int authenticated_raw;
|
||||
int disabled;
|
||||
time_t last_pkt;
|
||||
struct timeval dns_timeout;
|
||||
int seed;
|
||||
in_addr_t tun_ip;
|
||||
struct sockaddr_storage host;
|
||||
socklen_t hostlen;
|
||||
struct query q;
|
||||
struct query q_sendrealsoon;
|
||||
int q_sendrealsoon_new;
|
||||
struct packet inpacket;
|
||||
struct packet outpacket;
|
||||
int outfragresent;
|
||||
struct sockaddr_storage remoteforward_addr;
|
||||
socklen_t remoteforward_addr_len; /* 0 if no remote forwarding enabled */
|
||||
int remote_tcp_fd;
|
||||
int remote_forward_connected; /* 0 if not connected, -1 if error or 1 if OK */
|
||||
struct frag_buffer *incoming;
|
||||
struct frag_buffer *outgoing;
|
||||
int next_upstream_ack;
|
||||
struct encoder *encoder;
|
||||
char downenc;
|
||||
int out_acked_seqno;
|
||||
int out_acked_fragment;
|
||||
int downenc_bits;
|
||||
int down_compression;
|
||||
int fragsize;
|
||||
enum connection conn;
|
||||
int lazy;
|
||||
unsigned char qmemping_cmc[QMEMPING_LEN * 4];
|
||||
unsigned short qmemping_type[QMEMPING_LEN];
|
||||
int qmemping_lastfilled;
|
||||
unsigned char qmemdata_cmc[QMEMDATA_LEN * 4];
|
||||
unsigned short qmemdata_type[QMEMDATA_LEN];
|
||||
int qmemdata_lastfilled;
|
||||
#ifdef OUTPACKETQ_LEN
|
||||
struct packet outpacketq[OUTPACKETQ_LEN];
|
||||
int outpacketq_nexttouse;
|
||||
int outpacketq_filled;
|
||||
#endif
|
||||
#ifdef DNSCACHE_LEN
|
||||
struct query dnscache_q[DNSCACHE_LEN];
|
||||
char dnscache_answer[DNSCACHE_LEN][4096];
|
||||
int dnscache_answerlen[DNSCACHE_LEN];
|
||||
int dnscache_lastfilled;
|
||||
#endif
|
||||
struct qmem_buffer qmem;
|
||||
};
|
||||
|
||||
extern struct tun_user *users;
|
||||
extern int created_users;
|
||||
|
||||
int user_sending(int user);
|
||||
int all_users_waiting_to_send();
|
||||
int user_active(int i);
|
||||
int check_authenticated_user_and_ip(int userid, struct query *q, int check_ip);
|
||||
int check_user_and_ip(int userid, struct query *q, int check_ip);
|
||||
|
||||
int init_users(in_addr_t, int);
|
||||
const char* users_get_first_ip();
|
||||
int find_user_by_ip(uint32_t);
|
||||
int all_users_waiting_to_send();
|
||||
int find_available_user();
|
||||
void user_switch_codec(int userid, struct encoder *enc);
|
||||
void user_set_conn_type(int userid, enum connection c);
|
||||
int set_user_tcp_fds(fd_set *fds, int);
|
||||
|
||||
#endif
|
||||
|
|
22
src/util.c
22
src/util.c
|
@ -18,6 +18,23 @@
|
|||
#include <stdio.h>
|
||||
#include "common.h"
|
||||
|
||||
time_t
|
||||
timeval_to_ms(struct timeval *tv)
|
||||
{
|
||||
time_t ms = tv->tv_sec * 1000;
|
||||
ms += (tv->tv_usec + 500) / 1000;
|
||||
return ms;
|
||||
}
|
||||
|
||||
struct timeval
|
||||
ms_to_timeval(time_t ms)
|
||||
{
|
||||
struct timeval tv;
|
||||
tv.tv_sec = ms / 1000;
|
||||
tv.tv_usec = (ms - tv.tv_sec * 1000) * 1000;
|
||||
return tv;
|
||||
}
|
||||
|
||||
char *
|
||||
get_resolvconf_addr()
|
||||
{
|
||||
|
@ -43,7 +60,10 @@ get_resolvconf_addr()
|
|||
err(1, "/etc/resolv.conf");
|
||||
|
||||
while (feof(fp) == 0) {
|
||||
fgets(buf, sizeof(buf), fp);
|
||||
if (!fgets(buf, sizeof(buf), fp)) {
|
||||
/* resolv.conf is empty (we got to EOF without reading anything yet */
|
||||
err(1, "/etc/resolv.conf is empty! Please specify a nameserver.");
|
||||
}
|
||||
|
||||
if (sscanf(buf, "nameserver %15s", addr) == 1) {
|
||||
rv = addr;
|
||||
|
|
|
@ -18,7 +18,14 @@
|
|||
#ifndef __UTIL_H__
|
||||
#define __UTIL_H__
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
char *get_resolvconf_addr();
|
||||
void socket_setrtable(int fd, int rtable);
|
||||
|
||||
time_t timeval_to_ms(struct timeval *tv);
|
||||
|
||||
struct timeval ms_to_timeval(time_t ms);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
/* This is the version of the network protocol
|
||||
It is usually equal to the latest iodine version number */
|
||||
#define PROTOCOL_VERSION 0x00000502
|
||||
#define PROTOCOL_VERSION 0x00000800
|
||||
|
||||
#endif /* _VERSION_H_ */
|
||||
|
||||
|
|
|
@ -0,0 +1,453 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Frekk van Blagh <frekk@frekkworks.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <stdio.h>
|
||||
#ifndef WINDOWS32
|
||||
#include <err.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "util.h"
|
||||
#include "window.h"
|
||||
|
||||
int window_debug = 0;
|
||||
|
||||
struct frag_buffer *
|
||||
window_buffer_init(size_t length, unsigned windowsize, unsigned fragsize, int dir)
|
||||
{
|
||||
struct frag_buffer *buf;
|
||||
buf = calloc(sizeof(struct frag_buffer), 1);
|
||||
if (!buf) {
|
||||
errx(1, "Failed to allocate window buffer memory!");
|
||||
}
|
||||
if (dir != WINDOW_RECVING && dir != WINDOW_SENDING) {
|
||||
errx(1, "Invalid window direction!");
|
||||
}
|
||||
if (fragsize > MAX_FRAGSIZE) {
|
||||
errx(fragsize, "Fragsize too large! Please recompile with larger MAX_FRAGSIZE!");
|
||||
}
|
||||
|
||||
buf->frags = calloc(length, sizeof(fragment));
|
||||
if (!buf->frags) {
|
||||
errx(1, "Failed to allocate fragment buffer!");
|
||||
}
|
||||
buf->length = length;
|
||||
buf->windowsize = windowsize;
|
||||
buf->maxfraglen = fragsize;
|
||||
buf->window_end = AFTER(buf, windowsize);
|
||||
buf->direction = dir;
|
||||
buf->timeout.tv_sec = 5;
|
||||
buf->timeout.tv_usec = 0;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void
|
||||
window_buffer_reset(struct frag_buffer *w)
|
||||
{
|
||||
w->chunk_start = 0;
|
||||
w->cur_seq_id = 0;
|
||||
w->last_write = 0;
|
||||
w->numitems = 0;
|
||||
w->oos = 0;
|
||||
w->resends = 0;
|
||||
w->start_seq_id = 0;
|
||||
w->window_start = 0;
|
||||
w->window_end = AFTER(w, w->windowsize);
|
||||
}
|
||||
|
||||
void
|
||||
window_buffer_resize(struct frag_buffer *w, size_t length)
|
||||
{
|
||||
if (w->length == length) return;
|
||||
if (w->numitems > 0) {
|
||||
WDEBUG("Resizing window buffer with things still in it! This will cause problems!");
|
||||
}
|
||||
if (w->frags) free(w->frags);
|
||||
w->frags = calloc(length, sizeof(fragment));
|
||||
if (!w->frags) {
|
||||
errx(1, "Failed to resize window buffer!");
|
||||
}
|
||||
w->length = length;
|
||||
window_buffer_reset(w);
|
||||
}
|
||||
|
||||
void
|
||||
window_buffer_destroy(struct frag_buffer *w)
|
||||
{
|
||||
if (!w) return;
|
||||
if (w->frags) free(w->frags);
|
||||
free(w);
|
||||
}
|
||||
|
||||
void
|
||||
window_buffer_clear(struct frag_buffer *w)
|
||||
{
|
||||
if (!w) return;
|
||||
|
||||
memset(w->frags, 0, w->length * sizeof(fragment));
|
||||
window_buffer_reset(w);
|
||||
}
|
||||
|
||||
/* Returns number of available fragment slots (NOT BYTES) */
|
||||
size_t
|
||||
window_buffer_available(struct frag_buffer *w)
|
||||
{
|
||||
return w->length - w->numitems;
|
||||
}
|
||||
|
||||
/* Places a fragment in the window after the last one */
|
||||
int
|
||||
window_append_fragment(struct frag_buffer *w, fragment *src)
|
||||
{
|
||||
if (window_buffer_available(w) < 1) return 0;
|
||||
memcpy(&w->frags[w->last_write], src, sizeof(fragment));
|
||||
w->last_write = WRAP(w->last_write + 1);
|
||||
w->numitems ++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
window_process_incoming_fragment(struct frag_buffer *w, fragment *f)
|
||||
/* Handles fragment received from the sending side (RECV)
|
||||
* Returns index of fragment in window or <0 if dropped
|
||||
* The next ACK MUST be for this fragment */
|
||||
{
|
||||
/* Check if packet is in window */
|
||||
unsigned startid, endid, offset;
|
||||
fragment *fd;
|
||||
startid = w->start_seq_id;
|
||||
endid = (w->start_seq_id + w->windowsize) % MAX_SEQ_ID;
|
||||
offset = SEQ_OFFSET(startid, f->seqID);
|
||||
|
||||
if (!INWINDOW_SEQ(startid, endid, f->seqID)) {
|
||||
w->oos++;
|
||||
if (offset > MIN(w->length - w->numitems, MAX_SEQ_ID / 2)) {
|
||||
/* Only drop the fragment if it is ancient */
|
||||
WDEBUG("Dropping frag with seqID %u: not in window (%u-%u)", f->seqID, startid, endid);
|
||||
return -1;
|
||||
} else {
|
||||
/* Save "new" fragments to avoid causing other end to advance
|
||||
* when this fragment is ACK'd despite being dropped */
|
||||
WDEBUG("WARNING: Got future fragment (%u), offset %u from start %u (wsize %u).",
|
||||
f->seqID, offset, startid, w->windowsize);
|
||||
}
|
||||
}
|
||||
/* Place fragment into correct location in buffer */
|
||||
ssize_t dest = WRAP(w->window_start + SEQ_OFFSET(startid, f->seqID));
|
||||
WDEBUG(" Putting frag seq %u into frags[%" L "u + %u = %" L "u]",
|
||||
f->seqID, w->window_start, SEQ_OFFSET(startid, f->seqID), dest);
|
||||
|
||||
/* Check if fragment already received */
|
||||
fd = &w->frags[dest];
|
||||
if (fd->len != 0) {
|
||||
WDEBUG("Received duplicate frag, dropping. (prev %u/new %u)", fd->seqID, f->seqID);
|
||||
if (f->seqID == fd->seqID) {
|
||||
/* use retries as counter for dupes */
|
||||
fd->retries ++;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(fd, f, sizeof(fragment));
|
||||
w->numitems ++;
|
||||
|
||||
fd->retries = 0;
|
||||
fd->ack_other = -1;
|
||||
|
||||
/* We assume this packet gets ACKed immediately on return of this function */
|
||||
fd->acks = 1;
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
/* Reassembles first complete sequence of fragments into data. (RECV)
|
||||
* Returns length of data reassembled, or 0 if no data reassembled */
|
||||
size_t
|
||||
window_reassemble_data(struct frag_buffer *w, uint8_t *data, size_t maxlen, int *compression)
|
||||
{
|
||||
size_t woffs, fraglen, datalen = 0;
|
||||
uint8_t *dest; //, *fdata_start;
|
||||
dest = data;
|
||||
if (w->direction != WINDOW_RECVING)
|
||||
return 0;
|
||||
if (w->frags[w->chunk_start].start == 0 && w->numitems > 0) {
|
||||
WDEBUG("chunk_start (%" L "u) pointing to non-start fragment (seq %u, len %" L "u)!",
|
||||
w->chunk_start, w->frags[w->chunk_start].seqID, w->frags[w->chunk_start].len);
|
||||
return 0;
|
||||
}
|
||||
if (compression) *compression = 1;
|
||||
|
||||
fragment *f;
|
||||
size_t i;
|
||||
unsigned curseq;
|
||||
int end = 0;
|
||||
curseq = w->frags[w->chunk_start].seqID;
|
||||
for (i = 0; i < w->numitems; ++i) {
|
||||
woffs = WRAP(w->chunk_start + i);
|
||||
f = &w->frags[woffs];
|
||||
fraglen = f->len;
|
||||
if (fraglen == 0 || f->seqID != curseq) {
|
||||
WDEBUG("Missing next frag %u [%" L "u], got seq %u (%" L "u bytes) instead! Not reassembling!",
|
||||
curseq, woffs, f->seqID, fraglen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
WDEBUG(" Fragment seq %u, data length %" L "u, data offset %" L "u, total len %" L "u, maxlen %" L "u",
|
||||
f->seqID, fraglen, dest - data, datalen, maxlen);
|
||||
memcpy(dest, f->data, MIN(fraglen, maxlen));
|
||||
dest += fraglen;
|
||||
datalen += fraglen;
|
||||
if (compression) {
|
||||
*compression &= f->compressed & 1;
|
||||
if (f->compressed != *compression) {
|
||||
WDEBUG("Inconsistent compression flags in chunk. Will reassemble anyway!");
|
||||
}
|
||||
}
|
||||
if (fraglen > maxlen) {
|
||||
WDEBUG("Data buffer too small! Reassembled %" L "u bytes.", datalen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Move window along to avoid weird issues */
|
||||
window_tick(w);
|
||||
|
||||
if (f->end == 1) {
|
||||
WDEBUG("Found end of chunk! (seqID %u, chunk len %" L "u, datalen %" L "u)", f->seqID, i, datalen);
|
||||
end = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Move position counters and expected next seqID */
|
||||
maxlen -= fraglen;
|
||||
curseq = (curseq + 1) % MAX_SEQ_ID;
|
||||
}
|
||||
|
||||
if (end == 0) {
|
||||
/* no end of chunk found because the window buffer has no more frags
|
||||
* meaning they haven't been received yet. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
WDEBUG("Reassembled %" L "u bytes from %" L "u frags; %scompressed!", datalen, i + 1, *compression ? "" : "un");
|
||||
/* Clear all used fragments */
|
||||
size_t p;
|
||||
ITER_FORWARD(w->chunk_start, WRAP(w->chunk_start + i + 1), w->length, p,
|
||||
memset(&w->frags[p], 0, sizeof(fragment));
|
||||
);
|
||||
w->chunk_start = WRAP(woffs + 1);
|
||||
w->numitems -= i + 1;
|
||||
return datalen;
|
||||
}
|
||||
|
||||
size_t
|
||||
window_sending(struct frag_buffer *w, struct timeval *nextresend)
|
||||
/* Returns number of fragments that can be sent immediately; effectively
|
||||
the same as window_get_next_sending_fragment but without doing anything.
|
||||
*nextresend is time before the next frag will be resent */
|
||||
{
|
||||
struct timeval age, now, oldest;
|
||||
fragment *f;
|
||||
size_t tosend = 0;
|
||||
|
||||
oldest.tv_sec = 0;
|
||||
oldest.tv_usec = 0;
|
||||
|
||||
if (w->numitems == 0) {
|
||||
if (nextresend) {
|
||||
nextresend->tv_sec = 0;
|
||||
nextresend->tv_usec = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
|
||||
for (size_t i = 0; i < w->windowsize; i++) {
|
||||
f = &w->frags[WRAP(w->window_start + i)];
|
||||
if (f->len == 0 || f->acks >= 1) continue;
|
||||
|
||||
if (f->retries < 1 || f->lastsent.tv_sec == 0) {
|
||||
/* Sending frag for first time
|
||||
* Note: if retries==0 then lastsent MUST also be 0 */
|
||||
tosend++;
|
||||
} else {
|
||||
/* Frag has been sent before so lastsent is a valid timestamp */
|
||||
timersub(&now, &f->lastsent, &age);
|
||||
|
||||
if (!timercmp(&age, &w->timeout, <)) {
|
||||
/* ACK timeout: Frag will be resent */
|
||||
tosend++;
|
||||
} else if (timercmp(&age, &oldest, >)) {
|
||||
/* Hasn't timed out yet and is oldest so far */
|
||||
oldest = age;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nextresend) {
|
||||
/* nextresend = time before oldest fragment (not being sent now)
|
||||
* will be re-sent = timeout - age */
|
||||
timersub(&w->timeout, &oldest, nextresend);
|
||||
}
|
||||
|
||||
return tosend;
|
||||
}
|
||||
|
||||
/* Returns next fragment to be sent or NULL if nothing (SEND)
|
||||
* This also handles packet resends, timeouts etc. */
|
||||
fragment *
|
||||
window_get_next_sending_fragment(struct frag_buffer *w, int *other_ack)
|
||||
{
|
||||
struct timeval age, now;
|
||||
fragment *f = NULL;
|
||||
|
||||
if (*other_ack >= MAX_SEQ_ID || *other_ack < 0)
|
||||
*other_ack = -1;
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
|
||||
for (size_t i = 0; i < w->windowsize; i++) {
|
||||
f = &w->frags[WRAP(w->window_start + i)];
|
||||
if (f->acks >= 1 || f->len == 0) continue;
|
||||
|
||||
timersub(&now, &f->lastsent, &age);
|
||||
|
||||
if (f->retries >= 1 && !timercmp(&age, &w->timeout, <)) {
|
||||
/* Resending fragment due to ACK timeout */
|
||||
WDEBUG("Retrying frag %u (%ld ms old/timeout %ld ms), retries: %u/total %u",
|
||||
f->seqID, timeval_to_ms(&age), timeval_to_ms(&w->timeout), f->retries, w->resends);
|
||||
w->resends ++;
|
||||
goto found;
|
||||
} else if (f->retries == 0 && f->len > 0) {
|
||||
/* Fragment not sent */
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
if (f)
|
||||
WDEBUG("Not sending any fragments (last frag checked: retries %u, seqid %u, len %" L "u)",
|
||||
f->retries, f->seqID, f->len);
|
||||
return NULL;
|
||||
|
||||
found:
|
||||
/* store other ACK into fragment for sending; ignore any previous values.
|
||||
Don't resend ACKs because by the time we do, the other end will have
|
||||
resent the corresponding fragment so may as well not cause trouble. */
|
||||
f->ack_other = *other_ack, *other_ack = -1;
|
||||
f->start &= 1;
|
||||
f->end &= 1;
|
||||
f->retries++;
|
||||
gettimeofday(&f->lastsent, NULL);
|
||||
return f;
|
||||
}
|
||||
|
||||
/* Gets the seqid of next fragment to be ACK'd (RECV) */
|
||||
int
|
||||
window_get_next_ack(struct frag_buffer *w)
|
||||
{
|
||||
fragment *f;
|
||||
for (size_t i = 0; i < w->windowsize; i++) {
|
||||
f = &w->frags[WRAP(w->window_start + i)];
|
||||
if (f->len > 0 && f->acks <= 0) {
|
||||
f->acks = 1;
|
||||
return f->seqID;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Sets the fragment with seqid to be ACK'd (SEND) */
|
||||
void
|
||||
window_ack(struct frag_buffer *w, int seqid)
|
||||
{
|
||||
fragment *f;
|
||||
if (seqid < 0 || seqid > MAX_SEQ_ID) return;
|
||||
for (size_t i = 0; i < w->windowsize; i++) {
|
||||
f = &w->frags[AFTER(w, i)];
|
||||
if (f->seqID == seqid && f->len > 0) { /* ACK first non-empty frag */
|
||||
if (f->acks > 0)
|
||||
WDEBUG("DUPE ACK: %d ACKs for seqId %u", f->acks, seqid);
|
||||
f->acks ++;
|
||||
WDEBUG(" ACK frag seq %u, ACKs %u, len %" L "u, s %u e %u", f->seqID, f->acks, f->len, f->start, f->end);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Function to be called after all other processing has been done
|
||||
* when anything happens (moves window etc) (SEND/RECV) */
|
||||
void
|
||||
window_tick(struct frag_buffer *w)
|
||||
{
|
||||
for (size_t i = 0; i < w->windowsize; i++) {
|
||||
if (w->frags[w->window_start].acks >= 1) {
|
||||
#ifdef DEBUG_BUILD
|
||||
unsigned old_start_id = w->start_seq_id;
|
||||
#endif
|
||||
w->start_seq_id = (w->start_seq_id + 1) % MAX_SEQ_ID;
|
||||
WDEBUG("moving window forwards; %" L "u-%" L "u (%u) to %" L "u-%" L "u (%u) len=%" L "u",
|
||||
w->window_start, w->window_end, old_start_id, AFTER(w, 1),
|
||||
AFTER(w, w->windowsize + 1), w->start_seq_id, w->length);
|
||||
if (w->direction == WINDOW_SENDING) {
|
||||
WDEBUG("Clearing old fragments in SENDING window.");
|
||||
w->numitems --; /* Clear old fragments */
|
||||
memset(&w->frags[w->window_start], 0, sizeof(fragment));
|
||||
}
|
||||
w->window_start = AFTER(w, 1);
|
||||
|
||||
w->window_end = AFTER(w, w->windowsize);
|
||||
} else break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Splits data into fragments and adds to the end of the window buffer for sending
|
||||
* All fragment meta-data is created here (SEND) */
|
||||
int
|
||||
window_add_outgoing_data(struct frag_buffer *w, uint8_t *data, size_t len, int compressed)
|
||||
{
|
||||
// Split data into thingies of <= fragsize
|
||||
size_t n = ((len - 1) / w->maxfraglen) + 1;
|
||||
if (!data || n == 0 || len == 0 || n > window_buffer_available(w)) {
|
||||
WDEBUG("Failed to append fragment (buffer too small!)");
|
||||
return -1;
|
||||
}
|
||||
compressed &= 1;
|
||||
size_t offset = 0;
|
||||
static fragment f;
|
||||
WDEBUG("add data len %" L "u, %" L "u frags, max fragsize %u", len, n, w->maxfraglen);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
memset(&f, 0, sizeof(f));
|
||||
f.len = MIN(len - offset, w->maxfraglen);
|
||||
memcpy(f.data, data + offset, f.len);
|
||||
f.seqID = w->cur_seq_id;
|
||||
f.start = (i == 0) ? 1 : 0;
|
||||
f.end = (i == n - 1) ? 1 : 0;
|
||||
f.compressed = compressed;
|
||||
f.ack_other = -1;
|
||||
window_append_fragment(w, &f);
|
||||
w->cur_seq_id = (w->cur_seq_id + 1) % MAX_SEQ_ID;
|
||||
WDEBUG(" fragment len %" L "u, seqID %u, s %u, end %u, dOffs %" L "u", f.len, f.seqID, f.start, f.end, offset);
|
||||
offset += f.len;
|
||||
}
|
||||
return n;
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Frekk van Blagh <frekk@frekkworks.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __WINDOW_H__
|
||||
#define __WINDOW_H__
|
||||
|
||||
/* Hard-coded sequence ID and fragment size limits
|
||||
* These should match the limitations of the protocol. */
|
||||
#define MAX_SEQ_ID 256
|
||||
#define MAX_FRAGSIZE 4096
|
||||
|
||||
/* Window function definitions. */
|
||||
#define WINDOW_SENDING 1
|
||||
#define WINDOW_RECVING 0
|
||||
|
||||
typedef struct fragment {
|
||||
size_t len; /* Length of fragment data (0 if fragment unused) */
|
||||
unsigned seqID; /* fragment sequence ID */
|
||||
int ack_other; /* other way ACK seqID (>=0) or unset (<0) */
|
||||
int compressed; /* compression flag */
|
||||
uint8_t start; /* start of chunk flag */
|
||||
uint8_t end; /* end of chunk flag */
|
||||
uint8_t data[MAX_FRAGSIZE]; /* fragment data */
|
||||
unsigned retries; /* number of times has been sent or dupes recv'd */
|
||||
struct timeval lastsent; /* timestamp of most recent send attempt */
|
||||
int acks; /* number of times packet has been ack'd */
|
||||
} fragment;
|
||||
|
||||
struct frag_buffer {
|
||||
fragment *frags; /* pointer to array of data fragments */
|
||||
unsigned windowsize; /* Max number of fragments in flight */
|
||||
unsigned maxfraglen; /* Max outgoing fragment data size */
|
||||
size_t length; /* Length of buffer */
|
||||
size_t numitems; /* number of non-empty fragments stored in buffer */
|
||||
size_t window_start; /* Start of window (index) */
|
||||
size_t window_end; /* End of window (index) */
|
||||
size_t last_write; /* Last fragment appended (index) */
|
||||
size_t chunk_start; /* Start of current chunk of fragments (index) */
|
||||
unsigned cur_seq_id; /* Next unused sequence ID */
|
||||
unsigned start_seq_id; /* Start of window sequence ID */
|
||||
unsigned resends; /* number of fragments resent or number of dupes received */
|
||||
unsigned oos; /* Number of out-of-sequence fragments received */
|
||||
int direction; /* WINDOW_SENDING or WINDOW_RECVING */
|
||||
struct timeval timeout; /* Fragment ACK timeout before resend */
|
||||
};
|
||||
|
||||
extern int window_debug;
|
||||
|
||||
/* Window debugging macro */
|
||||
#ifdef DEBUG_BUILD
|
||||
#define WDEBUG(...) if (window_debug) {\
|
||||
TIMEPRINT("[WINDOW-DEBUG] (%s:%d) ", __FILE__, __LINE__);\
|
||||
fprintf(stderr, __VA_ARGS__);\
|
||||
fprintf(stderr, "\n");\
|
||||
}
|
||||
#else
|
||||
#define WDEBUG(...)
|
||||
#endif
|
||||
|
||||
/* Gets index of fragment o fragments after window start */
|
||||
#define AFTER(w, o) ((w->window_start + o) % w->length)
|
||||
|
||||
/* Distance (going forwards) between a and b in window of length l */
|
||||
#define DISTF(l, a, b) (((a > b) ? a-b : l-a+b-1) % l)
|
||||
|
||||
/* Distance backwards between a and b in window of length l */
|
||||
#define DISTB(l, a, b) (((a < b) ? l-b+a-1 : a-b) % l)
|
||||
|
||||
/* Check if fragment index a is within window_buffer *w */
|
||||
#define INWINDOW_INDEX(w, a) ((w->window_start < w->window_end) ? \
|
||||
(a >= w->window_start && a <= w->window_end) : \
|
||||
((a >= w->window_start && a <= w->length - 1) || \
|
||||
(a >= 0 && a <= w->window_end)))
|
||||
|
||||
/* Check if sequence ID a is within sequence range start to end */
|
||||
#define INWINDOW_SEQ(start, end, a) ((start < end) ? \
|
||||
(a >= start && a <= end) : \
|
||||
((a >= start && a <= MAX_SEQ_ID - 1) || \
|
||||
(a <= end)))
|
||||
|
||||
/* Find the wrapped offset between sequence IDs start and a
|
||||
* Note: the maximum possible offset is MAX_SEQ_ID - 1 */
|
||||
#define SEQ_OFFSET(start, a) ((a >= start) ? a - start : MAX_SEQ_ID - start + a)
|
||||
|
||||
/* Wrap index x to a value within the window buffer length */
|
||||
#define WRAP(x) ((x) % w->length)
|
||||
|
||||
/* Perform wrapped iteration of statement with pos = (begin to end) wrapped at
|
||||
* max, executing statement f for every value of pos. */
|
||||
#define ITER_FORWARD(begin, end, max, pos, f) { \
|
||||
if (end >= begin) \
|
||||
for (pos = begin; pos < end && pos < max; pos++) {f}\
|
||||
else {\
|
||||
for (pos = begin; pos < max; pos++) {f}\
|
||||
for (pos = 0; pos < end && pos < max; pos++) {f}\
|
||||
}\
|
||||
}
|
||||
|
||||
/* Window buffer creation and housekeeping */
|
||||
struct frag_buffer *window_buffer_init(size_t length, unsigned windowsize, unsigned fragsize, int dir);
|
||||
void window_buffer_resize(struct frag_buffer *w, size_t length);
|
||||
void window_buffer_destroy(struct frag_buffer *w);
|
||||
|
||||
/* Clears fragments and resets window stats */
|
||||
void window_buffer_clear(struct frag_buffer *w);
|
||||
|
||||
/* Resets window stats without clearing fragments */
|
||||
void window_buffer_reset(struct frag_buffer *w);
|
||||
|
||||
/* Returns number of available fragment slots (NOT BYTES) */
|
||||
size_t window_buffer_available(struct frag_buffer *w);
|
||||
|
||||
/* Places a fragment in the window after the last one */
|
||||
int window_append_fragment(struct frag_buffer *w, fragment *src);
|
||||
|
||||
/* Handles fragment received from the sending side (RECV) */
|
||||
ssize_t window_process_incoming_fragment(struct frag_buffer *w, fragment *f);
|
||||
|
||||
/* Reassembles first complete sequence of fragments into data. (RECV)
|
||||
* Returns length of data reassembled, or 0 if no data reassembled */
|
||||
size_t window_reassemble_data(struct frag_buffer *w, uint8_t *data, size_t maxlen, int *compression);
|
||||
|
||||
/* Returns number of fragments to be sent */
|
||||
size_t window_sending(struct frag_buffer *w, struct timeval *);
|
||||
|
||||
/* Returns next fragment to be sent or NULL if nothing (SEND) */
|
||||
fragment *window_get_next_sending_fragment(struct frag_buffer *w, int *other_ack);
|
||||
|
||||
/* Gets the seqid of next fragment to be ACK'd (RECV) */
|
||||
int window_get_next_ack(struct frag_buffer *w);
|
||||
|
||||
/* Sets the fragment with seqid to be ACK'd (SEND) */
|
||||
void window_ack(struct frag_buffer *w, int seqid);
|
||||
|
||||
/* To be called after all other processing has been done
|
||||
* when anything happens (moves window etc) (SEND/RECV) */
|
||||
void window_tick(struct frag_buffer *w);
|
||||
|
||||
/* Splits data into fragments and adds to the end of the window buffer for sending
|
||||
* All fragment meta-data is created here (SEND) */
|
||||
int window_add_outgoing_data(struct frag_buffer *w, uint8_t *data, size_t len, int compressed);
|
||||
|
||||
#endif /* __WINDOW_H__ */
|
|
@ -20,11 +20,13 @@
|
|||
|
||||
typedef unsigned int in_addr_t;
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <windns.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <iphlpapi.h>
|
||||
#include <time.h>
|
||||
|
||||
/* Missing from the mingw headers */
|
||||
#ifndef DNS_TYPE_SRV
|
||||
|
@ -74,7 +76,7 @@ typedef struct {
|
|||
} HEADER;
|
||||
|
||||
struct ip
|
||||
{
|
||||
{
|
||||
unsigned int ip_hl:4; /* header length */
|
||||
unsigned int ip_v:4; /* version */
|
||||
u_char ip_tos; /* type of service */
|
||||
|
@ -89,7 +91,79 @@ struct ip
|
|||
u_char ip_p; /* protocol */
|
||||
u_short ip_sum; /* checksum */
|
||||
struct in_addr ip_src, ip_dst; /* source and dest address */
|
||||
};
|
||||
};
|
||||
|
||||
/* Convenience macros for operations on timevals.
|
||||
NOTE: `timercmp' does not work for >= or <=. */
|
||||
#define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)
|
||||
|
||||
#ifndef timerclear
|
||||
#define timerclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0)
|
||||
#endif
|
||||
#ifndef timercmp
|
||||
#define timercmp(a, b, CMP) \
|
||||
(((a)->tv_sec == (b)->tv_sec) ? \
|
||||
((a)->tv_usec CMP (b)->tv_usec) : \
|
||||
((a)->tv_sec CMP (b)->tv_sec))
|
||||
#endif
|
||||
#define timeradd(a, b, result) \
|
||||
do { \
|
||||
(result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
|
||||
(result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
|
||||
if ((result)->tv_usec >= 1000000) \
|
||||
{ \
|
||||
++(result)->tv_sec; \
|
||||
(result)->tv_usec -= 1000000; \
|
||||
} \
|
||||
} while (0)
|
||||
#define timersub(a, b, result) \
|
||||
do { \
|
||||
(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
|
||||
(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
|
||||
if ((result)->tv_usec < 0) { \
|
||||
--(result)->tv_sec; \
|
||||
(result)->tv_usec += 1000000; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#if 0
|
||||
inline int
|
||||
gettimeofday(struct timeval *tv, struct timezone *tz)
|
||||
{
|
||||
FILETIME ft;
|
||||
unsigned __int64 tmpres = 0;
|
||||
int tzflag = 0;
|
||||
|
||||
if (NULL != tv)
|
||||
{
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
|
||||
tmpres |= ft.dwHighDateTime;
|
||||
tmpres <<= 32;
|
||||
tmpres |= ft.dwLowDateTime;
|
||||
|
||||
/* convert into microseconds */
|
||||
tmpres /= 10;
|
||||
/* converting file time to unix epoch */
|
||||
tmpres -= DELTA_EPOCH_IN_MICROSECS;
|
||||
tv->tv_sec = (long) (tmpres / 1000000UL);
|
||||
tv->tv_usec = (long) (tmpres % 1000000UL);
|
||||
}
|
||||
|
||||
if (NULL != tz)
|
||||
{
|
||||
if (!tzflag)
|
||||
{
|
||||
_tzset();
|
||||
tzflag++;
|
||||
}
|
||||
tz->tz_minuteswest = _timezone / 60;
|
||||
tz->tz_dsttime = _daylight;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
DWORD WINAPI tun_reader(LPVOID arg);
|
||||
struct tun_data {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
TEST = test
|
||||
OBJS = test.o base32.o base64.o common.o read.o dns.o encoding.o login.o user.o fw_query.o
|
||||
SRCOBJS = ../src/base32.o ../src/base64.o ../src/common.o ../src/read.o ../src/dns.o ../src/encoding.o ../src/login.o ../src/md5.o ../src/user.o ../src/fw_query.o
|
||||
OBJS = test.o base32.o base64.o common.o read.o dns.o encoding.o login.o user.o fw_query.o window.o
|
||||
SRCOBJS = ../src/base32.o ../src/base64.o ../src/window.o ../src/common.o ../src/read.o ../src/dns.o ../src/encoding.o ../src/login.o ../src/md5.o ../src/user.o ../src/fw_query.o ../src/util.o
|
||||
|
||||
OS = `uname | tr "a-z" "A-Z"`
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ START_TEST(test_base32_encode)
|
|||
b32 = get_base32_encoder();
|
||||
|
||||
len = sizeof(buf);
|
||||
val = b32->encode(buf, &len, testpairs[_i].a, strlen(testpairs[_i].a));
|
||||
val = b32->encode((uint8_t *)buf, &len, (uint8_t *)testpairs[_i].a, strlen(testpairs[_i].a));
|
||||
|
||||
fail_unless(val == strlen(testpairs[_i].b));
|
||||
fail_unless(strcmp(buf, testpairs[_i].b) == 0,
|
||||
|
@ -67,7 +67,7 @@ START_TEST(test_base32_decode)
|
|||
b32 = get_base32_encoder();
|
||||
|
||||
len = sizeof(buf);
|
||||
val = b32->decode(buf, &len, testpairs[_i].b, strlen(testpairs[_i].b));
|
||||
val = b32->decode((uint8_t *)buf, &len, (uint8_t *)testpairs[_i].b, strlen(testpairs[_i].b));
|
||||
|
||||
fail_unless(val == strlen(testpairs[_i].a));
|
||||
fail_unless(strcmp(buf, testpairs[_i].a) == 0,
|
||||
|
@ -110,7 +110,7 @@ START_TEST(test_base32_blksize)
|
|||
}
|
||||
rawbuf[i] = 0;
|
||||
|
||||
val = b32->encode(encbuf, &enclen, rawbuf, rawlen);
|
||||
val = b32->encode((uint8_t *)encbuf, &enclen, (uint8_t *)rawbuf, rawlen);
|
||||
|
||||
fail_unless(rawlen == 5, "raw length was %d not 5", rawlen);
|
||||
fail_unless(enclen == 5, "encoded %d bytes, not 5", enclen);
|
||||
|
@ -119,7 +119,7 @@ START_TEST(test_base32_blksize)
|
|||
memset(rawbuf, 0, rawlen + 16);
|
||||
|
||||
enclen = val;
|
||||
val = b32->decode(rawbuf, &rawlen, encbuf, enclen);
|
||||
val = b32->decode((uint8_t *)rawbuf, &rawlen, (uint8_t *)encbuf, enclen);
|
||||
|
||||
fail_unless(rawlen == 5, "raw length was %d not 5", rawlen);
|
||||
fail_unless(val == 5, "val was not 5 but %d", val);
|
||||
|
|
|
@ -75,7 +75,7 @@ START_TEST(test_base64_encode)
|
|||
b64 = get_base64_encoder();
|
||||
|
||||
len = sizeof(buf);
|
||||
val = b64->encode(buf, &len, testpairs[_i].a, strlen(testpairs[_i].a));
|
||||
val = b64->encode((uint8_t *)buf, &len, (uint8_t *)testpairs[_i].a, strlen(testpairs[_i].a));
|
||||
|
||||
fail_unless(val == strlen(testpairs[_i].b));
|
||||
fail_unless(strcmp(buf, testpairs[_i].b) == 0,
|
||||
|
@ -93,7 +93,7 @@ START_TEST(test_base64_decode)
|
|||
b64 = get_base64_encoder();
|
||||
|
||||
len = sizeof(buf);
|
||||
val = b64->decode(buf, &len, testpairs[_i].b, strlen(testpairs[_i].b));
|
||||
val = b64->decode((uint8_t *)buf, &len, (uint8_t *)testpairs[_i].b, strlen(testpairs[_i].b));
|
||||
|
||||
fail_unless(val == strlen(testpairs[_i].a));
|
||||
fail_unless(strcmp(buf, testpairs[_i].a) == 0,
|
||||
|
@ -124,7 +124,7 @@ START_TEST(test_base64_blksize)
|
|||
}
|
||||
rawbuf[i] = 0;
|
||||
|
||||
val = b64->encode(encbuf, &enclen, rawbuf, rawlen);
|
||||
val = b64->encode((uint8_t *)encbuf, &enclen, (uint8_t *)rawbuf, rawlen);
|
||||
|
||||
fail_unless(rawlen == 3, "raw length was %d not 3", rawlen);
|
||||
fail_unless(enclen == 3, "encoded %d bytes, not 3", enclen);
|
||||
|
@ -133,7 +133,7 @@ START_TEST(test_base64_blksize)
|
|||
memset(rawbuf, 0, rawlen + 16);
|
||||
|
||||
enclen = val;
|
||||
val = b64->decode(rawbuf, &rawlen, encbuf, enclen);
|
||||
val = b64->decode((uint8_t *)rawbuf, &rawlen, (uint8_t *)encbuf, enclen);
|
||||
|
||||
fail_unless(rawlen == 3, "raw length was %d not 3", rawlen);
|
||||
fail_unless(val == 3);
|
||||
|
|
|
@ -86,7 +86,7 @@ START_TEST(test_encode_query)
|
|||
enc = get_base32_encoder();
|
||||
|
||||
*d++ = 'A';
|
||||
enc->encode(d, &enclen, innerData, strlen(innerData));
|
||||
enc->encode((uint8_t *)d, &enclen, (uint8_t *)innerData, strlen(innerData));
|
||||
d = resolv + strlen(resolv);
|
||||
if (*d != '.') {
|
||||
*d++ = '.';
|
||||
|
@ -123,7 +123,7 @@ START_TEST(test_decode_query)
|
|||
dns_decode(buf, sizeof(buf), &q, QR_QUERY, query_packet, len);
|
||||
domain = strstr(q.name, topdomain);
|
||||
len = sizeof(buf);
|
||||
unpack_data(buf, len, &(q.name[1]), (int) (domain - q.name) - 1, enc);
|
||||
unpack_data((uint8_t *)buf, len, (uint8_t *)(q.name + 1), (int) (domain - q.name) - 1, enc);
|
||||
|
||||
fail_unless(strncmp(buf, innerData, strlen(innerData)) == 0, "Did not extract expected host: '%s'", buf);
|
||||
fail_unless(strlen(buf) == strlen(innerData), "Bad host length: %d, expected %d: '%s'", strlen(buf), strlen(innerData), buf);
|
||||
|
|
|
@ -32,13 +32,13 @@ static struct tuple
|
|||
char *a;
|
||||
char *b;
|
||||
} dottests[] = {
|
||||
{ "aaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"aaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaa"},
|
||||
{ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."},
|
||||
{ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
|
||||
{ "abc123", "abc123" },
|
||||
{ "aaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"aaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaa"},
|
||||
{ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaa"},
|
||||
{ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaa"},
|
||||
{ "abcdefghijklmnopqrtsuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrtsuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVW.XYZ" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
|
@ -50,7 +50,7 @@ START_TEST(test_inline_dotify)
|
|||
memset(temp, 0, sizeof(temp));
|
||||
strcpy(temp, dottests[_i].a);
|
||||
b = temp;
|
||||
inline_dotify(b, sizeof(temp));
|
||||
inline_dotify((uint8_t *)b, sizeof(temp));
|
||||
|
||||
fail_unless(strcmp(dottests[_i].b, temp) == 0,
|
||||
"'%s' != '%s'", temp, dottests[_i].b);
|
||||
|
@ -65,7 +65,7 @@ START_TEST(test_inline_undotify)
|
|||
memset(temp, 0, sizeof(temp));
|
||||
strcpy(temp, dottests[_i].b);
|
||||
b = temp;
|
||||
inline_undotify(b, sizeof(temp));
|
||||
inline_undotify((uint8_t *)b, sizeof(temp));
|
||||
|
||||
fail_unless(strcmp(dottests[_i].a, temp) == 0,
|
||||
"'%s' != '%s'", temp, dottests[_i].a);
|
||||
|
@ -76,7 +76,7 @@ START_TEST(test_build_hostname)
|
|||
{
|
||||
char data[256];
|
||||
char buf[1024];
|
||||
char *topdomain = "a.c";
|
||||
char *topdomain = "iodine.test.example.com";
|
||||
int buflen;
|
||||
int i;
|
||||
|
||||
|
@ -86,11 +86,16 @@ START_TEST(test_build_hostname)
|
|||
|
||||
buflen = sizeof(buf);
|
||||
|
||||
for (int j = 0; j < 10; j++) /* dummy header length */
|
||||
for (i = 1; i < sizeof(data); i++) {
|
||||
int len = build_hostname(buf, buflen, data, i, topdomain, get_base32_encoder(), sizeof(buf));
|
||||
buf[j] = j + 'A';
|
||||
int len = build_hostname((uint8_t *)buf, buflen, (uint8_t *)data, i, topdomain, get_base32_encoder(), buflen, j);
|
||||
|
||||
fail_if(len > i);
|
||||
fail_if((strstr(buf, ".") - buf) > 63, "First label in encoded hostname >63 bytes!");
|
||||
fail_if(strstr(buf, ".."), "Found double dots when encoding data len %d! buf: %s", i, buf);
|
||||
fail_if(!strstr(buf, topdomain), "Didn't find topdomain in hostname!");
|
||||
fail_if(buf[j] == j, "Header has been changed during encode hostname!");
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
|
||||
#include "test.h"
|
||||
|
||||
int check_ip = 0;
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
|
@ -60,6 +62,9 @@ main()
|
|||
test = test_fw_query_create_tests();
|
||||
suite_add_tcase(iodine, test);
|
||||
|
||||
test = test_window_create_tests();
|
||||
suite_add_tcase(iodine, test);
|
||||
|
||||
runner = srunner_create(iodine);
|
||||
srunner_run_all(runner, CK_NORMAL);
|
||||
failed = srunner_ntests_failed(runner);
|
||||
|
|
|
@ -27,6 +27,7 @@ TCase *test_read_create_tests();
|
|||
TCase *test_login_create_tests();
|
||||
TCase *test_user_create_tests();
|
||||
TCase *test_fw_query_create_tests();
|
||||
TCase *test_window_create_tests();
|
||||
|
||||
char *va_str(const char *, ...);
|
||||
|
||||
|
|
20
tests/user.c
20
tests/user.c
|
@ -28,6 +28,8 @@
|
|||
#include "user.h"
|
||||
#include "test.h"
|
||||
|
||||
int debug = 0;
|
||||
|
||||
START_TEST(test_init_users)
|
||||
{
|
||||
in_addr_t ip;
|
||||
|
@ -39,11 +41,9 @@ START_TEST(test_init_users)
|
|||
count = init_users(ip, 27);
|
||||
for (i = 0; i < count; i++) {
|
||||
fail_unless(users[i].id == i);
|
||||
fail_unless(users[i].q.id == 0);
|
||||
fail_unless(users[i].inpacket.len == 0);
|
||||
fail_unless(users[i].outpacket.len == 0);
|
||||
snprintf(givenip, sizeof(givenip), "127.0.0.%d", i + 2);
|
||||
fail_unless(users[i].tun_ip == inet_addr(givenip));
|
||||
fail_if(user_active(i), "user_active true for new users");
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
@ -87,25 +87,17 @@ START_TEST(test_all_users_waiting_to_send)
|
|||
ip = inet_addr("127.0.0.1");
|
||||
init_users(ip, 27);
|
||||
|
||||
fail_unless(all_users_waiting_to_send() == 1);
|
||||
fail_unless(all_users_waiting_to_send() == 0, "empty user list all waiting to send");
|
||||
|
||||
users[0].conn = CONN_DNS_NULL;
|
||||
users[0].active = 1;
|
||||
|
||||
fail_unless(all_users_waiting_to_send() == 1);
|
||||
fail_unless(all_users_waiting_to_send() == 0, "single user with empty buffer waiting to send");
|
||||
|
||||
users[0].last_pkt = time(NULL);
|
||||
users[0].outpacket.len = 0;
|
||||
|
||||
fail_unless(all_users_waiting_to_send() == 0);
|
||||
fail_unless(all_users_waiting_to_send() == 0, "single active user with empty buffer waiting to send");
|
||||
|
||||
#ifdef OUTPACKETQ_LEN
|
||||
users[0].outpacketq_filled = 1;
|
||||
#else
|
||||
users[0].outpacket.len = 44;
|
||||
#endif
|
||||
|
||||
fail_unless(all_users_waiting_to_send() == 1);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Frekk van Blagh <frekk@frekkworks.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#include <check.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <err.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "window.h"
|
||||
#include "test.h"
|
||||
|
||||
struct frag_buffer *in, *out;
|
||||
char origdata[1000] = "";
|
||||
|
||||
START_TEST(test_window_everything)
|
||||
{
|
||||
in = window_buffer_init(1000, 10, 5, WINDOW_RECVING);
|
||||
out = window_buffer_init(1000, 10, 5, WINDOW_SENDING);
|
||||
for (unsigned i = 0; i < 20; i++) {
|
||||
char c[100] = "0ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!@#$%^&*()-=`';\\|][{}/?~";
|
||||
c[0] += i;
|
||||
window_add_outgoing_data(out, (uint8_t *)c, i + 1, 0);
|
||||
strncat(origdata, c, i + 1);
|
||||
//warnx(" OUT: %u available, current seq %u, new seq %u\n", window_buffer_available(out), i, out->cur_seq_id);
|
||||
}
|
||||
// printf("Original data: '%s' (%lu)\n", origdata, strlen(origdata));
|
||||
// warnx("Added data, fragmented into %lu frags, next seq %u.", out->numitems, out->cur_seq_id);
|
||||
// "send" data
|
||||
int a = -1;
|
||||
for (; out->numitems > 0;) {
|
||||
fragment *f = window_get_next_sending_fragment(out, &a);
|
||||
if (!f) {
|
||||
// warnx("Nothing to send.");
|
||||
continue;
|
||||
}
|
||||
fail_if(window_process_incoming_fragment(in, f) < 0, "Dropped fragment!");
|
||||
// warnx("Received fragment with seqid %u, remaining space %lu.", f->seqID, window_buffer_available(in));
|
||||
window_tick(in);
|
||||
window_ack(out, f->seqID);
|
||||
window_tick(out);
|
||||
fail_if(out->start_seq_id != in->start_seq_id, "in/out windows have different start IDs!");
|
||||
}
|
||||
// warnx("Added %lu fragments, reassembling into data.", in->numitems);
|
||||
uint8_t data[100];
|
||||
uint8_t newdata[1000];
|
||||
memset(newdata, 0, 1000);
|
||||
unsigned i;
|
||||
int c;
|
||||
for (i = 0; i < 50; i++) {
|
||||
memset(data, 0, 100);
|
||||
size_t len = window_reassemble_data(in, data, 100, &c);
|
||||
fail_if(c != 0, "Compression flag weird");
|
||||
// printf("Reassembled %lu bytes, num frags %lu: '", len, in->numitems);
|
||||
// for (unsigned i = 0; i < len; i++) {
|
||||
// printf("%c", data[i]);
|
||||
// }
|
||||
// printf("'\n");
|
||||
strncat((char *)newdata, (char *)data, len);
|
||||
if (in->numitems <= 0) break;
|
||||
}
|
||||
// printf("New data: '%s' (%lu)\n", newdata, strlen((char *)newdata));
|
||||
fail_if(strcmp((char *)newdata, origdata), "Reassembled data didn't match original data.");
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
||||
TCase *
|
||||
test_window_create_tests()
|
||||
{
|
||||
TCase *tc;
|
||||
|
||||
tc = tcase_create("Windowing");
|
||||
tcase_add_test(tc, test_window_everything);
|
||||
|
||||
return tc;
|
||||
}
|
Loading…
Reference in New Issue