xref: /haiku/src/preferences/time/ntp.cpp (revision 8c333297c193c3b21191888795bd0525489a7a5e)
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 
20*8c333297SRyan Leavengood #include <Catalog.h>
21*8c333297SRyan Leavengood 
22*8c333297SRyan Leavengood 
23*8c333297SRyan Leavengood #undef B_TRANSLATE_CONTEXT
24*8c333297SRyan Leavengood #define B_TRANSLATE_CONTEXT "Time"
25*8c333297SRyan Leavengood 
26323ba9b7SRyan Leavengood 
27323ba9b7SRyan Leavengood /* This structure and its data fields are described in RFC 1305
28323ba9b7SRyan Leavengood  * "Network Time Protocol (Version 3)" in appendix A.
29323ba9b7SRyan Leavengood  */
30323ba9b7SRyan Leavengood 
31323ba9b7SRyan Leavengood struct fixed32 {
32323ba9b7SRyan Leavengood 	int16	integer;
33323ba9b7SRyan Leavengood 	uint16	fraction;
34323ba9b7SRyan Leavengood 
35323ba9b7SRyan Leavengood 	void
36323ba9b7SRyan Leavengood 	SetTo(int16 integer, uint16 fraction = 0)
37323ba9b7SRyan Leavengood 	{
38323ba9b7SRyan Leavengood 		this->integer = htons(integer);
39323ba9b7SRyan Leavengood 		this->fraction = htons(fraction);
40323ba9b7SRyan Leavengood 	}
41323ba9b7SRyan Leavengood 
42323ba9b7SRyan Leavengood 	int16 Integer() { return htons(integer); }
43323ba9b7SRyan Leavengood 	uint16 Fraction() { return htons(fraction); }
44323ba9b7SRyan Leavengood };
45323ba9b7SRyan Leavengood 
46323ba9b7SRyan Leavengood struct ufixed64 {
47323ba9b7SRyan Leavengood 	uint32	integer;
48323ba9b7SRyan Leavengood 	uint32	fraction;
49323ba9b7SRyan Leavengood 
50323ba9b7SRyan Leavengood 	void
51323ba9b7SRyan Leavengood 	SetTo(uint32 integer, uint32 fraction = 0)
52323ba9b7SRyan Leavengood 	{
53323ba9b7SRyan Leavengood 		this->integer = htonl(integer);
54323ba9b7SRyan Leavengood 		this->fraction = htonl(fraction);
55323ba9b7SRyan Leavengood 	}
56323ba9b7SRyan Leavengood 
57323ba9b7SRyan Leavengood 	uint32 Integer() { return htonl(integer); }
58323ba9b7SRyan Leavengood 	uint32 Fraction() { return htonl(fraction); }
59323ba9b7SRyan Leavengood };
60323ba9b7SRyan Leavengood 
61323ba9b7SRyan Leavengood struct ntp_data {
62323ba9b7SRyan Leavengood 	uint8		mode : 3;
63323ba9b7SRyan Leavengood 	uint8		version : 3;
64323ba9b7SRyan Leavengood 	uint8		leap_indicator : 2;
65323ba9b7SRyan Leavengood 
66323ba9b7SRyan Leavengood 	uint8		stratum;
67323ba9b7SRyan Leavengood 	int8		poll;
68323ba9b7SRyan Leavengood 	int8		precision;	/* in seconds of the nearest power of two */
69323ba9b7SRyan Leavengood 
70323ba9b7SRyan Leavengood 	fixed32		root_delay;
71323ba9b7SRyan Leavengood 	fixed32		root_dispersion;
72323ba9b7SRyan Leavengood 	uint32		root_identifier;
73323ba9b7SRyan Leavengood 
74323ba9b7SRyan Leavengood 	ufixed64	reference_timestamp;
75323ba9b7SRyan Leavengood 	ufixed64	originate_timestamp;
76323ba9b7SRyan Leavengood 	ufixed64	receive_timestamp;
77323ba9b7SRyan Leavengood 	ufixed64	transmit_timestamp;
78323ba9b7SRyan Leavengood 
79323ba9b7SRyan Leavengood 	/* optional authenticator follows (96 bits) */
80323ba9b7SRyan Leavengood };
81323ba9b7SRyan Leavengood 
82323ba9b7SRyan Leavengood #define NTP_PORT		123
83323ba9b7SRyan Leavengood #define NTP_VERSION_3	3
84323ba9b7SRyan Leavengood 
85323ba9b7SRyan Leavengood enum ntp_leap_warnings {
86323ba9b7SRyan Leavengood 	LEAP_NO_WARNING = 0,
87323ba9b7SRyan Leavengood 	LEAP_LAST_MINUTE_61_SECONDS,
88323ba9b7SRyan Leavengood 	LEAP_LAST_MINUTE_59_SECONDS,
89323ba9b7SRyan Leavengood 	LEAP_CLOCK_NOT_IN_SYNC,
90323ba9b7SRyan Leavengood };
91323ba9b7SRyan Leavengood 
92323ba9b7SRyan Leavengood enum ntp_modes {
93323ba9b7SRyan Leavengood 	MODE_RESERVED = 0,
94323ba9b7SRyan Leavengood 	MODE_SYMMETRIC_ACTIVE,
95323ba9b7SRyan Leavengood 	MODE_SYMMETRIC_PASSIVE,
96323ba9b7SRyan Leavengood 	MODE_CLIENT,
97323ba9b7SRyan Leavengood 	MODE_SERVER,
98323ba9b7SRyan Leavengood 	MODE_BROADCAST,
99323ba9b7SRyan Leavengood 	MODE_NTP_CONTROL_MESSAGE,
100323ba9b7SRyan Leavengood };
101323ba9b7SRyan Leavengood 
102323ba9b7SRyan Leavengood 
103323ba9b7SRyan Leavengood const uint32 kSecondsBetween1900And1970 = 2208988800UL;
104323ba9b7SRyan Leavengood 
105323ba9b7SRyan Leavengood 
106323ba9b7SRyan Leavengood uint32
107323ba9b7SRyan Leavengood seconds_since_1900(void)
108323ba9b7SRyan Leavengood {
109323ba9b7SRyan Leavengood 	return kSecondsBetween1900And1970 + real_time_clock();
110323ba9b7SRyan Leavengood }
111323ba9b7SRyan Leavengood 
112323ba9b7SRyan Leavengood 
113323ba9b7SRyan Leavengood status_t
114323ba9b7SRyan Leavengood ntp_update_time(const char* hostname, const char** errorString,
115323ba9b7SRyan Leavengood 	int32* errorCode)
116323ba9b7SRyan Leavengood {
117323ba9b7SRyan Leavengood 	hostent	*server = gethostbyname(hostname);
118323ba9b7SRyan Leavengood 
119323ba9b7SRyan Leavengood 	if (server == NULL) {
120323ba9b7SRyan Leavengood 
121*8c333297SRyan Leavengood 		*errorString = B_TRANSLATE("Could not contact server");
122323ba9b7SRyan Leavengood 		return B_ENTRY_NOT_FOUND;
123323ba9b7SRyan Leavengood 	}
124323ba9b7SRyan Leavengood 
125323ba9b7SRyan Leavengood 	ntp_data message;
126323ba9b7SRyan Leavengood 	memset(&message, 0, sizeof(ntp_data));
127323ba9b7SRyan Leavengood 
128323ba9b7SRyan Leavengood 	message.leap_indicator = LEAP_CLOCK_NOT_IN_SYNC;
129323ba9b7SRyan Leavengood 	message.version = NTP_VERSION_3;
130323ba9b7SRyan Leavengood 	message.mode = MODE_CLIENT;
131323ba9b7SRyan Leavengood 
132323ba9b7SRyan Leavengood 	message.stratum = 1;	// primary reference
133323ba9b7SRyan Leavengood 	message.precision = -5;	// 2^-5 ~ 32-64 Hz precision
134323ba9b7SRyan Leavengood 
135323ba9b7SRyan Leavengood 	message.root_delay.SetTo(1);	// 1 sec
136323ba9b7SRyan Leavengood 	message.root_dispersion.SetTo(1);
137323ba9b7SRyan Leavengood 
138323ba9b7SRyan Leavengood 	message.transmit_timestamp.SetTo(seconds_since_1900());
139323ba9b7SRyan Leavengood 
140323ba9b7SRyan Leavengood 	int connection = socket(AF_INET, SOCK_DGRAM, 0);
141323ba9b7SRyan Leavengood 	if (connection < 0) {
142*8c333297SRyan Leavengood 		*errorString = B_TRANSLATE("Could not create socket");
143323ba9b7SRyan Leavengood 		*errorCode = errno;
144323ba9b7SRyan Leavengood 		return B_ERROR;
145323ba9b7SRyan Leavengood 	}
146323ba9b7SRyan Leavengood 
147323ba9b7SRyan Leavengood 	struct sockaddr_in address;
148323ba9b7SRyan Leavengood 	address.sin_family = AF_INET;
149323ba9b7SRyan Leavengood 	address.sin_port = htons(NTP_PORT);
150323ba9b7SRyan Leavengood 	address.sin_addr.s_addr = *(uint32 *)server->h_addr_list[0];
151323ba9b7SRyan Leavengood 
152323ba9b7SRyan Leavengood 	if (sendto(connection, (char *)&message, sizeof(ntp_data),
153323ba9b7SRyan Leavengood 			0, (struct sockaddr *)&address, sizeof(address)) < 0) {
154*8c333297SRyan Leavengood 		*errorString = B_TRANSLATE("Sending request failed");
155323ba9b7SRyan Leavengood 		*errorCode = errno;
156323ba9b7SRyan Leavengood 		return B_ERROR;
157323ba9b7SRyan Leavengood 	}
158323ba9b7SRyan Leavengood 
159323ba9b7SRyan Leavengood 	fd_set waitForReceived;
160323ba9b7SRyan Leavengood 	FD_ZERO(&waitForReceived);
161323ba9b7SRyan Leavengood 	FD_SET(connection, &waitForReceived);
162323ba9b7SRyan Leavengood 
163323ba9b7SRyan Leavengood 	struct timeval timeout;
164323ba9b7SRyan Leavengood 	timeout.tv_sec = 3;
165323ba9b7SRyan Leavengood 	timeout.tv_usec = 0;
166323ba9b7SRyan Leavengood 	// we'll wait 3 seconds for the answer
167323ba9b7SRyan Leavengood 
168323ba9b7SRyan Leavengood 	if (select(connection + 1, &waitForReceived, NULL, NULL, &timeout) <= 0) {
169*8c333297SRyan Leavengood 		*errorString = B_TRANSLATE("Waiting for answer failed");
170323ba9b7SRyan Leavengood 		*errorCode = errno;
171323ba9b7SRyan Leavengood 		return B_ERROR;
172323ba9b7SRyan Leavengood 	}
173323ba9b7SRyan Leavengood 
174323ba9b7SRyan Leavengood 	message.transmit_timestamp.SetTo(0);
175323ba9b7SRyan Leavengood 
176323ba9b7SRyan Leavengood 	socklen_t addressSize = sizeof(address);
177323ba9b7SRyan Leavengood 	if (recvfrom(connection, (char *)&message, sizeof(ntp_data), 0,
178323ba9b7SRyan Leavengood 			(sockaddr *)&address, &addressSize) < (ssize_t)sizeof(ntp_data)) {
179*8c333297SRyan Leavengood 		*errorString = B_TRANSLATE("Message receiving failed");
180323ba9b7SRyan Leavengood 		*errorCode = errno;
181323ba9b7SRyan Leavengood 		close(connection);
182323ba9b7SRyan Leavengood 		return B_ERROR;
183323ba9b7SRyan Leavengood 	}
184323ba9b7SRyan Leavengood 
185323ba9b7SRyan Leavengood 	close(connection);
186323ba9b7SRyan Leavengood 
187323ba9b7SRyan Leavengood 	if (message.transmit_timestamp.Integer() == 0) {
188*8c333297SRyan Leavengood 		*errorString = B_TRANSLATE("Received invalid time");
189323ba9b7SRyan Leavengood 		return B_BAD_VALUE;
190323ba9b7SRyan Leavengood 	}
191323ba9b7SRyan Leavengood 
192323ba9b7SRyan Leavengood 	time_t now = message.transmit_timestamp.Integer() - kSecondsBetween1900And1970;
193323ba9b7SRyan Leavengood 	set_real_time_clock(now);
194323ba9b7SRyan Leavengood 	return B_OK;
195323ba9b7SRyan Leavengood }
196