2 ** Ancestor was ripped off from ../ntpres/ntpres.c by Greg Troxel 4/2/92
4 ** The previous resolver only needed to do forward lookups, and all names
5 ** were known before we started the resolver process.
7 ** The new code must be able to handle reverse lookups, and the requests can
8 ** show up at any time.
10 ** Here's the drill for the new logic.
12 ** We want to be able to do forward or reverse lookups. Forward lookups
13 ** require one set of information to be sent back to the daemon, reverse
14 ** lookups require a different set of information. The caller knows this.
16 ** The daemon must not block. This includes communicating with the resolver
17 ** process (if the resolver process is a separate task).
19 ** Current resolver code blocks waiting for the response, so the
22 ** - Find a nonblocking resolver library
23 ** - Do each (initial) lookup in a separate process
24 ** - - subsequent lookups *could* be handled by a different process that has
25 ** a queue of pending requests
27 ** We could use nonblocking lookups in a separate process (just to help out
30 ** If we don't have nonblocking resolver calls we have more opportunities
31 ** for denial-of-service problems.
34 ** - communications path
44 #include "ntp_request.h"
45 #include "ntp_stdlib.h"
46 #include "ntp_syslog.h"
53 #include <netinet/in.h>
54 #include <arpa/inet.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.
66 #define DE_NA (DE_NAME | DE_ADDR)
67 #define DE_PENDING 000
70 #define DE_RESULT (DE_PENDING | DE_GOT | DE_FAIL)
71 struct dns_entry *de_next;
72 struct info_dns_assoc de_info; /* DNS info for peer */
74 #define de_associd de_info.associd
75 #define de_peeraddr de_info.peeraddr
76 #define de_hostname de_info.hostname
79 * dns_entries is a pointer to the list of configuration entries
82 static struct dns_entry *dns_entries = NULL;
85 * We take an interrupt every thirty seconds, at which time we decrement
86 * config_timer and resolve_timer. The former is set to 2, so we retry
87 * unsucessful reconfigurations every minute. The latter is set to
88 * an exponentially increasing value which starts at 2 and increases to
89 * 32. When this expires we retry failed name resolutions.
91 * We sleep SLEEPTIME seconds before doing anything, to give the server
92 * time to arrange itself.
101 static volatile int config_timer = 0;
102 static volatile int resolve_timer = 0;
104 static int resolve_value; /* next value of resolve timer */
109 #define LOCALHOST 0x7f000001 /* 127.0.0.1, in hex, of course */
110 #define SKEWTIME 0x08000000 /* 0.03125 seconds as a l_fp fraction */
113 * Select time out. Set to 2 seconds. The server is on the local machine,
116 #define TIMEOUT_SEC 2
117 #define TIMEOUT_USEC 0
120 * File descriptor for ntp request code.
122 static int sockfd = -1;
127 int p_fd[2] = { -1, -1 };
129 /* stuff to be filled in by caller */
131 extern keyid_t req_keyid; /* request keyid */
133 /* end stuff to be filled in */
135 void ntp_res P((void));
136 static RETSIGTYPE bong P((int));
137 static void checkparent P((void));
138 static void removeentry P((struct dns_entry *));
139 static void addentry P((char *, u_int32, u_short));
140 static void findhostaddr P((struct dns_entry *));
141 static void openntp P((void));
142 static int tell_ntpd P((struct info_dns_assoc *));
143 static void doconfigure P((int));
145 struct ntp_res_t_pkt { /* Tagged packet: */
146 void *tag; /* For the caller */
147 u_int32 paddr; /* IP to look up, or 0 */
148 char name[NTP_MAXHOSTNAME]; /* Name to look up (if 1st byte is not 0) */
151 struct ntp_res_c_pkt { /* Control packet: */
152 char name[NTP_MAXHOSTNAME];
161 u_char keystr[MAXFILENAME];
170 u_int32 paddr, /* Address to resolve */
171 u_short associd /* Association ID */
179 * - child stuffs data and calls ntp_res()
182 for (pid = -1; pid == -1;) {
189 msyslog(LOG_ERR, "ntp_res_name: fork() failed: %m");
195 msyslog(LOG_INFO, "ntp_res_name: error...");
202 (void) signal_no_reset(SIGCHLD, SIG_DFL);
204 openlog("ntp_res", LOG_PID);
205 # else /* LOG_DAEMON */
207 # define LOG_NTP LOG_DAEMON
209 openlog("ntp_res_name", LOG_PID | LOG_NDELAY, LOG_NTP);
212 addentry(NULL, paddr, associd);
216 default: /* Parent */
217 /* Nothing to do. (In Real Life, this never happens.) */
225 * req_key(???), req_keyid valid
232 #ifdef HAVE_SIGSUSPEND
236 #endif /* HAVE_SIGSUSPEND */
240 msyslog(LOG_INFO, "NTP_RESOLVER running");
244 /* check out auth stuff */
245 if (sys_authenticate) {
246 if (!authistrusted(req_keyid)) {
247 msyslog(LOG_ERR, "invalid request keyid %08x",
254 * Make a first cut at resolving the bunch
257 if (dns_entries == NULL) {
259 msyslog(LOG_INFO, "NTP_RESOLVER done!");
261 #if defined SYS_WINNT
262 ExitThread(0); /* Don't want to kill whole NT process */
264 exit(0); /* done that quick */
269 * Here we've got some problem children. Set up the timer
272 resolve_value = resolve_timer = MINRESOLVE;
273 config_timer = CONFIG_TIME;
275 (void) signal_no_reset(SIGALRM, bong);
277 #endif /* SYS_WINNT */
280 if (dns_entries == NULL)
285 if (resolve_timer == 0) {
286 if (resolve_value < MAXRESOLVE)
288 resolve_timer = resolve_value;
290 msyslog(LOG_INFO, "resolve_timer: 0->%d", resolve_timer);
292 config_timer = CONFIG_TIME;
295 } else if (config_timer == 0) {
296 config_timer = CONFIG_TIME;
298 msyslog(LOG_INFO, "config_timer: 0->%d", config_timer);
305 * There is a race in here. Is okay, though, since
306 * all it does is delay things by 30 seconds.
308 # ifdef HAVE_SIGSUSPEND
312 # endif /* HAVE_SIGSUSPEND */
314 if (config_timer > 0)
316 if (resolve_timer > 0)
319 #endif /* SYS_WINNT */
326 * bong - service and reschedule an alarm() interrupt
333 if (config_timer > 0)
335 if (resolve_timer > 0)
339 #endif /* SYS_WINNT */
342 * checkparent - see if our parent process is still running
344 * No need to worry in the Windows NT environment whether the
345 * main thread is still running, because if it goes
346 * down it takes the whole process down with it (in
347 * which case we won't be running this thread either)
348 * Turn function into NOP;
354 #if !defined (SYS_WINNT) && !defined (SYS_VXWORKS)
357 * If our parent (the server) has died we will have been
358 * inherited by init. If so, exit.
360 if (getppid() == 1) {
361 msyslog(LOG_INFO, "parent died before we finished, exiting");
364 #endif /* SYS_WINNT && SYS_VXWORKS*/
369 * removeentry - we are done with an entry, remove it from the list
373 struct dns_entry *entry
376 register struct dns_entry *de;
380 dns_entries = de->de_next;
385 if (de->de_next == entry) {
386 de->de_next = entry->de_next;
395 * addentry - add an entry to the configuration list
404 register struct dns_entry *de;
412 "ntp_res_name: <%s> %s associd %d\n",
413 (name) ? name : "", inet_ntoa(si), associd);
417 de = (struct dns_entry *)emalloc(sizeof(struct dns_entry));
419 strncpy(de->de_hostname, name, sizeof de->de_hostname);
420 de->de_done = DE_PENDING | DE_ADDR;
422 de->de_hostname[0] = 0;
423 de->de_done = DE_PENDING | DE_NAME;
425 de->de_peeraddr = paddr;
426 de->de_associd = associd;
429 if (dns_entries == NULL) {
432 register struct dns_entry *dep;
434 for (dep = dns_entries; dep->de_next != NULL;
443 * findhostaddr - resolve a host name into an address (Or vice-versa)
445 * sets entry->de_done appropriately when we're finished. We're finished if
446 * we either successfully look up the missing name or address, or if we get a
447 * "permanent" failure on the lookup.
452 struct dns_entry *entry
457 checkparent(); /* make sure our guy is still running */
460 * The following should never trip - this subroutine isn't
461 * called if hostname and peeraddr are "filled".
463 if (entry->de_hostname[0] && entry->de_peeraddr) {
466 si.s_addr = entry->de_peeraddr;
467 msyslog(LOG_ERR, "findhostaddr: both de_hostname and de_peeraddr are defined: <%s>/%s: state %#x",
468 &entry->de_hostname[0], inet_ntoa(si), entry->de_done);
473 * The following should never trip.
475 if (!entry->de_hostname[0] && !entry->de_peeraddr) {
476 msyslog(LOG_ERR, "findhostaddr: both de_hostname and de_peeraddr are undefined!");
477 entry->de_done |= DE_FAIL;
481 if (entry->de_hostname[0]) {
484 msyslog(LOG_INFO, "findhostaddr: Resolving <%s>",
485 &entry->de_hostname[0]);
487 hp = gethostbyname(&entry->de_hostname[0]);
493 si.s_addr = entry->de_peeraddr;
494 msyslog(LOG_INFO, "findhostaddr: Resolving %s",
498 hp = gethostbyaddr((const char *)&entry->de_peeraddr,
499 sizeof entry->de_peeraddr,
505 * Bail if we should TRY_AGAIN.
506 * Otherwise, we have a permanent failure.
508 if (h_errno == TRY_AGAIN)
510 entry->de_done |= DE_FAIL;
512 entry->de_done |= DE_GOT;
515 if (entry->de_done & DE_GOT) {
516 switch (entry->de_done & DE_NA) {
521 "findhostaddr: name resolved.");
524 * Use the first address. We don't have any way to
525 * tell preferences and older gethostbyname()
526 * implementations only return one.
528 memmove((char *)&(entry->de_peeraddr),
530 sizeof(struct in_addr));
536 "findhostaddr: address resolved.");
538 strncpy(&entry->de_hostname[0], hp->h_name,
539 sizeof entry->de_hostname);
542 msyslog(LOG_ERR, "findhostaddr: Bogus de_done: %#x",
551 #ifndef HAVE_HSTRERROR
556 hes = "Authoritive Answer Host not found";
559 hes = "Non-Authoritative Host not found, or SERVERFAIL";
562 hes = "Non recoverable errors, FORMERR, REFUSED, NOTIMP";
565 hes = "Valid name, no data record of requested type";
568 snprintf(hnum, sizeof hnum, "%d", h_errno);
573 hes = hstrerror(h_errno);
576 si.s_addr = entry->de_peeraddr;
578 "findhostaddr: Failed resolution on <%s>/%s: %s",
579 entry->de_hostname, inet_ntoa(si), hes);
582 /* Send a NAK message back to the daemon */
589 * openntp - open a socket to the ntp server
594 struct sockaddr_in saddr;
599 sockfd = socket(AF_INET, SOCK_DGRAM, 0);
601 msyslog(LOG_ERR, "socket() failed: %m");
605 memset((char *)&saddr, 0, sizeof(saddr));
606 saddr.sin_family = AF_INET;
607 saddr.sin_port = htons(NTP_PORT); /* trash */
608 saddr.sin_addr.s_addr = htonl(LOCALHOST); /* garbage */
611 * Make the socket non-blocking. We'll wait with select()
614 # if defined(O_NONBLOCK)
615 if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == -1) {
616 msyslog(LOG_ERR, "fcntl(O_NONBLOCK) failed: %m");
620 # if defined(FNDELAY)
621 if (fcntl(sockfd, F_SETFL, FNDELAY) == -1) {
622 msyslog(LOG_ERR, "fcntl(FNDELAY) failed: %m");
626 # include "Bletch: NEED NON BLOCKING IO"
627 # endif /* FNDDELAY */
628 # endif /* O_NONBLOCK */
629 #else /* SYS_WINNT */
633 if (ioctlsocket(sockfd,FIONBIO,(u_long *) &on) == SOCKET_ERROR) {
634 msyslog(LOG_ERR, "ioctlsocket(FIONBIO) fails: %m");
635 exit(1); /* Windows NT - set socket in non-blocking mode */
638 #endif /* SYS_WINNT */
640 if (connect(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) {
641 msyslog(LOG_ERR, "openntp: connect() failed: %m");
648 * tell_ntpd: Tell ntpd what we discovered.
652 struct info_dns_assoc *conf
656 struct timeval tvout;
657 struct req_pkt reqpkt;
661 HANDLE hReadWriteEvent = NULL;
663 DWORD NumberOfBytesWritten, NumberOfBytesRead, dwWait;
665 #endif /* SYS_WINNT */
667 checkparent(); /* make sure our guy is still running */
673 hReadWriteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
674 #endif /* SYS_WINNT */
677 * Try to clear out any previously received traffic so it
678 * doesn't fool us. Note the socket is nonblocking.
683 FD_SET(sockfd, &fdset);
684 while (select(sockfd + 1, &fdset, (fd_set *)0, (fd_set *)0, &tvout) >
686 recv(sockfd, (char *)&reqpkt, REQ_LEN_MAC, 0);
688 FD_SET(sockfd, &fdset);
692 * Make up a request packet with the configuration info
694 memset((char *)&reqpkt, 0, sizeof(reqpkt));
696 reqpkt.rm_vn_mode = RM_VN_MODE(0, 0, 0);
697 reqpkt.auth_seq = AUTH_SEQ(1, 0); /* authenticated, no seq */
698 reqpkt.implementation = IMPL_XNTPD; /* local implementation */
699 reqpkt.request = REQ_HOSTNAME_ASSOCID; /* Hostname for associd */
700 reqpkt.err_nitems = ERR_NITEMS(0, 1); /* one item */
701 reqpkt.mbz_itemsize = MBZ_ITEMSIZE(sizeof(struct info_dns_assoc));
702 memmove(reqpkt.data, (char *)conf, sizeof(struct info_dns_assoc));
703 reqpkt.keyid = htonl(req_keyid);
706 L_ADDUF(&ts, SKEWTIME);
707 HTONL_FP(&ts, &reqpkt.tstamp);
709 if (sys_authenticate)
710 n = authencrypt(req_keyid, (u_int32 *)&reqpkt, REQ_LEN_NOMAC);
716 n = send(sockfd, (char *)&reqpkt, (unsigned)(REQ_LEN_NOMAC + n), 0);
718 msyslog(LOG_ERR, "send to NTP server failed: %m");
719 return 0; /* maybe should exit */
722 /* In the NT world, documentation seems to indicate that there
723 * exist _write and _read routines that can be used to do blocking
724 * I/O on sockets. Problem is these routines require a socket
725 * handle obtained through the _open_osf_handle C run-time API
726 * of which there is no explanation in the documentation. We need
727 * nonblocking write's and read's anyway for our purpose here.
728 * We're therefore forced to deviate a little bit from the Unix
729 * model here and use the ReadFile and WriteFile Win32 I/O API's
732 overlap.Offset = overlap.OffsetHigh = (DWORD)0;
733 overlap.hEvent = hReadWriteEvent;
734 ret = WriteFile((HANDLE)sockfd, (char *)&reqpkt, REQ_LEN_NOMAC + n,
735 (LPDWORD)&NumberOfBytesWritten, (LPOVERLAPPED)&overlap);
736 if ((ret == FALSE) && (GetLastError() != ERROR_IO_PENDING)) {
737 msyslog(LOG_ERR, "send to NTP server failed: %m");
740 dwWait = WaitForSingleObject(hReadWriteEvent, (DWORD) TIMEOUT_SEC * 1000);
741 if ((dwWait == WAIT_FAILED) || (dwWait == WAIT_TIMEOUT)) {
742 if (dwWait == WAIT_FAILED)
743 msyslog(LOG_ERR, "WaitForSingleObject failed: %m");
746 #endif /* SYS_WINNT */
750 * Wait for a response. A weakness of the mode 7 protocol used
751 * is that there is no way to associate a response with a
752 * particular request, i.e. the response to this configuration
753 * request is indistinguishable from that to any other. I should
754 * fix this some day. In any event, the time out is fairly
755 * pessimistic to make sure that if an answer is coming back
760 FD_SET(sockfd, &fdset);
761 tvout.tv_sec = TIMEOUT_SEC;
762 tvout.tv_usec = TIMEOUT_USEC;
764 n = select(sockfd + 1, &fdset, (fd_set *)0,
765 (fd_set *)0, &tvout);
769 msyslog(LOG_ERR, "select() fails: %m");
775 msyslog(LOG_INFO, "select() returned 0.");
780 n = recv(sockfd, (char *)&reqpkt, REQ_LEN_MAC, 0);
783 msyslog(LOG_ERR, "recv() fails: %m");
788 #else /* Overlapped I/O used on non-blocking sockets on Windows NT */
789 ret = ReadFile((HANDLE)sockfd, (char *)&reqpkt, (DWORD)REQ_LEN_MAC,
790 (LPDWORD)&NumberOfBytesRead, (LPOVERLAPPED)&overlap);
791 if ((ret == FALSE) && (GetLastError() != ERROR_IO_PENDING)) {
792 msyslog(LOG_ERR, "ReadFile() fails: %m");
795 dwWait = WaitForSingleObject(hReadWriteEvent, (DWORD) TIMEOUT_SEC * 1000);
796 if ((dwWait == WAIT_FAILED) || (dwWait == WAIT_TIMEOUT)) {
797 if (dwWait == WAIT_FAILED) {
798 msyslog(LOG_ERR, "WaitForSingleObject fails: %m");
803 n = NumberOfBytesRead;
804 #endif /* SYS_WINNT */
807 * Got one. Check through to make sure it is what
810 if (n < RESP_HEADER_SIZE) {
811 msyslog(LOG_ERR, "received runt response (%d octets)",
816 if (!ISRESPONSE(reqpkt.rm_vn_mode)) {
819 msyslog(LOG_INFO, "received non-response packet");
824 if (ISMORE(reqpkt.rm_vn_mode)) {
827 msyslog(LOG_INFO, "received fragmented packet");
832 if ( ( (INFO_VERSION(reqpkt.rm_vn_mode) < 2)
833 || (INFO_VERSION(reqpkt.rm_vn_mode) > NTP_VERSION))
834 || INFO_MODE(reqpkt.rm_vn_mode) != MODE_PRIVATE) {
838 "version (%d/%d) or mode (%d/%d) incorrect",
839 INFO_VERSION(reqpkt.rm_vn_mode),
841 INFO_MODE(reqpkt.rm_vn_mode),
847 if (INFO_SEQ(reqpkt.auth_seq) != 0) {
851 "nonzero sequence number (%d)",
852 INFO_SEQ(reqpkt.auth_seq));
857 if (reqpkt.implementation != IMPL_XNTPD ||
858 reqpkt.request != REQ_HOSTNAME_ASSOCID) {
862 "implementation (%d/%d) or request (%d/%d) incorrect",
863 reqpkt.implementation, IMPL_XNTPD,
864 reqpkt.request, REQ_HOSTNAME_ASSOCID);
869 if (INFO_NITEMS(reqpkt.err_nitems) != 0 ||
870 INFO_MBZ(reqpkt.mbz_itemsize) != 0 ||
871 INFO_ITEMSIZE(reqpkt.mbz_itemsize) != 0) {
875 "nitems (%d) mbz (%d) or itemsize (%d) nonzero",
876 INFO_NITEMS(reqpkt.err_nitems),
877 INFO_MBZ(reqpkt.mbz_itemsize),
878 INFO_ITEMSIZE(reqpkt.mbz_itemsize));
883 n = INFO_ERR(reqpkt.err_nitems);
891 "server reports implementation mismatch!!");
896 "server claims configuration request is unknown");
901 "server indicates a format error occurred(!!)");
904 case INFO_ERR_NODATA:
906 "server indicates no data available (shouldn't happen)");
911 "server returns a permission denied error");
916 "server returns unknown error code %d", n);
924 * doconfigure - attempt to resolve names/addresses
931 register struct dns_entry *de;
932 register struct dns_entry *deremove;
941 si.s_addr = de->de_peeraddr;
943 "doconfigure: name: <%s> peeraddr: %s",
944 de->de_hostname, inet_ntoa(si));
947 if (dores && (de->de_hostname[0] == 0 || de->de_peeraddr == 0)) {
951 switch (de->de_done & DE_RESULT) {
956 done_msg = "succeeded";
962 done_msg = "(error - shouldn't happen)";
966 /* Send the answer */
967 if (tell_ntpd(&de->de_info)) {
970 si.s_addr = de->de_peeraddr;
974 "DNS resolution on <%s>/%s %s",
975 de->de_hostname, inet_ntoa(si),
980 de = deremove->de_next;
981 removeentry(deremove);