Network Time Protocol NTP SNTP

Share your advanced PureBasic knowledge/code with the community.
Baldrick
Addict
Addict
Posts: 860
Joined: Fri Jul 02, 2004 6:49 pm
Location: Australia

Network Time Protocol NTP SNTP

Post by Baldrick »

Here is a piece of code I am in the process of writing which some of you may find useful in your projects where you may wish to access a network time server

The code is not complete, but I figured is comprehensive enough to place here in tips & tricks. :)
1 flaw in my code I know of is that I have not worked up any way of getting fractions of seconds from the PB unix timestamp.
I have not tried to do this yet & do not know if it is even possible.
Small modification made to EncodeNTPVersionModeRequest(Version.a,Mode.a) procedure. 22/11/2009

Code: Select all

; SNTP client as a helper for PB community people wishing for an accurate timestamp in projects
; 22/11/2009
; Updated 24/01/2010
; Baldrick 
; PB4.40, 4.41RC

;{  NTP DataGram Structure diagram 
;-NTP structure diagram
;                       1                   2                   3
;   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
;  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
; 0|LI | VN  |Mode |    Stratum    |     Poll      |   Precision   |
;  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
; 4|                    Root Delay (32)                            |
;  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
; 8|                    Root Dispersion (32)                       |
;  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;12|                    Reference Identifier (32)                  |
;  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;16|  Ref_Timestamp1                                               |
;20|  Ref_Timestamp2    Reference Timestamp (64)                   |
;  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;24|  Orig_Timestamp1                                              |
;28|  Orig_Timestamp2   Originate Timestamp (64)                   |
;  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;32|  Rx_Timestamp1                                                |
;36|  Rx_Timestamp2     Receive Timestamp (64)                     |
;  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;40|  Tx_Timestamp1                                                |
;44|  Tx_Timestamp2     Transmit Timestamp (64)                    |
;  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;48|                    Key Identifier (optional) (32)             |
;  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;52|  Msg_Digest1                                                  |
;56|  Msg_Digest2                                                  |
;60|  Msg_Digest3       Message Digest (optional) (128)            |
;64|  Msg_Digest4                                                  |68
;  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
;}
;{ Header detail:
;-Header Detail 
; Leap Indicator
;This is a two-bit code warning of an impending leap second to be inserted/deleted 
;in the last minute of the current day, with bit 0 and bit 1, respectively, 
;LI Binary|Value Meaning:
;00 0 no warning
;01 1 last minute has 61 seconds
;10 2 last minute has 59 seconds)
;11 3 alarm condition (clock Not synchronized)
;  -------------------------------------------------------------------------------
;
;Version
;This is a three-bit integer indicating the NTP/SNTP version number. 
;The version number is 3 for Version 3 (IPv4 only) and 4 for Version 4 
;(IPv4, IPv6 and OSI). If necessary to distinguish between IPv4, IPv6 and OSI, 
;the encapsulating context must be inspected.
;  --------------------------------------------------------------------------------
;
;
;Mode 
;This is a three-bit integer indicating the mode, with values defined as follows:
;0 reserved
;1 symmetric active
;2 symmetric passive
;3 client
;4 server
;5 broadcast
;6 reserved For NTP control message
;7 reserved For private use 
;
;In unicast and anycast modes, the client sets this field to 3 (client) in the 
;request and the server sets it to 4 (server) in the reply. 
;In multicast mode, the server sets this field To 5 (broadcast).
;  -----------------------------------------------------------------------------------
;
;Stratum:
;This is a eight-bit unsigned integer indicating the stratum level of 
;the local clock, with values defined as follows:  
;0 unspecified or unavailable
;1 primary reference (e.g., radio clock)
;2-15 secondary reference (via NTP Or SNTP)
;16-255 reserved  
;  ------------------------------------------------------------------------------------
;
;Poll Interval: 
;This is an eight-bit signed integer indicating the maximum 
;interval between successive messages, in seconds to the nearest power of two. 
;The values that can appear in this field presently range from 4 (16 s) to 14 (16284 s)
;however, most applications use only the sub-range 6 (64 s) to 10 (1024 s). 
; --------------------------------------------------------------------------------------
;
;Precision:
;This is an eight-bit signed integer indicating the precision of the local clock, 
;in seconds to the nearest power of two. The values that normally appear in this 
;field range from -6 for mains-frequency clocks to -20 for microsecond clocks found 
;in some workstations
; --------------------------------------------------------------------------------------
;
;Reference Identifier: This is a 32-bit string identifying the particular reference 
;source. In the Case of NTP Version 3 Or Version 4 stratum-0 (unspecified) Or stratum-1 
;(primary) servers, this is a four-character ASCII string, left justified And zero padded 
;To 32 bits. In NTP Version 3 secondary servers, this is the 32-bit IPv4 address of the 
;reference source. In NTP Version 4 secondary servers, this is the low order 32 bits of 
;the latest transmit timestamp of the reference source. 
; ---------------------------------------------------------------------------------------
;
;Reference Timestamp: 
;This is the time at which the local clock was last set Or corrected, 
;
;Originate Timestamp: 
;This is the time at which the request departed the client 
;For the server.
;
;Receive Timestamp: 
;This is the time at which the request arrived at the server.
;
;Transmit Timestamp: 
;This is the time at which the reply departed the server 
;For the client.
; ---------------------------------------------------------------------------------------
; Originate Timestamp T1 time request sent by client
; Receive Timestamp T2 time request received by server
; Transmit Timestamp T3 time reply sent by server
; Destination Timestamp T4 time reply received by client
; 
; The roundtrip delay d And local clock offset t are defined As
; 
; d = (T4 - T1) - (T2 - T3) t = ((T2 - T1) + (T3 - T4)) / 2.
;
;}

   #NTP_VERSION=3 
   #NTP_CLIENT=3 
   #NTP_PORT=123 
   #NTP_BUFFER_SIZE=48 
   #NTP_Unix_TimeStamp_Differential=$83AA7E80 ;thanks to Harkon
   #SecondsPerMinte=60 
   
Enumeration           ;LeapIndicator
    #NoWarning		      ;0 - No warning
	  #LastMinute61	      ;1 - Last minute has 61 seconds
	  #LastMinute59	      ;2 - Last minute has 59 seconds
    #Alarm			        ;3 - Alarm condition (clock Not synchronized)
EndEnumeration

Enumeration           ;Mode 
    #Unknown            ;0 - Reserved
		#SymmetricActive    ;1 - Symmetric active
		#SymmetricPassive	  ;2 - Symmetric pasive
		#Client				      ;3 - Client
		#Server				      ;4 - Server
		#Broadcast			    ;5 - Broadcast
		#ReservedA				  ;6 - Reserved 
		#ReservedB          ;7 - Reserved 
EndEnumeration 
;- Set your own UTC to local Time Offset here for non windows  
CompilerSelect #PB_Compiler_OS
  CompilerCase #PB_OS_Windows
      Select GetTimeZoneInformation_(Tz.Time_zone_information) 
        Case #TIME_ZONE_ID_STANDARD
          TimeZoneOffset=(Tz\Bias+Tz\StandardBias) 
        Case #TIME_ZONE_ID_DAYLIGHT
          TimeZoneOffset=(Tz\Bias+Tz\DaylightBias)
      EndSelect
;   CompilerCase #PB_OS_Linux 
;     ;get local computers timezone bias & setup into offset minutes
;   CompilerCase #PB_OS_AmigaOS
;     ;get local computers timezone bias & setup into offset minutes
;   CompilerCase #PB_OS_MacOS
;     ;get local computers timezone bias & setup into offset minutes
  CompilerDefault 
    TimeZoneOffset=-660 ;manually set to your own bias for non windows OS's in minutes 
    ;fyi, -660 is my current Sydney Au timezone with Daylight time active @23/01/2010
CompilerEndSelect 
   Timeout.i=3000   ;milliseconds for network response timeout 
;- Some ntp servers
; a list can be found here:
; http://support.ntp.org/bin/view/Servers/StratumTwoTimeServers
   ;ServerName$="au.pool.ntp.org"
   ServerName$="202.60.94.15" ;=au.pool.ntp.org
   ;ServerName$="ntp.bri.connect.com.au"
   ;ServerName$="pool.ntp.org"
   ;ServerName$="0.pool.ntp.org"
   ;ServerName$="202.81.208.160" ;=0.pool.ntp.org
   ;ServerName$="time.windows.com"
Structure ntp_struct 
   Li_Vn_Mode.a       ;ubyte sharing Leap indicator, Version number, Mode
   stratum.a          ;ubyte
   poll_interval.b    ;byte
   precision.b        ;byte 
   root_delay.l       ;long
   root_dispersion.l  ;ulong
   ref_id.l           ;ulong
   ref_timestamp1.l   ;ulong
   ref_timestamp2.l   ;ulong
   orig_timestamp1.l  ;ulong
   orig_timestamp2.l  ;ulong
   rx_timestamp1.l    ;ulong
   rx_timestamp2.l    ;ulong
   tx_timestamp1.l    ;ulong
   tx_timestamp2.l    ;ulong
;    key_id.l          ;optional  ulong
;    msg_digest1.l     ;optional  ulong
;    msg_digest2.l     ;optional  ulong
;    msg_digest3.l     ;optional  ulong
;    msg_digest4.l     ;optional  ulong
EndStructure 
Ntp_Data.ntp_struct 


Procedure ByteOrder32(Val.l) 
  *Mem=AllocateMemory(SizeOf(long)) 
    If  *Mem 
        a=0:b=24
        PokeL(*Mem,Val) 
          Repeat 
            Result.l+PeekA(*Mem+a)<<b
            a+1:b-8
          Until a>3 
        FreeMemory(*Mem)             
      ProcedureReturn Result
    EndIf
EndProcedure

Procedure.a EncodeNTPVersionModeRequest(Version.a,Mode.a) ;bits 1 & 2 are for leap indicator & need not be touched here
    ProcedureReturn Version<<3+Mode
EndProcedure 

Procedure EncodeNTPRequestTimeStamp(UNIX_Timestamp.l,LocalUTCOffsetMinutes.l)
    TimeOffset=#SecondsPerMinte*LocalUTCOffsetMinutes  
    UNIX_Timestamp+TimeOffset
    UNIX_Timestamp+#NTP_Unix_TimeStamp_Differential
  ProcedureReturn ByteOrder32(UNIX_Timestamp) 
EndProcedure

Procedure DecodeNTPTimestamp(NTP_Timestamp.l,NTP_Timestamp2.l,LocalUTCOffsetMinutes.l) 
    Result=ByteOrder32(NTP_Timestamp)+(ByteOrder32(NTP_Timestamp2)>>31) 
  ProcedureReturn (Result-#NTP_Unix_TimeStamp_Differential)-(#SecondsPerMinte*LocalUTCOffsetMinutes);TimeOffset
EndProcedure 

Procedure DecodeTimestampNTPasUTC(NTP_Timestamp.l,NTP_Timestamp2.l) 
    Result=ByteOrder32(NTP_Timestamp)+(ByteOrder32(NTP_Timestamp2)>>31)
  ProcedureReturn Result-#NTP_Unix_TimeStamp_Differential
EndProcedure 

Procedure.a DecodeNTPLeapIndicator(HeaderByte.a) ;isolate & return value of Leap indicator bits
  ProcedureReturn HeaderByte>>6 ;move 2 bits right to pos 1,2 for correct value
EndProcedure 

Procedure.a DecodeNTPVersion(HeaderByte.a) ;isolate & return value of Version bits
  HeaderByte<<2 ;move left 2 bits to drop leap indicator bits
  ProcedureReturn HeaderByte>>5 ;move 3 remaining bits right to bit pos 1,2,3 to get wanted value
EndProcedure

Procedure.a DecodeNTPMode(HeaderByte.a) ; isolate & return value of Mode bits
  HeaderByte<<5 ;move all unwanted bits off to left so they are lost
  ProcedureReturn HeaderByte>>5 ;move wanted ramining 3 bits back to pos 1,2,3
EndProcedure

*membuffer=AllocateMemory(#NTP_BUFFER_SIZE) 

;- Encode request 
  If *membuffer  
    PokeA(*membuffer,EncodeNTPVersionModeRequest(#NTP_VERSION,#NTP_CLIENT)) 
    PokeL(*membuffer+40,EncodeNTPRequestTimeStamp(Date(),TimeZoneOffset)) 
      CompilerSelect #PB_Compiler_OS
        CompilerCase #PB_OS_Windows
          If GetSystemTime_(sTime.systemtime) ;get Milliseconds from system direct
            PokeL(*membuffer+44,ByteOrder32(sTime\wMilliseconds<<21)) ;shift 1st 10 bits  
          EndIf ;to highest order to match as close as possible to 32 bit fraction stamp
      CompilerEndSelect 
        Else    
    Debug "membuffer Failed to allocate memory" 
    End 
  EndIf 
   
  IsNetwork=InitNetwork() 
    If Not IsNetwork 
      MessageRequester("Network","Initialisation fail",#MB_ICONERROR)
      End 
    EndIf 
    If IsNetwork 
      ClientID=OpenNetworkConnection(ServerName$,#NTP_PORT,#PB_Network_UDP) 
        If Not ClientID 
          MessageRequester("Network","Connection to: [ "+ServerName$+" ] failed",#MB_ICONERROR) 
          End 
            Else 
          SendNetworkData(ClientID,*membuffer,#NTP_BUFFER_SIZE) 
          TimerA=ElapsedMilliseconds() 
          TimerA+Timeout 
          Debug "Connection to [ "+ServerName$+" ] opened, request sent"
            Repeat 
              TimerB=ElapsedMilliseconds() 
                If NetworkClientEvent(ClientID) 
                  DtRxd=1 
                    If ReceiveNetworkData(ClientID,*membuffer,#NTP_BUFFER_SIZE) 
                      With Ntp_Data  
                        \Li_Vn_Mode=PeekA(*membuffer)  
                        \stratum=PeekA(*membuffer+1) 
                        \poll_interval=PeekA(*membuffer+2) 
                        \precision=PeekA(*membuffer+3)    
                        \root_delay=PeekL(*membuffer+4)   
                        \root_dispersion=PeekL(*membuffer+8)   
                        \ref_id=PeekL(*membuffer+12)  
                        \ref_timestamp1=PeekL(*membuffer+16)  
                        \ref_timestamp2=PeekL(*membuffer+20)  
                        \orig_timestamp1=PeekL(*membuffer+24)  
                        \orig_timestamp2=PeekL(*membuffer+28)   
                        \rx_timestamp1=PeekL(*membuffer+32)  
                        \rx_timestamp2=PeekL(*membuffer+36)  
                        \tx_timestamp1=PeekL(*membuffer+40)   
                        \tx_timestamp2=PeekL(*membuffer+44)  
                      EndWith 
                    EndIf 
                EndIf  
              Delay(1) 
            Until TimerB>TimerA Or DtRxd 
          If Not DtRxd 
            MessageRequester("Timeout", "No response from [ "+ServerName$+" ]",#MB_ICONEXCLAMATION) 
          EndIf            
        EndIf 
    EndIf 
  If DtRxd      
    With Ntp_Data
      Debug "---Header information:---------------------------------------------------"
      Debug "Li_Vn_Mode raw byte value = "+Str(\Li_Vn_Mode ) 
        Select DecodeNTPLeapIndicator(\Li_Vn_Mode) 
          Case #NoWarning	 
	          Debug "Leap indicator = no warning" 
	        Case #LastMinute61 
	          Debug "Leapindicator = Last minute has 61 seconds" 
	        Case #LastMinute59 
	          Debug "Leap indicator = Last minute has 59 seconds"
          Case #Alarm  
            Debug "Leap indicator = Alarm condition (clock Not synchronized)" 
        EndSelect 
      Debug "Version = "+Str(DecodeNTPVersion(\Li_Vn_Mode)) 
        Select DecodeNTPMode(\Li_Vn_Mode) 
          Case #Unknown,#ReservedA,#ReservedB 
            Debug "Mode message = Value Reserved" 
          Case #SymmetricActive 
            Debug "Mode message = Symmetric Active" 
          Case #SymmetricPassive 
            Debug "Mode message = Symmetric Passive" 
          Case #Client 
            Debug "Mode message = Client" 
          Case #Server 
            Debug "Mode message = Server" 
          Case #Broadcast 
            Debug "Mode message = Broadcast" 
        EndSelect 
      Debug "stratum = "+Str(\stratum) 
      Debug "poll_interval = "+Str(\poll_interval) 
      Debug "precision = "+Str(\precision) 
      Debug "--root info still To Do--------------------------------------------------"
      Debug "root_delay = "+Str(\root_delay) 
      Debug "root_dispersion = "+Str(\root_dispersion) 
      Debug "----Reference IP Address-------------------------------------------------"
      Debug "ref_id = "+Str(\ref_id) 
      Debug "Ref IP Address = "+IPString(\ref_id) 
      Debug "A reverse DNS lookup could now be done using Freaks code here:"
      Debug "http://www.purebasic.fr/english/viewtopic.php?f=12&t=25916&p=182886"
      Debug "----Server timestamp was last syncronized @-UTC--------------------------"
      Debug "ref_timestamp1 = "+Str(\ref_timestamp1) 
      Debug "ref_timestamp2 = "+Str(\ref_timestamp2) 
      Debug "Reference Timestamp = "+FormatDate("%hh:%ii:%ss - %dd/%mm/%yyyy",DecodeTimestampNTPasUTC(\ref_timestamp1,\ref_timestamp2)) 
      Debug "---Timestamp originating from local computer--UTC----------------------"
      Debug "orig_timestamp1 = "+Str(\orig_timestamp1) 
      Debug "orig_timestamp2 = "+Str(\orig_timestamp2) 
      Debug "Originating Timestamp = "+FormatDate("%hh:%ii:%ss - %dd/%mm/%yyyy",DecodeTimestampNTPasUTC(\orig_timestamp1,\orig_timestamp2))
      Debug "-------------------------------------------------------------------------"
      Debug "rx_timestamp1 = "+Str(\rx_timestamp1) 
      Debug "rx_timestamp2 = "+Str(\rx_timestamp2) 
      Debug "tx_timestamp1 = "+Str(\tx_timestamp1) 
      Debug "tx_timestamp2 = "+Str(\tx_timestamp2) 
      Debug "Current time, including localised timezone offset of "+StrF(-TimeZoneOffset/60,2)+" Hours or "+Str(-TimeZoneOffset)+" minutes"
      Debug FormatDate("%hh:%ii:%ss - %dd/%mm/%yyyy",DecodeNTPTimestamp(\tx_timestamp1,\tx_timestamp2,TimeZoneOffset)) 
      Debug "As UTC time:" 
      Debug FormatDate("%hh:%ii:%ss - %dd/%mm/%yyyy",DecodeTimestampNTPasUTC(\tx_timestamp1,\tx_timestamp2))   
      Debug "-------------------------------------------------------------------------"
    EndWith 
;{ This part not yet finished.( Something wrong with my calculations here) 
      Ms1K=$FFFFFFFF/$3E8 
      a.f= (NTP_Data\tx_timestamp2/Ms1K)
      tx.l=a 
      a.f= (NTP_Data\rx_timestamp2/Ms1K)
      rx.l=a
      delay=tx-rx
        If delay<0  ;should not be neccesary ??
          delay=-delay 
        EndIf 
      Debug "server processing delay = "+Str(delay)+" milliseconds" 
      Debug ""
;}
  EndIf       
    If ClientID 
      CloseNetworkConnection(ClientID) 
    EndIf 
    If *membuffer
      FreeMemory(*membuffer) 
    EndIf 
End  


Updated to use Rescators Endian procedure. Look here for details on Rescators code:
http://www.purebasic.fr/english/viewtop ... 12&t=14524
Updated with a little more information. 24/01/2010
Last edited by Baldrick on Sun Jan 24, 2010 5:08 am, edited 1 time in total.
SFSxOI
Addict
Addict
Posts: 2970
Joined: Sat Dec 31, 2005 5:24 pm
Location: Where ya would never look.....

Re: Network Time Protocol NTP SNTP

Post by SFSxOI »

Thanks for posting, very nice.

For PB 4.31, I changed all occurances of '.a' to '.c' and all occurances of PeekA and PokeA to PeekC and PokeC. Seems to work, so were these changes correct for use in PB 4.31?
Baldrick
Addict
Addict
Posts: 860
Joined: Fri Jul 02, 2004 6:49 pm
Location: Australia

Re: Network Time Protocol NTP SNTP

Post by Baldrick »

SFSxOI wrote: For PB 4.31, I changed all occurances of '.a' to '.c' and all occurances of PeekA and PokeA to PeekC and PokeC. Seems to work, so were these changes correct for use in PB 4.31?
I see no problem. The only reason I was using the '.a' and PeekA / PokeA was in keeping with the original protocols use of unsigned bytes. The protocol also uses a mix of both signed & unsigned longs but PB's lack of an unsigned long has not stopped it. ( Same horse different jockey sort of thing. :) ) - Still it would be nice to get that uLong included into PB at some stage, especially now that we have access to unsigned bytes as '.a' & unsigned words as '.u'... :mrgreen:
I guess that raises a small question of "if I had been able to use uLongs, would I have still needed to be swapping the byte order in all the timestamp data to & from the remote server??"
alokdube
Enthusiast
Enthusiast
Posts: 148
Joined: Fri Nov 02, 2007 10:55 am
Location: India
Contact:

Re: Network Time Protocol NTP SNTP

Post by alokdube »

thanks for this one! was really useful!
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Re: Network Time Protocol NTP SNTP

Post by Rescator »

Baldrick wrote:I guess that raises a small question of "if I had been able to use uLongs, would I have still needed to be swapping the byte order in all the timestamp data to & from the remote server??"
Probably! Seeing as the majority of "internet" standards use network byte order (aka Big Endian or Motorola order) as opposed to the x86 Little Endian (aka Intel order), I assume the NTP proctocol uses network byte order as well!
Baldrick
Addict
Addict
Posts: 860
Joined: Fri Jul 02, 2004 6:49 pm
Location: Australia

Re: Network Time Protocol NTP SNTP

Post by Baldrick »

Rescator wrote:
Probably! Seeing as the majority of "internet" standards use network byte order (aka Big Endian or Motorola order) as opposed to the x86 Little Endian (aka Intel order), I assume the NTP proctocol uses network byte order as well!
Yes you are correct as usual Rescator. That comment was as much as anything just me having a little snipe about my wanting an unsigned long type added to PB :oops:, which from my reading around the forum just isn't going to happen.. :cry:

I have also been working on another part for this little app using RFC868 which allows me to use just a single long to sync this Ntp time from PC to multiple Pic microcontrollers on the LAN side, which is triggered by a single empty request from these controllers via official port 37 (Daytime or Time port whichever is 37 ). This also uses the same byte order.
Baldrick
Addict
Addict
Posts: 860
Joined: Fri Jul 02, 2004 6:49 pm
Location: Australia

Re: Network Time Protocol NTP SNTP

Post by Baldrick »

Here is another smaller version using RFC868 protocol which is much simpler than the 1 posted above. Hopefully may be useful to somebody. :)
The only thing I would ask here is that people trying or using this code, please observe the requests of NIST in the link below which incidently states:
All users should ensure that their software NEVER queries a server more frequently than once every 4 seconds. Systems that exceed this rate will be refused service. In extreme cases, systems that exceed this limit may be considered as attempting a denial-of-service attack.
http://tf.nist.gov/tf-cgi/servers.cgi

Code: Select all

; RFC 868 Network Time client using Time port 37 
; this protocol returns a simple 4 byte number representing the time since
; 00:00:00 January 1st 1900 as UTC & the same as the main ntp protocol will overflow
; sometime in 2036 apparently. 
; ( Note: this is an older protocol not much in use anymore )
; I have just added the calculations to read as your own local times.
; PB4.40,4.41RC

  #TIME_PORT=37 ;#TIME_PORT=37 not to be confused with #NTP_PORT=123 
  #TIME_PORT_BUFFER_SIZE=4
  #NTP_Unix_TimeStamp_Differential=$83AA7E80 ;thanks to Harkon
  #SecondsPerMinute=$3C 
;- Timezone Info based on your own localised settings
CompilerSelect #PB_Compiler_OS 
  CompilerCase  #PB_OS_Windows
    Select GetTimeZoneInformation_(Tz.Time_zone_information)  
      Case #TIME_ZONE_ID_INVALID 
        Debug "TIME_ZONE_ID_INVALID"
        End 
      Case #TIME_ZONE_ID_UNKNOWN 
        Debug "TIME_ZONE_ID_UNKNOWN" 
        End 
      Case #TIME_ZONE_ID_STANDARD 
        Debug "TIME_ZONE_ID_STANDARD" 
        OffSet=(Tz\Bias+Tz\StandardBias)  
      Case #TIME_ZONE_ID_DAYLIGHT 
        Debug "TIME_ZONE_ID_DAYLIGHT" 
        OffSet=(Tz\Bias+Tz\DaylightBias)
    EndSelect 
  CompilerDefault 
    Offset=-660 ; My current offset here in the land of Oz, modify to suit yourselves
CompilerEndSelect 
   Timeout=5000   ;milliseconds for network response timeout 
   ;ServerName$="192.168.1.4"
   ;ServerName$="nist1-ny.ustiming.org"
   ;ServerName$="64.90.182.55"  ;nist1-ny.ustiming.org bypassing DNS lookup
   ;ServerName$="nist1-dc.ustiming.org"
   ServerName$="206.246.118.250" ;nist1-dc.ustiming.org bypassing DNS lookup

Procedure.l Endian(val.l) ; courtesy of Rescator 
!MOV Eax,dword[p.v_val]    ;see http://www.purebasic.fr/english/viewtopic.php?f=12&t=14524
!BSWAP Eax
ProcedureReturn
EndProcedure

Procedure DecodeNTPTimestamp(NTP_Timestamp.l,OffsetMinutes.l) ;decode rx'd data         
    TimeOffsetSeconds=#SecondsPerMinute*OffsetMinutes
  ProcedureReturn Endian(NTP_Timestamp)-#NTP_Unix_TimeStamp_Differential-TimeOffsetSeconds 
EndProcedure

    If ServerName$=""
      MessageRequester("Error","You Must specify a Server Name",#MB_ICONERROR) 
      End 
    EndIf 
  *Timestamp=AllocateMemory(#TIME_PORT_BUFFER_SIZE) 
    If Not *TimeStamp 
      MessageRequester("Error","Memory allocation failed",#MB_ICONERROR) 
      End 
    EndIf 
  IsNetwork=InitNetwork()
    If Not IsNetwork
      MessageRequester("Network","Initialisation fail",#MB_ICONERROR)
      End
    EndIf
    If IsNetwork 
      ClientID=OpenNetworkConnection(ServerName$,#TIME_PORT,#PB_Network_UDP)
        If Not ClientID
          MessageRequester("Network","Connection to: [ "+ServerName$+" ] failed",#MB_ICONERROR)
          End
            Else
          SendNetworkData(ClientID,*Timestamp,#TIME_PORT_BUFFER_SIZE) ;tx'd data can be empty
          TimerA=ElapsedMilliseconds()
            Repeat
              TimerB=ElapsedMilliseconds()
                If NetworkClientEvent(ClientID)
                  DtRxd=1
                    If ReceiveNetworkData(ClientID,*Timestamp,#TIME_PORT_BUFFER_SIZE) 
                        Rx=PeekL(*Timestamp)  
                      Debug FormatDate("%hh:%ii:%ss - %dd/%mm/%yyyy",DecodeNTPTimeStamp(Rx,OffSet)) 
                    EndIf
                EndIf 
              Delay(1)
            Until TimerB>TimerA+Timeout Or DtRxd
          If Not DtRxd
            MessageRequester("Timeout", "No response from server - ["+ServerName$+" ]",#MB_ICONEXCLAMATION)
          EndIf           
        EndIf
    EndIf 
    If ClientID
      CloseNetworkConnection(ClientID)
    EndIf
    If *Timestamp
      FreeMemory(*Timestamp)
    EndIf 
End 
When the mood takes me, I will knock up a quick server to work with this code I think as it will be quite simple & also useful for me in some of my projects. :mrgreen:
rsts
Addict
Addict
Posts: 2736
Joined: Wed Aug 24, 2005 8:39 am
Location: Southwest OH - USA

Re: Network Time Protocol NTP SNTP

Post by rsts »

Very nice.

Thanks for sharing it with us :)

cheers
Post Reply