/************************************************************************1Copyright 1988, 1991 by Carnegie Mellon University23All Rights Reserved45Permission to use, copy, modify, and distribute this software and its6documentation for any purpose and without fee is hereby granted, provided7that the above copyright notice appear in all copies and that both that8copyright notice and this permission notice appear in supporting9documentation, and that the name of Carnegie Mellon University not be used10in advertising or publicity pertaining to distribution of the software11without specific, written prior permission.1213CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS14SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.15IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL16DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR17PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS18ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS19SOFTWARE.2021************************************************************************/2223/*24* BOOTP (bootstrap protocol) server daemon.25*26* Answers BOOTP request packets from booting client machines.27* See [SRI-NIC]<RFC>RFC951.TXT for a description of the protocol.28* See [SRI-NIC]<RFC>RFC1048.TXT for vendor-information extensions.29* See RFC 1395 for option tags 14-17.30* See accompanying man page -- bootpd.831*32* HISTORY33* See ./Changes34*35* BUGS36* See ./ToDo37*/3839#include <sys/types.h>40#include <sys/param.h>41#include <sys/socket.h>42#include <sys/ioctl.h>43#include <sys/file.h>44#include <sys/time.h>45#include <sys/stat.h>46#include <sys/utsname.h>4748#include <net/if.h>49#include <netinet/in.h>50#include <arpa/inet.h> /* inet_ntoa */5152#ifndef NO_UNISTD53#include <unistd.h>54#endif5556#include <stdlib.h>57#include <signal.h>58#include <stdio.h>59#include <string.h>60#include <errno.h>61#include <ctype.h>62#include <netdb.h>63#include <paths.h>64#include <syslog.h>65#include <assert.h>66#include <inttypes.h>6768#ifdef NO_SETSID69# include <fcntl.h> /* for O_RDONLY, etc */70#endif7172#include "bootp.h"73#include "hash.h"74#include "hwaddr.h"75#include "bootpd.h"76#include "dovend.h"77#include "getif.h"78#include "readfile.h"79#include "report.h"80#include "tzone.h"81#include "patchlevel.h"8283#ifndef CONFIG_FILE84#define CONFIG_FILE "/etc/bootptab"85#endif86#ifndef DUMPTAB_FILE87#define DUMPTAB_FILE "/tmp/bootpd.dump"88#endif89909192/*93* Externals, forward declarations, and global variables94*/9596extern void dumptab(char *);9798PRIVATE void catcher(int);99PRIVATE int chk_access(char *, int32 *);100#ifdef VEND_CMU101PRIVATE void dovend_cmu(struct bootp *, struct host *);102#endif103PRIVATE void dovend_rfc1048(struct bootp *, struct host *, int32);104PRIVATE void handle_reply(void);105PRIVATE void handle_request(void);106PRIVATE void sendreply(int forward, int32 dest_override);107PRIVATE void usage(void);108109/*110* IP port numbers for client and server obtained from /etc/services111*/112113u_short bootps_port, bootpc_port;114115116/*117* Internet socket and interface config structures118*/119120struct sockaddr_in bind_addr; /* Listening */121struct sockaddr_in recv_addr; /* Packet source */122struct sockaddr_in send_addr; /* destination */123124125/*126* option defaults127*/128int debug = 0; /* Debugging flag (level) */129struct timeval actualtimeout =130{ /* fifteen minutes */13115 * 60L, /* tv_sec */1320 /* tv_usec */133};134int arpmod = TRUE; /* modify the ARP table */135136/*137* General138*/139140int s; /* Socket file descriptor */141char *pktbuf; /* Receive packet buffer */142int pktlen;143char *progname;144char *chdir_path;145struct in_addr my_ip_addr;146147static const char *hostname;148static char default_hostname[MAXHOSTNAMELEN];149150/* Flags set by signal catcher. */151PRIVATE int do_readtab = 0;152PRIVATE int do_dumptab = 0;153154/*155* Globals below are associated with the bootp database file (bootptab).156*/157158char *bootptab = CONFIG_FILE;159char *bootpd_dump = DUMPTAB_FILE;160161162163/*164* Initialization such as command-line processing is done and then the165* main server loop is started.166*/167168int169main(int argc, char **argv)170{171struct timeval *timeout;172struct bootp *bp;173struct servent *servp;174struct hostent *hep;175char *stmp;176socklen_t ba_len, ra_len;177int n;178int nfound;179fd_set readfds;180int standalone;181#ifdef SA_NOCLDSTOP /* Have POSIX sigaction(2). */182struct sigaction sa;183#endif184185progname = strrchr(argv[0], '/');186if (progname) progname++;187else progname = argv[0];188189/*190* Initialize logging.191*/192report_init(0); /* uses progname */193194/*195* Log startup196*/197report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);198199/* Debugging for compilers with struct padding. */200assert(sizeof(struct bootp) == BP_MINPKTSZ);201202/* Get space for receiving packets and composing replies. */203pktbuf = malloc(MAX_MSG_SIZE);204if (!pktbuf) {205report(LOG_ERR, "malloc failed");206exit(1);207}208bp = (struct bootp *) pktbuf;209210/*211* Check to see if a socket was passed to us from inetd.212*213* Use getsockname() to determine if descriptor 0 is indeed a socket214* (and thus we are probably a child of inetd) or if it is instead215* something else and we are running standalone.216*/217s = 0;218ba_len = sizeof(bind_addr);219bzero((char *) &bind_addr, ba_len);220errno = 0;221standalone = TRUE;222if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {223/*224* Descriptor 0 is a socket. Assume we are a child of inetd.225*/226if (bind_addr.sin_family == AF_INET) {227standalone = FALSE;228bootps_port = ntohs(bind_addr.sin_port);229} else {230/* Some other type of socket? */231report(LOG_ERR, "getsockname: not an INET socket");232}233}234235/*236* Set defaults that might be changed by option switches.237*/238stmp = NULL;239timeout = &actualtimeout;240241if (gethostname(default_hostname, sizeof(default_hostname) - 1) < 0) {242report(LOG_ERR, "bootpd: can't get hostname\n");243exit(1);244}245default_hostname[sizeof(default_hostname) - 1] = '\0';246hostname = default_hostname;247248/*249* Read switches.250*/251for (argc--, argv++; argc > 0; argc--, argv++) {252if (argv[0][0] != '-')253break;254switch (argv[0][1]) {255256case 'a': /* don't modify the ARP table */257arpmod = FALSE;258break;259case 'c': /* chdir_path */260if (argv[0][2]) {261stmp = &(argv[0][2]);262} else {263argc--;264argv++;265stmp = argv[0];266}267if (!stmp || (stmp[0] != '/')) {268report(LOG_ERR,269"bootpd: invalid chdir specification\n");270break;271}272chdir_path = stmp;273break;274275case 'd': /* debug level */276if (argv[0][2]) {277stmp = &(argv[0][2]);278} else if (argv[1] && argv[1][0] == '-') {279/*280* Backwards-compatible behavior:281* no parameter, so just increment the debug flag.282*/283debug++;284break;285} else {286argc--;287argv++;288stmp = argv[0];289}290if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {291report(LOG_ERR,292"%s: invalid debug level\n", progname);293break;294}295debug = n;296break;297298case 'h': /* override hostname */299if (argv[0][2]) {300stmp = &(argv[0][2]);301} else {302argc--;303argv++;304stmp = argv[0];305}306if (!stmp) {307report(LOG_ERR,308"bootpd: missing hostname\n");309break;310}311hostname = stmp;312break;313314case 'i': /* inetd mode */315standalone = FALSE;316break;317318case 's': /* standalone mode */319standalone = TRUE;320break;321322case 't': /* timeout */323if (argv[0][2]) {324stmp = &(argv[0][2]);325} else {326argc--;327argv++;328stmp = argv[0];329}330if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {331report(LOG_ERR,332"%s: invalid timeout specification\n", progname);333break;334}335actualtimeout.tv_sec = (int32) (60 * n);336/*337* If the actual timeout is zero, pass a NULL pointer338* to select so it blocks indefinitely, otherwise,339* point to the actual timeout value.340*/341timeout = (n > 0) ? &actualtimeout : NULL;342break;343344default:345report(LOG_ERR, "%s: unknown switch: -%c\n",346progname, argv[0][1]);347usage();348break;349350} /* switch */351} /* for args */352353/*354* Override default file names if specified on the command line.355*/356if (argc > 0)357bootptab = argv[0];358359if (argc > 1)360bootpd_dump = argv[1];361362/*363* Get my hostname and IP address.364*/365366hep = gethostbyname(hostname);367if (!hep) {368report(LOG_ERR, "Can not get my IP address\n");369exit(1);370}371bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));372373if (standalone) {374/*375* Go into background and disassociate from controlling terminal.376*/377if (debug < 3) {378if (fork())379exit(0);380#ifdef NO_SETSID381setpgrp(0,0);382#ifdef TIOCNOTTY383n = open(_PATH_TTY, O_RDWR);384if (n >= 0) {385ioctl(n, TIOCNOTTY, (char *) 0);386(void) close(n);387}388#endif /* TIOCNOTTY */389#else /* SETSID */390if (setsid() < 0)391perror("setsid");392#endif /* SETSID */393} /* if debug < 3 */394395/*396* Nuke any timeout value397*/398timeout = NULL;399400} /* if standalone (1st) */401402/* Set the cwd (i.e. to /tftpboot) */403if (chdir_path) {404if (chdir(chdir_path) < 0)405report(LOG_ERR, "%s: chdir failed", chdir_path);406}407408/* Get the timezone. */409tzone_init();410411/* Allocate hash tables. */412rdtab_init();413414/*415* Read the bootptab file.416*/417readtab(1); /* force read */418419if (standalone) {420421/*422* Create a socket.423*/424if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {425report(LOG_ERR, "socket: %s", get_network_errmsg());426exit(1);427}428429/*430* Get server's listening port number431*/432servp = getservbyname("bootps", "udp");433if (servp) {434bootps_port = ntohs((u_short) servp->s_port);435} else {436bootps_port = (u_short) IPPORT_BOOTPS;437report(LOG_ERR,438"bootps/udp: unknown service -- using port %d",439bootps_port);440}441442/*443* Bind socket to BOOTPS port.444*/445bind_addr.sin_family = AF_INET;446bind_addr.sin_addr.s_addr = INADDR_ANY;447bind_addr.sin_port = htons(bootps_port);448if (bind(s, (struct sockaddr *) &bind_addr,449sizeof(bind_addr)) < 0)450{451report(LOG_ERR, "bind: %s", get_network_errmsg());452exit(1);453}454} /* if standalone (2nd)*/455456/*457* Get destination port number so we can reply to client458*/459servp = getservbyname("bootpc", "udp");460if (servp) {461bootpc_port = ntohs(servp->s_port);462} else {463report(LOG_ERR,464"bootpc/udp: unknown service -- using port %d",465IPPORT_BOOTPC);466bootpc_port = (u_short) IPPORT_BOOTPC;467}468469/*470* Set up signals to read or dump the table.471*/472#ifdef SA_NOCLDSTOP /* Have POSIX sigaction(2). */473sa.sa_handler = catcher;474sigemptyset(&sa.sa_mask);475sa.sa_flags = 0;476if (sigaction(SIGHUP, &sa, NULL) < 0) {477report(LOG_ERR, "sigaction: %s", get_errmsg());478exit(1);479}480if (sigaction(SIGUSR1, &sa, NULL) < 0) {481report(LOG_ERR, "sigaction: %s", get_errmsg());482exit(1);483}484#else /* SA_NOCLDSTOP */485/* Old-fashioned UNIX signals */486if ((int) signal(SIGHUP, catcher) < 0) {487report(LOG_ERR, "signal: %s", get_errmsg());488exit(1);489}490if ((int) signal(SIGUSR1, catcher) < 0) {491report(LOG_ERR, "signal: %s", get_errmsg());492exit(1);493}494#endif /* SA_NOCLDSTOP */495496/*497* Process incoming requests.498*/499FD_ZERO(&readfds);500for (;;) {501struct timeval tv;502503FD_SET(s, &readfds);504if (timeout)505tv = *timeout;506507nfound = select(s + 1, &readfds, NULL, NULL,508(timeout) ? &tv : NULL);509if (nfound < 0) {510if (errno != EINTR) {511report(LOG_ERR, "select: %s", get_errmsg());512}513/*514* Call readtab() or dumptab() here to avoid the515* dangers of doing I/O from a signal handler.516*/517if (do_readtab) {518do_readtab = 0;519readtab(1); /* force read */520}521if (do_dumptab) {522do_dumptab = 0;523dumptab(bootpd_dump);524}525continue;526}527if (!FD_ISSET(s, &readfds)) {528if (debug > 1)529report(LOG_INFO, "exiting after %jd minutes of inactivity",530(intmax_t)actualtimeout.tv_sec / 60);531exit(0);532}533ra_len = sizeof(recv_addr);534n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,535(struct sockaddr *) &recv_addr, &ra_len);536if (n <= 0) {537continue;538}539if (debug > 1) {540report(LOG_INFO, "recvd pkt from IP addr %s",541inet_ntoa(recv_addr.sin_addr));542}543if (n < sizeof(struct bootp)) {544if (debug) {545report(LOG_NOTICE, "received short packet");546}547continue;548}549pktlen = n;550551readtab(0); /* maybe re-read bootptab */552553switch (bp->bp_op) {554case BOOTREQUEST:555handle_request();556break;557case BOOTREPLY:558handle_reply();559break;560}561}562return 0;563}564565566567568/*569* Print "usage" message and exit570*/571572PRIVATE void573usage()574{575fprintf(stderr,576"usage: bootpd [-a] [-i | -s] [-c chdir-path] [-d level] [-h hostname]\n"577" [-t timeout] [bootptab [dumpfile]]\n");578fprintf(stderr, " -a\tdon't modify ARP table\n");579fprintf(stderr, " -c n\tset current directory\n");580fprintf(stderr, " -d n\tset debug level\n");581fprintf(stderr, " -h n\tset the hostname to listen on\n");582fprintf(stderr, " -i\tforce inetd mode (run as child of inetd)\n");583fprintf(stderr, " -s\tforce standalone mode (run without inetd)\n");584fprintf(stderr, " -t n\tset inetd exit timeout to n minutes\n");585exit(1);586}587588/* Signal catchers */589PRIVATE void590catcher(int sig)591{592if (sig == SIGHUP)593do_readtab = 1;594if (sig == SIGUSR1)595do_dumptab = 1;596#if !defined(SA_NOCLDSTOP) && defined(SYSV)597/* For older "System V" derivatives with no sigaction(). */598signal(sig, catcher);599#endif600}601602603604/*605* Process BOOTREQUEST packet.606*607* Note: This version of the bootpd.c server never forwards608* a request to another server. That is the job of a gateway609* program such as the "bootpgw" program included here.610*611* (Also this version does not interpret the hostname field of612* the request packet; it COULD do a name->address lookup and613* forward the request there.)614*/615PRIVATE void616handle_request(void)617{618struct bootp *bp = (struct bootp *) pktbuf;619struct host *hp = NULL;620struct host dummyhost;621int32 bootsize = 0;622unsigned hlen, hashcode;623int32 dest;624char realpath[1024];625char *clntpath;626char *homedir, *bootfile;627int n;628629if (bp->bp_htype >= hwinfocnt) {630report(LOG_NOTICE, "bad hw addr type %u", bp->bp_htype);631return;632}633bp->bp_file[sizeof(bp->bp_file)-1] = '\0';634635/* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */636637/*638* If the servername field is set, compare it against us.639* If we're not being addressed, ignore this request.640* If the server name field is null, throw in our name.641*/642if (strlen(bp->bp_sname)) {643if (strcmp(bp->bp_sname, hostname)) {644if (debug)645report(LOG_INFO, "\646ignoring request for server %s from client at %s address %s",647bp->bp_sname, netname(bp->bp_htype),648haddrtoa(bp->bp_chaddr, bp->bp_hlen));649/* XXX - Is it correct to ignore such a request? -gwr */650return;651}652} else {653strcpy(bp->bp_sname, hostname);654}655656/* Convert the request into a reply. */657bp->bp_op = BOOTREPLY;658if (bp->bp_ciaddr.s_addr == 0) {659/*660* client doesn't know his IP address,661* search by hardware address.662*/663if (debug > 1) {664report(LOG_INFO, "request from %s address %s",665netname(bp->bp_htype),666haddrtoa(bp->bp_chaddr, bp->bp_hlen));667}668hlen = haddrlength(bp->bp_htype);669if (hlen != bp->bp_hlen) {670report(LOG_NOTICE, "bad addr len from %s address %s",671netname(bp->bp_htype),672haddrtoa(bp->bp_chaddr, hlen));673}674dummyhost.htype = bp->bp_htype;675bcopy(bp->bp_chaddr, dummyhost.haddr, hlen);676hashcode = hash_HashFunction(bp->bp_chaddr, hlen);677hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp,678&dummyhost);679if (hp == NULL &&680bp->bp_htype == HTYPE_IEEE802)681{682/* Try again with address in "canonical" form. */683haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen);684if (debug > 1) {685report(LOG_INFO, "\686HW addr type is IEEE 802. convert to %s and check again\n",687haddrtoa(dummyhost.haddr, bp->bp_hlen));688}689hashcode = hash_HashFunction(dummyhost.haddr, hlen);690hp = (struct host *) hash_Lookup(hwhashtable, hashcode,691hwlookcmp, &dummyhost);692}693if (hp == NULL) {694/*695* XXX - Add dynamic IP address assignment?696*/697if (debug)698report(LOG_NOTICE, "unknown client %s address %s",699netname(bp->bp_htype),700haddrtoa(bp->bp_chaddr, bp->bp_hlen));701return; /* not found */702}703(bp->bp_yiaddr).s_addr = hp->iaddr.s_addr;704705} else {706707/*708* search by IP address.709*/710if (debug > 1) {711report(LOG_INFO, "request from IP addr %s",712inet_ntoa(bp->bp_ciaddr));713}714dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr;715hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4);716hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp,717&dummyhost);718if (hp == NULL) {719if (debug) {720report(LOG_NOTICE, "IP address not found: %s",721inet_ntoa(bp->bp_ciaddr));722}723return;724}725}726727if (debug) {728report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr),729hp->hostname->string);730}731732/*733* If there is a response delay threshold, ignore requests734* with a timestamp lower than the threshold.735*/736if (hp->flags.min_wait) {737u_int32 t = (u_int32) ntohs(bp->bp_secs);738if (t < hp->min_wait) {739if (debug > 1)740report(LOG_INFO,741"ignoring request due to timestamp (%d < %d)",742t, hp->min_wait);743return;744}745}746747#ifdef YORK_EX_OPTION748/*749* The need for the "ex" tag arose out of the need to empty750* shared networked drives on diskless PCs. This solution is751* not very clean but it does work fairly well.752* Written by Edmund J. Sutcliffe <[email protected]>753*754* XXX - This could compromise security if a non-trusted user755* managed to write an entry in the bootptab with :ex=trojan:756* so I would leave this turned off unless you need it. -gwr757*/758/* Run a program, passing the client name as a parameter. */759if (hp->flags.exec_file) {760char tst[100];761/* XXX - Check string lengths? -gwr */762strcpy (tst, hp->exec_file->string);763strcat (tst, " ");764strcat (tst, hp->hostname->string);765strcat (tst, " &");766if (debug)767report(LOG_INFO, "executing %s", tst);768system(tst); /* Hope this finishes soon... */769}770#endif /* YORK_EX_OPTION */771772/*773* If a specific TFTP server address was specified in the bootptab file,774* fill it in, otherwise zero it.775* XXX - Rather than zero it, should it be the bootpd address? -gwr776*/777(bp->bp_siaddr).s_addr = (hp->flags.bootserver) ?778hp->bootserver.s_addr : 0L;779780#ifdef STANFORD_PROM_COMPAT781/*782* Stanford bootp PROMs (for a Sun?) have no way to leave783* the boot file name field blank (because the boot file784* name is automatically generated from some index).785* As a work-around, this little hack allows those PROMs to786* specify "sunboot14" with the same effect as a NULL name.787* (The user specifies boot device 14 or some such magic.)788*/789if (strcmp(bp->bp_file, "sunboot14") == 0)790bp->bp_file[0] = '\0'; /* treat it as unspecified */791#endif792793/*794* Fill in the client's proper bootfile.795*796* If the client specifies an absolute path, try that file with a797* ".host" suffix and then without. If the file cannot be found, no798* reply is made at all.799*800* If the client specifies a null or relative file, use the following801* table to determine the appropriate action:802*803* Homedir Bootfile Client's file804* specified? specified? specification Action805* -------------------------------------------------------------------806* No No Null Send null filename807* No No Relative Discard request808* No Yes Null Send if absolute else null809* No Yes Relative Discard request *XXX810* Yes No Null Send null filename811* Yes No Relative Lookup with ".host"812* Yes Yes Null Send home/boot or bootfile813* Yes Yes Relative Lookup with ".host" *XXX814*815*/816817/*818* XXX - I don't like the policy of ignoring a client when the819* boot file is not accessible. The TFTP server might not be820* running on the same machine as the BOOTP server, in which821* case checking accessibility of the boot file is pointless.822*823* Therefore, file accessibility is now demanded ONLY if you824* define CHECK_FILE_ACCESS in the Makefile options. -gwr825*/826827/*828* The "real" path is as seen by the BOOTP daemon on this829* machine, while the client path is relative to the TFTP830* daemon chroot directory (i.e. /tftpboot).831*/832if (hp->flags.tftpdir) {833snprintf(realpath, sizeof(realpath), "%s", hp->tftpdir->string);834clntpath = &realpath[strlen(realpath)];835} else {836realpath[0] = '\0';837clntpath = realpath;838}839840/*841* Determine client's requested homedir and bootfile.842*/843homedir = NULL;844bootfile = NULL;845if (bp->bp_file[0]) {846homedir = bp->bp_file;847bootfile = strrchr(homedir, '/');848if (bootfile) {849if (homedir == bootfile)850homedir = NULL;851*bootfile++ = '\0';852} else {853/* no "/" in the string */854bootfile = homedir;855homedir = NULL;856}857if (debug > 2) {858report(LOG_INFO, "requested path=\"%s\" file=\"%s\"",859(homedir) ? homedir : "",860(bootfile) ? bootfile : "");861}862}863864/*865* Specifications in bootptab override client requested values.866*/867if (hp->flags.homedir)868homedir = hp->homedir->string;869if (hp->flags.bootfile)870bootfile = hp->bootfile->string;871872/*873* Construct bootfile path.874*/875if (homedir) {876if (homedir[0] != '/')877strcat(clntpath, "/");878strcat(clntpath, homedir);879homedir = NULL;880}881if (bootfile) {882if (bootfile[0] != '/')883strcat(clntpath, "/");884strcat(clntpath, bootfile);885bootfile = NULL;886}887888/*889* First try to find the file with a ".host" suffix890*/891n = strlen(clntpath);892strcat(clntpath, ".");893strcat(clntpath, hp->hostname->string);894if (chk_access(realpath, &bootsize) < 0) {895clntpath[n] = 0; /* Try it without the suffix */896if (chk_access(realpath, &bootsize) < 0) {897/* neither "file.host" nor "file" was found */898#ifdef CHECK_FILE_ACCESS899900if (bp->bp_file[0]) {901/*902* Client wanted specific file903* and we didn't have it.904*/905report(LOG_NOTICE,906"requested file not found: \"%s\"", clntpath);907return;908}909/*910* Client didn't ask for a specific file and we couldn't911* access the default file, so just zero-out the bootfile912* field in the packet and continue processing the reply.913*/914bzero(bp->bp_file, sizeof(bp->bp_file));915goto null_file_name;916917#else /* CHECK_FILE_ACCESS */918919/* Complain only if boot file size was needed. */920if (hp->flags.bootsize_auto) {921report(LOG_ERR, "can not determine size of file \"%s\"",922clntpath);923}924925#endif /* CHECK_FILE_ACCESS */926}927}928strncpy(bp->bp_file, clntpath, BP_FILE_LEN);929if (debug > 2)930report(LOG_INFO, "bootfile=\"%s\"", clntpath);931932#ifdef CHECK_FILE_ACCESS933null_file_name:934#endif /* CHECK_FILE_ACCESS */935936937/*938* Handle vendor options based on magic number.939*/940941if (debug > 1) {942report(LOG_INFO, "vendor magic field is %d.%d.%d.%d",943(int) ((bp->bp_vend)[0]),944(int) ((bp->bp_vend)[1]),945(int) ((bp->bp_vend)[2]),946(int) ((bp->bp_vend)[3]));947}948/*949* If this host isn't set for automatic vendor info then copy the950* specific cookie into the bootp packet, thus forcing a certain951* reply format. Only force reply format if user specified it.952*/953if (hp->flags.vm_cookie) {954/* Slam in the user specified magic number. */955bcopy(hp->vm_cookie, bp->bp_vend, 4);956}957/*958* Figure out the format for the vendor-specific info.959* Note that bp->bp_vend may have been set above.960*/961if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) {962/* RFC1048 conformant bootp client */963dovend_rfc1048(bp, hp, bootsize);964if (debug > 1) {965report(LOG_INFO, "sending reply (with RFC1048 options)");966}967}968#ifdef VEND_CMU969else if (!bcmp(bp->bp_vend, vm_cmu, 4)) {970dovend_cmu(bp, hp);971if (debug > 1) {972report(LOG_INFO, "sending reply (with CMU options)");973}974}975#endif976else {977if (debug > 1) {978report(LOG_INFO, "sending reply (with no options)");979}980}981982dest = (hp->flags.reply_addr) ?983hp->reply_addr.s_addr : 0L;984985/* not forwarded */986sendreply(0, dest);987}988989990/*991* Process BOOTREPLY packet.992*/993PRIVATE void994handle_reply(void)995{996if (debug) {997report(LOG_INFO, "processing boot reply");998}999/* forwarded, no destination override */1000sendreply(1, 0);1001}100210031004/*1005* Send a reply packet to the client. 'forward' flag is set if we are1006* not the originator of this reply packet.1007*/1008PRIVATE void1009sendreply(int forward, int32 dst_override)1010{1011struct bootp *bp = (struct bootp *) pktbuf;1012struct in_addr dst;1013u_short port = bootpc_port;1014unsigned char *ha;1015int len, haf;10161017/*1018* XXX - Should honor bp_flags "broadcast" bit here.1019* Temporary workaround: use the :ra=ADDR: option to1020* set the reply address to the broadcast address.1021*/10221023/*1024* If the destination address was specified explicitly1025* (i.e. the broadcast address for HP compatibility)1026* then send the response to that address. Otherwise,1027* act in accordance with RFC951:1028* If the client IP address is specified, use that1029* else if gateway IP address is specified, use that1030* else make a temporary arp cache entry for the client's1031* NEW IP/hardware address and use that.1032*/1033if (dst_override) {1034dst.s_addr = dst_override;1035if (debug > 1) {1036report(LOG_INFO, "reply address override: %s",1037inet_ntoa(dst));1038}1039} else if (bp->bp_ciaddr.s_addr) {1040dst = bp->bp_ciaddr;1041} else if (bp->bp_giaddr.s_addr && forward == 0) {1042dst = bp->bp_giaddr;1043port = bootps_port;1044if (debug > 1) {1045report(LOG_INFO, "sending reply to gateway %s",1046inet_ntoa(dst));1047}1048} else {1049dst = bp->bp_yiaddr;1050ha = bp->bp_chaddr;1051len = bp->bp_hlen;1052if (len > MAXHADDRLEN)1053len = MAXHADDRLEN;1054haf = (int) bp->bp_htype;1055if (haf == 0)1056haf = HTYPE_ETHERNET;10571058if (arpmod) {1059if (debug > 1)1060report(LOG_INFO, "setarp %s - %s",1061inet_ntoa(dst), haddrtoa(ha, len));1062setarp(s, &dst, haf, ha, len);1063}1064}10651066if ((forward == 0) &&1067(bp->bp_siaddr.s_addr == 0))1068{1069struct ifreq *ifr;1070struct in_addr siaddr;1071/*1072* If we are originating this reply, we1073* need to find our own interface address to1074* put in the bp_siaddr field of the reply.1075* If this server is multi-homed, pick the1076* 'best' interface (the one on the same net1077* as the client). Of course, the client may1078* be on the other side of a BOOTP gateway...1079*/1080ifr = getif(s, &dst);1081if (ifr) {1082struct sockaddr_in *sip;1083sip = (struct sockaddr_in *) &(ifr->ifr_addr);1084siaddr = sip->sin_addr;1085} else {1086/* Just use my "official" IP address. */1087siaddr = my_ip_addr;1088}10891090/* XXX - No need to set bp_giaddr here. */10911092/* Finally, set the server address field. */1093bp->bp_siaddr = siaddr;1094}1095/* Set up socket address for send. */1096send_addr.sin_family = AF_INET;1097send_addr.sin_port = htons(port);1098send_addr.sin_addr = dst;10991100/* Send reply with same size packet as request used. */1101if (sendto(s, pktbuf, pktlen, 0,1102(struct sockaddr *) &send_addr,1103sizeof(send_addr)) < 0)1104{1105report(LOG_ERR, "sendto: %s", get_network_errmsg());1106}1107} /* sendreply */110811091110/* nmatch() - now in getif.c */1111/* setarp() - now in hwaddr.c */111211131114/*1115* This call checks read access to a file. It returns 0 if the file given1116* by "path" exists and is publicly readable. A value of -1 is returned if1117* access is not permitted or an error occurs. Successful calls also1118* return the file size in bytes using the long pointer "filesize".1119*1120* The read permission bit for "other" users is checked. This bit must be1121* set for tftpd(8) to allow clients to read the file.1122*/11231124PRIVATE int1125chk_access(char *path, int32 *filesize)1126{1127struct stat st;11281129if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) {1130*filesize = (int32) st.st_size;1131return 0;1132} else {1133return -1;1134}1135}113611371138/*1139* Now in dumptab.c :1140* dumptab()1141* dump_host()1142* list_ipaddresses()1143*/11441145#ifdef VEND_CMU11461147/*1148* Insert the CMU "vendor" data for the host pointed to by "hp" into the1149* bootp packet pointed to by "bp".1150*/11511152PRIVATE void1153dovend_cmu(struct bootp *bp, struct host *hp)1154{1155struct cmu_vend *vendp;1156struct in_addr_list *taddr;11571158/*1159* Initialize the entire vendor field to zeroes.1160*/1161bzero(bp->bp_vend, sizeof(bp->bp_vend));11621163/*1164* Fill in vendor information. Subnet mask, default gateway,1165* domain name server, ien name server, time server1166*/1167vendp = (struct cmu_vend *) bp->bp_vend;1168strcpy(vendp->v_magic, (char *)vm_cmu);1169if (hp->flags.subnet_mask) {1170(vendp->v_smask).s_addr = hp->subnet_mask.s_addr;1171(vendp->v_flags) |= VF_SMASK;1172if (hp->flags.gateway) {1173(vendp->v_dgate).s_addr = hp->gateway->addr->s_addr;1174}1175}1176if (hp->flags.domain_server) {1177taddr = hp->domain_server;1178if (taddr->addrcount > 0) {1179(vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr;1180if (taddr->addrcount > 1) {1181(vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr;1182}1183}1184}1185if (hp->flags.name_server) {1186taddr = hp->name_server;1187if (taddr->addrcount > 0) {1188(vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr;1189if (taddr->addrcount > 1) {1190(vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr;1191}1192}1193}1194if (hp->flags.time_server) {1195taddr = hp->time_server;1196if (taddr->addrcount > 0) {1197(vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr;1198if (taddr->addrcount > 1) {1199(vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr;1200}1201}1202}1203/* Log message now done by caller. */1204} /* dovend_cmu */12051206#endif /* VEND_CMU */1207120812091210/*1211* Insert the RFC1048 vendor data for the host pointed to by "hp" into the1212* bootp packet pointed to by "bp".1213*/1214#define NEED(LEN, MSG) do \1215if (bytesleft < (LEN)) { \1216report(LOG_NOTICE, noroom, \1217hp->hostname->string, MSG); \1218return; \1219} while (0)1220PRIVATE void1221dovend_rfc1048(struct bootp *bp, struct host *hp, int32 bootsize)1222{1223int bytesleft, len;1224byte *vp;12251226static const char noroom[] = "%s: No room for \"%s\" option";12271228vp = bp->bp_vend;12291230if (hp->flags.msg_size) {1231pktlen = hp->msg_size;1232} else {1233/*1234* If the request was longer than the official length, build1235* a response of that same length where the additional length1236* is assumed to be part of the bp_vend (options) area.1237*/1238if (pktlen > sizeof(*bp)) {1239if (debug > 1)1240report(LOG_INFO, "request message length=%d", pktlen);1241}1242/*1243* Check whether the request contains the option:1244* Maximum DHCP Message Size (RFC1533 sec. 9.8)1245* and if so, override the response length with its value.1246* This request must lie within the first BP_VEND_LEN1247* bytes of the option space.1248*/1249{1250byte *p, *ep;1251byte tag, len;1252short msgsz = 0;12531254p = vp + 4;1255ep = p + BP_VEND_LEN - 4;1256while (p < ep) {1257tag = *p++;1258/* Check for tags with no data first. */1259if (tag == TAG_PAD)1260continue;1261if (tag == TAG_END)1262break;1263/* Now scan the length byte. */1264len = *p++;1265switch (tag) {1266case TAG_MAX_MSGSZ:1267if (len == 2) {1268bcopy(p, (char*)&msgsz, 2);1269msgsz = ntohs(msgsz);1270}1271break;1272case TAG_SUBNET_MASK:1273/* XXX - Should preserve this if given... */1274break;1275} /* swtich */1276p += len;1277}12781279if (msgsz > sizeof(*bp) + BP_MSG_OVERHEAD) {1280if (debug > 1)1281report(LOG_INFO, "request has DHCP msglen=%d", msgsz);1282pktlen = msgsz - BP_MSG_OVERHEAD;1283}1284}1285}12861287if (pktlen < sizeof(*bp)) {1288report(LOG_ERR, "invalid response length=%d", pktlen);1289pktlen = sizeof(*bp);1290}1291bytesleft = ((byte*)bp + pktlen) - vp;1292if (pktlen > sizeof(*bp)) {1293if (debug > 1)1294report(LOG_INFO, "extended reply, length=%d, options=%d",1295pktlen, bytesleft);1296}12971298/* Copy in the magic cookie */1299bcopy(vm_rfc1048, vp, 4);1300vp += 4;1301bytesleft -= 4;13021303if (hp->flags.subnet_mask) {1304/* always enough room here. */1305*vp++ = TAG_SUBNET_MASK;/* -1 byte */1306*vp++ = 4; /* -1 byte */1307insert_u_long(hp->subnet_mask.s_addr, &vp); /* -4 bytes */1308bytesleft -= 6; /* Fix real count */1309if (hp->flags.gateway) {1310(void) insert_ip(TAG_GATEWAY,1311hp->gateway,1312&vp, &bytesleft);1313}1314}1315if (hp->flags.bootsize) {1316/* always enough room here */1317bootsize = (hp->flags.bootsize_auto) ?1318((bootsize + 511) / 512) : (hp->bootsize); /* Round up */1319*vp++ = TAG_BOOT_SIZE;1320*vp++ = 2;1321*vp++ = (byte) ((bootsize >> 8) & 0xFF);1322*vp++ = (byte) (bootsize & 0xFF);1323bytesleft -= 4; /* Tag, length, and 16 bit blocksize */1324}1325/*1326* This one is special: Remaining options go in the ext file.1327* Only the subnet_mask, bootsize, and gateway should precede.1328*/1329if (hp->flags.exten_file) {1330/*1331* Check for room for exten_file. Add 3 to account for1332* TAG_EXTEN_FILE, length, and TAG_END.1333*/1334len = strlen(hp->exten_file->string);1335NEED((len + 3), "ef");1336*vp++ = TAG_EXTEN_FILE;1337*vp++ = (byte) (len & 0xFF);1338bcopy(hp->exten_file->string, vp, len);1339vp += len;1340*vp++ = TAG_END;1341bytesleft -= len + 3;1342return; /* no more options here. */1343}1344/*1345* The remaining options are inserted by the following1346* function (which is shared with bootpef.c).1347* Keep back one byte for the TAG_END.1348*/1349len = dovend_rfc1497(hp, vp, bytesleft - 1);1350vp += len;1351bytesleft -= len;13521353/* There should be at least one byte left. */1354NEED(1, "(end)");1355*vp++ = TAG_END;1356bytesleft--;13571358/* Log message done by caller. */1359if (bytesleft > 0) {1360/*1361* Zero out any remaining part of the vendor area.1362*/1363bzero(vp, bytesleft);1364}1365} /* dovend_rfc1048 */1366#undef NEED136713681369/*1370* Now in readfile.c:1371* hwlookcmp()1372* iplookcmp()1373*/13741375/* haddrtoa() - now in hwaddr.c */1376/*1377* Now in dovend.c:1378* insert_ip()1379* insert_generic()1380* insert_u_long()1381*/13821383/* get_errmsg() - now in report.c */13841385/*1386* Local Variables:1387* tab-width: 41388* c-indent-level: 41389* c-argdecl-indent: 41390* c-continued-statement-offset: 41391* c-continued-brace-offset: -41392* c-label-offset: -41393* c-brace-offset: 01394* End:1395*/139613971398