PageRenderTime 67ms CodeModel.GetById 15ms app.highlight 43ms RepoModel.GetById 1ms app.codeStats 0ms

/contrib/ntp/ntpd/ntp_intres.c

https://bitbucket.org/freebsd/freebsd-head/
C | 1185 lines | 819 code | 155 blank | 211 comment | 200 complexity | b9bdfd06a4e027139907039db0bc5926 MD5 | raw file
   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}