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