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