Compare commits

...

17 Commits

Author SHA1 Message Date
wwqgtxx
3e966e82c7 chore: update quic-go to 0.48.0 2024-10-21 09:38:21 +08:00
wwqgtxx
b9171ade7f chore: update sing-tun to v0.4.0-rc.4 2024-10-21 09:17:37 +08:00
xishang0128
95af5f7325 chore: change subscription-userinfo retrieval 2024-10-20 06:01:02 +08:00
xishang0128
ca3f1ebae6 fix: sticky-sessions may not be effective 2024-10-12 08:26:37 +08:00
ForestL
4437c8861c
chore: better getUpdateTime() for iterating all Geofiles (#1570) 2024-10-11 08:46:31 +08:00
xishang0128
57725078e0 chore: Adjust the error log for the search process 2024-10-11 07:35:51 +08:00
wwqgtxx
08dcef80bf fix: mistaken using net.Dialer
https://github.com/MetaCubeX/mihomo/issues/1572
2024-10-09 12:04:56 +08:00
wwqgtxx
9fd63fe938 chore: update dependencies 2024-10-06 10:34:54 +08:00
wwqgtxx
8e6eb70e71 chore: temporary update general in ParseRawConfig and rollback before its retur 2024-10-06 00:21:00 +08:00
wwqgtxx
9937ae1002 fix: defaultNS not working in system dns 2024-10-05 14:20:54 +08:00
wwqgtxx
8f5a86410c chore: cleanup unneeded setting in parseGeneral, move to executor 2024-10-05 13:58:49 +08:00
wwqgtxx
9286e21026 chore: rebuild external ui updater 2024-10-05 13:40:00 +08:00
wwqgtxx
c63a851bba feat: add direct-nameserver and direct-nameserver-follow-policy in dns section 2024-10-04 14:20:10 +08:00
wwqgtxx
4a16d22398 chore: no longer used net.DefaultResolver when dns section is disabled, now is equally only "system://" 2024-10-02 14:45:06 +08:00
wwqgtxx
990de84391 chore: better atomic using 2024-10-02 14:45:06 +08:00
Skyxim
ecd8facd81 chore: add warning for unified delay test when second failed 2024-10-01 03:14:37 +00:00
wwqgtxx
a330fa1506 chore: disallow some restful api for CMFA 2024-09-30 13:08:50 +08:00
39 changed files with 476 additions and 434 deletions

View File

@ -10,6 +10,7 @@ import (
"net/netip" "net/netip"
"net/url" "net/url"
"strconv" "strconv"
"strings"
"time" "time"
"github.com/metacubex/mihomo/common/atomic" "github.com/metacubex/mihomo/common/atomic"
@ -18,6 +19,7 @@ import (
"github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/ca"
"github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/dialer"
C "github.com/metacubex/mihomo/constant" C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/log"
"github.com/puzpuzpuz/xsync/v3" "github.com/puzpuzpuz/xsync/v3"
) )
@ -260,10 +262,18 @@ func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.In
if unifiedDelay { if unifiedDelay {
second := time.Now() second := time.Now()
resp, err = client.Do(req) var ignoredErr error
if err == nil { var secondResp *http.Response
secondResp, ignoredErr = client.Do(req)
if ignoredErr == nil {
resp = secondResp
_ = resp.Body.Close() _ = resp.Body.Close()
start = second start = second
} else {
if strings.HasPrefix(url, "http://") {
log.Errorln("%s failed to get the second response from %s: %v", p.Name(), url, ignoredErr)
log.Warnln("It is recommended to use HTTPS for provider.health-check.url and group.url to ensure better reliability. Due to some proxy providers hijacking test addresses and not being compatible with repeated HEAD requests, using HTTP may result in failed tests.")
}
} }
} }

View File

@ -32,7 +32,7 @@ func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ...
return nil, err return nil, err
} }
} }
opts = append(opts, dialer.WithResolver(resolver.DefaultResolver)) opts = append(opts, dialer.WithResolver(resolver.DirectHostResolver))
c, err := dialer.DialContext(ctx, "tcp", metadata.RemoteAddress(), d.Base.DialOptions(opts...)...) c, err := dialer.DialContext(ctx, "tcp", metadata.RemoteAddress(), d.Base.DialOptions(opts...)...)
if err != nil { if err != nil {
return nil, err return nil, err
@ -49,7 +49,7 @@ func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata,
} }
// net.UDPConn.WriteTo only working with *net.UDPAddr, so we need a net.UDPAddr // net.UDPConn.WriteTo only working with *net.UDPAddr, so we need a net.UDPAddr
if !metadata.Resolved() { if !metadata.Resolved() {
ip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, resolver.DefaultResolver) ip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, resolver.DirectHostResolver)
if err != nil { if err != nil {
return nil, errors.New("can't resolve ip") return nil, errors.New("can't resolve ip")
} }

View File

@ -69,7 +69,7 @@ func (h *Hysteria) ListenPacketContext(ctx context.Context, metadata *C.Metadata
func (h *Hysteria) genHdc(ctx context.Context, opts ...dialer.Option) utils.PacketDialer { func (h *Hysteria) genHdc(ctx context.Context, opts ...dialer.Option) utils.PacketDialer {
return &hyDialerWithContext{ return &hyDialerWithContext{
ctx: context.Background(), ctx: context.Background(),
hyDialer: func(network string) (net.PacketConn, error) { hyDialer: func(network string, rAddr net.Addr) (net.PacketConn, error) {
var err error var err error
var cDialer C.Dialer = dialer.NewDialer(h.Base.DialOptions(opts...)...) var cDialer C.Dialer = dialer.NewDialer(h.Base.DialOptions(opts...)...)
if len(h.option.DialerProxy) > 0 { if len(h.option.DialerProxy) > 0 {
@ -78,7 +78,7 @@ func (h *Hysteria) genHdc(ctx context.Context, opts ...dialer.Option) utils.Pack
return nil, err return nil, err
} }
} }
rAddrPort, _ := netip.ParseAddrPort(h.Addr()) rAddrPort, _ := netip.ParseAddrPort(rAddr.String())
return cDialer.ListenPacket(ctx, network, "", rAddrPort) return cDialer.ListenPacket(ctx, network, "", rAddrPort)
}, },
remoteAddr: func(addr string) (net.Addr, error) { remoteAddr: func(addr string) (net.Addr, error) {
@ -131,11 +131,7 @@ func (c *HysteriaOption) Speed() (uint64, uint64, error) {
} }
func NewHysteria(option HysteriaOption) (*Hysteria, error) { func NewHysteria(option HysteriaOption) (*Hysteria, error) {
clientTransport := &transport.ClientTransport{ clientTransport := &transport.ClientTransport{}
Dialer: &net.Dialer{
Timeout: 8 * time.Second,
},
}
addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
ports := option.Ports ports := option.Ports
@ -284,7 +280,7 @@ func (c *hyPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
} }
type hyDialerWithContext struct { type hyDialerWithContext struct {
hyDialer func(network string) (net.PacketConn, error) hyDialer func(network string, rAddr net.Addr) (net.PacketConn, error)
ctx context.Context ctx context.Context
remoteAddr func(host string) (net.Addr, error) remoteAddr func(host string) (net.Addr, error)
} }
@ -294,7 +290,7 @@ func (h *hyDialerWithContext) ListenPacket(rAddr net.Addr) (net.PacketConn, erro
if addrPort, err := netip.ParseAddrPort(rAddr.String()); err == nil { if addrPort, err := netip.ParseAddrPort(rAddr.String()); err == nil {
network = dialer.ParseNetwork(network, addrPort.Addr()) network = dialer.ParseNetwork(network, addrPort.Addr())
} }
return h.hyDialer(network) return h.hyDialer(network, rAddr)
} }
func (h *hyDialerWithContext) Context() context.Context { func (h *hyDialerWithContext) Context() context.Context {

View File

@ -55,7 +55,7 @@ func resolveUDPAddr(ctx context.Context, network, address string) (*net.UDPAddr,
return nil, err return nil, err
} }
ip, err := resolver.ResolveProxyServerHost(ctx, host) ip, err := resolver.ResolveIPWithResolver(ctx, host, resolver.ProxyServerHostResolver)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -71,12 +71,12 @@ func resolveUDPAddrWithPrefer(ctx context.Context, network, address string, pref
var fallback netip.Addr var fallback netip.Addr
switch prefer { switch prefer {
case C.IPv4Only: case C.IPv4Only:
ip, err = resolver.ResolveIPv4ProxyServerHost(ctx, host) ip, err = resolver.ResolveIPv4WithResolver(ctx, host, resolver.ProxyServerHostResolver)
case C.IPv6Only: case C.IPv6Only:
ip, err = resolver.ResolveIPv6ProxyServerHost(ctx, host) ip, err = resolver.ResolveIPv6WithResolver(ctx, host, resolver.ProxyServerHostResolver)
case C.IPv6Prefer: case C.IPv6Prefer:
var ips []netip.Addr var ips []netip.Addr
ips, err = resolver.LookupIPProxyServerHost(ctx, host) ips, err = resolver.LookupIPWithResolver(ctx, host, resolver.ProxyServerHostResolver)
if err == nil { if err == nil {
for _, addr := range ips { for _, addr := range ips {
if addr.Is6() { if addr.Is6() {
@ -92,7 +92,7 @@ func resolveUDPAddrWithPrefer(ctx context.Context, network, address string, pref
default: default:
// C.IPv4Prefer, C.DualStack and other // C.IPv4Prefer, C.DualStack and other
var ips []netip.Addr var ips []netip.Addr
ips, err = resolver.LookupIPProxyServerHost(ctx, host) ips, err = resolver.LookupIPWithResolver(ctx, host, resolver.ProxyServerHostResolver)
if err == nil { if err == nil {
for _, addr := range ips { for _, addr := range ips {
if addr.Is4() { if addr.Is4() {

View File

@ -44,7 +44,7 @@ type WireGuard struct {
device wireguardGoDevice device wireguardGoDevice
tunDevice wireguard.Device tunDevice wireguard.Device
dialer proxydialer.SingDialer dialer proxydialer.SingDialer
resolver *dns.Resolver resolver resolver.Resolver
refP *refProxyAdapter refP *refProxyAdapter
initOk atomic.Bool initOk atomic.Bool
@ -296,7 +296,7 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) {
for i := range nss { for i := range nss {
nss[i].ProxyAdapter = refP nss[i].ProxyAdapter = refP
} }
outbound.resolver, _ = dns.NewResolver(dns.Config{ outbound.resolver = dns.NewResolver(dns.Config{
Main: nss, Main: nss,
IPv6: has6, IPv6: has6,
}) })

View File

@ -204,7 +204,7 @@ func strategyStickySessions(url string) strategyFn {
for i := 1; i < maxRetry; i++ { for i := 1; i < maxRetry; i++ {
proxy := proxies[nowIdx] proxy := proxies[nowIdx]
if proxy.AliveForTestUrl(url) { if proxy.AliveForTestUrl(url) {
if nowIdx != idx { if !has || nowIdx != idx {
lruCache.Set(key, nowIdx) lruCache.Set(key, nowIdx)
} }

View File

@ -1,11 +1,9 @@
package provider package provider
import ( import (
"context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"net/http"
"reflect" "reflect"
"runtime" "runtime"
"strings" "strings"
@ -14,7 +12,7 @@ import (
"github.com/metacubex/mihomo/adapter" "github.com/metacubex/mihomo/adapter"
"github.com/metacubex/mihomo/common/convert" "github.com/metacubex/mihomo/common/convert"
"github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/common/utils"
mihomoHttp "github.com/metacubex/mihomo/component/http" "github.com/metacubex/mihomo/component/profile/cachefile"
"github.com/metacubex/mihomo/component/resource" "github.com/metacubex/mihomo/component/resource"
C "github.com/metacubex/mihomo/constant" C "github.com/metacubex/mihomo/constant"
types "github.com/metacubex/mihomo/constant/provider" types "github.com/metacubex/mihomo/constant/provider"
@ -80,7 +78,9 @@ func (pp *proxySetProvider) Initial() error {
if err != nil { if err != nil {
return err return err
} }
pp.getSubscriptionInfo() if subscriptionInfo := cachefile.Cache().GetSubscriptionInfo(pp.Name()); subscriptionInfo != "" {
pp.SetSubscriptionInfo(subscriptionInfo)
}
pp.closeAllConnections() pp.closeAllConnections()
return nil return nil
} }
@ -117,35 +117,14 @@ func (pp *proxySetProvider) setProxies(proxies []C.Proxy) {
} }
} }
func (pp *proxySetProvider) getSubscriptionInfo() { func (pp *proxySetProvider) SetSubscriptionInfo(userInfo string) {
if pp.VehicleType() != types.HTTP { pp.subscriptionInfo = NewSubscriptionInfo(userInfo)
return }
}
go func() {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*90)
defer cancel()
resp, err := mihomoHttp.HttpRequestWithProxy(ctx, pp.Vehicle().Url(),
http.MethodGet, nil, nil, pp.Vehicle().Proxy())
if err != nil {
return
}
defer resp.Body.Close()
userInfoStr := strings.TrimSpace(resp.Header.Get("subscription-userinfo")) func (pp *proxySetProvider) SetProvider(provider types.ProxyProvider) {
if userInfoStr == "" { if httpVehicle, ok := pp.Vehicle().(*resource.HTTPVehicle); ok {
resp2, err := mihomoHttp.HttpRequestWithProxy(ctx, pp.Vehicle().Url(), httpVehicle.SetProvider(provider)
http.MethodGet, http.Header{"User-Agent": {"Quantumultx"}}, nil, pp.Vehicle().Proxy())
if err != nil {
return
} }
defer resp2.Body.Close()
userInfoStr = strings.TrimSpace(resp2.Header.Get("subscription-userinfo"))
if userInfoStr == "" {
return
}
}
pp.subscriptionInfo = NewSubscriptionInfo(userInfoStr)
}()
} }
func (pp *proxySetProvider) closeAllConnections() { func (pp *proxySetProvider) closeAllConnections() {
@ -196,6 +175,9 @@ func NewProxySetProvider(name string, interval time.Duration, filter string, exc
fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, excludeFilter, excludeTypeArray, filterRegs, excludeFilterReg, dialerProxy, override), proxiesOnUpdate(pd)) fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, excludeFilter, excludeTypeArray, filterRegs, excludeFilterReg, dialerProxy, override), proxiesOnUpdate(pd))
pd.Fetcher = fetcher pd.Fetcher = fetcher
wrapper := &ProxySetProvider{pd} wrapper := &ProxySetProvider{pd}
if httpVehicle, ok := vehicle.(*resource.HTTPVehicle); ok {
httpVehicle.SetProvider(wrapper)
}
runtime.SetFinalizer(wrapper, (*ProxySetProvider).Close) runtime.SetFinalizer(wrapper, (*ProxySetProvider).Close)
return wrapper, nil return wrapper, nil
} }
@ -205,6 +187,10 @@ func (pp *ProxySetProvider) Close() error {
return pp.proxySetProvider.Close() return pp.proxySetProvider.Close()
} }
func (pp *ProxySetProvider) SetProvider(provider types.ProxyProvider) {
pp.proxySetProvider.SetProvider(provider)
}
// CompatibleProvider for auto gc // CompatibleProvider for auto gc
type CompatibleProvider struct { type CompatibleProvider struct {
*compatibleProvider *compatibleProvider
@ -213,6 +199,7 @@ type CompatibleProvider struct {
type compatibleProvider struct { type compatibleProvider struct {
name string name string
healthCheck *HealthCheck healthCheck *HealthCheck
subscriptionInfo *SubscriptionInfo
proxies []C.Proxy proxies []C.Proxy
version uint32 version uint32
} }
@ -284,6 +271,10 @@ func (cp *compatibleProvider) Close() error {
return nil return nil
} }
func (cp *compatibleProvider) SetSubscriptionInfo(userInfo string) {
cp.subscriptionInfo = NewSubscriptionInfo(userInfo)
}
func NewCompatibleProvider(name string, proxies []C.Proxy, hc *HealthCheck) (*CompatibleProvider, error) { func NewCompatibleProvider(name string, proxies []C.Proxy, hc *HealthCheck) (*CompatibleProvider, error) {
if len(proxies) == 0 { if len(proxies) == 0 {
return nil, errors.New("provider need one proxy at least") return nil, errors.New("provider need one proxy at least")
@ -313,7 +304,6 @@ func proxiesOnUpdate(pd *proxySetProvider) func([]C.Proxy) {
return func(elm []C.Proxy) { return func(elm []C.Proxy) {
pd.setProxies(elm) pd.setProxies(elm)
pd.version += 1 pd.version += 1
pd.getSubscriptionInfo()
} }
} }

View File

@ -16,8 +16,7 @@ type SubscriptionInfo struct {
} }
func NewSubscriptionInfo(userinfo string) (si *SubscriptionInfo) { func NewSubscriptionInfo(userinfo string) (si *SubscriptionInfo) {
userinfo = strings.ToLower(userinfo) userinfo = strings.ReplaceAll(strings.ToLower(userinfo), " ", "")
userinfo = strings.ReplaceAll(userinfo, " ", "")
si = new(SubscriptionInfo) si = new(SubscriptionInfo)
for _, field := range strings.Split(userinfo, ";") { for _, field := range strings.Split(userinfo, ";") {

View File

@ -340,27 +340,19 @@ func parseAddr(ctx context.Context, network, address string, preferResolver reso
return nil, "-1", err return nil, "-1", err
} }
if preferResolver == nil {
preferResolver = resolver.ProxyServerHostResolver
}
var ips []netip.Addr var ips []netip.Addr
switch network { switch network {
case "tcp4", "udp4": case "tcp4", "udp4":
if preferResolver == nil {
ips, err = resolver.LookupIPv4ProxyServerHost(ctx, host)
} else {
ips, err = resolver.LookupIPv4WithResolver(ctx, host, preferResolver) ips, err = resolver.LookupIPv4WithResolver(ctx, host, preferResolver)
}
case "tcp6", "udp6": case "tcp6", "udp6":
if preferResolver == nil {
ips, err = resolver.LookupIPv6ProxyServerHost(ctx, host)
} else {
ips, err = resolver.LookupIPv6WithResolver(ctx, host, preferResolver) ips, err = resolver.LookupIPv6WithResolver(ctx, host, preferResolver)
}
default: default:
if preferResolver == nil {
ips, err = resolver.LookupIPProxyServerHost(ctx, host)
} else {
ips, err = resolver.LookupIPWithResolver(ctx, host, preferResolver) ips, err = resolver.LookupIPWithResolver(ctx, host, preferResolver)
} }
}
if err != nil { if err != nil {
return nil, "-1", fmt.Errorf("dns resolve failed: %w", err) return nil, "-1", fmt.Errorf("dns resolve failed: %w", err)
} }

View File

@ -1,29 +0,0 @@
package dialer
import (
"context"
"net"
"net/netip"
)
func init() {
// We must use this DialContext to query DNS
// when using net default resolver.
net.DefaultResolver.PreferGo = true
net.DefaultResolver.Dial = resolverDialContext
}
func resolverDialContext(ctx context.Context, network, address string) (net.Conn, error) {
d := &net.Dialer{}
interfaceName := DefaultInterface.Load()
if interfaceName != "" {
dstIP, err := netip.ParseAddr(address)
if err == nil {
_ = bindIfaceToDialer(interfaceName, d, network, dstIP)
}
}
return d.DialContext(ctx, network, address)
}

View File

@ -12,6 +12,7 @@ import (
"time" "time"
"github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/ca"
"github.com/metacubex/mihomo/component/dialer"
"github.com/metacubex/mihomo/listener/inner" "github.com/metacubex/mihomo/listener/inner"
) )
@ -71,8 +72,7 @@ func HttpRequestWithProxy(ctx context.Context, url, method string, header map[st
if conn, err := inner.HandleTcp(address, specialProxy); err == nil { if conn, err := inner.HandleTcp(address, specialProxy); err == nil {
return conn, nil return conn, nil
} else { } else {
d := net.Dialer{} return dialer.DialContext(ctx, network, address)
return d.DialContext(ctx, network, address)
} }
}, },
TLSClientConfig: ca.GetGlobalTLSConfig(&tls.Config{}), TLSClientConfig: ca.GetGlobalTLSConfig(&tls.Config{}),

View File

@ -20,6 +20,7 @@ var (
bucketSelected = []byte("selected") bucketSelected = []byte("selected")
bucketFakeip = []byte("fakeip") bucketFakeip = []byte("fakeip")
bucketETag = []byte("etag") bucketETag = []byte("etag")
bucketSubscriptionInfo = []byte("subscriptioninfo")
) )
// CacheFile store and update the cache file // CacheFile store and update the cache file

View File

@ -0,0 +1,41 @@
package cachefile
import (
"github.com/metacubex/mihomo/log"
"github.com/metacubex/bbolt"
)
func (c *CacheFile) SetSubscriptionInfo(name string, userInfo string) {
if c.DB == nil {
return
}
err := c.DB.Batch(func(t *bbolt.Tx) error {
bucket, err := t.CreateBucketIfNotExists(bucketSubscriptionInfo)
if err != nil {
return err
}
return bucket.Put([]byte(name), []byte(userInfo))
})
if err != nil {
log.Warnln("[CacheFile] write cache to %s failed: %s", c.DB.Path(), err.Error())
return
}
}
func (c *CacheFile) GetSubscriptionInfo(name string) (userInfo string) {
if c.DB == nil {
return
}
c.DB.View(func(t *bbolt.Tx) error {
if bucket := t.Bucket(bucketSubscriptionInfo); bucket != nil {
if v := bucket.Get([]byte(name)); v != nil {
userInfo = string(v)
}
}
return nil
})
return
}

View File

@ -1,12 +0,0 @@
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
package resolver
import _ "unsafe"
//go:linkname defaultNS net.defaultNS
var defaultNS []string
func init() {
defaultNS = []string{"114.114.114.114:53", "8.8.8.8:53"}
}

View File

@ -1,19 +0,0 @@
//go:build !go1.22
// a simple standard lib fix from: https://github.com/golang/go/commit/33d4a5105cf2b2d549922e909e9239a48b8cefcc
package resolver
import (
"golang.org/x/sys/windows"
_ "unsafe"
)
//go:linkname testHookHostsPath net.testHookHostsPath
var testHookHostsPath string
func init() {
if dir, err := windows.GetSystemDirectory(); err == nil {
testHookHostsPath = dir + "/Drivers/etc/hosts"
}
}

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"net"
"net/netip" "net/netip"
"strings" "strings"
"time" "time"
@ -20,9 +19,15 @@ var (
// DefaultResolver aim to resolve ip // DefaultResolver aim to resolve ip
DefaultResolver Resolver DefaultResolver Resolver
// ProxyServerHostResolver resolve ip to proxies server host // ProxyServerHostResolver resolve ip for proxies server host, only nil when DefaultResolver is nil
ProxyServerHostResolver Resolver ProxyServerHostResolver Resolver
// DirectHostResolver resolve ip for direct outbound host, only nil when DefaultResolver is nil
DirectHostResolver Resolver
// SystemResolver always using system dns, and was init in dns module
SystemResolver Resolver
// DisableIPv6 means don't resolve ipv6 host // DisableIPv6 means don't resolve ipv6 host
// default value is true // default value is true
DisableIPv6 = true DisableIPv6 = true
@ -72,14 +77,7 @@ func LookupIPv4WithResolver(ctx context.Context, host string, r Resolver) ([]net
return r.LookupIPv4(ctx, host) return r.LookupIPv4(ctx, host)
} }
ipAddrs, err := net.DefaultResolver.LookupNetIP(ctx, "ip4", host) return SystemResolver.LookupIPv4(ctx, host)
if err != nil {
return nil, err
} else if len(ipAddrs) == 0 {
return nil, ErrIPNotFound
}
return ipAddrs, nil
} }
// LookupIPv4 with a host, return ipv4 list // LookupIPv4 with a host, return ipv4 list
@ -128,14 +126,7 @@ func LookupIPv6WithResolver(ctx context.Context, host string, r Resolver) ([]net
return r.LookupIPv6(ctx, host) return r.LookupIPv6(ctx, host)
} }
ipAddrs, err := net.DefaultResolver.LookupNetIP(ctx, "ip6", host) return SystemResolver.LookupIPv6(ctx, host)
if err != nil {
return nil, err
} else if len(ipAddrs) == 0 {
return nil, ErrIPNotFound
}
return ipAddrs, nil
} }
// LookupIPv6 with a host, return ipv6 list // LookupIPv6 with a host, return ipv6 list
@ -177,14 +168,7 @@ func LookupIPWithResolver(ctx context.Context, host string, r Resolver) ([]netip
return []netip.Addr{ip}, nil return []netip.Addr{ip}, nil
} }
ips, err := net.DefaultResolver.LookupNetIP(ctx, "ip", host) return SystemResolver.LookupIP(ctx, host)
if err != nil {
return nil, err
} else if len(ips) == 0 {
return nil, ErrIPNotFound
}
return ips, nil
} }
// LookupIP with a host, return ip // LookupIP with a host, return ip
@ -212,58 +196,10 @@ func ResolveIP(ctx context.Context, host string) (netip.Addr, error) {
return ResolveIPWithResolver(ctx, host, DefaultResolver) return ResolveIPWithResolver(ctx, host, DefaultResolver)
} }
// ResolveIPv4ProxyServerHost proxies server host only
func ResolveIPv4ProxyServerHost(ctx context.Context, host string) (netip.Addr, error) {
if ProxyServerHostResolver != nil {
return ResolveIPv4WithResolver(ctx, host, ProxyServerHostResolver)
}
return ResolveIPv4(ctx, host)
}
// ResolveIPv6ProxyServerHost proxies server host only
func ResolveIPv6ProxyServerHost(ctx context.Context, host string) (netip.Addr, error) {
if ProxyServerHostResolver != nil {
return ResolveIPv6WithResolver(ctx, host, ProxyServerHostResolver)
}
return ResolveIPv6(ctx, host)
}
// ResolveProxyServerHost proxies server host only
func ResolveProxyServerHost(ctx context.Context, host string) (netip.Addr, error) {
if ProxyServerHostResolver != nil {
return ResolveIPWithResolver(ctx, host, ProxyServerHostResolver)
}
return ResolveIP(ctx, host)
}
func LookupIPv6ProxyServerHost(ctx context.Context, host string) ([]netip.Addr, error) {
if ProxyServerHostResolver != nil {
return LookupIPv6WithResolver(ctx, host, ProxyServerHostResolver)
}
return LookupIPv6(ctx, host)
}
func LookupIPv4ProxyServerHost(ctx context.Context, host string) ([]netip.Addr, error) {
if ProxyServerHostResolver != nil {
return LookupIPv4WithResolver(ctx, host, ProxyServerHostResolver)
}
return LookupIPv4(ctx, host)
}
func LookupIPProxyServerHost(ctx context.Context, host string) ([]netip.Addr, error) {
if ProxyServerHostResolver != nil {
return LookupIPWithResolver(ctx, host, ProxyServerHostResolver)
}
return LookupIP(ctx, host)
}
func ResetConnection() { func ResetConnection() {
if DefaultResolver != nil { if DefaultResolver != nil {
go DefaultResolver.ResetConnection() go DefaultResolver.ResetConnection()
} }
if ProxyServerHostResolver != nil {
go ProxyServerHostResolver.ResetConnection()
}
} }
func SortationAddr(ips []netip.Addr) (ipv4s, ipv6s []netip.Addr) { func SortationAddr(ips []netip.Addr) (ipv4s, ipv6s []netip.Addr) {

View File

@ -89,6 +89,7 @@ type HTTPVehicle struct {
proxy string proxy string
header http.Header header http.Header
timeout time.Duration timeout time.Duration
provider types.ProxyProvider
} }
func (h *HTTPVehicle) Url() string { func (h *HTTPVehicle) Url() string {
@ -111,6 +112,10 @@ func (h *HTTPVehicle) Write(buf []byte) error {
return safeWrite(h.path, buf) return safeWrite(h.path, buf)
} }
func (h *HTTPVehicle) SetProvider(provider types.ProxyProvider) {
h.provider = provider
}
func (h *HTTPVehicle) Read(ctx context.Context, oldHash utils.HashType) (buf []byte, hash utils.HashType, err error) { func (h *HTTPVehicle) Read(ctx context.Context, oldHash utils.HashType) (buf []byte, hash utils.HashType, err error) {
ctx, cancel := context.WithTimeout(ctx, h.timeout) ctx, cancel := context.WithTimeout(ctx, h.timeout)
defer cancel() defer cancel()
@ -133,6 +138,12 @@ func (h *HTTPVehicle) Read(ctx context.Context, oldHash utils.HashType) (buf []b
return return
} }
defer resp.Body.Close() defer resp.Body.Close()
if subscriptionInfo := resp.Header.Get("subscription-userinfo"); h.provider != nil && subscriptionInfo != "" {
cachefile.Cache().SetSubscriptionInfo(h.provider.Name(), subscriptionInfo)
h.provider.SetSubscriptionInfo(subscriptionInfo)
}
if resp.StatusCode < 200 || resp.StatusCode > 299 { if resp.StatusCode < 200 || resp.StatusCode > 299 {
if setIfNoneMatch && resp.StatusCode == http.StatusNotModified { if setIfNoneMatch && resp.StatusCode == http.StatusNotModified {
return nil, oldHash, nil return nil, oldHash, nil

View File

@ -229,20 +229,22 @@ func UpdateGeoDatabases() error {
} }
func getUpdateTime() (err error, time time.Time) { func getUpdateTime() (err error, time time.Time) {
var fileInfo os.FileInfo filesToCheck := []string{
if geodata.GeodataMode() { C.Path.GeoIP(),
fileInfo, err = os.Stat(C.Path.GeoIP()) C.Path.MMDB(),
if err != nil { C.Path.ASN(),
return err, time C.Path.GeoSite(),
} }
} else {
fileInfo, err = os.Stat(C.Path.MMDB()) for _, file := range filesToCheck {
if err != nil { var fileInfo os.FileInfo
return err, time fileInfo, err = os.Stat(file)
if err == nil {
return nil, fileInfo.ModTime()
} }
} }
return nil, fileInfo.ModTime() return
} }
func RegisterGeoUpdater() { func RegisterGeoUpdater() {

View File

@ -14,24 +14,69 @@ import (
"github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/log"
) )
var ( type UIUpdater struct {
ExternalUIURL string externalUIURL string
ExternalUIPath string externalUIPath string
AutoDownloadUI bool autoDownloadUI bool
)
var xdMutex sync.Mutex mutex sync.Mutex
}
func DownloadUI() error { var DefaultUiUpdater = &UIUpdater{}
xdMutex.Lock()
defer xdMutex.Unlock()
err := prepareUIPath() func NewUiUpdater(externalUI, externalUIURL, externalUIName string) *UIUpdater {
updater := &UIUpdater{}
// checkout externalUI exist
if externalUI != "" {
updater.autoDownloadUI = true
updater.externalUIPath = C.Path.Resolve(externalUI)
} else {
// default externalUI path
updater.externalUIPath = path.Join(C.Path.HomeDir(), "ui")
}
// checkout UIpath/name exist
if externalUIName != "" {
updater.autoDownloadUI = true
updater.externalUIPath = path.Join(updater.externalUIPath, externalUIName)
}
if externalUIURL != "" {
updater.externalUIURL = externalUIURL
}
return updater
}
func (u *UIUpdater) AutoDownloadUI() {
u.mutex.Lock()
defer u.mutex.Unlock()
if u.autoDownloadUI {
dirEntries, _ := os.ReadDir(u.externalUIPath)
if len(dirEntries) > 0 {
log.Infoln("UI already exists, skip downloading")
} else {
log.Infoln("External UI downloading ...")
err := u.downloadUI()
if err != nil {
log.Errorln("Error downloading UI: %s", err)
}
}
}
}
func (u *UIUpdater) DownloadUI() error {
u.mutex.Lock()
defer u.mutex.Unlock()
return u.downloadUI()
}
func (u *UIUpdater) downloadUI() error {
err := u.prepareUIPath()
if err != nil { if err != nil {
return fmt.Errorf("prepare UI path failed: %w", err) return fmt.Errorf("prepare UI path failed: %w", err)
} }
data, err := downloadForBytes(ExternalUIURL) data, err := downloadForBytes(u.externalUIURL)
if err != nil { if err != nil {
return fmt.Errorf("can't download file: %w", err) return fmt.Errorf("can't download file: %w", err)
} }
@ -42,7 +87,7 @@ func DownloadUI() error {
} }
defer os.Remove(saved) defer os.Remove(saved)
err = cleanup(ExternalUIPath) err = cleanup(u.externalUIPath)
if err != nil { if err != nil {
if !os.IsNotExist(err) { if !os.IsNotExist(err) {
return fmt.Errorf("cleanup exist file error: %w", err) return fmt.Errorf("cleanup exist file error: %w", err)
@ -54,18 +99,18 @@ func DownloadUI() error {
return fmt.Errorf("can't extract zip file: %w", err) return fmt.Errorf("can't extract zip file: %w", err)
} }
err = os.Rename(unzipFolder, ExternalUIPath) err = os.Rename(unzipFolder, u.externalUIPath)
if err != nil { if err != nil {
return fmt.Errorf("rename UI folder failed: %w", err) return fmt.Errorf("rename UI folder failed: %w", err)
} }
return nil return nil
} }
func prepareUIPath() error { func (u *UIUpdater) prepareUIPath() error {
if _, err := os.Stat(ExternalUIPath); os.IsNotExist(err) { if _, err := os.Stat(u.externalUIPath); os.IsNotExist(err) {
log.Infoln("dir %s does not exist, creating", ExternalUIPath) log.Infoln("dir %s does not exist, creating", u.externalUIPath)
if err := os.MkdirAll(ExternalUIPath, os.ModePerm); err != nil { if err := os.MkdirAll(u.externalUIPath, os.ModePerm); err != nil {
log.Warnln("create dir %s error: %s", ExternalUIPath, err) log.Warnln("create dir %s error: %s", u.externalUIPath, err)
} }
} }
return nil return nil

View File

@ -7,9 +7,9 @@ import (
"net" "net"
"net/netip" "net/netip"
"net/url" "net/url"
"path"
"strings" "strings"
"time" "time"
_ "unsafe"
"github.com/metacubex/mihomo/adapter" "github.com/metacubex/mihomo/adapter"
"github.com/metacubex/mihomo/adapter/outbound" "github.com/metacubex/mihomo/adapter/outbound"
@ -20,15 +20,10 @@ import (
"github.com/metacubex/mihomo/component/cidr" "github.com/metacubex/mihomo/component/cidr"
"github.com/metacubex/mihomo/component/fakeip" "github.com/metacubex/mihomo/component/fakeip"
"github.com/metacubex/mihomo/component/geodata" "github.com/metacubex/mihomo/component/geodata"
mihomoHttp "github.com/metacubex/mihomo/component/http"
"github.com/metacubex/mihomo/component/keepalive"
P "github.com/metacubex/mihomo/component/process" P "github.com/metacubex/mihomo/component/process"
"github.com/metacubex/mihomo/component/resolver" "github.com/metacubex/mihomo/component/resolver"
"github.com/metacubex/mihomo/component/resource"
"github.com/metacubex/mihomo/component/sniffer" "github.com/metacubex/mihomo/component/sniffer"
tlsC "github.com/metacubex/mihomo/component/tls"
"github.com/metacubex/mihomo/component/trie" "github.com/metacubex/mihomo/component/trie"
"github.com/metacubex/mihomo/component/updater"
C "github.com/metacubex/mihomo/constant" C "github.com/metacubex/mihomo/constant"
providerTypes "github.com/metacubex/mihomo/constant/provider" providerTypes "github.com/metacubex/mihomo/constant/provider"
snifferTypes "github.com/metacubex/mihomo/constant/sniffer" snifferTypes "github.com/metacubex/mihomo/constant/sniffer"
@ -67,6 +62,9 @@ type General struct {
GlobalClientFingerprint string `json:"global-client-fingerprint"` GlobalClientFingerprint string `json:"global-client-fingerprint"`
GlobalUA string `json:"global-ua"` GlobalUA string `json:"global-ua"`
ETagSupport bool `json:"etag-support"` ETagSupport bool `json:"etag-support"`
KeepAliveIdle int `json:"keep-alive-idle"`
KeepAliveInterval int `json:"keep-alive-interval"`
DisableKeepAlive bool `json:"disable-keep-alive"`
} }
// Inbound config // Inbound config
@ -105,6 +103,8 @@ type Controller struct {
ExternalControllerUnix string ExternalControllerUnix string
ExternalControllerPipe string ExternalControllerPipe string
ExternalUI string ExternalUI string
ExternalUIURL string
ExternalUIName string
ExternalDohServer string ExternalDohServer string
Secret string Secret string
Cors Cors Cors Cors
@ -160,6 +160,8 @@ type DNS struct {
Hosts *trie.DomainTrie[resolver.HostValue] Hosts *trie.DomainTrie[resolver.HostValue]
NameServerPolicy []dns.Policy NameServerPolicy []dns.Policy
ProxyServerNameserver []dns.NameServer ProxyServerNameserver []dns.NameServer
DirectNameServer []dns.NameServer
DirectFollowPolicy bool
} }
// Profile config // Profile config
@ -222,6 +224,8 @@ type RawDNS struct {
CacheAlgorithm string `yaml:"cache-algorithm" json:"cache-algorithm"` CacheAlgorithm string `yaml:"cache-algorithm" json:"cache-algorithm"`
NameServerPolicy *orderedmap.OrderedMap[string, any] `yaml:"nameserver-policy" json:"nameserver-policy"` NameServerPolicy *orderedmap.OrderedMap[string, any] `yaml:"nameserver-policy" json:"nameserver-policy"`
ProxyServerNameserver []string `yaml:"proxy-server-nameserver" json:"proxy-server-nameserver"` ProxyServerNameserver []string `yaml:"proxy-server-nameserver" json:"proxy-server-nameserver"`
DirectNameServer []string `yaml:"direct-nameserver" json:"direct-nameserver"`
DirectNameServerFollowPolicy bool `yaml:"direct-nameserver-follow-policy" json:"direct-nameserver-follow-policy"`
} }
type RawFallbackFilter struct { type RawFallbackFilter struct {
@ -582,10 +586,11 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
} }
config.General = general config.General = general
if len(config.General.GlobalClientFingerprint) != 0 { // We need to temporarily apply some configuration in general and roll back after parsing the complete configuration.
log.Debugln("GlobalClientFingerprint: %s", config.General.GlobalClientFingerprint) // The loading and downloading of geodata in the parseRules and parseRuleProviders rely on these.
tlsC.SetGlobalUtlsClient(config.General.GlobalClientFingerprint) // This implementation is very disgusting, but there is currently no better solution
} rollback := temporaryUpdateGeneral(config.General)
defer rollback()
controller, err := parseController(rawCfg) controller, err := parseController(rawCfg)
if err != nil { if err != nil {
@ -701,46 +706,10 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
return config, nil return config, nil
} }
//go:linkname temporaryUpdateGeneral
func temporaryUpdateGeneral(general *General) func()
func parseGeneral(cfg *RawConfig) (*General, error) { func parseGeneral(cfg *RawConfig) (*General, error) {
updater.SetGeoAutoUpdate(cfg.GeoAutoUpdate)
updater.SetGeoUpdateInterval(cfg.GeoUpdateInterval)
geodata.SetGeodataMode(cfg.GeodataMode)
geodata.SetLoader(cfg.GeodataLoader)
geodata.SetSiteMatcher(cfg.GeositeMatcher)
geodata.SetGeoIpUrl(cfg.GeoXUrl.GeoIp)
geodata.SetGeoSiteUrl(cfg.GeoXUrl.GeoSite)
geodata.SetMmdbUrl(cfg.GeoXUrl.Mmdb)
geodata.SetASNUrl(cfg.GeoXUrl.ASN)
mihomoHttp.SetUA(cfg.GlobalUA)
resource.SetETag(cfg.ETagSupport)
if cfg.KeepAliveIdle != 0 {
keepalive.SetKeepAliveIdle(time.Duration(cfg.KeepAliveIdle) * time.Second)
}
if cfg.KeepAliveInterval != 0 {
keepalive.SetKeepAliveInterval(time.Duration(cfg.KeepAliveInterval) * time.Second)
}
keepalive.SetDisableKeepAlive(cfg.DisableKeepAlive)
// checkout externalUI exist
if cfg.ExternalUI != "" {
updater.AutoDownloadUI = true
updater.ExternalUIPath = C.Path.Resolve(cfg.ExternalUI)
} else {
// default externalUI path
updater.ExternalUIPath = path.Join(C.Path.HomeDir(), "ui")
}
// checkout UIpath/name exist
if cfg.ExternalUIName != "" {
updater.AutoDownloadUI = true
updater.ExternalUIPath = path.Join(updater.ExternalUIPath, cfg.ExternalUIName)
}
if cfg.ExternalUIURL != "" {
updater.ExternalUIURL = cfg.ExternalUIURL
}
return &General{ return &General{
Inbound: Inbound{ Inbound: Inbound{
Port: cfg.Port, Port: cfg.Port,
@ -774,11 +743,15 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
GeoUpdateInterval: cfg.GeoUpdateInterval, GeoUpdateInterval: cfg.GeoUpdateInterval,
GeodataMode: cfg.GeodataMode, GeodataMode: cfg.GeodataMode,
GeodataLoader: cfg.GeodataLoader, GeodataLoader: cfg.GeodataLoader,
GeositeMatcher: cfg.GeositeMatcher,
TCPConcurrent: cfg.TCPConcurrent, TCPConcurrent: cfg.TCPConcurrent,
FindProcessMode: cfg.FindProcessMode, FindProcessMode: cfg.FindProcessMode,
GlobalClientFingerprint: cfg.GlobalClientFingerprint, GlobalClientFingerprint: cfg.GlobalClientFingerprint,
GlobalUA: cfg.GlobalUA, GlobalUA: cfg.GlobalUA,
ETagSupport: cfg.ETagSupport, ETagSupport: cfg.ETagSupport,
KeepAliveIdle: cfg.KeepAliveIdle,
KeepAliveInterval: cfg.KeepAliveInterval,
DisableKeepAlive: cfg.DisableKeepAlive,
}, nil }, nil
} }
@ -786,6 +759,8 @@ func parseController(cfg *RawConfig) (*Controller, error) {
return &Controller{ return &Controller{
ExternalController: cfg.ExternalController, ExternalController: cfg.ExternalController,
ExternalUI: cfg.ExternalUI, ExternalUI: cfg.ExternalUI,
ExternalUIURL: cfg.ExternalUIURL,
ExternalUIName: cfg.ExternalUIName,
Secret: cfg.Secret, Secret: cfg.Secret,
ExternalControllerPipe: cfg.ExternalControllerPipe, ExternalControllerPipe: cfg.ExternalControllerPipe,
ExternalControllerUnix: cfg.ExternalControllerUnix, ExternalControllerUnix: cfg.ExternalControllerUnix,
@ -1423,6 +1398,11 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul
return nil, err return nil, err
} }
if dnsCfg.DirectNameServer, err = parseNameServer(cfg.DirectNameServer, false, cfg.PreferH3); err != nil {
return nil, err
}
dnsCfg.DirectFollowPolicy = cfg.DirectNameServerFollowPolicy
if len(cfg.DefaultNameserver) == 0 { if len(cfg.DefaultNameserver) == 0 {
return nil, errors.New("default nameserver should have at least one nameserver") return nil, errors.New("default nameserver should have at least one nameserver")
} }

View File

@ -213,6 +213,8 @@ func (at AdapterType) String() string {
return "WireGuard" return "WireGuard"
case Tuic: case Tuic:
return "Tuic" return "Tuic"
case Ssh:
return "Ssh"
case Relay: case Relay:
return "Relay" return "Relay"
@ -224,8 +226,6 @@ func (at AdapterType) String() string {
return "URLTest" return "URLTest"
case LoadBalance: case LoadBalance:
return "LoadBalance" return "LoadBalance"
case Ssh:
return "Ssh"
default: default:
return "Unknown" return "Unknown"
} }

View File

@ -81,6 +81,7 @@ type ProxyProvider interface {
Version() uint32 Version() uint32
RegisterHealthCheckTask(url string, expectedStatus utils.IntRanges[uint16], filter string, interval uint) RegisterHealthCheckTask(url string, expectedStatus utils.IntRanges[uint16], filter string, interval uint)
HealthCheckURL() string HealthCheckURL() string
SetSubscriptionInfo(userInfo string)
} }
// RuleProvider interface // RuleProvider interface

View File

@ -12,6 +12,9 @@ func FlushCacheWithDefaultResolver() {
if r := resolver.DefaultResolver; r != nil { if r := resolver.DefaultResolver; r != nil {
r.ClearCache() r.ClearCache()
} }
if r := resolver.SystemResolver; r != nil {
r.ClearCache()
}
resolver.ResetConnection() resolver.ResetConnection()
} }

View File

@ -13,7 +13,6 @@ import (
"github.com/metacubex/mihomo/component/resolver" "github.com/metacubex/mihomo/component/resolver"
"github.com/metacubex/mihomo/component/trie" "github.com/metacubex/mihomo/component/trie"
C "github.com/metacubex/mihomo/constant" C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/constant/provider"
"github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/log"
D "github.com/miekg/dns" D "github.com/miekg/dns"
@ -428,6 +427,8 @@ type Config struct {
Main, Fallback []NameServer Main, Fallback []NameServer
Default []NameServer Default []NameServer
ProxyServer []NameServer ProxyServer []NameServer
DirectServer []NameServer
DirectFollowPolicy bool
IPv6 bool IPv6 bool
IPv6Timeout uint IPv6Timeout uint
EnhancedMode C.DNSMode EnhancedMode C.DNSMode
@ -436,7 +437,6 @@ type Config struct {
Pool *fakeip.Pool Pool *fakeip.Pool
Hosts *trie.DomainTrie[resolver.HostValue] Hosts *trie.DomainTrie[resolver.HostValue]
Policy []Policy Policy []Policy
Tunnel provider.Tunnel
CacheAlgorithm string CacheAlgorithm string
} }
@ -448,7 +448,25 @@ func (config Config) newCache() dnsCache {
} }
} }
func NewResolver(config Config) (r *Resolver, pr *Resolver) { type Resolvers struct {
*Resolver
ProxyResolver *Resolver
DirectResolver *Resolver
}
func (rs Resolvers) ClearCache() {
rs.Resolver.ClearCache()
rs.ProxyResolver.ClearCache()
rs.DirectResolver.ClearCache()
}
func (rs Resolvers) ResetConnection() {
rs.Resolver.ResetConnection()
rs.ProxyResolver.ResetConnection()
rs.DirectResolver.ResetConnection()
}
func NewResolver(config Config) (rs Resolvers) {
defaultResolver := &Resolver{ defaultResolver := &Resolver{
main: transform(config.Default, nil), main: transform(config.Default, nil),
cache: config.newCache(), cache: config.newCache(),
@ -482,7 +500,7 @@ func NewResolver(config Config) (r *Resolver, pr *Resolver) {
return return
} }
r = &Resolver{ r := &Resolver{
ipv6: config.IPv6, ipv6: config.IPv6,
main: cacheTransform(config.Main), main: cacheTransform(config.Main),
cache: config.newCache(), cache: config.newCache(),
@ -490,9 +508,10 @@ func NewResolver(config Config) (r *Resolver, pr *Resolver) {
ipv6Timeout: time.Duration(config.IPv6Timeout) * time.Millisecond, ipv6Timeout: time.Duration(config.IPv6Timeout) * time.Millisecond,
} }
r.defaultResolver = defaultResolver r.defaultResolver = defaultResolver
rs.Resolver = r
if len(config.ProxyServer) != 0 { if len(config.ProxyServer) != 0 {
pr = &Resolver{ rs.ProxyResolver = &Resolver{
ipv6: config.IPv6, ipv6: config.IPv6,
main: cacheTransform(config.ProxyServer), main: cacheTransform(config.ProxyServer),
cache: config.newCache(), cache: config.newCache(),
@ -501,8 +520,20 @@ func NewResolver(config Config) (r *Resolver, pr *Resolver) {
} }
} }
if len(config.DirectServer) != 0 {
rs.DirectResolver = &Resolver{
ipv6: config.IPv6,
main: cacheTransform(config.DirectServer),
cache: config.newCache(),
hosts: config.Hosts,
ipv6Timeout: time.Duration(config.IPv6Timeout) * time.Millisecond,
}
}
if len(config.Fallback) != 0 { if len(config.Fallback) != 0 {
r.fallback = cacheTransform(config.Fallback) r.fallback = cacheTransform(config.Fallback)
r.fallbackIPFilters = config.FallbackIPFilter
r.fallbackDomainFilters = config.FallbackDomainFilter
} }
if len(config.Policy) != 0 { if len(config.Policy) != 0 {
@ -531,9 +562,11 @@ func NewResolver(config Config) (r *Resolver, pr *Resolver) {
} }
} }
insertPolicy(nil) insertPolicy(nil)
if rs.DirectResolver != nil && config.DirectFollowPolicy {
rs.DirectResolver.policy = r.policy
}
} }
r.fallbackIPFilters = config.FallbackIPFilter
r.fallbackDomainFilters = config.FallbackDomainFilter
return return
} }

View File

@ -7,6 +7,8 @@ import (
"sync" "sync"
"time" "time"
"github.com/metacubex/mihomo/component/resolver"
D "github.com/miekg/dns" D "github.com/miekg/dns"
) )
@ -24,10 +26,15 @@ type systemClient struct {
mu sync.Mutex mu sync.Mutex
dnsClients map[string]*systemDnsClient dnsClients map[string]*systemDnsClient
lastFlush time.Time lastFlush time.Time
defaultNS []dnsClient
} }
func (c *systemClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { func (c *systemClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) {
dnsClients, err := c.getDnsClients() dnsClients, err := c.getDnsClients()
if len(dnsClients) == 0 && len(c.defaultNS) > 0 {
dnsClients = c.defaultNS
err = nil
}
if err != nil { if err != nil {
return return
} }
@ -38,11 +45,16 @@ func (c *systemClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Ms
// Address implements dnsClient // Address implements dnsClient
func (c *systemClient) Address() string { func (c *systemClient) Address() string {
dnsClients, _ := c.getDnsClients() dnsClients, _ := c.getDnsClients()
isDefault := ""
if len(dnsClients) == 0 && len(c.defaultNS) > 0 {
dnsClients = c.defaultNS
isDefault = "[defaultNS]"
}
addrs := make([]string, 0, len(dnsClients)) addrs := make([]string, 0, len(dnsClients))
for _, c := range dnsClients { for _, c := range dnsClients {
addrs = append(addrs, c.Address()) addrs = append(addrs, c.Address())
} }
return fmt.Sprintf("system(%s)", strings.Join(addrs, ",")) return fmt.Sprintf("system%s(%s)", isDefault, strings.Join(addrs, ","))
} }
var _ dnsClient = (*systemClient)(nil) var _ dnsClient = (*systemClient)(nil)
@ -52,3 +64,11 @@ func newSystemClient() *systemClient {
dnsClients: map[string]*systemDnsClient{}, dnsClients: map[string]*systemDnsClient{},
} }
} }
func init() {
r := NewResolver(Config{})
c := newSystemClient()
c.defaultNS = transform([]NameServer{{Addr: "114.114.114.114:53"}, {Addr: "8.8.8.8:53"}}, nil)
r.main = []dnsClient{c}
resolver.SystemResolver = r
}

View File

@ -294,11 +294,16 @@ dns:
# - tcp://1.1.1.1 # - tcp://1.1.1.1
# - 'tcp://1.1.1.1#ProxyGroupName' # 指定 DNS 过代理查询ProxyGroupName 为策略组名或节点名,过代理配置优先于配置出口网卡,当找不到策略组或节点名则设置为出口网卡 # - 'tcp://1.1.1.1#ProxyGroupName' # 指定 DNS 过代理查询ProxyGroupName 为策略组名或节点名,过代理配置优先于配置出口网卡,当找不到策略组或节点名则设置为出口网卡
# 专用于节点域名解析的 DNS 服务器,非必要配置项 # 专用于节点域名解析的 DNS 服务器,非必要配置项如果不填则遵循nameserver-policy、nameserver和fallback的配置
# proxy-server-nameserver: # proxy-server-nameserver:
# - https://dns.google/dns-query # - https://dns.google/dns-query
# - tls://one.one.one.one # - tls://one.one.one.one
# 专用于direct出口域名解析的 DNS 服务器非必要配置项如果不填则遵循nameserver-policy、nameserver和fallback的配置
# direct-nameserver:
# - system://
# direct-nameserver-follow-policy: false # 是否遵循nameserver-policy默认为不遵守仅当direct-nameserver不为空时生效
# 配置 fallback 使用条件 # 配置 fallback 使用条件
# fallback-filter: # fallback-filter:
# geoip: true # 配置是否使用 geoip # geoip: true # 配置是否使用 geoip

24
go.mod
View File

@ -5,13 +5,13 @@ go 1.20
require ( require (
github.com/3andne/restls-client-go v0.1.6 github.com/3andne/restls-client-go v0.1.6
github.com/bahlo/generic-list-go v0.2.0 github.com/bahlo/generic-list-go v0.2.0
github.com/coreos/go-iptables v0.7.0 github.com/coreos/go-iptables v0.8.0
github.com/dlclark/regexp2 v1.11.4 github.com/dlclark/regexp2 v1.11.4
github.com/go-chi/chi/v5 v5.1.0 github.com/go-chi/chi/v5 v5.1.0
github.com/go-chi/render v1.0.3 github.com/go-chi/render v1.0.3
github.com/gobwas/ws v1.4.0 github.com/gobwas/ws v1.4.0
github.com/gofrs/uuid/v5 v5.3.0 github.com/gofrs/uuid/v5 v5.3.0
github.com/insomniacslk/dhcp v0.0.0-20240812123929-b105c29bd1b5 github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475
github.com/klauspost/compress v1.17.9 github.com/klauspost/compress v1.17.9
github.com/klauspost/cpuid/v2 v2.2.8 github.com/klauspost/cpuid/v2 v2.2.8
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40
@ -20,15 +20,15 @@ require (
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399
github.com/metacubex/chacha v0.1.0 github.com/metacubex/chacha v0.1.0
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759
github.com/metacubex/quic-go v0.47.1-0.20240909010619-6b38f24bfcc4 github.com/metacubex/quic-go v0.48.1-0.20241021013658-51ca987e0174
github.com/metacubex/randv2 v0.2.0 github.com/metacubex/randv2 v0.2.0
github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4 github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4
github.com/metacubex/sing-shadowsocks v0.2.8 github.com/metacubex/sing-shadowsocks v0.2.8
github.com/metacubex/sing-shadowsocks2 v0.2.2 github.com/metacubex/sing-shadowsocks2 v0.2.2
github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1 github.com/metacubex/sing-tun v0.2.7-0.20241021011113-857bcd6ee47c
github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9
github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3 github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3
github.com/metacubex/tfo-go v0.0.0-20240830120620-c5e019b67785 github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa
github.com/metacubex/utls v1.6.6 github.com/metacubex/utls v1.6.6
github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181
github.com/miekg/dns v1.1.62 github.com/miekg/dns v1.1.62
@ -39,7 +39,7 @@ require (
github.com/sagernet/cors v1.2.1 github.com/sagernet/cors v1.2.1
github.com/sagernet/fswatch v0.1.1 github.com/sagernet/fswatch v0.1.1
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a
github.com/sagernet/sing v0.5.0-alpha.13 github.com/sagernet/sing v0.5.0-rc.4
github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6
github.com/sagernet/sing-shadowtls v0.1.4 github.com/sagernet/sing-shadowtls v0.1.4
github.com/samber/lo v1.47.0 github.com/samber/lo v1.47.0
@ -51,10 +51,10 @@ require (
gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7 gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7
go.uber.org/automaxprocs v1.6.0 go.uber.org/automaxprocs v1.6.0
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
golang.org/x/crypto v0.27.0 golang.org/x/crypto v0.28.0
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // lastest version compatible with golang1.20
golang.org/x/net v0.29.0 golang.org/x/net v0.30.0
golang.org/x/sys v0.25.0 golang.org/x/sys v0.26.0
google.golang.org/protobuf v1.34.2 google.golang.org/protobuf v1.34.2
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
lukechampine.com/blake3 v1.3.0 lukechampine.com/blake3 v1.3.0
@ -111,9 +111,9 @@ require (
go.uber.org/mock v0.4.0 // indirect go.uber.org/mock v0.4.0 // indirect
golang.org/x/mod v0.20.0 // indirect golang.org/x/mod v0.20.0 // indirect
golang.org/x/sync v0.8.0 // indirect golang.org/x/sync v0.8.0 // indirect
golang.org/x/text v0.18.0 // indirect golang.org/x/text v0.19.0 // indirect
golang.org/x/time v0.5.0 // indirect golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.24.0 // indirect golang.org/x/tools v0.24.0 // indirect
) )
replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240724044459-6f3cf5896297 replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20241021005542-18b67490300a

46
go.sum
View File

@ -19,8 +19,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8= github.com/coreos/go-iptables v0.8.0 h1:MPc2P89IhuVpLI7ETL/2tx3XZ61VeICZjYqDEgNsPRc=
github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= github.com/coreos/go-iptables v0.8.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@ -71,8 +71,8 @@ github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/insomniacslk/dhcp v0.0.0-20240812123929-b105c29bd1b5 h1:GkMacU5ftc+IEg1449N3UEy2XLDz58W4fkrRu2fibb8= github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475 h1:hxST5pwMBEOWmxpkX20w9oZG+hXdhKmAIPQ3NGGAxas=
github.com/insomniacslk/dhcp v0.0.0-20240812123929-b105c29bd1b5/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic= github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
@ -104,26 +104,26 @@ github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvO
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88= github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88=
github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec h1:HxreOiFTUrJXJautEo8rnE1uKTVGY8wtZepY1Tii/Nc= github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec h1:HxreOiFTUrJXJautEo8rnE1uKTVGY8wtZepY1Tii/Nc=
github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw= github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw=
github.com/metacubex/quic-go v0.47.1-0.20240909010619-6b38f24bfcc4 h1:CgdUBRxmNlxEGkp35HwvgQ10jwOOUJKWdOxpi8yWi8o= github.com/metacubex/quic-go v0.48.1-0.20241021013658-51ca987e0174 h1:GvigRPEU+cbnzdLWne47cxLrc28Abohl3ECtVdnrbq0=
github.com/metacubex/quic-go v0.47.1-0.20240909010619-6b38f24bfcc4/go.mod h1:Y7yRGqFE6UQL/3aKPYmiYdjfVkeujJaStP4+jiZMcN8= github.com/metacubex/quic-go v0.48.1-0.20241021013658-51ca987e0174/go.mod h1:AiZ+UPgrkO1DTnmiAX4b+kRoV1Vfc65UkYD7RbFlIZA=
github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs=
github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY=
github.com/metacubex/sing v0.0.0-20240724044459-6f3cf5896297 h1:YG/JkwGPbca5rUtEMHIu8ZuqzR7BSVm1iqY8hNoMeMA= github.com/metacubex/sing v0.0.0-20241021005542-18b67490300a h1:JuR0/7RDxQtlZp/GOzrdqNq04HplTxavPRHrek8ouJk=
github.com/metacubex/sing v0.0.0-20240724044459-6f3cf5896297/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/metacubex/sing v0.0.0-20241021005542-18b67490300a/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4 h1:HobpULaPK6OoxrHMmgcwLkwwIduXVmwdcznwUfH1GQM= github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4 h1:HobpULaPK6OoxrHMmgcwLkwwIduXVmwdcznwUfH1GQM=
github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4/go.mod h1:g7Mxj7b7zm7YVqD975mk/hSmrb0A0G4bVvIMr2MMzn8= github.com/metacubex/sing-quic v0.0.0-20240827003841-cd97758ed8b4/go.mod h1:g7Mxj7b7zm7YVqD975mk/hSmrb0A0G4bVvIMr2MMzn8=
github.com/metacubex/sing-shadowsocks v0.2.8 h1:wIhlaigswzjPw4hej75sEvWte3QR0+AJRafgwBHO5B4= github.com/metacubex/sing-shadowsocks v0.2.8 h1:wIhlaigswzjPw4hej75sEvWte3QR0+AJRafgwBHO5B4=
github.com/metacubex/sing-shadowsocks v0.2.8/go.mod h1:X3x88XtJpBxG0W0/ECOJL6Ib0SJ3xdniAkU/6/RMWU0= github.com/metacubex/sing-shadowsocks v0.2.8/go.mod h1:X3x88XtJpBxG0W0/ECOJL6Ib0SJ3xdniAkU/6/RMWU0=
github.com/metacubex/sing-shadowsocks2 v0.2.2 h1:eaf42uVx4Lr21S6MDYs0ZdTvGA0GEhDpb9no4+gdXPo= github.com/metacubex/sing-shadowsocks2 v0.2.2 h1:eaf42uVx4Lr21S6MDYs0ZdTvGA0GEhDpb9no4+gdXPo=
github.com/metacubex/sing-shadowsocks2 v0.2.2/go.mod h1:BhOug03a/RbI7y6hp6q+6ITM1dXjnLTmeWBHSTwvv2Q= github.com/metacubex/sing-shadowsocks2 v0.2.2/go.mod h1:BhOug03a/RbI7y6hp6q+6ITM1dXjnLTmeWBHSTwvv2Q=
github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1 h1:ypfofGDZbP8p3Y4P/m74JYu7sQViesi3c8nbmT6cS0Y= github.com/metacubex/sing-tun v0.2.7-0.20241021011113-857bcd6ee47c h1:qfUZ8xBrViOCZamvcC8CyV7Ii8sAUrn7RqZxFGn56tQ=
github.com/metacubex/sing-tun v0.2.7-0.20240729131039-ed03f557dee1/go.mod h1:olbEx9yVcaw5tHTNlRamRoxmMKcvDvcVS1YLnQGzvWE= github.com/metacubex/sing-tun v0.2.7-0.20241021011113-857bcd6ee47c/go.mod h1:lCrP0AW7ieKnXG1JEeZLW+9h99QzjuOX0MfCQfz6TgE=
github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 h1:OAXiCosqY8xKDp3pqTW3qbrCprZ1l6WkrXSFSCwyY4I= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9 h1:OAXiCosqY8xKDp3pqTW3qbrCprZ1l6WkrXSFSCwyY4I=
github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-vmess v0.1.9-0.20240719134745-1df6fb20bbf9/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY=
github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3 h1:xg71VmzLS6ByAzi/57phwDvjE+dLLs+ozH00k4DnOns= github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3 h1:xg71VmzLS6ByAzi/57phwDvjE+dLLs+ozH00k4DnOns=
github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3/go.mod h1:6nitcmzPDL3MXnLdhu6Hm126Zk4S1fBbX3P7jxUxSFw= github.com/metacubex/sing-wireguard v0.0.0-20240924052438-b0976fc59ea3/go.mod h1:6nitcmzPDL3MXnLdhu6Hm126Zk4S1fBbX3P7jxUxSFw=
github.com/metacubex/tfo-go v0.0.0-20240830120620-c5e019b67785 h1:NNmI+ZV0DzNuqaAInRQuZFLHlWVuyHeow8jYpdKjHjo= github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa h1:9mcjV+RGZVC3reJBNDjjNPyS8PmFG97zq56X7WNaFO4=
github.com/metacubex/tfo-go v0.0.0-20240830120620-c5e019b67785/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa/go.mod h1:4tLB5c8U0CxpkFM+AJJB77jEaVDbLH5XQvy42vAGsWw=
github.com/metacubex/utls v1.6.6 h1:3D12YKHTf2Z41UPhQU2dWerNWJ5TVQD9gKoQ+H+iLC8= github.com/metacubex/utls v1.6.6 h1:3D12YKHTf2Z41UPhQU2dWerNWJ5TVQD9gKoQ+H+iLC8=
github.com/metacubex/utls v1.6.6/go.mod h1:+WLFUnXjcpdxXCnyX25nggw8C6YonZ8zOK2Zm/oRvdo= github.com/metacubex/utls v1.6.6/go.mod h1:+WLFUnXjcpdxXCnyX25nggw8C6YonZ8zOK2Zm/oRvdo=
github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 h1:hJLQviGySBuaynlCwf/oYgIxbVbGRUIKZCxdya9YrbQ= github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 h1:hJLQviGySBuaynlCwf/oYgIxbVbGRUIKZCxdya9YrbQ=
@ -230,18 +230,18 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI= golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk=
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
@ -261,12 +261,12 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=

View File

@ -9,6 +9,7 @@ import (
"strconv" "strconv"
"sync" "sync"
"time" "time"
_ "unsafe"
"github.com/metacubex/mihomo/adapter" "github.com/metacubex/mihomo/adapter"
"github.com/metacubex/mihomo/adapter/inbound" "github.com/metacubex/mihomo/adapter/inbound"
@ -16,9 +17,10 @@ import (
"github.com/metacubex/mihomo/component/auth" "github.com/metacubex/mihomo/component/auth"
"github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/ca"
"github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/dialer"
G "github.com/metacubex/mihomo/component/geodata" "github.com/metacubex/mihomo/component/geodata"
mihomoHttp "github.com/metacubex/mihomo/component/http" mihomoHttp "github.com/metacubex/mihomo/component/http"
"github.com/metacubex/mihomo/component/iface" "github.com/metacubex/mihomo/component/iface"
"github.com/metacubex/mihomo/component/keepalive"
"github.com/metacubex/mihomo/component/profile" "github.com/metacubex/mihomo/component/profile"
"github.com/metacubex/mihomo/component/profile/cachefile" "github.com/metacubex/mihomo/component/profile/cachefile"
"github.com/metacubex/mihomo/component/resolver" "github.com/metacubex/mihomo/component/resolver"
@ -100,7 +102,7 @@ func ApplyConfig(cfg *config.Config, force bool) {
updateRules(cfg.Rules, cfg.SubRules, cfg.RuleProviders) updateRules(cfg.Rules, cfg.SubRules, cfg.RuleProviders)
updateSniffer(cfg.Sniffer) updateSniffer(cfg.Sniffer)
updateHosts(cfg.Hosts) updateHosts(cfg.Hosts)
updateGeneral(cfg.General) updateGeneral(cfg.General, true)
updateNTP(cfg.NTP) updateNTP(cfg.NTP)
updateDNS(cfg.DNS, cfg.General.IPv6) updateDNS(cfg.DNS, cfg.General.IPv6)
updateListeners(cfg.General, cfg.Listeners, force) updateListeners(cfg.General, cfg.Listeners, force)
@ -117,7 +119,7 @@ func ApplyConfig(cfg *config.Config, force bool) {
runtime.GC() runtime.GC()
tunnel.OnRunning() tunnel.OnRunning()
hcCompatibleProvider(cfg.Providers) hcCompatibleProvider(cfg.Providers)
initExternalUI() updateUpdater(cfg)
resolver.ResetConnection() resolver.ResetConnection()
} }
@ -160,22 +162,25 @@ func GetGeneral() *config.General {
Interface: dialer.DefaultInterface.Load(), Interface: dialer.DefaultInterface.Load(),
RoutingMark: int(dialer.DefaultRoutingMark.Load()), RoutingMark: int(dialer.DefaultRoutingMark.Load()),
GeoXUrl: config.GeoXUrl{ GeoXUrl: config.GeoXUrl{
GeoIp: G.GeoIpUrl(), GeoIp: geodata.GeoIpUrl(),
Mmdb: G.MmdbUrl(), Mmdb: geodata.MmdbUrl(),
ASN: G.ASNUrl(), ASN: geodata.ASNUrl(),
GeoSite: G.GeoSiteUrl(), GeoSite: geodata.GeoSiteUrl(),
}, },
GeoAutoUpdate: updater.GeoAutoUpdate(), GeoAutoUpdate: updater.GeoAutoUpdate(),
GeoUpdateInterval: updater.GeoUpdateInterval(), GeoUpdateInterval: updater.GeoUpdateInterval(),
GeodataMode: G.GeodataMode(), GeodataMode: geodata.GeodataMode(),
GeodataLoader: G.LoaderName(), GeodataLoader: geodata.LoaderName(),
GeositeMatcher: G.SiteMatcherName(), GeositeMatcher: geodata.SiteMatcherName(),
TCPConcurrent: dialer.GetTcpConcurrent(), TCPConcurrent: dialer.GetTcpConcurrent(),
FindProcessMode: tunnel.FindProcessMode(), FindProcessMode: tunnel.FindProcessMode(),
Sniffing: tunnel.IsSniffing(), Sniffing: tunnel.IsSniffing(),
GlobalClientFingerprint: tlsC.GetGlobalFingerprint(), GlobalClientFingerprint: tlsC.GetGlobalFingerprint(),
GlobalUA: mihomoHttp.UA(), GlobalUA: mihomoHttp.UA(),
ETagSupport: resource.ETag(), ETagSupport: resource.ETag(),
KeepAliveInterval: int(keepalive.KeepAliveInterval() / time.Second),
KeepAliveIdle: int(keepalive.KeepAliveIdle() / time.Second),
DisableKeepAlive: keepalive.DisableKeepAlive(),
} }
return general return general
@ -235,6 +240,8 @@ func updateDNS(c *config.DNS, generalIPv6 bool) {
resolver.DefaultResolver = nil resolver.DefaultResolver = nil
resolver.DefaultHostMapper = nil resolver.DefaultHostMapper = nil
resolver.DefaultLocalServer = nil resolver.DefaultLocalServer = nil
resolver.ProxyServerHostResolver = nil
resolver.DirectHostResolver = nil
dns.ReCreateServer("", nil, nil) dns.ReCreateServer("", nil, nil)
return return
} }
@ -251,11 +258,12 @@ func updateDNS(c *config.DNS, generalIPv6 bool) {
Default: c.DefaultNameserver, Default: c.DefaultNameserver,
Policy: c.NameServerPolicy, Policy: c.NameServerPolicy,
ProxyServer: c.ProxyServerNameserver, ProxyServer: c.ProxyServerNameserver,
Tunnel: tunnel.Tunnel, DirectServer: c.DirectNameServer,
DirectFollowPolicy: c.DirectFollowPolicy,
CacheAlgorithm: c.CacheAlgorithm, CacheAlgorithm: c.CacheAlgorithm,
} }
r, pr := dns.NewResolver(cfg) r := dns.NewResolver(cfg)
m := dns.NewEnhancer(cfg) m := dns.NewEnhancer(cfg)
// reuse cache of old host mapper // reuse cache of old host mapper
@ -265,14 +273,22 @@ func updateDNS(c *config.DNS, generalIPv6 bool) {
resolver.DefaultResolver = r resolver.DefaultResolver = r
resolver.DefaultHostMapper = m resolver.DefaultHostMapper = m
resolver.DefaultLocalServer = dns.NewLocalServer(r, m) resolver.DefaultLocalServer = dns.NewLocalServer(r.Resolver, m)
resolver.UseSystemHosts = c.UseSystemHosts resolver.UseSystemHosts = c.UseSystemHosts
if pr.Invalid() { if r.ProxyResolver.Invalid() {
resolver.ProxyServerHostResolver = pr resolver.ProxyServerHostResolver = r.ProxyResolver
} else {
resolver.ProxyServerHostResolver = r.Resolver
} }
dns.ReCreateServer(c.Listen, r, m) if r.DirectResolver.Invalid() {
resolver.DirectHostResolver = r.DirectResolver
} else {
resolver.DirectHostResolver = r.Resolver
}
dns.ReCreateServer(c.Listen, r.Resolver, m)
} }
func updateHosts(tree *trie.DomainTrie[resolver.HostValue]) { func updateHosts(tree *trie.DomainTrie[resolver.HostValue]) {
@ -383,42 +399,63 @@ func updateTunnels(tunnels []LC.Tunnel) {
listener.PatchTunnel(tunnels, tunnel.Tunnel) listener.PatchTunnel(tunnels, tunnel.Tunnel)
} }
func initExternalUI() { func updateUpdater(cfg *config.Config) {
if updater.AutoDownloadUI { general := cfg.General
dirEntries, _ := os.ReadDir(updater.ExternalUIPath) updater.SetGeoAutoUpdate(general.GeoAutoUpdate)
if len(dirEntries) > 0 { updater.SetGeoUpdateInterval(general.GeoUpdateInterval)
log.Infoln("UI already exists, skip downloading")
} else { controller := cfg.Controller
log.Infoln("External UI downloading ...") updater.DefaultUiUpdater = updater.NewUiUpdater(controller.ExternalUI, controller.ExternalUIURL, controller.ExternalUIName)
updater.DownloadUI() updater.DefaultUiUpdater.AutoDownloadUI()
} }
//go:linkname temporaryUpdateGeneral github.com/metacubex/mihomo/config.temporaryUpdateGeneral
func temporaryUpdateGeneral(general *config.General) func() {
oldGeneral := GetGeneral()
updateGeneral(general, false)
return func() {
updateGeneral(oldGeneral, false)
} }
} }
func updateGeneral(general *config.General) { func updateGeneral(general *config.General, logging bool) {
tunnel.SetMode(general.Mode) tunnel.SetMode(general.Mode)
tunnel.SetFindProcessMode(general.FindProcessMode) tunnel.SetFindProcessMode(general.FindProcessMode)
resolver.DisableIPv6 = !general.IPv6 resolver.DisableIPv6 = !general.IPv6
if general.TCPConcurrent {
dialer.SetTcpConcurrent(general.TCPConcurrent) dialer.SetTcpConcurrent(general.TCPConcurrent)
if logging && general.TCPConcurrent {
log.Infoln("Use tcp concurrent") log.Infoln("Use tcp concurrent")
} }
inbound.SetTfo(general.InboundTfo) inbound.SetTfo(general.InboundTfo)
inbound.SetMPTCP(general.InboundMPTCP) inbound.SetMPTCP(general.InboundMPTCP)
keepalive.SetKeepAliveIdle(time.Duration(general.KeepAliveIdle) * time.Second)
keepalive.SetKeepAliveInterval(time.Duration(general.KeepAliveInterval) * time.Second)
keepalive.SetDisableKeepAlive(general.DisableKeepAlive)
adapter.UnifiedDelay.Store(general.UnifiedDelay) adapter.UnifiedDelay.Store(general.UnifiedDelay)
dialer.DefaultInterface.Store(general.Interface) dialer.DefaultInterface.Store(general.Interface)
dialer.DefaultRoutingMark.Store(int32(general.RoutingMark)) dialer.DefaultRoutingMark.Store(int32(general.RoutingMark))
if general.RoutingMark > 0 { if logging && general.RoutingMark > 0 {
log.Infoln("Use routing mark: %#x", general.RoutingMark) log.Infoln("Use routing mark: %#x", general.RoutingMark)
} }
iface.FlushCache() iface.FlushCache()
G.SetLoader(general.GeodataLoader)
G.SetSiteMatcher(general.GeositeMatcher) geodata.SetGeodataMode(general.GeodataMode)
geodata.SetLoader(general.GeodataLoader)
geodata.SetSiteMatcher(general.GeositeMatcher)
geodata.SetGeoIpUrl(general.GeoXUrl.GeoIp)
geodata.SetGeoSiteUrl(general.GeoXUrl.GeoSite)
geodata.SetMmdbUrl(general.GeoXUrl.Mmdb)
geodata.SetASNUrl(general.GeoXUrl.ASN)
mihomoHttp.SetUA(general.GlobalUA)
resource.SetETag(general.ETagSupport)
tlsC.SetGlobalUtlsClient(general.GlobalClientFingerprint)
} }
func updateUsers(users []auth.AuthUser) { func updateUsers(users []auth.AuthUser) {

View File

@ -24,9 +24,11 @@ import (
func configRouter() http.Handler { func configRouter() http.Handler {
r := chi.NewRouter() r := chi.NewRouter()
r.Get("/", getConfigs) r.Get("/", getConfigs)
if !embedMode { // disallow update/patch configs in embed mode
r.Put("/", updateConfigs) r.Put("/", updateConfigs)
r.Post("/geo", updateGeoDatabases) r.Post("/geo", updateGeoDatabases)
r.Patch("/", patchConfigs) r.Patch("/", patchConfigs)
}
return r return r
} }

View File

@ -16,7 +16,7 @@ import (
"github.com/metacubex/mihomo/tunnel" "github.com/metacubex/mihomo/tunnel"
) )
func GroupRouter() http.Handler { func groupRouter() http.Handler {
r := chi.NewRouter() r := chi.NewRouter()
r.Get("/", getGroups) r.Get("/", getGroups)

View File

@ -0,0 +1,7 @@
//go:build android && cmfa
package route
func init() {
SetEmbedMode(true) // set embed mode default
}

View File

@ -36,8 +36,14 @@ var (
tlsServer *http.Server tlsServer *http.Server
unixServer *http.Server unixServer *http.Server
pipeServer *http.Server pipeServer *http.Server
embedMode = false
) )
func SetEmbedMode(embed bool) {
embedMode = embed
}
type Traffic struct { type Traffic struct {
Up int64 `json:"up"` Up int64 `json:"up"`
Down int64 `json:"down"` Down int64 `json:"down"`
@ -114,15 +120,17 @@ func router(isDebug bool, secret string, dohServer string, cors Cors) *chi.Mux {
r.Get("/version", version) r.Get("/version", version)
r.Mount("/configs", configRouter()) r.Mount("/configs", configRouter())
r.Mount("/proxies", proxyRouter()) r.Mount("/proxies", proxyRouter())
r.Mount("/group", GroupRouter()) r.Mount("/group", groupRouter())
r.Mount("/rules", ruleRouter()) r.Mount("/rules", ruleRouter())
r.Mount("/connections", connectionRouter()) r.Mount("/connections", connectionRouter())
r.Mount("/providers/proxies", proxyProviderRouter()) r.Mount("/providers/proxies", proxyProviderRouter())
r.Mount("/providers/rules", ruleProviderRouter()) r.Mount("/providers/rules", ruleProviderRouter())
r.Mount("/cache", cacheRouter()) r.Mount("/cache", cacheRouter())
r.Mount("/dns", dnsRouter()) r.Mount("/dns", dnsRouter())
if !embedMode { // disallow restart and upgrade in embed mode
r.Mount("/restart", restartRouter()) r.Mount("/restart", restartRouter())
r.Mount("/upgrade", upgradeRouter()) r.Mount("/upgrade", upgradeRouter())
}
addExternalRouters(r) addExternalRouters(r)
}) })

View File

@ -47,7 +47,7 @@ func upgradeCore(w http.ResponseWriter, r *http.Request) {
} }
func updateUI(w http.ResponseWriter, r *http.Request) { func updateUI(w http.ResponseWriter, r *http.Request) {
err := updater.DownloadUI() err := updater.DefaultUiUpdater.DownloadUI()
if err != nil { if err != nil {
log.Warnln("%s", err) log.Warnln("%s", err)
render.Status(r, http.StatusInternalServerError) render.Status(r, http.StatusInternalServerError)

View File

@ -279,7 +279,7 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis
return return
} }
defaultInterfaceMonitor, err = tun.NewDefaultInterfaceMonitor(networkUpdateMonitor, log.SingLogger, tun.DefaultInterfaceMonitorOptions{OverrideAndroidVPN: true}) defaultInterfaceMonitor, err = tun.NewDefaultInterfaceMonitor(networkUpdateMonitor, log.SingLogger, tun.DefaultInterfaceMonitorOptions{InterfaceFinder: interfaceFinder, OverrideAndroidVPN: true})
if err != nil { if err != nil {
err = E.Cause(err, "create DefaultInterfaceMonitor") err = E.Cause(err, "create DefaultInterfaceMonitor")
return return

View File

@ -1,9 +1,11 @@
package main package main
import ( import (
"context"
"encoding/base64" "encoding/base64"
"flag" "flag"
"fmt" "fmt"
"net"
"os" "os"
"os/signal" "os/signal"
"path/filepath" "path/filepath"
@ -55,6 +57,12 @@ func init() {
} }
func main() { func main() {
// Defensive programming: panic when code mistakenly calls net.DefaultResolver
net.DefaultResolver.PreferGo = true
net.DefaultResolver.Dial = func(ctx context.Context, network, address string) (net.Conn, error) {
panic("should never be called")
}
_, _ = maxprocs.Set(maxprocs.Logger(func(string, ...any) {})) _, _ = maxprocs.Set(maxprocs.Logger(func(string, ...any) {}))
if len(os.Args) > 1 && os.Args[1] == "convert-ruleset" { if len(os.Args) > 1 && os.Args[1] == "convert-ruleset" {

View File

@ -4,7 +4,6 @@ import (
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"net" "net"
"strings"
"time" "time"
"github.com/metacubex/quic-go" "github.com/metacubex/quic-go"
@ -16,9 +15,7 @@ import (
"github.com/metacubex/mihomo/transport/hysteria/utils" "github.com/metacubex/mihomo/transport/hysteria/utils"
) )
type ClientTransport struct { type ClientTransport struct{}
Dialer *net.Dialer
}
func (ct *ClientTransport) quicPacketConn(proto string, rAddr net.Addr, serverPorts string, obfs obfsPkg.Obfuscator, hopInterval time.Duration, dialer utils.PacketDialer) (net.PacketConn, error) { func (ct *ClientTransport) quicPacketConn(proto string, rAddr net.Addr, serverPorts string, obfs obfsPkg.Obfuscator, hopInterval time.Duration, dialer utils.PacketDialer) (net.PacketConn, error) {
server := rAddr.String() server := rAddr.String()
@ -86,23 +83,3 @@ func (ct *ClientTransport) QUICDial(proto string, server string, serverPorts str
} }
return qs, nil return qs, nil
} }
func (ct *ClientTransport) DialTCP(raddr *net.TCPAddr) (*net.TCPConn, error) {
conn, err := ct.Dialer.Dial("tcp", raddr.String())
if err != nil {
return nil, err
}
return conn.(*net.TCPConn), nil
}
func (ct *ClientTransport) ListenUDP() (*net.UDPConn, error) {
return net.ListenUDP("udp", nil)
}
func isMultiPortAddr(addr string) bool {
_, portStr, err := net.SplitHostPort(addr)
if err == nil && (strings.Contains(portStr, ",") || strings.Contains(portStr, "-")) {
return true
}
return false
}

View File

@ -114,10 +114,8 @@ func (m *Manager) handle() {
ticker := time.NewTicker(time.Second) ticker := time.NewTicker(time.Second)
for range ticker.C { for range ticker.C {
m.uploadBlip.Store(m.uploadTemp.Load()) m.uploadBlip.Store(m.uploadTemp.Swap(0))
m.uploadTemp.Store(0) m.downloadBlip.Store(m.downloadTemp.Swap(0))
m.downloadBlip.Store(m.downloadTemp.Load())
m.downloadTemp.Store(0)
} }
} }

View File

@ -625,7 +625,7 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) {
// normal check for process // normal check for process
uid, path, err := P.FindProcessName(metadata.NetWork.String(), metadata.SrcIP, int(metadata.SrcPort)) uid, path, err := P.FindProcessName(metadata.NetWork.String(), metadata.SrcIP, int(metadata.SrcPort))
if err != nil { if err != nil {
log.Debugln("[Process] find process %s error: %v", metadata.String(), err) log.Debugln("[Process] find process error for %s: %v", metadata.String(), err)
} else { } else {
metadata.Process = filepath.Base(path) metadata.Process = filepath.Base(path)
metadata.ProcessPath = path metadata.ProcessPath = path
@ -639,7 +639,7 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) {
// check package names // check package names
pkg, err := P.FindPackageName(metadata) pkg, err := P.FindPackageName(metadata)
if err != nil { if err != nil {
log.Debugln("[Process] find process %s error: %v", metadata.String(), err) log.Debugln("[Process] find process error for %s: %v", metadata.String(), err)
} else { } else {
metadata.Process = pkg metadata.Process = pkg
} }