mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-16 03:22:22 +08:00
ntp: Add write_to_system service option
This commit is contained in:
parent
99b2ab5526
commit
0558b3fc5c
|
@ -4,19 +4,21 @@ import (
|
|||
"context"
|
||||
|
||||
box "github.com/sagernet/sing-box"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var commandToolsFlagOutbound string
|
||||
|
||||
var commandTools = &cobra.Command{
|
||||
Use: "tools",
|
||||
Short: "Experimental tools",
|
||||
}
|
||||
|
||||
func init() {
|
||||
commandTools.PersistentFlags().StringVarP(&commandToolsFlagOutbound, "outbound", "o", "", "Use specified tag instead of default outbound")
|
||||
mainCommand.AddCommand(commandTools)
|
||||
}
|
||||
|
||||
|
@ -25,10 +27,6 @@ func createPreStartedClient() (*box.Box, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if options.Log == nil {
|
||||
options.Log = &option.LogOptions{}
|
||||
}
|
||||
options.Log.Disabled = true
|
||||
instance, err := box.New(context.Background(), options, nil)
|
||||
if err != nil {
|
||||
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) {
|
||||
if outboundTag == "" {
|
||||
outbound := instance.Router().DefaultOutbound(network)
|
||||
outbound := instance.Router().DefaultOutbound(N.NetworkName(network))
|
||||
if outbound == nil {
|
||||
return nil, E.New("missing default outbound")
|
||||
}
|
||||
|
|
|
@ -15,10 +15,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
commandConnectFlagNetwork string
|
||||
commandConnectFlagOutbound string
|
||||
)
|
||||
var commandConnectFlagNetwork string
|
||||
|
||||
var commandConnect = &cobra.Command{
|
||||
Use: "connect [address]",
|
||||
|
@ -33,8 +30,7 @@ var commandConnect = &cobra.Command{
|
|||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -49,7 +45,7 @@ func connect(address string) error {
|
|||
return err
|
||||
}
|
||||
defer instance.Close()
|
||||
dialer, err := createDialer(instance, commandConnectFlagNetwork, commandConnectFlagOutbound)
|
||||
dialer, err := createDialer(instance, commandConnectFlagNetwork, commandToolsFlagOutbound)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -12,13 +12,10 @@ import (
|
|||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var commandFetchFlagOutbound string
|
||||
|
||||
var commandFetch = &cobra.Command{
|
||||
Use: "fetch",
|
||||
Short: "Fetch an URL",
|
||||
|
@ -32,7 +29,6 @@ var commandFetch = &cobra.Command{
|
|||
}
|
||||
|
||||
func init() {
|
||||
commandFetch.Flags().StringVar(&commandFetchFlagOutbound, "outbound", "", "outbound tag")
|
||||
commandTools.AddCommand(commandFetch)
|
||||
}
|
||||
|
||||
|
@ -47,7 +43,7 @@ func fetch(args []string) error {
|
|||
httpClient = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
|
69
cmd/sing-box/cmd_tools_synctime.go
Normal file
69
cmd/sing-box/cmd_tools_synctime.go
Normal 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
|
||||
}
|
12
common/settings/time_stub.go
Normal file
12
common/settings/time_stub.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
//go:build !(windows || linux || darwin)
|
||||
|
||||
package settings
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
func SetSystemTime(nowTime time.Time) error {
|
||||
return os.ErrInvalid
|
||||
}
|
14
common/settings/time_unix.go
Normal file
14
common/settings/time_unix.go
Normal 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)
|
||||
}
|
32
common/settings/time_windows.go
Normal file
32
common/settings/time_windows.go
Normal 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
3
constant/time.go
Normal file
|
@ -0,0 +1,3 @@
|
|||
package constant
|
||||
|
||||
const TimeLayout = "2006-01-02 15:04:05 -0700"
|
|
@ -6,6 +6,8 @@ import (
|
|||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"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"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
|
@ -14,19 +16,17 @@ import (
|
|||
"github.com/sagernet/sing/common/ntp"
|
||||
)
|
||||
|
||||
const timeLayout = "2006-01-02 15:04:05 -0700"
|
||||
|
||||
var _ adapter.TimeService = (*Service)(nil)
|
||||
|
||||
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 {
|
||||
|
@ -42,12 +42,13 @@ func NewService(ctx context.Context, router adapter.Router, logger logger.Logger
|
|||
interval = 30 * time.Minute
|
||||
}
|
||||
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 {
|
||||
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()
|
||||
return nil
|
||||
}
|
||||
|
@ -82,7 +83,7 @@ func (s *Service) loopUpdate() {
|
|||
}
|
||||
err := s.update()
|
||||
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 {
|
||||
s.logger.Warn("update time: ", err)
|
||||
}
|
||||
|
@ -95,5 +96,11 @@ func (s *Service) update() error {
|
|||
return err
|
||||
}
|
||||
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
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package option
|
||||
|
||||
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
|
||||
DialerOptions
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user