1#include <sys/socket.h>
2#include <netdb.h>
3
4#include <syslog.h>
5
6#include <TargetConditionals.h>
7
8static
9int
10my_getaddrinfo(const char* nodename,
11	       const char *servname,
12	       const struct addrinfo *hints,
13	       struct addrinfo **res)
14{
15	int rc = getaddrinfo(nodename, servname, hints, res);
16	if (rc == EAI_NONAME) {
17		syslog(LOG_INFO, "getaddrinfo('%s','%s') returned EAI_NONAME, mapping to EAI_AGAIN", nodename, servname);
18		return EAI_AGAIN;
19	}
20	return rc;
21}
22#define getaddrinfo my_getaddrinfo
23
24/*
25 * ripped off from ../ntpres/ntpres.c by Greg Troxel 4/2/92
26 * routine callable from ntpd, rather than separate program
27 * also, key info passed in via a global, so no key file needed.
28 */
29
30/*
31 * ntpres - process configuration entries which require use of the resolver
32 *
33 * This is meant to be run by ntpd on the fly.  It is not guaranteed
34 * to work properly if run by hand.  This is actually a quick hack to
35 * stave off violence from people who hate using numbers in the
36 * configuration file (at least I hope the rest of the daemon is
37 * better than this).  Also might provide some ideas about how one
38 * might go about autoconfiguring an NTP distribution network.
39 *
40 */
41
42#ifdef HAVE_CONFIG_H
43# include <config.h>
44#endif
45
46#include "ntp_machine.h"
47#include "ntpd.h"
48#include "ntp_io.h"
49#include "ntp_request.h"
50#include "ntp_stdlib.h"
51#include "ntp_syslog.h"
52#include "ntp_config.h"
53
54#ifndef NO_INTRES		/* from ntp_config.h */
55
56#include <stdio.h>
57#include <ctype.h>
58#include <signal.h>
59
60/**/
61#ifdef HAVE_NETINET_IN_H
62#include <netinet/in.h>
63#endif
64#include <arpa/inet.h>
65/**/
66#ifdef HAVE_SYS_PARAM_H
67# include <sys/param.h>		/* MAXHOSTNAMELEN (often) */
68#endif
69
70#if defined(HAVE_RES_INIT) || defined(HAVE___RES_INIT)
71#include <resolv.h>
72#endif
73
74#include <isc/net.h>
75#include <isc/result.h>
76
77#define	STREQ(a, b)	(*(a) == *(b) && strcmp((a), (b)) == 0)
78
79/*
80 * Each item we are to resolve and configure gets one of these
81 * structures defined for it.
82 */
83struct conf_entry {
84	struct conf_entry *ce_next;
85	char *ce_name;			/* name to resolve */
86	struct conf_peer ce_config;	/* config info for peer */
87	int no_needed;			/* number of addresses needed (pool) */
88	/*  no_needed isn't used yet: It's needed to fix bug-975 */
89	int type;			/* -4 and -6 flags */
90	sockaddr_u peer_store;		/* address info for both fams */
91};
92#define	ce_peeraddr	ce_config.peeraddr
93#define	ce_peeraddr6	ce_config.peeraddr6
94#define	ce_hmode	ce_config.hmode
95#define	ce_version	ce_config.version
96#define ce_minpoll	ce_config.minpoll
97#define ce_maxpoll	ce_config.maxpoll
98#define	ce_flags	ce_config.flags
99#define ce_ttl		ce_config.ttl
100#define	ce_keyid	ce_config.keyid
101#define ce_keystr	ce_config.keystr
102
103/*
104 * confentries is a pointer to the list of configuration entries
105 * we have left to do.
106 */
107static struct conf_entry *confentries = NULL;
108
109/*
110 * We take an interrupt every thirty seconds, at which time we decrement
111 * config_timer and resolve_timer.  The former is set to 2, so we retry
112 * unsucessful reconfigurations every minute.  The latter is set to
113 * an exponentially increasing value which starts at 2 and increases to
114 * 32.  When this expires we retry failed name resolutions.
115 *
116 * We sleep SLEEPTIME seconds before doing anything, to give the server
117 * time to arrange itself.
118 */
119#define	MINRESOLVE	2
120#define	MAXRESOLVE	32
121#define	CONFIG_TIME	2
122#define	ALARM_TIME	30
123#define	SLEEPTIME	2
124
125#if TARGET_OS_EMBEDDED
126// <rdar://problem/8278196>
127#undef ALARM_TIME
128#define ALARM_TIME	10
129#endif
130
131
132static	volatile int config_timer = 0;
133static	volatile int resolve_timer = 0;
134
135static	int resolve_value;	/* next value of resolve timer */
136
137/*
138 * Big hack attack
139 */
140#define	SKEWTIME	0x08000000	/* 0.03125 seconds as a l_fp fraction */
141
142/*
143 * Select time out.  Set to 2 seconds.  The server is on the local machine,
144 * after all.
145 */
146#define	TIMEOUT_SEC	2
147#define	TIMEOUT_USEC	0
148
149
150/*
151 * Input processing.  The data on each line in the configuration file
152 * is supposed to consist of entries in the following order
153 */
154#define	TOK_HOSTNAME	0
155#define	TOK_NEEDED	1
156#define	TOK_TYPE	2
157#define	TOK_HMODE	3
158#define	TOK_VERSION	4
159#define	TOK_MINPOLL	5
160#define	TOK_MAXPOLL	6
161#define	TOK_FLAGS	7
162#define	TOK_TTL		8
163#define	TOK_KEYID	9
164#define	TOK_KEYSTR	10
165#define	NUMTOK		11
166
167#define	MAXLINESIZE	512
168
169
170/*
171 * File descriptor for ntp request code.
172 */
173static	SOCKET sockfd = INVALID_SOCKET;	/* NT uses SOCKET */
174
175/* stuff to be filled in by caller */
176
177keyid_t req_keyid;	/* request keyid */
178int	req_keytype;	/* OpenSSL NID such as NID_md5 */
179size_t	req_hashlen;	/* digest size for req_keytype */
180char *req_file;		/* name of the file with configuration info */
181
182/* end stuff to be filled in */
183
184
185static	void	checkparent	(void);
186static	struct conf_entry *
187		removeentry	(struct conf_entry *);
188static	void	addentry	(char *, int, int, int, int, int, int, u_int,
189				   int, keyid_t, char *);
190static	int	findhostaddr	(struct conf_entry *);
191static	void	openntp		(void);
192static	int	request		(struct conf_peer *);
193static	char *	nexttoken	(char **);
194static	void	readconf	(FILE *, char *);
195static	void	doconfigure	(int);
196
197struct ntp_res_t_pkt {		/* Tagged packet: */
198	void *tag;		/* For the caller */
199	u_int32 paddr;		/* IP to look up, or 0 */
200	char name[MAXHOSTNAMELEN]; /* Name to look up (if 1st byte is not 0) */
201};
202
203struct ntp_res_c_pkt {		/* Control packet: */
204	char name[MAXHOSTNAMELEN];
205	u_int32 paddr;
206	int mode;
207	int version;
208	int minpoll;
209	int maxpoll;
210	u_int flags;
211	int ttl;
212	keyid_t keyid;
213	u_char keystr[MAXFILENAME];
214};
215
216
217static void	resolver_exit (int);
218
219/*
220 * Call here instead of just exiting
221 */
222
223static void resolver_exit (int code)
224{
225#ifdef SYS_WINNT
226	CloseHandle(ResolverEventHandle);
227	ResolverEventHandle = NULL;
228	_endthreadex(code);	/* Just to kill the thread not the process */
229#else
230	exit(code);		/* kill the forked process */
231#endif
232}
233
234/*
235 * ntp_res_recv: Process an answer from the resolver
236 */
237
238void
239ntp_res_recv(void)
240{
241	/*
242	  We have data ready on our descriptor.
243	  It may be an EOF, meaning the resolver process went away.
244	  Otherwise, it will be an "answer".
245	*/
246}
247
248
249/*
250 * ntp_intres needs;
251 *
252 *	req_key(???), req_keyid, req_file valid
253 *	syslog still open
254 */
255
256void
257ntp_intres(void)
258{
259	FILE *in;
260#ifdef SYS_WINNT
261	DWORD rc;
262#else
263	int	rc;
264	struct	timeval tv;
265	fd_set	fdset;
266	int	time_left;
267#endif
268
269#ifdef DEBUG
270	if (debug > 1) {
271		msyslog(LOG_INFO, "NTP_INTRES running");
272	}
273#endif
274
275	/* check out auth stuff */
276	if (sys_authenticate) {
277		if (!authistrusted(req_keyid)) {
278			msyslog(LOG_ERR, "invalid request keyid %08x",
279			    req_keyid );
280			resolver_exit(1);
281		}
282	}
283
284	/*
285	 * Read the configuration info
286	 * {this is bogus, since we are forked, but it is easier
287	 * to keep this code - gdt}
288	 */
289	if ((in = fopen(req_file, "r")) == NULL) {
290		msyslog(LOG_ERR, "can't open configuration file %s: %m",
291			req_file);
292		resolver_exit(1);
293	}
294	readconf(in, req_file);
295	(void) fclose(in);
296
297#ifdef DEBUG
298	if (!debug)
299#endif
300		if (unlink(req_file))
301			msyslog(LOG_WARNING,
302				"unable to remove intres request file %s, %m",
303				req_file);
304
305	/*
306	 * Set up the timers to do first shot immediately.
307	 */
308	resolve_timer = 0;
309	resolve_value = MINRESOLVE;
310	config_timer = CONFIG_TIME;
311
312	for (;;) {
313		checkparent();
314
315#if TARGET_OS_EMBEDDED
316		// Perform fixed-interval retries to bring up the configuration
317		// on iOS. Also retry DNS lookups on each iteration.
318		// <rdar://problem/8278196>
319		doconfigure(1);
320
321#else // !TARGET_OS_EMBEDDED
322		if (resolve_timer == 0) {
323			/*
324			 * Sleep a little to make sure the network is completely up
325			 */
326			sleep(SLEEPTIME);
327			doconfigure(1);
328
329			/* prepare retry, in case there's more work to do */
330			resolve_timer = resolve_value;
331#ifdef DEBUG
332			if (debug > 2)
333				msyslog(LOG_INFO, "resolve_timer: 0->%d", resolve_timer);
334#endif
335			if (resolve_value < MAXRESOLVE)
336				resolve_value <<= 1;
337
338			config_timer = CONFIG_TIME;
339		} else if (config_timer == 0) {  /* MB: in which case would this be required ? */
340			doconfigure(0);
341			/* MB: should we check now if we could exit, similar to the code above? */
342			config_timer = CONFIG_TIME;
343#ifdef DEBUG
344			if (debug > 2)
345				msyslog(LOG_INFO, "config_timer: 0->%d", config_timer);
346#endif
347		}
348#endif // TARGET_OS_EMBEDDED
349
350		if (confentries == NULL)
351			resolver_exit(0);   /* done */
352
353#ifdef SYS_WINNT
354		rc = WaitForSingleObject(ResolverEventHandle, 1000 * ALARM_TIME);  /* in milliseconds */
355
356		if ( rc == WAIT_OBJECT_0 ) { /* signaled by the main thread */
357			resolve_timer = 0;         /* retry resolving immediately */
358			continue;
359		}
360
361		if ( rc != WAIT_TIMEOUT ) /* not timeout: error */
362			resolver_exit(1);
363
364#else  /* not SYS_WINNT */
365		/* Bug 1386: fork() in NetBSD leaves timers running. */
366		/* So we need to retry select on EINTR */
367		time_left = ALARM_TIME;
368		while (time_left > 0) {
369		    tv.tv_sec = time_left;
370		    tv.tv_usec = 0;
371		    FD_ZERO(&fdset);
372		    FD_SET(resolver_pipe_fd[0], &fdset);
373		    rc = select(resolver_pipe_fd[0] + 1, &fdset, (fd_set *)0, (fd_set *)0, &tv);
374
375		    if (rc == 0)		/* normal timeout */
376			break;
377
378		    if (rc > 0) {  /* parent process has written to the pipe */
379			read(resolver_pipe_fd[0], (char *)&rc, sizeof(rc));  /* make pipe empty */
380			resolve_timer = 0;   /* retry resolving immediately */
381			break;
382		    }
383
384		    if ( rc < 0 ) {		/* select() returned error */
385			if (errno == EINTR) {	/* Timer went off */
386			    time_left -= (1<<EVENT_TIMEOUT);
387			    continue;		/* try again */
388			}
389			msyslog(LOG_ERR, "ntp_intres: Error from select: %s",
390			    strerror(errno));
391			resolver_exit(1);
392		    }
393		}
394#endif
395
396		/* normal timeout, keep on waiting */
397		if (config_timer > 0)
398			config_timer--;
399		if (resolve_timer > 0)
400			resolve_timer--;
401	}
402}
403
404
405#ifdef SYS_WINNT
406/*
407 * ntp_intres_thread wraps the slightly different interface of Windows
408 * thread functions and ntp_intres
409 */
410unsigned WINAPI
411ntp_intres_thread(void *UnusedThreadArg)
412{
413	UNUSED_ARG(UnusedThreadArg);
414
415	ntp_intres();
416	return 0;
417}
418#endif /* SYS_WINNT */
419
420
421/*
422 * checkparent - see if our parent process is still running
423 *
424 * No need to worry in the Windows NT environment whether the
425 * main thread is still running, because if it goes
426 * down it takes the whole process down with it (in
427 * which case we won't be running this thread either)
428 * Turn function into NOP;
429 */
430
431static void
432checkparent(void)
433{
434#if !defined (SYS_WINNT) && !defined (SYS_VXWORKS)
435
436	/*
437	 * If our parent (the server) has died we will have been
438	 * inherited by init.  If so, exit.
439	 */
440	if (getppid() == 1) {
441		msyslog(LOG_INFO, "parent died before we finished, exiting");
442		resolver_exit(0);
443	}
444#endif /* SYS_WINNT && SYS_VXWORKS*/
445}
446
447
448
449/*
450 * removeentry - we are done with an entry, remove it from the list
451 */
452static struct conf_entry *
453removeentry(
454	struct conf_entry *entry
455	)
456{
457	register struct conf_entry *ce;
458	struct conf_entry *next_ce;
459
460	ce = confentries;
461	if (ce == entry)
462		confentries = ce->ce_next;
463	else
464		while (ce != NULL) {
465			if (ce->ce_next == entry) {
466				ce->ce_next = entry->ce_next;
467				break;
468			}
469			ce = ce->ce_next;
470		}
471
472	next_ce = entry->ce_next;
473	if (entry->ce_name != NULL)
474		free(entry->ce_name);
475	free(entry);
476
477	return next_ce;
478}
479
480
481/*
482 * addentry - add an entry to the configuration list
483 */
484static void
485addentry(
486	char *name,
487	int no_needed,
488	int type,
489	int mode,
490	int version,
491	int minpoll,
492	int maxpoll,
493	u_int flags,
494	int ttl,
495	keyid_t keyid,
496	char *keystr
497	)
498{
499	register struct conf_entry *ce;
500
501#ifdef DEBUG
502	if (debug > 1)
503		msyslog(LOG_INFO,
504		    "intres: <%s> %d %d %d %d %d %d %x %d %x %s",
505		    name, no_needed, type, mode, version,
506		    minpoll, maxpoll, flags, ttl, keyid, keystr);
507#endif
508	ce = emalloc(sizeof(*ce));
509	ce->ce_name = estrdup(name);
510	ce->ce_peeraddr = 0;
511#ifdef ISC_PLATFORM_HAVEIPV6
512	ce->ce_peeraddr6 = in6addr_any;
513#endif
514	ZERO_SOCK(&ce->peer_store);
515	ce->ce_hmode = (u_char)mode;
516	ce->ce_version = (u_char)version;
517	ce->ce_minpoll = (u_char)minpoll;
518	ce->ce_maxpoll = (u_char)maxpoll;
519	ce->no_needed = no_needed;	/* Not used after here. */
520					/* Start of fixing bug-975 */
521	ce->type = type;
522	ce->ce_flags = (u_char)flags;
523	ce->ce_ttl = (u_char)ttl;
524	ce->ce_keyid = keyid;
525	if (keyid) {
526		strlcpy((char *)ce->ce_keystr, keystr, MAXFILENAME);
527	} else {
528		strlcpy((char *)ce->ce_keystr, name, MAXFILENAME);
529	}
530	strncpy(ce->ce_keystr, keystr, sizeof(ce->ce_keystr) - 1);
531	ce->ce_keystr[sizeof(ce->ce_keystr) - 1] = 0;
532	ce->ce_next = NULL;
533
534	if (confentries == NULL) {
535		confentries = ce;
536	} else {
537		register struct conf_entry *cep;
538
539		for (cep = confentries; cep->ce_next != NULL;
540		     cep = cep->ce_next)
541		    /* nothing */;
542		cep->ce_next = ce;
543	}
544}
545
546
547/*
548 * findhostaddr - resolve a host name into an address (Or vice-versa)
549 *
550 * Given one of {ce_peeraddr,ce_name}, find the other one.
551 * It returns 1 for "success" and 0 for an uncorrectable failure.
552 * Note that "success" includes try again errors.  You can tell that you
553 *  got a "try again" since {ce_peeraddr,ce_name} will still be zero.
554 */
555static int
556findhostaddr(
557	struct conf_entry *entry
558	)
559{
560	static int eai_again_seen = 0;
561	struct addrinfo *addr;
562	struct addrinfo hints;
563	int again;
564	int error;
565
566	checkparent();		/* make sure our guy is still running */
567
568	if (entry->ce_name != NULL && !SOCK_UNSPEC(&entry->peer_store)) {
569		/* HMS: Squawk? */
570		msyslog(LOG_ERR, "findhostaddr: both ce_name and ce_peeraddr are defined...");
571		return 1;
572	}
573
574	if (entry->ce_name == NULL && SOCK_UNSPEC(&entry->peer_store)) {
575		msyslog(LOG_ERR, "findhostaddr: both ce_name and ce_peeraddr are undefined!");
576		return 0;
577	}
578
579	if (entry->ce_name) {
580		DPRINTF(2, ("findhostaddr: Resolving <%s>\n",
581			entry->ce_name));
582
583		memset(&hints, 0, sizeof(hints));
584		hints.ai_family = entry->type;
585		hints.ai_socktype = SOCK_DGRAM;
586		hints.ai_protocol = IPPROTO_UDP;
587		/*
588		 * If IPv6 is not available look only for v4 addresses
589		 */
590		if (!ipv6_works)
591			hints.ai_family = AF_INET;
592		error = getaddrinfo(entry->ce_name, NULL, &hints, &addr);
593		if (error == 0) {
594			entry->peer_store = *((sockaddr_u *)(addr->ai_addr));
595			if (IS_IPV4(&entry->peer_store)) {
596				entry->ce_peeraddr =
597				    NSRCADR(&entry->peer_store);
598				entry->ce_config.v6_flag = 0;
599			} else {
600				entry->ce_peeraddr6 =
601				    SOCK_ADDR6(&entry->peer_store);
602				entry->ce_config.v6_flag = 1;
603			}
604			freeaddrinfo(addr);
605		}
606	} else {
607		DPRINTF(2, ("findhostaddr: Resolving <%s>\n",
608			stoa(&entry->peer_store)));
609
610		entry->ce_name = emalloc(MAXHOSTNAMELEN);
611		error = getnameinfo((const struct sockaddr *)&entry->peer_store,
612				   SOCKLEN(&entry->peer_store),
613				   (char *)&entry->ce_name, MAXHOSTNAMELEN,
614				   NULL, 0, 0);
615	}
616
617	if (0 == error) {
618
619		/* again is our return value, for success it is 1 */
620		again = 1;
621
622		DPRINTF(2, ("findhostaddr: %s resolved.\n",
623			(entry->ce_name) ? "name" : "address"));
624	} else {
625		/*
626		 * If the resolver failed, see if the failure is
627		 * temporary. If so, return success.
628		 */
629		again = 0;
630
631		switch (error) {
632
633		case EAI_FAIL:
634			again = 1;
635			break;
636
637		case EAI_AGAIN:
638			again = 1;
639			eai_again_seen = 1;
640			break;
641
642		case EAI_NONAME:
643#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
644		case EAI_NODATA:
645#endif
646			msyslog(LOG_ERR, "host name not found%s%s: %s",
647				(EAI_NONAME == error) ? "" : " EAI_NODATA",
648				(eai_again_seen) ? " (permanent)" : "",
649				entry->ce_name);
650			again = !eai_again_seen;
651			break;
652
653#ifdef EAI_SYSTEM
654		case EAI_SYSTEM:
655			/*
656			 * EAI_SYSTEM means the real error is in errno.  We should be more
657			 * discriminating about which errno values require retrying, but
658			 * this matches existing behavior.
659			 */
660			again = 1;
661			DPRINTF(1, ("intres: EAI_SYSTEM errno %d (%s) means try again, right?\n",
662				errno, strerror(errno)));
663			break;
664#endif
665		}
666
667		/* do this here to avoid perturbing errno earlier */
668		DPRINTF(2, ("intres: got error status of: %d\n", error));
669	}
670
671	return again;
672}
673
674
675/*
676 * openntp - open a socket to the ntp server
677 */
678static void
679openntp(void)
680{
681	const char	*localhost = "127.0.0.1";	/* Use IPv4 loopback */
682	struct addrinfo	hints;
683	struct addrinfo	*addr;
684	u_long		on;
685	int		err;
686
687	if (sockfd != INVALID_SOCKET)
688		return;
689
690	memset(&hints, 0, sizeof(hints));
691
692	/*
693	 * For now only bother with IPv4
694	 */
695	hints.ai_family = AF_INET;
696	hints.ai_socktype = SOCK_DGRAM;
697
698	err = getaddrinfo(localhost, "ntp", &hints, &addr);
699
700	if (err) {
701#ifdef EAI_SYSTEM
702		if (EAI_SYSTEM == err)
703			msyslog(LOG_ERR, "getaddrinfo(%s) failed: %m",
704				localhost);
705		else
706#endif
707			msyslog(LOG_ERR, "getaddrinfo(%s) failed: %s",
708				localhost, gai_strerror(err));
709		resolver_exit(1);
710	}
711
712	sockfd = socket(addr->ai_family, addr->ai_socktype, 0);
713
714	if (INVALID_SOCKET == sockfd) {
715		msyslog(LOG_ERR, "socket() failed: %m");
716		resolver_exit(1);
717	}
718
719#ifndef SYS_WINNT
720	/*
721	 * On Windows only the count of sockets must be less than
722	 * FD_SETSIZE. On Unix each descriptor's value must be less
723	 * than FD_SETSIZE, as fd_set is a bit array.
724	 */
725	if (sockfd >= FD_SETSIZE) {
726		msyslog(LOG_ERR, "socket fd %d too large, FD_SETSIZE %d",
727			(int)sockfd, FD_SETSIZE);
728		resolver_exit(1);
729	}
730
731	/*
732	 * Make the socket non-blocking.  We'll wait with select()
733	 * Unix: fcntl(O_NONBLOCK) or fcntl(FNDELAY)
734	 */
735# ifdef O_NONBLOCK
736	if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == -1) {
737		msyslog(LOG_ERR, "fcntl(O_NONBLOCK) failed: %m");
738		resolver_exit(1);
739	}
740# else
741#  ifdef FNDELAY
742	if (fcntl(sockfd, F_SETFL, FNDELAY) == -1) {
743		msyslog(LOG_ERR, "fcntl(FNDELAY) failed: %m");
744		resolver_exit(1);
745	}
746#  else
747#   include "Bletch: NEED NON BLOCKING IO"
748#  endif	/* FNDDELAY */
749# endif	/* O_NONBLOCK */
750	(void)on;	/* quiet unused warning */
751#else	/* !SYS_WINNT above */
752	/*
753	 * Make the socket non-blocking.  We'll wait with select()
754	 * Windows: ioctlsocket(FIONBIO)
755	 */
756	on = 1;
757	err = ioctlsocket(sockfd, FIONBIO, &on);
758	if (SOCKET_ERROR == err) {
759		msyslog(LOG_ERR, "ioctlsocket(FIONBIO) fails: %m");
760		resolver_exit(1);
761	}
762#endif /* SYS_WINNT */
763
764	err = connect(sockfd, addr->ai_addr, addr->ai_addrlen);
765	if (SOCKET_ERROR == err) {
766		msyslog(LOG_ERR, "openntp: connect() failed: %m");
767		resolver_exit(1);
768	}
769
770	freeaddrinfo(addr);
771}
772
773
774/*
775 * request - send a configuration request to the server, wait for a response
776 */
777static int
778request(
779	struct conf_peer *conf
780	)
781{
782	struct sock_timeval tvout;
783	struct req_pkt reqpkt;
784	size_t	req_len;
785	size_t	total_len;	/* req_len plus keyid & digest */
786	fd_set	fdset;
787	l_fp	ts;
788	char *	pch;
789	char *	pchEnd;
790	l_fp *	pts;
791	keyid_t *pkeyid;
792	int n;
793#ifdef SYS_WINNT
794	HANDLE	hReadWriteEvent = NULL;
795	BOOL	ret;
796	DWORD	NumberOfBytesWritten, NumberOfBytesRead, dwWait;
797	OVERLAPPED overlap;
798#endif /* SYS_WINNT */
799
800	checkparent();		/* make sure our guy is still running */
801
802	if (sockfd == INVALID_SOCKET)
803		openntp();
804
805#ifdef SYS_WINNT
806	hReadWriteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
807#endif /* SYS_WINNT */
808
809	/*
810	 * Try to clear out any previously received traffic so it
811	 * doesn't fool us.  Note the socket is nonblocking.
812	 */
813	tvout.tv_sec =  0;
814	tvout.tv_usec = 0;
815	FD_ZERO(&fdset);
816	FD_SET(sockfd, &fdset);
817	while (select(sockfd + 1, &fdset, (fd_set *)0, (fd_set *)0, &tvout) >
818	       0) {
819		recv(sockfd, (char *)&reqpkt, sizeof(reqpkt), 0);
820		FD_ZERO(&fdset);
821		FD_SET(sockfd, &fdset);
822	}
823
824	/*
825	 * Make up a request packet with the configuration info
826	 */
827	memset(&reqpkt, 0, sizeof(reqpkt));
828
829	reqpkt.rm_vn_mode = RM_VN_MODE(0, 0, 0);
830	reqpkt.auth_seq = AUTH_SEQ(1, 0);	/* authenticated, no seq */
831	reqpkt.implementation = IMPL_XNTPD;	/* local implementation */
832	reqpkt.request = REQ_CONFIG;		/* configure a new peer */
833	reqpkt.err_nitems = ERR_NITEMS(0, 1);	/* one item */
834	reqpkt.mbz_itemsize = MBZ_ITEMSIZE(sizeof(*conf));
835	/* Make sure mbz_itemsize <= sizeof reqpkt.data */
836	if (sizeof(*conf) > sizeof(reqpkt.data)) {
837		msyslog(LOG_ERR,
838			"Bletch: conf_peer is too big for reqpkt.data!");
839		resolver_exit(1);
840	}
841	memcpy(reqpkt.data, conf, sizeof(*conf));
842
843	if (sys_authenticate && req_hashlen > 16) {
844		pch = reqpkt.data;
845		/* 32-bit alignment */
846		pch += (sizeof(*conf) + 3) & ~3;
847		pts = (void *)pch;
848		pkeyid = (void *)(pts + 1);
849		pchEnd = (void *)pkeyid;
850		req_len = pchEnd - (char *)&reqpkt;
851		pchEnd = (void *)(pkeyid + 1);
852		pchEnd += req_hashlen;
853		total_len = pchEnd - (char *)&reqpkt;
854		if (total_len > sizeof(reqpkt)) {
855			msyslog(LOG_ERR,
856				"intres total_len %u limit is %u (%u octet digest)\n",
857				total_len, sizeof(reqpkt),
858				req_hashlen);
859			resolver_exit(1);
860		}
861	} else {
862		pts = &reqpkt.tstamp;
863		pkeyid = &reqpkt.keyid;
864		req_len = REQ_LEN_NOMAC;
865	}
866
867	*pkeyid = htonl(req_keyid);
868	get_systime(&ts);
869	L_ADDUF(&ts, SKEWTIME);
870	HTONL_FP(&ts, pts);
871	if (sys_authenticate) {
872		n = authencrypt(req_keyid, (void *)&reqpkt, req_len);
873		if ((size_t)n != req_hashlen + sizeof(reqpkt.keyid)) {
874			msyslog(LOG_ERR,
875				"intres maclen %d expected %u\n",
876				n, req_hashlen + sizeof(reqpkt.keyid));
877			resolver_exit(1);
878		}
879		req_len += n;
880	}
881
882	/*
883	 * Done.  Send it.
884	 */
885#ifndef SYS_WINNT
886	n = send(sockfd, (char *)&reqpkt, req_len, 0);
887	if (n < 0) {
888		msyslog(LOG_ERR, "send to NTP server failed: %m");
889		return 0;	/* maybe should exit */
890	}
891#else
892	/* In the NT world, documentation seems to indicate that there
893	 * exist _write and _read routines that can be used to do blocking
894	 * I/O on sockets. Problem is these routines require a socket
895	 * handle obtained through the _open_osf_handle C run-time API
896	 * of which there is no explanation in the documentation. We need
897	 * nonblocking write's and read's anyway for our purpose here.
898	 * We're therefore forced to deviate a little bit from the Unix
899	 * model here and use the ReadFile and WriteFile Win32 I/O API's
900	 * on the socket
901	 */
902	overlap.Offset = overlap.OffsetHigh = (DWORD)0;
903	overlap.hEvent = hReadWriteEvent;
904	ret = WriteFile((HANDLE)sockfd, (char *)&reqpkt, req_len,
905			NULL, (LPOVERLAPPED)&overlap);
906	if ((ret == FALSE) && (GetLastError() != ERROR_IO_PENDING)) {
907		msyslog(LOG_ERR, "send to NTP server failed: %m");
908		return 0;
909	}
910	dwWait = WaitForSingleObject(hReadWriteEvent, (DWORD) TIMEOUT_SEC * 1000);
911	if ((dwWait == WAIT_FAILED) || (dwWait == WAIT_TIMEOUT)) {
912		if (dwWait == WAIT_FAILED)
913		    msyslog(LOG_ERR, "WaitForSingleObject failed: %m");
914		return 0;
915	}
916	if (!GetOverlappedResult((HANDLE)sockfd, (LPOVERLAPPED)&overlap,
917				(LPDWORD)&NumberOfBytesWritten, FALSE)) {
918		msyslog(LOG_ERR, "GetOverlappedResult for WriteFile fails: %m");
919		return 0;
920	}
921#endif /* SYS_WINNT */
922
923
924	/*
925	 * Wait for a response.  A weakness of the mode 7 protocol used
926	 * is that there is no way to associate a response with a
927	 * particular request, i.e. the response to this configuration
928	 * request is indistinguishable from that to any other.  I should
929	 * fix this some day.  In any event, the time out is fairly
930	 * pessimistic to make sure that if an answer is coming back
931	 * at all, we get it.
932	 */
933	for (;;) {
934		FD_ZERO(&fdset);
935		FD_SET(sockfd, &fdset);
936		tvout.tv_sec = TIMEOUT_SEC;
937		tvout.tv_usec = TIMEOUT_USEC;
938
939		n = select(sockfd + 1, &fdset, (fd_set *)0,
940			   (fd_set *)0, &tvout);
941
942		if (n < 0) {
943			if (errno != EINTR)
944				msyslog(LOG_ERR, "select() fails: %m");
945			return 0;
946		} else if (n == 0) {
947#ifdef DEBUG
948			if (debug)
949				msyslog(LOG_INFO, "ntp_intres select() returned 0.");
950#endif
951			return 0;
952		}
953
954#ifndef SYS_WINNT
955		n = recv(sockfd, (char *)&reqpkt, sizeof(reqpkt), 0);
956		if (n <= 0) {
957			if (n < 0) {
958				msyslog(LOG_ERR, "recv() fails: %m");
959				return 0;
960			}
961			continue;
962		}
963#else /* Overlapped I/O used on non-blocking sockets on Windows NT */
964		ret = ReadFile((HANDLE)sockfd, (char *)&reqpkt, sizeof(reqpkt),
965			       NULL, (LPOVERLAPPED)&overlap);
966		if ((ret == FALSE) && (GetLastError() != ERROR_IO_PENDING)) {
967			msyslog(LOG_ERR, "ReadFile() fails: %m");
968			return 0;
969		}
970		dwWait = WaitForSingleObject(hReadWriteEvent, (DWORD) TIMEOUT_SEC * 1000);
971		if ((dwWait == WAIT_FAILED) || (dwWait == WAIT_TIMEOUT)) {
972			if (dwWait == WAIT_FAILED) {
973				msyslog(LOG_ERR, "WaitForSingleObject for ReadFile fails: %m");
974				return 0;
975			}
976			continue;
977		}
978		if (!GetOverlappedResult((HANDLE)sockfd, (LPOVERLAPPED)&overlap,
979					(LPDWORD)&NumberOfBytesRead, FALSE)) {
980			msyslog(LOG_ERR, "GetOverlappedResult fails: %m");
981			return 0;
982		}
983		n = NumberOfBytesRead;
984#endif /* SYS_WINNT */
985
986		/*
987		 * Got one.  Check through to make sure it is what
988		 * we expect.
989		 */
990		if (n < RESP_HEADER_SIZE) {
991			msyslog(LOG_ERR, "received runt response (%d octets)",
992				n);
993			continue;
994		}
995
996		if (!ISRESPONSE(reqpkt.rm_vn_mode)) {
997#ifdef DEBUG
998			if (debug > 1)
999			    msyslog(LOG_INFO, "received non-response packet");
1000#endif
1001			continue;
1002		}
1003
1004		if (ISMORE(reqpkt.rm_vn_mode)) {
1005#ifdef DEBUG
1006			if (debug > 1)
1007			    msyslog(LOG_INFO, "received fragmented packet");
1008#endif
1009			continue;
1010		}
1011
1012		if ( ( (INFO_VERSION(reqpkt.rm_vn_mode) < 2)
1013		       || (INFO_VERSION(reqpkt.rm_vn_mode) > NTP_VERSION))
1014		     || INFO_MODE(reqpkt.rm_vn_mode) != MODE_PRIVATE) {
1015#ifdef DEBUG
1016			if (debug > 1)
1017			    msyslog(LOG_INFO,
1018				    "version (%d/%d) or mode (%d/%d) incorrect",
1019				    INFO_VERSION(reqpkt.rm_vn_mode),
1020				    NTP_VERSION,
1021				    INFO_MODE(reqpkt.rm_vn_mode),
1022				    MODE_PRIVATE);
1023#endif
1024			continue;
1025		}
1026
1027		if (INFO_SEQ(reqpkt.auth_seq) != 0) {
1028#ifdef DEBUG
1029			if (debug > 1)
1030			    msyslog(LOG_INFO,
1031				    "nonzero sequence number (%d)",
1032				    INFO_SEQ(reqpkt.auth_seq));
1033#endif
1034			continue;
1035		}
1036
1037		if (reqpkt.implementation != IMPL_XNTPD ||
1038		    reqpkt.request != REQ_CONFIG) {
1039#ifdef DEBUG
1040			if (debug > 1)
1041			    msyslog(LOG_INFO,
1042				    "implementation (%d) or request (%d) incorrect",
1043				    reqpkt.implementation, reqpkt.request);
1044#endif
1045			continue;
1046		}
1047
1048		if (INFO_NITEMS(reqpkt.err_nitems) != 0 ||
1049		    INFO_MBZ(reqpkt.mbz_itemsize) != 0 ||
1050		    INFO_ITEMSIZE(reqpkt.mbz_itemsize) != 0) {
1051#ifdef DEBUG
1052			if (debug > 1)
1053			    msyslog(LOG_INFO,
1054				    "nitems (%d) mbz (%d) or itemsize (%d) nonzero",
1055				    INFO_NITEMS(reqpkt.err_nitems),
1056				    INFO_MBZ(reqpkt.mbz_itemsize),
1057				    INFO_ITEMSIZE(reqpkt.mbz_itemsize));
1058#endif
1059			continue;
1060		}
1061
1062		n = INFO_ERR(reqpkt.err_nitems);
1063		switch (n) {
1064		    case INFO_OKAY:
1065			/* success */
1066			return 1;
1067
1068		    case INFO_ERR_NODATA:
1069			/*
1070			 * newpeer() refused duplicate association, no
1071			 * point in retrying so call it success.
1072			 */
1073			return 1;
1074
1075		    case INFO_ERR_IMPL:
1076			msyslog(LOG_ERR,
1077				"ntp_intres.request: implementation mismatch");
1078			return 0;
1079
1080		    case INFO_ERR_REQ:
1081			msyslog(LOG_ERR,
1082				"ntp_intres.request: request unknown");
1083			return 0;
1084
1085		    case INFO_ERR_FMT:
1086			msyslog(LOG_ERR,
1087				"ntp_intres.request: format error");
1088			return 0;
1089
1090		    case INFO_ERR_AUTH:
1091			msyslog(LOG_ERR,
1092				"ntp_intres.request: permission denied");
1093			return 0;
1094
1095		    default:
1096			msyslog(LOG_ERR,
1097				"ntp_intres.request: unknown error code %d", n);
1098			return 0;
1099		}
1100	}
1101}
1102
1103
1104/*
1105 * nexttoken - return the next token from a line
1106 */
1107static char *
1108nexttoken(
1109	char **lptr
1110	)
1111{
1112	register char *cp;
1113	register char *tstart;
1114
1115	cp = *lptr;
1116
1117	/*
1118	 * Skip leading white space
1119	 */
1120	while (*cp == ' ' || *cp == '\t')
1121	    cp++;
1122
1123	/*
1124	 * If this is the end of the line, return nothing.
1125	 */
1126	if (*cp == '\n' || *cp == '\0') {
1127		*lptr = cp;
1128		return NULL;
1129	}
1130
1131	/*
1132	 * Must be the start of a token.  Record the pointer and look
1133	 * for the end.
1134	 */
1135	tstart = cp++;
1136	while (*cp != ' ' && *cp != '\t' && *cp != '\n' && *cp != '\0')
1137	    cp++;
1138
1139	/*
1140	 * Terminate the token with a \0.  If this isn't the end of the
1141	 * line, space to the next character.
1142	 */
1143	if (*cp == '\n' || *cp == '\0')
1144	    *cp = '\0';
1145	else
1146	    *cp++ = '\0';
1147
1148	*lptr = cp;
1149	return tstart;
1150}
1151
1152
1153/*
1154 * readconf - read the configuration information out of the file we
1155 *	      were passed.  Note that since the file is supposed to be
1156 *	      machine generated, we bail out at the first sign of trouble.
1157 */
1158static void
1159readconf(
1160	FILE *fp,
1161	char *name
1162	)
1163{
1164	register int i;
1165	char *token[NUMTOK];
1166	u_long intval[NUMTOK];
1167	u_int flags;
1168	char buf[MAXLINESIZE];
1169	char *bp;
1170
1171	while (fgets(buf, MAXLINESIZE, fp) != NULL) {
1172
1173		bp = buf;
1174		for (i = 0; i < NUMTOK; i++) {
1175			if ((token[i] = nexttoken(&bp)) == NULL) {
1176				msyslog(LOG_ERR,
1177					"tokenizing error in file `%s', quitting",
1178					name);
1179				resolver_exit(1);
1180			}
1181		}
1182
1183		for (i = 1; i < NUMTOK - 1; i++) {
1184			if (!atouint(token[i], &intval[i])) {
1185				msyslog(LOG_ERR,
1186					"format error for integer token `%s', file `%s', quitting",
1187					token[i], name);
1188				resolver_exit(1);
1189			}
1190		}
1191
1192#if 0 /* paranoid checking - these are done in newpeer() */
1193		if (intval[TOK_HMODE] != MODE_ACTIVE &&
1194		    intval[TOK_HMODE] != MODE_CLIENT &&
1195		    intval[TOK_HMODE] != MODE_BROADCAST) {
1196			msyslog(LOG_ERR, "invalid mode (%ld) in file %s",
1197				intval[TOK_HMODE], name);
1198			resolver_exit(1);
1199		}
1200
1201		if (intval[TOK_VERSION] > NTP_VERSION ||
1202		    intval[TOK_VERSION] < NTP_OLDVERSION) {
1203			msyslog(LOG_ERR, "invalid version (%ld) in file %s",
1204				intval[TOK_VERSION], name);
1205			resolver_exit(1);
1206		}
1207		if (intval[TOK_MINPOLL] < ntp_minpoll ||
1208		    intval[TOK_MINPOLL] > NTP_MAXPOLL) {
1209
1210			msyslog(LOG_ERR, "invalid MINPOLL value (%ld) in file %s",
1211				intval[TOK_MINPOLL], name);
1212			resolver_exit(1);
1213		}
1214
1215		if (intval[TOK_MAXPOLL] < ntp_minpoll ||
1216		    intval[TOK_MAXPOLL] > NTP_MAXPOLL) {
1217			msyslog(LOG_ERR, "invalid MAXPOLL value (%ld) in file %s",
1218				intval[TOK_MAXPOLL], name);
1219			resolver_exit(1);
1220		}
1221
1222		if ((intval[TOK_FLAGS] & ~(FLAG_PREFER | FLAG_NOSELECT |
1223		    FLAG_BURST | FLAG_IBURST | FLAG_SKEY)) != 0) {
1224			msyslog(LOG_ERR, "invalid flags (%ld) in file %s",
1225				intval[TOK_FLAGS], name);
1226			resolver_exit(1);
1227		}
1228#endif /* end paranoid checking */
1229
1230		flags = 0;
1231		if (intval[TOK_FLAGS] & FLAG_PREFER)
1232		    flags |= CONF_FLAG_PREFER;
1233		if (intval[TOK_FLAGS] & FLAG_NOSELECT)
1234		    flags |= CONF_FLAG_NOSELECT;
1235		if (intval[TOK_FLAGS] & FLAG_BURST)
1236		    flags |= CONF_FLAG_BURST;
1237		if (intval[TOK_FLAGS] & FLAG_IBURST)
1238		    flags |= CONF_FLAG_IBURST;
1239
1240#ifdef OPENSSL
1241		if (intval[TOK_FLAGS] & FLAG_SKEY)
1242		    flags |= CONF_FLAG_SKEY;
1243#endif /* OPENSSL */
1244
1245		/*
1246		 * This is as good as we can check it.  Add it in.
1247		 */
1248		addentry(token[TOK_HOSTNAME],
1249			 (int)intval[TOK_NEEDED], (int)intval[TOK_TYPE],
1250			 (int)intval[TOK_HMODE], (int)intval[TOK_VERSION],
1251			 (int)intval[TOK_MINPOLL], (int)intval[TOK_MAXPOLL],
1252			 flags, (int)intval[TOK_TTL],
1253			 intval[TOK_KEYID], token[TOK_KEYSTR]);
1254	}
1255}
1256
1257
1258/*
1259 * doconfigure - attempt to resolve names and configure the server
1260 */
1261static void
1262doconfigure(
1263	int dores
1264	)
1265{
1266	register struct conf_entry *ce;
1267
1268#ifdef DEBUG
1269		if (debug > 1)
1270			msyslog(LOG_INFO, "Running doconfigure %s DNS",
1271			    dores ? "with" : "without" );
1272#endif
1273
1274#if defined(HAVE_RES_INIT) || defined(HAVE___RES_INIT)
1275	if (dores)	   /* Reload /etc/resolv.conf - bug 1226 */
1276		res_init();
1277#endif
1278	ce = confentries;
1279	while (ce != NULL) {
1280#ifdef DEBUG
1281		if (debug > 1)
1282			msyslog(LOG_INFO,
1283			    "doconfigure: <%s> has peeraddr %s",
1284			    ce->ce_name, stoa(&ce->peer_store));
1285#endif
1286		if (dores && SOCK_UNSPEC(&ce->peer_store)) {
1287			if (!findhostaddr(ce)) {
1288#ifndef IGNORE_DNS_ERRORS
1289				msyslog(LOG_ERR,
1290					"couldn't resolve `%s', giving up on it",
1291					ce->ce_name);
1292				ce = removeentry(ce);
1293				continue;
1294#endif
1295			} else if (!SOCK_UNSPEC(&ce->peer_store))
1296				msyslog(LOG_INFO,
1297					"DNS %s -> %s", ce->ce_name,
1298					stoa(&ce->peer_store));
1299		}
1300
1301		if (!SOCK_UNSPEC(&ce->peer_store)) {
1302			if (request(&ce->ce_config)) {
1303				ce = removeentry(ce);
1304				continue;
1305			}
1306			/*
1307			 * Failed case.  Should bump counter and give
1308			 * up.
1309			 */
1310#ifdef DEBUG
1311			if (debug > 1) {
1312				msyslog(LOG_INFO,
1313				    "doconfigure: request() FAILED, maybe next time.");
1314			}
1315#endif
1316		}
1317		ce = ce->ce_next;
1318	}
1319}
1320
1321#else	/* NO_INTRES follows */
1322int ntp_intres_nonempty_compilation_unit;
1323#endif
1324