3

I'm curious to see how I can create a UDP proxy server using the async module. I tried earlier today with Twisted, but wasn't making much headway. I did find some simple solutions here on stackoverflow using sockets directly, but i'm wondering if this is possible using the async lib.

For clarity, what I'm looking for is:

Step 1: client A (can be many) speaks to proxy B Step 2: proxy B sends the request along to server C. Step 3: Server C responds to proxy B Sep 4: Proxy B responds to client A.

Here's what I have so far:

import asyncio

class EchoServerProtocol(asyncio.DatagramProtocol):
    def connection_made(self, transport):
        self.transport = transport

    def datagram_received(self, data, addr, args=None):
        message = data
        server_addr = ('localhost', 27015)
        print('Received %r from %s' % (message, addr))
        print('Send %r to %s' % (message, server_addr))
        self.transport.sendto(data, server_addr)

loop = asyncio.get_event_loop()
print("Starting UDP server")
# One protocol instance will be created to serve all client requests
listen = loop.create_datagram_endpoint(
    EchoServerProtocol,
    local_addr=('0.0.0.0', 27016),
)
transport, protocol = loop.run_until_complete(listen)

try:
    loop.run_forever()
except KeyboardInterrupt:
    pass

transport.close()
loop.close()

The above is basically this:

https://docs.python.org/3/library/asyncio-protocol.html#udp-echo-server-protocol

What I have will get you to step 3, but of course fails at step 4. It sends it back to server C.

You will note that one of the other protocol examples, EchoServerClientProtocol, creates a new instance for each connection. I assume I need to create a new instance for every connection, I believe, because then I can store the original address in the instance, and return that. I assume this doesn't apply to create_datagram_endpoint, or I can't figure out how to do it.

There might be a better approach though.

James R
  • 4,571
  • 3
  • 30
  • 45
  • How is server B supposed to know where to forward the server C replies? Also, UDP does not make much sense for a request/reply pattern, since packets can get lost. – Vincent Jan 31 '17 at 20:33
  • @Vincent I'm very much a UDP novice, but is it possible to retain the original sender using socks5? – James R Feb 01 '17 at 22:21
  • 1
    I don't know much about it, but relaying UDP packets with socks5 requires [a TCP connection to the proxy and special headers for the datagrams](http://stackoverflow.com/questions/41967217/why-socks5-requires-to-relay-udp-over-udp). – Vincent Feb 02 '17 at 10:22
  • @JamesR I'll need to build on the UDP proxy implementation you describe here. Can you share what worked for you? – ndemou Mar 31 '20 at 22:14

1 Answers1

4

You need a local endpoint for server B, plus a remote endpoint to server C for each client A.

Here is a possible implementation.

Also, you might be interested in this module providing high-level UDP endpoints for asyncio.

Vincent
  • 12,919
  • 1
  • 42
  • 64