@@ -12,6 +12,7 @@ package ntp
1212
1313import (
1414 "bytes"
15+ "crypto/rand"
1516 "encoding/binary"
1617 "errors"
1718 "fmt"
@@ -266,15 +267,15 @@ type Response struct {
266267 Stratum uint8
267268
268269 // Timescale reports the timescale used by the server to timestamp
269- // messages it sent and received. Used only in NTPv5 .
270+ // messages it sent and received. Always TimescaleUTC for NTPv3 and NTPv4 .
270271 Timescale Timescale
271272
272273 // TimescaleOffsets contains the time offsets of any requested additional
273274 // timescales relative to the primary timescale. Used only in NTPv5.
274275 TimescaleOffsets []TimescaleOffset
275276
276277 // Era is the NTP era number returned by the server. Era 0 spans
277- // 1900-2036, Era 1 spans 2036-2172, etc. Used only in NTPv5.
278+ // 1900-2036, Era 1 spans 2036-2172, etc.
278279 Era uint8
279280
280281 // ReferenceID is a 32-bit integer used to help identify which server or
@@ -716,6 +717,62 @@ func getEra(t time.Time) int64 {
716717 return int64 (ntpSec >> 32 )
717718}
718719
720+ // offset calculates an uncorrected clock offset using the four NTP
721+ // timestamps.
722+ func offset (t1 , t2 , t3 , t4 time.Time ) time.Duration {
723+ return (t2 .Sub (t1 ) + t3 .Sub (t4 )) / 2
724+ }
725+
726+ // rtt calculates the round-trip-time delay using the four NTP timestamps.
727+ func rtt (t1 , t2 , t3 , t4 time.Time ) time.Duration {
728+ return max (t4 .Sub (t1 )- t3 .Sub (t2 ), 0 )
729+ }
730+
731+ // minError calculates a lower bound on the error between the client and
732+ // server clocks using the four NTP timestamps.
733+ func minError (t1 , t2 , t3 , t4 time.Time ) time.Duration {
734+ if t2 .Before (t1 ) || t4 .Before (t3 ) {
735+ return max (t1 .Sub (t2 ), t3 .Sub (t4 ))
736+ }
737+ return 0
738+ }
739+
740+ // rootDistance estimates the root distance between the client and the stratum
741+ // 1 server.
742+ func rootDistance (rtt , rootDelay , rootDisp time.Duration ) time.Duration {
743+ // The root distance is:
744+ // the maximum error due to all causes of the local clock
745+ // relative to the primary server. It is defined as half the
746+ // total delay plus total dispersion plus peer jitter.
747+ // (https://tools.ietf.org/html/rfc5905#appendix-A.5.5.2)
748+ //
749+ // In the reference implementation, it is calculated as follows:
750+ // rootDist = max(MINDISP, rootDelay + rtt)/2 + rootDisp
751+ // + peerDisp + PHI * (uptime - peerUptime)
752+ // + peerJitter
753+ // For an SNTP client which sends only a single packet, most of these
754+ // terms are irrelevant and become 0.
755+ totalDelay := rtt + rootDelay
756+ return totalDelay / 2 + rootDisp
757+ }
758+
759+ // randUint64 creates a random non-zero 64-bit cookie value.
760+ func randUint64 () (uint64 , error ) {
761+ for range 4 {
762+ var cookieBytes [8 ]byte
763+ _ , err := rand .Read (cookieBytes [:])
764+ if err != nil {
765+ return 0 , err
766+ }
767+
768+ cookie := binary .BigEndian .Uint64 (cookieBytes [:])
769+ if cookie != 0 {
770+ return cookie , nil
771+ }
772+ }
773+ return 0 , errors .New ("failed to generate non-zero random value" )
774+ }
775+
719776// An timestamp is a 64-bit fixed-point (Q32.32) representation of the
720777// number of seconds elapsed.
721778type timestamp uint64
@@ -740,15 +797,24 @@ func (t timestamp) Time(era uint8) time.Time {
740797// TimeV4 interprets the NTPv3/NTPv4 timestamp value as an absolute time and
741798// returns the corresponding time.Time value.
742799func (t timestamp ) TimeV4 () time.Time {
800+ if inferEra (t ) == 1 {
801+ return ntpEra1 .Add (t .Duration ())
802+ }
803+ return ntpEra0 .Add (t .Duration ())
804+ }
805+
806+ // inferEra infers an NTP era from a timestamp value. This function is used
807+ // only for NTPv3 and NTPv4 timestamps.
808+ func inferEra (t timestamp ) uint8 {
743809 // Assume NTP era 1 (year 2036+) if the raw timestamp suggests a year
744- // before 1970. Otherwise assume NTP era 0. This allows the function to
745- // report an accurate time value both before and after the 0-to-1 era
746- // rollover .
810+ // before 1970. Otherwise assume NTP era 0. This allows us to properly
811+ // handle the rollover from era 0 to 1 when converting timestamps to
812+ // times .
747813 const t1970 = 0x83aa_7e80_0000_0000
748814 if uint64 (t ) < t1970 {
749- return ntpEra1 . Add ( t . Duration ())
815+ return 1
750816 }
751- return ntpEra0 . Add ( t . Duration ())
817+ return 0
752818}
753819
754820// toTimestamp converts a Time value into its 64-bit fixed-point timestamp
0 commit comments