xref: /haiku/src/preferences/time/ntp.cpp (revision 323b65468e5836bb27a5e373b14027d902349437)
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_TRANSLATE_CONTEXT
24 #define B_TRANSLATE_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 
31 struct 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 
46 struct 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 
61 struct 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 
85 enum 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 
92 enum 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 
103 const uint32 kSecondsBetween1900And1970 = 2208988800UL;
104 
105 
106 uint32
107 seconds_since_1900(void)
108 {
109 	return kSecondsBetween1900And1970 + real_time_clock();
110 }
111 
112 
113 status_t
114 ntp_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 		return B_ERROR;
157 	}
158 
159 	fd_set waitForReceived;
160 	FD_ZERO(&waitForReceived);
161 	FD_SET(connection, &waitForReceived);
162 
163 	struct timeval timeout;
164 	timeout.tv_sec = 3;
165 	timeout.tv_usec = 0;
166 	// we'll wait 3 seconds for the answer
167 
168 	if (select(connection + 1, &waitForReceived, NULL, NULL, &timeout) <= 0) {
169 		*errorString = B_TRANSLATE("Waiting for answer failed");
170 		*errorCode = errno;
171 		return B_ERROR;
172 	}
173 
174 	message.transmit_timestamp.SetTo(0);
175 
176 	socklen_t addressSize = sizeof(address);
177 	if (recvfrom(connection, (char *)&message, sizeof(ntp_data), 0,
178 			(sockaddr *)&address, &addressSize) < (ssize_t)sizeof(ntp_data)) {
179 		*errorString = B_TRANSLATE("Message receiving failed");
180 		*errorCode = errno;
181 		close(connection);
182 		return B_ERROR;
183 	}
184 
185 	close(connection);
186 
187 	if (message.transmit_timestamp.Integer() == 0) {
188 		*errorString = B_TRANSLATE("Received invalid time");
189 		return B_BAD_VALUE;
190 	}
191 
192 	time_t now = message.transmit_timestamp.Integer() - kSecondsBetween1900And1970;
193 	set_real_time_clock(now);
194 	return B_OK;
195 }
196