我在
UDP
中有以下 DGRAM
/ socket
Python
:
sock.bind((UDP_IP, UDP_PORT))
while True:
data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
print "received message:", data
此代码在代理服务器中运行,因此目标 IP 和端口与套接字不匹配。我正在使用
tproxy
来拦截数据包。
如何获取目标IP和端口,而不是源IP和端口?
您显然在 2019 年使用了 Python2,我仍然会回答您的问题,但使用 Python3,因为我认为这对最终来到这里的人会更有帮助。
当使用 Linux 内核的 TPROXY 功能时,为了获取传入数据报的目标地址,必须使用低级原语
socket.recvmsg()
。您可以在here找到其官方文档。
您还必须设置套接字选项
IP_RECVORIGDSTADDR
,根据man 7 ip
:
启用 IP_ORIGDSTADDR 辅助消息 recvmsg(2),其中内核返回原始目标地址 正在接收的数据报。 辅助消息包含一个结构体 sockaddr_in。
sshuttle
工具为其创建了一个包装函数。我重写了它,使其更加通用并且易于任何人使用:
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")
不要忘记,为了处理 TPROXY,您还必须使用
IP_TRANSPARENT
设置 setsockopt()
选项,并且它要求您以 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
的回答已经差不多完成了。但值得注意的是,如果我们正在侦听多播流量,则不需要设置 IP_TRANSPARENT,root 权限也是如此。下面是识别多播数据包的目标组地址的简单示例:
import socket
import struct
import time
MCAST_GRP = "240.0.0.0"
MCAST_PORT = 1234
IP_RECVORIGDSTADDR = 20
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setsockopt(socket.SOL_IP, IP_RECVORIGDSTADDR, 1)
# on this port, receives ALL multicast groups
sock.bind(('', MCAST_PORT))
mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
max_ancillary_size=28 #sizeof(struct sockaddr_in6)
bufsize = 10240
while True:
data, ancdata, flags, client = 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)
print(ip)
time.sleep(0.5)