├── .gitignore ├── config.yaml ├── config └── config.go ├── go.mod ├── go.sum ├── lib.go ├── limits └── limits.go ├── neko-relay.go ├── relay ├── UDPDistribute.go ├── http.go ├── relay.go ├── resversproxy.go ├── reverse_proxy.go ├── tcp.go ├── tls.go ├── traffic.go ├── udp.go ├── ws_tunnel_client.go ├── ws_tunnel_server.go ├── wss_tunnel_client.go └── wss_tunnel_server.go ├── rules └── rules.go └── stat └── stat.go /.gitignore: -------------------------------------------------------------------------------- 1 | neko-relay 2 | *.key 3 | *.pem 4 | rules.json -------------------------------------------------------------------------------- /config.yaml: -------------------------------------------------------------------------------- 1 | key: 2 | port: 8080 3 | debug: false 4 | certfile: public.pem 5 | keyfile: private.key 6 | syncfile: rules.json 7 | fakehost: www.upyun.com 8 | fakeurl: https://www.upyun.com -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type CONF struct { 4 | Key string 5 | Port int 6 | Debug bool 7 | Certfile string 8 | Keyfile string 9 | Syncfile string 10 | Fakehost string 11 | Fakeurl string 12 | } 13 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module neko-relay 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 // indirect 7 | github.com/gin-gonic/gin v1.7.1 8 | github.com/go-ole/go-ole v1.2.5 // indirect 9 | github.com/go-playground/validator/v10 v10.6.0 // indirect 10 | github.com/golang/protobuf v1.5.2 // indirect 11 | github.com/gorilla/websocket v1.4.2 // indirect 12 | github.com/json-iterator/go v1.1.11 // indirect 13 | github.com/leodido/go-urn v1.2.1 // indirect 14 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 15 | github.com/modern-go/reflect2 v1.0.1 // indirect 16 | github.com/orcaman/concurrent-map v0.0.0-20210501183033-44dafcb38ecc // indirect 17 | github.com/patrickmn/go-cache v2.1.0+incompatible 18 | github.com/pires/go-proxyproto v0.5.0 19 | github.com/shirou/gopsutil v3.21.4+incompatible 20 | github.com/tklauser/go-sysconf v0.3.5 // indirect 21 | github.com/txthinking/runnergroup v0.0.0-20210326110939-37fc67d0da7c 22 | github.com/txthinking/socks5 v0.0.0-20210326104807-61b5745ff346 23 | github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe // indirect 24 | github.com/ugorji/go v1.2.5 // indirect 25 | golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf // indirect 26 | golang.org/x/net v0.0.0-20210508051633-16afe75a6701 27 | golang.org/x/sys v0.0.0-20210507161434-a76c4d0a0096 // indirect 28 | golang.org/x/text v0.3.6 // indirect 29 | gopkg.in/yaml.v2 v2.4.0 // indirect 30 | ) 31 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 h1:5sXbqlSomvdjlRbWyNqkPsJ3Fg+tQZCbgeX1VGljbQY= 4 | github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= 5 | github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= 6 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 7 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 8 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M= 9 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 10 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 11 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 12 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 13 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 14 | github.com/envoyproxy/go-control-plane v0.9.8 h1:bbmjRkjmP0ZggMoahdNMmJFFnK7v5H+/j5niP5QH6bg= 15 | github.com/envoyproxy/go-control-plane v0.9.8/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 16 | github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= 17 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 18 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 19 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 20 | github.com/gin-gonic/gin v1.7.1 h1:qC89GU3p8TvKWMAVhEpmpB2CIb1hnqt2UdKZaP93mS8= 21 | github.com/gin-gonic/gin v1.7.1/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= 22 | github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= 23 | github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 24 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 25 | github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= 26 | github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= 27 | github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= 28 | github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= 29 | github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= 30 | github.com/go-playground/validator/v10 v10.5.0 h1:X9rflw/KmpACwT8zdrm1upefpvdy6ur8d1kWyq6sg3E= 31 | github.com/go-playground/validator/v10 v10.5.0/go.mod h1:xm76BBt941f7yWdGnI2DVPFFg1UK3YY04qifoXU3lOk= 32 | github.com/go-playground/validator/v10 v10.6.0 h1:UGIt4xR++fD9QrBOoo/ascJfGe3AGHEB9s6COnss4Rk= 33 | github.com/go-playground/validator/v10 v10.6.0/go.mod h1:xm76BBt941f7yWdGnI2DVPFFg1UK3YY04qifoXU3lOk= 34 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 35 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 36 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 37 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 38 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 39 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 40 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 41 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 42 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 43 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 44 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 45 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 46 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 47 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 48 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 49 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 50 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 51 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 52 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 53 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 54 | github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= 55 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 56 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 57 | github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= 58 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 59 | github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= 60 | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 61 | github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= 62 | github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= 63 | github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= 64 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 65 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 66 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 67 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 68 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 69 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 70 | github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= 71 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 72 | github.com/orcaman/concurrent-map v0.0.0-20210501183033-44dafcb38ecc h1:Ak86L+yDSOzKFa7WM5bf5itSOo1e3Xh8bm5YCMUXIjQ= 73 | github.com/orcaman/concurrent-map v0.0.0-20210501183033-44dafcb38ecc/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= 74 | github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= 75 | github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= 76 | github.com/pires/go-proxyproto v0.5.0 h1:A4Jv4ZCaV3AFJeGh5mGwkz4iuWUYMlQ7IoO/GTuSuLo= 77 | github.com/pires/go-proxyproto v0.5.0/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY= 78 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 79 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 80 | github.com/shirou/gopsutil v3.21.3+incompatible h1:uenXGGa8ESCQq+dbgtl916dmg6PSAz2cXov0uORQ9v8= 81 | github.com/shirou/gopsutil v3.21.3+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= 82 | github.com/shirou/gopsutil v3.21.4+incompatible h1:fuHcTm5mX+wzo542cmYcV9RTGQLbnHLI5SyQ5ryTVck= 83 | github.com/shirou/gopsutil v3.21.4+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= 84 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 85 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 86 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 87 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 88 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 89 | github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= 90 | github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= 91 | github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= 92 | github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= 93 | github.com/txthinking/runnergroup v0.0.0-20210326110939-37fc67d0da7c h1:6WIrmZPMl2Q61vozy5MfJNfD6CAgivGFgqvXsrho8GM= 94 | github.com/txthinking/runnergroup v0.0.0-20210326110939-37fc67d0da7c/go.mod h1:CLUSJbazqETbaR+i0YAhXBICV9TrKH93pziccMhmhpM= 95 | github.com/txthinking/socks5 v0.0.0-20210326104807-61b5745ff346 h1:czf2yvkvrQA9CfHK3S0UaE+Sh8jYIgCWlSzYLJjMHJQ= 96 | github.com/txthinking/socks5 v0.0.0-20210326104807-61b5745ff346/go.mod h1:d3n8NJ6QMRb6I/WAlp4z5ZPAoaeqDmX5NgVZA0mhe+I= 97 | github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe h1:gMWxZxBFRAXqoGkwkYlPX2zvyyKNWJpxOxCrjqJkm5A= 98 | github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe/go.mod h1:WgqbSEmUYSjEV3B1qmee/PpP2NYEz4bL9/+mF1ma+s4= 99 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= 100 | github.com/ugorji/go v1.2.5 h1:NozRHfUeEta89taVkyfsDVSy2f7v89Frft4pjnWuGuc= 101 | github.com/ugorji/go v1.2.5/go.mod h1:gat2tIT8KJG8TVI8yv77nEO/KYT6dV7JE1gfUa8Xuls= 102 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= 103 | github.com/ugorji/go/codec v1.2.5 h1:8WobZKAk18Msm2CothY2jnztY56YVY8kF1oQrj21iis= 104 | github.com/ugorji/go/codec v1.2.5/go.mod h1:QPxoTbPKSEAlAHPYt02++xp/en9B/wUdwFCz+hj5caA= 105 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 106 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 107 | golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg= 108 | golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 109 | golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf h1:B2n+Zi5QeYRDAEodEu72OS36gmTWjgpXr2+cWcBW90o= 110 | golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= 111 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 112 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 113 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 114 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 115 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 116 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 117 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 118 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 119 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 120 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= 121 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 122 | golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0= 123 | golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= 124 | golang.org/x/net v0.0.0-20210508051633-16afe75a6701 h1:lQVgcB3+FoAXOb20Dp6zTzAIrpj1k/yOOBN7s+Zv1rA= 125 | golang.org/x/net v0.0.0-20210508051633-16afe75a6701/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 126 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 127 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 128 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 129 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 130 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 131 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 132 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 133 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 134 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 135 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 136 | golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 137 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 138 | golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw= 139 | golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 140 | golang.org/x/sys v0.0.0-20210507161434-a76c4d0a0096 h1:5PbJGn5Sp3GEUjJ61aYbUP6RIo3Z3r2E4Tv9y2z8UHo= 141 | golang.org/x/sys v0.0.0-20210507161434-a76c4d0a0096/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 142 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 143 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 144 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 145 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 146 | golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= 147 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 148 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 149 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 150 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 151 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 152 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 153 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 154 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 155 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 156 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 157 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= 158 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 159 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 160 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 161 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 162 | google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg= 163 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 164 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 165 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 166 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 167 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 168 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 169 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 170 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 171 | google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= 172 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 173 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 174 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 175 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 176 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 177 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 178 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 179 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 180 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 181 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 182 | -------------------------------------------------------------------------------- /lib.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "neko-relay/relay" 6 | . "neko-relay/rules" 7 | "net" 8 | "time" 9 | 10 | cmap "github.com/orcaman/concurrent-map" 11 | ) 12 | 13 | var ( 14 | Rules = cmap.New() 15 | Traffic = cmap.New() 16 | Svrs = cmap.New() 17 | syncing = false 18 | // used = [65536]bool{} 19 | ) 20 | 21 | func getTF(rid string) (tf *relay.TF) { 22 | Tf, has := Traffic.Get(rid) 23 | if has { 24 | tf = Tf.(*relay.TF) 25 | } 26 | if !has { 27 | tf = relay.NewTF() 28 | Traffic.Set(rid, tf) 29 | } 30 | return 31 | } 32 | 33 | func start(rid string, r Rule) (err error) { 34 | // if used[r.Port] { 35 | // s := strconv.Itoa(int(r.Port)) + "has been used" 36 | // err = errors.New(s) 37 | // return 38 | // } 39 | // used[r.Port] = true 40 | svr, err := relay.NewRelay(r, 30, 10, getTF(rid), r.Type) 41 | if err != nil { 42 | return 43 | } 44 | Svrs.Set(rid, svr) 45 | svr.Serve() 46 | time.Sleep(5 * time.Millisecond) 47 | return 48 | } 49 | func stop(rid string, r Rule) { 50 | Svr, has := Svrs.Get(rid) 51 | if has { 52 | Svr.(*relay.Relay).Close() 53 | time.Sleep(10 * time.Millisecond) 54 | Svrs.Remove(rid) 55 | } 56 | // used[r.Rport] = false 57 | } 58 | func cmp(x, y Rule) bool { 59 | return x.Port == y.Port && x.Remote == y.Remote && x.Rport == y.Rport && x.Type == y.Type 60 | } 61 | 62 | func sync(newRules map[string]Rule) { 63 | if syncing { 64 | return 65 | } 66 | syncing = true 67 | if Config.Debug { 68 | fmt.Println(newRules) 69 | } 70 | for item := range Rules.Iter() { 71 | rid := item.Key 72 | rule, has := newRules[rid] 73 | if has && cmp(rule, item.Val.(Rule)) { 74 | delete(newRules, rid) 75 | } else { 76 | stop(rid, rule) 77 | Rules.Remove(rid) 78 | Traffic.Remove(rid) 79 | } 80 | } 81 | for rid, r := range newRules { 82 | if Config.Debug { 83 | fmt.Println(r) 84 | } 85 | rip, err := getIP(r.Remote) 86 | if err != nil { 87 | continue 88 | } 89 | r.RIP = rip 90 | pass, _ := check(r) 91 | if !pass { 92 | continue 93 | } 94 | Rules.Set(rid, r) 95 | start(rid, r) 96 | } 97 | syncing = false 98 | } 99 | 100 | func getIP(host string) (string, error) { 101 | ips, err := net.LookupHost(host) 102 | if err != nil { 103 | return "", err 104 | } 105 | return ips[0], nil 106 | } 107 | 108 | func ddns() { 109 | for { 110 | time.Sleep(time.Second * 60) 111 | for syncing { 112 | time.Sleep(100 * time.Millisecond) 113 | } 114 | for item := range Rules.Iter() { 115 | rid, r := item.Key, item.Val.(Rule) 116 | RIP, err := getIP(r.Remote) 117 | if err == nil && RIP != r.RIP { 118 | r.RIP = RIP 119 | stop(rid, r) 120 | start(rid, r) 121 | } 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /limits/limits.go: -------------------------------------------------------------------------------- 1 | package limits 2 | 3 | import ( 4 | "runtime" 5 | "syscall" 6 | ) 7 | 8 | func Raise() error { 9 | var l syscall.Rlimit 10 | if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &l); err != nil { 11 | return err 12 | } 13 | if runtime.GOOS == "darwin" && l.Cur < 10240 { 14 | l.Cur = 10240 15 | } 16 | if runtime.GOOS != "darwin" && l.Cur < 60000 { 17 | if l.Max < 60000 { 18 | l.Max = 60000 // with CAP_SYS_RESOURCE capability 19 | } 20 | l.Cur = l.Max 21 | } 22 | if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &l); err != nil { 23 | return err 24 | } 25 | return nil 26 | } 27 | -------------------------------------------------------------------------------- /neko-relay.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "flag" 7 | "fmt" 8 | "io/ioutil" 9 | "log" 10 | "neko-relay/config" 11 | "neko-relay/relay" 12 | . "neko-relay/rules" 13 | "neko-relay/stat" 14 | "strconv" 15 | 16 | "github.com/gin-gonic/gin" 17 | "gopkg.in/yaml.v2" 18 | ) 19 | 20 | var Config config.CONF 21 | 22 | func resp(c *gin.Context, success bool, data interface{}, code int) { 23 | c.JSON(code, gin.H{ 24 | "success": success, 25 | "data": data, 26 | }) 27 | } 28 | func check(r Rule) (bool, error) { 29 | if r.Port > 65535 || r.Rport > 65535 { 30 | return false, errors.New("port is not in range") 31 | } 32 | return true, nil 33 | } 34 | func ParseRule(c *gin.Context) (rid string, r Rule, ok bool, err error) { 35 | rid = c.PostForm("rid") 36 | port, _ := strconv.Atoi(c.PostForm("port")) 37 | Port := uint(port) 38 | remote := c.PostForm("remote") 39 | rport, _ := strconv.Atoi(c.PostForm("rport")) 40 | Rport := uint(rport) 41 | typ := c.PostForm("type") 42 | var RIP string 43 | RIP, err = getIP(remote) 44 | if err != nil { 45 | ok = false 46 | return 47 | } 48 | r = Rule{Port: Port, Remote: remote, RIP: RIP, Rport: Rport, Type: typ} 49 | ok, err = check(r) 50 | return 51 | } 52 | func main() { 53 | var confpath string 54 | var show_version bool 55 | Debug := false 56 | flag.StringVar(&confpath, "c", "", "config") 57 | flag.StringVar(&Config.Key, "key", "", "api key") 58 | flag.IntVar(&Config.Port, "port", 8080, "api port") 59 | flag.StringVar(&Config.Certfile, "certfile", "public.pem", "cert file") 60 | flag.StringVar(&Config.Keyfile, "keyfile", "private.key", "key file") 61 | flag.StringVar(&Config.Syncfile, "syncfile", "", "sync file") 62 | flag.BoolVar(&Debug, "debug", false, "enable Config.Debug") 63 | flag.BoolVar(&show_version, "v", false, "show version") 64 | flag.Parse() 65 | if confpath != "" { 66 | data, err := ioutil.ReadFile(confpath) 67 | if err != nil { 68 | log.Panic(err) 69 | } 70 | err = yaml.Unmarshal([]byte(data), &Config) 71 | if err != nil { 72 | panic(err) 73 | } 74 | // fmt.Println(Config) 75 | } 76 | Config.Debug = Debug 77 | if show_version != false { 78 | fmt.Println("neko-relay v1.4.3") 79 | fmt.Println("TCP & UDP & WS TUNNEL & WSS TUNNEL & HTTP & HTTPS & STAT") 80 | return 81 | } 82 | if Config.Debug != true { 83 | gin.SetMode(gin.ReleaseMode) 84 | } 85 | relay.Config = Config 86 | relay.GetCert() 87 | r := gin.New() 88 | datapath := "/data" 89 | if Config.Key != "" { 90 | datapath = "/data/" + Config.Key 91 | } 92 | r.GET(datapath, getData) 93 | if Config.Debug != true && Config.Key != "" { 94 | r.Use(checkKey) 95 | } 96 | r.POST("/traffic", func(c *gin.Context) { 97 | reset, _ := strconv.ParseBool(c.DefaultPostForm("reset", "false")) 98 | y := gin.H{} 99 | for item := range Traffic.Iter() { 100 | rid, tf := item.Key, item.Val.(*relay.TF) 101 | y[rid] = tf.Total() 102 | if reset { 103 | tf.Reset() 104 | } 105 | } 106 | resp(c, true, y, 200) 107 | }) 108 | r.POST("/add", func(c *gin.Context) { 109 | rid, r, ok, err := ParseRule(c) 110 | if ok { 111 | Rules.Set(rid, r) 112 | start(rid, r) 113 | resp(c, true, nil, 200) 114 | } else { 115 | resp(c, false, err.Error(), 500) 116 | return 117 | } 118 | }) 119 | r.POST("/edit", func(c *gin.Context) { 120 | rid, r, ok, err := ParseRule(c) 121 | if ok { 122 | stop(rid, r) 123 | Rules.Set(rid, r) 124 | start(rid, r) 125 | resp(c, true, nil, 200) 126 | } 127 | if err != nil { 128 | resp(c, false, err.Error(), 500) 129 | return 130 | } 131 | 132 | }) 133 | r.POST("/del", func(c *gin.Context) { 134 | rid := c.PostForm("rid") 135 | rule, has := Rules.Get(rid) 136 | if !has { 137 | resp(c, false, gin.H{ 138 | "rule": nil, 139 | "traffic": 0, 140 | }, 200) 141 | return 142 | } 143 | r := rule.(Rule) 144 | traffic, _ := Traffic.Get(rid) 145 | stop(rid, r) 146 | Rules.Remove(rid) 147 | Traffic.Remove(rid) 148 | resp(c, true, gin.H{ 149 | "rule": rule, 150 | "traffic": traffic, 151 | }, 200) 152 | }) 153 | r.POST("/sync", func(c *gin.Context) { 154 | newRules := make(map[string]Rule) 155 | data := []byte(c.PostForm("rules")) 156 | json.Unmarshal(data, &newRules) 157 | if Config.Syncfile != "" { 158 | err := ioutil.WriteFile(Config.Syncfile, data, 0644) 159 | if err != nil { 160 | log.Println(err) 161 | } 162 | } 163 | sync(newRules) 164 | resp(c, true, Rules, 200) 165 | }) 166 | r.GET("/stat", func(c *gin.Context) { 167 | res, err := stat.GetStat() 168 | if err == nil { 169 | resp(c, true, res, 200) 170 | } else { 171 | resp(c, false, err, 500) 172 | } 173 | }) 174 | go Init() 175 | fmt.Println("Api port:", Config.Port) 176 | fmt.Println("Api key:", Config.Key) 177 | r.Run(":" + strconv.Itoa(Config.Port)) 178 | } 179 | func Init() { 180 | if Config.Syncfile != "" { 181 | data, err := ioutil.ReadFile(Config.Syncfile) 182 | if err == nil { 183 | newRules := make(map[string]Rule) 184 | json.Unmarshal(data, &newRules) 185 | sync(newRules) 186 | } else { 187 | log.Println(err) 188 | } 189 | } 190 | go ddns() 191 | } 192 | func checkKey(c *gin.Context) { 193 | if c.Request.Header.Get("key") != Config.Key { 194 | resp(c, false, "Api key Incorrect", 500) 195 | c.Abort() 196 | return 197 | } 198 | c.Next() 199 | } 200 | func getData(c *gin.Context) { 201 | if syncing { 202 | c.JSON(500, "syncing") 203 | } else { 204 | working := Rules.Items() 205 | errs := make(map[string]Rule) 206 | for t := range Svrs.Iter() { 207 | svr := t.Val.(*relay.Relay) 208 | failed := false 209 | if svr.TCPListen == nil && working[t.Key].(Rule).Type != "udp" { 210 | failed = true 211 | } 212 | if failed { 213 | errs[t.Key] = working[t.Key].(Rule) 214 | delete(working, t.Key) 215 | } 216 | } 217 | c.JSON(200, gin.H{ 218 | "errors": errs, 219 | "working": working, 220 | }) 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /relay/UDPDistribute.go: -------------------------------------------------------------------------------- 1 | package relay 2 | 3 | import ( 4 | "errors" 5 | "net" 6 | "time" 7 | ) 8 | 9 | type UDPDistribute struct { 10 | Connected bool 11 | Conn *(net.UDPConn) 12 | Cache chan []byte 13 | RAddr net.Addr 14 | LAddr net.Addr 15 | } 16 | 17 | func NewUDPDistribute(conn *(net.UDPConn), addr net.Addr) *UDPDistribute { 18 | return &UDPDistribute{ 19 | Connected: true, 20 | Conn: conn, 21 | Cache: make(chan []byte, 16), 22 | RAddr: addr, 23 | LAddr: conn.LocalAddr(), 24 | } 25 | } 26 | 27 | func (this *UDPDistribute) Close() error { 28 | this.Connected = false 29 | return this.Conn.Close() 30 | // return nil 31 | } 32 | 33 | func (this *UDPDistribute) Read(b []byte) (n int, err error) { 34 | if !this.Connected { 35 | return 0, errors.New("udp conn has been closed") 36 | } 37 | select { 38 | case <-time.After(16 * time.Second): 39 | return 0, errors.New("i/o read timeout") 40 | case data := <-this.Cache: 41 | n := len(data) 42 | copy(b, data) 43 | return n, nil 44 | } 45 | } 46 | 47 | func (this *UDPDistribute) Write(b []byte) (int, error) { 48 | if !this.Connected { 49 | return 0, errors.New("udp conn has been closed") 50 | } 51 | return this.Conn.WriteTo(b, this.RAddr) 52 | } 53 | 54 | func (this *UDPDistribute) RemoteAddr() net.Addr { 55 | return this.RAddr 56 | } 57 | func (this *UDPDistribute) LocalAddr() net.Addr { 58 | return this.LAddr 59 | } 60 | func (this *UDPDistribute) SetDeadline(t time.Time) error { 61 | return this.Conn.SetDeadline(t) 62 | } 63 | func (this *UDPDistribute) SetReadDeadline(t time.Time) error { 64 | return this.Conn.SetReadDeadline(t) 65 | } 66 | func (this *UDPDistribute) SetWriteDeadline(t time.Time) error { 67 | return this.Conn.SetWriteDeadline(t) 68 | } 69 | -------------------------------------------------------------------------------- /relay/http.go: -------------------------------------------------------------------------------- 1 | package relay 2 | 3 | import ( 4 | "net/http" 5 | "net/url" 6 | ) 7 | 8 | func (s *Relay) RunHttpServer(tls bool) error { 9 | s.ListenTCP() 10 | defer s.TCPListen.Close() 11 | handler := http.NewServeMux() 12 | 13 | target := "http://" + s.Raddr 14 | if tls { 15 | target = "https://" + s.Raddr 16 | } 17 | u, err := url.Parse(target) 18 | if err != nil { 19 | return err 20 | } 21 | handler.Handle("/", NewSingleHostReverseProxy(u, s)) 22 | s.Svr = &http.Server{Handler: handler} 23 | if tls { 24 | s.Svr.ServeTLS(s.TCPListen, Config.Certfile, Config.Keyfile) 25 | } else { 26 | s.Svr.Serve(s.TCPListen) 27 | } 28 | // defer svr.Shutdown(nil) 29 | return nil 30 | } 31 | -------------------------------------------------------------------------------- /relay/relay.go: -------------------------------------------------------------------------------- 1 | package relay 2 | 3 | import ( 4 | "io" 5 | "log" 6 | "neko-relay/config" 7 | "neko-relay/limits" 8 | . "neko-relay/rules" 9 | "net" 10 | "net/http" 11 | "strconv" 12 | "sync" 13 | "time" 14 | ) 15 | 16 | var ( 17 | Config config.CONF 18 | ) 19 | 20 | type Relay struct { 21 | TCPAddr *net.TCPAddr 22 | UDPAddr *net.UDPAddr 23 | TCPListen *net.TCPListener 24 | UDPConn *net.UDPConn 25 | Svr *http.Server 26 | TCPTimeout int 27 | UDPTimeout int 28 | Laddr string 29 | Raddr string 30 | REMOTE string 31 | RIP string 32 | RPORT int 33 | Traffic *TF 34 | Protocol string 35 | Status bool 36 | } 37 | 38 | func NewRelay(r Rule, tcpTimeout, udpTimeout int, traffic *TF, protocol string) (*Relay, error) { 39 | laddr := ":" + strconv.Itoa(int(r.Port)) 40 | raddr := r.RIP + ":" + strconv.Itoa(int(r.Rport)) 41 | taddr, err := net.ResolveTCPAddr("tcp", laddr) 42 | if err != nil { 43 | return nil, err 44 | } 45 | uaddr, err := net.ResolveUDPAddr("udp", laddr) 46 | if err != nil { 47 | return nil, err 48 | } 49 | if err := limits.Raise(); err != nil { 50 | log.Println("Try to raise system limits, got", err) 51 | } 52 | s := &Relay{ 53 | TCPAddr: taddr, 54 | UDPAddr: uaddr, 55 | TCPTimeout: tcpTimeout, 56 | UDPTimeout: udpTimeout, 57 | Laddr: laddr, 58 | Raddr: raddr, 59 | RIP: r.RIP, 60 | REMOTE: r.Remote, 61 | Traffic: traffic, 62 | Protocol: protocol, 63 | Status: true, 64 | } 65 | return s, nil 66 | } 67 | 68 | // Run server. 69 | func (s *Relay) Serve() error { 70 | if s.Protocol == "tcp" || s.Protocol == "tcp+udp" { 71 | go s.RunTCPServer() 72 | } 73 | if s.Protocol == "udp" || s.Protocol == "tcp+udp" { 74 | go s.RunUDPServer() 75 | } 76 | if s.Protocol == "http" { 77 | go s.RunHttpServer(false) 78 | } 79 | if s.Protocol == "https" { 80 | go s.RunHttpServer(true) 81 | } 82 | if s.Protocol == "tls" { 83 | go s.RunTCPServer() 84 | } 85 | if s.Protocol == "ws_tunnel_server" { 86 | go s.RunWsTunnelServer(true, true) 87 | } 88 | if s.Protocol == "ws_tunnel_client" { 89 | go s.RunWsTunnelTcpClient() 90 | go s.RunWsTunnelUdpClient() 91 | } 92 | 93 | if s.Protocol == "wss_tunnel_server" { 94 | go s.RunWssTunnelServer(true, true) 95 | } 96 | if s.Protocol == "wss_tunnel_client" { 97 | go s.RunWssTunnelTcpClient() 98 | go s.RunWssTunnelUdpClient() 99 | } 100 | return nil 101 | } 102 | 103 | // Shutdown server. 104 | func (s *Relay) Close() error { 105 | s.Status = false 106 | if s.Svr != nil { 107 | s.Svr.Shutdown(nil) 108 | } 109 | time.Sleep(10 * time.Millisecond) 110 | if s.TCPListen != nil { 111 | s.TCPListen.Close() 112 | s.TCPListen = nil 113 | } 114 | if s.UDPConn != nil { 115 | s.UDPConn.Close() 116 | s.UDPConn = nil 117 | } 118 | return nil 119 | } 120 | 121 | var ( 122 | Pool = sync.Pool{ 123 | New: func() interface{} { 124 | return make([]byte, 16*1024) 125 | }, 126 | } 127 | ) 128 | 129 | func Copy(dst, src net.Conn, s *Relay) error { 130 | defer src.Close() 131 | defer dst.Close() 132 | return Copy_io(dst, src, s) 133 | } 134 | 135 | func Copy_io(dst io.Writer, src io.Reader, s *Relay) error { 136 | // n, err := io.Copy(dst, src) 137 | // if err != nil { 138 | // return nil 139 | // } 140 | // if tf != nil { 141 | // tf.Add(uint64(n)) 142 | // } 143 | // return nil 144 | buf := Pool.Get().([]byte) 145 | defer Pool.Put(buf) 146 | if n, err := io.CopyBuffer(dst, src, buf); err == nil { 147 | if s.Traffic != nil { 148 | s.Traffic.Add(uint64(n)) 149 | } 150 | return nil 151 | } else { 152 | return err 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /relay/resversproxy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // HTTP reverse proxy handler 6 | 7 | package relay 8 | 9 | import ( 10 | "context" 11 | "crypto/tls" 12 | "fmt" 13 | "io" 14 | "log" 15 | "net" 16 | "net/http" 17 | "net/textproto" 18 | "net/url" 19 | "strings" 20 | "sync" 21 | "time" 22 | 23 | "golang.org/x/net/http/httpguts" 24 | ) 25 | 26 | // ReverseProxy is an HTTP Handler that takes an incoming request and 27 | // sends it to another server, proxying the response back to the 28 | // client. 29 | // 30 | // ReverseProxy by default sets the client IP as the value of the 31 | // X-Forwarded-For header. 32 | // 33 | // If an X-Forwarded-For header already exists, the client IP is 34 | // appended to the existing values. As a special case, if the header 35 | // exists in the Request.Header map but has a nil value (such as when 36 | // set by the Director func), the X-Forwarded-For header is 37 | // not modified. 38 | // 39 | // To prevent IP spoofing, be sure to delete any pre-existing 40 | // X-Forwarded-For header coming from the client or 41 | // an untrusted proxy. 42 | type ReverseProxy struct { 43 | s *Relay 44 | // Director must be a function which modifies 45 | // the request into a new request to be sent 46 | // using Transport. Its response is then copied 47 | // back to the original client unmodified. 48 | // Director must not access the provided Request 49 | // after returning. 50 | Director func(*http.Request) 51 | 52 | // The transport used to perform proxy requests. 53 | // If nil, http.DefaultTransport is used. 54 | Transport http.RoundTripper 55 | 56 | // FlushInterval specifies the flush interval 57 | // to flush to the client while copying the 58 | // response body. 59 | // If zero, no periodic flushing is done. 60 | // A negative value means to flush immediately 61 | // after each write to the client. 62 | // The FlushInterval is ignored when ReverseProxy 63 | // recognizes a response as a streaming response, or 64 | // if its ContentLength is -1; for such responses, writes 65 | // are flushed to the client immediately. 66 | FlushInterval time.Duration 67 | 68 | // ErrorLog specifies an optional logger for errors 69 | // that occur when attempting to proxy the request. 70 | // If nil, logging is done via the log package's standard logger. 71 | ErrorLog *log.Logger 72 | 73 | // BufferPool optionally specifies a buffer pool to 74 | // get byte slices for use by io.CopyBuffer when 75 | // copying HTTP response bodies. 76 | BufferPool BufferPool 77 | 78 | // ModifyResponse is an optional function that modifies the 79 | // Response from the backend. It is called if the backend 80 | // returns a response at all, with any HTTP status code. 81 | // If the backend is unreachable, the optional ErrorHandler is 82 | // called without any call to ModifyResponse. 83 | // 84 | // If ModifyResponse returns an error, ErrorHandler is called 85 | // with its error value. If ErrorHandler is nil, its default 86 | // implementation is used. 87 | ModifyResponse func(*http.Response) error 88 | 89 | // ErrorHandler is an optional function that handles errors 90 | // reaching the backend or errors from ModifyResponse. 91 | // 92 | // If nil, the default is to log the provided error and return 93 | // a 502 Status Bad Gateway response. 94 | ErrorHandler func(http.ResponseWriter, *http.Request, error) 95 | } 96 | 97 | // A BufferPool is an interface for getting and returning temporary 98 | // byte slices for use by io.CopyBuffer. 99 | type BufferPool interface { 100 | Get() []byte 101 | Put([]byte) 102 | } 103 | 104 | func singleJoiningSlash(a, b string) string { 105 | aslash := strings.HasSuffix(a, "/") 106 | bslash := strings.HasPrefix(b, "/") 107 | switch { 108 | case aslash && bslash: 109 | return a + b[1:] 110 | case !aslash && !bslash: 111 | return a + "/" + b 112 | } 113 | return a + b 114 | } 115 | 116 | func joinURLPath(a, b *url.URL) (path, rawpath string) { 117 | if a.RawPath == "" && b.RawPath == "" { 118 | return singleJoiningSlash(a.Path, b.Path), "" 119 | } 120 | // Same as singleJoiningSlash, but uses EscapedPath to determine 121 | // whether a slash should be added 122 | apath := a.EscapedPath() 123 | bpath := b.EscapedPath() 124 | 125 | aslash := strings.HasSuffix(apath, "/") 126 | bslash := strings.HasPrefix(bpath, "/") 127 | 128 | switch { 129 | case aslash && bslash: 130 | return a.Path + b.Path[1:], apath + bpath[1:] 131 | case !aslash && !bslash: 132 | return a.Path + "/" + b.Path, apath + "/" + bpath 133 | } 134 | return a.Path + b.Path, apath + bpath 135 | } 136 | 137 | // NewSingleHostReverseProxy returns a new ReverseProxy that routes 138 | // URLs to the scheme, host, and base path provided in target. If the 139 | // target's path is "/base" and the incoming request was for "/dir", 140 | // the target request will be for /base/dir. 141 | // NewSingleHostReverseProxy does not rewrite the Host header. 142 | // To rewrite Host headers, use ReverseProxy directly with a custom 143 | // Director policy. 144 | func NewSingleHostReverseProxy(target *url.URL, s *Relay) *ReverseProxy { 145 | targetQuery := target.RawQuery 146 | director := func(req *http.Request) { 147 | req.URL.Scheme = target.Scheme 148 | req.URL.Host = target.Host 149 | req.URL.Path, req.URL.RawPath = joinURLPath(target, req.URL) 150 | if targetQuery == "" || req.URL.RawQuery == "" { 151 | req.URL.RawQuery = targetQuery + req.URL.RawQuery 152 | } else { 153 | req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery 154 | } 155 | if _, ok := req.Header["User-Agent"]; !ok { 156 | // explicitly disable User-Agent so it's not set to default value 157 | req.Header.Set("User-Agent", "") 158 | } 159 | } 160 | return &ReverseProxy{ 161 | s: s, 162 | Director: director, 163 | Transport: &http.Transport{ 164 | Proxy: http.ProxyFromEnvironment, 165 | Dial: (&net.Dialer{ 166 | Timeout: 30 * time.Second, 167 | }).Dial, 168 | TLSHandshakeTimeout: 10 * time.Second, 169 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 170 | }, 171 | } 172 | } 173 | 174 | func copyHeader(dst, src http.Header) { 175 | // fmt.Println("copy header") 176 | for k, vv := range src { 177 | for _, v := range vv { 178 | dst.Add(k, v) 179 | } 180 | } 181 | } 182 | 183 | // Hop-by-hop headers. These are removed when sent to the backend. 184 | // As of RFC 7230, hop-by-hop headers are required to appear in the 185 | // Connection header field. These are the headers defined by the 186 | // obsoleted RFC 2616 (section 13.5.1) and are used for backward 187 | // compatibility. 188 | var hopHeaders = []string{ 189 | "Connection", 190 | "Proxy-Connection", // non-standard but still sent by libcurl and rejected by e.g. google 191 | "Keep-Alive", 192 | "Proxy-Authenticate", 193 | "Proxy-Authorization", 194 | "Te", // canonicalized version of "TE" 195 | "Trailer", // not Trailers per URL above; https://www.rfc-editor.org/errata_search.php?eid=4522 196 | "Transfer-Encoding", 197 | "Upgrade", 198 | } 199 | 200 | func (p *ReverseProxy) defaultErrorHandler(rw http.ResponseWriter, req *http.Request, err error) { 201 | p.logf("http: proxy error: %v", err) 202 | rw.WriteHeader(http.StatusBadGateway) 203 | } 204 | 205 | func (p *ReverseProxy) getErrorHandler() func(http.ResponseWriter, *http.Request, error) { 206 | if p.ErrorHandler != nil { 207 | return p.ErrorHandler 208 | } 209 | return p.defaultErrorHandler 210 | } 211 | 212 | // modifyResponse conditionally runs the optional ModifyResponse hook 213 | // and reports whether the request should proceed. 214 | func (p *ReverseProxy) modifyResponse(rw http.ResponseWriter, res *http.Response, req *http.Request) bool { 215 | if p.ModifyResponse == nil { 216 | return true 217 | } 218 | if err := p.ModifyResponse(res); err != nil { 219 | res.Body.Close() 220 | p.getErrorHandler()(rw, req, err) 221 | return false 222 | } 223 | return true 224 | } 225 | 226 | func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { 227 | req.Host = p.s.REMOTE 228 | // fmt.Println("change host") 229 | transport := p.Transport 230 | if transport == nil { 231 | transport = http.DefaultTransport 232 | } 233 | 234 | ctx := req.Context() 235 | if cn, ok := rw.(http.CloseNotifier); ok { 236 | var cancel context.CancelFunc 237 | ctx, cancel = context.WithCancel(ctx) 238 | defer cancel() 239 | notifyChan := cn.CloseNotify() 240 | go func() { 241 | select { 242 | case <-notifyChan: 243 | cancel() 244 | case <-ctx.Done(): 245 | } 246 | }() 247 | } 248 | 249 | outreq := req.Clone(ctx) 250 | if req.ContentLength == 0 { 251 | outreq.Body = nil // Issue 16036: nil Body for http.Transport retries 252 | } 253 | if outreq.Header == nil { 254 | outreq.Header = make(http.Header) // Issue 33142: historical behavior was to always allocate 255 | } 256 | 257 | p.Director(outreq) 258 | outreq.Close = false 259 | 260 | reqUpType := upgradeType(outreq.Header) 261 | removeConnectionHeaders(outreq.Header) 262 | 263 | // Remove hop-by-hop headers to the backend. Especially 264 | // important is "Connection" because we want a persistent 265 | // connection, regardless of what the client sent to us. 266 | for _, h := range hopHeaders { 267 | hv := outreq.Header.Get(h) 268 | if hv == "" { 269 | continue 270 | } 271 | if h == "Te" && hv == "trailers" { 272 | // Issue 21096: tell backend applications that 273 | // care about trailer support that we support 274 | // trailers. (We do, but we don't go out of 275 | // our way to advertise that unless the 276 | // incoming client request thought it was 277 | // worth mentioning) 278 | continue 279 | } 280 | outreq.Header.Del(h) 281 | } 282 | 283 | // After stripping all the hop-by-hop connection headers above, add back any 284 | // necessary for protocol upgrades, such as for websockets. 285 | if reqUpType != "" { 286 | outreq.Header.Set("Connection", "Upgrade") 287 | outreq.Header.Set("Upgrade", reqUpType) 288 | } 289 | 290 | if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil { 291 | // If we aren't the first proxy retain prior 292 | // X-Forwarded-For information as a comma+space 293 | // separated list and fold multiple headers into one. 294 | prior, ok := outreq.Header["X-Forwarded-For"] 295 | omit := ok && prior == nil // Issue 38079: nil now means don't populate the header 296 | if len(prior) > 0 { 297 | clientIP = strings.Join(prior, ", ") + ", " + clientIP 298 | } 299 | if !omit { 300 | outreq.Header.Set("X-Forwarded-For", clientIP) 301 | } 302 | } 303 | 304 | res, err := transport.RoundTrip(outreq) 305 | if err != nil { 306 | p.getErrorHandler()(rw, outreq, err) 307 | return 308 | } 309 | 310 | // Deal with 101 Switching Protocols responses: (WebSocket, h2c, etc) 311 | if res.StatusCode == http.StatusSwitchingProtocols { 312 | if !p.modifyResponse(rw, res, outreq) { 313 | return 314 | } 315 | p.handleUpgradeResponse(rw, outreq, res) 316 | return 317 | } 318 | 319 | removeConnectionHeaders(res.Header) 320 | 321 | for _, h := range hopHeaders { 322 | res.Header.Del(h) 323 | } 324 | 325 | if !p.modifyResponse(rw, res, outreq) { 326 | return 327 | } 328 | 329 | // fmt.Println("copy header") 330 | copyHeader(rw.Header(), res.Header) 331 | 332 | // The "Trailer" header isn't included in the Transport's response, 333 | // at least for *http.Transport. Build it up from Trailer. 334 | announcedTrailers := len(res.Trailer) 335 | if announcedTrailers > 0 { 336 | trailerKeys := make([]string, 0, len(res.Trailer)) 337 | for k := range res.Trailer { 338 | trailerKeys = append(trailerKeys, k) 339 | } 340 | rw.Header().Add("Trailer", strings.Join(trailerKeys, ", ")) 341 | } 342 | 343 | rw.WriteHeader(res.StatusCode) 344 | 345 | err = p.copyResponse(rw, res.Body, p.flushInterval(res)) 346 | if err != nil { 347 | defer res.Body.Close() 348 | // Since we're streaming the response, if we run into an error all we can do 349 | // is abort the request. Issue 23643: ReverseProxy should use ErrAbortHandler 350 | // on read error while copying body. 351 | if !shouldPanicOnCopyError(req) { 352 | p.logf("suppressing panic for copyResponse error in test; copy error: %v", err) 353 | return 354 | } 355 | panic(http.ErrAbortHandler) 356 | } 357 | res.Body.Close() // close now, instead of defer, to populate res.Trailer 358 | 359 | if len(res.Trailer) > 0 { 360 | // Force chunking if we saw a response trailer. 361 | // This prevents net/http from calculating the length for short 362 | // bodies and adding a Content-Length. 363 | if fl, ok := rw.(http.Flusher); ok { 364 | fl.Flush() 365 | } 366 | } 367 | 368 | if len(res.Trailer) == announcedTrailers { 369 | copyHeader(rw.Header(), res.Trailer) 370 | return 371 | } 372 | 373 | for k, vv := range res.Trailer { 374 | k = http.TrailerPrefix + k 375 | for _, v := range vv { 376 | rw.Header().Add(k, v) 377 | } 378 | } 379 | } 380 | 381 | var inOurTests bool // whether we're in our own tests 382 | 383 | // shouldPanicOnCopyError reports whether the reverse proxy should 384 | // panic with http.ErrAbortHandler. This is the right thing to do by 385 | // default, but Go 1.10 and earlier did not, so existing unit tests 386 | // weren't expecting panics. Only panic in our own tests, or when 387 | // running under the HTTP server. 388 | func shouldPanicOnCopyError(req *http.Request) bool { 389 | if inOurTests { 390 | // Our tests know to handle this panic. 391 | return true 392 | } 393 | if req.Context().Value(http.ServerContextKey) != nil { 394 | // We seem to be running under an HTTP server, so 395 | // it'll recover the panic. 396 | return true 397 | } 398 | // Otherwise act like Go 1.10 and earlier to not break 399 | // existing tests. 400 | return false 401 | } 402 | 403 | // removeConnectionHeaders removes hop-by-hop headers listed in the "Connection" header of h. 404 | // See RFC 7230, section 6.1 405 | func removeConnectionHeaders(h http.Header) { 406 | for _, f := range h["Connection"] { 407 | for _, sf := range strings.Split(f, ",") { 408 | if sf = textproto.TrimString(sf); sf != "" { 409 | h.Del(sf) 410 | } 411 | } 412 | } 413 | } 414 | 415 | // flushInterval returns the p.FlushInterval value, conditionally 416 | // overriding its value for a specific request/response. 417 | func (p *ReverseProxy) flushInterval(res *http.Response) time.Duration { 418 | resCT := res.Header.Get("Content-Type") 419 | 420 | // For Server-Sent Events responses, flush immediately. 421 | // The MIME type is defined in https://www.w3.org/TR/eventsource/#text-event-stream 422 | if resCT == "text/event-stream" { 423 | return -1 // negative means immediately 424 | } 425 | 426 | // We might have the case of streaming for which Content-Length might be unset. 427 | if res.ContentLength == -1 { 428 | return -1 429 | } 430 | 431 | return p.FlushInterval 432 | } 433 | 434 | func (p *ReverseProxy) copyResponse(dst io.Writer, src io.Reader, flushInterval time.Duration) error { 435 | if flushInterval != 0 { 436 | if wf, ok := dst.(writeFlusher); ok { 437 | mlw := &maxLatencyWriter{ 438 | dst: wf, 439 | latency: flushInterval, 440 | } 441 | defer mlw.stop() 442 | 443 | // set up initial timer so headers get flushed even if body writes are delayed 444 | mlw.flushPending = true 445 | mlw.t = time.AfterFunc(flushInterval, mlw.delayedFlush) 446 | 447 | dst = mlw 448 | } 449 | } 450 | 451 | return Copy_io(dst, src, p.s) 452 | // var buf []byte 453 | // if p.BufferPool != nil { 454 | // buf = p.BufferPool.Get() 455 | // defer p.BufferPool.Put(buf) 456 | // } 457 | // fmt.Println("copy response") 458 | // _, err := p.copyBuffer(dst, src, buf) 459 | // return err 460 | } 461 | 462 | func (p *ReverseProxy) logf(format string, args ...interface{}) { 463 | if p.ErrorLog != nil { 464 | p.ErrorLog.Printf(format, args...) 465 | } else { 466 | log.Printf(format, args...) 467 | } 468 | } 469 | 470 | type writeFlusher interface { 471 | io.Writer 472 | http.Flusher 473 | } 474 | 475 | type maxLatencyWriter struct { 476 | dst writeFlusher 477 | latency time.Duration // non-zero; negative means to flush immediately 478 | 479 | mu sync.Mutex // protects t, flushPending, and dst.Flush 480 | t *time.Timer 481 | flushPending bool 482 | } 483 | 484 | func (m *maxLatencyWriter) Write(p []byte) (n int, err error) { 485 | m.mu.Lock() 486 | defer m.mu.Unlock() 487 | n, err = m.dst.Write(p) 488 | if m.latency < 0 { 489 | m.dst.Flush() 490 | return 491 | } 492 | if m.flushPending { 493 | return 494 | } 495 | if m.t == nil { 496 | m.t = time.AfterFunc(m.latency, m.delayedFlush) 497 | } else { 498 | m.t.Reset(m.latency) 499 | } 500 | m.flushPending = true 501 | return 502 | } 503 | 504 | func (m *maxLatencyWriter) delayedFlush() { 505 | m.mu.Lock() 506 | defer m.mu.Unlock() 507 | if !m.flushPending { // if stop was called but AfterFunc already started this goroutine 508 | return 509 | } 510 | m.dst.Flush() 511 | m.flushPending = false 512 | } 513 | 514 | func (m *maxLatencyWriter) stop() { 515 | m.mu.Lock() 516 | defer m.mu.Unlock() 517 | m.flushPending = false 518 | if m.t != nil { 519 | m.t.Stop() 520 | } 521 | } 522 | 523 | func upgradeType(h http.Header) string { 524 | if !httpguts.HeaderValuesContainsToken(h["Connection"], "Upgrade") { 525 | return "" 526 | } 527 | return strings.ToLower(h.Get("Upgrade")) 528 | } 529 | 530 | func (p *ReverseProxy) handleUpgradeResponse(rw http.ResponseWriter, req *http.Request, res *http.Response) { 531 | reqUpType := upgradeType(req.Header) 532 | resUpType := upgradeType(res.Header) 533 | if reqUpType != resUpType { 534 | p.getErrorHandler()(rw, req, fmt.Errorf("backend tried to switch protocol %q when %q was requested", resUpType, reqUpType)) 535 | return 536 | } 537 | 538 | hj, ok := rw.(http.Hijacker) 539 | if !ok { 540 | p.getErrorHandler()(rw, req, fmt.Errorf("can't switch protocols using non-Hijacker ResponseWriter type %T", rw)) 541 | return 542 | } 543 | backConn, ok := res.Body.(io.ReadWriteCloser) 544 | if !ok { 545 | p.getErrorHandler()(rw, req, fmt.Errorf("internal error: 101 switching protocols response with non-writable body")) 546 | return 547 | } 548 | 549 | backConnCloseCh := make(chan bool) 550 | go func() { 551 | // Ensure that the cancelation of a request closes the backend. 552 | // See issue https://golang.org/issue/35559. 553 | select { 554 | case <-req.Context().Done(): 555 | case <-backConnCloseCh: 556 | } 557 | backConn.Close() 558 | }() 559 | 560 | defer close(backConnCloseCh) 561 | 562 | conn, brw, err := hj.Hijack() 563 | if err != nil { 564 | p.getErrorHandler()(rw, req, fmt.Errorf("Hijack failed on protocol switch: %v", err)) 565 | return 566 | } 567 | defer conn.Close() 568 | 569 | copyHeader(rw.Header(), res.Header) 570 | 571 | res.Header = rw.Header() 572 | res.Body = nil // so res.Write only writes the headers; we have res.Body in backConn above 573 | // fmt.Println("write response") 574 | if err := res.Write(brw); err != nil { 575 | p.getErrorHandler()(rw, req, fmt.Errorf("response write: %v", err)) 576 | return 577 | } 578 | if err := brw.Flush(); err != nil { 579 | p.getErrorHandler()(rw, req, fmt.Errorf("response flush: %v", err)) 580 | return 581 | } 582 | go Copy_io(conn, backConn, p.s) 583 | Copy_io(backConn, conn, p.s) 584 | // errc := make(chan error, 1) 585 | // spc := switchProtocolCopier{user: conn, backend: backConn} 586 | // go spc.copyToBackend(errc) 587 | // go spc.copyFromBackend(errc) 588 | // <-errc 589 | return 590 | } 591 | 592 | // switchProtocolCopier exists so goroutines proxying data back and 593 | // forth have nice names in stacks. 594 | type switchProtocolCopier struct { 595 | user, backend io.ReadWriter 596 | } 597 | 598 | func (c switchProtocolCopier) copyFromBackend(errc chan<- error) { 599 | _, err := io.Copy(c.user, c.backend) 600 | errc <- err 601 | } 602 | 603 | func (c switchProtocolCopier) copyToBackend(errc chan<- error) { 604 | _, err := io.Copy(c.backend, c.user) 605 | errc <- err 606 | } 607 | -------------------------------------------------------------------------------- /relay/reverse_proxy.go: -------------------------------------------------------------------------------- 1 | package relay 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httputil" 6 | "net/url" 7 | ) 8 | 9 | type RP struct { 10 | RProxy *httputil.ReverseProxy 11 | host string 12 | } 13 | 14 | func (f *RP) ServeHTTP(wr http.ResponseWriter, req *http.Request) { 15 | if f.host != "" { 16 | req.Host = f.host 17 | } 18 | f.RProxy.ServeHTTP(wr, req) 19 | } 20 | func NewRP(target, host string) *RP { 21 | u, _ := url.Parse(target) 22 | return &RP{ 23 | RProxy: httputil.NewSingleHostReverseProxy(u), 24 | host: host, 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /relay/tcp.go: -------------------------------------------------------------------------------- 1 | package relay 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "time" 7 | ) 8 | 9 | func (s *Relay) ListenTCP() (err error) { 10 | wait := 2.0 11 | for s.Status && s.TCPListen == nil { 12 | s.TCPListen, err = net.ListenTCP("tcp", s.TCPAddr) 13 | if err != nil { 14 | fmt.Println("Listen TCP", s.Laddr, err, "(retry in", wait, "s)") 15 | time.Sleep(time.Duration(wait) * time.Second) 16 | wait *= 1.1 17 | } 18 | } 19 | return 20 | } 21 | 22 | func (s *Relay) AcceptAndHandleTCP(handle func(c *net.TCPConn) error) error { 23 | wait := 1.0 24 | for s.Status && s.TCPListen != nil { 25 | c, err := s.TCPListen.AcceptTCP() 26 | if err == nil { 27 | go handle(c) 28 | wait = 1.0 29 | } else { 30 | fmt.Println("Accept", s.Laddr, err) 31 | if err, ok := err.(net.Error); ok && err.Temporary() { 32 | continue 33 | } 34 | time.Sleep(time.Duration(wait) * time.Second) 35 | wait *= 1.1 36 | } 37 | } 38 | return nil 39 | } 40 | 41 | func (s *Relay) RunTCPServer() error { 42 | s.ListenTCP() 43 | defer s.TCPListen.Close() 44 | s.AcceptAndHandleTCP(s.TCPHandle) 45 | return nil 46 | } 47 | 48 | func (s *Relay) TCPHandle(c *net.TCPConn) error { 49 | defer c.Close() 50 | rc, err := net.DialTimeout("tcp", s.Raddr, time.Duration(s.TCPTimeout)*time.Second) 51 | if err != nil { 52 | fmt.Println("Dial TCP", s.Laddr, "<=>", s.Raddr, err) 53 | return err 54 | } 55 | defer rc.Close() 56 | go Copy(c, rc, s) 57 | Copy(rc, c, s) 58 | 59 | return nil 60 | } 61 | -------------------------------------------------------------------------------- /relay/tls.go: -------------------------------------------------------------------------------- 1 | package relay 2 | 3 | import ( 4 | "crypto/rand" 5 | "crypto/rsa" 6 | "crypto/x509" 7 | "crypto/x509/pkix" 8 | "encoding/pem" 9 | "fmt" 10 | "io" 11 | "io/ioutil" 12 | "math/big" 13 | "net" 14 | "net/http" 15 | "os" 16 | "strings" 17 | "time" 18 | ) 19 | 20 | func GetCert() { 21 | _, err := os.Stat(Config.Certfile) 22 | if err != nil { 23 | fmt.Println(Config.Certfile, "Not exit, generating ...") 24 | CreateTLSFile(Config.Certfile, Config.Keyfile) 25 | } 26 | 27 | _, err = os.Stat(Config.Keyfile) 28 | if err != nil { 29 | fmt.Println(Config.Keyfile, "Not exit, generating ...") 30 | CreateTLSFile(Config.Certfile, Config.Keyfile) 31 | } 32 | } 33 | 34 | func (s *Relay) RunTlsServer() error { 35 | return nil 36 | } 37 | 38 | func Tls_Handle(s *Relay) error { 39 | return nil 40 | } 41 | 42 | func sendRequest(url string, body io.Reader, addHeaders map[string]string, method string) (statuscode int, resp []byte, err error) { 43 | req, err := http.NewRequest(method, url, body) 44 | if err != nil { 45 | return 46 | } 47 | 48 | req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36") 49 | 50 | if len(addHeaders) > 0 { 51 | for k, v := range addHeaders { 52 | req.Header.Add(k, v) 53 | } 54 | } 55 | 56 | client := &http.Client{} 57 | response, err := client.Do(req) 58 | if err != nil { 59 | return 60 | } 61 | defer response.Body.Close() 62 | 63 | statuscode = response.StatusCode 64 | resp, err = ioutil.ReadAll(response.Body) 65 | return 66 | } 67 | 68 | func CreateTLSFile(certFile, keyFile string) { 69 | var ip string 70 | os.Remove(certFile) 71 | os.Remove(keyFile) 72 | max := new(big.Int).Lsh(big.NewInt(1), 128) 73 | serialNumber, _ := rand.Int(rand.Reader, max) 74 | subject := pkix.Name{ 75 | Country: []string{"US"}, 76 | Province: []string{"WDC"}, 77 | Organization: []string{"Microsoft Corporation"}, 78 | OrganizationalUnit: []string{"Microsoft Corporation"}, 79 | CommonName: "www.microstft.com", 80 | } 81 | 82 | _, resp, err := sendRequest("https://api.ip.sb/ip", nil, nil, "GET") 83 | if err == nil { 84 | ip = string(resp) 85 | ip = strings.Replace(ip, "\n", "", -1) 86 | } else { 87 | ip = "127.0.0.1" 88 | } 89 | 90 | template := x509.Certificate{ 91 | SerialNumber: serialNumber, 92 | Subject: subject, 93 | NotBefore: time.Now(), 94 | NotAfter: time.Now().Add(365 * 24 * time.Hour), 95 | KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, 96 | ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, 97 | IPAddresses: []net.IP{net.ParseIP(ip)}, 98 | } 99 | 100 | pk, _ := rsa.GenerateKey(rand.Reader, 2048) 101 | 102 | derBytes, _ := x509.CreateCertificate(rand.Reader, &template, &template, &pk.PublicKey, pk) 103 | certOut, _ := os.Create(certFile) 104 | pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) 105 | certOut.Close() 106 | 107 | keyOut, _ := os.Create(keyFile) 108 | pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(pk)}) 109 | keyOut.Close() 110 | } 111 | -------------------------------------------------------------------------------- /relay/traffic.go: -------------------------------------------------------------------------------- 1 | package relay 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | type TF struct { 8 | Counter uint64 9 | RW *sync.RWMutex 10 | } 11 | 12 | func NewTF() *TF { 13 | return &TF{Counter: 0, RW: new(sync.RWMutex)} 14 | } 15 | func (tf *TF) Add(val uint64) { 16 | tf.RW.Lock() 17 | tf.Counter += val 18 | tf.RW.Unlock() 19 | } 20 | func (tf *TF) Total() uint64 { 21 | tf.RW.RLock() 22 | defer tf.RW.RUnlock() 23 | return tf.Counter 24 | } 25 | func (tf *TF) Reset() { 26 | tf.RW.Lock() 27 | tf.Counter = 0 28 | tf.RW.Unlock() 29 | } 30 | -------------------------------------------------------------------------------- /relay/udp.go: -------------------------------------------------------------------------------- 1 | package relay 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "time" 7 | ) 8 | 9 | func (s *Relay) ListenUDP() (err error) { 10 | wait := 1.0 11 | for s.Status && s.UDPConn == nil { 12 | s.UDPConn, err = net.ListenUDP("udp", s.UDPAddr) 13 | if err != nil { 14 | fmt.Println("Listen UDP", s.Laddr, err, "(retry in", wait, "s)") 15 | time.Sleep(time.Duration(wait) * time.Second) 16 | wait *= 1.1 17 | } 18 | } 19 | return 20 | } 21 | func (s *Relay) AcceptAndHandleUDP(handle func(c net.Conn) error) error { 22 | wait := 1.0 23 | table := make(map[string]*UDPDistribute) 24 | buf := make([]byte, 1024*16) 25 | for s.Status && s.UDPConn != nil { 26 | n, addr, err := s.UDPConn.ReadFrom(buf) 27 | if err != nil { 28 | fmt.Println("Accept", s.Laddr, err) 29 | if err, ok := err.(net.Error); ok && err.Temporary() { 30 | continue 31 | } 32 | time.Sleep(time.Duration(wait) * time.Second) 33 | wait *= 1.1 34 | break 35 | } else { 36 | wait = 1.0 37 | } 38 | go func() { 39 | buf = buf[:n] 40 | if d, ok := table[addr.String()]; ok { 41 | if d.Connected { 42 | d.Cache <- buf 43 | return 44 | } else { 45 | delete(table, addr.String()) 46 | } 47 | } 48 | c := NewUDPDistribute(s.UDPConn, addr) 49 | table[addr.String()] = c 50 | c.Cache <- buf 51 | handle(c) 52 | }() 53 | } 54 | return nil 55 | } 56 | func (s *Relay) RunUDPServer() error { 57 | s.ListenUDP() 58 | defer s.UDPConn.Close() 59 | s.AcceptAndHandleUDP(s.UDPHandle) 60 | return nil 61 | } 62 | 63 | func (s *Relay) UDPHandle(c net.Conn) error { 64 | defer c.Close() 65 | rc, err := net.DialTimeout("udp", s.Raddr, time.Duration(s.UDPTimeout)*time.Second) 66 | if err != nil { 67 | fmt.Println("Dial UDP", s.Laddr, "<=>", s.Raddr, err) 68 | return err 69 | } 70 | defer rc.Close() 71 | go Copy(c, rc, s) 72 | Copy(rc, c, s) 73 | return nil 74 | } 75 | -------------------------------------------------------------------------------- /relay/ws_tunnel_client.go: -------------------------------------------------------------------------------- 1 | package relay 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | 7 | "golang.org/x/net/websocket" 8 | ) 9 | 10 | func (s *Relay) RunWsTunnelTcpClient() error { 11 | s.ListenTCP() 12 | defer s.TCPListen.Close() 13 | s.AcceptAndHandleTCP(s.WsTunnelClientTcpHandle) 14 | return nil 15 | } 16 | 17 | func (s *Relay) WsTunnelClientTcpHandle(c *net.TCPConn) error { 18 | defer c.Close() 19 | ws_config, err := websocket.NewConfig("ws://"+s.Raddr+"/wstcp/", "http://"+s.Raddr+"/wstcp/") 20 | if err != nil { 21 | fmt.Println("WS Config", s.Raddr, err) 22 | return err 23 | } 24 | ws_config.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36") 25 | ws_config.Header.Set("X-Forward-For", s.RIP) 26 | ws_config.Header.Set("X-Forward-Host", Config.Fakehost) 27 | ws_config.Header.Set("X-Forward-Protocol", c.RemoteAddr().Network()) 28 | ws_config.Header.Set("X-Forward-Address", c.RemoteAddr().String()) 29 | 30 | rc, err := websocket.DialConfig(ws_config) 31 | if err != nil { 32 | fmt.Println("Dial WS", s.Raddr, err) 33 | return err 34 | } 35 | defer rc.Close() 36 | rc.PayloadType = websocket.BinaryFrame 37 | 38 | go Copy(rc, c, s) 39 | Copy(c, rc, s) 40 | return nil 41 | } 42 | 43 | func (s *Relay) RunWsTunnelUdpClient() error { 44 | s.ListenUDP() 45 | defer s.UDPConn.Close() 46 | s.AcceptAndHandleUDP(s.WsTunnelClientUdpHandle) 47 | return nil 48 | } 49 | 50 | func (s *Relay) WsTunnelClientUdpHandle(c net.Conn) error { 51 | defer c.Close() 52 | ws_config, err := websocket.NewConfig("ws://"+s.Raddr+"/wsudp/", "http://"+s.Raddr+"/wsudp/") 53 | if err != nil { 54 | fmt.Println("WS Config", s.Raddr, err) 55 | return err 56 | } 57 | ws_config.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4240.198 Safari/537.36") 58 | ws_config.Header.Set("X-Forward-For", s.RIP) 59 | ws_config.Header.Set("X-Forward-Host", Config.Fakehost) 60 | ws_config.Header.Set("X-Forward-Protocol", c.RemoteAddr().Network()) 61 | ws_config.Header.Set("X-Forward-Address", c.RemoteAddr().String()) 62 | 63 | rc, err := websocket.DialConfig(ws_config) 64 | if err != nil { 65 | fmt.Println("Dial WS", s.Raddr, err) 66 | return err 67 | } 68 | defer rc.Close() 69 | rc.PayloadType = websocket.BinaryFrame 70 | 71 | go Copy(c, rc, s) 72 | Copy(rc, c, s) 73 | return nil 74 | } 75 | -------------------------------------------------------------------------------- /relay/ws_tunnel_server.go: -------------------------------------------------------------------------------- 1 | package relay 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "net/http" 7 | "time" 8 | 9 | "golang.org/x/net/websocket" 10 | ) 11 | 12 | func (s *Relay) RunWsTunnelServer(tcp, udp bool) error { 13 | s.ListenTCP() 14 | defer s.TCPListen.Close() 15 | handler := http.NewServeMux() 16 | if tcp { 17 | handler.Handle("/wstcp/", websocket.Handler(s.WsTunnelServerTcpHandle)) 18 | } 19 | if udp { 20 | handler.Handle("/wsudp/", websocket.Handler(s.WsTunnelServerUdpHandle)) 21 | } 22 | handler.Handle("/", NewRP(Config.Fakeurl, Config.Fakehost)) 23 | 24 | s.Svr = &http.Server{Handler: handler} 25 | s.Svr.Serve(s.TCPListen) 26 | defer s.Svr.Shutdown(nil) 27 | return nil 28 | } 29 | 30 | func (s *Relay) WsTunnelServerTcpHandle(ws *websocket.Conn) { 31 | ws.PayloadType = websocket.BinaryFrame 32 | defer ws.Close() 33 | 34 | rc, err := net.DialTimeout("tcp", s.Raddr, time.Duration(s.TCPTimeout)*time.Second) 35 | if err != nil { 36 | fmt.Println("Dial TCP", s.Laddr, "<=>", s.Raddr, err) 37 | return 38 | } 39 | defer rc.Close() 40 | go Copy(rc, ws, s) 41 | Copy(ws, rc, s) 42 | return 43 | } 44 | 45 | func (s *Relay) WsTunnelServerUdpHandle(ws *websocket.Conn) { 46 | ws.PayloadType = websocket.BinaryFrame 47 | defer ws.Close() 48 | 49 | rc, err := net.DialTimeout("udp", s.Raddr, time.Duration(s.UDPTimeout)*time.Second) 50 | if err != nil { 51 | fmt.Println("Dial UDP", s.Laddr, "<=>", s.Raddr, err) 52 | return 53 | } 54 | defer rc.Close() 55 | go Copy(rc, ws, s) 56 | Copy(ws, rc, s) 57 | return 58 | } 59 | -------------------------------------------------------------------------------- /relay/wss_tunnel_client.go: -------------------------------------------------------------------------------- 1 | package relay 2 | 3 | import ( 4 | "crypto/tls" 5 | "fmt" 6 | "net" 7 | 8 | "golang.org/x/net/websocket" 9 | ) 10 | 11 | func (s *Relay) RunWssTunnelTcpClient() error { 12 | s.ListenTCP() 13 | defer s.TCPListen.Close() 14 | s.AcceptAndHandleTCP(s.WssTunnelClientTcpHandle) 15 | return nil 16 | } 17 | 18 | func (s *Relay) WssTunnelClientTcpHandle(c *net.TCPConn) error { 19 | ws_config, err := websocket.NewConfig("wss://"+s.Raddr+"/wstcp/", "https://"+s.Raddr+"/wstcp/") 20 | if err != nil { 21 | return err 22 | } 23 | ws_config.TlsConfig = &tls.Config{InsecureSkipVerify: true} 24 | ws_config.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4240.198 Safari/537.36") 25 | ws_config.Header.Set("X-Forward-For", s.RIP) 26 | ws_config.Header.Set("X-Forward-Host", Config.Fakehost) 27 | ws_config.Header.Set("X-Forward-Protocol", c.RemoteAddr().Network()) 28 | ws_config.Header.Set("X-Forward-Address", c.RemoteAddr().String()) 29 | 30 | rc, err := websocket.DialConfig(ws_config) 31 | if err != nil { 32 | fmt.Println("Dial ws", s.Raddr, err) 33 | return err 34 | } 35 | rc.PayloadType = websocket.BinaryFrame 36 | defer rc.Close() 37 | 38 | go Copy(rc, c, s) 39 | Copy(c, rc, s) 40 | return nil 41 | } 42 | 43 | func (s *Relay) RunWssTunnelUdpClient() error { 44 | s.ListenUDP() 45 | defer s.UDPConn.Close() 46 | s.AcceptAndHandleUDP(s.WssTunnelClientUdpHandle) 47 | return nil 48 | } 49 | 50 | func (s *Relay) WssTunnelClientUdpHandle(c net.Conn) error { 51 | ws_config, err := websocket.NewConfig("wss://"+s.Raddr+"/wsudp/", "https://"+s.Raddr+"/wsudp/") 52 | if err != nil { 53 | return err 54 | } 55 | ws_config.TlsConfig = &tls.Config{InsecureSkipVerify: true} 56 | ws_config.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4240.198 Safari/537.36") 57 | ws_config.Header.Set("X-Forward-For", s.RIP) 58 | ws_config.Header.Set("X-Forward-Host", Config.Fakehost) 59 | ws_config.Header.Set("X-Forward-Protocol", c.RemoteAddr().Network()) 60 | ws_config.Header.Set("X-Forward-Address", c.RemoteAddr().String()) 61 | 62 | rc, err := websocket.DialConfig(ws_config) 63 | if err != nil { 64 | return err 65 | } 66 | rc.PayloadType = websocket.BinaryFrame 67 | defer rc.Close() 68 | 69 | go Copy(c, rc, s) 70 | Copy(rc, c, s) 71 | return nil 72 | } 73 | -------------------------------------------------------------------------------- /relay/wss_tunnel_server.go: -------------------------------------------------------------------------------- 1 | package relay 2 | 3 | import ( 4 | "net" 5 | "net/http" 6 | "time" 7 | 8 | "golang.org/x/net/websocket" 9 | ) 10 | 11 | func (s *Relay) RunWssTunnelServer(tcp, udp bool) error { 12 | s.ListenTCP() 13 | defer s.TCPListen.Close() 14 | handler := http.NewServeMux() 15 | if tcp { 16 | handler.Handle("/wstcp/", websocket.Handler(s.WssTunnelServerTcpHandle)) 17 | } 18 | if udp { 19 | handler.Handle("/wsudp/", websocket.Handler(s.WssTunnelServerUdpHandle)) 20 | } 21 | handler.Handle("/", NewRP(Config.Fakeurl, Config.Fakehost)) 22 | s.Svr = &http.Server{Handler: handler} 23 | s.Svr.ServeTLS(s.TCPListen, Config.Certfile, Config.Keyfile) 24 | defer s.Svr.Shutdown(nil) 25 | return nil 26 | } 27 | func (s *Relay) WssTunnelServerTcpHandle(ws *websocket.Conn) { 28 | ws.PayloadType = websocket.BinaryFrame 29 | defer ws.Close() 30 | 31 | tmp, err := net.DialTimeout("tcp", s.Raddr, time.Duration(s.TCPTimeout)*time.Second) 32 | if err != nil { 33 | return 34 | } 35 | rc := tmp.(*net.TCPConn) 36 | defer rc.Close() 37 | go Copy(rc, ws, s) 38 | Copy(ws, rc, s) 39 | return 40 | } 41 | 42 | func (s *Relay) WssTunnelServerUdpHandle(ws *websocket.Conn) { 43 | ws.PayloadType = websocket.BinaryFrame 44 | defer ws.Close() 45 | 46 | rc, err := net.DialTimeout("udp", s.Raddr, time.Duration(s.UDPTimeout)*time.Second) 47 | if err != nil { 48 | return 49 | } 50 | defer rc.Close() 51 | 52 | go Copy(rc, ws, s) 53 | Copy(ws, rc, s) 54 | return 55 | } 56 | -------------------------------------------------------------------------------- /rules/rules.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | type Rule struct { 4 | Port uint `json:port` 5 | Remote string `json:remote` 6 | RIP string 7 | Rport uint `json:rport` 8 | Type string `json:type` 9 | } 10 | -------------------------------------------------------------------------------- /stat/stat.go: -------------------------------------------------------------------------------- 1 | package stat 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/gin-gonic/gin" 7 | "github.com/shirou/gopsutil/cpu" 8 | "github.com/shirou/gopsutil/host" 9 | "github.com/shirou/gopsutil/mem" 10 | "github.com/shirou/gopsutil/net" 11 | ) 12 | 13 | func GetStat() (map[string]interface{}, error) { 14 | CPU1, err := cpu.Times(true) 15 | if err != nil { 16 | return nil, err 17 | } 18 | NET1, err := net.IOCounters(true) 19 | if err != nil { 20 | return nil, err 21 | } 22 | time.Sleep(200 * time.Millisecond) 23 | CPU2, err := cpu.Times(true) 24 | if err != nil { 25 | return nil, err 26 | } 27 | NET2, err := net.IOCounters(true) 28 | if err != nil { 29 | return nil, err 30 | } 31 | MEM, err := mem.VirtualMemory() 32 | if err != nil { 33 | return nil, err 34 | } 35 | SWAP, err := mem.SwapMemory() 36 | if err != nil { 37 | return nil, err 38 | } 39 | single := make([]float64, len(CPU1)) 40 | var idle, total, multi float64 41 | idle, total = 0, 0 42 | for i, c1 := range CPU1 { 43 | c2 := CPU2[i] 44 | single[i] = 1 - (c2.Idle-c1.Idle)/(c2.Total()-c1.Total()) 45 | idle += c2.Idle - c1.Idle 46 | total += c2.Total() - c1.Total() 47 | } 48 | multi = 1 - idle/total 49 | var in, out, in_total, out_total uint64 50 | in, out, in_total, out_total = 0, 0, 0, 0 51 | for i, x := range NET2 { 52 | if x.Name == "lo" { 53 | continue 54 | } 55 | in += x.BytesRecv - NET1[i].BytesRecv 56 | out += x.BytesSent - NET1[i].BytesSent 57 | in_total += x.BytesRecv 58 | out_total += x.BytesSent 59 | } 60 | host, err := host.Info() 61 | if err != nil { 62 | return nil, err 63 | } 64 | return gin.H{ 65 | "cpu": gin.H{"multi": multi, "single": single}, 66 | "net": gin.H{ 67 | "delta": gin.H{ 68 | "in": float64(in) / 0.2, 69 | "out": float64(out) / 0.2, 70 | }, 71 | "total": gin.H{ 72 | "in": in_total, 73 | "out": out_total, 74 | }, 75 | }, 76 | "mem": gin.H{ 77 | "virtual": MEM, 78 | "swap": SWAP, 79 | }, 80 | "host": host, 81 | }, nil 82 | } 83 | --------------------------------------------------------------------------------