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