main.c revision 294554
1161748Scperciva#include <config.h>
2161748Scperciva
3161748Scperciva#include <event2/util.h>
4173441Scperciva#include <event2/event.h>
5161748Scperciva
6161748Scperciva#include "ntp_workimpl.h"
7161748Scperciva#ifdef WORK_THREAD
8161748Scperciva# include <event2/thread.h>
9161748Scperciva#endif
10161748Scperciva
11161748Scperciva#include "main.h"
12161748Scperciva#include "ntp_libopts.h"
13161748Scperciva#include "kod_management.h"
14161748Scperciva#include "networking.h"
15161748Scperciva#include "utilities.h"
16161748Scperciva#include "log.h"
17161748Scperciva#include "libntp.h"
18161748Scperciva
19161748Scperciva
20161748Scpercivaint shutting_down;
21161748Scpercivaint time_derived;
22161748Scpercivaint time_adjusted;
23161748Scpercivaint n_pending_dns = 0;
24161748Scpercivaint n_pending_ntp = 0;
25161748Scpercivaint ai_fam_pref = AF_UNSPEC;
26161748Scpercivaint ntpver = 4;
27161748Scpercivadouble steplimit = -1;
28161748ScpercivaSOCKET sock4 = -1;		/* Socket for IPv4 */
29161748ScpercivaSOCKET sock6 = -1;		/* Socket for IPv6 */
30161748Scperciva/*
31161748Scperciva** BCAST *must* listen on port 123 (by default), so we can only
32161748Scperciva** use the UCST sockets (above) if they too are using port 123
33161748Scperciva*/
34161748ScpercivaSOCKET bsock4 = -1;		/* Broadcast Socket for IPv4 */
35161748ScpercivaSOCKET bsock6 = -1;		/* Broadcast Socket for IPv6 */
36161748Scpercivastruct event_base *base;
37161748Scpercivastruct event *ev_sock4;
38161748Scpercivastruct event *ev_sock6;
39161748Scpercivastruct event *ev_worker_timeout;
40161748Scpercivastruct event *ev_xmt_timer;
41161748Scperciva
42161748Scpercivastruct dns_ctx {
43161748Scperciva	const char *	name;
44161748Scperciva	int		flags;
45161748Scperciva#define CTX_BCST	0x0001
46161748Scperciva#define CTX_UCST	0x0002
47173564Scperciva#define CTX_xCST	0x0003
48161748Scperciva#define CTX_CONC	0x0004
49161748Scperciva#define CTX_unused	0xfffd
50161748Scperciva	int		key_id;
51161748Scperciva	struct timeval	timeout;
52161748Scperciva	struct key *	key;
53161748Scperciva};
54161748Scperciva
55161748Scpercivatypedef struct sent_pkt_tag sent_pkt;
56173564Scpercivastruct sent_pkt_tag {
57173564Scperciva	sent_pkt *		link;
58161748Scperciva	struct dns_ctx *	dctx;
59181142Scperciva	sockaddr_u		addr;
60161748Scperciva	time_t			stime;
61161748Scperciva	int			done;
62161748Scperciva	struct pkt		x_pkt;
63161748Scperciva};
64161748Scperciva
65161748Scpercivatypedef struct xmt_ctx_tag xmt_ctx;
66161748Scpercivastruct xmt_ctx_tag {
67161748Scperciva	xmt_ctx *		link;
68161748Scperciva	SOCKET			sock;
69161748Scperciva	time_t			sched;
70161748Scperciva	sent_pkt *		spkt;
71161748Scperciva};
72161748Scperciva
73161748Scpercivastruct timeval	gap;
74161748Scpercivaxmt_ctx *	xmt_q;
75161748Scpercivastruct key *	keys = NULL;
76161748Scpercivaint		response_timeout;
77161748Scpercivastruct timeval	response_tv;
78161748Scpercivastruct timeval	start_tv;
79161748Scperciva/* check the timeout at least once per second */
80161748Scpercivastruct timeval	wakeup_tv = { 0, 888888 };
81161748Scperciva
82161748Scpercivasent_pkt *	fam_listheads[2];
83161748Scperciva#define v4_pkts_list	(fam_listheads[0])
84161748Scperciva#define v6_pkts_list	(fam_listheads[1])
85161748Scperciva
86161748Scpercivastatic union {
87161748Scperciva	struct pkt pkt;
88161748Scperciva	char   buf[LEN_PKT_NOMAC + NTP_MAXEXTEN + MAX_MAC_LEN];
89161748Scperciva} rbuf;
90181142Scperciva
91196392Ssimon#define r_pkt  rbuf.pkt
92161748Scperciva
93161748Scperciva#ifdef HAVE_DROPROOT
94161748Scpercivaint droproot;			/* intres imports these */
95161748Scpercivaint root_dropped;
96161748Scperciva#endif
97161748Scpercivau_long current_time;		/* libntp/authkeys.c */
98161748Scperciva
99161748Scpercivavoid open_sockets(void);
100161748Scpercivavoid handle_lookup(const char *name, int flags);
101161748Scpercivavoid sntp_addremove_fd(int fd, int is_pipe, int remove_it);
102161748Scpercivavoid worker_timeout(evutil_socket_t, short, void *);
103161748Scpercivavoid worker_resp_cb(evutil_socket_t, short, void *);
104161748Scpercivavoid sntp_name_resolved(int, int, void *, const char *, const char *,
105161748Scperciva			const struct addrinfo *,
106161748Scperciva			const struct addrinfo *);
107161748Scpercivavoid queue_xmt(SOCKET sock, struct dns_ctx *dctx, sent_pkt *spkt,
108161748Scperciva	       u_int xmt_delay);
109161748Scpercivavoid xmt_timer_cb(evutil_socket_t, short, void *ptr);
110161748Scpercivavoid xmt(xmt_ctx *xctx);
111161748Scpercivaint  check_kod(const struct addrinfo *ai);
112161748Scpercivavoid timeout_query(sent_pkt *);
113161748Scpercivavoid timeout_queries(void);
114161748Scpercivavoid sock_cb(evutil_socket_t, short, void *);
115161748Scpercivavoid check_exit_conditions(void);
116161748Scpercivavoid sntp_libevent_log_cb(int, const char *);
117161748Scpercivavoid set_li_vn_mode(struct pkt *spkt, char leap, char version, char mode);
118161748Scpercivaint  set_time(double offset);
119161748Scpercivavoid dec_pending_ntp(const char *, sockaddr_u *);
120161748Scpercivaint  libevent_version_ok(void);
121161748Scpercivaint  gettimeofday_cached(struct event_base *b, struct timeval *tv);
122161748Scperciva
123161748Scperciva
124161748Scperciva/*
125161748Scperciva * The actual main function.
126161748Scperciva */
127161748Scpercivaint
128161748Scpercivasntp_main (
129161748Scperciva	int argc,
130161748Scperciva	char **argv,
131161748Scperciva	const char *sntpVersion
132161748Scperciva	)
133161748Scperciva{
134161748Scperciva	int			i;
135161748Scperciva	int			exitcode;
136161748Scperciva	int			optct;
137161748Scperciva	struct event_config *	evcfg;
138161748Scperciva
139161748Scperciva	/* Initialize logging system - sets up progname */
140161748Scperciva	sntp_init_logging(argv[0]);
141161748Scperciva
142161748Scperciva	if (!libevent_version_ok())
143161748Scperciva		exit(EX_SOFTWARE);
144161748Scperciva
145161748Scperciva	init_lib();
146161748Scperciva	init_auth();
147161748Scperciva
148161748Scperciva	optct = ntpOptionProcess(&sntpOptions, argc, argv);
149161748Scperciva	argc -= optct;
150161748Scperciva	argv += optct;
151161748Scperciva
152161748Scperciva
153161748Scperciva	debug = OPT_VALUE_SET_DEBUG_LEVEL;
154161748Scperciva
155161748Scperciva	TRACE(2, ("init_lib() done, %s%s\n",
156161748Scperciva		  (ipv4_works)
157161748Scperciva		      ? "ipv4_works "
158161748Scperciva		      : "",
159161748Scperciva		  (ipv6_works)
160161748Scperciva		      ? "ipv6_works "
161161748Scperciva		      : ""));
162161748Scperciva	ntpver = OPT_VALUE_NTPVERSION;
163161748Scperciva	steplimit = OPT_VALUE_STEPLIMIT / 1e3;
164161748Scperciva	gap.tv_usec = max(0, OPT_VALUE_GAP * 1000);
165161748Scperciva	gap.tv_usec = min(gap.tv_usec, 999999);
166161748Scperciva
167161748Scperciva	if (HAVE_OPT(LOGFILE))
168161748Scperciva		open_logfile(OPT_ARG(LOGFILE));
169161748Scperciva
170161748Scperciva	msyslog(LOG_INFO, "%s", sntpVersion);
171161748Scperciva
172161748Scperciva	if (0 == argc && !HAVE_OPT(BROADCAST) && !HAVE_OPT(CONCURRENT)) {
173161748Scperciva		printf("%s: Must supply at least one of -b hostname, -c hostname, or hostname.\n",
174161748Scperciva		       progname);
175161748Scperciva		exit(EX_USAGE);
176161748Scperciva	}
177161748Scperciva
178161748Scperciva
179161748Scperciva	/*
180161748Scperciva	** Eventually, we probably want:
181161748Scperciva	** - separate bcst and ucst timeouts (why?)
182161748Scperciva	** - multiple --timeout values in the commandline
183161748Scperciva	*/
184161748Scperciva
185161748Scperciva	response_timeout = OPT_VALUE_TIMEOUT;
186161748Scperciva	response_tv.tv_sec = response_timeout;
187161748Scperciva	response_tv.tv_usec = 0;
188161748Scperciva
189161748Scperciva	/* IPv6 available? */
190161748Scperciva	if (isc_net_probeipv6() != ISC_R_SUCCESS) {
191161748Scperciva		ai_fam_pref = AF_INET;
192161748Scperciva		TRACE(1, ("No ipv6 support available, forcing ipv4\n"));
193161748Scperciva	} else {
194161748Scperciva		/* Check for options -4 and -6 */
195161748Scperciva		if (HAVE_OPT(IPV4))
196161748Scperciva			ai_fam_pref = AF_INET;
197161748Scperciva		else if (HAVE_OPT(IPV6))
198161748Scperciva			ai_fam_pref = AF_INET6;
199161748Scperciva	}
200161748Scperciva
201161748Scperciva	/* TODO: Parse config file if declared */
202161748Scperciva
203161748Scperciva	/*
204161748Scperciva	** Init the KOD system.
205161748Scperciva	** For embedded systems with no writable filesystem,
206161748Scperciva	** -K /dev/null can be used to disable KoD storage.
207161748Scperciva	*/
208161748Scperciva	kod_init_kod_db(OPT_ARG(KOD), FALSE);
209161748Scperciva
210161748Scperciva	// HMS: Should we use arg-defalt for this too?
211161748Scperciva	if (HAVE_OPT(KEYFILE))
212161748Scperciva		auth_init(OPT_ARG(KEYFILE), &keys);
213161748Scperciva
214161748Scperciva	/*
215161748Scperciva	** Considering employing a variable that prevents functions of doing
216161748Scperciva	** anything until everything is initialized properly
217161748Scperciva	**
218161748Scperciva	** HMS: What exactly does the above mean?
219161748Scperciva	*/
220161748Scperciva	event_set_log_callback(&sntp_libevent_log_cb);
221161748Scperciva	if (debug > 0)
222161748Scperciva		event_enable_debug_mode();
223161748Scperciva#ifdef WORK_THREAD
224161748Scperciva	evthread_use_pthreads();
225161748Scperciva	/* we use libevent from main thread only, locks should be academic */
226161748Scperciva	if (debug > 0)
227181142Scperciva		evthread_enable_lock_debuging();
228181142Scperciva#endif
229181142Scperciva	evcfg = event_config_new();
230181142Scperciva	if (NULL == evcfg) {
231181142Scperciva		printf("%s: event_config_new() failed!\n", progname);
232181142Scperciva		return -1;
233181142Scperciva	}
234161748Scperciva#ifndef HAVE_SOCKETPAIR
235161748Scperciva	event_config_require_features(evcfg, EV_FEATURE_FDS);
236161748Scperciva#endif
237161748Scperciva	/* all libevent calls are from main thread */
238161748Scperciva	/* event_config_set_flag(evcfg, EVENT_BASE_FLAG_NOLOCK); */
239161748Scperciva	base = event_base_new_with_config(evcfg);
240161748Scperciva	event_config_free(evcfg);
241161748Scperciva	if (NULL == base) {
242173564Scperciva		printf("%s: event_base_new() failed!\n", progname);
243173564Scperciva		return -1;
244173564Scperciva	}
245173564Scperciva
246173564Scperciva	/* wire into intres resolver */
247173564Scperciva	worker_per_query = TRUE;
248173564Scperciva	addremove_io_fd = &sntp_addremove_fd;
249173564Scperciva
250161748Scperciva	open_sockets();
251161748Scperciva
252161748Scperciva	if (HAVE_OPT(BROADCAST)) {
253161748Scperciva		int		cn = STACKCT_OPT(  BROADCAST );
254161748Scperciva		const char **	cp = STACKLST_OPT( BROADCAST );
255161748Scperciva
256161748Scperciva		while (cn-- > 0) {
257161748Scperciva			handle_lookup(*cp, CTX_BCST);
258161748Scperciva			cp++;
259173564Scperciva		}
260173564Scperciva	}
261173564Scperciva
262173564Scperciva	if (HAVE_OPT(CONCURRENT)) {
263173564Scperciva		int		cn = STACKCT_OPT( CONCURRENT );
264173564Scperciva		const char **	cp = STACKLST_OPT( CONCURRENT );
265173564Scperciva
266173564Scperciva		while (cn-- > 0) {
267173564Scperciva			handle_lookup(*cp, CTX_UCST | CTX_CONC);
268173564Scperciva			cp++;
269173564Scperciva		}
270173564Scperciva	}
271173564Scperciva
272173564Scperciva	for (i = 0; i < argc; ++i)
273173564Scperciva		handle_lookup(argv[i], CTX_UCST);
274173564Scperciva
275173564Scperciva	gettimeofday_cached(base, &start_tv);
276173564Scperciva	event_base_dispatch(base);
277173564Scperciva	event_base_free(base);
278173564Scperciva
279173564Scperciva	if (!time_adjusted &&
280173564Scperciva	    (ENABLED_OPT(STEP) || ENABLED_OPT(SLEW)))
281173564Scperciva		exitcode = 1;
282173564Scperciva	else
283173564Scperciva		exitcode = 0;
284173564Scperciva
285173564Scperciva	return exitcode;
286173564Scperciva}
287173564Scperciva
288173564Scperciva
289161748Scperciva/*
290161748Scperciva** open sockets and make them non-blocking
291161748Scperciva*/
292161748Scpercivavoid
293161748Scpercivaopen_sockets(
294161748Scperciva	void
295161748Scperciva	)
296161748Scperciva{
297161748Scperciva	sockaddr_u	name;
298161748Scperciva
299161748Scperciva	if (-1 == sock4) {
300161748Scperciva		sock4 = socket(PF_INET, SOCK_DGRAM, 0);
301161748Scperciva		if (-1 == sock4) {
302161748Scperciva			/* error getting a socket */
303161748Scperciva			msyslog(LOG_ERR, "open_sockets: socket(PF_INET) failed: %m");
304161748Scperciva			exit(1);
305161748Scperciva		}
306161748Scperciva		/* Make it non-blocking */
307161748Scperciva		make_socket_nonblocking(sock4);
308161748Scperciva
309161748Scperciva		/* Let's try using a wildcard... */
310161748Scperciva		ZERO(name);
311196392Ssimon		AF(&name) = AF_INET;
312196392Ssimon		SET_ADDR4N(&name, INADDR_ANY);
313196392Ssimon		SET_PORT(&name, (HAVE_OPT(USERESERVEDPORT) ? 123 : 0));
314196392Ssimon
315196392Ssimon		if (-1 == bind(sock4, &name.sa,
316196392Ssimon			       SOCKLEN(&name))) {
317196392Ssimon			msyslog(LOG_ERR, "open_sockets: bind(sock4) failed: %m");
318196392Ssimon			exit(1);
319196392Ssimon		}
320196392Ssimon
321196392Ssimon		/* Register an NTP callback for recv/timeout */
322196392Ssimon		ev_sock4 = event_new(base, sock4,
323196392Ssimon				     EV_TIMEOUT | EV_READ | EV_PERSIST,
324196392Ssimon				     &sock_cb, NULL);
325196392Ssimon		if (NULL == ev_sock4) {
326196392Ssimon			msyslog(LOG_ERR,
327196392Ssimon				"open_sockets: event_new(base, sock4) failed!");
328196392Ssimon		} else {
329196392Ssimon			event_add(ev_sock4, &wakeup_tv);
330196392Ssimon		}
331196392Ssimon	}
332196392Ssimon
333196392Ssimon	/* We may not always have IPv6... */
334196392Ssimon	if (-1 == sock6 && ipv6_works) {
335196392Ssimon		sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
336196392Ssimon		if (-1 == sock6 && ipv6_works) {
337196392Ssimon			/* error getting a socket */
338196392Ssimon			msyslog(LOG_ERR, "open_sockets: socket(PF_INET6) failed: %m");
339196392Ssimon			exit(1);
340196392Ssimon		}
341196392Ssimon		/* Make it non-blocking */
342196392Ssimon		make_socket_nonblocking(sock6);
343196392Ssimon
344196392Ssimon		/* Let's try using a wildcard... */
345196392Ssimon		ZERO(name);
346196392Ssimon		AF(&name) = AF_INET6;
347196392Ssimon		SET_ADDR6N(&name, in6addr_any);
348196392Ssimon		SET_PORT(&name, (HAVE_OPT(USERESERVEDPORT) ? 123 : 0));
349196392Ssimon
350196392Ssimon		if (-1 == bind(sock6, &name.sa,
351196392Ssimon			       SOCKLEN(&name))) {
352196392Ssimon			msyslog(LOG_ERR, "open_sockets: bind(sock6) failed: %m");
353196392Ssimon			exit(1);
354196392Ssimon		}
355196392Ssimon		/* Register an NTP callback for recv/timeout */
356196392Ssimon		ev_sock6 = event_new(base, sock6,
357196392Ssimon				     EV_TIMEOUT | EV_READ | EV_PERSIST,
358196392Ssimon				     &sock_cb, NULL);
359196392Ssimon		if (NULL == ev_sock6) {
360196392Ssimon			msyslog(LOG_ERR,
361196392Ssimon				"open_sockets: event_new(base, sock6) failed!");
362196392Ssimon		} else {
363196392Ssimon			event_add(ev_sock6, &wakeup_tv);
364196392Ssimon		}
365196392Ssimon	}
366196392Ssimon
367196392Ssimon	return;
368196392Ssimon}
369196392Ssimon
370196392Ssimon
371196392Ssimon/*
372196392Ssimon** handle_lookup
373196392Ssimon*/
374196392Ssimonvoid
375161748Scpercivahandle_lookup(
376161748Scperciva	const char *name,
377161748Scperciva	int flags
378161748Scperciva	)
379161748Scperciva{
380161748Scperciva	struct addrinfo	hints;	/* Local copy is OK */
381161748Scperciva	struct dns_ctx *ctx;
382161748Scperciva	long		l;
383161748Scperciva	char *		name_copy;
384161748Scperciva	size_t		name_sz;
385161748Scperciva	size_t		octets;
386161748Scperciva
387161748Scperciva	TRACE(1, ("handle_lookup(%s,%#x)\n", name, flags));
388161748Scperciva
389161748Scperciva	ZERO(hints);
390161748Scperciva	hints.ai_family = ai_fam_pref;
391161748Scperciva	hints.ai_flags = AI_CANONNAME | Z_AI_NUMERICSERV;
392161748Scperciva	/*
393161748Scperciva	** Unless we specify a socktype, we'll get at least two
394161748Scperciva	** entries for each address: one for TCP and one for
395161748Scperciva	** UDP. That's not what we want.
396161748Scperciva	*/
397161748Scperciva	hints.ai_socktype = SOCK_DGRAM;
398161748Scperciva	hints.ai_protocol = IPPROTO_UDP;
399161748Scperciva
400161748Scperciva	name_sz = 1 + strlen(name);
401161748Scperciva	octets = sizeof(*ctx) + name_sz;	// Space for a ctx and the name
402161748Scperciva	ctx = emalloc_zero(octets);		// ctx at ctx[0]
403161748Scperciva	name_copy = (char *)(ctx + 1);		// Put the name at ctx[1]
404161748Scperciva	memcpy(name_copy, name, name_sz);	// copy the name to ctx[1]
405161748Scperciva	ctx->name = name_copy;			// point to it...
406161748Scperciva	ctx->flags = flags;
407161748Scperciva	ctx->timeout = response_tv;
408161748Scperciva
409161748Scperciva	/* The following should arguably be passed in... */
410161748Scperciva	if (ENABLED_OPT(AUTHENTICATION) &&
411161748Scperciva	    atoint(OPT_ARG(AUTHENTICATION), &l)) {
412161748Scperciva		ctx->key_id = l;
413161748Scperciva		get_key(ctx->key_id, &ctx->key);
414161748Scperciva	} else {
415161748Scperciva		ctx->key_id = -1;
416161748Scperciva		ctx->key = NULL;
417161748Scperciva	}
418161748Scperciva
419161748Scperciva	++n_pending_dns;
420161748Scperciva	getaddrinfo_sometime(name, "123", &hints, 0,
421161748Scperciva			     &sntp_name_resolved, ctx);
422161748Scperciva}
423161748Scperciva
424161748Scperciva
425161748Scperciva/*
426161748Scperciva** DNS Callback:
427161748Scperciva** - For each IP:
428161748Scperciva** - - open a socket
429173564Scperciva** - - increment n_pending_ntp
430173564Scperciva** - - send a request if this is a Unicast callback
431173564Scperciva** - - queue wait for response
432173564Scperciva** - decrement n_pending_dns
433161748Scperciva*/
434161748Scpercivavoid
435161748Scpercivasntp_name_resolved(
436161748Scperciva	int			rescode,
437161748Scperciva	int			gai_errno,
438161748Scperciva	void *			context,
439161748Scperciva	const char *		name,
440161748Scperciva	const char *		service,
441161748Scperciva	const struct addrinfo *	hints,
442161748Scperciva	const struct addrinfo *	addr
443161748Scperciva	)
444161748Scperciva{
445161748Scperciva	struct dns_ctx *	dctx;
446161748Scperciva	sent_pkt *		spkt;
447161748Scperciva	const struct addrinfo *	ai;
448161748Scperciva	SOCKET			sock;
449161748Scperciva	u_int			xmt_delay_v4;
450161748Scperciva	u_int			xmt_delay_v6;
451181142Scperciva	u_int			xmt_delay;
452161748Scperciva	size_t			octets;
453161748Scperciva
454161748Scperciva	xmt_delay_v4 = 0;
455161748Scperciva	xmt_delay_v6 = 0;
456161748Scperciva	dctx = context;
457161748Scperciva	if (rescode) {
458161748Scperciva#ifdef EAI_SYSTEM
459161748Scperciva		if (EAI_SYSTEM == rescode) {
460161748Scperciva			errno = gai_errno;
461161748Scperciva			mfprintf(stderr, "%s lookup error %m\n",
462161748Scperciva				 dctx->name);
463161748Scperciva		} else
464161748Scperciva#endif
465161748Scperciva			fprintf(stderr, "%s lookup error %s\n",
466161748Scperciva				dctx->name, gai_strerror(rescode));
467161748Scperciva	} else {
468161748Scperciva		TRACE(3, ("%s [%s]\n", dctx->name,
469161748Scperciva			  (addr->ai_canonname != NULL)
470161748Scperciva			      ? addr->ai_canonname
471161748Scperciva			      : ""));
472161748Scperciva
473161748Scperciva		for (ai = addr; ai != NULL; ai = ai->ai_next) {
474161748Scperciva
475161748Scperciva			if (check_kod(ai))
476161748Scperciva				continue;
477161748Scperciva
478161748Scperciva			switch (ai->ai_family) {
479161748Scperciva
480161748Scperciva			case AF_INET:
481161748Scperciva				sock = sock4;
482161748Scperciva				xmt_delay = xmt_delay_v4;
483161748Scperciva				xmt_delay_v4++;
484161748Scperciva				break;
485161748Scperciva
486161748Scperciva			case AF_INET6:
487161748Scperciva				if (!ipv6_works)
488161748Scperciva					continue;
489161748Scperciva
490161748Scperciva				sock = sock6;
491161748Scperciva				xmt_delay = xmt_delay_v6;
492161748Scperciva				xmt_delay_v6++;
493161748Scperciva				break;
494161748Scperciva
495161748Scperciva			default:
496161748Scperciva				msyslog(LOG_ERR, "sntp_name_resolved: unexpected ai_family: %d",
497161748Scperciva					ai->ai_family);
498161748Scperciva				exit(1);
499161748Scperciva				break;
500161748Scperciva			}
501161748Scperciva
502161748Scperciva			/*
503161748Scperciva			** We're waiting for a response for either unicast
504161748Scperciva			** or broadcast, so...
505161748Scperciva			*/
506161748Scperciva			++n_pending_ntp;
507161748Scperciva
508161748Scperciva			/* If this is for a unicast IP, queue a request */
509161748Scperciva			if (dctx->flags & CTX_UCST) {
510161748Scperciva				spkt = emalloc_zero(sizeof(*spkt));
511161748Scperciva				spkt->dctx = dctx;
512161748Scperciva				octets = min(ai->ai_addrlen, sizeof(spkt->addr));
513161748Scperciva				memcpy(&spkt->addr, ai->ai_addr, octets);
514161748Scperciva				queue_xmt(sock, dctx, spkt, xmt_delay);
515161748Scperciva			}
516161748Scperciva		}
517161748Scperciva	}
518161748Scperciva	/* n_pending_dns really should be >0 here... */
519161748Scperciva	--n_pending_dns;
520161748Scperciva	check_exit_conditions();
521161748Scperciva}
522161748Scperciva
523161748Scperciva
524161748Scperciva/*
525161748Scperciva** queue_xmt
526161748Scperciva*/
527173564Scpercivavoid
528196392Ssimonqueue_xmt(
529196392Ssimon	SOCKET			sock,
530196392Ssimon	struct dns_ctx *	dctx,
531161748Scperciva	sent_pkt *		spkt,
532161748Scperciva	u_int			xmt_delay
533161748Scperciva	)
534161748Scperciva{
535161748Scperciva	sockaddr_u *	dest;
536161748Scperciva	sent_pkt **	pkt_listp;
537161748Scperciva	sent_pkt *	match;
538161748Scperciva	xmt_ctx *	xctx;
539161748Scperciva	struct timeval	start_cb;
540161748Scperciva	struct timeval	delay;
541161748Scperciva
542161748Scperciva	dest = &spkt->addr;
543161748Scperciva	if (IS_IPV6(dest))
544161748Scperciva		pkt_listp = &v6_pkts_list;
545161748Scperciva	else
546161748Scperciva		pkt_listp = &v4_pkts_list;
547161748Scperciva
548161748Scperciva	/* reject attempts to add address already listed */
549161748Scperciva	for (match = *pkt_listp; match != NULL; match = match->link) {
550161748Scperciva		if (ADDR_PORT_EQ(&spkt->addr, &match->addr)) {
551161748Scperciva			if (strcasecmp(spkt->dctx->name,
552161748Scperciva				       match->dctx->name))
553161748Scperciva				printf("%s %s duplicate address from %s ignored.\n",
554161748Scperciva				       sptoa(&match->addr),
555161748Scperciva				       match->dctx->name,
556161748Scperciva				       spkt->dctx->name);
557161748Scperciva			else
558161748Scperciva				printf("%s %s, duplicate address ignored.\n",
559161748Scperciva				       sptoa(&match->addr),
560161748Scperciva				       match->dctx->name);
561161748Scperciva			dec_pending_ntp(spkt->dctx->name, &spkt->addr);
562161748Scperciva			free(spkt);
563161748Scperciva			return;
564161748Scperciva		}
565161748Scperciva	}
566161748Scperciva
567161748Scperciva	LINK_SLIST(*pkt_listp, spkt, link);
568161748Scperciva
569161748Scperciva	xctx = emalloc_zero(sizeof(*xctx));
570161748Scperciva	xctx->sock = sock;
571161748Scperciva	xctx->spkt = spkt;
572161748Scperciva	gettimeofday_cached(base, &start_cb);
573161748Scperciva	xctx->sched = start_cb.tv_sec + (2 * xmt_delay);
574161748Scperciva
575161748Scperciva	LINK_SORT_SLIST(xmt_q, xctx, (xctx->sched < L_S_S_CUR()->sched),
576161748Scperciva			link, xmt_ctx);
577161748Scperciva	if (xmt_q == xctx) {
578161748Scperciva		/*
579161748Scperciva		 * The new entry is the first scheduled.  The timer is
580161748Scperciva		 * either not active or is set for the second xmt
581161748Scperciva		 * context in xmt_q.
582161748Scperciva		 */
583161748Scperciva		if (NULL == ev_xmt_timer)
584161748Scperciva			ev_xmt_timer = event_new(base, INVALID_SOCKET,
585161748Scperciva						 EV_TIMEOUT,
586161748Scperciva						 &xmt_timer_cb, NULL);
587161748Scperciva		if (NULL == ev_xmt_timer) {
588161748Scperciva			msyslog(LOG_ERR,
589161748Scperciva				"queue_xmt: event_new(base, -1, EV_TIMEOUT) failed!");
590161748Scperciva			exit(1);
591161748Scperciva		}
592161748Scperciva		ZERO(delay);
593161748Scperciva		if (xctx->sched > start_cb.tv_sec)
594161748Scperciva			delay.tv_sec = xctx->sched - start_cb.tv_sec;
595161748Scperciva		event_add(ev_xmt_timer, &delay);
596161748Scperciva		TRACE(2, ("queue_xmt: xmt timer for %u usec\n",
597161748Scperciva			  (u_int)delay.tv_usec));
598161748Scperciva	}
599161748Scperciva}
600161748Scperciva
601161748Scperciva
602161748Scperciva/*
603161748Scperciva** xmt_timer_cb
604161748Scperciva*/
605161748Scpercivavoid
606161748Scpercivaxmt_timer_cb(
607161748Scperciva	evutil_socket_t	fd,
608161748Scperciva	short		what,
609161748Scperciva	void *		ctx
610161748Scperciva	)
611161748Scperciva{
612161748Scperciva	struct timeval	start_cb;
613161748Scperciva	struct timeval	delay;
614173564Scperciva	xmt_ctx *	x;
615161748Scperciva
616161748Scperciva	UNUSED_ARG(fd);
617161748Scperciva	UNUSED_ARG(ctx);
618161748Scperciva	DEBUG_INSIST(EV_TIMEOUT == what);
619161748Scperciva
620161748Scperciva	if (NULL == xmt_q || shutting_down)
621161748Scperciva		return;
622161748Scperciva	gettimeofday_cached(base, &start_cb);
623161748Scperciva	if (xmt_q->sched <= start_cb.tv_sec) {
624167189Scperciva		UNLINK_HEAD_SLIST(x, xmt_q, link);
625167189Scperciva		TRACE(2, ("xmt_timer_cb: at .%6.6u -> %s\n",
626167189Scperciva			  (u_int)start_cb.tv_usec, stoa(&x->spkt->addr)));
627167189Scperciva		xmt(x);
628167189Scperciva		free(x);
629167189Scperciva		if (NULL == xmt_q)
630167189Scperciva			return;
631167189Scperciva	}
632167189Scperciva	if (xmt_q->sched <= start_cb.tv_sec) {
633167189Scperciva		event_add(ev_xmt_timer, &gap);
634167189Scperciva		TRACE(2, ("xmt_timer_cb: at .%6.6u gap %6.6u\n",
635167189Scperciva			  (u_int)start_cb.tv_usec,
636167189Scperciva			  (u_int)gap.tv_usec));
637167189Scperciva	} else {
638167189Scperciva		delay.tv_sec = xmt_q->sched - start_cb.tv_sec;
639167189Scperciva		delay.tv_usec = 0;
640167189Scperciva		event_add(ev_xmt_timer, &delay);
641167189Scperciva		TRACE(2, ("xmt_timer_cb: at .%6.6u next %ld seconds\n",
642161748Scperciva			  (u_int)start_cb.tv_usec,
643161748Scperciva			  (long)delay.tv_sec));
644161748Scperciva	}
645161748Scperciva}
646161748Scperciva
647161748Scperciva
648161748Scperciva/*
649161748Scperciva** xmt()
650161748Scperciva*/
651161748Scpercivavoid
652161748Scpercivaxmt(
653161748Scperciva	xmt_ctx *	xctx
654173564Scperciva	)
655173564Scperciva{
656173564Scperciva	SOCKET		sock = xctx->sock;
657173564Scperciva	struct dns_ctx *dctx = xctx->spkt->dctx;
658173564Scperciva	sent_pkt *	spkt = xctx->spkt;
659173564Scperciva	sockaddr_u *	dst = &spkt->addr;
660173564Scperciva	struct timeval	tv_xmt;
661173564Scperciva	struct pkt	x_pkt;
662173564Scperciva	size_t		pkt_len;
663173564Scperciva	int		sent;
664173564Scperciva
665173564Scperciva	if (0 != gettimeofday(&tv_xmt, NULL)) {
666173564Scperciva		msyslog(LOG_ERR,
667173564Scperciva			"xmt: gettimeofday() failed: %m");
668173564Scperciva		exit(1);
669173564Scperciva	}
670173564Scperciva	tv_xmt.tv_sec += JAN_1970;
671173564Scperciva
672173564Scperciva	pkt_len = generate_pkt(&x_pkt, &tv_xmt, dctx->key_id,
673173564Scperciva			       dctx->key);
674173564Scperciva
675173564Scperciva	sent = sendpkt(sock, dst, &x_pkt, pkt_len);
676173564Scperciva	if (sent) {
677173564Scperciva		/* Save the packet we sent... */
678173564Scperciva		memcpy(&spkt->x_pkt, &x_pkt, min(sizeof(spkt->x_pkt),
679173564Scperciva		       pkt_len));
680173564Scperciva		spkt->stime = tv_xmt.tv_sec - JAN_1970;
681173564Scperciva
682173564Scperciva		TRACE(2, ("xmt: %lx.%6.6u %s %s\n", (u_long)tv_xmt.tv_sec,
683173564Scperciva			  (u_int)tv_xmt.tv_usec, dctx->name, stoa(dst)));
684173564Scperciva	} else {
685173564Scperciva		dec_pending_ntp(dctx->name, dst);
686173564Scperciva	}
687173564Scperciva
688173564Scperciva	return;
689173564Scperciva}
690173564Scperciva
691173564Scperciva
692173564Scperciva/*
693173564Scperciva * timeout_queries() -- give up on unrequited NTP queries
694161748Scperciva */
695161748Scpercivavoid
696161748Scpercivatimeout_queries(void)
697161748Scperciva{
698161748Scperciva	struct timeval	start_cb;
699161748Scperciva	u_int		idx;
700161748Scperciva	sent_pkt *	head;
701161748Scperciva	sent_pkt *	spkt;
702161748Scperciva	sent_pkt *	spkt_next;
703173441Scperciva	long		age;
704173441Scperciva	int didsomething = 0;
705173441Scperciva
706173441Scperciva	TRACE(3, ("timeout_queries: called to check %u items\n",
707173441Scperciva		  (unsigned)COUNTOF(fam_listheads)));
708173441Scperciva
709173441Scperciva	gettimeofday_cached(base, &start_cb);
710161748Scperciva	for (idx = 0; idx < COUNTOF(fam_listheads); idx++) {
711161748Scperciva		head = fam_listheads[idx];
712161748Scperciva		for (spkt = head; spkt != NULL; spkt = spkt_next) {
713161748Scperciva			char xcst;
714161748Scperciva
715161748Scperciva			didsomething = 1;
716161748Scperciva			switch (spkt->dctx->flags & CTX_xCST) {
717161748Scperciva			    case CTX_BCST:
718161748Scperciva				xcst = 'B';
719161748Scperciva				break;
720161748Scperciva
721161748Scperciva			    case CTX_UCST:
722161748Scperciva				xcst = 'U';
723161748Scperciva				break;
724161748Scperciva
725161748Scperciva			    default:
726161748Scperciva				INSIST(!"spkt->dctx->flags neither UCST nor BCST");
727161748Scperciva				break;
728161748Scperciva			}
729161748Scperciva
730161748Scperciva			spkt_next = spkt->link;
731161748Scperciva			if (0 == spkt->stime || spkt->done)
732161748Scperciva				continue;
733161748Scperciva			age = start_cb.tv_sec - spkt->stime;
734161748Scperciva			TRACE(3, ("%s %s %cCST age %ld\n",
735196392Ssimon				  stoa(&spkt->addr),
736196392Ssimon				  spkt->dctx->name, xcst, age));
737196392Ssimon			if (age > response_timeout)
738196392Ssimon				timeout_query(spkt);
739196392Ssimon		}
740196392Ssimon	}
741196392Ssimon	// Do we care about didsomething?
742196392Ssimon	TRACE(3, ("timeout_queries: didsomething is %d, age is %ld\n",
743161748Scperciva		  didsomething, (long) (start_cb.tv_sec - start_tv.tv_sec)));
744161748Scperciva	if (start_cb.tv_sec - start_tv.tv_sec > response_timeout) {
745161748Scperciva		TRACE(3, ("timeout_queries: bail!\n"));
746161748Scperciva		event_base_loopexit(base, NULL);
747161748Scperciva		shutting_down = TRUE;
748161748Scperciva	}
749161748Scperciva}
750161748Scperciva
751161748Scperciva
752161748Scpercivavoid dec_pending_ntp(
753161748Scperciva	const char *	name,
754161748Scperciva	sockaddr_u *	server
755161748Scperciva	)
756161748Scperciva{
757161748Scperciva	if (n_pending_ntp > 0) {
758161748Scperciva		--n_pending_ntp;
759161748Scperciva		check_exit_conditions();
760161748Scperciva	} else {
761161748Scperciva		INSIST(0 == n_pending_ntp);
762161748Scperciva		TRACE(1, ("n_pending_ntp was zero before decrement for %s\n",
763161748Scperciva			  hostnameaddr(name, server)));
764161748Scperciva	}
765161748Scperciva}
766161748Scperciva
767161748Scperciva
768161748Scpercivavoid timeout_query(
769161748Scperciva	sent_pkt *	spkt
770161748Scperciva	)
771161748Scperciva{
772161748Scperciva	sockaddr_u *	server;
773161748Scperciva	char		xcst;
774161748Scperciva
775161748Scperciva
776161748Scperciva	switch (spkt->dctx->flags & CTX_xCST) {
777161748Scperciva	    case CTX_BCST:
778161748Scperciva		xcst = 'B';
779181142Scperciva		break;
780181142Scperciva
781181142Scperciva	    case CTX_UCST:
782181142Scperciva		xcst = 'U';
783181142Scperciva		break;
784181142Scperciva
785181142Scperciva	    default:
786181142Scperciva		INSIST(!"spkt->dctx->flags neither UCST nor BCST");
787181142Scperciva		break;
788181142Scperciva	}
789181142Scperciva	spkt->done = TRUE;
790181142Scperciva	server = &spkt->addr;
791181142Scperciva	msyslog(LOG_INFO, "%s no %cCST response after %d seconds",
792181142Scperciva		hostnameaddr(spkt->dctx->name, server), xcst,
793181142Scperciva		response_timeout);
794181142Scperciva	dec_pending_ntp(spkt->dctx->name, server);
795181142Scperciva	return;
796181142Scperciva}
797181142Scperciva
798181142Scperciva
799181142Scperciva/*
800181142Scperciva** check_kod
801181142Scperciva*/
802181142Scpercivaint
803181142Scpercivacheck_kod(
804181142Scperciva	const struct addrinfo *	ai
805181142Scperciva	)
806181142Scperciva{
807181142Scperciva	char *hostname;
808181142Scperciva	struct kod_entry *reason;
809181142Scperciva
810181142Scperciva	/* Is there a KoD on file for this address? */
811181142Scperciva	hostname = addrinfo_to_str(ai);
812181142Scperciva	TRACE(2, ("check_kod: checking <%s>\n", hostname));
813181142Scperciva	if (search_entry(hostname, &reason)) {
814181142Scperciva		printf("prior KoD for %s, skipping.\n",
815181142Scperciva			hostname);
816181142Scperciva		free(reason);
817181142Scperciva		free(hostname);
818181142Scperciva
819181142Scperciva		return 1;
820181142Scperciva	}
821181142Scperciva	free(hostname);
822181142Scperciva
823181142Scperciva	return 0;
824181142Scperciva}
825181142Scperciva
826181142Scperciva
827181142Scperciva/*
828181142Scperciva** Socket readable/timeout Callback:
829181142Scperciva** Read in the packet
830181142Scperciva** Unicast:
831181142Scperciva** - close socket
832181142Scperciva** - decrement n_pending_ntp
833181142Scperciva** - If packet is good, set the time and "exit"
834181142Scperciva** Broadcast:
835181142Scperciva** - If packet is good, set the time and "exit"
836181142Scperciva*/
837181142Scpercivavoid
838181142Scpercivasock_cb(
839181142Scperciva	evutil_socket_t fd,
840181142Scperciva	short what,
841181142Scperciva	void *ptr
842181142Scperciva	)
843181142Scperciva{
844181142Scperciva	sockaddr_u	sender;
845181142Scperciva	sockaddr_u *	psau;
846181142Scperciva	sent_pkt **	p_pktlist;
847181142Scperciva	sent_pkt *	spkt;
848181142Scperciva	int		rpktl;
849181142Scperciva	int		rc;
850181142Scperciva
851181142Scperciva	INSIST(sock4 == fd || sock6 == fd);
852181142Scperciva
853181142Scperciva	TRACE(3, ("sock_cb: event on sock%s:%s%s%s%s\n",
854181142Scperciva		  (fd == sock6)
855181142Scperciva		      ? "6"
856181142Scperciva		      : "4",
857181142Scperciva		  (what & EV_TIMEOUT) ? " timeout" : "",
858181142Scperciva		  (what & EV_READ)    ? " read" : "",
859181142Scperciva		  (what & EV_WRITE)   ? " write" : "",
860181142Scperciva		  (what & EV_SIGNAL)  ? " signal" : ""));
861181142Scperciva
862181142Scperciva	if (!(EV_READ & what)) {
863181142Scperciva		if (EV_TIMEOUT & what)
864161748Scperciva			timeout_queries();
865161748Scperciva
866161748Scperciva		return;
867161748Scperciva	}
868161748Scperciva
869161748Scperciva	/* Read in the packet */
870161748Scperciva	rpktl = recvdata(fd, &sender, &rbuf, sizeof(rbuf));
871161748Scperciva	if (rpktl < 0) {
872161748Scperciva		msyslog(LOG_DEBUG, "recvfrom error %m");
873161748Scperciva		return;
874161748Scperciva	}
875161748Scperciva
876161748Scperciva	if (sock6 == fd)
877161748Scperciva		p_pktlist = &v6_pkts_list;
878161748Scperciva	else
879161748Scperciva		p_pktlist = &v4_pkts_list;
880161748Scperciva
881161748Scperciva	for (spkt = *p_pktlist; spkt != NULL; spkt = spkt->link) {
882161748Scperciva		psau = &spkt->addr;
883161748Scperciva		if (SOCK_EQ(&sender, psau))
884161748Scperciva			break;
885161748Scperciva	}
886161748Scperciva	if (NULL == spkt) {
887161748Scperciva		msyslog(LOG_WARNING,
888161748Scperciva			"Packet from unexpected source %s dropped",
889161748Scperciva			sptoa(&sender));
890161748Scperciva		return;
891161748Scperciva	}
892161748Scperciva
893161748Scperciva	TRACE(1, ("sock_cb: %s %s\n", spkt->dctx->name,
894161748Scperciva		  sptoa(&sender)));
895161748Scperciva
896161748Scperciva	rpktl = process_pkt(&r_pkt, &sender, rpktl, MODE_SERVER,
897161748Scperciva			    &spkt->x_pkt, "sock_cb");
898161748Scperciva
899161748Scperciva	TRACE(2, ("sock_cb: process_pkt returned %d\n", rpktl));
900161748Scperciva
901161748Scperciva	/* If this is a Unicast packet, one down ... */
902161748Scperciva	if (!spkt->done && (CTX_UCST & spkt->dctx->flags)) {
903161748Scperciva		dec_pending_ntp(spkt->dctx->name, &spkt->addr);
904161748Scperciva		spkt->done = TRUE;
905161748Scperciva	}
906161748Scperciva
907161748Scperciva
908161748Scperciva	/* If the packet is good, set the time and we're all done */
909161748Scperciva	rc = handle_pkt(rpktl, &r_pkt, &spkt->addr, spkt->dctx->name);
910161748Scperciva	if (0 != rc)
911161748Scperciva		TRACE(1, ("sock_cb: handle_pkt() returned %d\n", rc));
912161748Scperciva	check_exit_conditions();
913161748Scperciva}
914161748Scperciva
915161748Scperciva
916161748Scperciva/*
917161748Scperciva * check_exit_conditions()
918161748Scperciva *
919161748Scperciva * If sntp has a reply, ask the event loop to stop after this round of
920161748Scperciva * callbacks, unless --wait was used.
921161748Scperciva */
922161748Scpercivavoid
923161748Scpercivacheck_exit_conditions(void)
924161748Scperciva{
925161748Scperciva	if ((0 == n_pending_ntp && 0 == n_pending_dns) ||
926161748Scperciva	    (time_derived && !HAVE_OPT(WAIT))) {
927161748Scperciva		event_base_loopexit(base, NULL);
928161748Scperciva		shutting_down = TRUE;
929161748Scperciva	} else {
930161748Scperciva		TRACE(2, ("%d NTP and %d name queries pending\n",
931161748Scperciva			  n_pending_ntp, n_pending_dns));
932161748Scperciva	}
933161748Scperciva}
934161748Scperciva
935161748Scperciva
936161748Scperciva/*
937161748Scperciva * sntp_addremove_fd() is invoked by the intres blocking worker code
938161748Scperciva * to read from a pipe, or to stop same.
939161748Scperciva */
940161748Scpercivavoid sntp_addremove_fd(
941161748Scperciva	int	fd,
942161748Scperciva	int	is_pipe,
943161748Scperciva	int	remove_it
944161748Scperciva	)
945161748Scperciva{
946161748Scperciva	u_int		idx;
947161748Scperciva	blocking_child *c;
948161748Scperciva	struct event *	ev;
949161748Scperciva
950161748Scperciva#ifdef HAVE_SOCKETPAIR
951161748Scperciva	if (is_pipe) {
952161748Scperciva		/* sntp only asks for EV_FEATURE_FDS without HAVE_SOCKETPAIR */
953161748Scperciva		msyslog(LOG_ERR, "fatal: pipes not supported on systems with socketpair()");
954161748Scperciva		exit(1);
955161748Scperciva	}
956161748Scperciva#endif
957161748Scperciva
958161748Scperciva	c = NULL;
959161748Scperciva	for (idx = 0; idx < blocking_children_alloc; idx++) {
960161748Scperciva		c = blocking_children[idx];
961161748Scperciva		if (NULL == c)
962161748Scperciva			continue;
963161748Scperciva		if (fd == c->resp_read_pipe)
964161748Scperciva			break;
965161748Scperciva	}
966161748Scperciva	if (idx == blocking_children_alloc)
967161748Scperciva		return;
968161748Scperciva
969161748Scperciva	if (remove_it) {
970161748Scperciva		ev = c->resp_read_ctx;
971161748Scperciva		c->resp_read_ctx = NULL;
972161748Scperciva		event_del(ev);
973161748Scperciva		event_free(ev);
974161748Scperciva
975161748Scperciva		return;
976161748Scperciva	}
977161748Scperciva
978161748Scperciva	ev = event_new(base, fd, EV_READ | EV_PERSIST,
979161748Scperciva		       &worker_resp_cb, c);
980161748Scperciva	if (NULL == ev) {
981161748Scperciva		msyslog(LOG_ERR,
982161748Scperciva			"sntp_addremove_fd: event_new(base, fd) failed!");
983161748Scperciva		return;
984161748Scperciva	}
985161748Scperciva	c->resp_read_ctx = ev;
986161748Scperciva	event_add(ev, NULL);
987161748Scperciva}
988161748Scperciva
989161748Scperciva
990161748Scperciva/* called by forked intres child to close open descriptors */
991161748Scperciva#ifdef WORK_FORK
992161748Scpercivavoid
993161748Scpercivakill_asyncio(
994161748Scperciva	int	startfd
995161748Scperciva	)
996161748Scperciva{
997161748Scperciva	if (INVALID_SOCKET != sock4) {
998161748Scperciva		closesocket(sock4);
999161748Scperciva		sock4 = INVALID_SOCKET;
1000161748Scperciva	}
1001161748Scperciva	if (INVALID_SOCKET != sock6) {
1002161748Scperciva		closesocket(sock6);
1003161748Scperciva		sock6 = INVALID_SOCKET;
1004161748Scperciva	}
1005161748Scperciva	if (INVALID_SOCKET != bsock4) {
1006161748Scperciva		closesocket(sock4);
1007173564Scperciva		sock4 = INVALID_SOCKET;
1008173564Scperciva	}
1009173564Scperciva	if (INVALID_SOCKET != bsock6) {
1010173564Scperciva		closesocket(sock6);
1011173564Scperciva		sock6 = INVALID_SOCKET;
1012173564Scperciva	}
1013173564Scperciva}
1014173564Scperciva#endif
1015173564Scperciva
1016173564Scperciva
1017173564Scperciva/*
1018173564Scperciva * worker_resp_cb() is invoked when resp_read_pipe is readable.
1019173564Scperciva */
1020173564Scpercivavoid
1021161748Scpercivaworker_resp_cb(
1022161748Scperciva	evutil_socket_t	fd,
1023161748Scperciva	short		what,
1024161748Scperciva	void *		ctx	/* blocking_child * */
1025161748Scperciva	)
1026161748Scperciva{
1027161748Scperciva	blocking_child *	c;
1028161748Scperciva
1029161748Scperciva	DEBUG_INSIST(EV_READ & what);
1030161748Scperciva	c = ctx;
1031161748Scperciva	DEBUG_INSIST(fd == c->resp_read_pipe);
1032161748Scperciva	process_blocking_resp(c);
1033161748Scperciva}
1034161748Scperciva
1035161748Scperciva
1036161748Scperciva/*
1037161748Scperciva * intres_timeout_req(s) is invoked in the parent to schedule an idle
1038161748Scperciva * timeout to fire in s seconds, if not reset earlier by a call to
1039161748Scperciva * intres_timeout_req(0), which clears any pending timeout.  When the
1040161748Scperciva * timeout expires, worker_idle_timer_fired() is invoked (again, in the
1041161748Scperciva * parent).
1042161748Scperciva *
1043161748Scperciva * sntp and ntpd each provide implementations adapted to their timers.
1044161748Scperciva */
1045161748Scpercivavoid
1046161748Scpercivaintres_timeout_req(
1047161748Scperciva	u_int	seconds		/* 0 cancels */
1048161748Scperciva	)
1049161748Scperciva{
1050161748Scperciva	struct timeval	tv_to;
1051161748Scperciva
1052161748Scperciva	if (NULL == ev_worker_timeout) {
1053173564Scperciva		ev_worker_timeout = event_new(base, -1,
1054173564Scperciva					      EV_TIMEOUT | EV_PERSIST,
1055161748Scperciva					      &worker_timeout, NULL);
1056161748Scperciva		DEBUG_INSIST(NULL != ev_worker_timeout);
1057161748Scperciva	} else {
1058161748Scperciva		event_del(ev_worker_timeout);
1059161748Scperciva	}
1060161748Scperciva	if (0 == seconds)
1061161748Scperciva		return;
1062161748Scperciva	tv_to.tv_sec = seconds;
1063161748Scperciva	tv_to.tv_usec = 0;
1064161748Scperciva	event_add(ev_worker_timeout, &tv_to);
1065161748Scperciva}
1066161748Scperciva
1067161748Scperciva
1068161748Scpercivavoid
1069161748Scpercivaworker_timeout(
1070161748Scperciva	evutil_socket_t	fd,
1071161748Scperciva	short		what,
1072161748Scperciva	void *		ctx
1073161748Scperciva	)
1074161748Scperciva{
1075161748Scperciva	UNUSED_ARG(fd);
1076161748Scperciva	UNUSED_ARG(ctx);
1077161748Scperciva
1078161748Scperciva	DEBUG_REQUIRE(EV_TIMEOUT & what);
1079161748Scperciva	worker_idle_timer_fired();
1080161748Scperciva}
1081161748Scperciva
1082161748Scperciva
1083161748Scpercivavoid
1084161748Scpercivasntp_libevent_log_cb(
1085161748Scperciva	int		severity,
1086161748Scperciva	const char *	msg
1087161748Scperciva	)
1088161748Scperciva{
1089161748Scperciva	int		level;
1090161748Scperciva
1091161748Scperciva	switch (severity) {
1092161748Scperciva
1093161748Scperciva	default:
1094161748Scperciva	case _EVENT_LOG_DEBUG:
1095161748Scperciva		level = LOG_DEBUG;
1096161748Scperciva		break;
1097161748Scperciva
1098161748Scperciva	case _EVENT_LOG_MSG:
1099161748Scperciva		level = LOG_NOTICE;
1100161748Scperciva		break;
1101161748Scperciva
1102161748Scperciva	case _EVENT_LOG_WARN:
1103161748Scperciva		level = LOG_WARNING;
1104161748Scperciva		break;
1105161748Scperciva
1106161748Scperciva	case _EVENT_LOG_ERR:
1107161748Scperciva		level = LOG_ERR;
1108161748Scperciva		break;
1109161748Scperciva	}
1110161748Scperciva
1111161748Scperciva	msyslog(level, "%s", msg);
1112161748Scperciva}
1113161748Scperciva
1114161748Scperciva
1115161748Scpercivaint
1116161748Scpercivagenerate_pkt (
1117161748Scperciva	struct pkt *x_pkt,
1118161748Scperciva	const struct timeval *tv_xmt,
1119161748Scperciva	int key_id,
1120161748Scperciva	struct key *pkt_key
1121161748Scperciva	)
1122161748Scperciva{
1123161748Scperciva	l_fp	xmt_fp;
1124161748Scperciva	int	pkt_len;
1125161748Scperciva	int	mac_size;
1126161748Scperciva
1127161748Scperciva	pkt_len = LEN_PKT_NOMAC;
1128161748Scperciva	ZERO(*x_pkt);
1129161748Scperciva	TVTOTS(tv_xmt, &xmt_fp);
1130161748Scperciva	HTONL_FP(&xmt_fp, &x_pkt->xmt);
1131161748Scperciva	x_pkt->stratum = STRATUM_TO_PKT(STRATUM_UNSPEC);
1132161748Scperciva	x_pkt->ppoll = 8;
1133161748Scperciva	/* FIXME! Modus broadcast + adr. check -> bdr. pkt */
1134161748Scperciva	set_li_vn_mode(x_pkt, LEAP_NOTINSYNC, ntpver, 3);
1135161748Scperciva	if (pkt_key != NULL) {
1136161748Scperciva		x_pkt->exten[0] = htonl(key_id);
1137161748Scperciva		mac_size = 20; /* max room for MAC */
1138161748Scperciva		mac_size = make_mac(x_pkt, pkt_len, mac_size,
1139161748Scperciva				    pkt_key, (char *)&x_pkt->exten[1]);
1140161748Scperciva		if (mac_size > 0)
1141161748Scperciva			pkt_len += mac_size + 4;
1142161748Scperciva	}
1143161748Scperciva	return pkt_len;
1144161748Scperciva}
1145161748Scperciva
1146161748Scperciva
1147161748Scpercivaint
1148161748Scpercivahandle_pkt(
1149161748Scperciva	int		rpktl,
1150161748Scperciva	struct pkt *	rpkt,
1151161748Scperciva	sockaddr_u *	host,
1152161748Scperciva	const char *	hostname
1153161748Scperciva	)
1154161748Scperciva{
1155161748Scperciva	char		disptxt[32];
1156161748Scperciva	const char *	addrtxt;
1157161748Scperciva	struct timeval	tv_dst;
1158161748Scperciva	int		cnt;
1159161748Scperciva	int		sw_case;
1160161748Scperciva	int		digits;
1161161748Scperciva	int		stratum;
1162161748Scperciva	char *		ref;
1163161748Scperciva	char *		ts_str;
1164161748Scperciva	const char *	leaptxt;
1165161748Scperciva	double		offset;
1166161748Scperciva	double		precision;
1167161748Scperciva	double		synch_distance;
1168161748Scperciva	char *		p_SNTP_PRETEND_TIME;
1169161748Scperciva	time_t		pretend_time;
1170161748Scperciva#if SIZEOF_TIME_T == 8
1171161748Scperciva	long long	ll;
1172161748Scperciva#else
1173161748Scperciva	long		l;
1174161748Scperciva#endif
1175161748Scperciva
1176161748Scperciva	ts_str = NULL;
1177161748Scperciva
1178161748Scperciva	if (rpktl > 0)
1179161748Scperciva		sw_case = 1;
1180161748Scperciva	else
1181161748Scperciva		sw_case = rpktl;
1182161748Scperciva
1183161748Scperciva	switch (sw_case) {
1184161748Scperciva
1185161748Scperciva	case SERVER_UNUSEABLE:
1186161748Scperciva		return -1;
1187161748Scperciva		break;
1188161748Scperciva
1189161748Scperciva	case PACKET_UNUSEABLE:
1190161748Scperciva		break;
1191161748Scperciva
1192161748Scperciva	case SERVER_AUTH_FAIL:
1193161748Scperciva		break;
1194161748Scperciva
1195161748Scperciva	case KOD_DEMOBILIZE:
1196161748Scperciva		/* Received a DENY or RESTR KOD packet */
1197161748Scperciva		addrtxt = stoa(host);
1198161748Scperciva		ref = (char *)&rpkt->refid;
1199161748Scperciva		add_entry(addrtxt, ref);
1200161748Scperciva		msyslog(LOG_WARNING, "KOD code %c%c%c%c from %s %s",
1201161748Scperciva			ref[0], ref[1], ref[2], ref[3], addrtxt, hostname);
1202161748Scperciva		break;
1203161748Scperciva
1204161748Scperciva	case KOD_RATE:
1205161748Scperciva		/*
1206161748Scperciva		** Hmm...
1207161748Scperciva		** We should probably call add_entry() with an
1208161748Scperciva		** expiration timestamp of several seconds in the future,
1209161748Scperciva		** and back-off even more if we get more RATE responses.
1210161748Scperciva		*/
1211161748Scperciva		break;
1212161748Scperciva
1213161748Scperciva	case 1:
1214161748Scperciva		TRACE(3, ("handle_pkt: %d bytes from %s %s\n",
1215161748Scperciva			  rpktl, stoa(host), hostname));
1216161748Scperciva
1217161748Scperciva		gettimeofday_cached(base, &tv_dst);
1218161748Scperciva
1219161748Scperciva		p_SNTP_PRETEND_TIME = getenv("SNTP_PRETEND_TIME");
1220161748Scperciva		if (p_SNTP_PRETEND_TIME) {
1221161748Scperciva			pretend_time = 0;
1222161748Scperciva#if SIZEOF_TIME_T == 4
1223161748Scperciva			if (1 == sscanf(p_SNTP_PRETEND_TIME, "%ld", &l))
1224161748Scperciva				pretend_time = (time_t)l;
1225161748Scperciva#elif SIZEOF_TIME_T == 8
1226161748Scperciva			if (1 == sscanf(p_SNTP_PRETEND_TIME, "%lld", &ll))
1227161748Scperciva				pretend_time = (time_t)ll;
1228161748Scperciva#else
1229161748Scperciva# include "GRONK: unexpected value for SIZEOF_TIME_T"
1230161748Scperciva#endif
1231161748Scperciva			if (0 != pretend_time)
1232161748Scperciva				tv_dst.tv_sec = pretend_time;
1233161748Scperciva		}
1234161748Scperciva
1235161748Scperciva		offset_calculation(rpkt, rpktl, &tv_dst, &offset,
1236161748Scperciva				   &precision, &synch_distance);
1237161748Scperciva		time_derived = TRUE;
1238161748Scperciva
1239161748Scperciva		for (digits = 0; (precision *= 10.) < 1.; ++digits)
1240161748Scperciva			/* empty */ ;
1241161748Scperciva		if (digits > 6)
1242161748Scperciva			digits = 6;
1243161748Scperciva
1244161748Scperciva		ts_str = tv_to_str(&tv_dst);
1245161748Scperciva		stratum = rpkt->stratum;
1246161748Scperciva		if (0 == stratum)
1247161748Scperciva				stratum = 16;
1248161748Scperciva
1249161748Scperciva		if (synch_distance > 0.) {
1250161748Scperciva			cnt = snprintf(disptxt, sizeof(disptxt),
1251161748Scperciva				       " +/- %f", synch_distance);
1252161748Scperciva			if ((size_t)cnt >= sizeof(disptxt))
1253161748Scperciva				snprintf(disptxt, sizeof(disptxt),
1254161748Scperciva					 "ERROR %d >= %d", cnt,
1255161748Scperciva					 (int)sizeof(disptxt));
1256161748Scperciva		} else {
1257161748Scperciva			disptxt[0] = '\0';
1258161748Scperciva		}
1259161748Scperciva
1260161748Scperciva		switch (PKT_LEAP(rpkt->li_vn_mode)) {
1261161748Scperciva		    case LEAP_NOWARNING:
1262161748Scperciva		    	leaptxt = "no-leap";
1263161748Scperciva			break;
1264161748Scperciva		    case LEAP_ADDSECOND:
1265161748Scperciva		    	leaptxt = "add-leap";
1266161748Scperciva			break;
1267161748Scperciva		    case LEAP_DELSECOND:
1268161748Scperciva		    	leaptxt = "del-leap";
1269161748Scperciva			break;
1270161748Scperciva		    case LEAP_NOTINSYNC:
1271161748Scperciva		    	leaptxt = "unsync";
1272161748Scperciva			break;
1273161748Scperciva		    default:
1274161748Scperciva		    	leaptxt = "LEAP-ERROR";
1275161748Scperciva			break;
1276161748Scperciva		}
1277161748Scperciva
1278161748Scperciva		msyslog(LOG_INFO, "%s %+.*f%s %s s%d %s%s", ts_str,
1279161748Scperciva			digits, offset, disptxt,
1280161748Scperciva			hostnameaddr(hostname, host), stratum,
1281161748Scperciva			leaptxt,
1282161748Scperciva			(time_adjusted)
1283161748Scperciva			    ? " [excess]"
1284161748Scperciva			    : "");
1285161748Scperciva		free(ts_str);
1286161748Scperciva
1287161748Scperciva		if (p_SNTP_PRETEND_TIME)
1288161748Scperciva			return 0;
1289161748Scperciva
1290161748Scperciva		if (!time_adjusted &&
1291161748Scperciva		    (ENABLED_OPT(STEP) || ENABLED_OPT(SLEW)))
1292161748Scperciva			return set_time(offset);
1293161748Scperciva
1294161748Scperciva		return EX_OK;
1295164600Scperciva	}
1296164600Scperciva
1297161748Scperciva	return 1;
1298161748Scperciva}
1299161748Scperciva
1300161748Scperciva
1301161748Scpercivavoid
1302161748Scpercivaoffset_calculation(
1303161748Scperciva	struct pkt *rpkt,
1304161748Scperciva	int rpktl,
1305161748Scperciva	struct timeval *tv_dst,
1306161748Scperciva	double *offset,
1307161748Scperciva	double *precision,
1308161748Scperciva	double *synch_distance
1309161748Scperciva	)
1310161748Scperciva{
1311161748Scperciva	l_fp p_rec, p_xmt, p_ref, p_org, tmp, dst;
1312161748Scperciva	u_fp p_rdly, p_rdsp;
1313161748Scperciva	double t21, t34, delta;
1314161748Scperciva
1315161748Scperciva	/* Convert timestamps from network to host byte order */
1316161748Scperciva	p_rdly = NTOHS_FP(rpkt->rootdelay);
1317161748Scperciva	p_rdsp = NTOHS_FP(rpkt->rootdisp);
1318161748Scperciva	NTOHL_FP(&rpkt->reftime, &p_ref);
1319161748Scperciva	NTOHL_FP(&rpkt->org, &p_org);
1320161748Scperciva	NTOHL_FP(&rpkt->rec, &p_rec);
1321161748Scperciva	NTOHL_FP(&rpkt->xmt, &p_xmt);
1322161748Scperciva
1323161748Scperciva	*precision = LOGTOD(rpkt->precision);
1324161748Scperciva
1325161748Scperciva	TRACE(3, ("offset_calculation: LOGTOD(rpkt->precision): %f\n", *precision));
1326161748Scperciva
1327161748Scperciva	/* Compute offset etc. */
1328161748Scperciva	tmp = p_rec;
1329161748Scperciva	L_SUB(&tmp, &p_org);
1330161748Scperciva	LFPTOD(&tmp, t21);
1331161748Scperciva	TVTOTS(tv_dst, &dst);
1332161748Scperciva	dst.l_ui += JAN_1970;
1333161748Scperciva	tmp = p_xmt;
1334161748Scperciva	L_SUB(&tmp, &dst);
1335161748Scperciva	LFPTOD(&tmp, t34);
1336161748Scperciva	*offset = (t21 + t34) / 2.;
1337161748Scperciva	delta = t21 - t34;
1338161748Scperciva
1339161748Scperciva	// synch_distance is:
1340161748Scperciva	// (peer->delay + peer->rootdelay) / 2 + peer->disp
1341161748Scperciva	// + peer->rootdisp + clock_phi * (current_time - peer->update)
1342161748Scperciva	// + peer->jitter;
1343161748Scperciva	//
1344161748Scperciva	// and peer->delay = fabs(peer->offset - p_offset) * 2;
1345173564Scperciva	// and peer->offset needs history, so we're left with
1346173564Scperciva	// p_offset = (t21 + t34) / 2.;
1347173564Scperciva	// peer->disp = 0; (we have no history to augment this)
1348173564Scperciva	// clock_phi = 15e-6;
1349173564Scperciva	// peer->jitter = LOGTOD(sys_precision); (we have no history to augment this)
1350173564Scperciva	// and ntp_proto.c:set_sys_tick_precision() should get us sys_precision.
1351173564Scperciva	//
1352173564Scperciva	// so our answer seems to be:
1353173564Scperciva	//
1354173564Scperciva	// (fabs(t21 + t34) + peer->rootdelay) / 3.
1355173564Scperciva	// + 0 (peer->disp)
1356173564Scperciva	// + peer->rootdisp
1357173564Scperciva	// + 15e-6 (clock_phi)
1358173564Scperciva	// + LOGTOD(sys_precision)
1359173564Scperciva
1360161869Scperciva	INSIST( FPTOD(p_rdly) >= 0. );
1361161748Scperciva#if 1
1362161748Scperciva	*synch_distance = (fabs(t21 + t34) + FPTOD(p_rdly)) / 3.
1363161748Scperciva		+ 0.
1364161748Scperciva		+ FPTOD(p_rdsp)
1365173564Scperciva		+ 15e-6
1366173564Scperciva		+ 0.	/* LOGTOD(sys_precision) when we can get it */
1367161748Scperciva		;
1368161748Scperciva	INSIST( *synch_distance >= 0. );
1369161748Scperciva#else
1370161748Scperciva	*synch_distance = (FPTOD(p_rdly) + FPTOD(p_rdsp))/2.0;
1371161748Scperciva#endif
1372173564Scperciva
1373161748Scperciva#ifdef DEBUG
1374161748Scperciva	if (debug > 3) {
1375161748Scperciva		printf("sntp rootdelay: %f\n", FPTOD(p_rdly));
1376161748Scperciva		printf("sntp rootdisp: %f\n", FPTOD(p_rdsp));
1377161748Scperciva		printf("sntp syncdist: %f\n", *synch_distance);
1378161748Scperciva
1379161748Scperciva		pkt_output(rpkt, rpktl, stdout);
1380161748Scperciva
1381161748Scperciva		printf("sntp offset_calculation: rpkt->reftime:\n");
1382161748Scperciva		l_fp_output(&p_ref, stdout);
1383161748Scperciva		printf("sntp offset_calculation: rpkt->org:\n");
1384173564Scperciva		l_fp_output(&p_org, stdout);
1385161748Scperciva		printf("sntp offset_calculation: rpkt->rec:\n");
1386161748Scperciva		l_fp_output(&p_rec, stdout);
1387173564Scperciva		printf("sntp offset_calculation: rpkt->xmt:\n");
1388164600Scperciva		l_fp_output(&p_xmt, stdout);
1389173564Scperciva	}
1390164600Scperciva#endif
1391164600Scperciva
1392164600Scperciva	TRACE(3, ("sntp offset_calculation:\trec - org t21: %.6f\n"
1393161748Scperciva		  "\txmt - dst t34: %.6f\tdelta: %.6f\toffset: %.6f\n",
1394173564Scperciva		  t21, t34, delta, *offset));
1395173564Scperciva
1396161748Scperciva	return;
1397161748Scperciva}
1398164600Scperciva
1399173564Scperciva
1400173564Scperciva
1401164600Scperciva/* Compute the 8 bits for li_vn_mode */
1402164600Scpercivavoid
1403161748Scpercivaset_li_vn_mode (
1404161748Scperciva	struct pkt *spkt,
1405161748Scperciva	char leap,
1406161748Scperciva	char version,
1407161748Scperciva	char mode
1408161748Scperciva	)
1409161748Scperciva{
1410161748Scperciva	if (leap > 3) {
1411161748Scperciva		msyslog(LOG_DEBUG, "set_li_vn_mode: leap > 3, using max. 3");
1412161748Scperciva		leap = 3;
1413161748Scperciva	}
1414161748Scperciva
1415161748Scperciva	if ((unsigned char)version > 7) {
1416161748Scperciva		msyslog(LOG_DEBUG, "set_li_vn_mode: version < 0 or > 7, using 4");
1417161748Scperciva		version = 4;
1418161748Scperciva	}
1419161748Scperciva
1420161748Scperciva	if (mode > 7) {
1421161748Scperciva		msyslog(LOG_DEBUG, "set_li_vn_mode: mode > 7, using client mode 3");
1422161748Scperciva		mode = 3;
1423161748Scperciva	}
1424161748Scperciva
1425161748Scperciva	spkt->li_vn_mode  = leap << 6;
1426161748Scperciva	spkt->li_vn_mode |= version << 3;
1427161748Scperciva	spkt->li_vn_mode |= mode;
1428161748Scperciva}
1429161748Scperciva
1430161748Scperciva
1431161748Scperciva/*
1432161748Scperciva** set_time applies 'offset' to the local clock.
1433161748Scperciva*/
1434161748Scpercivaint
1435161748Scpercivaset_time(
1436161748Scperciva	double offset
1437161748Scperciva	)
1438161748Scperciva{
1439161748Scperciva	int rc;
1440161748Scperciva
1441161748Scperciva	if (time_adjusted)
1442161748Scperciva		return EX_OK;
1443161748Scperciva
1444161748Scperciva	/*
1445161748Scperciva	** If we can step but we cannot slew, then step.
1446161748Scperciva	** If we can step or slew and and |offset| > steplimit, then step.
1447161748Scperciva	*/
1448161748Scperciva	if (ENABLED_OPT(STEP) &&
1449161748Scperciva	    (   !ENABLED_OPT(SLEW)
1450161748Scperciva	     || (ENABLED_OPT(SLEW) && (fabs(offset) > steplimit))
1451161748Scperciva	    )) {
1452161748Scperciva		rc = step_systime(offset);
1453161748Scperciva
1454161748Scperciva		/* If there was a problem, can we rely on errno? */
1455161748Scperciva		if (1 == rc)
1456161748Scperciva			time_adjusted = TRUE;
1457161748Scperciva		return (time_adjusted)
1458161748Scperciva			   ? EX_OK
1459161748Scperciva			   : 1;
1460161748Scperciva		/*
1461161748Scperciva		** In case of error, what should we use?
1462161748Scperciva		** EX_UNAVAILABLE?
1463161748Scperciva		** EX_OSERR?
1464161748Scperciva		** EX_NOPERM?
1465161748Scperciva		*/
1466161748Scperciva	}
1467161748Scperciva
1468161748Scperciva	if (ENABLED_OPT(SLEW)) {
1469161748Scperciva		rc = adj_systime(offset);
1470161748Scperciva
1471161748Scperciva		/* If there was a problem, can we rely on errno? */
1472161748Scperciva		if (1 == rc)
1473161748Scperciva			time_adjusted = TRUE;
1474161748Scperciva		return (time_adjusted)
1475161748Scperciva			   ? EX_OK
1476161748Scperciva			   : 1;
1477161748Scperciva		/*
1478161748Scperciva		** In case of error, what should we use?
1479161748Scperciva		** EX_UNAVAILABLE?
1480161748Scperciva		** EX_OSERR?
1481161748Scperciva		** EX_NOPERM?
1482161748Scperciva		*/
1483173564Scperciva	}
1484173564Scperciva
1485173564Scperciva	return EX_SOFTWARE;
1486173564Scperciva}
1487173564Scperciva
1488173564Scperciva
1489173564Scpercivaint
1490173564Scpercivalibevent_version_ok(void)
1491173564Scperciva{
1492173564Scperciva	ev_uint32_t v_compile_maj;
1493173564Scperciva	ev_uint32_t v_run_maj;
1494173564Scperciva
1495173564Scperciva	v_compile_maj = LIBEVENT_VERSION_NUMBER & 0xffff0000;
1496173564Scperciva	v_run_maj = event_get_version_number() & 0xffff0000;
1497173564Scperciva	if (v_compile_maj != v_run_maj) {
1498173564Scperciva		fprintf(stderr,
1499173564Scperciva			"Incompatible libevent versions: have %s, built with %s\n",
1500173564Scperciva			event_get_version(),
1501173564Scperciva			LIBEVENT_VERSION);
1502173564Scperciva		return 0;
1503173564Scperciva	}
1504173564Scperciva	return 1;
1505173564Scperciva}
1506173564Scperciva
1507173564Scperciva/*
1508173564Scperciva * gettimeofday_cached()
1509173564Scperciva *
1510173564Scperciva * Clones the event_base_gettimeofday_cached() interface but ensures the
1511173564Scperciva * times are always on the gettimeofday() 1970 scale.  Older libevent 2
1512173564Scperciva * sometimes used gettimeofday(), sometimes the since-system-start
1513173564Scperciva * clock_gettime(CLOCK_MONOTONIC), depending on the platform.
1514173564Scperciva *
1515161748Scperciva * It is not cleanly possible to tell which timescale older libevent is
1516173564Scperciva * using.
1517173564Scperciva *
1518173564Scperciva * The strategy involves 1 hour thresholds chosen to be far longer than
1519173564Scperciva * the duration of a round of libevent callbacks, which share a cached
1520161748Scperciva * start-of-round time.  First compare the last cached time with the
1521161748Scperciva * current gettimeofday() time.  If they are within one hour, libevent
1522161748Scperciva * is using the proper timescale so leave the offset 0.  Otherwise,
1523161748Scperciva * compare libevent's cached time and the current time on the monotonic
1524161748Scperciva * scale.  If they are within an hour, libevent is using the monotonic
1525161748Scperciva * scale so calculate the offset to add to such times to bring them to
1526161748Scperciva * gettimeofday()'s scale.
1527161748Scperciva */
1528161748Scpercivaint
1529161748Scpercivagettimeofday_cached(
1530161748Scperciva	struct event_base *	b,
1531161748Scperciva	struct timeval *	caller_tv
1532161748Scperciva	)
1533161748Scperciva{
1534161748Scperciva#if defined(_EVENT_HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
1535161748Scperciva	static struct event_base *	cached_b;
1536161748Scperciva	static struct timeval		cached;
1537161748Scperciva	static struct timeval		adj_cached;
1538161748Scperciva	static struct timeval		offset;
1539161748Scperciva	static int			offset_ready;
1540161748Scperciva	struct timeval			latest;
1541173564Scperciva	struct timeval			systemt;
1542173564Scperciva	struct timespec			ts;
1543173564Scperciva	struct timeval			mono;
1544173564Scperciva	struct timeval			diff;
1545173564Scperciva	int				cgt_rc;
1546173564Scperciva	int				gtod_rc;
1547173564Scperciva
1548173564Scperciva	event_base_gettimeofday_cached(b, &latest);
1549173564Scperciva	if (b == cached_b &&
1550161748Scperciva	    !memcmp(&latest, &cached, sizeof(latest))) {
1551161748Scperciva		*caller_tv = adj_cached;
1552161748Scperciva		return 0;
1553161748Scperciva	}
1554161748Scperciva	cached = latest;
1555161748Scperciva	cached_b = b;
1556161748Scperciva	if (!offset_ready) {
1557161748Scperciva		cgt_rc = clock_gettime(CLOCK_MONOTONIC, &ts);
1558161748Scperciva		gtod_rc = gettimeofday(&systemt, NULL);
1559161748Scperciva		if (0 != gtod_rc) {
1560161748Scperciva			msyslog(LOG_ERR,
1561161748Scperciva				"%s: gettimeofday() error %m",
1562161748Scperciva				progname);
1563161748Scperciva			exit(1);
1564161748Scperciva		}
1565161748Scperciva		diff = sub_tval(systemt, latest);
1566161748Scperciva		if (debug > 1)
1567161748Scperciva			printf("system minus cached %+ld.%06ld\n",
1568161748Scperciva			       (long)diff.tv_sec, (long)diff.tv_usec);
1569161748Scperciva		if (0 != cgt_rc || labs((long)diff.tv_sec) < 3600) {
1570161748Scperciva			/*
1571161748Scperciva			 * Either use_monotonic == 0, or this libevent
1572161748Scperciva			 * has been repaired.  Leave offset at zero.
1573161748Scperciva			 */
1574161748Scperciva		} else {
1575161748Scperciva			mono.tv_sec = ts.tv_sec;
1576161748Scperciva			mono.tv_usec = ts.tv_nsec / 1000;
1577161748Scperciva			diff = sub_tval(latest, mono);
1578161748Scperciva			if (debug > 1)
1579161748Scperciva				printf("cached minus monotonic %+ld.%06ld\n",
1580161748Scperciva				       (long)diff.tv_sec, (long)diff.tv_usec);
1581161748Scperciva			if (labs((long)diff.tv_sec) < 3600) {
1582161748Scperciva				/* older libevent2 using monotonic */
1583161748Scperciva				offset = sub_tval(systemt, mono);
1584161748Scperciva				TRACE(1, ("%s: Offsetting libevent CLOCK_MONOTONIC times  by %+ld.%06ld\n",
1585161748Scperciva					 "gettimeofday_cached",
1586161748Scperciva					 (long)offset.tv_sec,
1587161748Scperciva					 (long)offset.tv_usec));
1588161748Scperciva			}
1589161748Scperciva		}
1590161748Scperciva		offset_ready = TRUE;
1591161748Scperciva	}
1592161748Scperciva	adj_cached = add_tval(cached, offset);
1593161748Scperciva	*caller_tv = adj_cached;
1594161748Scperciva
1595161748Scperciva	return 0;
1596161748Scperciva#else
1597161748Scperciva	return event_base_gettimeofday_cached(b, caller_tv);
1598161748Scperciva#endif
1599161748Scperciva}
1600161748Scperciva
1601161748Scperciva