Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

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

V刘晨曦
154 0 0
简介
- V! z" r5 g1 Bhttps://github.com/Bytom/bytom
+ M* g' l9 U  c/ H2 r/ z. @本章介绍bytom代码P2P网络中upnp端口映射9 ^# W! h0 |$ [2 q6 r: g! I3 ~2 x
8 N- W; p: u5 U! g- [! b  _
作者使用MacOS操作系统,其他平台也大同小异! i2 [0 I; N: V4 S5 D

, L3 H2 r4 o* s5 V$ H; x9 k: ]# k$ w/ t
Golang Version: 1.8( y$ M' ?" c) ]$ l3 n7 I

* t! A' ~1 v  ?9 A0 o* [UPNP介绍
1 P& |; I+ {; ~' vUPNP(Universal Plug and Play)通用即插即用。UPNP端口映射将一个外部端口映射到一个内网ip:port。从而实现p2p网络从外网能够穿透网关访问到内网的bytomd节点。
0 ~2 J! x3 q8 ?2 {UPNP协议7 k4 M0 t8 t' _( Q* C9 I0 B
SSDP(Simple Service Discovery Protocol 简单服务发现协议)
1 c) ~  Z# w4 f* Q. mGENA(Generic Event Notification Architecture 通用事件通知结构)
- W: ~) l( j, U1 L1 S. a( bSOAP(Simple Object Access Protocol 简单对象访问协议)& }, I  p/ [1 U
XML(Extensible Markup Language 可扩张标记语言)
+ y1 _# }, C* D. ?" B9 F% Q# l7 FUPNP代码, W) o% a. h+ I/ L$ Y* Z* b) p
** p2p/upnp/upnp.go **2 G5 s: o6 y# z) [6 c8 R( t' Z
发现网络中支持UPNP功能的设备; H9 r3 V: u1 Y+ F, y/ R" m+ {) ]' G+ S
从网络中发现支持UPNP功能的设备,并得到该设备的location和url等相关信息
9 t" M# [+ ~6 \9 C+ Z" v5 Wtype upnpNAT struct {
4 j8 }# U- H# a  D, Y) N$ R/ ]        serviceURL string // 设备的描述文件URL,用于得到该设备的描述信息1 h1 g7 |/ B- f9 M8 o3 ^6 W
        ourIP      string // 节点本地ip地址
' F7 H/ p  ]1 U6 i" H% k( c        urnDomain  string // 设备类型& t+ q* ?8 P9 J6 j9 I6 t; s
}
( H& F7 p3 n+ S3 p# z9 Afunc Discover() (nat NAT, err error) {2 D! n/ O+ ~3 Q$ Y+ D" B
        ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900")# r& C# r( ^6 I: r
        if err != nil {
$ u4 ~0 G8 T' J; N6 x. }$ X                return
/ Y) ^" U! |% t2 H1 I$ h: a        }1 j# s$ p% [' w) h9 ]: i
        conn, err := net.ListenPacket("udp4", ":0")2 t! M" z; s7 G, Z  ?4 S
        if err != nil {. i/ i. P6 V/ g2 z' g/ K; Q
                return# K0 y3 z+ Q( y/ Z
        }. @" }# u0 p. u9 N/ w9 h! G
        socket := conn.(*net.UDPConn)8 I2 ^1 D% ~/ o. E
        defer socket.Close()
7 C' y; M- D5 X  a1 Z) }! j: h* D        err = socket.SetDeadline(time.Now().Add(3 * time.Second))
9 V8 q/ |# t" n! ]# {  O3 m        if err != nil {
1 m6 U! A4 Z0 t. }                return& \! E8 z; y$ v% F, x
        }
2 m3 O, x. h, b" o8 ?        st := "InternetGatewayDevice:1"' m) Y% P0 P$ l7 R3 o% v
        // 多播请求:M-SEARCH SSDP协议定义的发现请求。
+ q# N' E- Q4 U9 o        buf := bytes.NewBufferString(
% {5 g6 P5 p2 ?8 n7 y# [' F                "M-SEARCH * HTTP/1.1\r\n" +, C2 I4 W5 z' d* y
                        "HOST: 239.255.255.250:1900\r\n" +
' e# g2 x# e. q( T- Q7 w5 U- p- l* k' O! |                        "ST: ssdp:all\r\n" +
  |5 q5 _  A! ^, G, N* c                        "MAN: \"ssdp:discover\"\r\n" +% B9 M" b  H1 v- ?% {4 N$ M
                        "MX: 2\r\n\r\n")% z* Q1 A/ J, ~/ l0 \+ Z
        message := buf.Bytes()
0 ]3 \* m  k+ L- R" y$ I        answerBytes := make([]byte, 1024)+ ^" j- A9 n- q/ U( V) L
        for i := 0; i
* ~2 G" D' }* S( U: f添加端口映射
4 v+ H! C& s! j7 U向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port做映射- D( G$ z# s- O' s2 q& ~
func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) {
1 V( m/ c5 V7 m. T% t3 K% g" v        // A single concatenation would break ARM compilation.
0 S6 ?" a# ^& B" C, k! P        message := "\r\n" +2 R! X; j9 v- N5 W* Y" K7 a: C8 |3 v
                "" + strconv.Itoa(externalPort)
0 f% u' h1 e* O$ e9 E5 M7 a        message += "" + protocol + ""+ O1 g/ I8 f* w7 }4 z+ P' Q& a' ^
        message += "" + strconv.Itoa(internalPort) + "" +2 B7 X" h' ~# j8 @, a6 X6 @. R
                "" + n.ourIP + "" +
7 X- F8 F, v* H7 }                "1"
" s  y) H, S$ u8 _9 W5 v$ Y# W( G        message += description +: a+ m. B* P) V% F$ p) s; E9 D, y
                "" + strconv.Itoa(timeout) +$ t4 P4 I) {& S# @6 p/ x9 D
                "
"7 g# N# J- J  ]4 Z+ L3 V. S2 N
        var response *http.Response& d2 _0 L3 J/ R, T. \& A& h
        response, err = soapRequest(n.serviceURL, "AddPortMapping", message, n.urnDomain)
% x# e. Y; `2 ?9 r5 Z7 O: H        if response != nil {! k. v3 L) ~; o/ I5 I; j4 ^0 i
                defer response.Body.Close()5 U; y" m$ p# g0 H
        }
- I. y  h9 n, x' D$ T6 X        if err != nil {2 C* k2 Z% [; Y' H
                return
! J! S" \* C4 j7 l' t& n+ V9 d2 R        }
: a4 n$ ~) a$ t4 j. B- K* N( Y        // TODO: check response to see if the port was forwarded7 O4 ~* H) S, A' b/ g- d% _1 Q+ ]& y
        // log.Println(message, response)* N# N# t# `" m
        // JAE:3 N& H5 g, D' U3 u  V
        // body, err := ioutil.ReadAll(response.Body)3 ~+ P& L( p& G9 ~# m  X( r0 @# p
        // fmt.Println(string(body), err)
! f+ q5 `8 c* J7 w        mappedExternalPort = externalPort
  S1 R& b0 O, N8 ?" ~+ x! ~        _ = response7 l# d$ d% M8 e+ d& X8 R1 R7 E. n
        return" M9 {" i2 y: c+ o
}
: g; l" g4 W0 ~8 z5 j删除端口映射3 T% l  v4 ]: i7 R! K9 L# ~
向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port删除映射关系' m. q8 G4 t$ F, r# F6 }: v
func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {% P$ D% {" p7 i5 c! m5 b/ [1 f
        message := "\r\n" +
7 ?+ e9 W3 Z5 m1 C  W7 G1 q( e                "" + strconv.Itoa(externalPort) +0 A! `* W' |4 d$ {
                "" + protocol + "" +
4 `$ X4 L1 u! P# W) k                "
"% L8 M$ X3 ^. p0 \+ P9 F
        var response *http.Response' R# v/ F, {( ]" H; k5 V' d( V
        response, err = soapRequest(n.serviceURL, "DeletePortMapping", message, n.urnDomain)
! M  I3 f+ P- H. @) `        if response != nil {
7 B( S0 h8 z. p( |; E+ O3 F$ h2 Y$ o                defer response.Body.Close()* m- h+ L9 A( k, j3 A
        }
0 @# v2 f# e1 n' T7 s* W' I        if err != nil {
9 I/ h" P) y' I: a6 U; S( ~                return7 @9 R- F$ j6 b% j- x
        }& n: r; I( @6 q2 s
        // TODO: check response to see if the port was deleted
) ~1 h' C, a6 T. R3 `        // log.Println(message, response)% l% P" F2 U, k3 k/ m
        _ = response
* }' N  I6 [/ @! B& f        return
1 b9 f4 j+ z1 J}
; j+ G! ~, I" u: j  ~获取映射后的公网地址" V# T: f- \! W# Y& C
func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {1 L  X7 C' h( A3 q+ \: ?
        info, err := n.getExternalIPAddress()6 L5 B" |3 d6 P( j# w
        if err != nil {, A- N0 W, U( a7 O
                return
) O+ {! h* u$ I7 A        }
. N1 a8 A: M4 O4 f; F        addr = net.ParseIP(info.externalIpAddress)
$ S7 I: [2 Y( O. ~9 w        return
/ n+ d7 w+ G- D" Y% R1 A+ `1 U}% n' {4 p  _5 z+ I$ z+ @
func (n *upnpNAT) getExternalIPAddress() (info statusInfo, err error) {
  E' ~# T8 \0 Y2 G$ F        message := "\r\n" +
  U$ T2 ^# A$ c% W9 C  L+ P                "
"
/ [+ h9 x: |1 ?* L        var response *http.Response6 S+ Y8 v7 z/ l, s1 r; R# H
        response, err = soapRequest(n.serviceURL, "GetExternalIPAddress", message, n.urnDomain)! E8 _$ r; L3 }, m# F  Q
        if response != nil {
( I; g4 U, K# I6 F                defer response.Body.Close(): Q0 o5 [; u( n4 ^4 w7 I# }+ r
        }
) a* f* o) w2 [" P- K' [        if err != nil {
. w; r7 z3 w$ S( s                return& i' u5 x- g" A
        }
1 Q9 |1 R# z0 i  \  a        var envelope Envelope0 o% V. O9 N3 \: g
        data, err := ioutil.ReadAll(response.Body)
$ |" M! Y: s4 B* a! z  u        reader := bytes.NewReader(data)& p: ^# v  T# d# s6 @1 o- b
        xml.NewDecoder(reader).Decode(&envelope)
& f& e" n3 E8 o0 Y  X$ r1 s  y        info = statusInfo{envelope.Soap.ExternalIP.IPAddress}
% G7 Y. D& [2 v% ~+ ?6 w# {        if err != nil {
3 K- S# I' s: U$ m* Z$ E                return
) i5 n  J# h& |* f1 E5 @' w) ]        }
, t1 D0 J: O4 @1 `5 q        return) h6 ^  A4 t$ w7 C
}4 q% a) m: `, E) f9 Q$ t9 S0 `
感谢比原社区开发者Derek的辛勤写作
0 x- [& {3 t9 _$ [* L3 n如果你对比原项目有兴趣请添加微信号:matrix2140 咨询,备注blockflow
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

V刘晨曦 初中生
  • 粉丝

    0

  • 关注

    3

  • 主题

    14