From c99c71a9690f016d1b9cb7e600302dfa7a33afdb Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 16 Jan 2025 10:16:37 +0800 Subject: [PATCH] chore: listening tcp together for dns server (#1792) --- adapter/inbound/listen.go | 27 +++++++- common/sockopt/reuse_common.go | 30 +++++++++ .../sockopt/reuse_other.go | 8 +-- common/sockopt/reuse_unix.go | 22 +++++++ common/sockopt/reuse_windows.go | 9 +++ common/sockopt/reuseaddr_linux.go | 19 ------ common/sockopt/reuseaddr_other.go | 11 ---- .../dialer/{reuse_windows.go => reuse.go} | 6 +- component/dialer/reuse_unix.go | 20 ------ dns/server.go | 66 +++++++++++-------- listener/shadowsocks/udp.go | 5 +- listener/sing_hysteria2/server.go | 5 +- listener/sing_shadowsocks/server.go | 5 +- listener/socks/udp.go | 4 +- listener/tuic/server.go | 5 +- 15 files changed, 140 insertions(+), 102 deletions(-) create mode 100644 common/sockopt/reuse_common.go rename component/dialer/reuse_others.go => common/sockopt/reuse_other.go (55%) create mode 100644 common/sockopt/reuse_unix.go create mode 100644 common/sockopt/reuse_windows.go delete mode 100644 common/sockopt/reuseaddr_linux.go delete mode 100644 common/sockopt/reuseaddr_other.go rename component/dialer/{reuse_windows.go => reuse.go} (58%) delete mode 100644 component/dialer/reuse_unix.go diff --git a/adapter/inbound/listen.go b/adapter/inbound/listen.go index ad944006a2..b4c6bf6b6a 100644 --- a/adapter/inbound/listen.go +++ b/adapter/inbound/listen.go @@ -43,7 +43,7 @@ func MPTCP() bool { return getMultiPathTCP(&lc.ListenConfig) } -func ListenContext(ctx context.Context, network, address string) (net.Listener, error) { +func preResolve(network, address string) (string, error) { switch network { // like net.Resolver.internetAddrList but filter domain to avoid call net.Resolver.lookupIPAddr case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6", "ip", "ip4", "ip6": if host, port, err := net.SplitHostPort(address); err == nil { @@ -59,11 +59,19 @@ func ListenContext(ctx context.Context, network, address string) (net.Listener, break default: if _, err := netip.ParseAddr(host); err != nil { // not ip - return nil, fmt.Errorf("invalid network address: %s", address) + return "", fmt.Errorf("invalid network address: %s", address) } } } } + return address, nil +} + +func ListenContext(ctx context.Context, network, address string) (net.Listener, error) { + address, err := preResolve(network, address) + if err != nil { + return nil, err + } mutex.RLock() defer mutex.RUnlock() @@ -74,6 +82,21 @@ func Listen(network, address string) (net.Listener, error) { return ListenContext(context.Background(), network, address) } +func ListenPacketContext(ctx context.Context, network, address string) (net.PacketConn, error) { + address, err := preResolve(network, address) + if err != nil { + return nil, err + } + + mutex.RLock() + defer mutex.RUnlock() + return lc.ListenPacket(ctx, network, address) +} + +func ListenPacket(network, address string) (net.PacketConn, error) { + return ListenPacketContext(context.Background(), network, address) +} + func init() { keepalive.SetDisableKeepAliveCallback.Register(func(b bool) { mutex.Lock() diff --git a/common/sockopt/reuse_common.go b/common/sockopt/reuse_common.go new file mode 100644 index 0000000000..907697c32c --- /dev/null +++ b/common/sockopt/reuse_common.go @@ -0,0 +1,30 @@ +package sockopt + +import ( + "net" + "syscall" +) + +func RawConnReuseaddr(rc syscall.RawConn) (err error) { + var innerErr error + err = rc.Control(func(fd uintptr) { + innerErr = reuseControl(fd) + }) + + if innerErr != nil { + err = innerErr + } + return +} + +func UDPReuseaddr(c net.PacketConn) error { + if c, ok := c.(syscall.Conn); ok { + rc, err := c.SyscallConn() + if err != nil { + return err + } + + return RawConnReuseaddr(rc) + } + return nil +} diff --git a/component/dialer/reuse_others.go b/common/sockopt/reuse_other.go similarity index 55% rename from component/dialer/reuse_others.go rename to common/sockopt/reuse_other.go index db67a095d6..39d901a050 100644 --- a/component/dialer/reuse_others.go +++ b/common/sockopt/reuse_other.go @@ -1,9 +1,5 @@ //go:build !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows -package dialer +package sockopt -import ( - "net" -) - -func addrReuseToListenConfig(*net.ListenConfig) {} +func reuseControl(fd uintptr) error { return nil } diff --git a/common/sockopt/reuse_unix.go b/common/sockopt/reuse_unix.go new file mode 100644 index 0000000000..e000b9e89c --- /dev/null +++ b/common/sockopt/reuse_unix.go @@ -0,0 +1,22 @@ +//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris + +package sockopt + +import ( + "golang.org/x/sys/unix" +) + +func reuseControl(fd uintptr) error { + e1 := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1) + e2 := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1) + + if e1 != nil { + return e1 + } + + if e2 != nil { + return e2 + } + + return nil +} diff --git a/common/sockopt/reuse_windows.go b/common/sockopt/reuse_windows.go new file mode 100644 index 0000000000..79305d02b0 --- /dev/null +++ b/common/sockopt/reuse_windows.go @@ -0,0 +1,9 @@ +package sockopt + +import ( + "golang.org/x/sys/windows" +) + +func reuseControl(fd uintptr) error { + return windows.SetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_REUSEADDR, 1) +} diff --git a/common/sockopt/reuseaddr_linux.go b/common/sockopt/reuseaddr_linux.go deleted file mode 100644 index a1d19bfdab..0000000000 --- a/common/sockopt/reuseaddr_linux.go +++ /dev/null @@ -1,19 +0,0 @@ -package sockopt - -import ( - "net" - "syscall" -) - -func UDPReuseaddr(c *net.UDPConn) (err error) { - rc, err := c.SyscallConn() - if err != nil { - return - } - - rc.Control(func(fd uintptr) { - err = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) - }) - - return -} diff --git a/common/sockopt/reuseaddr_other.go b/common/sockopt/reuseaddr_other.go deleted file mode 100644 index 04fc8ed731..0000000000 --- a/common/sockopt/reuseaddr_other.go +++ /dev/null @@ -1,11 +0,0 @@ -//go:build !linux - -package sockopt - -import ( - "net" -) - -func UDPReuseaddr(c *net.UDPConn) (err error) { - return -} diff --git a/component/dialer/reuse_windows.go b/component/dialer/reuse.go similarity index 58% rename from component/dialer/reuse_windows.go rename to component/dialer/reuse.go index b8d0d809c2..4a46e572fd 100644 --- a/component/dialer/reuse_windows.go +++ b/component/dialer/reuse.go @@ -5,13 +5,11 @@ import ( "net" "syscall" - "golang.org/x/sys/windows" + "github.com/metacubex/mihomo/common/sockopt" ) func addrReuseToListenConfig(lc *net.ListenConfig) { addControlToListenConfig(lc, func(ctx context.Context, network, address string, c syscall.RawConn) error { - return c.Control(func(fd uintptr) { - windows.SetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_REUSEADDR, 1) - }) + return sockopt.RawConnReuseaddr(c) }) } diff --git a/component/dialer/reuse_unix.go b/component/dialer/reuse_unix.go deleted file mode 100644 index a0cf738817..0000000000 --- a/component/dialer/reuse_unix.go +++ /dev/null @@ -1,20 +0,0 @@ -//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris - -package dialer - -import ( - "context" - "net" - "syscall" - - "golang.org/x/sys/unix" -) - -func addrReuseToListenConfig(lc *net.ListenConfig) { - addControlToListenConfig(lc, func(ctx context.Context, network, address string, c syscall.RawConn) error { - 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/dns/server.go b/dns/server.go index 1cf58d4d8a..caf1c2891a 100644 --- a/dns/server.go +++ b/dns/server.go @@ -5,6 +5,7 @@ import ( "errors" "net" + "github.com/metacubex/mihomo/adapter/inbound" "github.com/metacubex/mihomo/common/sockopt" "github.com/metacubex/mihomo/context" "github.com/metacubex/mihomo/log" @@ -20,8 +21,9 @@ var ( ) type Server struct { - *D.Server - handler handler + handler handler + tcpServer *D.Server + udpServer *D.Server } // ServeDNS implement D.Handler ServeDNS @@ -55,12 +57,19 @@ func ReCreateServer(addr string, resolver *Resolver, mapper *ResolverEnhancer) { return } - if server.Server != nil { - server.Shutdown() - server = &Server{} - address = "" + if server.tcpServer != nil { + _ = server.tcpServer.Shutdown() + server.tcpServer = nil } + if server.udpServer != nil { + _ = server.udpServer.Shutdown() + server.udpServer = nil + } + + server.handler = nil + address = "" + if addr == "" { return } @@ -77,31 +86,36 @@ func ReCreateServer(addr string, resolver *Resolver, mapper *ResolverEnhancer) { return } - udpAddr, err := net.ResolveUDPAddr("udp", addr) - if err != nil { - return - } - - p, err := net.ListenUDP("udp", udpAddr) - if err != nil { - return - } - - err = sockopt.UDPReuseaddr(p) - if err != nil { - log.Warnln("Failed to Reuse UDP Address: %s", err) - - err = nil - } - address = addr handler := NewHandler(resolver, mapper) server = &Server{handler: handler} - server.Server = &D.Server{Addr: addr, PacketConn: p, Handler: server} go func() { - server.ActivateAndServe() + p, err := inbound.ListenPacket("udp", addr) + if err != nil { + log.Errorln("Start DNS server(UDP) error: %s", err.Error()) + return + } + + if err := sockopt.UDPReuseaddr(p); err != nil { + log.Warnln("Failed to Reuse UDP Address: %s", err) + } + + log.Infoln("DNS server(UDP) listening at: %s", p.LocalAddr().String()) + server.udpServer = &D.Server{Addr: addr, PacketConn: p, Handler: server} + _ = server.udpServer.ActivateAndServe() + }() + + go func() { + l, err := inbound.Listen("tcp", addr) + if err != nil { + log.Errorln("Start DNS server(TCP) error: %s", err.Error()) + return + } + + log.Infoln("DNS server(TCP) listening at: %s", l.Addr().String()) + server.tcpServer = &D.Server{Addr: addr, Listener: l, Handler: server} + _ = server.tcpServer.ActivateAndServe() }() - log.Infoln("DNS server listening at: %s", p.LocalAddr().String()) } diff --git a/listener/shadowsocks/udp.go b/listener/shadowsocks/udp.go index 77932ed1b1..bdb6739ed7 100644 --- a/listener/shadowsocks/udp.go +++ b/listener/shadowsocks/udp.go @@ -18,13 +18,12 @@ type UDPListener struct { } func NewUDP(addr string, pickCipher core.Cipher, tunnel C.Tunnel, additions ...inbound.Addition) (*UDPListener, error) { - l, err := net.ListenPacket("udp", addr) + l, err := inbound.ListenPacket("udp", addr) if err != nil { return nil, err } - err = sockopt.UDPReuseaddr(l.(*net.UDPConn)) - if err != nil { + if err := sockopt.UDPReuseaddr(l); err != nil { log.Warnln("Failed to Reuse UDP Address: %s", err) } diff --git a/listener/sing_hysteria2/server.go b/listener/sing_hysteria2/server.go index 77ad8efdeb..524aa9a137 100644 --- a/listener/sing_hysteria2/server.go +++ b/listener/sing_hysteria2/server.go @@ -140,13 +140,12 @@ func New(config LC.Hysteria2Server, tunnel C.Tunnel, additions ...inbound.Additi _service := *service service := &_service // make a copy - ul, err := net.ListenPacket("udp", addr) + ul, err := inbound.ListenPacket("udp", addr) if err != nil { return nil, err } - err = sockopt.UDPReuseaddr(ul.(*net.UDPConn)) - if err != nil { + if err := sockopt.UDPReuseaddr(ul); err != nil { log.Warnln("Failed to Reuse UDP Address: %s", err) } diff --git a/listener/sing_shadowsocks/server.go b/listener/sing_shadowsocks/server.go index 5f2a4292e3..7ce1d4e916 100644 --- a/listener/sing_shadowsocks/server.go +++ b/listener/sing_shadowsocks/server.go @@ -82,13 +82,12 @@ func New(config LC.ShadowsocksServer, tunnel C.Tunnel, additions ...inbound.Addi if config.Udp { //UDP - ul, err := net.ListenPacket("udp", addr) + ul, err := inbound.ListenPacket("udp", addr) if err != nil { return nil, err } - err = sockopt.UDPReuseaddr(ul.(*net.UDPConn)) - if err != nil { + if err := sockopt.UDPReuseaddr(ul); err != nil { log.Warnln("Failed to Reuse UDP Address: %s", err) } diff --git a/listener/socks/udp.go b/listener/socks/udp.go index ef31b20e97..cb4fa37294 100644 --- a/listener/socks/udp.go +++ b/listener/socks/udp.go @@ -40,12 +40,12 @@ func NewUDP(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*UDPLi inbound.WithSpecialRules(""), } } - l, err := net.ListenPacket("udp", addr) + l, err := inbound.ListenPacket("udp", addr) if err != nil { return nil, err } - if err := sockopt.UDPReuseaddr(l.(*net.UDPConn)); err != nil { + if err := sockopt.UDPReuseaddr(l); err != nil { log.Warnln("Failed to Reuse UDP Address: %s", err) } diff --git a/listener/tuic/server.go b/listener/tuic/server.go index 5d807cbc21..837c1d10f9 100644 --- a/listener/tuic/server.go +++ b/listener/tuic/server.go @@ -152,13 +152,12 @@ func New(config LC.TuicServer, tunnel C.Tunnel, additions ...inbound.Addition) ( for _, addr := range strings.Split(config.Listen, ",") { addr := addr - ul, err := net.ListenPacket("udp", addr) + ul, err := inbound.ListenPacket("udp", addr) if err != nil { return nil, err } - err = sockopt.UDPReuseaddr(ul.(*net.UDPConn)) - if err != nil { + if err := sockopt.UDPReuseaddr(ul); err != nil { log.Warnln("Failed to Reuse UDP Address: %s", err) }