1 /*
2 * Copyright 2010-2011, Ryan Leavengood. All Rights Reserved.
3 * Copyright 2004-2009, pinc Software. All Rights Reserved.
4 * Distributed under the terms of the MIT license.
5 */
6
7 #include "ntp.h"
8
9 #include <errno.h>
10 #include <netdb.h>
11 #include <string.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <sys/select.h>
15 #include <sys/socket.h>
16 #include <unistd.h>
17
18 #include <OS.h>
19
20 #include <Catalog.h>
21 #include <NetworkAddress.h>
22 #include <NetworkAddressResolver.h>
23
24
25 #undef B_TRANSLATION_CONTEXT
26 #define B_TRANSLATION_CONTEXT "Time"
27
28
29 /* This structure and its data fields are described in RFC 1305
30 * "Network Time Protocol (Version 3)" in appendix A.
31 */
32
33 struct fixed32 {
34 int16 integer;
35 uint16 fraction;
36
37 void
SetTofixed3238 SetTo(int16 integer, uint16 fraction = 0)
39 {
40 this->integer = htons(integer);
41 this->fraction = htons(fraction);
42 }
43
Integerfixed3244 int16 Integer() { return htons(integer); }
Fractionfixed3245 uint16 Fraction() { return htons(fraction); }
46 };
47
48 struct ufixed64 {
49 uint32 integer;
50 uint32 fraction;
51
52 void
SetToufixed6453 SetTo(uint32 integer, uint32 fraction = 0)
54 {
55 this->integer = htonl(integer);
56 this->fraction = htonl(fraction);
57 }
58
Integerufixed6459 uint32 Integer() { return htonl(integer); }
Fractionufixed6460 uint32 Fraction() { return htonl(fraction); }
61 };
62
63 struct ntp_data {
64 uint8 mode : 3;
65 uint8 version : 3;
66 uint8 leap_indicator : 2;
67
68 uint8 stratum;
69 int8 poll;
70 int8 precision; /* in seconds of the nearest power of two */
71
72 fixed32 root_delay;
73 fixed32 root_dispersion;
74 uint32 root_identifier;
75
76 ufixed64 reference_timestamp;
77 ufixed64 originate_timestamp;
78 ufixed64 receive_timestamp;
79 ufixed64 transmit_timestamp;
80
81 /* optional authenticator follows (96 bits) */
82 };
83
84 #define NTP_PORT 123
85 #define NTP_VERSION_3 3
86
87 enum ntp_leap_warnings {
88 LEAP_NO_WARNING = 0,
89 LEAP_LAST_MINUTE_61_SECONDS,
90 LEAP_LAST_MINUTE_59_SECONDS,
91 LEAP_CLOCK_NOT_IN_SYNC,
92 };
93
94 enum ntp_modes {
95 MODE_RESERVED = 0,
96 MODE_SYMMETRIC_ACTIVE,
97 MODE_SYMMETRIC_PASSIVE,
98 MODE_CLIENT,
99 MODE_SERVER,
100 MODE_BROADCAST,
101 MODE_NTP_CONTROL_MESSAGE,
102 };
103
104
105 const uint32 kSecondsBetween1900And1970 = 2208988800UL;
106
107
108 uint32
seconds_since_1900(void)109 seconds_since_1900(void)
110 {
111 return kSecondsBetween1900And1970 + real_time_clock();
112 }
113
114
115 status_t
ntp_update_time(const char * hostname,const char ** errorString,int32 * errorCode)116 ntp_update_time(const char* hostname, const char** errorString,
117 int32* errorCode)
118 {
119 BNetworkAddressResolver resolver(hostname, NTP_PORT);
120 BNetworkAddress address;
121 uint32 cookie = 0;
122 bool success = false;
123
124 if (resolver.InitCheck() != B_OK) {
125 *errorString = B_TRANSLATE("Could not resolve server address");
126 return B_ENTRY_NOT_FOUND;
127 }
128
129 ntp_data message;
130 memset(&message, 0, sizeof(ntp_data));
131
132 message.leap_indicator = LEAP_CLOCK_NOT_IN_SYNC;
133 message.version = NTP_VERSION_3;
134 message.mode = MODE_CLIENT;
135
136 message.stratum = 1; // primary reference
137 message.precision = -5; // 2^-5 ~ 32-64 Hz precision
138
139 message.root_delay.SetTo(1); // 1 sec
140 message.root_dispersion.SetTo(1);
141
142 message.transmit_timestamp.SetTo(seconds_since_1900());
143
144 int connection = socket(AF_INET, SOCK_DGRAM, 0);
145 if (connection < 0) {
146 *errorString = B_TRANSLATE("Could not create socket");
147 *errorCode = errno;
148 return B_ERROR;
149 }
150
151 while (resolver.GetNextAddress(&cookie, address) == B_OK) {
152 if (sendto(connection, reinterpret_cast<char*>(&message),
153 sizeof(ntp_data), 0, &address.SockAddr(),
154 address.Length()) != -1) {
155 success = true;
156 break;
157 }
158 }
159
160 if (!success) {
161 *errorString = B_TRANSLATE("Sending request failed");
162 close(connection);
163 return B_ERROR;
164 }
165
166 fd_set waitForReceived;
167 FD_ZERO(&waitForReceived);
168 FD_SET(connection, &waitForReceived);
169
170 struct timeval timeout;
171 timeout.tv_sec = 3;
172 timeout.tv_usec = 0;
173 // we'll wait 3 seconds for the answer
174
175 int status;
176 do {
177 status = select(connection + 1, &waitForReceived, NULL, NULL,
178 &timeout);
179 } while (status == -1 && errno == EINTR);
180 if (status <= 0) {
181 *errorString = B_TRANSLATE("Waiting for answer failed");
182 *errorCode = errno;
183 close(connection);
184 return B_ERROR;
185 }
186
187 message.transmit_timestamp.SetTo(0);
188
189 socklen_t addressSize = address.Length();
190 if (recvfrom(connection, reinterpret_cast<char*>(&message), sizeof(ntp_data), 0,
191 &address.SockAddr(), &addressSize) < (ssize_t)sizeof(ntp_data)) {
192 *errorString = B_TRANSLATE("Message receiving failed");
193 *errorCode = errno;
194 close(connection);
195 return B_ERROR;
196 }
197
198 close(connection);
199
200 if (message.transmit_timestamp.Integer() == 0) {
201 *errorString = B_TRANSLATE("Received invalid time");
202 return B_BAD_VALUE;
203 }
204
205 time_t now = message.transmit_timestamp.Integer() - kSecondsBetween1900And1970;
206 set_real_time_clock(now);
207 return B_OK;
208 }
209