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