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 */ 52struct 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 */ 73static 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 91static volatile int config_timer = 0; 92static volatile int resolve_timer = 0; 93 94static 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 */ 132static SOCKET sockfd = INVALID_SOCKET; /* NT uses SOCKET */ 133 134/* stuff to be filled in by caller */ 135 136keyid_t req_keyid; /* request keyid */ 137char *req_file; /* name of the file with configuration info */ 138 139/* end stuff to be filled in */ 140 141 142static void checkparent P((void)); 143static void removeentry P((struct conf_entry *)); 144static void addentry P((char *, int, int, int, int, u_int, 145 int, keyid_t, char *, u_char)); 146static int findhostaddr P((struct conf_entry *)); 147static void openntp P((void)); 148static int request P((struct conf_peer *)); 149static char * nexttoken P((char **)); 150static void readconf P((FILE *, char *)); 151static void doconfigure P((int)); 152 153struct 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 159struct 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 173static void resolver_exit P((int)); 174 175/* 176 * Call here instead of just exiting 177 */ 178 179static 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 194void 195ntp_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 212void 213ntp_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 345static void 346checkparent(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 */ 366static void 367removeentry( 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 */ 392static void 393addentry( 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 */ 460static int 461findhostaddr( 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 */ 582static void 583openntp(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 */ 681static int 682request( 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 */ 974static char * 975nexttoken( 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 */ 1025static void 1026readconf( 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 */ 1130static void 1131doconfigure( 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} 1186