Home | History | Annotate | Line # | Download | only in bootpd
bootpd.c revision 1.19
      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.19 2003/07/14 06:08:04 itojun 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 			strlcpy(hostname, stmp, sizeof(hostname));
    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 minute%s of inactivity",
    517 					   actualtimeout / 60000,
    518 					   actualtimeout == 60000 ? "" : "s");
    519 			exit(0);
    520 		}
    521 		ra_len = sizeof(recv_addr);
    522 		n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
    523 					 (struct sockaddr *) &recv_addr, &ra_len);
    524 		if (n <= 0) {
    525 			continue;
    526 		}
    527 		if (debug > 1) {
    528 			report(LOG_INFO, "recvd pkt from IP addr %s",
    529 				   inet_ntoa(recv_addr.sin_addr));
    530 		}
    531 		if (n < sizeof(struct bootp)) {
    532 			if (debug) {
    533 				report(LOG_INFO, "received short packet");
    534 			}
    535 			continue;
    536 		}
    537 		pktlen = n;
    538 
    539 		readtab(0);				/* maybe re-read bootptab */
    540 
    541 		switch (bp->bp_op) {
    542 		case BOOTREQUEST:
    543 			handle_request();
    544 			break;
    545 		case BOOTREPLY:
    546 			handle_reply();
    547 			break;
    548 		}
    549 	}
    550 }
    551 
    552 
    553 
    555 
    556 /*
    557  * Print "usage" message and exit
    558  */
    559 
    560 PRIVATE void
    561 usage(void)
    562 {
    563 	fprintf(stderr,
    564 			"usage:  bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n");
    565 	fprintf(stderr, "\t -c n\tset current directory\n");
    566 	fprintf(stderr, "\t -d n\tset debug level\n");
    567 	fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n");
    568 	fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n");
    569 	fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n");
    570 	exit(1);
    571 }
    572 
    573 /* Signal catchers */
    574 PRIVATE void
    575 catcher(int sig)
    576 {
    577 	if (sig == SIGHUP)
    578 		do_readtab = 1;
    579 	if (sig == SIGUSR1)
    580 		do_dumptab = 1;
    581 #ifdef	SYSV
    582 	/* For older "System V" derivatives with no sigset(). */
    583 	/* XXX - Should just do it the POSIX way (sigaction). */
    584 	signal(sig, catcher);
    585 #endif
    586 }
    587 
    588 
    589 
    591 /*
    592  * Process BOOTREQUEST packet.
    593  *
    594  * Note:  This version of the bootpd.c server never forwards
    595  * a request to another server.  That is the job of a gateway
    596  * program such as the "bootpgw" program included here.
    597  *
    598  * (Also this version does not interpret the hostname field of
    599  * the request packet;  it COULD do a name->address lookup and
    600  * forward the request there.)
    601  */
    602 PRIVATE void
    603 handle_request(void)
    604 {
    605 	struct bootp *bp = (struct bootp *) pktbuf;
    606 	struct host *hp = NULL;
    607 	struct host dummyhost;
    608 	int32 bootsize = 0;
    609 	unsigned hlen, hashcode;
    610 	int32 dest;
    611 	char realpath[1024];
    612 	char *clntpath;
    613 	char *homedir, *bootfile;
    614 	int n;
    615 
    616 	/* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
    617 
    618 	/*
    619 	 * If the servername field is set, compare it against us.
    620 	 * If we're not being addressed, ignore this request.
    621 	 * If the server name field is null, throw in our name.
    622 	 */
    623 	if (strlen(bp->bp_sname)) {
    624 		if (strcmp(bp->bp_sname, hostname)) {
    625 			if (debug)
    626 				report(LOG_INFO, "\
    627 ignoring request for server %s from client at %s address %s",
    628 					   bp->bp_sname, netname(bp->bp_htype),
    629 					   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
    630 			/* XXX - Is it correct to ignore such a request? -gwr */
    631 			return;
    632 		}
    633 	} else {
    634 		strlcpy(bp->bp_sname, hostname, sizeof(bp->bp_sname));
    635 	}
    636 
    637 	/* If it uses an unknown network type, ignore the request.  */
    638 	if (bp->bp_htype >= hwinfocnt) {
    639 		if (debug)
    640 			report(LOG_INFO,
    641 			    "Request with unknown network type %u",
    642 			    bp->bp_htype);
    643 		return;
    644 	}
    645 
    646 	/* Convert the request into a reply. */
    647 	bp->bp_op = BOOTREPLY;
    648 	if (bp->bp_ciaddr.s_addr == 0) {
    649 		/*
    650 		 * client doesnt know his IP address,
    651 		 * search by hardware address.
    652 		 */
    653 		if (debug > 1) {
    654 			report(LOG_INFO, "request from %s address %s",
    655 				   netname(bp->bp_htype),
    656 				   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
    657 		}
    658 		hlen = haddrlength(bp->bp_htype);
    659 		if (hlen != bp->bp_hlen) {
    660 			report(LOG_NOTICE, "bad addr len from %s address %s",
    661 				   netname(bp->bp_htype),
    662 				   haddrtoa(bp->bp_chaddr, hlen));
    663 		}
    664 		dummyhost.htype = bp->bp_htype;
    665 		bcopy(bp->bp_chaddr, dummyhost.haddr, hlen);
    666 		hashcode = hash_HashFunction(bp->bp_chaddr, hlen);
    667 		hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp,
    668 										 &dummyhost);
    669 		if (hp == NULL &&
    670 			bp->bp_htype == HTYPE_IEEE802)
    671 		{
    672 			/* Try again with address in "canonical" form. */
    673 			haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen);
    674 			if (debug > 1) {
    675 				report(LOG_INFO, "\
    676 HW addr type is IEEE 802.  convert to %s and check again\n",
    677 					   haddrtoa(dummyhost.haddr, bp->bp_hlen));
    678 			}
    679 			hashcode = hash_HashFunction(dummyhost.haddr, hlen);
    680 			hp = (struct host *) hash_Lookup(hwhashtable, hashcode,
    681 											 hwlookcmp, &dummyhost);
    682 		}
    683 		if (hp == NULL) {
    684 			/*
    685 			 * XXX - Add dynamic IP address assignment?
    686 			 */
    687 			if (debug > 1)
    688 				report(LOG_INFO, "unknown client %s address %s",
    689 					   netname(bp->bp_htype),
    690 					   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
    691 			return; /* not found */
    692 		}
    693 		(bp->bp_yiaddr).s_addr = hp->iaddr.s_addr;
    694 
    695 	} else {
    696 
    697 		/*
    698 		 * search by IP address.
    699 		 */
    700 		if (debug > 1) {
    701 			report(LOG_INFO, "request from IP addr %s",
    702 				   inet_ntoa(bp->bp_ciaddr));
    703 		}
    704 		dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr;
    705 		hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4);
    706 		hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp,
    707 										 &dummyhost);
    708 		if (hp == NULL) {
    709 			if (debug > 1) {
    710 				report(LOG_NOTICE, "IP address not found: %s",
    711 					   inet_ntoa(bp->bp_ciaddr));
    712 			}
    713 			return;
    714 		}
    715 	}
    716 
    717 	if (debug) {
    718 		report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr),
    719 			   hp->hostname->string);
    720 	}
    721 
    722 	/*
    723 	 * If there is a response delay threshold, ignore requests
    724 	 * with a timestamp lower than the threshold.
    725 	 */
    726 	if (hp->flags.min_wait) {
    727 		u_int32 t = (u_int32) ntohs(bp->bp_secs);
    728 		if (t < hp->min_wait) {
    729 			if (debug > 1)
    730 				report(LOG_INFO,
    731 					   "ignoring request due to timestamp (%d < %d)",
    732 					   t, hp->min_wait);
    733 			return;
    734 		}
    735 	}
    736 
    737 #ifdef	YORK_EX_OPTION
    738 	/*
    739 	 * The need for the "ex" tag arose out of the need to empty
    740 	 * shared networked drives on diskless PCs.  This solution is
    741 	 * not very clean but it does work fairly well.
    742 	 * Written by Edmund J. Sutcliffe <edmund (at) york.ac.uk>
    743 	 *
    744 	 * XXX - This could compromise security if a non-trusted user
    745 	 * managed to write an entry in the bootptab with :ex=trojan:
    746 	 * so I would leave this turned off unless you need it. -gwr
    747 	 */
    748 	/* Run a program, passing the client name as a parameter. */
    749 	if (hp->flags.exec_file) {
    750 		char tst[100];
    751 		/* XXX - Check string lengths? -gwr */
    752 		strlcpy(tst, hp->exec_file->string, sizeof(tst));
    753 		strlcat(tst, " ", sizeof(tst));
    754 		strlcat(tst, hp->hostname->string, sizeof(tst));
    755 		strlcat(tst, " &", sizeof(tst));
    756 		if (debug)
    757 			report(LOG_INFO, "executing %s", tst);
    758 		system(tst);	/* Hope this finishes soon... */
    759 	}
    760 #endif	/* YORK_EX_OPTION */
    761 
    762 	/*
    763 	 * If a specific TFTP server address was specified in the bootptab file,
    764 	 * fill it in, otherwise zero it.
    765 	 * XXX - Rather than zero it, should it be the bootpd address? -gwr
    766 	 */
    767 	(bp->bp_siaddr).s_addr = (hp->flags.bootserver) ?
    768 		hp->bootserver.s_addr : 0L;
    769 
    770 #ifdef	STANFORD_PROM_COMPAT
    771 	/*
    772 	 * Stanford bootp PROMs (for a Sun?) have no way to leave
    773 	 * the boot file name field blank (because the boot file
    774 	 * name is automatically generated from some index).
    775 	 * As a work-around, this little hack allows those PROMs to
    776 	 * specify "sunboot14" with the same effect as a NULL name.
    777 	 * (The user specifies boot device 14 or some such magic.)
    778 	 */
    779 	if (strcmp(bp->bp_file, "sunboot14") == 0)
    780 		bp->bp_file[0] = '\0';	/* treat it as unspecified */
    781 #endif
    782 
    783 	/*
    784 	 * Fill in the client's proper bootfile.
    785 	 *
    786 	 * If the client specifies an absolute path, try that file with a
    787 	 * ".host" suffix and then without.  If the file cannot be found, no
    788 	 * reply is made at all.
    789 	 *
    790 	 * If the client specifies a null or relative file, use the following
    791 	 * table to determine the appropriate action:
    792 	 *
    793 	 *  Homedir      Bootfile    Client's file
    794 	 * specified?   specified?   specification   Action
    795 	 * -------------------------------------------------------------------
    796 	 *      No          No          Null         Send null filename
    797 	 *      No          No          Relative     Discard request
    798 	 *      No          Yes         Null         Send if absolute else null
    799 	 *      No          Yes         Relative     Discard request     *XXX
    800 	 *      Yes         No          Null         Send null filename
    801 	 *      Yes         No          Relative     Lookup with ".host"
    802 	 *      Yes         Yes         Null         Send home/boot or bootfile
    803 	 *      Yes         Yes         Relative     Lookup with ".host" *XXX
    804 	 *
    805 	 */
    806 
    807 	/*
    808 	 * XXX - I don't like the policy of ignoring a client when the
    809 	 * boot file is not accessible.  The TFTP server might not be
    810 	 * running on the same machine as the BOOTP server, in which
    811 	 * case checking accessibility of the boot file is pointless.
    812 	 *
    813 	 * Therefore, file accessibility is now demanded ONLY if you
    814 	 * define CHECK_FILE_ACCESS in the Makefile options. -gwr
    815 	 */
    816 
    817 	/*
    818 	 * The "real" path is as seen by the BOOTP daemon on this
    819 	 * machine, while the client path is relative to the TFTP
    820 	 * daemon chroot directory (i.e. /tftpboot).
    821 	 */
    822 	if (hp->flags.tftpdir) {
    823 		strlcpy(realpath, hp->tftpdir->string, sizeof(realpath));
    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 			strlcat(realpath, "/", sizeof(realpath));
    880 		strlcat(realpath, homedir, sizeof(realpath));
    881 		homedir = NULL;
    882 	}
    883 	if (bootfile) {
    884 		if (bootfile[0] != '/') {
    885 			strlcat(realpath, "/", sizeof(realpath));
    886 			realpath[sizeof(realpath) - 1] = '\0';
    887 		}
    888 		strlcat(realpath, bootfile, sizeof(realpath));
    889 		realpath[sizeof(realpath) - 1] = '\0';
    890 		bootfile = NULL;
    891 	}
    892 
    893 	/*
    894 	 * First try to find the file with a ".host" suffix
    895 	 */
    896 	n = strlen(clntpath);
    897 	strlcat(clntpath, ".", sizeof(clntpath));
    898 	strlcat(clntpath, hp->hostname->string, sizeof(clntpath));
    899 	if (chk_access(realpath, &bootsize) < 0) {
    900 		clntpath[n] = 0;			/* Try it without the suffix */
    901 		if (chk_access(realpath, &bootsize) < 0) {
    902 			/* neither "file.host" nor "file" was found */
    903 #ifdef	CHECK_FILE_ACCESS
    904 
    905 			if (bp->bp_file[0]) {
    906 				/*
    907 				 * Client wanted specific file
    908 				 * and we didn't have it.
    909 				 */
    910 				report(LOG_NOTICE,
    911 					   "requested file not found: \"%s\"", clntpath);
    912 				return;
    913 			}
    914 			/*
    915 			 * Client didn't ask for a specific file and we couldn't
    916 			 * access the default file, so just zero-out the bootfile
    917 			 * field in the packet and continue processing the reply.
    918 			 */
    919 			bzero(bp->bp_file, sizeof(bp->bp_file));
    920 			goto null_file_name;
    921 
    922 #else	/* CHECK_FILE_ACCESS */
    923 
    924 			/* Complain only if boot file size was needed. */
    925 			if (hp->flags.bootsize_auto) {
    926 				report(LOG_ERR, "can not determine size of file \"%s\"",
    927 					   clntpath);
    928 			}
    929 
    930 #endif	/* CHECK_FILE_ACCESS */
    931 		}
    932 	}
    933 	strlcpy(bp->bp_file, clntpath, sizeof(bp->bp_file));
    934 	if (debug > 2)
    935 		report(LOG_INFO, "bootfile=\"%s\"", clntpath);
    936 
    937 #ifdef	CHECK_FILE_ACCESS
    938 null_file_name:
    939 #endif	/* CHECK_FILE_ACCESS */
    940 
    941 
    942 	/*
    944 	 * Handle vendor options based on magic number.
    945 	 */
    946 
    947 	if (debug > 1) {
    948 		report(LOG_INFO, "vendor magic field is %d.%d.%d.%d",
    949 			   (int) ((bp->bp_vend)[0]),
    950 			   (int) ((bp->bp_vend)[1]),
    951 			   (int) ((bp->bp_vend)[2]),
    952 			   (int) ((bp->bp_vend)[3]));
    953 	}
    954 	/*
    955 	 * If this host isn't set for automatic vendor info then copy the
    956 	 * specific cookie into the bootp packet, thus forcing a certain
    957 	 * reply format.  Only force reply format if user specified it.
    958 	 */
    959 	if (hp->flags.vm_cookie) {
    960 		/* Slam in the user specified magic number. */
    961 		bcopy(hp->vm_cookie, bp->bp_vend, 4);
    962 	}
    963 	/*
    964 	 * Figure out the format for the vendor-specific info.
    965 	 * Note that bp->bp_vend may have been set above.
    966 	 */
    967 	if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) {
    968 		/* RFC1048 conformant bootp client */
    969 		dovend_rfc1048(bp, hp, bootsize);
    970 		if (debug > 1) {
    971 			report(LOG_INFO, "sending reply (with RFC1048 options)");
    972 		}
    973 	}
    974 #ifdef VEND_CMU
    975 	else if (!bcmp(bp->bp_vend, vm_cmu, 4)) {
    976 		dovend_cmu(bp, hp);
    977 		if (debug > 1) {
    978 			report(LOG_INFO, "sending reply (with CMU options)");
    979 		}
    980 	}
    981 #endif
    982 	else {
    983 		if (debug > 1) {
    984 			report(LOG_INFO, "sending reply (with no options)");
    985 		}
    986 	}
    987 
    988 	dest = (hp->flags.reply_addr) ?
    989 		hp->reply_addr.s_addr : 0L;
    990 
    991 	/* not forwarded */
    992 	sendreply(0, dest);
    993 }
    994 
    995 
    996 /*
    997  * Process BOOTREPLY packet.
    998  */
    999 PRIVATE void
   1000 handle_reply(void)
   1001 {
   1002 	if (debug) {
   1003 		report(LOG_INFO, "processing boot reply");
   1004 	}
   1005 	/* forwarded, no destination override */
   1006 	sendreply(1, 0);
   1007 }
   1008 
   1009 
   1010 /*
   1011  * Send a reply packet to the client.  'forward' flag is set if we are
   1012  * not the originator of this reply packet.
   1013  */
   1014 PRIVATE void
   1015 sendreply(int forward, int32 dst_override)
   1016 {
   1017 	struct bootp *bp = (struct bootp *) pktbuf;
   1018 	struct in_addr dst;
   1019 	u_short port = bootpc_port;
   1020 	unsigned char *ha;
   1021 	int len;
   1022 
   1023 	/*
   1024 	 * XXX - Should honor bp_flags "broadcast" bit here.
   1025 	 * Temporary workaround: use the :ra=ADDR: option to
   1026 	 * set the reply address to the broadcast address.
   1027 	 */
   1028 
   1029 	/*
   1030 	 * If the destination address was specified explicitly
   1031 	 * (i.e. the broadcast address for HP compatibility)
   1032 	 * then send the response to that address.  Otherwise,
   1033 	 * act in accordance with RFC951:
   1034 	 *   If the client IP address is specified, use that
   1035 	 * else if gateway IP address is specified, use that
   1036 	 * else make a temporary arp cache entry for the client's
   1037 	 * NEW IP/hardware address and use that.
   1038 	 */
   1039 	if (dst_override) {
   1040 		dst.s_addr = dst_override;
   1041 		if (debug > 1) {
   1042 			report(LOG_INFO, "reply address override: %s",
   1043 				   inet_ntoa(dst));
   1044 		}
   1045 	} else if (bp->bp_ciaddr.s_addr) {
   1046 		dst = bp->bp_ciaddr;
   1047 	} else if (bp->bp_giaddr.s_addr && forward == 0) {
   1048 		dst = bp->bp_giaddr;
   1049 		port = bootps_port;
   1050 		if (debug > 1) {
   1051 			report(LOG_INFO, "sending reply to gateway %s",
   1052 				   inet_ntoa(dst));
   1053 		}
   1054 	} else {
   1055 		dst = bp->bp_yiaddr;
   1056 		ha = bp->bp_chaddr;
   1057 		len = bp->bp_hlen;
   1058 		if (len > MAXHADDRLEN)
   1059 			len = MAXHADDRLEN;
   1060 
   1061 		if (debug > 1)
   1062 			report(LOG_INFO, "setarp %s - %s",
   1063 				   inet_ntoa(dst), haddrtoa(ha, len));
   1064 		setarp(s, &dst, ha, len);
   1065 	}
   1066 
   1067 	if ((forward == 0) &&
   1068 		(bp->bp_siaddr.s_addr == 0))
   1069 	{
   1070 		struct ifreq *ifr;
   1071 		struct in_addr siaddr;
   1072 		/*
   1073 		 * If we are originating this reply, we
   1074 		 * need to find our own interface address to
   1075 		 * put in the bp_siaddr field of the reply.
   1076 		 * If this server is multi-homed, pick the
   1077 		 * 'best' interface (the one on the same net
   1078 		 * as the client).  Of course, the client may
   1079 		 * be on the other side of a BOOTP gateway...
   1080 		 */
   1081 		ifr = getif(s, &dst);
   1082 		if (ifr) {
   1083 			struct sockaddr_in *sip;
   1084 			sip = (struct sockaddr_in *) &(ifr->ifr_addr);
   1085 			siaddr = sip->sin_addr;
   1086 		} else {
   1087 			/* Just use my "official" IP address. */
   1088 			siaddr = my_ip_addr;
   1089 		}
   1090 
   1091 		/* XXX - No need to set bp_giaddr here. */
   1092 
   1093 		/* Finally, set the server address field. */
   1094 		bp->bp_siaddr = siaddr;
   1095 	}
   1096 	/* Set up socket address for send. */
   1097 	send_addr.sin_family = AF_INET;
   1098 	send_addr.sin_port = htons(port);
   1099 	send_addr.sin_addr = dst;
   1100 
   1101 	/* Send reply with same size packet as request used. */
   1102 	if (sendto(s, pktbuf, pktlen, 0,
   1103 			   (struct sockaddr *) &send_addr,
   1104 			   sizeof(send_addr)) < 0)
   1105 	{
   1106 		report(LOG_ERR, "sendto: %s", get_network_errmsg());
   1107 	}
   1108 } /* sendreply */
   1109 
   1110 
   1112 /* nmatch() - now in getif.c */
   1113 /* setarp() - now in hwaddr.c */
   1114 
   1115 
   1116 /*
   1117  * This call checks read access to a file.  It returns 0 if the file given
   1118  * by "path" exists and is publically readable.  A value of -1 is returned if
   1119  * access is not permitted or an error occurs.  Successful calls also
   1120  * return the file size in bytes using the long pointer "filesize".
   1121  *
   1122  * The read permission bit for "other" users is checked.  This bit must be
   1123  * set for tftpd(8) to allow clients to read the file.
   1124  */
   1125 
   1126 PRIVATE int
   1127 chk_access(char *path, int32 *filesize)
   1128 {
   1129 	struct stat st;
   1130 
   1131 	if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) {
   1132 		*filesize = (int32) st.st_size;
   1133 		return 0;
   1134 	} else {
   1135 		return -1;
   1136 	}
   1137 }
   1138 
   1139 
   1141 /*
   1142  * Now in dumptab.c :
   1143  *	dumptab()
   1144  *	dump_host()
   1145  *	list_ipaddresses()
   1146  */
   1147 
   1148 #ifdef VEND_CMU
   1149 
   1150 /*
   1151  * Insert the CMU "vendor" data for the host pointed to by "hp" into the
   1152  * bootp packet pointed to by "bp".
   1153  */
   1154 
   1155 PRIVATE void
   1156 dovend_cmu(struct bootp *bp, struct host *hp)
   1157 {
   1158 	struct cmu_vend *vendp;
   1159 	struct in_addr_list *taddr;
   1160 
   1161 	/*
   1162 	 * Initialize the entire vendor field to zeroes.
   1163 	 */
   1164 	bzero(bp->bp_vend, sizeof(bp->bp_vend));
   1165 
   1166 	/*
   1167 	 * Fill in vendor information. Subnet mask, default gateway,
   1168 	 * domain name server, ien name server, time server
   1169 	 */
   1170 	vendp = (struct cmu_vend *) bp->bp_vend;
   1171 	strlcpy(vendp->v_magic, (char *)vm_cmu, sizeof(vendp->v_magic));
   1172 	if (hp->flags.subnet_mask) {
   1173 		(vendp->v_smask).s_addr = hp->subnet_mask.s_addr;
   1174 		(vendp->v_flags) |= VF_SMASK;
   1175 		if (hp->flags.gateway) {
   1176 			(vendp->v_dgate).s_addr = hp->gateway->addr->s_addr;
   1177 		}
   1178 	}
   1179 	if (hp->flags.domain_server) {
   1180 		taddr = hp->domain_server;
   1181 		if (taddr->addrcount > 0) {
   1182 			(vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr;
   1183 			if (taddr->addrcount > 1) {
   1184 				(vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr;
   1185 			}
   1186 		}
   1187 	}
   1188 	if (hp->flags.name_server) {
   1189 		taddr = hp->name_server;
   1190 		if (taddr->addrcount > 0) {
   1191 			(vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr;
   1192 			if (taddr->addrcount > 1) {
   1193 				(vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr;
   1194 			}
   1195 		}
   1196 	}
   1197 	if (hp->flags.time_server) {
   1198 		taddr = hp->time_server;
   1199 		if (taddr->addrcount > 0) {
   1200 			(vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr;
   1201 			if (taddr->addrcount > 1) {
   1202 				(vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr;
   1203 			}
   1204 		}
   1205 	}
   1206 	/* Log message now done by caller. */
   1207 } /* dovend_cmu */
   1208 
   1209 #endif /* VEND_CMU */
   1210 
   1211 
   1213 
   1214 /*
   1215  * Insert the RFC1048 vendor data for the host pointed to by "hp" into the
   1216  * bootp packet pointed to by "bp".
   1217  */
   1218 #define	NEED(LEN, MSG) do \
   1219 	if (bytesleft < (LEN)) { \
   1220 		report(LOG_NOTICE, noroom, \
   1221 			   hp->hostname->string, MSG); \
   1222 		return; \
   1223 	} while (0)
   1224 PRIVATE void
   1225 dovend_rfc1048(struct bootp *bp, struct host *hp, int32 bootsize)
   1226 {
   1227 	int bytesleft, len;
   1228 	byte *vp;
   1229 
   1230 	static const char noroom[] = "%s: No room for \"%s\" option";
   1231 
   1232 	vp = bp->bp_vend;
   1233 
   1234 	if (hp->flags.msg_size) {
   1235 		pktlen = hp->msg_size;
   1236 	} else {
   1237 		/*
   1238 		 * If the request was longer than the official length, build
   1239 		 * a response of that same length where the additional length
   1240 		 * is assumed to be part of the bp_vend (options) area.
   1241 		 */
   1242 		if (pktlen > sizeof(*bp)) {
   1243 			if (debug > 1)
   1244 				report(LOG_INFO, "request message length=%d", pktlen);
   1245 		}
   1246 		/*
   1247 		 * Check whether the request contains the option:
   1248 		 * Maximum DHCP Message Size (RFC1533 sec. 9.8)
   1249 		 * and if so, override the response length with its value.
   1250 		 * This request must lie within the first BP_VEND_LEN
   1251 		 * bytes of the option space.
   1252 		 */
   1253 		{
   1254 			byte *p, *ep;
   1255 			byte tag, len;
   1256 			short msgsz = 0;
   1257 
   1258 			p = vp + 4;
   1259 			ep = p + BP_VEND_LEN - 4;
   1260 			while (p < ep) {
   1261 				tag = *p++;
   1262 				/* Check for tags with no data first. */
   1263 				if (tag == TAG_PAD)
   1264 					continue;
   1265 				if (tag == TAG_END)
   1266 					break;
   1267 				/* Now scan the length byte. */
   1268 				len = *p++;
   1269 				switch (tag) {
   1270 				case TAG_MAX_MSGSZ:
   1271 					if (len == 2) {
   1272 						bcopy(p, (char*)&msgsz, 2);
   1273 						msgsz = ntohs(msgsz);
   1274 					}
   1275 					break;
   1276 				case TAG_SUBNET_MASK:
   1277 					/* XXX - Should preserve this if given... */
   1278 					break;
   1279 				} /* swtich */
   1280 				p += len;
   1281 			}
   1282 
   1283 			if (msgsz > sizeof(*bp)) {
   1284 				if (debug > 1)
   1285 					report(LOG_INFO, "request has DHCP msglen=%d", msgsz);
   1286 				pktlen = msgsz;
   1287 			}
   1288 		}
   1289 	}
   1290 
   1291 	if (pktlen < sizeof(*bp)) {
   1292 		report(LOG_ERR, "invalid response length=%d", pktlen);
   1293 		pktlen = sizeof(*bp);
   1294 	}
   1295 	bytesleft = ((byte*)bp + pktlen) - vp;
   1296 	if (pktlen > sizeof(*bp)) {
   1297 		if (debug > 1)
   1298 			report(LOG_INFO, "extended reply, length=%d, options=%d",
   1299 				   pktlen, bytesleft);
   1300 	}
   1301 
   1302 	/* Copy in the magic cookie */
   1303 	bcopy(vm_rfc1048, vp, 4);
   1304 	vp += 4;
   1305 	bytesleft -= 4;
   1306 
   1307 	if (hp->flags.subnet_mask) {
   1308 		/* always enough room here. */
   1309 		*vp++ = TAG_SUBNET_MASK;/* -1 byte  */
   1310 		*vp++ = 4;				/* -1 byte  */
   1311 		insert_u_long(hp->subnet_mask.s_addr, &vp);	/* -4 bytes */
   1312 		bytesleft -= 6;			/* Fix real count */
   1313 		if (hp->flags.gateway) {
   1314 			(void) insert_ip(TAG_GATEWAY,
   1315 							 hp->gateway,
   1316 							 &vp, &bytesleft);
   1317 		}
   1318 	}
   1319 	if (hp->flags.bootsize) {
   1320 		/* always enough room here */
   1321 		bootsize = (hp->flags.bootsize_auto) ?
   1322 			((bootsize + 511) / 512) : (hp->bootsize);	/* Round up */
   1323 		*vp++ = TAG_BOOT_SIZE;
   1324 		*vp++ = 2;
   1325 		*vp++ = (byte) ((bootsize >> 8) & 0xFF);
   1326 		*vp++ = (byte) (bootsize & 0xFF);
   1327 		bytesleft -= 4;			/* Tag, length, and 16 bit blocksize */
   1328 	}
   1329 	/*
   1330 	 * This one is special: Remaining options go in the ext file.
   1331 	 * Only the subnet_mask, bootsize, and gateway should precede.
   1332 	 */
   1333 	if (hp->flags.exten_file) {
   1334 		/*
   1335 		 * Check for room for exten_file.  Add 3 to account for
   1336 		 * TAG_EXTEN_FILE, length, and TAG_END.
   1337 		 */
   1338 		len = strlen(hp->exten_file->string);
   1339 		NEED((len + 3), "ef");
   1340 		*vp++ = TAG_EXTEN_FILE;
   1341 		*vp++ = (byte) (len & 0xFF);
   1342 		bcopy(hp->exten_file->string, vp, len);
   1343 		vp += len;
   1344 		*vp++ = TAG_END;
   1345 		bytesleft -= len + 3;
   1346 		return;					/* no more options here. */
   1347 	}
   1348 	/*
   1349 	 * The remaining options are inserted by the following
   1350 	 * function (which is shared with bootpef.c).
   1351 	 * Keep back one byte for the TAG_END.
   1352 	 */
   1353 	len = dovend_rfc1497(hp, vp, bytesleft - 1);
   1354 	vp += len;
   1355 	bytesleft -= len;
   1356 
   1357 	/* There should be at least one byte left. */
   1358 	NEED(1, "(end)");
   1359 	*vp++ = TAG_END;
   1360 	bytesleft--;
   1361 
   1362 	/* Log message done by caller. */
   1363 	if (bytesleft > 0) {
   1364 		/*
   1365 		 * Zero out any remaining part of the vendor area.
   1366 		 */
   1367 		bzero(vp, bytesleft);
   1368 	}
   1369 } /* dovend_rfc1048 */
   1370 #undef	NEED
   1371 
   1372 
   1374 /*
   1375  * Now in readfile.c:
   1376  * 	hwlookcmp()
   1377  *	iplookcmp()
   1378  */
   1379 
   1380 /* haddrtoa() - now in hwaddr.c */
   1381 /*
   1382  * Now in dovend.c:
   1383  * insert_ip()
   1384  * insert_generic()
   1385  * insert_u_long()
   1386  */
   1387 
   1388 /* get_errmsg() - now in report.c */
   1389 
   1390 /*
   1391  * Local Variables:
   1392  * tab-width: 4
   1393  * c-indent-level: 4
   1394  * c-argdecl-indent: 4
   1395  * c-continued-statement-offset: 4
   1396  * c-continued-brace-offset: -4
   1397  * c-label-offset: -4
   1398  * c-brace-offset: 0
   1399  * End:
   1400  */
   1401