Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

bytom源码分析-P2P网络-upnp端口映射

V刘晨曦
85 0 0
简介
2 K( E3 t. s3 R: L  `https://github.com/Bytom/bytom
* x3 g2 p  m! i5 U! g本章介绍bytom代码P2P网络中upnp端口映射% ?( n9 h5 {7 N6 w

1 X. b# u$ f7 L' s7 H8 T/ ^5 z+ X作者使用MacOS操作系统,其他平台也大同小异
' d. N+ G# z4 R5 S: W$ }

8 ]4 v" ?, P4 t2 @  L/ E$ q
' C% U/ s0 N, h' H& }) h* w% p7 u2 @Golang Version: 1.8
4 M6 N8 k' j) v# Z4 ~

+ F9 T$ T6 F9 y0 \5 l) W8 j/ f- A5 yUPNP介绍* t; v5 ^, ]  K  `, u+ ]
UPNP(Universal Plug and Play)通用即插即用。UPNP端口映射将一个外部端口映射到一个内网ip:port。从而实现p2p网络从外网能够穿透网关访问到内网的bytomd节点。3 d* U/ ?( [; A: V
UPNP协议
5 p8 q0 d4 _2 U! N" b2 U  j; QSSDP(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 可扩张标记语言)
4 Q1 ]' v! g* [; ~& B. a! x3 xUPNP代码8 }4 d4 a2 @6 A( `" U$ X2 l# a1 h( `1 ~
** p2p/upnp/upnp.go **
& m8 I6 ]+ y/ G% |# A发现网络中支持UPNP功能的设备) ^9 h! W2 r8 g
从网络中发现支持UPNP功能的设备,并得到该设备的location和url等相关信息
1 L$ L# a- l' Etype upnpNAT struct {% X. {( @" V+ }* z6 q( p, J8 D" [2 v
        serviceURL string // 设备的描述文件URL,用于得到该设备的描述信息
' m- [* ~( O2 W2 v# c; S2 A  d        ourIP      string // 节点本地ip地址
) h) j5 W/ u6 n* ^        urnDomain  string // 设备类型
8 K2 n2 s- s4 m9 u2 K) x}
& v6 x' A) G  s1 Zfunc Discover() (nat NAT, err error) {
% z- D: T  k6 H' f0 q% i' X        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
  `9 ]4 t* A5 z  O  [        }
: g: D1 G) h. w) `4 g) t        conn, err := net.ListenPacket("udp4", ":0")! S9 j9 R( @$ k8 q
        if err != nil {
! A) F$ l+ u7 P- b                return8 _9 T& @7 Z- ?/ F
        }
% _0 y( X5 L' b0 P9 \        socket := conn.(*net.UDPConn)
+ n4 b4 ?5 V" k7 |) g  o6 h        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 {
% K, d, K( D3 r% ?+ L5 e                return
. f9 W& y( m1 {" F* k8 }1 x1 F3 b        }4 W* `+ E9 Q" H) h
        st := "InternetGatewayDevice:1"
2 X. _" I  M5 H/ o        // 多播请求:M-SEARCH SSDP协议定义的发现请求。
( |( g+ l# y# I* n5 Z5 y9 I        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" +
4 D6 b8 N) k! `8 m! b                        "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
" ~- {; T/ A: ]4 Z- a+ f添加端口映射
! Q8 ~; y+ V; \" Z  l9 C6 m1 ^7 K向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port做映射
( x3 T: L5 B+ ]" E/ b" Ofunc (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) {
4 a( ?8 B0 S2 S' f; @        // A single concatenation would break ARM compilation.
1 k  }9 \$ ^8 i" u( m        message := "\r\n" +
- t3 `# N; Q, d3 x                "" + strconv.Itoa(externalPort)
+ P- L4 K8 G, B5 X        message += "" + protocol + ""# P1 M8 t# B+ n$ x
        message += "" + strconv.Itoa(internalPort) + "" +
0 j7 v" H8 P+ D: p! i  s                "" + 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) +
6 G/ M7 g9 U4 ?. a                "
"
+ h% {! o9 Z% A$ u        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()
1 g- Z0 g0 j" e7 o7 f' V* K. Y* o        }
6 K% Q/ W9 V5 q/ M! N; ?        if err != nil {
0 u8 E1 f( q. f                return" `7 c) q! v# b
        }
; O0 ?6 @# z1 T. T8 G        // 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:
$ K. W" U) J+ }  L( W0 p/ L        // body, err := ioutil.ReadAll(response.Body)
( P; L6 ~, z0 N  t* r9 g        // fmt.Println(string(body), err)
5 G9 h9 Z1 S3 a        mappedExternalPort = externalPort$ V) |7 i9 R# I4 }1 v
        _ = response" w; P# M) Y; Y7 |0 @8 B: |: t
        return
, F) s3 Y, g- L$ d+ m# t) W}, n7 C9 F  D6 {: D" S
删除端口映射
5 v2 r; J; |0 w2 z% d* z向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 + "" +
0 M$ r% g0 S* j1 W* r/ s8 a                "
", 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)
4 @" G; g5 F! r9 L9 u        if response != nil {
) M4 m9 l; M) Q# r3 S- I- Y                defer response.Body.Close()
6 W3 w: B- K) n' i8 `        }
# P( |* c0 Z  P! x7 s        if err != nil {/ T% z2 `/ E) T' b5 R1 t( y
                return
0 U" ~# j0 O% M- N        }
0 u- X3 y& o& J8 f/ Z& k- t        // TODO: check response to see if the port was deleted
! `8 `" H2 B8 I" Q        // log.Println(message, response)# B" ]1 V; E% N" n: H$ \
        _ = response# H; N) T2 h+ Y* C
        return
* Z2 a: Q" M" J$ I1 r/ L4 {% m}
! l2 i5 E  J& y$ T+ ^& Q获取映射后的公网地址" }4 a+ Y% B7 B1 z, f
func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {
8 R7 A9 t, A8 }) Q1 N        info, err := n.getExternalIPAddress()$ e0 r5 D3 o2 V" h; \9 t
        if err != nil {
0 j# q0 |) ~3 a. F: `+ [                return/ I* q$ x7 [% ^+ ~* Y# s
        }# T2 `6 N. {  }' b, W" C# |/ y3 Z
        addr = net.ParseIP(info.externalIpAddress)
/ F+ r3 c) u/ X" H1 f        return/ n% T* m+ @5 y
}
# r/ a3 |, Y1 vfunc (n *upnpNAT) getExternalIPAddress() (info statusInfo, err error) {! L7 k9 N, b/ |! \8 V
        message := "\r\n" +
; o" E6 Y% N0 s0 C6 Y2 c( f                "
"
; L/ X: T1 E8 k! A6 N( v        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 {
( i3 @  x( S+ T" ]  h3 l0 h                defer response.Body.Close()
* I1 X! q% i+ _, C+ m9 P        }9 L9 d+ w- m$ p; g
        if err != nil {
, u5 S( l+ ^/ b) Q$ J) j                return. K5 c1 f5 A9 t# R* h$ v( C7 }
        }5 j  a7 q# P% ~* `
        var envelope Envelope
, s6 ?+ C/ H  ?        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
, K. S! F( W% U; x}
( n8 [: z" }* H: ^感谢比原社区开发者Derek的辛勤写作
. ?$ f$ t; [1 p, M+ U如果你对比原项目有兴趣请添加微信号:matrix2140 咨询,备注blockflow
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

V刘晨曦 初中生
  • 粉丝

    0

  • 关注

    3

  • 主题

    14