Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

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

V刘晨曦
105 0 0
简介
+ R% [% Y  ^+ v, ~; P1 vhttps://github.com/Bytom/bytom7 A& v3 O/ S3 Q# R9 D; b
本章介绍bytom代码P2P网络中upnp端口映射' w$ l' y/ K  S: g2 m7 G
! D% r6 N: c7 V" M4 ]4 M
作者使用MacOS操作系统,其他平台也大同小异
3 Y4 m" b- q1 v" C7 e( d

$ h( L+ {$ F7 P  n: D! t% |; Q) u3 f
5 |6 j' ^+ E+ EGolang Version: 1.88 W" Y8 U" @5 v: D+ X* ?2 T
$ ~9 B% g7 ~9 G5 i
UPNP介绍
( p, K& n8 q* [4 b3 P: E7 @- y  WUPNP(Universal Plug and Play)通用即插即用。UPNP端口映射将一个外部端口映射到一个内网ip:port。从而实现p2p网络从外网能够穿透网关访问到内网的bytomd节点。& |$ J! N! B- s
UPNP协议
7 v+ E! `; _6 ~7 l+ I. I9 ?0 p0 ^SSDP(Simple Service Discovery Protocol 简单服务发现协议)
8 V) A1 e4 O1 O/ V2 lGENA(Generic Event Notification Architecture 通用事件通知结构)
1 m- f& l" ?7 p! B: t& bSOAP(Simple Object Access Protocol 简单对象访问协议)3 o2 w7 u9 a5 y" C1 J8 {0 O
XML(Extensible Markup Language 可扩张标记语言)
' a3 l5 N6 ?& qUPNP代码
4 v: B$ ^! v# s" b** p2p/upnp/upnp.go **- B' v) v) E3 ~' v7 l8 d$ X
发现网络中支持UPNP功能的设备
" T' z& G! R# G( \+ X从网络中发现支持UPNP功能的设备,并得到该设备的location和url等相关信息( z6 ?2 R0 b) e! U  k& K. A
type upnpNAT struct {3 ~! J% \+ F1 Y0 {# {4 r0 M
        serviceURL string // 设备的描述文件URL,用于得到该设备的描述信息
' y% I0 ?- B5 X3 m        ourIP      string // 节点本地ip地址* \0 O- m$ a5 I" Q  t
        urnDomain  string // 设备类型
. c5 H. E: h1 _! [( b+ Q}) h) z4 a% h5 r7 Y- i( x% B+ Q
func Discover() (nat NAT, err error) {
9 U) V0 @, s" z* a6 J, R        ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900")
, O$ D; b' o4 g5 J) y        if err != nil {8 C5 e+ I9 z) x" Y/ \
                return3 R+ ~$ f+ L- @9 v) B
        }+ ~# a9 n  j7 L/ ?, X! ?  G( }, `
        conn, err := net.ListenPacket("udp4", ":0"): l, h+ Q+ \5 ~$ j6 N8 u; O* O
        if err != nil {
1 j9 a% u' H. L0 U                return8 |* r2 ~6 K7 M* R* W: D' t. P
        }7 ?7 H+ H% T  h; P
        socket := conn.(*net.UDPConn)) k# c: V4 n7 \' S7 k' B, L8 r
        defer socket.Close()) s6 M! B, w8 g( K1 G4 _
        err = socket.SetDeadline(time.Now().Add(3 * time.Second))
* z; o+ i8 y& l* p9 A        if err != nil {3 ^1 c( o% {. l0 W7 x5 q
                return
) D8 u6 }" Z2 I' B& L        }
( l" t, f; }) J( y0 x  E, i        st := "InternetGatewayDevice:1"
3 O7 t1 M9 c  W+ Q3 a9 I8 x5 p' |9 c* F4 |        // 多播请求:M-SEARCH SSDP协议定义的发现请求。1 U6 \3 W  x4 j5 Z
        buf := bytes.NewBufferString(
( ]' q# E9 _; k# N                "M-SEARCH * HTTP/1.1\r\n" +
9 V5 E4 a/ N/ W, f                        "HOST: 239.255.255.250:1900\r\n" +
, s( g! e8 N0 T$ B" S1 B                        "ST: ssdp:all\r\n" +. T. a- c: ~: G# I
                        "MAN: \"ssdp:discover\"\r\n" +
) Z/ n( {* A/ }9 y                        "MX: 2\r\n\r\n")
; y+ m0 L" U: E) S9 U: N- h        message := buf.Bytes(); m/ N0 `) w7 F/ O* T4 ~
        answerBytes := make([]byte, 1024)
  `2 }" m2 Y6 h4 z: f& B; p% {        for i := 0; i
" y  x! r% c$ p  D+ V" x, X7 E  N添加端口映射
, J4 e( o& ]0 z" e向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port做映射
7 }- A" ~, R7 b: X& efunc (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) {
( i1 r: i# u4 W        // A single concatenation would break ARM compilation.
8 U/ d8 q; k! M1 [4 ~: S6 y- H1 K/ @        message := "\r\n" +
/ G/ ^, H2 u' ~- `* r' E1 [7 A                "" + strconv.Itoa(externalPort), ~0 G& ?  a; X0 O, k7 h" {* D# W8 `
        message += "" + protocol + ""
4 i  g( @9 M' Q" y" w1 @        message += "" + strconv.Itoa(internalPort) + "" +3 c& h8 w+ p% M6 v, \# [
                "" + n.ourIP + "" +6 P- X3 I: k9 y8 A
                "1"
* u+ L6 s9 w7 p$ |) N- W: N        message += description +9 [" u2 V* v. u3 i1 g
                "" + strconv.Itoa(timeout) +
! z# l, Z& Q& l                "
"4 `! I' U+ c  @0 b  M
        var response *http.Response% _  ]+ I, @( @% b( w8 ~
        response, err = soapRequest(n.serviceURL, "AddPortMapping", message, n.urnDomain)
5 Z" ~* d* g; F- M0 q        if response != nil {
& B1 i: K' z) [$ h3 ], ^3 l7 B2 j! B& A                defer response.Body.Close(). s+ H9 b6 ?8 |. s# C' {
        }
# d5 s& P( ]/ U/ v        if err != nil {
, Y4 h7 I9 T( [. {7 E  c. \                return
1 n* G6 c6 |$ [/ h' w2 M4 W4 s        }5 E! u7 F. u7 N) c, j* c
        // TODO: check response to see if the port was forwarded
/ b1 ]+ e3 s! _6 p        // log.Println(message, response). d% y8 ^! Q2 @4 S7 d+ e
        // JAE:
9 c$ _6 b/ k& C( g& D        // body, err := ioutil.ReadAll(response.Body)
: H, ]3 ?# b" ^" D1 \        // fmt.Println(string(body), err)
# n8 L/ Y0 s2 M        mappedExternalPort = externalPort
2 \% }+ J8 e. t        _ = response
  W) j+ d# a; U$ }        return0 S8 r; V' l3 V
}! `) t4 S' Z, I8 w
删除端口映射- X: d0 @& h/ J+ r
向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port删除映射关系6 q7 D& ~2 l# n1 o
func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {6 Y5 Q$ \$ S* |0 @. ?5 S
        message := "\r\n" +
3 ^' y7 h# J8 }9 e8 a$ ?                "" + strconv.Itoa(externalPort) +
5 w5 ]5 G  B+ J2 R, V. f9 k/ R% T                "" + protocol + "" +1 \* |% K& i4 a; C
                "
"5 l9 z5 S) I5 Z* k/ p% a
        var response *http.Response4 }5 o& w, O  L+ K  h# I3 R
        response, err = soapRequest(n.serviceURL, "DeletePortMapping", message, n.urnDomain)
# }" `4 O* @+ U5 T/ ]& T        if response != nil {
3 s+ Z& _# h, q6 N) |                defer response.Body.Close()
+ @# }3 s- @! a1 ?3 h8 i        }+ l+ Y; W, t/ }) i
        if err != nil {
; ^1 u3 T2 A0 J; h( o1 n- n1 E  ?5 e                return, X9 V4 j' G/ Y0 B, u! w$ C# ?
        }* x, W9 ~$ Z( L) x. M  e
        // TODO: check response to see if the port was deleted
( T! a3 C. _" D        // log.Println(message, response)9 i" _' z) e6 e3 q" t7 f
        _ = response
0 S% r; t0 `* O+ o2 D- W; \        return
' p  W) V5 d& ?* n}4 e1 b( G! q  k; |, y3 w. b+ R2 z
获取映射后的公网地址
, q7 V+ @; _3 ^; L# d, Sfunc (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {4 b$ \  A1 j& n. z0 _7 X3 c6 G
        info, err := n.getExternalIPAddress()
+ O8 N: y2 K. w- o# d2 A& {# `" h        if err != nil {
% a! Y9 l9 g3 u" n0 R" ?& ]0 h                return
. i/ m! x$ N, c" {3 @8 }        }9 z9 K/ u  S1 r! m( W2 \
        addr = net.ParseIP(info.externalIpAddress)- q' }7 d% {- _8 x1 b9 g  g! P
        return
& T1 ~: w$ Y( \9 ^}; B2 e8 n/ L9 W! ]/ s3 e- H3 H2 C
func (n *upnpNAT) getExternalIPAddress() (info statusInfo, err error) {
- X( ^( j, A, R; O( E        message := "\r\n" +
$ a+ |% ~. c, Z+ c# O  c# [                "
", [% ?: ]- ^3 d
        var response *http.Response5 ~: s: w6 N' o4 e) N, G* W
        response, err = soapRequest(n.serviceURL, "GetExternalIPAddress", message, n.urnDomain)
  P. a* v) q5 Z        if response != nil {( R2 {1 X- \, L0 ?2 h3 T
                defer response.Body.Close()
- h: B8 B2 F! p1 F        }
& l4 S0 S) u; d. M" P- a0 y; o# ]        if err != nil {
! N) u0 t# ?5 X* _# g, c% j. y7 Z: c                return1 M) R0 a% @6 _; a# M$ S+ s5 r
        }- E5 ?( z* Q. Z4 R. }
        var envelope Envelope
$ L5 B) L5 a3 \+ e! [4 T        data, err := ioutil.ReadAll(response.Body): V; }0 y( }$ Z. O6 a; T
        reader := bytes.NewReader(data)
" x3 v7 B* D9 e2 N- X3 x        xml.NewDecoder(reader).Decode(&envelope)/ y# I% Y& f/ K2 a1 q
        info = statusInfo{envelope.Soap.ExternalIP.IPAddress}
& K5 z( {, M: p& ]& q& k7 A        if err != nil {
# u; }/ p4 h$ Y# D. S                return
. I4 Z% x$ b' ]( W, w3 _        }
  d- {' u6 U3 f! k: s        return# L3 c/ q$ T3 U3 L" F4 x
}
2 B3 K, R: G+ \, U感谢比原社区开发者Derek的辛勤写作
! C  J: @' _0 H9 }8 a' f如果你对比原项目有兴趣请添加微信号:matrix2140 咨询,备注blockflow
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

V刘晨曦 初中生
  • 粉丝

    0

  • 关注

    3

  • 主题

    14