Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

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

V刘晨曦
83 0 0
简介
5 _7 l- K2 K( n1 Lhttps://github.com/Bytom/bytom0 J; N9 o( |+ E3 H. d9 |
本章介绍bytom代码P2P网络中upnp端口映射) P, V/ Y& O7 J1 I+ p! O

( X6 P/ G! z, z作者使用MacOS操作系统,其他平台也大同小异
, P, Q# Y7 x3 ~

& \7 J7 h, E% X$ z! U# c2 T: ^' m+ [+ n8 u
Golang Version: 1.8
! E6 ]& @6 n9 [7 p: n

% n5 h  x" H; B4 D; yUPNP介绍& x$ l4 L2 K) G7 E  D. \" s
UPNP(Universal Plug and Play)通用即插即用。UPNP端口映射将一个外部端口映射到一个内网ip:port。从而实现p2p网络从外网能够穿透网关访问到内网的bytomd节点。. {2 R$ ~* L9 L% T2 V* v" z1 Y) M& S
UPNP协议1 U& H  m. m6 n; D  {1 k- O" q- B
SSDP(Simple Service Discovery Protocol 简单服务发现协议)& P& R3 [' y+ F
GENA(Generic Event Notification Architecture 通用事件通知结构)
7 J8 l0 V0 Z# s- u8 NSOAP(Simple Object Access Protocol 简单对象访问协议)
, h7 E3 A& r  W/ hXML(Extensible Markup Language 可扩张标记语言); k' G1 P# U$ z& o
UPNP代码
$ c* p; y  }8 F9 S, [" u  j** p2p/upnp/upnp.go *** v: e8 {" S# x& k' p* r! j
发现网络中支持UPNP功能的设备
# j4 D2 X6 J4 ^3 P5 E6 k/ R+ g从网络中发现支持UPNP功能的设备,并得到该设备的location和url等相关信息* v7 m$ g' U3 ^) @6 f3 s
type upnpNAT struct {& q) E; a( {  o% a. @# x: U
        serviceURL string // 设备的描述文件URL,用于得到该设备的描述信息
' V2 x% h+ C& S" B. e) U0 D        ourIP      string // 节点本地ip地址
7 P) K  k' P" T5 L        urnDomain  string // 设备类型
& o3 H% J0 y* w}, i& k6 l! G# P' ]2 B( j) @
func Discover() (nat NAT, err error) {  ]2 r/ r  g% f
        ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900"). V0 T1 A, x, u: T8 F- c) D+ \
        if err != nil {9 _) n1 L& _+ v; F0 z4 R) V
                return
- n6 O8 d1 p- l8 d0 f8 D. e7 h) S& i) I        }
) m0 m! p) s1 F        conn, err := net.ListenPacket("udp4", ":0")5 _! G% q) @* Q7 \
        if err != nil {9 j- b9 t( `! Z8 W9 I/ v
                return2 a7 h  I) t; B# n8 ^8 ?
        }
4 z; K! p  Q" }9 n' \2 Q        socket := conn.(*net.UDPConn)9 J' [9 \2 P* y. R' i/ w# l
        defer socket.Close()
5 z/ X% l# ^6 E1 B4 z        err = socket.SetDeadline(time.Now().Add(3 * time.Second))
: M" w& ~, q* K        if err != nil {9 ~$ H% Q1 I& R: x2 q8 _
                return
1 `! q7 W- ^$ t$ L0 [+ V  Q        }
7 I6 J5 {# n9 X) d- j2 f        st := "InternetGatewayDevice:1"( X( E' D8 v$ ]+ m; S
        // 多播请求:M-SEARCH SSDP协议定义的发现请求。0 R2 S5 k4 }. }+ p, D3 |
        buf := bytes.NewBufferString(
8 s7 L% G; }1 k; {% g. E% h                "M-SEARCH * HTTP/1.1\r\n" +) n+ l; n: G! L, \7 c% F) d' U6 J
                        "HOST: 239.255.255.250:1900\r\n" +* s, M' P2 v: _8 q
                        "ST: ssdp:all\r\n" +
% @* p7 E# R5 s7 q( @                        "MAN: \"ssdp:discover\"\r\n" +) j& H5 g2 i# J( _; F+ R& n, s2 N
                        "MX: 2\r\n\r\n")' L! d+ I6 `2 D  E3 _
        message := buf.Bytes()
0 A" o4 Y$ I* a- R" o0 S        answerBytes := make([]byte, 1024)
. ]2 Q6 ?7 V* c. M7 w# T% D0 G        for i := 0; i
6 N& |- G& R4 @% `$ S& ]- T% j" J添加端口映射8 }0 M5 z& i! t0 f
向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port做映射2 x. m& s" l+ o- u
func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) {
* Y! T6 B. k" O+ S  M% U        // A single concatenation would break ARM compilation.5 m& Y9 x1 U  b5 f- V( U" y
        message := "\r\n" +  o" I: A- i- q$ V
                "" + strconv.Itoa(externalPort)
+ j" F6 a4 k& x5 I$ B/ \& O  s2 A        message += "" + protocol + ""
2 r' P! ]- w  K/ I; O5 h( l# O* B        message += "" + strconv.Itoa(internalPort) + "" +0 Y4 w2 k+ R, T; [
                "" + n.ourIP + "" +( }/ r) J4 L9 I7 x9 r
                "1": c: [% Y* K7 K1 N; p$ Y/ q
        message += description +% f/ L/ W1 D* G2 m4 |
                "" + strconv.Itoa(timeout) +- h4 e2 `- ^. X6 i& F
                "
"
7 e- f8 `- w, o        var response *http.Response
5 c/ R, \6 Q4 ^$ |( c9 w) L" R        response, err = soapRequest(n.serviceURL, "AddPortMapping", message, n.urnDomain)& S6 t6 X% ^0 V' B7 C6 u: |
        if response != nil {3 L$ v( J5 F1 J" _! J& R
                defer response.Body.Close()
2 o5 G6 {# E; h# F& ^        }
- B: k& F: R' h. m3 t3 J" C$ M        if err != nil {. z" Z; q' D( c
                return
3 Q3 f4 W4 @5 I  ~% S: }* u( o        }
; z7 q! S( p8 n* m% v) L        // TODO: check response to see if the port was forwarded% m* S5 V" A' e/ C$ ]
        // log.Println(message, response)& \$ b' @; ?- Z$ y0 z3 H
        // JAE:
9 I- |, P* Q" a+ x        // body, err := ioutil.ReadAll(response.Body)
' {) ]9 G- \; k5 H5 b* R" P  }2 e1 r        // fmt.Println(string(body), err)
! O5 ?, v0 L9 @+ b8 J0 Y        mappedExternalPort = externalPort
* d+ o, U! U" B* \8 K0 u  Z/ X        _ = response9 J" z4 E# z8 B9 o0 d6 C! n
        return- ]: a" J  t" J, ]' w+ i5 n/ m* @+ \
}
% X2 Z% q$ L, T5 `4 N: i删除端口映射# w5 O9 D8 p; y1 [: O) s# S/ {
向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port删除映射关系4 c0 U2 d  F$ P( D; ~& J: M# u$ Y, y, O
func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {
6 n  Q$ q3 M/ ~+ k# W( {        message := "\r\n" +4 F1 I) ?3 O( U, z; A
                "" + strconv.Itoa(externalPort) +# _9 e/ J6 t$ M1 @1 A& F
                "" + protocol + "" +/ I3 W4 Z' y6 G9 c6 c8 y1 R( a4 h
                "
"
7 o1 u0 b" v( B5 K2 L' ~        var response *http.Response
; V+ P  p7 a& J2 G8 b) O        response, err = soapRequest(n.serviceURL, "DeletePortMapping", message, n.urnDomain)
% V3 M, m6 w! @+ X        if response != nil {
2 a) L% o6 a, p                defer response.Body.Close()
- j  W, k1 V5 ~# w        }
4 x+ l, ]8 P& V* x% D5 V        if err != nil {
+ ~6 P: \* o- Z( G: u& g                return4 V6 d$ c+ v) d% o- \) _
        }" ]0 G' H7 Z. [0 G3 P( G$ O9 j
        // TODO: check response to see if the port was deleted
; y& J- q3 `7 S# @        // log.Println(message, response)5 A$ F' ~- t2 }+ M8 J
        _ = response+ w- b( p3 P& i* w) f
        return
0 J+ p+ \) \* k}  {! d; p6 P2 z0 H6 J0 ?
获取映射后的公网地址7 p* u8 n* R0 c) K5 a) f& k. V
func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {' U" n' }/ J# M% Y
        info, err := n.getExternalIPAddress()* A! S/ V: C6 }. F5 E
        if err != nil {; d+ L! `9 m* J
                return% A. N  X: n9 b% b9 }/ |
        }
( `' e3 b% M" H( Z5 J( z        addr = net.ParseIP(info.externalIpAddress)
: U& U& {" \3 {  ^        return
! M" B3 N! ~0 w- X, N0 x( H; Q}% U' E, s# g- @6 o; @. b' K
func (n *upnpNAT) getExternalIPAddress() (info statusInfo, err error) {
0 b( `2 {3 w; Z, Z        message := "\r\n" +! I' s4 B: j8 T$ v% P' e% c0 @3 N$ n
                "
"
3 X- |, s/ a) M        var response *http.Response
+ {/ ~/ q' o' h6 ?& J+ r2 q        response, err = soapRequest(n.serviceURL, "GetExternalIPAddress", message, n.urnDomain)
$ E# ~: f- n9 A$ Z0 `        if response != nil {, \* I! G# g$ S3 H4 `
                defer response.Body.Close()0 f! U  q  Y- ]" j5 {
        }
& T7 ~. A0 t/ \+ {. U7 A( G5 X; s        if err != nil {8 ~- T9 O, U# R, z" G7 @3 R
                return
# V0 w( w7 G$ L$ c        }2 z- b. ~+ |. r2 G9 Y$ U
        var envelope Envelope
% K" R7 v1 G: e( O; |: u( S5 [2 t        data, err := ioutil.ReadAll(response.Body)8 o" a* j+ p$ ^- m0 N
        reader := bytes.NewReader(data)( Y5 a% |: Z0 B3 |# M
        xml.NewDecoder(reader).Decode(&envelope)- c9 T4 p4 v* q: R3 I! d
        info = statusInfo{envelope.Soap.ExternalIP.IPAddress}/ _" J$ r: z9 Q! ?: ^
        if err != nil {
& O, p" G+ C5 e0 m1 x* B  v' G                return
$ ]: P9 g  k4 L$ }; h7 M+ q        }
# q& `1 n# d6 h* U5 t4 \        return
) X8 M. W& B6 |, G8 g, F}
0 ]- v4 U- T# W# V, a感谢比原社区开发者Derek的辛勤写作
- ?; [- Z7 z2 z1 \& ^如果你对比原项目有兴趣请添加微信号:matrix2140 咨询,备注blockflow
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

V刘晨曦 初中生
  • 粉丝

    0

  • 关注

    3

  • 主题

    14