因疫情在家辦公,遠程操作公司電腦。突然想起來試一試很久以前一直想做的試驗: udp打洞。畢竟男人鱔變,最愛鑽洞。
試驗步驟記錄如下:
注:家裏的電腦記爲PC_A,公司電腦記爲PC_B
1.利用stun服務,檢測兩邊電腦的NAT類型,並得到兩邊的內網,外網地址。
這裏使用https://github.com/ccding/go-stun這個庫。由於go-stun並沒有在控制檯輸出本機udp的端口,故需要到他的源碼中加上一句:
fmt.Println("local addr:", conn.LocalAddr().String())
func (c *Client) Discover() (NATType, *Host, error) {
if c.serverAddr == "" {
c.SetServerAddr(DefaultServerAddr)
}
serverUDPAddr, err := net.ResolveUDPAddr("udp", c.serverAddr)
if err != nil {
return NATError, nil, err
}
// Use the connection passed to the client if it is not nil, otherwise
// create a connection and close it at the end.
conn := c.conn
if conn == nil {
conn, err = net.ListenUDP("udp", nil)
// 在此處新增以打印出本機的端口
fmt.Println("local addr:", conn.LocalAddr().String())
if err != nil {
return NATError, nil, err
}
defer conn.Close()
}
return c.discover(conn, serverUDPAddr)
}
重新打包生成go-stun.exe後,分別在PC_A,PC_B中執行以下命令
go-stun.exe -s stun.sipgate.net:10000
其中stun.sipgate.net:10000爲免費的公網stun服務器,這裏面一定要挑選能用的,因爲某些不可描述原因,大部分的stun服務器是無法訪問的.
PC_A輸出如下:
PC_B輸出如下:
幸運的是,PC_A, PC_B都是屬於端口限制型NAT,理論上打洞能夠成功。
2.編寫python代碼
經過上一步,我們有:
PC_A 內部端口 52280 外部地址 xxx.xxx.0.195
PC_B 內部端口 64320 外部地址 xxx.xxx.169.96
那麼,py程序的大概工作便是:
1.建立一個udp socket,綁定至本機的內部端口
2.起一個發送線程,每隔若干時間,向對方的外部地址發送消息
3.主線程起一個循環,接收對方發來的消息
PC_A, PC_B的py腳本應該完全一致的,區別僅是一些參數。
PC_A的py腳本如下:
import socket
import time
from threading import Thread
local_port = 52280
remote_ip, remote_port = "xxx.xxx.169.96", 64320
msg = "hello, i am aaaaaaaaaaaa"
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("0.0.0.0", local_port))
class SendThread(Thread):
def run(self):
while True:
s.sendto(msg.encode(), (remote_ip, remote_port))
print("sent")
time.sleep(1)
st = SendThread()
st.start()
while True:
data = s.recv(1024) # 一次接收1024字節
print(data.decode()) # decode()解碼收到的字節
類似的,PC_B的py腳本如下:
import socket
import time
from threading import Thread
local_port = 64320
remote_ip, remote_port = "xxx.xxx.0.195", 52280
msg = "hello, i am bbbbbbbb"
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("0.0.0.0", local_port))
class SendThread(Thread):
def run(self):
while True:
s.sendto(msg.encode(), (remote_ip, remote_port))
print("sent")
time.sleep(1)
st = SendThread()
st.start()
while True:
data = s.recv(1024) # 一次接收1024字節
print(data.decode()) # decode()解碼收到的字節
3.開始打洞!
分別在PC_A, PC_B運行py腳本
PC_A的控制檯輸出:
sent
sent
sent
sent
sent
hello, i am bbbbbbbb
sent
hello, i am bbbbbbbb
sent
hello, i am bbbbbbbb
sent
hello, i am bbbbbbbb
sent
hello, i am bbbbbbbb
sent
PC_B的控制檯輸出:
sent
hello, i am aaaaaaaaaaaa
sent
hello, i am aaaaaaaaaaaa
sent
hello, i am aaaaaaaaaaaa
sent
hello, i am aaaaaaaaaaaa
sent
hello, i am aaaaaaaaaaaa
sent
hello, i am aaaaaaaaaaaa
sent
hello, i am aaaaaaaaaaaa
sent
hello, i am aaaaaaaaaaaa
顯而易見,打出了一個完美的洞
4.後記
一條簡簡單單的hello消息,卻是代表着文件傳輸,遠程桌面的無限可能