增加代理方式的打洞,进行tcp的转发

This commit is contained in:
2025-10-28 19:33:20 +08:00
parent c48c85f075
commit 2f4097b697
9 changed files with 236 additions and 67 deletions

5
config/proxy.yaml Normal file
View File

@@ -0,0 +1,5 @@
#tcp:
# - key: "12D3KooWQsb1137nCzqbMMCzwHsyU8aaCZeFnBUBTkWVsfp8gs26"
# port: 51888
# ip: ay.cname.com
# local_port: 20000

View File

@@ -37,18 +37,14 @@ var (
s := g.Server(consts.Name) s := g.Server(consts.Name)
parser, err = gcmd.Parse(g.MapStrBool{
"p,port": true,
})
//port := parser.GetOpt("port", "23333").Int()
parser, err = gcmd.Parse(g.MapStrBool{ parser, err = gcmd.Parse(g.MapStrBool{
"w,ws": true, "w,ws": true,
"g,gateway": true, "g,gateway": true,
"p,port": true, "p,port": true,
"t,type": true,
}) })
addr := g.Cfg().MustGet(ctx, "ws.address").String() //addr := g.Cfg().MustGet(ctx, "ws.address").String()
ws := parser.GetOpt("ws", addr).String() ws := parser.GetOpt("ws").String()
if ws == "" { if ws == "" {
listVar := g.Cfg().MustGet(ctx, "p2p.list") listVar := g.Cfg().MustGet(ctx, "p2p.list")
var p2pItem []struct { var p2pItem []struct {
@@ -64,9 +60,10 @@ var (
} }
port := parser.GetOpt("port", 0).Int() port := parser.GetOpt("port", 0).Int()
if port > 0 {
s.SetPort(port) s.SetPort(port)
} //if port > 0 {
// s.SetPort(port)
//}
s.Group("/", func(group *ghttp.RouterGroup) { s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(ghttp.MiddlewareHandlerResponse) group.Middleware(ghttp.MiddlewareHandlerResponse)
@@ -85,6 +82,8 @@ var (
} }
}) })
startType := parser.GetOpt("type").String()
if startType != "server" {
// 延迟启动 // 延迟启动
gtimer.SetTimeout(ctx, time.Second*5, func(ctx context.Context) { gtimer.SetTimeout(ctx, time.Second*5, func(ctx context.Context) {
g.Log().Debug(ctx, "开始执行客户端") g.Log().Debug(ctx, "开始执行客户端")
@@ -92,8 +91,9 @@ var (
err = service.P2P().Start(ws) err = service.P2P().Start(ws)
g.Log().Debugf(ctx, "当前监听端口:%v", s.GetListenedPort()) g.Log().Debugf(ctx, "当前监听端口:%v", s.GetListenedPort())
}) })
//s.SetPort(0)
}
// 启动系统托盘 // 启动系统托盘
service.OS().Load(consts.Name, consts.Name+"服务端", "manifest/images/favicon.ico") service.OS().Load(consts.Name, consts.Name+"服务端", "manifest/images/favicon.ico")

View File

@@ -40,20 +40,20 @@ func (c *ControllerV1) Update(ctx context.Context, req *v1.UpdateReq) (res *v1.U
} }
} }
//更新文件
err = service.System().Update(ctx)
type DataType struct { type DataType struct {
File []byte `json:"file"` File []byte `json:"file"`
Name string `json:"name"` Name string `json:"name"`
} }
//var GatewayMessage *p2p.GatewayMessage
var msgData = struct { var msgData = struct {
Files []*DataType `json:"files"` Files []*DataType `json:"files"`
}{} }{}
msgData.Files = []*DataType{} msgData.Files = []*DataType{}
files, _ := gfile.ScanDir("download", ".*gz") files, _ := gfile.ScanDir("download", "*.gz")
for _, v := range files { for _, v := range files {
msgData.Files = append(msgData.Files, &DataType{ msgData.Files = append(msgData.Files, &DataType{
@@ -63,5 +63,8 @@ func (c *ControllerV1) Update(ctx context.Context, req *v1.UpdateReq) (res *v1.U
} }
service.P2P().SendAll("update", msgData) service.P2P().SendAll("update", msgData)
//更新自己的文件
//err = service.System().Update(ctx)
return return
} }

View File

@@ -14,6 +14,14 @@ import (
"github.com/gogf/gf/v2/os/gfile" "github.com/gogf/gf/v2/os/gfile"
) )
// 引入 Windows API 函数
var (
user32 = syscall.NewLazyDLL("user32.dll")
kernel32 = syscall.NewLazyDLL("kernel32.dll")
showWindow = user32.NewProc("ShowWindow")
getConsoleWnd = kernel32.NewProc("GetConsoleWindow")
)
func (s *sOS) start() { func (s *sOS) start() {
// 系统托盘初始化(设置图标、右键菜单) // 系统托盘初始化(设置图标、右键菜单)
@@ -22,6 +30,7 @@ func (s *sOS) start() {
// 系统托盘初始化(设置图标、右键菜单) // 系统托盘初始化(设置图标、右键菜单)
func (s *sOS) onSystrayReady() { func (s *sOS) onSystrayReady() {
//s.hideConsole()
iconByte := gfile.GetBytes(s.systray.Icon) iconByte := gfile.GetBytes(s.systray.Icon)
systray.SetIcon(iconByte) systray.SetIcon(iconByte)
@@ -29,7 +38,7 @@ func (s *sOS) onSystrayReady() {
systray.SetTooltip(s.systray.Tooltip) systray.SetTooltip(s.systray.Tooltip)
mQuit := systray.AddMenuItem("退出", "退出应用") mQuit := systray.AddMenuItem("退出", "退出应用")
systray.AddMenuItemCheckbox("隐藏窗口", "隐藏窗口", false) mShow := systray.AddMenuItemCheckbox("显示窗口", "显示窗口", false)
// Sets the icon of a menu item. Only available on Mac and Windows. // Sets the icon of a menu item. Only available on Mac and Windows.
//mQuit.SetIcon(iconByte) //mQuit.SetIcon(iconByte)
go func() { go func() {
@@ -37,6 +46,9 @@ func (s *sOS) onSystrayReady() {
select { select {
case <-mQuit.ClickedCh: case <-mQuit.ClickedCh:
systray.Quit() systray.Quit()
case <-mShow.ClickedCh:
// 显示窗口
s.showConsole()
} }
} }
@@ -62,3 +74,25 @@ func (s *sOS) update(version, server string) {
return return
} }
} }
// 隐藏控制台窗口
func (s *sOS) hideConsole() {
// 获取当前控制台窗口句柄
hWnd, _, _ := getConsoleWnd.Call()
if hWnd == 0 {
return // 无控制台窗口如编译为GUI子系统时
}
// SW_HIDE = 0隐藏窗口
showWindow.Call(hWnd, 0)
}
// 显示控制台窗口
func (s *sOS) showConsole() {
// 获取当前控制台窗口句柄
hWnd, _, _ := getConsoleWnd.Call()
if hWnd == 0 {
return
}
// SW_SHOW = 5显示窗口
showWindow.Call(hWnd, 5)
}

View File

@@ -4,16 +4,19 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net" "io"
"path" "path"
"strconv" "strconv"
"time" "time"
"github.com/ayflying/p2p/internal/service"
"github.com/gogf/gf/v2/encoding/gjson" "github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/gtcp"
"github.com/gogf/gf/v2/os/gctx" "github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/os/gfile" "github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/glog" "github.com/gogf/gf/v2/os/glog"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/os/gtimer" "github.com/gogf/gf/v2/os/gtimer"
"github.com/gogf/gf/v2/util/grand" "github.com/gogf/gf/v2/util/grand"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
@@ -47,8 +50,11 @@ type Message struct {
// SendP2P 发送格式化消息 // SendP2P 发送格式化消息
func (s *sP2P) SendP2P(targetID string, typ string, data []byte) (err error) { func (s *sP2P) SendP2P(targetID string, typ string, data []byte) (err error) {
if typ == "" {
typ = "message"
}
message := &Message{ message := &Message{
Type: "message", Type: typ,
From: s.client.Id, From: s.client.Id,
Data: data, Data: data,
} }
@@ -56,22 +62,6 @@ func (s *sP2P) SendP2P(targetID string, typ string, data []byte) (err error) {
return return
} }
func (s *sP2P) linkTcp(addr string) {
//conn, err := gtcp.Dial(addr)
// 使用标准库 net.Dial 建立连接
stdConn, err := net.Dial("tcp", addr)
if err != nil {
fmt.Printf("Dial error: %v\n", err)
return
}
defer stdConn.Close()
//// 将标准库的 net.Conn 封装为 gtcp.Conn
//gtcpConn := gtcp.NewConn(stdConn)
//defer gtcpConn.Close()
}
func (s *sP2P) Start(wsStr string) (err error) { func (s *sP2P) Start(wsStr string) (err error) {
var ctx = gctx.New() var ctx = gctx.New()
hostObj, err := s.CreateLibp2pHost(ctx, 0) hostObj, err := s.CreateLibp2pHost(ctx, 0)
@@ -92,20 +82,22 @@ func (s *sP2P) Start(wsStr string) (err error) {
// 设置流处理函数处理P2P消息 // 设置流处理函数处理P2P消息
hostObj.SetStreamHandler(protocol.ID(ProtocolID), s.handleStream) hostObj.SetStreamHandler(protocol.ID(ProtocolID), s.handleStream)
for {
// 连接网关WebSocket // 连接网关WebSocket
if err = s.connectGateway(); err != nil { if err = s.connectGateway(); err != nil {
g.Log().Fatalf(ctx, "连接网关失败: %v", err) g.Log().Errorf(ctx, "连接网关失败,60秒后重试: %v", err)
time.Sleep(60 * time.Second)
} else {
break
}
} }
// 启动网关消息接收协程 // 启动网关消息接收协程
go s.receiveGatewayMessages(ctx) go s.receiveGatewayMessages(ctx)
g.Log().Infof(ctx, "已连接网关成功客户端ID: %s", s.client.Id) //启动代理初始化
//g.Log().Infof(ctx,"当前地址http://127.0.0.1/") s.ProxyInit()
//select {
//case <-ctx.Done():
//}
return return
} }
@@ -226,23 +218,76 @@ func (s *sP2P) DiscoverAndConnect(targetID string) error {
func (s *sP2P) handleStream(stream network.Stream) { func (s *sP2P) handleStream(stream network.Stream) {
ctx := gctx.New() ctx := gctx.New()
defer stream.Close() defer stream.Close()
//var err error
peerID := stream.Conn().RemotePeer().String() //peerID := stream.Conn().RemotePeer().String()
//glog.Infof(ctx, "收到来自 %s 的连接", peerID) //glog.Infof(ctx, "收到来自 %s 的连接", peerID)
// 读取数据 // 读取数据
buf := make([]byte, 1024) buf := make([]byte, 1024)
var msg []byte
for {
n, err := stream.Read(buf) n, err := stream.Read(buf)
msg = append(msg, buf[:n]...)
// 再判断错误
if err != nil { if err != nil {
glog.Errorf(ctx, "读取流数据失败: %v", err) if err == io.EOF {
// EOF 是正常结束,不算错误
err = nil
break
} else {
return return
} }
}
//if err != nil {
// glog.Errorf(ctx, "读取流数据失败: %v", err)
// return
//}
}
var msg = buf[:n]
// 解析消息 // 解析消息
var message *Message var message *Message
err = gjson.DecodeTo(msg, &message) if err := gjson.DecodeTo(msg, &message); err != nil {
g.Log().Debugf(ctx, "收到来自 %s 的消息: %v ", peerID, gjson.MustEncodeString(message)) g.Log().Debugf(ctx, "解析消息失败: %v", msg)
}
//g.Log().Debugf(ctx, "收到来自 %s 的消息: %v ", peerID, gjson.MustEncodeString(message))
switch message.Type {
case "proxy":
var data *ProxyType
gjson.DecodeTo(message.Data, &data)
//g.Dump(data)
// Client
for {
if conn, err := gtcp.NewConn(fmt.Sprintf("%s:%v", data.Ip, data.Port)); err == nil {
if b, err := conn.SendRecv([]byte(gtime.Datetime()), -1); err == nil {
fmt.Println(string(b), conn.LocalAddr(), conn.RemoteAddr())
err = s.SendP2P(message.From, "proxy_ack", gjson.MustEncode(&ProxyType{
Ip: ip,
Port: message.Port,
Data: b,
}))
} else {
fmt.Println(err)
}
conn.Close()
} else {
//glog.Error(err)
}
//time.Sleep(time.Second)
}
//conn, err := gtcp.NewConn(fmt.Sprintf("%s:%v", data.Ip, data.Port))
//if err != nil {
// g.Log().Errorf(ctx, "连接失败:%v", err)
// return
//}
//defer conn.Close()
}
} }
// 发送数据到目标节点 // 发送数据到目标节点
@@ -331,9 +376,9 @@ func (s *sP2P) receiveGatewayMessages(ctx context.Context) {
for { for {
_, data, err := s.client.wsConn.ReadMessage() _, data, err := s.client.wsConn.ReadMessage()
if err != nil { if err != nil {
glog.Errorf(ctx, "接收网关消息失败: %v", err) g.Log().Errorf(ctx, "接收网关消息失败: %v", err)
gtimer.SetTimeout(ctx, 30*time.Second, func(ctx context.Context) { gtimer.SetTimeout(ctx, 10*time.Second, func(ctx context.Context) {
err = s.connectGateway() err = s.connectGateway()
return return
}) })
@@ -346,11 +391,11 @@ func (s *sP2P) receiveGatewayMessages(ctx context.Context) {
continue continue
} }
//// 验证消息是否发给自己to必须是当前客户端ID或空 // 验证消息是否发给自己to必须是当前客户端ID或空
//if msg.To != "" && msg.To != s.client.Id { if msg.To != "" && msg.To != s.client.Id {
// g.Log().Debugf(ctx, "忽略非本客户端的消息from=%s, to=%s", msg.From, msg.To) g.Log().Debugf(ctx, "忽略非本客户端的消息from=%s, to=%s", msg.From, msg.To)
// continue continue
//} }
// 处理不同类型消息 // 处理不同类型消息
switch msg.Type { switch msg.Type {
@@ -370,7 +415,7 @@ func (s *sP2P) receiveGatewayMessages(ctx context.Context) {
} }
if !msgData.Found { if !msgData.Found {
fmt.Println("gateway未找到目标节点") g.Log().Debug(ctx, "gateway未找到目标节点")
continue continue
} }
@@ -434,7 +479,9 @@ func (s *sP2P) receiveGatewayMessages(ctx context.Context) {
// 更新器路径(假设与主程序同目录) // 更新器路径(假设与主程序同目录)
//updaterPath := filepath.Join(filepath.Dir(selfPath), "updater.exe") //updaterPath := filepath.Join(filepath.Dir(selfPath), "updater.exe")
g.Log().Infof(ctx, "更新节点信息: %v", data) g.Log().Info(ctx, "文件接收完成")
// 开始覆盖文件与重启
err = service.System().Update(ctx)
//// 调用不同系统的更新服务 //// 调用不同系统的更新服务
//service.OS().Update(msgData.Version, msgData.Server) //service.OS().Update(msgData.Version, msgData.Server)

View File

@@ -140,7 +140,7 @@ func (s *sP2P) handleRegister(ctx context.Context, conn *websocket.Conn, msg *Ga
s.Clients[msg.From] = client s.Clients[msg.From] = client
s.lock.Unlock() s.lock.Unlock()
glog.Infof(ctx, "客户端 ip=%s,%s 注册成功PeerID: %s", conn.RemoteAddr(), msg.From, data.PeerID) g.Log().Infof(ctx, "客户端 ip=%s,%s 注册成功PeerID: %s", conn.RemoteAddr(), msg.From, data.PeerID)
// 发送注册成功响应 // 发送注册成功响应
err := s.sendMessage(conn, &GatewayMessage{ err := s.sendMessage(conn, &GatewayMessage{

View File

@@ -26,7 +26,7 @@ var (
// 常量定义 // 常量定义
const ( const (
ProtocolID string = "/ay" ProtocolID string = "/ay/p2p/1.0.0"
//DefaultPort = 51888 //DefaultPort = 51888
) )

View File

@@ -0,0 +1,78 @@
package p2p
import (
"fmt"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/gtcp"
"github.com/gogf/gf/v2/os/gctx"
)
type ProxyType struct {
Ip string `json:"ip"`
Port int `json:"port"`
Data []byte `json:"data"`
}
func (s *sP2P) ProxyInit() {
type cfgType struct {
Key string `json:"key"`
Ip string `json:"ip"`
Port int `json:"port"`
LocalPort int `json:"local_port"`
}
var cfgList []*cfgType
proxyCfg, err := g.Cfg("proxy").Get(gctx.New(), "tcp")
if err == nil {
proxyCfg.Scan(&cfgList)
for _, v := range cfgList {
go s.Tcp(v.Key, v.Port, v.LocalPort, v.Ip)
}
}
}
func (s *sP2P) Tcp(key string, toPort, myPort int, ip string) {
if ip == "" {
ip = "127.0.0.1"
}
// 建立p2p连接
err := s.DiscoverAndConnect(key)
if err != nil {
}
err = gtcp.NewServer(fmt.Sprintf("0.0.0.0:%d", myPort), func(conn *gtcp.Conn) {
defer conn.Close()
for {
ctx := gctx.New()
data, err := conn.Recv(-1)
if len(data) > 0 {
g.Log().Debugf(gctx.New(), "内容:%v", string(data))
err = s.SendP2P(key, "proxy", gjson.MustEncode(&ProxyType{
Ip: ip,
Port: toPort,
Data: data,
}))
if err != nil {
g.Log().Errorf(ctx, "发送失败:%v", err)
s.Tcp(key, toPort, myPort, ip)
return
}
//if err = conn.Send(append([]byte("> "), data...)); err != nil {
// fmt.Println(err)
//}
}
if err != nil {
break
}
}
}).Run()
if err != nil {
g.Log().Error(gctx.New(), err)
}
}

View File

@@ -37,6 +37,8 @@ type (
Send(conn *websocket.Conn, typ string, data any) (err error) Send(conn *websocket.Conn, typ string, data any) (err error)
// 只获取IPv4公网IP过滤IPv6结果 // 只获取IPv4公网IP过滤IPv6结果
GetIPv4PublicIP() (string, error) GetIPv4PublicIP() (string, error)
ProxyInit()
Tcp(key string, toPort int, myPort int, ip string)
} }
) )