Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

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

V刘晨曦
158 0 0
简介; b& a7 m( \9 _( b
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操作系统,其他平台也大同小异
* c+ p5 N1 a* C7 {

7 }0 y  M( S+ f" t9 C: F1 ^. 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 简单对象访问协议)
2 Y$ v7 B' q0 {. LXML(Extensible Markup Language 可扩张标记语言)
6 p1 G. O/ F3 R6 RUPNP代码
. F  f2 P- N. ]+ l# D7 P1 ~" U** p2p/upnp/upnp.go **
' {1 e# O+ ^2 O' b+ C4 _发现网络中支持UPNP功能的设备
" O: }+ ~& F# n' o$ C2 F% u从网络中发现支持UPNP功能的设备,并得到该设备的location和url等相关信息' R3 S0 G3 G: Q2 c! _0 l3 ]# L
type upnpNAT struct {
. @$ u: g" ^/ P# Q        serviceURL string // 设备的描述文件URL,用于得到该设备的描述信息7 d  L2 V( `' B+ r& Y# m$ w
        ourIP      string // 节点本地ip地址
1 S; ]% t3 f4 ~4 n8 }        urnDomain  string // 设备类型
7 T+ x1 Y  [% D}
, F$ Z" `" N6 Q0 A  Afunc Discover() (nat NAT, err error) {
' K" h8 N# h$ v8 U7 Q        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
- m4 M! U" o" }        }
; e2 W: I0 g, K) M* E        conn, err := net.ListenPacket("udp4", ":0")$ U' M+ J2 C8 {" M% j  P( h
        if err != nil {
6 D& c2 T' Z! Z. A                return. o: H% v8 I( `3 G
        }
4 c: R0 B3 D: q/ b) F        socket := conn.(*net.UDPConn)+ a) K9 b% [1 F: F9 f( P" q1 X
        defer socket.Close()
* E7 f# p4 l: [) ?. {        err = socket.SetDeadline(time.Now().Add(3 * time.Second)): `0 ]/ v" h- G, q/ K" [- e* L
        if err != nil {
& Y* {( F. v! k! L                return6 c- c( h. R, R/ K# _3 g- S
        }+ ~; a& m" p; C2 H7 t; S* ^
        st := "InternetGatewayDevice:1"
) k0 X, v  z2 o- ]2 l        // 多播请求: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" +
2 b! e/ A7 g- H                        "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" +
/ P2 r1 P# p, F                        "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" +
& ]* Q4 o7 }, S4 f# E+ R( d                "" + strconv.Itoa(externalPort); U. X: K% `$ ~  m7 I, |! H
        message += "" + protocol + ""
! e, k4 `* v) c% c- n- H/ ^5 i        message += "" + strconv.Itoa(internalPort) + "" +
0 ]# g) a8 k% ]9 h                "" + n.ourIP + "" +
- X+ R, O7 A* F8 u, k- m                "1"8 w9 Y, ~0 m8 l. D6 y1 d
        message += description +8 n* K5 o$ \; a" P( t( n
                "" + strconv.Itoa(timeout) +
0 X1 }+ K+ U) d, M# I! ]5 N                "
"
. G1 N+ ~3 w% Z2 T8 ]" c  g        var response *http.Response
- X8 h/ X/ T# Y8 Q        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()
+ e+ r( D' F9 I9 c# Y        }- i& X+ p' _: v1 F! ?8 A
        if err != nil {0 g1 u0 I7 s: R6 g2 q5 e. y
                return
! b, ]( _7 C# G( M  J        }
2 @/ ~" n1 Q8 U* r; f        // TODO: check response to see if the port was forwarded
( p3 D: A, I$ I; \$ P1 `        // log.Println(message, response)
8 X% y. D% i7 K# G0 c( a6 ?        // JAE:
: i" ]& k8 g7 S3 g% m8 w3 Q        // body, err := ioutil.ReadAll(response.Body)
! q2 H# Y8 b. Q0 w0 b4 U9 k- \        // fmt.Println(string(body), err)
; R- w6 w% k; w* x; Y: d        mappedExternalPort = externalPort& n! a3 v; I3 M; r) |: l! U
        _ = response
, L2 k& I2 Z8 T        return
, h' K5 k: v" b. Y8 ]& l) X}8 E- P- A8 p2 G7 h' j
删除端口映射1 o; h1 X" X& Q0 _% N; I
向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port删除映射关系
- J# D. F. A" x0 A5 k% U( Cfunc (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) +
' v, ~. ?( p$ Z0 J. I: O# @* k                "" + protocol + "" +
# R5 B4 T0 Y$ A4 o& c* N                "
"
# C/ _7 P6 q' Y8 W. D. `7 l        var response *http.Response
' d1 T+ y; ~+ x* Y" V% W6 ~+ w2 a        response, err = soapRequest(n.serviceURL, "DeletePortMapping", message, n.urnDomain)* U) e! b* H9 {" J  _" i2 z/ ?
        if response != nil {
# Q( E/ g, J5 ?6 V# Q4 R: I2 b0 m. ?                defer response.Body.Close()
! B2 J  D8 C) u8 W3 t( K        }
) L5 R6 ]' S. N        if err != nil {
+ U' J8 |- Y" R$ P, ]                return
% r6 G# Y% S0 F: n        }% _- ^# 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)
4 T; }0 G$ }4 `, m- T& y& W2 X/ V$ u        _ = response
% @- v; F2 @. `  J  U        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) {
0 S+ ]. O! p9 r( H. W# P! ]2 z/ R        info, err := n.getExternalIPAddress()
  [$ r( I7 }4 l9 p        if err != nil {
% @+ h+ a6 g" {- `                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) {
/ \# ?1 v/ N3 G' ]5 _        message := "\r\n" +( r. D& F2 b! x) z
                "
"
* n- I4 C/ @3 R# e2 v: G% R  n        var response *http.Response: T1 ?4 Q' s5 A1 X) S' }
        response, err = soapRequest(n.serviceURL, "GetExternalIPAddress", message, n.urnDomain)
( i8 t4 g1 Y3 Y. S! u        if response != nil {
# T; b8 b6 q% o  n                defer response.Body.Close()4 Q5 U1 n. q- M% |0 R$ |* c
        }
- g: Q9 e  }7 ]+ E- G- P" D        if err != nil {
: ~" \6 s) q, |7 d% V' k1 }8 O6 v3 g                return, D% K1 b" H& `
        }" N; C5 _4 D) }) \* B
        var envelope Envelope
! t- f( ~  P* w* l% U        data, err := ioutil.ReadAll(response.Body)
9 p0 x" v: n3 Y, Q& S        reader := bytes.NewReader(data)
: I* q9 A( \! M        xml.NewDecoder(reader).Decode(&envelope)
2 I" ]: }) q6 J0 D7 P        info = statusInfo{envelope.Soap.ExternalIP.IPAddress}
6 a) {/ b, d" Q        if err != nil {9 X- o* N( K* W4 h4 z; V* M7 S
                return
3 T% Q- s" H1 e" }* A+ v' Z        }; a! Q2 a3 f0 z& ~8 E: I, S
        return
: _) c7 ~; S+ q8 @) d$ b}$ l3 U( o  |, \9 N3 ?7 E" Q
感谢比原社区开发者Derek的辛勤写作
. C& i& ]$ l2 v$ Z5 u. c如果你对比原项目有兴趣请添加微信号:matrix2140 咨询,备注blockflow
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

V刘晨曦 初中生
  • 粉丝

    0

  • 关注

    3

  • 主题

    14