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