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