bytom源码分析-P2P网络-upnp端口映射
V刘晨曦
发表于 2022-11-6 13:59:23
78
0
0
https://github.com/Bytom/bytom# m: ^# i, }* q; W$ X6 {
本章介绍bytom代码P2P网络中upnp端口映射* ?; }2 S+ m3 T2 I5 F1 X5 C
作者使用MacOS操作系统,其他平台也大同小异( F7 Z6 |) b/ } p) P4 j, J: F
- a {! I. q* q. l# L
Golang Version: 1.86 g9 P6 {+ L, _, \& n( |
UPNP介绍% `7 y# _! d& W& S* c3 u
UPNP(Universal Plug and Play)通用即插即用。UPNP端口映射将一个外部端口映射到一个内网ip:port。从而实现p2p网络从外网能够穿透网关访问到内网的bytomd节点。
UPNP协议+ z" b2 f! W, z1 _/ ?5 U
SSDP(Simple Service Discovery Protocol 简单服务发现协议)' o! j, l( ]5 P) N
GENA(Generic Event Notification Architecture 通用事件通知结构)
SOAP(Simple Object Access Protocol 简单对象访问协议)
XML(Extensible Markup Language 可扩张标记语言)$ U& m0 q4 N8 `; m, s
UPNP代码( |9 E5 U \6 @+ x l
** p2p/upnp/upnp.go **
发现网络中支持UPNP功能的设备
从网络中发现支持UPNP功能的设备,并得到该设备的location和url等相关信息
type upnpNAT struct {
serviceURL string // 设备的描述文件URL,用于得到该设备的描述信息
ourIP string // 节点本地ip地址
urnDomain string // 设备类型
}# z6 |. E. m/ j
func Discover() (nat NAT, err error) {
ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900") u, Q( V6 S7 Z! O5 x) {& S
if err != nil {
return
}# m% ^" B5 ^. j7 N- `* X
conn, err := net.ListenPacket("udp4", ":0")) G$ Y; `* j! q4 v Y. h( r% p
if err != nil {
return. P3 p3 C( h8 x& x* N7 K0 U! X
}$ \2 g: J4 ^8 I" p
socket := conn.(*net.UDPConn)
defer socket.Close()) N" D5 Q3 N2 B+ |: ~$ M' U
err = socket.SetDeadline(time.Now().Add(3 * time.Second))
if err != nil {
return& H9 j& C0 P+ f( h+ [+ x3 I
}4 H( R3 R/ Q( {8 M
st := "InternetGatewayDevice:1"
// 多播请求:M-SEARCH SSDP协议定义的发现请求。
buf := bytes.NewBufferString(
"M-SEARCH * HTTP/1.1\r\n" +
"HOST: 239.255.255.250:1900\r\n" +
"ST: ssdp:all\r\n" +
"MAN: \"ssdp:discover\"\r\n" +
"MX: 2\r\n\r\n")
message := buf.Bytes()
answerBytes := make([]byte, 1024)
for i := 0; i 3 U. t$ P. ]% |
添加端口映射% I- U- w S( p, t7 o9 U2 \' v
向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port做映射: A- f$ T; o n( L4 p
func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) {
// A single concatenation would break ARM compilation.+ m" s2 j1 E7 W: F# |4 [
message := "\r\n" +
"" + strconv.Itoa(externalPort)6 P4 M5 b% f0 Z+ a1 i! H
message += "" + protocol + ""
message += "" + strconv.Itoa(internalPort) + "" +
"" + n.ourIP + "" +- a* D# c& v- [. F7 w8 n ]) Z
"1"4 j3 g5 I; S' z6 \, E
message += description +
"" + strconv.Itoa(timeout) +7 _" V5 B/ Y1 u) M
""
var response *http.Response
response, err = soapRequest(n.serviceURL, "AddPortMapping", message, n.urnDomain)
if response != nil { C+ K% B* z! v a
defer response.Body.Close(), s8 o N, q# ]1 N
}) `+ K7 h2 V4 O, r, J& N$ C
if err != nil {
return
}
// TODO: check response to see if the port was forwarded" |2 O6 U/ d5 Z
// log.Println(message, response)8 [" `! j, Y) C
// JAE:( J8 Z0 T% P; {$ ~: \) m
// body, err := ioutil.ReadAll(response.Body)
// fmt.Println(string(body), err)* t' q' B3 Z u. X
mappedExternalPort = externalPort
_ = response
return! l" e+ I ?0 H/ i7 d* I; d
}
删除端口映射1 ^1 R3 }- u2 A' |! Y! B& d% o
向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port删除映射关系
func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {
message := "\r\n" +
"" + strconv.Itoa(externalPort) +0 w6 `, G& ^* a; M& A4 i6 k8 D
"" + protocol + "" +6 R; x ~2 U8 R y) {
""9 b7 r# _: r7 c
var response *http.Response- P6 |) O: h! W) i" ?6 Z' v5 l
response, err = soapRequest(n.serviceURL, "DeletePortMapping", message, n.urnDomain)
if response != nil {
defer response.Body.Close()
}, ^* g( P9 y% ?( }6 u& J
if err != nil {: f& k3 U" ]' p: n U# r
return: ]& l- |- z2 Y1 f
}
// TODO: check response to see if the port was deleted w& R# X* L! w! _# B
// log.Println(message, response)
_ = response# _7 n7 K' v2 d, i. L; [" E
return# G6 g+ F) z4 J5 r9 W$ E
}
获取映射后的公网地址" e' q, l5 F- \% N. s; }
func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {6 e5 t. e4 V |7 K- d+ H5 h% S4 s
info, err := n.getExternalIPAddress()( w' K6 e" r, S8 B' U
if err != nil {# g7 V; y5 z+ W6 I
return/ B# M0 G, ?: ~- g' N9 u
}
addr = net.ParseIP(info.externalIpAddress)
return
}1 }& {7 X. t8 T2 E# {5 k/ G% X
func (n *upnpNAT) getExternalIPAddress() (info statusInfo, err error) {
message := "\r\n" +6 r- g3 w; f! [0 O# w
""
var response *http.Response
response, err = soapRequest(n.serviceURL, "GetExternalIPAddress", message, n.urnDomain)
if response != nil {
defer response.Body.Close(), @1 J: b+ K& }4 s) I: d* m5 B
}' J9 s/ D+ {$ V; f
if err != nil {
return( o. T7 a! d5 Z! r: ~) Z- m
}6 W% s0 b- c, g* _
var envelope Envelope
data, err := ioutil.ReadAll(response.Body)
reader := bytes.NewReader(data)
xml.NewDecoder(reader).Decode(&envelope)( {& N! K' G' h+ x4 [
info = statusInfo{envelope.Soap.ExternalIP.IPAddress}+ R" Q2 b! r7 `2 J
if err != nil {
return( K ~+ l/ `- q6 T5 f" }/ t
}
return
}* D3 k4 j3 k, C9 U$ d) g# d- `
感谢比原社区开发者Derek的辛勤写作8 l" x3 H) M% \0 ~! R
如果你对比原项目有兴趣请添加微信号:matrix2140 咨询,备注blockflow
成为第一个吐槽的人