2

I have the following UDP / DGRAM socket in Python:

sock.bind((UDP_IP, UDP_PORT))

while True:
    data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
    print "received message:", data

This code is running in a proxy server, so destination IP and port is not matching the socket. I'm using tproxy to intercept the packets.

How can I get the destination IP and port, not the source IP and port?

Alfred Balle
  • 1,135
  • 4
  • 16
  • 32
  • 1
    I think I've answered your question, but might be misinterpreting you. you've just bound the socket to an address/port and you've got the address of the other side (in `addr`) so you've already got both pieces of info. – Sam Mason Jul 11 '19 at 12:58
  • It doesn't give me the packets destination IP and port. Only what the socket is listening on, and source IP and port from the packet. This code is running in a proxy server, so destination IP and port is not matching the socket. I'm using `tproxy` to intercept the packets. – Alfred Balle Jul 12 '19 at 06:25
  • You need to use `recv_msg()` if it exists in Python, with whatever the option is to retrieve the source-address. – user207421 Jul 12 '19 at 08:39
  • this question seems to have already been answered here: https://stackoverflow.com/a/44206723/1358308 – Sam Mason Jul 12 '19 at 09:03
  • A slightly simpler version of the question would be if `sock.bind(("", UDP_PORT))`, then how do you determine what the destination ip was...here you can get rid of the complexity of the proxy and just want to know which interface it came in on. – teeks99 Oct 05 '22 at 13:03

2 Answers2

1

You were apparently using Python2 back in 2019, I will still answer your question but using Python3 because I think it will be more helpful to people ending up here.

When using the TPROXY feature of the Linux kernel, in order to get the destination address of an incoming datagram, you must use the low level primitive socket.recvmsg(). You will find its official documentation here.

You must also set the socket option IP_RECVORIGDSTADDR which according to man 7 ip :

Enables the IP_ORIGDSTADDR ancillary message in recvmsg(2), in which the kernel returns the original destination address of the datagram being received. The ancillary message contains a struct sockaddr_in.

The sshuttle tool created a wrapper function for it. I rewrote it to be more generic and easy to use for anyone:

import socket
import struct
from typing import Tuple

Host = Tuple[str, int]

# They are not in the standard library yet !
IP_RECVORIGDSTADDR = 20
SOL_IPV6 = 41
IPV6_RECVORIGDSTADDR = 74

def recv_tproxy_udp(bind_sock, bufsize) -> Tuple[Host, Host, bytes]:
    max_ancillary_size = 28 # sizeof(struct sockaddr_in6)
    data, ancdata, flags, client = bind_sock.recvmsg(bufsize,
                                                     socket.CMSG_SPACE(max_ancillary_size))


    for cmsg_level, cmsg_type, cmsg_data in ancdata:

        # Handling IPv4
        if cmsg_level == socket.SOL_IP and cmsg_type == IP_RECVORIGDSTADDR:
            family, port = struct.unpack('=HH', cmsg_data[0:4])
            port = socket.htons(port)

            if family != socket.AF_INET:
                raise TypeError(f"Unsupported socket type '{family}'")

            ip = socket.inet_ntop(family, cmsg_data[4:8])
            destination = (ip, port)
            return client, destination, data

        # Handling IPv6
        elif cmsg_level == SOL_IPV6 and cmsg_type == IPV6_RECVORIGDSTADDR:
            family, port = struct.unpack('=HH', cmsg_data[0:4])
            port = socket.htons(port)

            if family != socket.AF_INET6:
                raise TypeError(f"Unsupported socket type '{family}'")

            ip = socket.inet_ntop(family, cmsg_data[8:24])
            destination = (ip, port)
            return client, destination, data

    raise ValueError("Unable to parse datagram")

Don't forget that in order to handle TPROXY, you must also set the IP_TRANSPARENT option using setsockopt(), and that it requires you to run your script as root:

server_sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
server_sock.setsockopt(socket.SOL_IP, socket.IP_TRANSPARENT, 1)
server_sock.setsockopt(socket.SOL_IP, IP_RECVORIGDSTADDR, 1)
server_sock.bind((bind_addr, bind_port))
ShellCode
  • 1,072
  • 8
  • 17
-2

from the docs of socket.getsockname():

Return the socket’s own address. This is useful to find out the port number of an IPv4/v6 socket, for instance.

so you'd want to print out sock.getsockname()

Sam Mason
  • 15,216
  • 1
  • 41
  • 60
  • 1
    That gives me the sockets own address, as you also write. Not the packets destination IP and port. – Alfred Balle Jul 12 '19 at 06:23
  • your use of the term destination is **very** ambiguous, it depends which side of the conversation you're talking about. i.e. destination when you send the packet is the source when it's received. I'd suggest being more precise when talking about this again... – Sam Mason Jul 12 '19 at 09:00