bytom源码分析-P2P网络-upnp端口映射
V刘晨曦
发表于 2022-11-6 13:59:23
158
0
0
https://github.com/Bytom/bytom/ C. [& n- q! U5 X* ?6 u
本章介绍bytom代码P2P网络中upnp端口映射. s6 \* T4 B% ~( x, m
6 R3 P7 ^9 O! L. X8 z, D3 b
作者使用MacOS操作系统,其他平台也大同小异
. s+ J: O0 y+ |9 L" F
Golang Version: 1.8' a; Q) G8 E( `3 {
- M* Z+ a" m+ Y- e; g
UPNP介绍4 R8 ^/ |7 R: Y2 `6 g8 U
UPNP(Universal Plug and Play)通用即插即用。UPNP端口映射将一个外部端口映射到一个内网ip:port。从而实现p2p网络从外网能够穿透网关访问到内网的bytomd节点。0 S$ Z$ V# @% {
UPNP协议! R# m2 O7 N+ \$ n
SSDP(Simple Service Discovery Protocol 简单服务发现协议)! {7 g- e, }1 q
GENA(Generic Event Notification Architecture 通用事件通知结构)* O R, W8 |1 U; b& I5 R4 Y4 l- q8 ?
SOAP(Simple Object Access Protocol 简单对象访问协议)
XML(Extensible Markup Language 可扩张标记语言)
UPNP代码
** p2p/upnp/upnp.go **
发现网络中支持UPNP功能的设备
从网络中发现支持UPNP功能的设备,并得到该设备的location和url等相关信息' R3 S0 G3 G: Q2 c! _0 l3 ]# L
type upnpNAT struct {
serviceURL string // 设备的描述文件URL,用于得到该设备的描述信息7 d L2 V( `' B+ r& Y# m$ w
ourIP string // 节点本地ip地址
urnDomain string // 设备类型
}
func Discover() (nat NAT, err error) {
ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900"); H& X$ V, a" Z. }) g: C
if err != nil {2 s; M- Z Z. |/ j! Q
return
}
conn, err := net.ListenPacket("udp4", ":0")$ U' M+ J2 C8 {" M% j P( h
if err != nil {
return. o: H% v8 I( `3 G
}
socket := conn.(*net.UDPConn)+ a) K9 b% [1 F: F9 f( P" q1 X
defer socket.Close()
err = socket.SetDeadline(time.Now().Add(3 * time.Second)): `0 ]/ v" h- G, q/ K" [- e* L
if err != nil {
return6 c- c( h. R, R/ K# _3 g- S
}+ ~; a& m" p; C2 H7 t; S* ^
st := "InternetGatewayDevice:1"
// 多播请求:M-SEARCH SSDP协议定义的发现请求。- f6 I B6 e: ]% z
buf := bytes.NewBufferString( D8 D$ b6 u2 y9 H: w
"M-SEARCH * HTTP/1.1\r\n" +
"HOST: 239.255.255.250:1900\r\n" +2 I6 ?; q" Z' b& b9 @7 ]/ N
"ST: ssdp:all\r\n" +1 C: D% R. r; u. Z. }- [
"MAN: \"ssdp:discover\"\r\n" +
"MX: 2\r\n\r\n")' ~" d# u; h! t6 y' h
message := buf.Bytes()0 m* J+ p- q, g( D: [' n7 l
answerBytes := make([]byte, 1024). i* T& A, D: ]8 {3 X5 h
for i := 0; i / s* B( l* Q! Q. o h3 E5 g
添加端口映射& i# D6 F) B8 i! u% d
向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port做映射/ f, Z" a6 c* Z" {( q; G
func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) {, R+ s& o" S5 r& V3 T% [6 ^
// A single concatenation would break ARM compilation.$ l- N9 [& q* g3 V0 c; X% e' }
message := "\r\n" +
"" + strconv.Itoa(externalPort); U. X: K% `$ ~ m7 I, |! H
message += "" + protocol + ""
message += "" + strconv.Itoa(internalPort) + "" +
"" + n.ourIP + "" +
"1"8 w9 Y, ~0 m8 l. D6 y1 d
message += description +8 n* K5 o$ \; a" P( t( n
"" + strconv.Itoa(timeout) +
""
var response *http.Response
response, err = soapRequest(n.serviceURL, "AddPortMapping", message, n.urnDomain)" e! h5 d7 s& c4 I, R
if response != nil {2 Q9 N( y+ N" a# n' _! f* z8 y8 j
defer response.Body.Close()
}- i& X+ p' _: v1 F! ?8 A
if err != nil {0 g1 u0 I7 s: R6 g2 q5 e. y
return
}
// TODO: check response to see if the port was forwarded
// log.Println(message, response)
// JAE:
// body, err := ioutil.ReadAll(response.Body)
// fmt.Println(string(body), err)
mappedExternalPort = externalPort& n! a3 v; I3 M; r) |: l! U
_ = response
return
}8 E- P- A8 p2 G7 h' j
删除端口映射1 o; h1 X" X& Q0 _% N; I
向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port删除映射关系
func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {" Q4 l& L" I$ Q( ], B
message := "\r\n" ++ n; t& d8 k+ X5 B; p4 A# s
"" + strconv.Itoa(externalPort) +
"" + protocol + "" +
""
var response *http.Response
response, err = soapRequest(n.serviceURL, "DeletePortMapping", message, n.urnDomain)* U) e! b* H9 {" J _" i2 z/ ?
if response != nil {
defer response.Body.Close()
}
if err != nil {
return
}% _- ^# J2 B: D- a& G
// TODO: check response to see if the port was deleted( g: I5 w) z& H) y p) U; h
// log.Println(message, response)
_ = response
return& g5 b# n9 u& Y
}- O) G& c/ v7 q* k n; u2 l5 Y1 y
获取映射后的公网地址, ] J" j3 Z2 d/ c/ n
func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {
info, err := n.getExternalIPAddress()
if err != nil {
return: B {, N1 ] W5 [/ R& o0 E) C& n
}4 ^; I, ~5 d( |: I) t' ^. N
addr = net.ParseIP(info.externalIpAddress)0 k' ?; {! h! z5 J$ z/ X- T5 g
return5 V- F& q; \6 s- J% N$ T& V
}! d; L9 i5 |4 w+ g5 z3 F
func (n *upnpNAT) getExternalIPAddress() (info statusInfo, err error) {
message := "\r\n" +( r. D& F2 b! x) z
""
var response *http.Response: T1 ?4 Q' s5 A1 X) S' }
response, err = soapRequest(n.serviceURL, "GetExternalIPAddress", message, n.urnDomain)
if response != nil {
defer response.Body.Close()4 Q5 U1 n. q- M% |0 R$ |* c
}
if err != nil {
return, D% K1 b" H& `
}" N; C5 _4 D) }) \* B
var envelope Envelope
data, err := ioutil.ReadAll(response.Body)
reader := bytes.NewReader(data)
xml.NewDecoder(reader).Decode(&envelope)
info = statusInfo{envelope.Soap.ExternalIP.IPAddress}
if err != nil {9 X- o* N( K* W4 h4 z; V* M7 S
return
}; a! Q2 a3 f0 z& ~8 E: I, S
return
}$ l3 U( o |, \9 N3 ?7 E" Q
感谢比原社区开发者Derek的辛勤写作
如果你对比原项目有兴趣请添加微信号:matrix2140 咨询,备注blockflow
成为第一个吐槽的人