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.
8 * ntpres - process configuration entries which require use of the resolver
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.
23 #include "ntp_machine.h"
26 #include "ntp_request.h"
27 #include "ntp_stdlib.h"
28 #include "ntp_syslog.h"
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
39 #ifdef HAVE_SYS_PARAM_H
40 # include <sys/param.h> /* MAXHOSTNAMELEN (often) */
43 #define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
46 * Each item we are to resolve and configure gets one of these
47 * structures defined for it.
50 struct conf_entry *ce_next;
51 char *ce_name; /* name we are trying to resolve */
52 struct conf_peer ce_config; /* configuration info for peer */
53 struct sockaddr_storage peer_store; /* address info for both fams */
55 #define ce_peeraddr ce_config.peeraddr
56 #define ce_peeraddr6 ce_config.peeraddr6
57 #define ce_hmode ce_config.hmode
58 #define ce_version ce_config.version
59 #define ce_minpoll ce_config.minpoll
60 #define ce_maxpoll ce_config.maxpoll
61 #define ce_flags ce_config.flags
62 #define ce_ttl ce_config.ttl
63 #define ce_keyid ce_config.keyid
64 #define ce_keystr ce_config.keystr
67 * confentries is a pointer to the list of configuration entries
70 static struct conf_entry *confentries = NULL;
73 * We take an interrupt every thirty seconds, at which time we decrement
74 * config_timer and resolve_timer. The former is set to 2, so we retry
75 * unsucessful reconfigurations every minute. The latter is set to
76 * an exponentially increasing value which starts at 2 and increases to
77 * 32. When this expires we retry failed name resolutions.
79 * We sleep SLEEPTIME seconds before doing anything, to give the server
80 * time to arrange itself.
88 static volatile int config_timer = 0;
89 static volatile int resolve_timer = 0;
91 static int resolve_value; /* next value of resolve timer */
96 #define LOCALHOST 0x7f000001 /* 127.0.0.1, in hex, of course */
97 #define SKEWTIME 0x08000000 /* 0.03125 seconds as a l_fp fraction */
100 * Select time out. Set to 2 seconds. The server is on the local machine,
103 #define TIMEOUT_SEC 2
104 #define TIMEOUT_USEC 0
108 * Input processing. The data on each line in the configuration file
109 * is supposed to consist of entries in the following order
111 #define TOK_HOSTNAME 0
113 #define TOK_VERSION 2
114 #define TOK_MINPOLL 3
115 #define TOK_MAXPOLL 4
122 #define MAXLINESIZE 512
126 * File descriptor for ntp request code.
128 static SOCKET sockfd = INVALID_SOCKET; /* NT uses SOCKET */
130 /* stuff to be filled in by caller */
132 keyid_t req_keyid; /* request keyid */
133 char *req_file; /* name of the file with configuration info */
135 /* end stuff to be filled in */
138 static RETSIGTYPE bong P((int));
139 static void checkparent P((void));
140 static void removeentry P((struct conf_entry *));
141 static void addentry P((char *, int, int, int, int, u_int,
142 int, keyid_t, char *));
143 static int findhostaddr P((struct conf_entry *));
144 static void openntp P((void));
145 static int request P((struct conf_peer *));
146 static char * nexttoken P((char **));
147 static void readconf P((FILE *, char *));
148 static void doconfigure P((int));
150 struct ntp_res_t_pkt { /* Tagged packet: */
151 void *tag; /* For the caller */
152 u_int32 paddr; /* IP to look up, or 0 */
153 char name[MAXHOSTNAMELEN]; /* Name to look up (if 1st byte is not 0) */
156 struct ntp_res_c_pkt { /* Control packet: */
157 char name[MAXHOSTNAMELEN];
166 u_char keystr[MAXFILENAME];
171 * ntp_res_recv: Process an answer from the resolver
178 We have data ready on our descriptor.
179 It may be an EOF, meaning the resolver process went away.
180 Otherwise, it will be an "answer".
188 * req_key(???), req_keyid, req_file valid
196 #ifdef HAVE_SIGSUSPEND
200 #endif /* HAVE_SIGSUSPEND */
204 msyslog(LOG_INFO, "NTP_INTRES running");
208 /* check out auth stuff */
209 if (sys_authenticate) {
210 if (!authistrusted(req_keyid)) {
211 msyslog(LOG_ERR, "invalid request keyid %08x",
218 * Read the configuration info
219 * {this is bogus, since we are forked, but it is easier
220 * to keep this code - gdt}
222 if ((in = fopen(req_file, "r")) == NULL) {
223 msyslog(LOG_ERR, "can't open configuration file %s: %m",
227 readconf(in, req_file);
231 (void) unlink(req_file);
234 * Sleep a little to make sure the server is completely up
240 * Make a first cut at resolving the bunch
243 if (confentries == NULL) {
244 #if defined SYS_WINNT
245 ExitThread(0); /* Don't want to kill whole NT process */
247 exit(0); /* done that quick */
252 * Here we've got some problem children. Set up the timer
255 resolve_value = resolve_timer = MINRESOLVE;
256 config_timer = CONFIG_TIME;
258 (void) signal_no_reset(SIGALRM, bong);
260 #endif /* SYS_WINNT */
263 if (confentries == NULL)
268 if (resolve_timer == 0) {
269 if (resolve_value < MAXRESOLVE)
271 resolve_timer = resolve_value;
274 msyslog(LOG_INFO, "resolve_timer: 0->%d", resolve_timer);
276 config_timer = CONFIG_TIME;
279 } else if (config_timer == 0) {
280 config_timer = CONFIG_TIME;
283 msyslog(LOG_INFO, "config_timer: 0->%d", config_timer);
290 * There is a race in here. Is okay, though, since
291 * all it does is delay things by 30 seconds.
293 # ifdef HAVE_SIGSUSPEND
297 # endif /* HAVE_SIGSUSPEND */
299 if (config_timer > 0)
301 if (resolve_timer > 0)
304 #endif /* SYS_WINNT */
311 * bong - service and reschedule an alarm() interrupt
318 if (config_timer > 0)
320 if (resolve_timer > 0)
324 #endif /* SYS_WINNT */
327 * checkparent - see if our parent process is still running
329 * No need to worry in the Windows NT environment whether the
330 * main thread is still running, because if it goes
331 * down it takes the whole process down with it (in
332 * which case we won't be running this thread either)
333 * Turn function into NOP;
339 #if !defined (SYS_WINNT) && !defined (SYS_VXWORKS)
342 * If our parent (the server) has died we will have been
343 * inherited by init. If so, exit.
345 if (getppid() == 1) {
346 msyslog(LOG_INFO, "parent died before we finished, exiting");
349 #endif /* SYS_WINNT && SYS_VXWORKS*/
355 * removeentry - we are done with an entry, remove it from the list
359 struct conf_entry *entry
362 register struct conf_entry *ce;
366 confentries = ce->ce_next;
371 if (ce->ce_next == entry) {
372 ce->ce_next = entry->ce_next;
381 * addentry - add an entry to the configuration list
397 register struct conf_entry *ce;
403 "intres: <%s> %d %d %d %d %x %d %x %s\n", name,
404 mode, version, minpoll, maxpoll, flags, ttl, keyid,
407 len = strlen(name) + 1;
408 cp = (char *)emalloc(len);
409 memmove(cp, name, len);
411 ce = (struct conf_entry *)emalloc(sizeof(struct conf_entry));
414 ce->ce_peeraddr6 = in6addr_any;
415 ANYSOCK(&ce->peer_store);
416 ce->ce_hmode = (u_char)mode;
417 ce->ce_version = (u_char)version;
418 ce->ce_minpoll = (u_char)minpoll;
419 ce->ce_maxpoll = (u_char)maxpoll;
420 ce->ce_flags = (u_char)flags;
421 ce->ce_ttl = (u_char)ttl;
422 ce->ce_keyid = keyid;
423 strncpy((char *)ce->ce_keystr, keystr, MAXFILENAME);
426 if (confentries == NULL) {
429 register struct conf_entry *cep;
431 for (cep = confentries; cep->ce_next != NULL;
440 * findhostaddr - resolve a host name into an address (Or vice-versa)
442 * Given one of {ce_peeraddr,ce_name}, find the other one.
443 * It returns 1 for "success" and 0 for an uncorrectable failure.
444 * Note that "success" includes try again errors. You can tell that you
445 * got a "try again" since {ce_peeraddr,ce_name} will still be zero.
449 struct conf_entry *entry
452 struct addrinfo *addr;
455 checkparent(); /* make sure our guy is still running */
457 if (entry->ce_name != NULL && SOCKNUL(&entry->peer_store)) {
459 msyslog(LOG_ERR, "findhostaddr: both ce_name and ce_peeraddr are defined...");
463 if (entry->ce_name == NULL && !SOCKNUL(&entry->peer_store)) {
464 msyslog(LOG_ERR, "findhostaddr: both ce_name and ce_peeraddr are undefined!");
468 if (entry->ce_name) {
471 msyslog(LOG_INFO, "findhostaddr: Resolving <%s>",
474 error = getaddrinfo(entry->ce_name, NULL, NULL, &addr);
476 entry->peer_store = *((struct sockaddr_storage*)(addr->ai_addr));
477 if (entry->peer_store.ss_family == AF_INET) {
479 GET_INADDR(entry->peer_store);
480 entry->ce_config.v6_flag = 0;
482 entry->ce_peeraddr6 =
483 GET_INADDR6(entry->peer_store);
484 entry->ce_config.v6_flag = 1;
490 msyslog(LOG_INFO, "findhostaddr: Resolving %s>",
491 stoa(&entry->peer_store));
493 entry->ce_name = emalloc(MAXHOSTNAMELEN);
494 error = getnameinfo((const struct sockaddr *)&entry->peer_store,
495 SOCKLEN(&entry->peer_store),
496 (char *)&entry->ce_name, MAXHOSTNAMELEN,
502 * If the resolver is in use, see if the failure is
503 * temporary. If so, return success.
505 if (h_errno == TRY_AGAIN)
510 if (entry->ce_name) {
513 msyslog(LOG_INFO, "findhostaddr: name resolved.");
518 msyslog(LOG_INFO, "findhostaddr: address resolved.");
527 * openntp - open a socket to the ntp server
532 struct addrinfo hints;
533 struct addrinfo *addrResult;
538 memset(&hints, 0, sizeof(hints));
539 hints.ai_family = AF_UNSPEC;
540 hints.ai_socktype = SOCK_DGRAM;
541 if (getaddrinfo(NULL, "ntp", &hints, &addrResult)!=0) {
542 msyslog(LOG_ERR, "getaddrinfo failed: %m");
545 sockfd = socket(addrResult->ai_family, addrResult->ai_socktype, 0);
548 msyslog(LOG_ERR, "socket() failed: %m");
553 * Make the socket non-blocking. We'll wait with select()
556 #if defined(O_NONBLOCK)
557 if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == -1) {
558 msyslog(LOG_ERR, "fcntl(O_NONBLOCK) failed: %m");
563 if (fcntl(sockfd, F_SETFL, FNDELAY) == -1) {
564 msyslog(LOG_ERR, "fcntl(FNDELAY) failed: %m");
568 # include "Bletch: NEED NON BLOCKING IO"
569 #endif /* FNDDELAY */
570 #endif /* O_NONBLOCK */
571 #else /* SYS_WINNT */
574 if (ioctlsocket(sockfd,FIONBIO,(u_long *) &on) == SOCKET_ERROR) {
575 msyslog(LOG_ERR, "ioctlsocket(FIONBIO) fails: %m");
576 exit(1); /* Windows NT - set socket in non-blocking mode */
579 #endif /* SYS_WINNT */
580 if (connect(sockfd, addrResult->ai_addr, addrResult->ai_addrlen) == -1) {
581 msyslog(LOG_ERR, "openntp: connect() failed: %m");
584 freeaddrinfo(addrResult);
589 * request - send a configuration request to the server, wait for a response
593 struct conf_peer *conf
597 struct timeval tvout;
598 struct req_pkt reqpkt;
602 HANDLE hReadWriteEvent = NULL;
604 DWORD NumberOfBytesWritten, NumberOfBytesRead, dwWait;
606 #endif /* SYS_WINNT */
608 checkparent(); /* make sure our guy is still running */
614 hReadWriteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
615 #endif /* SYS_WINNT */
618 * Try to clear out any previously received traffic so it
619 * doesn't fool us. Note the socket is nonblocking.
624 FD_SET(sockfd, &fdset);
625 while (select(sockfd + 1, &fdset, (fd_set *)0, (fd_set *)0, &tvout) >
627 recv(sockfd, (char *)&reqpkt, REQ_LEN_MAC, 0);
629 FD_SET(sockfd, &fdset);
633 * Make up a request packet with the configuration info
635 memset((char *)&reqpkt, 0, sizeof(reqpkt));
637 reqpkt.rm_vn_mode = RM_VN_MODE(0, 0, 0);
638 reqpkt.auth_seq = AUTH_SEQ(1, 0); /* authenticated, no seq */
639 reqpkt.implementation = IMPL_XNTPD; /* local implementation */
640 reqpkt.request = REQ_CONFIG; /* configure a new peer */
641 reqpkt.err_nitems = ERR_NITEMS(0, 1); /* one item */
642 reqpkt.mbz_itemsize = MBZ_ITEMSIZE(sizeof(struct conf_peer));
643 /* Make sure mbz_itemsize <= sizeof reqpkt.data */
644 if (sizeof(struct conf_peer) > sizeof (reqpkt.data)) {
645 msyslog(LOG_ERR, "Bletch: conf_peer is too big for reqpkt.data!");
648 memmove(reqpkt.data, (char *)conf, sizeof(struct conf_peer));
649 reqpkt.keyid = htonl(req_keyid);
652 L_ADDUF(&ts, SKEWTIME);
653 HTONL_FP(&ts, &reqpkt.tstamp);
655 if (sys_authenticate)
656 n = authencrypt(req_keyid, (u_int32 *)&reqpkt, REQ_LEN_NOMAC);
662 n = send(sockfd, (char *)&reqpkt, (unsigned)(REQ_LEN_NOMAC + n), 0);
664 msyslog(LOG_ERR, "send to NTP server failed: %m");
665 return 0; /* maybe should exit */
668 /* In the NT world, documentation seems to indicate that there
669 * exist _write and _read routines that can be used to do blocking
670 * I/O on sockets. Problem is these routines require a socket
671 * handle obtained through the _open_osf_handle C run-time API
672 * of which there is no explanation in the documentation. We need
673 * nonblocking write's and read's anyway for our purpose here.
674 * We're therefore forced to deviate a little bit from the Unix
675 * model here and use the ReadFile and WriteFile Win32 I/O API's
678 overlap.Offset = overlap.OffsetHigh = (DWORD)0;
679 overlap.hEvent = hReadWriteEvent;
680 ret = WriteFile((HANDLE)sockfd, (char *)&reqpkt, REQ_LEN_NOMAC + n,
681 (LPDWORD)&NumberOfBytesWritten, (LPOVERLAPPED)&overlap);
682 if ((ret == FALSE) && (GetLastError() != ERROR_IO_PENDING)) {
683 msyslog(LOG_ERR, "send to NTP server failed: %m");
686 dwWait = WaitForSingleObject(hReadWriteEvent, (DWORD) TIMEOUT_SEC * 1000);
687 if ((dwWait == WAIT_FAILED) || (dwWait == WAIT_TIMEOUT)) {
688 if (dwWait == WAIT_FAILED)
689 msyslog(LOG_ERR, "WaitForSingleObject failed: %m");
692 #endif /* SYS_WINNT */
696 * Wait for a response. A weakness of the mode 7 protocol used
697 * is that there is no way to associate a response with a
698 * particular request, i.e. the response to this configuration
699 * request is indistinguishable from that to any other. I should
700 * fix this some day. In any event, the time out is fairly
701 * pessimistic to make sure that if an answer is coming back
706 FD_SET(sockfd, &fdset);
707 tvout.tv_sec = TIMEOUT_SEC;
708 tvout.tv_usec = TIMEOUT_USEC;
710 n = select(sockfd + 1, &fdset, (fd_set *)0,
711 (fd_set *)0, &tvout);
716 msyslog(LOG_ERR, "select() fails: %m");
722 msyslog(LOG_INFO, "select() returned 0.");
727 n = recv(sockfd, (char *)&reqpkt, REQ_LEN_MAC, 0);
730 msyslog(LOG_ERR, "recv() fails: %m");
735 #else /* Overlapped I/O used on non-blocking sockets on Windows NT */
736 ret = ReadFile((HANDLE)sockfd, (char *)&reqpkt, (DWORD)REQ_LEN_MAC,
737 (LPDWORD)&NumberOfBytesRead, (LPOVERLAPPED)&overlap);
738 if ((ret == FALSE) && (GetLastError() != ERROR_IO_PENDING)) {
739 msyslog(LOG_ERR, "ReadFile() fails: %m");
742 dwWait = WaitForSingleObject(hReadWriteEvent, (DWORD) TIMEOUT_SEC * 1000);
743 if ((dwWait == WAIT_FAILED) || (dwWait == WAIT_TIMEOUT)) {
744 if (dwWait == WAIT_FAILED) {
745 msyslog(LOG_ERR, "WaitForSingleObject fails: %m");
750 n = NumberOfBytesRead;
751 #endif /* SYS_WINNT */
754 * Got one. Check through to make sure it is what
757 if (n < RESP_HEADER_SIZE) {
758 msyslog(LOG_ERR, "received runt response (%d octets)",
763 if (!ISRESPONSE(reqpkt.rm_vn_mode)) {
766 msyslog(LOG_INFO, "received non-response packet");
771 if (ISMORE(reqpkt.rm_vn_mode)) {
774 msyslog(LOG_INFO, "received fragmented packet");
779 if ( ( (INFO_VERSION(reqpkt.rm_vn_mode) < 2)
780 || (INFO_VERSION(reqpkt.rm_vn_mode) > NTP_VERSION))
781 || INFO_MODE(reqpkt.rm_vn_mode) != MODE_PRIVATE) {
785 "version (%d/%d) or mode (%d/%d) incorrect",
786 INFO_VERSION(reqpkt.rm_vn_mode),
788 INFO_MODE(reqpkt.rm_vn_mode),
794 if (INFO_SEQ(reqpkt.auth_seq) != 0) {
798 "nonzero sequence number (%d)",
799 INFO_SEQ(reqpkt.auth_seq));
804 if (reqpkt.implementation != IMPL_XNTPD ||
805 reqpkt.request != REQ_CONFIG) {
809 "implementation (%d) or request (%d) incorrect",
810 reqpkt.implementation, reqpkt.request);
815 if (INFO_NITEMS(reqpkt.err_nitems) != 0 ||
816 INFO_MBZ(reqpkt.mbz_itemsize) != 0 ||
817 INFO_ITEMSIZE(reqpkt.mbz_itemsize) != 0) {
821 "nitems (%d) mbz (%d) or itemsize (%d) nonzero",
822 INFO_NITEMS(reqpkt.err_nitems),
823 INFO_MBZ(reqpkt.mbz_itemsize),
824 INFO_ITEMSIZE(reqpkt.mbz_itemsize));
829 n = INFO_ERR(reqpkt.err_nitems);
837 "ntpd reports implementation mismatch!");
842 "ntpd says configuration request is unknown!");
847 "ntpd indicates a format error occurred!");
850 case INFO_ERR_NODATA:
852 "ntpd indicates no data available!");
857 "ntpd returns a permission denied error!");
862 "ntpd returns unknown error code %d!", n);
870 * nexttoken - return the next token from a line
878 register char *tstart;
883 * Skip leading white space
885 while (*cp == ' ' || *cp == '\t')
889 * If this is the end of the line, return nothing.
891 if (*cp == '\n' || *cp == '\0') {
897 * Must be the start of a token. Record the pointer and look
901 while (*cp != ' ' && *cp != '\t' && *cp != '\n' && *cp != '\0')
905 * Terminate the token with a \0. If this isn't the end of the
906 * line, space to the next character.
908 if (*cp == '\n' || *cp == '\0')
919 * readconf - read the configuration information out of the file we
920 * were passed. Note that since the file is supposed to be
921 * machine generated, we bail out at the first sign of trouble.
931 u_long intval[NUMTOK];
933 char buf[MAXLINESIZE];
936 while (fgets(buf, MAXLINESIZE, fp) != NULL) {
939 for (i = 0; i < NUMTOK; i++) {
940 if ((token[i] = nexttoken(&bp)) == NULL) {
942 "tokenizing error in file `%s', quitting",
948 for (i = 1; i < NUMTOK - 1; i++) {
949 if (!atouint(token[i], &intval[i])) {
951 "format error for integer token `%s', file `%s', quitting",
957 if (intval[TOK_HMODE] != MODE_ACTIVE &&
958 intval[TOK_HMODE] != MODE_CLIENT &&
959 intval[TOK_HMODE] != MODE_BROADCAST) {
960 msyslog(LOG_ERR, "invalid mode (%ld) in file %s",
961 intval[TOK_HMODE], name);
965 if (intval[TOK_VERSION] > NTP_VERSION ||
966 intval[TOK_VERSION] < NTP_OLDVERSION) {
967 msyslog(LOG_ERR, "invalid version (%ld) in file %s",
968 intval[TOK_VERSION], name);
971 if (intval[TOK_MINPOLL] < NTP_MINPOLL ||
972 intval[TOK_MINPOLL] > NTP_MAXPOLL) {
973 msyslog(LOG_ERR, "invalid MINPOLL value (%ld) in file %s",
974 intval[TOK_MINPOLL], name);
978 if (intval[TOK_MAXPOLL] < NTP_MINPOLL ||
979 intval[TOK_MAXPOLL] > NTP_MAXPOLL) {
980 msyslog(LOG_ERR, "invalid MAXPOLL value (%ld) in file %s",
981 intval[TOK_MAXPOLL], name);
985 if ((intval[TOK_FLAGS] & ~(FLAG_AUTHENABLE | FLAG_PREFER |
986 FLAG_NOSELECT | FLAG_BURST | FLAG_IBURST | FLAG_SKEY))
988 msyslog(LOG_ERR, "invalid flags (%ld) in file %s",
989 intval[TOK_FLAGS], name);
994 if (intval[TOK_FLAGS] & FLAG_AUTHENABLE)
995 flags |= CONF_FLAG_AUTHENABLE;
996 if (intval[TOK_FLAGS] & FLAG_PREFER)
997 flags |= CONF_FLAG_PREFER;
998 if (intval[TOK_FLAGS] & FLAG_NOSELECT)
999 flags |= CONF_FLAG_NOSELECT;
1000 if (intval[TOK_FLAGS] & FLAG_BURST)
1001 flags |= CONF_FLAG_BURST;
1002 if (intval[TOK_FLAGS] & FLAG_IBURST)
1003 flags |= CONF_FLAG_IBURST;
1004 if (intval[TOK_FLAGS] & FLAG_SKEY)
1005 flags |= CONF_FLAG_SKEY;
1008 * This is as good as we can check it. Add it in.
1010 addentry(token[TOK_HOSTNAME], (int)intval[TOK_HMODE],
1011 (int)intval[TOK_VERSION], (int)intval[TOK_MINPOLL],
1012 (int)intval[TOK_MAXPOLL], flags, (int)intval[TOK_TTL],
1013 intval[TOK_KEYID], token[TOK_KEYSTR]);
1019 * doconfigure - attempt to resolve names and configure the server
1026 register struct conf_entry *ce;
1027 register struct conf_entry *ceremove;
1030 while (ce != NULL) {
1034 "doconfigure: <%s> has peeraddr %s",
1035 ce->ce_name, stoa(&ce->peer_store));
1037 if (dores && !SOCKNUL(&(ce->peer_store))) {
1038 if (!findhostaddr(ce)) {
1040 "couldn't resolve `%s', giving up on it",
1043 ce = ceremove->ce_next;
1044 removeentry(ceremove);
1049 if (!SOCKNUL(&ce->peer_store)) {
1050 if (request(&ce->ce_config)) {
1052 ce = ceremove->ce_next;
1053 removeentry(ceremove);
1059 "doconfigure: request() FAILED, maybe next time.");