@@ -14,8 +14,10 @@ import (
1414 "bytes"
1515 "crypto/rand"
1616 "encoding/binary"
17+ "encoding/hex"
1718 "errors"
1819 "fmt"
20+ "io"
1921 "math"
2022 "net"
2123 "strconv"
@@ -57,6 +59,7 @@ const (
5759
5860 // Time constants
5961 nanoPerSec = 1_000_000_000
62+ timeFormat = "Mon Jan _2 2006 15:04:05.00000000 (MST)"
6063
6164 // Fixed-point constants (Q*.16, Q*.28, Q*.32)
6265 mask16 = (1 << 16 ) - 1 // 0x0000ffff
@@ -458,6 +461,45 @@ func (r *Response) IsKissOfDeath() bool {
458461 return r .Version < 5 && r .Stratum == 0
459462}
460463
464+ // Log outputs a human-readable representation of the NTP response to the
465+ // provided io.Writer. Meant for debugging purposes.
466+ func (r * Response ) Log (w io.Writer ) {
467+ now := time .Now ().Local ()
468+ fmt .Fprintf (w , " Version: %d\n " , r .Version )
469+ fmt .Fprintf (w , "ClockOffset: %s\n " , r .ClockOffset )
470+ fmt .Fprintf (w , " RTT: %s\n " , r .RTT )
471+ fmt .Fprintf (w , " SystemTime: %s\n " , fmtTime (now ))
472+ fmt .Fprintf (w , " ~TrueTime: %s\n " , fmtTime (now .Add (r .ClockOffset )))
473+ fmt .Fprintf (w , " ClientXmit: %s\n " , fmtTime (r .Timestamps .ClientXmit ))
474+ fmt .Fprintf (w , " ServerRecv: %s\n " , fmtTime (r .Timestamps .ServerRecv ))
475+ fmt .Fprintf (w , " ServerXmit: %s\n " , fmtTime (r .Timestamps .ServerXmit ))
476+ fmt .Fprintf (w , " ClientRecv: %s\n " , fmtTime (r .Timestamps .ClientRecv ))
477+ fmt .Fprintf (w , " Stratum: %d\n " , r .Stratum )
478+ fmt .Fprintf (w , " Leap: %s\n " , fmtLeap (r .Leap ))
479+ fmt .Fprintf (w , " Flags: %s\n " , fmtFlags (r .Flags ))
480+ fmt .Fprintf (w , " Era: %d\n " , r .Era )
481+ fmt .Fprintf (w , " Timescale: %s\n " , fmtTimescale (r .Timescale ))
482+ fmt .Fprintf (w , " Poll: %s\n " , r .Poll )
483+ fmt .Fprintf (w , " Precision: %s\n " , r .Precision )
484+ fmt .Fprintf (w , " RootDelay: %s\n " , r .RootDelay )
485+ fmt .Fprintf (w , " RootDisp: %s\n " , r .RootDispersion )
486+ fmt .Fprintf (w , " RootDist: %s\n " , r .RootDistance )
487+ fmt .Fprintf (w , " MinError: %s\n " , r .MinError )
488+ fmt .Fprintf (w , " RefTime: %s\n " , fmtTime (r .ReferenceTime ))
489+ if r .Version == 5 {
490+ fmt .Fprintf (w , " RefIDBytes: %s\n " , fmtRefIDFilter (r .ReferenceIDFilterValues ))
491+ fmt .Fprintf (w , " Correction: %s\n " , fmtCorrection (r .Correction ))
492+ fmt .Fprintf (w , " Offsets: %s\n " , fmtTimescaleOffsets (r .TimescaleOffsets ))
493+ fmt .Fprintf (w , " MonoOffset: %s\n " , r .MonotonicOffset )
494+ fmt .Fprintf (w , " MonoEpoch: %s\n " , fmtEpoch (r .MonotonicEpochID ))
495+ fmt .Fprintf (w , " Supported: %v\n " , r .SupportedVersions )
496+ fmt .Fprintf (w , " SrvCookie: %s" , fmtCookie (r .ServerCookie ))
497+ } else {
498+ fmt .Fprintf (w , " RefID: %s (0x%08x)\n " , r .ReferenceString (), r .ReferenceID )
499+ fmt .Fprintf (w , " KissCode: %s" , fmtKissCode (r .KissCode ))
500+ }
501+ }
502+
461503// ReferenceString returns the response's ReferenceID value formatted as a
462504// string. If the response's stratum is zero, then the "kiss o' death" string
463505// is returned. If stratum is one, then the server is a reference clock and
@@ -886,3 +928,126 @@ func toInterval(t int8) time.Duration {
886928 return time .Second
887929 }
888930}
931+
932+ func fmtCookie (c uint64 ) string {
933+ if c == 0 {
934+ return "<zero>"
935+ }
936+ return fmt .Sprintf ("0x%016x" , c )
937+ }
938+
939+ func fmtCorrection (c Correction ) string {
940+ if c .OriginDelay < 0 || c .ReturnDelay < 0 {
941+ return "<invalid>"
942+ }
943+ if c .OriginPathID == 0 && c .ReturnPathID == 0 {
944+ return "<none>"
945+ }
946+ return fmt .Sprintf ("%s (0x%04x) / %s (0x%04x)" ,
947+ c .OriginDelay , c .OriginPathID ,
948+ c .ReturnDelay , c .ReturnPathID )
949+ }
950+
951+ func fmtEpoch (epoch uint32 ) string {
952+ if epoch == 0 {
953+ return "<zero>"
954+ }
955+ return fmt .Sprintf ("0x%08x" , epoch )
956+ }
957+
958+ func fmtKissCode (s string ) string {
959+ if s == "" {
960+ return "<empty>"
961+ }
962+ return s
963+ }
964+
965+ func fmtLeap (li LeapIndicator ) string {
966+ switch li {
967+ case LeapNoWarning :
968+ return "No Warning"
969+ case LeapAddSecond :
970+ return "Add Second"
971+ case LeapDelSecond :
972+ return "Delete Second"
973+ default :
974+ return "Unknown"
975+ }
976+ }
977+
978+ func fmtRefIDFilter (filter []byte ) string {
979+ if filter == nil {
980+ return "<nil>"
981+ }
982+ l := min (len (filter ), 24 )
983+ return "0x" + hex .EncodeToString (filter [:l ]) + "..."
984+ }
985+
986+ func fmtFlags (flags ResponseFlags ) string {
987+ ftab := map [ResponseFlags ]string {
988+ FlagSynchronized : "Synchronized" ,
989+ FlagInterleaved : "Interleaved" ,
990+ }
991+
992+ copy := flags
993+ var s strings.Builder
994+ s .WriteString ("[" )
995+ for flags != 0 {
996+ f := flags & - flags
997+ if s .Len () > 1 {
998+ s .WriteString (" " )
999+ }
1000+ if ss , ok := ftab [f ]; ok {
1001+ s .WriteString (ss )
1002+ } else {
1003+ s .WriteString ("Unknown" )
1004+ }
1005+ flags &= ^ f
1006+ }
1007+ fmt .Fprintf (& s , "] (0x%08x)" , uint32 (copy ))
1008+ return s .String ()
1009+ }
1010+
1011+ func fmtTime (value time.Time ) string {
1012+ if value .IsZero () {
1013+ return "<zero>"
1014+ }
1015+ return value .Format (timeFormat )
1016+ }
1017+
1018+ func fmtTimescale (ts Timescale ) string {
1019+ switch ts {
1020+ case TimescaleUTC :
1021+ return "UTC"
1022+ case TimescaleTAI :
1023+ return "TAI"
1024+ case TimescaleUT1 :
1025+ return "UT1"
1026+ case TimescaleUTCSmeared :
1027+ return "UTC(smeared)"
1028+ default :
1029+ return "Unknown"
1030+ }
1031+ }
1032+
1033+ func fmtTimescaleOffset (o TimescaleOffset ) string {
1034+ return fmt .Sprintf ("%s=%v" , fmtTimescale (o .Timescale ), o .Offset )
1035+ }
1036+
1037+ func fmtTimescaleOffsets (offsets []TimescaleOffset ) string {
1038+ if offsets == nil {
1039+ return "<none>"
1040+ }
1041+
1042+ var s strings.Builder
1043+ s .WriteString ("[" )
1044+ for i , o := range offsets {
1045+ if i > 0 {
1046+ s .WriteString (", " )
1047+ }
1048+ s .WriteString (fmtTimescaleOffset (o ))
1049+ }
1050+ s .WriteString ("]" )
1051+
1052+ return s .String ()
1053+ }
0 commit comments