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