diff --git a/adapter/outbound/direct.go b/adapter/outbound/direct.go index 83ea22eb8..4b53e306c 100644 --- a/adapter/outbound/direct.go +++ b/adapter/outbound/direct.go @@ -24,7 +24,7 @@ func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, // DialUDP implements C.ProxyAdapter func (d *Direct) DialUDP(metadata *C.Metadata) (C.PacketConn, error) { - pc, err := dialer.ListenPacket("udp", "") + pc, err := dialer.ListenPacket(context.Background(), "udp", "") if err != nil { return nil, err } diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 39d1e36d0..64b431a9b 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -89,7 +89,7 @@ func (ss *ShadowSocks) DialContext(ctx context.Context, metadata *C.Metadata) (_ // DialUDP implements C.ProxyAdapter func (ss *ShadowSocks) DialUDP(metadata *C.Metadata) (C.PacketConn, error) { - pc, err := dialer.ListenPacket("udp", "") + pc, err := dialer.ListenPacket(context.Background(), "udp", "") if err != nil { return nil, err } diff --git a/adapter/outbound/shadowsocksr.go b/adapter/outbound/shadowsocksr.go index b44f46e56..9ba8bc229 100644 --- a/adapter/outbound/shadowsocksr.go +++ b/adapter/outbound/shadowsocksr.go @@ -74,7 +74,7 @@ func (ssr *ShadowSocksR) DialContext(ctx context.Context, metadata *C.Metadata) // DialUDP implements C.ProxyAdapter func (ssr *ShadowSocksR) DialUDP(metadata *C.Metadata) (C.PacketConn, error) { - pc, err := dialer.ListenPacket("udp", "") + pc, err := dialer.ListenPacket(context.Background(), "udp", "") if err != nil { return nil, err } diff --git a/adapter/outbound/socks5.go b/adapter/outbound/socks5.go index 8106e0e2e..1402c6186 100644 --- a/adapter/outbound/socks5.go +++ b/adapter/outbound/socks5.go @@ -110,7 +110,7 @@ func (ss *Socks5) DialUDP(metadata *C.Metadata) (_ C.PacketConn, err error) { return } - pc, err := dialer.ListenPacket("udp", "") + pc, err := dialer.ListenPacket(context.Background(), "udp", "") if err != nil { return } diff --git a/adapter/outboundgroup/relay.go b/adapter/outboundgroup/relay.go index 8d8128bac..78410a449 100644 --- a/adapter/outboundgroup/relay.go +++ b/adapter/outboundgroup/relay.go @@ -3,7 +3,6 @@ package outboundgroup import ( "context" "encoding/json" - "errors" "fmt" "github.com/Dreamacro/clash/adapter/outbound" @@ -21,10 +20,20 @@ type Relay struct { // DialContext implements C.ProxyAdapter func (r *Relay) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, error) { - proxies := r.proxies(metadata, true) - if len(proxies) == 0 { - return nil, errors.New("proxy does not exist") + var proxies []C.Proxy + for _, proxy := range r.proxies(metadata, true) { + if proxy.Type() != C.Direct { + proxies = append(proxies, proxy) + } } + + switch len(proxies) { + case 0: + return outbound.NewDirect().DialContext(ctx, metadata) + case 1: + return proxies[0].DialContext(ctx, metadata) + } + first := proxies[0] last := proxies[len(proxies)-1] diff --git a/adapter/provider/vehicle.go b/adapter/provider/vehicle.go index ea6a835a2..f556e69c4 100644 --- a/adapter/provider/vehicle.go +++ b/adapter/provider/vehicle.go @@ -3,6 +3,7 @@ package provider import ( "context" "io/ioutil" + "net" "net/http" "net/url" "time" @@ -71,7 +72,9 @@ func (h *HTTPVehicle) Read() ([]byte, error) { IdleConnTimeout: 90 * time.Second, TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 1 * time.Second, - DialContext: dialer.DialContext, + DialContext: func(ctx context.Context, network, address string) (net.Conn, error) { + return dialer.DialContext(ctx, network, address) + }, } client := http.Client{Transport: transport} diff --git a/component/dhcp/conn.go b/component/dhcp/conn.go new file mode 100644 index 000000000..90a9e25b5 --- /dev/null +++ b/component/dhcp/conn.go @@ -0,0 +1,18 @@ +package dhcp + +import ( + "context" + "net" + "runtime" + + "github.com/Dreamacro/clash/component/dialer" +) + +func ListenDHCPClient(ctx context.Context, ifaceName string) (net.PacketConn, error) { + listenAddr := "0.0.0.0:68" + if runtime.GOOS == "linux" || runtime.GOOS == "android" { + listenAddr = "255.255.255.255:68" + } + + return dialer.ListenPacket(ctx, "udp4", listenAddr, dialer.WithInterface(ifaceName), dialer.WithAddrReuse(true)) +} diff --git a/component/dhcp/dhcp.go b/component/dhcp/dhcp.go new file mode 100644 index 000000000..b2ca6928f --- /dev/null +++ b/component/dhcp/dhcp.go @@ -0,0 +1,94 @@ +package dhcp + +import ( + "context" + "errors" + "math/rand" + "net" + + "github.com/insomniacslk/dhcp/dhcpv4" +) + +var ( + ErrNotResponding = errors.New("DHCP not responding") + ErrNotFound = errors.New("DNS option not found") +) + +func ResolveDNSFromDHCP(context context.Context, ifaceName string) ([]net.IP, error) { + conn, err := ListenDHCPClient(context, ifaceName) + if err != nil { + return nil, err + } + defer conn.Close() + + result := make(chan []net.IP, 1) + + discovery, err := dhcpv4.NewDiscovery(randomHardware(), dhcpv4.WithBroadcast(true), dhcpv4.WithRequestedOptions(dhcpv4.OptionDomainNameServer)) + if err != nil { + return nil, err + } + + go receiveOffer(conn, discovery.TransactionID, result) + + _, err = conn.WriteTo(discovery.ToBytes(), &net.UDPAddr{IP: net.IPv4bcast, Port: 67}) + if err != nil { + return nil, err + } + + select { + case r, ok := <-result: + if !ok { + return nil, ErrNotFound + } + return r, nil + case <-context.Done(): + return nil, ErrNotResponding + } +} + +func receiveOffer(conn net.PacketConn, id dhcpv4.TransactionID, result chan<- []net.IP) { + defer close(result) + + buf := make([]byte, dhcpv4.MaxMessageSize) + + for { + n, _, err := conn.ReadFrom(buf) + if err != nil { + return + } + + pkt, err := dhcpv4.FromBytes(buf[:n]) + if err != nil { + continue + } + + if pkt.MessageType() != dhcpv4.MessageTypeOffer { + continue + } + + if pkt.TransactionID != id { + continue + } + + dns := pkt.DNS() + if len(dns) == 0 { + return + } + + result <- dns + + return + } +} + +func randomHardware() net.HardwareAddr { + addr := make(net.HardwareAddr, 6) + + addr[0] = 0xff + + for i := 1; i < len(addr); i++ { + addr[i] = byte(rand.Intn(254) + 1) + } + + return addr +} diff --git a/component/dialer/bind.go b/component/dialer/bind.go deleted file mode 100644 index 0ad520683..000000000 --- a/component/dialer/bind.go +++ /dev/null @@ -1,118 +0,0 @@ -package dialer - -import ( - "errors" - "net" - "time" - - "github.com/Dreamacro/clash/common/singledo" -) - -// In some OS, such as Windows, it takes a little longer to get interface information -var ifaceSingle = singledo.NewSingle(time.Second * 20) - -var ( - errPlatformNotSupport = errors.New("unsupport platform") -) - -func lookupTCPAddr(ip net.IP, addrs []net.Addr) (*net.TCPAddr, error) { - ipv4 := ip.To4() != nil - - for _, elm := range addrs { - addr, ok := elm.(*net.IPNet) - if !ok { - continue - } - - addrV4 := addr.IP.To4() != nil - - if addrV4 && ipv4 { - return &net.TCPAddr{IP: addr.IP, Port: 0}, nil - } else if !addrV4 && !ipv4 { - return &net.TCPAddr{IP: addr.IP, Port: 0}, nil - } - } - - return nil, ErrAddrNotFound -} - -func lookupUDPAddr(ip net.IP, addrs []net.Addr) (*net.UDPAddr, error) { - ipv4 := ip.To4() != nil - - for _, elm := range addrs { - addr, ok := elm.(*net.IPNet) - if !ok { - continue - } - - addrV4 := addr.IP.To4() != nil - - if addrV4 && ipv4 { - return &net.UDPAddr{IP: addr.IP, Port: 0}, nil - } else if !addrV4 && !ipv4 { - return &net.UDPAddr{IP: addr.IP, Port: 0}, nil - } - } - - return nil, ErrAddrNotFound -} - -func fallbackBindToDialer(dialer *net.Dialer, network string, ip net.IP, name string) error { - if !ip.IsGlobalUnicast() { - return nil - } - - iface, err, _ := ifaceSingle.Do(func() (interface{}, error) { - return net.InterfaceByName(name) - }) - if err != nil { - return err - } - - addrs, err := iface.(*net.Interface).Addrs() - if err != nil { - return err - } - - switch network { - case "tcp", "tcp4", "tcp6": - if addr, err := lookupTCPAddr(ip, addrs); err == nil { - dialer.LocalAddr = addr - } else { - return err - } - case "udp", "udp4", "udp6": - if addr, err := lookupUDPAddr(ip, addrs); err == nil { - dialer.LocalAddr = addr - } else { - return err - } - } - - return nil -} - -func fallbackBindToListenConfig(name string) (string, error) { - iface, err, _ := ifaceSingle.Do(func() (interface{}, error) { - return net.InterfaceByName(name) - }) - if err != nil { - return "", err - } - - addrs, err := iface.(*net.Interface).Addrs() - if err != nil { - return "", err - } - - for _, elm := range addrs { - addr, ok := elm.(*net.IPNet) - if !ok || addr.IP.To4() == nil { - continue - } - - return net.JoinHostPort(addr.IP.String(), "0"), nil - } - - return "", ErrAddrNotFound -} diff --git a/component/dialer/bind_darwin.go b/component/dialer/bind_darwin.go index 0ce505412..b3ae9d815 100644 --- a/component/dialer/bind_darwin.go +++ b/component/dialer/bind_darwin.go @@ -3,51 +3,57 @@ package dialer import ( "net" "syscall" + + "golang.org/x/sys/unix" + + "github.com/Dreamacro/clash/component/iface" ) type controlFn = func(network, address string, c syscall.RawConn) error -func bindControl(ifaceIdx int) controlFn { - return func(network, address string, c syscall.RawConn) error { +func bindControl(ifaceIdx int, chain controlFn) controlFn { + return func(network, address string, c syscall.RawConn) (err error) { + defer func() { + if err == nil && chain != nil { + err = chain(network, address, c) + } + }() + ipStr, _, err := net.SplitHostPort(address) if err == nil { ip := net.ParseIP(ipStr) if ip != nil && !ip.IsGlobalUnicast() { - return nil + return } } return c.Control(func(fd uintptr) { switch network { case "tcp4", "udp4": - syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_BOUND_IF, ifaceIdx) + unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_BOUND_IF, ifaceIdx) case "tcp6", "udp6": - syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_BOUND_IF, ifaceIdx) + unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_BOUND_IF, ifaceIdx) } }) } } -func bindIfaceToDialer(dialer *net.Dialer, ifaceName string) error { - iface, err, _ := ifaceSingle.Do(func() (interface{}, error) { - return net.InterfaceByName(ifaceName) - }) +func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, _ string, _ net.IP) error { + ifaceObj, err := iface.ResolveInterface(ifaceName) if err != nil { return err } - dialer.Control = bindControl(iface.(*net.Interface).Index) + dialer.Control = bindControl(ifaceObj.Index, dialer.Control) return nil } -func bindIfaceToListenConfig(lc *net.ListenConfig, ifaceName string) error { - iface, err, _ := ifaceSingle.Do(func() (interface{}, error) { - return net.InterfaceByName(ifaceName) - }) +func bindIfaceToListenConfig(ifaceName string, lc *net.ListenConfig, _, address string) (string, error) { + ifaceObj, err := iface.ResolveInterface(ifaceName) if err != nil { - return err + return "", err } - lc.Control = bindControl(iface.(*net.Interface).Index) - return nil + lc.Control = bindControl(ifaceObj.Index, lc.Control) + return address, nil } diff --git a/component/dialer/bind_linux.go b/component/dialer/bind_linux.go index 7e9d308dd..ae772a71e 100644 --- a/component/dialer/bind_linux.go +++ b/component/dialer/bind_linux.go @@ -3,34 +3,42 @@ package dialer import ( "net" "syscall" + + "golang.org/x/sys/unix" ) type controlFn = func(network, address string, c syscall.RawConn) error -func bindControl(ifaceName string) controlFn { - return func(network, address string, c syscall.RawConn) error { +func bindControl(ifaceName string, chain controlFn) controlFn { + return func(network, address string, c syscall.RawConn) (err error) { + defer func() { + if err == nil && chain != nil { + err = chain(network, address, c) + } + }() + ipStr, _, err := net.SplitHostPort(address) if err == nil { ip := net.ParseIP(ipStr) if ip != nil && !ip.IsGlobalUnicast() { - return nil + return } } return c.Control(func(fd uintptr) { - syscall.BindToDevice(int(fd), ifaceName) + unix.BindToDevice(int(fd), ifaceName) }) } } -func bindIfaceToDialer(dialer *net.Dialer, ifaceName string) error { - dialer.Control = bindControl(ifaceName) +func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, _ string, _ net.IP) error { + dialer.Control = bindControl(ifaceName, dialer.Control) return nil } -func bindIfaceToListenConfig(lc *net.ListenConfig, ifaceName string) error { - lc.Control = bindControl(ifaceName) +func bindIfaceToListenConfig(ifaceName string, lc *net.ListenConfig, _, address string) (string, error) { + lc.Control = bindControl(ifaceName, lc.Control) - return nil + return address, nil } diff --git a/component/dialer/bind_others.go b/component/dialer/bind_others.go index e09b5ff84..7f1923aa2 100644 --- a/component/dialer/bind_others.go +++ b/component/dialer/bind_others.go @@ -3,12 +3,91 @@ package dialer -import "net" +import ( + "net" + "strconv" + "strings" -func bindIfaceToDialer(dialer *net.Dialer, ifaceName string) error { - return errPlatformNotSupport + "github.com/Dreamacro/clash/component/iface" +) + +func lookupLocalAddr(ifaceName string, network string, destination net.IP, port int) (net.Addr, error) { + ifaceObj, err := iface.ResolveInterface(ifaceName) + if err != nil { + return nil, err + } + + var addr *net.IPNet + switch network { + case "udp4", "tcp4": + addr, err = ifaceObj.PickIPv4Addr(destination) + case "tcp6", "udp6": + addr, err = ifaceObj.PickIPv6Addr(destination) + default: + if destination != nil { + if destination.To4() != nil { + addr, err = ifaceObj.PickIPv4Addr(destination) + } else { + addr, err = ifaceObj.PickIPv6Addr(destination) + } + } else { + addr, err = ifaceObj.PickIPv4Addr(destination) + } + } + if err != nil { + return nil, err + } + + if strings.HasPrefix(network, "tcp") { + return &net.TCPAddr{ + IP: addr.IP, + Port: port, + }, nil + } else if strings.HasPrefix(network, "udp") { + return &net.UDPAddr{ + IP: addr.IP, + Port: port, + }, nil + } + + return nil, iface.ErrAddrNotFound } -func bindIfaceToListenConfig(lc *net.ListenConfig, ifaceName string) error { - return errPlatformNotSupport +func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, network string, destination net.IP) error { + if !destination.IsGlobalUnicast() { + return nil + } + + local := 0 + if dialer.LocalAddr != nil { + _, port, err := net.SplitHostPort(dialer.LocalAddr.String()) + if err == nil { + local, _ = strconv.Atoi(port) + } + } + + addr, err := lookupLocalAddr(ifaceName, network, destination, local) + if err != nil { + return err + } + + dialer.LocalAddr = addr + + return nil +} + +func bindIfaceToListenConfig(ifaceName string, _ *net.ListenConfig, network, address string) (string, error) { + _, port, err := net.SplitHostPort(address) + if err != nil { + port = "0" + } + + local, _ := strconv.Atoi(port) + + addr, err := lookupLocalAddr(ifaceName, network, nil, local) + if err != nil { + return "", err + } + + return addr.String(), nil } diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index be26681a7..75bbb8687 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -8,22 +8,7 @@ import ( "github.com/Dreamacro/clash/component/resolver" ) -func Dialer() (*net.Dialer, error) { - dialer := &net.Dialer{} - if DialerHook != nil { - if err := DialerHook(dialer); err != nil { - return nil, err - } - } - - return dialer, nil -} - -func Dial(network, address string) (net.Conn, error) { - return DialContext(context.Background(), network, address) -} - -func DialContext(ctx context.Context, network, address string) (net.Conn, error) { +func DialContext(ctx context.Context, network, address string, options ...Option) (net.Conn, error) { switch network { case "tcp4", "tcp6", "udp4", "udp6": host, port, err := net.SplitHostPort(address) @@ -31,11 +16,6 @@ func DialContext(ctx context.Context, network, address string) (net.Conn, error) return nil, err } - dialer, err := Dialer() - if err != nil { - return nil, err - } - var ip net.IP switch network { case "tcp4", "udp4": @@ -43,38 +23,70 @@ func DialContext(ctx context.Context, network, address string) (net.Conn, error) default: ip, err = resolver.ResolveIPv6(host) } - if err != nil { return nil, err } - if DialHook != nil { - if err := DialHook(dialer, network, ip); err != nil { - return nil, err - } - } - return dialer.DialContext(ctx, network, net.JoinHostPort(ip.String(), port)) + return dialContext(ctx, network, ip, port, options) case "tcp", "udp": - return dualStackDialContext(ctx, network, address) + return dualStackDialContext(ctx, network, address, options) default: return nil, errors.New("network invalid") } } -func ListenPacket(network, address string) (net.PacketConn, error) { - cfg := &net.ListenConfig{} - if ListenPacketHook != nil { - var err error - address, err = ListenPacketHook(cfg, address) +func ListenPacket(ctx context.Context, network, address string, options ...Option) (net.PacketConn, error) { + cfg := &config{} + + if !cfg.skipDefault { + for _, o := range DefaultOptions { + o(cfg) + } + } + + for _, o := range options { + o(cfg) + } + + lc := &net.ListenConfig{} + if cfg.interfaceName != "" { + addr, err := bindIfaceToListenConfig(cfg.interfaceName, lc, network, address) if err != nil { return nil, err } + address = addr + } + if cfg.addrReuse { + addrReuseToListenConfig(lc) + } + + return lc.ListenPacket(ctx, network, address) +} + +func dialContext(ctx context.Context, network string, destination net.IP, port string, options []Option) (net.Conn, error) { + opt := &config{} + + if !opt.skipDefault { + for _, o := range DefaultOptions { + o(opt) + } + } + + for _, o := range options { + o(opt) + } + + dialer := &net.Dialer{} + if opt.interfaceName != "" { + if err := bindIfaceToDialer(opt.interfaceName, dialer, network, destination); err != nil { + return nil, err + } } - return cfg.ListenPacket(context.Background(), network, address) + return dialer.DialContext(ctx, network, net.JoinHostPort(destination.String(), port)) } -func dualStackDialContext(ctx context.Context, network, address string) (net.Conn, error) { +func dualStackDialContext(ctx context.Context, network, address string, options []Option) (net.Conn, error) { host, port, err := net.SplitHostPort(address) if err != nil { return nil, err @@ -105,12 +117,6 @@ func dualStackDialContext(ctx context.Context, network, address string) (net.Con } }() - dialer, err := Dialer() - if err != nil { - result.error = err - return - } - var ip net.IP if ipv6 { ip, result.error = resolver.ResolveIPv6(host) @@ -122,12 +128,7 @@ func dualStackDialContext(ctx context.Context, network, address string) (net.Con } result.resolved = true - if DialHook != nil { - if result.error = DialHook(dialer, network, ip); result.error != nil { - return - } - } - result.Conn, result.error = dialer.DialContext(ctx, network, net.JoinHostPort(ip.String(), port)) + result.Conn, result.error = dialContext(ctx, network, ip, port, options) } go startRacer(ctx, network+"4", host, false) diff --git a/component/dialer/hook.go b/component/dialer/hook.go deleted file mode 100644 index 356e4b25f..000000000 --- a/component/dialer/hook.go +++ /dev/null @@ -1,43 +0,0 @@ -package dialer - -import ( - "errors" - "net" -) - -type DialerHookFunc = func(dialer *net.Dialer) error -type DialHookFunc = func(dialer *net.Dialer, network string, ip net.IP) error -type ListenPacketHookFunc = func(lc *net.ListenConfig, address string) (string, error) - -var ( - DialerHook DialerHookFunc - DialHook DialHookFunc - ListenPacketHook ListenPacketHookFunc -) - -var ( - ErrAddrNotFound = errors.New("addr not found") - ErrNetworkNotSupport = errors.New("network not support") -) - -func ListenPacketWithInterface(name string) ListenPacketHookFunc { - return func(lc *net.ListenConfig, address string) (string, error) { - err := bindIfaceToListenConfig(lc, name) - if err == errPlatformNotSupport { - address, err = fallbackBindToListenConfig(name) - } - - return address, err - } -} - -func DialerWithInterface(name string) DialHookFunc { - return func(dialer *net.Dialer, network string, ip net.IP) error { - err := bindIfaceToDialer(dialer, name) - if err == errPlatformNotSupport { - err = fallbackBindToDialer(dialer, network, ip, name) - } - - return err - } -} diff --git a/component/dialer/options.go b/component/dialer/options.go new file mode 100644 index 000000000..4b5d64c0e --- /dev/null +++ b/component/dialer/options.go @@ -0,0 +1,31 @@ +package dialer + +var ( + DefaultOptions []Option +) + +type config struct { + skipDefault bool + interfaceName string + addrReuse bool +} + +type Option func(opt *config) + +func WithInterface(name string) Option { + return func(opt *config) { + opt.interfaceName = name + } +} + +func WithAddrReuse(reuse bool) Option { + return func(opt *config) { + opt.addrReuse = reuse + } +} + +func WithSkipDefault(skip bool) Option { + return func(opt *config) { + opt.skipDefault = skip + } +} diff --git a/component/dialer/reuse_others.go b/component/dialer/reuse_others.go new file mode 100644 index 000000000..b76213a7f --- /dev/null +++ b/component/dialer/reuse_others.go @@ -0,0 +1,10 @@ +//go:build !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows +// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows + +package dialer + +import ( + "net" +) + +func addrReuseToListenConfig(*net.ListenConfig) {} diff --git a/component/dialer/reuse_unix.go b/component/dialer/reuse_unix.go new file mode 100644 index 000000000..d5f43d8fe --- /dev/null +++ b/component/dialer/reuse_unix.go @@ -0,0 +1,28 @@ +//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +// +build darwin dragonfly freebsd linux netbsd openbsd solaris + +package dialer + +import ( + "net" + "syscall" + + "golang.org/x/sys/unix" +) + +func addrReuseToListenConfig(lc *net.ListenConfig) { + chain := lc.Control + + lc.Control = func(network, address string, c syscall.RawConn) (err error) { + defer func() { + if err == nil && chain != nil { + err = chain(network, address, c) + } + }() + + return c.Control(func(fd uintptr) { + unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1) + unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1) + }) + } +} diff --git a/component/dialer/reuse_windows.go b/component/dialer/reuse_windows.go new file mode 100644 index 000000000..77fcf7ab2 --- /dev/null +++ b/component/dialer/reuse_windows.go @@ -0,0 +1,24 @@ +package dialer + +import ( + "net" + "syscall" + + "golang.org/x/sys/windows" +) + +func addrReuseToListenConfig(lc *net.ListenConfig) { + chain := lc.Control + + lc.Control = func(network, address string, c syscall.RawConn) (err error) { + defer func() { + if err == nil && chain != nil { + err = chain(network, address, c) + } + }() + + return c.Control(func(fd uintptr) { + windows.SetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_REUSEADDR, 1) + }) + } +} diff --git a/component/iface/iface.go b/component/iface/iface.go new file mode 100644 index 000000000..30c6a6bcb --- /dev/null +++ b/component/iface/iface.go @@ -0,0 +1,113 @@ +package iface + +import ( + "errors" + "net" + "time" + + "github.com/Dreamacro/clash/common/singledo" +) + +type Interface struct { + Index int + Name string + Addrs []*net.IPNet + HardwareAddr net.HardwareAddr +} + +var ErrIfaceNotFound = errors.New("interface not found") +var ErrAddrNotFound = errors.New("addr not found") + +var interfaces = singledo.NewSingle(time.Second * 20) + +func ResolveInterface(name string) (*Interface, error) { + value, err, _ := interfaces.Do(func() (interface{}, error) { + ifaces, err := net.Interfaces() + if err != nil { + return nil, err + } + + r := map[string]*Interface{} + + for _, iface := range ifaces { + addrs, err := iface.Addrs() + if err != nil { + continue + } + + ipNets := make([]*net.IPNet, 0, len(addrs)) + for _, addr := range addrs { + ipNet := addr.(*net.IPNet) + if v4 := ipNet.IP.To4(); v4 != nil { + ipNet.IP = v4 + } + + ipNets = append(ipNets, ipNet) + } + + r[iface.Name] = &Interface{ + Index: iface.Index, + Name: iface.Name, + Addrs: ipNets, + HardwareAddr: iface.HardwareAddr, + } + } + + return r, nil + }) + if err != nil { + return nil, err + } + + ifaces := value.(map[string]*Interface) + iface, ok := ifaces[name] + if !ok { + return nil, ErrIfaceNotFound + } + + return iface, nil +} + +func FlushCache() { + interfaces.Reset() +} + +func (iface *Interface) PickIPv4Addr(destination net.IP) (*net.IPNet, error) { + return iface.pickIPAddr(destination, func(addr *net.IPNet) bool { + return addr.IP.To4() != nil + }) +} + +func (iface *Interface) PickIPv6Addr(destination net.IP) (*net.IPNet, error) { + return iface.pickIPAddr(destination, func(addr *net.IPNet) bool { + return addr.IP.To4() == nil + }) +} + +func (iface *Interface) pickIPAddr(destination net.IP, accept func(addr *net.IPNet) bool) (*net.IPNet, error) { + var fallback *net.IPNet + + for _, addr := range iface.Addrs { + if !accept(addr) { + continue + } + + if fallback == nil && !addr.IP.IsLinkLocalUnicast() { + fallback = addr + + if destination == nil { + break + } + } + + if destination != nil && addr.Contains(destination) { + return addr, nil + } + } + + if fallback == nil { + return nil, ErrAddrNotFound + } + + return fallback, nil +} diff --git a/config/config.go b/config/config.go index 85b7096b0..d3f8c4ee0 100644 --- a/config/config.go +++ b/config/config.go @@ -516,6 +516,9 @@ func parseNameServer(servers []string) ([]dns.NameServer, error) { clearURL := url.URL{Scheme: "https", Host: u.Host, Path: u.Path} addr = clearURL.String() dnsNetType = "https" // DNS over HTTPS + case "dhcp": + addr = u.Host + dnsNetType = "dhcp" // UDP from DHCP default: return nil, fmt.Errorf("DNS NameServer[%d] unsupport scheme: %s", idx, u.Scheme) } diff --git a/dns/client.go b/dns/client.go index 405b3a989..d386ed4c3 100644 --- a/dns/client.go +++ b/dns/client.go @@ -2,6 +2,7 @@ package dns import ( "context" + "crypto/tls" "fmt" "net" "strings" @@ -34,22 +35,16 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err } } - d, err := dialer.Dialer() + network := "udp" + if strings.HasPrefix(c.Client.Net, "tcp") { + network = "tcp" + } + + conn, err := dialer.DialContext(ctx, network, net.JoinHostPort(ip.String(), c.port)) if err != nil { return nil, err } - - if ip != nil && ip.IsGlobalUnicast() && dialer.DialHook != nil { - network := "udp" - if strings.HasPrefix(c.Client.Net, "tcp") { - network = "tcp" - } - if err := dialer.DialHook(d, network, ip); err != nil { - return nil, err - } - } - - c.Client.Dialer = d + defer conn.Close() // miekg/dns ExchangeContext doesn't respond to context cancel. // this is a workaround @@ -59,7 +54,17 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err } ch := make(chan result, 1) go func() { - msg, _, err := c.Client.Exchange(m, net.JoinHostPort(ip.String(), c.port)) + if strings.HasSuffix(c.Client.Net, "tls") { + conn = tls.Client(conn, c.Client.TLSConfig) + } + + msg, _, err = c.Client.ExchangeWithConn(m, &D.Conn{ + Conn: conn, + UDPSize: c.Client.UDPSize, + TsigSecret: c.Client.TsigSecret, + TsigProvider: c.Client.TsigProvider, + }) + ch <- result{msg, err} }() diff --git a/dns/dhcp.go b/dns/dhcp.go new file mode 100644 index 000000000..94f8a36ce --- /dev/null +++ b/dns/dhcp.go @@ -0,0 +1,144 @@ +package dns + +import ( + "bytes" + "context" + "net" + "sync" + "time" + + "github.com/Dreamacro/clash/component/dhcp" + "github.com/Dreamacro/clash/component/iface" + "github.com/Dreamacro/clash/component/resolver" + + D "github.com/miekg/dns" +) + +const ( + IfaceTTL = time.Second * 20 + DHCPTTL = time.Hour + DHCPTimeout = time.Minute +) + +type dhcpClient struct { + ifaceName string + + lock sync.Mutex + ifaceInvalidate time.Time + dnsInvalidate time.Time + + ifaceAddr *net.IPNet + done chan struct{} + resolver *Resolver + err error +} + +func (d *dhcpClient) Exchange(m *D.Msg) (msg *D.Msg, err error) { + ctx, cancel := context.WithTimeout(context.Background(), resolver.DefaultDNSTimeout) + defer cancel() + + return d.ExchangeContext(ctx, m) +} + +func (d *dhcpClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { + res, err := d.resolve(ctx) + if err != nil { + return nil, err + } + + return res.ExchangeContext(ctx, m) +} + +func (d *dhcpClient) resolve(ctx context.Context) (*Resolver, error) { + d.lock.Lock() + + invalidated, err := d.invalidate() + if err != nil { + d.err = err + } else if invalidated { + done := make(chan struct{}) + + d.done = done + + go func() { + ctx, cancel := context.WithTimeout(context.Background(), DHCPTimeout) + defer cancel() + + var res *Resolver + dns, err := dhcp.ResolveDNSFromDHCP(ctx, d.ifaceName) + if err == nil { + nameserver := make([]NameServer, 0, len(dns)) + for _, d := range dns { + nameserver = append(nameserver, NameServer{Addr: net.JoinHostPort(d.String(), "53")}) + } + + res = NewResolver(Config{ + Main: nameserver, + }) + } + + d.lock.Lock() + defer d.lock.Unlock() + + close(done) + + d.done = nil + d.resolver = res + d.err = err + }() + } + + d.lock.Unlock() + + for { + d.lock.Lock() + + res, err, done := d.resolver, d.err, d.done + + d.lock.Unlock() + + // initializing + if res == nil && err == nil { + select { + case <-done: + continue + case <-ctx.Done(): + return nil, ctx.Err() + } + } + + // dirty return + return res, err + } +} + +func (d *dhcpClient) invalidate() (bool, error) { + if time.Now().Before(d.ifaceInvalidate) { + return false, nil + } + + d.ifaceInvalidate = time.Now().Add(IfaceTTL) + + ifaceObj, err := iface.ResolveInterface(d.ifaceName) + if err != nil { + return false, err + } + + addr, err := ifaceObj.PickIPv4Addr(nil) + if err != nil { + return false, err + } + + if time.Now().Before(d.dnsInvalidate) && d.ifaceAddr.IP.Equal(addr.IP) && bytes.Equal(d.ifaceAddr.Mask, addr.Mask) { + return false, nil + } + + d.dnsInvalidate = time.Now().Add(DHCPTTL) + d.ifaceAddr = addr + + return d.done == nil, nil +} + +func newDHCPClient(ifaceName string) *dhcpClient { + return &dhcpClient{ifaceName: ifaceName} +} diff --git a/dns/resolver.go b/dns/resolver.go index 56b5e3268..914f1418e 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -87,6 +87,11 @@ func (r *Resolver) shouldIPFallback(ip net.IP) bool { // Exchange a batch of dns request, and it use cache func (r *Resolver) Exchange(m *D.Msg) (msg *D.Msg, err error) { + return r.ExchangeContext(context.Background(), m) +} + +// ExchangeContext a batch of dns request with context.Context, and it use cache +func (r *Resolver) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { if len(m.Question) == 0 { return nil, errors.New("should have one question at least") } @@ -98,17 +103,17 @@ func (r *Resolver) Exchange(m *D.Msg) (msg *D.Msg, err error) { msg = cache.(*D.Msg).Copy() if expireTime.Before(now) { setMsgTTL(msg, uint32(1)) // Continue fetch - go r.exchangeWithoutCache(m) + go r.exchangeWithoutCache(ctx, m) } else { setMsgTTL(msg, uint32(time.Until(expireTime).Seconds())) } return } - return r.exchangeWithoutCache(m) + return r.exchangeWithoutCache(ctx, m) } // ExchangeWithoutCache a batch of dns request, and it do NOT GET from cache -func (r *Resolver) exchangeWithoutCache(m *D.Msg) (msg *D.Msg, err error) { +func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { q := m.Question[0] ret, err, shared := r.group.Do(q.String(), func() (result interface{}, err error) { @@ -124,13 +129,13 @@ func (r *Resolver) exchangeWithoutCache(m *D.Msg) (msg *D.Msg, err error) { isIPReq := isIPRequest(q) if isIPReq { - return r.ipExchange(m) + return r.ipExchange(ctx, m) } if matched := r.matchPolicy(m); len(matched) != 0 { - return r.batchExchange(matched, m) + return r.batchExchange(ctx, matched, m) } - return r.batchExchange(r.main, m) + return r.batchExchange(ctx, r.main, m) }) if err == nil { @@ -143,8 +148,8 @@ func (r *Resolver) exchangeWithoutCache(m *D.Msg) (msg *D.Msg, err error) { return } -func (r *Resolver) batchExchange(clients []dnsClient, m *D.Msg) (msg *D.Msg, err error) { - fast, ctx := picker.WithTimeout(context.Background(), resolver.DefaultDNSTimeout) +func (r *Resolver) batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, err error) { + fast, ctx := picker.WithTimeout(ctx, resolver.DefaultDNSTimeout) for _, client := range clients { r := client fast.Go(func() (interface{}, error) { @@ -209,21 +214,21 @@ func (r *Resolver) shouldOnlyQueryFallback(m *D.Msg) bool { return false } -func (r *Resolver) ipExchange(m *D.Msg) (msg *D.Msg, err error) { +func (r *Resolver) ipExchange(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { if matched := r.matchPolicy(m); len(matched) != 0 { - res := <-r.asyncExchange(matched, m) + res := <-r.asyncExchange(ctx, matched, m) return res.Msg, res.Error } onlyFallback := r.shouldOnlyQueryFallback(m) if onlyFallback { - res := <-r.asyncExchange(r.fallback, m) + res := <-r.asyncExchange(ctx, r.fallback, m) return res.Msg, res.Error } - msgCh := r.asyncExchange(r.main, m) + msgCh := r.asyncExchange(ctx, r.main, m) if r.fallback == nil { // directly return if no fallback servers are available res := <-msgCh @@ -231,7 +236,7 @@ func (r *Resolver) ipExchange(m *D.Msg) (msg *D.Msg, err error) { return } - fallbackMsg := r.asyncExchange(r.fallback, m) + fallbackMsg := r.asyncExchange(ctx, r.fallback, m) res := <-msgCh if res.Error == nil { if ips := msgToIP(res.Msg); len(ips) != 0 { @@ -287,10 +292,10 @@ func (r *Resolver) msgToDomain(msg *D.Msg) string { return "" } -func (r *Resolver) asyncExchange(client []dnsClient, msg *D.Msg) <-chan *result { +func (r *Resolver) asyncExchange(ctx context.Context, client []dnsClient, msg *D.Msg) <-chan *result { ch := make(chan *result, 1) go func() { - res, err := r.batchExchange(client, msg) + res, err := r.batchExchange(ctx, client, msg) ch <- &result{Msg: res, Error: err} }() return ch diff --git a/dns/util.go b/dns/util.go index e56aaeb5a..0c3f42e59 100644 --- a/dns/util.go +++ b/dns/util.go @@ -117,9 +117,13 @@ func isIPRequest(q D.Question) bool { func transform(servers []NameServer, resolver *Resolver) []dnsClient { ret := []dnsClient{} for _, s := range servers { - if s.Net == "https" { + switch s.Net { + case "https": ret = append(ret, newDoHClient(s.Addr, resolver)) continue + case "dhcp": + ret = append(ret, newDHCPClient(s.Addr)) + continue } host, port, _ := net.SplitHostPort(s.Addr) diff --git a/go.mod b/go.mod index b5f9ba6de..9ca422ba3 100644 --- a/go.mod +++ b/go.mod @@ -4,11 +4,12 @@ go 1.17 require ( github.com/Dreamacro/go-shadowsocks2 v0.1.7 - github.com/go-chi/chi/v5 v5.0.3 + github.com/go-chi/chi/v5 v5.0.4 github.com/go-chi/cors v1.2.0 github.com/go-chi/render v1.0.1 github.com/gofrs/uuid v4.0.0+incompatible github.com/gorilla/websocket v1.4.2 + github.com/insomniacslk/dhcp v0.0.0-20210827173440-b95caade3eac github.com/kr328/tun2socket v0.0.0-20210412191540-3d56c47e2d99 github.com/miekg/dns v1.1.43 github.com/sirupsen/logrus v1.8.1 @@ -16,9 +17,9 @@ require ( github.com/xtls/go v0.0.0-20201118062508-3632bf3b7499 go.uber.org/atomic v1.9.0 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 - golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d + golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2 + golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34 google.golang.org/protobuf v1.27.1 gopkg.in/yaml.v2 v2.4.0 gvisor.dev/gvisor v0.0.0-20210519191755-bd7eb2c99ba9 @@ -28,6 +29,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/google/btree v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect golang.org/x/text v0.3.6 // indirect golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect diff --git a/go.sum b/go.sum index 3e3099aa6..a955a47bc 100644 --- a/go.sum +++ b/go.sum @@ -104,10 +104,11 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-chi/chi/v5 v5.0.3 h1:khYQBdPivkYG1s1TAzDQG1f6eX4kD2TItYVZexL5rS4= -github.com/go-chi/chi/v5 v5.0.3/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/chi/v5 v5.0.4 h1:5e494iHzsYBiyXQAHHuI4tyJS9M3V84OuX3ufIIGHFo= +github.com/go-chi/chi/v5 v5.0.4/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.0 h1:tV1g1XENQ8ku4Bq3K9ub2AtgG+p16SmzeMSGTwrOKdE= github.com/go-chi/cors v1.2.0/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8= @@ -195,6 +196,7 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= @@ -203,14 +205,22 @@ github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/insomniacslk/dhcp v0.0.0-20210827173440-b95caade3eac h1:IO6EfdRnPhxgKOsk9DbewdtQZHKZKnGlW7QCUttvNys= +github.com/insomniacslk/dhcp v0.0.0-20210827173440-b95caade3eac/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= +github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= +github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= +github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= +github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg= github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -225,6 +235,13 @@ github.com/kr328/tun2socket v0.0.0-20210412191540-3d56c47e2d99 h1:dkEFEnGUg2z/FA github.com/kr328/tun2socket v0.0.0-20210412191540-3d56c47e2d99/go.mod h1:FWfSixjrLgtK+dHkDoN6lHMNhvER24gnjUZd/wt8Z9o= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a/go.mod h1:M1qoD/MqPgTZIk0EWKB38wE28ACRfVcn+cU08jyArI0= +github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y= +github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= +github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= +github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= +github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= +github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= +github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -265,6 +282,8 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -277,9 +296,12 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA= +github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= @@ -352,13 +374,16 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -373,12 +398,14 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d h1:LO7XpTYMwTqxjLcGWPijK3vRXg1aWdlNOVOHRq45d7c= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f h1:w6wWR0H+nyVpbSAQbzVEIACVyr/h8l/BEkY6Sokc7Eg= +golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -405,16 +432,21 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -440,14 +472,17 @@ golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2 h1:c8PlLMqBbOHoqtjteWm5/kbe6rNY2pbRfbIMVnepueo= -golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34 h1:GkvMjFtXUmahfDtashnc1mnrCtuBVcwse5QV2lUk/tI= +golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -470,6 +505,7 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 46a6dec67..7a50b9402 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -14,6 +14,7 @@ import ( "github.com/Dreamacro/clash/adapter/outboundgroup" "github.com/Dreamacro/clash/component/auth" "github.com/Dreamacro/clash/component/dialer" + "github.com/Dreamacro/clash/component/iface" "github.com/Dreamacro/clash/component/profile" "github.com/Dreamacro/clash/component/profile/cachefile" "github.com/Dreamacro/clash/component/resolver" @@ -195,13 +196,13 @@ func updateGeneral(general *config.General, force bool) { } if general.Interface != "" { - dialer.DialHook = dialer.DialerWithInterface(general.Interface) - dialer.ListenPacketHook = dialer.ListenPacketWithInterface(general.Interface) + dialer.DefaultOptions = []dialer.Option{dialer.WithInterface(general.Interface)} } else { - dialer.DialHook = nil - dialer.ListenPacketHook = nil + dialer.DefaultOptions = nil } + iface.FlushCache() + if !force { log.SetLevel(general.LogLevel) return diff --git a/test/clash_test.go b/test/clash_test.go index 366cc260b..4011b04a4 100644 --- a/test/clash_test.go +++ b/test/clash_test.go @@ -27,11 +27,12 @@ import ( ) const ( - ImageShadowsocks = "mritd/shadowsocks:latest" - ImageVmess = "v2fly/v2fly-core:latest" - ImageTrojan = "trojangfw/trojan:latest" - ImageSnell = "icpz/snell-server:latest" - ImageXray = "teddysun/xray:latest" + ImageShadowsocks = "mritd/shadowsocks:latest" + ImageShadowsocksRust = "ghcr.io/shadowsocks/ssserver-rust:latest" + ImageVmess = "v2fly/v2fly-core:latest" + ImageTrojan = "trojangfw/trojan:latest" + ImageSnell = "icpz/snell-server:latest" + ImageXray = "teddysun/xray:latest" ) var ( @@ -209,10 +210,11 @@ func newLargeDataPair() (chan hashPair, chan hashPair, func(t *testing.T) error) func testPingPongWithSocksPort(t *testing.T, port int) { pingCh, pongCh, test := newPingPongPair() go func() { - l, err := net.Listen("tcp", ":10001") + l, err := Listen("tcp", ":10001") if err != nil { assert.FailNow(t, err.Error()) } + defer l.Close() c, err := l.Accept() if err != nil { @@ -228,7 +230,6 @@ func testPingPongWithSocksPort(t *testing.T, port int) { if _, err := c.Write([]byte("pong")); err != nil { assert.FailNow(t, err.Error()) } - l.Close() }() go func() { @@ -236,6 +237,7 @@ func testPingPongWithSocksPort(t *testing.T, port int) { if err != nil { assert.FailNow(t, err.Error()) } + defer c.Close() if _, err := socks5.ClientHandshake(c, socks5.ParseAddr("127.0.0.1:10001"), socks5.CmdConnect, nil); err != nil { assert.FailNow(t, err.Error()) @@ -251,14 +253,13 @@ func testPingPongWithSocksPort(t *testing.T, port int) { } pongCh <- buf - c.Close() }() test(t) } func testPingPongWithConn(t *testing.T, c net.Conn) error { - l, err := net.Listen("tcp", ":10001") + l, err := Listen("tcp", ":10001") if err != nil { return err } @@ -299,7 +300,7 @@ func testPingPongWithConn(t *testing.T, c net.Conn) error { } func testPingPongWithPacketConn(t *testing.T, pc net.PacketConn) error { - l, err := net.ListenPacket("udp", ":10001") + l, err := ListenPacket("udp", ":10001") if err != nil { return err } @@ -344,7 +345,7 @@ type hashPair struct { } func testLargeDataWithConn(t *testing.T, c net.Conn) error { - l, err := net.Listen("tcp", ":10001") + l, err := Listen("tcp", ":10001") if err != nil { return err } @@ -379,6 +380,7 @@ func testLargeDataWithConn(t *testing.T, c net.Conn) error { if err != nil { return } + defer c.Close() hashMap := map[int][]byte{} buf := make([]byte, chunkSize) @@ -437,7 +439,7 @@ func testLargeDataWithConn(t *testing.T, c net.Conn) error { } func testLargeDataWithPacketConn(t *testing.T, pc net.PacketConn) error { - l, err := net.ListenPacket("udp", ":10001") + l, err := ListenPacket("udp", ":10001") if err != nil { return err } @@ -621,23 +623,25 @@ func testSuit(t *testing.T, proxy C.ProxyAdapter) { } func benchmarkProxy(b *testing.B, proxy C.ProxyAdapter) { - l, err := net.Listen("tcp", ":10001") + l, err := Listen("tcp", ":10001") if err != nil { assert.FailNow(b, err.Error()) } + defer l.Close() go func() { c, err := l.Accept() if err != nil { assert.FailNow(b, err.Error()) } + defer c.Close() io.Copy(io.Discard, c) - c.Close() }() chunkSize := int64(16 * 1024) chunk := make([]byte, chunkSize) + rand.Read(chunk) conn, err := proxy.DialContext(context.Background(), &C.Metadata{ Host: localIP.String(), DstPort: "10001", diff --git a/test/go.mod b/test/go.mod index 5d86eda93..990edb6c5 100644 --- a/test/go.mod +++ b/test/go.mod @@ -3,14 +3,16 @@ module clash-test go 1.17 require ( - github.com/Dreamacro/clash v1.6.6-0.20210821162529-0267b2efad9a + github.com/Dreamacro/clash v1.6.6-0.20210905062555-c7b718f6512d github.com/docker/docker v20.10.8+incompatible github.com/docker/go-connections v0.4.0 github.com/miekg/dns v1.1.43 github.com/stretchr/testify v1.7.0 - golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d + golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f ) +replace github.com/Dreamacro/clash => ../ + require ( github.com/Dreamacro/go-shadowsocks2 v0.1.7 // indirect github.com/Microsoft/go-winio v0.5.0 // indirect @@ -23,6 +25,7 @@ require ( github.com/golang/protobuf v1.5.0 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/websocket v1.4.2 // indirect + github.com/insomniacslk/dhcp v0.0.0-20210827173440-b95caade3eac // indirect github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect @@ -32,10 +35,11 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/sirupsen/logrus v1.8.1 // indirect + github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect go.uber.org/atomic v1.9.0 // indirect golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2 // indirect + golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34 // indirect golang.org/x/text v0.3.6 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a // indirect diff --git a/test/go.sum b/test/go.sum index e83047937..27d2c4de2 100644 --- a/test/go.sum +++ b/test/go.sum @@ -38,8 +38,6 @@ github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/Dreamacro/clash v1.6.6-0.20210821162529-0267b2efad9a h1:Y+EWU18ZaK9ZxcZGv2O3nUzGk3XYLZe2r5i8FMku7wA= -github.com/Dreamacro/clash v1.6.6-0.20210821162529-0267b2efad9a/go.mod h1:eN07P85tFwbnkHnEQPYlFsyTc8spXBNa4fOFUea6F58= github.com/Dreamacro/go-shadowsocks2 v0.1.7 h1:8CtbE1HoPPMfrQZGXmlluq6dO2lL31W6WRRE8fabc4Q= github.com/Dreamacro/go-shadowsocks2 v0.1.7/go.mod h1:8p5G4cAj5ZlXwUR+Ww63gfSikr8kvw8uw3TDwLAJpUc= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= @@ -253,6 +251,7 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= @@ -262,7 +261,7 @@ github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXt github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-chi/chi/v5 v5.0.3/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/chi/v5 v5.0.4/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.0/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -378,16 +377,23 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/insomniacslk/dhcp v0.0.0-20210827173440-b95caade3eac h1:IO6EfdRnPhxgKOsk9DbewdtQZHKZKnGlW7QCUttvNys= +github.com/insomniacslk/dhcp v0.0.0-20210827173440-b95caade3eac/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= +github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= +github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= +github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -424,6 +430,13 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y= +github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= +github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= +github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= +github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= +github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= +github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= @@ -553,6 +566,7 @@ github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= @@ -587,6 +601,8 @@ github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA= +github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= @@ -682,6 +698,7 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= @@ -693,6 +710,7 @@ golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -703,12 +721,13 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d h1:LO7XpTYMwTqxjLcGWPijK3vRXg1aWdlNOVOHRq45d7c= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f h1:w6wWR0H+nyVpbSAQbzVEIACVyr/h8l/BEkY6Sokc7Eg= +golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -732,13 +751,16 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -751,6 +773,7 @@ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -778,6 +801,8 @@ golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -788,10 +813,11 @@ golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2 h1:c8PlLMqBbOHoqtjteWm5/kbe6rNY2pbRfbIMVnepueo= -golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34 h1:GkvMjFtXUmahfDtashnc1mnrCtuBVcwse5QV2lUk/tI= +golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/test/ss_test.go b/test/ss_test.go index 7cba7efd9..446bae938 100644 --- a/test/ss_test.go +++ b/test/ss_test.go @@ -12,8 +12,9 @@ import ( func TestClash_Shadowsocks(t *testing.T) { cfg := &container.Config{ - Image: ImageShadowsocks, - Env: []string{"SS_MODULE=ss-server", "SS_CONFIG=-s 0.0.0.0 -u -v -p 10002 -m chacha20-ietf-poly1305 -k FzcLbKs2dY9mhL"}, + Image: ImageShadowsocksRust, + Entrypoint: []string{"ssserver"}, + Cmd: []string{"-s", "0.0.0.0:10002", "-m", "chacha20-ietf-poly1305", "-k", "FzcLbKs2dY9mhL", "-U"}, ExposedPorts: defaultExposedPorts, } hostCfg := &container.HostConfig{ @@ -173,8 +174,9 @@ func TestClash_ShadowsocksV2RayPlugin(t *testing.T) { func Benchmark_Shadowsocks(b *testing.B) { cfg := &container.Config{ - Image: ImageShadowsocks, - Env: []string{"SS_MODULE=ss-server", "SS_CONFIG=-s 0.0.0.0 -u -v -p 10002 -m aes-256-gcm -k FzcLbKs2dY9mhL"}, + Image: ImageShadowsocksRust, + Entrypoint: []string{"ssserver"}, + Cmd: []string{"-s", "0.0.0.0:10002", "-m", "aes-256-gcm", "-k", "FzcLbKs2dY9mhL", "-U"}, ExposedPorts: defaultExposedPorts, } hostCfg := &container.HostConfig{ diff --git a/test/util.go b/test/util.go new file mode 100644 index 000000000..f3325aeb6 --- /dev/null +++ b/test/util.go @@ -0,0 +1,37 @@ +package main + +import ( + "context" + "net" + "time" +) + +func Listen(network, address string) (net.Listener, error) { + lc := net.ListenConfig{} + + var lastErr error + for i := 0; i < 5; i++ { + l, err := lc.Listen(context.Background(), network, address) + if err == nil { + return l, nil + } + + lastErr = err + time.Sleep(time.Millisecond * 200) + } + return nil, lastErr +} + +func ListenPacket(network, address string) (net.PacketConn, error) { + var lastErr error + for i := 0; i < 5; i++ { + l, err := net.ListenPacket(network, address) + if err == nil { + return l, nil + } + + lastErr = err + time.Sleep(time.Millisecond * 200) + } + return nil, lastErr +} diff --git a/test/util_other_test.go b/test/util_other_test.go index e325bd8ba..13ba87f33 100644 --- a/test/util_other_test.go +++ b/test/util_other_test.go @@ -1,3 +1,4 @@ +//go:build !darwin // +build !darwin package main