I recently found myself in need of a quick-and-dirty way to forward UDP packets from one machine to another. SSH port forwarding was out, unfortunately, as the source host was Windows machine and I wasn’t about to install Cygwin or MSYS, etc. After a brief and unsuccessful search for simple tools to accomplish this, I decided to whip something up myself. The project I was working on was already using python with the Twisted networking library, so that’s what I used. I tried to make it as flexible as possible, allowing forwarding to multiple hosts and name resolution, while keeping it as simple as I could. Here’s the result:

from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor

# edit this to map local port numbers to a list of host:port destinations
forwardMap = {
    5500 : [ "192.168.110.32:5500", "10.0.1.1:5500" ],
    5499 : [ "192.168.110.32:5499" ],
    15501 : [ "localhost:5501" ]
    }

class Forward(DatagramProtocol):
    def __init__(self, targetTuples):
        self._targetTuples = targetTuples

    def datagramReceived(self, data, (host, port)):
        for targetHost, targetPort in self._targetTuples:
            self.transport.write(data, (targetHost, targetPort))

def bindListeners(resolvedMap):
    for port, targetPairs in resolvedMap.items():
        reactor.listenUDP(port, Forward(targetPairs))

def resolved(ip, lport, host, dport, rmap={}):
    rmap.setdefault(lport, []).append((ip, dport))
    cons = lambda a, b: a + b
    if (len(reduce(cons, forwardMap.values())) == len(reduce(cons, rmap.values()))):
        bindListeners(rmap)

for port, destinations in forwardMap.items():
    strPairs = [d.split(':') for d in destinations]
    map(lambda (host, dport): (reactor.resolve(host).addCallback(resolved, port, host, int(dport))), strPairs)

reactor.run()

The most current code can always be found in this gist.