Home | History | Annotate | Line # | Download | only in bootpd
bootpd.c revision 1.23
      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.23 2009/04/15 00:23:28 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 #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 PRIVATE void usage(void);
    120 int main(int, char **);
    121 
    122 /*
    123  * IP port numbers for client and server obtained from /etc/services
    124  */
    125 
    126 u_short bootps_port, bootpc_port;
    127 
    128 
    129 /*
    130  * Internet socket and interface config structures
    131  */
    132 
    133 struct sockaddr_in bind_addr;	/* Listening */
    134 struct sockaddr_in recv_addr;	/* Packet source */
    135 struct sockaddr_in send_addr;	/*  destination */
    136 
    137 
    138 /*
    139  * option defaults
    140  */
    141 int debug = 0;					/* Debugging flag (level) */
    142 int actualtimeout = 15 * 60000;			/* fifteen minutes */
    143 
    144 /*
    145  * General
    146  */
    147 
    148 int s;							/* Socket file descriptor */
    149 char *pktbuf;					/* Receive packet buffer */
    150 int pktlen;
    151 const char *progname;
    152 char *chdir_path;
    153 char hostname[MAXHOSTNAMELEN + 1];	/* System host name */
    154 struct in_addr my_ip_addr;
    155 
    156 /* Flags set by signal catcher. */
    157 PRIVATE int do_readtab = 0;
    158 PRIVATE int do_dumptab = 0;
    159 
    160 /*
    161  * Globals below are associated with the bootp database file (bootptab).
    162  */
    163 
    164 const char *bootptab = CONFIG_FILE;
    165 const char *bootpd_dump = DUMPTAB_FILE;
    166 
    167 
    168 
    170 /*
    171  * Initialization such as command-line processing is done and then the
    172  * main server loop is started.
    173  */
    174 
    175 int
    176 main(int argc, char **argv)
    177 {
    178 	int timeout;
    179 	struct bootp *bp;
    180 	struct servent *servp;
    181 	struct hostent *hep;
    182 	char *stmp;
    183 	socklen_t ba_len, ra_len;
    184 	int n;
    185 	int nfound;
    186 	struct pollfd set[1];
    187 	int standalone;
    188 
    189 	progname = strrchr(argv[0], '/');
    190 	if (progname)
    191 		progname++;
    192 	else
    193 		progname = argv[0];
    194 
    195 	/*
    196 	 * Initialize logging.
    197 	 */
    198 	report_init(0);				/* uses progname */
    199 
    200 	/*
    201 	 * Log startup
    202 	 */
    203 	report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
    204 
    205 	/* Debugging for compilers with struct padding. */
    206 	assert(sizeof(struct bootp) == BP_MINPKTSZ);
    207 
    208 	/* Get space for receiving packets and composing replies. */
    209 	pktbuf = malloc(MAX_MSG_SIZE);
    210 	if (!pktbuf) {
    211 		report(LOG_ERR, "malloc failed");
    212 		exit(1);
    213 	}
    214 	bp = (struct bootp *) pktbuf;
    215 
    216 	/*
    217 	 * Check to see if a socket was passed to us from inetd.
    218 	 *
    219 	 * Use getsockname() to determine if descriptor 0 is indeed a socket
    220 	 * (and thus we are probably a child of inetd) or if it is instead
    221 	 * something else and we are running standalone.
    222 	 */
    223 	s = 0;
    224 	ba_len = sizeof(bind_addr);
    225 	bzero((char *) &bind_addr, ba_len);
    226 	errno = 0;
    227 	standalone = TRUE;
    228 	if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {
    229 		/*
    230 		 * Descriptor 0 is a socket.  Assume we are a child of inetd.
    231 		 */
    232 		if (bind_addr.sin_family == AF_INET) {
    233 			standalone = FALSE;
    234 			bootps_port = ntohs(bind_addr.sin_port);
    235 		} else {
    236 			/* Some other type of socket? */
    237 			report(LOG_ERR, "getsockname: not an INET socket");
    238 		}
    239 	}
    240 
    241 	/*
    242 	 * Set defaults that might be changed by option switches.
    243 	 */
    244 	stmp = NULL;
    245 	timeout = actualtimeout;
    246 
    247 	/*
    248 	 * Read switches.
    249 	 */
    250 	for (argc--, argv++; argc > 0; argc--, argv++) {
    251 		if (argv[0][0] != '-')
    252 			break;
    253 		switch (argv[0][1]) {
    254 
    255 		case 'c':				/* chdir_path */
    256 			if (argv[0][2]) {
    257 				stmp = &(argv[0][2]);
    258 			} else {
    259 				argc--;
    260 				argv++;
    261 				stmp = argv[0];
    262 			}
    263 			if (!stmp || (stmp[0] != '/')) {
    264 				fprintf(stderr,
    265 						"bootpd: invalid chdir specification\n");
    266 				break;
    267 			}
    268 			chdir_path = stmp;
    269 			break;
    270 
    271 		case 'd':				/* debug level */
    272 			if (argv[0][2]) {
    273 				stmp = &(argv[0][2]);
    274 			} else if (argv[1] && argv[1][0] == '-') {
    275 				/*
    276 				 * Backwards-compatible behavior:
    277 				 * no parameter, so just increment the debug flag.
    278 				 */
    279 				debug++;
    280 				break;
    281 			} else {
    282 				argc--;
    283 				argv++;
    284 				stmp = argv[0];
    285 			}
    286 			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
    287 				fprintf(stderr,
    288 						"%s: invalid debug level\n", progname);
    289 				break;
    290 			}
    291 			debug = n;
    292 			break;
    293 
    294 		case 'h':				/* override hostname */
    295 			if (argv[0][2]) {
    296 				stmp = &(argv[0][2]);
    297 			} else {
    298 				argc--;
    299 				argv++;
    300 				stmp = argv[0];
    301 			}
    302 			if (!stmp) {
    303 				fprintf(stderr,
    304 						"bootpd: missing hostname\n");
    305 				break;
    306 			}
    307 			strlcpy(hostname, stmp, sizeof(hostname));
    308 			break;
    309 
    310 		case 'i':				/* inetd mode */
    311 			standalone = FALSE;
    312 			break;
    313 
    314 		case 's':				/* standalone mode */
    315 			standalone = TRUE;
    316 			break;
    317 
    318 		case 't':				/* timeout */
    319 			if (argv[0][2]) {
    320 				stmp = &(argv[0][2]);
    321 			} else {
    322 				argc--;
    323 				argv++;
    324 				stmp = argv[0];
    325 			}
    326 			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
    327 				fprintf(stderr,
    328 						"%s: invalid timeout specification\n", progname);
    329 				break;
    330 			}
    331 			actualtimeout = n * 60000;
    332 			/*
    333 			 * If the actual timeout is zero, pass INFTIM
    334 			 * to poll so it blocks indefinitely, otherwise,
    335 			 * use the actual timeout value.
    336 			 */
    337 			timeout = (n > 0) ? actualtimeout : INFTIM;
    338 			break;
    339 
    340 		default:
    341 			fprintf(stderr, "%s: unknown switch: -%c\n",
    342 					progname, argv[0][1]);
    343 			usage();
    344 			break;
    345 
    346 		} /* switch */
    347 	} /* for args */
    348 
    349 	/*
    350 	 * Override default file names if specified on the command line.
    351 	 */
    352 	if (argc > 0)
    353 		bootptab = argv[0];
    354 
    355 	if (argc > 1)
    356 		bootpd_dump = argv[1];
    357 
    358 	/*
    359 	 * Get my hostname and IP address.
    360 	 */
    361 	if (hostname[0] == '\0') {
    362 		if (gethostname(hostname, sizeof(hostname)) == -1) {
    363 			fprintf(stderr, "bootpd: can't get hostname\n");
    364 			exit(1);
    365 		}
    366 		hostname[sizeof(hostname) - 1] = '\0';
    367 	}
    368 	hep = gethostbyname(hostname);
    369 	if (!hep) {
    370 		fprintf(stderr, "Can not get my IP address\n");
    371 		exit(1);
    372 	}
    373 	bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
    374 
    375 	if (standalone) {
    376 		/*
    377 		 * Go into background and disassociate from controlling terminal.
    378 		 */
    379 		if (debug < 3) {
    380 			if (fork())
    381 				exit(0);
    382 #ifdef	NO_SETSID
    383 			setpgrp(0,0);
    384 #ifdef TIOCNOTTY
    385 			n = open("/dev/tty", O_RDWR);
    386 			if (n >= 0) {
    387 				ioctl(n, TIOCNOTTY, (char *) 0);
    388 				(void) close(n);
    389 			}
    390 #endif	/* TIOCNOTTY */
    391 #else	/* SETSID */
    392 			if (setsid() < 0)
    393 				perror("setsid");
    394 #endif	/* SETSID */
    395 		} /* if debug < 3 */
    396 
    397 		/*
    398 		 * Nuke any timeout value
    399 		 */
    400 		timeout = INFTIM;
    401 
    402 	} /* if standalone (1st) */
    403 
    404 	/* Set the cwd (i.e. to /tftpboot) */
    405 	if (chdir_path) {
    406 		if (chdir(chdir_path) < 0)
    407 			report(LOG_ERR, "%s: chdir failed", chdir_path);
    408 	}
    409 
    410 	/* Get the timezone. */
    411 	tzone_init();
    412 
    413 	/* Allocate hash tables. */
    414 	rdtab_init();
    415 
    416 	/*
    417 	 * Read the bootptab file.
    418 	 */
    419 	readtab(1);					/* force read */
    420 
    421 	if (standalone) {
    422 
    423 		/*
    424 		 * Create a socket.
    425 		 */
    426 		if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    427 			report(LOG_ERR, "socket: %s", get_network_errmsg());
    428 			exit(1);
    429 		}
    430 
    431 		/*
    432 		 * Get server's listening port number
    433 		 */
    434 		servp = getservbyname("bootps", "udp");
    435 		if (servp) {
    436 			bootps_port = ntohs((u_short) servp->s_port);
    437 		} else {
    438 			bootps_port = (u_short) IPPORT_BOOTPS;
    439 			report(LOG_ERR,
    440 				   "udp/bootps: unknown service -- assuming port %d",
    441 				   bootps_port);
    442 		}
    443 
    444 		/*
    445 		 * Bind socket to BOOTPS port.
    446 		 */
    447 		bind_addr.sin_family = AF_INET;
    448 		bind_addr.sin_addr.s_addr = INADDR_ANY;
    449 		bind_addr.sin_port = htons(bootps_port);
    450 		if (bind(s, (struct sockaddr *) &bind_addr,
    451 				 sizeof(bind_addr)) < 0)
    452 		{
    453 			report(LOG_ERR, "bind: %s", get_network_errmsg());
    454 			exit(1);
    455 		}
    456 	} /* if standalone (2nd)*/
    457 
    458 	/*
    459 	 * Get destination port number so we can reply to client
    460 	 */
    461 	servp = getservbyname("bootpc", "udp");
    462 	if (servp) {
    463 		bootpc_port = ntohs(servp->s_port);
    464 	} else {
    465 		report(LOG_ERR,
    466 			   "udp/bootpc: unknown service -- assuming port %d",
    467 			   IPPORT_BOOTPC);
    468 		bootpc_port = (u_short) IPPORT_BOOTPC;
    469 	}
    470 
    471 	/*
    472 	 * Set up signals to read or dump the table.
    473 	 */
    474 	if ((long) signal(SIGHUP, catcher) < 0) {
    475 		report(LOG_ERR, "signal: %s", get_errmsg());
    476 		exit(1);
    477 	}
    478 	if ((long) signal(SIGUSR1, catcher) < 0) {
    479 		report(LOG_ERR, "signal: %s", get_errmsg());
    480 		exit(1);
    481 	}
    482 
    483 	/*
    484 	 * Process incoming requests.
    485 	 */
    486 	set[0].fd = s;
    487 	set[0].events = POLLIN;
    488 	for (;;) {
    489 		nfound = poll(set, 1, timeout);
    490 		if (nfound < 0) {
    491 			if (errno != EINTR) {
    492 				report(LOG_ERR, "poll: %s", get_errmsg());
    493 			}
    494 			/*
    495 			 * Call readtab() or dumptab() here to avoid the
    496 			 * dangers of doing I/O from a signal handler.
    497 			 */
    498 			if (do_readtab) {
    499 				do_readtab = 0;
    500 				readtab(1);		/* force read */
    501 			}
    502 			if (do_dumptab) {
    503 				do_dumptab = 0;
    504 				dumptab(bootpd_dump);
    505 			}
    506 			continue;
    507 		}
    508 		if (nfound == 0) {
    509 			if (debug > 1)
    510 				report(LOG_INFO, "exiting after %d minute%s of inactivity",
    511 					   actualtimeout / 60000,
    512 					   actualtimeout == 60000 ? "" : "s");
    513 			exit(0);
    514 		}
    515 		ra_len = sizeof(recv_addr);
    516 		n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
    517 					 (struct sockaddr *) &recv_addr, &ra_len);
    518 		if (n <= 0) {
    519 			continue;
    520 		}
    521 		if (debug > 1) {
    522 			report(LOG_INFO, "recvd pkt from IP addr %s",
    523 				   inet_ntoa(recv_addr.sin_addr));
    524 		}
    525 		if (n < (int)sizeof(struct bootp)) {
    526 			if (debug) {
    527 				report(LOG_INFO, "received short packet");
    528 			}
    529 			continue;
    530 		}
    531 		pktlen = n;
    532 
    533 		readtab(0);				/* maybe re-read bootptab */
    534 
    535 		switch (bp->bp_op) {
    536 		case BOOTREQUEST:
    537 			handle_request();
    538 			break;
    539 		case BOOTREPLY:
    540 			handle_reply();
    541 			break;
    542 		}
    543 	}
    544 }
    545 
    546 
    547 
    549 
    550 /*
    551  * Print "usage" message and exit
    552  */
    553 
    554 PRIVATE void
    555 usage(void)
    556 {
    557 	fprintf(stderr,
    558 			"usage:  bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n");
    559 	fprintf(stderr, "\t -c n\tset current directory\n");
    560 	fprintf(stderr, "\t -d n\tset debug level\n");
    561 	fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n");
    562 	fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n");
    563 	fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n");
    564 	exit(1);
    565 }
    566 
    567 /* Signal catchers */
    568 PRIVATE void
    569 catcher(int sig)
    570 {
    571 	if (sig == SIGHUP)
    572 		do_readtab = 1;
    573 	if (sig == SIGUSR1)
    574 		do_dumptab = 1;
    575 #ifdef	SYSV
    576 	/* For older "System V" derivatives with no sigset(). */
    577 	/* XXX - Should just do it the POSIX way (sigaction). */
    578 	signal(sig, catcher);
    579 #endif
    580 }
    581 
    582 
    583 
    585 /*
    586  * Process BOOTREQUEST packet.
    587  *
    588  * Note:  This version of the bootpd.c server never forwards
    589  * a request to another server.  That is the job of a gateway
    590  * program such as the "bootpgw" program included here.
    591  *
    592  * (Also this version does not interpret the hostname field of
    593  * the request packet;  it COULD do a name->address lookup and
    594  * forward the request there.)
    595  */
    596 PRIVATE void
    597 handle_request(void)
    598 {
    599 	struct bootp *bp = (struct bootp *) pktbuf;
    600 	struct host *hp = NULL;
    601 	struct host dummyhost;
    602 	int32 bootsize = 0;
    603 	unsigned hlen, hashcode;
    604 	int32 dest;
    605 	char lrealpath[1024];
    606 	char *clntpath;
    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 	} else {
    820 		lrealpath[0] = '\0';
    821 		clntpath = lrealpath;
    822 	}
    823 
    824 	/*
    825 	 * Determine client's requested homedir and bootfile.
    826 	 */
    827 	homedir = NULL;
    828 	bootfile = NULL;
    829 	if (bp->bp_file[0]) {
    830 		char	*t;
    831 
    832 		homedir = bp->bp_file;
    833 
    834 		/* make sure that the file is nul terminated */
    835 		for (t = homedir; t - homedir < BP_FILE_LEN; t++)
    836 			if (*t == '\0')
    837 				break;
    838 		if (t - homedir < BP_FILE_LEN) {
    839 			report(LOG_INFO, "requested path length > BP_FILE_LEN  file = \"%s\", nul terminating", homedir);
    840 			homedir[BP_FILE_LEN - 1] = '\0';
    841 		}
    842 
    843 		bootfile = strrchr(homedir, '/');
    844 		if (bootfile) {
    845 			if (homedir == bootfile)
    846 				homedir = NULL;
    847 			*bootfile++ = '\0';
    848 		} else {
    849 			/* no "/" in the string */
    850 			bootfile = homedir;
    851 			homedir = NULL;
    852 		}
    853 		if (debug > 2) {
    854 			report(LOG_INFO, "requested path=\"%s\"  file=\"%s\"",
    855 				   (homedir) ? homedir : "",
    856 				   (bootfile) ? bootfile : "");
    857 		}
    858 	}
    859 
    860 	/*
    861 	 * Specifications in bootptab override client requested values.
    862 	 */
    863 	if (hp->flags.homedir)
    864 		homedir = hp->homedir->string;
    865 	if (hp->flags.bootfile)
    866 		bootfile = hp->bootfile->string;
    867 
    868 	/*
    869 	 * Construct bootfile path.
    870 	 */
    871 	if (homedir) {
    872 		if (homedir[0] != '/')
    873 			strlcat(lrealpath, "/", sizeof(lrealpath));
    874 		strlcat(lrealpath, homedir, sizeof(lrealpath));
    875 		homedir = NULL;
    876 	}
    877 	if (bootfile) {
    878 		if (bootfile[0] != '/') {
    879 			strlcat(lrealpath, "/", sizeof(lrealpath));
    880 			lrealpath[sizeof(lrealpath) - 1] = '\0';
    881 		}
    882 		strlcat(lrealpath, bootfile, sizeof(lrealpath));
    883 		lrealpath[sizeof(lrealpath) - 1] = '\0';
    884 		bootfile = NULL;
    885 	}
    886 
    887 	/*
    888 	 * First try to find the file with a ".host" suffix
    889 	 */
    890 	n = strlen(clntpath);
    891 	strlcat(clntpath, ".", sizeof(clntpath));
    892 	strlcat(clntpath, hp->hostname->string, sizeof(clntpath));
    893 	if (chk_access(lrealpath, &bootsize) < 0) {
    894 		clntpath[n] = 0;			/* Try it without the suffix */
    895 		if (chk_access(lrealpath, &bootsize) < 0) {
    896 			/* neither "file.host" nor "file" was found */
    897 #ifdef	CHECK_FILE_ACCESS
    898 
    899 			if (bp->bp_file[0]) {
    900 				/*
    901 				 * Client wanted specific file
    902 				 * and we didn't have it.
    903 				 */
    904 				report(LOG_NOTICE,
    905 					   "requested file not found: \"%s\"", clntpath);
    906 				return;
    907 			}
    908 			/*
    909 			 * Client didn't ask for a specific file and we couldn't
    910 			 * access the default file, so just zero-out the bootfile
    911 			 * field in the packet and continue processing the reply.
    912 			 */
    913 			bzero(bp->bp_file, sizeof(bp->bp_file));
    914 			goto null_file_name;
    915 
    916 #else	/* CHECK_FILE_ACCESS */
    917 
    918 			/* Complain only if boot file size was needed. */
    919 			if (hp->flags.bootsize_auto) {
    920 				report(LOG_ERR, "can not determine size of file \"%s\"",
    921 					   clntpath);
    922 			}
    923 
    924 #endif	/* CHECK_FILE_ACCESS */
    925 		}
    926 	}
    927 	strlcpy(bp->bp_file, clntpath, sizeof(bp->bp_file));
    928 	if (debug > 2)
    929 		report(LOG_INFO, "bootfile=\"%s\"", clntpath);
    930 
    931 #ifdef	CHECK_FILE_ACCESS
    932 null_file_name:
    933 #endif	/* CHECK_FILE_ACCESS */
    934 
    935 
    936 	/*
    938 	 * Handle vendor options based on magic number.
    939 	 */
    940 
    941 	if (debug > 1) {
    942 		report(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 the
    950 	 * specific cookie into the bootp packet, thus forcing a certain
    951 	 * reply format.  Only force reply format if user specified it.
    952 	 */
    953 	if (hp->flags.vm_cookie) {
    954 		/* Slam in the user specified magic number. */
    955 		bcopy(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 	 */
    961 	if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) {
    962 		/* RFC1048 conformant bootp client */
    963 		dovend_rfc1048(bp, hp, bootsize);
    964 		if (debug > 1) {
    965 			report(LOG_INFO, "sending reply (with RFC1048 options)");
    966 		}
    967 	}
    968 #ifdef VEND_CMU
    969 	else if (!bcmp(bp->bp_vend, vm_cmu, 4)) {
    970 		dovend_cmu(bp, hp);
    971 		if (debug > 1) {
    972 			report(LOG_INFO, "sending reply (with CMU options)");
    973 		}
    974 	}
    975 #endif
    976 	else {
    977 		if (debug > 1) {
    978 			report(LOG_INFO, "sending reply (with no options)");
    979 		}
    980 	}
    981 
    982 	dest = (hp->flags.reply_addr) ?
    983 		hp->reply_addr.s_addr : 0L;
    984 
    985 	/* not forwarded */
    986 	sendreply(0, dest);
    987 }
    988 
    989 
    990 /*
    991  * Process BOOTREPLY packet.
    992  */
    993 PRIVATE void
    994 handle_reply(void)
    995 {
    996 	if (debug) {
    997 		report(LOG_INFO, "processing boot reply");
    998 	}
    999 	/* forwarded, no destination override */
   1000 	sendreply(1, 0);
   1001 }
   1002 
   1003 
   1004 /*
   1005  * Send a reply packet to the client.  'forward' flag is set if we are
   1006  * not the originator of this reply packet.
   1007  */
   1008 PRIVATE void
   1009 sendreply(int forward, int32 dst_override)
   1010 {
   1011 	struct bootp *bp = (struct bootp *) pktbuf;
   1012 	struct in_addr dst;
   1013 	u_short port = bootpc_port;
   1014 	unsigned char *ha;
   1015 	int len;
   1016 
   1017 	/*
   1018 	 * XXX - Should honor bp_flags "broadcast" bit here.
   1019 	 * Temporary workaround: use the :ra=ADDR: option to
   1020 	 * set the reply address to the broadcast address.
   1021 	 */
   1022 
   1023 	/*
   1024 	 * If the destination address was specified explicitly
   1025 	 * (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 that
   1029 	 * else if gateway IP address is specified, use that
   1030 	 * else make a temporary arp cache entry for the client's
   1031 	 * NEW IP/hardware address and use that.
   1032 	 */
   1033 	if (dst_override) {
   1034 		dst.s_addr = dst_override;
   1035 		if (debug > 1) {
   1036 			report(LOG_INFO, "reply address override: %s",
   1037 				   inet_ntoa(dst));
   1038 		}
   1039 	} else if (bp->bp_ciaddr.s_addr) {
   1040 		dst = bp->bp_ciaddr;
   1041 	} else if (bp->bp_giaddr.s_addr && forward == 0) {
   1042 		dst = bp->bp_giaddr;
   1043 		port = bootps_port;
   1044 		if (debug > 1) {
   1045 			report(LOG_INFO, "sending reply to gateway %s",
   1046 				   inet_ntoa(dst));
   1047 		}
   1048 	} else {
   1049 		dst = bp->bp_yiaddr;
   1050 		ha = bp->bp_chaddr;
   1051 		len = bp->bp_hlen;
   1052 		if (len > MAXHADDRLEN)
   1053 			len = MAXHADDRLEN;
   1054 
   1055 		if (debug > 1)
   1056 			report(LOG_INFO, "setarp %s - %s",
   1057 				   inet_ntoa(dst), haddrtoa(ha, len));
   1058 		setarp(s, &dst, ha, len);
   1059 	}
   1060 
   1061 	if ((forward == 0) &&
   1062 		(bp->bp_siaddr.s_addr == 0))
   1063 	{
   1064 		struct ifreq *ifr;
   1065 		struct in_addr siaddr;
   1066 		/*
   1067 		 * If we are originating this reply, we
   1068 		 * need to find our own interface address to
   1069 		 * put in the bp_siaddr field of the reply.
   1070 		 * If this server is multi-homed, pick the
   1071 		 * 'best' interface (the one on the same net
   1072 		 * as the client).  Of course, the client may
   1073 		 * be on the other side of a BOOTP gateway...
   1074 		 */
   1075 		ifr = getif(s, &dst);
   1076 		if (ifr) {
   1077 			struct sockaddr_in *sip;
   1078 			sip = (struct sockaddr_in *) &(ifr->ifr_addr);
   1079 			siaddr = sip->sin_addr;
   1080 		} else {
   1081 			/* Just use my "official" IP address. */
   1082 			siaddr = my_ip_addr;
   1083 		}
   1084 
   1085 		/* XXX - No need to set bp_giaddr here. */
   1086 
   1087 		/* Finally, set the server address field. */
   1088 		bp->bp_siaddr = siaddr;
   1089 	}
   1090 	/* Set up socket address for send. */
   1091 	send_addr.sin_family = AF_INET;
   1092 	send_addr.sin_port = htons(port);
   1093 	send_addr.sin_addr = dst;
   1094 
   1095 	/* Send reply with same size packet as request used. */
   1096 	if (sendto(s, pktbuf, pktlen, 0,
   1097 			   (struct sockaddr *) &send_addr,
   1098 			   sizeof(send_addr)) < 0)
   1099 	{
   1100 		report(LOG_ERR, "sendto: %s", get_network_errmsg());
   1101 	}
   1102 } /* sendreply */
   1103 
   1104 
   1106 /* nmatch() - now in getif.c */
   1107 /* setarp() - now in hwaddr.c */
   1108 
   1109 
   1110 /*
   1111  * This call checks read access to a file.  It returns 0 if the file given
   1112  * by "path" exists and is publically readable.  A value of -1 is returned if
   1113  * access is not permitted or an error occurs.  Successful calls also
   1114  * return the file size in bytes using the long pointer "filesize".
   1115  *
   1116  * The read permission bit for "other" users is checked.  This bit must be
   1117  * set for tftpd(8) to allow clients to read the file.
   1118  */
   1119 
   1120 PRIVATE int
   1121 chk_access(char *path, int32 *filesize)
   1122 {
   1123 	struct stat st;
   1124 
   1125 	if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) {
   1126 		*filesize = (int32) st.st_size;
   1127 		return 0;
   1128 	} else {
   1129 		return -1;
   1130 	}
   1131 }
   1132 
   1133 
   1135 /*
   1136  * Now in dumptab.c :
   1137  *	dumptab()
   1138  *	dump_host()
   1139  *	list_ipaddresses()
   1140  */
   1141 
   1142 #ifdef VEND_CMU
   1143 
   1144 /*
   1145  * Insert the CMU "vendor" data for the host pointed to by "hp" into the
   1146  * bootp packet pointed to by "bp".
   1147  */
   1148 
   1149 PRIVATE void
   1150 dovend_cmu(struct bootp *bp, struct host *hp)
   1151 {
   1152 	struct cmu_vend *vendp;
   1153 	struct in_addr_list *taddr;
   1154 
   1155 	/*
   1156 	 * Initialize the entire vendor field to zeroes.
   1157 	 */
   1158 	bzero(bp->bp_vend, sizeof(bp->bp_vend));
   1159 
   1160 	/*
   1161 	 * Fill in vendor information. Subnet mask, default gateway,
   1162 	 * domain name server, ien name server, time server
   1163 	 */
   1164 	vendp = (struct cmu_vend *) bp->bp_vend;
   1165 	strlcpy(vendp->v_magic, (char *)vm_cmu, sizeof(vendp->v_magic));
   1166 	if (hp->flags.subnet_mask) {
   1167 		(vendp->v_smask).s_addr = hp->subnet_mask.s_addr;
   1168 		(vendp->v_flags) |= VF_SMASK;
   1169 		if (hp->flags.gateway) {
   1170 			(vendp->v_dgate).s_addr = hp->gateway->addr->s_addr;
   1171 		}
   1172 	}
   1173 	if (hp->flags.domain_server) {
   1174 		taddr = hp->domain_server;
   1175 		if (taddr->addrcount > 0) {
   1176 			(vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr;
   1177 			if (taddr->addrcount > 1) {
   1178 				(vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr;
   1179 			}
   1180 		}
   1181 	}
   1182 	if (hp->flags.name_server) {
   1183 		taddr = hp->name_server;
   1184 		if (taddr->addrcount > 0) {
   1185 			(vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr;
   1186 			if (taddr->addrcount > 1) {
   1187 				(vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr;
   1188 			}
   1189 		}
   1190 	}
   1191 	if (hp->flags.time_server) {
   1192 		taddr = hp->time_server;
   1193 		if (taddr->addrcount > 0) {
   1194 			(vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr;
   1195 			if (taddr->addrcount > 1) {
   1196 				(vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr;
   1197 			}
   1198 		}
   1199 	}
   1200 	/* Log message now done by caller. */
   1201 } /* dovend_cmu */
   1202 
   1203 #endif /* VEND_CMU */
   1204 
   1205 
   1207 
   1208 /*
   1209  * Insert the RFC1048 vendor data for the host pointed to by "hp" into the
   1210  * bootp packet pointed to by "bp".
   1211  */
   1212 #define	NEED(LEN, MSG) do \
   1213 	if (bytesleft < (LEN)) { \
   1214 		report(LOG_NOTICE, noroom, \
   1215 			   hp->hostname->string, MSG); \
   1216 		return; \
   1217 	} while (0)
   1218 PRIVATE void
   1219 dovend_rfc1048(struct bootp *bp, struct host *hp, int32 bootsize)
   1220 {
   1221 	int bytesleft, len;
   1222 	byte *vp;
   1223 
   1224 	static const char noroom[] = "%s: No room for \"%s\" option";
   1225 
   1226 	vp = bp->bp_vend;
   1227 
   1228 	if (hp->flags.msg_size) {
   1229 		pktlen = hp->msg_size;
   1230 	} else {
   1231 		/*
   1232 		 * If the request was longer than the official length, build
   1233 		 * a response of that same length where the additional length
   1234 		 * is assumed to be part of the bp_vend (options) area.
   1235 		 */
   1236 		if (pktlen > (int)sizeof(*bp)) {
   1237 			if (debug > 1)
   1238 				report(LOG_INFO, "request message length=%d", pktlen);
   1239 		}
   1240 		/*
   1241 		 * Check whether the request contains the option:
   1242 		 * Maximum DHCP Message Size (RFC1533 sec. 9.8)
   1243 		 * and if so, override the response length with its value.
   1244 		 * This request must lie within the first BP_VEND_LEN
   1245 		 * bytes of the option space.
   1246 		 */
   1247 		{
   1248 			byte *p, *ep;
   1249 			byte tag, llen;
   1250 			short msgsz = 0;
   1251 
   1252 			p = vp + 4;
   1253 			ep = p + BP_VEND_LEN - 4;
   1254 			while (p < ep) {
   1255 				tag = *p++;
   1256 				/* Check for tags with no data first. */
   1257 				if (tag == TAG_PAD)
   1258 					continue;
   1259 				if (tag == TAG_END)
   1260 					break;
   1261 				/* Now scan the length byte. */
   1262 				llen = *p++;
   1263 				switch (tag) {
   1264 				case TAG_MAX_MSGSZ:
   1265 					if (llen == 2) {
   1266 						bcopy(p, (char*)&msgsz, 2);
   1267 						msgsz = ntohs(msgsz);
   1268 					}
   1269 					break;
   1270 				case TAG_SUBNET_MASK:
   1271 					/* XXX - Should preserve this if given... */
   1272 					break;
   1273 				} /* swtich */
   1274 				p += llen;
   1275 			}
   1276 
   1277 			if (msgsz > (int)sizeof(*bp)) {
   1278 				if (debug > 1)
   1279 					report(LOG_INFO, "request has DHCP msglen=%d", msgsz);
   1280 				pktlen = msgsz;
   1281 			}
   1282 		}
   1283 	}
   1284 
   1285 	if (pktlen < (int)sizeof(*bp)) {
   1286 		report(LOG_ERR, "invalid response length=%d", pktlen);
   1287 		pktlen = sizeof(*bp);
   1288 	}
   1289 	bytesleft = ((byte*)bp + pktlen) - vp;
   1290 	if (pktlen > (int)sizeof(*bp)) {
   1291 		if (debug > 1)
   1292 			report(LOG_INFO, "extended reply, length=%d, options=%d",
   1293 				   pktlen, bytesleft);
   1294 	}
   1295 
   1296 	/* Copy in the magic cookie */
   1297 	bcopy(vm_rfc1048, vp, 4);
   1298 	vp += 4;
   1299 	bytesleft -= 4;
   1300 
   1301 	if (hp->flags.subnet_mask) {
   1302 		/* always enough room here. */
   1303 		*vp++ = TAG_SUBNET_MASK;/* -1 byte  */
   1304 		*vp++ = 4;				/* -1 byte  */
   1305 		insert_u_long(hp->subnet_mask.s_addr, &vp);	/* -4 bytes */
   1306 		bytesleft -= 6;			/* Fix real count */
   1307 		if (hp->flags.gateway) {
   1308 			(void) insert_ip(TAG_GATEWAY,
   1309 							 hp->gateway,
   1310 							 &vp, &bytesleft);
   1311 		}
   1312 	}
   1313 	if (hp->flags.bootsize) {
   1314 		/* always enough room here */
   1315 		bootsize = (hp->flags.bootsize_auto) ?
   1316 			((bootsize + 511) / 512) : ((int32_t)hp->bootsize);	/* Round up */
   1317 		*vp++ = TAG_BOOT_SIZE;
   1318 		*vp++ = 2;
   1319 		*vp++ = (byte) ((bootsize >> 8) & 0xFF);
   1320 		*vp++ = (byte) (bootsize & 0xFF);
   1321 		bytesleft -= 4;			/* Tag, length, and 16 bit blocksize */
   1322 	}
   1323 	/*
   1324 	 * This one is special: Remaining options go in the ext file.
   1325 	 * Only the subnet_mask, bootsize, and gateway should precede.
   1326 	 */
   1327 	if (hp->flags.exten_file) {
   1328 		/*
   1329 		 * Check for room for exten_file.  Add 3 to account for
   1330 		 * TAG_EXTEN_FILE, length, and TAG_END.
   1331 		 */
   1332 		len = strlen(hp->exten_file->string);
   1333 		NEED((len + 3), "ef");
   1334 		*vp++ = TAG_EXTEN_FILE;
   1335 		*vp++ = (byte) (len & 0xFF);
   1336 		bcopy(hp->exten_file->string, vp, len);
   1337 		vp += len;
   1338 		*vp++ = TAG_END;
   1339 		bytesleft -= len + 3;
   1340 		return;					/* no more options here. */
   1341 	}
   1342 	/*
   1343 	 * The remaining options are inserted by the following
   1344 	 * function (which is shared with bootpef.c).
   1345 	 * Keep back one byte for the TAG_END.
   1346 	 */
   1347 	len = dovend_rfc1497(hp, vp, bytesleft - 1);
   1348 	vp += len;
   1349 	bytesleft -= len;
   1350 
   1351 	/* There should be at least one byte left. */
   1352 	NEED(1, "(end)");
   1353 	*vp++ = TAG_END;
   1354 	bytesleft--;
   1355 
   1356 	/* Log message done by caller. */
   1357 	if (bytesleft > 0) {
   1358 		/*
   1359 		 * Zero out any remaining part of the vendor area.
   1360 		 */
   1361 		bzero(vp, bytesleft);
   1362 	}
   1363 } /* dovend_rfc1048 */
   1364 #undef	NEED
   1365 
   1366 
   1368 /*
   1369  * Now in readfile.c:
   1370  * 	hwlookcmp()
   1371  *	iplookcmp()
   1372  */
   1373 
   1374 /* haddrtoa() - now in hwaddr.c */
   1375 /*
   1376  * Now in dovend.c:
   1377  * insert_ip()
   1378  * insert_generic()
   1379  * insert_u_long()
   1380  */
   1381 
   1382 /* get_errmsg() - now in report.c */
   1383 
   1384 /*
   1385  * Local Variables:
   1386  * tab-width: 4
   1387  * c-indent-level: 4
   1388  * c-argdecl-indent: 4
   1389  * c-continued-statement-offset: 4
   1390  * c-continued-brace-offset: -4
   1391  * c-label-offset: -4
   1392  * c-brace-offset: 0
   1393  * End:
   1394  */
   1395