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