ntp: Add write_to_system service option

This commit is contained in:
世界 2023-03-18 23:01:10 +08:00
parent 99b2ab5526
commit 0558b3fc5c
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
10 changed files with 166 additions and 38 deletions

View file

@ -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")
} }

View file

@ -15,10 +15,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
var ( var commandConnectFlagNetwork string
commandConnectFlagNetwork string
commandConnectFlagOutbound 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().StringVarP(&commandConnectFlagNetwork, "network", "n", "tcp", "network type")
commandConnect.Flags().StringVar(&commandConnectFlagOutbound, "outbound", "", "outbound tag")
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
} }

View file

@ -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
} }

View file

@ -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
}

View file

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

View file

@ -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)
}

View file

@ -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
constant/time.go Normal file
View file

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

View file

@ -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 ctx context.Context
cancel context.CancelFunc cancel context.CancelFunc
server M.Socksaddr server M.Socksaddr
dialer N.Dialer writeToSystem bool
logger logger.Logger dialer N.Dialer
logger logger.Logger
ticker *time.Ticker ticker *time.Ticker
clockOffset time.Duration 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, ctx: ctx,
cancel: cancel, cancel: cancel,
server: server, server: server,
dialer: dialer.New(router, options.DialerOptions), writeToSystem: options.WriteToSystem,
logger: logger, dialer: dialer.New(router, options.DialerOptions),
ticker: time.NewTicker(interval), 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
} }

View file

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