1290001Sglebius#include <config.h> 2132451Sroberto 3290001Sglebius#include <event2/util.h> 4290001Sglebius#include <event2/event.h> 5132451Sroberto 6290001Sglebius#include "ntp_workimpl.h" 7290001Sglebius#ifdef WORK_THREAD 8290001Sglebius# include <event2/thread.h> 9290001Sglebius#endif 10132451Sroberto 11290001Sglebius#include "main.h" 12290001Sglebius#include "ntp_libopts.h" 13290001Sglebius#include "kod_management.h" 14290001Sglebius#include "networking.h" 15290001Sglebius#include "utilities.h" 16290001Sglebius#include "log.h" 17290001Sglebius#include "libntp.h" 18132451Sroberto 19132451Sroberto 20290001Sglebiusint shutting_down; 21290001Sglebiusint time_derived; 22290001Sglebiusint time_adjusted; 23290001Sglebiusint n_pending_dns = 0; 24290001Sglebiusint n_pending_ntp = 0; 25290001Sglebiusint ai_fam_pref = AF_UNSPEC; 26290001Sglebiusint ntpver = 4; 27290001Sglebiusdouble steplimit = -1; 28290001SglebiusSOCKET sock4 = -1; /* Socket for IPv4 */ 29290001SglebiusSOCKET sock6 = -1; /* Socket for IPv6 */ 30290001Sglebius/* 31290001Sglebius** BCAST *must* listen on port 123 (by default), so we can only 32290001Sglebius** use the UCST sockets (above) if they too are using port 123 33290001Sglebius*/ 34290001SglebiusSOCKET bsock4 = -1; /* Broadcast Socket for IPv4 */ 35290001SglebiusSOCKET bsock6 = -1; /* Broadcast Socket for IPv6 */ 36290001Sglebiusstruct event_base *base; 37290001Sglebiusstruct event *ev_sock4; 38290001Sglebiusstruct event *ev_sock6; 39290001Sglebiusstruct event *ev_worker_timeout; 40290001Sglebiusstruct event *ev_xmt_timer; 41132451Sroberto 42290001Sglebiusstruct dns_ctx { 43290001Sglebius const char * name; 44290001Sglebius int flags; 45290001Sglebius#define CTX_BCST 0x0001 46290001Sglebius#define CTX_UCST 0x0002 47290001Sglebius#define CTX_xCST 0x0003 48290001Sglebius#define CTX_CONC 0x0004 49290001Sglebius#define CTX_unused 0xfffd 50290001Sglebius int key_id; 51290001Sglebius struct timeval timeout; 52290001Sglebius struct key * key; 53290001Sglebius}; 54132451Sroberto 55290001Sglebiustypedef struct sent_pkt_tag sent_pkt; 56290001Sglebiusstruct sent_pkt_tag { 57290001Sglebius sent_pkt * link; 58290001Sglebius struct dns_ctx * dctx; 59290001Sglebius sockaddr_u addr; 60290001Sglebius time_t stime; 61290001Sglebius int done; 62290001Sglebius struct pkt x_pkt; 63290001Sglebius}; 64132451Sroberto 65290001Sglebiustypedef struct xmt_ctx_tag xmt_ctx; 66290001Sglebiusstruct xmt_ctx_tag { 67290001Sglebius xmt_ctx * link; 68290001Sglebius SOCKET sock; 69290001Sglebius time_t sched; 70290001Sglebius sent_pkt * spkt; 71290001Sglebius}; 72132451Sroberto 73290001Sglebiusstruct timeval gap; 74290001Sglebiusxmt_ctx * xmt_q; 75290001Sglebiusstruct key * keys = NULL; 76290001Sglebiusint response_timeout; 77290001Sglebiusstruct timeval response_tv; 78290001Sglebiusstruct timeval start_tv; 79290001Sglebius/* check the timeout at least once per second */ 80290001Sglebiusstruct timeval wakeup_tv = { 0, 888888 }; 81132451Sroberto 82290001Sglebiussent_pkt * fam_listheads[2]; 83290001Sglebius#define v4_pkts_list (fam_listheads[0]) 84290001Sglebius#define v6_pkts_list (fam_listheads[1]) 85132451Sroberto 86290001Sglebiusstatic union { 87290001Sglebius struct pkt pkt; 88290001Sglebius char buf[LEN_PKT_NOMAC + NTP_MAXEXTEN + MAX_MAC_LEN]; 89290001Sglebius} rbuf; 90132451Sroberto 91290001Sglebius#define r_pkt rbuf.pkt 92132451Sroberto 93290001Sglebius#ifdef HAVE_DROPROOT 94290001Sglebiusint droproot; /* intres imports these */ 95290001Sglebiusint root_dropped; 96290001Sglebius#endif 97290001Sglebiusu_long current_time; /* libntp/authkeys.c */ 98132451Sroberto 99290001Sglebiusvoid open_sockets(void); 100290001Sglebiusvoid handle_lookup(const char *name, int flags); 101290001Sglebiusvoid sntp_addremove_fd(int fd, int is_pipe, int remove_it); 102290001Sglebiusvoid worker_timeout(evutil_socket_t, short, void *); 103290001Sglebiusvoid worker_resp_cb(evutil_socket_t, short, void *); 104290001Sglebiusvoid sntp_name_resolved(int, int, void *, const char *, const char *, 105290001Sglebius const struct addrinfo *, 106290001Sglebius const struct addrinfo *); 107290001Sglebiusvoid queue_xmt(SOCKET sock, struct dns_ctx *dctx, sent_pkt *spkt, 108290001Sglebius u_int xmt_delay); 109290001Sglebiusvoid xmt_timer_cb(evutil_socket_t, short, void *ptr); 110290001Sglebiusvoid xmt(xmt_ctx *xctx); 111290001Sglebiusint check_kod(const struct addrinfo *ai); 112290001Sglebiusvoid timeout_query(sent_pkt *); 113290001Sglebiusvoid timeout_queries(void); 114290001Sglebiusvoid sock_cb(evutil_socket_t, short, void *); 115290001Sglebiusvoid check_exit_conditions(void); 116290001Sglebiusvoid sntp_libevent_log_cb(int, const char *); 117290001Sglebiusvoid set_li_vn_mode(struct pkt *spkt, char leap, char version, char mode); 118290001Sglebiusint set_time(double offset); 119290001Sglebiusvoid dec_pending_ntp(const char *, sockaddr_u *); 120290001Sglebiusint libevent_version_ok(void); 121290001Sglebiusint gettimeofday_cached(struct event_base *b, struct timeval *tv); 122132451Sroberto 123132451Sroberto 124290001Sglebius/* 125290001Sglebius * The actual main function. 126290001Sglebius */ 127290001Sglebiusint 128290001Sglebiussntp_main ( 129290001Sglebius int argc, 130290001Sglebius char **argv, 131290001Sglebius const char *sntpVersion 132290001Sglebius ) 133290001Sglebius{ 134290001Sglebius int i; 135290001Sglebius int exitcode; 136290001Sglebius int optct; 137290001Sglebius struct event_config * evcfg; 138132451Sroberto 139290001Sglebius /* Initialize logging system - sets up progname */ 140290001Sglebius sntp_init_logging(argv[0]); 141132451Sroberto 142290001Sglebius if (!libevent_version_ok()) 143290001Sglebius exit(EX_SOFTWARE); 144132451Sroberto 145290001Sglebius init_lib(); 146290001Sglebius init_auth(); 147132451Sroberto 148290001Sglebius optct = ntpOptionProcess(&sntpOptions, argc, argv); 149290001Sglebius argc -= optct; 150290001Sglebius argv += optct; 151132451Sroberto 152132451Sroberto 153290001Sglebius debug = OPT_VALUE_SET_DEBUG_LEVEL; 154132451Sroberto 155290001Sglebius TRACE(2, ("init_lib() done, %s%s\n", 156290001Sglebius (ipv4_works) 157290001Sglebius ? "ipv4_works " 158290001Sglebius : "", 159290001Sglebius (ipv6_works) 160290001Sglebius ? "ipv6_works " 161290001Sglebius : "")); 162290001Sglebius ntpver = OPT_VALUE_NTPVERSION; 163290001Sglebius steplimit = OPT_VALUE_STEPLIMIT / 1e3; 164290001Sglebius gap.tv_usec = max(0, OPT_VALUE_GAP * 1000); 165290001Sglebius gap.tv_usec = min(gap.tv_usec, 999999); 166132451Sroberto 167290001Sglebius if (HAVE_OPT(LOGFILE)) 168290001Sglebius open_logfile(OPT_ARG(LOGFILE)); 169132451Sroberto 170290001Sglebius msyslog(LOG_INFO, "%s", sntpVersion); 171132451Sroberto 172290001Sglebius if (0 == argc && !HAVE_OPT(BROADCAST) && !HAVE_OPT(CONCURRENT)) { 173290001Sglebius printf("%s: Must supply at least one of -b hostname, -c hostname, or hostname.\n", 174290001Sglebius progname); 175290001Sglebius exit(EX_USAGE); 176290001Sglebius } 177132451Sroberto 178132451Sroberto 179290001Sglebius /* 180290001Sglebius ** Eventually, we probably want: 181290001Sglebius ** - separate bcst and ucst timeouts (why?) 182290001Sglebius ** - multiple --timeout values in the commandline 183290001Sglebius */ 184132451Sroberto 185290001Sglebius response_timeout = OPT_VALUE_TIMEOUT; 186290001Sglebius response_tv.tv_sec = response_timeout; 187290001Sglebius response_tv.tv_usec = 0; 188132451Sroberto 189290001Sglebius /* IPv6 available? */ 190290001Sglebius if (isc_net_probeipv6() != ISC_R_SUCCESS) { 191290001Sglebius ai_fam_pref = AF_INET; 192290001Sglebius TRACE(1, ("No ipv6 support available, forcing ipv4\n")); 193290001Sglebius } else { 194290001Sglebius /* Check for options -4 and -6 */ 195290001Sglebius if (HAVE_OPT(IPV4)) 196290001Sglebius ai_fam_pref = AF_INET; 197290001Sglebius else if (HAVE_OPT(IPV6)) 198290001Sglebius ai_fam_pref = AF_INET6; 199290001Sglebius } 200132451Sroberto 201290001Sglebius /* TODO: Parse config file if declared */ 202132451Sroberto 203290001Sglebius /* 204290001Sglebius ** Init the KOD system. 205290001Sglebius ** For embedded systems with no writable filesystem, 206290001Sglebius ** -K /dev/null can be used to disable KoD storage. 207290001Sglebius */ 208290001Sglebius kod_init_kod_db(OPT_ARG(KOD), FALSE); 209132451Sroberto 210290001Sglebius // HMS: Should we use arg-defalt for this too? 211290001Sglebius if (HAVE_OPT(KEYFILE)) 212290001Sglebius auth_init(OPT_ARG(KEYFILE), &keys); 213132451Sroberto 214290001Sglebius /* 215290001Sglebius ** Considering employing a variable that prevents functions of doing 216290001Sglebius ** anything until everything is initialized properly 217290001Sglebius ** 218290001Sglebius ** HMS: What exactly does the above mean? 219290001Sglebius */ 220290001Sglebius event_set_log_callback(&sntp_libevent_log_cb); 221290001Sglebius if (debug > 0) 222290001Sglebius event_enable_debug_mode(); 223290001Sglebius#ifdef WORK_THREAD 224290001Sglebius evthread_use_pthreads(); 225290001Sglebius /* we use libevent from main thread only, locks should be academic */ 226290001Sglebius if (debug > 0) 227290001Sglebius evthread_enable_lock_debuging(); 228290001Sglebius#endif 229290001Sglebius evcfg = event_config_new(); 230290001Sglebius if (NULL == evcfg) { 231290001Sglebius printf("%s: event_config_new() failed!\n", progname); 232290001Sglebius return -1; 233290001Sglebius } 234290001Sglebius#ifndef HAVE_SOCKETPAIR 235290001Sglebius event_config_require_features(evcfg, EV_FEATURE_FDS); 236290001Sglebius#endif 237290001Sglebius /* all libevent calls are from main thread */ 238290001Sglebius /* event_config_set_flag(evcfg, EVENT_BASE_FLAG_NOLOCK); */ 239290001Sglebius base = event_base_new_with_config(evcfg); 240290001Sglebius event_config_free(evcfg); 241290001Sglebius if (NULL == base) { 242290001Sglebius printf("%s: event_base_new() failed!\n", progname); 243290001Sglebius return -1; 244290001Sglebius } 245132451Sroberto 246290001Sglebius /* wire into intres resolver */ 247290001Sglebius worker_per_query = TRUE; 248290001Sglebius addremove_io_fd = &sntp_addremove_fd; 249132451Sroberto 250290001Sglebius open_sockets(); 251132451Sroberto 252290001Sglebius if (HAVE_OPT(BROADCAST)) { 253290001Sglebius int cn = STACKCT_OPT( BROADCAST ); 254290001Sglebius const char ** cp = STACKLST_OPT( BROADCAST ); 255132451Sroberto 256290001Sglebius while (cn-- > 0) { 257290001Sglebius handle_lookup(*cp, CTX_BCST); 258290001Sglebius cp++; 259290001Sglebius } 260290001Sglebius } 261132451Sroberto 262290001Sglebius if (HAVE_OPT(CONCURRENT)) { 263290001Sglebius int cn = STACKCT_OPT( CONCURRENT ); 264290001Sglebius const char ** cp = STACKLST_OPT( CONCURRENT ); 265132451Sroberto 266290001Sglebius while (cn-- > 0) { 267290001Sglebius handle_lookup(*cp, CTX_UCST | CTX_CONC); 268290001Sglebius cp++; 269290001Sglebius } 270290001Sglebius } 271132451Sroberto 272290001Sglebius for (i = 0; i < argc; ++i) 273290001Sglebius handle_lookup(argv[i], CTX_UCST); 274132451Sroberto 275290001Sglebius gettimeofday_cached(base, &start_tv); 276290001Sglebius event_base_dispatch(base); 277290001Sglebius event_base_free(base); 278132451Sroberto 279290001Sglebius if (!time_adjusted && 280290001Sglebius (ENABLED_OPT(STEP) || ENABLED_OPT(SLEW))) 281290001Sglebius exitcode = 1; 282290001Sglebius else 283290001Sglebius exitcode = 0; 284132451Sroberto 285290001Sglebius return exitcode; 286290001Sglebius} 287132451Sroberto 288132451Sroberto 289290001Sglebius/* 290290001Sglebius** open sockets and make them non-blocking 291290001Sglebius*/ 292290001Sglebiusvoid 293290001Sglebiusopen_sockets( 294290001Sglebius void 295290001Sglebius ) 296290001Sglebius{ 297290001Sglebius sockaddr_u name; 298132451Sroberto 299290001Sglebius if (-1 == sock4) { 300290001Sglebius sock4 = socket(PF_INET, SOCK_DGRAM, 0); 301290001Sglebius if (-1 == sock4) { 302290001Sglebius /* error getting a socket */ 303290001Sglebius msyslog(LOG_ERR, "open_sockets: socket(PF_INET) failed: %m"); 304290001Sglebius exit(1); 305290001Sglebius } 306290001Sglebius /* Make it non-blocking */ 307290001Sglebius make_socket_nonblocking(sock4); 308132451Sroberto 309290001Sglebius /* Let's try using a wildcard... */ 310290001Sglebius ZERO(name); 311290001Sglebius AF(&name) = AF_INET; 312290001Sglebius SET_ADDR4N(&name, INADDR_ANY); 313290001Sglebius SET_PORT(&name, (HAVE_OPT(USERESERVEDPORT) ? 123 : 0)); 314132451Sroberto 315290001Sglebius if (-1 == bind(sock4, &name.sa, 316290001Sglebius SOCKLEN(&name))) { 317290001Sglebius msyslog(LOG_ERR, "open_sockets: bind(sock4) failed: %m"); 318290001Sglebius exit(1); 319290001Sglebius } 320132451Sroberto 321290001Sglebius /* Register an NTP callback for recv/timeout */ 322290001Sglebius ev_sock4 = event_new(base, sock4, 323290001Sglebius EV_TIMEOUT | EV_READ | EV_PERSIST, 324290001Sglebius &sock_cb, NULL); 325290001Sglebius if (NULL == ev_sock4) { 326290001Sglebius msyslog(LOG_ERR, 327290001Sglebius "open_sockets: event_new(base, sock4) failed!"); 328290001Sglebius } else { 329290001Sglebius event_add(ev_sock4, &wakeup_tv); 330290001Sglebius } 331290001Sglebius } 332132451Sroberto 333290001Sglebius /* We may not always have IPv6... */ 334290001Sglebius if (-1 == sock6 && ipv6_works) { 335290001Sglebius sock6 = socket(PF_INET6, SOCK_DGRAM, 0); 336290001Sglebius if (-1 == sock6 && ipv6_works) { 337290001Sglebius /* error getting a socket */ 338290001Sglebius msyslog(LOG_ERR, "open_sockets: socket(PF_INET6) failed: %m"); 339290001Sglebius exit(1); 340290001Sglebius } 341290001Sglebius /* Make it non-blocking */ 342290001Sglebius make_socket_nonblocking(sock6); 343132451Sroberto 344290001Sglebius /* Let's try using a wildcard... */ 345290001Sglebius ZERO(name); 346290001Sglebius AF(&name) = AF_INET6; 347290001Sglebius SET_ADDR6N(&name, in6addr_any); 348290001Sglebius SET_PORT(&name, (HAVE_OPT(USERESERVEDPORT) ? 123 : 0)); 349132451Sroberto 350290001Sglebius if (-1 == bind(sock6, &name.sa, 351290001Sglebius SOCKLEN(&name))) { 352290001Sglebius msyslog(LOG_ERR, "open_sockets: bind(sock6) failed: %m"); 353290001Sglebius exit(1); 354290001Sglebius } 355290001Sglebius /* Register an NTP callback for recv/timeout */ 356290001Sglebius ev_sock6 = event_new(base, sock6, 357290001Sglebius EV_TIMEOUT | EV_READ | EV_PERSIST, 358290001Sglebius &sock_cb, NULL); 359290001Sglebius if (NULL == ev_sock6) { 360290001Sglebius msyslog(LOG_ERR, 361290001Sglebius "open_sockets: event_new(base, sock6) failed!"); 362290001Sglebius } else { 363290001Sglebius event_add(ev_sock6, &wakeup_tv); 364290001Sglebius } 365290001Sglebius } 366290001Sglebius 367290001Sglebius return; 368290001Sglebius} 369132451Sroberto 370132451Sroberto 371290001Sglebius/* 372290001Sglebius** handle_lookup 373290001Sglebius*/ 374290001Sglebiusvoid 375290001Sglebiushandle_lookup( 376290001Sglebius const char *name, 377290001Sglebius int flags 378290001Sglebius ) 379290001Sglebius{ 380290001Sglebius struct addrinfo hints; /* Local copy is OK */ 381290001Sglebius struct dns_ctx *ctx; 382290001Sglebius long l; 383290001Sglebius char * name_copy; 384290001Sglebius size_t name_sz; 385290001Sglebius size_t octets; 386132451Sroberto 387290001Sglebius TRACE(1, ("handle_lookup(%s,%#x)\n", name, flags)); 388132451Sroberto 389290001Sglebius ZERO(hints); 390290001Sglebius hints.ai_family = ai_fam_pref; 391290001Sglebius hints.ai_flags = AI_CANONNAME | Z_AI_NUMERICSERV; 392290001Sglebius /* 393290001Sglebius ** Unless we specify a socktype, we'll get at least two 394290001Sglebius ** entries for each address: one for TCP and one for 395290001Sglebius ** UDP. That's not what we want. 396290001Sglebius */ 397290001Sglebius hints.ai_socktype = SOCK_DGRAM; 398290001Sglebius hints.ai_protocol = IPPROTO_UDP; 399132451Sroberto 400290001Sglebius name_sz = 1 + strlen(name); 401290001Sglebius octets = sizeof(*ctx) + name_sz; // Space for a ctx and the name 402290001Sglebius ctx = emalloc_zero(octets); // ctx at ctx[0] 403290001Sglebius name_copy = (char *)(ctx + 1); // Put the name at ctx[1] 404290001Sglebius memcpy(name_copy, name, name_sz); // copy the name to ctx[1] 405290001Sglebius ctx->name = name_copy; // point to it... 406290001Sglebius ctx->flags = flags; 407290001Sglebius ctx->timeout = response_tv; 408132451Sroberto 409290001Sglebius /* The following should arguably be passed in... */ 410290001Sglebius if (ENABLED_OPT(AUTHENTICATION) && 411290001Sglebius atoint(OPT_ARG(AUTHENTICATION), &l)) { 412290001Sglebius ctx->key_id = l; 413290001Sglebius get_key(ctx->key_id, &ctx->key); 414290001Sglebius } else { 415290001Sglebius ctx->key_id = -1; 416290001Sglebius ctx->key = NULL; 417290001Sglebius } 418132451Sroberto 419290001Sglebius ++n_pending_dns; 420290001Sglebius getaddrinfo_sometime(name, "123", &hints, 0, 421290001Sglebius &sntp_name_resolved, ctx); 422132451Sroberto} 423132451Sroberto 424132451Sroberto 425290001Sglebius/* 426290001Sglebius** DNS Callback: 427290001Sglebius** - For each IP: 428290001Sglebius** - - open a socket 429290001Sglebius** - - increment n_pending_ntp 430290001Sglebius** - - send a request if this is a Unicast callback 431290001Sglebius** - - queue wait for response 432290001Sglebius** - decrement n_pending_dns 433290001Sglebius*/ 434290001Sglebiusvoid 435290001Sglebiussntp_name_resolved( 436290001Sglebius int rescode, 437290001Sglebius int gai_errno, 438290001Sglebius void * context, 439290001Sglebius const char * name, 440290001Sglebius const char * service, 441290001Sglebius const struct addrinfo * hints, 442290001Sglebius const struct addrinfo * addr 443290001Sglebius ) 444290001Sglebius{ 445290001Sglebius struct dns_ctx * dctx; 446290001Sglebius sent_pkt * spkt; 447290001Sglebius const struct addrinfo * ai; 448290001Sglebius SOCKET sock; 449290001Sglebius u_int xmt_delay_v4; 450290001Sglebius u_int xmt_delay_v6; 451290001Sglebius u_int xmt_delay; 452290001Sglebius size_t octets; 453132451Sroberto 454290001Sglebius xmt_delay_v4 = 0; 455290001Sglebius xmt_delay_v6 = 0; 456290001Sglebius dctx = context; 457290001Sglebius if (rescode) { 458290001Sglebius#ifdef EAI_SYSTEM 459290001Sglebius if (EAI_SYSTEM == rescode) { 460290001Sglebius errno = gai_errno; 461290001Sglebius mfprintf(stderr, "%s lookup error %m\n", 462290001Sglebius dctx->name); 463290001Sglebius } else 464290001Sglebius#endif 465290001Sglebius fprintf(stderr, "%s lookup error %s\n", 466290001Sglebius dctx->name, gai_strerror(rescode)); 467290001Sglebius } else { 468290001Sglebius TRACE(3, ("%s [%s]\n", dctx->name, 469290001Sglebius (addr->ai_canonname != NULL) 470290001Sglebius ? addr->ai_canonname 471290001Sglebius : "")); 472132451Sroberto 473290001Sglebius for (ai = addr; ai != NULL; ai = ai->ai_next) { 474132451Sroberto 475290001Sglebius if (check_kod(ai)) 476290001Sglebius continue; 477132451Sroberto 478290001Sglebius switch (ai->ai_family) { 479132451Sroberto 480290001Sglebius case AF_INET: 481290001Sglebius sock = sock4; 482290001Sglebius xmt_delay = xmt_delay_v4; 483290001Sglebius xmt_delay_v4++; 484290001Sglebius break; 485132451Sroberto 486290001Sglebius case AF_INET6: 487290001Sglebius if (!ipv6_works) 488290001Sglebius continue; 489132451Sroberto 490290001Sglebius sock = sock6; 491290001Sglebius xmt_delay = xmt_delay_v6; 492290001Sglebius xmt_delay_v6++; 493290001Sglebius break; 494132451Sroberto 495290001Sglebius default: 496290001Sglebius msyslog(LOG_ERR, "sntp_name_resolved: unexpected ai_family: %d", 497290001Sglebius ai->ai_family); 498290001Sglebius exit(1); 499290001Sglebius break; 500290001Sglebius } 501132451Sroberto 502290001Sglebius /* 503290001Sglebius ** We're waiting for a response for either unicast 504290001Sglebius ** or broadcast, so... 505290001Sglebius */ 506290001Sglebius ++n_pending_ntp; 507132451Sroberto 508290001Sglebius /* If this is for a unicast IP, queue a request */ 509290001Sglebius if (dctx->flags & CTX_UCST) { 510290001Sglebius spkt = emalloc_zero(sizeof(*spkt)); 511290001Sglebius spkt->dctx = dctx; 512290001Sglebius octets = min(ai->ai_addrlen, sizeof(spkt->addr)); 513290001Sglebius memcpy(&spkt->addr, ai->ai_addr, octets); 514290001Sglebius queue_xmt(sock, dctx, spkt, xmt_delay); 515290001Sglebius } 516290001Sglebius } 517290001Sglebius } 518290001Sglebius /* n_pending_dns really should be >0 here... */ 519290001Sglebius --n_pending_dns; 520290001Sglebius check_exit_conditions(); 521132451Sroberto} 522132451Sroberto 523132451Sroberto 524290001Sglebius/* 525290001Sglebius** queue_xmt 526290001Sglebius*/ 527290001Sglebiusvoid 528290001Sglebiusqueue_xmt( 529290001Sglebius SOCKET sock, 530290001Sglebius struct dns_ctx * dctx, 531290001Sglebius sent_pkt * spkt, 532290001Sglebius u_int xmt_delay 533290001Sglebius ) 534290001Sglebius{ 535290001Sglebius sockaddr_u * dest; 536290001Sglebius sent_pkt ** pkt_listp; 537290001Sglebius sent_pkt * match; 538290001Sglebius xmt_ctx * xctx; 539290001Sglebius struct timeval start_cb; 540290001Sglebius struct timeval delay; 541132451Sroberto 542290001Sglebius dest = &spkt->addr; 543290001Sglebius if (IS_IPV6(dest)) 544290001Sglebius pkt_listp = &v6_pkts_list; 545290001Sglebius else 546290001Sglebius pkt_listp = &v4_pkts_list; 547132451Sroberto 548290001Sglebius /* reject attempts to add address already listed */ 549290001Sglebius for (match = *pkt_listp; match != NULL; match = match->link) { 550290001Sglebius if (ADDR_PORT_EQ(&spkt->addr, &match->addr)) { 551290001Sglebius if (strcasecmp(spkt->dctx->name, 552290001Sglebius match->dctx->name)) 553290001Sglebius printf("%s %s duplicate address from %s ignored.\n", 554290001Sglebius sptoa(&match->addr), 555290001Sglebius match->dctx->name, 556290001Sglebius spkt->dctx->name); 557290001Sglebius else 558290001Sglebius printf("%s %s, duplicate address ignored.\n", 559290001Sglebius sptoa(&match->addr), 560290001Sglebius match->dctx->name); 561290001Sglebius dec_pending_ntp(spkt->dctx->name, &spkt->addr); 562290001Sglebius free(spkt); 563290001Sglebius return; 564290001Sglebius } 565290001Sglebius } 566132451Sroberto 567290001Sglebius LINK_SLIST(*pkt_listp, spkt, link); 568132451Sroberto 569290001Sglebius xctx = emalloc_zero(sizeof(*xctx)); 570290001Sglebius xctx->sock = sock; 571290001Sglebius xctx->spkt = spkt; 572290001Sglebius gettimeofday_cached(base, &start_cb); 573290001Sglebius xctx->sched = start_cb.tv_sec + (2 * xmt_delay); 574132451Sroberto 575290001Sglebius LINK_SORT_SLIST(xmt_q, xctx, (xctx->sched < L_S_S_CUR()->sched), 576290001Sglebius link, xmt_ctx); 577290001Sglebius if (xmt_q == xctx) { 578290001Sglebius /* 579290001Sglebius * The new entry is the first scheduled. The timer is 580290001Sglebius * either not active or is set for the second xmt 581290001Sglebius * context in xmt_q. 582290001Sglebius */ 583290001Sglebius if (NULL == ev_xmt_timer) 584290001Sglebius ev_xmt_timer = event_new(base, INVALID_SOCKET, 585290001Sglebius EV_TIMEOUT, 586290001Sglebius &xmt_timer_cb, NULL); 587290001Sglebius if (NULL == ev_xmt_timer) { 588290001Sglebius msyslog(LOG_ERR, 589290001Sglebius "queue_xmt: event_new(base, -1, EV_TIMEOUT) failed!"); 590290001Sglebius exit(1); 591290001Sglebius } 592290001Sglebius ZERO(delay); 593290001Sglebius if (xctx->sched > start_cb.tv_sec) 594290001Sglebius delay.tv_sec = xctx->sched - start_cb.tv_sec; 595290001Sglebius event_add(ev_xmt_timer, &delay); 596290001Sglebius TRACE(2, ("queue_xmt: xmt timer for %u usec\n", 597290001Sglebius (u_int)delay.tv_usec)); 598290001Sglebius } 599132451Sroberto} 600132451Sroberto 601132451Sroberto 602290001Sglebius/* 603290001Sglebius** xmt_timer_cb 604290001Sglebius*/ 605290001Sglebiusvoid 606290001Sglebiusxmt_timer_cb( 607290001Sglebius evutil_socket_t fd, 608290001Sglebius short what, 609290001Sglebius void * ctx 610290001Sglebius ) 611290001Sglebius{ 612290001Sglebius struct timeval start_cb; 613290001Sglebius struct timeval delay; 614290001Sglebius xmt_ctx * x; 615132451Sroberto 616290001Sglebius UNUSED_ARG(fd); 617290001Sglebius UNUSED_ARG(ctx); 618290001Sglebius DEBUG_INSIST(EV_TIMEOUT == what); 619132451Sroberto 620290001Sglebius if (NULL == xmt_q || shutting_down) 621290001Sglebius return; 622290001Sglebius gettimeofday_cached(base, &start_cb); 623290001Sglebius if (xmt_q->sched <= start_cb.tv_sec) { 624290001Sglebius UNLINK_HEAD_SLIST(x, xmt_q, link); 625290001Sglebius TRACE(2, ("xmt_timer_cb: at .%6.6u -> %s\n", 626290001Sglebius (u_int)start_cb.tv_usec, stoa(&x->spkt->addr))); 627290001Sglebius xmt(x); 628290001Sglebius free(x); 629290001Sglebius if (NULL == xmt_q) 630290001Sglebius return; 631290001Sglebius } 632290001Sglebius if (xmt_q->sched <= start_cb.tv_sec) { 633290001Sglebius event_add(ev_xmt_timer, &gap); 634290001Sglebius TRACE(2, ("xmt_timer_cb: at .%6.6u gap %6.6u\n", 635290001Sglebius (u_int)start_cb.tv_usec, 636290001Sglebius (u_int)gap.tv_usec)); 637290001Sglebius } else { 638290001Sglebius delay.tv_sec = xmt_q->sched - start_cb.tv_sec; 639290001Sglebius delay.tv_usec = 0; 640290001Sglebius event_add(ev_xmt_timer, &delay); 641290001Sglebius TRACE(2, ("xmt_timer_cb: at .%6.6u next %ld seconds\n", 642290001Sglebius (u_int)start_cb.tv_usec, 643290001Sglebius (long)delay.tv_sec)); 644290001Sglebius } 645132451Sroberto} 646132451Sroberto 647132451Sroberto 648290001Sglebius/* 649290001Sglebius** xmt() 650290001Sglebius*/ 651290001Sglebiusvoid 652290001Sglebiusxmt( 653290001Sglebius xmt_ctx * xctx 654290001Sglebius ) 655290001Sglebius{ 656290001Sglebius SOCKET sock = xctx->sock; 657290001Sglebius struct dns_ctx *dctx = xctx->spkt->dctx; 658290001Sglebius sent_pkt * spkt = xctx->spkt; 659290001Sglebius sockaddr_u * dst = &spkt->addr; 660290001Sglebius struct timeval tv_xmt; 661290001Sglebius struct pkt x_pkt; 662290001Sglebius size_t pkt_len; 663290001Sglebius int sent; 664132451Sroberto 665290001Sglebius if (0 != gettimeofday(&tv_xmt, NULL)) { 666290001Sglebius msyslog(LOG_ERR, 667290001Sglebius "xmt: gettimeofday() failed: %m"); 668290001Sglebius exit(1); 669290001Sglebius } 670290001Sglebius tv_xmt.tv_sec += JAN_1970; 671132451Sroberto 672290001Sglebius pkt_len = generate_pkt(&x_pkt, &tv_xmt, dctx->key_id, 673290001Sglebius dctx->key); 674132451Sroberto 675290001Sglebius sent = sendpkt(sock, dst, &x_pkt, pkt_len); 676290001Sglebius if (sent) { 677290001Sglebius /* Save the packet we sent... */ 678290001Sglebius memcpy(&spkt->x_pkt, &x_pkt, min(sizeof(spkt->x_pkt), 679290001Sglebius pkt_len)); 680290001Sglebius spkt->stime = tv_xmt.tv_sec - JAN_1970; 681132451Sroberto 682290001Sglebius TRACE(2, ("xmt: %lx.%6.6u %s %s\n", (u_long)tv_xmt.tv_sec, 683290001Sglebius (u_int)tv_xmt.tv_usec, dctx->name, stoa(dst))); 684290001Sglebius } else { 685290001Sglebius dec_pending_ntp(dctx->name, dst); 686290001Sglebius } 687132451Sroberto 688290001Sglebius return; 689290001Sglebius} 690132451Sroberto 691132451Sroberto 692290001Sglebius/* 693290001Sglebius * timeout_queries() -- give up on unrequited NTP queries 694290001Sglebius */ 695290001Sglebiusvoid 696290001Sglebiustimeout_queries(void) 697290001Sglebius{ 698290001Sglebius struct timeval start_cb; 699290001Sglebius u_int idx; 700290001Sglebius sent_pkt * head; 701290001Sglebius sent_pkt * spkt; 702290001Sglebius sent_pkt * spkt_next; 703290001Sglebius long age; 704290001Sglebius int didsomething = 0; 705132451Sroberto 706290001Sglebius TRACE(3, ("timeout_queries: called to check %u items\n", 707290001Sglebius (unsigned)COUNTOF(fam_listheads))); 708132451Sroberto 709290001Sglebius gettimeofday_cached(base, &start_cb); 710290001Sglebius for (idx = 0; idx < COUNTOF(fam_listheads); idx++) { 711290001Sglebius head = fam_listheads[idx]; 712290001Sglebius for (spkt = head; spkt != NULL; spkt = spkt_next) { 713290001Sglebius char xcst; 714132451Sroberto 715290001Sglebius didsomething = 1; 716290001Sglebius switch (spkt->dctx->flags & CTX_xCST) { 717290001Sglebius case CTX_BCST: 718290001Sglebius xcst = 'B'; 719290001Sglebius break; 720132451Sroberto 721290001Sglebius case CTX_UCST: 722290001Sglebius xcst = 'U'; 723290001Sglebius break; 724132451Sroberto 725290001Sglebius default: 726290001Sglebius INSIST(!"spkt->dctx->flags neither UCST nor BCST"); 727290001Sglebius break; 728290001Sglebius } 729132451Sroberto 730290001Sglebius spkt_next = spkt->link; 731290001Sglebius if (0 == spkt->stime || spkt->done) 732290001Sglebius continue; 733290001Sglebius age = start_cb.tv_sec - spkt->stime; 734290001Sglebius TRACE(3, ("%s %s %cCST age %ld\n", 735290001Sglebius stoa(&spkt->addr), 736290001Sglebius spkt->dctx->name, xcst, age)); 737290001Sglebius if (age > response_timeout) 738290001Sglebius timeout_query(spkt); 739290001Sglebius } 740290001Sglebius } 741290001Sglebius // Do we care about didsomething? 742290001Sglebius TRACE(3, ("timeout_queries: didsomething is %d, age is %ld\n", 743290001Sglebius didsomething, (long) (start_cb.tv_sec - start_tv.tv_sec))); 744290001Sglebius if (start_cb.tv_sec - start_tv.tv_sec > response_timeout) { 745290001Sglebius TRACE(3, ("timeout_queries: bail!\n")); 746290001Sglebius event_base_loopexit(base, NULL); 747290001Sglebius shutting_down = TRUE; 748290001Sglebius } 749132451Sroberto} 750132451Sroberto 751132451Sroberto 752290001Sglebiusvoid dec_pending_ntp( 753290001Sglebius const char * name, 754290001Sglebius sockaddr_u * server 755290001Sglebius ) 756290001Sglebius{ 757290001Sglebius if (n_pending_ntp > 0) { 758290001Sglebius --n_pending_ntp; 759290001Sglebius check_exit_conditions(); 760290001Sglebius } else { 761290001Sglebius INSIST(0 == n_pending_ntp); 762290001Sglebius TRACE(1, ("n_pending_ntp was zero before decrement for %s\n", 763290001Sglebius hostnameaddr(name, server))); 764290001Sglebius } 765290001Sglebius} 766132451Sroberto 767132451Sroberto 768290001Sglebiusvoid timeout_query( 769290001Sglebius sent_pkt * spkt 770290001Sglebius ) 771290001Sglebius{ 772290001Sglebius sockaddr_u * server; 773290001Sglebius char xcst; 774132451Sroberto 775132451Sroberto 776290001Sglebius switch (spkt->dctx->flags & CTX_xCST) { 777290001Sglebius case CTX_BCST: 778290001Sglebius xcst = 'B'; 779290001Sglebius break; 780132451Sroberto 781290001Sglebius case CTX_UCST: 782290001Sglebius xcst = 'U'; 783290001Sglebius break; 784132451Sroberto 785290001Sglebius default: 786290001Sglebius INSIST(!"spkt->dctx->flags neither UCST nor BCST"); 787290001Sglebius break; 788290001Sglebius } 789290001Sglebius spkt->done = TRUE; 790290001Sglebius server = &spkt->addr; 791290001Sglebius msyslog(LOG_INFO, "%s no %cCST response after %d seconds", 792290001Sglebius hostnameaddr(spkt->dctx->name, server), xcst, 793290001Sglebius response_timeout); 794290001Sglebius dec_pending_ntp(spkt->dctx->name, server); 795290001Sglebius return; 796290001Sglebius} 797132451Sroberto 798132451Sroberto 799290001Sglebius/* 800290001Sglebius** check_kod 801290001Sglebius*/ 802290001Sglebiusint 803290001Sglebiuscheck_kod( 804290001Sglebius const struct addrinfo * ai 805290001Sglebius ) 806290001Sglebius{ 807290001Sglebius char *hostname; 808290001Sglebius struct kod_entry *reason; 809132451Sroberto 810290001Sglebius /* Is there a KoD on file for this address? */ 811290001Sglebius hostname = addrinfo_to_str(ai); 812290001Sglebius TRACE(2, ("check_kod: checking <%s>\n", hostname)); 813290001Sglebius if (search_entry(hostname, &reason)) { 814290001Sglebius printf("prior KoD for %s, skipping.\n", 815290001Sglebius hostname); 816290001Sglebius free(reason); 817290001Sglebius free(hostname); 818132451Sroberto 819290001Sglebius return 1; 820290001Sglebius } 821290001Sglebius free(hostname); 822132451Sroberto 823290001Sglebius return 0; 824132451Sroberto} 825132451Sroberto 826132451Sroberto 827290001Sglebius/* 828290001Sglebius** Socket readable/timeout Callback: 829290001Sglebius** Read in the packet 830290001Sglebius** Unicast: 831290001Sglebius** - close socket 832290001Sglebius** - decrement n_pending_ntp 833290001Sglebius** - If packet is good, set the time and "exit" 834290001Sglebius** Broadcast: 835290001Sglebius** - If packet is good, set the time and "exit" 836290001Sglebius*/ 837290001Sglebiusvoid 838290001Sglebiussock_cb( 839290001Sglebius evutil_socket_t fd, 840290001Sglebius short what, 841290001Sglebius void *ptr 842290001Sglebius ) 843290001Sglebius{ 844290001Sglebius sockaddr_u sender; 845290001Sglebius sockaddr_u * psau; 846290001Sglebius sent_pkt ** p_pktlist; 847290001Sglebius sent_pkt * spkt; 848290001Sglebius int rpktl; 849290001Sglebius int rc; 850132451Sroberto 851290001Sglebius INSIST(sock4 == fd || sock6 == fd); 852132451Sroberto 853290001Sglebius TRACE(3, ("sock_cb: event on sock%s:%s%s%s%s\n", 854290001Sglebius (fd == sock6) 855290001Sglebius ? "6" 856290001Sglebius : "4", 857290001Sglebius (what & EV_TIMEOUT) ? " timeout" : "", 858290001Sglebius (what & EV_READ) ? " read" : "", 859290001Sglebius (what & EV_WRITE) ? " write" : "", 860290001Sglebius (what & EV_SIGNAL) ? " signal" : "")); 861132451Sroberto 862290001Sglebius if (!(EV_READ & what)) { 863290001Sglebius if (EV_TIMEOUT & what) 864290001Sglebius timeout_queries(); 865132451Sroberto 866290001Sglebius return; 867290001Sglebius } 868132451Sroberto 869290001Sglebius /* Read in the packet */ 870290001Sglebius rpktl = recvdata(fd, &sender, &rbuf, sizeof(rbuf)); 871290001Sglebius if (rpktl < 0) { 872290001Sglebius msyslog(LOG_DEBUG, "recvfrom error %m"); 873290001Sglebius return; 874290001Sglebius } 875132451Sroberto 876290001Sglebius if (sock6 == fd) 877290001Sglebius p_pktlist = &v6_pkts_list; 878290001Sglebius else 879290001Sglebius p_pktlist = &v4_pkts_list; 880132451Sroberto 881290001Sglebius for (spkt = *p_pktlist; spkt != NULL; spkt = spkt->link) { 882290001Sglebius psau = &spkt->addr; 883290001Sglebius if (SOCK_EQ(&sender, psau)) 884290001Sglebius break; 885290001Sglebius } 886290001Sglebius if (NULL == spkt) { 887290001Sglebius msyslog(LOG_WARNING, 888290001Sglebius "Packet from unexpected source %s dropped", 889290001Sglebius sptoa(&sender)); 890290001Sglebius return; 891290001Sglebius } 892132451Sroberto 893290001Sglebius TRACE(1, ("sock_cb: %s %s\n", spkt->dctx->name, 894290001Sglebius sptoa(&sender))); 895132451Sroberto 896290001Sglebius rpktl = process_pkt(&r_pkt, &sender, rpktl, MODE_SERVER, 897290001Sglebius &spkt->x_pkt, "sock_cb"); 898132451Sroberto 899290001Sglebius TRACE(2, ("sock_cb: process_pkt returned %d\n", rpktl)); 900132451Sroberto 901290001Sglebius /* If this is a Unicast packet, one down ... */ 902290001Sglebius if (!spkt->done && (CTX_UCST & spkt->dctx->flags)) { 903290001Sglebius dec_pending_ntp(spkt->dctx->name, &spkt->addr); 904290001Sglebius spkt->done = TRUE; 905290001Sglebius } 906132451Sroberto 907132451Sroberto 908290001Sglebius /* If the packet is good, set the time and we're all done */ 909290001Sglebius rc = handle_pkt(rpktl, &r_pkt, &spkt->addr, spkt->dctx->name); 910290001Sglebius if (0 != rc) 911290001Sglebius TRACE(1, ("sock_cb: handle_pkt() returned %d\n", rc)); 912290001Sglebius check_exit_conditions(); 913290001Sglebius} 914132451Sroberto 915132451Sroberto 916290001Sglebius/* 917290001Sglebius * check_exit_conditions() 918290001Sglebius * 919290001Sglebius * If sntp has a reply, ask the event loop to stop after this round of 920290001Sglebius * callbacks, unless --wait was used. 921290001Sglebius */ 922290001Sglebiusvoid 923290001Sglebiuscheck_exit_conditions(void) 924290001Sglebius{ 925290001Sglebius if ((0 == n_pending_ntp && 0 == n_pending_dns) || 926290001Sglebius (time_derived && !HAVE_OPT(WAIT))) { 927290001Sglebius event_base_loopexit(base, NULL); 928290001Sglebius shutting_down = TRUE; 929290001Sglebius } else { 930290001Sglebius TRACE(2, ("%d NTP and %d name queries pending\n", 931290001Sglebius n_pending_ntp, n_pending_dns)); 932290001Sglebius } 933290001Sglebius} 934132451Sroberto 935132451Sroberto 936290001Sglebius/* 937290001Sglebius * sntp_addremove_fd() is invoked by the intres blocking worker code 938290001Sglebius * to read from a pipe, or to stop same. 939290001Sglebius */ 940290001Sglebiusvoid sntp_addremove_fd( 941290001Sglebius int fd, 942290001Sglebius int is_pipe, 943290001Sglebius int remove_it 944290001Sglebius ) 945290001Sglebius{ 946290001Sglebius u_int idx; 947290001Sglebius blocking_child *c; 948290001Sglebius struct event * ev; 949132451Sroberto 950290001Sglebius#ifdef HAVE_SOCKETPAIR 951290001Sglebius if (is_pipe) { 952290001Sglebius /* sntp only asks for EV_FEATURE_FDS without HAVE_SOCKETPAIR */ 953290001Sglebius msyslog(LOG_ERR, "fatal: pipes not supported on systems with socketpair()"); 954290001Sglebius exit(1); 955290001Sglebius } 956290001Sglebius#endif 957132451Sroberto 958290001Sglebius c = NULL; 959290001Sglebius for (idx = 0; idx < blocking_children_alloc; idx++) { 960290001Sglebius c = blocking_children[idx]; 961290001Sglebius if (NULL == c) 962290001Sglebius continue; 963290001Sglebius if (fd == c->resp_read_pipe) 964290001Sglebius break; 965290001Sglebius } 966290001Sglebius if (idx == blocking_children_alloc) 967290001Sglebius return; 968132451Sroberto 969290001Sglebius if (remove_it) { 970290001Sglebius ev = c->resp_read_ctx; 971290001Sglebius c->resp_read_ctx = NULL; 972290001Sglebius event_del(ev); 973290001Sglebius event_free(ev); 974132451Sroberto 975290001Sglebius return; 976290001Sglebius } 977132451Sroberto 978290001Sglebius ev = event_new(base, fd, EV_READ | EV_PERSIST, 979290001Sglebius &worker_resp_cb, c); 980290001Sglebius if (NULL == ev) { 981290001Sglebius msyslog(LOG_ERR, 982290001Sglebius "sntp_addremove_fd: event_new(base, fd) failed!"); 983290001Sglebius return; 984290001Sglebius } 985290001Sglebius c->resp_read_ctx = ev; 986290001Sglebius event_add(ev, NULL); 987290001Sglebius} 988132451Sroberto 989132451Sroberto 990290001Sglebius/* called by forked intres child to close open descriptors */ 991290001Sglebius#ifdef WORK_FORK 992290001Sglebiusvoid 993290001Sglebiuskill_asyncio( 994290001Sglebius int startfd 995290001Sglebius ) 996290001Sglebius{ 997290001Sglebius if (INVALID_SOCKET != sock4) { 998290001Sglebius closesocket(sock4); 999290001Sglebius sock4 = INVALID_SOCKET; 1000290001Sglebius } 1001290001Sglebius if (INVALID_SOCKET != sock6) { 1002290001Sglebius closesocket(sock6); 1003290001Sglebius sock6 = INVALID_SOCKET; 1004290001Sglebius } 1005290001Sglebius if (INVALID_SOCKET != bsock4) { 1006290001Sglebius closesocket(sock4); 1007290001Sglebius sock4 = INVALID_SOCKET; 1008290001Sglebius } 1009290001Sglebius if (INVALID_SOCKET != bsock6) { 1010290001Sglebius closesocket(sock6); 1011290001Sglebius sock6 = INVALID_SOCKET; 1012290001Sglebius } 1013290001Sglebius} 1014290001Sglebius#endif 1015132451Sroberto 1016132451Sroberto 1017290001Sglebius/* 1018290001Sglebius * worker_resp_cb() is invoked when resp_read_pipe is readable. 1019290001Sglebius */ 1020290001Sglebiusvoid 1021290001Sglebiusworker_resp_cb( 1022290001Sglebius evutil_socket_t fd, 1023290001Sglebius short what, 1024290001Sglebius void * ctx /* blocking_child * */ 1025290001Sglebius ) 1026290001Sglebius{ 1027290001Sglebius blocking_child * c; 1028132451Sroberto 1029290001Sglebius DEBUG_INSIST(EV_READ & what); 1030290001Sglebius c = ctx; 1031290001Sglebius DEBUG_INSIST(fd == c->resp_read_pipe); 1032290001Sglebius process_blocking_resp(c); 1033132451Sroberto} 1034132451Sroberto 1035132451Sroberto 1036290001Sglebius/* 1037290001Sglebius * intres_timeout_req(s) is invoked in the parent to schedule an idle 1038290001Sglebius * timeout to fire in s seconds, if not reset earlier by a call to 1039290001Sglebius * intres_timeout_req(0), which clears any pending timeout. When the 1040290001Sglebius * timeout expires, worker_idle_timer_fired() is invoked (again, in the 1041290001Sglebius * parent). 1042290001Sglebius * 1043290001Sglebius * sntp and ntpd each provide implementations adapted to their timers. 1044290001Sglebius */ 1045290001Sglebiusvoid 1046290001Sglebiusintres_timeout_req( 1047290001Sglebius u_int seconds /* 0 cancels */ 1048290001Sglebius ) 1049290001Sglebius{ 1050290001Sglebius struct timeval tv_to; 1051132451Sroberto 1052290001Sglebius if (NULL == ev_worker_timeout) { 1053290001Sglebius ev_worker_timeout = event_new(base, -1, 1054290001Sglebius EV_TIMEOUT | EV_PERSIST, 1055290001Sglebius &worker_timeout, NULL); 1056290001Sglebius DEBUG_INSIST(NULL != ev_worker_timeout); 1057290001Sglebius } else { 1058290001Sglebius event_del(ev_worker_timeout); 1059290001Sglebius } 1060290001Sglebius if (0 == seconds) 1061290001Sglebius return; 1062290001Sglebius tv_to.tv_sec = seconds; 1063290001Sglebius tv_to.tv_usec = 0; 1064290001Sglebius event_add(ev_worker_timeout, &tv_to); 1065290001Sglebius} 1066132451Sroberto 1067132451Sroberto 1068290001Sglebiusvoid 1069290001Sglebiusworker_timeout( 1070290001Sglebius evutil_socket_t fd, 1071290001Sglebius short what, 1072290001Sglebius void * ctx 1073290001Sglebius ) 1074290001Sglebius{ 1075290001Sglebius UNUSED_ARG(fd); 1076290001Sglebius UNUSED_ARG(ctx); 1077132451Sroberto 1078290001Sglebius DEBUG_REQUIRE(EV_TIMEOUT & what); 1079290001Sglebius worker_idle_timer_fired(); 1080132451Sroberto} 1081132451Sroberto 1082132451Sroberto 1083290001Sglebiusvoid 1084290001Sglebiussntp_libevent_log_cb( 1085290001Sglebius int severity, 1086290001Sglebius const char * msg 1087290001Sglebius ) 1088290001Sglebius{ 1089290001Sglebius int level; 1090132451Sroberto 1091290001Sglebius switch (severity) { 1092132451Sroberto 1093290001Sglebius default: 1094290001Sglebius case _EVENT_LOG_DEBUG: 1095290001Sglebius level = LOG_DEBUG; 1096290001Sglebius break; 1097132451Sroberto 1098290001Sglebius case _EVENT_LOG_MSG: 1099290001Sglebius level = LOG_NOTICE; 1100290001Sglebius break; 1101132451Sroberto 1102290001Sglebius case _EVENT_LOG_WARN: 1103290001Sglebius level = LOG_WARNING; 1104290001Sglebius break; 1105132451Sroberto 1106290001Sglebius case _EVENT_LOG_ERR: 1107290001Sglebius level = LOG_ERR; 1108290001Sglebius break; 1109290001Sglebius } 1110132451Sroberto 1111290001Sglebius msyslog(level, "%s", msg); 1112290001Sglebius} 1113132451Sroberto 1114132451Sroberto 1115290001Sglebiusint 1116290001Sglebiusgenerate_pkt ( 1117290001Sglebius struct pkt *x_pkt, 1118290001Sglebius const struct timeval *tv_xmt, 1119290001Sglebius int key_id, 1120290001Sglebius struct key *pkt_key 1121290001Sglebius ) 1122290001Sglebius{ 1123290001Sglebius l_fp xmt_fp; 1124290001Sglebius int pkt_len; 1125290001Sglebius int mac_size; 1126132451Sroberto 1127290001Sglebius pkt_len = LEN_PKT_NOMAC; 1128290001Sglebius ZERO(*x_pkt); 1129290001Sglebius TVTOTS(tv_xmt, &xmt_fp); 1130290001Sglebius HTONL_FP(&xmt_fp, &x_pkt->xmt); 1131290001Sglebius x_pkt->stratum = STRATUM_TO_PKT(STRATUM_UNSPEC); 1132290001Sglebius x_pkt->ppoll = 8; 1133290001Sglebius /* FIXME! Modus broadcast + adr. check -> bdr. pkt */ 1134290001Sglebius set_li_vn_mode(x_pkt, LEAP_NOTINSYNC, ntpver, 3); 1135290001Sglebius if (pkt_key != NULL) { 1136290001Sglebius x_pkt->exten[0] = htonl(key_id); 1137290001Sglebius mac_size = 20; /* max room for MAC */ 1138294905Sdelphij mac_size = make_mac(x_pkt, pkt_len, mac_size, 1139290001Sglebius pkt_key, (char *)&x_pkt->exten[1]); 1140290001Sglebius if (mac_size > 0) 1141290001Sglebius pkt_len += mac_size + 4; 1142290001Sglebius } 1143290001Sglebius return pkt_len; 1144290001Sglebius} 1145132451Sroberto 1146132451Sroberto 1147290001Sglebiusint 1148290001Sglebiushandle_pkt( 1149290001Sglebius int rpktl, 1150290001Sglebius struct pkt * rpkt, 1151290001Sglebius sockaddr_u * host, 1152290001Sglebius const char * hostname 1153290001Sglebius ) 1154290001Sglebius{ 1155290001Sglebius char disptxt[32]; 1156290001Sglebius const char * addrtxt; 1157290001Sglebius struct timeval tv_dst; 1158290001Sglebius int cnt; 1159290001Sglebius int sw_case; 1160290001Sglebius int digits; 1161290001Sglebius int stratum; 1162290001Sglebius char * ref; 1163290001Sglebius char * ts_str; 1164290001Sglebius const char * leaptxt; 1165290001Sglebius double offset; 1166290001Sglebius double precision; 1167290001Sglebius double synch_distance; 1168290001Sglebius char * p_SNTP_PRETEND_TIME; 1169290001Sglebius time_t pretend_time; 1170290001Sglebius#if SIZEOF_TIME_T == 8 1171290001Sglebius long long ll; 1172290001Sglebius#else 1173290001Sglebius long l; 1174290001Sglebius#endif 1175132451Sroberto 1176290001Sglebius ts_str = NULL; 1177132451Sroberto 1178290001Sglebius if (rpktl > 0) 1179290001Sglebius sw_case = 1; 1180290001Sglebius else 1181290001Sglebius sw_case = rpktl; 1182132451Sroberto 1183290001Sglebius switch (sw_case) { 1184132451Sroberto 1185290001Sglebius case SERVER_UNUSEABLE: 1186290001Sglebius return -1; 1187290001Sglebius break; 1188132451Sroberto 1189290001Sglebius case PACKET_UNUSEABLE: 1190290001Sglebius break; 1191132451Sroberto 1192290001Sglebius case SERVER_AUTH_FAIL: 1193290001Sglebius break; 1194132451Sroberto 1195290001Sglebius case KOD_DEMOBILIZE: 1196290001Sglebius /* Received a DENY or RESTR KOD packet */ 1197290001Sglebius addrtxt = stoa(host); 1198290001Sglebius ref = (char *)&rpkt->refid; 1199290001Sglebius add_entry(addrtxt, ref); 1200290001Sglebius msyslog(LOG_WARNING, "KOD code %c%c%c%c from %s %s", 1201290001Sglebius ref[0], ref[1], ref[2], ref[3], addrtxt, hostname); 1202290001Sglebius break; 1203132451Sroberto 1204290001Sglebius case KOD_RATE: 1205290001Sglebius /* 1206290001Sglebius ** Hmm... 1207290001Sglebius ** We should probably call add_entry() with an 1208290001Sglebius ** expiration timestamp of several seconds in the future, 1209290001Sglebius ** and back-off even more if we get more RATE responses. 1210290001Sglebius */ 1211290001Sglebius break; 1212132451Sroberto 1213290001Sglebius case 1: 1214290001Sglebius TRACE(3, ("handle_pkt: %d bytes from %s %s\n", 1215290001Sglebius rpktl, stoa(host), hostname)); 1216132451Sroberto 1217290001Sglebius gettimeofday_cached(base, &tv_dst); 1218132451Sroberto 1219290001Sglebius p_SNTP_PRETEND_TIME = getenv("SNTP_PRETEND_TIME"); 1220290001Sglebius if (p_SNTP_PRETEND_TIME) { 1221290001Sglebius pretend_time = 0; 1222290001Sglebius#if SIZEOF_TIME_T == 4 1223290001Sglebius if (1 == sscanf(p_SNTP_PRETEND_TIME, "%ld", &l)) 1224290001Sglebius pretend_time = (time_t)l; 1225290001Sglebius#elif SIZEOF_TIME_T == 8 1226290001Sglebius if (1 == sscanf(p_SNTP_PRETEND_TIME, "%lld", &ll)) 1227290001Sglebius pretend_time = (time_t)ll; 1228290001Sglebius#else 1229290001Sglebius# include "GRONK: unexpected value for SIZEOF_TIME_T" 1230290001Sglebius#endif 1231290001Sglebius if (0 != pretend_time) 1232290001Sglebius tv_dst.tv_sec = pretend_time; 1233290001Sglebius } 1234132451Sroberto 1235290001Sglebius offset_calculation(rpkt, rpktl, &tv_dst, &offset, 1236290001Sglebius &precision, &synch_distance); 1237290001Sglebius time_derived = TRUE; 1238132451Sroberto 1239290001Sglebius for (digits = 0; (precision *= 10.) < 1.; ++digits) 1240290001Sglebius /* empty */ ; 1241290001Sglebius if (digits > 6) 1242290001Sglebius digits = 6; 1243132451Sroberto 1244290001Sglebius ts_str = tv_to_str(&tv_dst); 1245290001Sglebius stratum = rpkt->stratum; 1246290001Sglebius if (0 == stratum) 1247290001Sglebius stratum = 16; 1248132451Sroberto 1249290001Sglebius if (synch_distance > 0.) { 1250290001Sglebius cnt = snprintf(disptxt, sizeof(disptxt), 1251290001Sglebius " +/- %f", synch_distance); 1252290001Sglebius if ((size_t)cnt >= sizeof(disptxt)) 1253290001Sglebius snprintf(disptxt, sizeof(disptxt), 1254290001Sglebius "ERROR %d >= %d", cnt, 1255290001Sglebius (int)sizeof(disptxt)); 1256290001Sglebius } else { 1257290001Sglebius disptxt[0] = '\0'; 1258290001Sglebius } 1259132451Sroberto 1260290001Sglebius switch (PKT_LEAP(rpkt->li_vn_mode)) { 1261290001Sglebius case LEAP_NOWARNING: 1262290001Sglebius leaptxt = "no-leap"; 1263290001Sglebius break; 1264290001Sglebius case LEAP_ADDSECOND: 1265290001Sglebius leaptxt = "add-leap"; 1266290001Sglebius break; 1267290001Sglebius case LEAP_DELSECOND: 1268290001Sglebius leaptxt = "del-leap"; 1269290001Sglebius break; 1270290001Sglebius case LEAP_NOTINSYNC: 1271290001Sglebius leaptxt = "unsync"; 1272290001Sglebius break; 1273290001Sglebius default: 1274290001Sglebius leaptxt = "LEAP-ERROR"; 1275290001Sglebius break; 1276290001Sglebius } 1277132451Sroberto 1278290001Sglebius msyslog(LOG_INFO, "%s %+.*f%s %s s%d %s%s", ts_str, 1279290001Sglebius digits, offset, disptxt, 1280290001Sglebius hostnameaddr(hostname, host), stratum, 1281290001Sglebius leaptxt, 1282290001Sglebius (time_adjusted) 1283290001Sglebius ? " [excess]" 1284290001Sglebius : ""); 1285290001Sglebius free(ts_str); 1286132451Sroberto 1287290001Sglebius if (p_SNTP_PRETEND_TIME) 1288290001Sglebius return 0; 1289132451Sroberto 1290290001Sglebius if (!time_adjusted && 1291290001Sglebius (ENABLED_OPT(STEP) || ENABLED_OPT(SLEW))) 1292290001Sglebius return set_time(offset); 1293132451Sroberto 1294290001Sglebius return EX_OK; 1295290001Sglebius } 1296132451Sroberto 1297290001Sglebius return 1; 1298290001Sglebius} 1299132451Sroberto 1300132451Sroberto 1301290001Sglebiusvoid 1302290001Sglebiusoffset_calculation( 1303290001Sglebius struct pkt *rpkt, 1304290001Sglebius int rpktl, 1305290001Sglebius struct timeval *tv_dst, 1306290001Sglebius double *offset, 1307290001Sglebius double *precision, 1308290001Sglebius double *synch_distance 1309290001Sglebius ) 1310290001Sglebius{ 1311290001Sglebius l_fp p_rec, p_xmt, p_ref, p_org, tmp, dst; 1312290001Sglebius u_fp p_rdly, p_rdsp; 1313290001Sglebius double t21, t34, delta; 1314132451Sroberto 1315290001Sglebius /* Convert timestamps from network to host byte order */ 1316290001Sglebius p_rdly = NTOHS_FP(rpkt->rootdelay); 1317290001Sglebius p_rdsp = NTOHS_FP(rpkt->rootdisp); 1318290001Sglebius NTOHL_FP(&rpkt->reftime, &p_ref); 1319290001Sglebius NTOHL_FP(&rpkt->org, &p_org); 1320290001Sglebius NTOHL_FP(&rpkt->rec, &p_rec); 1321290001Sglebius NTOHL_FP(&rpkt->xmt, &p_xmt); 1322132451Sroberto 1323290001Sglebius *precision = LOGTOD(rpkt->precision); 1324132451Sroberto 1325290001Sglebius TRACE(3, ("offset_calculation: LOGTOD(rpkt->precision): %f\n", *precision)); 1326132451Sroberto 1327290001Sglebius /* Compute offset etc. */ 1328290001Sglebius tmp = p_rec; 1329290001Sglebius L_SUB(&tmp, &p_org); 1330290001Sglebius LFPTOD(&tmp, t21); 1331290001Sglebius TVTOTS(tv_dst, &dst); 1332290001Sglebius dst.l_ui += JAN_1970; 1333290001Sglebius tmp = p_xmt; 1334290001Sglebius L_SUB(&tmp, &dst); 1335290001Sglebius LFPTOD(&tmp, t34); 1336290001Sglebius *offset = (t21 + t34) / 2.; 1337290001Sglebius delta = t21 - t34; 1338132451Sroberto 1339290001Sglebius // synch_distance is: 1340290001Sglebius // (peer->delay + peer->rootdelay) / 2 + peer->disp 1341290001Sglebius // + peer->rootdisp + clock_phi * (current_time - peer->update) 1342290001Sglebius // + peer->jitter; 1343290001Sglebius // 1344290001Sglebius // and peer->delay = fabs(peer->offset - p_offset) * 2; 1345290001Sglebius // and peer->offset needs history, so we're left with 1346290001Sglebius // p_offset = (t21 + t34) / 2.; 1347290001Sglebius // peer->disp = 0; (we have no history to augment this) 1348290001Sglebius // clock_phi = 15e-6; 1349290001Sglebius // peer->jitter = LOGTOD(sys_precision); (we have no history to augment this) 1350290001Sglebius // and ntp_proto.c:set_sys_tick_precision() should get us sys_precision. 1351290001Sglebius // 1352290001Sglebius // so our answer seems to be: 1353290001Sglebius // 1354290001Sglebius // (fabs(t21 + t34) + peer->rootdelay) / 3. 1355290001Sglebius // + 0 (peer->disp) 1356290001Sglebius // + peer->rootdisp 1357290001Sglebius // + 15e-6 (clock_phi) 1358290001Sglebius // + LOGTOD(sys_precision) 1359132451Sroberto 1360290001Sglebius INSIST( FPTOD(p_rdly) >= 0. ); 1361290001Sglebius#if 1 1362290001Sglebius *synch_distance = (fabs(t21 + t34) + FPTOD(p_rdly)) / 3. 1363290001Sglebius + 0. 1364290001Sglebius + FPTOD(p_rdsp) 1365290001Sglebius + 15e-6 1366290001Sglebius + 0. /* LOGTOD(sys_precision) when we can get it */ 1367290001Sglebius ; 1368290001Sglebius INSIST( *synch_distance >= 0. ); 1369290001Sglebius#else 1370290001Sglebius *synch_distance = (FPTOD(p_rdly) + FPTOD(p_rdsp))/2.0; 1371290001Sglebius#endif 1372132451Sroberto 1373290001Sglebius#ifdef DEBUG 1374290001Sglebius if (debug > 3) { 1375290001Sglebius printf("sntp rootdelay: %f\n", FPTOD(p_rdly)); 1376290001Sglebius printf("sntp rootdisp: %f\n", FPTOD(p_rdsp)); 1377290001Sglebius printf("sntp syncdist: %f\n", *synch_distance); 1378132451Sroberto 1379290001Sglebius pkt_output(rpkt, rpktl, stdout); 1380132451Sroberto 1381290001Sglebius printf("sntp offset_calculation: rpkt->reftime:\n"); 1382290001Sglebius l_fp_output(&p_ref, stdout); 1383290001Sglebius printf("sntp offset_calculation: rpkt->org:\n"); 1384290001Sglebius l_fp_output(&p_org, stdout); 1385290001Sglebius printf("sntp offset_calculation: rpkt->rec:\n"); 1386290001Sglebius l_fp_output(&p_rec, stdout); 1387290001Sglebius printf("sntp offset_calculation: rpkt->xmt:\n"); 1388290001Sglebius l_fp_output(&p_xmt, stdout); 1389290001Sglebius } 1390290001Sglebius#endif 1391132451Sroberto 1392290001Sglebius TRACE(3, ("sntp offset_calculation:\trec - org t21: %.6f\n" 1393290001Sglebius "\txmt - dst t34: %.6f\tdelta: %.6f\toffset: %.6f\n", 1394290001Sglebius t21, t34, delta, *offset)); 1395132451Sroberto 1396290001Sglebius return; 1397290001Sglebius} 1398132451Sroberto 1399132451Sroberto 1400132451Sroberto 1401290001Sglebius/* Compute the 8 bits for li_vn_mode */ 1402290001Sglebiusvoid 1403290001Sglebiusset_li_vn_mode ( 1404290001Sglebius struct pkt *spkt, 1405290001Sglebius char leap, 1406290001Sglebius char version, 1407290001Sglebius char mode 1408290001Sglebius ) 1409290001Sglebius{ 1410290001Sglebius if (leap > 3) { 1411290001Sglebius msyslog(LOG_DEBUG, "set_li_vn_mode: leap > 3, using max. 3"); 1412290001Sglebius leap = 3; 1413290001Sglebius } 1414132451Sroberto 1415290001Sglebius if ((unsigned char)version > 7) { 1416290001Sglebius msyslog(LOG_DEBUG, "set_li_vn_mode: version < 0 or > 7, using 4"); 1417290001Sglebius version = 4; 1418290001Sglebius } 1419132451Sroberto 1420290001Sglebius if (mode > 7) { 1421290001Sglebius msyslog(LOG_DEBUG, "set_li_vn_mode: mode > 7, using client mode 3"); 1422290001Sglebius mode = 3; 1423290001Sglebius } 1424132451Sroberto 1425290001Sglebius spkt->li_vn_mode = leap << 6; 1426290001Sglebius spkt->li_vn_mode |= version << 3; 1427290001Sglebius spkt->li_vn_mode |= mode; 1428132451Sroberto} 1429132451Sroberto 1430132451Sroberto 1431290001Sglebius/* 1432290001Sglebius** set_time applies 'offset' to the local clock. 1433290001Sglebius*/ 1434290001Sglebiusint 1435290001Sglebiusset_time( 1436290001Sglebius double offset 1437290001Sglebius ) 1438290001Sglebius{ 1439290001Sglebius int rc; 1440132451Sroberto 1441290001Sglebius if (time_adjusted) 1442290001Sglebius return EX_OK; 1443132451Sroberto 1444290001Sglebius /* 1445290001Sglebius ** If we can step but we cannot slew, then step. 1446290001Sglebius ** If we can step or slew and and |offset| > steplimit, then step. 1447290001Sglebius */ 1448290001Sglebius if (ENABLED_OPT(STEP) && 1449290001Sglebius ( !ENABLED_OPT(SLEW) 1450290001Sglebius || (ENABLED_OPT(SLEW) && (fabs(offset) > steplimit)) 1451290001Sglebius )) { 1452290001Sglebius rc = step_systime(offset); 1453132451Sroberto 1454290001Sglebius /* If there was a problem, can we rely on errno? */ 1455290001Sglebius if (1 == rc) 1456290001Sglebius time_adjusted = TRUE; 1457290001Sglebius return (time_adjusted) 1458290001Sglebius ? EX_OK 1459290001Sglebius : 1; 1460290001Sglebius /* 1461290001Sglebius ** In case of error, what should we use? 1462290001Sglebius ** EX_UNAVAILABLE? 1463290001Sglebius ** EX_OSERR? 1464290001Sglebius ** EX_NOPERM? 1465290001Sglebius */ 1466290001Sglebius } 1467132451Sroberto 1468290001Sglebius if (ENABLED_OPT(SLEW)) { 1469290001Sglebius rc = adj_systime(offset); 1470132451Sroberto 1471290001Sglebius /* If there was a problem, can we rely on errno? */ 1472290001Sglebius if (1 == rc) 1473290001Sglebius time_adjusted = TRUE; 1474290001Sglebius return (time_adjusted) 1475290001Sglebius ? EX_OK 1476290001Sglebius : 1; 1477290001Sglebius /* 1478290001Sglebius ** In case of error, what should we use? 1479290001Sglebius ** EX_UNAVAILABLE? 1480290001Sglebius ** EX_OSERR? 1481290001Sglebius ** EX_NOPERM? 1482290001Sglebius */ 1483290001Sglebius } 1484132451Sroberto 1485290001Sglebius return EX_SOFTWARE; 1486290001Sglebius} 1487132451Sroberto 1488132451Sroberto 1489290001Sglebiusint 1490290001Sglebiuslibevent_version_ok(void) 1491290001Sglebius{ 1492290001Sglebius ev_uint32_t v_compile_maj; 1493290001Sglebius ev_uint32_t v_run_maj; 1494132451Sroberto 1495290001Sglebius v_compile_maj = LIBEVENT_VERSION_NUMBER & 0xffff0000; 1496290001Sglebius v_run_maj = event_get_version_number() & 0xffff0000; 1497290001Sglebius if (v_compile_maj != v_run_maj) { 1498290001Sglebius fprintf(stderr, 1499290001Sglebius "Incompatible libevent versions: have %s, built with %s\n", 1500290001Sglebius event_get_version(), 1501290001Sglebius LIBEVENT_VERSION); 1502290001Sglebius return 0; 1503290001Sglebius } 1504290001Sglebius return 1; 1505290001Sglebius} 1506132451Sroberto 1507290001Sglebius/* 1508290001Sglebius * gettimeofday_cached() 1509290001Sglebius * 1510290001Sglebius * Clones the event_base_gettimeofday_cached() interface but ensures the 1511290001Sglebius * times are always on the gettimeofday() 1970 scale. Older libevent 2 1512290001Sglebius * sometimes used gettimeofday(), sometimes the since-system-start 1513290001Sglebius * clock_gettime(CLOCK_MONOTONIC), depending on the platform. 1514290001Sglebius * 1515290001Sglebius * It is not cleanly possible to tell which timescale older libevent is 1516290001Sglebius * using. 1517290001Sglebius * 1518290001Sglebius * The strategy involves 1 hour thresholds chosen to be far longer than 1519290001Sglebius * the duration of a round of libevent callbacks, which share a cached 1520290001Sglebius * start-of-round time. First compare the last cached time with the 1521290001Sglebius * current gettimeofday() time. If they are within one hour, libevent 1522290001Sglebius * is using the proper timescale so leave the offset 0. Otherwise, 1523290001Sglebius * compare libevent's cached time and the current time on the monotonic 1524290001Sglebius * scale. If they are within an hour, libevent is using the monotonic 1525290001Sglebius * scale so calculate the offset to add to such times to bring them to 1526290001Sglebius * gettimeofday()'s scale. 1527290001Sglebius */ 1528290001Sglebiusint 1529290001Sglebiusgettimeofday_cached( 1530290001Sglebius struct event_base * b, 1531290001Sglebius struct timeval * caller_tv 1532290001Sglebius ) 1533290001Sglebius{ 1534290001Sglebius#if defined(_EVENT_HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) 1535290001Sglebius static struct event_base * cached_b; 1536290001Sglebius static struct timeval cached; 1537290001Sglebius static struct timeval adj_cached; 1538290001Sglebius static struct timeval offset; 1539290001Sglebius static int offset_ready; 1540290001Sglebius struct timeval latest; 1541290001Sglebius struct timeval systemt; 1542290001Sglebius struct timespec ts; 1543290001Sglebius struct timeval mono; 1544290001Sglebius struct timeval diff; 1545290001Sglebius int cgt_rc; 1546290001Sglebius int gtod_rc; 1547132451Sroberto 1548290001Sglebius event_base_gettimeofday_cached(b, &latest); 1549290001Sglebius if (b == cached_b && 1550290001Sglebius !memcmp(&latest, &cached, sizeof(latest))) { 1551290001Sglebius *caller_tv = adj_cached; 1552290001Sglebius return 0; 1553290001Sglebius } 1554290001Sglebius cached = latest; 1555290001Sglebius cached_b = b; 1556290001Sglebius if (!offset_ready) { 1557290001Sglebius cgt_rc = clock_gettime(CLOCK_MONOTONIC, &ts); 1558290001Sglebius gtod_rc = gettimeofday(&systemt, NULL); 1559290001Sglebius if (0 != gtod_rc) { 1560290001Sglebius msyslog(LOG_ERR, 1561290001Sglebius "%s: gettimeofday() error %m", 1562290001Sglebius progname); 1563290001Sglebius exit(1); 1564290001Sglebius } 1565290001Sglebius diff = sub_tval(systemt, latest); 1566290001Sglebius if (debug > 1) 1567290001Sglebius printf("system minus cached %+ld.%06ld\n", 1568290001Sglebius (long)diff.tv_sec, (long)diff.tv_usec); 1569290001Sglebius if (0 != cgt_rc || labs((long)diff.tv_sec) < 3600) { 1570290001Sglebius /* 1571290001Sglebius * Either use_monotonic == 0, or this libevent 1572290001Sglebius * has been repaired. Leave offset at zero. 1573290001Sglebius */ 1574290001Sglebius } else { 1575290001Sglebius mono.tv_sec = ts.tv_sec; 1576290001Sglebius mono.tv_usec = ts.tv_nsec / 1000; 1577290001Sglebius diff = sub_tval(latest, mono); 1578290001Sglebius if (debug > 1) 1579290001Sglebius printf("cached minus monotonic %+ld.%06ld\n", 1580290001Sglebius (long)diff.tv_sec, (long)diff.tv_usec); 1581290001Sglebius if (labs((long)diff.tv_sec) < 3600) { 1582290001Sglebius /* older libevent2 using monotonic */ 1583290001Sglebius offset = sub_tval(systemt, mono); 1584290001Sglebius TRACE(1, ("%s: Offsetting libevent CLOCK_MONOTONIC times by %+ld.%06ld\n", 1585290001Sglebius "gettimeofday_cached", 1586290001Sglebius (long)offset.tv_sec, 1587290001Sglebius (long)offset.tv_usec)); 1588290001Sglebius } 1589290001Sglebius } 1590290001Sglebius offset_ready = TRUE; 1591290001Sglebius } 1592290001Sglebius adj_cached = add_tval(cached, offset); 1593290001Sglebius *caller_tv = adj_cached; 1594132451Sroberto 1595290001Sglebius return 0; 1596290001Sglebius#else 1597290001Sglebius return event_base_gettimeofday_cached(b, caller_tv); 1598290001Sglebius#endif 1599132451Sroberto} 1600132451Sroberto 1601