├── .github └── workflows │ └── go-cross-build.yml ├── .gitignore ├── LICENSE ├── README.md ├── cmd └── anyproxy │ └── main.go ├── go.mod ├── go.sum ├── init └── init.go ├── pprof ├── init.go └── pprof.go ├── proxies ├── httpproxy │ ├── httpproxy.go │ └── init.go ├── shadowsocks │ ├── init.go │ └── shadowsocks.go ├── socks4 │ ├── init.go │ └── socks4.go ├── socks5 │ ├── init.go │ └── socks5.go └── sshproxy │ ├── init.go │ └── sshproxy.go ├── proxy.go ├── server.go └── warpping.go /.github/workflows/go-cross-build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | push: 4 | tags: 5 | - v* 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Set up Go 13 | uses: actions/setup-go@v2 14 | with: 15 | go-version: 1.18 16 | - name: Build Cross Platform 17 | uses: wzshiming/action-go-build-cross-plantform@v1 18 | - name: Upload Release Assets 19 | uses: wzshiming/action-upload-release-assets@v1 20 | env: 21 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 22 | - name: Log into registry 23 | run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin 24 | - name: Upload Release Images 25 | uses: wzshiming/action-upload-release-images@v1 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 wzshiming 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # anyproxy 2 | 3 | Proxy server supporting http/ssh/socks4/socks5/shadowsocks on port 4 | 5 | [![Build](https://github.com/wzshiming/anyproxy/actions/workflows/go-cross-build.yml/badge.svg)](https://github.com/wzshiming/anyproxy/actions/workflows/go-cross-build.yml) 6 | [![Go Report Card](https://goreportcard.com/badge/github.com/wzshiming/anyproxy)](https://goreportcard.com/report/github.com/wzshiming/anyproxy) 7 | [![GoDoc](https://godoc.org/github.com/wzshiming/anyproxy?status.svg)](https://godoc.org/github.com/wzshiming/anyproxy) 8 | [![GitHub license](https://img.shields.io/github/license/wzshiming/anyproxy.svg)](https://github.com/wzshiming/anyproxy/blob/master/LICENSE) 9 | [![gocover.io](https://gocover.io/_badge/github.com/wzshiming/anyproxy)](https://gocover.io/github.com/wzshiming/anyproxy) 10 | 11 | This project is to add protocol support for the [Bridge](https://github.com/wzshiming/bridge), or it can be used alone 12 | 13 | The following is the implementation of other proxy protocols 14 | 15 | - [Socks4](https://github.com/wzshiming/socks4) 16 | - [Socks5](https://github.com/wzshiming/socks5) 17 | - [HTTP Proxy](https://github.com/wzshiming/httpproxy) 18 | - [Shadow Socks](https://github.com/wzshiming/shadowsocks) 19 | - [SSH Proxy](https://github.com/wzshiming/sshproxy) 20 | - [Emux](https://github.com/wzshiming/emux) 21 | 22 | ## Usage 23 | 24 | [API Documentation](https://godoc.org/github.com/wzshiming/anyproxy) 25 | 26 | [Example](https://github.com/wzshiming/anyproxy/blob/master/cmd/anyproxy/main.go) 27 | 28 | ## License 29 | 30 | Licensed under the MIT License. See [LICENSE](https://github.com/wzshiming/anyproxy/blob/master/LICENSE) for the full license text. 31 | -------------------------------------------------------------------------------- /cmd/anyproxy/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "log" 7 | "net" 8 | "os" 9 | 10 | _ "github.com/wzshiming/anyproxy/init" 11 | _ "github.com/wzshiming/anyproxy/pprof" 12 | 13 | "github.com/wzshiming/anyproxy" 14 | ) 15 | 16 | var address string 17 | 18 | func init() { 19 | flag.StringVar(&address, "a", ":8080", "listen on the address") 20 | flag.Parse() 21 | } 22 | 23 | func main() { 24 | logger := log.New(os.Stderr, "[any proxy] ", log.LstdFlags) 25 | var dialer net.Dialer 26 | 27 | addrs := flag.Args() 28 | if address != "" { 29 | addrs = append(addrs, "http://"+address, "socks4://"+address, "socks5://"+address, "ssh://"+address, "pprof://"+address) 30 | } 31 | 32 | conf := anyproxy.Config{ 33 | Dialer: &dialer, 34 | Logger: logger, 35 | } 36 | 37 | svc, err := anyproxy.NewAnyProxy(context.Background(), addrs, &conf) 38 | if err != nil { 39 | logger.Println(err) 40 | return 41 | } 42 | logger.Printf("listen %s", addrs) 43 | err = svc.Run(context.Background()) 44 | if err != nil { 45 | logger.Println(err) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/wzshiming/anyproxy 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/wzshiming/cmux v0.4.2 7 | github.com/wzshiming/httpproxy v0.5.7 8 | github.com/wzshiming/shadowsocks v0.4.2 9 | github.com/wzshiming/socks4 v0.3.3 10 | github.com/wzshiming/socks5 v0.5.2 11 | github.com/wzshiming/sshd v0.2.4 12 | github.com/wzshiming/sshproxy v0.5.2 13 | golang.org/x/crypto v0.31.0 14 | golang.org/x/sync v0.10.0 15 | ) 16 | 17 | require ( 18 | github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect 19 | github.com/wzshiming/trie v0.3.1 // indirect 20 | golang.org/x/sys v0.28.0 // indirect 21 | ) 22 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= 2 | github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= 3 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 4 | github.com/wzshiming/cmux v0.4.2 h1:tI73lL5ztVfiqw7R5m5BkxT1+vQ2PBo/oV6qPbNGPiA= 5 | github.com/wzshiming/cmux v0.4.2/go.mod h1:JgE61QfZAjEyNMX0iZo9zIKY6pr9bHVY132yYPwHW5U= 6 | github.com/wzshiming/httpproxy v0.5.7 h1:eAdbzsnr0JcXVLst9vp4oHL1rTaUgTYQeCerzkOAP3o= 7 | github.com/wzshiming/httpproxy v0.5.7/go.mod h1:vw/jA1IzuGBj+LndQ8h00IkU8OSS0lOZYw0HtjnnGZw= 8 | github.com/wzshiming/shadowsocks v0.4.2 h1:f3nVW20I/cpRXxHojRoosCM2DgCAGL8zv7T7QFe0Olw= 9 | github.com/wzshiming/shadowsocks v0.4.2/go.mod h1:4VlBH5YAkDUaU/f3rcuk3m+625Hyz3VajJJI2iRlK8E= 10 | github.com/wzshiming/socks4 v0.3.3 h1:IsuqRbDrYfJKCrEXYNW3Nxi5l+4xKpoN7Z18b6xhE1s= 11 | github.com/wzshiming/socks4 v0.3.3/go.mod h1:YEPfhjf/4JezwdTmgXZU+UX+A2KvD05quzhsUBVMNA0= 12 | github.com/wzshiming/socks5 v0.5.2 h1:LtoowVNwAmkIQSkP1r1Wg435xUmC+tfRxorNW30KtnM= 13 | github.com/wzshiming/socks5 v0.5.2/go.mod h1:BvCAqlzocQN5xwLjBZDBbvWlrx8sCYSSbHEOf2wZgT0= 14 | github.com/wzshiming/sshd v0.2.4 h1:GGhZx8qA1GjjY76C2JCwJZHAJ+Kd1Qrm6ykQQueKidg= 15 | github.com/wzshiming/sshd v0.2.4/go.mod h1:dT2CiVtyiXxEdbTTQ46tAzeT9opon98T263ZtV6xU84= 16 | github.com/wzshiming/sshproxy v0.5.2 h1:vV6nX2xVZNUP68gkuw6rOTOPzyV3pJmXNFtZNX8UAc4= 17 | github.com/wzshiming/sshproxy v0.5.2/go.mod h1:VvDPPaMcGav87evkR25DxF7xGx20E9fMhxYfRoZpiCM= 18 | github.com/wzshiming/trie v0.3.1 h1:YpuoqmEQFJiW0mns/mM6Qk4kdWrXc8kc28/KR1vn0m8= 19 | github.com/wzshiming/trie v0.3.1/go.mod h1:c9thxXTh4KcGkejt4sUsO4c5GUmWpxeWzOJ7AZJaI+8= 20 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 21 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 22 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 23 | golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= 24 | golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= 25 | golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= 26 | golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= 27 | golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= 28 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 29 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 30 | golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 31 | golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 32 | golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 33 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 34 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 35 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 36 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 37 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 38 | golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= 39 | golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= 40 | golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= 41 | golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= 42 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 43 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 44 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 45 | golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= 46 | golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 47 | golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 48 | golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= 49 | golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 50 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 51 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 52 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 53 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 54 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 55 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 56 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 57 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 58 | golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 59 | golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 60 | golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= 61 | golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 62 | golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= 63 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 64 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 65 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 66 | golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= 67 | golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= 68 | golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= 69 | golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= 70 | golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= 71 | golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= 72 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 73 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 74 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 75 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 76 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 77 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 78 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 79 | golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 80 | golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= 81 | golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= 82 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 83 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 84 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 85 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 86 | golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= 87 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= 88 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 89 | -------------------------------------------------------------------------------- /init/init.go: -------------------------------------------------------------------------------- 1 | package init 2 | 3 | import ( 4 | _ "github.com/wzshiming/anyproxy/proxies/httpproxy" 5 | _ "github.com/wzshiming/anyproxy/proxies/shadowsocks" 6 | _ "github.com/wzshiming/anyproxy/proxies/socks4" 7 | _ "github.com/wzshiming/anyproxy/proxies/socks5" 8 | _ "github.com/wzshiming/anyproxy/proxies/sshproxy" 9 | ) 10 | -------------------------------------------------------------------------------- /pprof/init.go: -------------------------------------------------------------------------------- 1 | package pprof 2 | 3 | import ( 4 | "github.com/wzshiming/anyproxy" 5 | ) 6 | 7 | func init() { 8 | anyproxy.Register("pprof", NewServeConn) 9 | } 10 | -------------------------------------------------------------------------------- /pprof/pprof.go: -------------------------------------------------------------------------------- 1 | package pprof 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "net/http" 7 | "net/http/pprof" 8 | 9 | "github.com/wzshiming/anyproxy" 10 | "github.com/wzshiming/cmux/pattern" 11 | ) 12 | 13 | const prefix = "/debug/pprof/" 14 | 15 | func NewServeConn(ctx context.Context, scheme string, address string, conf *anyproxy.Config) (anyproxy.ServeConn, []string, error) { 16 | mux := http.NewServeMux() 17 | mux.HandleFunc(prefix+"", pprof.Index) 18 | mux.HandleFunc(prefix+"cmdline", pprof.Cmdline) 19 | mux.HandleFunc(prefix+"profile", pprof.Profile) 20 | mux.HandleFunc(prefix+"symbol", pprof.Symbol) 21 | mux.HandleFunc(prefix+"trace", pprof.Trace) 22 | 23 | var patterns []string 24 | 25 | tmp := pattern.Pattern[pattern.HTTP] 26 | patterns = make([]string, 0, len(tmp)+1) 27 | for _, t := range tmp { 28 | patterns = append(patterns, t+"/debug/") 29 | } 30 | s := http.Server{ 31 | BaseContext: func(listener net.Listener) context.Context { 32 | return ctx 33 | }, 34 | Handler: mux, 35 | } 36 | return anyproxy.NewHttpServeConn(&s), patterns, nil 37 | } 38 | -------------------------------------------------------------------------------- /proxies/httpproxy/httpproxy.go: -------------------------------------------------------------------------------- 1 | package httpproxy 2 | 3 | import ( 4 | "context" 5 | "net" 6 | 7 | "github.com/wzshiming/anyproxy" 8 | "github.com/wzshiming/cmux/pattern" 9 | "github.com/wzshiming/httpproxy" 10 | ) 11 | 12 | var patterns = append(pattern.Pattern[pattern.HTTP], pattern.Pattern[pattern.HTTP2]...) 13 | 14 | func NewServeConn(ctx context.Context, scheme string, address string, conf *anyproxy.Config) (anyproxy.ServeConn, []string, error) { 15 | s, err := httpproxy.NewSimpleServer(scheme + "://" + address) 16 | if err != nil { 17 | return nil, nil, err 18 | } 19 | s.Server.BaseContext = func(listener net.Listener) context.Context { 20 | return ctx 21 | } 22 | if conf.Users != nil { 23 | auth := map[string]string{} 24 | for _, user := range conf.Users { 25 | password, _ := user.Password() 26 | auth[user.Username()] = password 27 | } 28 | s.Authentication = httpproxy.BasicAuthFunc(func(username, password string) bool { 29 | return auth[username] == password 30 | }) 31 | } 32 | s.Logger = conf.Logger 33 | if conf.Dialer != nil { 34 | s.ProxyDial = conf.Dialer.DialContext 35 | } 36 | s.BytesPool = conf.BytesPool 37 | return anyproxy.NewHttpServeConn(&s.Server), patterns, nil 38 | } 39 | -------------------------------------------------------------------------------- /proxies/httpproxy/init.go: -------------------------------------------------------------------------------- 1 | package httpproxy 2 | 3 | import ( 4 | "github.com/wzshiming/anyproxy" 5 | ) 6 | 7 | func init() { 8 | anyproxy.Register("http", NewServeConn) 9 | } 10 | -------------------------------------------------------------------------------- /proxies/shadowsocks/init.go: -------------------------------------------------------------------------------- 1 | package shadowsocks 2 | 3 | import ( 4 | "github.com/wzshiming/anyproxy" 5 | ) 6 | 7 | func init() { 8 | anyproxy.Register("shadowsocks", NewServeConn) 9 | anyproxy.Register("ss", NewServeConn) 10 | } 11 | -------------------------------------------------------------------------------- /proxies/shadowsocks/shadowsocks.go: -------------------------------------------------------------------------------- 1 | package shadowsocks 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/wzshiming/anyproxy" 8 | "github.com/wzshiming/shadowsocks" 9 | ) 10 | 11 | func NewServeConn(ctx context.Context, scheme string, address string, conf *anyproxy.Config) (anyproxy.ServeConn, []string, error) { 12 | if len(conf.Users) != 1 { 13 | return nil, nil, fmt.Errorf("shadowsocks only supports a single authentication method") 14 | } 15 | s, err := shadowsocks.NewSimpleServer(scheme + "://" + conf.Users[0].String() + "@" + address) 16 | if err != nil { 17 | return nil, nil, err 18 | } 19 | s.Context = ctx 20 | s.Logger = conf.Logger 21 | if conf.Dialer != nil { 22 | s.ProxyDial = conf.Dialer.DialContext 23 | } 24 | s.BytesPool = conf.BytesPool 25 | return s, nil, nil 26 | } 27 | -------------------------------------------------------------------------------- /proxies/socks4/init.go: -------------------------------------------------------------------------------- 1 | package socks4 2 | 3 | import ( 4 | "github.com/wzshiming/anyproxy" 5 | ) 6 | 7 | func init() { 8 | anyproxy.Register("socks4", NewServeConn) 9 | anyproxy.Register("socks4a", NewServeConn) 10 | } 11 | -------------------------------------------------------------------------------- /proxies/socks4/socks4.go: -------------------------------------------------------------------------------- 1 | package socks4 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/wzshiming/anyproxy" 7 | "github.com/wzshiming/cmux/pattern" 8 | "github.com/wzshiming/socks4" 9 | ) 10 | 11 | var patterns = pattern.Pattern[pattern.SOCKS4] 12 | 13 | func NewServeConn(ctx context.Context, scheme string, address string, conf *anyproxy.Config) (anyproxy.ServeConn, []string, error) { 14 | s, err := socks4.NewSimpleServer(scheme + "://" + address) 15 | if err != nil { 16 | return nil, nil, err 17 | } 18 | if conf.Users != nil { 19 | auth := map[string]struct{}{} 20 | for _, user := range conf.Users { 21 | auth[user.Username()] = struct{}{} 22 | } 23 | s.Authentication = socks4.AuthenticationFunc(func(cmd socks4.Command, username string) bool { 24 | _, ok := auth[username] 25 | return ok 26 | }) 27 | } 28 | s.Context = ctx 29 | s.Logger = conf.Logger 30 | if conf.Dialer != nil { 31 | s.ProxyDial = conf.Dialer.DialContext 32 | } 33 | s.BytesPool = conf.BytesPool 34 | return s, patterns, nil 35 | } 36 | -------------------------------------------------------------------------------- /proxies/socks5/init.go: -------------------------------------------------------------------------------- 1 | package socks5 2 | 3 | import ( 4 | "github.com/wzshiming/anyproxy" 5 | ) 6 | 7 | func init() { 8 | anyproxy.Register("socks5", NewServeConn) 9 | anyproxy.Register("socks5h", NewServeConn) 10 | } 11 | -------------------------------------------------------------------------------- /proxies/socks5/socks5.go: -------------------------------------------------------------------------------- 1 | package socks5 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/wzshiming/anyproxy" 7 | "github.com/wzshiming/cmux/pattern" 8 | "github.com/wzshiming/socks5" 9 | ) 10 | 11 | var patterns = pattern.Pattern[pattern.SOCKS5] 12 | 13 | func NewServeConn(ctx context.Context, scheme string, address string, conf *anyproxy.Config) (anyproxy.ServeConn, []string, error) { 14 | s, err := socks5.NewSimpleServer(scheme + "://" + address) 15 | if err != nil { 16 | return nil, nil, err 17 | } 18 | if conf.Users != nil { 19 | auth := map[string]string{} 20 | for _, user := range conf.Users { 21 | password, _ := user.Password() 22 | auth[user.Username()] = password 23 | } 24 | s.Authentication = socks5.AuthenticationFunc(func(cmd socks5.Command, username, password string) bool { 25 | return auth[username] == password 26 | }) 27 | } 28 | s.Context = ctx 29 | s.Logger = conf.Logger 30 | if conf.Dialer != nil { 31 | s.ProxyDial = conf.Dialer.DialContext 32 | } 33 | if conf.ListenConfig != nil { 34 | s.ProxyListen = conf.ListenConfig.Listen 35 | } 36 | s.BytesPool = conf.BytesPool 37 | return s, patterns, nil 38 | } 39 | -------------------------------------------------------------------------------- /proxies/sshproxy/init.go: -------------------------------------------------------------------------------- 1 | package sshproxy 2 | 3 | import ( 4 | "github.com/wzshiming/anyproxy" 5 | ) 6 | 7 | func init() { 8 | anyproxy.Register("ssh", NewServeConn) 9 | } 10 | -------------------------------------------------------------------------------- /proxies/sshproxy/sshproxy.go: -------------------------------------------------------------------------------- 1 | package sshproxy 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "strings" 7 | 8 | _ "github.com/wzshiming/sshd/directstreamlocal" 9 | _ "github.com/wzshiming/sshd/directtcp" 10 | _ "github.com/wzshiming/sshd/streamlocalforward" 11 | _ "github.com/wzshiming/sshd/tcpforward" 12 | 13 | "github.com/wzshiming/anyproxy" 14 | "github.com/wzshiming/cmux/pattern" 15 | "github.com/wzshiming/sshproxy" 16 | "golang.org/x/crypto/ssh" 17 | ) 18 | 19 | var patterns = pattern.Pattern[pattern.SSH] 20 | 21 | func NewServeConn(ctx context.Context, scheme string, address string, conf *anyproxy.Config) (anyproxy.ServeConn, []string, error) { 22 | s, err := sshproxy.NewSimpleServer(scheme + "://" + address + "?" + strings.Join(conf.RawQueries, "&")) 23 | if err != nil { 24 | return nil, nil, err 25 | } 26 | if conf.Users != nil { 27 | auth := map[string]string{} 28 | for _, user := range conf.Users { 29 | password, _ := user.Password() 30 | auth[user.Username()] = password 31 | } 32 | s.ServerConfig.PasswordCallback = func(conn ssh.ConnMetadata, pwd []byte) (*ssh.Permissions, error) { 33 | if p, ok := auth[conn.User()]; ok && p == string(pwd) { 34 | return nil, nil 35 | } 36 | return nil, fmt.Errorf("denied") 37 | } 38 | s.ServerConfig.NoClientAuth = false 39 | } 40 | s.Context = ctx 41 | s.Logger = conf.Logger 42 | if conf.Dialer != nil { 43 | s.ProxyDial = conf.Dialer.DialContext 44 | } 45 | if conf.ListenConfig != nil { 46 | s.ProxyListen = conf.ListenConfig.Listen 47 | } 48 | s.BytesPool = conf.BytesPool 49 | return s, patterns, nil 50 | } 51 | -------------------------------------------------------------------------------- /proxy.go: -------------------------------------------------------------------------------- 1 | package anyproxy 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net" 7 | "net/url" 8 | "sort" 9 | 10 | "github.com/wzshiming/cmux" 11 | "golang.org/x/sync/errgroup" 12 | ) 13 | 14 | type Dialer interface { 15 | DialContext(ctx context.Context, network, address string) (net.Conn, error) 16 | } 17 | 18 | type ListenConfig interface { 19 | Listen(ctx context.Context, network, address string) (net.Listener, error) 20 | } 21 | 22 | type AnyProxy struct { 23 | proxies map[string]*Host 24 | conf *Config 25 | } 26 | 27 | type Logger interface { 28 | Println(v ...interface{}) 29 | } 30 | 31 | func NewAnyProxy(ctx context.Context, addrs []string, conf *Config) (*AnyProxy, error) { 32 | if conf == nil { 33 | conf = &Config{} 34 | } else { 35 | c := *conf 36 | conf = &c 37 | } 38 | if conf.ListenConfig == nil { 39 | conf.ListenConfig = &net.ListenConfig{} 40 | } 41 | 42 | proxies := map[string]*Host{} 43 | users := map[string][]*url.Userinfo{} 44 | for _, addr := range addrs { 45 | u, err := url.Parse(addr) 46 | if err != nil { 47 | return nil, err 48 | } 49 | 50 | unique := fmt.Sprintf("%s://%s", u.Scheme, u.Host) 51 | if u.User == nil { 52 | users[unique] = nil 53 | } else { 54 | if user, ok := users[unique]; !ok || user != nil { 55 | users[unique] = append(users[unique], u.User) 56 | } 57 | } 58 | hostconf := *conf 59 | hostconf.RawQueries = append(hostconf.RawQueries, u.RawQuery) 60 | hostconf.Users = append(users[unique], hostconf.Users...) 61 | 62 | s, patterns, err := NewServeConn(ctx, u.Scheme, u.Host, &hostconf) 63 | if err != nil { 64 | return nil, err 65 | } 66 | mux, ok := proxies[u.Host] 67 | if !ok { 68 | mux = &Host{ 69 | cmux: cmux.NewCMux(), 70 | conf: conf, 71 | } 72 | } 73 | 74 | if p, ok := s.(proxyURLs); ok { 75 | mux.proxies = append(mux.proxies, p.ProxyURLs()...) 76 | } else if p, ok := s.(proxyURL); ok { 77 | mux.proxies = append(mux.proxies, p.ProxyURL()) 78 | } 79 | if patterns == nil { 80 | err = mux.cmux.NotFound(s) 81 | if err != nil { 82 | return nil, err 83 | } 84 | } else { 85 | err = mux.cmux.HandlePrefix(s, patterns...) 86 | } 87 | proxies[u.Host] = mux 88 | } 89 | proxy := &AnyProxy{ 90 | conf: conf, 91 | proxies: proxies, 92 | } 93 | return proxy, nil 94 | } 95 | 96 | func (a *AnyProxy) Match(addr string) *Host { 97 | return a.proxies[addr] 98 | } 99 | 100 | func (a *AnyProxy) Hosts() []string { 101 | hosts := make([]string, 0, len(a.proxies)) 102 | for proxy := range a.proxies { 103 | hosts = append(hosts, proxy) 104 | } 105 | sort.Strings(hosts) 106 | return hosts 107 | } 108 | 109 | func (a *AnyProxy) ListenAndServe(network, address string) error { 110 | host := a.Match(address) 111 | if host == nil { 112 | return fmt.Errorf("not match address %q", address) 113 | } 114 | return host.ListenAndServe(network, address) 115 | } 116 | 117 | func (a *AnyProxy) Run(ctx context.Context) error { 118 | group, ctx := errgroup.WithContext(ctx) 119 | for _, address := range a.Hosts() { 120 | address := address 121 | host := a.Match(address) 122 | if host == nil { 123 | return fmt.Errorf("not match address %q", address) 124 | } 125 | listener, err := a.conf.ListenConfig.Listen(ctx, "tcp", address) 126 | if err != nil { 127 | return err 128 | } 129 | group.Go(func() error { 130 | for { 131 | conn, err := listener.Accept() 132 | if err != nil { 133 | return err 134 | } 135 | go host.ServeConn(conn) 136 | } 137 | }) 138 | } 139 | return group.Wait() 140 | } 141 | 142 | type Host struct { 143 | cmux *cmux.CMux 144 | proxies []string 145 | conf *Config 146 | } 147 | 148 | func (h *Host) ProxyURLs() []string { 149 | return h.proxies 150 | } 151 | 152 | func (h *Host) ServeConn(conn net.Conn) { 153 | h.cmux.ServeConn(conn) 154 | } 155 | 156 | func (h *Host) ListenAndServe(network, address string) error { 157 | listener, err := h.conf.ListenConfig.Listen(context.Background(), network, address) 158 | if err != nil { 159 | return err 160 | } 161 | return h.Serve(listener) 162 | } 163 | 164 | func (h *Host) Serve(listener net.Listener) error { 165 | for { 166 | conn, err := listener.Accept() 167 | if err != nil { 168 | return err 169 | } 170 | go h.ServeConn(conn) 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /server.go: -------------------------------------------------------------------------------- 1 | package anyproxy 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net" 7 | "net/url" 8 | 9 | _ "github.com/wzshiming/shadowsocks/init" 10 | ) 11 | 12 | // BytesPool is an interface for getting and returning temporary 13 | // bytes for use by io.CopyBuffer. 14 | type BytesPool interface { 15 | Get() []byte 16 | Put([]byte) 17 | } 18 | 19 | var schemeMap = map[string]SchemeFunc{} 20 | 21 | func Register(scheme string, fun SchemeFunc) { 22 | schemeMap[scheme] = fun 23 | } 24 | 25 | type Config struct { 26 | RawQueries []string 27 | Users []*url.Userinfo 28 | Dialer Dialer 29 | ListenConfig ListenConfig 30 | Logger Logger 31 | BytesPool BytesPool 32 | } 33 | 34 | type SchemeFunc func(ctx context.Context, scheme string, address string, conf *Config) (ServeConn, []string, error) 35 | 36 | type ServeConn interface { 37 | ServeConn(conn net.Conn) 38 | } 39 | 40 | type proxyURLs interface { 41 | ProxyURLs() []string 42 | } 43 | 44 | type proxyURL interface { 45 | ProxyURL() string 46 | } 47 | 48 | func NewServeConn(ctx context.Context, scheme string, address string, conf *Config) (ServeConn, []string, error) { 49 | sch, ok := schemeMap[scheme] 50 | if !ok || sch == nil { 51 | return nil, nil, fmt.Errorf("can't support scheme %q", scheme) 52 | } 53 | return sch(ctx, scheme, address, conf) 54 | } 55 | -------------------------------------------------------------------------------- /warpping.go: -------------------------------------------------------------------------------- 1 | package anyproxy 2 | 3 | import ( 4 | "errors" 5 | "net" 6 | "net/http" 7 | "sync" 8 | 9 | "github.com/wzshiming/httpproxy" 10 | ) 11 | 12 | var ErrNetClosing = errors.New("use of closed network connection") 13 | 14 | type singleConnListener struct { 15 | addr net.Addr 16 | ch chan net.Conn 17 | once sync.Once 18 | } 19 | 20 | func newSingleConnListener(conn net.Conn) net.Listener { 21 | ch := make(chan net.Conn, 1) 22 | ch <- conn 23 | return &singleConnListener{ 24 | addr: conn.LocalAddr(), 25 | ch: ch, 26 | } 27 | } 28 | 29 | func (l *singleConnListener) Accept() (net.Conn, error) { 30 | conn, ok := <-l.ch 31 | if !ok || conn == nil { 32 | return nil, ErrNetClosing 33 | } 34 | return &connCloser{ 35 | l: l, 36 | Conn: conn, 37 | }, nil 38 | } 39 | 40 | func (l *singleConnListener) shutdown() error { 41 | l.once.Do(func() { 42 | close(l.ch) 43 | }) 44 | return nil 45 | } 46 | 47 | func (l *singleConnListener) Close() error { 48 | return nil 49 | } 50 | 51 | func (l *singleConnListener) Addr() net.Addr { 52 | return l.addr 53 | } 54 | 55 | type connCloser struct { 56 | l *singleConnListener 57 | net.Conn 58 | } 59 | 60 | func (c *connCloser) Close() error { 61 | c.l.shutdown() 62 | return c.Conn.Close() 63 | } 64 | 65 | type httpServeConn struct { 66 | *http.Server 67 | } 68 | 69 | func NewHttpServeConn(s *http.Server) ServeConn { 70 | return &httpServeConn{s} 71 | } 72 | 73 | func (w httpServeConn) ServeConn(conn net.Conn) { 74 | conn = httpproxy.NewConnCompatibilityReadDeadline(conn) 75 | listener := newSingleConnListener(conn) 76 | w.Serve(listener) 77 | } 78 | --------------------------------------------------------------------------------