masgo
masgo

Reputation: 480

Listening to EVERY port on a machine

For testing purposes I want to build a server that listens for TCP connections to every port (or at least on most of the ports) on a certain interface (e.g., eth0). The server is accessed via SSH to eth1, so no problem there. (I do not care about UDP or other protocols)

I want to do a special kind of middlebox detection/analysis, therefore I need to be able to fully establish a connection. Having an HTTP connection would be the best, because the "client" could be implemented as JS in the Browser.

I started with a simple jetty server, but had to realize that jetty needs to spawn at least on thread per port it is listening to. This leads to problems when I want to listen to several thousand ports. Or is there a way around that?

My next try was to use iptables:

sudo iptables -t nat -A PREROUTING -i eth0 -p tcp -j DNAT --to-destination 127.0.0.1:8080`

It seemed to work. It allows to connect on every port and the traffic gets routed to the local port 8080 where jetty listens. But now I no longer know which port was used by the client. Because jetty thinks the connection was established via port 8080. Is there a way to determine the real incomming port from jetty? I could send the port as part of the HTTP request, but if the client tries to contact port 1234 .. and a middlebox redirects this to port 5678 .. I am unable to know what port was used.

I also tried userland solutions like socat. The problem was even worse than before. Because now jetty also saw the remote IP as being 127.0.0.1.

Or, is there another way to achieve this?

Oh and btw: I have full control of the machine. So I could change the kernel or whatever is needed. Right now I use Ubuntu 14.04 LTS, but if a solution needs something else I could go with that.

Upvotes: 3

Views: 4029

Answers (1)

larsks
larsks

Reputation: 311606

NB: This is a Python solution, because I know Python, but you can accomplish the same thing in any language the exposes the underlying C library getsockopt call.

If you replace your DNAT rule with a REDIRECT rule, you can then use getsockopt with the SO_ORIGINAL_DST option to retrieve the original address of a REDIRECT-ed connection.

Consider the following code:

#!/usr/bin/python

import socket
import struct

SO_ORIGINAL_DST = 80

s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('0.0.0.0', 2000))
s.listen(10)

while True:
    csock, caddr = s.accept()
    orig_dst = csock.getsockopt(socket.SOL_IP, SO_ORIGINAL_DST, 16)
    
    orig_port = struct.unpack('>H', orig_dst[2:4])
    orig_addr = socket.inet_ntoa(orig_dst[4:8])

    print 'connection from', caddr
    print 'connection to', (orig_addr, orig_port)
    print

If I have an iptables rule that looks like:

# iptables -t nat -A PREROUTING -p tcp --dport 1500:1600 \
  -j REDIRECT --to-port 2000

And while the above Python code is running I connect from another host to my_ip_address:1500, I see:

connection from ('192.168.1.20', 35790)
connection to ('192.168.1.75', (1500,))

And if I connect to port 1550 I see:

connection from ('192.168.1.20', 42054)
connection to ('192.168.1.75', (1550,))

Which I think is exactly what you were asking for. Note that to my knowledge this will only work for TCP connections; there are other solutions (possibly involving the TPROXY iptables target) that may work with UDP connections as well.

Upvotes: 9

Related Questions