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