Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5b6447a

Browse files
committed
Add linux/mac connection types
1 parent b415835 commit 5b6447a

9 files changed

Lines changed: 176 additions & 156 deletions

File tree

conn.go

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,67 @@ import (
1111
"golang.org/x/net/ipv4"
1212
)
1313

14-
// conn is a wrapped for an underlying net.Conn. It provides platform-specific
15-
// support for reading hardware timestamps when available.
14+
// conn is a wrapper for an underlying net.Conn type. It provides
15+
// platform-specific support for reading hardware timestamps when available.
1616
type conn interface {
17-
net.Conn
17+
// Close the connection.
18+
Close() error
1819

19-
// SetTTL sets the IP Time To Live (TTL) field for outgoing packets.
20-
SetTTL(ttl int) error
20+
// Read a message from the connection. It returns the received message,
21+
// the hardware receive timestamp (if available), and any error
22+
// encountered. If hardware timestamps are unavailable, it returns the
23+
// less precise software-based system timestamp instead.
24+
Read() (b []byte, recvTime time.Time, err error)
2125

22-
// ReadWithTimestamp reads a message from the connection. It returns the
23-
// received message, the hardware receive timestamp (if available), and
24-
// any error encountered. If hardware timestamps are unavailable, it
25-
// returns the less precise software-based system timestamp instead.
26-
ReadWithTimestamp() (msg []byte, recvTime time.Time, err error)
26+
// Write a message to the connection.
27+
Write(b []byte) (n int, err error)
2728
}
2829

29-
func setTTL(c net.Conn, ttl int) error {
30-
ipconn := ipv4.NewConn(c)
31-
return ipconn.SetTTL(ttl)
30+
func applyOptions(c net.Conn, opt *QueryOptions) error {
31+
if opt.TTL != 0 {
32+
ipconn := ipv4.NewConn(c)
33+
if err := ipconn.SetTTL(opt.TTL); err != nil {
34+
return err
35+
}
36+
}
37+
38+
err := c.SetDeadline(time.Now().Add(opt.Timeout))
39+
return err
40+
}
41+
42+
// connFallback is a fallback implementation of the conn interface, used
43+
// when only software-based timestamps are available.
44+
type connFallback struct {
45+
base net.Conn
46+
msgBuf []byte
47+
getTime func() time.Time
48+
}
49+
50+
func newConnFallback(base net.Conn, opt *QueryOptions) (conn, error) {
51+
if err := applyOptions(base, opt); err != nil {
52+
return nil, err
53+
}
54+
55+
conn := &connFallback{
56+
base: base,
57+
msgBuf: make([]byte, 8192),
58+
getTime: opt.GetSystemTime,
59+
}
60+
return conn, nil
61+
}
62+
63+
func (c *connFallback) Close() error {
64+
return c.base.Close()
65+
}
66+
67+
func (c *connFallback) Read() (b []byte, recvTime time.Time, err error) {
68+
n, err := c.base.Read(c.msgBuf)
69+
if err != nil {
70+
return nil, time.Time{}, err
71+
}
72+
return c.msgBuf[:n], c.getTime(), nil
73+
}
74+
75+
func (c *connFallback) Write(b []byte) (n int, err error) {
76+
return c.base.Write(b)
3277
}

conn_darwin.go

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,55 +14,63 @@ import (
1414
"golang.org/x/sys/unix"
1515
)
1616

17-
func newConn(base net.Conn, getTime func() time.Time, timeout time.Duration) conn {
18-
return newConnFallback(base, getTime)
17+
type connDarwin struct {
18+
base net.Conn
19+
udpConn *net.UDPConn
20+
msgBuf []byte
21+
ctrlBuf []byte
22+
getTime func() time.Time
1923
}
2024

21-
// enableHardwareTimestamps enables SO_TIMESTAMP on the UDP connection to get
22-
// kernel-level receive timestamps with microsecond precision.
23-
func enableHardwareTimestamps(conn net.Conn) error {
24-
udpConn, ok := conn.(*net.UDPConn)
25+
func newConn(base net.Conn, opt *QueryOptions) (conn, error) {
26+
udpConn, ok := base.(*net.UDPConn)
2527
if !ok {
26-
return nil
28+
return newConnFallback(base, opt)
2729
}
2830

2931
rawConn, err := udpConn.SyscallConn()
3032
if err != nil {
31-
return err
33+
return newConnFallback(base, opt)
3234
}
3335

3436
var setErr error
3537
err = rawConn.Control(func(fd uintptr) {
3638
setErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_TIMESTAMP, 1)
3739
})
38-
if err != nil {
39-
return err
40+
if err != nil || setErr != nil {
41+
return newConnFallback(base, opt)
4042
}
41-
return setErr
42-
}
4343

44-
// readWithTimestamp reads from the connection and returns both the data and
45-
// the kernel-level receive timestamp (if available).
46-
func readWithTimestamp(conn net.Conn, b, oob []byte, opt *QueryOptions) (n int, recvTime time.Time, err error) {
47-
// If we don't have a UDP connection, fallback to imprecise time.
48-
udpConn, ok := conn.(*net.UDPConn)
49-
if !ok {
50-
n, err = conn.Read(b)
51-
return n, opt.GetSystemTime(), err
44+
if err := applyOptions(base, opt); err != nil {
45+
return nil, err
46+
}
47+
48+
conn := &connDarwin{
49+
base: base,
50+
udpConn: udpConn,
51+
msgBuf: make([]byte, 8192),
52+
ctrlBuf: make([]byte, 128),
53+
getTime: opt.GetSystemTime,
5254
}
55+
return conn, nil
56+
}
57+
58+
func (c *connDarwin) Close() error {
59+
return c.base.Close()
60+
}
5361

54-
// Read message with out-of-band control data.
55-
n, on, _, _, err := udpConn.ReadMsgUDP(b, oob)
62+
func (c *connDarwin) Read() (b []byte, recvTime time.Time, err error) {
63+
n, cn, _, _, err := c.udpConn.ReadMsgUDP(c.msgBuf, c.ctrlBuf)
5664
if err != nil {
57-
return n, opt.GetSystemTime(), err
65+
return nil, c.getTime(), err
5866
}
5967

6068
// Get imprecise time in case we can't get a kernel timestamp.
61-
recvTime = opt.GetSystemTime()
69+
recvTime = c.getTime()
6270

6371
// Parse control messages to extract timestamp.
64-
if on > 0 {
65-
msgs, parseErr := unix.ParseSocketControlMessage(oob[:on])
72+
if cn > 0 {
73+
msgs, parseErr := unix.ParseSocketControlMessage(c.ctrlBuf[:cn])
6674
if parseErr == nil {
6775
for _, m := range msgs {
6876
if m.Header.Level == unix.SOL_SOCKET && m.Header.Type == unix.SCM_TIMESTAMP {
@@ -77,5 +85,9 @@ func readWithTimestamp(conn net.Conn, b, oob []byte, opt *QueryOptions) (n int,
7785
}
7886
}
7987

80-
return n, recvTime, nil
88+
return c.msgBuf[:n], recvTime, nil
89+
}
90+
91+
func (c *connDarwin) Write(b []byte) (n int, err error) {
92+
return c.base.Write(b)
8193
}

conn_fallback.go

Lines changed: 0 additions & 39 deletions
This file was deleted.

conn_linux.go

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,55 +14,63 @@ import (
1414
"golang.org/x/sys/unix"
1515
)
1616

17-
func newConn(base net.Conn, getTime func() time.Time, timeout time.Duration) conn {
18-
return newConnFallback(base, getTime)
17+
type connLinux struct {
18+
base net.Conn
19+
udpConn *net.UDPConn
20+
msgBuf []byte
21+
ctrlBuf []byte
22+
getTime func() time.Time
1923
}
2024

21-
// enableHardwareTimestamps enables SO_TIMESTAMPNS on the UDP connection to
22-
// get kernel-level receive timestamps with nanosecond precision.
23-
func enableHardwareTimestamps(conn net.Conn) error {
24-
udpConn, ok := conn.(*net.UDPConn)
25+
func newConn(base net.Conn, opt *QueryOptions) (conn, error) {
26+
udpConn, ok := base.(*net.UDPConn)
2527
if !ok {
26-
return nil
28+
return newConnFallback(base, opt)
2729
}
2830

2931
rawConn, err := udpConn.SyscallConn()
3032
if err != nil {
31-
return err
33+
return newConnFallback(base, opt)
3234
}
3335

3436
var setErr error
3537
err = rawConn.Control(func(fd uintptr) {
3638
setErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_TIMESTAMPNS, 1)
3739
})
38-
if err != nil {
39-
return err
40+
if err != nil || setErr != nil {
41+
return newConnFallback(base, opt)
4042
}
41-
return setErr
42-
}
4343

44-
// readWithTimestamp reads from the connection and returns both the data and
45-
// the kernel-level receive timestamp (if available).
46-
func readWithTimestamp(conn net.Conn, b, oob []byte, opt *QueryOptions) (n int, recvTime time.Time, err error) {
47-
// If we don't have a UDP connection, fallback to imprecise time.
48-
udpConn, ok := conn.(*net.UDPConn)
49-
if !ok {
50-
n, err = conn.Read(b)
51-
return n, opt.GetSystemTime(), err
44+
if err := applyOptions(base, opt); err != nil {
45+
return newConnFallback(base, opt)
46+
}
47+
48+
conn := &connLinux{
49+
base: base,
50+
udpConn: udpConn,
51+
msgBuf: make([]byte, 8192),
52+
ctrlBuf: make([]byte, 128),
53+
getTime: opt.GetSystemTime,
5254
}
55+
return conn, nil
56+
}
57+
58+
func (c *connLinux) Close() error {
59+
return c.base.Close()
60+
}
5361

54-
// Read message with out-of-band control data.
55-
n, on, _, _, err := udpConn.ReadMsgUDP(b, oob)
62+
func (c *connLinux) Read() (b []byte, recvTime time.Time, err error) {
63+
n, cn, _, _, err := c.udpConn.ReadMsgUDP(c.msgBuf, c.ctrlBuf)
5664
if err != nil {
57-
return n, opt.GetSystemTime(), err
65+
return nil, time.Time{}, err
5866
}
5967

60-
// Get imprecise time in case we can't get a kernel timestamp.
61-
recvTime = opt.GetSystemTime()
68+
// Get imprecise time in case we can't get a hardware timestamp.
69+
recvTime = c.getTime()
6270

6371
// Parse control messages to extract timestamp.
64-
if on > 0 {
65-
msgs, parseErr := unix.ParseSocketControlMessage(oob[:on])
72+
if cn > 0 {
73+
msgs, parseErr := unix.ParseSocketControlMessage(c.ctrlBuf[:cn])
6674
if parseErr == nil {
6775
for _, m := range msgs {
6876
// Try nanosecond precision first.
@@ -84,5 +92,9 @@ func readWithTimestamp(conn net.Conn, b, oob []byte, opt *QueryOptions) (n int,
8492
}
8593
}
8694

87-
return n, recvTime, nil
95+
return c.msgBuf[:n], recvTime, nil
96+
}
97+
98+
func (c *connLinux) Write(b []byte) (n int, err error) {
99+
return c.base.Write(b)
88100
}

conn_other.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,8 @@ package ntp
88

99
import (
1010
"net"
11-
"time"
1211
)
1312

14-
func newConn(base net.Conn, getTime func() time.Time, _ time.Duration) conn {
15-
return newConnFallback(base, getTime)
13+
func newConn(base net.Conn, opt *QueryOptions) (conn, error) {
14+
return newConnFallback(base, opt)
1615
}

0 commit comments

Comments
 (0)