Ruby Weekly is a weekly newsletter covering the latest Ruby and Rails news.

Skype-Style Firewall Busting with Ruby and UDP

By Peter Cooper / February 26, 2007

Skype and Google Talk are pretty clever in the way that they still work even if all of its users are behind firewalls (or NAT systems) that block incoming connections. The way they enable two-way connections is by using a 'firewall busting' technique. Simply, a central server does nothing but share IP addresses (and port numbers) and clients can then 'punch' holes through their firewalls and trick their firewalls and routers to route incoming packets back to them if they have certain source host and port numbers.

I was playing with the technique and have put together an example client that you can run on two separate hosts that have no open incoming ports but which demonstrates two way connection to an arbitrary port (6311 in this case) with UDP. Run this code on separate hosts and provide the hostname of the other host as a command line argument. This way, both programs will punch a hole through their own firewall and connect to each other, before randomly swapping messages back and forth.

require 'socket'

remote_host = ARGV.first

# Punches hole in firewall
punch = UDPSocket.new
punch.bind('', 6311)
punch.send('', 0, remote_host, 6311)
punch.close

# Bind for receiving
udp_in = UDPSocket.new
udp_in.bind('0.0.0.0', 6311)
puts "Binding to local port 6311"

loop do
  # Receive data or time out after 5 seconds
  if IO.select([udp_in], nil, nil, rand(4))
        data = udp_in.recvfrom(1024)
        remote_port = data[1][1]
        remote_addr = data[1][3]
        puts "Response from #{remote_addr}:#{remote_port} is #{data[0]}"
  else
        puts "Sending a little something.."
        udp_in.send(Time.now.to_s, 0, remote_host, 6311)
  end
end

If you have a cleaner way of doing this, post a comment. This is just a quick prototype, but it works fine on my test boxes.

Comments

  1. Dave says:

    Wow, that code is awesome! I can't wait to try it.

  2. Peter Cooper says:

    I just ran this on Dreamhost and a local machine and.. it works!

    This technique could, therefore, be used to send data to your own port even on shared hosts, as long as you know the IP of who's connecting so you can punch the hole.

    This could be done with the normally available HTTP technique..

  3. Alan says:

    Does this work with TCP? Can I send a "punch" UDP packet to the remote host and then try recieving TCP connections?

  4. Peter Cooper says:

    Alan: Afraid not.. at least, not in its current state. TCP punching is far less elegant than UDP punching and support for it is rather patchy. This paper explains it all.

  5. Alan says:

    But surely Skype uses TCP? I'll take a look at that paper. Nice post, regardless.

  6. Peter Cooper says:

    I won't speak on their behalf, but I think those sort of programs offer fallbacks.. so if one doesn't work, it'll try another.

    UDP is often used for streaming audio though since it doesn't matter if you lose a few packets here and there.

Other Posts to Enjoy

Twitter Mentions