Python3: 从 stun 服务器获取外部端口

Python3: getting external port from stun server

我想获取NAT发送udp包的外部端口
例如:-
当我发送一个 UDP 数据包时:
源 ip :: 源端口 :: 目标 ip :: 目标端口
192.168.1.20 :: 9000 :: 217.52.8.105 :: 5555
然后 NAT 将其翻译成:
源 ip :: 源端口 :: 目标 ip :: 目标端口
121.30.8.158 :: 7777 :: 217.52.8.105 :: 5555

我想得到那个 7777 端口。
我试过 pystun3,但它总是说外部端口是 54320(这是不正确的)
我也试过这段代码,但我无法从收到的 stun 消息中获取端口

#!/usr/bin/python3
import socket
def long_to_bytes(n, length):  # compatible to PY2 and PY3
    # Equivalent to n.to_bytes(length,byteorder='big') in Python 3
    return bytes(bytearray((n >> i*8) & 0xff for i in range(length-1, -1, -1)))

import secrets
def randint(n): return secrets.randbits(n)
    
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('0.0.0.0', 3425))

BIND_REQUEST_MSG = b'\x00\x01'
BIND_RESPONSE_MSG = b'\x01\x01'
send_data = b''
msg_len = long_to_bytes(len(send_data), 2)
trans_id = long_to_bytes(randint(128), 16)
data = BIND_REQUEST_MSG+msg_len+trans_id+send_data
sock.sendto(data, ('stun.l.google.com', 19302))
recv, addr = sock.recvfrom(2048)
print('recv:', recv)
#output: b'\x01\x01\x00\x0cr\xa7\x14\x05Bu\xfe)\xec?f\xd7\x83\x80\xc6u\x00\x01\x00\x08\x00\x01\ra\xd94\x08i'
# how can I get external port from these bytes ??

让我们一起解析你的眩晕反应。使用 RFC 3478 section 11.1 and on down

中描述的消息结构

这个字节串:

b'\x01\x01\x00\x0cr\xa7\x14\x05Bu\xfe)\xec?f\xd7\x83\x80\xc6u\x00\x01\x00\x08\x00\x01\ra\xd94\x08i'

当以 4 字节行打印出来时是这样的:

0x01 0x01 0x00 0x0c
0x72 0xa7 0x14 0x05
0x42 0x75 0xfe 0x29
0xec 0x3f 0x66 0xd7
0x83 0x80 0xc6 0x75
0x00 0x01 0x00 0x08
0x00 0x01 0x0d 0x61
0xd9 0x34 0x08 0x69

一行一行:

Row 0: 0x01 0x01 0x00 0x0c

这是一个 4 字节 header。前两个字节 0x0101 表示绑定响应。后面两个字节0x000c表示事务id为12字节后的消息总大小

接下来的四行:

Row 1: 0x72 0xa7 0x14 0x05
Row 2: 0x42 0x75 0xfe 0x29
Row 3: 0xec 0x3f 0x66 0xd7
Row 4: 0x83 0x80 0xc6 0x75

是交易id。您应该验证它是否与您发送的交易 ID 匹配。但让我们假设它是。

接下来是各个属性块。

属性 header 在第 5 行:

row 5: 0x00 0x01 0x00 0x08

0x00 0x01表示MAPPED-ADDRESS0x00 0x08表示消息属性长度为8字节。这是最后两行。

row 6: 0x00 0x01 0x0d 0x61
row 7: 0xd9 0x34 0x08 0x69

IPv4 的 MAPPED-ADDRESS 属性 body 始终以 0x00 0x01 开始前两个字节。那么后面的两个字节就是你映射的端口: 0x0d 0x61 is 3425 这不是巧合,这和你本地的端口是一样的。大多数 NAT 在转换为出站端口时会尝试保留本地端口,前提是它未被网络上的其他套接字或设备使用。

最后一行是 stun 服务器看到的您的 IP 地址。 0xd9 0x34 0x08 0x69 就是:217.52.8.105.

我不确定你的本地 IP 地址是什么,但你的代码使用本地端口 3425。并且 stun 服务器表明它看到你来自 217.52.8.105:3425