Add common proxy utilities

This commit is contained in:
Shelikhoo 2023-10-18 16:23:31 +01:00
parent 6b0421db0d
commit 8b46e60553
No known key found for this signature in database
GPG key ID: C4D5E79D22B25316
4 changed files with 296 additions and 2 deletions

18
common/proxy/check.go Normal file
View file

@ -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
}
}

253
common/proxy/client.go Normal file
View file

@ -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
}

6
go.mod
View file

@ -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
)

21
go.sum
View file

@ -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=