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
33struct fixed32 {
34	int16	integer;
35	uint16	fraction;
36
37	void
38	SetTo(int16 integer, uint16 fraction = 0)
39	{
40		this->integer = htons(integer);
41		this->fraction = htons(fraction);
42	}
43
44	int16 Integer() { return htons(integer); }
45	uint16 Fraction() { return htons(fraction); }
46};
47
48struct ufixed64 {
49	uint32	integer;
50	uint32	fraction;
51
52	void
53	SetTo(uint32 integer, uint32 fraction = 0)
54	{
55		this->integer = htonl(integer);
56		this->fraction = htonl(fraction);
57	}
58
59	uint32 Integer() { return htonl(integer); }
60	uint32 Fraction() { return htonl(fraction); }
61};
62
63struct 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
87enum 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
94enum 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
105const uint32 kSecondsBetween1900And1970 = 2208988800UL;
106
107
108uint32
109seconds_since_1900(void)
110{
111	return kSecondsBetween1900And1970 + real_time_clock();
112}
113
114
115status_t
116ntp_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