From: qydysky Date: Sun, 14 Apr 2024 03:40:12 +0000 (+0000) Subject: init X-Git-Url: http://127.0.0.1:8081/?a=commitdiff_plain;h=978a9d882d89170e641811f75042a310575ab273;p=forward%2F.git init --- 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 + } +}