Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / ntp / ntpd / ntp_resolver.c
1 /*
2 ** Ancestor was ripped off from ../ntpres/ntpres.c by Greg Troxel 4/2/92
3 **
4 ** The previous resolver only needed to do forward lookups, and all names
5 ** were known before we started the resolver process.
6 **
7 ** The new code must be able to handle reverse lookups, and the requests can
8 ** show up at any time.
9 **
10 ** Here's the drill for the new logic.
11 **
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.
15 **
16 ** The daemon must not block.  This includes communicating with the resolver
17 ** process (if the resolver process is a separate task).
18 **
19 ** Current resolver code blocks waiting for the response, so the
20 ** alternatives are:
21 **
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
26 **
27 ** We could use nonblocking lookups in a separate process (just to help out
28 ** with timers).
29 **
30 ** If we don't have nonblocking resolver calls we have more opportunities
31 ** for denial-of-service problems.
32 **
33 ** - too many fork()s
34 ** - communications path
35 **
36 */
37
38 #ifdef HAVE_CONFIG_H
39 # include <config.h>
40 #endif
41
42 #include "ntpd.h"
43 #include "ntp_io.h"
44 #include "ntp_request.h"
45 #include "ntp_stdlib.h"
46 #include "ntp_syslog.h"
47
48 #include <stdio.h>
49 #include <ctype.h>
50 #include <netdb.h>
51 #include <signal.h>
52
53 #include <netinet/in.h>
54 #include <arpa/inet.h>
55
56 #define STREQ(a, b)     (*(a) == *(b) && strcmp((a), (b)) == 0)
57
58 /*
59  * Each item we are to resolve and configure gets one of these
60  * structures defined for it.
61  */
62 struct dns_entry {
63         int de_done;
64 #define DE_NAME         001
65 #define DE_ADDR         002
66 #define DE_NA           (DE_NAME | DE_ADDR)
67 #define DE_PENDING      000
68 #define DE_GOT          010
69 #define DE_FAIL         020
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 */
73 };
74 #define de_associd      de_info.associd
75 #define de_peeraddr     de_info.peeraddr
76 #define de_hostname     de_info.hostname
77
78 /*
79  * dns_entries is a pointer to the list of configuration entries
80  * we have left to do.
81  */
82 static struct dns_entry *dns_entries = NULL;
83
84 /*
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.
90  *
91  * We sleep SLEEPTIME seconds before doing anything, to give the server
92  * time to arrange itself.
93  */
94 #define MINRESOLVE      2
95 #define MAXRESOLVE      32
96 #define CONFIG_TIME     2
97 #define ALARM_TIME      30
98
99 #define SLEEPTIME       2
100
101 static  volatile int config_timer = 0;
102 static  volatile int resolve_timer = 0;
103
104 static  int resolve_value;      /* next value of resolve timer */
105
106 /*
107  * Big hack attack
108  */
109 #define LOCALHOST       0x7f000001      /* 127.0.0.1, in hex, of course */
110 #define SKEWTIME        0x08000000      /* 0.03125 seconds as a l_fp fraction */
111
112 /*
113  * Select time out.  Set to 2 seconds.  The server is on the local machine,
114  * after all.
115  */
116 #define TIMEOUT_SEC     2
117 #define TIMEOUT_USEC    0
118
119 /*
120  * File descriptor for ntp request code.
121  */
122 static  int sockfd = -1;
123
124 /*
125  * Pipe descriptors
126  */
127 int p_fd[2] = { -1, -1 };
128
129 /* stuff to be filled in by caller */
130
131 extern keyid_t req_keyid;       /* request keyid */
132
133 /* end stuff to be filled in */
134
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));
144
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) */
149 };
150
151 struct ntp_res_c_pkt {          /* Control packet: */
152         char name[NTP_MAXHOSTNAME];
153         u_int32 paddr;
154         int mode;
155         int version;
156         int minpoll;
157         int maxpoll;
158         int flags;
159         int ttl;
160         keyid_t keyid;
161         u_char keystr[MAXFILENAME];
162 };
163
164 /*
165  * ntp_res_name
166  */
167
168 void
169 ntp_res_name(
170         u_int32 paddr,          /* Address to resolve */
171         u_short associd         /* Association ID */
172         )
173 {
174         pid_t pid;
175
176         /*
177          * fork.
178          * - parent returns
179          * - child stuffs data and calls ntp_res()
180          */
181
182         for (pid = -1; pid == -1;) {
183 #ifdef RES_TEST
184                 pid = 0;
185 #else
186                 pid = fork();
187 #endif
188                 if (pid == -1) {
189                         msyslog(LOG_ERR, "ntp_res_name: fork() failed: %m");
190                         sleep(2);
191                 }
192         }
193         switch (pid) {
194             case -1:    /* Error */
195                 msyslog(LOG_INFO, "ntp_res_name: error...");
196                 /* Can't happen */
197                 break;
198
199             case 0:     /* Child */
200                 closelog();
201                 kill_asyncio();
202                 (void) signal_no_reset(SIGCHLD, SIG_DFL);
203 #ifndef LOG_DAEMON
204                 openlog("ntp_res", LOG_PID);
205 # else /* LOG_DAEMON */
206 #  ifndef LOG_NTP
207 #   define      LOG_NTP LOG_DAEMON
208 #  endif
209                 openlog("ntp_res_name", LOG_PID | LOG_NDELAY, LOG_NTP);
210 #endif
211
212                 addentry(NULL, paddr, associd);
213                 ntp_res();
214                 break;
215
216             default:    /* Parent */
217                 /* Nothing to do.  (In Real Life, this never happens.) */
218                 return;
219         }
220 }
221
222 /*
223  * ntp_res needs;
224  *
225  *      req_key(???), req_keyid valid
226  *      syslog still open
227  */
228
229 void
230 ntp_res(void)
231 {
232 #ifdef HAVE_SIGSUSPEND
233         sigset_t set;
234
235         sigemptyset(&set);
236 #endif /* HAVE_SIGSUSPEND */
237
238 #ifdef DEBUG
239         if (debug) {
240                 msyslog(LOG_INFO, "NTP_RESOLVER running");
241         }
242 #endif
243
244         /* check out auth stuff */
245         if (sys_authenticate) {
246                 if (!authistrusted(req_keyid)) {
247                         msyslog(LOG_ERR, "invalid request keyid %08x",
248                             req_keyid );
249                         exit(1);
250                 }
251         }
252
253         /*
254          * Make a first cut at resolving the bunch
255          */
256         doconfigure(1);
257         if (dns_entries == NULL) {
258                 if (debug) {
259                         msyslog(LOG_INFO, "NTP_RESOLVER done!");
260                 }
261 #if defined SYS_WINNT
262                 ExitThread(0);  /* Don't want to kill whole NT process */
263 #else
264                 exit(0);        /* done that quick */
265 #endif
266         }
267         
268         /*
269          * Here we've got some problem children.  Set up the timer
270          * and wait for it.
271          */
272         resolve_value = resolve_timer = MINRESOLVE;
273         config_timer = CONFIG_TIME;
274 #ifndef SYS_WINNT
275         (void) signal_no_reset(SIGALRM, bong);
276         alarm(ALARM_TIME);
277 #endif /* SYS_WINNT */
278
279         for (;;) {
280                 if (dns_entries == NULL)
281                     exit(0);
282
283                 checkparent();
284
285                 if (resolve_timer == 0) {
286                         if (resolve_value < MAXRESOLVE)
287                             resolve_value <<= 1;
288                         resolve_timer = resolve_value;
289 #ifdef DEBUG
290                         msyslog(LOG_INFO, "resolve_timer: 0->%d", resolve_timer);
291 #endif
292                         config_timer = CONFIG_TIME;
293                         doconfigure(1);
294                         continue;
295                 } else if (config_timer == 0) {
296                         config_timer = CONFIG_TIME;
297 #ifdef DEBUG
298                         msyslog(LOG_INFO, "config_timer: 0->%d", config_timer);
299 #endif
300                         doconfigure(0);
301                         continue;
302                 }
303 #ifndef SYS_WINNT
304                 /*
305                  * There is a race in here.  Is okay, though, since
306                  * all it does is delay things by 30 seconds.
307                  */
308 # ifdef HAVE_SIGSUSPEND
309                 sigsuspend(&set);
310 # else
311                 sigpause(0);
312 # endif /* HAVE_SIGSUSPEND */
313 #else
314                 if (config_timer > 0)
315                     config_timer--;
316                 if (resolve_timer > 0)
317                     resolve_timer--;
318                 sleep(ALARM_TIME);
319 #endif /* SYS_WINNT */
320         }
321 }
322
323
324 #ifndef SYS_WINNT
325 /*
326  * bong - service and reschedule an alarm() interrupt
327  */
328 static RETSIGTYPE
329 bong(
330         int sig
331         )
332 {
333         if (config_timer > 0)
334             config_timer--;
335         if (resolve_timer > 0)
336             resolve_timer--;
337         alarm(ALARM_TIME);
338 }
339 #endif /* SYS_WINNT */
340
341 /*
342  * checkparent - see if our parent process is still running
343  *
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;
349  */
350
351 static void
352 checkparent(void)
353 {
354 #if !defined (SYS_WINNT) && !defined (SYS_VXWORKS)
355
356         /*
357          * If our parent (the server) has died we will have been
358          * inherited by init.  If so, exit.
359          */
360         if (getppid() == 1) {
361                 msyslog(LOG_INFO, "parent died before we finished, exiting");
362                 exit(0);
363         }
364 #endif /* SYS_WINNT && SYS_VXWORKS*/
365 }
366
367
368 /*
369  * removeentry - we are done with an entry, remove it from the list
370  */
371 static void
372 removeentry(
373         struct dns_entry *entry
374         )
375 {
376         register struct dns_entry *de;
377
378         de = dns_entries;
379         if (de == entry) {
380                 dns_entries = de->de_next;
381                 return;
382         }
383
384         while (de != NULL) {
385                 if (de->de_next == entry) {
386                         de->de_next = entry->de_next;
387                         return;
388                 }
389                 de = de->de_next;
390         }
391 }
392
393
394 /*
395  * addentry - add an entry to the configuration list
396  */
397 static void
398 addentry(
399         char *name,
400         u_int32 paddr,
401         u_short associd
402         )
403 {
404         register struct dns_entry *de;
405
406 #ifdef DEBUG
407         if (debug > 1) {
408                 struct in_addr si;
409
410                 si.s_addr = paddr;
411                 msyslog(LOG_INFO, 
412                         "ntp_res_name: <%s> %s associd %d\n",
413                         (name) ? name : "", inet_ntoa(si), associd);
414         }
415 #endif
416
417         de = (struct dns_entry *)emalloc(sizeof(struct dns_entry));
418         if (name) {
419                 strncpy(de->de_hostname, name, sizeof de->de_hostname);
420                 de->de_done = DE_PENDING | DE_ADDR;
421         } else {
422                 de->de_hostname[0] = 0;
423                 de->de_done = DE_PENDING | DE_NAME;
424         }
425         de->de_peeraddr = paddr;
426         de->de_associd = associd;
427         de->de_next = NULL;
428
429         if (dns_entries == NULL) {
430                 dns_entries = de;
431         } else {
432                 register struct dns_entry *dep;
433
434                 for (dep = dns_entries; dep->de_next != NULL;
435                      dep = dep->de_next)
436                     /* nothing */;
437                 dep->de_next = de;
438         }
439 }
440
441
442 /*
443  * findhostaddr - resolve a host name into an address (Or vice-versa)
444  *
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.
448  *
449  */
450 static void
451 findhostaddr(
452         struct dns_entry *entry
453         )
454 {
455         struct hostent *hp;
456
457         checkparent();          /* make sure our guy is still running */
458
459         /*
460          * The following should never trip - this subroutine isn't
461          * called if hostname and peeraddr are "filled".
462          */
463         if (entry->de_hostname[0] && entry->de_peeraddr) {
464                 struct in_addr si;
465
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);
469                 return;
470         }
471
472         /*
473          * The following should never trip.
474          */
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;
478                 return;
479         }
480
481         if (entry->de_hostname[0]) {
482 #ifdef DEBUG
483                 if (debug > 2)
484                         msyslog(LOG_INFO, "findhostaddr: Resolving <%s>",
485                                 &entry->de_hostname[0]);
486 #endif /* DEBUG */
487                 hp = gethostbyname(&entry->de_hostname[0]);
488         } else {
489 #ifdef DEBUG
490                 if (debug > 2) {
491                         struct in_addr si;
492
493                         si.s_addr = entry->de_peeraddr;
494                         msyslog(LOG_INFO, "findhostaddr: Resolving %s",
495                                 inet_ntoa(si));
496                 }
497 #endif
498                 hp = gethostbyaddr((const char *)&entry->de_peeraddr,
499                                    sizeof entry->de_peeraddr,
500                                    AF_INET);
501         }
502
503         if (hp == NULL) {
504                 /*
505                  * Bail if we should TRY_AGAIN.
506                  * Otherwise, we have a permanent failure.
507                  */
508                 if (h_errno == TRY_AGAIN)
509                         return;
510                 entry->de_done |= DE_FAIL;
511         } else {
512                 entry->de_done |= DE_GOT;
513         }
514
515         if (entry->de_done & DE_GOT) {
516                 switch (entry->de_done & DE_NA) {
517                     case DE_NAME:
518 #ifdef DEBUG
519                         if (debug > 2)
520                                 msyslog(LOG_INFO,
521                                         "findhostaddr: name resolved.");
522 #endif
523                         /*
524                          * Use the first address.  We don't have any way to
525                          * tell preferences and older gethostbyname()
526                          * implementations only return one.
527                          */
528                         memmove((char *)&(entry->de_peeraddr),
529                                 (char *)hp->h_addr,
530                                 sizeof(struct in_addr));
531                         break;
532                     case DE_ADDR:
533 #ifdef DEBUG
534                         if (debug > 2)
535                                 msyslog(LOG_INFO,
536                                         "findhostaddr: address resolved.");
537 #endif
538                         strncpy(&entry->de_hostname[0], hp->h_name,
539                                 sizeof entry->de_hostname);
540                         break;
541                     default:
542                         msyslog(LOG_ERR, "findhostaddr: Bogus de_done: %#x",
543                                 entry->de_done);
544                         break;
545                 }
546         } else {
547 #ifdef DEBUG
548                 if (debug > 2) {
549                         struct in_addr si;
550                         const char *hes;
551 #ifndef HAVE_HSTRERROR
552                         char hnum[20];
553                         
554                         switch (h_errno) {
555                             case HOST_NOT_FOUND:
556                                 hes = "Authoritive Answer Host not found";
557                                 break;
558                             case TRY_AGAIN:
559                                 hes = "Non-Authoritative Host not found, or SERVERFAIL";
560                                 break;
561                             case NO_RECOVERY:
562                                 hes = "Non recoverable errors, FORMERR, REFUSED, NOTIMP";
563                                 break;
564                             case NO_DATA:
565                                 hes = "Valid name, no data record of requested type";
566                                 break;
567                             default:
568                                 snprintf(hnum, sizeof hnum, "%d", h_errno);
569                                 hes = hnum;
570                                 break;
571                         }
572 #else
573                         hes = hstrerror(h_errno);
574 #endif
575
576                         si.s_addr = entry->de_peeraddr;
577                         msyslog(LOG_INFO,
578                                 "findhostaddr: Failed resolution on <%s>/%s: %s",
579                                 entry->de_hostname, inet_ntoa(si), hes);
580                 }
581 #endif
582                 /* Send a NAK message back to the daemon */
583         }
584         return;
585 }
586
587
588 /*
589  * openntp - open a socket to the ntp server
590  */
591 static void
592 openntp(void)
593 {
594         struct sockaddr_in saddr;
595
596         if (sockfd >= 0)
597             return;
598         
599         sockfd = socket(AF_INET, SOCK_DGRAM, 0);
600         if (sockfd == -1) {
601                 msyslog(LOG_ERR, "socket() failed: %m");
602                 exit(1);
603         }
604
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 */
609
610         /*
611          * Make the socket non-blocking.  We'll wait with select()
612          */
613 #ifndef SYS_WINNT
614 # if defined(O_NONBLOCK)
615         if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == -1) {
616                 msyslog(LOG_ERR, "fcntl(O_NONBLOCK) failed: %m");
617                 exit(1);
618         }
619 # else
620 #  if defined(FNDELAY)
621         if (fcntl(sockfd, F_SETFL, FNDELAY) == -1) {
622                 msyslog(LOG_ERR, "fcntl(FNDELAY) failed: %m");
623                 exit(1);
624         }
625 #  else
626 #   include "Bletch: NEED NON BLOCKING IO"
627 #  endif /* FNDDELAY */
628 # endif /* O_NONBLOCK */
629 #else  /* SYS_WINNT */
630         {
631                 int on = 1;
632
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 */
636                 }
637         }
638 #endif /* SYS_WINNT */
639
640         if (connect(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) {
641                 msyslog(LOG_ERR, "openntp: connect() failed: %m");
642                 exit(1);
643         }
644 }
645
646
647 /*
648  * tell_ntpd: Tell ntpd what we discovered.
649  */
650 static int
651 tell_ntpd(
652         struct info_dns_assoc *conf
653         )
654 {
655         fd_set fdset;
656         struct timeval tvout;
657         struct req_pkt reqpkt;
658         l_fp ts;
659         int n;
660 #ifdef SYS_WINNT
661         HANDLE hReadWriteEvent = NULL;
662         BOOL ret;
663         DWORD NumberOfBytesWritten, NumberOfBytesRead, dwWait;
664         OVERLAPPED overlap;
665 #endif /* SYS_WINNT */
666
667         checkparent();          /* make sure our guy is still running */
668
669         if (sockfd < 0)
670             openntp();
671         
672 #ifdef SYS_WINNT
673         hReadWriteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
674 #endif /* SYS_WINNT */
675
676         /*
677          * Try to clear out any previously received traffic so it
678          * doesn't fool us.  Note the socket is nonblocking.
679          */
680         tvout.tv_sec =  0;
681         tvout.tv_usec = 0;
682         FD_ZERO(&fdset);
683         FD_SET(sockfd, &fdset);
684         while (select(sockfd + 1, &fdset, (fd_set *)0, (fd_set *)0, &tvout) >
685                0) {
686                 recv(sockfd, (char *)&reqpkt, REQ_LEN_MAC, 0);
687                 FD_ZERO(&fdset);
688                 FD_SET(sockfd, &fdset);
689         }
690
691         /*
692          * Make up a request packet with the configuration info
693          */
694         memset((char *)&reqpkt, 0, sizeof(reqpkt));
695
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);
704
705         get_systime(&ts);
706         L_ADDUF(&ts, SKEWTIME);
707         HTONL_FP(&ts, &reqpkt.tstamp);
708         n = 0;
709         if (sys_authenticate)
710                 n = authencrypt(req_keyid, (u_int32 *)&reqpkt, REQ_LEN_NOMAC);
711
712         /*
713          * Done.  Send it.
714          */
715 #ifndef SYS_WINNT
716         n = send(sockfd, (char *)&reqpkt, (unsigned)(REQ_LEN_NOMAC + n), 0);
717         if (n < 0) {
718                 msyslog(LOG_ERR, "send to NTP server failed: %m");
719                 return 0;       /* maybe should exit */
720         }
721 #else
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
730          * on the socket
731          */
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");
738                 return 0;
739         }
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");
744                 return 0;
745         }
746 #endif /* SYS_WINNT */
747     
748
749         /*
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
756          * at all, we get it.
757          */
758         for (;;) {
759                 FD_ZERO(&fdset);
760                 FD_SET(sockfd, &fdset);
761                 tvout.tv_sec = TIMEOUT_SEC;
762                 tvout.tv_usec = TIMEOUT_USEC;
763
764                 n = select(sockfd + 1, &fdset, (fd_set *)0,
765                            (fd_set *)0, &tvout);
766
767                 if (n < 0)
768                 {
769                         msyslog(LOG_ERR, "select() fails: %m");
770                         return 0;
771                 }
772                 else if (n == 0)
773                 {
774                         if(debug)
775                             msyslog(LOG_INFO, "select() returned 0.");
776                         return 0;
777                 }
778
779 #ifndef SYS_WINNT
780                 n = recv(sockfd, (char *)&reqpkt, REQ_LEN_MAC, 0);
781                 if (n <= 0) {
782                         if (n < 0) {
783                                 msyslog(LOG_ERR, "recv() fails: %m");
784                                 return 0;
785                         }
786                         continue;
787                 }
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");
793                         return 0;
794                 }
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");
799                                 return 0;
800                         }
801                         continue;
802                 }
803                 n = NumberOfBytesRead;
804 #endif /* SYS_WINNT */
805
806                 /*
807                  * Got one.  Check through to make sure it is what
808                  * we expect.
809                  */
810                 if (n < RESP_HEADER_SIZE) {
811                         msyslog(LOG_ERR, "received runt response (%d octets)",
812                                 n);
813                         continue;
814                 }
815
816                 if (!ISRESPONSE(reqpkt.rm_vn_mode)) {
817 #ifdef DEBUG
818                         if (debug > 1)
819                             msyslog(LOG_INFO, "received non-response packet");
820 #endif
821                         continue;
822                 }
823
824                 if (ISMORE(reqpkt.rm_vn_mode)) {
825 #ifdef DEBUG
826                         if (debug > 1)
827                             msyslog(LOG_INFO, "received fragmented packet");
828 #endif
829                         continue;
830                 }
831
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) {
835 #ifdef DEBUG
836                         if (debug > 1)
837                             msyslog(LOG_INFO,
838                                     "version (%d/%d) or mode (%d/%d) incorrect",
839                                     INFO_VERSION(reqpkt.rm_vn_mode),
840                                     NTP_VERSION,
841                                     INFO_MODE(reqpkt.rm_vn_mode),
842                                     MODE_PRIVATE);
843 #endif
844                         continue;
845                 }
846
847                 if (INFO_SEQ(reqpkt.auth_seq) != 0) {
848 #ifdef DEBUG
849                         if (debug > 1)
850                             msyslog(LOG_INFO,
851                                     "nonzero sequence number (%d)",
852                                     INFO_SEQ(reqpkt.auth_seq));
853 #endif
854                         continue;
855                 }
856
857                 if (reqpkt.implementation != IMPL_XNTPD ||
858                     reqpkt.request != REQ_HOSTNAME_ASSOCID) {
859 #ifdef DEBUG
860                         if (debug > 1)
861                             msyslog(LOG_INFO,
862                                     "implementation (%d/%d) or request (%d/%d) incorrect",
863                                     reqpkt.implementation, IMPL_XNTPD,
864                                     reqpkt.request, REQ_HOSTNAME_ASSOCID);
865 #endif
866                         continue;
867                 }
868
869                 if (INFO_NITEMS(reqpkt.err_nitems) != 0 ||
870                     INFO_MBZ(reqpkt.mbz_itemsize) != 0 ||
871                     INFO_ITEMSIZE(reqpkt.mbz_itemsize) != 0) {
872 #ifdef DEBUG
873                         if (debug > 1)
874                             msyslog(LOG_INFO,
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));
879 #endif
880                         continue;
881                 }
882
883                 n = INFO_ERR(reqpkt.err_nitems);
884                 switch (n) {
885                     case INFO_OKAY:
886                         /* success */
887                         return 1;
888                 
889                     case INFO_ERR_IMPL:
890                         msyslog(LOG_ERR,
891                                 "server reports implementation mismatch!!");
892                         return 0;
893                 
894                     case INFO_ERR_REQ:
895                         msyslog(LOG_ERR,
896                                 "server claims configuration request is unknown");
897                         return 0;
898                 
899                     case INFO_ERR_FMT:
900                         msyslog(LOG_ERR,
901                                 "server indicates a format error occurred(!!)");
902                         return 0;
903
904                     case INFO_ERR_NODATA:
905                         msyslog(LOG_ERR,
906                                 "server indicates no data available (shouldn't happen)");
907                         return 0;
908                 
909                     case INFO_ERR_AUTH:
910                         msyslog(LOG_ERR,
911                                 "server returns a permission denied error");
912                         return 0;
913
914                     default:
915                         msyslog(LOG_ERR,
916                                 "server returns unknown error code %d", n);
917                         return 0;
918                 }
919         }
920 }
921
922
923 /*
924  * doconfigure - attempt to resolve names/addresses
925  */
926 static void
927 doconfigure(
928         int dores
929         )
930 {
931         register struct dns_entry *de;
932         register struct dns_entry *deremove;
933         char *done_msg = "";
934
935         de = dns_entries;
936         while (de != NULL) {
937 #ifdef DEBUG
938                 if (debug > 1) {
939                         struct in_addr si;
940
941                         si.s_addr = de->de_peeraddr;
942                         msyslog(LOG_INFO,
943                             "doconfigure: name: <%s> peeraddr: %s",
944                             de->de_hostname, inet_ntoa(si));
945                 }
946 #endif
947                 if (dores && (de->de_hostname[0] == 0 || de->de_peeraddr == 0)) {
948                         findhostaddr(de);
949                 }
950
951                 switch (de->de_done & DE_RESULT) {
952                     case DE_PENDING:
953                         done_msg = "";
954                         break;
955                     case DE_GOT:
956                         done_msg = "succeeded";
957                         break;
958                     case DE_FAIL:
959                         done_msg = "failed";
960                         break;
961                     default:
962                         done_msg = "(error - shouldn't happen)";
963                         break;
964                 }
965                 if (done_msg[0]) {
966                         /* Send the answer */
967                         if (tell_ntpd(&de->de_info)) {
968                                 struct in_addr si;
969
970                                 si.s_addr = de->de_peeraddr;
971 #ifdef DEBUG
972                                 if (debug > 1) {
973                                         msyslog(LOG_INFO,
974                                                 "DNS resolution on <%s>/%s %s",
975                                                 de->de_hostname, inet_ntoa(si),
976                                                 done_msg);
977                                 }
978 #endif
979                                 deremove = de;
980                                 de = deremove->de_next;
981                                 removeentry(deremove);
982                         }
983                 } else {
984                         de = de->de_next;
985                 }
986         }
987 }