Skip to content

Instantly share code, notes, and snippets.

@toke
Created January 28, 2020 13:06
Show Gist options
  • Save toke/417a6c9c658d2b1617f2b72bb18426d1 to your computer and use it in GitHub Desktop.
Save toke/417a6c9c658d2b1617f2b72bb18426d1 to your computer and use it in GitHub Desktop.
Using TPROXY to bind to any port on many IPs (asyncio variant)
import asyncio
import socket
BIND_IP = "127.0.0.1"
TCP_PORT = 1234
SOCK_BACKLOG = 32
IP_TRANSPARENT = 19
desc = f"""
Anybind; "bind" on many IPs and many Ports.
See: https://blog.cloudflare.com/how-we-built-spectrum/
You may need to configure routing and TPROXY:
sudo ip route add local 192.168.250.0/24 dev lo src {BIND_IP}
sudo iptables -t mangle -I PREROUTING \
-d 192.168.250.0/24 -p tcp \
-j TPROXY --on-port={TCP_PORT} --on-ip={BIND_IP}
Needs: CAP_NET_ADMIN capability
"""
async def handle_echo(_, writer):
try:
data = b"I'm everywhere.'\n"
s_ip, s_port = writer.get_extra_info('peername')
d_ip, d_port = writer.get_extra_info('sockname')
print(f"Conn tcp://{s_ip}:{s_port} → tcp://{d_ip}:{d_port}")
writer.write(data)
writer.write_eof()
await writer.drain()
except ConnectionResetError:
print("Conn: RIP")
finally:
print("Close the client socket")
writer.close()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.setsockopt(socket.IPPROTO_IP, IP_TRANSPARENT, 1)
s.bind((BIND_IP, TCP_PORT))
s.listen(SOCK_BACKLOG)
loop = asyncio.get_event_loop()
coro = asyncio.start_server(handle_echo, sock=s, loop=loop)
server = loop.run_until_complete(coro)
# Serve requests until Ctrl+C is pressed
b_ip, b_port = server.sockets[0].getsockname()
print(desc)
print(f'Serving on tcp://{b_ip}:{b_port}')
try:
loop.run_forever()
except KeyboardInterrupt:
pass
print("Close server")
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment