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