Fix a serious bug in the NTPD loopfilter. Basically what happens is that
[dragonfly.git] / contrib / ntp / ntpd / ntp_intres.c
1 /*
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.
5  */
6
7 /*
8  * ntpres - process configuration entries which require use of the resolver
9  *
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.
16  *
17  */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 #include "ntpd.h"
24 #include "ntp_io.h"
25 #include "ntp_request.h"
26 #include "ntp_stdlib.h"
27 #include "ntp_syslog.h"
28
29 #include <stdio.h>
30 #include <ctype.h>
31 #include <netdb.h>
32 #include <signal.h>
33
34 /**/
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
37 /**/
38 #ifdef HAVE_SYS_PARAM_H
39 # include <sys/param.h>         /* MAXHOSTNAMELEN (often) */
40 #endif
41
42 #define STREQ(a, b)     (*(a) == *(b) && strcmp((a), (b)) == 0)
43
44 /*
45  * Each item we are to resolve and configure gets one of these
46  * structures defined for it.
47  */
48 struct conf_entry {
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 */
52 };
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
62
63 /*
64  * confentries is a pointer to the list of configuration entries
65  * we have left to do.
66  */
67 static struct conf_entry *confentries = NULL;
68
69 /*
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.
75  *
76  * We sleep SLEEPTIME seconds before doing anything, to give the server
77  * time to arrange itself.
78  */
79 #define MINRESOLVE      2
80 #define MAXRESOLVE      32
81 #define CONFIG_TIME     2
82 #define ALARM_TIME      30
83 #define SLEEPTIME       2
84
85 static  volatile int config_timer = 0;
86 static  volatile int resolve_timer = 0;
87
88 static  int resolve_value;      /* next value of resolve timer */
89
90 /*
91  * Big hack attack
92  */
93 #define LOCALHOST       0x7f000001      /* 127.0.0.1, in hex, of course */
94 #define SKEWTIME        0x08000000      /* 0.03125 seconds as a l_fp fraction */
95
96 /*
97  * Select time out.  Set to 2 seconds.  The server is on the local machine,
98  * after all.
99  */
100 #define TIMEOUT_SEC     2
101 #define TIMEOUT_USEC    0
102
103
104 /*
105  * Input processing.  The data on each line in the configuration file
106  * is supposed to consist of entries in the following order
107  */
108 #define TOK_HOSTNAME    0
109 #define TOK_HMODE       1
110 #define TOK_VERSION     2
111 #define TOK_MINPOLL     3
112 #define TOK_MAXPOLL     4
113 #define TOK_FLAGS       5
114 #define TOK_TTL         6
115 #define TOK_KEYID       7
116 #define TOK_KEYSTR      8
117 #define NUMTOK          9
118
119 #define MAXLINESIZE     512
120
121
122 /*
123  * File descriptor for ntp request code.
124  */
125 static  int sockfd = -1;
126
127
128 /* stuff to be filled in by caller */
129
130 keyid_t req_keyid;      /* request keyid */
131 char *req_file;         /* name of the file with configuration info */
132
133 /* end stuff to be filled in */
134
135
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));
147
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) */
152 };
153
154 struct ntp_res_c_pkt {          /* Control packet: */
155         char name[MAXHOSTNAMELEN];
156         u_int32 paddr;
157         int mode;
158         int version;
159         int minpoll;
160         int maxpoll;
161         u_int flags;
162         int ttl;
163         keyid_t keyid;
164         u_char keystr[MAXFILENAME];
165 };
166
167
168 /*
169  * ntp_res_recv: Process an answer from the resolver
170  */
171
172 void
173 ntp_res_recv(void)
174 {
175         /*
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".
179         */
180 }
181
182
183 /*
184  * ntp_intres needs;
185  *
186  *      req_key(???), req_keyid, req_file valid
187  *      syslog still open
188  */
189
190 void
191 ntp_intres(void)
192 {
193         FILE *in;
194 #ifdef HAVE_SIGSUSPEND
195         sigset_t set;
196
197         sigemptyset(&set);
198 #endif /* HAVE_SIGSUSPEND */
199
200 #ifdef DEBUG
201         if (debug > 1) {
202                 msyslog(LOG_INFO, "NTP_INTRES running");
203         }
204 #endif
205
206         /* check out auth stuff */
207         if (sys_authenticate) {
208                 if (!authistrusted(req_keyid)) {
209                         msyslog(LOG_ERR, "invalid request keyid %08x",
210                             req_keyid );
211                         exit(1);
212                 }
213         }
214
215         /*
216          * Read the configuration info
217          * {this is bogus, since we are forked, but it is easier
218          * to keep this code - gdt}
219          */
220         if ((in = fopen(req_file, "r")) == NULL) {
221                 msyslog(LOG_ERR, "can't open configuration file %s: %m",
222                         req_file);
223                 exit(1);
224         }
225         readconf(in, req_file);
226         (void) fclose(in);
227
228         if (!debug )
229                 (void) unlink(req_file);
230
231         /*
232          * Sleep a little to make sure the server is completely up
233          */
234
235         sleep(SLEEPTIME);
236
237         /*
238          * Make a first cut at resolving the bunch
239          */
240         doconfigure(1);
241         if (confentries == NULL) {
242 #if defined SYS_WINNT
243                 ExitThread(0);  /* Don't want to kill whole NT process */
244 #else
245                 exit(0);        /* done that quick */
246 #endif
247         }
248         
249         /*
250          * Here we've got some problem children.  Set up the timer
251          * and wait for it.
252          */
253         resolve_value = resolve_timer = MINRESOLVE;
254         config_timer = CONFIG_TIME;
255 #ifndef SYS_WINNT
256         (void) signal_no_reset(SIGALRM, bong);
257         alarm(ALARM_TIME);
258 #endif /* SYS_WINNT */
259
260         for (;;) {
261                 if (confentries == NULL)
262                     exit(0);
263
264                 checkparent();
265
266                 if (resolve_timer == 0) {
267                         if (resolve_value < MAXRESOLVE)
268                             resolve_value <<= 1;
269                         resolve_timer = resolve_value;
270 #ifdef DEBUG
271                         if (debug > 2)
272                                 msyslog(LOG_INFO, "resolve_timer: 0->%d", resolve_timer);
273 #endif
274                         config_timer = CONFIG_TIME;
275                         doconfigure(1);
276                         continue;
277                 } else if (config_timer == 0) {
278                         config_timer = CONFIG_TIME;
279 #ifdef DEBUG
280                         if (debug > 2)
281                                 msyslog(LOG_INFO, "config_timer: 0->%d", config_timer);
282 #endif
283                         doconfigure(0);
284                         continue;
285                 }
286 #ifndef SYS_WINNT
287                 /*
288                  * There is a race in here.  Is okay, though, since
289                  * all it does is delay things by 30 seconds.
290                  */
291 # ifdef HAVE_SIGSUSPEND
292                 sigsuspend(&set);
293 # else
294                 sigpause(0);
295 # endif /* HAVE_SIGSUSPEND */
296 #else
297                 if (config_timer > 0)
298                     config_timer--;
299                 if (resolve_timer > 0)
300                     resolve_timer--;
301                 sleep(ALARM_TIME);
302 #endif /* SYS_WINNT */
303         }
304 }
305
306
307 #ifndef SYS_WINNT
308 /*
309  * bong - service and reschedule an alarm() interrupt
310  */
311 static RETSIGTYPE
312 bong(
313         int sig
314         )
315 {
316         if (config_timer > 0)
317             config_timer--;
318         if (resolve_timer > 0)
319             resolve_timer--;
320         alarm(ALARM_TIME);
321 }
322 #endif /* SYS_WINNT */
323
324 /*
325  * checkparent - see if our parent process is still running
326  *
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;
332  */
333
334 static void
335 checkparent(void)
336 {
337 #if !defined (SYS_WINNT) && !defined (SYS_VXWORKS)
338
339         /*
340          * If our parent (the server) has died we will have been
341          * inherited by init.  If so, exit.
342          */
343         if (getppid() == 1) {
344                 msyslog(LOG_INFO, "parent died before we finished, exiting");
345                 exit(0);
346         }
347 #endif /* SYS_WINNT && SYS_VXWORKS*/
348 }
349
350
351
352 /*
353  * removeentry - we are done with an entry, remove it from the list
354  */
355 static void
356 removeentry(
357         struct conf_entry *entry
358         )
359 {
360         register struct conf_entry *ce;
361
362         ce = confentries;
363         if (ce == entry) {
364                 confentries = ce->ce_next;
365                 return;
366         }
367
368         while (ce != NULL) {
369                 if (ce->ce_next == entry) {
370                         ce->ce_next = entry->ce_next;
371                         return;
372                 }
373                 ce = ce->ce_next;
374         }
375 }
376
377
378 /*
379  * addentry - add an entry to the configuration list
380  */
381 static void
382 addentry(
383         char *name,
384         int mode,
385         int version,
386         int minpoll,
387         int maxpoll,
388         u_int flags,
389         int ttl,
390         keyid_t keyid,
391         char *keystr
392         )
393 {
394         register char *cp;
395         register struct conf_entry *ce;
396         unsigned int len;
397
398 #ifdef DEBUG
399         if (debug > 1)
400                 msyslog(LOG_INFO, 
401                     "intres: <%s> %d %d %d %d %x %d %x %s\n", name,
402                     mode, version, minpoll, maxpoll, flags, ttl, keyid,
403                     keystr);
404 #endif
405         len = strlen(name) + 1;
406         cp = (char *)emalloc(len);
407         memmove(cp, name, len);
408
409         ce = (struct conf_entry *)emalloc(sizeof(struct conf_entry));
410         ce->ce_name = cp;
411         ce->ce_peeraddr = 0;
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);
420         ce->ce_next = NULL;
421
422         if (confentries == NULL) {
423                 confentries = ce;
424         } else {
425                 register struct conf_entry *cep;
426
427                 for (cep = confentries; cep->ce_next != NULL;
428                      cep = cep->ce_next)
429                     /* nothing */;
430                 cep->ce_next = ce;
431         }
432 }
433
434
435 /*
436  * findhostaddr - resolve a host name into an address (Or vice-versa)
437  *
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.
442  */
443 static int
444 findhostaddr(
445         struct conf_entry *entry
446         )
447 {
448         struct hostent *hp;
449         struct in_addr in;
450
451         checkparent();          /* make sure our guy is still running */
452
453         if (entry->ce_name && entry->ce_peeraddr) {
454                 /* HMS: Squawk? */
455                 msyslog(LOG_ERR, "findhostaddr: both ce_name and ce_peeraddr are defined...");
456                 return 1;
457         }
458
459         if (!entry->ce_name && !entry->ce_peeraddr) {
460                 msyslog(LOG_ERR, "findhostaddr: both ce_name and ce_peeraddr are undefined!");
461                 return 0;
462         }
463
464         if (entry->ce_name) {
465 #ifdef DEBUG
466                 if (debug > 2)
467                         msyslog(LOG_INFO, "findhostaddr: Resolving <%s>",
468                                 entry->ce_name);
469 #endif /* DEBUG */
470                 hp = gethostbyname(entry->ce_name);
471         } else {
472 #ifdef DEBUG
473                 if (debug > 2)
474                         msyslog(LOG_INFO, "findhostaddr: Resolving %x>",
475                                 entry->ce_peeraddr);
476 #endif
477                 in.s_addr = entry->ce_peeraddr;
478                 hp = gethostbyaddr((const char *)&in,
479                                    sizeof entry->ce_peeraddr,
480                                    AF_INET);
481         }
482
483         if (hp == NULL) {
484                 /*
485                  * If the resolver is in use, see if the failure is
486                  * temporary.  If so, return success.
487                  */
488                 if (h_errno == TRY_AGAIN)
489                     return (1);
490                 return (0);
491         }
492
493         if (entry->ce_name) {
494 #ifdef DEBUG
495                 if (debug > 2)
496                         msyslog(LOG_INFO, "findhostaddr: name resolved.");
497 #endif
498                 /*
499                  * Use the first address.  We don't have any way to tell
500                  * preferences and older gethostbyname() implementations
501                  * only return one.
502                  */
503                 memmove((char *)&(entry->ce_peeraddr),
504                         (char *)hp->h_addr,
505                         sizeof(struct in_addr));
506                 if (entry->ce_keystr[0] == '*')
507                         strncpy((char *)&(entry->ce_keystr), hp->h_name,
508                                 MAXFILENAME);
509         } else {
510                 char *cp;
511                 size_t s;
512
513 #ifdef DEBUG
514                 if (debug > 2)
515                         msyslog(LOG_INFO, "findhostaddr: address resolved.");
516 #endif
517                 s = strlen(hp->h_name) + 1;
518                 cp = emalloc(s);
519                 strcpy(cp, hp->h_name);
520                 entry->ce_name = cp;
521         }
522                    
523         return (1);
524 }
525
526
527 /*
528  * openntp - open a socket to the ntp server
529  */
530 static void
531 openntp(void)
532 {
533         struct sockaddr_in saddr;
534
535         if (sockfd >= 0)
536             return;
537         
538         sockfd = socket(AF_INET, SOCK_DGRAM, 0);
539         if (sockfd == -1) {
540                 msyslog(LOG_ERR, "socket() failed: %m");
541                 exit(1);
542         }
543
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 */
548
549         /*
550          * Make the socket non-blocking.  We'll wait with select()
551          */
552 #ifndef SYS_WINNT
553 #if defined(O_NONBLOCK)
554         if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == -1) {
555                 msyslog(LOG_ERR, "fcntl(O_NONBLOCK) failed: %m");
556                 exit(1);
557         }
558 #else
559 #if defined(FNDELAY)
560         if (fcntl(sockfd, F_SETFL, FNDELAY) == -1) {
561                 msyslog(LOG_ERR, "fcntl(FNDELAY) failed: %m");
562                 exit(1);
563         }
564 #else
565 # include "Bletch: NEED NON BLOCKING IO"
566 #endif /* FNDDELAY */
567 #endif /* O_NONBLOCK */
568 #else  /* SYS_WINNT */
569         {
570                 int on = 1;
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 */
574                 }
575         }
576 #endif /* SYS_WINNT */
577
578
579         if (connect(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) {
580                 msyslog(LOG_ERR, "openntp: connect() failed: %m");
581                 exit(1);
582         }
583 }
584
585
586 /*
587  * request - send a configuration request to the server, wait for a response
588  */
589 static int
590 request(
591         struct conf_peer *conf
592         )
593 {
594         fd_set fdset;
595         struct timeval tvout;
596         struct req_pkt reqpkt;
597         l_fp ts;
598         int n;
599 #ifdef SYS_WINNT
600         HANDLE hReadWriteEvent = NULL;
601         BOOL ret;
602         DWORD NumberOfBytesWritten, NumberOfBytesRead, dwWait;
603         OVERLAPPED overlap;
604 #endif /* SYS_WINNT */
605
606         checkparent();          /* make sure our guy is still running */
607
608         if (sockfd < 0)
609             openntp();
610         
611 #ifdef SYS_WINNT
612         hReadWriteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
613 #endif /* SYS_WINNT */
614
615         /*
616          * Try to clear out any previously received traffic so it
617          * doesn't fool us.  Note the socket is nonblocking.
618          */
619         tvout.tv_sec =  0;
620         tvout.tv_usec = 0;
621         FD_ZERO(&fdset);
622         FD_SET(sockfd, &fdset);
623         while (select(sockfd + 1, &fdset, (fd_set *)0, (fd_set *)0, &tvout) >
624                0) {
625                 recv(sockfd, (char *)&reqpkt, REQ_LEN_MAC, 0);
626                 FD_ZERO(&fdset);
627                 FD_SET(sockfd, &fdset);
628         }
629
630         /*
631          * Make up a request packet with the configuration info
632          */
633         memset((char *)&reqpkt, 0, sizeof(reqpkt));
634
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!");
644                 exit(1);
645         }
646         memmove(reqpkt.data, (char *)conf, sizeof(struct conf_peer));
647         reqpkt.keyid = htonl(req_keyid);
648
649         get_systime(&ts);
650         L_ADDUF(&ts, SKEWTIME);
651         HTONL_FP(&ts, &reqpkt.tstamp);
652         n = 0;
653         if (sys_authenticate)
654                 n = authencrypt(req_keyid, (u_int32 *)&reqpkt, REQ_LEN_NOMAC);
655
656         /*
657          * Done.  Send it.
658          */
659 #ifndef SYS_WINNT
660         n = send(sockfd, (char *)&reqpkt, (unsigned)(REQ_LEN_NOMAC + n), 0);
661         if (n < 0) {
662                 msyslog(LOG_ERR, "send to NTP server failed: %m");
663                 return 0;       /* maybe should exit */
664         }
665 #else
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
674          * on the socket
675          */
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");
682                 return 0;
683         }
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");
688                 return 0;
689         }
690 #endif /* SYS_WINNT */
691     
692
693         /*
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
700          * at all, we get it.
701          */
702         for (;;) {
703                 FD_ZERO(&fdset);
704                 FD_SET(sockfd, &fdset);
705                 tvout.tv_sec = TIMEOUT_SEC;
706                 tvout.tv_usec = TIMEOUT_USEC;
707
708                 n = select(sockfd + 1, &fdset, (fd_set *)0,
709                            (fd_set *)0, &tvout);
710
711                 if (n < 0)
712                 {
713                         msyslog(LOG_ERR, "select() fails: %m");
714                         return 0;
715                 }
716                 else if (n == 0)
717                 {
718                         if (debug)
719                             msyslog(LOG_INFO, "select() returned 0.");
720                         return 0;
721                 }
722
723 #ifndef SYS_WINNT
724                 n = recv(sockfd, (char *)&reqpkt, REQ_LEN_MAC, 0);
725                 if (n <= 0) {
726                         if (n < 0) {
727                                 msyslog(LOG_ERR, "recv() fails: %m");
728                                 return 0;
729                         }
730                         continue;
731                 }
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");
737                         return 0;
738                 }
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");
743                                 return 0;
744                         }
745                         continue;
746                 }
747                 n = NumberOfBytesRead;
748 #endif /* SYS_WINNT */
749
750                 /*
751                  * Got one.  Check through to make sure it is what
752                  * we expect.
753                  */
754                 if (n < RESP_HEADER_SIZE) {
755                         msyslog(LOG_ERR, "received runt response (%d octets)",
756                                 n);
757                         continue;
758                 }
759
760                 if (!ISRESPONSE(reqpkt.rm_vn_mode)) {
761 #ifdef DEBUG
762                         if (debug > 1)
763                             msyslog(LOG_INFO, "received non-response packet");
764 #endif
765                         continue;
766                 }
767
768                 if (ISMORE(reqpkt.rm_vn_mode)) {
769 #ifdef DEBUG
770                         if (debug > 1)
771                             msyslog(LOG_INFO, "received fragmented packet");
772 #endif
773                         continue;
774                 }
775
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) {
779 #ifdef DEBUG
780                         if (debug > 1)
781                             msyslog(LOG_INFO,
782                                     "version (%d/%d) or mode (%d/%d) incorrect",
783                                     INFO_VERSION(reqpkt.rm_vn_mode),
784                                     NTP_VERSION,
785                                     INFO_MODE(reqpkt.rm_vn_mode),
786                                     MODE_PRIVATE);
787 #endif
788                         continue;
789                 }
790
791                 if (INFO_SEQ(reqpkt.auth_seq) != 0) {
792 #ifdef DEBUG
793                         if (debug > 1)
794                             msyslog(LOG_INFO,
795                                     "nonzero sequence number (%d)",
796                                     INFO_SEQ(reqpkt.auth_seq));
797 #endif
798                         continue;
799                 }
800
801                 if (reqpkt.implementation != IMPL_XNTPD ||
802                     reqpkt.request != REQ_CONFIG) {
803 #ifdef DEBUG
804                         if (debug > 1)
805                             msyslog(LOG_INFO,
806                                     "implementation (%d) or request (%d) incorrect",
807                                     reqpkt.implementation, reqpkt.request);
808 #endif
809                         continue;
810                 }
811
812                 if (INFO_NITEMS(reqpkt.err_nitems) != 0 ||
813                     INFO_MBZ(reqpkt.mbz_itemsize) != 0 ||
814                     INFO_ITEMSIZE(reqpkt.mbz_itemsize) != 0) {
815 #ifdef DEBUG
816                         if (debug > 1)
817                             msyslog(LOG_INFO,
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));
822 #endif
823                         continue;
824                 }
825
826                 n = INFO_ERR(reqpkt.err_nitems);
827                 switch (n) {
828                     case INFO_OKAY:
829                         /* success */
830                         return 1;
831                 
832                     case INFO_ERR_IMPL:
833                         msyslog(LOG_ERR,
834                                 "server reports implementation mismatch!!");
835                         return 0;
836                 
837                     case INFO_ERR_REQ:
838                         msyslog(LOG_ERR,
839                                 "server claims configuration request is unknown");
840                         return 0;
841                 
842                     case INFO_ERR_FMT:
843                         msyslog(LOG_ERR,
844                                 "server indicates a format error occurred(!!)");
845                         return 0;
846
847                     case INFO_ERR_NODATA:
848                         msyslog(LOG_ERR,
849                                 "server indicates no data available (shouldn't happen)");
850                         return 0;
851                 
852                     case INFO_ERR_AUTH:
853                         msyslog(LOG_ERR,
854                                 "server returns a permission denied error");
855                         return 0;
856
857                     default:
858                         msyslog(LOG_ERR,
859                                 "server returns unknown error code %d", n);
860                         return 0;
861                 }
862         }
863 }
864
865
866 /*
867  * nexttoken - return the next token from a line
868  */
869 static char *
870 nexttoken(
871         char **lptr
872         )
873 {
874         register char *cp;
875         register char *tstart;
876
877         cp = *lptr;
878
879         /*
880          * Skip leading white space
881          */
882         while (*cp == ' ' || *cp == '\t')
883             cp++;
884         
885         /*
886          * If this is the end of the line, return nothing.
887          */
888         if (*cp == '\n' || *cp == '\0') {
889                 *lptr = cp;
890                 return NULL;
891         }
892         
893         /*
894          * Must be the start of a token.  Record the pointer and look
895          * for the end.
896          */
897         tstart = cp++;
898         while (*cp != ' ' && *cp != '\t' && *cp != '\n' && *cp != '\0')
899             cp++;
900         
901         /*
902          * Terminate the token with a \0.  If this isn't the end of the
903          * line, space to the next character.
904          */
905         if (*cp == '\n' || *cp == '\0')
906             *cp = '\0';
907         else
908             *cp++ = '\0';
909
910         *lptr = cp;
911         return tstart;
912 }
913
914
915 /*
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.
919  */
920 static void
921 readconf(
922         FILE *fp,
923         char *name
924         )
925 {
926         register int i;
927         char *token[NUMTOK];
928         u_long intval[NUMTOK];
929         u_int flags;
930         char buf[MAXLINESIZE];
931         char *bp;
932
933         while (fgets(buf, MAXLINESIZE, fp) != NULL) {
934
935                 bp = buf;
936                 for (i = 0; i < NUMTOK; i++) {
937                         if ((token[i] = nexttoken(&bp)) == NULL) {
938                                 msyslog(LOG_ERR,
939                                         "tokenizing error in file `%s', quitting",
940                                         name);
941                                 exit(1);
942                         }
943                 }
944
945                 for (i = 1; i < NUMTOK - 1; i++) {
946                         if (!atouint(token[i], &intval[i])) {
947                                 msyslog(LOG_ERR,
948                                         "format error for integer token `%s', file `%s', quitting",
949                                         token[i], name);
950                                 exit(1);
951                         }
952                 }
953
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);
959                         exit(1);
960                 }
961
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);
966                         exit(1);
967                 }
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);
972                         exit(1);
973                 }
974
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);
979                         exit(1);
980                 }
981
982                 if ((intval[TOK_FLAGS] & ~(FLAG_AUTHENABLE | FLAG_PREFER |
983                     FLAG_NOSELECT | FLAG_BURST | FLAG_IBURST | FLAG_SKEY))
984                     != 0) {
985                         msyslog(LOG_ERR, "invalid flags (%ld) in file %s",
986                                 intval[TOK_FLAGS], name);
987                         exit(1);
988                 }
989
990                 flags = 0;
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;
1003
1004                 /*
1005                  * This is as good as we can check it.  Add it in.
1006                  */
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]);
1011         }
1012 }
1013
1014
1015 /*
1016  * doconfigure - attempt to resolve names and configure the server
1017  */
1018 static void
1019 doconfigure(
1020         int dores
1021         )
1022 {
1023         register struct conf_entry *ce;
1024         register struct conf_entry *ceremove;
1025
1026         ce = confentries;
1027         while (ce != NULL) {
1028 #ifdef DEBUG
1029                 if (debug > 1)
1030                         msyslog(LOG_INFO,
1031                             "doconfigure: <%s> has peeraddr %#x",
1032                             ce->ce_name, ce->ce_peeraddr);
1033 #endif
1034                 if (dores && ce->ce_peeraddr == 0) {
1035                         if (!findhostaddr(ce)) {
1036                                 msyslog(LOG_ERR,
1037                                         "couldn't resolve `%s', giving up on it",
1038                                         ce->ce_name);
1039                                 ceremove = ce;
1040                                 ce = ceremove->ce_next;
1041                                 removeentry(ceremove);
1042                                 continue;
1043                         }
1044                 }
1045
1046                 if (ce->ce_peeraddr != 0) {
1047                         if (request(&ce->ce_config)) {
1048                                 ceremove = ce;
1049                                 ce = ceremove->ce_next;
1050                                 removeentry(ceremove);
1051                                 continue;
1052                         }
1053 #ifdef DEBUG
1054                         if (debug > 1) {
1055                                 msyslog(LOG_INFO,
1056                                     "doconfigure: request() FAILED, maybe next time.");
1057                         }
1058 #endif
1059                 }
1060                 ce = ce->ce_next;
1061         }
1062 }