├── .dockerignore ├── .github └── workflows │ ├── AutoBuild.yml │ └── docker-image.yml ├── Dockerfile ├── LICENSE.MD ├── README.MD ├── client └── main.go ├── common └── main.go ├── docker-entrypoint.sh ├── go.mod ├── go.sum └── server └── main.go /.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile -------------------------------------------------------------------------------- /.github/workflows/AutoBuild.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: AutoBuild 5 | permissions: 6 | contents: write 7 | on: 8 | push: 9 | tags: 10 | - '*' 11 | pull_request: 12 | tags: 13 | - '*' 14 | 15 | jobs: 16 | 17 | build: 18 | runs-on: ubuntu-latest 19 | strategy: 20 | matrix: 21 | os: [linux, darwin, windows] 22 | 23 | steps: 24 | - uses: actions/checkout@v3 25 | 26 | - name: Set up Go 27 | uses: actions/setup-go@v4 28 | with: 29 | go-version: '1.20' 30 | 31 | - name: Build_client 32 | run: cd client && export GOOS=${{matrix.os}} && export GOARCH=amd64 && go build -o client_windows.exe . 33 | if: matrix.os == 'windows' 34 | - name: Build_server 35 | run: cd server && export GOOS=${{matrix.os}} && export GOARCH=amd64 && go build -o server_windows.exe . 36 | if: matrix.os == 'windows' 37 | 38 | - name: Build_client 39 | run: cd client && export GOOS=${{matrix.os}} && export GOARCH=amd64 && go build -o client_linux . 40 | if: matrix.os == 'linux' 41 | - name: Build_server 42 | run: cd server && export GOOS=${{matrix.os}} && export GOARCH=amd64 && go build -o server_linux . 43 | if: matrix.os == 'linux' 44 | 45 | - name: Build_client 46 | run: cd client && export GOOS=${{matrix.os}} && export GOARCH=amd64 && go build -o client_macos . 47 | if: matrix.os == 'darwin' 48 | - name: Build_server 49 | run: cd server && export GOOS=${{matrix.os}} && export GOARCH=amd64 && go build -o server_macos . 50 | if: matrix.os == 'darwin' 51 | 52 | - name: Upload to GitHub Releases 53 | uses: softprops/action-gh-release@v1 54 | with: 55 | files: client/client_windows.exe,client/client_linux,client/client_macos,server/server_windows.exe,server/server_linux,server/server_macos 56 | env: 57 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 58 | 59 | -------------------------------------------------------------------------------- /.github/workflows/docker-image.yml: -------------------------------------------------------------------------------- 1 | name: Build and Push Docker Image 2 | 3 | on: 4 | push: 5 | branches: [ "dev" ] # Trigger the workflow on push to the main branch 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout code 12 | uses: actions/checkout@v2 13 | 14 | - name: Set up Docker Buildx 15 | uses: docker/setup-buildx-action@v1 16 | 17 | - name: Log in to Docker Hub 18 | uses: docker/login-action@v1 19 | with: 20 | username: ${{ secrets.DOCKER_HUB_USERNAME }} 21 | password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} 22 | 23 | - name: Build and push Docker image 24 | uses: docker/build-push-action@v2 25 | with: 26 | platforms: linux/amd64,linux/arm64 27 | context: . 28 | push: true 29 | tags: golangboyme/wsproxy 30 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang as builder 2 | WORKDIR /app 3 | COPY . . 4 | WORKDIR /app/client 5 | RUN export CGO_ENABLED=0 && go build -o app . 6 | WORKDIR /app/server 7 | RUN export CGO_ENABLED=0 && go build -o app . 8 | WORKDIR /app 9 | RUN chmod +x docker-entrypoint.sh 10 | 11 | FROM alpine 12 | COPY --from=builder /app /app 13 | EXPOSE 80 1180 14 | CMD ["/app/docker-entrypoint.sh"] 15 | -------------------------------------------------------------------------------- /LICENSE.MD: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 wsproxy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # 📖 Introduction 2 | A lightweight proxy tool based on the WebSocket protocol 3 | # 🚀 Features 4 | - Lightweight 5 | - No configuration 6 | - Wrap CDN 7 | - Support SOCKS5/HTTP 8 | # 🔨️ Build 9 | ```shell 10 | git clone github.com/golangboy/wsproxy 11 | ``` 12 | ## Client 13 | ```shell 14 | cd client 15 | go build . 16 | ./client -h 17 | ``` 18 | 19 | ```shell 20 | Usage of ./client: 21 | -listen string 22 | listen socks5 address (default ":1180") 23 | -ws string 24 | websocket server address (default "localhost:80") 25 | 26 | ``` 27 | 28 | ## Server 29 | ```shell 30 | cd server 31 | go build . 32 | ./server -h 33 | ``` 34 | 35 | ```shell 36 | Usage of ./server: 37 | -listen string 38 | listen address (default ":80") 39 | 40 | ``` 41 | # 🐳 Docker 42 | 43 | ## From Build 44 | ```shell 45 | git clone github.com/golangboy/wsproxy 46 | cd wsproxy 47 | docker build -t wsproxy . 48 | ``` 49 | #### client 50 | ```shell 51 | docker run -itd -p 1180:1180 -e ws=your_server:80 wsproxy 52 | ``` 53 | #### server 54 | ```shell 55 | docker run -itd -p 80:80 wsproxy 56 | ``` 57 | 58 | ## From DockerHub 59 | ```shell 60 | docker pull golangboyme/wsproxy 61 | ``` 62 | #### client 63 | ```shell 64 | docker run -itd -p 1180:1180 -e ws=your_server:80 golangboyme/wsproxy 65 | ``` 66 | #### server 67 | ```shell 68 | docker run -itd -p 80:80 golangboyme/wsproxy 69 | ``` 70 | # 💻 Test 71 | ```shell 72 | export all_proxy=socks5://localhost:1180 73 | curl https://google.com -v 74 | ``` 75 | -------------------------------------------------------------------------------- /client/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/base64" 5 | "flag" 6 | "fmt" 7 | "io" 8 | "log" 9 | "main/common" 10 | "net" 11 | "net/url" 12 | "os" 13 | "strconv" 14 | "strings" 15 | 16 | "github.com/gorilla/websocket" 17 | uuid "github.com/satori/go.uuid" 18 | ) 19 | 20 | var wsServer *string 21 | 22 | func main() { 23 | os.Setenv("http_proxy", "") 24 | os.Setenv("https_proxy", "") 25 | os.Setenv("HTTP_PROXY", "") 26 | os.Setenv("HTTPS_PROXY", "") 27 | os.Setenv("ALL_PROXY", "") 28 | 29 | listenAddr := flag.String("listen", ":1180", "listen socks5 address") 30 | wsServer = flag.String("ws", "localhost:80", "websocket server address") 31 | flag.Parse() 32 | log.SetFlags(log.LstdFlags | log.Lshortfile | log.Lmicroseconds) 33 | listener, err := net.Listen("tcp", *listenAddr) 34 | if err != nil { 35 | log.Fatalf("Failed to listen on %s: %v", listenAddr, err) 36 | } 37 | 38 | log.Printf("SOCKS5 proxy server is listening on %s", *listenAddr) 39 | 40 | for { 41 | conn, err := listener.Accept() 42 | if err != nil { 43 | log.Printf("Failed to accept connection: %v", err) 44 | continue 45 | } 46 | 47 | go handleConnection(conn) 48 | } 49 | } 50 | func convertToMap(input string) map[string]string { 51 | result := make(map[string]string) 52 | 53 | lines := strings.Split(input, "\n") 54 | for _, line := range lines { 55 | parts := strings.SplitN(line, ": ", 2) 56 | if len(parts) == 2 { 57 | result[strings.ToLower(strings.TrimSpace(parts[0]))] = strings.TrimSpace(parts[1]) 58 | } 59 | } 60 | 61 | return result 62 | } 63 | 64 | func handleConnection(clientConn net.Conn) { 65 | defer clientConn.Close() 66 | 67 | // Step 1: Version identification and authentication 68 | // Read and verify the SOCKS5 initial handshake message 69 | buf := make([]byte, 257) 70 | nbytes, err := io.ReadAtLeast(clientConn, buf, 2) 71 | if err != nil { 72 | log.Printf("Failed to read client handshake: %v", err) 73 | return 74 | } 75 | _ = nbytes 76 | 77 | var destAddr string 78 | var destPort string 79 | isSocks5 := false 80 | isHttps := false 81 | // Check SOCKS version and authentication methods 82 | if buf[0] == 0x05 { 83 | isSocks5 = true 84 | // Number of authentication methods supported 85 | numMethods := int(buf[1]) 86 | authMethods := buf[2 : 2+numMethods] 87 | 88 | // Check if "no authentication" method (0x00) is supported 89 | noAuth := false 90 | for _, m := range authMethods { 91 | if m == 0x00 { 92 | noAuth = true 93 | break 94 | } 95 | } 96 | 97 | if !noAuth { 98 | log.Printf("No supported authentication methods") 99 | // Send handshake failure response to client 100 | clientConn.Write([]byte{0x05, 0xFF}) 101 | return 102 | } 103 | 104 | // Send handshake response to client indicating "no authentication" method 105 | clientConn.Write([]byte{0x05, 0x00}) 106 | 107 | // Step 2: Request processing 108 | // Read and verify the SOCKS5 request 109 | _, err = io.ReadAtLeast(clientConn, buf, 4) 110 | if err != nil { 111 | log.Printf("Failed to read client request: %v", err) 112 | return 113 | } 114 | 115 | if buf[0] != 0x05 { 116 | log.Printf("Unsupported SOCKS version: %v", buf[0]) 117 | return 118 | } 119 | 120 | if buf[1] != 0x01 { 121 | log.Printf("Unsupported command: %v", buf[1]) 122 | return 123 | } 124 | 125 | // Check the address type 126 | switch buf[3] { 127 | case 0x01: // IPv4 address 128 | ip := net.IP(buf[4 : 4+net.IPv4len]) 129 | destAddr = ip.String() 130 | destPort = fmt.Sprintf("%d", int(buf[8])<<8+int(buf[9])) 131 | case 0x03: // Domain name 132 | domainLen := int(buf[4]) 133 | domain := string(buf[5 : 5+domainLen]) 134 | destAddr = domain 135 | destPort = strconv.Itoa(int(buf[5+domainLen])<<8 + int(buf[5+domainLen+1])) 136 | case 0x04: // IPv6 address 137 | ip := net.IP(buf[4 : 4+net.IPv6len]) 138 | destAddr = ip.String() 139 | destPort = strconv.Itoa(int(buf[20])<<8 + int(buf[21])) 140 | default: 141 | log.Printf("Unsupported address type: %v", buf[3]) 142 | return 143 | } 144 | 145 | // Send request response to client indicating success 146 | clientConn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) 147 | } else if (buf[0] == 'G' && buf[1] == 'E') || (buf[0] == 'P' && buf[1] == 'O') || (buf[0] == 'O' && buf[1] == 'P') || (buf[0] == 'P' && buf[1] == 'U') || (buf[0] == 'D' && buf[1] == 'E') || (buf[0] == 'H' && buf[1] == 'E') || (buf[0] == 'T' && buf[1] == 'R') { 148 | r := convertToMap(string(buf[:nbytes])) 149 | hosts := r["host"] 150 | if strings.Index(hosts, ":") == -1 { 151 | destAddr = hosts 152 | destPort = "80" 153 | } else { 154 | destAddr = strings.Split(hosts, ":")[0] 155 | destPort = strings.Split(hosts, ":")[1] 156 | } 157 | } else if buf[0] == 'C' && buf[1] == 'O' { 158 | isHttps = true 159 | r := convertToMap(string(buf[:nbytes])) 160 | hosts := r["host"] 161 | if strings.Index(hosts, ":") == -1 { 162 | destAddr = hosts 163 | destPort = "443" 164 | } else { 165 | destAddr = strings.Split(hosts, ":")[0] 166 | destPort = strings.Split(hosts, ":")[1] 167 | } 168 | } 169 | u := url.URL{ 170 | Scheme: "ws", 171 | Host: *wsServer, 172 | Path: "/chat", 173 | } 174 | msgId := uuid.NewV4().String() 175 | log.Printf("[%s]Client requests proxy connection: %s:%s", msgId, destAddr, destPort) 176 | // connection to websocket server 177 | conn, _, err := websocket.DefaultDialer.Dial(u.String(), nil) 178 | if err != nil { 179 | log.Printf("[%s]Failed to connect to the WebSocket server: %v", msgId, err) 180 | clientConn.Close() 181 | return 182 | } 183 | ws := common.WsConn{ 184 | Conn: conn, 185 | } 186 | ws.Lock() 187 | err = ws.Conn.WriteJSON(common.Proto{ 188 | MsgType: common.ReqConnect, 189 | Data: []byte(base64.StdEncoding.EncodeToString([]byte(destAddr + ":" + destPort))), 190 | MsgId: msgId, 191 | }) 192 | ws.Unlock() 193 | if err != nil { 194 | log.Printf("[%s]Failed to write connect request to WebSocket server: %v", msgId, err) 195 | clientConn.Close() 196 | return 197 | } 198 | connectResp := common.Proto{} 199 | 200 | err = ws.Conn.ReadJSON(&connectResp) 201 | if err != nil { 202 | log.Printf("[%s]Failed to request proxy target from WebSocket server: %v", msgId, err) 203 | clientConn.Close() 204 | return 205 | } 206 | 207 | if connectResp.MsgType != common.ReqConnect { 208 | log.Printf("[%s]ReqConnect failed: %v", msgId, err) 209 | ws.Conn.Close() 210 | clientConn.Close() 211 | return 212 | } 213 | if isHttps { 214 | clientConn.Write([]byte("HTTP/1.1 200 Connection Established\r\n\r\n")) 215 | } 216 | if false == isSocks5 && false == isHttps { 217 | err = ws.Conn.WriteJSON(common.Proto{MsgType: common.ReqData, Data: buf[:nbytes], MsgId: msgId}) 218 | if err != nil { 219 | log.Printf("[%s]Failed to send http data to WebSocket server: %v", msgId, err) 220 | } 221 | } 222 | go func() { 223 | resp := common.Proto{} 224 | for { 225 | err = ws.Conn.ReadJSON(&resp) 226 | if err != nil { 227 | log.Printf("[%s]Failed to read data from WebSocket server: %v", msgId, err) 228 | break 229 | } 230 | _, err = clientConn.Write(resp.Data) 231 | if err != nil { 232 | log.Printf("[%s]Failed to write data to the client: %v", msgId, err) 233 | break 234 | } 235 | } 236 | }() 237 | for { 238 | var buf [10240]byte 239 | n, err := clientConn.Read(buf[:]) 240 | if err != nil { 241 | log.Printf("[%s]Failed to read data from the client: %v", msgId, err) 242 | break 243 | } 244 | ws.Lock() 245 | err = ws.Conn.WriteJSON(common.Proto{ 246 | MsgType: common.ReqData, 247 | Data: buf[:n], 248 | MsgId: msgId, 249 | }) 250 | ws.Unlock() 251 | if err != nil { 252 | log.Printf("[%s]Failed to write data to the WebSocket server: %v", msgId, err) 253 | break 254 | } 255 | } 256 | ws.Conn.Close() 257 | clientConn.Close() 258 | log.Printf("[%s]Closing the connection", msgId) 259 | } 260 | -------------------------------------------------------------------------------- /common/main.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "github.com/gorilla/websocket" 5 | "sync" 6 | ) 7 | 8 | type Proto struct { 9 | MsgType int `json:"msg_type"` 10 | MsgId string `json:"msg_id"` 11 | Data []byte `json:"data"` 12 | } 13 | type WsConn struct { 14 | Conn *websocket.Conn 15 | sync.Mutex 16 | } 17 | 18 | const ( 19 | ReqConnect = iota 20 | ReqData = iota 21 | ) 22 | -------------------------------------------------------------------------------- /docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ -z $ws ]; then 3 | echo "Starting server" 4 | /app/server/app 5 | else 6 | echo "Starting client" 7 | /app/client/app -ws=$ws 8 | fi -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module main 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/gin-gonic/gin v1.9.1 7 | github.com/gorilla/websocket v1.5.0 8 | github.com/satori/go.uuid v1.2.0 9 | ) 10 | 11 | require ( 12 | github.com/bytedance/sonic v1.9.1 // indirect 13 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect 14 | github.com/gabriel-vasile/mimetype v1.4.2 // indirect 15 | github.com/gin-contrib/sse v0.1.0 // indirect 16 | github.com/go-playground/locales v0.14.1 // indirect 17 | github.com/go-playground/universal-translator v0.18.1 // indirect 18 | github.com/go-playground/validator/v10 v10.14.0 // indirect 19 | github.com/goccy/go-json v0.10.2 // indirect 20 | github.com/json-iterator/go v1.1.12 // indirect 21 | github.com/klauspost/cpuid/v2 v2.2.4 // indirect 22 | github.com/leodido/go-urn v1.2.4 // indirect 23 | github.com/mattn/go-isatty v0.0.19 // indirect 24 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 25 | github.com/modern-go/reflect2 v1.0.2 // indirect 26 | github.com/pelletier/go-toml/v2 v2.0.8 // indirect 27 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 28 | github.com/ugorji/go/codec v1.2.11 // indirect 29 | golang.org/x/arch v0.3.0 // indirect 30 | golang.org/x/crypto v0.9.0 // indirect 31 | golang.org/x/net v0.10.0 // indirect 32 | golang.org/x/sys v0.8.0 // indirect 33 | golang.org/x/text v0.9.0 // indirect 34 | google.golang.org/protobuf v1.30.0 // indirect 35 | gopkg.in/yaml.v3 v3.0.1 // indirect 36 | ) 37 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= 2 | github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= 3 | github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= 4 | github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= 5 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= 6 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= 7 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 9 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 10 | github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= 11 | github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= 12 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 13 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 14 | github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= 15 | github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= 16 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= 17 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 18 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 19 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 20 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 21 | github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= 22 | github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= 23 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 24 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 25 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 26 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= 27 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 28 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 29 | github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= 30 | github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 31 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 32 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 33 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 34 | github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= 35 | github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= 36 | github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= 37 | github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= 38 | github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 39 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 40 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 41 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 42 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 43 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 44 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 45 | github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= 46 | github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= 47 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 48 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 49 | github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= 50 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 51 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 52 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 53 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 54 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 55 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 56 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 57 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 58 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 59 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 60 | github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= 61 | github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 62 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 63 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 64 | github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= 65 | github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 66 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 67 | golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= 68 | golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 69 | golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= 70 | golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= 71 | golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= 72 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 73 | golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 74 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 75 | golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= 76 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 77 | golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= 78 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 79 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 80 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 81 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 82 | google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= 83 | google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 84 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 85 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 86 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 87 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 88 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 89 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 90 | -------------------------------------------------------------------------------- /server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/base64" 5 | "flag" 6 | "log" 7 | "main/common" 8 | "net" 9 | 10 | "github.com/gin-gonic/gin" 11 | "github.com/gorilla/websocket" 12 | ) 13 | 14 | func main() { 15 | listenAddr := flag.String("listen", ":80", "listen address") 16 | flag.Parse() 17 | g := gin.Default() 18 | log.SetFlags(log.LstdFlags | log.Lshortfile | log.Lmicroseconds) 19 | g.GET("/chat", func(context *gin.Context) { 20 | upgrade := websocket.Upgrader{} 21 | conn, err := upgrade.Upgrade(context.Writer, context.Request, nil) 22 | ws := common.WsConn{Conn: conn} 23 | if err != nil { 24 | return 25 | } 26 | go func() { 27 | msgId := "" 28 | p := common.Proto{} 29 | var target net.Conn 30 | for { 31 | err = ws.Conn.ReadJSON(&p) 32 | if err != nil { 33 | break 34 | } 35 | if p.MsgType == common.ReqConnect { 36 | msgId = p.MsgId 37 | bytes, _ := base64.StdEncoding.DecodeString(string(p.Data)) 38 | target, err = net.Dial("tcp", string(bytes)) 39 | if err != nil { 40 | log.Printf("[%s]Failed to connect to the target server: %v", msgId, err) 41 | break 42 | } 43 | err = ws.Conn.WriteJSON(common.Proto{ 44 | MsgType: common.ReqConnect, 45 | MsgId: msgId, 46 | }) 47 | if err != nil { 48 | log.Printf("[%s]Failed to send connection success message to the client: %v", msgId, err) 49 | break 50 | } 51 | go func() { 52 | for { 53 | buf := make([]byte, 10240) 54 | n, err := target.Read(buf[:]) 55 | if err != nil { 56 | log.Printf("[%s]Failed to read data from the target server: %v", msgId, err) 57 | break 58 | } 59 | ws.Lock() 60 | err = ws.Conn.WriteJSON(common.Proto{ 61 | MsgType: common.ReqData, 62 | Data: buf[:n], 63 | MsgId: msgId, 64 | }) 65 | ws.Unlock() 66 | if err != nil { 67 | log.Printf("[%s]Failed to send data to the client: %v", msgId, err) 68 | break 69 | } 70 | } 71 | target.Close() 72 | log.Printf("[%s]Connection closed_2", msgId) 73 | }() 74 | } else if p.MsgType == common.ReqData { 75 | _, err = target.Write(p.Data) 76 | if err != nil { 77 | log.Printf("[%s]Failed to send data to the target server:%v", msgId, err) 78 | break 79 | } 80 | } 81 | } 82 | ws.Lock() 83 | ws.Conn.Close() 84 | ws.Unlock() 85 | log.Printf("[%s]Connection closed_1", msgId) 86 | }() 87 | //context.JSON(200, gin.H{ 88 | // "message": "ok", 89 | //}) 90 | }) 91 | g.Run(*listenAddr) 92 | } 93 | --------------------------------------------------------------------------------