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