Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

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

V刘晨曦
84 0 0
简介
! }& A. ?% e* q) W3 u  R' chttps://github.com/Bytom/bytom$ ?4 `* _6 L% s! E& x& B
本章介绍bytom代码P2P网络中upnp端口映射0 s( |+ B! W8 D9 L% r: i6 e, a
- C5 A$ C( m  n
作者使用MacOS操作系统,其他平台也大同小异* ~/ K6 [- g3 [2 r

0 v3 [. j: G% e; n, i. `4 d) n9 r8 ?3 `7 g0 n, U- |# Y, U: a# t: |
Golang Version: 1.8
& e+ g+ r5 C1 }% [7 V* l
; W+ b( q! W  X) H# m
UPNP介绍  p9 ^: y' p7 i/ Y7 z3 |
UPNP(Universal Plug and Play)通用即插即用。UPNP端口映射将一个外部端口映射到一个内网ip:port。从而实现p2p网络从外网能够穿透网关访问到内网的bytomd节点。
1 F7 b* o" @# s; n& BUPNP协议. Q! K3 q/ S/ R
SSDP(Simple Service Discovery Protocol 简单服务发现协议)
  A5 F8 Y6 M% vGENA(Generic Event Notification Architecture 通用事件通知结构)
& y! @! Q$ u1 l9 X! gSOAP(Simple Object Access Protocol 简单对象访问协议)
2 c9 i% }3 `8 @$ _. cXML(Extensible Markup Language 可扩张标记语言)& u* m7 g; W1 ]+ @; c9 a
UPNP代码
8 e$ w- m! b3 M; x! H7 }4 _( d' L2 c3 l** p2p/upnp/upnp.go **' v5 Q6 z! R( r7 m. B! f
发现网络中支持UPNP功能的设备2 P5 R# |3 v: t, M
从网络中发现支持UPNP功能的设备,并得到该设备的location和url等相关信息% x: \' V4 Z$ |3 C( e
type upnpNAT struct {# U8 I% m# c$ Q
        serviceURL string // 设备的描述文件URL,用于得到该设备的描述信息
! ?! i1 S! m/ u* D# t$ ?        ourIP      string // 节点本地ip地址
3 H% Q8 D0 _, P& g, z        urnDomain  string // 设备类型
6 W' h: Y! n5 m4 ~9 x) U}! G( t2 P/ ^. p% {* K2 k( ]
func Discover() (nat NAT, err error) {7 T; X! O7 B4 i" y$ e/ E# q" ?
        ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900")0 j: j" M/ p: a9 b
        if err != nil {3 k% ?+ S( P/ z" A+ s
                return
0 N( Z1 a( b: x' L: W" `7 F        }
/ }( H% a) X. Q5 E        conn, err := net.ListenPacket("udp4", ":0")
2 ?0 U' s* o: K+ G- G        if err != nil {. Z+ _$ t6 j/ l9 @  C: l
                return
3 \4 d. c2 L2 d, p& f        }
6 ^# y0 M2 `' s1 s2 o1 }        socket := conn.(*net.UDPConn)
4 D% F( h( q% l3 U        defer socket.Close()
3 e+ J3 ]: j5 F+ [0 i- X9 g        err = socket.SetDeadline(time.Now().Add(3 * time.Second))
3 G5 y. m9 e; h; I        if err != nil {
5 U  d6 _5 E$ @+ ]. o                return2 f; A) P2 H; A1 B  j# \# H
        }
  z0 s6 H' d" x2 t8 Y        st := "InternetGatewayDevice:1"9 Q6 R3 W0 [/ w0 h5 d# F
        // 多播请求:M-SEARCH SSDP协议定义的发现请求。
9 f( I! u: J3 ~: d        buf := bytes.NewBufferString(' C. O$ L$ j; i* {  {
                "M-SEARCH * HTTP/1.1\r\n" +# |* A- b3 q' p2 t3 x' ?8 Z) Y$ i
                        "HOST: 239.255.255.250:1900\r\n" +
' U; C3 K9 n( I, `2 \                        "ST: ssdp:all\r\n" +9 u  G: [6 v/ ?; H
                        "MAN: \"ssdp:discover\"\r\n" +2 h9 E& J( {% G3 @7 T) O0 C
                        "MX: 2\r\n\r\n")
6 A* x+ s. q, b        message := buf.Bytes()
% U4 a2 K% V, n! o7 O        answerBytes := make([]byte, 1024)
; f$ R2 M4 g! ~/ d" a, q        for i := 0; i
- n; E0 f7 I" [( W5 J添加端口映射
! ?6 ^8 W5 c! A; n向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port做映射9 S6 V2 _0 R- ]! c5 I( ?) E
func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) {
7 x/ I- w# Q7 X: ~# I( g3 @' I        // A single concatenation would break ARM compilation., ^% D5 k5 v8 z- q" n: @
        message := "\r\n" +5 U( a# I0 ~7 ]0 Z3 m9 x% U) e
                "" + strconv.Itoa(externalPort)
( a0 f, ?/ W1 G( Y' `5 e, `        message += "" + protocol + ""- U# i, y6 ~) F! A6 `
        message += "" + strconv.Itoa(internalPort) + "" +) M# `9 u* J' Y" X# z/ c
                "" + n.ourIP + "" +
# V( x: j& x5 G& f' U1 j                "1"# T$ b; x$ V4 J* U
        message += description +! s, E+ E( W5 f* L* U
                "" + strconv.Itoa(timeout) +# ~7 Z) a4 x# E4 ?. y0 e7 ?6 a8 J
                "
"
; U, m) Y# f$ b+ G4 L: G        var response *http.Response$ t2 Q/ V- N' a0 W+ _! J' g6 ^
        response, err = soapRequest(n.serviceURL, "AddPortMapping", message, n.urnDomain)
4 X' H2 C* [+ n2 s9 U, Z        if response != nil {5 X% e% I+ i3 x( {% U" F
                defer response.Body.Close()* i: d9 w0 U1 y
        }. k" A' l1 a7 G
        if err != nil {
2 |+ C! g# [+ X4 u                return$ D; [3 o$ T( |9 k8 S
        }' Q& F/ T+ R% s* b: w5 `
        // TODO: check response to see if the port was forwarded! a4 u/ x6 [5 d. N1 ~: H. I9 g
        // log.Println(message, response)
& A% T& N8 b/ O        // JAE:
+ k! ]( e8 L! {6 |+ I5 G        // body, err := ioutil.ReadAll(response.Body)6 ^1 G( b7 V9 S5 a# }3 D! y
        // fmt.Println(string(body), err)- K- z7 \% p7 e2 q" |
        mappedExternalPort = externalPort& C' D% m  y2 w9 s8 i+ H+ T
        _ = response
* E' e3 y. b6 a$ t" J! G        return( H1 h/ n. p) w: i  r( d( x" W
}
2 h' B7 g! b/ f# ?" G  V& i) s删除端口映射- x8 m" K1 T  \6 x% x. X) Y
向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port删除映射关系
7 d! c% T+ [5 R- ~  g- ?7 ~: Cfunc (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {  y' R9 I* J% d
        message := "\r\n" +
' o- ], n) z0 S$ G9 Y9 v                "" + strconv.Itoa(externalPort) +
2 k3 _% `) U1 k% c+ W) q# H                "" + protocol + "" +
+ K" }+ x% g! U  R# J( d* v4 f6 {                "
"" c" A' w5 ?$ `1 ~
        var response *http.Response
' o- @6 }' l/ X; S+ n7 l) d        response, err = soapRequest(n.serviceURL, "DeletePortMapping", message, n.urnDomain)
' P/ Z  B2 _$ B( F        if response != nil {# ^+ B6 p% [( p/ v3 E& o& c- g: X
                defer response.Body.Close()" w/ l% {- }* R% K8 l
        }) b! i# o4 ]( ]. d! F# N4 F6 U: g& w
        if err != nil {- e7 x' b- Q0 G4 n3 q0 T( ~% d
                return
1 y, y, Q6 m! L# x+ N        }. w: G$ i) M" q+ D; V
        // TODO: check response to see if the port was deleted0 e) [) G% F, S  {. \% P
        // log.Println(message, response)
2 o: e4 a) c. p5 i  V. ?) g4 s/ U        _ = response
" c3 ~( F; z# _" C1 d        return$ k  R! Q( @/ K1 p/ N
}+ c7 L$ t8 P$ O, s
获取映射后的公网地址
. z2 Q. O: F: ], |+ W) Bfunc (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {1 i- M8 n5 P& x6 I- U8 k9 H
        info, err := n.getExternalIPAddress()
: w1 c9 e, r, H0 l" r9 c- u. I+ `        if err != nil {
6 J, W* x/ c2 r                return
$ E1 ^) Z* j' L+ [        }
7 j" h/ Z; V4 z        addr = net.ParseIP(info.externalIpAddress). l% }# H$ {0 L' h" {
        return$ J- Z' g! z% m' t1 `6 b( [. L
}
! [" c6 _/ J" E# z% ^func (n *upnpNAT) getExternalIPAddress() (info statusInfo, err error) {% ]# b7 S: E: X, ^
        message := "\r\n" +
, [  ]$ r2 U/ ~! c                "
"& N7 ^( G9 R% t5 O! a
        var response *http.Response% r9 Q; t4 `) r& A" w
        response, err = soapRequest(n.serviceURL, "GetExternalIPAddress", message, n.urnDomain)4 l# J7 X8 V( y
        if response != nil {
, h0 G- K, V: y3 I/ B; j7 w. o                defer response.Body.Close()
3 e5 \% w% ~; F' W& B: [        }, r& z. Y$ X1 q7 P/ U
        if err != nil {
: _8 k: s) e2 [6 ]                return
9 D1 g2 v- n* `, K        }. E6 G1 f9 {" W2 s7 O
        var envelope Envelope
) R# I- q) Y9 Y+ |" L5 R  @        data, err := ioutil.ReadAll(response.Body)
7 G! J7 X4 Q; G8 S* ~  O        reader := bytes.NewReader(data): I$ p. s- u: ?9 c: t; d
        xml.NewDecoder(reader).Decode(&envelope)2 t  M8 D: [2 N6 P! `" ?# k1 K/ N' [
        info = statusInfo{envelope.Soap.ExternalIP.IPAddress}
4 f$ t) H7 [: m/ e  `. n4 ]0 S        if err != nil {
  K/ c4 f/ v5 A                return0 u& A8 I1 o2 v
        }7 s5 U7 o; f% S3 Z* V8 Y
        return  A$ ^% ~1 W5 _5 z# z- L4 k
}2 V' J3 F- ~" Z; K
感谢比原社区开发者Derek的辛勤写作. s9 T4 X# Z: b5 l* ~
如果你对比原项目有兴趣请添加微信号:matrix2140 咨询,备注blockflow
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

V刘晨曦 初中生
  • 粉丝

    0

  • 关注

    3

  • 主题

    14