--- /dev/null
+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=
--- /dev/null
+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
+}
--- /dev/null
+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
+ }
+}