]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - contrib/ntp/ntpd/ntp_intres.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.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  * For special situations define the FORCE_DNSRETRY Macro
20  * to force retries even if it fails the lookup.
21  * Use with extreme caution since it will then retry forever.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 # include <config.h>
26 #endif
27
28 #include "ntp_machine.h"
29 #include "ntpd.h"
30 #include "ntp_io.h"
31 #include "ntp_request.h"
32 #include "ntp_stdlib.h"
33 #include "ntp_syslog.h"
34
35 #include <stdio.h>
36 #include <ctype.h>
37 #include <signal.h>
38
39 /**/
40 #include <netinet/in.h>
41 #include <arpa/inet.h>
42 /**/
43 #ifdef HAVE_SYS_PARAM_H
44 # include <sys/param.h>         /* MAXHOSTNAMELEN (often) */
45 #endif
46
47 #include <isc/net.h>
48 #include <isc/result.h>
49
50 #define STREQ(a, b)     (*(a) == *(b) && strcmp((a), (b)) == 0)
51
52 /*
53  * Each item we are to resolve and configure gets one of these
54  * structures defined for it.
55  */
56 struct conf_entry {
57         struct conf_entry *ce_next;
58         char *ce_name;                  /* name we are trying to resolve */
59         struct conf_peer ce_config;     /* configuration info for peer */
60         struct sockaddr_storage peer_store; /* address info for both fams */
61 };
62 #define ce_peeraddr     ce_config.peeraddr
63 #define ce_peeraddr6    ce_config.peeraddr6
64 #define ce_hmode        ce_config.hmode
65 #define ce_version      ce_config.version
66 #define ce_minpoll      ce_config.minpoll
67 #define ce_maxpoll      ce_config.maxpoll
68 #define ce_flags        ce_config.flags
69 #define ce_ttl          ce_config.ttl
70 #define ce_keyid        ce_config.keyid
71 #define ce_keystr       ce_config.keystr
72
73 /*
74  * confentries is a pointer to the list of configuration entries
75  * we have left to do.
76  */
77 static struct conf_entry *confentries = NULL;
78
79 /*
80  * We take an interrupt every thirty seconds, at which time we decrement
81  * config_timer and resolve_timer.  The former is set to 2, so we retry
82  * unsucessful reconfigurations every minute.  The latter is set to
83  * an exponentially increasing value which starts at 2 and increases to
84  * 32.  When this expires we retry failed name resolutions.
85  *
86  * We sleep SLEEPTIME seconds before doing anything, to give the server
87  * time to arrange itself.
88  */
89 #define MINRESOLVE      2
90 #define MAXRESOLVE      32
91 #define CONFIG_TIME     2
92 #define ALARM_TIME      30
93 #define SLEEPTIME       2
94
95 static  volatile int config_timer = 0;
96 static  volatile int resolve_timer = 0;
97
98 static  int resolve_value;      /* next value of resolve timer */
99
100 /*
101  * Big hack attack
102  */
103 #define LOCALHOST       0x7f000001      /* 127.0.0.1, in hex, of course */
104 #define SKEWTIME        0x08000000      /* 0.03125 seconds as a l_fp fraction */
105
106 /*
107  * Select time out.  Set to 2 seconds.  The server is on the local machine,
108  * after all.
109  */
110 #define TIMEOUT_SEC     2
111 #define TIMEOUT_USEC    0
112
113
114 /*
115  * Input processing.  The data on each line in the configuration file
116  * is supposed to consist of entries in the following order
117  */
118 #define TOK_HOSTNAME    0
119 #define TOK_HMODE       1
120 #define TOK_VERSION     2
121 #define TOK_MINPOLL     3
122 #define TOK_MAXPOLL     4
123 #define TOK_FLAGS       5
124 #define TOK_TTL         6
125 #define TOK_KEYID       7
126 #define TOK_KEYSTR      8
127 #define NUMTOK          9
128
129 #define MAXLINESIZE     512
130
131
132 /*
133  * File descriptor for ntp request code.
134  */
135 static  SOCKET sockfd = INVALID_SOCKET; /* NT uses SOCKET */
136
137 /* stuff to be filled in by caller */
138
139 keyid_t req_keyid;      /* request keyid */
140 char *req_file;         /* name of the file with configuration info */
141
142 /* end stuff to be filled in */
143
144
145 static  void    checkparent     P((void));
146 static  void    removeentry     P((struct conf_entry *));
147 static  void    addentry        P((char *, int, int, int, int, u_int,
148                                    int, keyid_t, char *));
149 static  int     findhostaddr    P((struct conf_entry *));
150 static  void    openntp         P((void));
151 static  int     request         P((struct conf_peer *));
152 static  char *  nexttoken       P((char **));
153 static  void    readconf        P((FILE *, char *));
154 static  void    doconfigure     P((int));
155
156 struct ntp_res_t_pkt {          /* Tagged packet: */
157         void *tag;              /* For the caller */
158         u_int32 paddr;          /* IP to look up, or 0 */
159         char name[MAXHOSTNAMELEN]; /* Name to look up (if 1st byte is not 0) */
160 };
161
162 struct ntp_res_c_pkt {          /* Control packet: */
163         char name[MAXHOSTNAMELEN];
164         u_int32 paddr;
165         int mode;
166         int version;
167         int minpoll;
168         int maxpoll;
169         u_int flags;
170         int ttl;
171         keyid_t keyid;
172         u_char keystr[MAXFILENAME];
173 };
174
175
176 static void     resolver_exit P((int));
177
178 /*
179  * Call here instead of just exiting
180  */
181
182 static void resolver_exit (int code)
183 {
184 #ifdef SYS_WINNT
185         CloseHandle(ResolverEventHandle);
186         ResolverEventHandle = NULL;
187         ExitThread(code);       /* Just to kill the thread not the process */
188 #else
189         exit(code);             /* kill the forked process */
190 #endif
191 }
192
193 /*
194  * ntp_res_recv: Process an answer from the resolver
195  */
196
197 void
198 ntp_res_recv(void)
199 {
200         /*
201           We have data ready on our descriptor.
202           It may be an EOF, meaning the resolver process went away.
203           Otherwise, it will be an "answer".
204         */
205 }
206
207
208 /*
209  * ntp_intres needs;
210  *
211  *      req_key(???), req_keyid, req_file valid
212  *      syslog still open
213  */
214
215 void
216 ntp_intres(void)
217 {
218         FILE *in;
219         struct timeval tv;
220         fd_set fdset;
221 #ifdef SYS_WINNT
222         DWORD rc;
223 #else
224         int rc;
225 #endif
226
227 #ifdef DEBUG
228         if (debug > 1) {
229                 msyslog(LOG_INFO, "NTP_INTRES running");
230         }
231 #endif
232
233         /* check out auth stuff */
234         if (sys_authenticate) {
235                 if (!authistrusted(req_keyid)) {
236                         msyslog(LOG_ERR, "invalid request keyid %08x",
237                             req_keyid );
238                         resolver_exit(1);
239                 }
240         }
241
242         /*
243          * Read the configuration info
244          * {this is bogus, since we are forked, but it is easier
245          * to keep this code - gdt}
246          */
247         if ((in = fopen(req_file, "r")) == NULL) {
248                 msyslog(LOG_ERR, "can't open configuration file %s: %m",
249                         req_file);
250                 resolver_exit(1);
251         }
252         readconf(in, req_file);
253         (void) fclose(in);
254
255 #ifdef DEBUG
256         if (!debug )
257 #endif
258                 (void) unlink(req_file);
259
260         /*
261          * Set up the timers to do first shot immediately.
262          */
263         resolve_timer = 0;
264         resolve_value = MINRESOLVE;
265         config_timer = CONFIG_TIME;
266
267         for (;;) {
268                 checkparent();
269
270                 if (resolve_timer == 0) {
271                         /*
272                          * Sleep a little to make sure the network is completely up
273                          */
274                         sleep(SLEEPTIME);
275                         doconfigure(1);
276
277                         /* prepare retry, in case there's more work to do */
278                         resolve_timer = resolve_value;
279 #ifdef DEBUG
280                         if (debug > 2)
281                                 msyslog(LOG_INFO, "resolve_timer: 0->%d", resolve_timer);
282 #endif
283                         if (resolve_value < MAXRESOLVE)
284                                 resolve_value <<= 1;
285
286                         config_timer = CONFIG_TIME;
287                 } else if (config_timer == 0) {  /* MB: in which case would this be required ? */
288                         doconfigure(0);
289                         /* MB: should we check now if we could exit, similar to the code above? */
290                         config_timer = CONFIG_TIME;
291 #ifdef DEBUG
292                         if (debug > 2)
293                                 msyslog(LOG_INFO, "config_timer: 0->%d", config_timer);
294 #endif
295                 }
296
297                 if (confentries == NULL)
298                         resolver_exit(0);   /* done */
299
300 #ifdef SYS_WINNT
301                 rc = WaitForSingleObject(ResolverEventHandle, 1000 * ALARM_TIME);  /* in milliseconds */
302
303                 if ( rc == WAIT_OBJECT_0 ) { /* signaled by the main thread */
304                         resolve_timer = 0;         /* retry resolving immediately */
305                         continue;
306                 }
307
308                 if ( rc != WAIT_TIMEOUT ) /* not timeout: error */
309                         resolver_exit(1);
310
311 #else  /* not SYS_WINNT */
312                 tv.tv_sec = ALARM_TIME;
313                 tv.tv_usec = 0;
314                 FD_ZERO(&fdset);
315                 FD_SET(resolver_pipe_fd[0], &fdset);
316                 rc = select(resolver_pipe_fd[0] + 1, &fdset, (fd_set *)0, (fd_set *)0, &tv);
317
318                 if (rc > 0) {  /* parent process has written to the pipe */
319                         read(resolver_pipe_fd[0], (char *)&rc, sizeof(rc));  /* make pipe empty */
320                         resolve_timer = 0;   /* retry resolving immediately */
321                         continue;
322                 }
323
324                 if ( rc < 0 )  /* select() returned error */
325                         resolver_exit(1);
326 #endif
327
328                 /* normal timeout, keep on waiting */
329                 if (config_timer > 0)
330                         config_timer--;
331                 if (resolve_timer > 0)
332                         resolve_timer--;
333         }
334 }
335
336
337
338 /*
339  * checkparent - see if our parent process is still running
340  *
341  * No need to worry in the Windows NT environment whether the
342  * main thread is still running, because if it goes
343  * down it takes the whole process down with it (in
344  * which case we won't be running this thread either)
345  * Turn function into NOP;
346  */
347
348 static void
349 checkparent(void)
350 {
351 #if !defined (SYS_WINNT) && !defined (SYS_VXWORKS)
352
353         /*
354          * If our parent (the server) has died we will have been
355          * inherited by init.  If so, exit.
356          */
357         if (getppid() == 1) {
358                 msyslog(LOG_INFO, "parent died before we finished, exiting");
359                 resolver_exit(0);
360         }
361 #endif /* SYS_WINNT && SYS_VXWORKS*/
362 }
363
364
365
366 /*
367  * removeentry - we are done with an entry, remove it from the list
368  */
369 static void
370 removeentry(
371         struct conf_entry *entry
372         )
373 {
374         register struct conf_entry *ce;
375
376         ce = confentries;
377         if (ce == entry) {
378                 confentries = ce->ce_next;
379                 return;
380         }
381
382         while (ce != NULL) {
383                 if (ce->ce_next == entry) {
384                         ce->ce_next = entry->ce_next;
385                         return;
386                 }
387                 ce = ce->ce_next;
388         }
389 }
390
391
392 /*
393  * addentry - add an entry to the configuration list
394  */
395 static void
396 addentry(
397         char *name,
398         int mode,
399         int version,
400         int minpoll,
401         int maxpoll,
402         u_int flags,
403         int ttl,
404         keyid_t keyid,
405         char *keystr
406         )
407 {
408         register char *cp;
409         register struct conf_entry *ce;
410         unsigned int len;
411
412 #ifdef DEBUG
413         if (debug > 1)
414                 msyslog(LOG_INFO, 
415                     "intres: <%s> %d %d %d %d %x %d %x %s\n", name,
416                     mode, version, minpoll, maxpoll, flags, ttl, keyid,
417                     keystr);
418 #endif
419         len = strlen(name) + 1;
420         cp = (char *)emalloc(len);
421         memmove(cp, name, len);
422
423         ce = (struct conf_entry *)emalloc(sizeof(struct conf_entry));
424         ce->ce_name = cp;
425         ce->ce_peeraddr = 0;
426 #ifdef ISC_PLATFORM_HAVEIPV6
427         ce->ce_peeraddr6 = in6addr_any;
428 #endif
429         ANYSOCK(&ce->peer_store);
430         ce->ce_hmode = (u_char)mode;
431         ce->ce_version = (u_char)version;
432         ce->ce_minpoll = (u_char)minpoll;
433         ce->ce_maxpoll = (u_char)maxpoll;
434         ce->ce_flags = (u_char)flags;
435         ce->ce_ttl = (u_char)ttl;
436         ce->ce_keyid = keyid;
437         strncpy((char *)ce->ce_keystr, keystr, MAXFILENAME);
438         ce->ce_next = NULL;
439
440         if (confentries == NULL) {
441                 confentries = ce;
442         } else {
443                 register struct conf_entry *cep;
444
445                 for (cep = confentries; cep->ce_next != NULL;
446                      cep = cep->ce_next)
447                     /* nothing */;
448                 cep->ce_next = ce;
449         }
450 }
451
452
453 /*
454  * findhostaddr - resolve a host name into an address (Or vice-versa)
455  *
456  * Given one of {ce_peeraddr,ce_name}, find the other one.
457  * It returns 1 for "success" and 0 for an uncorrectable failure.
458  * Note that "success" includes try again errors.  You can tell that you
459  *  got a "try again" since {ce_peeraddr,ce_name} will still be zero.
460  */
461 static int
462 findhostaddr(
463         struct conf_entry *entry
464         )
465 {
466         struct addrinfo *addr;
467         struct addrinfo hints;
468         int error;
469
470         checkparent();          /* make sure our guy is still running */
471
472         if (entry->ce_name != NULL && !SOCKNUL(&entry->peer_store)) {
473                 /* HMS: Squawk? */
474                 msyslog(LOG_ERR, "findhostaddr: both ce_name and ce_peeraddr are defined...");
475                 return 1;
476         }
477
478         if (entry->ce_name == NULL && SOCKNUL(&entry->peer_store)) {
479                 msyslog(LOG_ERR, "findhostaddr: both ce_name and ce_peeraddr are undefined!");
480                 return 0;
481         }
482
483         if (entry->ce_name) {
484 #ifdef DEBUG
485                 if (debug > 2)
486                         msyslog(LOG_INFO, "findhostaddr: Resolving <%s>",
487                                 entry->ce_name);
488 #endif /* DEBUG */
489
490                 memset(&hints, 0, sizeof(hints));
491                 hints.ai_family = AF_UNSPEC;
492                 /*
493                  * If the IPv6 stack is not available look only for IPv4 addresses
494                  */
495                 if (isc_net_probeipv6() != ISC_R_SUCCESS)
496                         hints.ai_family = AF_INET;
497
498                 error = getaddrinfo(entry->ce_name, NULL, &hints, &addr);
499                 if (error == 0) {
500                         entry->peer_store = *((struct sockaddr_storage*)(addr->ai_addr));
501                         if (entry->peer_store.ss_family == AF_INET) {
502                                 entry->ce_peeraddr =
503                                     GET_INADDR(entry->peer_store);
504                                 entry->ce_config.v6_flag = 0;
505                         } else {
506                                 entry->ce_peeraddr6 =
507                                     GET_INADDR6(entry->peer_store);
508                                 entry->ce_config.v6_flag = 1;
509                         }
510                 }
511                 else if (error == EAI_NONAME)
512                 {
513                         msyslog(LOG_ERR, "host name not found: %s", entry->ce_name);
514                 }
515         } else {
516 #ifdef DEBUG
517                 if (debug > 2)
518                         msyslog(LOG_INFO, "findhostaddr: Resolving %s>",
519                                 stoa(&entry->peer_store));
520 #endif
521                 entry->ce_name = emalloc(MAXHOSTNAMELEN);
522                 error = getnameinfo((const struct sockaddr *)&entry->peer_store,
523                                    SOCKLEN(&entry->peer_store),
524                                    (char *)&entry->ce_name, MAXHOSTNAMELEN,
525                                    NULL, 0, 0);
526         }
527 #ifdef DEBUG
528         if (debug > 2)
529                 printf("intres: got error status of: %d\n", error);
530 #endif
531
532         /*
533          * If the resolver failed, see if the failure is
534          * temporary. If so, return success.
535          */
536         if (error != 0) {
537                 switch (error)
538                 {
539                 case EAI_AGAIN:
540                         return (1);
541                 case EAI_NONAME:
542 #ifndef FORCE_DNSRETRY
543                         return (0);
544 #else
545                         return (1);
546 #endif
547 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
548                 case EAI_NODATA:
549 #endif
550                 case EAI_FAIL:
551 #ifdef EAI_SYSTEM
552                 case EAI_SYSTEM:
553                         return (1);
554 #endif
555                 default:
556                         return (0);
557                 }
558         }
559
560         if (entry->ce_name) {
561 #ifdef DEBUG
562                 if (debug > 2)
563                         msyslog(LOG_INFO, "findhostaddr: name resolved.");
564 #endif
565
566 #ifdef DEBUG
567                 if (debug > 2)
568                         msyslog(LOG_INFO, "findhostaddr: address resolved.");
569 #endif
570         }
571                    
572         return (1);
573 }
574
575
576 /*
577  * openntp - open a socket to the ntp server
578  */
579 static void
580 openntp(void)
581 {
582         struct addrinfo hints;
583         struct addrinfo *addrResult;
584         const char *localhost = "127.0.0.1";    /* Use IPv6 loopback */
585
586         if (sockfd != INVALID_SOCKET)
587             return;
588
589         memset(&hints, 0, sizeof(hints));
590
591         /*
592          * For now only bother with IPv4
593          */
594         hints.ai_family = AF_INET;
595
596         hints.ai_socktype = SOCK_DGRAM;
597         if (getaddrinfo(localhost, "ntp", &hints, &addrResult)!=0) {
598                 msyslog(LOG_ERR, "getaddrinfo failed: %m");
599                 resolver_exit(1);
600         }
601         sockfd = socket(addrResult->ai_family, addrResult->ai_socktype, 0);
602
603         if (sockfd == -1) {
604                 msyslog(LOG_ERR, "socket() failed: %m");
605                 resolver_exit(1);
606         }
607
608         /*
609          * Make the socket non-blocking.  We'll wait with select()
610          */
611 #ifndef SYS_WINNT
612 #if defined(O_NONBLOCK)
613         if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == -1) {
614                 msyslog(LOG_ERR, "fcntl(O_NONBLOCK) failed: %m");
615                 resolver_exit(1);
616         }
617 #else
618 #if defined(FNDELAY)
619         if (fcntl(sockfd, F_SETFL, FNDELAY) == -1) {
620                 msyslog(LOG_ERR, "fcntl(FNDELAY) failed: %m");
621                 resolver_exit(1);
622         }
623 #else
624 # include "Bletch: NEED NON BLOCKING IO"
625 #endif /* FNDDELAY */
626 #endif /* O_NONBLOCK */
627 #else  /* SYS_WINNT */
628         {
629                 int on = 1;
630                 if (ioctlsocket(sockfd,FIONBIO,(u_long *) &on) == SOCKET_ERROR) {
631                         msyslog(LOG_ERR, "ioctlsocket(FIONBIO) fails: %m");
632                         resolver_exit(1); /* Windows NT - set socket in non-blocking mode */
633                 }
634         }
635 #endif /* SYS_WINNT */
636         if (connect(sockfd, addrResult->ai_addr, addrResult->ai_addrlen) == -1) {
637                 msyslog(LOG_ERR, "openntp: connect() failed: %m");
638                 resolver_exit(1);
639         }
640         freeaddrinfo(addrResult);
641 }
642
643
644 /*
645  * request - send a configuration request to the server, wait for a response
646  */
647 static int
648 request(
649         struct conf_peer *conf
650         )
651 {
652         fd_set fdset;
653         struct timeval tvout;
654         struct req_pkt reqpkt;
655         l_fp ts;
656         int n;
657 #ifdef SYS_WINNT
658         HANDLE hReadWriteEvent = NULL;
659         BOOL ret;
660         DWORD NumberOfBytesWritten, NumberOfBytesRead, dwWait;
661         OVERLAPPED overlap;
662 #endif /* SYS_WINNT */
663
664         checkparent();          /* make sure our guy is still running */
665
666         if (sockfd == INVALID_SOCKET)
667             openntp();
668         
669 #ifdef SYS_WINNT
670         hReadWriteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
671 #endif /* SYS_WINNT */
672
673         /*
674          * Try to clear out any previously received traffic so it
675          * doesn't fool us.  Note the socket is nonblocking.
676          */
677         tvout.tv_sec =  0;
678         tvout.tv_usec = 0;
679         FD_ZERO(&fdset);
680         FD_SET(sockfd, &fdset);
681         while (select(sockfd + 1, &fdset, (fd_set *)0, (fd_set *)0, &tvout) >
682                0) {
683                 recv(sockfd, (char *)&reqpkt, REQ_LEN_MAC, 0);
684                 FD_ZERO(&fdset);
685                 FD_SET(sockfd, &fdset);
686         }
687
688         /*
689          * Make up a request packet with the configuration info
690          */
691         memset((char *)&reqpkt, 0, sizeof(reqpkt));
692
693         reqpkt.rm_vn_mode = RM_VN_MODE(0, 0, 0);
694         reqpkt.auth_seq = AUTH_SEQ(1, 0);       /* authenticated, no seq */
695         reqpkt.implementation = IMPL_XNTPD;     /* local implementation */
696         reqpkt.request = REQ_CONFIG;            /* configure a new peer */
697         reqpkt.err_nitems = ERR_NITEMS(0, 1);   /* one item */
698         reqpkt.mbz_itemsize = MBZ_ITEMSIZE(sizeof(struct conf_peer));
699         /* Make sure mbz_itemsize <= sizeof reqpkt.data */
700         if (sizeof(struct conf_peer) > sizeof (reqpkt.data)) {
701                 msyslog(LOG_ERR, "Bletch: conf_peer is too big for reqpkt.data!");
702                 resolver_exit(1);
703         }
704         memmove(reqpkt.data, (char *)conf, sizeof(struct conf_peer));
705         reqpkt.keyid = htonl(req_keyid);
706
707         get_systime(&ts);
708         L_ADDUF(&ts, SKEWTIME);
709         HTONL_FP(&ts, &reqpkt.tstamp);
710         n = 0;
711         if (sys_authenticate)
712                 n = authencrypt(req_keyid, (u_int32 *)&reqpkt, REQ_LEN_NOMAC);
713
714         /*
715          * Done.  Send it.
716          */
717 #ifndef SYS_WINNT
718         n = send(sockfd, (char *)&reqpkt, (unsigned)(REQ_LEN_NOMAC + n), 0);
719         if (n < 0) {
720                 msyslog(LOG_ERR, "send to NTP server failed: %m");
721                 return 0;       /* maybe should exit */
722         }
723 #else
724         /* In the NT world, documentation seems to indicate that there
725          * exist _write and _read routines that can be used to do blocking
726          * I/O on sockets. Problem is these routines require a socket
727          * handle obtained through the _open_osf_handle C run-time API
728          * of which there is no explanation in the documentation. We need
729          * nonblocking write's and read's anyway for our purpose here.
730          * We're therefore forced to deviate a little bit from the Unix
731          * model here and use the ReadFile and WriteFile Win32 I/O API's
732          * on the socket
733          */
734         overlap.Offset = overlap.OffsetHigh = (DWORD)0;
735         overlap.hEvent = hReadWriteEvent;
736         ret = WriteFile((HANDLE)sockfd, (char *)&reqpkt, REQ_LEN_NOMAC + n,
737                         NULL, (LPOVERLAPPED)&overlap);
738         if ((ret == FALSE) && (GetLastError() != ERROR_IO_PENDING)) {
739                 msyslog(LOG_ERR, "send to NTP server failed: %m");
740                 return 0;
741         }
742         dwWait = WaitForSingleObject(hReadWriteEvent, (DWORD) TIMEOUT_SEC * 1000);
743         if ((dwWait == WAIT_FAILED) || (dwWait == WAIT_TIMEOUT)) {
744                 if (dwWait == WAIT_FAILED)
745                     msyslog(LOG_ERR, "WaitForSingleObject failed: %m");
746                 return 0;
747         }
748         if (!GetOverlappedResult((HANDLE)sockfd, (LPOVERLAPPED)&overlap,
749                                 (LPDWORD)&NumberOfBytesWritten, FALSE)) {
750                 msyslog(LOG_ERR, "GetOverlappedResult for WriteFile fails: %m");
751                 return 0;
752         }
753 #endif /* SYS_WINNT */
754     
755
756         /*
757          * Wait for a response.  A weakness of the mode 7 protocol used
758          * is that there is no way to associate a response with a
759          * particular request, i.e. the response to this configuration
760          * request is indistinguishable from that to any other.  I should
761          * fix this some day.  In any event, the time out is fairly
762          * pessimistic to make sure that if an answer is coming back
763          * at all, we get it.
764          */
765         for (;;) {
766                 FD_ZERO(&fdset);
767                 FD_SET(sockfd, &fdset);
768                 tvout.tv_sec = TIMEOUT_SEC;
769                 tvout.tv_usec = TIMEOUT_USEC;
770
771                 n = select(sockfd + 1, &fdset, (fd_set *)0,
772                            (fd_set *)0, &tvout);
773
774                 if (n < 0)
775                 {
776                         if (errno != EINTR)
777                             msyslog(LOG_ERR, "select() fails: %m");
778                         return 0;
779                 }
780                 else if (n == 0)
781                 {
782 #ifdef DEBUG
783                         if (debug)
784                             msyslog(LOG_INFO, "select() returned 0.");
785 #endif
786                         return 0;
787                 }
788
789 #ifndef SYS_WINNT
790                 n = recv(sockfd, (char *)&reqpkt, REQ_LEN_MAC, 0);
791                 if (n <= 0) {
792                         if (n < 0) {
793                                 msyslog(LOG_ERR, "recv() fails: %m");
794                                 return 0;
795                         }
796                         continue;
797                 }
798 #else /* Overlapped I/O used on non-blocking sockets on Windows NT */
799                 ret = ReadFile((HANDLE)sockfd, (char *)&reqpkt, (DWORD)REQ_LEN_MAC,
800                                NULL, (LPOVERLAPPED)&overlap);
801                 if ((ret == FALSE) && (GetLastError() != ERROR_IO_PENDING)) {
802                         msyslog(LOG_ERR, "ReadFile() fails: %m");
803                         return 0;
804                 }
805                 dwWait = WaitForSingleObject(hReadWriteEvent, (DWORD) TIMEOUT_SEC * 1000);
806                 if ((dwWait == WAIT_FAILED) || (dwWait == WAIT_TIMEOUT)) {
807                         if (dwWait == WAIT_FAILED) {
808                                 msyslog(LOG_ERR, "WaitForSingleObject for ReadFile fails: %m");
809                                 return 0;
810                         }
811                         continue;
812                 }
813                 if (!GetOverlappedResult((HANDLE)sockfd, (LPOVERLAPPED)&overlap,
814                                         (LPDWORD)&NumberOfBytesRead, FALSE)) {
815                         msyslog(LOG_ERR, "GetOverlappedResult fails: %m");
816                         return 0;
817                 }
818                 n = NumberOfBytesRead;
819 #endif /* SYS_WINNT */
820
821                 /*
822                  * Got one.  Check through to make sure it is what
823                  * we expect.
824                  */
825                 if (n < RESP_HEADER_SIZE) {
826                         msyslog(LOG_ERR, "received runt response (%d octets)",
827                                 n);
828                         continue;
829                 }
830
831                 if (!ISRESPONSE(reqpkt.rm_vn_mode)) {
832 #ifdef DEBUG
833                         if (debug > 1)
834                             msyslog(LOG_INFO, "received non-response packet");
835 #endif
836                         continue;
837                 }
838
839                 if (ISMORE(reqpkt.rm_vn_mode)) {
840 #ifdef DEBUG
841                         if (debug > 1)
842                             msyslog(LOG_INFO, "received fragmented packet");
843 #endif
844                         continue;
845                 }
846
847                 if ( ( (INFO_VERSION(reqpkt.rm_vn_mode) < 2)
848                        || (INFO_VERSION(reqpkt.rm_vn_mode) > NTP_VERSION))
849                      || INFO_MODE(reqpkt.rm_vn_mode) != MODE_PRIVATE) {
850 #ifdef DEBUG
851                         if (debug > 1)
852                             msyslog(LOG_INFO,
853                                     "version (%d/%d) or mode (%d/%d) incorrect",
854                                     INFO_VERSION(reqpkt.rm_vn_mode),
855                                     NTP_VERSION,
856                                     INFO_MODE(reqpkt.rm_vn_mode),
857                                     MODE_PRIVATE);
858 #endif
859                         continue;
860                 }
861
862                 if (INFO_SEQ(reqpkt.auth_seq) != 0) {
863 #ifdef DEBUG
864                         if (debug > 1)
865                             msyslog(LOG_INFO,
866                                     "nonzero sequence number (%d)",
867                                     INFO_SEQ(reqpkt.auth_seq));
868 #endif
869                         continue;
870                 }
871
872                 if (reqpkt.implementation != IMPL_XNTPD ||
873                     reqpkt.request != REQ_CONFIG) {
874 #ifdef DEBUG
875                         if (debug > 1)
876                             msyslog(LOG_INFO,
877                                     "implementation (%d) or request (%d) incorrect",
878                                     reqpkt.implementation, reqpkt.request);
879 #endif
880                         continue;
881                 }
882
883                 if (INFO_NITEMS(reqpkt.err_nitems) != 0 ||
884                     INFO_MBZ(reqpkt.mbz_itemsize) != 0 ||
885                     INFO_ITEMSIZE(reqpkt.mbz_itemsize) != 0) {
886 #ifdef DEBUG
887                         if (debug > 1)
888                             msyslog(LOG_INFO,
889                                     "nitems (%d) mbz (%d) or itemsize (%d) nonzero",
890                                     INFO_NITEMS(reqpkt.err_nitems),
891                                     INFO_MBZ(reqpkt.mbz_itemsize),
892                                     INFO_ITEMSIZE(reqpkt.mbz_itemsize));
893 #endif
894                         continue;
895                 }
896
897                 n = INFO_ERR(reqpkt.err_nitems);
898                 switch (n) {
899                     case INFO_OKAY:
900                         /* success */
901                         return 1;
902                 
903                     case INFO_ERR_IMPL:
904                         msyslog(LOG_ERR,
905                                 "ntpd reports implementation mismatch!");
906                         return 0;
907                 
908                     case INFO_ERR_REQ:
909                         msyslog(LOG_ERR,
910                                 "ntpd says configuration request is unknown!");
911                         return 0;
912                 
913                     case INFO_ERR_FMT:
914                         msyslog(LOG_ERR,
915                                 "ntpd indicates a format error occurred!");
916                         return 0;
917
918                     case INFO_ERR_NODATA:
919                         msyslog(LOG_ERR,
920                                 "ntpd indicates no data available!");
921                         return 0;
922                 
923                     case INFO_ERR_AUTH:
924                         msyslog(LOG_ERR,
925                                 "ntpd returns a permission denied error!");
926                         return 0;
927
928                     default:
929                         msyslog(LOG_ERR,
930                                 "ntpd returns unknown error code %d!", n);
931                         return 0;
932                 }
933         }
934 }
935
936
937 /*
938  * nexttoken - return the next token from a line
939  */
940 static char *
941 nexttoken(
942         char **lptr
943         )
944 {
945         register char *cp;
946         register char *tstart;
947
948         cp = *lptr;
949
950         /*
951          * Skip leading white space
952          */
953         while (*cp == ' ' || *cp == '\t')
954             cp++;
955         
956         /*
957          * If this is the end of the line, return nothing.
958          */
959         if (*cp == '\n' || *cp == '\0') {
960                 *lptr = cp;
961                 return NULL;
962         }
963         
964         /*
965          * Must be the start of a token.  Record the pointer and look
966          * for the end.
967          */
968         tstart = cp++;
969         while (*cp != ' ' && *cp != '\t' && *cp != '\n' && *cp != '\0')
970             cp++;
971         
972         /*
973          * Terminate the token with a \0.  If this isn't the end of the
974          * line, space to the next character.
975          */
976         if (*cp == '\n' || *cp == '\0')
977             *cp = '\0';
978         else
979             *cp++ = '\0';
980
981         *lptr = cp;
982         return tstart;
983 }
984
985
986 /*
987  * readconf - read the configuration information out of the file we
988  *            were passed.  Note that since the file is supposed to be
989  *            machine generated, we bail out at the first sign of trouble.
990  */
991 static void
992 readconf(
993         FILE *fp,
994         char *name
995         )
996 {
997         register int i;
998         char *token[NUMTOK];
999         u_long intval[NUMTOK];
1000         u_int flags;
1001         char buf[MAXLINESIZE];
1002         char *bp;
1003
1004         while (fgets(buf, MAXLINESIZE, fp) != NULL) {
1005
1006                 bp = buf;
1007                 for (i = 0; i < NUMTOK; i++) {
1008                         if ((token[i] = nexttoken(&bp)) == NULL) {
1009                                 msyslog(LOG_ERR,
1010                                         "tokenizing error in file `%s', quitting",
1011                                         name);
1012                                 resolver_exit(1);
1013                         }
1014                 }
1015
1016                 for (i = 1; i < NUMTOK - 1; i++) {
1017                         if (!atouint(token[i], &intval[i])) {
1018                                 msyslog(LOG_ERR,
1019                                         "format error for integer token `%s', file `%s', quitting",
1020                                         token[i], name);
1021                                 resolver_exit(1);
1022                         }
1023                 }
1024
1025                 if (intval[TOK_HMODE] != MODE_ACTIVE &&
1026                     intval[TOK_HMODE] != MODE_CLIENT &&
1027                     intval[TOK_HMODE] != MODE_BROADCAST) {
1028                         msyslog(LOG_ERR, "invalid mode (%ld) in file %s",
1029                                 intval[TOK_HMODE], name);
1030                         resolver_exit(1);
1031                 }
1032
1033                 if (intval[TOK_VERSION] > NTP_VERSION ||
1034                     intval[TOK_VERSION] < NTP_OLDVERSION) {
1035                         msyslog(LOG_ERR, "invalid version (%ld) in file %s",
1036                                 intval[TOK_VERSION], name);
1037                         resolver_exit(1);
1038                 }
1039                 if (intval[TOK_MINPOLL] < NTP_MINPOLL ||
1040                     intval[TOK_MINPOLL] > NTP_MAXPOLL) {
1041                         msyslog(LOG_ERR, "invalid MINPOLL value (%ld) in file %s",
1042                                 intval[TOK_MINPOLL], name);
1043                         resolver_exit(1);
1044                 }
1045
1046                 if (intval[TOK_MAXPOLL] < NTP_MINPOLL ||
1047                     intval[TOK_MAXPOLL] > NTP_MAXPOLL) {
1048                         msyslog(LOG_ERR, "invalid MAXPOLL value (%ld) in file %s",
1049                                 intval[TOK_MAXPOLL], name);
1050                         resolver_exit(1);
1051                 }
1052
1053                 if ((intval[TOK_FLAGS] & ~(FLAG_AUTHENABLE | FLAG_PREFER |
1054                     FLAG_NOSELECT | FLAG_BURST | FLAG_IBURST | FLAG_SKEY))
1055                     != 0) {
1056                         msyslog(LOG_ERR, "invalid flags (%ld) in file %s",
1057                                 intval[TOK_FLAGS], name);
1058                         resolver_exit(1);
1059                 }
1060
1061                 flags = 0;
1062                 if (intval[TOK_FLAGS] & FLAG_AUTHENABLE)
1063                     flags |= CONF_FLAG_AUTHENABLE;
1064                 if (intval[TOK_FLAGS] & FLAG_PREFER)
1065                     flags |= CONF_FLAG_PREFER;
1066                 if (intval[TOK_FLAGS] & FLAG_NOSELECT)
1067                     flags |= CONF_FLAG_NOSELECT;
1068                 if (intval[TOK_FLAGS] & FLAG_BURST)
1069                     flags |= CONF_FLAG_BURST;
1070                 if (intval[TOK_FLAGS] & FLAG_IBURST)
1071                     flags |= CONF_FLAG_IBURST;
1072                 if (intval[TOK_FLAGS] & FLAG_SKEY)
1073                     flags |= CONF_FLAG_SKEY;
1074
1075                 /*
1076                  * This is as good as we can check it.  Add it in.
1077                  */
1078                 addentry(token[TOK_HOSTNAME], (int)intval[TOK_HMODE],
1079                          (int)intval[TOK_VERSION], (int)intval[TOK_MINPOLL],
1080                          (int)intval[TOK_MAXPOLL], flags, (int)intval[TOK_TTL],
1081                          intval[TOK_KEYID], token[TOK_KEYSTR]);
1082         }
1083 }
1084
1085
1086 /*
1087  * doconfigure - attempt to resolve names and configure the server
1088  */
1089 static void
1090 doconfigure(
1091         int dores
1092         )
1093 {
1094         register struct conf_entry *ce;
1095         register struct conf_entry *ceremove;
1096
1097 #ifdef DEBUG
1098                 if (debug > 1)
1099                         msyslog(LOG_INFO, "Running doconfigure %s DNS",
1100                             dores ? "with" : "without" );
1101 #endif
1102
1103         ce = confentries;
1104         while (ce != NULL) {
1105 #ifdef DEBUG
1106                 if (debug > 1)
1107                         msyslog(LOG_INFO,
1108                             "doconfigure: <%s> has peeraddr %s",
1109                             ce->ce_name, stoa(&ce->peer_store));
1110 #endif
1111                 if (dores && SOCKNUL(&(ce->peer_store))) {
1112                         if (!findhostaddr(ce)) {
1113                                 msyslog(LOG_ERR,
1114                                         "couldn't resolve `%s', giving up on it",
1115                                         ce->ce_name);
1116                                 ceremove = ce;
1117                                 ce = ceremove->ce_next;
1118                                 removeentry(ceremove);
1119                                 continue;
1120                         }
1121                 }
1122
1123                 if (!SOCKNUL(&ce->peer_store)) {
1124                         if (request(&ce->ce_config)) {
1125                                 ceremove = ce;
1126                                 ce = ceremove->ce_next;
1127                                 removeentry(ceremove);
1128                                 continue;
1129                         }
1130 #ifdef DEBUG
1131                         if (debug > 1) {
1132                                 msyslog(LOG_INFO,
1133                                     "doconfigure: request() FAILED, maybe next time.");
1134                         }
1135 #endif
1136                 }
1137                 ce = ce->ce_next;
1138         }
1139 }