├── .gitignore ├── LICENSE ├── README.md ├── go.mod ├── go.sum ├── http.go ├── main.go └── socks5.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | upload 4 | *.exe 5 | *.db 6 | build -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 zu1k 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go Proxy IPV6 Pool 2 | 3 | Random ipv6 egress proxy server (support http/socks5) 4 | 5 | The Go language implementation of [zu1k/http-proxy-ipv6-pool](https://github.com/zu1k/http-proxy-ipv6-pool) 6 | 7 | ## Usage 8 | 9 | ```bash 10 | go run . --port --cidr < your ipv6 cidr > # e.g. 2001:399:8205:ae00::/64 11 | ``` 12 | 13 | ### Use as a proxy server 14 | 15 | ```bash 16 | curl -x http://xxx:52122 http://6.ipw.cn/ # 2001:399:8205:ae00:456a:ab12 (random ipv6 address) 17 | ``` 18 | 19 | ```bash 20 | curl -x socks5://xxx:52123 http://6.ipw.cn/ # 2001:399:8205:ae00:456a:ab12 (random ipv6 address) 21 | ``` 22 | 23 | ## License 24 | 25 | MIT License (see [LICENSE](LICENSE)) 26 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module go-proxy-ipv6-pool 2 | 3 | go 1.20 4 | 5 | require github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 6 | 7 | require ( 8 | github.com/elazarl/goproxy v0.0.0-20231117061959-7cc037d33fb5 // indirect 9 | golang.org/x/net v0.19.0 // indirect 10 | ) 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= 2 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= 3 | github.com/elazarl/goproxy v0.0.0-20231117061959-7cc037d33fb5 h1:m62nsMU279qRD9PQSWD1l66kmkXzuYcnVJqL4XLeV2M= 4 | github.com/elazarl/goproxy v0.0.0-20231117061959-7cc037d33fb5/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= 5 | github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= 6 | github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= 7 | golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= 8 | golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= 9 | -------------------------------------------------------------------------------- /http.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "log" 6 | "net" 7 | "net/http" 8 | 9 | "github.com/elazarl/goproxy" 10 | ) 11 | 12 | var httpProxy = goproxy.NewProxyHttpServer() 13 | 14 | func init() { 15 | httpProxy.Verbose = true 16 | 17 | httpProxy.OnRequest().DoFunc( 18 | func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { 19 | // 为 IPv6 地址添加方括号 20 | outgoingIP, err := generateRandomIPv6(cidr) 21 | if err != nil { 22 | log.Printf("Generate random IPv6 error: %v", err) 23 | return req, nil 24 | } 25 | outgoingIP = "[" + outgoingIP + "]" 26 | // 使用指定的出口 IP 地址创建连接 27 | localAddr, err := net.ResolveTCPAddr("tcp", outgoingIP+":0") 28 | if err != nil { 29 | log.Printf("[http] Resolve local address error: %v", err) 30 | return req, nil 31 | } 32 | dialer := net.Dialer{ 33 | LocalAddr: localAddr, 34 | } 35 | 36 | // 通过代理服务器建立到目标服务器的连接 37 | // 发送 http 请求 38 | // 使用自定义拨号器设置 HTTP 客户端 39 | // 创建新的 HTTP 请求 40 | 41 | newReq, err := http.NewRequest(req.Method, req.URL.String(), req.Body) 42 | if err != nil { 43 | log.Printf("[http] New request error: %v", err) 44 | return req, nil 45 | } 46 | newReq.Header = req.Header 47 | 48 | // 设置自定义拨号器的 HTTP 客户端 49 | client := &http.Client{ 50 | Transport: &http.Transport{ 51 | DialContext: dialer.DialContext, 52 | }, 53 | } 54 | 55 | // 发送 HTTP 请求 56 | resp, err := client.Do(newReq) 57 | if err != nil { 58 | log.Printf("[http] Send request error: %v", err) 59 | return req, nil 60 | } 61 | return req, resp 62 | }, 63 | ) 64 | 65 | httpProxy.OnRequest().HijackConnect( 66 | func(req *http.Request, client net.Conn, ctx *goproxy.ProxyCtx) { 67 | // 通过代理服务器建立到目标服务器的连接 68 | outgoingIP, err := generateRandomIPv6(cidr) 69 | if err != nil { 70 | log.Printf("Generate random IPv6 error: %v", err) 71 | return 72 | } 73 | outgoingIP = "[" + outgoingIP + "]" 74 | // 使用指定的出口 IP 地址创建连接 75 | localAddr, err := net.ResolveTCPAddr("tcp", outgoingIP+":0") 76 | if err != nil { 77 | log.Printf("[http] Resolve local address error: %v", err) 78 | return 79 | } 80 | dialer := net.Dialer{ 81 | LocalAddr: localAddr, 82 | } 83 | 84 | // 通过代理服务器建立到目标服务器的连接 85 | server, err := dialer.Dial("tcp", req.URL.Host) 86 | if err != nil { 87 | log.Printf("[http] Dial to %s error: %v", req.URL.Host, err) 88 | client.Write([]byte("HTTP/1.1 500 Internal Server Error\r\n\r\n")) 89 | client.Close() 90 | return 91 | } 92 | 93 | // 响应客户端连接已建立 94 | client.Write([]byte("HTTP/1.0 200 OK\r\n\r\n")) 95 | // 从客户端复制数据到目标服务器 96 | go func() { 97 | defer server.Close() 98 | defer client.Close() 99 | io.Copy(server, client) 100 | }() 101 | 102 | // 从目标服务器复制数据到客户端 103 | go func() { 104 | defer server.Close() 105 | defer client.Close() 106 | io.Copy(client, server) 107 | }() 108 | 109 | }, 110 | ) 111 | } 112 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/rand" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "net" 9 | "net/http" 10 | "sync" 11 | ) 12 | 13 | var cidr string 14 | var port int 15 | 16 | func main() { 17 | 18 | flag.IntVar(&port, "port", 52122, "server port") 19 | flag.StringVar(&cidr, "cidr", "", "ipv6 cidr") 20 | flag.Parse() 21 | 22 | if cidr == "" { 23 | log.Fatal("cidr is empty") 24 | } 25 | 26 | httpPort := port 27 | socks5Port := port + 1 28 | 29 | if socks5Port > 65535 { 30 | log.Fatal("port too large") 31 | } 32 | 33 | var wg sync.WaitGroup 34 | wg.Add(2) 35 | 36 | go func() { 37 | err := socks5Server.ListenAndServe("tcp", fmt.Sprintf("0.0.0.0:%d", socks5Port)) 38 | if err != nil { 39 | log.Fatal("socks5 Server err:",err) 40 | } 41 | 42 | }() 43 | go func() { 44 | err := http.ListenAndServe(fmt.Sprintf("0.0.0.0:%d", httpPort), httpProxy) 45 | if err != nil { 46 | log.Fatal("http Server err",err) 47 | } 48 | }() 49 | 50 | 51 | log.Println("server running ...") 52 | log.Printf("http running on 0.0.0.0:%d", httpPort) 53 | log.Printf("socks5 running on 0.0.0.0:%d", socks5Port) 54 | log.Printf("ipv6 cidr:[%s]", cidr) 55 | wg.Wait() 56 | 57 | } 58 | 59 | func generateRandomIPv6(cidr string) (string, error) { 60 | // 解析CIDR 61 | _, ipv6Net, err := net.ParseCIDR(cidr) 62 | if err != nil { 63 | return "", err 64 | } 65 | 66 | // 获取网络部分和掩码长度 67 | maskSize, _ := ipv6Net.Mask.Size() 68 | 69 | // 计算随机部分的长度 70 | randomPartLength := 128 - maskSize 71 | 72 | // 生成随机部分 73 | randomPart := make([]byte, randomPartLength/8) 74 | _, err = rand.Read(randomPart) 75 | if err != nil { 76 | return "", err 77 | } 78 | 79 | // 获取网络部分 80 | networkPart := ipv6Net.IP.To16() 81 | 82 | // 合并网络部分和随机部分 83 | for i := 0; i < len(randomPart); i++ { 84 | networkPart[16-len(randomPart)+i] = randomPart[i] 85 | } 86 | 87 | return networkPart.String(), nil 88 | } 89 | -------------------------------------------------------------------------------- /socks5.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "net" 7 | 8 | socks5 "github.com/armon/go-socks5" 9 | ) 10 | 11 | var socks5Conf = &socks5.Config{} 12 | var socks5Server *socks5.Server 13 | 14 | func init() { 15 | // 指定出口 IP 地址 16 | // 指定本地出口 IPv6 地址 17 | 18 | // 创建一个 SOCKS5 服务器配置 19 | socks5Conf = &socks5.Config{ 20 | Dial: func(ctx context.Context, network, addr string) (net.Conn, error) { 21 | 22 | outgoingIP, err := generateRandomIPv6(cidr) 23 | if err != nil { 24 | log.Printf("Generate random IPv6 error: %v", err) 25 | return nil, err 26 | } 27 | outgoingIP = "[" + outgoingIP + "]" 28 | 29 | // 使用指定的出口 IP 地址创建连接 30 | localAddr, err := net.ResolveTCPAddr("tcp", outgoingIP+":0") 31 | if err != nil { 32 | log.Printf("[socks5] Resolve local address error: %v", err) 33 | return nil, err 34 | } 35 | dialer := net.Dialer{ 36 | LocalAddr: localAddr, 37 | } 38 | // 通过代理服务器建立到目标服务器的连接 39 | 40 | log.Println("[socks5]",addr, "via", outgoingIP) 41 | return dialer.DialContext(ctx, network, addr) 42 | }, 43 | } 44 | var err error 45 | // 创建 SOCKS5 服务器 46 | socks5Server, err = socks5.New(socks5Conf) 47 | if err != nil { 48 | log.Fatal(err) 49 | } 50 | } 51 | --------------------------------------------------------------------------------