Home | History | Annotate | Line # | Download | only in bootpd
bootpd.c revision 1.25
      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.25 2014/03/29 18:23:00 apb Exp $");
     26 #endif
     27 
     28 /*
     29  * BOOTP (bootstrap protocol) server daemon.
     30  *
     31  * Answers BOOTP request packets from booting client machines.
     32  * See [SRI-NIC]<RFC>RFC951.TXT for a description of the protocol.
     33  * See [SRI-NIC]<RFC>RFC1048.TXT for vendor-information extensions.
     34  * See RFC 1395 for option tags 14-17.
     35  * See accompanying man page -- bootpd.8
     36  *
     37  * HISTORY
     38  *	See ./Changes
     39  *
     40  * BUGS
     41  *	See ./ToDo
     42  */
     43 
     44 
     45 
     47 #include <sys/types.h>
     48 #include <sys/param.h>
     49 #include <sys/socket.h>
     50 #include <sys/ioctl.h>
     51 #include <sys/file.h>
     52 #include <sys/time.h>
     53 #include <sys/stat.h>
     54 #include <sys/poll.h>
     55 
     56 #include <net/if.h>
     57 #include <netinet/in.h>
     58 #include <arpa/inet.h>	/* inet_ntoa */
     59 
     60 #ifndef	NO_UNISTD
     61 #include <unistd.h>
     62 #endif
     63 #include <stdlib.h>
     64 #include <signal.h>
     65 #include <stdio.h>
     66 #include <string.h>
     67 #include <strings.h>
     68 #include <errno.h>
     69 #include <ctype.h>
     70 #include <netdb.h>
     71 #include <syslog.h>
     72 #include <assert.h>
     73 
     74 #ifdef	NO_SETSID
     75 # include <fcntl.h>		/* for O_RDONLY, etc */
     76 #endif
     77 
     78 #ifdef	SVR4
     79 /* Using sigset() avoids the need to re-arm each time. */
     80 #define signal sigset
     81 #endif
     82 
     83 #include "bootp.h"
     84 #include "hash.h"
     85 #include "hwaddr.h"
     86 #include "bootpd.h"
     87 #include "dovend.h"
     88 #include "getif.h"
     89 #include "readfile.h"
     90 #include "report.h"
     91 #include "tzone.h"
     92 #include "patchlevel.h"
     93 
     94 #ifndef CONFIG_FILE
     95 #define CONFIG_FILE		"/etc/bootptab"
     96 #endif
     97 #ifndef DUMPTAB_FILE
     98 #define DUMPTAB_FILE		"/tmp/bootpd.dump"
     99 #endif
    100 
    101 
    102 
    104 /*
    105  * Externals, forward declarations, and global variables
    106  */
    107 
    108 extern void dumptab(const char *);
    109 
    110 PRIVATE void catcher(int);
    111 PRIVATE int chk_access(char *, int32 *);
    112 #ifdef VEND_CMU
    113 PRIVATE void dovend_cmu(struct bootp *, struct host *);
    114 #endif
    115 PRIVATE void dovend_rfc1048(struct bootp *, struct host *, int32);
    116 PRIVATE void handle_reply(void);
    117 PRIVATE void handle_request(void);
    118 PRIVATE void sendreply(int forward, int32 dest_override);
    119 __dead PRIVATE void usage(void);
    120 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 	size_t clntpathmaxlen;
    608 	char *homedir, *bootfile;
    609 	int n;
    610 
    611 	/* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
    612 
    613 	/*
    614 	 * If the servername field is set, compare it against us.
    615 	 * If we're not being addressed, ignore this request.
    616 	 * If the server name field is null, throw in our name.
    617 	 */
    618 	if (strlen(bp->bp_sname)) {
    619 		if (strcmp(bp->bp_sname, hostname)) {
    620 			if (debug)
    621 				report(LOG_INFO, "\
    622 ignoring request for server %s from client at %s address %s",
    623 					   bp->bp_sname, netname(bp->bp_htype),
    624 					   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
    625 			/* XXX - Is it correct to ignore such a request? -gwr */
    626 			return;
    627 		}
    628 	} else {
    629 		strlcpy(bp->bp_sname, hostname, sizeof(bp->bp_sname));
    630 	}
    631 
    632 	/* If it uses an unknown network type, ignore the request.  */
    633 	if (bp->bp_htype >= hwinfocnt) {
    634 		if (debug)
    635 			report(LOG_INFO,
    636 			    "Request with unknown network type %u",
    637 			    bp->bp_htype);
    638 		return;
    639 	}
    640 
    641 	/* Convert the request into a reply. */
    642 	bp->bp_op = BOOTREPLY;
    643 	if (bp->bp_ciaddr.s_addr == 0) {
    644 		/*
    645 		 * client doesnt know his IP address,
    646 		 * search by hardware address.
    647 		 */
    648 		if (debug > 1) {
    649 			report(LOG_INFO, "request from %s address %s",
    650 				   netname(bp->bp_htype),
    651 				   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
    652 		}
    653 		hlen = haddrlength(bp->bp_htype);
    654 		if (hlen != bp->bp_hlen) {
    655 			report(LOG_NOTICE, "bad addr len from %s address %s",
    656 				   netname(bp->bp_htype),
    657 				   haddrtoa(bp->bp_chaddr, hlen));
    658 		}
    659 		dummyhost.htype = bp->bp_htype;
    660 		bcopy(bp->bp_chaddr, dummyhost.haddr, hlen);
    661 		hashcode = hash_HashFunction(bp->bp_chaddr, hlen);
    662 		hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp,
    663 										 &dummyhost);
    664 		if (hp == NULL &&
    665 			bp->bp_htype == HTYPE_IEEE802)
    666 		{
    667 			/* Try again with address in "canonical" form. */
    668 			haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen);
    669 			if (debug > 1) {
    670 				report(LOG_INFO, "\
    671 HW addr type is IEEE 802.  convert to %s and check again\n",
    672 					   haddrtoa(dummyhost.haddr, bp->bp_hlen));
    673 			}
    674 			hashcode = hash_HashFunction(dummyhost.haddr, hlen);
    675 			hp = (struct host *) hash_Lookup(hwhashtable, hashcode,
    676 											 hwlookcmp, &dummyhost);
    677 		}
    678 		if (hp == NULL) {
    679 			/*
    680 			 * XXX - Add dynamic IP address assignment?
    681 			 */
    682 			if (debug > 1)
    683 				report(LOG_INFO, "unknown client %s address %s",
    684 					   netname(bp->bp_htype),
    685 					   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
    686 			return; /* not found */
    687 		}
    688 		(bp->bp_yiaddr).s_addr = hp->iaddr.s_addr;
    689 
    690 	} else {
    691 
    692 		/*
    693 		 * search by IP address.
    694 		 */
    695 		if (debug > 1) {
    696 			report(LOG_INFO, "request from IP addr %s",
    697 				   inet_ntoa(bp->bp_ciaddr));
    698 		}
    699 		dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr;
    700 		hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4);
    701 		hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp,
    702 										 &dummyhost);
    703 		if (hp == NULL) {
    704 			if (debug > 1) {
    705 				report(LOG_NOTICE, "IP address not found: %s",
    706 					   inet_ntoa(bp->bp_ciaddr));
    707 			}
    708 			return;
    709 		}
    710 	}
    711 
    712 	if (debug) {
    713 		report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr),
    714 			   hp->hostname->string);
    715 	}
    716 
    717 	/*
    718 	 * If there is a response delay threshold, ignore requests
    719 	 * with a timestamp lower than the threshold.
    720 	 */
    721 	if (hp->flags.min_wait) {
    722 		u_int32 t = (u_int32) ntohs(bp->bp_secs);
    723 		if (t < hp->min_wait) {
    724 			if (debug > 1)
    725 				report(LOG_INFO,
    726 					   "ignoring request due to timestamp (%d < %d)",
    727 					   t, hp->min_wait);
    728 			return;
    729 		}
    730 	}
    731 
    732 #ifdef	YORK_EX_OPTION
    733 	/*
    734 	 * The need for the "ex" tag arose out of the need to empty
    735 	 * shared networked drives on diskless PCs.  This solution is
    736 	 * not very clean but it does work fairly well.
    737 	 * Written by Edmund J. Sutcliffe <edmund (at) york.ac.uk>
    738 	 *
    739 	 * XXX - This could compromise security if a non-trusted user
    740 	 * managed to write an entry in the bootptab with :ex=trojan:
    741 	 * so I would leave this turned off unless you need it. -gwr
    742 	 */
    743 	/* Run a program, passing the client name as a parameter. */
    744 	if (hp->flags.exec_file) {
    745 		char tst[100];
    746 		/* XXX - Check string lengths? -gwr */
    747 		strlcpy(tst, hp->exec_file->string, sizeof(tst));
    748 		strlcat(tst, " ", sizeof(tst));
    749 		strlcat(tst, hp->hostname->string, sizeof(tst));
    750 		strlcat(tst, " &", sizeof(tst));
    751 		if (debug)
    752 			report(LOG_INFO, "executing %s", tst);
    753 		system(tst);	/* Hope this finishes soon... */
    754 	}
    755 #endif	/* YORK_EX_OPTION */
    756 
    757 	/*
    758 	 * If a specific TFTP server address was specified in the bootptab file,
    759 	 * fill it in, otherwise zero it.
    760 	 * XXX - Rather than zero it, should it be the bootpd address? -gwr
    761 	 */
    762 	(bp->bp_siaddr).s_addr = (hp->flags.bootserver) ?
    763 		hp->bootserver.s_addr : 0L;
    764 
    765 #ifdef	STANFORD_PROM_COMPAT
    766 	/*
    767 	 * Stanford bootp PROMs (for a Sun?) have no way to leave
    768 	 * the boot file name field blank (because the boot file
    769 	 * name is automatically generated from some index).
    770 	 * As a work-around, this little hack allows those PROMs to
    771 	 * specify "sunboot14" with the same effect as a NULL name.
    772 	 * (The user specifies boot device 14 or some such magic.)
    773 	 */
    774 	if (strcmp(bp->bp_file, "sunboot14") == 0)
    775 		bp->bp_file[0] = '\0';	/* treat it as unspecified */
    776 #endif
    777 
    778 	/*
    779 	 * Fill in the client's proper bootfile.
    780 	 *
    781 	 * If the client specifies an absolute path, try that file with a
    782 	 * ".host" suffix and then without.  If the file cannot be found, no
    783 	 * reply is made at all.
    784 	 *
    785 	 * If the client specifies a null or relative file, use the following
    786 	 * table to determine the appropriate action:
    787 	 *
    788 	 *  Homedir      Bootfile    Client's file
    789 	 * specified?   specified?   specification   Action
    790 	 * -------------------------------------------------------------------
    791 	 *      No          No          Null         Send null filename
    792 	 *      No          No          Relative     Discard request
    793 	 *      No          Yes         Null         Send if absolute else null
    794 	 *      No          Yes         Relative     Discard request     *XXX
    795 	 *      Yes         No          Null         Send null filename
    796 	 *      Yes         No          Relative     Lookup with ".host"
    797 	 *      Yes         Yes         Null         Send home/boot or bootfile
    798 	 *      Yes         Yes         Relative     Lookup with ".host" *XXX
    799 	 *
    800 	 */
    801 
    802 	/*
    803 	 * XXX - I don't like the policy of ignoring a client when the
    804 	 * boot file is not accessible.  The TFTP server might not be
    805 	 * running on the same machine as the BOOTP server, in which
    806 	 * case checking accessibility of the boot file is pointless.
    807 	 *
    808 	 * Therefore, file accessibility is now demanded ONLY if you
    809 	 * define CHECK_FILE_ACCESS in the Makefile options. -gwr
    810 	 */
    811 
    812 	/*
    813 	 * The "real" path is as seen by the BOOTP daemon on this
    814 	 * machine, while the client path is relative to the TFTP
    815 	 * daemon chroot directory (i.e. /tftpboot).
    816 	 */
    817 	if (hp->flags.tftpdir) {
    818 		strlcpy(lrealpath, hp->tftpdir->string, sizeof(lrealpath));
    819 		clntpath = &lrealpath[strlen(lrealpath)];
    820 		clntpathmaxlen = sizeof(lrealpath) + lrealpath - clntpath;
    821 	} else {
    822 		lrealpath[0] = '\0';
    823 		clntpath = lrealpath;
    824 		clntpathmaxlen = sizeof(lrealpath)
    825 	}
    826 
    827 	/*
    828 	 * Determine client's requested homedir and bootfile.
    829 	 */
    830 	homedir = NULL;
    831 	bootfile = NULL;
    832 	if (bp->bp_file[0]) {
    833 		char	*t;
    834 
    835 		homedir = bp->bp_file;
    836 
    837 		/* make sure that the file is nul terminated */
    838 		for (t = homedir; t - homedir < BP_FILE_LEN; t++)
    839 			if (*t == '\0')
    840 				break;
    841 		if (t - homedir < BP_FILE_LEN) {
    842 			report(LOG_INFO, "requested path length > BP_FILE_LEN  file = \"%s\", nul terminating", homedir);
    843 			homedir[BP_FILE_LEN - 1] = '\0';
    844 		}
    845 
    846 		bootfile = strrchr(homedir, '/');
    847 		if (bootfile) {
    848 			if (homedir == bootfile)
    849 				homedir = NULL;
    850 			*bootfile++ = '\0';
    851 		} else {
    852 			/* no "/" in the string */
    853 			bootfile = homedir;
    854 			homedir = NULL;
    855 		}
    856 		if (debug > 2) {
    857 			report(LOG_INFO, "requested path=\"%s\"  file=\"%s\"",
    858 				   (homedir) ? homedir : "",
    859 				   (bootfile) ? bootfile : "");
    860 		}
    861 	}
    862 
    863 	/*
    864 	 * Specifications in bootptab override client requested values.
    865 	 */
    866 	if (hp->flags.homedir)
    867 		homedir = hp->homedir->string;
    868 	if (hp->flags.bootfile)
    869 		bootfile = hp->bootfile->string;
    870 
    871 	/*
    872 	 * Construct bootfile path.
    873 	 */
    874 	if (homedir) {
    875 		if (homedir[0] != '/')
    876 			strlcat(lrealpath, "/", sizeof(lrealpath));
    877 		strlcat(lrealpath, homedir, sizeof(lrealpath));
    878 		homedir = NULL;
    879 	}
    880 	if (bootfile) {
    881 		if (bootfile[0] != '/') {
    882 			strlcat(lrealpath, "/", sizeof(lrealpath));
    883 			lrealpath[sizeof(lrealpath) - 1] = '\0';
    884 		}
    885 		strlcat(lrealpath, bootfile, sizeof(lrealpath));
    886 		lrealpath[sizeof(lrealpath) - 1] = '\0';
    887 		bootfile = NULL;
    888 	}
    889 
    890 	/*
    891 	 * First try to find the file with a ".host" suffix
    892 	 */
    893 	n = strlen(clntpath);
    894 	strlcat(clntpath, ".", clntpathmaxlen);
    895 	strlcat(clntpath, hp->hostname->string, clntpathmaxlen);
    896 	if (chk_access(lrealpath, &bootsize) < 0) {
    897 		clntpath[n] = 0;			/* Try it without the suffix */
    898 		if (chk_access(lrealpath, &bootsize) < 0) {
    899 			/* neither "file.host" nor "file" was found */
    900 #ifdef	CHECK_FILE_ACCESS
    901 
    902 			if (bp->bp_file[0]) {
    903 				/*
    904 				 * Client wanted specific file
    905 				 * and we didn't have it.
    906 				 */
    907 				report(LOG_NOTICE,
    908 					   "requested file not found: \"%s\"", clntpath);
    909 				return;
    910 			}
    911 			/*
    912 			 * Client didn't ask for a specific file and we couldn't
    913 			 * access the default file, so just zero-out the bootfile
    914 			 * field in the packet and continue processing the reply.
    915 			 */
    916 			bzero(bp->bp_file, sizeof(bp->bp_file));
    917 			goto null_file_name;
    918 
    919 #else	/* CHECK_FILE_ACCESS */
    920 
    921 			/* Complain only if boot file size was needed. */
    922 			if (hp->flags.bootsize_auto) {
    923 				report(LOG_ERR, "can not determine size of file \"%s\"",
    924 					   clntpath);
    925 			}
    926 
    927 #endif	/* CHECK_FILE_ACCESS */
    928 		}
    929 	}
    930 	strlcpy(bp->bp_file, clntpath, sizeof(bp->bp_file));
    931 	if (debug > 2)
    932 		report(LOG_INFO, "bootfile=\"%s\"", clntpath);
    933 
    934 #ifdef	CHECK_FILE_ACCESS
    935 null_file_name:
    936 #endif	/* CHECK_FILE_ACCESS */
    937 
    938 
    939 	/*
    941 	 * Handle vendor options based on magic number.
    942 	 */
    943 
    944 	if (debug > 1) {
    945 		report(LOG_INFO, "vendor magic field is %d.%d.%d.%d",
    946 			   (int) ((bp->bp_vend)[0]),
    947 			   (int) ((bp->bp_vend)[1]),
    948 			   (int) ((bp->bp_vend)[2]),
    949 			   (int) ((bp->bp_vend)[3]));
    950 	}
    951 	/*
    952 	 * If this host isn't set for automatic vendor info then copy the
    953 	 * specific cookie into the bootp packet, thus forcing a certain
    954 	 * reply format.  Only force reply format if user specified it.
    955 	 */
    956 	if (hp->flags.vm_cookie) {
    957 		/* Slam in the user specified magic number. */
    958 		bcopy(hp->vm_cookie, bp->bp_vend, 4);
    959 	}
    960 	/*
    961 	 * Figure out the format for the vendor-specific info.
    962 	 * Note that bp->bp_vend may have been set above.
    963 	 */
    964 	if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) {
    965 		/* RFC1048 conformant bootp client */
    966 		dovend_rfc1048(bp, hp, bootsize);
    967 		if (debug > 1) {
    968 			report(LOG_INFO, "sending reply (with RFC1048 options)");
    969 		}
    970 	}
    971 #ifdef VEND_CMU
    972 	else if (!bcmp(bp->bp_vend, vm_cmu, 4)) {
    973 		dovend_cmu(bp, hp);
    974 		if (debug > 1) {
    975 			report(LOG_INFO, "sending reply (with CMU options)");
    976 		}
    977 	}
    978 #endif
    979 	else {
    980 		if (debug > 1) {
    981 			report(LOG_INFO, "sending reply (with no options)");
    982 		}
    983 	}
    984 
    985 	dest = (hp->flags.reply_addr) ?
    986 		hp->reply_addr.s_addr : 0L;
    987 
    988 	/* not forwarded */
    989 	sendreply(0, dest);
    990 }
    991 
    992 
    993 /*
    994  * Process BOOTREPLY packet.
    995  */
    996 PRIVATE void
    997 handle_reply(void)
    998 {
    999 	if (debug) {
   1000 		report(LOG_INFO, "processing boot reply");
   1001 	}
   1002 	/* forwarded, no destination override */
   1003 	sendreply(1, 0);
   1004 }
   1005 
   1006 
   1007 /*
   1008  * Send a reply packet to the client.  'forward' flag is set if we are
   1009  * not the originator of this reply packet.
   1010  */
   1011 PRIVATE void
   1012 sendreply(int forward, int32 dst_override)
   1013 {
   1014 	struct bootp *bp = (struct bootp *) pktbuf;
   1015 	struct in_addr dst;
   1016 	u_short port = bootpc_port;
   1017 	unsigned char *ha;
   1018 	int len;
   1019 
   1020 	/*
   1021 	 * XXX - Should honor bp_flags "broadcast" bit here.
   1022 	 * Temporary workaround: use the :ra=ADDR: option to
   1023 	 * set the reply address to the broadcast address.
   1024 	 */
   1025 
   1026 	/*
   1027 	 * If the destination address was specified explicitly
   1028 	 * (i.e. the broadcast address for HP compatibility)
   1029 	 * then send the response to that address.  Otherwise,
   1030 	 * act in accordance with RFC951:
   1031 	 *   If the client IP address is specified, use that
   1032 	 * else if gateway IP address is specified, use that
   1033 	 * else make a temporary arp cache entry for the client's
   1034 	 * NEW IP/hardware address and use that.
   1035 	 */
   1036 	if (dst_override) {
   1037 		dst.s_addr = dst_override;
   1038 		if (debug > 1) {
   1039 			report(LOG_INFO, "reply address override: %s",
   1040 				   inet_ntoa(dst));
   1041 		}
   1042 	} else if (bp->bp_ciaddr.s_addr) {
   1043 		dst = bp->bp_ciaddr;
   1044 	} else if (bp->bp_giaddr.s_addr && forward == 0) {
   1045 		dst = bp->bp_giaddr;
   1046 		port = bootps_port;
   1047 		if (debug > 1) {
   1048 			report(LOG_INFO, "sending reply to gateway %s",
   1049 				   inet_ntoa(dst));
   1050 		}
   1051 	} else {
   1052 		dst = bp->bp_yiaddr;
   1053 		ha = bp->bp_chaddr;
   1054 		len = bp->bp_hlen;
   1055 		if (len > MAXHADDRLEN)
   1056 			len = MAXHADDRLEN;
   1057 
   1058 		if (debug > 1)
   1059 			report(LOG_INFO, "setarp %s - %s",
   1060 				   inet_ntoa(dst), haddrtoa(ha, len));
   1061 		setarp(s, &dst, ha, len);
   1062 	}
   1063 
   1064 	if ((forward == 0) &&
   1065 		(bp->bp_siaddr.s_addr == 0))
   1066 	{
   1067 		struct ifreq *ifr;
   1068 		struct in_addr siaddr;
   1069 		/*
   1070 		 * If we are originating this reply, we
   1071 		 * need to find our own interface address to
   1072 		 * put in the bp_siaddr field of the reply.
   1073 		 * If this server is multi-homed, pick the
   1074 		 * 'best' interface (the one on the same net
   1075 		 * as the client).  Of course, the client may
   1076 		 * be on the other side of a BOOTP gateway...
   1077 		 */
   1078 		ifr = getif(s, &dst);
   1079 		if (ifr) {
   1080 			struct sockaddr_in *sip;
   1081 			sip = (struct sockaddr_in *) &(ifr->ifr_addr);
   1082 			siaddr = sip->sin_addr;
   1083 		} else {
   1084 			/* Just use my "official" IP address. */
   1085 			siaddr = my_ip_addr;
   1086 		}
   1087 
   1088 		/* XXX - No need to set bp_giaddr here. */
   1089 
   1090 		/* Finally, set the server address field. */
   1091 		bp->bp_siaddr = siaddr;
   1092 	}
   1093 	/* Set up socket address for send. */
   1094 	send_addr.sin_family = AF_INET;
   1095 	send_addr.sin_port = htons(port);
   1096 	send_addr.sin_addr = dst;
   1097 
   1098 	/* Send reply with same size packet as request used. */
   1099 	if (sendto(s, pktbuf, pktlen, 0,
   1100 			   (struct sockaddr *) &send_addr,
   1101 			   sizeof(send_addr)) < 0)
   1102 	{
   1103 		report(LOG_ERR, "sendto: %s", get_network_errmsg());
   1104 	}
   1105 } /* sendreply */
   1106 
   1107 
   1109 /* nmatch() - now in getif.c */
   1110 /* setarp() - now in hwaddr.c */
   1111 
   1112 
   1113 /*
   1114  * This call checks read access to a file.  It returns 0 if the file given
   1115  * by "path" exists and is publically readable.  A value of -1 is returned if
   1116  * access is not permitted or an error occurs.  Successful calls also
   1117  * return the file size in bytes using the long pointer "filesize".
   1118  *
   1119  * The read permission bit for "other" users is checked.  This bit must be
   1120  * set for tftpd(8) to allow clients to read the file.
   1121  */
   1122 
   1123 PRIVATE int
   1124 chk_access(char *path, int32 *filesize)
   1125 {
   1126 	struct stat st;
   1127 
   1128 	if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) {
   1129 		*filesize = (int32) st.st_size;
   1130 		return 0;
   1131 	} else {
   1132 		return -1;
   1133 	}
   1134 }
   1135 
   1136 
   1138 /*
   1139  * Now in dumptab.c :
   1140  *	dumptab()
   1141  *	dump_host()
   1142  *	list_ipaddresses()
   1143  */
   1144 
   1145 #ifdef VEND_CMU
   1146 
   1147 /*
   1148  * Insert the CMU "vendor" data for the host pointed to by "hp" into the
   1149  * bootp packet pointed to by "bp".
   1150  */
   1151 
   1152 PRIVATE void
   1153 dovend_cmu(struct bootp *bp, struct host *hp)
   1154 {
   1155 	struct cmu_vend *vendp;
   1156 	struct in_addr_list *taddr;
   1157 
   1158 	/*
   1159 	 * Initialize the entire vendor field to zeroes.
   1160 	 */
   1161 	bzero(bp->bp_vend, sizeof(bp->bp_vend));
   1162 
   1163 	/*
   1164 	 * Fill in vendor information. Subnet mask, default gateway,
   1165 	 * domain name server, ien name server, time server
   1166 	 */
   1167 	vendp = (struct cmu_vend *) bp->bp_vend;
   1168 	strlcpy(vendp->v_magic, (char *)vm_cmu, sizeof(vendp->v_magic));
   1169 	if (hp->flags.subnet_mask) {
   1170 		(vendp->v_smask).s_addr = hp->subnet_mask.s_addr;
   1171 		(vendp->v_flags) |= VF_SMASK;
   1172 		if (hp->flags.gateway) {
   1173 			(vendp->v_dgate).s_addr = hp->gateway->addr->s_addr;
   1174 		}
   1175 	}
   1176 	if (hp->flags.domain_server) {
   1177 		taddr = hp->domain_server;
   1178 		if (taddr->addrcount > 0) {
   1179 			(vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr;
   1180 			if (taddr->addrcount > 1) {
   1181 				(vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr;
   1182 			}
   1183 		}
   1184 	}
   1185 	if (hp->flags.name_server) {
   1186 		taddr = hp->name_server;
   1187 		if (taddr->addrcount > 0) {
   1188 			(vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr;
   1189 			if (taddr->addrcount > 1) {
   1190 				(vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr;
   1191 			}
   1192 		}
   1193 	}
   1194 	if (hp->flags.time_server) {
   1195 		taddr = hp->time_server;
   1196 		if (taddr->addrcount > 0) {
   1197 			(vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr;
   1198 			if (taddr->addrcount > 1) {
   1199 				(vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr;
   1200 			}
   1201 		}
   1202 	}
   1203 	/* Log message now done by caller. */
   1204 } /* dovend_cmu */
   1205 
   1206 #endif /* VEND_CMU */
   1207 
   1208 
   1210 
   1211 /*
   1212  * Insert the RFC1048 vendor data for the host pointed to by "hp" into the
   1213  * bootp packet pointed to by "bp".
   1214  */
   1215 #define	NEED(LEN, MSG) do \
   1216 	if (bytesleft < (LEN)) { \
   1217 		report(LOG_NOTICE, noroom, \
   1218 			   hp->hostname->string, MSG); \
   1219 		return; \
   1220 	} while (0)
   1221 PRIVATE void
   1222 dovend_rfc1048(struct bootp *bp, struct host *hp, int32 bootsize)
   1223 {
   1224 	int bytesleft, len;
   1225 	byte *vp;
   1226 
   1227 	static const char noroom[] = "%s: No room for \"%s\" option";
   1228 
   1229 	vp = bp->bp_vend;
   1230 
   1231 	if (hp->flags.msg_size) {
   1232 		pktlen = hp->msg_size;
   1233 	} else {
   1234 		/*
   1235 		 * If the request was longer than the official length, build
   1236 		 * a response of that same length where the additional length
   1237 		 * is assumed to be part of the bp_vend (options) area.
   1238 		 */
   1239 		if (pktlen > (int)sizeof(*bp)) {
   1240 			if (debug > 1)
   1241 				report(LOG_INFO, "request message length=%d", pktlen);
   1242 		}
   1243 		/*
   1244 		 * Check whether the request contains the option:
   1245 		 * Maximum DHCP Message Size (RFC1533 sec. 9.8)
   1246 		 * and if so, override the response length with its value.
   1247 		 * This request must lie within the first BP_VEND_LEN
   1248 		 * bytes of the option space.
   1249 		 */
   1250 		{
   1251 			byte *p, *ep;
   1252 			byte tag, llen;
   1253 			short msgsz = 0;
   1254 
   1255 			p = vp + 4;
   1256 			ep = p + BP_VEND_LEN - 4;
   1257 			while (p < ep) {
   1258 				tag = *p++;
   1259 				/* Check for tags with no data first. */
   1260 				if (tag == TAG_PAD)
   1261 					continue;
   1262 				if (tag == TAG_END)
   1263 					break;
   1264 				/* Now scan the length byte. */
   1265 				llen = *p++;
   1266 				switch (tag) {
   1267 				case TAG_MAX_MSGSZ:
   1268 					if (llen == 2) {
   1269 						bcopy(p, (char*)&msgsz, 2);
   1270 						msgsz = ntohs(msgsz);
   1271 					}
   1272 					break;
   1273 				case TAG_SUBNET_MASK:
   1274 					/* XXX - Should preserve this if given... */
   1275 					break;
   1276 				} /* swtich */
   1277 				p += llen;
   1278 			}
   1279 
   1280 			if (msgsz > (int)sizeof(*bp)) {
   1281 				if (debug > 1)
   1282 					report(LOG_INFO, "request has DHCP msglen=%d", msgsz);
   1283 				pktlen = msgsz;
   1284 			}
   1285 		}
   1286 	}
   1287 
   1288 	if (pktlen < (int)sizeof(*bp)) {
   1289 		report(LOG_ERR, "invalid response length=%d", pktlen);
   1290 		pktlen = sizeof(*bp);
   1291 	}
   1292 	bytesleft = ((byte*)bp + pktlen) - vp;
   1293 	if (pktlen > (int)sizeof(*bp)) {
   1294 		if (debug > 1)
   1295 			report(LOG_INFO, "extended reply, length=%d, options=%d",
   1296 				   pktlen, bytesleft);
   1297 	}
   1298 
   1299 	/* Copy in the magic cookie */
   1300 	bcopy(vm_rfc1048, vp, 4);
   1301 	vp += 4;
   1302 	bytesleft -= 4;
   1303 
   1304 	if (hp->flags.subnet_mask) {
   1305 		/* always enough room here. */
   1306 		*vp++ = TAG_SUBNET_MASK;/* -1 byte  */
   1307 		*vp++ = 4;				/* -1 byte  */
   1308 		insert_u_long(hp->subnet_mask.s_addr, &vp);	/* -4 bytes */
   1309 		bytesleft -= 6;			/* Fix real count */
   1310 		if (hp->flags.gateway) {
   1311 			(void) insert_ip(TAG_GATEWAY,
   1312 							 hp->gateway,
   1313 							 &vp, &bytesleft);
   1314 		}
   1315 	}
   1316 	if (hp->flags.bootsize) {
   1317 		/* always enough room here */
   1318 		bootsize = (hp->flags.bootsize_auto) ?
   1319 			((bootsize + 511) / 512) : ((int32_t)hp->bootsize);	/* Round up */
   1320 		*vp++ = TAG_BOOT_SIZE;
   1321 		*vp++ = 2;
   1322 		*vp++ = (byte) ((bootsize >> 8) & 0xFF);
   1323 		*vp++ = (byte) (bootsize & 0xFF);
   1324 		bytesleft -= 4;			/* Tag, length, and 16 bit blocksize */
   1325 	}
   1326 	/*
   1327 	 * This one is special: Remaining options go in the ext file.
   1328 	 * Only the subnet_mask, bootsize, and gateway should precede.
   1329 	 */
   1330 	if (hp->flags.exten_file) {
   1331 		/*
   1332 		 * Check for room for exten_file.  Add 3 to account for
   1333 		 * TAG_EXTEN_FILE, length, and TAG_END.
   1334 		 */
   1335 		len = strlen(hp->exten_file->string);
   1336 		NEED((len + 3), "ef");
   1337 		*vp++ = TAG_EXTEN_FILE;
   1338 		*vp++ = (byte) (len & 0xFF);
   1339 		bcopy(hp->exten_file->string, vp, len);
   1340 		vp += len;
   1341 		*vp++ = TAG_END;
   1342 		bytesleft -= len + 3;
   1343 		return;					/* no more options here. */
   1344 	}
   1345 	/*
   1346 	 * The remaining options are inserted by the following
   1347 	 * function (which is shared with bootpef.c).
   1348 	 * Keep back one byte for the TAG_END.
   1349 	 */
   1350 	len = dovend_rfc1497(hp, vp, bytesleft - 1);
   1351 	vp += len;
   1352 	bytesleft -= len;
   1353 
   1354 	/* There should be at least one byte left. */
   1355 	NEED(1, "(end)");
   1356 	*vp++ = TAG_END;
   1357 	bytesleft--;
   1358 
   1359 	/* Log message done by caller. */
   1360 	if (bytesleft > 0) {
   1361 		/*
   1362 		 * Zero out any remaining part of the vendor area.
   1363 		 */
   1364 		bzero(vp, bytesleft);
   1365 	}
   1366 } /* dovend_rfc1048 */
   1367 #undef	NEED
   1368 
   1369 
   1371 /*
   1372  * Now in readfile.c:
   1373  * 	hwlookcmp()
   1374  *	iplookcmp()
   1375  */
   1376 
   1377 /* haddrtoa() - now in hwaddr.c */
   1378 /*
   1379  * Now in dovend.c:
   1380  * insert_ip()
   1381  * insert_generic()
   1382  * insert_u_long()
   1383  */
   1384 
   1385 /* get_errmsg() - now in report.c */
   1386 
   1387 /*
   1388  * Local Variables:
   1389  * tab-width: 4
   1390  * c-indent-level: 4
   1391  * c-argdecl-indent: 4
   1392  * c-continued-statement-offset: 4
   1393  * c-continued-brace-offset: -4
   1394  * c-label-offset: -4
   1395  * c-brace-offset: 0
   1396  * End:
   1397  */
   1398