xref: /haiku/src/preferences/time/ntp.cpp (revision 0376026d3127c64497290d0ed78666133ff27b23)
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