-- -- -- package GNAT.Sockets.NTP Copyright (c) Dmitry A. Kazakov -- -- Implementation Luebeck -- -- Autumn, 2017 -- -- -- -- Last revision : 23:22 29 Sep 2017 -- -- -- -- This library is free software; you can redistribute it and/or -- -- modify it under the terms of the GNU General Public License as -- -- published by the Free Software Foundation; either version 2 of -- -- the License, or (at your option) any later version. This library -- -- is distributed in the hope that it will be useful, but WITHOUT -- -- ANY WARRANTY; without even the implied warranty of -- -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -- -- General Public License for more details. You should have -- -- received a copy of the GNU General Public License along with -- -- this library; if not, write to the Free Software Foundation, -- -- Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -- -- -- -- As a special exception, if other files instantiate generics from -- -- this unit, or you link this unit with other files to produce an -- -- executable, this unit does not by itself cause the resulting -- -- executable to be covered by the GNU General Public License. This -- -- exception does not however invalidate any other reasons why the -- -- executable file might be covered by the GNU Public License. -- --____________________________________________________________________-- with Ada.Calendar.Formatting; use Ada.Calendar.Formatting; with Ada.Exceptions; use Ada.Exceptions; with Ada.IO_Exceptions; use Ada.IO_Exceptions; with Ada.Streams; use Ada.Streams; with Interfaces; use Interfaces; package body GNAT.Sockets.NTP is NTP_Packet_Size : constant := 48; -- -- RFC 5905: Official NTP era begins at 1 Jan 1900. We cannot have it -- in Ada.Calendar.Time, so taking a later time. Note Time_Zone = 0 -- in order to have it UTC -- Era : constant Time := Time_Of (1999, 12, 31, Time_Zone => 0); -- -- RFC 5905: seconds since 1 Jan 1900 to 31 Dec 1999 -- Era_Offset : constant := 3_155_587_200; function To_Addr (Host : String) return Inet_Addr_Type is begin for Index in Host'Range loop case Host (Index) is when '.' | '0'..'9' => null; when others => return Addresses (Get_Host_By_Name (Host), 1); end case; end loop; return Inet_Addr (Host); end To_Addr; function Get_Time ( Server : String; Timeout : Timeval_Duration := 10.0; Adjust : Boolean := True ) return Time is Socket : Socket_Type; Address : Sock_Addr_Type; Offset : Duration := 0.0; -- Round-trip time Seconds : Unsigned_32; Fraction : Unsigned_32; Last : Stream_Element_Offset; Data : Stream_Element_Array (1..NTP_Packet_Size) := ( 1 => 2#1110_0011#, -- LI, Version, Mode 2 => 0, -- Stratum, or type of clock 3 => 0, -- Polling Interval 4 => 16#EC#, -- Peer Clock Precision 13 => 49, 14 => 16#4E#, 15 => 49, 16 => 52, others => 0 ); begin Address.Addr := To_Addr (Server); Address.Port := 123; -- NTP port Create_Socket (Socket, Family_Inet, Socket_Datagram); Set_Socket_Option ( Socket, Socket_Level, (Receive_Timeout, Timeout) ); if Adjust then declare Start : constant Time := Clock; -- Exchange begin begin Send_Socket (Socket, Data, Last, Address); Receive_Socket (Socket, Data, Last, Address); Offset := (Clock - Start) / 2.0; end; else Send_Socket (Socket, Data, Last, Address); Receive_Socket (Socket, Data, Last, Address); end if; if Last /= Data'Last then Raise_Exception (Data_Error'Identity, "Mangled NTP response"); end if; Seconds := ( Unsigned_32 (Data (41)) * 2**24 + Unsigned_32 (Data (42)) * 2**16 + Unsigned_32 (Data (43)) * 2**8 + Unsigned_32 (Data (44)) - Era_OFfset ); Fraction := ( Unsigned_32 (Data (45)) * 2**24 + Unsigned_32 (Data (46)) * 2**16 + Unsigned_32 (Data (47)) * 2**8 + Unsigned_32 (Data (48)) ); return ( Era + Duration (Seconds) + Duration (Long_Float (Fraction) / 2.0**32) - Offset ); end Get_Time; end GNAT.Sockets.NTP;