]> 127.0.0.1 Git - forward/.git/commitdiff
init
authorqydysky <qydysky@foxmail.com>
Sun, 14 Apr 2024 03:40:12 +0000 (03:40 +0000)
committerqydysky <qydysky@foxmail.com>
Sun, 14 Apr 2024 03:40:12 +0000 (03:40 +0000)
.gitignore [new file with mode: 0644]
build.sh [new file with mode: 0755]
config.go [new file with mode: 0755]
go.mod [new file with mode: 0755]
go.sum [new file with mode: 0755]
main.go [new file with mode: 0755]
main.json [new file with mode: 0755]
main_test.go [new file with mode: 0755]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..417d048
--- /dev/null
@@ -0,0 +1,2 @@
+Forward.exe
+Forward.run
diff --git a/build.sh b/build.sh
new file mode 100755 (executable)
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 (executable)
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 (executable)
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 (executable)
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 (executable)
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 (executable)
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 (executable)
index 0000000..19a2e4e
--- /dev/null
@@ -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
+       }
+}