diff options
author | Shelikhoo <xiaokangwang@outlook.com> | 2023-10-18 16:23:31 +0100 |
---|---|---|
committer | Shelikhoo <xiaokangwang@outlook.com> | 2023-10-24 17:42:46 +0100 |
commit | 8b46e60553b9de34df12079906f72559361ea20d (patch) | |
tree | b50ebcb9965317ec21d01b1c3d3a20540dfa9c3a | |
parent | 6b0421db0d0f7f24fc0e1251a8a2803eeb06c5fe (diff) | |
download | snowflake-8b46e60553b9de34df12079906f72559361ea20d.tar.gz snowflake-8b46e60553b9de34df12079906f72559361ea20d.zip |
Add common proxy utilities
-rw-r--r-- | common/proxy/check.go | 18 | ||||
-rw-r--r-- | common/proxy/client.go | 253 | ||||
-rw-r--r-- | go.mod | 6 | ||||
-rw-r--r-- | go.sum | 21 |
4 files changed, 296 insertions, 2 deletions
diff --git a/common/proxy/check.go b/common/proxy/check.go new file mode 100644 index 0000000..797b3eb --- /dev/null +++ b/common/proxy/check.go @@ -0,0 +1,18 @@ +package proxy + +import ( + "errors" + "net/url" + "strings" +) + +var errUnsupportedProxyType = errors.New("unsupported proxy type") + +func CheckProxyProtocolSupport(proxy *url.URL) error { + switch strings.ToLower(proxy.Scheme) { + case "socks5": + return nil + default: + return errUnsupportedProxyType + } +} diff --git a/common/proxy/client.go b/common/proxy/client.go new file mode 100644 index 0000000..cd1bd98 --- /dev/null +++ b/common/proxy/client.go @@ -0,0 +1,253 @@ +package proxy + +import ( + "context" + "errors" + "log" + "net" + "net/url" + "strconv" + "time" + + "github.com/miekg/dns" + "github.com/pion/transport/v2" + "github.com/txthinking/socks5" +) + +func NewSocks5UDPClient(addr *url.URL) SocksClient { + return SocksClient{addr: addr} +} + +type SocksClient struct { + addr *url.URL +} + +type SocksConn struct { + net.Conn + socks5Client *socks5.Client +} + +func (s SocksConn) SetReadBuffer(bytes int) error { + return nil +} + +func (s SocksConn) SetWriteBuffer(bytes int) error { + return nil +} + +func (s SocksConn) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) { + var buf [2000]byte + n, err = s.Conn.Read(buf[:]) + if err != nil { + return 0, nil, err + } + Datagram, err := socks5.NewDatagramFromBytes(buf[:n]) + if err != nil { + return 0, nil, err + } + addr, err = net.ResolveUDPAddr("udp", Datagram.Address()) + if err != nil { + return 0, nil, err + } + n = copy(b, Datagram.Data) + if n < len(Datagram.Data) { + return 0, nil, errors.New("short buffer") + } + return len(Datagram.Data), addr, nil +} + +func (s SocksConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *net.UDPAddr, err error) { + panic("unimplemented") +} + +func (s SocksConn) WriteToUDP(b []byte, addr *net.UDPAddr) (int, error) { + + a, addrb, portb, err := socks5.ParseAddress(addr.String()) + if err != nil { + return 0, err + } + packet := socks5.NewDatagram(a, addrb, portb, b) + _, err = s.Conn.Write(packet.Bytes()) + if err != nil { + return 0, err + } + return len(b), nil +} + +func (s SocksConn) WriteMsgUDP(b, oob []byte, addr *net.UDPAddr) (n, oobn int, err error) { + panic("unimplemented") +} + +func (sc *SocksClient) ListenPacket(network string, locAddr *net.UDPAddr) (transport.UDPConn, error) { + conn, err := sc.listenPacket() + if err != nil { + log.Println("[SOCKS5 Client Error] cannot listen packet", err) + } + return conn, err +} + +func (sc *SocksClient) listenPacket() (transport.UDPConn, error) { + var username, password string + if sc.addr.User != nil { + username = sc.addr.User.Username() + password, _ = sc.addr.User.Password() + } + client, err := socks5.NewClient( + sc.addr.Host, + username, password, 300, 300) + if err != nil { + return nil, err + } + + err = client.Negotiate(nil) + if err != nil { + return nil, err + } + + udpRequest := socks5.NewRequest(socks5.CmdUDP, socks5.ATYPIPv4, []byte{0x00, 0x00, 0x00, 0x00}, []byte{0x00, 0x00}) + + reply, err := client.Request(udpRequest) + if err != nil { + return nil, err + } + + udpServerAddr := socks5.ToAddress(reply.Atyp, reply.BndAddr, reply.BndPort) + + conn, err := net.Dial("udp", udpServerAddr) + if err != nil { + return nil, err + } + + return &SocksConn{conn, client}, nil +} + +func (s SocksConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + return s.WriteToUDP(p, addr.(*net.UDPAddr)) +} + +func (s SocksConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + return s.ReadFromUDP(p) +} + +func (s SocksConn) Read(b []byte) (int, error) { + panic("implement me") +} + +func (s SocksConn) RemoteAddr() net.Addr { + panic("implement me") +} + +func (s SocksConn) Write(b []byte) (int, error) { + panic("implement me") +} + +func (sc *SocksClient) ResolveUDPAddr(network string, address string) (*net.UDPAddr, error) { + dnsServer, err := net.ResolveUDPAddr("udp", "1.1.1.1:53") + if err != nil { + return nil, err + } + proxiedResolver := newDnsResolver(sc, dnsServer) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + defer cancel() + host, port, err := net.SplitHostPort(address) + if err != nil { + return nil, err + } + ip, err := proxiedResolver.lookupIPAddr(ctx, host, network == "udp6") + if err != nil { + return nil, err + } + if len(ip) <= 0 { + return nil, errors.New("cannot resolve hostname: NXDOMAIN") + } + switch network { + case "udp4": + var v4IPAddr []net.IPAddr + for _, v := range ip { + if v.IP.To4() != nil { + v4IPAddr = append(v4IPAddr, v) + } + } + ip = v4IPAddr + case "udp6": + var v6IPAddr []net.IPAddr + for _, v := range ip { + if v.IP.To4() == nil { + v6IPAddr = append(v6IPAddr, v) + } + } + ip = v6IPAddr + case "udp": + default: + return nil, errors.New("unknown network") + } + + if len(ip) <= 0 { + return nil, errors.New("cannot resolve hostname: so suitable address") + } + + portInInt, err := strconv.ParseInt(port, 10, 32) + return &net.UDPAddr{ + IP: ip[0].IP, + Port: int(portInInt), + Zone: "", + }, nil +} + +func newDnsResolver(sc *SocksClient, + serverAddress net.Addr) *dnsResolver { + return &dnsResolver{sc: sc, serverAddress: serverAddress} +} + +type dnsResolver struct { + sc *SocksClient + serverAddress net.Addr +} + +func (r *dnsResolver) lookupIPAddr(ctx context.Context, host string, ipv6 bool) ([]net.IPAddr, error) { + packetConn, err := r.sc.listenPacket() + if err != nil { + return nil, err + } + msg := new(dns.Msg) + if !ipv6 { + msg.SetQuestion(dns.Fqdn(host), dns.TypeA) + } else { + msg.SetQuestion(dns.Fqdn(host), dns.TypeAAAA) + } + encodedMsg, err := msg.Pack() + if err != nil { + log.Println(err.Error()) + } + for i := 2; i >= 0; i-- { + _, err := packetConn.WriteTo(encodedMsg, r.serverAddress) + if err != nil { + log.Println(err.Error()) + } + } + ctx, cancel := context.WithTimeout(ctx, time.Second) + defer cancel() + go func() { + <-ctx.Done() + packetConn.Close() + }() + var dataBuf [1600]byte + n, _, err := packetConn.ReadFrom(dataBuf[:]) + if err != nil { + return nil, err + } + err = msg.Unpack(dataBuf[:n]) + if err != nil { + return nil, err + } + var returnedIPs []net.IPAddr + for _, resp := range msg.Answer { + switch respTyped := resp.(type) { + case *dns.A: + returnedIPs = append(returnedIPs, net.IPAddr{IP: respTyped.A}) + case *dns.AAAA: + returnedIPs = append(returnedIPs, net.IPAddr{IP: respTyped.AAAA}) + } + } + return returnedIPs, nil +} @@ -4,6 +4,7 @@ go 1.21 require ( github.com/gorilla/websocket v1.5.0 + github.com/miekg/dns v1.1.56 github.com/pion/ice/v2 v2.3.11 github.com/pion/sdp/v3 v3.0.6 github.com/pion/stun v0.6.1 @@ -14,6 +15,7 @@ require ( github.com/refraction-networking/utls v1.5.3 github.com/smartystreets/goconvey v1.8.1 github.com/stretchr/testify v1.8.4 + github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 github.com/xtaci/kcp-go/v5 v5.6.5 github.com/xtaci/smux v1.5.24 gitlab.torproject.org/tpo/anti-censorship/geoip v0.0.0-20210928150955-7ce4b3d98d01 @@ -40,6 +42,7 @@ require ( github.com/klauspost/reedsolomon v1.11.8 // indirect github.com/kr/text v0.2.0 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pion/datachannel v1.5.5 // indirect github.com/pion/dtls/v2 v2.2.7 // indirect github.com/pion/interceptor v0.1.19 // indirect @@ -60,6 +63,9 @@ require ( github.com/templexxx/cpu v0.1.0 // indirect github.com/templexxx/xorsimd v0.4.2 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect + github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect + golang.org/x/mod v0.12.0 // indirect golang.org/x/text v0.13.0 // indirect + golang.org/x/tools v0.13.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) @@ -76,6 +76,9 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/miekg/dns v1.1.51/go.mod h1:2Z9d3CP1LQWihRZUf29mQ19yDThaI4DAYzte2CaQW5c= +github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= +github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -90,6 +93,8 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pion/datachannel v1.5.5 h1:10ef4kwdjije+M9d7Xm9im2Y3O6A6ccQb0zcqZcJew8= github.com/pion/datachannel v1.5.5/go.mod h1:iMz+lECmfdCMqFRhXhcA/219B0SQlbpoR2V118yimL0= github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8= @@ -177,6 +182,10 @@ github.com/templexxx/xorsimd v0.4.2 h1:ocZZ+Nvu65LGHmCLZ7OoCtg8Fx8jnHKK37SjvngUo github.com/templexxx/xorsimd v0.4.2/go.mod h1:HgwaPoDREdi6OnULpSfxhzaiiSUY4Fi3JPn1wpt28NI= github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= +github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf h1:7PflaKRtU4np/epFxRXlFhlzLXZzKFrH5/I4so5Ove0= +github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf/go.mod h1:CLUSJbazqETbaR+i0YAhXBICV9TrKH93pziccMhmhpM= +github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 h1:d/Wr/Vl/wiJHc3AHYbYs5I3PucJvRuw3SvbmlIRf+oM= +github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301/go.mod h1:ntmMHL/xPq1WLeKiw8p/eRATaae6PiVRNipHFJxI8PM= github.com/xtaci/kcp-go/v5 v5.6.5 h1:oxGZNobj3OddrLzwdJYnR/waNgwrL98u02u0DWNHE3k= github.com/xtaci/kcp-go/v5 v5.6.5/go.mod h1:Qy3Zf2tWTdFdEs0E8JvhrX+39r5UDZoYac8anvud7/Q= github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM= @@ -207,7 +216,10 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -222,6 +234,7 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= @@ -239,6 +252,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -268,6 +283,7 @@ golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= @@ -296,9 +312,10 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= -golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |