First initialize

This commit is contained in:
chenboxing
2017-07-22 13:18:33 +08:00
commit e467fa58e8
30 changed files with 1919 additions and 0 deletions

10
.gitignore vendored Normal file
View File

@@ -0,0 +1,10 @@
*.swp
bin/
pkg/
src/code.google.com
src/github.com
src/bitbucket.org
src/launchpad.net
src/gopkg.in
src/ngrok/client/assets/
src/ngrok/server/assets/

7
src/client.conf Normal file
View File

@@ -0,0 +1,7 @@
[client]
proxy = ":"
listen = ":8585"
key = ""
[ThirdProxy]
address = "proxy.move8.cn:7777"

53
src/punching/Makefile Normal file
View File

@@ -0,0 +1,53 @@
.PHONY: default server client deps fmt clean all release-all assets client-assets server-assets contributors
export GOPATH:=$(shell pwd)
BUILDTAGS=debug
default: all
deps: assets
go get -tags '$(BUILDTAGS)' -d -v ngrok/...
server: deps
go install -tags '$(BUILDTAGS)' ngrok/main/ngrokd
fmt:
go fmt ngrok/...
client: deps
go install -tags '$(BUILDTAGS)' ngrok/main/ngrok
assets: client-assets server-assets
bin/go-bindata:
GOOS="" GOARCH="" go get github.com/jteeuwen/go-bindata/go-bindata
client-assets: bin/go-bindata
bin/go-bindata -nomemcopy -pkg=assets -tags=$(BUILDTAGS) \
-debug=$(if $(findstring debug,$(BUILDTAGS)),true,false) \
-o=src/ngrok/client/assets/assets_$(BUILDTAGS).go \
assets/client/...
server-assets: bin/go-bindata
bin/go-bindata -nomemcopy -pkg=assets -tags=$(BUILDTAGS) \
-debug=$(if $(findstring debug,$(BUILDTAGS)),true,false) \
-o=src/ngrok/server/assets/assets_$(BUILDTAGS).go \
assets/server/...
release-client: BUILDTAGS=release
release-client: client
release-server: BUILDTAGS=release
release-server: server
release-all: fmt release-client release-server
all: fmt client server
clean:
go clean -i -r ngrok/...
rm -rf src/ngrok/client/assets/ src/ngrok/server/assets/
contributors:
echo "Contributors to ngrok, both large and small:\n" > CONTRIBUTORS
git log --raw | grep "^Author: " | sort | uniq | cut -d ' ' -f2- | sed 's/^/- /' | cut -d '<' -f1 >> CONTRIBUTORS

41
src/punching/README.md Normal file
View File

@@ -0,0 +1,41 @@
### 使用场景
### 项目介绍
本项目完全是点对点的访问无须经由服务器转发代理转发端只是为了获取当自的Nat网络IP和端口为了Nat穿透,在获取各自对方的IP和端口后就不需要服务端的干预。
### 如何使用
1. 迁出源码git clone https://github.com/chenboxing/punching.git
2. 进入项目目录 src/punching/
3. 编译源码
make (all|server|client|proxy) (windows|darwin|linux|arm)
make all windows #编译windows平台下所有
make server|client linux #编译Windows平台Server端和Client端二进制文件
二进制文件编译在 punching/bin/里
你也可以直接下载已经编译好的文件:
4. 把proxy(代理转发端) 部署到公网计算机上配置config.conf配置节[proxy],设置侦听端口,默认7777如果没有公网计算机可以使用本站设置好的域名 nat.move8.cn, 在config.conf配置节[ThridProxy]设置好相关信息,比如
[ThirdProxy]
address = nat.move8.cn
email = xxxx@xxxxx.com
password = xxxxxxx
先在Nat网络一端你需要开放访问的服务的计算机上部署server端配置config.conf配置节[server],在listen项里设置你要开放的应用服务如 192.168.1.45:80, proxy添写你的代理转发端公网地址和端口比如 xxx.f3322.net:7777, 如果你需要使用本站提供的代理转发,此项请为空,参考上面填写节[ThirdProxy]信息。
配置好启动Server端:
nat_server.exe
配置在Client端
在Nat网络的另一端部署client端先配置config.conf,在节[client],需要先设置好要侦听的端口信息比如listen = :8585, proxy添写你的代理转发端公网地址和端口比如 xxx.f3322.net:7777, 如果你需要使用本站提供的代理转发,此项请为空,参考上面填写节[ThirdProxy]信息。
配置好启动Server端:
nat_client.exe
### 局限
只能P2P通讯(Client端,Server端),无法多人访问开启的服务

View File

@@ -0,0 +1,62 @@
package client
import (
"fmt"
"punching/util"
)
/*
[server]
proxy = ""
dial = ""
key = ""
[ThirdProxy]
address = "proxy.move8.cn:7777"
*/
type ServerConfig struct {
Proxy string `toml:"proxy"` // Proxy 服务的地址
Dial string `toml:"dial"` // 服务端提供的服务地址
Key string `toml:"key"` // 客户端和服务端的匹配码
}
type ThridProxyConfig struct {
Address string `toml:"address"` // Proxy 服务的地址
}
var Config *ServerConfig
var ThirdConfig *ThridProxyConfig
func InitConfig() (err error) {
if Config == nil {
// 加载配置信息
fileName := "server.conf"
sectionName1 := "server"
if err01 := util.DecodeSection(fileName, sectionName1, Config); err01 != nil {
err = fmt.Errorf("Load config file failed, error:%s", err01.Error())
return
}
sectionName2 := "ThridProxy"
if err02 := util.DecodeSection(fileName, sectionName2, ThirdConfig); err != nil {
err = fmt.Errorf("Load config file failed, error:%s", err02.Error())
return
}
if Config.Key == "" {
err = fmt.Errorf("匹配码不能为空请在client.conf配置匹配码")
return
}
if Config.Proxy == "" && ThirdConfig.Address == "" {
err = fmt.Errorf("Proxy服务地址和第三方Proxy服务地址不能同时为空")
return
}
}
return nil
}

View File

@@ -0,0 +1,50 @@
package client
import (
"fmt"
"log"
)
func Main() {
// 加载配置信息
if err := InitConfig(); err != nil {
fmt.Println("加载配置信息出错,原因为:%s", err)
return
}
proxyAddr := Config.Dial || ThirdConfig.Address
pairName := Config.Key
localAddr, destAddr, pairname, err := util.ClientDialProxy(proxyAddr, pairName)
if err != nil {
log.Println(err)
return
}
//连接对方
connPeer, errPeer := util.DialPeer(localAddr, destAddr)
if errPeer != nil { //无法连接上
log.Println(errPeer)
return
}
// 连接上 P2P客户端
Dch = make(chan bool)
Rch = make(chan DataPackage)
Wch = make(chan []byte)
go RHandler(connPeer)
go WHandler(connPeer)
ClientListenHandle()
}
func RHandler(conn util.NetConn) {
}
func WHandler(conn util.NetConn) {
}

View File

@@ -0,0 +1,175 @@
package client
import (
"crypto/md5"
"crypto/rand"
"encoding/base64"
"encoding/hex"
"fmt"
"io"
"log"
"net"
"os"
"sync"
"time"
)
var ListenAcceptMap map[string]net.Conn
var ExitChanMap map[string]chan bool
var RWLock *sync.RWMutex
//生成32位md5字串
func GetMd5String(s string) string {
h := md5.New()
h.Write([]byte(s))
return hex.EncodeToString(h.Sum(nil))
}
//生成Guid字串
func UniqueId() string {
b := make([]byte, 48)
if _, err := io.ReadFull(rand.Reader, b); err != nil {
return ""
}
return GetMd5String(base64.URLEncoding.EncodeToString(b))
}
func handleClientConn(source net.Conn) {
// 32位唯一码
uniqueid := UniqueId()
log.Println("Enter handleClientConn:", uniqueid)
RWLock.Lock()
ListenAcceptMap[uniqueid] = source
ExitChanMap[uniqueid] = make(chan bool)
RWLock.Unlock()
log.Println("建立Map", uniqueid)
defer func() {
e1 := source.Close()
if e1 != nil {
log.Println("关闭Sourcer失败")
}
RWLock.Lock()
delete(ListenAcceptMap, uniqueid)
delete(ExitChanMap, uniqueid)
log.Println("删除map", uniqueid)
RWLock.Unlock()
}()
go func() {
buf := make([]byte, 1024)
var flag int
for {
len01, err := source.Read(buf)
if len01 <= 0 || err != nil {
log.Println("读取Source源连接出错原因为", err.Error())
//发送控制
pack01 := Packet(uniqueid, "01", []byte(""))
Wch <- pack01
return
}
controlID := "00"
if flag == 0 {
// 第一次
controlID = "11"
flag = 1
}
pack := Packet(uniqueid, controlID, buf[0:len01])
Wch <- pack
}
}()
select {
case <-ExitChanMap[uniqueid]:
log.Println("需要退出Accept")
return
}
}
// 侦听端口将连接转到natConn
func ClientListenHandle() {
ListenAcceptMap = make(map[string]net.Conn)
ExitChanMap = make(map[string]chan bool)
RWLock = new(sync.RWMutex)
addrOn := Config.Dial
l, err := net.Listen("tcp", addrOn)
if err != nil {
fmt.Println("listen ", addrOn, " error:", err)
return
}
fmt.Println("server running at port", addrOn)
// 全局读取来自nat源的包
go handleReadConn()
go func() {
for {
c, err := l.Accept()
if err != nil {
fmt.Println("accept error:", err)
break
}
go handleClientConn(c)
}
}()
select {
case <-Dch:
os.Exit(3)
}
}
// 读取目标流到源
func handleReadConn() {
for {
select {
case r := <-Rch:
log.Println(time.Now().UnixNano(), "handleReadConn准备处理")
// 获取src
controlid := r.ControlID
uniqueid := r.Key
data := r.Data
log.Println("读取Nat包handleReadConn", uniqueid, "长度为", len(data))
//退出
if controlid == "01" {
if c, ok := ExitChanMap[uniqueid]; ok {
log.Println("发送退出信号")
c <- true
} else {
log.Println("在ExitChanMap里找不到Key为:", uniqueid)
}
} else {
if src, ok := ListenAcceptMap[uniqueid]; ok {
len2, err2 := src.Write(data)
if err2 != nil || len2 <= 0 {
log.Println("源写入出错", err2.Error())
}
log.Println(time.Now().UnixNano(), "源写入:", len2)
} else {
log.Println("在Map里找不到Key为:", uniqueid)
}
}
}
}
}

17
src/punching/config.conf Normal file
View File

@@ -0,0 +1,17 @@
[proxy]
listen = ":7777"
[server]
proxy = ":7777"
dial = "192.168.1.168:443"
key = "amychen"
[client]
proxy = ":7777"
listen = ":8585"
key = "amychen"
[ThirdProxy]
address = "proxy.move8.cn:7777"
email = 368123477@qq.com
password = dingding

View File

@@ -0,0 +1,19 @@
package const
// Constant for the client and server
const (
PAIR_PACT_HEAD byte = 'P' // C<->S 自定义包头
PAIR_CONTROL_FIRST byte = 11 // 控制码 C->S第一个包
PAIR_CONTROL_QUIT byte = 10 // 控制码 退出
PAIR_CONTROL_NORMAL byte = 0 // 控制码
CLIENT_PAIR_ACK byte = 1 // 客户端匹配确认
SERVER_PAIR_ACK byte = 2 // 服务端匹配确认
)
const (
ROLE_SERVER int = 1 // 点对点服务端
ROLE_CLIENT int = 2 // 点对点客户端
}

View File

@@ -0,0 +1,8 @@
package const
const (
PROXY_PACKAGE_HEAD byte = 'H' // C<->S 自定义包头
PROXY_CONTROL_ERROR_NO_SERVER byte = 201 // 服务端还没有注册
PROXY_CONTROL_ERROR_CLIENT_EXIST byte = 202 // 客户端已经存在
)

View File

@@ -0,0 +1,76 @@
package util
import (
"fmt"
"github.com/cihub/seelog"
)
// init 初始化包
func init() {
// 解析服务配置文件
xml := `
<seelog>
<outputs formatid="main">
<filter levels="info,critical,error,debug">
<console formatid="main" />
<rollingfile formatid="main" type="date" filename="./log/punching.log" datepattern="2006.01.02" />
</filter>
</outputs>
<formats>
<format id="main" format="%Date %Time [%LEV] %Msg [%File][%FuncShort][%Line]%n"/>
</formats>
</seelog>
`
// 解析日志配置(从默认配置)
logger, err := seelog.LoggerFromConfigAsBytes([]byte(xml))
if err != nil {
panic(fmt.Errorf("log configuration parse error: %s", err.Error()))
}
seelog.ReplaceLogger(logger)
}
var (
// Tracef 写一条格式化的日志信息。级别等于 Trace
Tracef = seelog.Tracef
// Trace 写一条日志信息。级别等于 Trace
Trace = seelog.Trace
// Debugf 写一条格式化的日志信息。级别等于 Debug
Debugf = seelog.Debugf
// Debug 写一条日志信息。级别等于 Debug
Debug = seelog.Debug
// Infof 写一条格式化的日志信息。级别等于 Info
Infof = seelog.Infof
// Info 写一条日志信息。级别等于 Info
Info = seelog.Info
// Warnf 写一条格式化的日志信息。级别等于 Warn
Warnf = seelog.Warnf
// Warn 写一条日志信息。级别等于 Warn
Warn = seelog.Warn
// Errorf 写一条格式化的日志信息。级别等于 Error
Errorf = seelog.Errorf
// Error 写一条日志信息。级别等于 Error
Error = seelog.Error
// Criticalf 写一条格式化的日志信息。级别等于 Critical
Criticalf = seelog.Criticalf
// Critical 写一条日志信息。级别等于 Critical
Critical = seelog.Critical
)
// Flush 将所有日志信息写入缓存
func Flush() {
seelog.Flush()
}

View File

@@ -0,0 +1,5 @@
package main
func main() {
client.Main()
}

View File

@@ -0,0 +1,5 @@
package main
func main() {
proxy.Main()
}

View File

@@ -0,0 +1,5 @@
package main
func main() {
server.Main()
}

2
src/punching/proxy.conf Normal file
View File

@@ -0,0 +1,2 @@
[proxy]
listen = ":7777"

View File

@@ -0,0 +1,32 @@
package proxy
import (
"fmt"
)
type ProxyConfig struct{
Listen string `toml:"listen"` // Proxy 服务的地址
}
var Config *ProxyConfig
func InitConfig() (err error){
if Config == nil {
// 加载配置信息
fileName = "proxy.conf"
if err01 := util.DecodeSection(fileName, sectionName, Config); err != nil {
err = fmt.Errorf("Load config file failed, error:%s", err.Error())
return
}
if Config.Listen == "" {
err = fmt.Errorf("侦听地址为空请在配置文件proxy.conf配置listen值")
return
}
}
return nil
}

343
src/punching/proxy/main.go Normal file
View File

@@ -0,0 +1,343 @@
package proxy
import (
"net"
"sync"
"fmt"
"punching/util"
"punching/logger"
"time"
"punching/const"
)
const (
PROXY_FIRST_HEAD byte = 'P' //包头标识
ROLE_SERVER byte = 1 //服务端
ROLE_CLIENT byte = 2 //客户端
PROXY_RESP_NO_SERVER = "no_server_partner" // 代理响应-服务端还没有启用
PROXY_RESP_CLIENT_EXIST = "client_existed" // 代理响应-客户端已经存在了
PROXY_RESP_ERR_SERVER_EXIST = "server_existed" // 代理响应-客户端已经存在了
CLIENT_RESP_ACK byte = 1
SERVER_RESP_ACK byte = 1
)
// ServerConn 服务端到代理端连接
type ServerConn struct {
Rch chan []byte // 读通道
Wch chan []byte // 写通道
Dch chan bool // 连接退
LocalAddr string // 客户端IP信息
Pairname string // 匹配名称
SyncAt int64 // 上次心跳时间
}
// ClientConn 客户端到代理端连接
type ClientConn struct{
Pairname string //匹配码
}
// 全局变量
var (
OnlineServerList map[string]ServerConn // 服务端连接列表Map
OnlineClientList map[string]ClientConn // 客户端连接列表Map
RWLockClient sync.RWMutex //读写锁
RWLockServer sync.RWMutex
)
func Main(){
// 加载配置信息
if err := InitConfig(); err != nil{
log.Println("加载配置信息出错,原因为:%s", err)
return
}
OnlineServerList = make(map[string]ServerConn)
OnlineClientList = make(map[string]ClientConn)
RWLockClient = new(sync.RWMutex)
RWLockServer = new(sync.RWMutex)
tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
if err != nil {
panic(err)
}
listen, err := net.ListenTCP("tcp", tcpAddr)
if err != nil {
fmt.Println("监听端口失败:", err.Error())
return
}
fmt.Println("已初始化连接,等待客户端连接...")
for {
conn, err := listen.AcceptTCP()
if err != nil {
fmt.Println("连接异常:", err.Error())
continue
}
fmt.Println("本地地址:", conn.LocalAddr().String(), "来自远程地址", conn.RemoteAddr().String())
go Handler(conn)
}
}
// parseFirstPackage 解析连接的第一个条
// [1]+[1]+[4-32] (包头标识+类型+32字节的Pairname)
func parseFirstPackage(data []byte)(pairname string, roleType int, err error){
if len(data) <= 6 || len(data) > 34 {
err = fmt.Errorf("%s","包长度不匹配")
return
}
roleType = int(data[1])
if data[0] != PROXY_FIRST_HEAD || ( roleType != ROLE_CLIENT && roleType != ROLE_SERVER) {
err = fmt.Errorf("%s","包内容不匹配")
return
}
pairname = string(byte[2:])
return
}
// processRoleClient 处理客户端连接
func processRoleClient(conn net.Connpairname string ){
// 判断匹配的服务端是否已经注册
RWLockServer.RLock()
serverConn,ok := OnlineServerList[pairname]
RWLockServer.RUnlock()
if !ok {
// 客户端没有注册
conn.Write([]byte(PROXY_RESP_NO_SERVER))
return
}
RWLockClient.RLock()
ok, _ := OnlineClientList[pairname]
RWLockClient.RUnlock()
if !ok{
conn.Write([]byte(PROXY_RESP_CLIENT_EXIST))
return
}
// 添加到客户端列表
RWLockClient.Lock()
OnlineClientList[pairname] = pairname
RWLockClient.Unlock()
// byteNat := bytes.NewBuffer(nil)
// byteNat.write([]byte())
// result.Bytes()
// 发送Nat地址和接收确认
toClientAddrs := serverConn.LocalAddr + "," + conn.LocalAddr
conn.Write(byte[]{toClientAddrs})
buf := make([]byte, 128)
lenAck, err := conn.Read(buf)
flag := 0
if buf[0] == CLIENT_RESP_ACK {
flag += 1
}
toServerAddrs := conn.LocalAddr + "," + serverConn.LocalAddr
serverSide.Wch <- toServerAddrs
// 等待服务端的确认数据
select {
case bufAck := <- serverSide.Rch
if bufAck[0] == SERVER_RESP_ACK {
flag += 1
break
}
}
// 收到服务端的确认数据
if flag == 2 {
RWLockServer.Lock()
serverConn.Dch <- true // 关闭服务端连接
delete( OnlineServerList, pairname)
RWLockServer.Unlock()
}
RWLockClient.Lock()
delete( OnlineClientList, pairname)
RWLockClient.Unlock()
return
}
// Handle 连接处理函数
func Handler(conn net.Conn) {
defer func() {
if r := recover(); r != nil {
logger.Println("连接出现问题:%s",r.Error())
}
}()
defer conn.Close()
buf := make([]byte, 1024)
var uid string
var C *OnlineServerSide
// 确定连接类型,判断是否是有效的连接,
// 对于客户端,需满足
// 1. 存在对应的服务端
// 2. 不能存在多个客户端
// 对于服务端:
// 1.
for {
i, err := conn.Read(buf)
if err != nil {
fmt.Println("读取数据错误:", err.Error())
return
}
//获取匹配名称和连接类型(服务端或客户端)
pairname, roleType, err := parseFirstPackage(buf[0:i])
if err != nil {
return
}
// 处理客户端连接
if roleType == ROLE_CLIENT{
processRoleClient(conn)
return // 退出客户端连接
}
break
}
// 下面的操作都是针对服务端连接
// 是否存在pair name
RWLockServer.RLock()
_, ok := OnlineServerList[pairname]
RWLockServer.RUnlock()
// 已存在
if ok {
conn.Write([]byte(PROXY_RESP_ERR_SERVER_EXIST))
return
}
// 生成服务器连接对象添加到列表
RWLockServer.Lock()
serverConn := &ServerConn{Rch: make(chan []byte), Wch: make(chan []byte), Pairname: pairname, LocalAddr: conn.LocalAddr}
OnlineServerList[pairname] = serverConn
RWLockServer.Unlock()
// 写通道
go WHandler(conn, serverConn)
// 读通道
go RHandler(conn, serverConn)
// 等待退出通道
select {
case <-C.Dch:
fmt.Println("close handler goroutine")
}
}
// 正常写数据 匹配端连接上来会写信息
// 定时检测 conn die => goroutine die
func WHandler(conn net.Conn, C *ServerConn) {
// 读取业务Work 写入Wch的数据
ticker := time.NewTicker(60 * time.Second)
for {
select {
case d := <-C.Wch:
conn.Write(d)
case <-ticker.C: //60秒无操作,可能连接已中断
RWLockServer.RLock()
_, ok := OnlineServerList[C.Pairname];
RWLockServer.RUnlock()
if !ok {
fmt.Println("conn die, close WHandler")
return
}
}
}
}
// 读客户端数据 + 心跳检测
func RHandler(conn net.Conn, C *ServerSide) {
// 心跳ack
// 业务数据 写入Wch
for {
data := make([]byte, 128)
// 设置读超时
err := conn.SetReadDeadline(time.Now().Add(10 * time.Second))
if err != nil {
fmt.Println(err)
}
if _, derr := conn.Read(data); derr == nil {
// 可能是来自客户端的消息确认
// 数据消息
fmt.Println(data)
if data[0] == Res {
fmt.Println("recv client data ack")
} else if data[0] == Req {
fmt.Println("recv client data")
fmt.Println(data)
conn.Write([]byte{Res, '#'})
// C.Rch <- data
}
continue
}
//如果等待10秒没有读到客户端数据或读写出错写心跳包
// 写心跳包
conn.Write([]byte{Req_HEARTBEAT, '#'})
fmt.Println("send ht packet")
conn.SetReadDeadline(time.Now().Add(5 * time.Second))
if _, herr := conn.Read(data); herr == nil {
// fmt.Println(string(data))
// 更新心跳时间
RWLockServer.RLock()
serverConn, ok := OnlineServerList[C.Pairname];
if ok {
serverConn.SyncAt = time.Now().Unix()
}
RWLockServer.RUnlock()
fmt.Println("resv ht packet ack")
} else {
RWLockServer.Lock()
delete(OnlineServerSide, C.Pairname)
RWLockServer.Unlock()
fmt.Println("delete user!")
return
}
}
}

8
src/punching/server.conf Normal file
View File

@@ -0,0 +1,8 @@
[server]
proxy = ""
dial = ""
key = ""
[ThirdProxy]
address = "proxy.move8.cn:7777"

View File

@@ -0,0 +1,57 @@
package server
import (
"fmt"
"punching/util"
)
/*
[server]
proxy = ""
dial = ""
key = ""
[ThirdProxy]
address = "proxy.move8.cn:7777"
*/
type ServerConfig struct {
Proxy string `toml:"proxy"` // Proxy 服务的地址
Dial string `toml:"dial"` // 服务端提供的服务地址
Key string `toml:"key"` // 客户端和服务端的匹配码
}
type ThridProxyConfig struct {
Address string `toml:"address"` // Proxy 服务的地址
}
var Config *ServerConfig
var ThirdConfig *ThridProxyConfig
func InitConfig() (err error) {
if Config == nil {
// 加载配置信息
fileName := "server.conf"
sectionName1 := "server"
if err01 := util.DecodeSection(fileName, sectionName1, Config); err01 != nil {
err = fmt.Errorf("Load config file failed, error:%s", err01.Error())
return
}
sectionName2 := "ThridProxy"
if err02 := util.DecodeSection(fileName, sectionName2, ThirdConfig); err != nil {
err = fmt.Errorf("Load config file failed, error:%s", err02.Error())
return
}
if Config.Proxy == "" && ThirdConfig.Address == "" {
err = fmt.Errorf("Proxy服务地址和第三方Proxy服务地址不能同时为空")
return
}
}
return nil
}

View File

@@ -0,0 +1,50 @@
package server
import (
"fmt"
"log"
"punching/util"
"time"
)
func Main() {
// 加载配置信息
if err := InitConfig(); err != nil {
fmt.Println("加载配置信息出错,原因为:%s", err)
return
}
proxyAddr := Config.Dial || ThirdConfig.Address
pairName := Config.Key
// 如果跟Peer连接出错要重新连接Proxy
for {
conn, err := ServerDialProxy(proxyAddr, pairName)
if err != nil {
log.Println(err)
time.Sleep(5 * time.Second)
continue
}
localAddr, remoteAddr, errWait := WaitForPeer(conn)
if errWait != nil {
log.Println(errWait)
time.Sleep(5 * time.Second)
continue
}
//连接对方
connPeer, errPeer := util.DialPeer(localAddr, destAddr)
if errPeer != nil { //无法连接上
log.Println(errPeer)
continue
}
//已经连接上
}
}

View File

@@ -0,0 +1,117 @@
package server
import (
"fmt"
"log"
"net"
"os"
)
func WaitForPeer(conn util.NetConn) (localAddr string, remoteAddr string, pairname string, err error) {
// 接收心跳和客户端连接确认
Rch = make(chan DataPackage)
Wch = make(chan []byte)
go RHandler(conn)
go WHandler(conn)
select {
case ret := <-Dch:
switch ret {
case 10: //无法跟proxy连接
//关闭连接
err = error("出现错误")
break
case 11: // 获取客户端发来信息
localAddr = ""
remoteAddr = ""
pairName = ""
break
}
return
}
}
func ServerDialProxy(proxyAddr string, pairkey string) (conn util.NetConn, err error) {
// 发第一个包
// 地址接收确认
// 心跑包接收和确认
var conn = util.NetConn{}
// 不指定端口,让系统自动分配
err = conn.Bind("tcp", "")
if err != nil {
log.Println("绑定出错", err.Error())
return
}
// 连接到Proxy解析服务器
tcpAddr, err := net.ResolveTCPAddr("tcp", proxyAddr)
err = conn.Connect(util.InetAddr(tcpAddr.IP.String()), tcpAddr.Port)
if err != nil {
fmt.Println("连接服务端出错", err.Error())
return
}
defer conn.Close()
fmt.Println("已连接服务器,服务器地址是:%s:%d", tcpAddr.IP.String(), tcpAddr.Port)
// 发送第一个包
PackageProxy()
key := "ok" + name
buff01 := make([]byte, len(key))
//发送第一个包
log.Println("发送数据1", name)
n, err := conn.Write([]byte(name))
if err != nil {
log.Println("出错,原因:", err.Error())
os.Exit(22)
}
log.Println("have write: ", n)
// 读取
// 错误,退出此次连接
// 没有错误,显示当前连接的信息
i, err := conn.Read(buff01)
if err != nil {
fmt.Println("读取数据出错,", err.Error())
os.Exit(1)
}
// log.Print("读取数据:", string(buff01[0:i]))
// if string(buff01[0:i]) == key {
// break
// }
// log.Println("发送数据", []byte(name))
// conn.Write([]byte(name))
}
func RHandler(conn util.NetConn) {
for {
// 心跳包,回复ack
data := make([]byte, 128)
i, _ := conn.Read(data)
if i == 0 {
Dch <- true
return
}
if data[0] == Req_HEARTBEAT {
fmt.Println("recv ht pack")
conn.Write([]byte{Res_REGISTER, '#', 'h'})
fmt.Println("send ht pack ack")
} else if data[0] == Req { // 接收到确认信息
fmt.Println("recv data pack")
fmt.Printf("%v\n", string(data[2:]))
conn.Write([]byte{Res, '#'})
}
}
}

View File

@@ -0,0 +1 @@
package server

View File

@@ -0,0 +1,157 @@
package client
import (
"log"
"net"
"os"
"reflect"
"sync"
"unsafe"
)
var DialTargetMap map[string]net.Conn
func b2s(buf []byte) string {
return *(*string)(unsafe.Pointer(&buf))
}
func s2b(s *string) []byte {
return *(*[]byte)(unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(s))))
}
func handleServerConn() {
DialTargetMap = make(map[string]net.Conn)
ExitChanMap = make(map[string]chan bool)
RWLock = new(sync.RWMutex)
//var target net.Conn
//var err error
// defer func() {
// if target != nil {
// target.Close()
// }
// //source.Close()
// }()
var targetIP string
targetIP = "192.168.3.2:5901"
for {
select {
case r := <-Rch:
//确定target是否存在,如果不存在重新生成target
//分析数据包
log.Println("接收到数据:", len(r.Data))
controlid := r.ControlID
uniqueid := r.Key
pack := r.Data
//log.Println("读取Nat接收包handleReadConn", string(r[0:34]), "长度为", len(r))
if controlid == "01" {
RWLock.RLock()
if c, ok := ExitChanMap[uniqueid]; ok {
log.Println("发送退出信号")
c <- true
} else {
log.Println("在ExitChanMap里找不到Key为:", uniqueid)
}
RWLock.RUnlock()
break
}
//第一次
if controlid == "11" {
log.Println("准备连接:", targetIP)
target, err := net.Dial("tcp", targetIP)
if err != nil {
log.Println("连接目标出错", targetIP)
break
}
ExitChanMap[uniqueid] = make(chan bool)
DialTargetMap[uniqueid] = target
log.Println("连接目标成功:", targetIP)
_, err2 := target.Write(pack)
if err2 != nil {
log.Println("连接成功后写目标出错", err2.Error())
break
}
go ReadFromTarget(target, uniqueid)
} else {
if dialtarget, ok := DialTargetMap[uniqueid]; ok {
len2, err2 := dialtarget.Write(pack)
log.Println("已写入:", len2)
if err2 != nil {
log.Println("写目标出错", targetIP, err2.Error())
//发送控制
pack01 := Packet(uniqueid, "01", []byte(""))
Wch <- pack01
break
}
} else {
log.Println("找不到目标Dial:")
}
}
case <-Dch:
//出错
os.Exit(3)
}
}
}
// 读取目标流到源
func ReadFromTarget(target net.Conn, uniqueid string) {
defer func() {
target.Close()
RWLock.Lock()
delete(DialTargetMap, uniqueid)
delete(ExitChanMap, uniqueid)
RWLock.Unlock()
}()
go func() {
//buf := make([]byte, 512-34)
buf := make([]byte, 1024)
for {
j, err := target.Read(buf)
if err != nil || j == 0 {
log.Println("读取目标连接数据出错,原因为:", err.Error())
pack := Packet(uniqueid, "01", []byte(""))
Wch <- pack
return
}
pack := Packet(uniqueid, "00", buf[0:j])
Wch <- pack
}
}()
//接受到退出标识
select {
case <-ExitChanMap[uniqueid]:
log.Println("需要退出Accept")
return
}
}

View File

@@ -0,0 +1,48 @@
package util
import (
"flag"
"fmt"
"os"
"github.com/BurntSushi/toml"
)
// LoadTomlFile 加载配置文件
func LoadTomlFile(filename string)(sections map[string]toml.Primitive, meta MetaData err error){
// 判断配置文件是否存在
if _, err := os.Stat(fileName); err != nil {
if os.IsNotExist(err) {
err = fmt.Errorf("configuration file %s does not exist.\r\n", fileName)
} else {
err = fmt.Errorf("configuration file %s execption:%s\r\n", fileName, err.Error())
}
return
}
// 加载配置文件
var sections map[string]toml.Primitive
if meta, err = toml.DecodeFile(fileName, &file); err != nil {
err = fmt.Errorf("load configuration file %s failed:%s", fileName, err.Error())
}else{
err = meta.PrimitiveDecode(file, &sections)
}
return
}
// DecodeSection 解码一个节点的配置信息
func DecodeSection(filename, name string, v interface{}) (err error) {
sections, _err := loadTomlFile(filename)
if err != nil{
return
}
if section, ok := sections[name]; ok {
return meta.PrimitiveDecode(section, v)
}
return nil
}

45
src/punching/util/conv.go Normal file
View File

@@ -0,0 +1,45 @@
package util
import (
"bytes"
"encoding/binary"
"strconv"
"strings"
)
func InetAddr(ipaddr string) [4]byte {
var (
ips = strings.Split(ipaddr, ".")
ip [4]uint64
ret [4]byte
)
for i := 0; i < 4; i++ {
ip[i], _ = strconv.ParseUint(ips[i], 10, 8)
}
for i := 0; i < 4; i++ {
ret[i] = byte(ip[i])
}
return ret
}
func InetPort(ipport string) uint16 {
ret, _ := strconv.ParseUint(ipport, 10, 16)
return uint16(ret)
}
// IntToBytes 整形转换成字节
func IntToBytes(n int) []byte {
x := int32(n)
bytesBuffer := bytes.NewBuffer([]byte{})
binary.Write(bytesBuffer, binary.BigEndian, x)
return bytesBuffer.Bytes()
}
// BytesToInt 字节转换成整形
func BytesToInt(b []byte) int {
bytesBuffer := bytes.NewBuffer(b)
var x int32
binary.Read(bytesBuffer, binary.BigEndian, &x)
return int(x)
}

137
src/punching/util/net.go Normal file
View File

@@ -0,0 +1,137 @@
package util
import (
"fmt"
"log"
"net"
"os"
"p2p/util"
"strings"
"time"
)
// 连接到Proxy代理解析端 地址/类型/IP
// 发送第一个包->(接收到错误数据退出否则接收到NatAddress,->确认收到 <->完成 不断接收心跳包,确认)
// 客户端 错误失败 | nat地址
// 服务端 错误失败 | 注册 心跑包
func ClientDialProxy(proxyAddr string, pairkey string, role string) (localAddr string, destAddr string, pairname string, err error) {
// 发第一个包
// 地址接收确认
// 心跑包接收和确认
var conn = util.NetConn{}
// 不指定端口,让系统自动分配
err = conn.Bind("tcp", "")
if err != nil {
log.Println("绑定出错", err.Error())
return
}
// 连接到Proxy解析服务器
tcpAddr, err := net.ResolveTCPAddr("tcp", proxyAddr)
err = conn.Connect(util.InetAddr(tcpAddr.IP.String()), tcpAddr.Port)
if err != nil {
fmt.Println("连接服务端出错", err.Error())
return
}
defer conn.Close()
fmt.Println("已连接服务器,服务器地址是:%s:%d", tcpAddr.IP.String(), tcpAddr.Port)
for {
PackageProxy()
key := "ok" + name
buff01 := make([]byte, len(key))
//发送第一个包
log.Println("发送数据1", name)
n, err := conn.Write([]byte(name))
if err != nil {
log.Println("出错,原因:", err.Error())
os.Exit(22)
}
log.Println("have write: ", n)
i, err := conn.Read(buff01)
if err != nil {
fmt.Println("读取数据出错,", err.Error())
os.Exit(1)
}
log.Print("读取数据:", string(buff01[0:i]))
if string(buff01[0:i]) == key {
break
}
log.Println("发送数据", []byte(name))
conn.Write([]byte(name))
}
fmt.Println("--- Request sent, waiting for parkner in Name ...", name)
buff := make([]byte, 512)
//获取服务发送过来的对方IP信息
len, err := conn.Read(buff)
if err != nil {
log.Println("客户端读取数据出错:", err.Error())
os.Exit(1)
}
pairAddr := string(buff[0:len])
arrPairAddr := strings.Split(pairAddr, ",")
remoteAddrStr := arrPairAddr[0]
localAddrStr := arrPairAddr[1]
log.Println("读取到远程IP", remoteAddrStr)
conn.Close()
//time.Sleep(3 * time.Second)
remoteAddr, err := net.ResolveTCPAddr("tcp", remoteAddrStr)
//localAddr.String()
log.Println("local addr:", localAddrStr)
}
func DialPeer(localAddr string, remoteAddr string) util.NetConn {
remoteTCPAddr, err := net.ResolveTCPAddr("tcp", remoteAddr)
var conn util.NetConn
// 不指定端口,让系统自动分配
err = conn.Bind("tcp", localAddr)
if err != nil {
log.Println("绑定出错", err.Error())
return
}
// TODO 应该封装成一个函数
// 有时连接并不成功,多次连接
log.Println("远程地址是:", remoteAddr.IP.String(), remoteAddr.Port)
tryCount := 0
for {
tryCount += 1
if tryCount > 10 {
log.Printf("连接不上,退出")
return
}
err02 := conn02.Connect(util.InetAddr(remoteAddr.IP.String()), remoteAddr.Port)
if err02 != nil {
log.Printf("第%d次不能连接远程服务器:%s", tryCount, err02.Error())
time.Sleep(1 * time.Second)
continue
} else {
log.Println("已经连接到peer: ", remoteAddr.String())
break
}
}
}

View File

@@ -0,0 +1,125 @@
package util
import (
"fmt"
"log"
"net"
"os"
"syscall"
"time"
)
type NetConn struct {
fd int // 文件句柄
conn *net.Conn // 连接对象
}
func (hole *NetConn) Close() {
if hole.conn != nil {
hole.conn.Close()
}
}
func (hole *NetConn) Bind(proto, addr string) (err error) {
if "tcp" != proto {
log.Println("tcp != proto")
return
}
syscall.ForkLock.RLock()
if fd, err = syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP); err != nil {
syscall.ForkLock.RUnlock()
return
}
syscall.ForkLock.RUnlock()
defer func() {
if err != nil {
syscall.Close(fd)
}
}()
if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
return
}
if len(addr) > 0 {
var tcp *net.TCPAddr
tcp, err = net.ResolveTCPAddr(proto, addr)
if err != nil && tcp.IP != nil {
log.Println(err)
return
}
sockaddr := &syscall.SockaddrInet4{Port: tcp.Port}
if err = syscall.Bind(fd, sockaddr); err != nil {
return
}
}
hole.fd = fd
return
}
func (hole *NetConn) Connect(addr [4]byte, port int) (err error) {
if hole.fd == 0 {
err = error{"请先调用Bind()函数"}
return
}
addrInet4 := syscall.SockaddrInet4{
Addr: addr,
Port: port,
}
chConnect := make(chan error)
go func() {
err = syscall.Connect(fd, &addrInet4)
chConnect <- err
}()
//有时候连接被远端抛弃的时候, syscall.Connect() 会很久才返回
ticker := time.NewTicker(60 * time.Second)
select {
case <-ticker.C:
err = fmt.Errorf("Connect timeout")
return
case e := <-chConnect:
if e != nil {
err = e
log.Println("Connect error: ", err)
return
}
}
// 转为net.conn对象
var file *os.File
file = os.NewFile(uintptr(fd), fmt.Sprintf("tcpholepunching.%d", time.Now().UnixNano()))
if conn0, err0 := net.FileConn(file); err0 != nil {
log.Println("Connect error", err0)
err = err0
return
} else {
hole.conn = &conn0
}
if err = file.Close(); err != nil {
log.Println("Connect error", err)
return
}
return
}
func (hole *NetConn) Read(buffer []byte) (length int, err error) {
return hole.Read(buffer)
}
func (hole *NetConn) Write(data []byte) (length int, err error) {
return hole.Write(data)
}

View File

@@ -0,0 +1,131 @@
package util
import (
"fmt"
"log"
"net"
"syscall"
"time"
)
type NetConn struct {
sock syscall.Handle
}
func (hole *NetConn) Close() {
syscall.WSACleanup()
syscall.Closesocket(hole.sock)
}
func (hole *NetConn) Bind(proto, addr string) (err error) {
if "tcp" != proto {
log.Println("tcp != proto")
return
}
var wsadata syscall.WSAData
if err = syscall.WSAStartup(MAKEWORD(2, 2), &wsadata); err != nil {
log.Println("Startup error")
return
}
var sock syscall.Handle
syscall.ForkLock.RLock()
if sock, err = syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP); err != nil {
syscall.ForkLock.RUnlock()
return
}
syscall.ForkLock.RUnlock()
defer func() {
if err != nil {
syscall.Close(sock)
}
}()
if err = syscall.SetsockoptInt(sock, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
return
}
if len(addr) > 0 {
var tcp *net.TCPAddr
tcp, err = net.ResolveTCPAddr(proto, addr)
if err != nil && tcp.IP != nil {
log.Println(err)
return
}
sockaddr := &syscall.SockaddrInet4{Port: tcp.Port}
if err = syscall.Bind(sock, sockaddr); err != nil {
return
}
}
hole.sock = sock
return
}
func (hole *NetConn) Connect(addr [4]byte, port int) (err error) {
if hole.sock == 0 {
err = Error{"请先执行Bind()"}
return
}
addrInet4 := syscall.SockaddrInet4{
Addr: addr,
Port: port,
}
chConnect := make(chan error)
go func() {
err = syscall.Connect(hole.sock, &addrInet4)
chConnect <- err
}()
//有时候连接被远端抛弃的时候, syscall.Connect() 会很久才返回
ticker := time.NewTicker(30 * time.Second)
select {
case <-ticker.C:
err = fmt.Errorf("Connect timeout")
return
case e := <-chConnect:
if e != nil {
err = e
log.Println("Connect error: ", err)
return
}
}
return nil
}
func (hole *NetConn) Read(buffer []byte) (length int, err error) {
dataWsaBuf := syscall.WSABuf{Len: uint32(len(buffer)), Buf: &buffer[0]}
flags := uint32(0)
recvd := uint32(0)
err = syscall.WSARecv(hole.sock, &dataWsaBuf, 1, &recvd, &flags, nil, nil)
if err != nil {
return 0, err
}
return int(recvd), nil
}
func (hole *NetConn) Write(data []byte) (length int, err error) {
var (
dataWsaBuf syscall.WSABuf
SendBytes uint32
overlapped syscall.Overlapped
)
dataWsaBuf.Len = uint32(len(data))
dataWsaBuf.Buf = &data[0]
err = syscall.WSASend(hole.sock, &dataWsaBuf, 1, &SendBytes, 0, &overlapped, nil)
if err != nil {
return 0, err
} else {
return int(SendBytes), nil
}
}

View File

@@ -0,0 +1,67 @@
package util
const {
PACKAGE_PROXY_HEADER byte = 'P'
PACKAGE_NAT_HEADER [6]byte = []byte{'C','B','X','N','A','T'}
}
// 跟代理解析端通讯封包
func PackageProxy(control byte, data []byte) []byte{
pack := bytes.NewBuffer(nil)
pack.Write(PACKAGE_PROXY_HEADER)
pack.Write(control)
pack.Write(data)
return pack.Bytes()
}
// 跟代理解析端通讯拆包
func UnpackageProxy(pact []byte)(control byte, data []byte) {
}
// Nat网络后面的Client端和Server端封包
func PackageNat(control byte, sessionKey [4]byte, data []byte) []byte{
}
// Nat网络后面的Client端和Server端拆包
// 需要考虑沾包分析出的完整封装包传入读channel
func UnpackageNat(pact []byte)(data []byte){
length := len(buffer)
log.Println("长度为:", length)
var i int
for i = 0; i < length; i = i + 1 {
if length < i+ConstHeaderLength+ConstControlLength+ConstDataLength {
break
}
if string(buffer[i:i+len(ConstHeaderKey)]) == ConstHeaderKey {
// 获取头数据长度
messageLength := BytesToInt(buffer[i+ConstHeaderLength+ConstControlLength : i+ConstHeaderLength+ConstControlLength+ConstDataLength])
log.Println("数据长度为:", messageLength)
log.Println("需要的包长度:", ConstHeaderLength+ConstControlLength+ConstDataLength+messageLength)
if length < i+ConstHeaderLength+ConstControlLength+ConstDataLength+messageLength {
break
}
data := buffer[i+ConstHeaderLength+ConstControlLength+ConstDataLength : i+ConstHeaderLength+ConstControlLength+ConstDataLength+messageLength]
controlID := string(buffer[i+ConstHeaderLength : i+ConstHeaderLength+ConstControlLength])
key := string(buffer[i+len(ConstHeaderKey) : i+ConstHeaderLength])
log.Println("控制ID为", controlID, "key为", key)
dataPackage := DataPackage{
Data: data,
ControlID: controlID,
Key: key,
}
Rch <- dataPackage
i += ConstHeaderLength + ConstControlLength + ConstDataLength + messageLength - 1
}
}
if i == length {
return make([]byte, 0)
}
return buffer[i:]
}

66
src/punching/util/rand.go Normal file
View File

@@ -0,0 +1,66 @@
package util
import (
"crypto/md5"
"crypto/rand"
"encoding/base64"
"encoding/hex"
"io"
"time"
)
//生成32位md5字串
func GetMd5String(s string) string {
h := md5.New()
h.Write([]byte(s))
return hex.EncodeToString(h.Sum(nil))
}
//生成Guid字串
func UniqueId() string {
b := make([]byte, 48)
if _, err := io.ReadFull(rand.Reader, b); err != nil {
return ""
}
return GetMd5String(base64.URLEncoding.EncodeToString(b))
}
// GenerateRandomPairKey 获取4位随机匹配码
func GenerateRandomPairKey() string {
//97122 小写字母
rndNums := GenerateRandomNumber(97, 122, 4)
key := ""
for num, _ := range rndNums {
key = key + string(byte{num})
}
return key
}
//生成count个[start,end)结束的不重复的随机数
func GenerateRandomNumber(start int, end int, count int) []int {
//范围检查
if end < start || (end-start) < count {
return nil
}
//存放结果的slice
nums := make([]int, 0)
//随机数生成器,加入时间戳保证每次生成的随机数不一样
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for len(nums) < count {
//生成随机数
num := r.Intn((end - start)) + start
//查重
exist := false
for _, v := range nums {
if v == num {
exist = true
break
}
}
if !exist {
nums = append(nums, num)
}
}
return nums
}