master
go 103 lines 1.97 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package chrony
4
5 import (
6 "fmt"
7 "net"
8 "time"
9
10 "github.com/facebook/time/ntp/chrony"
11 )
12
13 type chronyConn interface {
14 tracking() (*chrony.ReplyTracking, error)
15 activity() (*chrony.ReplyActivity, error)
16 close()
17 }
18
19 func newChronyConn(cfg Config) (chronyConn, error) {
20 conn, err := net.DialTimeout("udp", cfg.Address, cfg.Timeout.Duration())
21 if err != nil {
22 return nil, err
23 }
24
25 client := &chronyClient{
26 conn: conn,
27 client: &chrony.Client{
28 Connection: &connWithTimeout{
29 Conn: conn,
30 timeout: cfg.Timeout.Duration(),
31 },
32 },
33 }
34
35 return client, nil
36 }
37
38 type chronyClient struct {
39 conn net.Conn
40 client *chrony.Client
41 }
42
43 func (c *chronyClient) tracking() (*chrony.ReplyTracking, error) {
44 req := chrony.NewTrackingPacket()
45
46 reply, err := c.client.Communicate(req)
47 if err != nil {
48 return nil, err
49 }
50
51 tracking, ok := reply.(*chrony.ReplyTracking)
52 if !ok {
53 return nil, fmt.Errorf("unexpected reply type, want=%T, got=%T", &chrony.ReplyTracking{}, reply)
54 }
55
56 return tracking, nil
57 }
58
59 func (c *chronyClient) activity() (*chrony.ReplyActivity, error) {
60 req := chrony.NewActivityPacket()
61
62 reply, err := c.client.Communicate(req)
63 if err != nil {
64 return nil, err
65 }
66
67 activity, ok := reply.(*chrony.ReplyActivity)
68 if !ok {
69 return nil, fmt.Errorf("unexpected reply type, want=%T, got=%T", &chrony.ReplyActivity{}, reply)
70 }
71
72 return activity, nil
73 }
74
75 func (c *chronyClient) close() {
76 if c.conn != nil {
77 _ = c.conn.Close()
78 c.conn = nil
79 }
80 }
81
82 type connWithTimeout struct {
83 net.Conn
84 timeout time.Duration
85 }
86
87 func (c *connWithTimeout) Read(p []byte) (n int, err error) {
88 if err := c.Conn.SetReadDeadline(c.deadline()); err != nil {
89 return 0, err
90 }
91 return c.Conn.Read(p)
92 }
93
94 func (c *connWithTimeout) Write(p []byte) (n int, err error) {
95 if err := c.Conn.SetWriteDeadline(c.deadline()); err != nil {
96 return 0, err
97 }
98 return c.Conn.Write(p)
99 }
100
101 func (c *connWithTimeout) deadline() time.Time {
102 return time.Now().Add(c.timeout)
103 }