bytom源码分析-P2P网络-upnp端口映射
V刘晨曦
发表于 2022-11-6 13:59:23
84
0
0
https://github.com/Bytom/bytom$ ?4 `* _6 L% s! E& x& B
本章介绍bytom代码P2P网络中upnp端口映射0 s( |+ B! W8 D9 L% r: i6 e, a
- C5 A$ C( m n
作者使用MacOS操作系统,其他平台也大同小异* ~/ K6 [- g3 [2 r
8 ?3 `7 g0 n, U- |# Y, U: a# t: |
Golang Version: 1.8
; W+ b( q! W X) H# m
UPNP介绍 p9 ^: y' p7 i/ Y7 z3 |
UPNP(Universal Plug and Play)通用即插即用。UPNP端口映射将一个外部端口映射到一个内网ip:port。从而实现p2p网络从外网能够穿透网关访问到内网的bytomd节点。
UPNP协议. Q! K3 q/ S/ R
SSDP(Simple Service Discovery Protocol 简单服务发现协议)
GENA(Generic Event Notification Architecture 通用事件通知结构)
SOAP(Simple Object Access Protocol 简单对象访问协议)
XML(Extensible Markup Language 可扩张标记语言)& u* m7 g; W1 ]+ @; c9 a
UPNP代码
** p2p/upnp/upnp.go **' v5 Q6 z! R( r7 m. B! f
发现网络中支持UPNP功能的设备2 P5 R# |3 v: t, M
从网络中发现支持UPNP功能的设备,并得到该设备的location和url等相关信息% x: \' V4 Z$ |3 C( e
type upnpNAT struct {# U8 I% m# c$ Q
serviceURL string // 设备的描述文件URL,用于得到该设备的描述信息
ourIP string // 节点本地ip地址
urnDomain string // 设备类型
}! G( t2 P/ ^. p% {* K2 k( ]
func Discover() (nat NAT, err error) {7 T; X! O7 B4 i" y$ e/ E# q" ?
ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900")0 j: j" M/ p: a9 b
if err != nil {3 k% ?+ S( P/ z" A+ s
return
}
conn, err := net.ListenPacket("udp4", ":0")
if err != nil {. Z+ _$ t6 j/ l9 @ C: l
return
}
socket := conn.(*net.UDPConn)
defer socket.Close()
err = socket.SetDeadline(time.Now().Add(3 * time.Second))
if err != nil {
return2 f; A) P2 H; A1 B j# \# H
}
st := "InternetGatewayDevice:1"9 Q6 R3 W0 [/ w0 h5 d# F
// 多播请求:M-SEARCH SSDP协议定义的发现请求。
buf := bytes.NewBufferString(' C. O$ L$ j; i* { {
"M-SEARCH * HTTP/1.1\r\n" +# |* A- b3 q' p2 t3 x' ?8 Z) Y$ i
"HOST: 239.255.255.250:1900\r\n" +
"ST: ssdp:all\r\n" +9 u G: [6 v/ ?; H
"MAN: \"ssdp:discover\"\r\n" +2 h9 E& J( {% G3 @7 T) O0 C
"MX: 2\r\n\r\n")
message := buf.Bytes()
answerBytes := make([]byte, 1024)
for i := 0; i
添加端口映射
向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port做映射9 S6 V2 _0 R- ]! c5 I( ?) E
func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) {
// A single concatenation would break ARM compilation., ^% D5 k5 v8 z- q" n: @
message := "\r\n" +5 U( a# I0 ~7 ]0 Z3 m9 x% U) e
"" + strconv.Itoa(externalPort)
message += "" + protocol + ""- U# i, y6 ~) F! A6 `
message += "" + strconv.Itoa(internalPort) + "" +) M# `9 u* J' Y" X# z/ c
"" + n.ourIP + "" +
"1"# T$ b; x$ V4 J* U
message += description +! s, E+ E( W5 f* L* U
"" + strconv.Itoa(timeout) +# ~7 Z) a4 x# E4 ?. y0 e7 ?6 a8 J
""
var response *http.Response$ t2 Q/ V- N' a0 W+ _! J' g6 ^
response, err = soapRequest(n.serviceURL, "AddPortMapping", message, n.urnDomain)
if response != nil {5 X% e% I+ i3 x( {% U" F
defer response.Body.Close()* i: d9 w0 U1 y
}. k" A' l1 a7 G
if err != nil {
return$ D; [3 o$ T( |9 k8 S
}' Q& F/ T+ R% s* b: w5 `
// TODO: check response to see if the port was forwarded! a4 u/ x6 [5 d. N1 ~: H. I9 g
// log.Println(message, response)
// JAE:
// body, err := ioutil.ReadAll(response.Body)6 ^1 G( b7 V9 S5 a# }3 D! y
// fmt.Println(string(body), err)- K- z7 \% p7 e2 q" |
mappedExternalPort = externalPort& C' D% m y2 w9 s8 i+ H+ T
_ = response
return( H1 h/ n. p) w: i r( d( x" W
}
删除端口映射- x8 m" K1 T \6 x% x. X) Y
向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port删除映射关系
func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) { y' R9 I* J% d
message := "\r\n" +
"" + strconv.Itoa(externalPort) +
"" + protocol + "" +
""" c" A' w5 ?$ `1 ~
var response *http.Response
response, err = soapRequest(n.serviceURL, "DeletePortMapping", message, n.urnDomain)
if response != nil {# ^+ B6 p% [( p/ v3 E& o& c- g: X
defer response.Body.Close()" w/ l% {- }* R% K8 l
}) b! i# o4 ]( ]. d! F# N4 F6 U: g& w
if err != nil {- e7 x' b- Q0 G4 n3 q0 T( ~% d
return
}. w: G$ i) M" q+ D; V
// TODO: check response to see if the port was deleted0 e) [) G% F, S {. \% P
// log.Println(message, response)
_ = response
return$ k R! Q( @/ K1 p/ N
}+ c7 L$ t8 P$ O, s
获取映射后的公网地址
func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {1 i- M8 n5 P& x6 I- U8 k9 H
info, err := n.getExternalIPAddress()
if err != nil {
return
}
addr = net.ParseIP(info.externalIpAddress). l% }# H$ {0 L' h" {
return$ J- Z' g! z% m' t1 `6 b( [. L
}
func (n *upnpNAT) getExternalIPAddress() (info statusInfo, err error) {% ]# b7 S: E: X, ^
message := "\r\n" +
""& N7 ^( G9 R% t5 O! a
var response *http.Response% r9 Q; t4 `) r& A" w
response, err = soapRequest(n.serviceURL, "GetExternalIPAddress", message, n.urnDomain)4 l# J7 X8 V( y
if response != nil {
defer response.Body.Close()
}, r& z. Y$ X1 q7 P/ U
if err != nil {
return
}. E6 G1 f9 {" W2 s7 O
var envelope Envelope
data, err := ioutil.ReadAll(response.Body)
reader := bytes.NewReader(data): I$ p. s- u: ?9 c: t; d
xml.NewDecoder(reader).Decode(&envelope)2 t M8 D: [2 N6 P! `" ?# k1 K/ N' [
info = statusInfo{envelope.Soap.ExternalIP.IPAddress}
if err != nil {
return0 u& A8 I1 o2 v
}7 s5 U7 o; f% S3 Z* V8 Y
return A$ ^% ~1 W5 _5 z# z- L4 k
}2 V' J3 F- ~" Z; K
感谢比原社区开发者Derek的辛勤写作. s9 T4 X# Z: b5 l* ~
如果你对比原项目有兴趣请添加微信号:matrix2140 咨询,备注blockflow
成为第一个吐槽的人