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