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