Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

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

V刘晨曦
78 0 0
简介
! q+ r# c: _& w" u3 G; ehttps://github.com/Bytom/bytom# m: ^# i, }* q; W$ X6 {
本章介绍bytom代码P2P网络中upnp端口映射* ?; }2 S+ m3 T2 I5 F1 X5 C

- I! V/ [- U' @! [# }  g8 s8 J4 r作者使用MacOS操作系统,其他平台也大同小异( F7 Z6 |) b/ }  p) P4 j, J: F
- a  {! I. q* q. l# L

3 i: l! M8 N# ^) ^. ^Golang Version: 1.86 g9 P6 {+ L, _, \& n( |

2 r" b$ e: F% E! @0 ~& V3 K" [UPNP介绍% `7 y# _! d& W& S* c3 u
UPNP(Universal Plug and Play)通用即插即用。UPNP端口映射将一个外部端口映射到一个内网ip:port。从而实现p2p网络从外网能够穿透网关访问到内网的bytomd节点。
: ]) }  s$ _( g  t2 V2 h% iUPNP协议+ z" b2 f! W, z1 _/ ?5 U
SSDP(Simple Service Discovery Protocol 简单服务发现协议)' o! j, l( ]5 P) N
GENA(Generic Event Notification Architecture 通用事件通知结构)
; d2 X( O% U( V8 JSOAP(Simple Object Access Protocol 简单对象访问协议)
: O1 L$ R6 p% [% Y! bXML(Extensible Markup Language 可扩张标记语言)$ U& m0 q4 N8 `; m, s
UPNP代码( |9 E5 U  \6 @+ x  l
** p2p/upnp/upnp.go **
9 S' x0 D! W4 w发现网络中支持UPNP功能的设备
3 z; ]" z( m, S- W  u% m从网络中发现支持UPNP功能的设备,并得到该设备的location和url等相关信息
$ z( C' |' |- M' F& q% P7 j1 utype upnpNAT struct {
$ _! y6 K5 ^: q! L        serviceURL string // 设备的描述文件URL,用于得到该设备的描述信息
2 P( X4 Z# W9 t+ z        ourIP      string // 节点本地ip地址
4 ]# J3 T; F) w# M+ y) v        urnDomain  string // 设备类型
% f# p) c+ A& S}# z6 |. E. m/ j
func Discover() (nat NAT, err error) {
0 ~1 H: _4 B, ?' |        ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900")  u, Q( V6 S7 Z! O5 x) {& S
        if err != nil {
, A7 e3 N# [, w                return
! h* T  N8 n- k# k1 K        }# m% ^" B5 ^. j7 N- `* X
        conn, err := net.ListenPacket("udp4", ":0")) G$ Y; `* j! q4 v  Y. h( r% p
        if err != nil {
6 v8 q9 [  V" m: E                return. P3 p3 C( h8 x& x* N7 K0 U! X
        }$ \2 g: J4 ^8 I" p
        socket := conn.(*net.UDPConn)
! n2 e; h4 Z# j- p' U% _        defer socket.Close()) N" D5 Q3 N2 B+ |: ~$ M' U
        err = socket.SetDeadline(time.Now().Add(3 * time.Second))
2 C( h0 {! W. R6 w" M        if err != nil {
' {; P& I, Y1 A8 t0 ]0 C) Q                return& H9 j& C0 P+ f( h+ [+ x3 I
        }4 H( R3 R/ Q( {8 M
        st := "InternetGatewayDevice:1"
. N" a3 I9 J& z% W; I+ E        // 多播请求:M-SEARCH SSDP协议定义的发现请求。
8 \) y6 @) C' K. G* P5 ^9 ]        buf := bytes.NewBufferString(
, X* v0 G  S0 n5 b                "M-SEARCH * HTTP/1.1\r\n" +
/ |9 P3 @# S: V+ e6 ?" o- {" ?                        "HOST: 239.255.255.250:1900\r\n" +
; l! x+ l$ y7 h# N! Z8 k5 P0 I                        "ST: ssdp:all\r\n" +
! h& R" G. `9 A; ]6 f' E1 h                        "MAN: \"ssdp:discover\"\r\n" +
! z1 M4 {1 B5 {4 y; v$ Z                        "MX: 2\r\n\r\n")
5 q$ \6 K2 a: J) U        message := buf.Bytes()
) u, [) C+ B( f6 k+ p        answerBytes := make([]byte, 1024)
8 Y) {+ Z0 N& e9 O' t        for i := 0; i 3 U. t$ P. ]% |
添加端口映射% I- U- w  S( p, t7 o9 U2 \' v
向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port做映射: A- f$ T; o  n( L4 p
func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) {
- u8 H& r5 \: ?        // A single concatenation would break ARM compilation.+ m" s2 j1 E7 W: F# |4 [
        message := "\r\n" +
6 O4 O( {; f2 ]! i# ~8 ~5 m6 h                "" + strconv.Itoa(externalPort)6 P4 M5 b% f0 Z+ a1 i! H
        message += "" + protocol + ""
4 j( M) u: X, y        message += "" + strconv.Itoa(internalPort) + "" +
4 r3 P6 i, _, c! a: @! }                "" + n.ourIP + "" +- a* D# c& v- [. F7 w8 n  ]) Z
                "1"4 j3 g5 I; S' z6 \, E
        message += description +
) O1 P8 r, ]' E% H' |2 o  X                "" + strconv.Itoa(timeout) +7 _" V5 B/ Y1 u) M
                "
"
+ \4 J, `  T9 |- b- S        var response *http.Response
- Q' g& _* o7 t, {1 \        response, err = soapRequest(n.serviceURL, "AddPortMapping", message, n.urnDomain)
: E' P" h4 D7 h# V        if response != nil {  C+ K% B* z! v  a
                defer response.Body.Close(), s8 o  N, q# ]1 N
        }) `+ K7 h2 V4 O, r, J& N$ C
        if err != nil {
9 c* W! `( y3 ~                return
) f- ^) F4 g, T% y0 o        }
' f& R. z% C' e  l/ e* M/ }        // TODO: check response to see if the port was forwarded" |2 O6 U/ d5 Z
        // log.Println(message, response)8 [" `! j, Y) C
        // JAE:( J8 Z0 T% P; {$ ~: \) m
        // body, err := ioutil.ReadAll(response.Body)
6 ^2 d  E8 j9 X% a: W7 {2 ^        // fmt.Println(string(body), err)* t' q' B3 Z  u. X
        mappedExternalPort = externalPort
: |! M8 m4 y. S        _ = response
) A. S) e  n) ?# t- i% q" p. O& a        return! l" e+ I  ?0 H/ i7 d* I; d
}
# @% q. M6 g9 u8 g/ ~删除端口映射1 ^1 R3 }- u2 A' |! Y! B& d% o
向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port删除映射关系
: [% l8 O4 W' o# H8 h2 z$ ^7 Cfunc (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {
8 W5 i+ K: `6 i3 U8 b% Q& b( h1 x        message := "\r\n" +
: {1 V1 _% }$ D2 X* Q6 _2 i                "" + strconv.Itoa(externalPort) +0 w6 `, G& ^* a; M& A4 i6 k8 D
                "" + protocol + "" +6 R; x  ~2 U8 R  y) {
                "
"9 b7 r# _: r7 c
        var response *http.Response- P6 |) O: h! W) i" ?6 Z' v5 l
        response, err = soapRequest(n.serviceURL, "DeletePortMapping", message, n.urnDomain)
% x2 O- L- L; o  g& u        if response != nil {
) A# X' t2 R- M" L9 _                defer response.Body.Close()
# M6 j1 E8 o: {/ x; f9 y# f. G# H) a; c        }, ^* g( P9 y% ?( }6 u& J
        if err != nil {: f& k3 U" ]' p: n  U# r
                return: ]& l- |- z2 Y1 f
        }
! N+ V$ v+ w7 v' F        // TODO: check response to see if the port was deleted  w& R# X* L! w! _# B
        // log.Println(message, response)
- t- K) e( b" c6 L1 Z3 b+ P  f        _ = response# _7 n7 K' v2 d, i. L; [" E
        return# G6 g+ F) z4 J5 r9 W$ E
}
; b, L2 o* g4 N* @5 b  O获取映射后的公网地址" e' q, l5 F- \% N. s; }
func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {6 e5 t. e4 V  |7 K- d+ H5 h% S4 s
        info, err := n.getExternalIPAddress()( w' K6 e" r, S8 B' U
        if err != nil {# g7 V; y5 z+ W6 I
                return/ B# M0 G, ?: ~- g' N9 u
        }
3 D4 }2 t& r2 V% Z9 ^7 F7 F        addr = net.ParseIP(info.externalIpAddress)
; A8 V( Q( ]' y0 f1 a* S0 h        return
, @& y8 v8 r0 D( n% ]}1 }& {7 X. t8 T2 E# {5 k/ G% X
func (n *upnpNAT) getExternalIPAddress() (info statusInfo, err error) {
) ?, {: c% g9 b1 b) K% o        message := "\r\n" +6 r- g3 w; f! [0 O# w
                "
"
3 t3 b* }( c$ B' T9 ?* f        var response *http.Response
6 q, e: r2 v+ Q0 j: B        response, err = soapRequest(n.serviceURL, "GetExternalIPAddress", message, n.urnDomain)
6 ^! G( F5 j4 h4 X* a        if response != nil {
: h+ u9 p7 p; a# d# P                defer response.Body.Close(), @1 J: b+ K& }4 s) I: d* m5 B
        }' J9 s/ D+ {$ V; f
        if err != nil {
. t  `, J" p% [: Z" B7 m# y                return( o. T7 a! d5 Z! r: ~) Z- m
        }6 W% s0 b- c, g* _
        var envelope Envelope
6 A2 S* R9 Z; v. @9 T, _6 H        data, err := ioutil.ReadAll(response.Body)
- F* ]% @3 g6 O  t" F* W9 p        reader := bytes.NewReader(data)
) x  W4 a- o9 G; O7 L        xml.NewDecoder(reader).Decode(&envelope)( {& N! K' G' h+ x4 [
        info = statusInfo{envelope.Soap.ExternalIP.IPAddress}+ R" Q2 b! r7 `2 J
        if err != nil {
' O. m: f; L/ B8 c                return( K  ~+ l/ `- q6 T5 f" }/ t
        }
/ x+ M6 F! q3 n0 |& M        return
1 V7 r0 v% ]: V3 O# b# H3 q" R1 I}* D3 k4 j3 k, C9 U$ d) g# d- `
感谢比原社区开发者Derek的辛勤写作8 l" x3 H) M% \0 ~! R
如果你对比原项目有兴趣请添加微信号:matrix2140 咨询,备注blockflow
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

V刘晨曦 初中生
  • 粉丝

    0

  • 关注

    3

  • 主题

    14