From f98eec69f2ae22bfc051258962add20abd067d3d Mon Sep 17 00:00:00 2001 From: Toby Date: Sun, 27 Mar 2022 15:10:16 -0700 Subject: [PATCH] feat: resolve preference --- cmd/client.go | 12 +++++++++ cmd/config.go | 3 ++- cmd/server.go | 14 +++++++--- pkg/transport/client.go | 12 +++++++-- pkg/transport/resolve.go | 58 ++++++++++++++++++++++++++++++++++++++++ pkg/transport/server.go | 14 +++++----- 6 files changed, 101 insertions(+), 12 deletions(-) create mode 100644 pkg/transport/resolve.go diff --git a/cmd/client.go b/cmd/client.go index 63e9396..d0fba20 100644 --- a/cmd/client.go +++ b/cmd/client.go @@ -97,6 +97,18 @@ func client(config *clientConfig) { if len(config.Obfs) > 0 { obfuscator = obfs.NewXPlusObfuscator([]byte(config.Obfs)) } + // Resolve preference + if len(config.ResolvePreference) > 0 { + pref, excl, err := transport.ResolvePreferenceFromString(config.ResolvePreference) + if err != nil { + logrus.WithFields(logrus.Fields{ + "error": err, + }).Fatal("Failed to parse the resolve preference") + } + transport.DefaultClientTransport.PrefEnabled = true + transport.DefaultClientTransport.PrefIPv6 = pref + transport.DefaultClientTransport.PrefExclusive = excl + } // ACL var aclEngine *acl.Engine if len(config.ACL) > 0 { diff --git a/cmd/config.go b/cmd/config.go index 9a804e8..7e14c3c 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -50,8 +50,8 @@ type serverConfig struct { ReceiveWindowClient uint64 `json:"recv_window_client"` MaxConnClient int `json:"max_conn_client"` DisableMTUDiscovery bool `json:"disable_mtu_discovery"` - IPv6Only bool `json:"ipv6_only"` Resolver string `json:"resolver"` + ResolvePreference string `json:"resolve_preference"` SOCKS5Outbound struct { Server string `json:"server"` User string `json:"user"` @@ -159,6 +159,7 @@ type clientConfig struct { ReceiveWindow uint64 `json:"recv_window"` DisableMTUDiscovery bool `json:"disable_mtu_discovery"` Resolver string `json:"resolver"` + ResolvePreference string `json:"resolve_preference"` } func (c *clientConfig) Check() error { diff --git a/cmd/server.go b/cmd/server.go index cd6e096..6803ece 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -140,9 +140,17 @@ func server(config *serverConfig) { if len(config.Obfs) > 0 { obfuscator = obfs.NewXPlusObfuscator([]byte(config.Obfs)) } - // IPv6 only mode - if config.IPv6Only { - transport.DefaultServerTransport.IPv6Only = true + // Resolve preference + if len(config.ResolvePreference) > 0 { + pref, excl, err := transport.ResolvePreferenceFromString(config.ResolvePreference) + if err != nil { + logrus.WithFields(logrus.Fields{ + "error": err, + }).Fatal("Failed to parse the resolve preference") + } + transport.DefaultServerTransport.PrefEnabled = true + transport.DefaultServerTransport.PrefIPv6 = pref + transport.DefaultServerTransport.PrefExclusive = excl } // SOCKS5 outbound if config.SOCKS5Outbound.Server != "" { diff --git a/pkg/transport/client.go b/pkg/transport/client.go index 0191415..54c4838 100644 --- a/pkg/transport/client.go +++ b/pkg/transport/client.go @@ -13,13 +13,17 @@ import ( ) type ClientTransport struct { - Dialer *net.Dialer + Dialer *net.Dialer + PrefEnabled bool + PrefIPv6 bool + PrefExclusive bool } var DefaultClientTransport = &ClientTransport{ Dialer: &net.Dialer{ Timeout: 8 * time.Second, }, + PrefEnabled: false, } func (ct *ClientTransport) quicPacketConn(proto string, server string, obfs obfs.Obfuscator) (net.PacketConn, error) { @@ -80,7 +84,11 @@ func (ct *ClientTransport) QUICDial(proto string, server string, tlsConfig *tls. } func (ct *ClientTransport) ResolveIPAddr(address string) (*net.IPAddr, error) { - return net.ResolveIPAddr("ip", address) + if ct.PrefEnabled { + return resolveIPAddrWithPreference(address, ct.PrefIPv6, ct.PrefExclusive) + } else { + return net.ResolveIPAddr("ip", address) + } } func (ct *ClientTransport) DialTCP(raddr *net.TCPAddr) (*net.TCPConn, error) { diff --git a/pkg/transport/resolve.go b/pkg/transport/resolve.go new file mode 100644 index 0000000..2bed90e --- /dev/null +++ b/pkg/transport/resolve.go @@ -0,0 +1,58 @@ +package transport + +import ( + "errors" + "fmt" + "net" +) + +var ( + errNoIPv4Addr = errors.New("no IPv4 address") + errNoIPv6Addr = errors.New("no IPv6 address") +) + +func resolveIPAddrWithPreference(address string, preferIPv6 bool, exclusive bool) (*net.IPAddr, error) { + ips, err := net.LookupIP(address) + if err != nil { + return nil, err + } + if preferIPv6 { + for _, ip := range ips { + if ip.To4() == nil { + return &net.IPAddr{IP: ip}, nil + } + } + if exclusive { + return nil, errNoIPv6Addr + } else { + return &net.IPAddr{IP: ips[0]}, nil + } + } else { + // prefer IPv4 + for _, ip := range ips { + if ip.To4() != nil { + return &net.IPAddr{IP: ip}, nil + } + } + if exclusive { + return nil, errNoIPv4Addr + } else { + return &net.IPAddr{IP: ips[0]}, nil + } + } +} + +func ResolvePreferenceFromString(preference string) (bool, bool, error) { + switch preference { + case "4": + return false, true, nil + case "6": + return true, true, nil + case "46": + return false, false, nil + case "64": + return true, false, nil + default: + return false, false, fmt.Errorf("%s is not a valid preference", preference) + } +} diff --git a/pkg/transport/server.go b/pkg/transport/server.go index 56bc451..bf03e0f 100644 --- a/pkg/transport/server.go +++ b/pkg/transport/server.go @@ -13,9 +13,11 @@ import ( ) type ServerTransport struct { - Dialer *net.Dialer - IPv6Only bool - SOCKS5Client *SOCKS5Client + Dialer *net.Dialer + SOCKS5Client *SOCKS5Client + PrefEnabled bool + PrefIPv6 bool + PrefExclusive bool } type PUDPConn interface { @@ -28,7 +30,7 @@ var DefaultServerTransport = &ServerTransport{ Dialer: &net.Dialer{ Timeout: 8 * time.Second, }, - IPv6Only: false, + PrefEnabled: false, } func (st *ServerTransport) quicPacketConn(proto string, laddr string, obfs obfs.Obfuscator) (net.PacketConn, error) { @@ -92,8 +94,8 @@ func (ct *ServerTransport) QUICListen(proto string, listen string, tlsConfig *tl } func (ct *ServerTransport) ResolveIPAddr(address string) (*net.IPAddr, error) { - if ct.IPv6Only { - return net.ResolveIPAddr("ip6", address) + if ct.PrefEnabled { + return resolveIPAddrWithPreference(address, ct.PrefIPv6, ct.PrefExclusive) } else { return net.ResolveIPAddr("ip", address) }