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