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