Bläddra i källkod

ntp: Add write_to_system service option

世界 2 år sedan
förälder
incheckning
0558b3fc5c

+ 4 - 6
cmd/sing-box/cmd_tools.go

@@ -4,19 +4,21 @@ import (
 	"context"
 	"context"
 
 
 	box "github.com/sagernet/sing-box"
 	box "github.com/sagernet/sing-box"
-	"github.com/sagernet/sing-box/option"
 	E "github.com/sagernet/sing/common/exceptions"
 	E "github.com/sagernet/sing/common/exceptions"
 	N "github.com/sagernet/sing/common/network"
 	N "github.com/sagernet/sing/common/network"
 
 
 	"github.com/spf13/cobra"
 	"github.com/spf13/cobra"
 )
 )
 
 
+var commandToolsFlagOutbound string
+
 var commandTools = &cobra.Command{
 var commandTools = &cobra.Command{
 	Use:   "tools",
 	Use:   "tools",
 	Short: "Experimental tools",
 	Short: "Experimental tools",
 }
 }
 
 
 func init() {
 func init() {
+	commandTools.PersistentFlags().StringVarP(&commandToolsFlagOutbound, "outbound", "o", "", "Use specified tag instead of default outbound")
 	mainCommand.AddCommand(commandTools)
 	mainCommand.AddCommand(commandTools)
 }
 }
 
 
@@ -25,10 +27,6 @@ func createPreStartedClient() (*box.Box, error) {
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	if options.Log == nil {
-		options.Log = &option.LogOptions{}
-	}
-	options.Log.Disabled = true
 	instance, err := box.New(context.Background(), options, nil)
 	instance, err := box.New(context.Background(), options, nil)
 	if err != nil {
 	if err != nil {
 		return nil, E.Cause(err, "create service")
 		return nil, E.Cause(err, "create service")
@@ -42,7 +40,7 @@ func createPreStartedClient() (*box.Box, error) {
 
 
 func createDialer(instance *box.Box, network string, outboundTag string) (N.Dialer, error) {
 func createDialer(instance *box.Box, network string, outboundTag string) (N.Dialer, error) {
 	if outboundTag == "" {
 	if outboundTag == "" {
-		outbound := instance.Router().DefaultOutbound(network)
+		outbound := instance.Router().DefaultOutbound(N.NetworkName(network))
 		if outbound == nil {
 		if outbound == nil {
 			return nil, E.New("missing default outbound")
 			return nil, E.New("missing default outbound")
 		}
 		}

+ 3 - 7
cmd/sing-box/cmd_tools_connect.go

@@ -15,10 +15,7 @@ import (
 	"github.com/spf13/cobra"
 	"github.com/spf13/cobra"
 )
 )
 
 
-var (
-	commandConnectFlagNetwork  string
-	commandConnectFlagOutbound string
-)
+var commandConnectFlagNetwork string
 
 
 var commandConnect = &cobra.Command{
 var commandConnect = &cobra.Command{
 	Use:   "connect [address]",
 	Use:   "connect [address]",
@@ -33,8 +30,7 @@ var commandConnect = &cobra.Command{
 }
 }
 
 
 func init() {
 func init() {
-	commandConnect.Flags().StringVar(&commandConnectFlagNetwork, "network", "tcp", "network type")
-	commandConnect.Flags().StringVar(&commandConnectFlagOutbound, "outbound", "", "outbound tag")
+	commandConnect.Flags().StringVarP(&commandConnectFlagNetwork, "network", "n", "tcp", "network type")
 	commandTools.AddCommand(commandConnect)
 	commandTools.AddCommand(commandConnect)
 }
 }
 
 
@@ -49,7 +45,7 @@ func connect(address string) error {
 		return err
 		return err
 	}
 	}
 	defer instance.Close()
 	defer instance.Close()
-	dialer, err := createDialer(instance, commandConnectFlagNetwork, commandConnectFlagOutbound)
+	dialer, err := createDialer(instance, commandConnectFlagNetwork, commandToolsFlagOutbound)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}

+ 1 - 5
cmd/sing-box/cmd_tools_fetch.go

@@ -12,13 +12,10 @@ import (
 	"github.com/sagernet/sing-box/log"
 	"github.com/sagernet/sing-box/log"
 	"github.com/sagernet/sing/common/bufio"
 	"github.com/sagernet/sing/common/bufio"
 	M "github.com/sagernet/sing/common/metadata"
 	M "github.com/sagernet/sing/common/metadata"
-	N "github.com/sagernet/sing/common/network"
 
 
 	"github.com/spf13/cobra"
 	"github.com/spf13/cobra"
 )
 )
 
 
-var commandFetchFlagOutbound string
-
 var commandFetch = &cobra.Command{
 var commandFetch = &cobra.Command{
 	Use:   "fetch",
 	Use:   "fetch",
 	Short: "Fetch an URL",
 	Short: "Fetch an URL",
@@ -32,7 +29,6 @@ var commandFetch = &cobra.Command{
 }
 }
 
 
 func init() {
 func init() {
-	commandFetch.Flags().StringVar(&commandFetchFlagOutbound, "outbound", "", "outbound tag")
 	commandTools.AddCommand(commandFetch)
 	commandTools.AddCommand(commandFetch)
 }
 }
 
 
@@ -47,7 +43,7 @@ func fetch(args []string) error {
 	httpClient = &http.Client{
 	httpClient = &http.Client{
 		Transport: &http.Transport{
 		Transport: &http.Transport{
 			DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
 			DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
-				dialer, err := createDialer(instance, N.NetworkTCP, commandFetchFlagOutbound)
+				dialer, err := createDialer(instance, network, commandToolsFlagOutbound)
 				if err != nil {
 				if err != nil {
 					return nil, err
 					return nil, err
 				}
 				}

+ 69 - 0
cmd/sing-box/cmd_tools_synctime.go

@@ -0,0 +1,69 @@
+package main
+
+import (
+	"context"
+	"os"
+
+	"github.com/sagernet/sing-box/common/settings"
+	C "github.com/sagernet/sing-box/constant"
+	"github.com/sagernet/sing-box/log"
+	E "github.com/sagernet/sing/common/exceptions"
+	M "github.com/sagernet/sing/common/metadata"
+	N "github.com/sagernet/sing/common/network"
+	"github.com/sagernet/sing/common/ntp"
+
+	"github.com/spf13/cobra"
+)
+
+var (
+	commandSyncTimeFlagServer   string
+	commandSyncTimeOutputFormat string
+	commandSyncTimeWrite        bool
+)
+
+var commandSyncTime = &cobra.Command{
+	Use:   "synctime",
+	Short: "Sync time using the NTP protocol",
+	Args:  cobra.NoArgs,
+	Run: func(cmd *cobra.Command, args []string) {
+		err := syncTime()
+		if err != nil {
+			log.Fatal(err)
+		}
+	},
+}
+
+func init() {
+	commandSyncTime.Flags().StringVarP(&commandSyncTimeFlagServer, "server", "s", "time.apple.com", "Set NTP server")
+	commandSyncTime.Flags().StringVarP(&commandSyncTimeOutputFormat, "format", "f", C.TimeLayout, "Set output format")
+	commandSyncTime.Flags().BoolVarP(&commandSyncTimeWrite, "write", "w", false, "Write time to system")
+	commandTools.AddCommand(commandSyncTime)
+}
+
+func syncTime() error {
+	instance, err := createPreStartedClient()
+	if err != nil {
+		return err
+	}
+	dialer, err := createDialer(instance, N.NetworkUDP, commandToolsFlagOutbound)
+	if err != nil {
+		return err
+	}
+	defer instance.Close()
+	serverAddress := M.ParseSocksaddr(commandSyncTimeFlagServer)
+	if serverAddress.Port == 0 {
+		serverAddress.Port = 123
+	}
+	response, err := ntp.Exchange(context.Background(), dialer, serverAddress)
+	if err != nil {
+		return err
+	}
+	if commandSyncTimeWrite {
+		err = settings.SetSystemTime(response.Time)
+		if err != nil {
+			return E.Cause(err, "write time to system")
+		}
+	}
+	os.Stdout.WriteString(response.Time.Local().Format(commandSyncTimeOutputFormat))
+	return nil
+}

+ 12 - 0
common/settings/time_stub.go

@@ -0,0 +1,12 @@
+//go:build !(windows || linux || darwin)
+
+package settings
+
+import (
+	"os"
+	"time"
+)
+
+func SetSystemTime(nowTime time.Time) error {
+	return os.ErrInvalid
+}

+ 14 - 0
common/settings/time_unix.go

@@ -0,0 +1,14 @@
+//go:build linux || darwin
+
+package settings
+
+import (
+	"time"
+
+	"golang.org/x/sys/unix"
+)
+
+func SetSystemTime(nowTime time.Time) error {
+	timeVal := unix.NsecToTimeval(nowTime.UnixNano())
+	return unix.Settimeofday(&timeVal)
+}

+ 32 - 0
common/settings/time_windows.go

@@ -0,0 +1,32 @@
+package settings
+
+import (
+	"time"
+	"unsafe"
+
+	"golang.org/x/sys/windows"
+)
+
+func SetSystemTime(nowTime time.Time) error {
+	var systemTime windows.Systemtime
+	systemTime.Year = uint16(nowTime.Year())
+	systemTime.Month = uint16(nowTime.Month())
+	systemTime.Day = uint16(nowTime.Day())
+	systemTime.Hour = uint16(nowTime.Hour())
+	systemTime.Minute = uint16(nowTime.Minute())
+	systemTime.Second = uint16(nowTime.Second())
+	systemTime.Milliseconds = uint16(nowTime.UnixMilli() - nowTime.Unix()*1000)
+
+	dllKernel32 := windows.NewLazySystemDLL("kernel32.dll")
+	proc := dllKernel32.NewProc("SetSystemTime")
+
+	_, _, err := proc.Call(
+		uintptr(unsafe.Pointer(&systemTime)),
+	)
+
+	if err != nil && err.Error() != "The operation completed successfully." {
+		return err
+	}
+
+	return nil
+}

+ 3 - 0
constant/time.go

@@ -0,0 +1,3 @@
+package constant
+
+const TimeLayout = "2006-01-02 15:04:05 -0700"

+ 25 - 18
ntp/service.go

@@ -6,6 +6,8 @@ import (
 
 
 	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing-box/common/dialer"
 	"github.com/sagernet/sing-box/common/dialer"
+	"github.com/sagernet/sing-box/common/settings"
+	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing-box/option"
 	E "github.com/sagernet/sing/common/exceptions"
 	E "github.com/sagernet/sing/common/exceptions"
 	"github.com/sagernet/sing/common/logger"
 	"github.com/sagernet/sing/common/logger"
@@ -14,19 +16,17 @@ import (
 	"github.com/sagernet/sing/common/ntp"
 	"github.com/sagernet/sing/common/ntp"
 )
 )
 
 
-const timeLayout = "2006-01-02 15:04:05 -0700"
-
 var _ adapter.TimeService = (*Service)(nil)
 var _ adapter.TimeService = (*Service)(nil)
 
 
 type Service struct {
 type Service struct {
-	ctx    context.Context
-	cancel context.CancelFunc
-	server M.Socksaddr
-	dialer N.Dialer
-	logger logger.Logger
-
-	ticker      *time.Ticker
-	clockOffset time.Duration
+	ctx           context.Context
+	cancel        context.CancelFunc
+	server        M.Socksaddr
+	writeToSystem bool
+	dialer        N.Dialer
+	logger        logger.Logger
+	ticker        *time.Ticker
+	clockOffset   time.Duration
 }
 }
 
 
 func NewService(ctx context.Context, router adapter.Router, logger logger.Logger, options option.NTPOptions) *Service {
 func NewService(ctx context.Context, router adapter.Router, logger logger.Logger, options option.NTPOptions) *Service {
@@ -42,12 +42,13 @@ func NewService(ctx context.Context, router adapter.Router, logger logger.Logger
 		interval = 30 * time.Minute
 		interval = 30 * time.Minute
 	}
 	}
 	return &Service{
 	return &Service{
-		ctx:    ctx,
-		cancel: cancel,
-		server: server,
-		dialer: dialer.New(router, options.DialerOptions),
-		logger: logger,
-		ticker: time.NewTicker(interval),
+		ctx:           ctx,
+		cancel:        cancel,
+		server:        server,
+		writeToSystem: options.WriteToSystem,
+		dialer:        dialer.New(router, options.DialerOptions),
+		logger:        logger,
+		ticker:        time.NewTicker(interval),
 	}
 	}
 }
 }
 
 
@@ -56,7 +57,7 @@ func (s *Service) Start() error {
 	if err != nil {
 	if err != nil {
 		return E.Cause(err, "initialize time")
 		return E.Cause(err, "initialize time")
 	}
 	}
-	s.logger.Info("updated time: ", s.TimeFunc()().Local().Format(timeLayout))
+	s.logger.Info("updated time: ", s.TimeFunc()().Local().Format(C.TimeLayout))
 	go s.loopUpdate()
 	go s.loopUpdate()
 	return nil
 	return nil
 }
 }
@@ -82,7 +83,7 @@ func (s *Service) loopUpdate() {
 		}
 		}
 		err := s.update()
 		err := s.update()
 		if err == nil {
 		if err == nil {
-			s.logger.Debug("updated time: ", s.TimeFunc()().Local().Format(timeLayout))
+			s.logger.Debug("updated time: ", s.TimeFunc()().Local().Format(C.TimeLayout))
 		} else {
 		} else {
 			s.logger.Warn("update time: ", err)
 			s.logger.Warn("update time: ", err)
 		}
 		}
@@ -95,5 +96,11 @@ func (s *Service) update() error {
 		return err
 		return err
 	}
 	}
 	s.clockOffset = response.ClockOffset
 	s.clockOffset = response.ClockOffset
+	if s.writeToSystem {
+		writeErr := settings.SetSystemTime(s.TimeFunc()())
+		if writeErr != nil {
+			s.logger.Warn("write time to system: ", writeErr)
+		}
+	}
 	return nil
 	return nil
 }
 }

+ 3 - 2
option/ntp.go

@@ -1,8 +1,9 @@
 package option
 package option
 
 
 type NTPOptions struct {
 type NTPOptions struct {
-	Enabled  bool     `json:"enabled"`
-	Interval Duration `json:"interval,omitempty"`
+	Enabled       bool     `json:"enabled"`
+	Interval      Duration `json:"interval,omitempty"`
+	WriteToSystem bool     `json:"write_to_system,omitempty"`
 	ServerOptions
 	ServerOptions
 	DialerOptions
 	DialerOptions
 }
 }