vfs: check userland buffers before reading them.
[haiku.git] / src / preferences / time / ntp.cpp
blobd07b402e8b54511cd7e1c6e34c48fe5ce25d0d59
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 */
7 #include "ntp.h"
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>
18 #include <OS.h>
20 #include <Catalog.h>
21 #include <NetworkAddress.h>
22 #include <NetworkAddressResolver.h>
25 #undef B_TRANSLATION_CONTEXT
26 #define B_TRANSLATION_CONTEXT "Time"
29 /* This structure and its data fields are described in RFC 1305
30 * "Network Time Protocol (Version 3)" in appendix A.
33 struct fixed32 {
34 int16 integer;
35 uint16 fraction;
37 void
38 SetTo(int16 integer, uint16 fraction = 0)
40 this->integer = htons(integer);
41 this->fraction = htons(fraction);
44 int16 Integer() { return htons(integer); }
45 uint16 Fraction() { return htons(fraction); }
48 struct ufixed64 {
49 uint32 integer;
50 uint32 fraction;
52 void
53 SetTo(uint32 integer, uint32 fraction = 0)
55 this->integer = htonl(integer);
56 this->fraction = htonl(fraction);
59 uint32 Integer() { return htonl(integer); }
60 uint32 Fraction() { return htonl(fraction); }
63 struct ntp_data {
64 uint8 mode : 3;
65 uint8 version : 3;
66 uint8 leap_indicator : 2;
68 uint8 stratum;
69 int8 poll;
70 int8 precision; /* in seconds of the nearest power of two */
72 fixed32 root_delay;
73 fixed32 root_dispersion;
74 uint32 root_identifier;
76 ufixed64 reference_timestamp;
77 ufixed64 originate_timestamp;
78 ufixed64 receive_timestamp;
79 ufixed64 transmit_timestamp;
81 /* optional authenticator follows (96 bits) */
84 #define NTP_PORT 123
85 #define NTP_VERSION_3 3
87 enum 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,
94 enum 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,
105 const uint32 kSecondsBetween1900And1970 = 2208988800UL;
108 uint32
109 seconds_since_1900(void)
111 return kSecondsBetween1900And1970 + real_time_clock();
115 status_t
116 ntp_update_time(const char* hostname, const char** errorString,
117 int32* errorCode)
119 BNetworkAddressResolver resolver(hostname, NTP_PORT);
120 BNetworkAddress address;
121 uint32 cookie = 0;
122 bool success = false;
124 if (resolver.InitCheck() != B_OK) {
125 *errorString = B_TRANSLATE("Could not resolve server address");
126 return B_ENTRY_NOT_FOUND;
129 ntp_data message;
130 memset(&message, 0, sizeof(ntp_data));
132 message.leap_indicator = LEAP_CLOCK_NOT_IN_SYNC;
133 message.version = NTP_VERSION_3;
134 message.mode = MODE_CLIENT;
136 message.stratum = 1; // primary reference
137 message.precision = -5; // 2^-5 ~ 32-64 Hz precision
139 message.root_delay.SetTo(1); // 1 sec
140 message.root_dispersion.SetTo(1);
142 message.transmit_timestamp.SetTo(seconds_since_1900());
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;
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;
160 if (!success) {
161 *errorString = B_TRANSLATE("Sending request failed");
162 close(connection);
163 return B_ERROR;
166 fd_set waitForReceived;
167 FD_ZERO(&waitForReceived);
168 FD_SET(connection, &waitForReceived);
170 struct timeval timeout;
171 timeout.tv_sec = 3;
172 timeout.tv_usec = 0;
173 // we'll wait 3 seconds for the answer
175 if (select(connection + 1, &waitForReceived, NULL, NULL, &timeout) <= 0) {
176 *errorString = B_TRANSLATE("Waiting for answer failed");
177 *errorCode = errno;
178 close(connection);
179 return B_ERROR;
182 message.transmit_timestamp.SetTo(0);
184 socklen_t addressSize = address.Length();
185 if (recvfrom(connection, reinterpret_cast<char*>(&message), sizeof(ntp_data), 0,
186 &address.SockAddr(), &addressSize) < (ssize_t)sizeof(ntp_data)) {
187 *errorString = B_TRANSLATE("Message receiving failed");
188 *errorCode = errno;
189 close(connection);
190 return B_ERROR;
193 close(connection);
195 if (message.transmit_timestamp.Integer() == 0) {
196 *errorString = B_TRANSLATE("Received invalid time");
197 return B_BAD_VALUE;
200 time_t now = message.transmit_timestamp.Integer() - kSecondsBetween1900And1970;
201 set_real_time_clock(now);
202 return B_OK;