diff --git a/core/server/go.mod b/core/server/go.mod index b48eddc..71caadd 100644 --- a/core/server/go.mod +++ b/core/server/go.mod @@ -19,7 +19,7 @@ require ( google.golang.org/protobuf v1.34.2 ) -replace github.com/sagernet/sing-box => github.com/Mahdi-zarei/sing-box v1.3.5-0.20250224103252-b71e66ac2cc7 +replace github.com/sagernet/sing-box => github.com/Mahdi-zarei/sing-box v1.3.5-0.20250226183822-fbccffb7d54f replace github.com/sagernet/sing-dns => github.com/Mahdi-zarei/sing-dns v0.3.0-beta.14.0.20250201180230-3ed9d1ef74d5 diff --git a/core/server/go.sum b/core/server/go.sum index edcc107..89b5fdc 100644 --- a/core/server/go.sum +++ b/core/server/go.sum @@ -1,5 +1,5 @@ -github.com/Mahdi-zarei/sing-box v1.3.5-0.20250224103252-b71e66ac2cc7 h1:aKHzrLdePI4IzeDBeJmjeYcd4ONsVTiPc4bCSeTFEQ0= -github.com/Mahdi-zarei/sing-box v1.3.5-0.20250224103252-b71e66ac2cc7/go.mod h1:Gwt2fHum29zmmWw1gXMDQvoiCmu2ikv4ZozjKSxcZ7c= +github.com/Mahdi-zarei/sing-box v1.3.5-0.20250226183822-fbccffb7d54f h1:7BqlzXepzL2ARM8p+1pVUtvWLck89IM4hk6FPXw0LmQ= +github.com/Mahdi-zarei/sing-box v1.3.5-0.20250226183822-fbccffb7d54f/go.mod h1:Gwt2fHum29zmmWw1gXMDQvoiCmu2ikv4ZozjKSxcZ7c= github.com/Mahdi-zarei/sing-dns v0.3.0-beta.14.0.20250201180230-3ed9d1ef74d5 h1:AdwcBEKyia7lvdqJq77G0imGej8IFkHGENfjif+OwHs= github.com/Mahdi-zarei/sing-dns v0.3.0-beta.14.0.20250201180230-3ed9d1ef74d5/go.mod h1:8wuFcoFkWM4vJuQyg8e97LyvDwe0/Vl7G839WLcKDs8= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= diff --git a/core/server/internal/boxdns/dns_manager_windows.go b/core/server/internal/boxdns/dns_manager_windows.go index 900b730..e03761f 100644 --- a/core/server/internal/boxdns/dns_manager_windows.go +++ b/core/server/internal/boxdns/dns_manager_windows.go @@ -22,16 +22,22 @@ const ( var customDNS []netip.Addr var dnsIsSet bool -func handleInterfaceChange(_ *control.Interface, _ int) { +func HandleInterfaceChange(_ *control.Interface, _ int) { + monitorForUnderlyingDNS() + time.Sleep(2 * time.Second) if !dnsIsSet { return } - time.Sleep(2 * time.Second) _ = SetDefaultDNS(customDNS, false, false) } func getDefaultInterfaceGuid() (string, error) { - index := monitorDI.DefaultInterface().Index + ifc := DefaultIfcMonitor.DefaultInterface() + if ifc == nil { + log.Println("Default interface is nil!") + return "", E.New("Default interface is nil!") + } + index := ifc.Index var guid iphlpapi.GUID if errno := iphlpapi.Index2GUID(uint64(index), &guid); errno != 0 { return "", E.New("Failed to convert index to GUID") @@ -49,7 +55,12 @@ func getDefaultInterfaceGuid() (string, error) { } func getDefaultInterfaceLUID() (winipcfg.LUID, error) { - index := monitorDI.DefaultInterface().Index + ifc := DefaultIfcMonitor.DefaultInterface() + if ifc == nil { + log.Println("Default interface is nil!") + return 0, E.New("Default interface is nil!") + } + index := ifc.Index luid, err := winipcfg.LUIDFromIndex(uint32(index)) if err != nil { return 0, err diff --git a/core/server/internal/boxdns/monitor_stub.go b/core/server/internal/boxdns/monitor_stub.go new file mode 100644 index 0000000..4445b25 --- /dev/null +++ b/core/server/internal/boxdns/monitor_stub.go @@ -0,0 +1,13 @@ +//go:build !windows + +package boxdns + +import ( + tun "github.com/sagernet/sing-tun" +) + +var DefaultIfcMonitor tun.DefaultInterfaceMonitor + +func monitorForUnderlyingDNS() { + return +} diff --git a/core/server/internal/boxdns/monitor_windows.go b/core/server/internal/boxdns/monitor_windows.go index 14555e7..cad5d7c 100644 --- a/core/server/internal/boxdns/monitor_windows.go +++ b/core/server/internal/boxdns/monitor_windows.go @@ -1,58 +1,25 @@ package boxdns import ( - "context" - "github.com/sagernet/sing/common/control" + "github.com/matsuridayo/libneko/iphlpapi" "log" "strings" - "time" - "github.com/matsuridayo/libneko/iphlpapi" - - L "github.com/sagernet/sing-box/log" tun "github.com/sagernet/sing-tun" "github.com/gofrs/uuid/v5" "golang.org/x/sys/windows/registry" ) -var monitorNU tun.NetworkUpdateMonitor -var monitorDI tun.DefaultInterfaceMonitor +var DefaultIfcMonitor tun.DefaultInterfaceMonitor -func init() { - defer func() { - if err := recover(); err != nil { - log.Println("[Warning] failed to start sing-tun monitor:", err) - } - }() - - logFactory, _ := L.New(L.Options{ - Context: context.Background(), - BaseTime: time.Now(), - }) - logger := logFactory.NewLogger("windows-dns") - - ifcFinder := control.NewDefaultInterfaceFinder() - monitorNU, _ = tun.NewNetworkUpdateMonitor(logger) - monitorDI, _ = tun.NewDefaultInterfaceMonitor(monitorNU, logger, tun.DefaultInterfaceMonitorOptions{ - InterfaceFinder: ifcFinder, - }) - monitorDI.RegisterCallback(monitorForUnderlyingDNS) - monitorDI.RegisterCallback(handleInterfaceChange) - monitorDI.Start() - monitorNU.Start() - ifcFinder.Update() - - go func() { - for { - time.Sleep(5 * time.Second) - monitorForUnderlyingDNS(nil, 0) // to handle wifi change - } - }() -} - -func monitorForUnderlyingDNS(_ *control.Interface, _ int) { - index := monitorDI.DefaultInterface().Index +func monitorForUnderlyingDNS() { + ifc := DefaultIfcMonitor.DefaultInterface() + if ifc == nil { + log.Println("Default interface is nil!") + return + } + index := ifc.Index var guid iphlpapi.GUID if errno := iphlpapi.Index2GUID(uint64(index), &guid); errno != 0 { return diff --git a/core/server/server.go b/core/server/server.go index 203911f..c483ea4 100644 --- a/core/server/server.go +++ b/core/server/server.go @@ -15,6 +15,7 @@ import ( "nekobox_core/gen" "nekobox_core/internal/boxapi" "nekobox_core/internal/boxbox" + "nekobox_core/internal/boxdns" "nekobox_core/internal/boxmain" "nekobox_core/internal/sys" "net/http" @@ -64,7 +65,16 @@ func (s *server) Start(ctx context.Context, in *gen.LoadConfigReq) (out *gen.Err } boxInstance, instanceCancel, err = boxmain.Create([]byte(in.CoreConfig)) - if runtime.GOOS == "darwin" && strings.Contains(in.CoreConfig, "utun") && err == nil { + if err != nil { + return + } + if runtime.GOOS == "windows" { + nm := boxInstance.Network() + boxdns.DefaultIfcMonitor = nm.InterfaceMonitor() + boxdns.HandleInterfaceChange(nil, 0) + boxdns.DefaultIfcMonitor.RegisterCallback(boxdns.HandleInterfaceChange) + } + if runtime.GOOS == "darwin" && strings.Contains(in.CoreConfig, "utun") { err := sys.SetSystemDNS("172.19.0.2", boxInstance.Network().InterfaceMonitor()) if err != nil { log.Println("Failed to set system DNS:", err) @@ -186,10 +196,15 @@ func (s *server) QueryStats(ctx context.Context, _ *gen.EmptyReq) (*gen.QuerySta return nil, E.New("invalid clash server type") } outbounds := service.FromContext[adapter.OutboundManager](boxInstance.Context()) + endpoints := service.FromContext[adapter.EndpointManager](boxInstance.Context()) if outbounds == nil { log.Println("Failed to assert outbound manager") return nil, E.New("invalid outbound manager type") } + if endpoints == nil { + log.Println("Failed to assert endpoint manager") + return nil, E.New("invalid endpoint manager type") + } for _, out := range outbounds.Outbounds() { if len(out.Dependencies()) > 0 { // ignore, has detour @@ -199,6 +214,15 @@ func (s *server) QueryStats(ctx context.Context, _ *gen.EmptyReq) (*gen.QuerySta resp.Ups[out.Tag()] = u resp.Downs[out.Tag()] = d } + for _, end := range endpoints.Endpoints() { + if len(end.Dependencies()) > 0 { + // ignore, has detour + continue + } + u, d := cApi.TrafficManager().TotalOutbound(end.Tag()) + resp.Ups[end.Tag()] = u + resp.Downs[end.Tag()] = d + } } } diff --git a/core/server/test_utils.go b/core/server/test_utils.go index b5a2ab8..b0f6293 100644 --- a/core/server/test_utils.go +++ b/core/server/test_utils.go @@ -26,7 +26,6 @@ type URLTestResult struct { func BatchURLTest(ctx context.Context, i *boxbox.Box, outboundTags []string, url string, maxConcurrency int, twice bool) []*URLTestResult { outbounds := service.FromContext[adapter.OutboundManager](i.Context()) - endpoints := service.FromContext[adapter.EndpointManager](i.Context()) resMap := make(map[string]*URLTestResult) resAccess := sync.Mutex{} limiter := make(chan struct{}, maxConcurrency) @@ -50,10 +49,7 @@ func BatchURLTest(ctx context.Context, i *boxbox.Box, outboundTags []string, url defer wg.Done() outbound, found := outbounds.Outbound(t) if !found { - outbound, found = endpoints.Get(t) - if !found { - panic("no outbound with tag " + t + " found") - } + panic("no outbound with tag " + t + " found") } client := &http.Client{ Transport: &http.Transport{