Wednesday, December 30, 2009

SoDS; Care and Feeding of,

SoDS provides a covert method of bypassing firewall restrictions, obscuring traffic analysis and hiding your data by tunnelling it over DNS. SoDS was intended to be small, resource efficient, reasonably portable with few dependencies (at least on Unix) as well as taking basic precautions against misuse.

Test Ride

To get a feel for sods, its simple to try out:
git clone git://
cd sods/sods
cd ../sdt

Depending on your OS, you might need to adjust the Makefiles.

To test sods, you'll need to have an SSH server available somewhere and root privs on your test host. Start up the socket server:
$ sudo ./sods -d /tmp -L localhost:22 -vvv

In another window, open an SSH session:
$ ssh -o ProxyCommand="./sdt -r"

Real World SoDS

To use sods, you'll need a few things:
  1. A publicly addressable IP
  2. Something on which to run sods. The server doesn't need to be dedicated or high powered.
  3. A registered domain name
  4. A DNS server to host your domain

Well, that may seem to be waaaayyy too much bother, but it might not be as much work as it seems.

Publicly Available IP Address

You need an IP address that is reachable from the Internet or control of a device that can forward packets to your sods server.

The IP addresses the ISP assigns you tend to fall into 3 groups:
  1. the IP address is static and never (or rarely) changes
  2. the IP address changes infrequently, but the reverse lookup for the address is static
  3. the IP address and, consequently, the reverse address, changes frequently

You can work with any of these.

The IP address must be able to receive UDP packets on port 53 and send packets from port 53. If your ISP blocks port 53 (DNS), then you'll have to look around for another ISP, use a hosted service or maybe piggyback off of a friend.

A SoDS Server

You'll need sods to be running any time you might need it. SoDS doesn't require much in the way of resources, so I suggest using a low powered server that you can leave running all the time. The SoDS server might be the same device that you use for your gateway.

I use a Linksys WRT54gl as my home router and sods server. But any small, lower power device will do: I've also used a Linksys NSLU2, and, more recently, a Sheeva Plug.

A Registered Domain Name

If you're even thinking about setting this up, you probably already own a domain or two.

Another DNS Server

Finally, you will need another DNS server to delegate your domain name. This could be a DNS server you run on another IP address, a service provided by your registrar or one of the many free DNS services on the internet (there are a bunch, any of them should work, google for them).

Assuming your domain name is with an IP address of, here is what you would have to set up.
  • If you have a static IP address (or one that doesn't change too frequently to be inconvenient), delegate a name server for a subdomain (in this example, "a"):    IN  A
    a               IN  NS

  • Some ISP's will change your IP, but maintain a consistent reverse DNS lookup for your IP address (something like a DHCP name). You can use this by setting a CNAME.

    For example, if your IP address always resolves to:

    You could set up your DNS entry as follows:  IN  CNAME

  • Finally, if your ISP gives you a dynamic IP address, you can use one of the dynamic DNS services. Many of these services are free and will provide a consistent reverse DNS entry that you can exploit as with the previous example.

Set Up Your SoDS Server

Set up your sods server and point it to whatever SSH or tcp servers you're interested in. These servers don't have to be publicly exposed; they could be running somewhere behind your firewall.

SoDS is configured from the command line, but its easy to wrap it in a script for system startup. Here is an example for OpenWRT:
OPTIONS="-D -L ssh.server1:22 -L ssh.server2:22 -L"

#!/bin/sh /etc/rc.common
# Copyright (C) 2006


start() {
include /lib/network
config_load /var/state/network
config_get ipaddr wan ipaddr

[ -f $DEFAULT ] && . $DEFAULT
[ ! -d $CHROOT_D ] && mkdir -p $CHROOT_D

$BIN $OPTIONS -i $ipaddr $DOMAIN

stop() {
killall sods

Accessing Your DNS Tunnel

So next time you are enjoying a venti americano in your local coffee shop, you may think to pause from chatting with the cute barista to fire up your laptop or other mobile device and login to IRC. We're geeks, it happens.
$ ssh -o ProxyCommand="./sdt"

Since UDP is innately unreliable, for best results, wrap the ssh command in a script that reconnects on failure. On the server side, if you are using ssh, running everything within a GNU screen session provides seamless access to your console, in case your connection drops. See the example scripts for more details, but basically, all you need to do is use a named GNU screen session on your ssh server:
$ while [ "1" ]; do ssh -t -C -o ProxyCommand="./sdt" "screen -drR sshdns"; done

SoDS Client

  • sdt will try not to overwhelm the local DNS server and will back off. Some DNS servers throttle chatty clients.

    To add some reliability to the protocol, both the client and server track sent packets and will re-send if they are lost.
  • If you are having problems connecting, run with multiple "-v" switches to see what's going on. It'll fill your screen with debug messages but will eventually stop. To control the number of debug messages displayed, use the "-V" switch (it defaults to 100).
  • You can bounce your connection off of other DNS servers, beside your local DNS server, if they support recursion by using the "-r" flag. To see which servers sdt knows about, run:
    sdt -v -h

    To use a particular recursive server, choose it by name:
    sdt -r google

    To pick a random server, run:
    sdt -r random
  • sods supports tunnelling using TXT, CNAME and NULL records. The default is TXT records. I've found that some ISP's re-write TXT records and many DNS servers in the wild do not support NULL records at all. Using CNAME's seems to work with most networks.

    NULL records have the lowest encoding overhead and the specification allows for a large packet size, reducing overhead further. Since sods does not support TCP, large packets aren't supported.
  • If you are on a mobile device, you probably want to conserve your battery. sdt polls for data at regular intervals by sending a request to the local DNS server to flush any pending data from your SoDS server.

    You can control this polling (without much effect on latency) by using the "-b" option. Every time data is received polling ramps up and slowly decays as no new data is recieved.

    If you happen to be on linux, powertop is a great tool for fine tuning the polling intervals.
  • Files can be transferred securely over DNS by using sftp, see the "sdftp" script in the examples. If you're brave, you can also run a fuse filesystem using sshfs.


  1. Hi!

    First, thanks for this. Looks amazing.

    I was using ozyman until I found this and I want to change...

    I want to use it with ubuntu and also in my WRT54gl.

    To start, I'm trying to compile it in ubuntu.
    Sods compiles OK but when I try to compile sdt I get:

    $ make
    gcc -DHAVE_ERR -DHAVE_SSL -g -Wall -lcrypto -o sdt sdt.c sdt_dns.c sdt_err.c sdt_rand.c base32.c base64.c /usr/lib/libresolv.a
    sdt_rand.c:25:26: error: openssl/rand.h: No such file or directory
    sdt_rand.c:26:25: error: openssl/err.h: No such file or directory
    sdt_rand.c: In function ‘sdt_rand’:
    sdt_rand.c:54: warning: implicit declaration of function ‘RAND_pseudo_bytes’
    sdt_rand.c:55: warning: implicit declaration of function ‘ERR_error_string’
    sdt_rand.c:55: warning: implicit declaration of function ‘ERR_get_error’
    make: *** [all] Error 1

    I have openssl installed. Do I need any package?

    How can I compile it for wrt54gl?

    Sorry for disturbing you.

    Great work.

    Thank you very much

  2. Hey Diego!

    Yeah, sorry about that, the configure script isn't very smart. You can either install the openssl dev package (apt-get install libssl-dev) or just remove the "-DHAVE_SSL" and "-lcrypto" in the Makefile (SSL isn't a hard requirement for sdt).

    To compiles sods for the WRT54GL, you'll need a cross compiler. I used the OpenWRT SDK:

    It's sort of a pain to set up, so I put a compiled version of sods on github:

    It's compiled for Kamikaze, hopefully it will work with newer versions. If you have to compile your own, it's easier to do it by hand, rather than using the Makefile:

    mipsel-linux-gcc -g -Wall -DHAVE_SEND -o sods sods.c sods_handler.c sods_dns.c sods_io.c sods_q.c sods_sock.c sods_priv.c base32.c base64.c

    mipsel-linux-strip sods

    Hope that helps. If you have any questions or run into problems, feel free to post here or send an email (my email is on my github page).

  3. Hi!

    Thank you very much for answering.

    I've been able to compile it on Ubuntu and Debian and it works. By the way, it's way much faster than ozymandns. Great work!

    Now, I'll try to use it on my wrt54gl and even on android. I'll let you know.

    Thank you very much.

  4. hello!

    i'm trying to run your compiled version ( on my router with tomato 1.28 firmware, but i only get this:

    # ./sods 123
    User defined signal 1

    do you have an idea of what is wrong? :S

    and thanks a lot for the program, i've been using it successfully at overpriced hotspots for quite some time now :)

  5. Try this version:

    It's statically linked. If it doesn't work, you'll probably need to recompile sods under Tomato.

    Very happy to hear sods has been useful for you!

  6. thank you for your quick reply! unfortunately i still get the same error (User defined signal 1). im pretty new to compiling (ive only been using the automated make command so far), could you give me some suggestions as to how to compile it for tomato?

  7. If you clone the tomato git repository, there's a pre-compiled toolchain in tools/bcrm. Copy the toolchain to /opt/bcrm and run:

    mipsel-linux-gcc -DHAVE_SEND -o sods sods.c sods_handler.c sods_dns.c sods_io.c sods_q.c sods_sock.c sods_priv.c base32.c base64.c -lresolv

    (Doesn't work with uclibc gcc yet, I should fix that some day).

    Looked simple, so I compiled a version for tomato and put it here:

    I don't have tomato installed so let me know if you run into any problems!

  8. thanks for compiling it! sadly, it still won't work :(, this happens:

    # sh sods-mips-tomato-1.27
    sods-mips-tomato-1.27: line 1: syntax error: unexpected "("

  9. It's a binary, not a shell script. Try:

    chmod +x sods

  10. sorry for constantly bothering you... something very strange happens, file not found?! :S

    # ls
    # chmod +x sods-mips-tomato-1.27
    # ./sods-mips-tomato-1.27
    -sh: ./sods-mips-tomato-1.27: not found

  11. And another attempt :) This version is statically linked:

    Thanks for you patience doing all these tests!

  12. huge step forward, it runs now! :D however, i get this error when trying to start it:

    # ./sods -vvvv -d /tmp -L
    Forwarded sessions = 1
    Forward #0:
    sods: bind(ss->s, (struct sockaddr *)&ss->local, sizeof(ss->local)): Address already in use

  13. Awesome. About the bind error: you probably have another dns server (like dnsmasq) listening on port 53 on all interfaces. dnsmasq only needs to be listening on the LAN side and sods only needs to listen on the WAN side.

    I start dnsmasq with the --bind-interfaces option and then start sods with -i $external_ip_address

  14. you are right, i had dnsmasq running... now i added these 2 lines to its conf file:


    the bind error seems to be gone now, but now this happens:

    # ./sods -vvvv -d /tmp -L -i
    Forwarded sessions = 1
    Forward #0:
    sods: user does not exist: nobody
    sods: Could not drop privs

  15. Just add a sods (or nobody) user to /etc/passwd and a sods group to /etc/group. Also make sure the chroot directory exists (by default /var/chroot/sods). sods changes to an unprivileged user after startup.

    Then start sods with the name/group. Something like:

    ./sods -u sods -g sods

    with a /etc/passwd like:

    sods:*:4321:4321:sods user:/:/bin/false

    And for /etc/group something like:


  16. i'm sorry, i forgot to mention before that user nobody is already added:

    # cat /etc/passwd
    # cat /etc/group
    # ./sods -vvvv -u nobody -g nobody -d /tmp -L -i
    Forwarded sessions = 1
    Forward #0:
    sods: user does not exist: nobody
    sods: Could not drop privs

  17. So it seems the binary needs to be dynamically linked to use getuid().

    I recompiled the Tomato toolchain and made some changes to sods to get it to work with uclibc. I was able to use the result on OpenWRT.

    Here it is, hope this one works for you!

  18. # ./sods
    ./sods: can't load library ''


  19. It seems libresolv in uclibc is just a wrapper and isn't needed.

    Here are 2 versions without any other dependencies:

  20. GOT IT TO WORK :D finally, thanks so much for all the help!! :)

    if anyone else who wants to run sods on tomato is reading this, i found out you can't "forward" port 53 to the router with the webui (even though it says it's forwarded), you have to do it manually with iptables.

    also, tomato rewrites iptables every time you change any settings or reboot, so you have to put this script in the webui (administration->scripts->firewall):

    iptables -A wanin -p tcp -m tcp -d --dport 53 -j ACCEPT
    iptables -A wanin -p udp -m udp -d --dport 53 -j ACCEPT

    and of course, read the instructions above for dnsmasq - you have edit its conf file in the webui as well (advanced -> dhcp/dns)

  21. Hi,

    thanks for that piece of software! I was always searching for a soluting that a) is able to use CNAME and b) is written in C. :-))

    I managed to get it running for one service (SSH) like this:

    server: sods -L
    client: sdt -t CNAME

    What I would like to do now is run it for two services (HTTPS and SSH). If I got it right, I have to use the "-L" switch several times:

    sods -L -L

    But: how do I tell the client (sdt) which service to use?

    If I have only one service (https), is it possible to omit the "ssndns" prefix? Or is it always necessary?

    What do the client options "-D" and "-s" do? I read the client's help, but I do not understand it.

    Sorry for these dumb questions.

    Best regards, Sven

  22. Hey Sven!

    > I managed to get it running for one service (SSH) like this:
    > server: sods -L
    > client: sdt -t CNAME
    > What I would like to do now is run it for two services (HTTPS and SSH). If
    > I got it right, I have to use the "-L" switch several times:
    > sods -L -L

    Yup, that's right!

    > But: how do I tell the client (sdt) which service to use?

    Use the "-s" switch to sdt and the session number. The sessions are
    numbered starting from 0, from left to right. So in your example, to
    connect to port 22 you would use:

    sdt -s 0 -t CNAME

    And to connect to port 443:

    sdt -s 1 -t CNAME

    > If I have only one service (https), is it possible to omit the "ssndns"
    > prefix? Or is it always necessary?

    No, you can use any prefix. I just use a single character to keep the
    byte count down, something like

    > What do the client options "-D" and "-s" do? I read the client's help,
    > but I do not understand it.

    -s: chooses the session to use (a session is the destination IP/port you want
    to proxy). Session 0 is the default.

    -D: dynamic forwarding

    "-D" is experimental. It lets the client tell the server which IP
    address/port it wants to go to. I'm thinking of setting up a service on
    a virutal machine somewhere so people won't have to through the hassle
    of installing sods and all the DNS entries.

    The C version of sods doesn't support dynamic forwarding yet. The version
    (in Erlang) that does is here:

    > Sorry for these dumb questions.

    Those were great questions, thanks for taking the time to put them
    together! And let me know if you run into any other problems.

  23. Hi Michael,

    thank you for your quick and detailed reply. I shortened down "sshdns" to a single character. Using the "-s" parameter and the session numbers also work as you explained, I can now use multiple services. :-)

    Very useful piece of software, thanks a lot! ;-)

    Best regards, Sven

  24. Hi,

    I'm trying to get this to work on my phone but not quite getting it to.

    The server side is setup ok as I've run the local test and this worked.

    On the phone I get the error "res_search: unknown host" but this makes no sense (to me!) as if I ping the domain from the phone it resolves correctly.

    On the SODS server I can see some packets coming through but I don't get connected to the SSH server on the server that SODS is running on.

    I've likely made a noobie mistake but cannot work out what it is!

  25. Hey Chris!

    For some reason which I haven't quite worked out yet, sdt on android is a bit flakey, so the first thing to try is rebooting your phone (I know, lame).

    Here are some things you can try:

    1. It looks as if the end to end connection to your sods server is good since you're seeing packets. Can you run sods in debug mode (sods -vvv) and paste the output here or email it to me? Feel free to sanitize the domain names.

    2. Try specifying the IP of the dns server to rule out any problems in between (from an open network):

    sdt -r x.x.x.x

    3. And to rule out problems with Android, have you tested the tunnel on something besides the phone? What version of Android are you using?

    4. Finally, what flags are you passing to sdt and sods?


  26. Hi,

    On the SODS server I get :-
    2012-01-09 09:11:10
    2012-01-09 09:11:10 rejecting request for domain:
    2012-01-09 09:11:10
    2012-01-09 09:11:10 rejecting request for domain:

    SODS Server:
    [root@localhost sods]# ./sods -u nobody -g nobody -d /tmp -L -vvvv

    ./sdt -t CNAME -r -p 22220 -s 0 -vvvv

    I've used another IP over DNS program (not on the phone) and I found it best to set the "DNS Server" to be the actual server itself, rather than a real DNS server. When I do this with SDT I get a lot more packets through than going via google DNS but I get the same error.

    I'm using a Samsung SGS 2 with GB 2.3.5

  27. > 2012-01-09 09:11:10
    > 2012-01-09 09:11:10 rejecting request for domain:

    Domain check was triggered. You can disable it by using the magic keyword "any":

    ./sods -u nobody -g nobody -d /tmp -L any

    Adding another domain component to sdt should make the tunnel work:

    ./sdt -t CNAME -r -p 22220 -s 0 -vvvv

    (Added the subdomain "s" to

    Hopefully that will work but let me know if you run into more problems!

  28. This comment has been removed by the author.


Note: Only a member of this blog may post a comment.