1323ba9b7SRyan Leavengood /*
2323ba9b7SRyan Leavengood * Copyright 2010-2011, Ryan Leavengood. All Rights Reserved.
3323ba9b7SRyan Leavengood * Copyright 2004-2009, pinc Software. All Rights Reserved.
4323ba9b7SRyan Leavengood * Distributed under the terms of the MIT license.
5323ba9b7SRyan Leavengood */
6323ba9b7SRyan Leavengood
7323ba9b7SRyan Leavengood #include "ntp.h"
8323ba9b7SRyan Leavengood
9323ba9b7SRyan Leavengood #include <errno.h>
10323ba9b7SRyan Leavengood #include <netdb.h>
11323ba9b7SRyan Leavengood #include <string.h>
12323ba9b7SRyan Leavengood #include <stdio.h>
13323ba9b7SRyan Leavengood #include <stdlib.h>
14323ba9b7SRyan Leavengood #include <sys/select.h>
15323ba9b7SRyan Leavengood #include <sys/socket.h>
16323ba9b7SRyan Leavengood #include <unistd.h>
17323ba9b7SRyan Leavengood
18323ba9b7SRyan Leavengood #include <OS.h>
19323ba9b7SRyan Leavengood
208c333297SRyan Leavengood #include <Catalog.h>
21bb382cf8SA-star-ayush #include <NetworkAddress.h>
22bb382cf8SA-star-ayush #include <NetworkAddressResolver.h>
238c333297SRyan Leavengood
248c333297SRyan Leavengood
25546208a5SOliver Tappe #undef B_TRANSLATION_CONTEXT
26546208a5SOliver Tappe #define B_TRANSLATION_CONTEXT "Time"
278c333297SRyan Leavengood
28323ba9b7SRyan Leavengood
29323ba9b7SRyan Leavengood /* This structure and its data fields are described in RFC 1305
30323ba9b7SRyan Leavengood * "Network Time Protocol (Version 3)" in appendix A.
31323ba9b7SRyan Leavengood */
32323ba9b7SRyan Leavengood
33323ba9b7SRyan Leavengood struct fixed32 {
34323ba9b7SRyan Leavengood int16 integer;
35323ba9b7SRyan Leavengood uint16 fraction;
36323ba9b7SRyan Leavengood
37323ba9b7SRyan Leavengood void
SetTofixed3238323ba9b7SRyan Leavengood SetTo(int16 integer, uint16 fraction = 0)
39323ba9b7SRyan Leavengood {
40323ba9b7SRyan Leavengood this->integer = htons(integer);
41323ba9b7SRyan Leavengood this->fraction = htons(fraction);
42323ba9b7SRyan Leavengood }
43323ba9b7SRyan Leavengood
Integerfixed3244323ba9b7SRyan Leavengood int16 Integer() { return htons(integer); }
Fractionfixed3245323ba9b7SRyan Leavengood uint16 Fraction() { return htons(fraction); }
46323ba9b7SRyan Leavengood };
47323ba9b7SRyan Leavengood
48323ba9b7SRyan Leavengood struct ufixed64 {
49323ba9b7SRyan Leavengood uint32 integer;
50323ba9b7SRyan Leavengood uint32 fraction;
51323ba9b7SRyan Leavengood
52323ba9b7SRyan Leavengood void
SetToufixed6453323ba9b7SRyan Leavengood SetTo(uint32 integer, uint32 fraction = 0)
54323ba9b7SRyan Leavengood {
55323ba9b7SRyan Leavengood this->integer = htonl(integer);
56323ba9b7SRyan Leavengood this->fraction = htonl(fraction);
57323ba9b7SRyan Leavengood }
58323ba9b7SRyan Leavengood
Integerufixed6459323ba9b7SRyan Leavengood uint32 Integer() { return htonl(integer); }
Fractionufixed6460323ba9b7SRyan Leavengood uint32 Fraction() { return htonl(fraction); }
61323ba9b7SRyan Leavengood };
62323ba9b7SRyan Leavengood
63323ba9b7SRyan Leavengood struct ntp_data {
64323ba9b7SRyan Leavengood uint8 mode : 3;
65323ba9b7SRyan Leavengood uint8 version : 3;
66323ba9b7SRyan Leavengood uint8 leap_indicator : 2;
67323ba9b7SRyan Leavengood
68323ba9b7SRyan Leavengood uint8 stratum;
69323ba9b7SRyan Leavengood int8 poll;
70323ba9b7SRyan Leavengood int8 precision; /* in seconds of the nearest power of two */
71323ba9b7SRyan Leavengood
72323ba9b7SRyan Leavengood fixed32 root_delay;
73323ba9b7SRyan Leavengood fixed32 root_dispersion;
74323ba9b7SRyan Leavengood uint32 root_identifier;
75323ba9b7SRyan Leavengood
76323ba9b7SRyan Leavengood ufixed64 reference_timestamp;
77323ba9b7SRyan Leavengood ufixed64 originate_timestamp;
78323ba9b7SRyan Leavengood ufixed64 receive_timestamp;
79323ba9b7SRyan Leavengood ufixed64 transmit_timestamp;
80323ba9b7SRyan Leavengood
81323ba9b7SRyan Leavengood /* optional authenticator follows (96 bits) */
82323ba9b7SRyan Leavengood };
83323ba9b7SRyan Leavengood
84323ba9b7SRyan Leavengood #define NTP_PORT 123
85323ba9b7SRyan Leavengood #define NTP_VERSION_3 3
86323ba9b7SRyan Leavengood
87323ba9b7SRyan Leavengood enum ntp_leap_warnings {
88323ba9b7SRyan Leavengood LEAP_NO_WARNING = 0,
89323ba9b7SRyan Leavengood LEAP_LAST_MINUTE_61_SECONDS,
90323ba9b7SRyan Leavengood LEAP_LAST_MINUTE_59_SECONDS,
91323ba9b7SRyan Leavengood LEAP_CLOCK_NOT_IN_SYNC,
92323ba9b7SRyan Leavengood };
93323ba9b7SRyan Leavengood
94323ba9b7SRyan Leavengood enum ntp_modes {
95323ba9b7SRyan Leavengood MODE_RESERVED = 0,
96323ba9b7SRyan Leavengood MODE_SYMMETRIC_ACTIVE,
97323ba9b7SRyan Leavengood MODE_SYMMETRIC_PASSIVE,
98323ba9b7SRyan Leavengood MODE_CLIENT,
99323ba9b7SRyan Leavengood MODE_SERVER,
100323ba9b7SRyan Leavengood MODE_BROADCAST,
101323ba9b7SRyan Leavengood MODE_NTP_CONTROL_MESSAGE,
102323ba9b7SRyan Leavengood };
103323ba9b7SRyan Leavengood
104323ba9b7SRyan Leavengood
105323ba9b7SRyan Leavengood const uint32 kSecondsBetween1900And1970 = 2208988800UL;
106323ba9b7SRyan Leavengood
107323ba9b7SRyan Leavengood
108323ba9b7SRyan Leavengood uint32
seconds_since_1900(void)109323ba9b7SRyan Leavengood seconds_since_1900(void)
110323ba9b7SRyan Leavengood {
111323ba9b7SRyan Leavengood return kSecondsBetween1900And1970 + real_time_clock();
112323ba9b7SRyan Leavengood }
113323ba9b7SRyan Leavengood
114323ba9b7SRyan Leavengood
115323ba9b7SRyan Leavengood status_t
ntp_update_time(const char * hostname,const char ** errorString,int32 * errorCode)116323ba9b7SRyan Leavengood ntp_update_time(const char* hostname, const char** errorString,
117323ba9b7SRyan Leavengood int32* errorCode)
118323ba9b7SRyan Leavengood {
119bb382cf8SA-star-ayush BNetworkAddressResolver resolver(hostname, NTP_PORT);
120bb382cf8SA-star-ayush BNetworkAddress address;
121bb382cf8SA-star-ayush uint32 cookie = 0;
122bb382cf8SA-star-ayush bool success = false;
123323ba9b7SRyan Leavengood
124bb382cf8SA-star-ayush if (resolver.InitCheck() != B_OK) {
125bb382cf8SA-star-ayush *errorString = B_TRANSLATE("Could not resolve server address");
126323ba9b7SRyan Leavengood return B_ENTRY_NOT_FOUND;
127323ba9b7SRyan Leavengood }
128323ba9b7SRyan Leavengood
129323ba9b7SRyan Leavengood ntp_data message;
130323ba9b7SRyan Leavengood memset(&message, 0, sizeof(ntp_data));
131323ba9b7SRyan Leavengood
132323ba9b7SRyan Leavengood message.leap_indicator = LEAP_CLOCK_NOT_IN_SYNC;
133323ba9b7SRyan Leavengood message.version = NTP_VERSION_3;
134323ba9b7SRyan Leavengood message.mode = MODE_CLIENT;
135323ba9b7SRyan Leavengood
136323ba9b7SRyan Leavengood message.stratum = 1; // primary reference
137323ba9b7SRyan Leavengood message.precision = -5; // 2^-5 ~ 32-64 Hz precision
138323ba9b7SRyan Leavengood
139323ba9b7SRyan Leavengood message.root_delay.SetTo(1); // 1 sec
140323ba9b7SRyan Leavengood message.root_dispersion.SetTo(1);
141323ba9b7SRyan Leavengood
142323ba9b7SRyan Leavengood message.transmit_timestamp.SetTo(seconds_since_1900());
143323ba9b7SRyan Leavengood
144323ba9b7SRyan Leavengood int connection = socket(AF_INET, SOCK_DGRAM, 0);
145323ba9b7SRyan Leavengood if (connection < 0) {
1468c333297SRyan Leavengood *errorString = B_TRANSLATE("Could not create socket");
147323ba9b7SRyan Leavengood *errorCode = errno;
148323ba9b7SRyan Leavengood return B_ERROR;
149323ba9b7SRyan Leavengood }
150323ba9b7SRyan Leavengood
151bb382cf8SA-star-ayush while (resolver.GetNextAddress(&cookie, address) == B_OK) {
152bb382cf8SA-star-ayush if (sendto(connection, reinterpret_cast<char*>(&message),
153bb382cf8SA-star-ayush sizeof(ntp_data), 0, &address.SockAddr(),
154bb382cf8SA-star-ayush address.Length()) != -1) {
155bb382cf8SA-star-ayush success = true;
156bb382cf8SA-star-ayush break;
157bb382cf8SA-star-ayush }
158bb382cf8SA-star-ayush }
159323ba9b7SRyan Leavengood
160bb382cf8SA-star-ayush if (!success) {
1618c333297SRyan Leavengood *errorString = B_TRANSLATE("Sending request failed");
162163cd4bfSPhilippe Saint-Pierre close(connection);
163323ba9b7SRyan Leavengood return B_ERROR;
164323ba9b7SRyan Leavengood }
165323ba9b7SRyan Leavengood
166323ba9b7SRyan Leavengood fd_set waitForReceived;
167323ba9b7SRyan Leavengood FD_ZERO(&waitForReceived);
168323ba9b7SRyan Leavengood FD_SET(connection, &waitForReceived);
169323ba9b7SRyan Leavengood
170323ba9b7SRyan Leavengood struct timeval timeout;
171323ba9b7SRyan Leavengood timeout.tv_sec = 3;
172323ba9b7SRyan Leavengood timeout.tv_usec = 0;
173323ba9b7SRyan Leavengood // we'll wait 3 seconds for the answer
174323ba9b7SRyan Leavengood
175*0376026dSJérôme Duval int status;
176*0376026dSJérôme Duval do {
177*0376026dSJérôme Duval status = select(connection + 1, &waitForReceived, NULL, NULL,
178*0376026dSJérôme Duval &timeout);
179*0376026dSJérôme Duval } while (status == -1 && errno == EINTR);
180*0376026dSJérôme Duval if (status <= 0) {
1818c333297SRyan Leavengood *errorString = B_TRANSLATE("Waiting for answer failed");
182323ba9b7SRyan Leavengood *errorCode = errno;
183163cd4bfSPhilippe Saint-Pierre close(connection);
184323ba9b7SRyan Leavengood return B_ERROR;
185323ba9b7SRyan Leavengood }
186323ba9b7SRyan Leavengood
187323ba9b7SRyan Leavengood message.transmit_timestamp.SetTo(0);
188323ba9b7SRyan Leavengood
189bb382cf8SA-star-ayush socklen_t addressSize = address.Length();
190bb382cf8SA-star-ayush if (recvfrom(connection, reinterpret_cast<char*>(&message), sizeof(ntp_data), 0,
191bb382cf8SA-star-ayush &address.SockAddr(), &addressSize) < (ssize_t)sizeof(ntp_data)) {
1928c333297SRyan Leavengood *errorString = B_TRANSLATE("Message receiving failed");
193323ba9b7SRyan Leavengood *errorCode = errno;
194323ba9b7SRyan Leavengood close(connection);
195323ba9b7SRyan Leavengood return B_ERROR;
196323ba9b7SRyan Leavengood }
197323ba9b7SRyan Leavengood
198323ba9b7SRyan Leavengood close(connection);
199323ba9b7SRyan Leavengood
200323ba9b7SRyan Leavengood if (message.transmit_timestamp.Integer() == 0) {
2018c333297SRyan Leavengood *errorString = B_TRANSLATE("Received invalid time");
202323ba9b7SRyan Leavengood return B_BAD_VALUE;
203323ba9b7SRyan Leavengood }
204323ba9b7SRyan Leavengood
205323ba9b7SRyan Leavengood time_t now = message.transmit_timestamp.Integer() - kSecondsBetween1900And1970;
206323ba9b7SRyan Leavengood set_real_time_clock(now);
207323ba9b7SRyan Leavengood return B_OK;
208323ba9b7SRyan Leavengood }
209