2023-09-01 03:11:35 +08:00
|
|
|
package ntp
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2023-09-25 09:11:20 +08:00
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
2023-11-03 21:01:45 +08:00
|
|
|
"github.com/metacubex/mihomo/component/dialer"
|
|
|
|
"github.com/metacubex/mihomo/component/proxydialer"
|
|
|
|
"github.com/metacubex/mihomo/log"
|
2023-09-25 09:11:20 +08:00
|
|
|
|
2023-09-03 17:48:52 +08:00
|
|
|
M "github.com/sagernet/sing/common/metadata"
|
|
|
|
"github.com/sagernet/sing/common/ntp"
|
2023-09-01 03:11:35 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
var offset time.Duration
|
|
|
|
var service *Service
|
|
|
|
|
|
|
|
type Service struct {
|
2023-09-03 17:48:52 +08:00
|
|
|
server M.Socksaddr
|
2023-09-25 09:11:20 +08:00
|
|
|
dialer proxydialer.SingDialer
|
2023-09-03 17:48:52 +08:00
|
|
|
ticker *time.Ticker
|
|
|
|
ctx context.Context
|
|
|
|
cancel context.CancelFunc
|
|
|
|
mu sync.Mutex
|
|
|
|
syncSystemTime bool
|
|
|
|
running bool
|
2023-09-01 03:11:35 +08:00
|
|
|
}
|
|
|
|
|
2023-09-25 09:11:20 +08:00
|
|
|
func ReCreateNTPService(server string, interval time.Duration, dialerProxy string, syncSystemTime bool) {
|
2023-09-01 03:11:35 +08:00
|
|
|
if service != nil {
|
|
|
|
service.Stop()
|
|
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
2023-09-03 17:48:52 +08:00
|
|
|
service = &Service{
|
|
|
|
server: M.ParseSocksaddr(server),
|
2023-09-25 09:11:20 +08:00
|
|
|
dialer: proxydialer.NewByNameSingDialer(dialerProxy, dialer.NewDialer()),
|
2023-09-03 17:48:52 +08:00
|
|
|
ticker: time.NewTicker(interval * time.Minute),
|
2023-09-25 09:11:20 +08:00
|
|
|
ctx: ctx,
|
|
|
|
cancel: cancel,
|
2023-09-03 17:48:52 +08:00
|
|
|
syncSystemTime: syncSystemTime,
|
|
|
|
}
|
2023-09-01 03:11:35 +08:00
|
|
|
service.Start()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (srv *Service) Start() {
|
|
|
|
srv.mu.Lock()
|
|
|
|
defer srv.mu.Unlock()
|
2023-09-03 17:48:52 +08:00
|
|
|
log.Infoln("NTP service start, sync system time is %t", srv.syncSystemTime)
|
2024-01-17 16:13:55 +08:00
|
|
|
err := srv.update()
|
|
|
|
if err != nil {
|
|
|
|
log.Errorln("Initialize NTP time failed: %s", err)
|
|
|
|
return
|
|
|
|
}
|
2023-09-01 03:11:35 +08:00
|
|
|
service.running = true
|
2023-09-03 17:48:52 +08:00
|
|
|
go srv.loopUpdate()
|
2023-09-01 03:11:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (srv *Service) Stop() {
|
|
|
|
srv.mu.Lock()
|
|
|
|
defer srv.mu.Unlock()
|
2023-09-02 12:37:43 +08:00
|
|
|
if service.running {
|
|
|
|
srv.ticker.Stop()
|
|
|
|
srv.cancel()
|
|
|
|
service.running = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (srv *Service) Running() bool {
|
|
|
|
if srv == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
srv.mu.Lock()
|
|
|
|
defer srv.mu.Unlock()
|
|
|
|
return srv.running
|
2023-09-01 03:11:35 +08:00
|
|
|
}
|
|
|
|
|
2024-01-17 16:13:55 +08:00
|
|
|
func (srv *Service) update() error {
|
2023-09-04 18:19:22 +08:00
|
|
|
var response *ntp.Response
|
|
|
|
var err error
|
|
|
|
for i := 0; i < 3; i++ {
|
2024-01-17 16:13:55 +08:00
|
|
|
if response, err = ntp.Exchange(context.Background(), srv.dialer, srv.server); err == nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if i == 2 {
|
|
|
|
return err
|
2023-09-04 18:19:22 +08:00
|
|
|
}
|
2023-09-01 03:11:35 +08:00
|
|
|
}
|
2023-09-03 17:48:52 +08:00
|
|
|
offset = response.ClockOffset
|
2023-09-01 03:11:35 +08:00
|
|
|
if offset > time.Duration(0) {
|
2023-09-03 17:48:52 +08:00
|
|
|
log.Infoln("System clock is ahead of NTP time by %s", offset)
|
2023-09-01 03:11:35 +08:00
|
|
|
} else if offset < time.Duration(0) {
|
2023-09-03 17:48:52 +08:00
|
|
|
log.Infoln("System clock is behind NTP time by %s", -offset)
|
|
|
|
}
|
|
|
|
if srv.syncSystemTime {
|
|
|
|
timeNow := response.Time
|
2024-01-17 16:13:55 +08:00
|
|
|
syncErr := setSystemTime(timeNow)
|
|
|
|
if syncErr == nil {
|
2023-09-04 18:19:22 +08:00
|
|
|
log.Infoln("Sync system time success: %s", timeNow.Local().Format(ntp.TimeLayout))
|
2023-09-03 17:48:52 +08:00
|
|
|
} else {
|
2024-01-17 16:13:55 +08:00
|
|
|
log.Errorln("Write time to system: %s", syncErr)
|
2023-09-03 17:48:52 +08:00
|
|
|
srv.syncSystemTime = false
|
|
|
|
}
|
|
|
|
}
|
2024-01-17 16:13:55 +08:00
|
|
|
return nil
|
2023-09-03 17:48:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (srv *Service) loopUpdate() {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-srv.ctx.Done():
|
|
|
|
return
|
|
|
|
case <-srv.ticker.C:
|
|
|
|
}
|
2024-01-17 16:13:55 +08:00
|
|
|
err := srv.update()
|
|
|
|
if err != nil {
|
|
|
|
log.Warnln("Sync time failed: %s", err)
|
|
|
|
}
|
2023-09-01 03:11:35 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func Now() time.Time {
|
|
|
|
now := time.Now()
|
2023-09-02 12:37:43 +08:00
|
|
|
if service.Running() && offset.Abs() > 0 {
|
2023-09-01 03:11:35 +08:00
|
|
|
now = now.Add(offset)
|
|
|
|
}
|
|
|
|
return now
|
|
|
|
}
|