1285612Sdelphij#include <config.h>
2132451Sroberto
3285612Sdelphij#include <event2/util.h>
4285612Sdelphij#include <event2/event.h>
5132451Sroberto
6285612Sdelphij#include "ntp_workimpl.h"
7285612Sdelphij#ifdef WORK_THREAD
8285612Sdelphij# include <event2/thread.h>
9285612Sdelphij#endif
10132451Sroberto
11285612Sdelphij#include "main.h"
12285612Sdelphij#include "ntp_libopts.h"
13285612Sdelphij#include "kod_management.h"
14285612Sdelphij#include "networking.h"
15285612Sdelphij#include "utilities.h"
16285612Sdelphij#include "log.h"
17285612Sdelphij#include "libntp.h"
18132451Sroberto
19132451Sroberto
20285612Sdelphijint shutting_down;
21285612Sdelphijint time_derived;
22285612Sdelphijint time_adjusted;
23285612Sdelphijint n_pending_dns = 0;
24285612Sdelphijint n_pending_ntp = 0;
25285612Sdelphijint ai_fam_pref = AF_UNSPEC;
26285612Sdelphijint ntpver = 4;
27285612Sdelphijdouble steplimit = -1;
28285612SdelphijSOCKET sock4 = -1;		/* Socket for IPv4 */
29285612SdelphijSOCKET sock6 = -1;		/* Socket for IPv6 */
30285612Sdelphij/*
31285612Sdelphij** BCAST *must* listen on port 123 (by default), so we can only
32285612Sdelphij** use the UCST sockets (above) if they too are using port 123
33285612Sdelphij*/
34285612SdelphijSOCKET bsock4 = -1;		/* Broadcast Socket for IPv4 */
35285612SdelphijSOCKET bsock6 = -1;		/* Broadcast Socket for IPv6 */
36285612Sdelphijstruct event_base *base;
37285612Sdelphijstruct event *ev_sock4;
38285612Sdelphijstruct event *ev_sock6;
39285612Sdelphijstruct event *ev_worker_timeout;
40285612Sdelphijstruct event *ev_xmt_timer;
41132451Sroberto
42285612Sdelphijstruct dns_ctx {
43285612Sdelphij	const char *	name;
44285612Sdelphij	int		flags;
45285612Sdelphij#define CTX_BCST	0x0001
46285612Sdelphij#define CTX_UCST	0x0002
47285612Sdelphij#define CTX_xCST	0x0003
48285612Sdelphij#define CTX_CONC	0x0004
49285612Sdelphij#define CTX_unused	0xfffd
50285612Sdelphij	int		key_id;
51285612Sdelphij	struct timeval	timeout;
52285612Sdelphij	struct key *	key;
53285612Sdelphij};
54132451Sroberto
55285612Sdelphijtypedef struct sent_pkt_tag sent_pkt;
56285612Sdelphijstruct sent_pkt_tag {
57285612Sdelphij	sent_pkt *		link;
58285612Sdelphij	struct dns_ctx *	dctx;
59285612Sdelphij	sockaddr_u		addr;
60285612Sdelphij	time_t			stime;
61285612Sdelphij	int			done;
62285612Sdelphij	struct pkt		x_pkt;
63285612Sdelphij};
64132451Sroberto
65285612Sdelphijtypedef struct xmt_ctx_tag xmt_ctx;
66285612Sdelphijstruct xmt_ctx_tag {
67285612Sdelphij	xmt_ctx *		link;
68285612Sdelphij	SOCKET			sock;
69285612Sdelphij	time_t			sched;
70285612Sdelphij	sent_pkt *		spkt;
71285612Sdelphij};
72132451Sroberto
73285612Sdelphijstruct timeval	gap;
74285612Sdelphijxmt_ctx *	xmt_q;
75285612Sdelphijstruct key *	keys = NULL;
76285612Sdelphijint		response_timeout;
77285612Sdelphijstruct timeval	response_tv;
78285612Sdelphijstruct timeval	start_tv;
79285612Sdelphij/* check the timeout at least once per second */
80285612Sdelphijstruct timeval	wakeup_tv = { 0, 888888 };
81132451Sroberto
82285612Sdelphijsent_pkt *	fam_listheads[2];
83285612Sdelphij#define v4_pkts_list	(fam_listheads[0])
84285612Sdelphij#define v6_pkts_list	(fam_listheads[1])
85132451Sroberto
86285612Sdelphijstatic union {
87285612Sdelphij	struct pkt pkt;
88285612Sdelphij	char   buf[LEN_PKT_NOMAC + NTP_MAXEXTEN + MAX_MAC_LEN];
89285612Sdelphij} rbuf;
90132451Sroberto
91285612Sdelphij#define r_pkt  rbuf.pkt
92132451Sroberto
93285612Sdelphij#ifdef HAVE_DROPROOT
94285612Sdelphijint droproot;			/* intres imports these */
95285612Sdelphijint root_dropped;
96285612Sdelphij#endif
97285612Sdelphiju_long current_time;		/* libntp/authkeys.c */
98132451Sroberto
99285612Sdelphijvoid open_sockets(void);
100285612Sdelphijvoid handle_lookup(const char *name, int flags);
101285612Sdelphijvoid sntp_addremove_fd(int fd, int is_pipe, int remove_it);
102285612Sdelphijvoid worker_timeout(evutil_socket_t, short, void *);
103285612Sdelphijvoid worker_resp_cb(evutil_socket_t, short, void *);
104285612Sdelphijvoid sntp_name_resolved(int, int, void *, const char *, const char *,
105285612Sdelphij			const struct addrinfo *,
106285612Sdelphij			const struct addrinfo *);
107285612Sdelphijvoid queue_xmt(SOCKET sock, struct dns_ctx *dctx, sent_pkt *spkt,
108285612Sdelphij	       u_int xmt_delay);
109285612Sdelphijvoid xmt_timer_cb(evutil_socket_t, short, void *ptr);
110285612Sdelphijvoid xmt(xmt_ctx *xctx);
111285612Sdelphijint  check_kod(const struct addrinfo *ai);
112285612Sdelphijvoid timeout_query(sent_pkt *);
113285612Sdelphijvoid timeout_queries(void);
114285612Sdelphijvoid sock_cb(evutil_socket_t, short, void *);
115285612Sdelphijvoid check_exit_conditions(void);
116285612Sdelphijvoid sntp_libevent_log_cb(int, const char *);
117285612Sdelphijvoid set_li_vn_mode(struct pkt *spkt, char leap, char version, char mode);
118285612Sdelphijint  set_time(double offset);
119285612Sdelphijvoid dec_pending_ntp(const char *, sockaddr_u *);
120285612Sdelphijint  libevent_version_ok(void);
121285612Sdelphijint  gettimeofday_cached(struct event_base *b, struct timeval *tv);
122132451Sroberto
123132451Sroberto
124285612Sdelphij/*
125285612Sdelphij * The actual main function.
126285612Sdelphij */
127285612Sdelphijint
128285612Sdelphijsntp_main (
129285612Sdelphij	int argc,
130285612Sdelphij	char **argv,
131285612Sdelphij	const char *sntpVersion
132285612Sdelphij	)
133285612Sdelphij{
134285612Sdelphij	int			i;
135285612Sdelphij	int			exitcode;
136285612Sdelphij	int			optct;
137285612Sdelphij	struct event_config *	evcfg;
138132451Sroberto
139285612Sdelphij	/* Initialize logging system - sets up progname */
140285612Sdelphij	sntp_init_logging(argv[0]);
141132451Sroberto
142285612Sdelphij	if (!libevent_version_ok())
143285612Sdelphij		exit(EX_SOFTWARE);
144132451Sroberto
145285612Sdelphij	init_lib();
146285612Sdelphij	init_auth();
147132451Sroberto
148285612Sdelphij	optct = ntpOptionProcess(&sntpOptions, argc, argv);
149285612Sdelphij	argc -= optct;
150285612Sdelphij	argv += optct;
151132451Sroberto
152132451Sroberto
153285612Sdelphij	debug = OPT_VALUE_SET_DEBUG_LEVEL;
154132451Sroberto
155285612Sdelphij	TRACE(2, ("init_lib() done, %s%s\n",
156285612Sdelphij		  (ipv4_works)
157285612Sdelphij		      ? "ipv4_works "
158285612Sdelphij		      : "",
159285612Sdelphij		  (ipv6_works)
160285612Sdelphij		      ? "ipv6_works "
161285612Sdelphij		      : ""));
162285612Sdelphij	ntpver = OPT_VALUE_NTPVERSION;
163285612Sdelphij	steplimit = OPT_VALUE_STEPLIMIT / 1e3;
164285612Sdelphij	gap.tv_usec = max(0, OPT_VALUE_GAP * 1000);
165285612Sdelphij	gap.tv_usec = min(gap.tv_usec, 999999);
166132451Sroberto
167285612Sdelphij	if (HAVE_OPT(LOGFILE))
168285612Sdelphij		open_logfile(OPT_ARG(LOGFILE));
169132451Sroberto
170285612Sdelphij	msyslog(LOG_INFO, "%s", sntpVersion);
171132451Sroberto
172285612Sdelphij	if (0 == argc && !HAVE_OPT(BROADCAST) && !HAVE_OPT(CONCURRENT)) {
173285612Sdelphij		printf("%s: Must supply at least one of -b hostname, -c hostname, or hostname.\n",
174285612Sdelphij		       progname);
175285612Sdelphij		exit(EX_USAGE);
176285612Sdelphij	}
177132451Sroberto
178132451Sroberto
179285612Sdelphij	/*
180285612Sdelphij	** Eventually, we probably want:
181285612Sdelphij	** - separate bcst and ucst timeouts (why?)
182285612Sdelphij	** - multiple --timeout values in the commandline
183285612Sdelphij	*/
184132451Sroberto
185285612Sdelphij	response_timeout = OPT_VALUE_TIMEOUT;
186285612Sdelphij	response_tv.tv_sec = response_timeout;
187285612Sdelphij	response_tv.tv_usec = 0;
188132451Sroberto
189285612Sdelphij	/* IPv6 available? */
190285612Sdelphij	if (isc_net_probeipv6() != ISC_R_SUCCESS) {
191285612Sdelphij		ai_fam_pref = AF_INET;
192285612Sdelphij		TRACE(1, ("No ipv6 support available, forcing ipv4\n"));
193285612Sdelphij	} else {
194285612Sdelphij		/* Check for options -4 and -6 */
195285612Sdelphij		if (HAVE_OPT(IPV4))
196285612Sdelphij			ai_fam_pref = AF_INET;
197285612Sdelphij		else if (HAVE_OPT(IPV6))
198285612Sdelphij			ai_fam_pref = AF_INET6;
199285612Sdelphij	}
200132451Sroberto
201285612Sdelphij	/* TODO: Parse config file if declared */
202132451Sroberto
203285612Sdelphij	/*
204285612Sdelphij	** Init the KOD system.
205285612Sdelphij	** For embedded systems with no writable filesystem,
206285612Sdelphij	** -K /dev/null can be used to disable KoD storage.
207285612Sdelphij	*/
208285612Sdelphij	kod_init_kod_db(OPT_ARG(KOD), FALSE);
209132451Sroberto
210330567Sgordon	/* HMS: Check and see what happens if KEYFILE doesn't exist */
211330567Sgordon	auth_init(OPT_ARG(KEYFILE), &keys);
212132451Sroberto
213285612Sdelphij	/*
214285612Sdelphij	** Considering employing a variable that prevents functions of doing
215285612Sdelphij	** anything until everything is initialized properly
216285612Sdelphij	**
217285612Sdelphij	** HMS: What exactly does the above mean?
218285612Sdelphij	*/
219285612Sdelphij	event_set_log_callback(&sntp_libevent_log_cb);
220285612Sdelphij	if (debug > 0)
221285612Sdelphij		event_enable_debug_mode();
222285612Sdelphij#ifdef WORK_THREAD
223285612Sdelphij	evthread_use_pthreads();
224285612Sdelphij	/* we use libevent from main thread only, locks should be academic */
225285612Sdelphij	if (debug > 0)
226285612Sdelphij		evthread_enable_lock_debuging();
227285612Sdelphij#endif
228285612Sdelphij	evcfg = event_config_new();
229285612Sdelphij	if (NULL == evcfg) {
230285612Sdelphij		printf("%s: event_config_new() failed!\n", progname);
231285612Sdelphij		return -1;
232285612Sdelphij	}
233285612Sdelphij#ifndef HAVE_SOCKETPAIR
234285612Sdelphij	event_config_require_features(evcfg, EV_FEATURE_FDS);
235285612Sdelphij#endif
236285612Sdelphij	/* all libevent calls are from main thread */
237285612Sdelphij	/* event_config_set_flag(evcfg, EVENT_BASE_FLAG_NOLOCK); */
238285612Sdelphij	base = event_base_new_with_config(evcfg);
239285612Sdelphij	event_config_free(evcfg);
240285612Sdelphij	if (NULL == base) {
241285612Sdelphij		printf("%s: event_base_new() failed!\n", progname);
242285612Sdelphij		return -1;
243285612Sdelphij	}
244132451Sroberto
245285612Sdelphij	/* wire into intres resolver */
246285612Sdelphij	worker_per_query = TRUE;
247285612Sdelphij	addremove_io_fd = &sntp_addremove_fd;
248132451Sroberto
249285612Sdelphij	open_sockets();
250132451Sroberto
251285612Sdelphij	if (HAVE_OPT(BROADCAST)) {
252285612Sdelphij		int		cn = STACKCT_OPT(  BROADCAST );
253285612Sdelphij		const char **	cp = STACKLST_OPT( BROADCAST );
254132451Sroberto
255285612Sdelphij		while (cn-- > 0) {
256285612Sdelphij			handle_lookup(*cp, CTX_BCST);
257285612Sdelphij			cp++;
258285612Sdelphij		}
259285612Sdelphij	}
260132451Sroberto
261285612Sdelphij	if (HAVE_OPT(CONCURRENT)) {
262285612Sdelphij		int		cn = STACKCT_OPT( CONCURRENT );
263285612Sdelphij		const char **	cp = STACKLST_OPT( CONCURRENT );
264132451Sroberto
265285612Sdelphij		while (cn-- > 0) {
266285612Sdelphij			handle_lookup(*cp, CTX_UCST | CTX_CONC);
267285612Sdelphij			cp++;
268285612Sdelphij		}
269285612Sdelphij	}
270132451Sroberto
271285612Sdelphij	for (i = 0; i < argc; ++i)
272285612Sdelphij		handle_lookup(argv[i], CTX_UCST);
273132451Sroberto
274285612Sdelphij	gettimeofday_cached(base, &start_tv);
275285612Sdelphij	event_base_dispatch(base);
276285612Sdelphij	event_base_free(base);
277132451Sroberto
278285612Sdelphij	if (!time_adjusted &&
279285612Sdelphij	    (ENABLED_OPT(STEP) || ENABLED_OPT(SLEW)))
280285612Sdelphij		exitcode = 1;
281285612Sdelphij	else
282285612Sdelphij		exitcode = 0;
283132451Sroberto
284285612Sdelphij	return exitcode;
285285612Sdelphij}
286132451Sroberto
287132451Sroberto
288285612Sdelphij/*
289285612Sdelphij** open sockets and make them non-blocking
290285612Sdelphij*/
291285612Sdelphijvoid
292285612Sdelphijopen_sockets(
293285612Sdelphij	void
294285612Sdelphij	)
295285612Sdelphij{
296285612Sdelphij	sockaddr_u	name;
297132451Sroberto
298285612Sdelphij	if (-1 == sock4) {
299285612Sdelphij		sock4 = socket(PF_INET, SOCK_DGRAM, 0);
300285612Sdelphij		if (-1 == sock4) {
301285612Sdelphij			/* error getting a socket */
302285612Sdelphij			msyslog(LOG_ERR, "open_sockets: socket(PF_INET) failed: %m");
303285612Sdelphij			exit(1);
304285612Sdelphij		}
305285612Sdelphij		/* Make it non-blocking */
306285612Sdelphij		make_socket_nonblocking(sock4);
307132451Sroberto
308285612Sdelphij		/* Let's try using a wildcard... */
309285612Sdelphij		ZERO(name);
310285612Sdelphij		AF(&name) = AF_INET;
311285612Sdelphij		SET_ADDR4N(&name, INADDR_ANY);
312285612Sdelphij		SET_PORT(&name, (HAVE_OPT(USERESERVEDPORT) ? 123 : 0));
313132451Sroberto
314285612Sdelphij		if (-1 == bind(sock4, &name.sa,
315285612Sdelphij			       SOCKLEN(&name))) {
316285612Sdelphij			msyslog(LOG_ERR, "open_sockets: bind(sock4) failed: %m");
317285612Sdelphij			exit(1);
318285612Sdelphij		}
319132451Sroberto
320285612Sdelphij		/* Register an NTP callback for recv/timeout */
321285612Sdelphij		ev_sock4 = event_new(base, sock4,
322285612Sdelphij				     EV_TIMEOUT | EV_READ | EV_PERSIST,
323285612Sdelphij				     &sock_cb, NULL);
324285612Sdelphij		if (NULL == ev_sock4) {
325285612Sdelphij			msyslog(LOG_ERR,
326285612Sdelphij				"open_sockets: event_new(base, sock4) failed!");
327285612Sdelphij		} else {
328285612Sdelphij			event_add(ev_sock4, &wakeup_tv);
329285612Sdelphij		}
330285612Sdelphij	}
331132451Sroberto
332285612Sdelphij	/* We may not always have IPv6... */
333285612Sdelphij	if (-1 == sock6 && ipv6_works) {
334285612Sdelphij		sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
335285612Sdelphij		if (-1 == sock6 && ipv6_works) {
336285612Sdelphij			/* error getting a socket */
337285612Sdelphij			msyslog(LOG_ERR, "open_sockets: socket(PF_INET6) failed: %m");
338285612Sdelphij			exit(1);
339285612Sdelphij		}
340285612Sdelphij		/* Make it non-blocking */
341285612Sdelphij		make_socket_nonblocking(sock6);
342132451Sroberto
343285612Sdelphij		/* Let's try using a wildcard... */
344285612Sdelphij		ZERO(name);
345285612Sdelphij		AF(&name) = AF_INET6;
346285612Sdelphij		SET_ADDR6N(&name, in6addr_any);
347285612Sdelphij		SET_PORT(&name, (HAVE_OPT(USERESERVEDPORT) ? 123 : 0));
348132451Sroberto
349285612Sdelphij		if (-1 == bind(sock6, &name.sa,
350285612Sdelphij			       SOCKLEN(&name))) {
351285612Sdelphij			msyslog(LOG_ERR, "open_sockets: bind(sock6) failed: %m");
352285612Sdelphij			exit(1);
353285612Sdelphij		}
354285612Sdelphij		/* Register an NTP callback for recv/timeout */
355285612Sdelphij		ev_sock6 = event_new(base, sock6,
356285612Sdelphij				     EV_TIMEOUT | EV_READ | EV_PERSIST,
357285612Sdelphij				     &sock_cb, NULL);
358285612Sdelphij		if (NULL == ev_sock6) {
359285612Sdelphij			msyslog(LOG_ERR,
360285612Sdelphij				"open_sockets: event_new(base, sock6) failed!");
361285612Sdelphij		} else {
362285612Sdelphij			event_add(ev_sock6, &wakeup_tv);
363285612Sdelphij		}
364285612Sdelphij	}
365285612Sdelphij
366285612Sdelphij	return;
367285612Sdelphij}
368132451Sroberto
369132451Sroberto
370285612Sdelphij/*
371285612Sdelphij** handle_lookup
372285612Sdelphij*/
373285612Sdelphijvoid
374285612Sdelphijhandle_lookup(
375285612Sdelphij	const char *name,
376285612Sdelphij	int flags
377285612Sdelphij	)
378285612Sdelphij{
379285612Sdelphij	struct addrinfo	hints;	/* Local copy is OK */
380285612Sdelphij	struct dns_ctx *ctx;
381285612Sdelphij	char *		name_copy;
382285612Sdelphij	size_t		name_sz;
383285612Sdelphij	size_t		octets;
384132451Sroberto
385285612Sdelphij	TRACE(1, ("handle_lookup(%s,%#x)\n", name, flags));
386132451Sroberto
387285612Sdelphij	ZERO(hints);
388285612Sdelphij	hints.ai_family = ai_fam_pref;
389285612Sdelphij	hints.ai_flags = AI_CANONNAME | Z_AI_NUMERICSERV;
390285612Sdelphij	/*
391285612Sdelphij	** Unless we specify a socktype, we'll get at least two
392285612Sdelphij	** entries for each address: one for TCP and one for
393285612Sdelphij	** UDP. That's not what we want.
394285612Sdelphij	*/
395285612Sdelphij	hints.ai_socktype = SOCK_DGRAM;
396285612Sdelphij	hints.ai_protocol = IPPROTO_UDP;
397132451Sroberto
398285612Sdelphij	name_sz = 1 + strlen(name);
399285612Sdelphij	octets = sizeof(*ctx) + name_sz;	// Space for a ctx and the name
400285612Sdelphij	ctx = emalloc_zero(octets);		// ctx at ctx[0]
401285612Sdelphij	name_copy = (char *)(ctx + 1);		// Put the name at ctx[1]
402285612Sdelphij	memcpy(name_copy, name, name_sz);	// copy the name to ctx[1]
403285612Sdelphij	ctx->name = name_copy;			// point to it...
404285612Sdelphij	ctx->flags = flags;
405285612Sdelphij	ctx->timeout = response_tv;
406330567Sgordon	ctx->key = NULL;
407132451Sroberto
408285612Sdelphij	/* The following should arguably be passed in... */
409330567Sgordon	if (ENABLED_OPT(AUTHENTICATION)) {
410330567Sgordon		ctx->key_id = OPT_VALUE_AUTHENTICATION;
411285612Sdelphij		get_key(ctx->key_id, &ctx->key);
412330567Sgordon		if (NULL == ctx->key) {
413330567Sgordon			fprintf(stderr, "%s: Authentication with keyID %d requested, but no matching keyID found in <%s>!\n",
414330567Sgordon				progname, ctx->key_id, OPT_ARG(KEYFILE));
415330567Sgordon			exit(1);
416330567Sgordon		}
417285612Sdelphij	} else {
418285612Sdelphij		ctx->key_id = -1;
419285612Sdelphij	}
420132451Sroberto
421285612Sdelphij	++n_pending_dns;
422285612Sdelphij	getaddrinfo_sometime(name, "123", &hints, 0,
423285612Sdelphij			     &sntp_name_resolved, ctx);
424132451Sroberto}
425132451Sroberto
426132451Sroberto
427285612Sdelphij/*
428285612Sdelphij** DNS Callback:
429285612Sdelphij** - For each IP:
430285612Sdelphij** - - open a socket
431285612Sdelphij** - - increment n_pending_ntp
432285612Sdelphij** - - send a request if this is a Unicast callback
433285612Sdelphij** - - queue wait for response
434285612Sdelphij** - decrement n_pending_dns
435285612Sdelphij*/
436285612Sdelphijvoid
437285612Sdelphijsntp_name_resolved(
438285612Sdelphij	int			rescode,
439285612Sdelphij	int			gai_errno,
440285612Sdelphij	void *			context,
441285612Sdelphij	const char *		name,
442285612Sdelphij	const char *		service,
443285612Sdelphij	const struct addrinfo *	hints,
444285612Sdelphij	const struct addrinfo *	addr
445285612Sdelphij	)
446285612Sdelphij{
447285612Sdelphij	struct dns_ctx *	dctx;
448285612Sdelphij	sent_pkt *		spkt;
449285612Sdelphij	const struct addrinfo *	ai;
450285612Sdelphij	SOCKET			sock;
451285612Sdelphij	u_int			xmt_delay_v4;
452285612Sdelphij	u_int			xmt_delay_v6;
453285612Sdelphij	u_int			xmt_delay;
454285612Sdelphij	size_t			octets;
455132451Sroberto
456285612Sdelphij	xmt_delay_v4 = 0;
457285612Sdelphij	xmt_delay_v6 = 0;
458285612Sdelphij	dctx = context;
459285612Sdelphij	if (rescode) {
460285612Sdelphij#ifdef EAI_SYSTEM
461285612Sdelphij		if (EAI_SYSTEM == rescode) {
462285612Sdelphij			errno = gai_errno;
463285612Sdelphij			mfprintf(stderr, "%s lookup error %m\n",
464285612Sdelphij				 dctx->name);
465285612Sdelphij		} else
466285612Sdelphij#endif
467285612Sdelphij			fprintf(stderr, "%s lookup error %s\n",
468285612Sdelphij				dctx->name, gai_strerror(rescode));
469285612Sdelphij	} else {
470285612Sdelphij		TRACE(3, ("%s [%s]\n", dctx->name,
471285612Sdelphij			  (addr->ai_canonname != NULL)
472285612Sdelphij			      ? addr->ai_canonname
473285612Sdelphij			      : ""));
474132451Sroberto
475285612Sdelphij		for (ai = addr; ai != NULL; ai = ai->ai_next) {
476132451Sroberto
477285612Sdelphij			if (check_kod(ai))
478285612Sdelphij				continue;
479132451Sroberto
480285612Sdelphij			switch (ai->ai_family) {
481132451Sroberto
482285612Sdelphij			case AF_INET:
483285612Sdelphij				sock = sock4;
484285612Sdelphij				xmt_delay = xmt_delay_v4;
485285612Sdelphij				xmt_delay_v4++;
486285612Sdelphij				break;
487132451Sroberto
488285612Sdelphij			case AF_INET6:
489285612Sdelphij				if (!ipv6_works)
490285612Sdelphij					continue;
491132451Sroberto
492285612Sdelphij				sock = sock6;
493285612Sdelphij				xmt_delay = xmt_delay_v6;
494285612Sdelphij				xmt_delay_v6++;
495285612Sdelphij				break;
496132451Sroberto
497285612Sdelphij			default:
498285612Sdelphij				msyslog(LOG_ERR, "sntp_name_resolved: unexpected ai_family: %d",
499285612Sdelphij					ai->ai_family);
500285612Sdelphij				exit(1);
501285612Sdelphij				break;
502285612Sdelphij			}
503132451Sroberto
504285612Sdelphij			/*
505285612Sdelphij			** We're waiting for a response for either unicast
506285612Sdelphij			** or broadcast, so...
507285612Sdelphij			*/
508285612Sdelphij			++n_pending_ntp;
509132451Sroberto
510285612Sdelphij			/* If this is for a unicast IP, queue a request */
511285612Sdelphij			if (dctx->flags & CTX_UCST) {
512285612Sdelphij				spkt = emalloc_zero(sizeof(*spkt));
513285612Sdelphij				spkt->dctx = dctx;
514285612Sdelphij				octets = min(ai->ai_addrlen, sizeof(spkt->addr));
515285612Sdelphij				memcpy(&spkt->addr, ai->ai_addr, octets);
516285612Sdelphij				queue_xmt(sock, dctx, spkt, xmt_delay);
517285612Sdelphij			}
518285612Sdelphij		}
519285612Sdelphij	}
520285612Sdelphij	/* n_pending_dns really should be >0 here... */
521285612Sdelphij	--n_pending_dns;
522285612Sdelphij	check_exit_conditions();
523132451Sroberto}
524132451Sroberto
525132451Sroberto
526285612Sdelphij/*
527285612Sdelphij** queue_xmt
528285612Sdelphij*/
529285612Sdelphijvoid
530285612Sdelphijqueue_xmt(
531285612Sdelphij	SOCKET			sock,
532285612Sdelphij	struct dns_ctx *	dctx,
533285612Sdelphij	sent_pkt *		spkt,
534285612Sdelphij	u_int			xmt_delay
535285612Sdelphij	)
536285612Sdelphij{
537285612Sdelphij	sockaddr_u *	dest;
538285612Sdelphij	sent_pkt **	pkt_listp;
539285612Sdelphij	sent_pkt *	match;
540285612Sdelphij	xmt_ctx *	xctx;
541285612Sdelphij	struct timeval	start_cb;
542285612Sdelphij	struct timeval	delay;
543132451Sroberto
544285612Sdelphij	dest = &spkt->addr;
545285612Sdelphij	if (IS_IPV6(dest))
546285612Sdelphij		pkt_listp = &v6_pkts_list;
547285612Sdelphij	else
548285612Sdelphij		pkt_listp = &v4_pkts_list;
549132451Sroberto
550285612Sdelphij	/* reject attempts to add address already listed */
551285612Sdelphij	for (match = *pkt_listp; match != NULL; match = match->link) {
552285612Sdelphij		if (ADDR_PORT_EQ(&spkt->addr, &match->addr)) {
553285612Sdelphij			if (strcasecmp(spkt->dctx->name,
554285612Sdelphij				       match->dctx->name))
555285612Sdelphij				printf("%s %s duplicate address from %s ignored.\n",
556285612Sdelphij				       sptoa(&match->addr),
557285612Sdelphij				       match->dctx->name,
558285612Sdelphij				       spkt->dctx->name);
559285612Sdelphij			else
560285612Sdelphij				printf("%s %s, duplicate address ignored.\n",
561285612Sdelphij				       sptoa(&match->addr),
562285612Sdelphij				       match->dctx->name);
563285612Sdelphij			dec_pending_ntp(spkt->dctx->name, &spkt->addr);
564285612Sdelphij			free(spkt);
565285612Sdelphij			return;
566285612Sdelphij		}
567285612Sdelphij	}
568132451Sroberto
569285612Sdelphij	LINK_SLIST(*pkt_listp, spkt, link);
570132451Sroberto
571285612Sdelphij	xctx = emalloc_zero(sizeof(*xctx));
572285612Sdelphij	xctx->sock = sock;
573285612Sdelphij	xctx->spkt = spkt;
574285612Sdelphij	gettimeofday_cached(base, &start_cb);
575285612Sdelphij	xctx->sched = start_cb.tv_sec + (2 * xmt_delay);
576132451Sroberto
577285612Sdelphij	LINK_SORT_SLIST(xmt_q, xctx, (xctx->sched < L_S_S_CUR()->sched),
578285612Sdelphij			link, xmt_ctx);
579285612Sdelphij	if (xmt_q == xctx) {
580285612Sdelphij		/*
581285612Sdelphij		 * The new entry is the first scheduled.  The timer is
582285612Sdelphij		 * either not active or is set for the second xmt
583285612Sdelphij		 * context in xmt_q.
584285612Sdelphij		 */
585285612Sdelphij		if (NULL == ev_xmt_timer)
586285612Sdelphij			ev_xmt_timer = event_new(base, INVALID_SOCKET,
587285612Sdelphij						 EV_TIMEOUT,
588285612Sdelphij						 &xmt_timer_cb, NULL);
589285612Sdelphij		if (NULL == ev_xmt_timer) {
590285612Sdelphij			msyslog(LOG_ERR,
591285612Sdelphij				"queue_xmt: event_new(base, -1, EV_TIMEOUT) failed!");
592285612Sdelphij			exit(1);
593285612Sdelphij		}
594285612Sdelphij		ZERO(delay);
595285612Sdelphij		if (xctx->sched > start_cb.tv_sec)
596285612Sdelphij			delay.tv_sec = xctx->sched - start_cb.tv_sec;
597285612Sdelphij		event_add(ev_xmt_timer, &delay);
598285612Sdelphij		TRACE(2, ("queue_xmt: xmt timer for %u usec\n",
599285612Sdelphij			  (u_int)delay.tv_usec));
600285612Sdelphij	}
601132451Sroberto}
602132451Sroberto
603132451Sroberto
604285612Sdelphij/*
605285612Sdelphij** xmt_timer_cb
606285612Sdelphij*/
607285612Sdelphijvoid
608285612Sdelphijxmt_timer_cb(
609285612Sdelphij	evutil_socket_t	fd,
610285612Sdelphij	short		what,
611285612Sdelphij	void *		ctx
612285612Sdelphij	)
613285612Sdelphij{
614285612Sdelphij	struct timeval	start_cb;
615285612Sdelphij	struct timeval	delay;
616285612Sdelphij	xmt_ctx *	x;
617132451Sroberto
618285612Sdelphij	UNUSED_ARG(fd);
619285612Sdelphij	UNUSED_ARG(ctx);
620285612Sdelphij	DEBUG_INSIST(EV_TIMEOUT == what);
621132451Sroberto
622285612Sdelphij	if (NULL == xmt_q || shutting_down)
623285612Sdelphij		return;
624285612Sdelphij	gettimeofday_cached(base, &start_cb);
625285612Sdelphij	if (xmt_q->sched <= start_cb.tv_sec) {
626285612Sdelphij		UNLINK_HEAD_SLIST(x, xmt_q, link);
627285612Sdelphij		TRACE(2, ("xmt_timer_cb: at .%6.6u -> %s\n",
628285612Sdelphij			  (u_int)start_cb.tv_usec, stoa(&x->spkt->addr)));
629285612Sdelphij		xmt(x);
630285612Sdelphij		free(x);
631285612Sdelphij		if (NULL == xmt_q)
632285612Sdelphij			return;
633285612Sdelphij	}
634285612Sdelphij	if (xmt_q->sched <= start_cb.tv_sec) {
635285612Sdelphij		event_add(ev_xmt_timer, &gap);
636285612Sdelphij		TRACE(2, ("xmt_timer_cb: at .%6.6u gap %6.6u\n",
637285612Sdelphij			  (u_int)start_cb.tv_usec,
638285612Sdelphij			  (u_int)gap.tv_usec));
639285612Sdelphij	} else {
640285612Sdelphij		delay.tv_sec = xmt_q->sched - start_cb.tv_sec;
641285612Sdelphij		delay.tv_usec = 0;
642285612Sdelphij		event_add(ev_xmt_timer, &delay);
643285612Sdelphij		TRACE(2, ("xmt_timer_cb: at .%6.6u next %ld seconds\n",
644285612Sdelphij			  (u_int)start_cb.tv_usec,
645285612Sdelphij			  (long)delay.tv_sec));
646285612Sdelphij	}
647132451Sroberto}
648132451Sroberto
649132451Sroberto
650285612Sdelphij/*
651285612Sdelphij** xmt()
652285612Sdelphij*/
653285612Sdelphijvoid
654285612Sdelphijxmt(
655285612Sdelphij	xmt_ctx *	xctx
656285612Sdelphij	)
657285612Sdelphij{
658285612Sdelphij	SOCKET		sock = xctx->sock;
659285612Sdelphij	struct dns_ctx *dctx = xctx->spkt->dctx;
660285612Sdelphij	sent_pkt *	spkt = xctx->spkt;
661285612Sdelphij	sockaddr_u *	dst = &spkt->addr;
662285612Sdelphij	struct timeval	tv_xmt;
663285612Sdelphij	struct pkt	x_pkt;
664285612Sdelphij	size_t		pkt_len;
665285612Sdelphij	int		sent;
666132451Sroberto
667285612Sdelphij	if (0 != gettimeofday(&tv_xmt, NULL)) {
668285612Sdelphij		msyslog(LOG_ERR,
669285612Sdelphij			"xmt: gettimeofday() failed: %m");
670285612Sdelphij		exit(1);
671285612Sdelphij	}
672285612Sdelphij	tv_xmt.tv_sec += JAN_1970;
673132451Sroberto
674285612Sdelphij	pkt_len = generate_pkt(&x_pkt, &tv_xmt, dctx->key_id,
675285612Sdelphij			       dctx->key);
676132451Sroberto
677285612Sdelphij	sent = sendpkt(sock, dst, &x_pkt, pkt_len);
678285612Sdelphij	if (sent) {
679285612Sdelphij		/* Save the packet we sent... */
680285612Sdelphij		memcpy(&spkt->x_pkt, &x_pkt, min(sizeof(spkt->x_pkt),
681285612Sdelphij		       pkt_len));
682285612Sdelphij		spkt->stime = tv_xmt.tv_sec - JAN_1970;
683132451Sroberto
684285612Sdelphij		TRACE(2, ("xmt: %lx.%6.6u %s %s\n", (u_long)tv_xmt.tv_sec,
685285612Sdelphij			  (u_int)tv_xmt.tv_usec, dctx->name, stoa(dst)));
686285612Sdelphij	} else {
687285612Sdelphij		dec_pending_ntp(dctx->name, dst);
688285612Sdelphij	}
689132451Sroberto
690285612Sdelphij	return;
691285612Sdelphij}
692132451Sroberto
693132451Sroberto
694285612Sdelphij/*
695285612Sdelphij * timeout_queries() -- give up on unrequited NTP queries
696285612Sdelphij */
697285612Sdelphijvoid
698285612Sdelphijtimeout_queries(void)
699285612Sdelphij{
700285612Sdelphij	struct timeval	start_cb;
701285612Sdelphij	u_int		idx;
702285612Sdelphij	sent_pkt *	head;
703285612Sdelphij	sent_pkt *	spkt;
704285612Sdelphij	sent_pkt *	spkt_next;
705285612Sdelphij	long		age;
706285612Sdelphij	int didsomething = 0;
707132451Sroberto
708285612Sdelphij	TRACE(3, ("timeout_queries: called to check %u items\n",
709285612Sdelphij		  (unsigned)COUNTOF(fam_listheads)));
710132451Sroberto
711285612Sdelphij	gettimeofday_cached(base, &start_cb);
712285612Sdelphij	for (idx = 0; idx < COUNTOF(fam_listheads); idx++) {
713285612Sdelphij		head = fam_listheads[idx];
714285612Sdelphij		for (spkt = head; spkt != NULL; spkt = spkt_next) {
715285612Sdelphij			char xcst;
716132451Sroberto
717285612Sdelphij			didsomething = 1;
718285612Sdelphij			switch (spkt->dctx->flags & CTX_xCST) {
719285612Sdelphij			    case CTX_BCST:
720285612Sdelphij				xcst = 'B';
721285612Sdelphij				break;
722132451Sroberto
723285612Sdelphij			    case CTX_UCST:
724285612Sdelphij				xcst = 'U';
725285612Sdelphij				break;
726132451Sroberto
727285612Sdelphij			    default:
728285612Sdelphij				INSIST(!"spkt->dctx->flags neither UCST nor BCST");
729285612Sdelphij				break;
730285612Sdelphij			}
731132451Sroberto
732285612Sdelphij			spkt_next = spkt->link;
733285612Sdelphij			if (0 == spkt->stime || spkt->done)
734285612Sdelphij				continue;
735285612Sdelphij			age = start_cb.tv_sec - spkt->stime;
736285612Sdelphij			TRACE(3, ("%s %s %cCST age %ld\n",
737285612Sdelphij				  stoa(&spkt->addr),
738285612Sdelphij				  spkt->dctx->name, xcst, age));
739285612Sdelphij			if (age > response_timeout)
740285612Sdelphij				timeout_query(spkt);
741285612Sdelphij		}
742285612Sdelphij	}
743285612Sdelphij	// Do we care about didsomething?
744285612Sdelphij	TRACE(3, ("timeout_queries: didsomething is %d, age is %ld\n",
745285612Sdelphij		  didsomething, (long) (start_cb.tv_sec - start_tv.tv_sec)));
746285612Sdelphij	if (start_cb.tv_sec - start_tv.tv_sec > response_timeout) {
747285612Sdelphij		TRACE(3, ("timeout_queries: bail!\n"));
748285612Sdelphij		event_base_loopexit(base, NULL);
749285612Sdelphij		shutting_down = TRUE;
750285612Sdelphij	}
751132451Sroberto}
752132451Sroberto
753132451Sroberto
754285612Sdelphijvoid dec_pending_ntp(
755285612Sdelphij	const char *	name,
756285612Sdelphij	sockaddr_u *	server
757285612Sdelphij	)
758285612Sdelphij{
759285612Sdelphij	if (n_pending_ntp > 0) {
760285612Sdelphij		--n_pending_ntp;
761285612Sdelphij		check_exit_conditions();
762285612Sdelphij	} else {
763285612Sdelphij		INSIST(0 == n_pending_ntp);
764285612Sdelphij		TRACE(1, ("n_pending_ntp was zero before decrement for %s\n",
765285612Sdelphij			  hostnameaddr(name, server)));
766285612Sdelphij	}
767285612Sdelphij}
768132451Sroberto
769132451Sroberto
770285612Sdelphijvoid timeout_query(
771285612Sdelphij	sent_pkt *	spkt
772285612Sdelphij	)
773285612Sdelphij{
774285612Sdelphij	sockaddr_u *	server;
775285612Sdelphij	char		xcst;
776132451Sroberto
777132451Sroberto
778285612Sdelphij	switch (spkt->dctx->flags & CTX_xCST) {
779285612Sdelphij	    case CTX_BCST:
780285612Sdelphij		xcst = 'B';
781285612Sdelphij		break;
782132451Sroberto
783285612Sdelphij	    case CTX_UCST:
784285612Sdelphij		xcst = 'U';
785285612Sdelphij		break;
786132451Sroberto
787285612Sdelphij	    default:
788285612Sdelphij		INSIST(!"spkt->dctx->flags neither UCST nor BCST");
789285612Sdelphij		break;
790285612Sdelphij	}
791285612Sdelphij	spkt->done = TRUE;
792285612Sdelphij	server = &spkt->addr;
793285612Sdelphij	msyslog(LOG_INFO, "%s no %cCST response after %d seconds",
794285612Sdelphij		hostnameaddr(spkt->dctx->name, server), xcst,
795285612Sdelphij		response_timeout);
796285612Sdelphij	dec_pending_ntp(spkt->dctx->name, server);
797285612Sdelphij	return;
798285612Sdelphij}
799132451Sroberto
800132451Sroberto
801285612Sdelphij/*
802285612Sdelphij** check_kod
803285612Sdelphij*/
804285612Sdelphijint
805285612Sdelphijcheck_kod(
806285612Sdelphij	const struct addrinfo *	ai
807285612Sdelphij	)
808285612Sdelphij{
809285612Sdelphij	char *hostname;
810285612Sdelphij	struct kod_entry *reason;
811132451Sroberto
812285612Sdelphij	/* Is there a KoD on file for this address? */
813285612Sdelphij	hostname = addrinfo_to_str(ai);
814285612Sdelphij	TRACE(2, ("check_kod: checking <%s>\n", hostname));
815285612Sdelphij	if (search_entry(hostname, &reason)) {
816285612Sdelphij		printf("prior KoD for %s, skipping.\n",
817285612Sdelphij			hostname);
818285612Sdelphij		free(reason);
819285612Sdelphij		free(hostname);
820132451Sroberto
821285612Sdelphij		return 1;
822285612Sdelphij	}
823285612Sdelphij	free(hostname);
824132451Sroberto
825285612Sdelphij	return 0;
826132451Sroberto}
827132451Sroberto
828132451Sroberto
829285612Sdelphij/*
830285612Sdelphij** Socket readable/timeout Callback:
831285612Sdelphij** Read in the packet
832285612Sdelphij** Unicast:
833285612Sdelphij** - close socket
834285612Sdelphij** - decrement n_pending_ntp
835285612Sdelphij** - If packet is good, set the time and "exit"
836285612Sdelphij** Broadcast:
837285612Sdelphij** - If packet is good, set the time and "exit"
838285612Sdelphij*/
839285612Sdelphijvoid
840285612Sdelphijsock_cb(
841285612Sdelphij	evutil_socket_t fd,
842285612Sdelphij	short what,
843285612Sdelphij	void *ptr
844285612Sdelphij	)
845285612Sdelphij{
846285612Sdelphij	sockaddr_u	sender;
847285612Sdelphij	sockaddr_u *	psau;
848285612Sdelphij	sent_pkt **	p_pktlist;
849285612Sdelphij	sent_pkt *	spkt;
850285612Sdelphij	int		rpktl;
851285612Sdelphij	int		rc;
852132451Sroberto
853285612Sdelphij	INSIST(sock4 == fd || sock6 == fd);
854132451Sroberto
855285612Sdelphij	TRACE(3, ("sock_cb: event on sock%s:%s%s%s%s\n",
856285612Sdelphij		  (fd == sock6)
857285612Sdelphij		      ? "6"
858285612Sdelphij		      : "4",
859285612Sdelphij		  (what & EV_TIMEOUT) ? " timeout" : "",
860285612Sdelphij		  (what & EV_READ)    ? " read" : "",
861285612Sdelphij		  (what & EV_WRITE)   ? " write" : "",
862285612Sdelphij		  (what & EV_SIGNAL)  ? " signal" : ""));
863132451Sroberto
864285612Sdelphij	if (!(EV_READ & what)) {
865285612Sdelphij		if (EV_TIMEOUT & what)
866285612Sdelphij			timeout_queries();
867132451Sroberto
868285612Sdelphij		return;
869285612Sdelphij	}
870132451Sroberto
871285612Sdelphij	/* Read in the packet */
872285612Sdelphij	rpktl = recvdata(fd, &sender, &rbuf, sizeof(rbuf));
873285612Sdelphij	if (rpktl < 0) {
874285612Sdelphij		msyslog(LOG_DEBUG, "recvfrom error %m");
875285612Sdelphij		return;
876285612Sdelphij	}
877132451Sroberto
878285612Sdelphij	if (sock6 == fd)
879285612Sdelphij		p_pktlist = &v6_pkts_list;
880285612Sdelphij	else
881285612Sdelphij		p_pktlist = &v4_pkts_list;
882132451Sroberto
883285612Sdelphij	for (spkt = *p_pktlist; spkt != NULL; spkt = spkt->link) {
884285612Sdelphij		psau = &spkt->addr;
885285612Sdelphij		if (SOCK_EQ(&sender, psau))
886285612Sdelphij			break;
887285612Sdelphij	}
888285612Sdelphij	if (NULL == spkt) {
889285612Sdelphij		msyslog(LOG_WARNING,
890285612Sdelphij			"Packet from unexpected source %s dropped",
891285612Sdelphij			sptoa(&sender));
892285612Sdelphij		return;
893285612Sdelphij	}
894132451Sroberto
895285612Sdelphij	TRACE(1, ("sock_cb: %s %s\n", spkt->dctx->name,
896285612Sdelphij		  sptoa(&sender)));
897132451Sroberto
898285612Sdelphij	rpktl = process_pkt(&r_pkt, &sender, rpktl, MODE_SERVER,
899285612Sdelphij			    &spkt->x_pkt, "sock_cb");
900132451Sroberto
901285612Sdelphij	TRACE(2, ("sock_cb: process_pkt returned %d\n", rpktl));
902132451Sroberto
903285612Sdelphij	/* If this is a Unicast packet, one down ... */
904285612Sdelphij	if (!spkt->done && (CTX_UCST & spkt->dctx->flags)) {
905285612Sdelphij		dec_pending_ntp(spkt->dctx->name, &spkt->addr);
906285612Sdelphij		spkt->done = TRUE;
907285612Sdelphij	}
908132451Sroberto
909132451Sroberto
910285612Sdelphij	/* If the packet is good, set the time and we're all done */
911285612Sdelphij	rc = handle_pkt(rpktl, &r_pkt, &spkt->addr, spkt->dctx->name);
912285612Sdelphij	if (0 != rc)
913285612Sdelphij		TRACE(1, ("sock_cb: handle_pkt() returned %d\n", rc));
914285612Sdelphij	check_exit_conditions();
915285612Sdelphij}
916132451Sroberto
917132451Sroberto
918285612Sdelphij/*
919285612Sdelphij * check_exit_conditions()
920285612Sdelphij *
921285612Sdelphij * If sntp has a reply, ask the event loop to stop after this round of
922285612Sdelphij * callbacks, unless --wait was used.
923285612Sdelphij */
924285612Sdelphijvoid
925285612Sdelphijcheck_exit_conditions(void)
926285612Sdelphij{
927285612Sdelphij	if ((0 == n_pending_ntp && 0 == n_pending_dns) ||
928285612Sdelphij	    (time_derived && !HAVE_OPT(WAIT))) {
929285612Sdelphij		event_base_loopexit(base, NULL);
930285612Sdelphij		shutting_down = TRUE;
931285612Sdelphij	} else {
932285612Sdelphij		TRACE(2, ("%d NTP and %d name queries pending\n",
933285612Sdelphij			  n_pending_ntp, n_pending_dns));
934285612Sdelphij	}
935285612Sdelphij}
936132451Sroberto
937132451Sroberto
938285612Sdelphij/*
939285612Sdelphij * sntp_addremove_fd() is invoked by the intres blocking worker code
940285612Sdelphij * to read from a pipe, or to stop same.
941285612Sdelphij */
942285612Sdelphijvoid sntp_addremove_fd(
943285612Sdelphij	int	fd,
944285612Sdelphij	int	is_pipe,
945285612Sdelphij	int	remove_it
946285612Sdelphij	)
947285612Sdelphij{
948285612Sdelphij	u_int		idx;
949285612Sdelphij	blocking_child *c;
950285612Sdelphij	struct event *	ev;
951132451Sroberto
952285612Sdelphij#ifdef HAVE_SOCKETPAIR
953285612Sdelphij	if (is_pipe) {
954285612Sdelphij		/* sntp only asks for EV_FEATURE_FDS without HAVE_SOCKETPAIR */
955285612Sdelphij		msyslog(LOG_ERR, "fatal: pipes not supported on systems with socketpair()");
956285612Sdelphij		exit(1);
957285612Sdelphij	}
958285612Sdelphij#endif
959132451Sroberto
960285612Sdelphij	c = NULL;
961285612Sdelphij	for (idx = 0; idx < blocking_children_alloc; idx++) {
962285612Sdelphij		c = blocking_children[idx];
963285612Sdelphij		if (NULL == c)
964285612Sdelphij			continue;
965285612Sdelphij		if (fd == c->resp_read_pipe)
966285612Sdelphij			break;
967285612Sdelphij	}
968285612Sdelphij	if (idx == blocking_children_alloc)
969285612Sdelphij		return;
970132451Sroberto
971285612Sdelphij	if (remove_it) {
972285612Sdelphij		ev = c->resp_read_ctx;
973285612Sdelphij		c->resp_read_ctx = NULL;
974285612Sdelphij		event_del(ev);
975285612Sdelphij		event_free(ev);
976132451Sroberto
977285612Sdelphij		return;
978285612Sdelphij	}
979132451Sroberto
980285612Sdelphij	ev = event_new(base, fd, EV_READ | EV_PERSIST,
981285612Sdelphij		       &worker_resp_cb, c);
982285612Sdelphij	if (NULL == ev) {
983285612Sdelphij		msyslog(LOG_ERR,
984285612Sdelphij			"sntp_addremove_fd: event_new(base, fd) failed!");
985285612Sdelphij		return;
986285612Sdelphij	}
987285612Sdelphij	c->resp_read_ctx = ev;
988285612Sdelphij	event_add(ev, NULL);
989285612Sdelphij}
990132451Sroberto
991132451Sroberto
992285612Sdelphij/* called by forked intres child to close open descriptors */
993285612Sdelphij#ifdef WORK_FORK
994285612Sdelphijvoid
995285612Sdelphijkill_asyncio(
996285612Sdelphij	int	startfd
997285612Sdelphij	)
998285612Sdelphij{
999285612Sdelphij	if (INVALID_SOCKET != sock4) {
1000285612Sdelphij		closesocket(sock4);
1001285612Sdelphij		sock4 = INVALID_SOCKET;
1002285612Sdelphij	}
1003285612Sdelphij	if (INVALID_SOCKET != sock6) {
1004285612Sdelphij		closesocket(sock6);
1005285612Sdelphij		sock6 = INVALID_SOCKET;
1006285612Sdelphij	}
1007285612Sdelphij	if (INVALID_SOCKET != bsock4) {
1008285612Sdelphij		closesocket(sock4);
1009285612Sdelphij		sock4 = INVALID_SOCKET;
1010285612Sdelphij	}
1011285612Sdelphij	if (INVALID_SOCKET != bsock6) {
1012285612Sdelphij		closesocket(sock6);
1013285612Sdelphij		sock6 = INVALID_SOCKET;
1014285612Sdelphij	}
1015285612Sdelphij}
1016285612Sdelphij#endif
1017132451Sroberto
1018132451Sroberto
1019285612Sdelphij/*
1020285612Sdelphij * worker_resp_cb() is invoked when resp_read_pipe is readable.
1021285612Sdelphij */
1022285612Sdelphijvoid
1023285612Sdelphijworker_resp_cb(
1024285612Sdelphij	evutil_socket_t	fd,
1025285612Sdelphij	short		what,
1026285612Sdelphij	void *		ctx	/* blocking_child * */
1027285612Sdelphij	)
1028285612Sdelphij{
1029285612Sdelphij	blocking_child *	c;
1030132451Sroberto
1031285612Sdelphij	DEBUG_INSIST(EV_READ & what);
1032285612Sdelphij	c = ctx;
1033285612Sdelphij	DEBUG_INSIST(fd == c->resp_read_pipe);
1034285612Sdelphij	process_blocking_resp(c);
1035132451Sroberto}
1036132451Sroberto
1037132451Sroberto
1038285612Sdelphij/*
1039285612Sdelphij * intres_timeout_req(s) is invoked in the parent to schedule an idle
1040285612Sdelphij * timeout to fire in s seconds, if not reset earlier by a call to
1041285612Sdelphij * intres_timeout_req(0), which clears any pending timeout.  When the
1042285612Sdelphij * timeout expires, worker_idle_timer_fired() is invoked (again, in the
1043285612Sdelphij * parent).
1044285612Sdelphij *
1045285612Sdelphij * sntp and ntpd each provide implementations adapted to their timers.
1046285612Sdelphij */
1047285612Sdelphijvoid
1048285612Sdelphijintres_timeout_req(
1049285612Sdelphij	u_int	seconds		/* 0 cancels */
1050285612Sdelphij	)
1051285612Sdelphij{
1052285612Sdelphij	struct timeval	tv_to;
1053132451Sroberto
1054285612Sdelphij	if (NULL == ev_worker_timeout) {
1055285612Sdelphij		ev_worker_timeout = event_new(base, -1,
1056285612Sdelphij					      EV_TIMEOUT | EV_PERSIST,
1057285612Sdelphij					      &worker_timeout, NULL);
1058285612Sdelphij		DEBUG_INSIST(NULL != ev_worker_timeout);
1059285612Sdelphij	} else {
1060285612Sdelphij		event_del(ev_worker_timeout);
1061285612Sdelphij	}
1062285612Sdelphij	if (0 == seconds)
1063285612Sdelphij		return;
1064285612Sdelphij	tv_to.tv_sec = seconds;
1065285612Sdelphij	tv_to.tv_usec = 0;
1066285612Sdelphij	event_add(ev_worker_timeout, &tv_to);
1067285612Sdelphij}
1068132451Sroberto
1069132451Sroberto
1070285612Sdelphijvoid
1071285612Sdelphijworker_timeout(
1072285612Sdelphij	evutil_socket_t	fd,
1073285612Sdelphij	short		what,
1074285612Sdelphij	void *		ctx
1075285612Sdelphij	)
1076285612Sdelphij{
1077285612Sdelphij	UNUSED_ARG(fd);
1078285612Sdelphij	UNUSED_ARG(ctx);
1079132451Sroberto
1080285612Sdelphij	DEBUG_REQUIRE(EV_TIMEOUT & what);
1081285612Sdelphij	worker_idle_timer_fired();
1082132451Sroberto}
1083132451Sroberto
1084132451Sroberto
1085285612Sdelphijvoid
1086285612Sdelphijsntp_libevent_log_cb(
1087285612Sdelphij	int		severity,
1088285612Sdelphij	const char *	msg
1089285612Sdelphij	)
1090285612Sdelphij{
1091285612Sdelphij	int		level;
1092132451Sroberto
1093285612Sdelphij	switch (severity) {
1094132451Sroberto
1095285612Sdelphij	default:
1096285612Sdelphij	case _EVENT_LOG_DEBUG:
1097285612Sdelphij		level = LOG_DEBUG;
1098285612Sdelphij		break;
1099132451Sroberto
1100285612Sdelphij	case _EVENT_LOG_MSG:
1101285612Sdelphij		level = LOG_NOTICE;
1102285612Sdelphij		break;
1103132451Sroberto
1104285612Sdelphij	case _EVENT_LOG_WARN:
1105285612Sdelphij		level = LOG_WARNING;
1106285612Sdelphij		break;
1107132451Sroberto
1108285612Sdelphij	case _EVENT_LOG_ERR:
1109285612Sdelphij		level = LOG_ERR;
1110285612Sdelphij		break;
1111285612Sdelphij	}
1112132451Sroberto
1113285612Sdelphij	msyslog(level, "%s", msg);
1114285612Sdelphij}
1115132451Sroberto
1116132451Sroberto
1117285612Sdelphijint
1118285612Sdelphijgenerate_pkt (
1119285612Sdelphij	struct pkt *x_pkt,
1120285612Sdelphij	const struct timeval *tv_xmt,
1121285612Sdelphij	int key_id,
1122285612Sdelphij	struct key *pkt_key
1123285612Sdelphij	)
1124285612Sdelphij{
1125285612Sdelphij	l_fp	xmt_fp;
1126285612Sdelphij	int	pkt_len;
1127285612Sdelphij	int	mac_size;
1128132451Sroberto
1129285612Sdelphij	pkt_len = LEN_PKT_NOMAC;
1130285612Sdelphij	ZERO(*x_pkt);
1131285612Sdelphij	TVTOTS(tv_xmt, &xmt_fp);
1132285612Sdelphij	HTONL_FP(&xmt_fp, &x_pkt->xmt);
1133285612Sdelphij	x_pkt->stratum = STRATUM_TO_PKT(STRATUM_UNSPEC);
1134285612Sdelphij	x_pkt->ppoll = 8;
1135285612Sdelphij	/* FIXME! Modus broadcast + adr. check -> bdr. pkt */
1136285612Sdelphij	set_li_vn_mode(x_pkt, LEAP_NOTINSYNC, ntpver, 3);
1137330567Sgordon	if (debug > 0) {
1138330567Sgordon		printf("generate_pkt: key_id %d, key pointer %p\n", key_id, pkt_key);
1139330567Sgordon	}
1140285612Sdelphij	if (pkt_key != NULL) {
1141285612Sdelphij		x_pkt->exten[0] = htonl(key_id);
1142330567Sgordon		mac_size = make_mac(x_pkt, pkt_len, MAX_MDG_LEN,
1143285612Sdelphij				    pkt_key, (char *)&x_pkt->exten[1]);
1144285612Sdelphij		if (mac_size > 0)
1145330567Sgordon			pkt_len += mac_size + KEY_MAC_LEN;
1146330567Sgordon#ifdef DEBUG
1147330567Sgordon		if (debug > 0) {
1148330567Sgordon			printf("generate_pkt: mac_size is %d\n", mac_size);
1149330567Sgordon		}
1150330567Sgordon#endif
1151330567Sgordon
1152285612Sdelphij	}
1153285612Sdelphij	return pkt_len;
1154285612Sdelphij}
1155132451Sroberto
1156132451Sroberto
1157285612Sdelphijint
1158285612Sdelphijhandle_pkt(
1159285612Sdelphij	int		rpktl,
1160285612Sdelphij	struct pkt *	rpkt,
1161285612Sdelphij	sockaddr_u *	host,
1162285612Sdelphij	const char *	hostname
1163285612Sdelphij	)
1164285612Sdelphij{
1165285612Sdelphij	char		disptxt[32];
1166285612Sdelphij	const char *	addrtxt;
1167285612Sdelphij	struct timeval	tv_dst;
1168285612Sdelphij	int		cnt;
1169285612Sdelphij	int		sw_case;
1170285612Sdelphij	int		digits;
1171285612Sdelphij	int		stratum;
1172285612Sdelphij	char *		ref;
1173285612Sdelphij	char *		ts_str;
1174285612Sdelphij	const char *	leaptxt;
1175285612Sdelphij	double		offset;
1176285612Sdelphij	double		precision;
1177285612Sdelphij	double		synch_distance;
1178285612Sdelphij	char *		p_SNTP_PRETEND_TIME;
1179285612Sdelphij	time_t		pretend_time;
1180285612Sdelphij#if SIZEOF_TIME_T == 8
1181285612Sdelphij	long long	ll;
1182285612Sdelphij#else
1183285612Sdelphij	long		l;
1184285612Sdelphij#endif
1185132451Sroberto
1186285612Sdelphij	ts_str = NULL;
1187132451Sroberto
1188285612Sdelphij	if (rpktl > 0)
1189285612Sdelphij		sw_case = 1;
1190285612Sdelphij	else
1191285612Sdelphij		sw_case = rpktl;
1192132451Sroberto
1193285612Sdelphij	switch (sw_case) {
1194132451Sroberto
1195285612Sdelphij	case SERVER_UNUSEABLE:
1196285612Sdelphij		return -1;
1197285612Sdelphij		break;
1198132451Sroberto
1199285612Sdelphij	case PACKET_UNUSEABLE:
1200285612Sdelphij		break;
1201132451Sroberto
1202285612Sdelphij	case SERVER_AUTH_FAIL:
1203285612Sdelphij		break;
1204132451Sroberto
1205285612Sdelphij	case KOD_DEMOBILIZE:
1206285612Sdelphij		/* Received a DENY or RESTR KOD packet */
1207285612Sdelphij		addrtxt = stoa(host);
1208285612Sdelphij		ref = (char *)&rpkt->refid;
1209285612Sdelphij		add_entry(addrtxt, ref);
1210285612Sdelphij		msyslog(LOG_WARNING, "KOD code %c%c%c%c from %s %s",
1211285612Sdelphij			ref[0], ref[1], ref[2], ref[3], addrtxt, hostname);
1212285612Sdelphij		break;
1213132451Sroberto
1214285612Sdelphij	case KOD_RATE:
1215285612Sdelphij		/*
1216285612Sdelphij		** Hmm...
1217285612Sdelphij		** We should probably call add_entry() with an
1218285612Sdelphij		** expiration timestamp of several seconds in the future,
1219285612Sdelphij		** and back-off even more if we get more RATE responses.
1220285612Sdelphij		*/
1221285612Sdelphij		break;
1222132451Sroberto
1223285612Sdelphij	case 1:
1224285612Sdelphij		TRACE(3, ("handle_pkt: %d bytes from %s %s\n",
1225285612Sdelphij			  rpktl, stoa(host), hostname));
1226132451Sroberto
1227285612Sdelphij		gettimeofday_cached(base, &tv_dst);
1228132451Sroberto
1229285612Sdelphij		p_SNTP_PRETEND_TIME = getenv("SNTP_PRETEND_TIME");
1230285612Sdelphij		if (p_SNTP_PRETEND_TIME) {
1231285612Sdelphij			pretend_time = 0;
1232285612Sdelphij#if SIZEOF_TIME_T == 4
1233285612Sdelphij			if (1 == sscanf(p_SNTP_PRETEND_TIME, "%ld", &l))
1234285612Sdelphij				pretend_time = (time_t)l;
1235285612Sdelphij#elif SIZEOF_TIME_T == 8
1236285612Sdelphij			if (1 == sscanf(p_SNTP_PRETEND_TIME, "%lld", &ll))
1237285612Sdelphij				pretend_time = (time_t)ll;
1238285612Sdelphij#else
1239285612Sdelphij# include "GRONK: unexpected value for SIZEOF_TIME_T"
1240285612Sdelphij#endif
1241285612Sdelphij			if (0 != pretend_time)
1242285612Sdelphij				tv_dst.tv_sec = pretend_time;
1243285612Sdelphij		}
1244132451Sroberto
1245285612Sdelphij		offset_calculation(rpkt, rpktl, &tv_dst, &offset,
1246285612Sdelphij				   &precision, &synch_distance);
1247285612Sdelphij		time_derived = TRUE;
1248132451Sroberto
1249285612Sdelphij		for (digits = 0; (precision *= 10.) < 1.; ++digits)
1250285612Sdelphij			/* empty */ ;
1251285612Sdelphij		if (digits > 6)
1252285612Sdelphij			digits = 6;
1253132451Sroberto
1254285612Sdelphij		ts_str = tv_to_str(&tv_dst);
1255285612Sdelphij		stratum = rpkt->stratum;
1256285612Sdelphij		if (0 == stratum)
1257285612Sdelphij				stratum = 16;
1258132451Sroberto
1259285612Sdelphij		if (synch_distance > 0.) {
1260285612Sdelphij			cnt = snprintf(disptxt, sizeof(disptxt),
1261285612Sdelphij				       " +/- %f", synch_distance);
1262285612Sdelphij			if ((size_t)cnt >= sizeof(disptxt))
1263285612Sdelphij				snprintf(disptxt, sizeof(disptxt),
1264285612Sdelphij					 "ERROR %d >= %d", cnt,
1265285612Sdelphij					 (int)sizeof(disptxt));
1266285612Sdelphij		} else {
1267285612Sdelphij			disptxt[0] = '\0';
1268285612Sdelphij		}
1269132451Sroberto
1270285612Sdelphij		switch (PKT_LEAP(rpkt->li_vn_mode)) {
1271285612Sdelphij		    case LEAP_NOWARNING:
1272285612Sdelphij		    	leaptxt = "no-leap";
1273285612Sdelphij			break;
1274285612Sdelphij		    case LEAP_ADDSECOND:
1275285612Sdelphij		    	leaptxt = "add-leap";
1276285612Sdelphij			break;
1277285612Sdelphij		    case LEAP_DELSECOND:
1278285612Sdelphij		    	leaptxt = "del-leap";
1279285612Sdelphij			break;
1280285612Sdelphij		    case LEAP_NOTINSYNC:
1281285612Sdelphij		    	leaptxt = "unsync";
1282285612Sdelphij			break;
1283285612Sdelphij		    default:
1284285612Sdelphij		    	leaptxt = "LEAP-ERROR";
1285285612Sdelphij			break;
1286285612Sdelphij		}
1287132451Sroberto
1288285612Sdelphij		msyslog(LOG_INFO, "%s %+.*f%s %s s%d %s%s", ts_str,
1289285612Sdelphij			digits, offset, disptxt,
1290285612Sdelphij			hostnameaddr(hostname, host), stratum,
1291285612Sdelphij			leaptxt,
1292285612Sdelphij			(time_adjusted)
1293285612Sdelphij			    ? " [excess]"
1294285612Sdelphij			    : "");
1295285612Sdelphij		free(ts_str);
1296132451Sroberto
1297285612Sdelphij		if (p_SNTP_PRETEND_TIME)
1298285612Sdelphij			return 0;
1299132451Sroberto
1300285612Sdelphij		if (!time_adjusted &&
1301285612Sdelphij		    (ENABLED_OPT(STEP) || ENABLED_OPT(SLEW)))
1302285612Sdelphij			return set_time(offset);
1303132451Sroberto
1304285612Sdelphij		return EX_OK;
1305285612Sdelphij	}
1306132451Sroberto
1307285612Sdelphij	return 1;
1308285612Sdelphij}
1309132451Sroberto
1310132451Sroberto
1311285612Sdelphijvoid
1312285612Sdelphijoffset_calculation(
1313285612Sdelphij	struct pkt *rpkt,
1314285612Sdelphij	int rpktl,
1315285612Sdelphij	struct timeval *tv_dst,
1316285612Sdelphij	double *offset,
1317285612Sdelphij	double *precision,
1318285612Sdelphij	double *synch_distance
1319285612Sdelphij	)
1320285612Sdelphij{
1321285612Sdelphij	l_fp p_rec, p_xmt, p_ref, p_org, tmp, dst;
1322285612Sdelphij	u_fp p_rdly, p_rdsp;
1323285612Sdelphij	double t21, t34, delta;
1324132451Sroberto
1325285612Sdelphij	/* Convert timestamps from network to host byte order */
1326285612Sdelphij	p_rdly = NTOHS_FP(rpkt->rootdelay);
1327285612Sdelphij	p_rdsp = NTOHS_FP(rpkt->rootdisp);
1328285612Sdelphij	NTOHL_FP(&rpkt->reftime, &p_ref);
1329285612Sdelphij	NTOHL_FP(&rpkt->org, &p_org);
1330285612Sdelphij	NTOHL_FP(&rpkt->rec, &p_rec);
1331285612Sdelphij	NTOHL_FP(&rpkt->xmt, &p_xmt);
1332132451Sroberto
1333285612Sdelphij	*precision = LOGTOD(rpkt->precision);
1334132451Sroberto
1335285612Sdelphij	TRACE(3, ("offset_calculation: LOGTOD(rpkt->precision): %f\n", *precision));
1336132451Sroberto
1337285612Sdelphij	/* Compute offset etc. */
1338285612Sdelphij	tmp = p_rec;
1339285612Sdelphij	L_SUB(&tmp, &p_org);
1340285612Sdelphij	LFPTOD(&tmp, t21);
1341285612Sdelphij	TVTOTS(tv_dst, &dst);
1342285612Sdelphij	dst.l_ui += JAN_1970;
1343285612Sdelphij	tmp = p_xmt;
1344285612Sdelphij	L_SUB(&tmp, &dst);
1345285612Sdelphij	LFPTOD(&tmp, t34);
1346285612Sdelphij	*offset = (t21 + t34) / 2.;
1347285612Sdelphij	delta = t21 - t34;
1348132451Sroberto
1349285612Sdelphij	// synch_distance is:
1350285612Sdelphij	// (peer->delay + peer->rootdelay) / 2 + peer->disp
1351285612Sdelphij	// + peer->rootdisp + clock_phi * (current_time - peer->update)
1352285612Sdelphij	// + peer->jitter;
1353285612Sdelphij	//
1354285612Sdelphij	// and peer->delay = fabs(peer->offset - p_offset) * 2;
1355285612Sdelphij	// and peer->offset needs history, so we're left with
1356285612Sdelphij	// p_offset = (t21 + t34) / 2.;
1357285612Sdelphij	// peer->disp = 0; (we have no history to augment this)
1358285612Sdelphij	// clock_phi = 15e-6;
1359285612Sdelphij	// peer->jitter = LOGTOD(sys_precision); (we have no history to augment this)
1360285612Sdelphij	// and ntp_proto.c:set_sys_tick_precision() should get us sys_precision.
1361285612Sdelphij	//
1362285612Sdelphij	// so our answer seems to be:
1363285612Sdelphij	//
1364285612Sdelphij	// (fabs(t21 + t34) + peer->rootdelay) / 3.
1365285612Sdelphij	// + 0 (peer->disp)
1366285612Sdelphij	// + peer->rootdisp
1367285612Sdelphij	// + 15e-6 (clock_phi)
1368285612Sdelphij	// + LOGTOD(sys_precision)
1369132451Sroberto
1370285612Sdelphij	INSIST( FPTOD(p_rdly) >= 0. );
1371285612Sdelphij#if 1
1372285612Sdelphij	*synch_distance = (fabs(t21 + t34) + FPTOD(p_rdly)) / 3.
1373285612Sdelphij		+ 0.
1374285612Sdelphij		+ FPTOD(p_rdsp)
1375285612Sdelphij		+ 15e-6
1376285612Sdelphij		+ 0.	/* LOGTOD(sys_precision) when we can get it */
1377285612Sdelphij		;
1378285612Sdelphij	INSIST( *synch_distance >= 0. );
1379285612Sdelphij#else
1380285612Sdelphij	*synch_distance = (FPTOD(p_rdly) + FPTOD(p_rdsp))/2.0;
1381285612Sdelphij#endif
1382132451Sroberto
1383285612Sdelphij#ifdef DEBUG
1384285612Sdelphij	if (debug > 3) {
1385285612Sdelphij		printf("sntp rootdelay: %f\n", FPTOD(p_rdly));
1386285612Sdelphij		printf("sntp rootdisp: %f\n", FPTOD(p_rdsp));
1387285612Sdelphij		printf("sntp syncdist: %f\n", *synch_distance);
1388132451Sroberto
1389285612Sdelphij		pkt_output(rpkt, rpktl, stdout);
1390132451Sroberto
1391285612Sdelphij		printf("sntp offset_calculation: rpkt->reftime:\n");
1392285612Sdelphij		l_fp_output(&p_ref, stdout);
1393285612Sdelphij		printf("sntp offset_calculation: rpkt->org:\n");
1394285612Sdelphij		l_fp_output(&p_org, stdout);
1395285612Sdelphij		printf("sntp offset_calculation: rpkt->rec:\n");
1396285612Sdelphij		l_fp_output(&p_rec, stdout);
1397285612Sdelphij		printf("sntp offset_calculation: rpkt->xmt:\n");
1398285612Sdelphij		l_fp_output(&p_xmt, stdout);
1399285612Sdelphij	}
1400285612Sdelphij#endif
1401132451Sroberto
1402285612Sdelphij	TRACE(3, ("sntp offset_calculation:\trec - org t21: %.6f\n"
1403285612Sdelphij		  "\txmt - dst t34: %.6f\tdelta: %.6f\toffset: %.6f\n",
1404285612Sdelphij		  t21, t34, delta, *offset));
1405132451Sroberto
1406285612Sdelphij	return;
1407285612Sdelphij}
1408132451Sroberto
1409132451Sroberto
1410132451Sroberto
1411285612Sdelphij/* Compute the 8 bits for li_vn_mode */
1412285612Sdelphijvoid
1413285612Sdelphijset_li_vn_mode (
1414285612Sdelphij	struct pkt *spkt,
1415285612Sdelphij	char leap,
1416285612Sdelphij	char version,
1417285612Sdelphij	char mode
1418285612Sdelphij	)
1419285612Sdelphij{
1420285612Sdelphij	if (leap > 3) {
1421285612Sdelphij		msyslog(LOG_DEBUG, "set_li_vn_mode: leap > 3, using max. 3");
1422285612Sdelphij		leap = 3;
1423285612Sdelphij	}
1424132451Sroberto
1425285612Sdelphij	if ((unsigned char)version > 7) {
1426285612Sdelphij		msyslog(LOG_DEBUG, "set_li_vn_mode: version < 0 or > 7, using 4");
1427285612Sdelphij		version = 4;
1428285612Sdelphij	}
1429132451Sroberto
1430285612Sdelphij	if (mode > 7) {
1431285612Sdelphij		msyslog(LOG_DEBUG, "set_li_vn_mode: mode > 7, using client mode 3");
1432285612Sdelphij		mode = 3;
1433285612Sdelphij	}
1434132451Sroberto
1435285612Sdelphij	spkt->li_vn_mode  = leap << 6;
1436285612Sdelphij	spkt->li_vn_mode |= version << 3;
1437285612Sdelphij	spkt->li_vn_mode |= mode;
1438132451Sroberto}
1439132451Sroberto
1440132451Sroberto
1441285612Sdelphij/*
1442285612Sdelphij** set_time applies 'offset' to the local clock.
1443285612Sdelphij*/
1444285612Sdelphijint
1445285612Sdelphijset_time(
1446285612Sdelphij	double offset
1447285612Sdelphij	)
1448285612Sdelphij{
1449285612Sdelphij	int rc;
1450132451Sroberto
1451285612Sdelphij	if (time_adjusted)
1452285612Sdelphij		return EX_OK;
1453132451Sroberto
1454285612Sdelphij	/*
1455285612Sdelphij	** If we can step but we cannot slew, then step.
1456285612Sdelphij	** If we can step or slew and and |offset| > steplimit, then step.
1457285612Sdelphij	*/
1458285612Sdelphij	if (ENABLED_OPT(STEP) &&
1459285612Sdelphij	    (   !ENABLED_OPT(SLEW)
1460285612Sdelphij	     || (ENABLED_OPT(SLEW) && (fabs(offset) > steplimit))
1461285612Sdelphij	    )) {
1462285612Sdelphij		rc = step_systime(offset);
1463132451Sroberto
1464285612Sdelphij		/* If there was a problem, can we rely on errno? */
1465285612Sdelphij		if (1 == rc)
1466285612Sdelphij			time_adjusted = TRUE;
1467285612Sdelphij		return (time_adjusted)
1468285612Sdelphij			   ? EX_OK
1469285612Sdelphij			   : 1;
1470285612Sdelphij		/*
1471285612Sdelphij		** In case of error, what should we use?
1472285612Sdelphij		** EX_UNAVAILABLE?
1473285612Sdelphij		** EX_OSERR?
1474285612Sdelphij		** EX_NOPERM?
1475285612Sdelphij		*/
1476285612Sdelphij	}
1477132451Sroberto
1478285612Sdelphij	if (ENABLED_OPT(SLEW)) {
1479285612Sdelphij		rc = adj_systime(offset);
1480132451Sroberto
1481285612Sdelphij		/* If there was a problem, can we rely on errno? */
1482285612Sdelphij		if (1 == rc)
1483285612Sdelphij			time_adjusted = TRUE;
1484285612Sdelphij		return (time_adjusted)
1485285612Sdelphij			   ? EX_OK
1486285612Sdelphij			   : 1;
1487285612Sdelphij		/*
1488285612Sdelphij		** In case of error, what should we use?
1489285612Sdelphij		** EX_UNAVAILABLE?
1490285612Sdelphij		** EX_OSERR?
1491285612Sdelphij		** EX_NOPERM?
1492285612Sdelphij		*/
1493285612Sdelphij	}
1494132451Sroberto
1495285612Sdelphij	return EX_SOFTWARE;
1496285612Sdelphij}
1497132451Sroberto
1498132451Sroberto
1499285612Sdelphijint
1500285612Sdelphijlibevent_version_ok(void)
1501285612Sdelphij{
1502285612Sdelphij	ev_uint32_t v_compile_maj;
1503285612Sdelphij	ev_uint32_t v_run_maj;
1504132451Sroberto
1505285612Sdelphij	v_compile_maj = LIBEVENT_VERSION_NUMBER & 0xffff0000;
1506285612Sdelphij	v_run_maj = event_get_version_number() & 0xffff0000;
1507285612Sdelphij	if (v_compile_maj != v_run_maj) {
1508285612Sdelphij		fprintf(stderr,
1509285612Sdelphij			"Incompatible libevent versions: have %s, built with %s\n",
1510285612Sdelphij			event_get_version(),
1511285612Sdelphij			LIBEVENT_VERSION);
1512285612Sdelphij		return 0;
1513285612Sdelphij	}
1514285612Sdelphij	return 1;
1515285612Sdelphij}
1516132451Sroberto
1517285612Sdelphij/*
1518285612Sdelphij * gettimeofday_cached()
1519285612Sdelphij *
1520285612Sdelphij * Clones the event_base_gettimeofday_cached() interface but ensures the
1521285612Sdelphij * times are always on the gettimeofday() 1970 scale.  Older libevent 2
1522285612Sdelphij * sometimes used gettimeofday(), sometimes the since-system-start
1523285612Sdelphij * clock_gettime(CLOCK_MONOTONIC), depending on the platform.
1524285612Sdelphij *
1525285612Sdelphij * It is not cleanly possible to tell which timescale older libevent is
1526285612Sdelphij * using.
1527285612Sdelphij *
1528285612Sdelphij * The strategy involves 1 hour thresholds chosen to be far longer than
1529285612Sdelphij * the duration of a round of libevent callbacks, which share a cached
1530285612Sdelphij * start-of-round time.  First compare the last cached time with the
1531285612Sdelphij * current gettimeofday() time.  If they are within one hour, libevent
1532285612Sdelphij * is using the proper timescale so leave the offset 0.  Otherwise,
1533285612Sdelphij * compare libevent's cached time and the current time on the monotonic
1534285612Sdelphij * scale.  If they are within an hour, libevent is using the monotonic
1535285612Sdelphij * scale so calculate the offset to add to such times to bring them to
1536285612Sdelphij * gettimeofday()'s scale.
1537285612Sdelphij */
1538285612Sdelphijint
1539285612Sdelphijgettimeofday_cached(
1540285612Sdelphij	struct event_base *	b,
1541285612Sdelphij	struct timeval *	caller_tv
1542285612Sdelphij	)
1543285612Sdelphij{
1544285612Sdelphij#if defined(_EVENT_HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
1545285612Sdelphij	static struct event_base *	cached_b;
1546285612Sdelphij	static struct timeval		cached;
1547285612Sdelphij	static struct timeval		adj_cached;
1548285612Sdelphij	static struct timeval		offset;
1549285612Sdelphij	static int			offset_ready;
1550285612Sdelphij	struct timeval			latest;
1551285612Sdelphij	struct timeval			systemt;
1552285612Sdelphij	struct timespec			ts;
1553285612Sdelphij	struct timeval			mono;
1554285612Sdelphij	struct timeval			diff;
1555285612Sdelphij	int				cgt_rc;
1556285612Sdelphij	int				gtod_rc;
1557132451Sroberto
1558285612Sdelphij	event_base_gettimeofday_cached(b, &latest);
1559285612Sdelphij	if (b == cached_b &&
1560285612Sdelphij	    !memcmp(&latest, &cached, sizeof(latest))) {
1561285612Sdelphij		*caller_tv = adj_cached;
1562285612Sdelphij		return 0;
1563285612Sdelphij	}
1564285612Sdelphij	cached = latest;
1565285612Sdelphij	cached_b = b;
1566285612Sdelphij	if (!offset_ready) {
1567285612Sdelphij		cgt_rc = clock_gettime(CLOCK_MONOTONIC, &ts);
1568285612Sdelphij		gtod_rc = gettimeofday(&systemt, NULL);
1569285612Sdelphij		if (0 != gtod_rc) {
1570285612Sdelphij			msyslog(LOG_ERR,
1571285612Sdelphij				"%s: gettimeofday() error %m",
1572285612Sdelphij				progname);
1573285612Sdelphij			exit(1);
1574285612Sdelphij		}
1575285612Sdelphij		diff = sub_tval(systemt, latest);
1576285612Sdelphij		if (debug > 1)
1577285612Sdelphij			printf("system minus cached %+ld.%06ld\n",
1578285612Sdelphij			       (long)diff.tv_sec, (long)diff.tv_usec);
1579285612Sdelphij		if (0 != cgt_rc || labs((long)diff.tv_sec) < 3600) {
1580285612Sdelphij			/*
1581285612Sdelphij			 * Either use_monotonic == 0, or this libevent
1582285612Sdelphij			 * has been repaired.  Leave offset at zero.
1583285612Sdelphij			 */
1584285612Sdelphij		} else {
1585285612Sdelphij			mono.tv_sec = ts.tv_sec;
1586285612Sdelphij			mono.tv_usec = ts.tv_nsec / 1000;
1587285612Sdelphij			diff = sub_tval(latest, mono);
1588285612Sdelphij			if (debug > 1)
1589285612Sdelphij				printf("cached minus monotonic %+ld.%06ld\n",
1590285612Sdelphij				       (long)diff.tv_sec, (long)diff.tv_usec);
1591285612Sdelphij			if (labs((long)diff.tv_sec) < 3600) {
1592285612Sdelphij				/* older libevent2 using monotonic */
1593285612Sdelphij				offset = sub_tval(systemt, mono);
1594285612Sdelphij				TRACE(1, ("%s: Offsetting libevent CLOCK_MONOTONIC times  by %+ld.%06ld\n",
1595285612Sdelphij					 "gettimeofday_cached",
1596285612Sdelphij					 (long)offset.tv_sec,
1597285612Sdelphij					 (long)offset.tv_usec));
1598285612Sdelphij			}
1599285612Sdelphij		}
1600285612Sdelphij		offset_ready = TRUE;
1601285612Sdelphij	}
1602285612Sdelphij	adj_cached = add_tval(cached, offset);
1603285612Sdelphij	*caller_tv = adj_cached;
1604132451Sroberto
1605285612Sdelphij	return 0;
1606285612Sdelphij#else
1607285612Sdelphij	return event_base_gettimeofday_cached(b, caller_tv);
1608285612Sdelphij#endif
1609132451Sroberto}
1610132451Sroberto
1611