bytom源码分析-P2P网络-upnp端口映射
V刘晨曦
发表于 2022-11-6 13:59:23
85
0
0
https://github.com/Bytom/bytom
本章介绍bytom代码P2P网络中upnp端口映射% ?( n9 h5 {7 N6 w
作者使用MacOS操作系统,其他平台也大同小异
Golang Version: 1.8
UPNP介绍* t; v5 ^, ] K `, u+ ]
UPNP(Universal Plug and Play)通用即插即用。UPNP端口映射将一个外部端口映射到一个内网ip:port。从而实现p2p网络从外网能够穿透网关访问到内网的bytomd节点。3 d* U/ ?( [; A: V
UPNP协议
SSDP(Simple Service Discovery Protocol 简单服务发现协议)) u$ m! L+ o# d" c8 z7 R3 k e
GENA(Generic Event Notification Architecture 通用事件通知结构)0 S7 I% o9 c+ l
SOAP(Simple Object Access Protocol 简单对象访问协议)' F. i) {6 A; E' {0 ^+ j6 p" p5 x$ x
XML(Extensible Markup Language 可扩张标记语言)
UPNP代码8 }4 d4 a2 @6 A( `" U$ X2 l# a1 h( `1 ~
** p2p/upnp/upnp.go **
发现网络中支持UPNP功能的设备) ^9 h! W2 r8 g
从网络中发现支持UPNP功能的设备,并得到该设备的location和url等相关信息
type upnpNAT struct {% X. {( @" V+ }* z6 q( p, J8 D" [2 v
serviceURL string // 设备的描述文件URL,用于得到该设备的描述信息
ourIP string // 节点本地ip地址
urnDomain string // 设备类型
}
func Discover() (nat NAT, err error) {
ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900"), E+ C# ~" i/ a3 @- Z
if err != nil {$ o) O+ F; d1 c( A7 j# E n( g5 c
return
}
conn, err := net.ListenPacket("udp4", ":0")! S9 j9 R( @$ k8 q
if err != nil {
return8 _9 T& @7 Z- ?/ F
}
socket := conn.(*net.UDPConn)
defer socket.Close()& m+ ?) T& K. p) L9 W* o
err = socket.SetDeadline(time.Now().Add(3 * time.Second))' V8 ~4 A) ?" i/ @/ R' Y# P
if err != nil {
return
}4 W* `+ E9 Q" H) h
st := "InternetGatewayDevice:1"
// 多播请求:M-SEARCH SSDP协议定义的发现请求。
buf := bytes.NewBufferString(8 h2 B5 a0 Z) N2 ?3 Q' H+ }
"M-SEARCH * HTTP/1.1\r\n" +8 m9 `3 }7 s! U- I2 Y
"HOST: 239.255.255.250:1900\r\n" +- f7 T0 O! e; W( l0 C
"ST: ssdp:all\r\n" +6 }( o! o4 m& ?; o
"MAN: \"ssdp:discover\"\r\n" +
"MX: 2\r\n\r\n")' H0 a) M0 {; H1 W
message := buf.Bytes()$ U; E0 ^- P B5 W/ f% K, t
answerBytes := make([]byte, 1024)/ w2 ?! P$ h5 k' e
for i := 0; i
添加端口映射
向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port做映射
func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) {
// A single concatenation would break ARM compilation.
message := "\r\n" +
"" + strconv.Itoa(externalPort)
message += "" + protocol + ""# P1 M8 t# B+ n$ x
message += "" + strconv.Itoa(internalPort) + "" +
"" + n.ourIP + "" +! M9 Q; |% r1 T7 ^" R3 I, G
"1": _' Y6 O- i: w0 P; k
message += description +) ^. x- _0 `% o( D2 F* J; E
"" + strconv.Itoa(timeout) +
""
var response *http.Response; X8 D' o" Z, l) E Y8 K+ T3 `
response, err = soapRequest(n.serviceURL, "AddPortMapping", message, n.urnDomain). O1 G1 C& J0 b0 Q* H7 u1 T
if response != nil {. k+ ?3 a' F/ F$ L
defer response.Body.Close()
}
if err != nil {
return" `7 c) q! v# b
}
// TODO: check response to see if the port was forwarded6 g/ i6 Q8 g& c- |- X
// log.Println(message, response)8 |% y+ ]! h) y% |. c
// JAE:
// body, err := ioutil.ReadAll(response.Body)
// fmt.Println(string(body), err)
mappedExternalPort = externalPort$ V) |7 i9 R# I4 }1 v
_ = response" w; P# M) Y; Y7 |0 @8 B: |: t
return
}, n7 C9 F D6 {: D" S
删除端口映射
向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port删除映射关系- e( {: S0 I$ q. l$ C1 E* b
func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {0 K0 C( v2 `& V. g$ A* ~
message := "\r\n" +$ j1 o) u2 k. _; R9 |' E
"" + strconv.Itoa(externalPort) +) B) v6 v! ^8 Z/ b) f& |; B
"" + protocol + "" +
"", J9 G; O! h1 S/ R. X
var response *http.Response) Y, C S- G6 s% n2 f _: C! W
response, err = soapRequest(n.serviceURL, "DeletePortMapping", message, n.urnDomain)
if response != nil {
defer response.Body.Close()
}
if err != nil {/ T% z2 `/ E) T' b5 R1 t( y
return
}
// TODO: check response to see if the port was deleted
// log.Println(message, response)# B" ]1 V; E% N" n: H$ \
_ = response# H; N) T2 h+ Y* C
return
}
获取映射后的公网地址" }4 a+ Y% B7 B1 z, f
func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {
info, err := n.getExternalIPAddress()$ e0 r5 D3 o2 V" h; \9 t
if err != nil {
return/ I* q$ x7 [% ^+ ~* Y# s
}# T2 `6 N. { }' b, W" C# |/ y3 Z
addr = net.ParseIP(info.externalIpAddress)
return/ n% T* m+ @5 y
}
func (n *upnpNAT) getExternalIPAddress() (info statusInfo, err error) {! L7 k9 N, b/ |! \8 V
message := "\r\n" +
""
var response *http.Response' D) R" X; x6 y1 E; [( O9 L' y
response, err = soapRequest(n.serviceURL, "GetExternalIPAddress", message, n.urnDomain)8 T1 T3 f; U. S% f
if response != nil {
defer response.Body.Close()
}9 L9 d+ w- m$ p; g
if err != nil {
return. K5 c1 f5 A9 t# R* h$ v( C7 }
}5 j a7 q# P% ~* `
var envelope Envelope
data, err := ioutil.ReadAll(response.Body)) i* X6 e2 e* K. ], `2 R
reader := bytes.NewReader(data)# a1 l7 U4 n; ]
xml.NewDecoder(reader).Decode(&envelope)+ l5 S4 \) F/ W- J5 [- e' }
info = statusInfo{envelope.Soap.ExternalIP.IPAddress}+ m) x9 D9 J* ?; `6 A7 n8 n
if err != nil {, Z. Y2 N- Z# H1 t! j' v/ C
return) @0 [& W5 e; n, {; Q9 j+ j; z2 B
}5 V. x! ~$ u# o9 B8 ^0 D3 N
return
}
感谢比原社区开发者Derek的辛勤写作
如果你对比原项目有兴趣请添加微信号:matrix2140 咨询,备注blockflow
成为第一个吐槽的人