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