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