IdSNTP.pas 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. {
  2. $Project$
  3. $Workfile$
  4. $Revision$
  5. $DateUTC$
  6. $Id$
  7. This file is part of the Indy (Internet Direct) project, and is offered
  8. under the dual-licensing agreement described on the Indy website.
  9. (http://www.indyproject.org/)
  10. Copyright:
  11. (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
  12. }
  13. {
  14. $Log$
  15. }
  16. {
  17. Rev 1.6 2/8/2005 6:28:02 AM JPMugaas
  18. Should now work properly. I omitted a feild when outputting bytes from the
  19. packet object. OOPS!!!
  20. Rev 1.5 6/1/2004 9:09:00 PM DSiders
  21. Correct calculation for RoundTripDelay as per RFC 2030 errata.
  22. Rev 1.4 2/9/2004 11:26:46 AM JPMugaas
  23. Fixed some bugs reading the time. SHould work.
  24. Rev 1.3 2/8/2004 4:15:54 PM JPMugaas
  25. SNTP ported to DotNET.
  26. Rev 1.2 2004.02.03 5:44:24 PM czhower
  27. Name changes
  28. Rev 1.1 1/21/2004 4:03:42 PM JPMugaas
  29. InitComponent
  30. Rev 1.0 11/13/2002 08:01:12 AM JPMugaas
  31. 2002 Jan 21 Don
  32. Added suggestions from R. Brian Lindahl.
  33. Added CheckStratum property.
  34. Modified Disregard to use CheckStratum property.
  35. Modified GetAdjustmentTime to ignore optional NTP authentication in response.
  36. 2002 Jan 3 Don
  37. Corrected errors introduced in previous revision.
  38. Added TIdSNTP.Create to assign port number for the SNTP protocol.
  39. 2002 Jan 3 Don
  40. Corrected error in TIdSNTP.GetDateTime as per Bug Report
  41. http://sourceforge.net/tracker/?func=detail&atid=431491&aid=498843&group_id=41862
  42. 2001 Sep 4 Don
  43. Corrected error in Flip() as reported on BCB newsgroup
  44. 2000 Apr 21 Kudzu
  45. Updated to match UDP core changes
  46. 2000 Mar 28 Hadi
  47. Continued conversion to Indy
  48. 2000 Mar 24 Kudzu
  49. Converted to Indy
  50. 2000 Jan 13 MTL
  51. Moved to new Palette Tab scheme (Winshoes Clients)
  52. 1999
  53. }
  54. unit IdSNTP;
  55. {*
  56. Winshoe SNTP (Simple Network Time Protocol)
  57. Behaves more or less according to RFC-2030
  58. R. Brian Lindahl - Original Author
  59. *}
  60. interface
  61. {$i IdCompilerDefines.inc}
  62. uses
  63. Classes,
  64. IdGlobal,
  65. IdUDPClient;
  66. const
  67. NTPMaxInt = 4294967297.0;
  68. type
  69. // NTP Datagram format
  70. TNTPGram = packed record
  71. Head1 : byte;
  72. Head2: byte;
  73. Head3: byte;
  74. Head4: byte;
  75. RootDelay: UInt32;
  76. RootDispersion: UInt32;
  77. RefID: UInt32;
  78. Ref1: UInt32;
  79. Ref2: UInt32;
  80. Org1: UInt32;
  81. Org2: UInt32;
  82. Rcv1: UInt32;
  83. Rcv2: UInt32;
  84. Xmit1: UInt32;
  85. Xmit2: UInt32;
  86. end;
  87. TIdSNTP = class(TIdUDPClient)
  88. protected
  89. FDestinationTimestamp: TDateTime; // Destination Timestamp T4 time reply received by client
  90. FLocalClockOffset: TDateTime; // = ((T2 - T1) + (T3 - T4)) / 2
  91. FOriginateTimestamp: TDateTime; // Originate Timestamp T1 time request sent by client
  92. FReceiveTimestamp: TDateTime; // Receive Timestamp T2 time request received by server
  93. FRoundTripDelay: TDateTime; // = (T4 - T1) - (T2 - T3)
  94. FTransmitTimestamp: TDateTime; // Transmit Timestamp T3 time reply sent by server
  95. FCheckStratum: Boolean;
  96. //
  97. procedure DateTimeToNTP(ADateTime: TDateTime; var Second, Fraction: UInt32);
  98. function NTPToDateTime(Second, Fraction: UInt32): TDateTime;
  99. function Disregard(const ANTPMessage: TNTPGram): Boolean;
  100. function GetAdjustmentTime: TDateTime;
  101. function GetDateTime: TDateTime;
  102. procedure InitComponent; override;
  103. public
  104. function SyncTime: Boolean; // get datetime and adjust if needed
  105. //
  106. property AdjustmentTime: TDateTime read GetAdjustmentTime;
  107. property DateTime: TDateTime read GetDateTime;
  108. property RoundTripDelay: TDateTime read FRoundTripDelay;
  109. property CheckStratum: Boolean read FCheckStratum write FCheckStratum default True;
  110. end;
  111. implementation
  112. uses
  113. {$IFDEF USE_VCL_POSIX}
  114. Posix.SysTime,
  115. Posix.Time,
  116. {$ENDIF}
  117. IdGlobalProtocols,
  118. IdAssignedNumbers,
  119. IdStack,
  120. SysUtils;
  121. procedure TIdSNTP.DateTimeToNTP(ADateTime: TDateTime; var Second, Fraction: UInt32);
  122. var
  123. Value1, Value2: Double;
  124. begin
  125. Value1 := (LocalTimeToUTCTime(ADateTime) - 2) * 86400;
  126. Value2 := Value1;
  127. if Value2 > NTPMaxInt then
  128. begin
  129. Value2 := Value2 - NTPMaxInt;
  130. end;
  131. Second := UInt32(Trunc(Value2));
  132. Value2 := ((Frac(Value1) * 1000) / 1000) * NTPMaxInt;
  133. if Value2 > NTPMaxInt then
  134. begin
  135. Value2 := Value2 - NTPMaxInt;
  136. end;
  137. Fraction := Trunc(Value2);
  138. end;
  139. function TIdSNTP.NTPToDateTime(Second, Fraction: UInt32): TDateTime;
  140. var
  141. Value1: Double;
  142. Value2: Double;
  143. begin
  144. Value1 := Second;
  145. if Value1 < 0 then
  146. begin
  147. Value1 := NTPMaxInt + Value1 - 1;
  148. end;
  149. Value2 := Fraction;
  150. if Value2 < 0 then
  151. begin
  152. Value2 := NTPMaxInt + Value2 - 1;
  153. end;
  154. // Value2 := Value2 / NTPMaxInt;
  155. // Value2 := Trunc(Value2 * 1000) / 1000;
  156. Value2 := Trunc(Value2 / NTPMaxInt * 1000) / 1000;
  157. Result := UTCTimeToLocalTime(((Value1 + Value2) / 86400) + 2);
  158. end ;
  159. { TIdSNTP }
  160. procedure TIdSNTP.InitComponent;
  161. begin
  162. inherited;
  163. FPort := IdPORT_SNTP;
  164. FCheckStratum := True;
  165. end;
  166. function TIdSNTP.Disregard(const ANTPMessage: TNTPGram): Boolean;
  167. var
  168. LvStratum: Byte;
  169. LvLeapIndicator: Byte;
  170. begin
  171. LvLeapIndicator := (ANTPMessage.Head1 and $C0) shr 6;
  172. LvStratum := ANTPMessage.Head2;
  173. Result := (LvLeapIndicator = 3) or
  174. (((Int(FTransmitTimestamp)) = 0.0) and (Frac(FTransmitTimestamp) = 0.0));
  175. // DS ignore NTPGram when stratum is used, and value is reserved or unspecified
  176. if FCheckStratum and ((LvStratum > 15) or (LvStratum = 0)) then
  177. begin
  178. Result := True;
  179. end;
  180. end;
  181. function TIdSNTP.GetAdjustmentTime: TDateTime;
  182. begin
  183. Result := FLocalClockOffset;
  184. end;
  185. function TIdSNTP.GetDateTime: TDateTime;
  186. var
  187. LNTPDataGram: TNTPGram;
  188. LBuffer : TIdBytes;
  189. LBytesRecvd: Integer;
  190. begin
  191. // DS default result is an empty TDateTime value
  192. Result := 0.0;
  193. SetLength(LBuffer, SizeOf(TNTPGram));
  194. FillBytes(LBuffer, SizeOf(TNTPGram), $00);
  195. LBuffer[0] := $1B;
  196. DateTimeToNTP(Now, LNTPDataGram.Xmit1, LNTPDataGram.Xmit2);
  197. CopyTIdUInt32(GStack.HostToNetwork(LNTPDataGram.Xmit1), LBuffer, 40);
  198. CopyTIdUInt32(GStack.HostToNetwork(LNTPDataGram.Xmit2), LBuffer, 44);
  199. SendBuffer(LBuffer);
  200. LBytesRecvd := ReceiveBuffer(LBuffer);
  201. // DS response may contain optional NTP authentication scheme info not in NTPGram
  202. if LBytesRecvd >= SizeOf(TNTPGram) then
  203. begin
  204. FDestinationTimeStamp := Now;
  205. // DS copy result data back into NTPDataGram
  206. // DS ignore optional NTP authentication scheme info in response
  207. LNTPDataGram.Head1 := LBuffer[0];
  208. LNTPDataGram.Head2 := LBuffer[1];
  209. LNTPDataGram.Head3 := LBuffer[2];
  210. LNTPDataGram.Head4 := LBuffer[3];
  211. LNTPDataGram.RootDelay := GStack.NetworkToHost(BytesToUInt32(LBuffer, 4));
  212. LNTPDataGram.RootDispersion := GStack.NetworkToHost(BytesToUInt32(LBuffer, 8));
  213. LNTPDataGram.RefID := GStack.NetworkToHost(BytesToUInt32(LBuffer, 12));
  214. LNTPDataGram.Ref1 := GStack.NetworkToHost(BytesToUInt32(LBuffer, 16));
  215. LNTPDataGram.Ref2 := GStack.NetworkToHost(BytesToUInt32(LBuffer, 20));
  216. LNTPDataGram.Org1 := GStack.NetworkToHost(BytesToUInt32(LBuffer, 24));
  217. LNTPDataGram.Org2 := GStack.NetworkToHost(BytesToUInt32(LBuffer, 28));
  218. LNTPDataGram.Rcv1 := GStack.NetworkToHost(BytesToUInt32(LBuffer, 32));
  219. LNTPDataGram.Rcv2 := GStack.NetworkToHost(BytesToUInt32(LBuffer, 36));
  220. LNTPDataGram.Xmit1 := GStack.NetworkToHost(BytesToUInt32(LBuffer, 40));
  221. LNTPDataGram.Xmit2 := GStack.NetworkToHost(BytesToUInt32(LBuffer, 44));
  222. FOriginateTimeStamp := NTPToDateTime(LNTPDataGram.Org1, LNTPDataGram.Org2);
  223. FReceiveTimestamp := NTPToDateTime(LNTPDataGram.Rcv1, LNTPDataGram.Rcv2);
  224. FTransmitTimestamp := NTPToDateTime(LNTPDataGram.Xmit1, LNTPDataGram.Xmit2);
  225. // corrected as per RFC 2030 errata
  226. FRoundTripDelay := (FDestinationTimestamp - FOriginateTimestamp) -
  227. (FTransmitTimestamp - FReceiveTimestamp);
  228. FLocalClockOffset := ((FReceiveTimestamp - FOriginateTimestamp) +
  229. (FTransmitTimestamp - FDestinationTimestamp)) / 2;
  230. // DS update date/time when NTP datagram is not ignored
  231. if not Disregard(LNTPDataGram) then begin
  232. Result := FTransmitTimestamp;
  233. end;
  234. end;
  235. end;
  236. function TIdSNTP.SyncTime: Boolean;
  237. begin
  238. Result := DateTime <> 0.0;
  239. if Result then begin
  240. Result := IndySetLocalTime(FOriginateTimestamp + FLocalClockOffset + FRoundTripDelay);
  241. end;
  242. end;
  243. end.