From 978a9d882d89170e641811f75042a310575ab273 Mon Sep 17 00:00:00 2001 From: qydysky Date: Sun, 14 Apr 2024 03:40:12 +0000 Subject: [PATCH] init --- .gitignore | 2 + build.sh | 5 + config.go | 9 ++ go.mod | 32 +++++ go.sum | 46 ++++++ main.go | 119 ++++++++++++++++ main.json | 16 +++ main_test.go | 392 +++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 621 insertions(+) create mode 100644 .gitignore create mode 100755 build.sh create mode 100755 config.go create mode 100755 go.mod create mode 100755 go.sum create mode 100755 main.go create mode 100755 main.json create mode 100755 main_test.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..417d048 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +Forward.exe +Forward.run diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..b3b6374 --- /dev/null +++ b/build.sh @@ -0,0 +1,5 @@ +#!/bin/bash +rm -rf tcpF.* +CGO_ENABLED=0 go build -buildmode=exe -o Forward.run . +CGO_ENABLED=0 GOOS=windows go build -buildmode=exe -o Forward.exe . +echo ok \ No newline at end of file diff --git a/config.go b/config.go new file mode 100755 index 0000000..db1ab07 --- /dev/null +++ b/config.go @@ -0,0 +1,9 @@ +package main + +type Config []ConfigItem + +type ConfigItem struct { + Listen string `json:"listen"` + To string `json:"to"` + Accept []string `json:"accept"` +} diff --git a/go.mod b/go.mod new file mode 100755 index 0000000..911da47 --- /dev/null +++ b/go.mod @@ -0,0 +1,32 @@ +module github.com/qydysky/forward + +go 1.22 + +toolchain go1.22.1 + +require ( + github.com/dustin/go-humanize v1.0.1 + github.com/qydysky/part v0.28.20240411191949 +) + +require ( + github.com/andybalholm/brotli v1.1.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/klauspost/compress v1.17.7 // indirect + github.com/miekg/dns v1.1.58 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/shirou/gopsutil v3.21.11+incompatible // indirect + github.com/thedevsaddam/gojsonq/v2 v2.5.2 // indirect + github.com/tklauser/go-sysconf v0.3.13 // indirect + github.com/tklauser/numcpus v0.7.0 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect + golang.org/x/mod v0.16.0 // indirect + golang.org/x/net v0.22.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.19.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace github.com/qydysky/part => ../part diff --git a/go.sum b/go.sum new file mode 100755 index 0000000..3380e2d --- /dev/null +++ b/go.sum @@ -0,0 +1,46 @@ +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= +github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= +github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= +github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/thedevsaddam/gojsonq/v2 v2.5.2 h1:CoMVaYyKFsVj6TjU6APqAhAvC07hTI6IQen8PHzHYY0= +github.com/thedevsaddam/gojsonq/v2 v2.5.2/go.mod h1:bv6Xa7kWy82uT0LnXPE2SzGqTj33TAEeR560MdJkiXs= +github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4= +github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0= +github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4= +github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= +golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= +golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go new file mode 100755 index 0000000..3dd6a7c --- /dev/null +++ b/main.go @@ -0,0 +1,119 @@ +package main + +import ( + "context" + "encoding/json" + "errors" + "flag" + "io" + "log" + "net" + "os" + "os/signal" + "sync" + + "github.com/dustin/go-humanize" + "github.com/qydysky/part" + pctx "github.com/qydysky/part/ctx" + file "github.com/qydysky/part/file" +) + +func main() { + // 获取config路径 + c := flag.String("c", "main.json", "c") + flag.Parse() + if *c == "" { + return + } + + f := file.New(*c, 0, true) + if !f.IsExist() { + log.Fatal("config no exist") + return + } + + var config Config + if data, e := f.ReadAll(humanize.KByte, humanize.MByte); e != nil && !errors.Is(e, io.EOF) { + log.Fatal(e) + return + } else if e := json.Unmarshal(data, &config); e != nil { + log.Fatal(e) + return + } else { + // ctrl+c退出 + var interrupt = make(chan os.Signal, 2) + signal.Notify(interrupt, os.Interrupt) + + ctx := pctx.CarryCancel(context.WithCancel(context.Background())) + msdChan, wait := dealConfig(ctx, config) + + defer wait() + defer pctx.CallCancel(ctx) + + for { + select { + case msg := <-msdChan: + switch msg.fmsg.Type { + case part.LisnMsg: + log.Default().Printf("LISTEN %v => %v", msg.item.Listen, msg.item.To) + case part.AcceptMsg: + log.Default().Printf("ACCEPT %v => %v", (msg.fmsg.Msg).(net.Addr).String(), msg.item.To) + case part.DenyMsg: + log.Default().Printf("DENY %v => %v", (msg.fmsg.Msg).(net.Addr).String(), msg.item.To) + case part.ErrorMsg: + log.Default().Fatalf("ERROR %v => %v %v", msg.item.Listen, msg.item.To, msg.fmsg.Msg) + default: + } + case <-interrupt: + log.Default().Printf("CLOSE") + return + } + } + } +} + +type ConfigMsg struct { + item ConfigItem + fmsg part.ForwardMsg +} + +func dealConfig(ctx context.Context, config Config) (msgChan chan ConfigMsg, WaitFin func()) { + msgChan = make(chan ConfigMsg, 10) + var wg sync.WaitGroup + wg.Add(len(config)) + for _, v := range config { + go func(ctx context.Context, item ConfigItem) { + defer wg.Done() + + var msg_chan chan part.ForwardMsg + var close func() + + close, msg_chan = part.Forward(item.To, item.Listen, item.Accept) + + go func() { + <-ctx.Done() + close() + }() + defer pctx.CallCancel(ctx) + + for { + select { + case msg := <-msg_chan: + select { + case msgChan <- ConfigMsg{item: item, fmsg: msg}: + default: + <-msgChan + msgChan <- ConfigMsg{item: item, fmsg: msg} + } + if msg.Type == part.ErrorMsg { + return + } + case <-ctx.Done(): + return + } + } + }(ctx, v) + } + + return msgChan, wg.Wait +} diff --git a/main.json b/main.json new file mode 100755 index 0000000..3d96cbf --- /dev/null +++ b/main.json @@ -0,0 +1,16 @@ +[ + { + "listen":"tcp://0.0.0.0:10803", + "to":"tcp://10.79.250.18:3389", + "accept":[ + "0.0.0.0/0" + ] + }, + { + "listen":"tcp://0.0.0.0:10803", + "to":"tcp://10.87.72.84:3389", + "accept":[ + "0.0.0.0/0" + ] + } +] \ No newline at end of file diff --git a/main_test.go b/main_test.go new file mode 100755 index 0000000..19a2e4e --- /dev/null +++ b/main_test.go @@ -0,0 +1,392 @@ +package main + +import ( + "bufio" + "context" + "errors" + "log" + "net" + "testing" + "time" + + "github.com/qydysky/part" + pctx "github.com/qydysky/part/ctx" +) + +func Test(t *testing.T) { + ctx := pctx.CarryCancel(context.WithCancel(context.Background())) + msdChan, wait := dealConfig(ctx, []ConfigItem{ + { + Listen: "tcp://127.0.0.1:20000", + To: "tcp://127.0.0.1:20001", + Accept: []string{"127.0.0.1/32"}, + }, + { + Listen: "tcp://127.0.0.1:20002", + To: "tcp://127.0.0.1:20003", + Accept: []string{"127.0.0.2/32"}, + }, + { + Listen: "udp://127.0.0.1:20000", + To: "udp://127.0.0.1:20001", + Accept: []string{"127.0.0.1/32"}, + }, + { + Listen: "udp://127.0.0.1:20004", + To: "udp://127.0.0.1:20005", + Accept: []string{"127.0.0.2/32"}, + }, + { + Listen: "udp://127.0.0.1:20006", + To: "tcp://127.0.0.1:20007", + Accept: []string{"127.0.0.1/32"}, + }, + { + Listen: "tcp://127.0.0.1:20008", + To: "udp://127.0.0.1:20009", + Accept: []string{"127.0.0.1/32"}, + }, + }) + + go func() { + for { + select { + case msg := <-msdChan: + switch msg.fmsg.Type { + case part.LisnMsg: + log.Default().Printf("LISTEN %v => %v", msg.item.Listen, msg.item.To) + case part.AcceptMsg: + log.Default().Printf("ACCEPT %v => %v", (msg.fmsg.Msg).(net.Addr).String(), msg.item.To) + case part.DenyMsg: + log.Default().Printf("DENY %v => %v", (msg.fmsg.Msg).(net.Addr).String(), msg.item.To) + case part.ErrorMsg: + log.Default().Fatalf("ERROR %v => %v %v", msg.item.Listen, msg.item.To, msg.fmsg.Msg) + default: + } + case <-ctx.Done(): + log.Default().Printf("CLOSE") + return + } + } + }() + defer wait() + defer pctx.CallCancel(ctx) + + time.Sleep(time.Second) + if e := tcpSer("127.0.0.1:20000", "127.0.0.1:20001"); e != nil { + t.Fatal(e) + } + if e := tcpSer("127.0.0.1:20002", "127.0.0.1:20003"); e == nil { + t.Fatal(e) + } + if e := udpSer("127.0.0.1:20000", "127.0.0.1:20001"); e != nil { + t.Fatal(e) + } + if e := udpSer("127.0.0.1:20004", "127.0.0.1:20005"); e == nil { + t.Fatal(e) + } + if e := udp2tcpSer("127.0.0.1:20006", "127.0.0.1:20007"); e != nil { + t.Fatal(e) + } + if e := tcp2udpSer("127.0.0.1:20008", "127.0.0.1:20009"); e != nil { + t.Fatal(e) + } +} + +func tcpSer(lis, to string) error { + ec := make(chan error, 10) + { + // Resolve the string address to a TCP address + tcpAddr, err := net.ResolveTCPAddr("tcp4", to) + + if err != nil { + return err + } + + // Start listening for TCP connections on the given address + listener, err := net.ListenTCP("tcp", tcpAddr) + + if err != nil { + return err + } + + defer listener.Close() + + go func() { + // Accept new connections + conn, err := listener.Accept() + if err != nil { + ec <- err + return + } + defer conn.Close() + // Handle new connections in a Goroutine for concurrency + // Read from the connection untill a new line is send + _, err = bufio.NewReader(conn).ReadString('\n') + if err != nil { + ec <- err + return + } + + // Print the data read from the connection to the terminal + + // Write back the same message to the client + conn.Write([]byte("Hello TCP Client\n")) + }() + } + + tcpAddr, err := net.ResolveTCPAddr("tcp4", lis) + + if err != nil { + return err + } + + // Connect to the address with tcp + conn, err := net.DialTCP("tcp", nil, tcpAddr) + + if err != nil { + return err + } + + // Send a message to the server + _, err = conn.Write([]byte("Hello TCP Server\n")) + if err != nil { + return err + } + + // Read from the connection untill a new line is send + data, err := bufio.NewReader(conn).ReadString('\n') + if err != nil { + return err + } + + if string(data) != "Hello TCP Client\n" { + return errors.New("no match:" + string(data)) + } + + select { + case err := <-ec: + return err + default: + return nil + } +} + +func udpSer(lis, to string) error { + ec := make(chan error, 10) + { + // Resolve the string address to a TCP address + udpAddr, err := net.ResolveUDPAddr("udp", to) + + if err != nil { + return err + } + conn, err := net.ListenUDP("udp", udpAddr) + + if err != nil { + return err + } + + go func() { + var buf [512]byte + defer conn.Close() + for { + _, addr, err := conn.ReadFromUDP(buf[0:]) + if err != nil { + ec <- err + return + } + // Write back the message over UPD + _, err = conn.WriteToUDP([]byte("Hello UDP Client\n"), addr) + if err != nil { + ec <- err + return + } + } + }() + } + + udpAddr, err := net.ResolveUDPAddr("udp", lis) + + if err != nil { + return err + } + + conn1, err := net.ListenUDP("udp", nil) + + if err != nil { + return err + } + + conn1.WriteToUDP([]byte("Hello UDP Server\n"), udpAddr) + + var buf [512]byte + conn1.SetDeadline(time.Now().Add(time.Second)) + n, _, err := conn1.ReadFromUDP(buf[0:]) + if err != nil { + return err + } + + if string(buf[:n]) != "Hello UDP Client\n" { + return errors.New("no match:" + string(buf[:n])) + } + + conn1.WriteToUDP([]byte("Hello UDP Server\n"), udpAddr) + conn1.SetDeadline(time.Now().Add(time.Second)) + n, _, err = conn1.ReadFromUDP(buf[0:]) + if err != nil { + return err + } + + if string(buf[:n]) != "Hello UDP Client\n" { + return errors.New("no match:" + string(buf[:n])) + } + + select { + case err := <-ec: + return err + default: + return nil + } +} + +func udp2tcpSer(lis, to string) error { + ec := make(chan error, 10) + { + // Resolve the string address to a TCP address + tcpAddr, err := net.ResolveTCPAddr("tcp4", to) + + if err != nil { + return err + } + + // Start listening for TCP connections on the given address + listener, err := net.ListenTCP("tcp", tcpAddr) + + if err != nil { + return err + } + + defer listener.Close() + + go func() { + conn, err := listener.Accept() + if err != nil { + ec <- err + return + } + defer conn.Close() + + _, err = bufio.NewReader(conn).ReadString('\n') + if err != nil { + ec <- err + return + } + + // Print the data read from the connection to the terminal + + // Write back the same message to the client + conn.Write([]byte("Hello TCP Client\n")) + }() + } + + udpAddr, err := net.ResolveUDPAddr("udp", lis) + + if err != nil { + return err + } + + conn1, err := net.ListenUDP("udp", nil) + + if err != nil { + return err + } + + conn1.WriteToUDP([]byte("Hello UDP Server\n"), udpAddr) + + var buf [512]byte + conn1.SetDeadline(time.Now().Add(time.Second)) + n, _, err := conn1.ReadFromUDP(buf[0:]) + if err != nil { + return err + } + + if string(buf[:n]) != "Hello TCP Client\n" { + return errors.New("no match:" + string(buf[:n])) + } + + select { + case err := <-ec: + return err + default: + return nil + } +} + +func tcp2udpSer(lis, to string) error { + ec := make(chan error, 10) + { + listener, err := part.NewUdpListener("udp", to) + + if err != nil { + return err + } + + defer listener.Close() + + go func() { + conn, err := listener.Accept() + if err != nil { + ec <- err + return + } + defer conn.Close() + + _, err = bufio.NewReader(conn).ReadString('\n') + if err != nil { + ec <- err + return + } + + // Print the data read from the connection to the terminal + + // Write back the same message to the client + conn.Write([]byte("Hello UDP Client\n")) + }() + } + + addr, err := net.ResolveTCPAddr("tcp4", lis) + + if err != nil { + return err + } + + conn, err := net.DialTCP("tcp", nil, addr) + + if err != nil { + return err + } + + // Send a message to the server + _, err = conn.Write([]byte("Hello TCP Server\n")) + if err != nil { + return err + } + + // Read from the connection untill a new line is send + data, err := bufio.NewReader(conn).ReadString('\n') + if err != nil { + return err + } + + if string(data) != "Hello UDP Client\n" { + return errors.New("no match:" + string(data)) + } + + select { + case err := <-ec: + return err + default: + return nil + } +} -- 2.39.2