1290000Sglebius#include <config.h> 2132451Sroberto 3290000Sglebius#include <event2/util.h> 4290000Sglebius#include <event2/event.h> 5132451Sroberto 6290000Sglebius#include "ntp_workimpl.h" 7290000Sglebius#ifdef WORK_THREAD 8290000Sglebius# include <event2/thread.h> 9290000Sglebius#endif 10132451Sroberto 11290000Sglebius#include "main.h" 12290000Sglebius#include "ntp_libopts.h" 13290000Sglebius#include "kod_management.h" 14290000Sglebius#include "networking.h" 15290000Sglebius#include "utilities.h" 16290000Sglebius#include "log.h" 17290000Sglebius#include "libntp.h" 18132451Sroberto 19132451Sroberto 20290000Sglebiusint shutting_down; 21290000Sglebiusint time_derived; 22290000Sglebiusint time_adjusted; 23290000Sglebiusint n_pending_dns = 0; 24290000Sglebiusint n_pending_ntp = 0; 25290000Sglebiusint ai_fam_pref = AF_UNSPEC; 26290000Sglebiusint ntpver = 4; 27290000Sglebiusdouble steplimit = -1; 28290000SglebiusSOCKET sock4 = -1; /* Socket for IPv4 */ 29290000SglebiusSOCKET sock6 = -1; /* Socket for IPv6 */ 30290000Sglebius/* 31290000Sglebius** BCAST *must* listen on port 123 (by default), so we can only 32290000Sglebius** use the UCST sockets (above) if they too are using port 123 33290000Sglebius*/ 34290000SglebiusSOCKET bsock4 = -1; /* Broadcast Socket for IPv4 */ 35290000SglebiusSOCKET bsock6 = -1; /* Broadcast Socket for IPv6 */ 36290000Sglebiusstruct event_base *base; 37290000Sglebiusstruct event *ev_sock4; 38290000Sglebiusstruct event *ev_sock6; 39290000Sglebiusstruct event *ev_worker_timeout; 40290000Sglebiusstruct event *ev_xmt_timer; 41132451Sroberto 42290000Sglebiusstruct dns_ctx { 43290000Sglebius const char * name; 44290000Sglebius int flags; 45290000Sglebius#define CTX_BCST 0x0001 46290000Sglebius#define CTX_UCST 0x0002 47290000Sglebius#define CTX_xCST 0x0003 48290000Sglebius#define CTX_CONC 0x0004 49290000Sglebius#define CTX_unused 0xfffd 50290000Sglebius int key_id; 51290000Sglebius struct timeval timeout; 52290000Sglebius struct key * key; 53290000Sglebius}; 54132451Sroberto 55290000Sglebiustypedef struct sent_pkt_tag sent_pkt; 56290000Sglebiusstruct sent_pkt_tag { 57290000Sglebius sent_pkt * link; 58290000Sglebius struct dns_ctx * dctx; 59290000Sglebius sockaddr_u addr; 60290000Sglebius time_t stime; 61290000Sglebius int done; 62290000Sglebius struct pkt x_pkt; 63290000Sglebius}; 64132451Sroberto 65290000Sglebiustypedef struct xmt_ctx_tag xmt_ctx; 66290000Sglebiusstruct xmt_ctx_tag { 67290000Sglebius xmt_ctx * link; 68290000Sglebius SOCKET sock; 69290000Sglebius time_t sched; 70290000Sglebius sent_pkt * spkt; 71290000Sglebius}; 72132451Sroberto 73290000Sglebiusstruct timeval gap; 74290000Sglebiusxmt_ctx * xmt_q; 75290000Sglebiusstruct key * keys = NULL; 76290000Sglebiusint response_timeout; 77290000Sglebiusstruct timeval response_tv; 78290000Sglebiusstruct timeval start_tv; 79290000Sglebius/* check the timeout at least once per second */ 80290000Sglebiusstruct timeval wakeup_tv = { 0, 888888 }; 81132451Sroberto 82290000Sglebiussent_pkt * fam_listheads[2]; 83290000Sglebius#define v4_pkts_list (fam_listheads[0]) 84290000Sglebius#define v6_pkts_list (fam_listheads[1]) 85132451Sroberto 86290000Sglebiusstatic union { 87290000Sglebius struct pkt pkt; 88290000Sglebius char buf[LEN_PKT_NOMAC + NTP_MAXEXTEN + MAX_MAC_LEN]; 89290000Sglebius} rbuf; 90132451Sroberto 91290000Sglebius#define r_pkt rbuf.pkt 92132451Sroberto 93290000Sglebius#ifdef HAVE_DROPROOT 94290000Sglebiusint droproot; /* intres imports these */ 95290000Sglebiusint root_dropped; 96290000Sglebius#endif 97290000Sglebiusu_long current_time; /* libntp/authkeys.c */ 98132451Sroberto 99290000Sglebiusvoid open_sockets(void); 100290000Sglebiusvoid handle_lookup(const char *name, int flags); 101290000Sglebiusvoid sntp_addremove_fd(int fd, int is_pipe, int remove_it); 102290000Sglebiusvoid worker_timeout(evutil_socket_t, short, void *); 103290000Sglebiusvoid worker_resp_cb(evutil_socket_t, short, void *); 104290000Sglebiusvoid sntp_name_resolved(int, int, void *, const char *, const char *, 105290000Sglebius const struct addrinfo *, 106290000Sglebius const struct addrinfo *); 107290000Sglebiusvoid queue_xmt(SOCKET sock, struct dns_ctx *dctx, sent_pkt *spkt, 108290000Sglebius u_int xmt_delay); 109290000Sglebiusvoid xmt_timer_cb(evutil_socket_t, short, void *ptr); 110290000Sglebiusvoid xmt(xmt_ctx *xctx); 111290000Sglebiusint check_kod(const struct addrinfo *ai); 112290000Sglebiusvoid timeout_query(sent_pkt *); 113290000Sglebiusvoid timeout_queries(void); 114290000Sglebiusvoid sock_cb(evutil_socket_t, short, void *); 115290000Sglebiusvoid check_exit_conditions(void); 116290000Sglebiusvoid sntp_libevent_log_cb(int, const char *); 117290000Sglebiusvoid set_li_vn_mode(struct pkt *spkt, char leap, char version, char mode); 118290000Sglebiusint set_time(double offset); 119290000Sglebiusvoid dec_pending_ntp(const char *, sockaddr_u *); 120290000Sglebiusint libevent_version_ok(void); 121290000Sglebiusint gettimeofday_cached(struct event_base *b, struct timeval *tv); 122132451Sroberto 123132451Sroberto 124290000Sglebius/* 125290000Sglebius * The actual main function. 126290000Sglebius */ 127290000Sglebiusint 128290000Sglebiussntp_main ( 129290000Sglebius int argc, 130290000Sglebius char **argv, 131290000Sglebius const char *sntpVersion 132290000Sglebius ) 133290000Sglebius{ 134290000Sglebius int i; 135290000Sglebius int exitcode; 136290000Sglebius int optct; 137290000Sglebius struct event_config * evcfg; 138132451Sroberto 139290000Sglebius /* Initialize logging system - sets up progname */ 140290000Sglebius sntp_init_logging(argv[0]); 141132451Sroberto 142290000Sglebius if (!libevent_version_ok()) 143290000Sglebius exit(EX_SOFTWARE); 144132451Sroberto 145290000Sglebius init_lib(); 146290000Sglebius init_auth(); 147132451Sroberto 148290000Sglebius optct = ntpOptionProcess(&sntpOptions, argc, argv); 149290000Sglebius argc -= optct; 150290000Sglebius argv += optct; 151132451Sroberto 152132451Sroberto 153290000Sglebius debug = OPT_VALUE_SET_DEBUG_LEVEL; 154132451Sroberto 155290000Sglebius TRACE(2, ("init_lib() done, %s%s\n", 156290000Sglebius (ipv4_works) 157290000Sglebius ? "ipv4_works " 158290000Sglebius : "", 159290000Sglebius (ipv6_works) 160290000Sglebius ? "ipv6_works " 161290000Sglebius : "")); 162290000Sglebius ntpver = OPT_VALUE_NTPVERSION; 163290000Sglebius steplimit = OPT_VALUE_STEPLIMIT / 1e3; 164290000Sglebius gap.tv_usec = max(0, OPT_VALUE_GAP * 1000); 165290000Sglebius gap.tv_usec = min(gap.tv_usec, 999999); 166132451Sroberto 167290000Sglebius if (HAVE_OPT(LOGFILE)) 168290000Sglebius open_logfile(OPT_ARG(LOGFILE)); 169132451Sroberto 170290000Sglebius msyslog(LOG_INFO, "%s", sntpVersion); 171132451Sroberto 172290000Sglebius if (0 == argc && !HAVE_OPT(BROADCAST) && !HAVE_OPT(CONCURRENT)) { 173290000Sglebius printf("%s: Must supply at least one of -b hostname, -c hostname, or hostname.\n", 174290000Sglebius progname); 175290000Sglebius exit(EX_USAGE); 176290000Sglebius } 177132451Sroberto 178132451Sroberto 179290000Sglebius /* 180290000Sglebius ** Eventually, we probably want: 181290000Sglebius ** - separate bcst and ucst timeouts (why?) 182290000Sglebius ** - multiple --timeout values in the commandline 183290000Sglebius */ 184132451Sroberto 185290000Sglebius response_timeout = OPT_VALUE_TIMEOUT; 186290000Sglebius response_tv.tv_sec = response_timeout; 187290000Sglebius response_tv.tv_usec = 0; 188132451Sroberto 189290000Sglebius /* IPv6 available? */ 190290000Sglebius if (isc_net_probeipv6() != ISC_R_SUCCESS) { 191290000Sglebius ai_fam_pref = AF_INET; 192290000Sglebius TRACE(1, ("No ipv6 support available, forcing ipv4\n")); 193290000Sglebius } else { 194290000Sglebius /* Check for options -4 and -6 */ 195290000Sglebius if (HAVE_OPT(IPV4)) 196290000Sglebius ai_fam_pref = AF_INET; 197290000Sglebius else if (HAVE_OPT(IPV6)) 198290000Sglebius ai_fam_pref = AF_INET6; 199290000Sglebius } 200132451Sroberto 201290000Sglebius /* TODO: Parse config file if declared */ 202132451Sroberto 203290000Sglebius /* 204290000Sglebius ** Init the KOD system. 205290000Sglebius ** For embedded systems with no writable filesystem, 206290000Sglebius ** -K /dev/null can be used to disable KoD storage. 207290000Sglebius */ 208290000Sglebius kod_init_kod_db(OPT_ARG(KOD), FALSE); 209132451Sroberto 210290000Sglebius // HMS: Should we use arg-defalt for this too? 211290000Sglebius if (HAVE_OPT(KEYFILE)) 212290000Sglebius auth_init(OPT_ARG(KEYFILE), &keys); 213132451Sroberto 214290000Sglebius /* 215290000Sglebius ** Considering employing a variable that prevents functions of doing 216290000Sglebius ** anything until everything is initialized properly 217290000Sglebius ** 218290000Sglebius ** HMS: What exactly does the above mean? 219290000Sglebius */ 220290000Sglebius event_set_log_callback(&sntp_libevent_log_cb); 221290000Sglebius if (debug > 0) 222290000Sglebius event_enable_debug_mode(); 223290000Sglebius#ifdef WORK_THREAD 224290000Sglebius evthread_use_pthreads(); 225290000Sglebius /* we use libevent from main thread only, locks should be academic */ 226290000Sglebius if (debug > 0) 227290000Sglebius evthread_enable_lock_debuging(); 228290000Sglebius#endif 229290000Sglebius evcfg = event_config_new(); 230290000Sglebius if (NULL == evcfg) { 231290000Sglebius printf("%s: event_config_new() failed!\n", progname); 232290000Sglebius return -1; 233290000Sglebius } 234290000Sglebius#ifndef HAVE_SOCKETPAIR 235290000Sglebius event_config_require_features(evcfg, EV_FEATURE_FDS); 236290000Sglebius#endif 237290000Sglebius /* all libevent calls are from main thread */ 238290000Sglebius /* event_config_set_flag(evcfg, EVENT_BASE_FLAG_NOLOCK); */ 239290000Sglebius base = event_base_new_with_config(evcfg); 240290000Sglebius event_config_free(evcfg); 241290000Sglebius if (NULL == base) { 242290000Sglebius printf("%s: event_base_new() failed!\n", progname); 243290000Sglebius return -1; 244290000Sglebius } 245132451Sroberto 246290000Sglebius /* wire into intres resolver */ 247290000Sglebius worker_per_query = TRUE; 248290000Sglebius addremove_io_fd = &sntp_addremove_fd; 249132451Sroberto 250290000Sglebius open_sockets(); 251132451Sroberto 252290000Sglebius if (HAVE_OPT(BROADCAST)) { 253290000Sglebius int cn = STACKCT_OPT( BROADCAST ); 254290000Sglebius const char ** cp = STACKLST_OPT( BROADCAST ); 255132451Sroberto 256290000Sglebius while (cn-- > 0) { 257290000Sglebius handle_lookup(*cp, CTX_BCST); 258290000Sglebius cp++; 259290000Sglebius } 260290000Sglebius } 261132451Sroberto 262290000Sglebius if (HAVE_OPT(CONCURRENT)) { 263290000Sglebius int cn = STACKCT_OPT( CONCURRENT ); 264290000Sglebius const char ** cp = STACKLST_OPT( CONCURRENT ); 265132451Sroberto 266290000Sglebius while (cn-- > 0) { 267290000Sglebius handle_lookup(*cp, CTX_UCST | CTX_CONC); 268290000Sglebius cp++; 269290000Sglebius } 270290000Sglebius } 271132451Sroberto 272290000Sglebius for (i = 0; i < argc; ++i) 273290000Sglebius handle_lookup(argv[i], CTX_UCST); 274132451Sroberto 275290000Sglebius gettimeofday_cached(base, &start_tv); 276290000Sglebius event_base_dispatch(base); 277290000Sglebius event_base_free(base); 278132451Sroberto 279290000Sglebius if (!time_adjusted && 280290000Sglebius (ENABLED_OPT(STEP) || ENABLED_OPT(SLEW))) 281290000Sglebius exitcode = 1; 282290000Sglebius else 283290000Sglebius exitcode = 0; 284132451Sroberto 285290000Sglebius return exitcode; 286290000Sglebius} 287132451Sroberto 288132451Sroberto 289290000Sglebius/* 290290000Sglebius** open sockets and make them non-blocking 291290000Sglebius*/ 292290000Sglebiusvoid 293290000Sglebiusopen_sockets( 294290000Sglebius void 295290000Sglebius ) 296290000Sglebius{ 297290000Sglebius sockaddr_u name; 298132451Sroberto 299290000Sglebius if (-1 == sock4) { 300290000Sglebius sock4 = socket(PF_INET, SOCK_DGRAM, 0); 301290000Sglebius if (-1 == sock4) { 302290000Sglebius /* error getting a socket */ 303290000Sglebius msyslog(LOG_ERR, "open_sockets: socket(PF_INET) failed: %m"); 304290000Sglebius exit(1); 305290000Sglebius } 306290000Sglebius /* Make it non-blocking */ 307290000Sglebius make_socket_nonblocking(sock4); 308132451Sroberto 309290000Sglebius /* Let's try using a wildcard... */ 310290000Sglebius ZERO(name); 311290000Sglebius AF(&name) = AF_INET; 312290000Sglebius SET_ADDR4N(&name, INADDR_ANY); 313290000Sglebius SET_PORT(&name, (HAVE_OPT(USERESERVEDPORT) ? 123 : 0)); 314132451Sroberto 315290000Sglebius if (-1 == bind(sock4, &name.sa, 316290000Sglebius SOCKLEN(&name))) { 317290000Sglebius msyslog(LOG_ERR, "open_sockets: bind(sock4) failed: %m"); 318290000Sglebius exit(1); 319290000Sglebius } 320132451Sroberto 321290000Sglebius /* Register an NTP callback for recv/timeout */ 322290000Sglebius ev_sock4 = event_new(base, sock4, 323290000Sglebius EV_TIMEOUT | EV_READ | EV_PERSIST, 324290000Sglebius &sock_cb, NULL); 325290000Sglebius if (NULL == ev_sock4) { 326290000Sglebius msyslog(LOG_ERR, 327290000Sglebius "open_sockets: event_new(base, sock4) failed!"); 328290000Sglebius } else { 329290000Sglebius event_add(ev_sock4, &wakeup_tv); 330290000Sglebius } 331290000Sglebius } 332132451Sroberto 333290000Sglebius /* We may not always have IPv6... */ 334290000Sglebius if (-1 == sock6 && ipv6_works) { 335290000Sglebius sock6 = socket(PF_INET6, SOCK_DGRAM, 0); 336290000Sglebius if (-1 == sock6 && ipv6_works) { 337290000Sglebius /* error getting a socket */ 338290000Sglebius msyslog(LOG_ERR, "open_sockets: socket(PF_INET6) failed: %m"); 339290000Sglebius exit(1); 340290000Sglebius } 341290000Sglebius /* Make it non-blocking */ 342290000Sglebius make_socket_nonblocking(sock6); 343132451Sroberto 344290000Sglebius /* Let's try using a wildcard... */ 345290000Sglebius ZERO(name); 346290000Sglebius AF(&name) = AF_INET6; 347290000Sglebius SET_ADDR6N(&name, in6addr_any); 348290000Sglebius SET_PORT(&name, (HAVE_OPT(USERESERVEDPORT) ? 123 : 0)); 349132451Sroberto 350290000Sglebius if (-1 == bind(sock6, &name.sa, 351290000Sglebius SOCKLEN(&name))) { 352290000Sglebius msyslog(LOG_ERR, "open_sockets: bind(sock6) failed: %m"); 353290000Sglebius exit(1); 354290000Sglebius } 355290000Sglebius /* Register an NTP callback for recv/timeout */ 356290000Sglebius ev_sock6 = event_new(base, sock6, 357290000Sglebius EV_TIMEOUT | EV_READ | EV_PERSIST, 358290000Sglebius &sock_cb, NULL); 359290000Sglebius if (NULL == ev_sock6) { 360290000Sglebius msyslog(LOG_ERR, 361290000Sglebius "open_sockets: event_new(base, sock6) failed!"); 362290000Sglebius } else { 363290000Sglebius event_add(ev_sock6, &wakeup_tv); 364290000Sglebius } 365290000Sglebius } 366290000Sglebius 367290000Sglebius return; 368290000Sglebius} 369132451Sroberto 370132451Sroberto 371290000Sglebius/* 372290000Sglebius** handle_lookup 373290000Sglebius*/ 374290000Sglebiusvoid 375290000Sglebiushandle_lookup( 376290000Sglebius const char *name, 377290000Sglebius int flags 378290000Sglebius ) 379290000Sglebius{ 380290000Sglebius struct addrinfo hints; /* Local copy is OK */ 381290000Sglebius struct dns_ctx *ctx; 382290000Sglebius long l; 383290000Sglebius char * name_copy; 384290000Sglebius size_t name_sz; 385290000Sglebius size_t octets; 386132451Sroberto 387290000Sglebius TRACE(1, ("handle_lookup(%s,%#x)\n", name, flags)); 388132451Sroberto 389290000Sglebius ZERO(hints); 390290000Sglebius hints.ai_family = ai_fam_pref; 391290000Sglebius hints.ai_flags = AI_CANONNAME | Z_AI_NUMERICSERV; 392290000Sglebius /* 393290000Sglebius ** Unless we specify a socktype, we'll get at least two 394290000Sglebius ** entries for each address: one for TCP and one for 395290000Sglebius ** UDP. That's not what we want. 396290000Sglebius */ 397290000Sglebius hints.ai_socktype = SOCK_DGRAM; 398290000Sglebius hints.ai_protocol = IPPROTO_UDP; 399132451Sroberto 400290000Sglebius name_sz = 1 + strlen(name); 401290000Sglebius octets = sizeof(*ctx) + name_sz; // Space for a ctx and the name 402290000Sglebius ctx = emalloc_zero(octets); // ctx at ctx[0] 403290000Sglebius name_copy = (char *)(ctx + 1); // Put the name at ctx[1] 404290000Sglebius memcpy(name_copy, name, name_sz); // copy the name to ctx[1] 405290000Sglebius ctx->name = name_copy; // point to it... 406290000Sglebius ctx->flags = flags; 407290000Sglebius ctx->timeout = response_tv; 408132451Sroberto 409290000Sglebius /* The following should arguably be passed in... */ 410290000Sglebius if (ENABLED_OPT(AUTHENTICATION) && 411290000Sglebius atoint(OPT_ARG(AUTHENTICATION), &l)) { 412290000Sglebius ctx->key_id = l; 413290000Sglebius get_key(ctx->key_id, &ctx->key); 414290000Sglebius } else { 415290000Sglebius ctx->key_id = -1; 416290000Sglebius ctx->key = NULL; 417290000Sglebius } 418132451Sroberto 419290000Sglebius ++n_pending_dns; 420290000Sglebius getaddrinfo_sometime(name, "123", &hints, 0, 421290000Sglebius &sntp_name_resolved, ctx); 422132451Sroberto} 423132451Sroberto 424132451Sroberto 425290000Sglebius/* 426290000Sglebius** DNS Callback: 427290000Sglebius** - For each IP: 428290000Sglebius** - - open a socket 429290000Sglebius** - - increment n_pending_ntp 430290000Sglebius** - - send a request if this is a Unicast callback 431290000Sglebius** - - queue wait for response 432290000Sglebius** - decrement n_pending_dns 433290000Sglebius*/ 434290000Sglebiusvoid 435290000Sglebiussntp_name_resolved( 436290000Sglebius int rescode, 437290000Sglebius int gai_errno, 438290000Sglebius void * context, 439290000Sglebius const char * name, 440290000Sglebius const char * service, 441290000Sglebius const struct addrinfo * hints, 442290000Sglebius const struct addrinfo * addr 443290000Sglebius ) 444290000Sglebius{ 445290000Sglebius struct dns_ctx * dctx; 446290000Sglebius sent_pkt * spkt; 447290000Sglebius const struct addrinfo * ai; 448290000Sglebius SOCKET sock; 449290000Sglebius u_int xmt_delay_v4; 450290000Sglebius u_int xmt_delay_v6; 451290000Sglebius u_int xmt_delay; 452290000Sglebius size_t octets; 453132451Sroberto 454290000Sglebius xmt_delay_v4 = 0; 455290000Sglebius xmt_delay_v6 = 0; 456290000Sglebius dctx = context; 457290000Sglebius if (rescode) { 458290000Sglebius#ifdef EAI_SYSTEM 459290000Sglebius if (EAI_SYSTEM == rescode) { 460290000Sglebius errno = gai_errno; 461290000Sglebius mfprintf(stderr, "%s lookup error %m\n", 462290000Sglebius dctx->name); 463290000Sglebius } else 464290000Sglebius#endif 465290000Sglebius fprintf(stderr, "%s lookup error %s\n", 466290000Sglebius dctx->name, gai_strerror(rescode)); 467290000Sglebius } else { 468290000Sglebius TRACE(3, ("%s [%s]\n", dctx->name, 469290000Sglebius (addr->ai_canonname != NULL) 470290000Sglebius ? addr->ai_canonname 471290000Sglebius : "")); 472132451Sroberto 473290000Sglebius for (ai = addr; ai != NULL; ai = ai->ai_next) { 474132451Sroberto 475290000Sglebius if (check_kod(ai)) 476290000Sglebius continue; 477132451Sroberto 478290000Sglebius switch (ai->ai_family) { 479132451Sroberto 480290000Sglebius case AF_INET: 481290000Sglebius sock = sock4; 482290000Sglebius xmt_delay = xmt_delay_v4; 483290000Sglebius xmt_delay_v4++; 484290000Sglebius break; 485132451Sroberto 486290000Sglebius case AF_INET6: 487290000Sglebius if (!ipv6_works) 488290000Sglebius continue; 489132451Sroberto 490290000Sglebius sock = sock6; 491290000Sglebius xmt_delay = xmt_delay_v6; 492290000Sglebius xmt_delay_v6++; 493290000Sglebius break; 494132451Sroberto 495290000Sglebius default: 496290000Sglebius msyslog(LOG_ERR, "sntp_name_resolved: unexpected ai_family: %d", 497290000Sglebius ai->ai_family); 498290000Sglebius exit(1); 499290000Sglebius break; 500290000Sglebius } 501132451Sroberto 502290000Sglebius /* 503290000Sglebius ** We're waiting for a response for either unicast 504290000Sglebius ** or broadcast, so... 505290000Sglebius */ 506290000Sglebius ++n_pending_ntp; 507132451Sroberto 508290000Sglebius /* If this is for a unicast IP, queue a request */ 509290000Sglebius if (dctx->flags & CTX_UCST) { 510290000Sglebius spkt = emalloc_zero(sizeof(*spkt)); 511290000Sglebius spkt->dctx = dctx; 512290000Sglebius octets = min(ai->ai_addrlen, sizeof(spkt->addr)); 513290000Sglebius memcpy(&spkt->addr, ai->ai_addr, octets); 514290000Sglebius queue_xmt(sock, dctx, spkt, xmt_delay); 515290000Sglebius } 516290000Sglebius } 517290000Sglebius } 518290000Sglebius /* n_pending_dns really should be >0 here... */ 519290000Sglebius --n_pending_dns; 520290000Sglebius check_exit_conditions(); 521132451Sroberto} 522132451Sroberto 523132451Sroberto 524290000Sglebius/* 525290000Sglebius** queue_xmt 526290000Sglebius*/ 527290000Sglebiusvoid 528290000Sglebiusqueue_xmt( 529290000Sglebius SOCKET sock, 530290000Sglebius struct dns_ctx * dctx, 531290000Sglebius sent_pkt * spkt, 532290000Sglebius u_int xmt_delay 533290000Sglebius ) 534290000Sglebius{ 535290000Sglebius sockaddr_u * dest; 536290000Sglebius sent_pkt ** pkt_listp; 537290000Sglebius sent_pkt * match; 538290000Sglebius xmt_ctx * xctx; 539290000Sglebius struct timeval start_cb; 540290000Sglebius struct timeval delay; 541132451Sroberto 542290000Sglebius dest = &spkt->addr; 543290000Sglebius if (IS_IPV6(dest)) 544290000Sglebius pkt_listp = &v6_pkts_list; 545290000Sglebius else 546290000Sglebius pkt_listp = &v4_pkts_list; 547132451Sroberto 548290000Sglebius /* reject attempts to add address already listed */ 549290000Sglebius for (match = *pkt_listp; match != NULL; match = match->link) { 550290000Sglebius if (ADDR_PORT_EQ(&spkt->addr, &match->addr)) { 551290000Sglebius if (strcasecmp(spkt->dctx->name, 552290000Sglebius match->dctx->name)) 553290000Sglebius printf("%s %s duplicate address from %s ignored.\n", 554290000Sglebius sptoa(&match->addr), 555290000Sglebius match->dctx->name, 556290000Sglebius spkt->dctx->name); 557290000Sglebius else 558290000Sglebius printf("%s %s, duplicate address ignored.\n", 559290000Sglebius sptoa(&match->addr), 560290000Sglebius match->dctx->name); 561290000Sglebius dec_pending_ntp(spkt->dctx->name, &spkt->addr); 562290000Sglebius free(spkt); 563290000Sglebius return; 564290000Sglebius } 565290000Sglebius } 566132451Sroberto 567290000Sglebius LINK_SLIST(*pkt_listp, spkt, link); 568132451Sroberto 569290000Sglebius xctx = emalloc_zero(sizeof(*xctx)); 570290000Sglebius xctx->sock = sock; 571290000Sglebius xctx->spkt = spkt; 572290000Sglebius gettimeofday_cached(base, &start_cb); 573290000Sglebius xctx->sched = start_cb.tv_sec + (2 * xmt_delay); 574132451Sroberto 575290000Sglebius LINK_SORT_SLIST(xmt_q, xctx, (xctx->sched < L_S_S_CUR()->sched), 576290000Sglebius link, xmt_ctx); 577290000Sglebius if (xmt_q == xctx) { 578290000Sglebius /* 579290000Sglebius * The new entry is the first scheduled. The timer is 580290000Sglebius * either not active or is set for the second xmt 581290000Sglebius * context in xmt_q. 582290000Sglebius */ 583290000Sglebius if (NULL == ev_xmt_timer) 584290000Sglebius ev_xmt_timer = event_new(base, INVALID_SOCKET, 585290000Sglebius EV_TIMEOUT, 586290000Sglebius &xmt_timer_cb, NULL); 587290000Sglebius if (NULL == ev_xmt_timer) { 588290000Sglebius msyslog(LOG_ERR, 589290000Sglebius "queue_xmt: event_new(base, -1, EV_TIMEOUT) failed!"); 590290000Sglebius exit(1); 591290000Sglebius } 592290000Sglebius ZERO(delay); 593290000Sglebius if (xctx->sched > start_cb.tv_sec) 594290000Sglebius delay.tv_sec = xctx->sched - start_cb.tv_sec; 595290000Sglebius event_add(ev_xmt_timer, &delay); 596290000Sglebius TRACE(2, ("queue_xmt: xmt timer for %u usec\n", 597290000Sglebius (u_int)delay.tv_usec)); 598290000Sglebius } 599132451Sroberto} 600132451Sroberto 601132451Sroberto 602290000Sglebius/* 603290000Sglebius** xmt_timer_cb 604290000Sglebius*/ 605290000Sglebiusvoid 606290000Sglebiusxmt_timer_cb( 607290000Sglebius evutil_socket_t fd, 608290000Sglebius short what, 609290000Sglebius void * ctx 610290000Sglebius ) 611290000Sglebius{ 612290000Sglebius struct timeval start_cb; 613290000Sglebius struct timeval delay; 614290000Sglebius xmt_ctx * x; 615132451Sroberto 616290000Sglebius UNUSED_ARG(fd); 617290000Sglebius UNUSED_ARG(ctx); 618290000Sglebius DEBUG_INSIST(EV_TIMEOUT == what); 619132451Sroberto 620290000Sglebius if (NULL == xmt_q || shutting_down) 621290000Sglebius return; 622290000Sglebius gettimeofday_cached(base, &start_cb); 623290000Sglebius if (xmt_q->sched <= start_cb.tv_sec) { 624290000Sglebius UNLINK_HEAD_SLIST(x, xmt_q, link); 625290000Sglebius TRACE(2, ("xmt_timer_cb: at .%6.6u -> %s\n", 626290000Sglebius (u_int)start_cb.tv_usec, stoa(&x->spkt->addr))); 627290000Sglebius xmt(x); 628290000Sglebius free(x); 629290000Sglebius if (NULL == xmt_q) 630290000Sglebius return; 631290000Sglebius } 632290000Sglebius if (xmt_q->sched <= start_cb.tv_sec) { 633290000Sglebius event_add(ev_xmt_timer, &gap); 634290000Sglebius TRACE(2, ("xmt_timer_cb: at .%6.6u gap %6.6u\n", 635290000Sglebius (u_int)start_cb.tv_usec, 636290000Sglebius (u_int)gap.tv_usec)); 637290000Sglebius } else { 638290000Sglebius delay.tv_sec = xmt_q->sched - start_cb.tv_sec; 639290000Sglebius delay.tv_usec = 0; 640290000Sglebius event_add(ev_xmt_timer, &delay); 641290000Sglebius TRACE(2, ("xmt_timer_cb: at .%6.6u next %ld seconds\n", 642290000Sglebius (u_int)start_cb.tv_usec, 643290000Sglebius (long)delay.tv_sec)); 644290000Sglebius } 645132451Sroberto} 646132451Sroberto 647132451Sroberto 648290000Sglebius/* 649290000Sglebius** xmt() 650290000Sglebius*/ 651290000Sglebiusvoid 652290000Sglebiusxmt( 653290000Sglebius xmt_ctx * xctx 654290000Sglebius ) 655290000Sglebius{ 656290000Sglebius SOCKET sock = xctx->sock; 657290000Sglebius struct dns_ctx *dctx = xctx->spkt->dctx; 658290000Sglebius sent_pkt * spkt = xctx->spkt; 659290000Sglebius sockaddr_u * dst = &spkt->addr; 660290000Sglebius struct timeval tv_xmt; 661290000Sglebius struct pkt x_pkt; 662290000Sglebius size_t pkt_len; 663290000Sglebius int sent; 664132451Sroberto 665290000Sglebius if (0 != gettimeofday(&tv_xmt, NULL)) { 666290000Sglebius msyslog(LOG_ERR, 667290000Sglebius "xmt: gettimeofday() failed: %m"); 668290000Sglebius exit(1); 669290000Sglebius } 670290000Sglebius tv_xmt.tv_sec += JAN_1970; 671132451Sroberto 672290000Sglebius pkt_len = generate_pkt(&x_pkt, &tv_xmt, dctx->key_id, 673290000Sglebius dctx->key); 674132451Sroberto 675290000Sglebius sent = sendpkt(sock, dst, &x_pkt, pkt_len); 676290000Sglebius if (sent) { 677290000Sglebius /* Save the packet we sent... */ 678290000Sglebius memcpy(&spkt->x_pkt, &x_pkt, min(sizeof(spkt->x_pkt), 679290000Sglebius pkt_len)); 680290000Sglebius spkt->stime = tv_xmt.tv_sec - JAN_1970; 681132451Sroberto 682290000Sglebius TRACE(2, ("xmt: %lx.%6.6u %s %s\n", (u_long)tv_xmt.tv_sec, 683290000Sglebius (u_int)tv_xmt.tv_usec, dctx->name, stoa(dst))); 684290000Sglebius } else { 685290000Sglebius dec_pending_ntp(dctx->name, dst); 686290000Sglebius } 687132451Sroberto 688290000Sglebius return; 689290000Sglebius} 690132451Sroberto 691132451Sroberto 692290000Sglebius/* 693290000Sglebius * timeout_queries() -- give up on unrequited NTP queries 694290000Sglebius */ 695290000Sglebiusvoid 696290000Sglebiustimeout_queries(void) 697290000Sglebius{ 698290000Sglebius struct timeval start_cb; 699290000Sglebius u_int idx; 700290000Sglebius sent_pkt * head; 701290000Sglebius sent_pkt * spkt; 702290000Sglebius sent_pkt * spkt_next; 703290000Sglebius long age; 704290000Sglebius int didsomething = 0; 705132451Sroberto 706290000Sglebius TRACE(3, ("timeout_queries: called to check %u items\n", 707290000Sglebius (unsigned)COUNTOF(fam_listheads))); 708132451Sroberto 709290000Sglebius gettimeofday_cached(base, &start_cb); 710290000Sglebius for (idx = 0; idx < COUNTOF(fam_listheads); idx++) { 711290000Sglebius head = fam_listheads[idx]; 712290000Sglebius for (spkt = head; spkt != NULL; spkt = spkt_next) { 713290000Sglebius char xcst; 714132451Sroberto 715290000Sglebius didsomething = 1; 716290000Sglebius switch (spkt->dctx->flags & CTX_xCST) { 717290000Sglebius case CTX_BCST: 718290000Sglebius xcst = 'B'; 719290000Sglebius break; 720132451Sroberto 721290000Sglebius case CTX_UCST: 722290000Sglebius xcst = 'U'; 723290000Sglebius break; 724132451Sroberto 725290000Sglebius default: 726290000Sglebius INSIST(!"spkt->dctx->flags neither UCST nor BCST"); 727290000Sglebius break; 728290000Sglebius } 729132451Sroberto 730290000Sglebius spkt_next = spkt->link; 731290000Sglebius if (0 == spkt->stime || spkt->done) 732290000Sglebius continue; 733290000Sglebius age = start_cb.tv_sec - spkt->stime; 734290000Sglebius TRACE(3, ("%s %s %cCST age %ld\n", 735290000Sglebius stoa(&spkt->addr), 736290000Sglebius spkt->dctx->name, xcst, age)); 737290000Sglebius if (age > response_timeout) 738290000Sglebius timeout_query(spkt); 739290000Sglebius } 740290000Sglebius } 741290000Sglebius // Do we care about didsomething? 742290000Sglebius TRACE(3, ("timeout_queries: didsomething is %d, age is %ld\n", 743290000Sglebius didsomething, (long) (start_cb.tv_sec - start_tv.tv_sec))); 744290000Sglebius if (start_cb.tv_sec - start_tv.tv_sec > response_timeout) { 745290000Sglebius TRACE(3, ("timeout_queries: bail!\n")); 746290000Sglebius event_base_loopexit(base, NULL); 747290000Sglebius shutting_down = TRUE; 748290000Sglebius } 749132451Sroberto} 750132451Sroberto 751132451Sroberto 752290000Sglebiusvoid dec_pending_ntp( 753290000Sglebius const char * name, 754290000Sglebius sockaddr_u * server 755290000Sglebius ) 756290000Sglebius{ 757290000Sglebius if (n_pending_ntp > 0) { 758290000Sglebius --n_pending_ntp; 759290000Sglebius check_exit_conditions(); 760290000Sglebius } else { 761290000Sglebius INSIST(0 == n_pending_ntp); 762290000Sglebius TRACE(1, ("n_pending_ntp was zero before decrement for %s\n", 763290000Sglebius hostnameaddr(name, server))); 764290000Sglebius } 765290000Sglebius} 766132451Sroberto 767132451Sroberto 768290000Sglebiusvoid timeout_query( 769290000Sglebius sent_pkt * spkt 770290000Sglebius ) 771290000Sglebius{ 772290000Sglebius sockaddr_u * server; 773290000Sglebius char xcst; 774132451Sroberto 775132451Sroberto 776290000Sglebius switch (spkt->dctx->flags & CTX_xCST) { 777290000Sglebius case CTX_BCST: 778290000Sglebius xcst = 'B'; 779290000Sglebius break; 780132451Sroberto 781290000Sglebius case CTX_UCST: 782290000Sglebius xcst = 'U'; 783290000Sglebius break; 784132451Sroberto 785290000Sglebius default: 786290000Sglebius INSIST(!"spkt->dctx->flags neither UCST nor BCST"); 787290000Sglebius break; 788290000Sglebius } 789290000Sglebius spkt->done = TRUE; 790290000Sglebius server = &spkt->addr; 791290000Sglebius msyslog(LOG_INFO, "%s no %cCST response after %d seconds", 792290000Sglebius hostnameaddr(spkt->dctx->name, server), xcst, 793290000Sglebius response_timeout); 794290000Sglebius dec_pending_ntp(spkt->dctx->name, server); 795290000Sglebius return; 796290000Sglebius} 797132451Sroberto 798132451Sroberto 799290000Sglebius/* 800290000Sglebius** check_kod 801290000Sglebius*/ 802290000Sglebiusint 803290000Sglebiuscheck_kod( 804290000Sglebius const struct addrinfo * ai 805290000Sglebius ) 806290000Sglebius{ 807290000Sglebius char *hostname; 808290000Sglebius struct kod_entry *reason; 809132451Sroberto 810290000Sglebius /* Is there a KoD on file for this address? */ 811290000Sglebius hostname = addrinfo_to_str(ai); 812290000Sglebius TRACE(2, ("check_kod: checking <%s>\n", hostname)); 813290000Sglebius if (search_entry(hostname, &reason)) { 814290000Sglebius printf("prior KoD for %s, skipping.\n", 815290000Sglebius hostname); 816290000Sglebius free(reason); 817290000Sglebius free(hostname); 818132451Sroberto 819290000Sglebius return 1; 820290000Sglebius } 821290000Sglebius free(hostname); 822132451Sroberto 823290000Sglebius return 0; 824132451Sroberto} 825132451Sroberto 826132451Sroberto 827290000Sglebius/* 828290000Sglebius** Socket readable/timeout Callback: 829290000Sglebius** Read in the packet 830290000Sglebius** Unicast: 831290000Sglebius** - close socket 832290000Sglebius** - decrement n_pending_ntp 833290000Sglebius** - If packet is good, set the time and "exit" 834290000Sglebius** Broadcast: 835290000Sglebius** - If packet is good, set the time and "exit" 836290000Sglebius*/ 837290000Sglebiusvoid 838290000Sglebiussock_cb( 839290000Sglebius evutil_socket_t fd, 840290000Sglebius short what, 841290000Sglebius void *ptr 842290000Sglebius ) 843290000Sglebius{ 844290000Sglebius sockaddr_u sender; 845290000Sglebius sockaddr_u * psau; 846290000Sglebius sent_pkt ** p_pktlist; 847290000Sglebius sent_pkt * spkt; 848290000Sglebius int rpktl; 849290000Sglebius int rc; 850132451Sroberto 851290000Sglebius INSIST(sock4 == fd || sock6 == fd); 852132451Sroberto 853290000Sglebius TRACE(3, ("sock_cb: event on sock%s:%s%s%s%s\n", 854290000Sglebius (fd == sock6) 855290000Sglebius ? "6" 856290000Sglebius : "4", 857290000Sglebius (what & EV_TIMEOUT) ? " timeout" : "", 858290000Sglebius (what & EV_READ) ? " read" : "", 859290000Sglebius (what & EV_WRITE) ? " write" : "", 860290000Sglebius (what & EV_SIGNAL) ? " signal" : "")); 861132451Sroberto 862290000Sglebius if (!(EV_READ & what)) { 863290000Sglebius if (EV_TIMEOUT & what) 864290000Sglebius timeout_queries(); 865132451Sroberto 866290000Sglebius return; 867290000Sglebius } 868132451Sroberto 869290000Sglebius /* Read in the packet */ 870290000Sglebius rpktl = recvdata(fd, &sender, &rbuf, sizeof(rbuf)); 871290000Sglebius if (rpktl < 0) { 872290000Sglebius msyslog(LOG_DEBUG, "recvfrom error %m"); 873290000Sglebius return; 874290000Sglebius } 875132451Sroberto 876290000Sglebius if (sock6 == fd) 877290000Sglebius p_pktlist = &v6_pkts_list; 878290000Sglebius else 879290000Sglebius p_pktlist = &v4_pkts_list; 880132451Sroberto 881290000Sglebius for (spkt = *p_pktlist; spkt != NULL; spkt = spkt->link) { 882290000Sglebius psau = &spkt->addr; 883290000Sglebius if (SOCK_EQ(&sender, psau)) 884290000Sglebius break; 885290000Sglebius } 886290000Sglebius if (NULL == spkt) { 887290000Sglebius msyslog(LOG_WARNING, 888290000Sglebius "Packet from unexpected source %s dropped", 889290000Sglebius sptoa(&sender)); 890290000Sglebius return; 891290000Sglebius } 892132451Sroberto 893290000Sglebius TRACE(1, ("sock_cb: %s %s\n", spkt->dctx->name, 894290000Sglebius sptoa(&sender))); 895132451Sroberto 896290000Sglebius rpktl = process_pkt(&r_pkt, &sender, rpktl, MODE_SERVER, 897290000Sglebius &spkt->x_pkt, "sock_cb"); 898132451Sroberto 899290000Sglebius TRACE(2, ("sock_cb: process_pkt returned %d\n", rpktl)); 900132451Sroberto 901290000Sglebius /* If this is a Unicast packet, one down ... */ 902290000Sglebius if (!spkt->done && (CTX_UCST & spkt->dctx->flags)) { 903290000Sglebius dec_pending_ntp(spkt->dctx->name, &spkt->addr); 904290000Sglebius spkt->done = TRUE; 905290000Sglebius } 906132451Sroberto 907132451Sroberto 908290000Sglebius /* If the packet is good, set the time and we're all done */ 909290000Sglebius rc = handle_pkt(rpktl, &r_pkt, &spkt->addr, spkt->dctx->name); 910290000Sglebius if (0 != rc) 911290000Sglebius TRACE(1, ("sock_cb: handle_pkt() returned %d\n", rc)); 912290000Sglebius check_exit_conditions(); 913290000Sglebius} 914132451Sroberto 915132451Sroberto 916290000Sglebius/* 917290000Sglebius * check_exit_conditions() 918290000Sglebius * 919290000Sglebius * If sntp has a reply, ask the event loop to stop after this round of 920290000Sglebius * callbacks, unless --wait was used. 921290000Sglebius */ 922290000Sglebiusvoid 923290000Sglebiuscheck_exit_conditions(void) 924290000Sglebius{ 925290000Sglebius if ((0 == n_pending_ntp && 0 == n_pending_dns) || 926290000Sglebius (time_derived && !HAVE_OPT(WAIT))) { 927290000Sglebius event_base_loopexit(base, NULL); 928290000Sglebius shutting_down = TRUE; 929290000Sglebius } else { 930290000Sglebius TRACE(2, ("%d NTP and %d name queries pending\n", 931290000Sglebius n_pending_ntp, n_pending_dns)); 932290000Sglebius } 933290000Sglebius} 934132451Sroberto 935132451Sroberto 936290000Sglebius/* 937290000Sglebius * sntp_addremove_fd() is invoked by the intres blocking worker code 938290000Sglebius * to read from a pipe, or to stop same. 939290000Sglebius */ 940290000Sglebiusvoid sntp_addremove_fd( 941290000Sglebius int fd, 942290000Sglebius int is_pipe, 943290000Sglebius int remove_it 944290000Sglebius ) 945290000Sglebius{ 946290000Sglebius u_int idx; 947290000Sglebius blocking_child *c; 948290000Sglebius struct event * ev; 949132451Sroberto 950290000Sglebius#ifdef HAVE_SOCKETPAIR 951290000Sglebius if (is_pipe) { 952290000Sglebius /* sntp only asks for EV_FEATURE_FDS without HAVE_SOCKETPAIR */ 953290000Sglebius msyslog(LOG_ERR, "fatal: pipes not supported on systems with socketpair()"); 954290000Sglebius exit(1); 955290000Sglebius } 956290000Sglebius#endif 957132451Sroberto 958290000Sglebius c = NULL; 959290000Sglebius for (idx = 0; idx < blocking_children_alloc; idx++) { 960290000Sglebius c = blocking_children[idx]; 961290000Sglebius if (NULL == c) 962290000Sglebius continue; 963290000Sglebius if (fd == c->resp_read_pipe) 964290000Sglebius break; 965290000Sglebius } 966290000Sglebius if (idx == blocking_children_alloc) 967290000Sglebius return; 968132451Sroberto 969290000Sglebius if (remove_it) { 970290000Sglebius ev = c->resp_read_ctx; 971290000Sglebius c->resp_read_ctx = NULL; 972290000Sglebius event_del(ev); 973290000Sglebius event_free(ev); 974132451Sroberto 975290000Sglebius return; 976290000Sglebius } 977132451Sroberto 978290000Sglebius ev = event_new(base, fd, EV_READ | EV_PERSIST, 979290000Sglebius &worker_resp_cb, c); 980290000Sglebius if (NULL == ev) { 981290000Sglebius msyslog(LOG_ERR, 982290000Sglebius "sntp_addremove_fd: event_new(base, fd) failed!"); 983290000Sglebius return; 984290000Sglebius } 985290000Sglebius c->resp_read_ctx = ev; 986290000Sglebius event_add(ev, NULL); 987290000Sglebius} 988132451Sroberto 989132451Sroberto 990290000Sglebius/* called by forked intres child to close open descriptors */ 991290000Sglebius#ifdef WORK_FORK 992290000Sglebiusvoid 993290000Sglebiuskill_asyncio( 994290000Sglebius int startfd 995290000Sglebius ) 996290000Sglebius{ 997290000Sglebius if (INVALID_SOCKET != sock4) { 998290000Sglebius closesocket(sock4); 999290000Sglebius sock4 = INVALID_SOCKET; 1000290000Sglebius } 1001290000Sglebius if (INVALID_SOCKET != sock6) { 1002290000Sglebius closesocket(sock6); 1003290000Sglebius sock6 = INVALID_SOCKET; 1004290000Sglebius } 1005290000Sglebius if (INVALID_SOCKET != bsock4) { 1006290000Sglebius closesocket(sock4); 1007290000Sglebius sock4 = INVALID_SOCKET; 1008290000Sglebius } 1009290000Sglebius if (INVALID_SOCKET != bsock6) { 1010290000Sglebius closesocket(sock6); 1011290000Sglebius sock6 = INVALID_SOCKET; 1012290000Sglebius } 1013290000Sglebius} 1014290000Sglebius#endif 1015132451Sroberto 1016132451Sroberto 1017290000Sglebius/* 1018290000Sglebius * worker_resp_cb() is invoked when resp_read_pipe is readable. 1019290000Sglebius */ 1020290000Sglebiusvoid 1021290000Sglebiusworker_resp_cb( 1022290000Sglebius evutil_socket_t fd, 1023290000Sglebius short what, 1024290000Sglebius void * ctx /* blocking_child * */ 1025290000Sglebius ) 1026290000Sglebius{ 1027290000Sglebius blocking_child * c; 1028132451Sroberto 1029290000Sglebius DEBUG_INSIST(EV_READ & what); 1030290000Sglebius c = ctx; 1031290000Sglebius DEBUG_INSIST(fd == c->resp_read_pipe); 1032290000Sglebius process_blocking_resp(c); 1033132451Sroberto} 1034132451Sroberto 1035132451Sroberto 1036290000Sglebius/* 1037290000Sglebius * intres_timeout_req(s) is invoked in the parent to schedule an idle 1038290000Sglebius * timeout to fire in s seconds, if not reset earlier by a call to 1039290000Sglebius * intres_timeout_req(0), which clears any pending timeout. When the 1040290000Sglebius * timeout expires, worker_idle_timer_fired() is invoked (again, in the 1041290000Sglebius * parent). 1042290000Sglebius * 1043290000Sglebius * sntp and ntpd each provide implementations adapted to their timers. 1044290000Sglebius */ 1045290000Sglebiusvoid 1046290000Sglebiusintres_timeout_req( 1047290000Sglebius u_int seconds /* 0 cancels */ 1048290000Sglebius ) 1049290000Sglebius{ 1050290000Sglebius struct timeval tv_to; 1051132451Sroberto 1052290000Sglebius if (NULL == ev_worker_timeout) { 1053290000Sglebius ev_worker_timeout = event_new(base, -1, 1054290000Sglebius EV_TIMEOUT | EV_PERSIST, 1055290000Sglebius &worker_timeout, NULL); 1056290000Sglebius DEBUG_INSIST(NULL != ev_worker_timeout); 1057290000Sglebius } else { 1058290000Sglebius event_del(ev_worker_timeout); 1059290000Sglebius } 1060290000Sglebius if (0 == seconds) 1061290000Sglebius return; 1062290000Sglebius tv_to.tv_sec = seconds; 1063290000Sglebius tv_to.tv_usec = 0; 1064290000Sglebius event_add(ev_worker_timeout, &tv_to); 1065290000Sglebius} 1066132451Sroberto 1067132451Sroberto 1068290000Sglebiusvoid 1069290000Sglebiusworker_timeout( 1070290000Sglebius evutil_socket_t fd, 1071290000Sglebius short what, 1072290000Sglebius void * ctx 1073290000Sglebius ) 1074290000Sglebius{ 1075290000Sglebius UNUSED_ARG(fd); 1076290000Sglebius UNUSED_ARG(ctx); 1077132451Sroberto 1078290000Sglebius DEBUG_REQUIRE(EV_TIMEOUT & what); 1079290000Sglebius worker_idle_timer_fired(); 1080132451Sroberto} 1081132451Sroberto 1082132451Sroberto 1083290000Sglebiusvoid 1084290000Sglebiussntp_libevent_log_cb( 1085290000Sglebius int severity, 1086290000Sglebius const char * msg 1087290000Sglebius ) 1088290000Sglebius{ 1089290000Sglebius int level; 1090132451Sroberto 1091290000Sglebius switch (severity) { 1092132451Sroberto 1093290000Sglebius default: 1094290000Sglebius case _EVENT_LOG_DEBUG: 1095290000Sglebius level = LOG_DEBUG; 1096290000Sglebius break; 1097132451Sroberto 1098290000Sglebius case _EVENT_LOG_MSG: 1099290000Sglebius level = LOG_NOTICE; 1100290000Sglebius break; 1101132451Sroberto 1102290000Sglebius case _EVENT_LOG_WARN: 1103290000Sglebius level = LOG_WARNING; 1104290000Sglebius break; 1105132451Sroberto 1106290000Sglebius case _EVENT_LOG_ERR: 1107290000Sglebius level = LOG_ERR; 1108290000Sglebius break; 1109290000Sglebius } 1110132451Sroberto 1111290000Sglebius msyslog(level, "%s", msg); 1112290000Sglebius} 1113132451Sroberto 1114132451Sroberto 1115290000Sglebiusint 1116290000Sglebiusgenerate_pkt ( 1117290000Sglebius struct pkt *x_pkt, 1118290000Sglebius const struct timeval *tv_xmt, 1119290000Sglebius int key_id, 1120290000Sglebius struct key *pkt_key 1121290000Sglebius ) 1122290000Sglebius{ 1123290000Sglebius l_fp xmt_fp; 1124290000Sglebius int pkt_len; 1125290000Sglebius int mac_size; 1126132451Sroberto 1127290000Sglebius pkt_len = LEN_PKT_NOMAC; 1128290000Sglebius ZERO(*x_pkt); 1129290000Sglebius TVTOTS(tv_xmt, &xmt_fp); 1130290000Sglebius HTONL_FP(&xmt_fp, &x_pkt->xmt); 1131290000Sglebius x_pkt->stratum = STRATUM_TO_PKT(STRATUM_UNSPEC); 1132290000Sglebius x_pkt->ppoll = 8; 1133290000Sglebius /* FIXME! Modus broadcast + adr. check -> bdr. pkt */ 1134290000Sglebius set_li_vn_mode(x_pkt, LEAP_NOTINSYNC, ntpver, 3); 1135290000Sglebius if (pkt_key != NULL) { 1136290000Sglebius x_pkt->exten[0] = htonl(key_id); 1137290000Sglebius mac_size = 20; /* max room for MAC */ 1138294904Sdelphij mac_size = make_mac(x_pkt, pkt_len, mac_size, 1139290000Sglebius pkt_key, (char *)&x_pkt->exten[1]); 1140290000Sglebius if (mac_size > 0) 1141290000Sglebius pkt_len += mac_size + 4; 1142290000Sglebius } 1143290000Sglebius return pkt_len; 1144290000Sglebius} 1145132451Sroberto 1146132451Sroberto 1147290000Sglebiusint 1148290000Sglebiushandle_pkt( 1149290000Sglebius int rpktl, 1150290000Sglebius struct pkt * rpkt, 1151290000Sglebius sockaddr_u * host, 1152290000Sglebius const char * hostname 1153290000Sglebius ) 1154290000Sglebius{ 1155290000Sglebius char disptxt[32]; 1156290000Sglebius const char * addrtxt; 1157290000Sglebius struct timeval tv_dst; 1158290000Sglebius int cnt; 1159290000Sglebius int sw_case; 1160290000Sglebius int digits; 1161290000Sglebius int stratum; 1162290000Sglebius char * ref; 1163290000Sglebius char * ts_str; 1164290000Sglebius const char * leaptxt; 1165290000Sglebius double offset; 1166290000Sglebius double precision; 1167290000Sglebius double synch_distance; 1168290000Sglebius char * p_SNTP_PRETEND_TIME; 1169290000Sglebius time_t pretend_time; 1170290000Sglebius#if SIZEOF_TIME_T == 8 1171290000Sglebius long long ll; 1172290000Sglebius#else 1173290000Sglebius long l; 1174290000Sglebius#endif 1175132451Sroberto 1176290000Sglebius ts_str = NULL; 1177132451Sroberto 1178290000Sglebius if (rpktl > 0) 1179290000Sglebius sw_case = 1; 1180290000Sglebius else 1181290000Sglebius sw_case = rpktl; 1182132451Sroberto 1183290000Sglebius switch (sw_case) { 1184132451Sroberto 1185290000Sglebius case SERVER_UNUSEABLE: 1186290000Sglebius return -1; 1187290000Sglebius break; 1188132451Sroberto 1189290000Sglebius case PACKET_UNUSEABLE: 1190290000Sglebius break; 1191132451Sroberto 1192290000Sglebius case SERVER_AUTH_FAIL: 1193290000Sglebius break; 1194132451Sroberto 1195290000Sglebius case KOD_DEMOBILIZE: 1196290000Sglebius /* Received a DENY or RESTR KOD packet */ 1197290000Sglebius addrtxt = stoa(host); 1198290000Sglebius ref = (char *)&rpkt->refid; 1199290000Sglebius add_entry(addrtxt, ref); 1200290000Sglebius msyslog(LOG_WARNING, "KOD code %c%c%c%c from %s %s", 1201290000Sglebius ref[0], ref[1], ref[2], ref[3], addrtxt, hostname); 1202290000Sglebius break; 1203132451Sroberto 1204290000Sglebius case KOD_RATE: 1205290000Sglebius /* 1206290000Sglebius ** Hmm... 1207290000Sglebius ** We should probably call add_entry() with an 1208290000Sglebius ** expiration timestamp of several seconds in the future, 1209290000Sglebius ** and back-off even more if we get more RATE responses. 1210290000Sglebius */ 1211290000Sglebius break; 1212132451Sroberto 1213290000Sglebius case 1: 1214290000Sglebius TRACE(3, ("handle_pkt: %d bytes from %s %s\n", 1215290000Sglebius rpktl, stoa(host), hostname)); 1216132451Sroberto 1217290000Sglebius gettimeofday_cached(base, &tv_dst); 1218132451Sroberto 1219290000Sglebius p_SNTP_PRETEND_TIME = getenv("SNTP_PRETEND_TIME"); 1220290000Sglebius if (p_SNTP_PRETEND_TIME) { 1221290000Sglebius pretend_time = 0; 1222290000Sglebius#if SIZEOF_TIME_T == 4 1223290000Sglebius if (1 == sscanf(p_SNTP_PRETEND_TIME, "%ld", &l)) 1224290000Sglebius pretend_time = (time_t)l; 1225290000Sglebius#elif SIZEOF_TIME_T == 8 1226290000Sglebius if (1 == sscanf(p_SNTP_PRETEND_TIME, "%lld", &ll)) 1227290000Sglebius pretend_time = (time_t)ll; 1228290000Sglebius#else 1229290000Sglebius# include "GRONK: unexpected value for SIZEOF_TIME_T" 1230290000Sglebius#endif 1231290000Sglebius if (0 != pretend_time) 1232290000Sglebius tv_dst.tv_sec = pretend_time; 1233290000Sglebius } 1234132451Sroberto 1235290000Sglebius offset_calculation(rpkt, rpktl, &tv_dst, &offset, 1236290000Sglebius &precision, &synch_distance); 1237290000Sglebius time_derived = TRUE; 1238132451Sroberto 1239290000Sglebius for (digits = 0; (precision *= 10.) < 1.; ++digits) 1240290000Sglebius /* empty */ ; 1241290000Sglebius if (digits > 6) 1242290000Sglebius digits = 6; 1243132451Sroberto 1244290000Sglebius ts_str = tv_to_str(&tv_dst); 1245290000Sglebius stratum = rpkt->stratum; 1246290000Sglebius if (0 == stratum) 1247290000Sglebius stratum = 16; 1248132451Sroberto 1249290000Sglebius if (synch_distance > 0.) { 1250290000Sglebius cnt = snprintf(disptxt, sizeof(disptxt), 1251290000Sglebius " +/- %f", synch_distance); 1252290000Sglebius if ((size_t)cnt >= sizeof(disptxt)) 1253290000Sglebius snprintf(disptxt, sizeof(disptxt), 1254290000Sglebius "ERROR %d >= %d", cnt, 1255290000Sglebius (int)sizeof(disptxt)); 1256290000Sglebius } else { 1257290000Sglebius disptxt[0] = '\0'; 1258290000Sglebius } 1259132451Sroberto 1260290000Sglebius switch (PKT_LEAP(rpkt->li_vn_mode)) { 1261290000Sglebius case LEAP_NOWARNING: 1262290000Sglebius leaptxt = "no-leap"; 1263290000Sglebius break; 1264290000Sglebius case LEAP_ADDSECOND: 1265290000Sglebius leaptxt = "add-leap"; 1266290000Sglebius break; 1267290000Sglebius case LEAP_DELSECOND: 1268290000Sglebius leaptxt = "del-leap"; 1269290000Sglebius break; 1270290000Sglebius case LEAP_NOTINSYNC: 1271290000Sglebius leaptxt = "unsync"; 1272290000Sglebius break; 1273290000Sglebius default: 1274290000Sglebius leaptxt = "LEAP-ERROR"; 1275290000Sglebius break; 1276290000Sglebius } 1277132451Sroberto 1278290000Sglebius msyslog(LOG_INFO, "%s %+.*f%s %s s%d %s%s", ts_str, 1279290000Sglebius digits, offset, disptxt, 1280290000Sglebius hostnameaddr(hostname, host), stratum, 1281290000Sglebius leaptxt, 1282290000Sglebius (time_adjusted) 1283290000Sglebius ? " [excess]" 1284290000Sglebius : ""); 1285290000Sglebius free(ts_str); 1286132451Sroberto 1287290000Sglebius if (p_SNTP_PRETEND_TIME) 1288290000Sglebius return 0; 1289132451Sroberto 1290290000Sglebius if (!time_adjusted && 1291290000Sglebius (ENABLED_OPT(STEP) || ENABLED_OPT(SLEW))) 1292290000Sglebius return set_time(offset); 1293132451Sroberto 1294290000Sglebius return EX_OK; 1295290000Sglebius } 1296132451Sroberto 1297290000Sglebius return 1; 1298290000Sglebius} 1299132451Sroberto 1300132451Sroberto 1301290000Sglebiusvoid 1302290000Sglebiusoffset_calculation( 1303290000Sglebius struct pkt *rpkt, 1304290000Sglebius int rpktl, 1305290000Sglebius struct timeval *tv_dst, 1306290000Sglebius double *offset, 1307290000Sglebius double *precision, 1308290000Sglebius double *synch_distance 1309290000Sglebius ) 1310290000Sglebius{ 1311290000Sglebius l_fp p_rec, p_xmt, p_ref, p_org, tmp, dst; 1312290000Sglebius u_fp p_rdly, p_rdsp; 1313290000Sglebius double t21, t34, delta; 1314132451Sroberto 1315290000Sglebius /* Convert timestamps from network to host byte order */ 1316290000Sglebius p_rdly = NTOHS_FP(rpkt->rootdelay); 1317290000Sglebius p_rdsp = NTOHS_FP(rpkt->rootdisp); 1318290000Sglebius NTOHL_FP(&rpkt->reftime, &p_ref); 1319290000Sglebius NTOHL_FP(&rpkt->org, &p_org); 1320290000Sglebius NTOHL_FP(&rpkt->rec, &p_rec); 1321290000Sglebius NTOHL_FP(&rpkt->xmt, &p_xmt); 1322132451Sroberto 1323290000Sglebius *precision = LOGTOD(rpkt->precision); 1324132451Sroberto 1325290000Sglebius TRACE(3, ("offset_calculation: LOGTOD(rpkt->precision): %f\n", *precision)); 1326132451Sroberto 1327290000Sglebius /* Compute offset etc. */ 1328290000Sglebius tmp = p_rec; 1329290000Sglebius L_SUB(&tmp, &p_org); 1330290000Sglebius LFPTOD(&tmp, t21); 1331290000Sglebius TVTOTS(tv_dst, &dst); 1332290000Sglebius dst.l_ui += JAN_1970; 1333290000Sglebius tmp = p_xmt; 1334290000Sglebius L_SUB(&tmp, &dst); 1335290000Sglebius LFPTOD(&tmp, t34); 1336290000Sglebius *offset = (t21 + t34) / 2.; 1337290000Sglebius delta = t21 - t34; 1338132451Sroberto 1339290000Sglebius // synch_distance is: 1340290000Sglebius // (peer->delay + peer->rootdelay) / 2 + peer->disp 1341290000Sglebius // + peer->rootdisp + clock_phi * (current_time - peer->update) 1342290000Sglebius // + peer->jitter; 1343290000Sglebius // 1344290000Sglebius // and peer->delay = fabs(peer->offset - p_offset) * 2; 1345290000Sglebius // and peer->offset needs history, so we're left with 1346290000Sglebius // p_offset = (t21 + t34) / 2.; 1347290000Sglebius // peer->disp = 0; (we have no history to augment this) 1348290000Sglebius // clock_phi = 15e-6; 1349290000Sglebius // peer->jitter = LOGTOD(sys_precision); (we have no history to augment this) 1350290000Sglebius // and ntp_proto.c:set_sys_tick_precision() should get us sys_precision. 1351290000Sglebius // 1352290000Sglebius // so our answer seems to be: 1353290000Sglebius // 1354290000Sglebius // (fabs(t21 + t34) + peer->rootdelay) / 3. 1355290000Sglebius // + 0 (peer->disp) 1356290000Sglebius // + peer->rootdisp 1357290000Sglebius // + 15e-6 (clock_phi) 1358290000Sglebius // + LOGTOD(sys_precision) 1359132451Sroberto 1360290000Sglebius INSIST( FPTOD(p_rdly) >= 0. ); 1361290000Sglebius#if 1 1362290000Sglebius *synch_distance = (fabs(t21 + t34) + FPTOD(p_rdly)) / 3. 1363290000Sglebius + 0. 1364290000Sglebius + FPTOD(p_rdsp) 1365290000Sglebius + 15e-6 1366290000Sglebius + 0. /* LOGTOD(sys_precision) when we can get it */ 1367290000Sglebius ; 1368290000Sglebius INSIST( *synch_distance >= 0. ); 1369290000Sglebius#else 1370290000Sglebius *synch_distance = (FPTOD(p_rdly) + FPTOD(p_rdsp))/2.0; 1371290000Sglebius#endif 1372132451Sroberto 1373290000Sglebius#ifdef DEBUG 1374290000Sglebius if (debug > 3) { 1375290000Sglebius printf("sntp rootdelay: %f\n", FPTOD(p_rdly)); 1376290000Sglebius printf("sntp rootdisp: %f\n", FPTOD(p_rdsp)); 1377290000Sglebius printf("sntp syncdist: %f\n", *synch_distance); 1378132451Sroberto 1379290000Sglebius pkt_output(rpkt, rpktl, stdout); 1380132451Sroberto 1381290000Sglebius printf("sntp offset_calculation: rpkt->reftime:\n"); 1382290000Sglebius l_fp_output(&p_ref, stdout); 1383290000Sglebius printf("sntp offset_calculation: rpkt->org:\n"); 1384290000Sglebius l_fp_output(&p_org, stdout); 1385290000Sglebius printf("sntp offset_calculation: rpkt->rec:\n"); 1386290000Sglebius l_fp_output(&p_rec, stdout); 1387290000Sglebius printf("sntp offset_calculation: rpkt->xmt:\n"); 1388290000Sglebius l_fp_output(&p_xmt, stdout); 1389290000Sglebius } 1390290000Sglebius#endif 1391132451Sroberto 1392290000Sglebius TRACE(3, ("sntp offset_calculation:\trec - org t21: %.6f\n" 1393290000Sglebius "\txmt - dst t34: %.6f\tdelta: %.6f\toffset: %.6f\n", 1394290000Sglebius t21, t34, delta, *offset)); 1395132451Sroberto 1396290000Sglebius return; 1397290000Sglebius} 1398132451Sroberto 1399132451Sroberto 1400132451Sroberto 1401290000Sglebius/* Compute the 8 bits for li_vn_mode */ 1402290000Sglebiusvoid 1403290000Sglebiusset_li_vn_mode ( 1404290000Sglebius struct pkt *spkt, 1405290000Sglebius char leap, 1406290000Sglebius char version, 1407290000Sglebius char mode 1408290000Sglebius ) 1409290000Sglebius{ 1410290000Sglebius if (leap > 3) { 1411290000Sglebius msyslog(LOG_DEBUG, "set_li_vn_mode: leap > 3, using max. 3"); 1412290000Sglebius leap = 3; 1413290000Sglebius } 1414132451Sroberto 1415290000Sglebius if ((unsigned char)version > 7) { 1416290000Sglebius msyslog(LOG_DEBUG, "set_li_vn_mode: version < 0 or > 7, using 4"); 1417290000Sglebius version = 4; 1418290000Sglebius } 1419132451Sroberto 1420290000Sglebius if (mode > 7) { 1421290000Sglebius msyslog(LOG_DEBUG, "set_li_vn_mode: mode > 7, using client mode 3"); 1422290000Sglebius mode = 3; 1423290000Sglebius } 1424132451Sroberto 1425290000Sglebius spkt->li_vn_mode = leap << 6; 1426290000Sglebius spkt->li_vn_mode |= version << 3; 1427290000Sglebius spkt->li_vn_mode |= mode; 1428132451Sroberto} 1429132451Sroberto 1430132451Sroberto 1431290000Sglebius/* 1432290000Sglebius** set_time applies 'offset' to the local clock. 1433290000Sglebius*/ 1434290000Sglebiusint 1435290000Sglebiusset_time( 1436290000Sglebius double offset 1437290000Sglebius ) 1438290000Sglebius{ 1439290000Sglebius int rc; 1440132451Sroberto 1441290000Sglebius if (time_adjusted) 1442290000Sglebius return EX_OK; 1443132451Sroberto 1444290000Sglebius /* 1445290000Sglebius ** If we can step but we cannot slew, then step. 1446290000Sglebius ** If we can step or slew and and |offset| > steplimit, then step. 1447290000Sglebius */ 1448290000Sglebius if (ENABLED_OPT(STEP) && 1449290000Sglebius ( !ENABLED_OPT(SLEW) 1450290000Sglebius || (ENABLED_OPT(SLEW) && (fabs(offset) > steplimit)) 1451290000Sglebius )) { 1452290000Sglebius rc = step_systime(offset); 1453132451Sroberto 1454290000Sglebius /* If there was a problem, can we rely on errno? */ 1455290000Sglebius if (1 == rc) 1456290000Sglebius time_adjusted = TRUE; 1457290000Sglebius return (time_adjusted) 1458290000Sglebius ? EX_OK 1459290000Sglebius : 1; 1460290000Sglebius /* 1461290000Sglebius ** In case of error, what should we use? 1462290000Sglebius ** EX_UNAVAILABLE? 1463290000Sglebius ** EX_OSERR? 1464290000Sglebius ** EX_NOPERM? 1465290000Sglebius */ 1466290000Sglebius } 1467132451Sroberto 1468290000Sglebius if (ENABLED_OPT(SLEW)) { 1469290000Sglebius rc = adj_systime(offset); 1470132451Sroberto 1471290000Sglebius /* If there was a problem, can we rely on errno? */ 1472290000Sglebius if (1 == rc) 1473290000Sglebius time_adjusted = TRUE; 1474290000Sglebius return (time_adjusted) 1475290000Sglebius ? EX_OK 1476290000Sglebius : 1; 1477290000Sglebius /* 1478290000Sglebius ** In case of error, what should we use? 1479290000Sglebius ** EX_UNAVAILABLE? 1480290000Sglebius ** EX_OSERR? 1481290000Sglebius ** EX_NOPERM? 1482290000Sglebius */ 1483290000Sglebius } 1484132451Sroberto 1485290000Sglebius return EX_SOFTWARE; 1486290000Sglebius} 1487132451Sroberto 1488132451Sroberto 1489290000Sglebiusint 1490290000Sglebiuslibevent_version_ok(void) 1491290000Sglebius{ 1492290000Sglebius ev_uint32_t v_compile_maj; 1493290000Sglebius ev_uint32_t v_run_maj; 1494132451Sroberto 1495290000Sglebius v_compile_maj = LIBEVENT_VERSION_NUMBER & 0xffff0000; 1496290000Sglebius v_run_maj = event_get_version_number() & 0xffff0000; 1497290000Sglebius if (v_compile_maj != v_run_maj) { 1498290000Sglebius fprintf(stderr, 1499290000Sglebius "Incompatible libevent versions: have %s, built with %s\n", 1500290000Sglebius event_get_version(), 1501290000Sglebius LIBEVENT_VERSION); 1502290000Sglebius return 0; 1503290000Sglebius } 1504290000Sglebius return 1; 1505290000Sglebius} 1506132451Sroberto 1507290000Sglebius/* 1508290000Sglebius * gettimeofday_cached() 1509290000Sglebius * 1510290000Sglebius * Clones the event_base_gettimeofday_cached() interface but ensures the 1511290000Sglebius * times are always on the gettimeofday() 1970 scale. Older libevent 2 1512290000Sglebius * sometimes used gettimeofday(), sometimes the since-system-start 1513290000Sglebius * clock_gettime(CLOCK_MONOTONIC), depending on the platform. 1514290000Sglebius * 1515290000Sglebius * It is not cleanly possible to tell which timescale older libevent is 1516290000Sglebius * using. 1517290000Sglebius * 1518290000Sglebius * The strategy involves 1 hour thresholds chosen to be far longer than 1519290000Sglebius * the duration of a round of libevent callbacks, which share a cached 1520290000Sglebius * start-of-round time. First compare the last cached time with the 1521290000Sglebius * current gettimeofday() time. If they are within one hour, libevent 1522290000Sglebius * is using the proper timescale so leave the offset 0. Otherwise, 1523290000Sglebius * compare libevent's cached time and the current time on the monotonic 1524290000Sglebius * scale. If they are within an hour, libevent is using the monotonic 1525290000Sglebius * scale so calculate the offset to add to such times to bring them to 1526290000Sglebius * gettimeofday()'s scale. 1527290000Sglebius */ 1528290000Sglebiusint 1529290000Sglebiusgettimeofday_cached( 1530290000Sglebius struct event_base * b, 1531290000Sglebius struct timeval * caller_tv 1532290000Sglebius ) 1533290000Sglebius{ 1534290000Sglebius#if defined(_EVENT_HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) 1535290000Sglebius static struct event_base * cached_b; 1536290000Sglebius static struct timeval cached; 1537290000Sglebius static struct timeval adj_cached; 1538290000Sglebius static struct timeval offset; 1539290000Sglebius static int offset_ready; 1540290000Sglebius struct timeval latest; 1541290000Sglebius struct timeval systemt; 1542290000Sglebius struct timespec ts; 1543290000Sglebius struct timeval mono; 1544290000Sglebius struct timeval diff; 1545290000Sglebius int cgt_rc; 1546290000Sglebius int gtod_rc; 1547132451Sroberto 1548290000Sglebius event_base_gettimeofday_cached(b, &latest); 1549290000Sglebius if (b == cached_b && 1550290000Sglebius !memcmp(&latest, &cached, sizeof(latest))) { 1551290000Sglebius *caller_tv = adj_cached; 1552290000Sglebius return 0; 1553290000Sglebius } 1554290000Sglebius cached = latest; 1555290000Sglebius cached_b = b; 1556290000Sglebius if (!offset_ready) { 1557290000Sglebius cgt_rc = clock_gettime(CLOCK_MONOTONIC, &ts); 1558290000Sglebius gtod_rc = gettimeofday(&systemt, NULL); 1559290000Sglebius if (0 != gtod_rc) { 1560290000Sglebius msyslog(LOG_ERR, 1561290000Sglebius "%s: gettimeofday() error %m", 1562290000Sglebius progname); 1563290000Sglebius exit(1); 1564290000Sglebius } 1565290000Sglebius diff = sub_tval(systemt, latest); 1566290000Sglebius if (debug > 1) 1567290000Sglebius printf("system minus cached %+ld.%06ld\n", 1568290000Sglebius (long)diff.tv_sec, (long)diff.tv_usec); 1569290000Sglebius if (0 != cgt_rc || labs((long)diff.tv_sec) < 3600) { 1570290000Sglebius /* 1571290000Sglebius * Either use_monotonic == 0, or this libevent 1572290000Sglebius * has been repaired. Leave offset at zero. 1573290000Sglebius */ 1574290000Sglebius } else { 1575290000Sglebius mono.tv_sec = ts.tv_sec; 1576290000Sglebius mono.tv_usec = ts.tv_nsec / 1000; 1577290000Sglebius diff = sub_tval(latest, mono); 1578290000Sglebius if (debug > 1) 1579290000Sglebius printf("cached minus monotonic %+ld.%06ld\n", 1580290000Sglebius (long)diff.tv_sec, (long)diff.tv_usec); 1581290000Sglebius if (labs((long)diff.tv_sec) < 3600) { 1582290000Sglebius /* older libevent2 using monotonic */ 1583290000Sglebius offset = sub_tval(systemt, mono); 1584290000Sglebius TRACE(1, ("%s: Offsetting libevent CLOCK_MONOTONIC times by %+ld.%06ld\n", 1585290000Sglebius "gettimeofday_cached", 1586290000Sglebius (long)offset.tv_sec, 1587290000Sglebius (long)offset.tv_usec)); 1588290000Sglebius } 1589290000Sglebius } 1590290000Sglebius offset_ready = TRUE; 1591290000Sglebius } 1592290000Sglebius adj_cached = add_tval(cached, offset); 1593290000Sglebius *caller_tv = adj_cached; 1594132451Sroberto 1595290000Sglebius return 0; 1596290000Sglebius#else 1597290000Sglebius return event_base_gettimeofday_cached(b, caller_tv); 1598290000Sglebius#endif 1599132451Sroberto} 1600132451Sroberto 1601