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