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.14 2002/07/13 23:56:39 wiz 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 extern void dumptab(char *); 113 114 PRIVATE void catcher(int); 115 PRIVATE int chk_access(char *, int32 *); 116 #ifdef VEND_CMU 117 PRIVATE void dovend_cmu(struct bootp *, struct host *); 118 #endif 119 PRIVATE void dovend_rfc1048(struct bootp *, struct host *, int32); 120 PRIVATE void handle_reply(void); 121 PRIVATE void handle_request(void); 122 PRIVATE void sendreply(int forward, int32 dest_override); 123 PRIVATE void usage(void); 124 int main(int, char **); 125 126 /* 127 * IP port numbers for client and server obtained from /etc/services 128 */ 129 130 u_short bootps_port, bootpc_port; 131 132 133 /* 134 * Internet socket and interface config structures 135 */ 136 137 struct sockaddr_in bind_addr; /* Listening */ 138 struct sockaddr_in recv_addr; /* Packet source */ 139 struct sockaddr_in send_addr; /* destination */ 140 141 142 /* 143 * option defaults 144 */ 145 int debug = 0; /* Debugging flag (level) */ 146 struct timeval actualtimeout = 147 { /* fifteen minutes */ 148 15 * 60L, /* tv_sec */ 149 0 /* tv_usec */ 150 }; 151 152 /* 153 * General 154 */ 155 156 int s; /* Socket file descriptor */ 157 char *pktbuf; /* Receive packet buffer */ 158 int pktlen; 159 char *progname; 160 char *chdir_path; 161 char hostname[MAXHOSTNAMELEN + 1]; /* System host name */ 162 struct in_addr my_ip_addr; 163 164 /* Flags set by signal catcher. */ 165 PRIVATE int do_readtab = 0; 166 PRIVATE int do_dumptab = 0; 167 168 /* 169 * Globals below are associated with the bootp database file (bootptab). 170 */ 171 172 char *bootptab = CONFIG_FILE; 173 char *bootpd_dump = DUMPTAB_FILE; 174 175 176 177 /* 178 * Initialization such as command-line processing is done and then the 179 * main server loop is started. 180 */ 181 182 int 183 main(int argc, char **argv) 184 { 185 struct timeval *timeout; 186 struct bootp *bp; 187 struct servent *servp; 188 struct hostent *hep; 189 char *stmp; 190 int n, ba_len, ra_len; 191 int nfound, readfds; 192 int standalone; 193 194 progname = strrchr(argv[0], '/'); 195 if (progname) 196 progname++; 197 else 198 progname = argv[0]; 199 200 /* 201 * Initialize logging. 202 */ 203 report_init(0); /* uses progname */ 204 205 /* 206 * Log startup 207 */ 208 report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL); 209 210 /* Debugging for compilers with struct padding. */ 211 assert(sizeof(struct bootp) == BP_MINPKTSZ); 212 213 /* Get space for receiving packets and composing replies. */ 214 pktbuf = malloc(MAX_MSG_SIZE); 215 if (!pktbuf) { 216 report(LOG_ERR, "malloc failed"); 217 exit(1); 218 } 219 bp = (struct bootp *) pktbuf; 220 221 /* 222 * Check to see if a socket was passed to us from inetd. 223 * 224 * Use getsockname() to determine if descriptor 0 is indeed a socket 225 * (and thus we are probably a child of inetd) or if it is instead 226 * something else and we are running standalone. 227 */ 228 s = 0; 229 ba_len = sizeof(bind_addr); 230 bzero((char *) &bind_addr, ba_len); 231 errno = 0; 232 standalone = TRUE; 233 if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) { 234 /* 235 * Descriptor 0 is a socket. Assume we are a child of inetd. 236 */ 237 if (bind_addr.sin_family == AF_INET) { 238 standalone = FALSE; 239 bootps_port = ntohs(bind_addr.sin_port); 240 } else { 241 /* Some other type of socket? */ 242 report(LOG_ERR, "getsockname: not an INET socket"); 243 } 244 } 245 246 /* 247 * Set defaults that might be changed by option switches. 248 */ 249 stmp = NULL; 250 timeout = &actualtimeout; 251 252 /* 253 * Read switches. 254 */ 255 for (argc--, argv++; argc > 0; argc--, argv++) { 256 if (argv[0][0] != '-') 257 break; 258 switch (argv[0][1]) { 259 260 case 'c': /* chdir_path */ 261 if (argv[0][2]) { 262 stmp = &(argv[0][2]); 263 } else { 264 argc--; 265 argv++; 266 stmp = argv[0]; 267 } 268 if (!stmp || (stmp[0] != '/')) { 269 fprintf(stderr, 270 "bootpd: invalid chdir specification\n"); 271 break; 272 } 273 chdir_path = stmp; 274 break; 275 276 case 'd': /* debug level */ 277 if (argv[0][2]) { 278 stmp = &(argv[0][2]); 279 } else if (argv[1] && argv[1][0] == '-') { 280 /* 281 * Backwards-compatible behavior: 282 * no parameter, so just increment the debug flag. 283 */ 284 debug++; 285 break; 286 } else { 287 argc--; 288 argv++; 289 stmp = argv[0]; 290 } 291 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { 292 fprintf(stderr, 293 "%s: invalid debug level\n", progname); 294 break; 295 } 296 debug = n; 297 break; 298 299 case 'h': /* override hostname */ 300 if (argv[0][2]) { 301 stmp = &(argv[0][2]); 302 } else { 303 argc--; 304 argv++; 305 stmp = argv[0]; 306 } 307 if (!stmp) { 308 fprintf(stderr, 309 "bootpd: missing hostname\n"); 310 break; 311 } 312 strncpy(hostname, stmp, sizeof(hostname)-1); 313 break; 314 315 case 'i': /* inetd mode */ 316 standalone = FALSE; 317 break; 318 319 case 's': /* standalone mode */ 320 standalone = TRUE; 321 break; 322 323 case 't': /* timeout */ 324 if (argv[0][2]) { 325 stmp = &(argv[0][2]); 326 } else { 327 argc--; 328 argv++; 329 stmp = argv[0]; 330 } 331 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { 332 fprintf(stderr, 333 "%s: invalid timeout specification\n", progname); 334 break; 335 } 336 actualtimeout.tv_sec = (int32) (60 * n); 337 /* 338 * If the actual timeout is zero, pass a NULL pointer 339 * to select so it blocks indefinitely, otherwise, 340 * point to the actual timeout value. 341 */ 342 timeout = (n > 0) ? &actualtimeout : NULL; 343 break; 344 345 default: 346 fprintf(stderr, "%s: unknown switch: -%c\n", 347 progname, argv[0][1]); 348 usage(); 349 break; 350 351 } /* switch */ 352 } /* for args */ 353 354 /* 355 * Override default file names if specified on the command line. 356 */ 357 if (argc > 0) 358 bootptab = argv[0]; 359 360 if (argc > 1) 361 bootpd_dump = argv[1]; 362 363 /* 364 * Get my hostname and IP address. 365 */ 366 if (hostname[0] == '\0') { 367 if (gethostname(hostname, sizeof(hostname)) == -1) { 368 fprintf(stderr, "bootpd: can't get hostname\n"); 369 exit(1); 370 } 371 hostname[sizeof(hostname) - 1] = '\0'; 372 } 373 hep = gethostbyname(hostname); 374 if (!hep) { 375 fprintf(stderr, "Can not get my IP address\n"); 376 exit(1); 377 } 378 bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr)); 379 380 if (standalone) { 381 /* 382 * Go into background and disassociate from controlling terminal. 383 */ 384 if (debug < 3) { 385 if (fork()) 386 exit(0); 387 #ifdef NO_SETSID 388 setpgrp(0,0); 389 #ifdef TIOCNOTTY 390 n = open("/dev/tty", O_RDWR); 391 if (n >= 0) { 392 ioctl(n, TIOCNOTTY, (char *) 0); 393 (void) close(n); 394 } 395 #endif /* TIOCNOTTY */ 396 #else /* SETSID */ 397 if (setsid() < 0) 398 perror("setsid"); 399 #endif /* SETSID */ 400 } /* if debug < 3 */ 401 402 /* 403 * Nuke any timeout value 404 */ 405 timeout = NULL; 406 407 } /* if standalone (1st) */ 408 409 /* Set the cwd (i.e. to /tftpboot) */ 410 if (chdir_path) { 411 if (chdir(chdir_path) < 0) 412 report(LOG_ERR, "%s: chdir failed", chdir_path); 413 } 414 415 /* Get the timezone. */ 416 tzone_init(); 417 418 /* Allocate hash tables. */ 419 rdtab_init(); 420 421 /* 422 * Read the bootptab file. 423 */ 424 readtab(1); /* force read */ 425 426 if (standalone) { 427 428 /* 429 * Create a socket. 430 */ 431 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 432 report(LOG_ERR, "socket: %s", get_network_errmsg()); 433 exit(1); 434 } 435 436 /* 437 * Get server's listening port number 438 */ 439 servp = getservbyname("bootps", "udp"); 440 if (servp) { 441 bootps_port = ntohs((u_short) servp->s_port); 442 } else { 443 bootps_port = (u_short) IPPORT_BOOTPS; 444 report(LOG_ERR, 445 "udp/bootps: unknown service -- assuming port %d", 446 bootps_port); 447 } 448 449 /* 450 * Bind socket to BOOTPS port. 451 */ 452 bind_addr.sin_family = AF_INET; 453 bind_addr.sin_addr.s_addr = INADDR_ANY; 454 bind_addr.sin_port = htons(bootps_port); 455 if (bind(s, (struct sockaddr *) &bind_addr, 456 sizeof(bind_addr)) < 0) 457 { 458 report(LOG_ERR, "bind: %s", get_network_errmsg()); 459 exit(1); 460 } 461 } /* if standalone (2nd)*/ 462 463 /* 464 * Get destination port number so we can reply to client 465 */ 466 servp = getservbyname("bootpc", "udp"); 467 if (servp) { 468 bootpc_port = ntohs(servp->s_port); 469 } else { 470 report(LOG_ERR, 471 "udp/bootpc: unknown service -- assuming port %d", 472 IPPORT_BOOTPC); 473 bootpc_port = (u_short) IPPORT_BOOTPC; 474 } 475 476 /* 477 * Set up signals to read or dump the table. 478 */ 479 if ((long) signal(SIGHUP, catcher) < 0) { 480 report(LOG_ERR, "signal: %s", get_errmsg()); 481 exit(1); 482 } 483 if ((long) signal(SIGUSR1, catcher) < 0) { 484 report(LOG_ERR, "signal: %s", get_errmsg()); 485 exit(1); 486 } 487 488 /* 489 * Process incoming requests. 490 */ 491 for (;;) { 492 readfds = 1 << s; 493 nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL, timeout); 494 if (nfound < 0) { 495 if (errno != EINTR) { 496 report(LOG_ERR, "select: %s", get_errmsg()); 497 } 498 /* 499 * Call readtab() or dumptab() here to avoid the 500 * dangers of doing I/O from a signal handler. 501 */ 502 if (do_readtab) { 503 do_readtab = 0; 504 readtab(1); /* force read */ 505 } 506 if (do_dumptab) { 507 do_dumptab = 0; 508 dumptab(bootpd_dump); 509 } 510 continue; 511 } 512 if (!(readfds & (1 << s))) { 513 if (debug > 1) 514 report(LOG_INFO, "exiting after %ld minutes of inactivity", 515 actualtimeout.tv_sec / 60); 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 strncpy(realpath, hp->tftpdir->string, sizeof(realpath) - 1); 819 realpath[sizeof(realpath) - 1] = '\0'; 820 clntpath = &realpath[strlen(realpath)]; 821 } else { 822 realpath[0] = '\0'; 823 clntpath = realpath; 824 } 825 826 /* 827 * Determine client's requested homedir and bootfile. 828 */ 829 homedir = NULL; 830 bootfile = NULL; 831 if (bp->bp_file[0]) { 832 char *t; 833 834 homedir = bp->bp_file; 835 836 /* make sure that the file is nul terminated */ 837 for (t = homedir; t - homedir < BP_FILE_LEN; t++) 838 if (*t == '\0') 839 break; 840 if (t - homedir < BP_FILE_LEN) { 841 report(LOG_INFO, "requested path length > BP_FILE_LEN file = \"%s\", nul terminating", homedir); 842 homedir[BP_FILE_LEN - 1] = '\0'; 843 } 844 845 bootfile = strrchr(homedir, '/'); 846 if (bootfile) { 847 if (homedir == bootfile) 848 homedir = NULL; 849 *bootfile++ = '\0'; 850 } else { 851 /* no "/" in the string */ 852 bootfile = homedir; 853 homedir = NULL; 854 } 855 if (debug > 2) { 856 report(LOG_INFO, "requested path=\"%s\" file=\"%s\"", 857 (homedir) ? homedir : "", 858 (bootfile) ? bootfile : ""); 859 } 860 } 861 862 /* 863 * Specifications in bootptab override client requested values. 864 */ 865 if (hp->flags.homedir) 866 homedir = hp->homedir->string; 867 if (hp->flags.bootfile) 868 bootfile = hp->bootfile->string; 869 870 /* 871 * Construct bootfile path. 872 */ 873 if (homedir) { 874 if (homedir[0] != '/') { 875 strncat(realpath, "/", sizeof(realpath) - 1); 876 realpath[sizeof(realpath) - 1] = '\0'; 877 } 878 strncat(realpath, homedir, sizeof(realpath) - 1); 879 realpath[sizeof(realpath) - 1] = '\0'; 880 homedir = NULL; 881 } 882 if (bootfile) { 883 if (bootfile[0] != '/') { 884 strcat(realpath, "/"); 885 realpath[sizeof(realpath) - 1] = '\0'; 886 } 887 strcat(realpath, bootfile); 888 realpath[sizeof(realpath) - 1] = '\0'; 889 bootfile = NULL; 890 } 891 892 /* 893 * First try to find the file with a ".host" suffix 894 */ 895 n = strlen(clntpath); 896 strcat(clntpath, "."); 897 strcat(clntpath, hp->hostname->string); 898 if (chk_access(realpath, &bootsize) < 0) { 899 clntpath[n] = 0; /* Try it without the suffix */ 900 if (chk_access(realpath, &bootsize) < 0) { 901 /* neither "file.host" nor "file" was found */ 902 #ifdef CHECK_FILE_ACCESS 903 904 if (bp->bp_file[0]) { 905 /* 906 * Client wanted specific file 907 * and we didn't have it. 908 */ 909 report(LOG_NOTICE, 910 "requested file not found: \"%s\"", clntpath); 911 return; 912 } 913 /* 914 * Client didn't ask for a specific file and we couldn't 915 * access the default file, so just zero-out the bootfile 916 * field in the packet and continue processing the reply. 917 */ 918 bzero(bp->bp_file, sizeof(bp->bp_file)); 919 goto null_file_name; 920 921 #else /* CHECK_FILE_ACCESS */ 922 923 /* Complain only if boot file size was needed. */ 924 if (hp->flags.bootsize_auto) { 925 report(LOG_ERR, "can not determine size of file \"%s\"", 926 clntpath); 927 } 928 929 #endif /* CHECK_FILE_ACCESS */ 930 } 931 } 932 strncpy(bp->bp_file, clntpath, BP_FILE_LEN); 933 if (debug > 2) 934 report(LOG_INFO, "bootfile=\"%s\"", clntpath); 935 936 #ifdef CHECK_FILE_ACCESS 937 null_file_name: 938 #endif /* CHECK_FILE_ACCESS */ 939 940 941 /* 942 * Handle vendor options based on magic number. 943 */ 944 945 if (debug > 1) { 946 report(LOG_INFO, "vendor magic field is %d.%d.%d.%d", 947 (int) ((bp->bp_vend)[0]), 948 (int) ((bp->bp_vend)[1]), 949 (int) ((bp->bp_vend)[2]), 950 (int) ((bp->bp_vend)[3])); 951 } 952 /* 953 * If this host isn't set for automatic vendor info then copy the 954 * specific cookie into the bootp packet, thus forcing a certain 955 * reply format. Only force reply format if user specified it. 956 */ 957 if (hp->flags.vm_cookie) { 958 /* Slam in the user specified magic number. */ 959 bcopy(hp->vm_cookie, bp->bp_vend, 4); 960 } 961 /* 962 * Figure out the format for the vendor-specific info. 963 * Note that bp->bp_vend may have been set above. 964 */ 965 if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) { 966 /* RFC1048 conformant bootp client */ 967 dovend_rfc1048(bp, hp, bootsize); 968 if (debug > 1) { 969 report(LOG_INFO, "sending reply (with RFC1048 options)"); 970 } 971 } 972 #ifdef VEND_CMU 973 else if (!bcmp(bp->bp_vend, vm_cmu, 4)) { 974 dovend_cmu(bp, hp); 975 if (debug > 1) { 976 report(LOG_INFO, "sending reply (with CMU options)"); 977 } 978 } 979 #endif 980 else { 981 if (debug > 1) { 982 report(LOG_INFO, "sending reply (with no options)"); 983 } 984 } 985 986 dest = (hp->flags.reply_addr) ? 987 hp->reply_addr.s_addr : 0L; 988 989 /* not forwarded */ 990 sendreply(0, dest); 991 } 992 993 994 /* 995 * Process BOOTREPLY packet. 996 */ 997 PRIVATE void 998 handle_reply(void) 999 { 1000 if (debug) { 1001 report(LOG_INFO, "processing boot reply"); 1002 } 1003 /* forwarded, no destination override */ 1004 sendreply(1, 0); 1005 } 1006 1007 1008 /* 1009 * Send a reply packet to the client. 'forward' flag is set if we are 1010 * not the originator of this reply packet. 1011 */ 1012 PRIVATE void 1013 sendreply(int forward, int32 dst_override) 1014 { 1015 struct bootp *bp = (struct bootp *) pktbuf; 1016 struct in_addr dst; 1017 u_short port = bootpc_port; 1018 unsigned char *ha; 1019 int len; 1020 1021 /* 1022 * XXX - Should honor bp_flags "broadcast" bit here. 1023 * Temporary workaround: use the :ra=ADDR: option to 1024 * set the reply address to the broadcast address. 1025 */ 1026 1027 /* 1028 * If the destination address was specified explicitly 1029 * (i.e. the broadcast address for HP compatiblity) 1030 * then send the response to that address. Otherwise, 1031 * act in accordance with RFC951: 1032 * If the client IP address is specified, use that 1033 * else if gateway IP address is specified, use that 1034 * else make a temporary arp cache entry for the client's 1035 * NEW IP/hardware address and use that. 1036 */ 1037 if (dst_override) { 1038 dst.s_addr = dst_override; 1039 if (debug > 1) { 1040 report(LOG_INFO, "reply address override: %s", 1041 inet_ntoa(dst)); 1042 } 1043 } else if (bp->bp_ciaddr.s_addr) { 1044 dst = bp->bp_ciaddr; 1045 } else if (bp->bp_giaddr.s_addr && forward == 0) { 1046 dst = bp->bp_giaddr; 1047 port = bootps_port; 1048 if (debug > 1) { 1049 report(LOG_INFO, "sending reply to gateway %s", 1050 inet_ntoa(dst)); 1051 } 1052 } else { 1053 dst = bp->bp_yiaddr; 1054 ha = bp->bp_chaddr; 1055 len = bp->bp_hlen; 1056 if (len > MAXHADDRLEN) 1057 len = MAXHADDRLEN; 1058 1059 if (debug > 1) 1060 report(LOG_INFO, "setarp %s - %s", 1061 inet_ntoa(dst), haddrtoa(ha, len)); 1062 setarp(s, &dst, ha, len); 1063 } 1064 1065 if ((forward == 0) && 1066 (bp->bp_siaddr.s_addr == 0)) 1067 { 1068 struct ifreq *ifr; 1069 struct in_addr siaddr; 1070 /* 1071 * If we are originating this reply, we 1072 * need to find our own interface address to 1073 * put in the bp_siaddr field of the reply. 1074 * If this server is multi-homed, pick the 1075 * 'best' interface (the one on the same net 1076 * as the client). Of course, the client may 1077 * be on the other side of a BOOTP gateway... 1078 */ 1079 ifr = getif(s, &dst); 1080 if (ifr) { 1081 struct sockaddr_in *sip; 1082 sip = (struct sockaddr_in *) &(ifr->ifr_addr); 1083 siaddr = sip->sin_addr; 1084 } else { 1085 /* Just use my "official" IP address. */ 1086 siaddr = my_ip_addr; 1087 } 1088 1089 /* XXX - No need to set bp_giaddr here. */ 1090 1091 /* Finally, set the server address field. */ 1092 bp->bp_siaddr = siaddr; 1093 } 1094 /* Set up socket address for send. */ 1095 send_addr.sin_family = AF_INET; 1096 send_addr.sin_port = htons(port); 1097 send_addr.sin_addr = dst; 1098 1099 /* Send reply with same size packet as request used. */ 1100 if (sendto(s, pktbuf, pktlen, 0, 1101 (struct sockaddr *) &send_addr, 1102 sizeof(send_addr)) < 0) 1103 { 1104 report(LOG_ERR, "sendto: %s", get_network_errmsg()); 1105 } 1106 } /* sendreply */ 1107 1108 1109 /* nmatch() - now in getif.c */ 1110 /* setarp() - now in hwaddr.c */ 1111 1112 1113 /* 1114 * This call checks read access to a file. It returns 0 if the file given 1115 * by "path" exists and is publically readable. A value of -1 is returned if 1116 * access is not permitted or an error occurs. Successful calls also 1117 * return the file size in bytes using the long pointer "filesize". 1118 * 1119 * The read permission bit for "other" users is checked. This bit must be 1120 * set for tftpd(8) to allow clients to read the file. 1121 */ 1122 1123 PRIVATE int 1124 chk_access(char *path, int32 *filesize) 1125 { 1126 struct stat st; 1127 1128 if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) { 1129 *filesize = (int32) st.st_size; 1130 return 0; 1131 } else { 1132 return -1; 1133 } 1134 } 1135 1136 1137 /* 1138 * Now in dumptab.c : 1139 * dumptab() 1140 * dump_host() 1141 * list_ipaddresses() 1142 */ 1143 1144 #ifdef VEND_CMU 1145 1146 /* 1147 * Insert the CMU "vendor" data for the host pointed to by "hp" into the 1148 * bootp packet pointed to by "bp". 1149 */ 1150 1151 PRIVATE void 1152 dovend_cmu(struct bootp *bp, struct host *hp) 1153 { 1154 struct cmu_vend *vendp; 1155 struct in_addr_list *taddr; 1156 1157 /* 1158 * Initialize the entire vendor field to zeroes. 1159 */ 1160 bzero(bp->bp_vend, sizeof(bp->bp_vend)); 1161 1162 /* 1163 * Fill in vendor information. Subnet mask, default gateway, 1164 * domain name server, ien name server, time server 1165 */ 1166 vendp = (struct cmu_vend *) bp->bp_vend; 1167 strcpy(vendp->v_magic, (char *)vm_cmu); 1168 if (hp->flags.subnet_mask) { 1169 (vendp->v_smask).s_addr = hp->subnet_mask.s_addr; 1170 (vendp->v_flags) |= VF_SMASK; 1171 if (hp->flags.gateway) { 1172 (vendp->v_dgate).s_addr = hp->gateway->addr->s_addr; 1173 } 1174 } 1175 if (hp->flags.domain_server) { 1176 taddr = hp->domain_server; 1177 if (taddr->addrcount > 0) { 1178 (vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr; 1179 if (taddr->addrcount > 1) { 1180 (vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr; 1181 } 1182 } 1183 } 1184 if (hp->flags.name_server) { 1185 taddr = hp->name_server; 1186 if (taddr->addrcount > 0) { 1187 (vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr; 1188 if (taddr->addrcount > 1) { 1189 (vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr; 1190 } 1191 } 1192 } 1193 if (hp->flags.time_server) { 1194 taddr = hp->time_server; 1195 if (taddr->addrcount > 0) { 1196 (vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr; 1197 if (taddr->addrcount > 1) { 1198 (vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr; 1199 } 1200 } 1201 } 1202 /* Log message now done by caller. */ 1203 } /* dovend_cmu */ 1204 1205 #endif /* VEND_CMU */ 1206 1207 1208 1209 /* 1210 * Insert the RFC1048 vendor data for the host pointed to by "hp" into the 1211 * bootp packet pointed to by "bp". 1212 */ 1213 #define NEED(LEN, MSG) do \ 1214 if (bytesleft < (LEN)) { \ 1215 report(LOG_NOTICE, noroom, \ 1216 hp->hostname->string, MSG); \ 1217 return; \ 1218 } while (0) 1219 PRIVATE void 1220 dovend_rfc1048(struct bootp *bp, struct host *hp, int32 bootsize) 1221 { 1222 int bytesleft, len; 1223 byte *vp; 1224 1225 static const char noroom[] = "%s: No room for \"%s\" option"; 1226 1227 vp = bp->bp_vend; 1228 1229 if (hp->flags.msg_size) { 1230 pktlen = hp->msg_size; 1231 } else { 1232 /* 1233 * If the request was longer than the official length, build 1234 * a response of that same length where the additional length 1235 * is assumed to be part of the bp_vend (options) area. 1236 */ 1237 if (pktlen > sizeof(*bp)) { 1238 if (debug > 1) 1239 report(LOG_INFO, "request message length=%d", pktlen); 1240 } 1241 /* 1242 * Check whether the request contains the option: 1243 * Maximum DHCP Message Size (RFC1533 sec. 9.8) 1244 * and if so, override the response length with its value. 1245 * This request must lie within the first BP_VEND_LEN 1246 * bytes of the option space. 1247 */ 1248 { 1249 byte *p, *ep; 1250 byte tag, len; 1251 short msgsz = 0; 1252 1253 p = vp + 4; 1254 ep = p + BP_VEND_LEN - 4; 1255 while (p < ep) { 1256 tag = *p++; 1257 /* Check for tags with no data first. */ 1258 if (tag == TAG_PAD) 1259 continue; 1260 if (tag == TAG_END) 1261 break; 1262 /* Now scan the length byte. */ 1263 len = *p++; 1264 switch (tag) { 1265 case TAG_MAX_MSGSZ: 1266 if (len == 2) { 1267 bcopy(p, (char*)&msgsz, 2); 1268 msgsz = ntohs(msgsz); 1269 } 1270 break; 1271 case TAG_SUBNET_MASK: 1272 /* XXX - Should preserve this if given... */ 1273 break; 1274 } /* swtich */ 1275 p += len; 1276 } 1277 1278 if (msgsz > sizeof(*bp)) { 1279 if (debug > 1) 1280 report(LOG_INFO, "request has DHCP msglen=%d", msgsz); 1281 pktlen = msgsz; 1282 } 1283 } 1284 } 1285 1286 if (pktlen < sizeof(*bp)) { 1287 report(LOG_ERR, "invalid response length=%d", pktlen); 1288 pktlen = sizeof(*bp); 1289 } 1290 bytesleft = ((byte*)bp + pktlen) - vp; 1291 if (pktlen > sizeof(*bp)) { 1292 if (debug > 1) 1293 report(LOG_INFO, "extended reply, length=%d, options=%d", 1294 pktlen, bytesleft); 1295 } 1296 1297 /* Copy in the magic cookie */ 1298 bcopy(vm_rfc1048, vp, 4); 1299 vp += 4; 1300 bytesleft -= 4; 1301 1302 if (hp->flags.subnet_mask) { 1303 /* always enough room here. */ 1304 *vp++ = TAG_SUBNET_MASK;/* -1 byte */ 1305 *vp++ = 4; /* -1 byte */ 1306 insert_u_long(hp->subnet_mask.s_addr, &vp); /* -4 bytes */ 1307 bytesleft -= 6; /* Fix real count */ 1308 if (hp->flags.gateway) { 1309 (void) insert_ip(TAG_GATEWAY, 1310 hp->gateway, 1311 &vp, &bytesleft); 1312 } 1313 } 1314 if (hp->flags.bootsize) { 1315 /* always enough room here */ 1316 bootsize = (hp->flags.bootsize_auto) ? 1317 ((bootsize + 511) / 512) : (hp->bootsize); /* Round up */ 1318 *vp++ = TAG_BOOT_SIZE; 1319 *vp++ = 2; 1320 *vp++ = (byte) ((bootsize >> 8) & 0xFF); 1321 *vp++ = (byte) (bootsize & 0xFF); 1322 bytesleft -= 4; /* Tag, length, and 16 bit blocksize */ 1323 } 1324 /* 1325 * This one is special: Remaining options go in the ext file. 1326 * Only the subnet_mask, bootsize, and gateway should precede. 1327 */ 1328 if (hp->flags.exten_file) { 1329 /* 1330 * Check for room for exten_file. Add 3 to account for 1331 * TAG_EXTEN_FILE, length, and TAG_END. 1332 */ 1333 len = strlen(hp->exten_file->string); 1334 NEED((len + 3), "ef"); 1335 *vp++ = TAG_EXTEN_FILE; 1336 *vp++ = (byte) (len & 0xFF); 1337 bcopy(hp->exten_file->string, vp, len); 1338 vp += len; 1339 *vp++ = TAG_END; 1340 bytesleft -= len + 3; 1341 return; /* no more options here. */ 1342 } 1343 /* 1344 * The remaining options are inserted by the following 1345 * function (which is shared with bootpef.c). 1346 * Keep back one byte for the TAG_END. 1347 */ 1348 len = dovend_rfc1497(hp, vp, bytesleft - 1); 1349 vp += len; 1350 bytesleft -= len; 1351 1352 /* There should be at least one byte left. */ 1353 NEED(1, "(end)"); 1354 *vp++ = TAG_END; 1355 bytesleft--; 1356 1357 /* Log message done by caller. */ 1358 if (bytesleft > 0) { 1359 /* 1360 * Zero out any remaining part of the vendor area. 1361 */ 1362 bzero(vp, bytesleft); 1363 } 1364 } /* dovend_rfc1048 */ 1365 #undef NEED 1366 1367 1368 /* 1369 * Now in readfile.c: 1370 * hwlookcmp() 1371 * iplookcmp() 1372 */ 1373 1374 /* haddrtoa() - now in hwaddr.c */ 1375 /* 1376 * Now in dovend.c: 1377 * insert_ip() 1378 * insert_generic() 1379 * insert_u_long() 1380 */ 1381 1382 /* get_errmsg() - now in report.c */ 1383 1384 /* 1385 * Local Variables: 1386 * tab-width: 4 1387 * c-indent-level: 4 1388 * c-argdecl-indent: 4 1389 * c-continued-statement-offset: 4 1390 * c-continued-brace-offset: -4 1391 * c-label-offset: -4 1392 * c-brace-offset: 0 1393 * End: 1394 */ 1395