1/* 2 * libsntp_query.c 3 * ntp 4 * 5 * Created by Morgan Grainger on 2/23/11. 6 * Copyright 2011-2014 Apple Inc. All rights reserved. 7 * 8 */ 9 10#include <sys/time.h> 11#include <sys/socket.h> 12#include <dispatch/dispatch.h> 13#include <os/trace.h> 14#include <mach/mach_time.h> 15 16#include "networking.h" 17 18#include "libsntp.h" 19 20#define NTP_SERVICE_PORT 123 21#define GETTIMEOFDAY_FAILED -100 22 23volatile int debug; 24char *progname = "libsntp"; /* for msyslog */ 25 26/* Forward declarations */ 27sntp_query_result_t on_wire (struct addrinfo *host, bool use_service_port, int numTries, struct timeval timeout, /* out */ uint64_t *out_mach_time, /* out */ struct timeval *out_time, /* out */ double *out_delay, /* out */ double *out_dispersion, int *retry_attempts); 28void set_li_vn_mode (struct pkt *spkt, char leap, char version, char mode); 29void adjust_tv_by_offset(struct timeval *tv, double offset); 30 31void 32sntp_query(char *host, bool use_service_port, sntp_query_result_handler_t result_handler) 33{ 34 struct timeval attempt_timeout = {15, 0}; 35 return sntp_query_extended(host, "123", use_service_port, 5, attempt_timeout, ^(sntp_query_result_t query_result, uint64_t mach_timestamp, struct timeval time, double delay, double dispersion, bool more_servers, const char *ip_addr, const char *port, int retry_attempts){ 36 return result_handler(query_result, time, delay, dispersion, more_servers); 37 }); 38} 39 40void 41sntp_query_extended(const char *host, const char *servname, bool use_service_port, int attempts, struct timeval attempt_timeout, sntp_query_extended_result_handler_t result_handler) 42{ 43 __block char *our_host = strdup(host); 44 __block char *our_servname = strdup(servname?:"123"); 45 46 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 47 char adr_buf[INET6_ADDRSTRLEN]; 48 char serv_buf[NI_MAXSERV]; 49 struct addrinfo **resolved_hosts = NULL; 50 51 uint64_t mach_timestamp = 0; 52 struct timeval time_estimate; 53 double delay, dispersion; 54 55 if (resolve_hosts(&our_host, 1, our_servname, &resolved_hosts, AF_INET) != 1) { 56#ifdef DEBUG 57 fprintf(stderr, "Unable to resolve hostname %s\n", our_host); 58#endif 59 result_handler(SNTP_RESULT_FAILURE_DNS, mach_timestamp, time_estimate, delay, dispersion, false, NULL, NULL, 0); 60 } else { 61 struct addrinfo *ai = resolved_hosts[0]; 62 63 do { 64 int retry_attempts; 65 sntp_query_result_t this_result = on_wire(ai, use_service_port, attempts, attempt_timeout, &mach_timestamp, &time_estimate, &delay, &dispersion, &retry_attempts); 66 67 adr_buf[0] = serv_buf[0] = 0; 68 int name_error = getnameinfo(ai->ai_addr, ai->ai_addrlen, adr_buf, sizeof(adr_buf), serv_buf, sizeof(serv_buf), NI_NUMERICHOST|NI_NUMERICSERV); 69 if (name_error) { 70 fprintf(stderr, "libsntp: getnameinfo: %d: %s", name_error, gai_strerror(name_error)); 71 adr_buf[0] = serv_buf[0] = 0; 72 } 73 74 if (this_result != SNTP_RESULT_SUCCESS) { 75#ifdef DEBUG 76 fprintf(stderr, "on_wire failed for server %s:%s!\n", adr_buf, serv_buf); 77#endif 78 } 79 80 ai = ai->ai_next; 81 if (!result_handler(this_result, mach_timestamp, time_estimate, delay, dispersion, (ai != NULL), adr_buf, serv_buf, retry_attempts)) { 82 break; 83 } 84 } while (ai != NULL); 85 86 freeaddrinfo(resolved_hosts[0]); 87 free(resolved_hosts); 88 } 89 90 free(our_host); 91 free(our_servname); 92 }); 93} 94 95/* The heart of (S)NTP, exchange NTP packets and compute values to correct the local clock */ 96sntp_query_result_t 97on_wire ( 98 struct addrinfo *host, 99 bool use_service_port, 100 const int numTries, 101 struct timeval timeout, 102 /* out */ uint64_t *out_mach_time, 103 /* out */ struct timeval *out_time, 104 /* out */ double *out_delay, 105 /* out */ double *out_dispersion, 106 /* out */ int *retry_attempts 107 ) 108{ 109 char addr_buf[INET6_ADDRSTRLEN]; 110 SOCKET sock = -1; 111 struct pkt x_pkt; 112 struct pkt r_pkt; 113 sntp_query_result_t result = SNTP_RESULT_FAILURE_INTERNAL; 114 115 116 for(*retry_attempts=0; *retry_attempts<numTries; ++*retry_attempts) { 117 struct timeval tv_xmt, tv_dst; 118 double t21, t34, delta, offset, precision, root_dispersion; 119 int digits, rpktl, sw_case; 120 u_fp p_rdly, p_rdsp; 121 l_fp p_rec, p_xmt, p_ref, p_org, xmt, tmp, dst; 122 123 memset(&r_pkt, 0, sizeof(r_pkt)); 124 memset(&x_pkt, 0, sizeof(x_pkt)); 125 126 create_socket(&sock, (sockaddr_u *)host->ai_addr); 127 if (sock < 0) { 128 os_trace("socket failed errno:%d", errno); 129 continue; 130 } 131 if (use_service_port) { 132 struct sockaddr_in send_addr; 133 int reuse = 1; 134 bzero(&send_addr, sizeof(send_addr)); 135 136 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); 137 138 send_addr.sin_family = host->ai_addr->sa_family; 139 send_addr.sin_addr.s_addr = INADDR_ANY; 140 send_addr.sin_port = htons(NTP_SERVICE_PORT); 141 if (0 != bind(sock, (struct sockaddr *)&send_addr, sizeof(send_addr))) { 142 os_trace("bind failed errno:%d", errno); 143 result = SNTP_RESULT_FAILURE_CANNOT_BIND_SOCKET; 144 closesocket(sock); 145 return result; 146 } 147 } 148 149 if (GETTIMEOFDAY(&tv_xmt, (struct timezone *)NULL)) { 150 fprintf(stderr, "libsntp: gettimeofday failed: %d: %s\n", errno, strerror(errno)); 151 rpktl = GETTIMEOFDAY_FAILED; 152 } else { 153 tv_xmt.tv_sec += JAN_1970; 154 155#ifdef DEBUG 156 printf("sntp on_wire: Current time sec: %i msec: %i\n", (unsigned int) tv_xmt.tv_sec, 157 (unsigned int) tv_xmt.tv_usec); 158#endif 159 160 TVTOTS(&tv_xmt, &xmt); 161 HTONL_FP(&xmt, &(x_pkt.xmt)); 162 163 x_pkt.stratum = STRATUM_TO_PKT(STRATUM_UNSPEC); 164 x_pkt.ppoll = 8; 165 /* FIXME! Modus broadcast + adr. check -> bdr. pkt */ 166 set_li_vn_mode(&x_pkt, LEAP_NOTINSYNC, 4, 3); 167 168 if (0 == sendpkt(sock, (sockaddr_u *)host->ai_addr, &x_pkt, LEN_PKT_NOMAC)) { 169 rpktl = recvpkt(sock, timeout, &r_pkt, &x_pkt); 170 } else { 171 rpktl = SERVER_UNUSEABLE; 172 fprintf(stderr, "libsntp: sendpkt failed\n"); 173 } 174 } 175 176 *out_mach_time = mach_absolute_time(); 177 if (GETTIMEOFDAY(&tv_dst, (struct timezone *)NULL)) { 178 fprintf(stderr, "libsntp: gettimeofday failed: %d: %s\n", errno, strerror(errno)); 179 if (rpktl > 0) { 180 rpktl = GETTIMEOFDAY_FAILED; 181 } 182 } 183 184 185 closesocket(sock); 186 187 if(rpktl > 0) 188 sw_case = 1; 189 else 190 sw_case = rpktl; 191 192 switch(sw_case) { 193 case SERVER_UNUSEABLE: 194 result = SNTP_RESULT_FAILURE_SERVER_UNUSABLE; 195 break; 196 197 case PACKET_UNUSEABLE: 198 result = SNTP_RESULT_FAILURE_PACKET_UNUSABLE; 199 break; 200 201 case SERVER_AUTH_FAIL: 202 result = SNTP_RESULT_FAILURE_AUTHORIZATION; 203 break; 204 205 case KOD_DEMOBILIZE: 206 result = SNTP_RESULT_FAILURE_SERVER_KISSOFDEATH; 207 break; 208 209 case KOD_RATE: 210 result = SNTP_RESULT_FAILURE_SERVER_RATE_LIMIT; 211 break; 212 213 case GETTIMEOFDAY_FAILED: 214 // this really shouldn't happen, no need for it's own code 215 result = SNTP_RESULT_FAILURE_INTERNAL; 216 break; 217 218 case 1: 219 220 /* Convert timestamps from network to host byte order */ 221 p_rdly = NTOHS_FP(r_pkt.rootdelay); 222 p_rdsp = NTOHS_FP(r_pkt.rootdisp); 223 NTOHL_FP(&r_pkt.reftime, &p_ref); 224 NTOHL_FP(&r_pkt.org, &p_org); 225 NTOHL_FP(&r_pkt.rec, &p_rec); 226 NTOHL_FP(&r_pkt.xmt, &p_xmt); 227 228 if (ENABLED_OPT(NORMALVERBOSE)) { 229 getnameinfo(host->ai_addr, host->ai_addrlen, addr_buf, 230 sizeof(addr_buf), NULL, 0, NI_NUMERICHOST); 231 232 printf("sntp on_wire: Received %i bytes from %s\n", rpktl, addr_buf); 233 } 234 235 precision = LOGTOD(r_pkt.precision); 236#ifdef DEBUG 237 fprintf(stderr, "sntp precision: %f\n", precision); 238#endif /* DEBUG */ 239 for (digits = 0; (precision *= 10.) < 1.; ++digits) 240 /* empty */ ; 241 if (digits > 6) 242 digits = 6; 243 244 root_dispersion = FPTOD(p_rdsp); 245 246#ifdef DEBUG 247 fprintf(stderr, "sntp rootdelay: %f\n", FPTOD(p_rdly)); 248 fprintf(stderr, "sntp rootdisp: %f\n", root_dispersion); 249 250 pkt_output(&r_pkt, rpktl, stdout); 251 252 fprintf(stderr, "sntp on_wire: r_pkt.reftime:\n"); 253 l_fp_output(&(r_pkt.reftime), stdout); 254 fprintf(stderr, "sntp on_wire: r_pkt.org:\n"); 255 l_fp_output(&(r_pkt.org), stdout); 256 fprintf(stderr, "sntp on_wire: r_pkt.rec:\n"); 257 l_fp_output(&(r_pkt.rec), stdout); 258 fprintf(stderr, "sntp on_wire: r_pkt.rec:\n"); 259 l_fp_output_bin(&(r_pkt.rec), stdout); 260 fprintf(stderr, "sntp on_wire: r_pkt.rec:\n"); 261 l_fp_output_dec(&(r_pkt.rec), stdout); 262 fprintf(stderr, "sntp on_wire: r_pkt.xmt:\n"); 263 l_fp_output(&(r_pkt.xmt), stdout); 264#endif 265 266 /* Compute offset etc. */ 267 tv_dst.tv_sec += JAN_1970; 268 269 tmp = p_rec; 270 L_SUB(&tmp, &p_org); 271 272 LFPTOD(&tmp, t21); 273 274 TVTOTS(&tv_dst, &dst); 275 276 tmp = p_xmt; 277 L_SUB(&tmp, &dst); 278 279 LFPTOD(&tmp, t34); 280 281 offset = (t21 + t34) / 2.; 282 delta = t21 - t34; 283 284 *out_time = tv_dst; 285 out_time->tv_sec -= JAN_1970; 286 287 adjust_tv_by_offset(out_time, offset); 288 *out_delay = delta; 289 *out_dispersion = (root_dispersion > 0 ? root_dispersion : -1); 290 291 return SNTP_RESULT_SUCCESS; 292 } 293 } 294 295#if DEBUG 296 getnameinfo(host->ai_addr, host->ai_addrlen, addr_buf, sizeof(addr_buf), NULL, 0, NI_NUMERICHOST); 297 fprintf(stderr, "Received no useable packet from %s!", addr_buf); 298#endif 299 300 return result; 301} 302 303void 304adjust_tv_by_offset(struct timeval *tv, double offset) 305{ 306 double frac, whole; 307 frac = modf(offset, &whole); 308 309 tv->tv_sec += (int) offset; 310 tv->tv_usec += frac * USEC_PER_SEC; 311 if (tv->tv_usec < 0) { 312 tv->tv_usec += USEC_PER_SEC; 313 tv->tv_sec--; 314 } else if (tv->tv_usec > USEC_PER_SEC) { 315 tv->tv_usec -= USEC_PER_SEC; 316 tv->tv_sec++; 317 } 318} 319 320/* Compute the 8 bits for li_vn_mode */ 321void 322set_li_vn_mode ( 323 struct pkt *spkt, 324 char leap, 325 char version, 326 char mode 327 ) 328{ 329 330 if(leap > 3) { 331 debug_msg("set_li_vn_mode: leap > 3 using max. 3"); 332 leap = 3; 333 } 334 335 if(mode > 7) { 336 debug_msg("set_li_vn_mode: mode > 7, using client mode 3"); 337 mode = 3; 338 } 339 340 spkt->li_vn_mode = leap << 6; 341 spkt->li_vn_mode |= version << 3; 342 spkt->li_vn_mode |= mode; 343} 344