Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

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

V刘晨曦
86 0 0
简介0 h- O6 v7 r$ P2 s$ ~7 p# T
https://github.com/Bytom/bytom% k) r' m. }+ m* [8 r& b& Z) I
本章介绍bytom代码P2P网络中upnp端口映射( {# ^7 q! m3 {, K/ E' x
. y9 P) C5 ?1 Y0 V% f- [2 s# g" p
作者使用MacOS操作系统,其他平台也大同小异( I: A( E' m6 G# a5 \' z- N
9 ]- h# W. {0 u& f0 ]5 Z
: w) O/ f0 \( {3 J* O) j
Golang Version: 1.8! M! Q0 A8 {2 W0 a- w! l" q- ]

3 _  P0 x+ E2 }UPNP介绍3 a' F4 E: h4 v( c
UPNP(Universal Plug and Play)通用即插即用。UPNP端口映射将一个外部端口映射到一个内网ip:port。从而实现p2p网络从外网能够穿透网关访问到内网的bytomd节点。
  I7 n  c1 g! r$ b( u$ C) \5 `6 @UPNP协议0 }+ Y* Q' r1 k5 E9 m: I. v/ a" [
SSDP(Simple Service Discovery Protocol 简单服务发现协议)
9 r( o! \" T* u! g& j9 xGENA(Generic Event Notification Architecture 通用事件通知结构)  `  [0 [. I: }+ |* ~2 J
SOAP(Simple Object Access Protocol 简单对象访问协议)
! u3 B! k. w9 c; S, `+ J  C* mXML(Extensible Markup Language 可扩张标记语言)
% p7 S# B: h. ], A. SUPNP代码. y$ ~- V, E8 M; l1 T; K( n6 E
** p2p/upnp/upnp.go **, u- L' Z2 g, T2 t) M3 B+ {/ o1 s) e
发现网络中支持UPNP功能的设备
) N( n& w  R' [# i$ f从网络中发现支持UPNP功能的设备,并得到该设备的location和url等相关信息& u) t# w3 n/ c; \: u' i. g- k# ?
type upnpNAT struct {$ C4 Z: l) ^* A$ P. ^  U
        serviceURL string // 设备的描述文件URL,用于得到该设备的描述信息, W% g# V- K1 e2 t# w2 D& j2 v
        ourIP      string // 节点本地ip地址6 R: a8 j3 I) b6 }. Q
        urnDomain  string // 设备类型2 u7 m. l- t3 u' l# L+ _
}
) o+ K7 C7 |# K# G& gfunc Discover() (nat NAT, err error) {
$ M* |1 v, I2 V- z0 z3 V        ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900"). u3 T. Y& D! k8 K
        if err != nil {- J; W% j; M6 o; a1 M
                return. W3 w6 z8 t" H- K
        }! |- [  G0 t7 U' h0 S
        conn, err := net.ListenPacket("udp4", ":0")6 |6 \" i4 Q' I0 _  W* z/ b
        if err != nil {+ d1 V* c! k3 p/ D  y% ~5 E
                return
! u  I* ?7 W1 `9 E5 N3 d        }: K( Z6 f$ r5 D4 h9 B5 x
        socket := conn.(*net.UDPConn)3 J/ q: K: `, D
        defer socket.Close()
# p/ J& S2 X9 N' s, L        err = socket.SetDeadline(time.Now().Add(3 * time.Second)): H; |7 t/ e+ [: Y
        if err != nil {
$ s( e) y$ q  b$ _3 i                return
9 @8 N$ T  O. ~! }0 v4 X        }
# l! B! X5 K2 s2 Z; s# }, ?        st := "InternetGatewayDevice:1"
" X: }8 ?" ?/ b4 U        // 多播请求:M-SEARCH SSDP协议定义的发现请求。
$ k8 T9 a4 D: |( q. o        buf := bytes.NewBufferString(
+ O$ s" g2 Z2 F# |6 `, ]" P3 S3 ~8 m                "M-SEARCH * HTTP/1.1\r\n" +0 ]0 O/ q% e+ Y5 s4 W1 b8 t5 q
                        "HOST: 239.255.255.250:1900\r\n" +
1 F* _& k; H( \5 G& v                        "ST: ssdp:all\r\n" +; f9 u4 m9 b$ Q
                        "MAN: \"ssdp:discover\"\r\n" +
( H: \5 A7 i+ Q* h  ?1 h: x  }                        "MX: 2\r\n\r\n")" f) \! _6 P2 D- K; ~+ Q+ A0 h
        message := buf.Bytes()' I& e5 j9 G9 I: t1 u2 c; f
        answerBytes := make([]byte, 1024)
. g1 E& {7 q* _* g! b7 y- P  G        for i := 0; i
5 i0 y. q# v. R添加端口映射" Z4 H: d. u! Z
向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port做映射
  u5 Y9 @4 i9 N* H# H0 y) wfunc (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) {
1 [4 {. z# `6 D, O' z; a( p, q+ p        // A single concatenation would break ARM compilation.4 @! `$ \; U6 |) l0 W. r6 C& {
        message := "\r\n" +
# O" T! W; I7 w  K. q                "" + strconv.Itoa(externalPort)4 @0 g) @0 E, L% G/ H2 l- O2 T
        message += "" + protocol + ""8 X. N7 a& K8 f! @$ k
        message += "" + strconv.Itoa(internalPort) + "" +  a, E& M1 {1 ~* H# P; k' d; y+ C3 m! b
                "" + n.ourIP + "" +
' i. |( G8 Y8 `" {                "1"
" X. c5 t! X  z. E/ r        message += description +! _1 c! F; s/ A; [; v6 r
                "" + strconv.Itoa(timeout) +( N) f( L" r( {7 t4 O
                "
"+ X0 M. |# R4 Q# D# R
        var response *http.Response
! T% y% v7 _3 D  p2 ]        response, err = soapRequest(n.serviceURL, "AddPortMapping", message, n.urnDomain)( f5 l6 P5 e3 R/ B4 M2 B6 ]
        if response != nil {3 w" I3 I, w1 ]/ c3 f* _0 A, O
                defer response.Body.Close()/ n( P! \# b2 ^3 g* K
        }
5 v4 a5 S+ ]) l+ h        if err != nil {* g' c  p2 @7 D
                return6 @, o: A0 S6 G; r; ^8 k6 I
        }
/ t9 m* G/ G7 N, a6 j' X        // TODO: check response to see if the port was forwarded
+ o3 r. h. C* X: ?8 y$ g' a        // log.Println(message, response)0 I8 O9 J: Q5 @, ?
        // JAE:
# Z' a4 r7 q2 K6 J7 ]9 @$ w        // body, err := ioutil.ReadAll(response.Body)# I7 V* D- @5 G8 |
        // fmt.Println(string(body), err)
9 L! F0 x; h, O/ {* I& s8 E3 V        mappedExternalPort = externalPort* m: P. X$ q" Q+ M
        _ = response# F8 b; j: J0 z
        return
: E! ]9 U2 f5 f6 t' d* U: e}" h/ s6 [3 {( x: p
删除端口映射
/ s5 G; Z6 y0 H& C向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port删除映射关系
- s% v  x7 p, L8 I( ~func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {2 k, U- g* h* u
        message := "\r\n" +
' v0 ]3 t5 n4 N9 |                "" + strconv.Itoa(externalPort) +
5 X1 o% a1 C) U+ Z! z                "" + protocol + "" +
+ ^/ n, F) o& |0 @5 D1 n6 |                "
"& J1 c0 J$ ]9 f9 S9 k6 N; p
        var response *http.Response/ |! h/ ]. p! a6 a  D
        response, err = soapRequest(n.serviceURL, "DeletePortMapping", message, n.urnDomain)$ O8 a! I" d3 i- @) o  ^  P
        if response != nil {
! |2 ]7 j6 s+ V# ^) `. Z+ N& i                defer response.Body.Close()) j0 o* y7 j% I5 v2 |2 x6 }
        }8 {% g0 R) t0 o4 C
        if err != nil {2 C6 L6 R' G. |
                return
. l* D: F; i5 K# I0 m( k8 B3 j: W- o        }; ]) {+ O/ ~9 c
        // TODO: check response to see if the port was deleted
( P$ U  R) D" r        // log.Println(message, response)
7 t3 b' x% Q, V. H2 _% e+ o9 \        _ = response" N2 \0 a7 X1 j  |$ t4 m( w* @
        return1 [' X; F, a! g2 |
}
. ?2 Z% w' O. x* v8 V获取映射后的公网地址
* [1 \8 p) Y, b6 m4 D) r' dfunc (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {
/ k) k( s$ ^; D9 s. K$ S* \        info, err := n.getExternalIPAddress()/ a/ _* U, l* O) J- ?
        if err != nil {
% A. M$ j4 M2 Z& ]5 a$ Q0 W" D3 N4 I                return
3 P- f9 y4 C5 D9 `& s" c3 U/ G5 X1 ^        }( U3 h. A, u! b! h: g" C( k
        addr = net.ParseIP(info.externalIpAddress)" ?: I$ h! [8 j& M
        return; M! U9 P& B* u$ C1 |
}
. X9 H& I0 Y6 ~+ K! H& J' Z% xfunc (n *upnpNAT) getExternalIPAddress() (info statusInfo, err error) {1 |- Z" @, l' y6 f1 L7 t* c0 d
        message := "\r\n" +
$ V. E& c2 {8 L. |3 T0 S# W                "
"
( G# H# O2 o) s        var response *http.Response
, _& P' F" W2 {: _, `        response, err = soapRequest(n.serviceURL, "GetExternalIPAddress", message, n.urnDomain)
+ Y3 P. X5 \& B7 [7 P        if response != nil {6 M1 F% @1 W& T/ ]# l' w) d
                defer response.Body.Close()
7 ^2 |4 o: ]6 M2 a- j, M1 X        }* R3 y+ _8 i8 d! D
        if err != nil {
8 t2 `6 v2 H) |- A# ~; u* ]                return4 Z* L! i5 q" I/ L* `& Z
        }8 m! d5 G+ C! w) a* z/ v
        var envelope Envelope
" [2 _. v& K0 N; J        data, err := ioutil.ReadAll(response.Body)
! s! z. }" B% R1 y# E        reader := bytes.NewReader(data)
# O) m6 L3 n) `! M        xml.NewDecoder(reader).Decode(&envelope): M' z4 ?4 k8 N: m% I
        info = statusInfo{envelope.Soap.ExternalIP.IPAddress}, c2 C% h* D. E* Q/ N' O
        if err != nil {% N# k/ l9 d0 Z' v
                return
5 O6 `& h! i" V" h! h0 e        }, u# `* `3 a  q
        return  O1 ^  C6 k2 H1 E: o  C
}
3 g' N: x4 N; a) i感谢比原社区开发者Derek的辛勤写作
: N: Q1 d/ O/ n: Q9 E如果你对比原项目有兴趣请添加微信号:matrix2140 咨询,备注blockflow
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

V刘晨曦 初中生
  • 粉丝

    0

  • 关注

    3

  • 主题

    14