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.
25 #include "ntp_request.h"
26 #include "ntp_stdlib.h"
27 #include "ntp_syslog.h"
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
38 #ifdef HAVE_SYS_PARAM_H
39 # include <sys/param.h> /* MAXHOSTNAMELEN (often) */
42 #define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
45 * Each item we are to resolve and configure gets one of these
46 * structures defined for it.
49 struct conf_entry *ce_next;
50 char *ce_name; /* name we are trying to resolve */
51 struct conf_peer ce_config; /* configuration info for peer */
53 #define ce_peeraddr ce_config.peeraddr
54 #define ce_hmode ce_config.hmode
55 #define ce_version ce_config.version
56 #define ce_minpoll ce_config.minpoll
57 #define ce_maxpoll ce_config.maxpoll
58 #define ce_flags ce_config.flags
59 #define ce_ttl ce_config.ttl
60 #define ce_keyid ce_config.keyid
61 #define ce_keystr ce_config.keystr
64 * confentries is a pointer to the list of configuration entries
67 static struct conf_entry *confentries = NULL;
70 * We take an interrupt every thirty seconds, at which time we decrement
71 * config_timer and resolve_timer. The former is set to 2, so we retry
72 * unsucessful reconfigurations every minute. The latter is set to
73 * an exponentially increasing value which starts at 2 and increases to
74 * 32. When this expires we retry failed name resolutions.
76 * We sleep SLEEPTIME seconds before doing anything, to give the server
77 * time to arrange itself.
85 static volatile int config_timer = 0;
86 static volatile int resolve_timer = 0;
88 static int resolve_value; /* next value of resolve timer */
93 #define LOCALHOST 0x7f000001 /* 127.0.0.1, in hex, of course */
94 #define SKEWTIME 0x08000000 /* 0.03125 seconds as a l_fp fraction */
97 * Select time out. Set to 2 seconds. The server is on the local machine,
100 #define TIMEOUT_SEC 2
101 #define TIMEOUT_USEC 0
105 * Input processing. The data on each line in the configuration file
106 * is supposed to consist of entries in the following order
108 #define TOK_HOSTNAME 0
110 #define TOK_VERSION 2
111 #define TOK_MINPOLL 3
112 #define TOK_MAXPOLL 4
119 #define MAXLINESIZE 512
123 * File descriptor for ntp request code.
125 static int sockfd = -1;
128 /* stuff to be filled in by caller */
130 keyid_t req_keyid; /* request keyid */
131 char *req_file; /* name of the file with configuration info */
133 /* end stuff to be filled in */
136 static RETSIGTYPE bong P((int));
137 static void checkparent P((void));
138 static void removeentry P((struct conf_entry *));
139 static void addentry P((char *, int, int, int, int, u_int,
140 int, keyid_t, char *));
141 static int findhostaddr P((struct conf_entry *));
142 static void openntp P((void));
143 static int request P((struct conf_peer *));
144 static char * nexttoken P((char **));
145 static void readconf P((FILE *, char *));
146 static void doconfigure P((int));
148 struct ntp_res_t_pkt { /* Tagged packet: */
149 void *tag; /* For the caller */
150 u_int32 paddr; /* IP to look up, or 0 */
151 char name[MAXHOSTNAMELEN]; /* Name to look up (if 1st byte is not 0) */
154 struct ntp_res_c_pkt { /* Control packet: */
155 char name[MAXHOSTNAMELEN];
164 u_char keystr[MAXFILENAME];
169 * ntp_res_recv: Process an answer from the resolver
176 We have data ready on our descriptor.
177 It may be an EOF, meaning the resolver process went away.
178 Otherwise, it will be an "answer".
186 * req_key(???), req_keyid, req_file valid
194 #ifdef HAVE_SIGSUSPEND
198 #endif /* HAVE_SIGSUSPEND */
202 msyslog(LOG_INFO, "NTP_INTRES running");
206 /* check out auth stuff */
207 if (sys_authenticate) {
208 if (!authistrusted(req_keyid)) {
209 msyslog(LOG_ERR, "invalid request keyid %08x",
216 * Read the configuration info
217 * {this is bogus, since we are forked, but it is easier
218 * to keep this code - gdt}
220 if ((in = fopen(req_file, "r")) == NULL) {
221 msyslog(LOG_ERR, "can't open configuration file %s: %m",
225 readconf(in, req_file);
229 (void) unlink(req_file);
232 * Sleep a little to make sure the server is completely up
238 * Make a first cut at resolving the bunch
241 if (confentries == NULL) {
242 #if defined SYS_WINNT
243 ExitThread(0); /* Don't want to kill whole NT process */
245 exit(0); /* done that quick */
250 * Here we've got some problem children. Set up the timer
253 resolve_value = resolve_timer = MINRESOLVE;
254 config_timer = CONFIG_TIME;
256 (void) signal_no_reset(SIGALRM, bong);
258 #endif /* SYS_WINNT */
261 if (confentries == NULL)
266 if (resolve_timer == 0) {
267 if (resolve_value < MAXRESOLVE)
269 resolve_timer = resolve_value;
272 msyslog(LOG_INFO, "resolve_timer: 0->%d", resolve_timer);
274 config_timer = CONFIG_TIME;
277 } else if (config_timer == 0) {
278 config_timer = CONFIG_TIME;
281 msyslog(LOG_INFO, "config_timer: 0->%d", config_timer);
288 * There is a race in here. Is okay, though, since
289 * all it does is delay things by 30 seconds.
291 # ifdef HAVE_SIGSUSPEND
295 # endif /* HAVE_SIGSUSPEND */
297 if (config_timer > 0)
299 if (resolve_timer > 0)
302 #endif /* SYS_WINNT */
309 * bong - service and reschedule an alarm() interrupt
316 if (config_timer > 0)
318 if (resolve_timer > 0)
322 #endif /* SYS_WINNT */
325 * checkparent - see if our parent process is still running
327 * No need to worry in the Windows NT environment whether the
328 * main thread is still running, because if it goes
329 * down it takes the whole process down with it (in
330 * which case we won't be running this thread either)
331 * Turn function into NOP;
337 #if !defined (SYS_WINNT) && !defined (SYS_VXWORKS)
340 * If our parent (the server) has died we will have been
341 * inherited by init. If so, exit.
343 if (getppid() == 1) {
344 msyslog(LOG_INFO, "parent died before we finished, exiting");
347 #endif /* SYS_WINNT && SYS_VXWORKS*/
353 * removeentry - we are done with an entry, remove it from the list
357 struct conf_entry *entry
360 register struct conf_entry *ce;
364 confentries = ce->ce_next;
369 if (ce->ce_next == entry) {
370 ce->ce_next = entry->ce_next;
379 * addentry - add an entry to the configuration list
395 register struct conf_entry *ce;
401 "intres: <%s> %d %d %d %d %x %d %x %s\n", name,
402 mode, version, minpoll, maxpoll, flags, ttl, keyid,
405 len = strlen(name) + 1;
406 cp = (char *)emalloc(len);
407 memmove(cp, name, len);
409 ce = (struct conf_entry *)emalloc(sizeof(struct conf_entry));
412 ce->ce_hmode = (u_char)mode;
413 ce->ce_version = (u_char)version;
414 ce->ce_minpoll = (u_char)minpoll;
415 ce->ce_maxpoll = (u_char)maxpoll;
416 ce->ce_flags = (u_char)flags;
417 ce->ce_ttl = (u_char)ttl;
418 ce->ce_keyid = keyid;
419 strncpy((char *)ce->ce_keystr, keystr, MAXFILENAME);
422 if (confentries == NULL) {
425 register struct conf_entry *cep;
427 for (cep = confentries; cep->ce_next != NULL;
436 * findhostaddr - resolve a host name into an address (Or vice-versa)
438 * Given one of {ce_peeraddr,ce_name}, find the other one.
439 * It returns 1 for "success" and 0 for an uncorrectable failure.
440 * Note that "success" includes try again errors. You can tell that you
441 * got a "try again" since {ce_peeraddr,ce_name} will still be zero.
445 struct conf_entry *entry
451 checkparent(); /* make sure our guy is still running */
453 if (entry->ce_name && entry->ce_peeraddr) {
455 msyslog(LOG_ERR, "findhostaddr: both ce_name and ce_peeraddr are defined...");
459 if (!entry->ce_name && !entry->ce_peeraddr) {
460 msyslog(LOG_ERR, "findhostaddr: both ce_name and ce_peeraddr are undefined!");
464 if (entry->ce_name) {
467 msyslog(LOG_INFO, "findhostaddr: Resolving <%s>",
470 hp = gethostbyname(entry->ce_name);
474 msyslog(LOG_INFO, "findhostaddr: Resolving %x>",
477 in.s_addr = entry->ce_peeraddr;
478 hp = gethostbyaddr((const char *)&in,
479 sizeof entry->ce_peeraddr,
485 * If the resolver is in use, see if the failure is
486 * temporary. If so, return success.
488 if (h_errno == TRY_AGAIN)
493 if (entry->ce_name) {
496 msyslog(LOG_INFO, "findhostaddr: name resolved.");
499 * Use the first address. We don't have any way to tell
500 * preferences and older gethostbyname() implementations
503 memmove((char *)&(entry->ce_peeraddr),
505 sizeof(struct in_addr));
506 if (entry->ce_keystr[0] == '*')
507 strncpy((char *)&(entry->ce_keystr), hp->h_name,
515 msyslog(LOG_INFO, "findhostaddr: address resolved.");
517 s = strlen(hp->h_name) + 1;
519 strcpy(cp, hp->h_name);
528 * openntp - open a socket to the ntp server
533 struct sockaddr_in saddr;
538 sockfd = socket(AF_INET, SOCK_DGRAM, 0);
540 msyslog(LOG_ERR, "socket() failed: %m");
544 memset((char *)&saddr, 0, sizeof(saddr));
545 saddr.sin_family = AF_INET;
546 saddr.sin_port = htons(NTP_PORT); /* trash */
547 saddr.sin_addr.s_addr = htonl(LOCALHOST); /* garbage */
550 * Make the socket non-blocking. We'll wait with select()
553 #if defined(O_NONBLOCK)
554 if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == -1) {
555 msyslog(LOG_ERR, "fcntl(O_NONBLOCK) failed: %m");
560 if (fcntl(sockfd, F_SETFL, FNDELAY) == -1) {
561 msyslog(LOG_ERR, "fcntl(FNDELAY) failed: %m");
565 # include "Bletch: NEED NON BLOCKING IO"
566 #endif /* FNDDELAY */
567 #endif /* O_NONBLOCK */
568 #else /* SYS_WINNT */
571 if (ioctlsocket(sockfd,FIONBIO,(u_long *) &on) == SOCKET_ERROR) {
572 msyslog(LOG_ERR, "ioctlsocket(FIONBIO) fails: %m");
573 exit(1); /* Windows NT - set socket in non-blocking mode */
576 #endif /* SYS_WINNT */
579 if (connect(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) {
580 msyslog(LOG_ERR, "openntp: connect() failed: %m");
587 * request - send a configuration request to the server, wait for a response
591 struct conf_peer *conf
595 struct timeval tvout;
596 struct req_pkt reqpkt;
600 HANDLE hReadWriteEvent = NULL;
602 DWORD NumberOfBytesWritten, NumberOfBytesRead, dwWait;
604 #endif /* SYS_WINNT */
606 checkparent(); /* make sure our guy is still running */
612 hReadWriteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
613 #endif /* SYS_WINNT */
616 * Try to clear out any previously received traffic so it
617 * doesn't fool us. Note the socket is nonblocking.
622 FD_SET(sockfd, &fdset);
623 while (select(sockfd + 1, &fdset, (fd_set *)0, (fd_set *)0, &tvout) >
625 recv(sockfd, (char *)&reqpkt, REQ_LEN_MAC, 0);
627 FD_SET(sockfd, &fdset);
631 * Make up a request packet with the configuration info
633 memset((char *)&reqpkt, 0, sizeof(reqpkt));
635 reqpkt.rm_vn_mode = RM_VN_MODE(0, 0, 0);
636 reqpkt.auth_seq = AUTH_SEQ(1, 0); /* authenticated, no seq */
637 reqpkt.implementation = IMPL_XNTPD; /* local implementation */
638 reqpkt.request = REQ_CONFIG; /* configure a new peer */
639 reqpkt.err_nitems = ERR_NITEMS(0, 1); /* one item */
640 reqpkt.mbz_itemsize = MBZ_ITEMSIZE(sizeof(struct conf_peer));
641 /* Make sure mbz_itemsize <= sizeof reqpkt.data */
642 if (sizeof(struct conf_peer) > sizeof (reqpkt.data)) {
643 msyslog(LOG_ERR, "Bletch: conf_peer is too big for reqpkt.data!");
646 memmove(reqpkt.data, (char *)conf, sizeof(struct conf_peer));
647 reqpkt.keyid = htonl(req_keyid);
650 L_ADDUF(&ts, SKEWTIME);
651 HTONL_FP(&ts, &reqpkt.tstamp);
653 if (sys_authenticate)
654 n = authencrypt(req_keyid, (u_int32 *)&reqpkt, REQ_LEN_NOMAC);
660 n = send(sockfd, (char *)&reqpkt, (unsigned)(REQ_LEN_NOMAC + n), 0);
662 msyslog(LOG_ERR, "send to NTP server failed: %m");
663 return 0; /* maybe should exit */
666 /* In the NT world, documentation seems to indicate that there
667 * exist _write and _read routines that can be used to do blocking
668 * I/O on sockets. Problem is these routines require a socket
669 * handle obtained through the _open_osf_handle C run-time API
670 * of which there is no explanation in the documentation. We need
671 * nonblocking write's and read's anyway for our purpose here.
672 * We're therefore forced to deviate a little bit from the Unix
673 * model here and use the ReadFile and WriteFile Win32 I/O API's
676 overlap.Offset = overlap.OffsetHigh = (DWORD)0;
677 overlap.hEvent = hReadWriteEvent;
678 ret = WriteFile((HANDLE)sockfd, (char *)&reqpkt, REQ_LEN_NOMAC + n,
679 (LPDWORD)&NumberOfBytesWritten, (LPOVERLAPPED)&overlap);
680 if ((ret == FALSE) && (GetLastError() != ERROR_IO_PENDING)) {
681 msyslog(LOG_ERR, "send to NTP server failed: %m");
684 dwWait = WaitForSingleObject(hReadWriteEvent, (DWORD) TIMEOUT_SEC * 1000);
685 if ((dwWait == WAIT_FAILED) || (dwWait == WAIT_TIMEOUT)) {
686 if (dwWait == WAIT_FAILED)
687 msyslog(LOG_ERR, "WaitForSingleObject failed: %m");
690 #endif /* SYS_WINNT */
694 * Wait for a response. A weakness of the mode 7 protocol used
695 * is that there is no way to associate a response with a
696 * particular request, i.e. the response to this configuration
697 * request is indistinguishable from that to any other. I should
698 * fix this some day. In any event, the time out is fairly
699 * pessimistic to make sure that if an answer is coming back
704 FD_SET(sockfd, &fdset);
705 tvout.tv_sec = TIMEOUT_SEC;
706 tvout.tv_usec = TIMEOUT_USEC;
708 n = select(sockfd + 1, &fdset, (fd_set *)0,
709 (fd_set *)0, &tvout);
713 msyslog(LOG_ERR, "select() fails: %m");
719 msyslog(LOG_INFO, "select() returned 0.");
724 n = recv(sockfd, (char *)&reqpkt, REQ_LEN_MAC, 0);
727 msyslog(LOG_ERR, "recv() fails: %m");
732 #else /* Overlapped I/O used on non-blocking sockets on Windows NT */
733 ret = ReadFile((HANDLE)sockfd, (char *)&reqpkt, (DWORD)REQ_LEN_MAC,
734 (LPDWORD)&NumberOfBytesRead, (LPOVERLAPPED)&overlap);
735 if ((ret == FALSE) && (GetLastError() != ERROR_IO_PENDING)) {
736 msyslog(LOG_ERR, "ReadFile() fails: %m");
739 dwWait = WaitForSingleObject(hReadWriteEvent, (DWORD) TIMEOUT_SEC * 1000);
740 if ((dwWait == WAIT_FAILED) || (dwWait == WAIT_TIMEOUT)) {
741 if (dwWait == WAIT_FAILED) {
742 msyslog(LOG_ERR, "WaitForSingleObject fails: %m");
747 n = NumberOfBytesRead;
748 #endif /* SYS_WINNT */
751 * Got one. Check through to make sure it is what
754 if (n < RESP_HEADER_SIZE) {
755 msyslog(LOG_ERR, "received runt response (%d octets)",
760 if (!ISRESPONSE(reqpkt.rm_vn_mode)) {
763 msyslog(LOG_INFO, "received non-response packet");
768 if (ISMORE(reqpkt.rm_vn_mode)) {
771 msyslog(LOG_INFO, "received fragmented packet");
776 if ( ( (INFO_VERSION(reqpkt.rm_vn_mode) < 2)
777 || (INFO_VERSION(reqpkt.rm_vn_mode) > NTP_VERSION))
778 || INFO_MODE(reqpkt.rm_vn_mode) != MODE_PRIVATE) {
782 "version (%d/%d) or mode (%d/%d) incorrect",
783 INFO_VERSION(reqpkt.rm_vn_mode),
785 INFO_MODE(reqpkt.rm_vn_mode),
791 if (INFO_SEQ(reqpkt.auth_seq) != 0) {
795 "nonzero sequence number (%d)",
796 INFO_SEQ(reqpkt.auth_seq));
801 if (reqpkt.implementation != IMPL_XNTPD ||
802 reqpkt.request != REQ_CONFIG) {
806 "implementation (%d) or request (%d) incorrect",
807 reqpkt.implementation, reqpkt.request);
812 if (INFO_NITEMS(reqpkt.err_nitems) != 0 ||
813 INFO_MBZ(reqpkt.mbz_itemsize) != 0 ||
814 INFO_ITEMSIZE(reqpkt.mbz_itemsize) != 0) {
818 "nitems (%d) mbz (%d) or itemsize (%d) nonzero",
819 INFO_NITEMS(reqpkt.err_nitems),
820 INFO_MBZ(reqpkt.mbz_itemsize),
821 INFO_ITEMSIZE(reqpkt.mbz_itemsize));
826 n = INFO_ERR(reqpkt.err_nitems);
834 "server reports implementation mismatch!!");
839 "server claims configuration request is unknown");
844 "server indicates a format error occurred(!!)");
847 case INFO_ERR_NODATA:
849 "server indicates no data available (shouldn't happen)");
854 "server returns a permission denied error");
859 "server returns unknown error code %d", n);
867 * nexttoken - return the next token from a line
875 register char *tstart;
880 * Skip leading white space
882 while (*cp == ' ' || *cp == '\t')
886 * If this is the end of the line, return nothing.
888 if (*cp == '\n' || *cp == '\0') {
894 * Must be the start of a token. Record the pointer and look
898 while (*cp != ' ' && *cp != '\t' && *cp != '\n' && *cp != '\0')
902 * Terminate the token with a \0. If this isn't the end of the
903 * line, space to the next character.
905 if (*cp == '\n' || *cp == '\0')
916 * readconf - read the configuration information out of the file we
917 * were passed. Note that since the file is supposed to be
918 * machine generated, we bail out at the first sign of trouble.
928 u_long intval[NUMTOK];
930 char buf[MAXLINESIZE];
933 while (fgets(buf, MAXLINESIZE, fp) != NULL) {
936 for (i = 0; i < NUMTOK; i++) {
937 if ((token[i] = nexttoken(&bp)) == NULL) {
939 "tokenizing error in file `%s', quitting",
945 for (i = 1; i < NUMTOK - 1; i++) {
946 if (!atouint(token[i], &intval[i])) {
948 "format error for integer token `%s', file `%s', quitting",
954 if (intval[TOK_HMODE] != MODE_ACTIVE &&
955 intval[TOK_HMODE] != MODE_CLIENT &&
956 intval[TOK_HMODE] != MODE_BROADCAST) {
957 msyslog(LOG_ERR, "invalid mode (%ld) in file %s",
958 intval[TOK_HMODE], name);
962 if (intval[TOK_VERSION] > NTP_VERSION ||
963 intval[TOK_VERSION] < NTP_OLDVERSION) {
964 msyslog(LOG_ERR, "invalid version (%ld) in file %s",
965 intval[TOK_VERSION], name);
968 if (intval[TOK_MINPOLL] < NTP_MINPOLL ||
969 intval[TOK_MINPOLL] > NTP_MAXPOLL) {
970 msyslog(LOG_ERR, "invalid MINPOLL value (%ld) in file %s",
971 intval[TOK_MINPOLL], name);
975 if (intval[TOK_MAXPOLL] < NTP_MINPOLL ||
976 intval[TOK_MAXPOLL] > NTP_MAXPOLL) {
977 msyslog(LOG_ERR, "invalid MAXPOLL value (%ld) in file %s",
978 intval[TOK_MAXPOLL], name);
982 if ((intval[TOK_FLAGS] & ~(FLAG_AUTHENABLE | FLAG_PREFER |
983 FLAG_NOSELECT | FLAG_BURST | FLAG_IBURST | FLAG_SKEY))
985 msyslog(LOG_ERR, "invalid flags (%ld) in file %s",
986 intval[TOK_FLAGS], name);
991 if (intval[TOK_FLAGS] & FLAG_AUTHENABLE)
992 flags |= CONF_FLAG_AUTHENABLE;
993 if (intval[TOK_FLAGS] & FLAG_PREFER)
994 flags |= CONF_FLAG_PREFER;
995 if (intval[TOK_FLAGS] & FLAG_NOSELECT)
996 flags |= CONF_FLAG_NOSELECT;
997 if (intval[TOK_FLAGS] & FLAG_BURST)
998 flags |= CONF_FLAG_BURST;
999 if (intval[TOK_FLAGS] & FLAG_IBURST)
1000 flags |= CONF_FLAG_IBURST;
1001 if (intval[TOK_FLAGS] & FLAG_SKEY)
1002 flags |= CONF_FLAG_SKEY;
1005 * This is as good as we can check it. Add it in.
1007 addentry(token[TOK_HOSTNAME], (int)intval[TOK_HMODE],
1008 (int)intval[TOK_VERSION], (int)intval[TOK_MINPOLL],
1009 (int)intval[TOK_MAXPOLL], flags, (int)intval[TOK_TTL],
1010 intval[TOK_KEYID], token[TOK_KEYSTR]);
1016 * doconfigure - attempt to resolve names and configure the server
1023 register struct conf_entry *ce;
1024 register struct conf_entry *ceremove;
1027 while (ce != NULL) {
1031 "doconfigure: <%s> has peeraddr %#x",
1032 ce->ce_name, ce->ce_peeraddr);
1034 if (dores && ce->ce_peeraddr == 0) {
1035 if (!findhostaddr(ce)) {
1037 "couldn't resolve `%s', giving up on it",
1040 ce = ceremove->ce_next;
1041 removeentry(ceremove);
1046 if (ce->ce_peeraddr != 0) {
1047 if (request(&ce->ce_config)) {
1049 ce = ceremove->ce_next;
1050 removeentry(ceremove);
1056 "doconfigure: request() FAILED, maybe next time.");