bytom源码分析-P2P网络-upnp端口映射
V刘晨曦
发表于 2022-11-6 13:59:23
170
0
0
https://github.com/Bytom/bytom
本章介绍bytom代码P2P网络中upnp端口映射
! J3 W- H: R/ v. [: b1 F/ v
作者使用MacOS操作系统,其他平台也大同小异
+ `1 w3 R! U4 [( m' ~& Y
Golang Version: 1.8, ?/ ?' I0 Y3 m6 @9 H! O
. |. p+ T2 i. q& O+ o. N' B
UPNP介绍
UPNP(Universal Plug and Play)通用即插即用。UPNP端口映射将一个外部端口映射到一个内网ip:port。从而实现p2p网络从外网能够穿透网关访问到内网的bytomd节点。2 J# J, _; n1 q
UPNP协议' @. I( p F7 W3 q
SSDP(Simple Service Discovery Protocol 简单服务发现协议)1 o, @9 j+ w y; T) f; B
GENA(Generic Event Notification Architecture 通用事件通知结构)6 s" o/ w3 P1 y9 n' S
SOAP(Simple Object Access Protocol 简单对象访问协议)
XML(Extensible Markup Language 可扩张标记语言)
UPNP代码
** p2p/upnp/upnp.go **. K8 Y( A7 ^; c+ C+ Y- V
发现网络中支持UPNP功能的设备, F( A: D" Y& j+ n3 h9 @: H' g
从网络中发现支持UPNP功能的设备,并得到该设备的location和url等相关信息
type upnpNAT struct {
serviceURL string // 设备的描述文件URL,用于得到该设备的描述信息
ourIP string // 节点本地ip地址
urnDomain string // 设备类型
}
func Discover() (nat NAT, err error) {
ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900")
if err != nil {
return8 M7 p* t/ I7 e" P
}$ c" F! T. c. b
conn, err := net.ListenPacket("udp4", ":0")
if err != nil {
return
}# C6 V2 t7 j0 p) l; T" x2 p1 u. s
socket := conn.(*net.UDPConn)" f, t0 R+ C" C; C, o! |' j
defer socket.Close()3 N; R: H- T- v+ s3 V
err = socket.SetDeadline(time.Now().Add(3 * time.Second))
if err != nil {
return" g A ~( B* b
}; H* H% }: p& i' m% h& d
st := "InternetGatewayDevice:1"
// 多播请求:M-SEARCH SSDP协议定义的发现请求。
buf := bytes.NewBufferString(7 k: U+ T1 `& Y9 q, T
"M-SEARCH * HTTP/1.1\r\n" +/ |% w; p* u% M) h9 o0 x3 k
"HOST: 239.255.255.250:1900\r\n" +& f$ Q% }4 t9 ~1 w7 Z; k: N% ~; k
"ST: ssdp:all\r\n" +2 s7 w* C/ C" V8 {$ C! R6 o7 q
"MAN: \"ssdp:discover\"\r\n" +. { X; j: r2 w, u" Z/ }* ?* J
"MX: 2\r\n\r\n")
message := buf.Bytes()
answerBytes := make([]byte, 1024)7 A4 y+ u- C K, l, T" [
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.' ^( q4 r, l$ P. l* V$ p
message := "\r\n" +6 U G$ s' g5 {& e$ d' o
"" + strconv.Itoa(externalPort)
message += "" + protocol + ""
message += "" + strconv.Itoa(internalPort) + "" +
"" + n.ourIP + "" +5 K! Z7 a; A7 f# A0 `' J
"1"
message += description +4 w: G+ d/ r" D# N! m. ?3 y" W
"" + strconv.Itoa(timeout) +
""3 N% D T9 L3 ?6 X7 B5 @% c
var response *http.Response% }, e4 L8 m( m! L
response, err = soapRequest(n.serviceURL, "AddPortMapping", message, n.urnDomain)
if response != nil {! f# u2 ^$ G2 V1 \+ L( K
defer response.Body.Close()' {/ p h. b( p# S
}1 h5 M: Y w3 ]+ u8 j( o9 P
if err != nil {
return
}
// TODO: check response to see if the port was forwarded6 L* ?! I) K, _/ n& b- ?* }
// log.Println(message, response)/ y% S' v" ^* ~1 X6 g! Q8 C
// JAE:7 B8 c; S4 q/ t# d9 ?, Y% _6 B( M. U
// body, err := ioutil.ReadAll(response.Body)' ?/ t0 c) `5 D/ X, _. m
// fmt.Println(string(body), err), F; u3 S" M/ i8 m( Z, q+ O' n
mappedExternalPort = externalPort/ D8 l. Z8 X( s e: R3 ?2 ?* ^" K8 k
_ = response
return
}
删除端口映射
向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port删除映射关系1 e3 I( A; y6 d+ r- @0 U9 A
func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {3 y1 m X0 r, _# V
message := "\r\n" +4 Y# J( z) K! n8 S; ?1 X
"" + strconv.Itoa(externalPort) ++ z0 m6 }( u+ c: \5 z9 O
"" + protocol + "" +
""0 z9 y% h3 a5 s4 x! M
var response *http.Response
response, err = soapRequest(n.serviceURL, "DeletePortMapping", message, n.urnDomain). e \' }' z7 F9 Y) x9 A3 T. M* i; E# e
if response != nil {
defer response.Body.Close()9 d" O6 V0 ~+ T2 n! p" Y" L
}+ a0 s. C, ^4 Q1 F! N+ ?
if err != nil {* x! _" w5 @) ^" a( W% _- }
return3 P( Z2 v# j) X9 D4 W" D
}
// TODO: check response to see if the port was deleted
// log.Println(message, response)
_ = response! F* n3 y! h" y8 x9 I
return
}
获取映射后的公网地址( u5 I( ]& y) X
func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {
info, err := n.getExternalIPAddress()* V$ j' E, h! N N& Z" e7 e" D Z4 `/ D
if err != nil {/ i, i7 B9 a' E7 p- P
return8 ~6 P+ r9 }2 y1 V4 U4 H
}* y0 O1 g# H4 S8 X9 M0 v
addr = net.ParseIP(info.externalIpAddress)
return2 m% A# f* k& U* @& K7 Q* N( {5 a
}
func (n *upnpNAT) getExternalIPAddress() (info statusInfo, err error) {$ E ]) K9 G) I; q/ o
message := "\r\n" +
""
var response *http.Response( D& {$ h3 {7 {6 J# I! T
response, err = soapRequest(n.serviceURL, "GetExternalIPAddress", message, n.urnDomain)3 a1 _7 S' l/ r
if response != nil {
defer response.Body.Close()
}
if err != nil {
return
}- _% q: p; _" I6 J' V, I
var envelope Envelope
data, err := ioutil.ReadAll(response.Body); A b4 G' z: f4 V4 p; I+ a
reader := bytes.NewReader(data)
xml.NewDecoder(reader).Decode(&envelope)
info = statusInfo{envelope.Soap.ExternalIP.IPAddress}, T0 F, h0 [/ r" }
if err != nil {; d! Q: ^/ T. U" _, V" U* \; {- e& M
return1 s" ^% ~6 ^4 _
}
return
}* S* O) _2 P5 {. O% A, L; H7 {+ j0 i
感谢比原社区开发者Derek的辛勤写作0 y# ]$ h. U% W
如果你对比原项目有兴趣请添加微信号:matrix2140 咨询,备注blockflow
成为第一个吐槽的人