1 /************************************************************************ 2 Copyright 1988, 1991 by Carnegie Mellon University 3 4 All Rights Reserved 5 6 Permission to use, copy, modify, and distribute this software and its 7 documentation for any purpose and without fee is hereby granted, provided 8 that the above copyright notice appear in all copies and that both that 9 copyright notice and this permission notice appear in supporting 10 documentation, and that the name of Carnegie Mellon University not be used 11 in advertising or publicity pertaining to distribution of the software 12 without specific, written prior permission. 13 14 CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 15 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. 16 IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL 17 DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 18 PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 19 ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 20 SOFTWARE. 21 22 $FreeBSD$ 23 24 ************************************************************************/ 25 26 /* 27 * BOOTP (bootstrap protocol) server daemon. 28 * 29 * Answers BOOTP request packets from booting client machines. 30 * See [SRI-NIC]<RFC>RFC951.TXT for a description of the protocol. 31 * See [SRI-NIC]<RFC>RFC1048.TXT for vendor-information extensions. 32 * See RFC 1395 for option tags 14-17. 33 * See accompanying man page -- bootpd.8 34 * 35 * HISTORY 36 * See ./Changes 37 * 38 * BUGS 39 * See ./ToDo 40 */ 41 42 43 44 #include <sys/types.h> 45 #include <sys/param.h> 46 #include <sys/socket.h> 47 #include <sys/ioctl.h> 48 #include <sys/file.h> 49 #include <sys/time.h> 50 #include <sys/stat.h> 51 #include <sys/utsname.h> 52 53 #include <net/if.h> 54 #include <netinet/in.h> 55 #include <arpa/inet.h> /* inet_ntoa */ 56 57 #ifndef NO_UNISTD 58 #include <unistd.h> 59 #endif 60 61 #include <stdlib.h> 62 #include <signal.h> 63 #include <stdio.h> 64 #include <string.h> 65 #include <errno.h> 66 #include <ctype.h> 67 #include <netdb.h> 68 #include <paths.h> 69 #include <syslog.h> 70 #include <assert.h> 71 72 #ifdef NO_SETSID 73 # include <fcntl.h> /* for O_RDONLY, etc */ 74 #endif 75 76 #ifndef USE_BFUNCS 77 # include <memory.h> 78 /* Yes, memcpy is OK here (no overlapped copies). */ 79 # define bcopy(a,b,c) memcpy(b,a,c) 80 # define bzero(p,l) memset(p,0,l) 81 # define bcmp(a,b,c) memcmp(a,b,c) 82 #endif 83 84 #include "bootp.h" 85 #include "hash.h" 86 #include "hwaddr.h" 87 #include "bootpd.h" 88 #include "dovend.h" 89 #include "getif.h" 90 #include "readfile.h" 91 #include "report.h" 92 #include "tzone.h" 93 #include "patchlevel.h" 94 95 #ifndef CONFIG_FILE 96 #define CONFIG_FILE "/etc/bootptab" 97 #endif 98 #ifndef DUMPTAB_FILE 99 #define DUMPTAB_FILE "/tmp/bootpd.dump" 100 #endif 101 102 103 104 /* 105 * Externals, forward declarations, and global variables 106 */ 107 108 #ifdef __STDC__ 109 #define P(args) args 110 #else 111 #define P(args) () 112 #endif 113 114 extern void dumptab P((char *)); 115 116 PRIVATE void catcher P((int)); 117 PRIVATE int chk_access P((char *, int32 *)); 118 #ifdef VEND_CMU 119 PRIVATE void dovend_cmu P((struct bootp *, struct host *)); 120 #endif 121 PRIVATE void dovend_rfc1048 P((struct bootp *, struct host *, int32)); 122 PRIVATE void handle_reply P((void)); 123 PRIVATE void handle_request P((void)); 124 PRIVATE void sendreply P((int forward, int32 dest_override)); 125 PRIVATE void usage P((void)); 126 127 #undef P 128 129 /* 130 * IP port numbers for client and server obtained from /etc/services 131 */ 132 133 u_short bootps_port, bootpc_port; 134 135 136 /* 137 * Internet socket and interface config structures 138 */ 139 140 struct sockaddr_in bind_addr; /* Listening */ 141 struct sockaddr_in recv_addr; /* Packet source */ 142 struct sockaddr_in send_addr; /* destination */ 143 144 145 /* 146 * option defaults 147 */ 148 int debug = 0; /* Debugging flag (level) */ 149 struct timeval actualtimeout = 150 { /* fifteen minutes */ 151 15 * 60L, /* tv_sec */ 152 0 /* tv_usec */ 153 }; 154 155 /* 156 * General 157 */ 158 159 int s; /* Socket file descriptor */ 160 char *pktbuf; /* Receive packet buffer */ 161 int pktlen; 162 char *progname; 163 char *chdir_path; 164 struct in_addr my_ip_addr; 165 166 static const char *hostname; 167 static char default_hostname[MAXHOSTNAMELEN]; 168 169 /* Flags set by signal catcher. */ 170 PRIVATE int do_readtab = 0; 171 PRIVATE int do_dumptab = 0; 172 173 /* 174 * Globals below are associated with the bootp database file (bootptab). 175 */ 176 177 char *bootptab = CONFIG_FILE; 178 char *bootpd_dump = DUMPTAB_FILE; 179 180 181 182 /* 183 * Initialization such as command-line processing is done and then the 184 * main server loop is started. 185 */ 186 187 int 188 main(argc, argv) 189 int argc; 190 char **argv; 191 { 192 struct timeval *timeout; 193 struct bootp *bp; 194 struct servent *servp; 195 struct hostent *hep; 196 char *stmp; 197 int n, ba_len, ra_len; 198 int nfound, readfds; 199 int standalone; 200 #ifdef SA_NOCLDSTOP /* Have POSIX sigaction(2). */ 201 struct sigaction sa; 202 #endif 203 204 progname = strrchr(argv[0], '/'); 205 if (progname) progname++; 206 else progname = argv[0]; 207 208 /* 209 * Initialize logging. 210 */ 211 report_init(0); /* uses progname */ 212 213 /* 214 * Log startup 215 */ 216 report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL); 217 218 /* Debugging for compilers with struct padding. */ 219 assert(sizeof(struct bootp) == BP_MINPKTSZ); 220 221 /* Get space for receiving packets and composing replies. */ 222 pktbuf = malloc(MAX_MSG_SIZE); 223 if (!pktbuf) { 224 report(LOG_ERR, "malloc failed"); 225 exit(1); 226 } 227 bp = (struct bootp *) pktbuf; 228 229 /* 230 * Check to see if a socket was passed to us from inetd. 231 * 232 * Use getsockname() to determine if descriptor 0 is indeed a socket 233 * (and thus we are probably a child of inetd) or if it is instead 234 * something else and we are running standalone. 235 */ 236 s = 0; 237 ba_len = sizeof(bind_addr); 238 bzero((char *) &bind_addr, ba_len); 239 errno = 0; 240 standalone = TRUE; 241 if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) { 242 /* 243 * Descriptor 0 is a socket. Assume we are a child of inetd. 244 */ 245 if (bind_addr.sin_family == AF_INET) { 246 standalone = FALSE; 247 bootps_port = ntohs(bind_addr.sin_port); 248 } else { 249 /* Some other type of socket? */ 250 report(LOG_ERR, "getsockname: not an INET socket"); 251 } 252 } 253 254 /* 255 * Set defaults that might be changed by option switches. 256 */ 257 stmp = NULL; 258 timeout = &actualtimeout; 259 260 if (gethostname(default_hostname, sizeof(default_hostname) - 1) < 0) { 261 report(LOG_ERR, "bootpd: can't get hostname\n"); 262 exit(1); 263 } 264 default_hostname[sizeof(default_hostname) - 1] = '\0'; 265 hostname = default_hostname; 266 267 /* 268 * Read switches. 269 */ 270 for (argc--, argv++; argc > 0; argc--, argv++) { 271 if (argv[0][0] != '-') 272 break; 273 switch (argv[0][1]) { 274 275 case 'c': /* chdir_path */ 276 if (argv[0][2]) { 277 stmp = &(argv[0][2]); 278 } else { 279 argc--; 280 argv++; 281 stmp = argv[0]; 282 } 283 if (!stmp || (stmp[0] != '/')) { 284 report(LOG_ERR, 285 "bootpd: invalid chdir specification\n"); 286 break; 287 } 288 chdir_path = stmp; 289 break; 290 291 case 'd': /* debug level */ 292 if (argv[0][2]) { 293 stmp = &(argv[0][2]); 294 } else if (argv[1] && argv[1][0] == '-') { 295 /* 296 * Backwards-compatible behavior: 297 * no parameter, so just increment the debug flag. 298 */ 299 debug++; 300 break; 301 } else { 302 argc--; 303 argv++; 304 stmp = argv[0]; 305 } 306 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { 307 report(LOG_ERR, 308 "%s: invalid debug level\n", progname); 309 break; 310 } 311 debug = n; 312 break; 313 314 case 'h': /* override hostname */ 315 if (argv[0][2]) { 316 stmp = &(argv[0][2]); 317 } else { 318 argc--; 319 argv++; 320 stmp = argv[0]; 321 } 322 if (!stmp) { 323 report(LOG_ERR, 324 "bootpd: missing hostname\n"); 325 break; 326 } 327 hostname = stmp; 328 break; 329 330 case 'i': /* inetd mode */ 331 standalone = FALSE; 332 break; 333 334 case 's': /* standalone mode */ 335 standalone = TRUE; 336 break; 337 338 case 't': /* timeout */ 339 if (argv[0][2]) { 340 stmp = &(argv[0][2]); 341 } else { 342 argc--; 343 argv++; 344 stmp = argv[0]; 345 } 346 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { 347 report(LOG_ERR, 348 "%s: invalid timeout specification\n", progname); 349 break; 350 } 351 actualtimeout.tv_sec = (int32) (60 * n); 352 /* 353 * If the actual timeout is zero, pass a NULL pointer 354 * to select so it blocks indefinitely, otherwise, 355 * point to the actual timeout value. 356 */ 357 timeout = (n > 0) ? &actualtimeout : NULL; 358 break; 359 360 default: 361 report(LOG_ERR, "%s: unknown switch: -%c\n", 362 progname, argv[0][1]); 363 usage(); 364 break; 365 366 } /* switch */ 367 } /* for args */ 368 369 /* 370 * Override default file names if specified on the command line. 371 */ 372 if (argc > 0) 373 bootptab = argv[0]; 374 375 if (argc > 1) 376 bootpd_dump = argv[1]; 377 378 /* 379 * Get my hostname and IP address. 380 */ 381 382 hep = gethostbyname(hostname); 383 if (!hep) { 384 report(LOG_ERR, "Can not get my IP address\n"); 385 exit(1); 386 } 387 bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr)); 388 389 if (standalone) { 390 /* 391 * Go into background and disassociate from controlling terminal. 392 */ 393 if (debug < 3) { 394 if (fork()) 395 exit(0); 396 #ifdef NO_SETSID 397 setpgrp(0,0); 398 #ifdef TIOCNOTTY 399 n = open(_PATH_TTY, O_RDWR); 400 if (n >= 0) { 401 ioctl(n, TIOCNOTTY, (char *) 0); 402 (void) close(n); 403 } 404 #endif /* TIOCNOTTY */ 405 #else /* SETSID */ 406 if (setsid() < 0) 407 perror("setsid"); 408 #endif /* SETSID */ 409 } /* if debug < 3 */ 410 411 /* 412 * Nuke any timeout value 413 */ 414 timeout = NULL; 415 416 } /* if standalone (1st) */ 417 418 /* Set the cwd (i.e. to /tftpboot) */ 419 if (chdir_path) { 420 if (chdir(chdir_path) < 0) 421 report(LOG_ERR, "%s: chdir failed", chdir_path); 422 } 423 424 /* Get the timezone. */ 425 tzone_init(); 426 427 /* Allocate hash tables. */ 428 rdtab_init(); 429 430 /* 431 * Read the bootptab file. 432 */ 433 readtab(1); /* force read */ 434 435 if (standalone) { 436 437 /* 438 * Create a socket. 439 */ 440 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 441 report(LOG_ERR, "socket: %s", get_network_errmsg()); 442 exit(1); 443 } 444 445 /* 446 * Get server's listening port number 447 */ 448 servp = getservbyname("bootps", "udp"); 449 if (servp) { 450 bootps_port = ntohs((u_short) servp->s_port); 451 } else { 452 bootps_port = (u_short) IPPORT_BOOTPS; 453 report(LOG_ERR, 454 "udp/bootps: unknown service -- assuming port %d", 455 bootps_port); 456 } 457 458 /* 459 * Bind socket to BOOTPS port. 460 */ 461 bind_addr.sin_family = AF_INET; 462 bind_addr.sin_addr.s_addr = INADDR_ANY; 463 bind_addr.sin_port = htons(bootps_port); 464 if (bind(s, (struct sockaddr *) &bind_addr, 465 sizeof(bind_addr)) < 0) 466 { 467 report(LOG_ERR, "bind: %s", get_network_errmsg()); 468 exit(1); 469 } 470 } /* if standalone (2nd)*/ 471 472 /* 473 * Get destination port number so we can reply to client 474 */ 475 servp = getservbyname("bootpc", "udp"); 476 if (servp) { 477 bootpc_port = ntohs(servp->s_port); 478 } else { 479 report(LOG_ERR, 480 "udp/bootpc: unknown service -- assuming port %d", 481 IPPORT_BOOTPC); 482 bootpc_port = (u_short) IPPORT_BOOTPC; 483 } 484 485 /* 486 * Set up signals to read or dump the table. 487 */ 488 #ifdef SA_NOCLDSTOP /* Have POSIX sigaction(2). */ 489 sa.sa_handler = catcher; 490 sigemptyset(&sa.sa_mask); 491 sa.sa_flags = 0; 492 if (sigaction(SIGHUP, &sa, NULL) < 0) { 493 report(LOG_ERR, "sigaction: %s", get_errmsg()); 494 exit(1); 495 } 496 if (sigaction(SIGUSR1, &sa, NULL) < 0) { 497 report(LOG_ERR, "sigaction: %s", get_errmsg()); 498 exit(1); 499 } 500 #else /* SA_NOCLDSTOP */ 501 /* Old-fashioned UNIX signals */ 502 if ((int) signal(SIGHUP, catcher) < 0) { 503 report(LOG_ERR, "signal: %s", get_errmsg()); 504 exit(1); 505 } 506 if ((int) signal(SIGUSR1, catcher) < 0) { 507 report(LOG_ERR, "signal: %s", get_errmsg()); 508 exit(1); 509 } 510 #endif /* SA_NOCLDSTOP */ 511 512 /* 513 * Process incoming requests. 514 */ 515 for (;;) { 516 struct timeval tv; 517 518 readfds = 1 << s; 519 if (timeout) 520 tv = *timeout; 521 522 nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL, 523 (timeout) ? &tv : NULL); 524 if (nfound < 0) { 525 if (errno != EINTR) { 526 report(LOG_ERR, "select: %s", get_errmsg()); 527 } 528 /* 529 * Call readtab() or dumptab() here to avoid the 530 * dangers of doing I/O from a signal handler. 531 */ 532 if (do_readtab) { 533 do_readtab = 0; 534 readtab(1); /* force read */ 535 } 536 if (do_dumptab) { 537 do_dumptab = 0; 538 dumptab(bootpd_dump); 539 } 540 continue; 541 } 542 if (!(readfds & (1 << s))) { 543 if (debug > 1) 544 report(LOG_INFO, "exiting after %ld minutes of inactivity", 545 actualtimeout.tv_sec / 60); 546 exit(0); 547 } 548 ra_len = sizeof(recv_addr); 549 n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0, 550 (struct sockaddr *) &recv_addr, &ra_len); 551 if (n <= 0) { 552 continue; 553 } 554 if (debug > 1) { 555 report(LOG_INFO, "recvd pkt from IP addr %s", 556 inet_ntoa(recv_addr.sin_addr)); 557 } 558 if (n < sizeof(struct bootp)) { 559 if (debug) { 560 report(LOG_NOTICE, "received short packet"); 561 } 562 continue; 563 } 564 pktlen = n; 565 566 readtab(0); /* maybe re-read bootptab */ 567 568 switch (bp->bp_op) { 569 case BOOTREQUEST: 570 handle_request(); 571 break; 572 case BOOTREPLY: 573 handle_reply(); 574 break; 575 } 576 } 577 return 0; 578 } 579 580 581 582 583 /* 584 * Print "usage" message and exit 585 */ 586 587 PRIVATE void 588 usage() 589 { 590 fprintf(stderr, 591 "usage: bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n"); 592 fprintf(stderr, "\t -c n\tset current directory\n"); 593 fprintf(stderr, "\t -d n\tset debug level\n"); 594 fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n"); 595 fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n"); 596 fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n"); 597 exit(1); 598 } 599 600 /* Signal catchers */ 601 PRIVATE void 602 catcher(sig) 603 int sig; 604 { 605 if (sig == SIGHUP) 606 do_readtab = 1; 607 if (sig == SIGUSR1) 608 do_dumptab = 1; 609 #if !defined(SA_NOCLDSTOP) && defined(SYSV) 610 /* For older "System V" derivatives with no sigaction(). */ 611 signal(sig, catcher); 612 #endif 613 } 614 615 616 617 /* 618 * Process BOOTREQUEST packet. 619 * 620 * Note: This version of the bootpd.c server never forwards 621 * a request to another server. That is the job of a gateway 622 * program such as the "bootpgw" program included here. 623 * 624 * (Also this version does not interpret the hostname field of 625 * the request packet; it COULD do a name->address lookup and 626 * forward the request there.) 627 */ 628 PRIVATE void 629 handle_request() 630 { 631 struct bootp *bp = (struct bootp *) pktbuf; 632 struct host *hp = NULL; 633 struct host dummyhost; 634 int32 bootsize = 0; 635 unsigned hlen, hashcode; 636 int32 dest; 637 char realpath[1024]; 638 char *clntpath; 639 char *homedir, *bootfile; 640 int n; 641 642 bp->bp_file[sizeof(bp->bp_file)-1] = '\0'; 643 644 /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */ 645 646 /* 647 * If the servername field is set, compare it against us. 648 * If we're not being addressed, ignore this request. 649 * If the server name field is null, throw in our name. 650 */ 651 if (strlen(bp->bp_sname)) { 652 if (strcmp(bp->bp_sname, hostname)) { 653 if (debug) 654 report(LOG_INFO, "\ 655 ignoring request for server %s from client at %s address %s", 656 bp->bp_sname, netname(bp->bp_htype), 657 haddrtoa(bp->bp_chaddr, bp->bp_hlen)); 658 /* XXX - Is it correct to ignore such a request? -gwr */ 659 return; 660 } 661 } else { 662 strcpy(bp->bp_sname, hostname); 663 } 664 665 /* Convert the request into a reply. */ 666 bp->bp_op = BOOTREPLY; 667 if (bp->bp_ciaddr.s_addr == 0) { 668 /* 669 * client doesnt know his IP address, 670 * search by hardware address. 671 */ 672 if (debug > 1) { 673 report(LOG_INFO, "request from %s address %s", 674 netname(bp->bp_htype), 675 haddrtoa(bp->bp_chaddr, bp->bp_hlen)); 676 } 677 hlen = haddrlength(bp->bp_htype); 678 if (hlen != bp->bp_hlen) { 679 report(LOG_NOTICE, "bad addr len from from %s address %s", 680 netname(bp->bp_htype), 681 haddrtoa(bp->bp_chaddr, hlen)); 682 } 683 dummyhost.htype = bp->bp_htype; 684 bcopy(bp->bp_chaddr, dummyhost.haddr, hlen); 685 hashcode = hash_HashFunction(bp->bp_chaddr, hlen); 686 hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp, 687 &dummyhost); 688 if (hp == NULL && 689 bp->bp_htype == HTYPE_IEEE802) 690 { 691 /* Try again with address in "canonical" form. */ 692 haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen); 693 if (debug > 1) { 694 report(LOG_INFO, "\ 695 HW addr type is IEEE 802. convert to %s and check again\n", 696 haddrtoa(dummyhost.haddr, bp->bp_hlen)); 697 } 698 hashcode = hash_HashFunction(dummyhost.haddr, hlen); 699 hp = (struct host *) hash_Lookup(hwhashtable, hashcode, 700 hwlookcmp, &dummyhost); 701 } 702 if (hp == NULL) { 703 /* 704 * XXX - Add dynamic IP address assignment? 705 */ 706 if (debug) 707 report(LOG_NOTICE, "unknown client %s address %s", 708 netname(bp->bp_htype), 709 haddrtoa(bp->bp_chaddr, bp->bp_hlen)); 710 return; /* not found */ 711 } 712 (bp->bp_yiaddr).s_addr = hp->iaddr.s_addr; 713 714 } else { 715 716 /* 717 * search by IP address. 718 */ 719 if (debug > 1) { 720 report(LOG_INFO, "request from IP addr %s", 721 inet_ntoa(bp->bp_ciaddr)); 722 } 723 dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr; 724 hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4); 725 hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp, 726 &dummyhost); 727 if (hp == NULL) { 728 if (debug) { 729 report(LOG_NOTICE, "IP address not found: %s", 730 inet_ntoa(bp->bp_ciaddr)); 731 } 732 return; 733 } 734 } 735 736 if (debug) { 737 report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr), 738 hp->hostname->string); 739 } 740 741 /* 742 * If there is a response delay threshold, ignore requests 743 * with a timestamp lower than the threshold. 744 */ 745 if (hp->flags.min_wait) { 746 u_int32 t = (u_int32) ntohs(bp->bp_secs); 747 if (t < hp->min_wait) { 748 if (debug > 1) 749 report(LOG_INFO, 750 "ignoring request due to timestamp (%d < %d)", 751 t, hp->min_wait); 752 return; 753 } 754 } 755 756 #ifdef YORK_EX_OPTION 757 /* 758 * The need for the "ex" tag arose out of the need to empty 759 * shared networked drives on diskless PCs. This solution is 760 * not very clean but it does work fairly well. 761 * Written by Edmund J. Sutcliffe <edmund@york.ac.uk> 762 * 763 * XXX - This could compromise security if a non-trusted user 764 * managed to write an entry in the bootptab with :ex=trojan: 765 * so I would leave this turned off unless you need it. -gwr 766 */ 767 /* Run a program, passing the client name as a parameter. */ 768 if (hp->flags.exec_file) { 769 char tst[100]; 770 /* XXX - Check string lengths? -gwr */ 771 strcpy (tst, hp->exec_file->string); 772 strcat (tst, " "); 773 strcat (tst, hp->hostname->string); 774 strcat (tst, " &"); 775 if (debug) 776 report(LOG_INFO, "executing %s", tst); 777 system(tst); /* Hope this finishes soon... */ 778 } 779 #endif /* YORK_EX_OPTION */ 780 781 /* 782 * If a specific TFTP server address was specified in the bootptab file, 783 * fill it in, otherwise zero it. 784 * XXX - Rather than zero it, should it be the bootpd address? -gwr 785 */ 786 (bp->bp_siaddr).s_addr = (hp->flags.bootserver) ? 787 hp->bootserver.s_addr : 0L; 788 789 #ifdef STANFORD_PROM_COMPAT 790 /* 791 * Stanford bootp PROMs (for a Sun?) have no way to leave 792 * the boot file name field blank (because the boot file 793 * name is automatically generated from some index). 794 * As a work-around, this little hack allows those PROMs to 795 * specify "sunboot14" with the same effect as a NULL name. 796 * (The user specifies boot device 14 or some such magic.) 797 */ 798 if (strcmp(bp->bp_file, "sunboot14") == 0) 799 bp->bp_file[0] = '\0'; /* treat it as unspecified */ 800 #endif 801 802 /* 803 * Fill in the client's proper bootfile. 804 * 805 * If the client specifies an absolute path, try that file with a 806 * ".host" suffix and then without. If the file cannot be found, no 807 * reply is made at all. 808 * 809 * If the client specifies a null or relative file, use the following 810 * table to determine the appropriate action: 811 * 812 * Homedir Bootfile Client's file 813 * specified? specified? specification Action 814 * ------------------------------------------------------------------- 815 * No No Null Send null filename 816 * No No Relative Discard request 817 * No Yes Null Send if absolute else null 818 * No Yes Relative Discard request *XXX 819 * Yes No Null Send null filename 820 * Yes No Relative Lookup with ".host" 821 * Yes Yes Null Send home/boot or bootfile 822 * Yes Yes Relative Lookup with ".host" *XXX 823 * 824 */ 825 826 /* 827 * XXX - I don't like the policy of ignoring a client when the 828 * boot file is not accessible. The TFTP server might not be 829 * running on the same machine as the BOOTP server, in which 830 * case checking accessibility of the boot file is pointless. 831 * 832 * Therefore, file accessibility is now demanded ONLY if you 833 * define CHECK_FILE_ACCESS in the Makefile options. -gwr 834 */ 835 836 /* 837 * The "real" path is as seen by the BOOTP daemon on this 838 * machine, while the client path is relative to the TFTP 839 * daemon chroot directory (i.e. /tftpboot). 840 */ 841 if (hp->flags.tftpdir) { 842 snprintf(realpath, sizeof(realpath), "%s", hp->tftpdir->string); 843 clntpath = &realpath[strlen(realpath)]; 844 } else { 845 realpath[0] = '\0'; 846 clntpath = realpath; 847 } 848 849 /* 850 * Determine client's requested homedir and bootfile. 851 */ 852 homedir = NULL; 853 bootfile = NULL; 854 if (bp->bp_file[0]) { 855 homedir = bp->bp_file; 856 bootfile = strrchr(homedir, '/'); 857 if (bootfile) { 858 if (homedir == bootfile) 859 homedir = NULL; 860 *bootfile++ = '\0'; 861 } else { 862 /* no "/" in the string */ 863 bootfile = homedir; 864 homedir = NULL; 865 } 866 if (debug > 2) { 867 report(LOG_INFO, "requested path=\"%s\" file=\"%s\"", 868 (homedir) ? homedir : "", 869 (bootfile) ? bootfile : ""); 870 } 871 } 872 873 /* 874 * Specifications in bootptab override client requested values. 875 */ 876 if (hp->flags.homedir) 877 homedir = hp->homedir->string; 878 if (hp->flags.bootfile) 879 bootfile = hp->bootfile->string; 880 881 /* 882 * Construct bootfile path. 883 */ 884 if (homedir) { 885 if (homedir[0] != '/') 886 strcat(clntpath, "/"); 887 strcat(clntpath, homedir); 888 homedir = NULL; 889 } 890 if (bootfile) { 891 if (bootfile[0] != '/') 892 strcat(clntpath, "/"); 893 strcat(clntpath, bootfile); 894 bootfile = NULL; 895 } 896 897 /* 898 * First try to find the file with a ".host" suffix 899 */ 900 n = strlen(clntpath); 901 strcat(clntpath, "."); 902 strcat(clntpath, hp->hostname->string); 903 if (chk_access(realpath, &bootsize) < 0) { 904 clntpath[n] = 0; /* Try it without the suffix */ 905 if (chk_access(realpath, &bootsize) < 0) { 906 /* neither "file.host" nor "file" was found */ 907 #ifdef CHECK_FILE_ACCESS 908 909 if (bp->bp_file[0]) { 910 /* 911 * Client wanted specific file 912 * and we didn't have it. 913 */ 914 report(LOG_NOTICE, 915 "requested file not found: \"%s\"", clntpath); 916 return; 917 } 918 /* 919 * Client didn't ask for a specific file and we couldn't 920 * access the default file, so just zero-out the bootfile 921 * field in the packet and continue processing the reply. 922 */ 923 bzero(bp->bp_file, sizeof(bp->bp_file)); 924 goto null_file_name; 925 926 #else /* CHECK_FILE_ACCESS */ 927 928 /* Complain only if boot file size was needed. */ 929 if (hp->flags.bootsize_auto) { 930 report(LOG_ERR, "can not determine size of file \"%s\"", 931 clntpath); 932 } 933 934 #endif /* CHECK_FILE_ACCESS */ 935 } 936 } 937 strncpy(bp->bp_file, clntpath, BP_FILE_LEN); 938 if (debug > 2) 939 report(LOG_INFO, "bootfile=\"%s\"", clntpath); 940 941 #ifdef CHECK_FILE_ACCESS 942 null_file_name: 943 #endif /* CHECK_FILE_ACCESS */ 944 945 946 /* 947 * Handle vendor options based on magic number. 948 */ 949 950 if (debug > 1) { 951 report(LOG_INFO, "vendor magic field is %d.%d.%d.%d", 952 (int) ((bp->bp_vend)[0]), 953 (int) ((bp->bp_vend)[1]), 954 (int) ((bp->bp_vend)[2]), 955 (int) ((bp->bp_vend)[3])); 956 } 957 /* 958 * If this host isn't set for automatic vendor info then copy the 959 * specific cookie into the bootp packet, thus forcing a certain 960 * reply format. Only force reply format if user specified it. 961 */ 962 if (hp->flags.vm_cookie) { 963 /* Slam in the user specified magic number. */ 964 bcopy(hp->vm_cookie, bp->bp_vend, 4); 965 } 966 /* 967 * Figure out the format for the vendor-specific info. 968 * Note that bp->bp_vend may have been set above. 969 */ 970 if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) { 971 /* RFC1048 conformant bootp client */ 972 dovend_rfc1048(bp, hp, bootsize); 973 if (debug > 1) { 974 report(LOG_INFO, "sending reply (with RFC1048 options)"); 975 } 976 } 977 #ifdef VEND_CMU 978 else if (!bcmp(bp->bp_vend, vm_cmu, 4)) { 979 dovend_cmu(bp, hp); 980 if (debug > 1) { 981 report(LOG_INFO, "sending reply (with CMU options)"); 982 } 983 } 984 #endif 985 else { 986 if (debug > 1) { 987 report(LOG_INFO, "sending reply (with no options)"); 988 } 989 } 990 991 dest = (hp->flags.reply_addr) ? 992 hp->reply_addr.s_addr : 0L; 993 994 /* not forwarded */ 995 sendreply(0, dest); 996 } 997 998 999 /* 1000 * Process BOOTREPLY packet. 1001 */ 1002 PRIVATE void 1003 handle_reply() 1004 { 1005 if (debug) { 1006 report(LOG_INFO, "processing boot reply"); 1007 } 1008 /* forwarded, no destination override */ 1009 sendreply(1, 0); 1010 } 1011 1012 1013 /* 1014 * Send a reply packet to the client. 'forward' flag is set if we are 1015 * not the originator of this reply packet. 1016 */ 1017 PRIVATE void 1018 sendreply(forward, dst_override) 1019 int forward; 1020 int32 dst_override; 1021 { 1022 struct bootp *bp = (struct bootp *) pktbuf; 1023 struct in_addr dst; 1024 u_short port = bootpc_port; 1025 unsigned char *ha; 1026 int len, haf; 1027 1028 /* 1029 * XXX - Should honor bp_flags "broadcast" bit here. 1030 * Temporary workaround: use the :ra=ADDR: option to 1031 * set the reply address to the broadcast address. 1032 */ 1033 1034 /* 1035 * If the destination address was specified explicitly 1036 * (i.e. the broadcast address for HP compatiblity) 1037 * then send the response to that address. Otherwise, 1038 * act in accordance with RFC951: 1039 * If the client IP address is specified, use that 1040 * else if gateway IP address is specified, use that 1041 * else make a temporary arp cache entry for the client's 1042 * NEW IP/hardware address and use that. 1043 */ 1044 if (dst_override) { 1045 dst.s_addr = dst_override; 1046 if (debug > 1) { 1047 report(LOG_INFO, "reply address override: %s", 1048 inet_ntoa(dst)); 1049 } 1050 } else if (bp->bp_ciaddr.s_addr) { 1051 dst = bp->bp_ciaddr; 1052 } else if (bp->bp_giaddr.s_addr && forward == 0) { 1053 dst = bp->bp_giaddr; 1054 port = bootps_port; 1055 if (debug > 1) { 1056 report(LOG_INFO, "sending reply to gateway %s", 1057 inet_ntoa(dst)); 1058 } 1059 } else { 1060 dst = bp->bp_yiaddr; 1061 ha = bp->bp_chaddr; 1062 len = bp->bp_hlen; 1063 if (len > MAXHADDRLEN) 1064 len = MAXHADDRLEN; 1065 haf = (int) bp->bp_htype; 1066 if (haf == 0) 1067 haf = HTYPE_ETHERNET; 1068 1069 if (debug > 1) 1070 report(LOG_INFO, "setarp %s - %s", 1071 inet_ntoa(dst), haddrtoa(ha, len)); 1072 setarp(s, &dst, haf, ha, len); 1073 } 1074 1075 if ((forward == 0) && 1076 (bp->bp_siaddr.s_addr == 0)) 1077 { 1078 struct ifreq *ifr; 1079 struct in_addr siaddr; 1080 /* 1081 * If we are originating this reply, we 1082 * need to find our own interface address to 1083 * put in the bp_siaddr field of the reply. 1084 * If this server is multi-homed, pick the 1085 * 'best' interface (the one on the same net 1086 * as the client). Of course, the client may 1087 * be on the other side of a BOOTP gateway... 1088 */ 1089 ifr = getif(s, &dst); 1090 if (ifr) { 1091 struct sockaddr_in *sip; 1092 sip = (struct sockaddr_in *) &(ifr->ifr_addr); 1093 siaddr = sip->sin_addr; 1094 } else { 1095 /* Just use my "official" IP address. */ 1096 siaddr = my_ip_addr; 1097 } 1098 1099 /* XXX - No need to set bp_giaddr here. */ 1100 1101 /* Finally, set the server address field. */ 1102 bp->bp_siaddr = siaddr; 1103 } 1104 /* Set up socket address for send. */ 1105 send_addr.sin_family = AF_INET; 1106 send_addr.sin_port = htons(port); 1107 send_addr.sin_addr = dst; 1108 1109 /* Send reply with same size packet as request used. */ 1110 if (sendto(s, pktbuf, pktlen, 0, 1111 (struct sockaddr *) &send_addr, 1112 sizeof(send_addr)) < 0) 1113 { 1114 report(LOG_ERR, "sendto: %s", get_network_errmsg()); 1115 } 1116 } /* sendreply */ 1117 1118 1119 /* nmatch() - now in getif.c */ 1120 /* setarp() - now in hwaddr.c */ 1121 1122 1123 /* 1124 * This call checks read access to a file. It returns 0 if the file given 1125 * by "path" exists and is publically readable. A value of -1 is returned if 1126 * access is not permitted or an error occurs. Successful calls also 1127 * return the file size in bytes using the long pointer "filesize". 1128 * 1129 * The read permission bit for "other" users is checked. This bit must be 1130 * set for tftpd(8) to allow clients to read the file. 1131 */ 1132 1133 PRIVATE int 1134 chk_access(path, filesize) 1135 char *path; 1136 int32 *filesize; 1137 { 1138 struct stat st; 1139 1140 if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) { 1141 *filesize = (int32) st.st_size; 1142 return 0; 1143 } else { 1144 return -1; 1145 } 1146 } 1147 1148 1149 /* 1150 * Now in dumptab.c : 1151 * dumptab() 1152 * dump_host() 1153 * list_ipaddresses() 1154 */ 1155 1156 #ifdef VEND_CMU 1157 1158 /* 1159 * Insert the CMU "vendor" data for the host pointed to by "hp" into the 1160 * bootp packet pointed to by "bp". 1161 */ 1162 1163 PRIVATE void 1164 dovend_cmu(bp, hp) 1165 struct bootp *bp; 1166 struct host *hp; 1167 { 1168 struct cmu_vend *vendp; 1169 struct in_addr_list *taddr; 1170 1171 /* 1172 * Initialize the entire vendor field to zeroes. 1173 */ 1174 bzero(bp->bp_vend, sizeof(bp->bp_vend)); 1175 1176 /* 1177 * Fill in vendor information. Subnet mask, default gateway, 1178 * domain name server, ien name server, time server 1179 */ 1180 vendp = (struct cmu_vend *) bp->bp_vend; 1181 strcpy(vendp->v_magic, (char *)vm_cmu); 1182 if (hp->flags.subnet_mask) { 1183 (vendp->v_smask).s_addr = hp->subnet_mask.s_addr; 1184 (vendp->v_flags) |= VF_SMASK; 1185 if (hp->flags.gateway) { 1186 (vendp->v_dgate).s_addr = hp->gateway->addr->s_addr; 1187 } 1188 } 1189 if (hp->flags.domain_server) { 1190 taddr = hp->domain_server; 1191 if (taddr->addrcount > 0) { 1192 (vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr; 1193 if (taddr->addrcount > 1) { 1194 (vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr; 1195 } 1196 } 1197 } 1198 if (hp->flags.name_server) { 1199 taddr = hp->name_server; 1200 if (taddr->addrcount > 0) { 1201 (vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr; 1202 if (taddr->addrcount > 1) { 1203 (vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr; 1204 } 1205 } 1206 } 1207 if (hp->flags.time_server) { 1208 taddr = hp->time_server; 1209 if (taddr->addrcount > 0) { 1210 (vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr; 1211 if (taddr->addrcount > 1) { 1212 (vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr; 1213 } 1214 } 1215 } 1216 /* Log message now done by caller. */ 1217 } /* dovend_cmu */ 1218 1219 #endif /* VEND_CMU */ 1220 1221 1222 1223 /* 1224 * Insert the RFC1048 vendor data for the host pointed to by "hp" into the 1225 * bootp packet pointed to by "bp". 1226 */ 1227 #define NEED(LEN, MSG) do \ 1228 if (bytesleft < (LEN)) { \ 1229 report(LOG_NOTICE, noroom, \ 1230 hp->hostname->string, MSG); \ 1231 return; \ 1232 } while (0) 1233 PRIVATE void 1234 dovend_rfc1048(bp, hp, bootsize) 1235 struct bootp *bp; 1236 struct host *hp; 1237 int32 bootsize; 1238 { 1239 int bytesleft, len; 1240 byte *vp; 1241 1242 static const char noroom[] = "%s: No room for \"%s\" option"; 1243 1244 vp = bp->bp_vend; 1245 1246 if (hp->flags.msg_size) { 1247 pktlen = hp->msg_size; 1248 } else { 1249 /* 1250 * If the request was longer than the official length, build 1251 * a response of that same length where the additional length 1252 * is assumed to be part of the bp_vend (options) area. 1253 */ 1254 if (pktlen > sizeof(*bp)) { 1255 if (debug > 1) 1256 report(LOG_INFO, "request message length=%d", pktlen); 1257 } 1258 /* 1259 * Check whether the request contains the option: 1260 * Maximum DHCP Message Size (RFC1533 sec. 9.8) 1261 * and if so, override the response length with its value. 1262 * This request must lie within the first BP_VEND_LEN 1263 * bytes of the option space. 1264 */ 1265 { 1266 byte *p, *ep; 1267 byte tag, len; 1268 short msgsz = 0; 1269 1270 p = vp + 4; 1271 ep = p + BP_VEND_LEN - 4; 1272 while (p < ep) { 1273 tag = *p++; 1274 /* Check for tags with no data first. */ 1275 if (tag == TAG_PAD) 1276 continue; 1277 if (tag == TAG_END) 1278 break; 1279 /* Now scan the length byte. */ 1280 len = *p++; 1281 switch (tag) { 1282 case TAG_MAX_MSGSZ: 1283 if (len == 2) { 1284 bcopy(p, (char*)&msgsz, 2); 1285 msgsz = ntohs(msgsz); 1286 } 1287 break; 1288 case TAG_SUBNET_MASK: 1289 /* XXX - Should preserve this if given... */ 1290 break; 1291 } /* swtich */ 1292 p += len; 1293 } 1294 1295 if (msgsz > sizeof(*bp) + BP_MSG_OVERHEAD) { 1296 if (debug > 1) 1297 report(LOG_INFO, "request has DHCP msglen=%d", msgsz); 1298 pktlen = msgsz - BP_MSG_OVERHEAD; 1299 } 1300 } 1301 } 1302 1303 if (pktlen < sizeof(*bp)) { 1304 report(LOG_ERR, "invalid response length=%d", pktlen); 1305 pktlen = sizeof(*bp); 1306 } 1307 bytesleft = ((byte*)bp + pktlen) - vp; 1308 if (pktlen > sizeof(*bp)) { 1309 if (debug > 1) 1310 report(LOG_INFO, "extended reply, length=%d, options=%d", 1311 pktlen, bytesleft); 1312 } 1313 1314 /* Copy in the magic cookie */ 1315 bcopy(vm_rfc1048, vp, 4); 1316 vp += 4; 1317 bytesleft -= 4; 1318 1319 if (hp->flags.subnet_mask) { 1320 /* always enough room here. */ 1321 *vp++ = TAG_SUBNET_MASK;/* -1 byte */ 1322 *vp++ = 4; /* -1 byte */ 1323 insert_u_long(hp->subnet_mask.s_addr, &vp); /* -4 bytes */ 1324 bytesleft -= 6; /* Fix real count */ 1325 if (hp->flags.gateway) { 1326 (void) insert_ip(TAG_GATEWAY, 1327 hp->gateway, 1328 &vp, &bytesleft); 1329 } 1330 } 1331 if (hp->flags.bootsize) { 1332 /* always enough room here */ 1333 bootsize = (hp->flags.bootsize_auto) ? 1334 ((bootsize + 511) / 512) : (hp->bootsize); /* Round up */ 1335 *vp++ = TAG_BOOT_SIZE; 1336 *vp++ = 2; 1337 *vp++ = (byte) ((bootsize >> 8) & 0xFF); 1338 *vp++ = (byte) (bootsize & 0xFF); 1339 bytesleft -= 4; /* Tag, length, and 16 bit blocksize */ 1340 } 1341 /* 1342 * This one is special: Remaining options go in the ext file. 1343 * Only the subnet_mask, bootsize, and gateway should precede. 1344 */ 1345 if (hp->flags.exten_file) { 1346 /* 1347 * Check for room for exten_file. Add 3 to account for 1348 * TAG_EXTEN_FILE, length, and TAG_END. 1349 */ 1350 len = strlen(hp->exten_file->string); 1351 NEED((len + 3), "ef"); 1352 *vp++ = TAG_EXTEN_FILE; 1353 *vp++ = (byte) (len & 0xFF); 1354 bcopy(hp->exten_file->string, vp, len); 1355 vp += len; 1356 *vp++ = TAG_END; 1357 bytesleft -= len + 3; 1358 return; /* no more options here. */ 1359 } 1360 /* 1361 * The remaining options are inserted by the following 1362 * function (which is shared with bootpef.c). 1363 * Keep back one byte for the TAG_END. 1364 */ 1365 len = dovend_rfc1497(hp, vp, bytesleft - 1); 1366 vp += len; 1367 bytesleft -= len; 1368 1369 /* There should be at least one byte left. */ 1370 NEED(1, "(end)"); 1371 *vp++ = TAG_END; 1372 bytesleft--; 1373 1374 /* Log message done by caller. */ 1375 if (bytesleft > 0) { 1376 /* 1377 * Zero out any remaining part of the vendor area. 1378 */ 1379 bzero(vp, bytesleft); 1380 } 1381 } /* dovend_rfc1048 */ 1382 #undef NEED 1383 1384 1385 /* 1386 * Now in readfile.c: 1387 * hwlookcmp() 1388 * iplookcmp() 1389 */ 1390 1391 /* haddrtoa() - now in hwaddr.c */ 1392 /* 1393 * Now in dovend.c: 1394 * insert_ip() 1395 * insert_generic() 1396 * insert_u_long() 1397 */ 1398 1399 /* get_errmsg() - now in report.c */ 1400 1401 /* 1402 * Local Variables: 1403 * tab-width: 4 1404 * c-indent-level: 4 1405 * c-argdecl-indent: 4 1406 * c-continued-statement-offset: 4 1407 * c-continued-brace-offset: -4 1408 * c-label-offset: -4 1409 * c-brace-offset: 0 1410 * End: 1411 */ 1412