diff --git a/dns/dialer.go b/dns/dialer.go new file mode 100644 index 000000000..309b6c188 --- /dev/null +++ b/dns/dialer.go @@ -0,0 +1,12 @@ +package dns + +// export functions from tunnel module + +import "github.com/metacubex/mihomo/tunnel" + +const RespectRules = tunnel.DnsRespectRules + +type dialHandler = tunnel.DnsDialHandler + +var getDialHandler = tunnel.GetDnsDialHandler +var listenPacket = tunnel.DnsListenPacket diff --git a/dns/dial.go b/tunnel/dns_dialer.go similarity index 77% rename from dns/dial.go rename to tunnel/dns_dialer.go index 431707c56..f4c0be713 100644 --- a/dns/dial.go +++ b/tunnel/dns_dialer.go @@ -1,4 +1,6 @@ -package dns +package tunnel + +// WARNING: all function in this file should only be using in dns module import ( "context" @@ -11,15 +13,14 @@ import ( "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/resolver" C "github.com/metacubex/mihomo/constant" - "github.com/metacubex/mihomo/tunnel" "github.com/metacubex/mihomo/tunnel/statistic" ) -const RespectRules = "RULES" +const DnsRespectRules = "RULES" -type dialHandler func(ctx context.Context, network, addr string) (net.Conn, error) +type DnsDialHandler func(ctx context.Context, network, addr string) (net.Conn, error) -func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts ...dialer.Option) dialHandler { +func GetDnsDialHandler(r resolver.Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts ...dialer.Option) DnsDialHandler { return func(ctx context.Context, network, addr string) (net.Conn, error) { if len(proxyName) == 0 && proxyAdapter == nil { opts = append(opts, dialer.WithResolver(r)) @@ -47,22 +48,22 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, var rule C.Rule if proxyAdapter == nil { - if proxyName == RespectRules { + if proxyName == DnsRespectRules { if !metadata.Resolved() { - // resolve here before ResolveMetadata to avoid its inner resolver.ResolveIP + // resolve here before resolveMetadata to avoid its inner resolver.ResolveIP dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) if err != nil { return nil, err } metadata.DstIP = dstIP } - proxyAdapter, rule, err = tunnel.ResolveMetadata(metadata) + proxyAdapter, rule, err = resolveMetadata(metadata) if err != nil { return nil, err } } else { var ok bool - proxyAdapter, ok = tunnel.Proxies()[proxyName] + proxyAdapter, ok = Proxies()[proxyName] if !ok { opts = append(opts, dialer.WithInterface(proxyName)) } @@ -88,8 +89,10 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, conn, err := proxyAdapter.DialContext(ctx, metadata, opts...) if err != nil { + logMetadataErr(metadata, rule, proxyAdapter, err) return nil, err } + logMetadata(metadata, rule, conn) conn = statistic.NewTCPTracker(conn, statistic.DefaultManager, metadata, rule, 0, 0, false) @@ -105,8 +108,10 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...) if err != nil { + logMetadataErr(metadata, rule, proxyAdapter, err) return nil, err } + logMetadata(metadata, rule, packetConn) packetConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false) @@ -116,7 +121,7 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, } } -func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName string, network string, addr string, r *Resolver, opts ...dialer.Option) (net.PacketConn, error) { +func DnsListenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName string, network string, addr string, r resolver.Resolver, opts ...dialer.Option) (net.PacketConn, error) { metadata := &C.Metadata{ NetWork: C.UDP, Type: C.INNER, @@ -136,14 +141,14 @@ func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName st var rule C.Rule if proxyAdapter == nil { - if proxyName == RespectRules { - proxyAdapter, rule, err = tunnel.ResolveMetadata(metadata) + if proxyName == DnsRespectRules { + proxyAdapter, rule, err = resolveMetadata(metadata) if err != nil { return nil, err } } else { var ok bool - proxyAdapter, ok = tunnel.Proxies()[proxyName] + proxyAdapter, ok = Proxies()[proxyName] if !ok { opts = append(opts, dialer.WithInterface(proxyName)) } @@ -160,8 +165,10 @@ func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName st packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...) if err != nil { + logMetadataErr(metadata, rule, proxyAdapter, err) return nil, err } + logMetadata(metadata, rule, packetConn) packetConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 78628a568..2c1b894f9 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -8,6 +8,7 @@ import ( "net/netip" "path/filepath" "runtime" + "strings" "sync" "time" @@ -278,7 +279,7 @@ func preHandleMetadata(metadata *C.Metadata) error { return nil } -func ResolveMetadata(metadata *C.Metadata) (proxy C.Proxy, rule C.Rule, err error) { +func resolveMetadata(metadata *C.Metadata) (proxy C.Proxy, rule C.Rule, err error) { if metadata.SpecialProxy != "" { var exist bool proxy, exist = proxies[metadata.SpecialProxy] @@ -375,7 +376,7 @@ func handleUDPConn(packet C.PacketAdapter) { cond.Broadcast() }() - proxy, rule, err := ResolveMetadata(metadata) + proxy, rule, err := resolveMetadata(metadata) if err != nil { log.Warnln("[UDP] Parse metadata failed: %s", err.Error()) return @@ -386,43 +387,18 @@ func handleUDPConn(packet C.PacketAdapter) { rawPc, err := retry(ctx, func(ctx context.Context) (C.PacketConn, error) { return proxy.ListenPacketContext(ctx, metadata.Pure()) }, func(err error) { - if rule == nil { - log.Warnln( - "[UDP] dial %s %s --> %s error: %s", - proxy.Name(), - metadata.SourceDetail(), - metadata.RemoteAddress(), - err.Error(), - ) - } else { - log.Warnln("[UDP] dial %s (match %s/%s) %s --> %s error: %s", proxy.Name(), rule.RuleType().String(), rule.Payload(), metadata.SourceDetail(), metadata.RemoteAddress(), err.Error()) - } + logMetadataErr(metadata, rule, proxy, err) }) if err != nil { return } + logMetadata(metadata, rule, rawPc) pc := statistic.NewUDPTracker(rawPc, statistic.DefaultManager, metadata, rule, 0, 0, true) - switch true { - case metadata.SpecialProxy != "": - log.Infoln("[UDP] %s --> %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), metadata.SpecialProxy) - case rule != nil: - if rule.Payload() != "" { - log.Infoln("[UDP] %s --> %s match %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), fmt.Sprintf("%s(%s)", rule.RuleType().String(), rule.Payload()), rawPc.Chains().String()) - if rawPc.Chains().Last() == "REJECT-DROP" { - pc.Close() - return - } - } else { - log.Infoln("[UDP] %s --> %s match %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), rule.Payload(), rawPc.Chains().String()) - } - case mode == Global: - log.Infoln("[UDP] %s --> %s using GLOBAL", metadata.SourceDetail(), metadata.RemoteAddress()) - case mode == Direct: - log.Infoln("[UDP] %s --> %s using DIRECT", metadata.SourceDetail(), metadata.RemoteAddress()) - default: - log.Infoln("[UDP] %s --> %s doesn't match any rule using DIRECT", metadata.SourceDetail(), metadata.RemoteAddress()) + if rawPc.Chains().Last() == "REJECT-DROP" { + pc.Close() + return } oAddrPort := metadata.AddrPort() @@ -486,7 +462,7 @@ func handleTCPConn(connCtx C.ConnContext) { }() } - proxy, rule, err := ResolveMetadata(metadata) + proxy, rule, err := resolveMetadata(metadata) if err != nil { log.Warnln("[Metadata] parse failed: %s", err.Error()) return @@ -539,48 +515,18 @@ func handleTCPConn(connCtx C.ConnContext) { } return }, func(err error) { - if rule == nil { - log.Warnln( - "[TCP] dial %s %s --> %s error: %s", - proxy.Name(), - metadata.SourceDetail(), - metadata.RemoteAddress(), - err.Error(), - ) - } else { - log.Warnln("[TCP] dial %s (match %s/%s) %s --> %s error: %s", proxy.Name(), rule.RuleType().String(), rule.Payload(), metadata.SourceDetail(), metadata.RemoteAddress(), err.Error()) - } + logMetadataErr(metadata, rule, proxy, err) }) if err != nil { return } + logMetadata(metadata, rule, remoteConn) remoteConn = statistic.NewTCPTracker(remoteConn, statistic.DefaultManager, metadata, rule, 0, int64(peekLen), true) defer func(remoteConn C.Conn) { _ = remoteConn.Close() }(remoteConn) - switch true { - case metadata.SpecialProxy != "": - log.Infoln("[TCP] %s --> %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), metadata.SpecialProxy) - case rule != nil: - if rule.Payload() != "" { - log.Infoln("[TCP] %s --> %s match %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), fmt.Sprintf("%s(%s)", rule.RuleType().String(), rule.Payload()), remoteConn.Chains().String()) - } else { - log.Infoln("[TCP] %s --> %s match %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), rule.RuleType().String(), remoteConn.Chains().String()) - } - case mode == Global: - log.Infoln("[TCP] %s --> %s using GLOBAL", metadata.SourceDetail(), metadata.RemoteAddress()) - case mode == Direct: - log.Infoln("[TCP] %s --> %s using DIRECT", metadata.SourceDetail(), metadata.RemoteAddress()) - default: - log.Infoln( - "[TCP] %s --> %s doesn't match any rule using DIRECT", - metadata.SourceDetail(), - metadata.RemoteAddress(), - ) - } - _ = conn.SetReadDeadline(time.Now()) // stop unfinished peek peekMutex.Lock() defer peekMutex.Unlock() @@ -588,6 +534,33 @@ func handleTCPConn(connCtx C.ConnContext) { handleSocket(conn, remoteConn) } +func logMetadataErr(metadata *C.Metadata, rule C.Rule, proxy C.ProxyAdapter, err error) { + if rule == nil { + log.Warnln("[%s] dial %s %s --> %s error: %s", strings.ToUpper(metadata.NetWork.String()), proxy.Name(), metadata.SourceDetail(), metadata.RemoteAddress(), err.Error()) + } else { + log.Warnln("[%s] dial %s (match %s/%s) %s --> %s error: %s", strings.ToUpper(metadata.NetWork.String()), proxy.Name(), rule.RuleType().String(), rule.Payload(), metadata.SourceDetail(), metadata.RemoteAddress(), err.Error()) + } +} + +func logMetadata(metadata *C.Metadata, rule C.Rule, remoteConn C.Connection) { + switch { + case metadata.SpecialProxy != "": + log.Infoln("[%s] %s --> %s using %s", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress(), metadata.SpecialProxy) + case rule != nil: + if rule.Payload() != "" { + log.Infoln("[%s] %s --> %s match %s using %s", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress(), fmt.Sprintf("%s(%s)", rule.RuleType().String(), rule.Payload()), remoteConn.Chains().String()) + } else { + log.Infoln("[%s] %s --> %s match %s using %s", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress(), rule.RuleType().String(), remoteConn.Chains().String()) + } + case mode == Global: + log.Infoln("[%s] %s --> %s using GLOBAL", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress()) + case mode == Direct: + log.Infoln("[%s] %s --> %s using DIRECT", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress()) + default: + log.Infoln("[%s] %s --> %s doesn't match any rule using %s", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress(), remoteConn.Chains().Last()) + } +} + func shouldResolveIP(rule C.Rule, metadata *C.Metadata) bool { return rule.ShouldResolveIP() && metadata.Host != "" && !metadata.DstIP.IsValid() }