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.16 2002/09/18 23:16:13 mycroft 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 strncpy(hostname, stmp, sizeof(hostname)-1); 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 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