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