博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Derek解读Bytom源码-P2P网络 upnp端口映射
阅读量:7113 次
发布时间:2019-06-28

本文共 4859 字,大约阅读时间需要 16 分钟。

作者:Derek

简介

Github地址:

Gitee地址:

本章介绍bytom代码P2P网络中upnp端口映射

作者使用MacOS操作系统,其他平台也大同小异

Golang Version: 1.8

UPNP介绍

UPNP(Universal Plug and Play)通用即插即用。UPNP端口映射将一个外部端口映射到一个内网ip:port。从而实现p2p网络从外网能够穿透网关访问到内网的bytomd节点。

UPNP协议

SSDP(Simple Service Discovery Protocol 简单服务发现协议) GENA(Generic Event Notification Architecture 通用事件通知结构) SOAP(Simple Object Access Protocol 简单对象访问协议) XML(Extensible Markup Language 可扩张标记语言)

UPNP代码

** p2p/upnp/upnp.go **

发现网络中支持UPNP功能的设备

从网络中发现支持UPNP功能的设备,并得到该设备的location和url等相关信息

type upnpNAT struct {	serviceURL string // 设备的描述文件URL,用于得到该设备的描述信息	ourIP      string // 节点本地ip地址	urnDomain  string // 设备类型}func Discover() (nat NAT, err error) {	ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900")	if err != nil {		return	}	conn, err := net.ListenPacket("udp4", ":0")	if err != nil {		return	}	socket := conn.(*net.UDPConn)	defer socket.Close()	err = socket.SetDeadline(time.Now().Add(3 * time.Second))	if err != nil {		return	}	st := "InternetGatewayDevice:1"	// 多播请求:M-SEARCH SSDP协议定义的发现请求。	buf := bytes.NewBufferString(		"M-SEARCH * HTTP/1.1\r\n" +			"HOST: 239.255.255.250:1900\r\n" +			"ST: ssdp:all\r\n" +			"MAN: \"ssdp:discover\"\r\n" +			"MX: 2\r\n\r\n")	message := buf.Bytes()	answerBytes := make([]byte, 1024)	for i := 0; i < 3; i++ {		// 向239.255.255.250:1900发送一条多播请求		_, err = socket.WriteToUDP(message, ssdp)		if err != nil {			return		}		// 如果从网络中发现UPNP设备则会从239.255.255.250:1900收到响应消息		var n int		n, _, err = socket.ReadFromUDP(answerBytes)		for {			n, _, err = socket.ReadFromUDP(answerBytes)			if err != nil {				break			}			answer := string(answerBytes[0:n])			if strings.Index(answer, st) < 0 {				continue			}			// HTTP header field names are case-insensitive.			// http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2			// 获得设备location			locString := "\r\nlocation:"			answer = strings.ToLower(answer)			locIndex := strings.Index(answer, locString)			if locIndex < 0 {				continue			}			loc := answer[locIndex+len(locString):]			endIndex := strings.Index(loc, "\r\n")			if endIndex < 0 {				continue			}			// 获得设备的描述url和设备类型			locURL := strings.TrimSpace(loc[0:endIndex])			var serviceURL, urnDomain string			serviceURL, urnDomain, err = getServiceURL(locURL)			if err != nil {				return			}			var ourIP net.IP			ourIP, err = localIPv4()			if err != nil {				return			}			nat = &upnpNAT{serviceURL: serviceURL, ourIP: ourIP.String(), urnDomain: urnDomain}			return		}	}	err = errors.New("UPnP port discovery failed.")	return}复制代码

添加端口映射

向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port做映射

func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) {	// A single concatenation would break ARM compilation.	message := "
\r\n" + "
" + strconv.Itoa(externalPort) message += "
" + protocol + "
" message += "
" + strconv.Itoa(internalPort) + "
" + "
" + n.ourIP + "
" + "
1
" message += description + "
" + strconv.Itoa(timeout) + "
" var response *http.Response response, err = soapRequest(n.serviceURL, "AddPortMapping", message, n.urnDomain) if response != nil { defer response.Body.Close() } if err != nil { return } // TODO: check response to see if the port was forwarded // log.Println(message, response) // JAE: // body, err := ioutil.ReadAll(response.Body) // fmt.Println(string(body), err) mappedExternalPort = externalPort _ = response return}复制代码

删除端口映射

向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port删除映射关系

func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {	message := "
\r\n" + "
" + strconv.Itoa(externalPort) + "
" + protocol + "
" + "
" var response *http.Response response, err = soapRequest(n.serviceURL, "DeletePortMapping", message, n.urnDomain) if response != nil { defer response.Body.Close() } if err != nil { return } // TODO: check response to see if the port was deleted // log.Println(message, response) _ = response return}复制代码

获取映射后的公网地址

func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {	info, err := n.getExternalIPAddress()	if err != nil {		return	}	addr = net.ParseIP(info.externalIpAddress)	return}func (n *upnpNAT) getExternalIPAddress() (info statusInfo, err error) {	message := "
\r\n" + "
" var response *http.Response response, err = soapRequest(n.serviceURL, "GetExternalIPAddress", message, n.urnDomain) if response != nil { defer response.Body.Close() } if err != nil { return } var envelope Envelope data, err := ioutil.ReadAll(response.Body) reader := bytes.NewReader(data) xml.NewDecoder(reader).Decode(&envelope) info = statusInfo{envelope.Soap.ExternalIP.IPAddress} if err != nil { return } return}复制代码

转载地址:http://dczel.baihongyu.com/

你可能感兴趣的文章
Java 中的反射机制
查看>>
我的CPG插件 (什么是CPG,就是跟号称全球唯一C++编写的魔镜是一样的格式的)
查看>>
Py中axis理解【转载】
查看>>
mvvm windows app DataBinding
查看>>
Struts2_day02--Action获取表单提交数据
查看>>
博客作业01-抽象数据类型
查看>>
leetcode357
查看>>
leetcode474
查看>>
jquery Ajax跨域调用WebServices方法
查看>>
Android中实现圆角矩形及半透明效果。
查看>>
Redis系列十:缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级
查看>>
gitlab本地部署方法(ubuntu16.04+gitlab9.5.5)
查看>>
《Linux程序设计》笔记(二)shell程序设计
查看>>
mysql修改端口经验
查看>>
在床上手机看完电影让电脑关机 休眠 golang源码--配合手机ES浏览器开一个FTP
查看>>
java操作properties文件
查看>>
C语言内存讲解
查看>>
域名解析记录
查看>>
EF支持复杂类型的实现
查看>>
快速入门系列--WCF--07传输安全、授权与审核
查看>>