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 33 struct 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 48 struct 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 63 struct 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 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, 92 }; 93 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, 102 }; 103 104 105 const uint32 kSecondsBetween1900And1970 = 2208988800UL; 106 107 108 uint32 109 seconds_since_1900(void) 110 { 111 return kSecondsBetween1900And1970 + real_time_clock(); 112 } 113 114 115 status_t 116 ntp_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