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