Sync usage with man page.
[netbsd-mini2440.git] / external / bsd / ntp / dist / ntpd / ntp_intres.c
blob208188dd7520a15219e41738949b44cf20d84a4a
1 /* $NetBSD$ */
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"
31 #include "ntp_config.h"
33 #ifndef NO_INTRES /* from ntp_config.h */
35 #include <stdio.h>
36 #include <ctype.h>
37 #include <signal.h>
39 /**/
40 #ifdef HAVE_NETINET_IN_H
41 #include <netinet/in.h>
42 #endif
43 #include <arpa/inet.h>
44 /**/
45 #ifdef HAVE_SYS_PARAM_H
46 # include <sys/param.h> /* MAXHOSTNAMELEN (often) */
47 #endif
49 #if defined(HAVE_RES_INIT) || defined(HAVE___RES_INIT)
50 #include <resolv.h>
51 #endif
53 #include <isc/net.h>
54 #include <isc/result.h>
56 #define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
59 * Each item we are to resolve and configure gets one of these
60 * structures defined for it.
62 struct conf_entry {
63 struct conf_entry *ce_next;
64 char *ce_name; /* name to resolve */
65 struct conf_peer ce_config; /* config info for peer */
66 int no_needed; /* number of addresses needed (pool) */
67 /* no_needed isn't used yet: It's needed to fix bug-975 */
68 int type; /* -4 and -6 flags */
69 sockaddr_u peer_store; /* address info for both fams */
71 #define ce_peeraddr ce_config.peeraddr
72 #define ce_peeraddr6 ce_config.peeraddr6
73 #define ce_hmode ce_config.hmode
74 #define ce_version ce_config.version
75 #define ce_minpoll ce_config.minpoll
76 #define ce_maxpoll ce_config.maxpoll
77 #define ce_flags ce_config.flags
78 #define ce_ttl ce_config.ttl
79 #define ce_keyid ce_config.keyid
80 #define ce_keystr ce_config.keystr
83 * confentries is a pointer to the list of configuration entries
84 * we have left to do.
86 static struct conf_entry *confentries = NULL;
89 * We take an interrupt every thirty seconds, at which time we decrement
90 * config_timer and resolve_timer. The former is set to 2, so we retry
91 * unsucessful reconfigurations every minute. The latter is set to
92 * an exponentially increasing value which starts at 2 and increases to
93 * 32. When this expires we retry failed name resolutions.
95 * We sleep SLEEPTIME seconds before doing anything, to give the server
96 * time to arrange itself.
98 #define MINRESOLVE 2
99 #define MAXRESOLVE 32
100 #define CONFIG_TIME 2
101 #define ALARM_TIME 30
102 #define SLEEPTIME 2
104 static volatile int config_timer = 0;
105 static volatile int resolve_timer = 0;
107 static int resolve_value; /* next value of resolve timer */
110 * Big hack attack
112 #define SKEWTIME 0x08000000 /* 0.03125 seconds as a l_fp fraction */
115 * Select time out. Set to 2 seconds. The server is on the local machine,
116 * after all.
118 #define TIMEOUT_SEC 2
119 #define TIMEOUT_USEC 0
123 * Input processing. The data on each line in the configuration file
124 * is supposed to consist of entries in the following order
126 #define TOK_HOSTNAME 0
127 #define TOK_NEEDED 1
128 #define TOK_TYPE 2
129 #define TOK_HMODE 3
130 #define TOK_VERSION 4
131 #define TOK_MINPOLL 5
132 #define TOK_MAXPOLL 6
133 #define TOK_FLAGS 7
134 #define TOK_TTL 8
135 #define TOK_KEYID 9
136 #define TOK_KEYSTR 10
137 #define NUMTOK 11
139 #define MAXLINESIZE 512
143 * File descriptor for ntp request code.
145 static SOCKET sockfd = INVALID_SOCKET; /* NT uses SOCKET */
147 /* stuff to be filled in by caller */
149 keyid_t req_keyid; /* request keyid */
150 int req_keytype; /* OpenSSL NID such as NID_md5 */
151 size_t req_hashlen; /* digest size for req_keytype */
152 char *req_file; /* name of the file with configuration info */
154 /* end stuff to be filled in */
157 static void checkparent (void);
158 static struct conf_entry *
159 removeentry (struct conf_entry *);
160 static void addentry (char *, int, int, int, int, int, int, u_int,
161 int, keyid_t, char *);
162 static int findhostaddr (struct conf_entry *);
163 static void openntp (void);
164 static int request (struct conf_peer *);
165 static char * nexttoken (char **);
166 static void readconf (FILE *, char *);
167 static void doconfigure (int);
169 struct ntp_res_t_pkt { /* Tagged packet: */
170 void *tag; /* For the caller */
171 u_int32 paddr; /* IP to look up, or 0 */
172 char name[MAXHOSTNAMELEN]; /* Name to look up (if 1st byte is not 0) */
175 struct ntp_res_c_pkt { /* Control packet: */
176 char name[MAXHOSTNAMELEN];
177 u_int32 paddr;
178 int mode;
179 int version;
180 int minpoll;
181 int maxpoll;
182 u_int flags;
183 int ttl;
184 keyid_t keyid;
185 u_char keystr[MAXFILENAME];
189 static void resolver_exit (int);
192 * Call here instead of just exiting
195 static void resolver_exit (int code)
197 #ifdef SYS_WINNT
198 CloseHandle(ResolverEventHandle);
199 ResolverEventHandle = NULL;
200 _endthreadex(code); /* Just to kill the thread not the process */
201 #else
202 exit(code); /* kill the forked process */
203 #endif
207 * ntp_res_recv: Process an answer from the resolver
210 void
211 ntp_res_recv(void)
214 We have data ready on our descriptor.
215 It may be an EOF, meaning the resolver process went away.
216 Otherwise, it will be an "answer".
222 * ntp_intres needs;
224 * req_key(???), req_keyid, req_file valid
225 * syslog still open
228 void
229 ntp_intres(void)
231 FILE *in;
232 #ifdef SYS_WINNT
233 DWORD rc;
234 #else
235 int rc;
236 struct timeval tv;
237 fd_set fdset;
238 int time_left;
239 #endif
241 #ifdef DEBUG
242 if (debug > 1) {
243 msyslog(LOG_INFO, "NTP_INTRES running");
245 #endif
247 /* check out auth stuff */
248 if (sys_authenticate) {
249 if (!authistrusted(req_keyid)) {
250 msyslog(LOG_ERR, "invalid request keyid %08x",
251 req_keyid );
252 resolver_exit(1);
257 * Read the configuration info
258 * {this is bogus, since we are forked, but it is easier
259 * to keep this code - gdt}
261 if ((in = fopen(req_file, "r")) == NULL) {
262 msyslog(LOG_ERR, "can't open configuration file %s: %m",
263 req_file);
264 resolver_exit(1);
266 readconf(in, req_file);
267 (void) fclose(in);
269 #ifdef DEBUG
270 if (!debug)
271 #endif
272 if (unlink(req_file))
273 msyslog(LOG_WARNING,
274 "unable to remove intres request file %s, %m",
275 req_file);
278 * Set up the timers to do first shot immediately.
280 resolve_timer = 0;
281 resolve_value = MINRESOLVE;
282 config_timer = CONFIG_TIME;
284 for (;;) {
285 checkparent();
287 if (resolve_timer == 0) {
289 * Sleep a little to make sure the network is completely up
291 sleep(SLEEPTIME);
292 doconfigure(1);
294 /* prepare retry, in case there's more work to do */
295 resolve_timer = resolve_value;
296 #ifdef DEBUG
297 if (debug > 2)
298 msyslog(LOG_INFO, "resolve_timer: 0->%d", resolve_timer);
299 #endif
300 if (resolve_value < MAXRESOLVE)
301 resolve_value <<= 1;
303 config_timer = CONFIG_TIME;
304 } else if (config_timer == 0) { /* MB: in which case would this be required ? */
305 doconfigure(0);
306 /* MB: should we check now if we could exit, similar to the code above? */
307 config_timer = CONFIG_TIME;
308 #ifdef DEBUG
309 if (debug > 2)
310 msyslog(LOG_INFO, "config_timer: 0->%d", config_timer);
311 #endif
314 if (confentries == NULL)
315 resolver_exit(0); /* done */
317 #ifdef SYS_WINNT
318 rc = WaitForSingleObject(ResolverEventHandle, 1000 * ALARM_TIME); /* in milliseconds */
320 if ( rc == WAIT_OBJECT_0 ) { /* signaled by the main thread */
321 resolve_timer = 0; /* retry resolving immediately */
322 continue;
325 if ( rc != WAIT_TIMEOUT ) /* not timeout: error */
326 resolver_exit(1);
328 #else /* not SYS_WINNT */
329 /* Bug 1386: fork() in NetBSD leaves timers running. */
330 /* So we need to retry select on EINTR */
331 time_left = ALARM_TIME;
332 while (time_left > 0) {
333 tv.tv_sec = time_left;
334 tv.tv_usec = 0;
335 FD_ZERO(&fdset);
336 FD_SET(resolver_pipe_fd[0], &fdset);
337 rc = select(resolver_pipe_fd[0] + 1, &fdset, (fd_set *)0, (fd_set *)0, &tv);
339 if (rc == 0) /* normal timeout */
340 break;
342 if (rc > 0) { /* parent process has written to the pipe */
343 read(resolver_pipe_fd[0], (char *)&rc, sizeof(rc)); /* make pipe empty */
344 resolve_timer = 0; /* retry resolving immediately */
345 break;
348 if ( rc < 0 ) { /* select() returned error */
349 if (errno == EINTR) { /* Timer went off */
350 time_left -= (1<<EVENT_TIMEOUT);
351 continue; /* try again */
353 msyslog(LOG_ERR, "ntp_intres: Error from select: %s",
354 strerror(errno));
355 resolver_exit(1);
358 #endif
360 /* normal timeout, keep on waiting */
361 if (config_timer > 0)
362 config_timer--;
363 if (resolve_timer > 0)
364 resolve_timer--;
369 #ifdef SYS_WINNT
371 * ntp_intres_thread wraps the slightly different interface of Windows
372 * thread functions and ntp_intres
374 unsigned WINAPI
375 ntp_intres_thread(void *UnusedThreadArg)
377 UNUSED_ARG(UnusedThreadArg);
379 ntp_intres();
380 return 0;
382 #endif /* SYS_WINNT */
386 * checkparent - see if our parent process is still running
388 * No need to worry in the Windows NT environment whether the
389 * main thread is still running, because if it goes
390 * down it takes the whole process down with it (in
391 * which case we won't be running this thread either)
392 * Turn function into NOP;
395 static void
396 checkparent(void)
398 #if !defined (SYS_WINNT) && !defined (SYS_VXWORKS)
401 * If our parent (the server) has died we will have been
402 * inherited by init. If so, exit.
404 if (getppid() == 1) {
405 msyslog(LOG_INFO, "parent died before we finished, exiting");
406 resolver_exit(0);
408 #endif /* SYS_WINNT && SYS_VXWORKS*/
414 * removeentry - we are done with an entry, remove it from the list
416 static struct conf_entry *
417 removeentry(
418 struct conf_entry *entry
421 register struct conf_entry *ce;
422 struct conf_entry *next_ce;
424 ce = confentries;
425 if (ce == entry)
426 confentries = ce->ce_next;
427 else
428 while (ce != NULL) {
429 if (ce->ce_next == entry) {
430 ce->ce_next = entry->ce_next;
431 break;
433 ce = ce->ce_next;
436 next_ce = entry->ce_next;
437 if (entry->ce_name != NULL)
438 free(entry->ce_name);
439 free(entry);
441 return next_ce;
446 * addentry - add an entry to the configuration list
448 static void
449 addentry(
450 char *name,
451 int no_needed,
452 int type,
453 int mode,
454 int version,
455 int minpoll,
456 int maxpoll,
457 u_int flags,
458 int ttl,
459 keyid_t keyid,
460 char *keystr
463 register struct conf_entry *ce;
465 #ifdef DEBUG
466 if (debug > 1)
467 msyslog(LOG_INFO,
468 "intres: <%s> %d %d %d %d %d %d %x %d %x %s",
469 name, no_needed, type, mode, version,
470 minpoll, maxpoll, flags, ttl, keyid, keystr);
471 #endif
472 ce = emalloc(sizeof(*ce));
473 ce->ce_name = estrdup(name);
474 ce->ce_peeraddr = 0;
475 #ifdef ISC_PLATFORM_HAVEIPV6
476 ce->ce_peeraddr6 = in6addr_any;
477 #endif
478 ZERO_SOCK(&ce->peer_store);
479 ce->ce_hmode = (u_char)mode;
480 ce->ce_version = (u_char)version;
481 ce->ce_minpoll = (u_char)minpoll;
482 ce->ce_maxpoll = (u_char)maxpoll;
483 ce->no_needed = no_needed; /* Not used after here. */
484 /* Start of fixing bug-975 */
485 ce->type = type;
486 ce->ce_flags = (u_char)flags;
487 ce->ce_ttl = (u_char)ttl;
488 ce->ce_keyid = keyid;
489 strncpy(ce->ce_keystr, keystr, sizeof(ce->ce_keystr) - 1);
490 ce->ce_keystr[sizeof(ce->ce_keystr) - 1] = 0;
491 ce->ce_next = NULL;
493 if (confentries == NULL) {
494 confentries = ce;
495 } else {
496 register struct conf_entry *cep;
498 for (cep = confentries; cep->ce_next != NULL;
499 cep = cep->ce_next)
500 /* nothing */;
501 cep->ce_next = ce;
507 * findhostaddr - resolve a host name into an address (Or vice-versa)
509 * Given one of {ce_peeraddr,ce_name}, find the other one.
510 * It returns 1 for "success" and 0 for an uncorrectable failure.
511 * Note that "success" includes try again errors. You can tell that you
512 * got a "try again" since {ce_peeraddr,ce_name} will still be zero.
514 static int
515 findhostaddr(
516 struct conf_entry *entry
519 static int eai_again_seen = 0;
520 struct addrinfo *addr;
521 struct addrinfo hints;
522 int again;
523 int error;
525 checkparent(); /* make sure our guy is still running */
527 if (entry->ce_name != NULL && !SOCK_UNSPEC(&entry->peer_store)) {
528 /* HMS: Squawk? */
529 msyslog(LOG_ERR, "findhostaddr: both ce_name and ce_peeraddr are defined...");
530 return 1;
533 if (entry->ce_name == NULL && SOCK_UNSPEC(&entry->peer_store)) {
534 msyslog(LOG_ERR, "findhostaddr: both ce_name and ce_peeraddr are undefined!");
535 return 0;
538 if (entry->ce_name) {
539 DPRINTF(2, ("findhostaddr: Resolving <%s>\n",
540 entry->ce_name));
542 memset(&hints, 0, sizeof(hints));
543 hints.ai_family = entry->type;
544 hints.ai_socktype = SOCK_DGRAM;
545 hints.ai_protocol = IPPROTO_UDP;
547 * If IPv6 is not available look only for v4 addresses
549 if (!ipv6_works)
550 hints.ai_family = AF_INET;
551 error = getaddrinfo(entry->ce_name, NULL, &hints, &addr);
552 if (error == 0) {
553 entry->peer_store = *((sockaddr_u *)(addr->ai_addr));
554 if (IS_IPV4(&entry->peer_store)) {
555 entry->ce_peeraddr =
556 NSRCADR(&entry->peer_store);
557 entry->ce_config.v6_flag = 0;
558 } else {
559 entry->ce_peeraddr6 =
560 SOCK_ADDR6(&entry->peer_store);
561 entry->ce_config.v6_flag = 1;
563 freeaddrinfo(addr);
565 } else {
566 DPRINTF(2, ("findhostaddr: Resolving <%s>\n",
567 stoa(&entry->peer_store)));
569 entry->ce_name = emalloc(MAXHOSTNAMELEN);
570 error = getnameinfo((const struct sockaddr *)&entry->peer_store,
571 SOCKLEN(&entry->peer_store),
572 (char *)&entry->ce_name, MAXHOSTNAMELEN,
573 NULL, 0, 0);
576 if (0 == error) {
578 /* again is our return value, for success it is 1 */
579 again = 1;
581 DPRINTF(2, ("findhostaddr: %s resolved.\n",
582 (entry->ce_name) ? "name" : "address"));
583 } else {
585 * If the resolver failed, see if the failure is
586 * temporary. If so, return success.
588 again = 0;
590 switch (error) {
592 case EAI_FAIL:
593 again = 1;
594 break;
596 case EAI_AGAIN:
597 again = 1;
598 eai_again_seen = 1;
599 break;
601 case EAI_NONAME:
602 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
603 case EAI_NODATA:
604 #endif
605 msyslog(LOG_ERR, "host name not found%s%s: %s",
606 (EAI_NONAME == error) ? "" : " EAI_NODATA",
607 (eai_again_seen) ? " (permanent)" : "",
608 entry->ce_name);
609 again = !eai_again_seen;
610 break;
612 #ifdef EAI_SYSTEM
613 case EAI_SYSTEM:
615 * EAI_SYSTEM means the real error is in errno. We should be more
616 * discriminating about which errno values require retrying, but
617 * this matches existing behavior.
619 again = 1;
620 DPRINTF(1, ("intres: EAI_SYSTEM errno %d (%s) means try again, right?\n",
621 errno, strerror(errno)));
622 break;
623 #endif
626 /* do this here to avoid perturbing errno earlier */
627 DPRINTF(2, ("intres: got error status of: %d\n", error));
630 return again;
635 * openntp - open a socket to the ntp server
637 static void
638 openntp(void)
640 const char *localhost = "127.0.0.1"; /* Use IPv4 loopback */
641 struct addrinfo hints;
642 struct addrinfo *addr;
643 u_long on;
644 int err;
646 if (sockfd != INVALID_SOCKET)
647 return;
649 memset(&hints, 0, sizeof(hints));
652 * For now only bother with IPv4
654 hints.ai_family = AF_INET;
655 hints.ai_socktype = SOCK_DGRAM;
657 err = getaddrinfo(localhost, "ntp", &hints, &addr);
659 if (err) {
660 #ifdef EAI_SYSTEM
661 if (EAI_SYSTEM == err)
662 msyslog(LOG_ERR, "getaddrinfo(%s) failed: %m",
663 localhost);
664 else
665 #endif
666 msyslog(LOG_ERR, "getaddrinfo(%s) failed: %s",
667 localhost, gai_strerror(err));
668 resolver_exit(1);
671 sockfd = socket(addr->ai_family, addr->ai_socktype, 0);
673 if (INVALID_SOCKET == sockfd) {
674 msyslog(LOG_ERR, "socket() failed: %m");
675 resolver_exit(1);
678 #ifndef SYS_WINNT
680 * On Windows only the count of sockets must be less than
681 * FD_SETSIZE. On Unix each descriptor's value must be less
682 * than FD_SETSIZE, as fd_set is a bit array.
684 if (sockfd >= FD_SETSIZE) {
685 msyslog(LOG_ERR, "socket fd %d too large, FD_SETSIZE %d",
686 (int)sockfd, FD_SETSIZE);
687 resolver_exit(1);
691 * Make the socket non-blocking. We'll wait with select()
692 * Unix: fcntl(O_NONBLOCK) or fcntl(FNDELAY)
694 # ifdef O_NONBLOCK
695 if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == -1) {
696 msyslog(LOG_ERR, "fcntl(O_NONBLOCK) failed: %m");
697 resolver_exit(1);
699 # else
700 # ifdef FNDELAY
701 if (fcntl(sockfd, F_SETFL, FNDELAY) == -1) {
702 msyslog(LOG_ERR, "fcntl(FNDELAY) failed: %m");
703 resolver_exit(1);
705 # else
706 # include "Bletch: NEED NON BLOCKING IO"
707 # endif /* FNDDELAY */
708 # endif /* O_NONBLOCK */
709 (void)on; /* quiet unused warning */
710 #else /* !SYS_WINNT above */
712 * Make the socket non-blocking. We'll wait with select()
713 * Windows: ioctlsocket(FIONBIO)
715 on = 1;
716 err = ioctlsocket(sockfd, FIONBIO, &on);
717 if (SOCKET_ERROR == err) {
718 msyslog(LOG_ERR, "ioctlsocket(FIONBIO) fails: %m");
719 resolver_exit(1);
721 #endif /* SYS_WINNT */
723 err = connect(sockfd, addr->ai_addr, addr->ai_addrlen);
724 if (SOCKET_ERROR == err) {
725 msyslog(LOG_ERR, "openntp: connect() failed: %m");
726 resolver_exit(1);
729 freeaddrinfo(addr);
734 * request - send a configuration request to the server, wait for a response
736 static int
737 request(
738 struct conf_peer *conf
741 struct sock_timeval tvout;
742 struct req_pkt reqpkt;
743 size_t req_len;
744 size_t total_len; /* req_len plus keyid & digest */
745 fd_set fdset;
746 l_fp ts;
747 char * pch;
748 char * pchEnd;
749 l_fp * pts;
750 keyid_t *pkeyid;
751 int n;
752 #ifdef SYS_WINNT
753 HANDLE hReadWriteEvent = NULL;
754 BOOL ret;
755 DWORD NumberOfBytesWritten, NumberOfBytesRead, dwWait;
756 OVERLAPPED overlap;
757 #endif /* SYS_WINNT */
759 checkparent(); /* make sure our guy is still running */
761 if (sockfd == INVALID_SOCKET)
762 openntp();
764 #ifdef SYS_WINNT
765 hReadWriteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
766 #endif /* SYS_WINNT */
769 * Try to clear out any previously received traffic so it
770 * doesn't fool us. Note the socket is nonblocking.
772 tvout.tv_sec = 0;
773 tvout.tv_usec = 0;
774 FD_ZERO(&fdset);
775 FD_SET(sockfd, &fdset);
776 while (select(sockfd + 1, &fdset, (fd_set *)0, (fd_set *)0, &tvout) >
777 0) {
778 recv(sockfd, (char *)&reqpkt, sizeof(reqpkt), 0);
779 FD_ZERO(&fdset);
780 FD_SET(sockfd, &fdset);
784 * Make up a request packet with the configuration info
786 memset(&reqpkt, 0, sizeof(reqpkt));
788 reqpkt.rm_vn_mode = RM_VN_MODE(0, 0, 0);
789 reqpkt.auth_seq = AUTH_SEQ(1, 0); /* authenticated, no seq */
790 reqpkt.implementation = IMPL_XNTPD; /* local implementation */
791 reqpkt.request = REQ_CONFIG; /* configure a new peer */
792 reqpkt.err_nitems = ERR_NITEMS(0, 1); /* one item */
793 reqpkt.mbz_itemsize = MBZ_ITEMSIZE(sizeof(*conf));
794 /* Make sure mbz_itemsize <= sizeof reqpkt.data */
795 if (sizeof(*conf) > sizeof(reqpkt.data)) {
796 msyslog(LOG_ERR,
797 "Bletch: conf_peer is too big for reqpkt.data!");
798 resolver_exit(1);
800 memcpy(reqpkt.data, conf, sizeof(*conf));
802 if (sys_authenticate && req_hashlen > 16) {
803 pch = reqpkt.data;
804 /* 32-bit alignment */
805 pch += (sizeof(*conf) + 3) & ~3;
806 pts = (void *)pch;
807 pkeyid = (void *)(pts + 1);
808 pchEnd = (void *)pkeyid;
809 req_len = pchEnd - (char *)&reqpkt;
810 pchEnd = (void *)(pkeyid + 1);
811 pchEnd += req_hashlen;
812 total_len = pchEnd - (char *)&reqpkt;
813 if (total_len > sizeof(reqpkt)) {
814 msyslog(LOG_ERR,
815 "intres total_len %u limit is %u (%u octet digest)\n",
816 total_len, sizeof(reqpkt),
817 req_hashlen);
818 resolver_exit(1);
820 } else {
821 pts = &reqpkt.tstamp;
822 pkeyid = &reqpkt.keyid;
823 req_len = REQ_LEN_NOMAC;
826 *pkeyid = htonl(req_keyid);
827 get_systime(&ts);
828 L_ADDUF(&ts, SKEWTIME);
829 HTONL_FP(&ts, pts);
830 if (sys_authenticate) {
831 n = authencrypt(req_keyid, (void *)&reqpkt, req_len);
832 if ((size_t)n != req_hashlen + sizeof(reqpkt.keyid)) {
833 msyslog(LOG_ERR,
834 "intres maclen %d expected %u\n",
835 n, req_hashlen + sizeof(reqpkt.keyid));
836 resolver_exit(1);
838 req_len += n;
842 * Done. Send it.
844 #ifndef SYS_WINNT
845 n = send(sockfd, (char *)&reqpkt, req_len, 0);
846 if (n < 0) {
847 msyslog(LOG_ERR, "send to NTP server failed: %m");
848 return 0; /* maybe should exit */
850 #else
851 /* In the NT world, documentation seems to indicate that there
852 * exist _write and _read routines that can be used to do blocking
853 * I/O on sockets. Problem is these routines require a socket
854 * handle obtained through the _open_osf_handle C run-time API
855 * of which there is no explanation in the documentation. We need
856 * nonblocking write's and read's anyway for our purpose here.
857 * We're therefore forced to deviate a little bit from the Unix
858 * model here and use the ReadFile and WriteFile Win32 I/O API's
859 * on the socket
861 overlap.Offset = overlap.OffsetHigh = (DWORD)0;
862 overlap.hEvent = hReadWriteEvent;
863 ret = WriteFile((HANDLE)sockfd, (char *)&reqpkt, req_len,
864 NULL, (LPOVERLAPPED)&overlap);
865 if ((ret == FALSE) && (GetLastError() != ERROR_IO_PENDING)) {
866 msyslog(LOG_ERR, "send to NTP server failed: %m");
867 return 0;
869 dwWait = WaitForSingleObject(hReadWriteEvent, (DWORD) TIMEOUT_SEC * 1000);
870 if ((dwWait == WAIT_FAILED) || (dwWait == WAIT_TIMEOUT)) {
871 if (dwWait == WAIT_FAILED)
872 msyslog(LOG_ERR, "WaitForSingleObject failed: %m");
873 return 0;
875 if (!GetOverlappedResult((HANDLE)sockfd, (LPOVERLAPPED)&overlap,
876 (LPDWORD)&NumberOfBytesWritten, FALSE)) {
877 msyslog(LOG_ERR, "GetOverlappedResult for WriteFile fails: %m");
878 return 0;
880 #endif /* SYS_WINNT */
884 * Wait for a response. A weakness of the mode 7 protocol used
885 * is that there is no way to associate a response with a
886 * particular request, i.e. the response to this configuration
887 * request is indistinguishable from that to any other. I should
888 * fix this some day. In any event, the time out is fairly
889 * pessimistic to make sure that if an answer is coming back
890 * at all, we get it.
892 for (;;) {
893 FD_ZERO(&fdset);
894 FD_SET(sockfd, &fdset);
895 tvout.tv_sec = TIMEOUT_SEC;
896 tvout.tv_usec = TIMEOUT_USEC;
898 n = select(sockfd + 1, &fdset, (fd_set *)0,
899 (fd_set *)0, &tvout);
901 if (n < 0) {
902 if (errno != EINTR)
903 msyslog(LOG_ERR, "select() fails: %m");
904 return 0;
905 } else if (n == 0) {
906 #ifdef DEBUG
907 if (debug)
908 msyslog(LOG_INFO, "ntp_intres select() returned 0.");
909 #endif
910 return 0;
913 #ifndef SYS_WINNT
914 n = recv(sockfd, (char *)&reqpkt, sizeof(reqpkt), 0);
915 if (n <= 0) {
916 if (n < 0) {
917 msyslog(LOG_ERR, "recv() fails: %m");
918 return 0;
920 continue;
922 #else /* Overlapped I/O used on non-blocking sockets on Windows NT */
923 ret = ReadFile((HANDLE)sockfd, (char *)&reqpkt, sizeof(reqpkt),
924 NULL, (LPOVERLAPPED)&overlap);
925 if ((ret == FALSE) && (GetLastError() != ERROR_IO_PENDING)) {
926 msyslog(LOG_ERR, "ReadFile() fails: %m");
927 return 0;
929 dwWait = WaitForSingleObject(hReadWriteEvent, (DWORD) TIMEOUT_SEC * 1000);
930 if ((dwWait == WAIT_FAILED) || (dwWait == WAIT_TIMEOUT)) {
931 if (dwWait == WAIT_FAILED) {
932 msyslog(LOG_ERR, "WaitForSingleObject for ReadFile fails: %m");
933 return 0;
935 continue;
937 if (!GetOverlappedResult((HANDLE)sockfd, (LPOVERLAPPED)&overlap,
938 (LPDWORD)&NumberOfBytesRead, FALSE)) {
939 msyslog(LOG_ERR, "GetOverlappedResult fails: %m");
940 return 0;
942 n = NumberOfBytesRead;
943 #endif /* SYS_WINNT */
946 * Got one. Check through to make sure it is what
947 * we expect.
949 if (n < RESP_HEADER_SIZE) {
950 msyslog(LOG_ERR, "received runt response (%d octets)",
952 continue;
955 if (!ISRESPONSE(reqpkt.rm_vn_mode)) {
956 #ifdef DEBUG
957 if (debug > 1)
958 msyslog(LOG_INFO, "received non-response packet");
959 #endif
960 continue;
963 if (ISMORE(reqpkt.rm_vn_mode)) {
964 #ifdef DEBUG
965 if (debug > 1)
966 msyslog(LOG_INFO, "received fragmented packet");
967 #endif
968 continue;
971 if ( ( (INFO_VERSION(reqpkt.rm_vn_mode) < 2)
972 || (INFO_VERSION(reqpkt.rm_vn_mode) > NTP_VERSION))
973 || INFO_MODE(reqpkt.rm_vn_mode) != MODE_PRIVATE) {
974 #ifdef DEBUG
975 if (debug > 1)
976 msyslog(LOG_INFO,
977 "version (%d/%d) or mode (%d/%d) incorrect",
978 INFO_VERSION(reqpkt.rm_vn_mode),
979 NTP_VERSION,
980 INFO_MODE(reqpkt.rm_vn_mode),
981 MODE_PRIVATE);
982 #endif
983 continue;
986 if (INFO_SEQ(reqpkt.auth_seq) != 0) {
987 #ifdef DEBUG
988 if (debug > 1)
989 msyslog(LOG_INFO,
990 "nonzero sequence number (%d)",
991 INFO_SEQ(reqpkt.auth_seq));
992 #endif
993 continue;
996 if (reqpkt.implementation != IMPL_XNTPD ||
997 reqpkt.request != REQ_CONFIG) {
998 #ifdef DEBUG
999 if (debug > 1)
1000 msyslog(LOG_INFO,
1001 "implementation (%d) or request (%d) incorrect",
1002 reqpkt.implementation, reqpkt.request);
1003 #endif
1004 continue;
1007 if (INFO_NITEMS(reqpkt.err_nitems) != 0 ||
1008 INFO_MBZ(reqpkt.mbz_itemsize) != 0 ||
1009 INFO_ITEMSIZE(reqpkt.mbz_itemsize) != 0) {
1010 #ifdef DEBUG
1011 if (debug > 1)
1012 msyslog(LOG_INFO,
1013 "nitems (%d) mbz (%d) or itemsize (%d) nonzero",
1014 INFO_NITEMS(reqpkt.err_nitems),
1015 INFO_MBZ(reqpkt.mbz_itemsize),
1016 INFO_ITEMSIZE(reqpkt.mbz_itemsize));
1017 #endif
1018 continue;
1021 n = INFO_ERR(reqpkt.err_nitems);
1022 switch (n) {
1023 case INFO_OKAY:
1024 /* success */
1025 return 1;
1027 case INFO_ERR_NODATA:
1029 * newpeer() refused duplicate association, no
1030 * point in retrying so call it success.
1032 return 1;
1034 case INFO_ERR_IMPL:
1035 msyslog(LOG_ERR,
1036 "ntp_intres.request: implementation mismatch");
1037 return 0;
1039 case INFO_ERR_REQ:
1040 msyslog(LOG_ERR,
1041 "ntp_intres.request: request unknown");
1042 return 0;
1044 case INFO_ERR_FMT:
1045 msyslog(LOG_ERR,
1046 "ntp_intres.request: format error");
1047 return 0;
1049 case INFO_ERR_AUTH:
1050 msyslog(LOG_ERR,
1051 "ntp_intres.request: permission denied");
1052 return 0;
1054 default:
1055 msyslog(LOG_ERR,
1056 "ntp_intres.request: unknown error code %d", n);
1057 return 0;
1064 * nexttoken - return the next token from a line
1066 static char *
1067 nexttoken(
1068 char **lptr
1071 register char *cp;
1072 register char *tstart;
1074 cp = *lptr;
1077 * Skip leading white space
1079 while (*cp == ' ' || *cp == '\t')
1080 cp++;
1083 * If this is the end of the line, return nothing.
1085 if (*cp == '\n' || *cp == '\0') {
1086 *lptr = cp;
1087 return NULL;
1091 * Must be the start of a token. Record the pointer and look
1092 * for the end.
1094 tstart = cp++;
1095 while (*cp != ' ' && *cp != '\t' && *cp != '\n' && *cp != '\0')
1096 cp++;
1099 * Terminate the token with a \0. If this isn't the end of the
1100 * line, space to the next character.
1102 if (*cp == '\n' || *cp == '\0')
1103 *cp = '\0';
1104 else
1105 *cp++ = '\0';
1107 *lptr = cp;
1108 return tstart;
1113 * readconf - read the configuration information out of the file we
1114 * were passed. Note that since the file is supposed to be
1115 * machine generated, we bail out at the first sign of trouble.
1117 static void
1118 readconf(
1119 FILE *fp,
1120 char *name
1123 register int i;
1124 char *token[NUMTOK];
1125 u_long intval[NUMTOK];
1126 u_int flags;
1127 char buf[MAXLINESIZE];
1128 char *bp;
1130 while (fgets(buf, MAXLINESIZE, fp) != NULL) {
1132 bp = buf;
1133 for (i = 0; i < NUMTOK; i++) {
1134 if ((token[i] = nexttoken(&bp)) == NULL) {
1135 msyslog(LOG_ERR,
1136 "tokenizing error in file `%s', quitting",
1137 name);
1138 resolver_exit(1);
1142 for (i = 1; i < NUMTOK - 1; i++) {
1143 if (!atouint(token[i], &intval[i])) {
1144 msyslog(LOG_ERR,
1145 "format error for integer token `%s', file `%s', quitting",
1146 token[i], name);
1147 resolver_exit(1);
1151 #if 0 /* paranoid checking - these are done in newpeer() */
1152 if (intval[TOK_HMODE] != MODE_ACTIVE &&
1153 intval[TOK_HMODE] != MODE_CLIENT &&
1154 intval[TOK_HMODE] != MODE_BROADCAST) {
1155 msyslog(LOG_ERR, "invalid mode (%ld) in file %s",
1156 intval[TOK_HMODE], name);
1157 resolver_exit(1);
1160 if (intval[TOK_VERSION] > NTP_VERSION ||
1161 intval[TOK_VERSION] < NTP_OLDVERSION) {
1162 msyslog(LOG_ERR, "invalid version (%ld) in file %s",
1163 intval[TOK_VERSION], name);
1164 resolver_exit(1);
1166 if (intval[TOK_MINPOLL] < ntp_minpoll ||
1167 intval[TOK_MINPOLL] > NTP_MAXPOLL) {
1169 msyslog(LOG_ERR, "invalid MINPOLL value (%ld) in file %s",
1170 intval[TOK_MINPOLL], name);
1171 resolver_exit(1);
1174 if (intval[TOK_MAXPOLL] < ntp_minpoll ||
1175 intval[TOK_MAXPOLL] > NTP_MAXPOLL) {
1176 msyslog(LOG_ERR, "invalid MAXPOLL value (%ld) in file %s",
1177 intval[TOK_MAXPOLL], name);
1178 resolver_exit(1);
1181 if ((intval[TOK_FLAGS] & ~(FLAG_PREFER | FLAG_NOSELECT |
1182 FLAG_BURST | FLAG_IBURST | FLAG_SKEY)) != 0) {
1183 msyslog(LOG_ERR, "invalid flags (%ld) in file %s",
1184 intval[TOK_FLAGS], name);
1185 resolver_exit(1);
1187 #endif /* end paranoid checking */
1189 flags = 0;
1190 if (intval[TOK_FLAGS] & FLAG_PREFER)
1191 flags |= CONF_FLAG_PREFER;
1192 if (intval[TOK_FLAGS] & FLAG_NOSELECT)
1193 flags |= CONF_FLAG_NOSELECT;
1194 if (intval[TOK_FLAGS] & FLAG_BURST)
1195 flags |= CONF_FLAG_BURST;
1196 if (intval[TOK_FLAGS] & FLAG_IBURST)
1197 flags |= CONF_FLAG_IBURST;
1199 #ifdef OPENSSL
1200 if (intval[TOK_FLAGS] & FLAG_SKEY)
1201 flags |= CONF_FLAG_SKEY;
1202 #endif /* OPENSSL */
1205 * This is as good as we can check it. Add it in.
1207 addentry(token[TOK_HOSTNAME],
1208 (int)intval[TOK_NEEDED], (int)intval[TOK_TYPE],
1209 (int)intval[TOK_HMODE], (int)intval[TOK_VERSION],
1210 (int)intval[TOK_MINPOLL], (int)intval[TOK_MAXPOLL],
1211 flags, (int)intval[TOK_TTL],
1212 intval[TOK_KEYID], token[TOK_KEYSTR]);
1218 * doconfigure - attempt to resolve names and configure the server
1220 static void
1221 doconfigure(
1222 int dores
1225 register struct conf_entry *ce;
1227 #ifdef DEBUG
1228 if (debug > 1)
1229 msyslog(LOG_INFO, "Running doconfigure %s DNS",
1230 dores ? "with" : "without" );
1231 #endif
1233 #if defined(HAVE_RES_INIT) || defined(HAVE___RES_INIT)
1234 if (dores) /* Reload /etc/resolv.conf - bug 1226 */
1235 res_init();
1236 #endif
1237 ce = confentries;
1238 while (ce != NULL) {
1239 #ifdef DEBUG
1240 if (debug > 1)
1241 msyslog(LOG_INFO,
1242 "doconfigure: <%s> has peeraddr %s",
1243 ce->ce_name, stoa(&ce->peer_store));
1244 #endif
1245 if (dores && SOCK_UNSPEC(&ce->peer_store)) {
1246 if (!findhostaddr(ce)) {
1247 #ifndef IGNORE_DNS_ERRORS
1248 msyslog(LOG_ERR,
1249 "couldn't resolve `%s', giving up on it",
1250 ce->ce_name);
1251 ce = removeentry(ce);
1252 continue;
1253 #endif
1254 } else if (!SOCK_UNSPEC(&ce->peer_store))
1255 msyslog(LOG_INFO,
1256 "DNS %s -> %s", ce->ce_name,
1257 stoa(&ce->peer_store));
1260 if (!SOCK_UNSPEC(&ce->peer_store)) {
1261 if (request(&ce->ce_config)) {
1262 ce = removeentry(ce);
1263 continue;
1266 * Failed case. Should bump counter and give
1267 * up.
1269 #ifdef DEBUG
1270 if (debug > 1) {
1271 msyslog(LOG_INFO,
1272 "doconfigure: request() FAILED, maybe next time.");
1274 #endif
1276 ce = ce->ce_next;
1280 #else /* NO_INTRES follows */
1281 int ntp_intres_nonempty_compilation_unit;
1282 #endif