diff --git a/.gitignore b/.gitignore index 6a17dad..bfc0a8b 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ *.so *.dylib +simple-tcp-proxy + # Test binary, build with `go test -c` *.test diff --git a/simple-tcp-proxy.go b/simple-tcp-proxy.go new file mode 100644 index 0000000..967cb11 --- /dev/null +++ b/simple-tcp-proxy.go @@ -0,0 +1,124 @@ +package main + +import ( + "fmt" + "io" + "io/ioutil" + "net" + "os" + "strings" + "encoding/json" +) + +type TcpListen struct { + Listen string `json:"listen"` + Backend string `json:"backend"` +} + +type TcpListenConfig struct { + TcpListens []TcpListen `json:"tcpListens"` +} + +// { +// "tcpListens": [ +// "listen": ":8080", +// "backend": "11.22.33.44:8080" +// ] +// } +// https://blog.csdn.net/fyxichen/article/details/51505542 +func main() { + + tcpListenConfig, err := readConfigFile() + + if err != nil { + fmt.Printf("Error in load config file: %v\n", err) + return + } + + if len(tcpListenConfig.TcpListens) == 0 { + fmt.Println("Not find any mapping in config") + return + } + + for _, tcpListen := range tcpListenConfig.TcpListens { + go server(tcpListen) + } + ExitChan := make(chan bool, 1) + <-ExitChan +} + +func server(tcpListen TcpListen) { + lis, err := net.Listen("tcp", tcpListen.Listen) + if err != nil { + fmt.Println(err) + return + } + defer lis.Close() + fmt.Printf("Listen TCP at: %v --> %v\n", tcpListen.Listen, tcpListen.Backend) + for { + conn, err := lis.Accept() + if err != nil { + fmt.Printf("Listen error: %v\n", err) + continue + } + fmt.Printf("TCP connection from %v, local: %v\n", conn.RemoteAddr(), conn.LocalAddr()) + go handle(tcpListen, conn) + } +} + +func handle(tcpListen TcpListen, sconn net.Conn) { + defer sconn.Close() + ip := tcpListen.Backend + dconn, err := net.Dial("tcp", ip) + if err != nil { + fmt.Printf("Connect to %v failed:%v\n", ip, err) + return + } + ExitChan := make(chan bool, 1) + go func(sconn net.Conn, dconn net.Conn, Exit chan bool) { + _, err := io.Copy(dconn, sconn) + if err != nil { + errStr := fmt.Sprintf("%v", err) + if strings.Contains(errStr, "use of closed network connection") { + fmt.Printf("Connection closed: %v\n", ip) + } else { + fmt.Printf("Send data to %v failed:%v\n", ip, err) + } + } + ExitChan <- true + }(sconn, dconn, ExitChan) + go func(sconn net.Conn, dconn net.Conn, Exit chan bool) { + _, err := io.Copy(sconn, dconn) + if err != nil { + errStr := fmt.Sprintf("%v", err) + if strings.Contains(errStr, "use of closed network connection") { + fmt.Printf("Connection closed: %v\n", ip) + } else { + fmt.Printf("Receive data from %v failed:%v\n", ip, err) + } + } + ExitChan <- true + }(sconn, dconn, ExitChan) + <-ExitChan + dconn.Close() +} + +func readConfigFile() (*TcpListenConfig, error) { + configFile, err := os.Open("tcp_listen_config.json") + if err != nil { + return nil, err + } + defer configFile.Close() + + configFileBytes, err := ioutil.ReadAll(configFile) + if err != nil { + return nil, err + } + var tcpListenConfig TcpListenConfig + err = json.Unmarshal(configFileBytes, &tcpListenConfig) + if err != nil { + return nil, err + } + + return &tcpListenConfig, nil +} \ No newline at end of file diff --git a/tcp_listen_config.json b/tcp_listen_config.json new file mode 100644 index 0000000..4f18d05 --- /dev/null +++ b/tcp_listen_config.json @@ -0,0 +1,8 @@ +{ + "tcpListens": [ + { + "listen": ":8443", + "backend": "101.132.122.240:443" + } + ] +}