Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

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

V刘晨曦
170 0 0
简介
& E, g, [6 B5 E0 y/ p0 d7 |https://github.com/Bytom/bytom
5 o* z% J5 [  |; J! Z本章介绍bytom代码P2P网络中upnp端口映射
4 |( c: D3 B# u4 P5 P! J3 W- H: R/ v. [: b1 F/ v
作者使用MacOS操作系统,其他平台也大同小异
+ j( }3 b1 }: S# K0 C
+ `1 w3 R! U4 [( m' ~& Y

0 Z: b3 e' a4 p, V, a$ HGolang Version: 1.8, ?/ ?' I0 Y3 m6 @9 H! O
. |. p+ T2 i. q& O+ o. N' B
UPNP介绍
- Q8 {5 |- g' _5 @, OUPNP(Universal Plug and Play)通用即插即用。UPNP端口映射将一个外部端口映射到一个内网ip:port。从而实现p2p网络从外网能够穿透网关访问到内网的bytomd节点。2 J# J, _; n1 q
UPNP协议' @. I( p  F7 W3 q
SSDP(Simple Service Discovery Protocol 简单服务发现协议)1 o, @9 j+ w  y; T) f; B
GENA(Generic Event Notification Architecture 通用事件通知结构)6 s" o/ w3 P1 y9 n' S
SOAP(Simple Object Access Protocol 简单对象访问协议)
( ^& d; T5 v, O5 p) d, N- bXML(Extensible Markup Language 可扩张标记语言)
' G1 z3 G4 J# P2 l2 o2 hUPNP代码
! Y2 n+ n( C/ ?& s, i, a** p2p/upnp/upnp.go **. K8 Y( A7 ^; c+ C+ Y- V
发现网络中支持UPNP功能的设备, F( A: D" Y& j+ n3 h9 @: H' g
从网络中发现支持UPNP功能的设备,并得到该设备的location和url等相关信息
7 r5 m1 P3 j7 v( @+ F) L8 Gtype upnpNAT struct {
0 {7 K0 C  v! P/ s; D9 z3 J, T        serviceURL string // 设备的描述文件URL,用于得到该设备的描述信息
7 r  q0 n4 g2 Q        ourIP      string // 节点本地ip地址
* ^2 ~/ J; r7 K( O! _6 \( O) B' Z4 n        urnDomain  string // 设备类型
7 m$ k& O, Z; Y, R: z$ v}
! m4 x4 T7 h# i( ~func Discover() (nat NAT, err error) {
. @) k( {( ]) a3 a2 M. ^% e7 l( [        ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900")
; S4 s  q3 K0 o3 c& v3 x4 L        if err != nil {
4 P# C# ~4 L" {8 o3 t( Z                return8 M7 p* t/ I7 e" P
        }$ c" F! T. c. b
        conn, err := net.ListenPacket("udp4", ":0")
, l4 m% x2 q3 c: I: B4 h; Q        if err != nil {
8 }7 @3 i: q- P6 C% ~. ^                return
5 g8 a5 p+ @' L" |/ c        }# C6 V2 t7 j0 p) l; T" x2 p1 u. s
        socket := conn.(*net.UDPConn)" f, t0 R+ C" C; C, o! |' j
        defer socket.Close()3 N; R: H- T- v+ s3 V
        err = socket.SetDeadline(time.Now().Add(3 * time.Second))
) K0 x) \+ O9 G* f! g# m        if err != nil {
7 G8 ^: I0 S- X+ ^1 O                return" g  A  ~( B* b
        }; H* H% }: p& i' m% h& d
        st := "InternetGatewayDevice:1"
: w4 r# C( o- Y/ k* c! k7 q        // 多播请求:M-SEARCH SSDP协议定义的发现请求。
7 ~+ V  ^+ |( |8 J5 n2 a& k$ e2 V! d3 [9 i        buf := bytes.NewBufferString(7 k: U+ T1 `& Y9 q, T
                "M-SEARCH * HTTP/1.1\r\n" +/ |% w; p* u% M) h9 o0 x3 k
                        "HOST: 239.255.255.250:1900\r\n" +& f$ Q% }4 t9 ~1 w7 Z; k: N% ~; k
                        "ST: ssdp:all\r\n" +2 s7 w* C/ C" V8 {$ C! R6 o7 q
                        "MAN: \"ssdp:discover\"\r\n" +. {  X; j: r2 w, u" Z/ }* ?* J
                        "MX: 2\r\n\r\n")
5 D. [% N7 _# y" [' p3 d+ j        message := buf.Bytes()
' L+ J! x  p3 }8 m* @        answerBytes := make([]byte, 1024)7 A4 y+ u- C  K, l, T" [
        for i := 0; i
0 W/ C. x/ s5 s3 }; n- X" W添加端口映射
) z  R% b5 x; O) }0 ]6 e" c向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port做映射
) L0 X) Z2 a9 d- N% h- d: ufunc (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) {
- B8 Q0 }: E: j0 t1 x* k7 U0 X& N        // A single concatenation would break ARM compilation.' ^( q4 r, l$ P. l* V$ p
        message := "\r\n" +6 U  G$ s' g5 {& e$ d' o
                "" + strconv.Itoa(externalPort)
  @$ o3 X6 H: a" d- {! k9 Q9 B        message += "" + protocol + ""
% \4 ~# U; i' V( c, x& P        message += "" + strconv.Itoa(internalPort) + "" +
9 |0 g. \" Y/ {7 t# [# x5 k  P4 S5 h                "" + n.ourIP + "" +5 K! Z7 a; A7 f# A0 `' J
                "1"
7 |1 q' x+ W' P: l$ G6 w* n. F' T0 d        message += description +4 w: G+ d/ r" D# N! m. ?3 y" W
                "" + strconv.Itoa(timeout) +
4 L% [6 W, R! h1 n                "
"3 N% D  T9 L3 ?6 X7 B5 @% c
        var response *http.Response% }, e4 L8 m( m! L
        response, err = soapRequest(n.serviceURL, "AddPortMapping", message, n.urnDomain)
/ w  B: x% d! ]6 J# B( ~/ {3 V        if response != nil {! f# u2 ^$ G2 V1 \+ L( K
                defer response.Body.Close()' {/ p  h. b( p# S
        }1 h5 M: Y  w3 ]+ u8 j( o9 P
        if err != nil {
% f+ b. N# M6 ]8 Q8 B0 _+ S' U                return
. _4 \$ P. k8 T* J        }
9 ^/ m& @# u/ M/ G% f1 P        // TODO: check response to see if the port was forwarded6 L* ?! I) K, _/ n& b- ?* }
        // log.Println(message, response)/ y% S' v" ^* ~1 X6 g! Q8 C
        // JAE:7 B8 c; S4 q/ t# d9 ?, Y% _6 B( M. U
        // body, err := ioutil.ReadAll(response.Body)' ?/ t0 c) `5 D/ X, _. m
        // fmt.Println(string(body), err), F; u3 S" M/ i8 m( Z, q+ O' n
        mappedExternalPort = externalPort/ D8 l. Z8 X( s  e: R3 ?2 ?* ^" K8 k
        _ = response
' A+ C9 b% m, h2 _4 w        return
! x% t" i8 j% D& j0 L5 \: c! @}
4 x$ t. c/ l7 Q( F删除端口映射
5 i5 {; V& N7 ~- i/ @向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port删除映射关系1 e3 I( A; y6 d+ r- @0 U9 A
func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {3 y1 m  X0 r, _# V
        message := "\r\n" +4 Y# J( z) K! n8 S; ?1 X
                "" + strconv.Itoa(externalPort) ++ z0 m6 }( u+ c: \5 z9 O
                "" + protocol + "" +
; V; l0 b: n% U0 h) [1 U. X. p                "
"0 z9 y% h3 a5 s4 x! M
        var response *http.Response
0 ?, B0 c* ^* @5 m5 o3 M        response, err = soapRequest(n.serviceURL, "DeletePortMapping", message, n.urnDomain). e  \' }' z7 F9 Y) x9 A3 T. M* i; E# e
        if response != nil {
/ Z4 ^( f  I0 j' a0 ^: Y                defer response.Body.Close()9 d" O6 V0 ~+ T2 n! p" Y" L
        }+ a0 s. C, ^4 Q1 F! N+ ?
        if err != nil {* x! _" w5 @) ^" a( W% _- }
                return3 P( Z2 v# j) X9 D4 W" D
        }
6 y/ R( q4 g, {- Z# F        // TODO: check response to see if the port was deleted
8 k9 x: ?" P  r3 V        // log.Println(message, response)
8 L( O% c4 K, G+ y8 R% T; \        _ = response! F* n3 y! h" y8 x9 I
        return
) X! C: c# M# y- }* D4 {}
- H% m5 J8 A- g& L# k获取映射后的公网地址( u5 I( ]& y) X
func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {
) b/ Z" }- N) g+ p) D0 P; p7 C        info, err := n.getExternalIPAddress()* V$ j' E, h! N  N& Z" e7 e" D  Z4 `/ D
        if err != nil {/ i, i7 B9 a' E7 p- P
                return8 ~6 P+ r9 }2 y1 V4 U4 H
        }* y0 O1 g# H4 S8 X9 M0 v
        addr = net.ParseIP(info.externalIpAddress)
7 G& d3 P7 L- n: P        return2 m% A# f* k& U* @& K7 Q* N( {5 a
}
  c) \2 u* {9 ^" b) X8 lfunc (n *upnpNAT) getExternalIPAddress() (info statusInfo, err error) {$ E  ]) K9 G) I; q/ o
        message := "\r\n" +
2 c7 h, s9 `( D! K0 ~7 a. d                "
"
* F. M+ z% K* Z) S        var response *http.Response( D& {$ h3 {7 {6 J# I! T
        response, err = soapRequest(n.serviceURL, "GetExternalIPAddress", message, n.urnDomain)3 a1 _7 S' l/ r
        if response != nil {
+ x: H3 G, D5 z                defer response.Body.Close()
+ N: M/ g' E) t! k        }
* e6 T8 j" P5 O! e" [        if err != nil {
- V( Q8 k* W2 ?9 b" m# k0 B9 x                return
4 k+ P$ ~. U+ k! v3 f7 l1 ~        }- _% q: p; _" I6 J' V, I
        var envelope Envelope
: e/ i( g$ Q( d. D7 m6 @' n        data, err := ioutil.ReadAll(response.Body); A  b4 G' z: f4 V4 p; I+ a
        reader := bytes.NewReader(data)
$ m1 k/ A; I8 G2 G6 t        xml.NewDecoder(reader).Decode(&envelope)
3 m/ b1 z" `& J% ]$ O" o  R        info = statusInfo{envelope.Soap.ExternalIP.IPAddress}, T0 F, h0 [/ r" }
        if err != nil {; d! Q: ^/ T. U" _, V" U* \; {- e& M
                return1 s" ^% ~6 ^4 _
        }
4 S8 `1 }, S2 {1 b0 R  A  Y# q        return
7 K! J) d$ D3 D7 |, l4 o2 G7 t}* S* O) _2 P5 {. O% A, L; H7 {+ j0 i
感谢比原社区开发者Derek的辛勤写作0 y# ]$ h. U% W
如果你对比原项目有兴趣请添加微信号:matrix2140 咨询,备注blockflow
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

V刘晨曦 初中生
  • 粉丝

    0

  • 关注

    3

  • 主题

    14