bytom源码分析-P2P网络-upnp端口映射
V刘晨曦
发表于 2022-11-6 13:59:23
154
0
0
https://github.com/Bytom/bytom
本章介绍bytom代码P2P网络中upnp端口映射9 ^# W! h0 |$ [2 q6 r: g! I3 ~2 x
8 N- W; p: u5 U! g- [! b _
作者使用MacOS操作系统,其他平台也大同小异! i2 [0 I; N: V4 S5 D
; x9 k: ]# k$ w/ t
Golang Version: 1.8( y$ M' ?" c) ]$ l3 n7 I
UPNP介绍
UPNP(Universal Plug and Play)通用即插即用。UPNP端口映射将一个外部端口映射到一个内网ip:port。从而实现p2p网络从外网能够穿透网关访问到内网的bytomd节点。
UPNP协议7 k4 M0 t8 t' _( Q* C9 I0 B
SSDP(Simple Service Discovery Protocol 简单服务发现协议)
GENA(Generic Event Notification Architecture 通用事件通知结构)
SOAP(Simple Object Access Protocol 简单对象访问协议)& }, I p/ [1 U
XML(Extensible Markup Language 可扩张标记语言)
UPNP代码, W) o% a. h+ I/ L$ Y* Z* b) p
** p2p/upnp/upnp.go **2 G5 s: o6 y# z) [6 c8 R( t' Z
发现网络中支持UPNP功能的设备; H9 r3 V: u1 Y+ F, y/ R" m+ {) ]' G+ S
从网络中发现支持UPNP功能的设备,并得到该设备的location和url等相关信息
type upnpNAT struct {
serviceURL string // 设备的描述文件URL,用于得到该设备的描述信息1 h1 g7 |/ B- f9 M8 o3 ^6 W
ourIP string // 节点本地ip地址
urnDomain string // 设备类型& t+ q* ?8 P9 J6 j9 I6 t; s
}
func Discover() (nat NAT, err error) {2 D! n/ O+ ~3 Q$ Y+ D" B
ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900")# r& C# r( ^6 I: r
if err != nil {
return
}1 j# s$ p% [' w) h9 ]: i
conn, err := net.ListenPacket("udp4", ":0")2 t! M" z; s7 G, Z ?4 S
if err != nil {. i/ i. P6 V/ g2 z' g/ K; Q
return# K0 y3 z+ Q( y/ Z
}. @" }# u0 p. u9 N/ w9 h! G
socket := conn.(*net.UDPConn)8 I2 ^1 D% ~/ o. E
defer socket.Close()
err = socket.SetDeadline(time.Now().Add(3 * time.Second))
if err != nil {
return& \! E8 z; y$ v% F, x
}
st := "InternetGatewayDevice:1"' m) Y% P0 P$ l7 R3 o% v
// 多播请求:M-SEARCH SSDP协议定义的发现请求。
buf := bytes.NewBufferString(
"M-SEARCH * HTTP/1.1\r\n" +, C2 I4 W5 z' d* y
"HOST: 239.255.255.250:1900\r\n" +
"ST: ssdp:all\r\n" +
"MAN: \"ssdp:discover\"\r\n" +% B9 M" b H1 v- ?% {4 N$ M
"MX: 2\r\n\r\n")% z* Q1 A/ J, ~/ l0 \+ Z
message := buf.Bytes()
answerBytes := make([]byte, 1024)+ ^" j- A9 n- q/ U( V) L
for i := 0; i
添加端口映射
向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port做映射- D( G$ z# s- O' s2 q& ~
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" +2 R! X; j9 v- N5 W* Y" K7 a: C8 |3 v
"" + strconv.Itoa(externalPort)
message += "" + protocol + ""+ O1 g/ I8 f* w7 }4 z+ P' Q& a' ^
message += "" + strconv.Itoa(internalPort) + "" +2 B7 X" h' ~# j8 @, a6 X6 @. R
"" + n.ourIP + "" +
"1"
message += description +: a+ m. B* P) V% F$ p) s; E9 D, y
"" + strconv.Itoa(timeout) +$ t4 P4 I) {& S# @6 p/ x9 D
""7 g# N# J- J ]4 Z+ L3 V. S2 N
var response *http.Response& d2 _0 L3 J/ R, T. \& A& h
response, err = soapRequest(n.serviceURL, "AddPortMapping", message, n.urnDomain)
if response != nil {! k. v3 L) ~; o/ I5 I; j4 ^0 i
defer response.Body.Close()5 U; y" m$ p# g0 H
}
if err != nil {2 C* k2 Z% [; Y' H
return
}
// TODO: check response to see if the port was forwarded7 O4 ~* H) S, A' b/ g- d% _1 Q+ ]& y
// log.Println(message, response)* N# N# t# `" m
// JAE:3 N& H5 g, D' U3 u V
// body, err := ioutil.ReadAll(response.Body)3 ~+ P& L( p& G9 ~# m X( r0 @# p
// fmt.Println(string(body), err)
mappedExternalPort = externalPort
_ = response7 l# d$ d% M8 e+ d& X8 R1 R7 E. n
return" M9 {" i2 y: c+ o
}
删除端口映射3 T% l v4 ]: i7 R! K9 L# ~
向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port删除映射关系' m. q8 G4 t$ F, r# F6 }: v
func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {% P$ D% {" p7 i5 c! m5 b/ [1 f
message := "\r\n" +
"" + strconv.Itoa(externalPort) +0 A! `* W' |4 d$ {
"" + protocol + "" +
""% L8 M$ X3 ^. p0 \+ P9 F
var response *http.Response' R# v/ F, {( ]" H; k5 V' d( V
response, err = soapRequest(n.serviceURL, "DeletePortMapping", message, n.urnDomain)
if response != nil {
defer response.Body.Close()* m- h+ L9 A( k, j3 A
}
if err != nil {
return7 @9 R- F$ j6 b% j- x
}& n: r; I( @6 q2 s
// TODO: check response to see if the port was deleted
// log.Println(message, response)% l% P" F2 U, k3 k/ m
_ = response
return
}
获取映射后的公网地址" V# T: f- \! W# Y& C
func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {1 L X7 C' h( A3 q+ \: ?
info, err := n.getExternalIPAddress()6 L5 B" |3 d6 P( j# w
if err != nil {, A- N0 W, U( a7 O
return
}
addr = net.ParseIP(info.externalIpAddress)
return
}% n' {4 p _5 z+ I$ z+ @
func (n *upnpNAT) getExternalIPAddress() (info statusInfo, err error) {
message := "\r\n" +
""
var response *http.Response6 S+ Y8 v7 z/ l, s1 r; R# H
response, err = soapRequest(n.serviceURL, "GetExternalIPAddress", message, n.urnDomain)! E8 _$ r; L3 }, m# F Q
if response != nil {
defer response.Body.Close(): Q0 o5 [; u( n4 ^4 w7 I# }+ r
}
if err != nil {
return& i' u5 x- g" A
}
var envelope Envelope0 o% V. O9 N3 \: g
data, err := ioutil.ReadAll(response.Body)
reader := bytes.NewReader(data)& p: ^# v T# d# s6 @1 o- b
xml.NewDecoder(reader).Decode(&envelope)
info = statusInfo{envelope.Soap.ExternalIP.IPAddress}
if err != nil {
return
}
return) h6 ^ A4 t$ w7 C
}4 q% a) m: `, E) f9 Q$ t9 S0 `
感谢比原社区开发者Derek的辛勤写作
如果你对比原项目有兴趣请添加微信号:matrix2140 咨询,备注blockflow
成为第一个吐槽的人