Home | History | Annotate | Line # | Download | only in bootpd
bootpd.c revision 1.17
      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.17 2003/01/06 13:26:28 wiz 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 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 		strcpy(bp->bp_sname, hostname);
    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 		strcpy (tst, hp->exec_file->string);
    753 		strcat (tst, " ");
    754 		strcat (tst, hp->hostname->string);
    755 		strcat (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 		strncpy(realpath, hp->tftpdir->string, sizeof(realpath) - 1);
    824 		realpath[sizeof(realpath) - 1] = '\0';
    825 		clntpath = &realpath[strlen(realpath)];
    826 	} else {
    827 		realpath[0] = '\0';
    828 		clntpath = realpath;
    829 	}
    830 
    831 	/*
    832 	 * Determine client's requested homedir and bootfile.
    833 	 */
    834 	homedir = NULL;
    835 	bootfile = NULL;
    836 	if (bp->bp_file[0]) {
    837 		char	*t;
    838 
    839 		homedir = bp->bp_file;
    840 
    841 		/* make sure that the file is nul terminated */
    842 		for (t = homedir; t - homedir < BP_FILE_LEN; t++)
    843 			if (*t == '\0')
    844 				break;
    845 		if (t - homedir < BP_FILE_LEN) {
    846 			report(LOG_INFO, "requested path length > BP_FILE_LEN  file = \"%s\", nul terminating", homedir);
    847 			homedir[BP_FILE_LEN - 1] = '\0';
    848 		}
    849 
    850 		bootfile = strrchr(homedir, '/');
    851 		if (bootfile) {
    852 			if (homedir == bootfile)
    853 				homedir = NULL;
    854 			*bootfile++ = '\0';
    855 		} else {
    856 			/* no "/" in the string */
    857 			bootfile = homedir;
    858 			homedir = NULL;
    859 		}
    860 		if (debug > 2) {
    861 			report(LOG_INFO, "requested path=\"%s\"  file=\"%s\"",
    862 				   (homedir) ? homedir : "",
    863 				   (bootfile) ? bootfile : "");
    864 		}
    865 	}
    866 
    867 	/*
    868 	 * Specifications in bootptab override client requested values.
    869 	 */
    870 	if (hp->flags.homedir)
    871 		homedir = hp->homedir->string;
    872 	if (hp->flags.bootfile)
    873 		bootfile = hp->bootfile->string;
    874 
    875 	/*
    876 	 * Construct bootfile path.
    877 	 */
    878 	if (homedir) {
    879 		if (homedir[0] != '/') {
    880 			strncat(realpath, "/", sizeof(realpath) - 1);
    881 			realpath[sizeof(realpath) - 1] = '\0';
    882 		}
    883 		strncat(realpath, homedir, sizeof(realpath) - 1);
    884 		realpath[sizeof(realpath) - 1] = '\0';
    885 		homedir = NULL;
    886 	}
    887 	if (bootfile) {
    888 		if (bootfile[0] != '/') {
    889 			strcat(realpath, "/");
    890 			realpath[sizeof(realpath) - 1] = '\0';
    891 		}
    892 		strcat(realpath, bootfile);
    893 		realpath[sizeof(realpath) - 1] = '\0';
    894 		bootfile = NULL;
    895 	}
    896 
    897 	/*
    898 	 * First try to find the file with a ".host" suffix
    899 	 */
    900 	n = strlen(clntpath);
    901 	strcat(clntpath, ".");
    902 	strcat(clntpath, hp->hostname->string);
    903 	if (chk_access(realpath, &bootsize) < 0) {
    904 		clntpath[n] = 0;			/* Try it without the suffix */
    905 		if (chk_access(realpath, &bootsize) < 0) {
    906 			/* neither "file.host" nor "file" was found */
    907 #ifdef	CHECK_FILE_ACCESS
    908 
    909 			if (bp->bp_file[0]) {
    910 				/*
    911 				 * Client wanted specific file
    912 				 * and we didn't have it.
    913 				 */
    914 				report(LOG_NOTICE,
    915 					   "requested file not found: \"%s\"", clntpath);
    916 				return;
    917 			}
    918 			/*
    919 			 * Client didn't ask for a specific file and we couldn't
    920 			 * access the default file, so just zero-out the bootfile
    921 			 * field in the packet and continue processing the reply.
    922 			 */
    923 			bzero(bp->bp_file, sizeof(bp->bp_file));
    924 			goto null_file_name;
    925 
    926 #else	/* CHECK_FILE_ACCESS */
    927 
    928 			/* Complain only if boot file size was needed. */
    929 			if (hp->flags.bootsize_auto) {
    930 				report(LOG_ERR, "can not determine size of file \"%s\"",
    931 					   clntpath);
    932 			}
    933 
    934 #endif	/* CHECK_FILE_ACCESS */
    935 		}
    936 	}
    937 	strncpy(bp->bp_file, clntpath, BP_FILE_LEN);
    938 	if (debug > 2)
    939 		report(LOG_INFO, "bootfile=\"%s\"", clntpath);
    940 
    941 #ifdef	CHECK_FILE_ACCESS
    942 null_file_name:
    943 #endif	/* CHECK_FILE_ACCESS */
    944 
    945 
    946 	/*
    948 	 * Handle vendor options based on magic number.
    949 	 */
    950 
    951 	if (debug > 1) {
    952 		report(LOG_INFO, "vendor magic field is %d.%d.%d.%d",
    953 			   (int) ((bp->bp_vend)[0]),
    954 			   (int) ((bp->bp_vend)[1]),
    955 			   (int) ((bp->bp_vend)[2]),
    956 			   (int) ((bp->bp_vend)[3]));
    957 	}
    958 	/*
    959 	 * If this host isn't set for automatic vendor info then copy the
    960 	 * specific cookie into the bootp packet, thus forcing a certain
    961 	 * reply format.  Only force reply format if user specified it.
    962 	 */
    963 	if (hp->flags.vm_cookie) {
    964 		/* Slam in the user specified magic number. */
    965 		bcopy(hp->vm_cookie, bp->bp_vend, 4);
    966 	}
    967 	/*
    968 	 * Figure out the format for the vendor-specific info.
    969 	 * Note that bp->bp_vend may have been set above.
    970 	 */
    971 	if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) {
    972 		/* RFC1048 conformant bootp client */
    973 		dovend_rfc1048(bp, hp, bootsize);
    974 		if (debug > 1) {
    975 			report(LOG_INFO, "sending reply (with RFC1048 options)");
    976 		}
    977 	}
    978 #ifdef VEND_CMU
    979 	else if (!bcmp(bp->bp_vend, vm_cmu, 4)) {
    980 		dovend_cmu(bp, hp);
    981 		if (debug > 1) {
    982 			report(LOG_INFO, "sending reply (with CMU options)");
    983 		}
    984 	}
    985 #endif
    986 	else {
    987 		if (debug > 1) {
    988 			report(LOG_INFO, "sending reply (with no options)");
    989 		}
    990 	}
    991 
    992 	dest = (hp->flags.reply_addr) ?
    993 		hp->reply_addr.s_addr : 0L;
    994 
    995 	/* not forwarded */
    996 	sendreply(0, dest);
    997 }
    998 
    999 
   1000 /*
   1001  * Process BOOTREPLY packet.
   1002  */
   1003 PRIVATE void
   1004 handle_reply(void)
   1005 {
   1006 	if (debug) {
   1007 		report(LOG_INFO, "processing boot reply");
   1008 	}
   1009 	/* forwarded, no destination override */
   1010 	sendreply(1, 0);
   1011 }
   1012 
   1013 
   1014 /*
   1015  * Send a reply packet to the client.  'forward' flag is set if we are
   1016  * not the originator of this reply packet.
   1017  */
   1018 PRIVATE void
   1019 sendreply(int forward, int32 dst_override)
   1020 {
   1021 	struct bootp *bp = (struct bootp *) pktbuf;
   1022 	struct in_addr dst;
   1023 	u_short port = bootpc_port;
   1024 	unsigned char *ha;
   1025 	int len;
   1026 
   1027 	/*
   1028 	 * XXX - Should honor bp_flags "broadcast" bit here.
   1029 	 * Temporary workaround: use the :ra=ADDR: option to
   1030 	 * set the reply address to the broadcast address.
   1031 	 */
   1032 
   1033 	/*
   1034 	 * If the destination address was specified explicitly
   1035 	 * (i.e. the broadcast address for HP compatibility)
   1036 	 * then send the response to that address.  Otherwise,
   1037 	 * act in accordance with RFC951:
   1038 	 *   If the client IP address is specified, use that
   1039 	 * else if gateway IP address is specified, use that
   1040 	 * else make a temporary arp cache entry for the client's
   1041 	 * NEW IP/hardware address and use that.
   1042 	 */
   1043 	if (dst_override) {
   1044 		dst.s_addr = dst_override;
   1045 		if (debug > 1) {
   1046 			report(LOG_INFO, "reply address override: %s",
   1047 				   inet_ntoa(dst));
   1048 		}
   1049 	} else if (bp->bp_ciaddr.s_addr) {
   1050 		dst = bp->bp_ciaddr;
   1051 	} else if (bp->bp_giaddr.s_addr && forward == 0) {
   1052 		dst = bp->bp_giaddr;
   1053 		port = bootps_port;
   1054 		if (debug > 1) {
   1055 			report(LOG_INFO, "sending reply to gateway %s",
   1056 				   inet_ntoa(dst));
   1057 		}
   1058 	} else {
   1059 		dst = bp->bp_yiaddr;
   1060 		ha = bp->bp_chaddr;
   1061 		len = bp->bp_hlen;
   1062 		if (len > MAXHADDRLEN)
   1063 			len = MAXHADDRLEN;
   1064 
   1065 		if (debug > 1)
   1066 			report(LOG_INFO, "setarp %s - %s",
   1067 				   inet_ntoa(dst), haddrtoa(ha, len));
   1068 		setarp(s, &dst, ha, len);
   1069 	}
   1070 
   1071 	if ((forward == 0) &&
   1072 		(bp->bp_siaddr.s_addr == 0))
   1073 	{
   1074 		struct ifreq *ifr;
   1075 		struct in_addr siaddr;
   1076 		/*
   1077 		 * If we are originating this reply, we
   1078 		 * need to find our own interface address to
   1079 		 * put in the bp_siaddr field of the reply.
   1080 		 * If this server is multi-homed, pick the
   1081 		 * 'best' interface (the one on the same net
   1082 		 * as the client).  Of course, the client may
   1083 		 * be on the other side of a BOOTP gateway...
   1084 		 */
   1085 		ifr = getif(s, &dst);
   1086 		if (ifr) {
   1087 			struct sockaddr_in *sip;
   1088 			sip = (struct sockaddr_in *) &(ifr->ifr_addr);
   1089 			siaddr = sip->sin_addr;
   1090 		} else {
   1091 			/* Just use my "official" IP address. */
   1092 			siaddr = my_ip_addr;
   1093 		}
   1094 
   1095 		/* XXX - No need to set bp_giaddr here. */
   1096 
   1097 		/* Finally, set the server address field. */
   1098 		bp->bp_siaddr = siaddr;
   1099 	}
   1100 	/* Set up socket address for send. */
   1101 	send_addr.sin_family = AF_INET;
   1102 	send_addr.sin_port = htons(port);
   1103 	send_addr.sin_addr = dst;
   1104 
   1105 	/* Send reply with same size packet as request used. */
   1106 	if (sendto(s, pktbuf, pktlen, 0,
   1107 			   (struct sockaddr *) &send_addr,
   1108 			   sizeof(send_addr)) < 0)
   1109 	{
   1110 		report(LOG_ERR, "sendto: %s", get_network_errmsg());
   1111 	}
   1112 } /* sendreply */
   1113 
   1114 
   1116 /* nmatch() - now in getif.c */
   1117 /* setarp() - now in hwaddr.c */
   1118 
   1119 
   1120 /*
   1121  * This call checks read access to a file.  It returns 0 if the file given
   1122  * by "path" exists and is publically readable.  A value of -1 is returned if
   1123  * access is not permitted or an error occurs.  Successful calls also
   1124  * return the file size in bytes using the long pointer "filesize".
   1125  *
   1126  * The read permission bit for "other" users is checked.  This bit must be
   1127  * set for tftpd(8) to allow clients to read the file.
   1128  */
   1129 
   1130 PRIVATE int
   1131 chk_access(char *path, int32 *filesize)
   1132 {
   1133 	struct stat st;
   1134 
   1135 	if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) {
   1136 		*filesize = (int32) st.st_size;
   1137 		return 0;
   1138 	} else {
   1139 		return -1;
   1140 	}
   1141 }
   1142 
   1143 
   1145 /*
   1146  * Now in dumptab.c :
   1147  *	dumptab()
   1148  *	dump_host()
   1149  *	list_ipaddresses()
   1150  */
   1151 
   1152 #ifdef VEND_CMU
   1153 
   1154 /*
   1155  * Insert the CMU "vendor" data for the host pointed to by "hp" into the
   1156  * bootp packet pointed to by "bp".
   1157  */
   1158 
   1159 PRIVATE void
   1160 dovend_cmu(struct bootp *bp, struct host *hp)
   1161 {
   1162 	struct cmu_vend *vendp;
   1163 	struct in_addr_list *taddr;
   1164 
   1165 	/*
   1166 	 * Initialize the entire vendor field to zeroes.
   1167 	 */
   1168 	bzero(bp->bp_vend, sizeof(bp->bp_vend));
   1169 
   1170 	/*
   1171 	 * Fill in vendor information. Subnet mask, default gateway,
   1172 	 * domain name server, ien name server, time server
   1173 	 */
   1174 	vendp = (struct cmu_vend *) bp->bp_vend;
   1175 	strcpy(vendp->v_magic, (char *)vm_cmu);
   1176 	if (hp->flags.subnet_mask) {
   1177 		(vendp->v_smask).s_addr = hp->subnet_mask.s_addr;
   1178 		(vendp->v_flags) |= VF_SMASK;
   1179 		if (hp->flags.gateway) {
   1180 			(vendp->v_dgate).s_addr = hp->gateway->addr->s_addr;
   1181 		}
   1182 	}
   1183 	if (hp->flags.domain_server) {
   1184 		taddr = hp->domain_server;
   1185 		if (taddr->addrcount > 0) {
   1186 			(vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr;
   1187 			if (taddr->addrcount > 1) {
   1188 				(vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr;
   1189 			}
   1190 		}
   1191 	}
   1192 	if (hp->flags.name_server) {
   1193 		taddr = hp->name_server;
   1194 		if (taddr->addrcount > 0) {
   1195 			(vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr;
   1196 			if (taddr->addrcount > 1) {
   1197 				(vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr;
   1198 			}
   1199 		}
   1200 	}
   1201 	if (hp->flags.time_server) {
   1202 		taddr = hp->time_server;
   1203 		if (taddr->addrcount > 0) {
   1204 			(vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr;
   1205 			if (taddr->addrcount > 1) {
   1206 				(vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr;
   1207 			}
   1208 		}
   1209 	}
   1210 	/* Log message now done by caller. */
   1211 } /* dovend_cmu */
   1212 
   1213 #endif /* VEND_CMU */
   1214 
   1215 
   1217 
   1218 /*
   1219  * Insert the RFC1048 vendor data for the host pointed to by "hp" into the
   1220  * bootp packet pointed to by "bp".
   1221  */
   1222 #define	NEED(LEN, MSG) do \
   1223 	if (bytesleft < (LEN)) { \
   1224 		report(LOG_NOTICE, noroom, \
   1225 			   hp->hostname->string, MSG); \
   1226 		return; \
   1227 	} while (0)
   1228 PRIVATE void
   1229 dovend_rfc1048(struct bootp *bp, struct host *hp, int32 bootsize)
   1230 {
   1231 	int bytesleft, len;
   1232 	byte *vp;
   1233 
   1234 	static const char noroom[] = "%s: No room for \"%s\" option";
   1235 
   1236 	vp = bp->bp_vend;
   1237 
   1238 	if (hp->flags.msg_size) {
   1239 		pktlen = hp->msg_size;
   1240 	} else {
   1241 		/*
   1242 		 * If the request was longer than the official length, build
   1243 		 * a response of that same length where the additional length
   1244 		 * is assumed to be part of the bp_vend (options) area.
   1245 		 */
   1246 		if (pktlen > sizeof(*bp)) {
   1247 			if (debug > 1)
   1248 				report(LOG_INFO, "request message length=%d", pktlen);
   1249 		}
   1250 		/*
   1251 		 * Check whether the request contains the option:
   1252 		 * Maximum DHCP Message Size (RFC1533 sec. 9.8)
   1253 		 * and if so, override the response length with its value.
   1254 		 * This request must lie within the first BP_VEND_LEN
   1255 		 * bytes of the option space.
   1256 		 */
   1257 		{
   1258 			byte *p, *ep;
   1259 			byte tag, len;
   1260 			short msgsz = 0;
   1261 
   1262 			p = vp + 4;
   1263 			ep = p + BP_VEND_LEN - 4;
   1264 			while (p < ep) {
   1265 				tag = *p++;
   1266 				/* Check for tags with no data first. */
   1267 				if (tag == TAG_PAD)
   1268 					continue;
   1269 				if (tag == TAG_END)
   1270 					break;
   1271 				/* Now scan the length byte. */
   1272 				len = *p++;
   1273 				switch (tag) {
   1274 				case TAG_MAX_MSGSZ:
   1275 					if (len == 2) {
   1276 						bcopy(p, (char*)&msgsz, 2);
   1277 						msgsz = ntohs(msgsz);
   1278 					}
   1279 					break;
   1280 				case TAG_SUBNET_MASK:
   1281 					/* XXX - Should preserve this if given... */
   1282 					break;
   1283 				} /* swtich */
   1284 				p += len;
   1285 			}
   1286 
   1287 			if (msgsz > sizeof(*bp)) {
   1288 				if (debug > 1)
   1289 					report(LOG_INFO, "request has DHCP msglen=%d", msgsz);
   1290 				pktlen = msgsz;
   1291 			}
   1292 		}
   1293 	}
   1294 
   1295 	if (pktlen < sizeof(*bp)) {
   1296 		report(LOG_ERR, "invalid response length=%d", pktlen);
   1297 		pktlen = sizeof(*bp);
   1298 	}
   1299 	bytesleft = ((byte*)bp + pktlen) - vp;
   1300 	if (pktlen > sizeof(*bp)) {
   1301 		if (debug > 1)
   1302 			report(LOG_INFO, "extended reply, length=%d, options=%d",
   1303 				   pktlen, bytesleft);
   1304 	}
   1305 
   1306 	/* Copy in the magic cookie */
   1307 	bcopy(vm_rfc1048, vp, 4);
   1308 	vp += 4;
   1309 	bytesleft -= 4;
   1310 
   1311 	if (hp->flags.subnet_mask) {
   1312 		/* always enough room here. */
   1313 		*vp++ = TAG_SUBNET_MASK;/* -1 byte  */
   1314 		*vp++ = 4;				/* -1 byte  */
   1315 		insert_u_long(hp->subnet_mask.s_addr, &vp);	/* -4 bytes */
   1316 		bytesleft -= 6;			/* Fix real count */
   1317 		if (hp->flags.gateway) {
   1318 			(void) insert_ip(TAG_GATEWAY,
   1319 							 hp->gateway,
   1320 							 &vp, &bytesleft);
   1321 		}
   1322 	}
   1323 	if (hp->flags.bootsize) {
   1324 		/* always enough room here */
   1325 		bootsize = (hp->flags.bootsize_auto) ?
   1326 			((bootsize + 511) / 512) : (hp->bootsize);	/* Round up */
   1327 		*vp++ = TAG_BOOT_SIZE;
   1328 		*vp++ = 2;
   1329 		*vp++ = (byte) ((bootsize >> 8) & 0xFF);
   1330 		*vp++ = (byte) (bootsize & 0xFF);
   1331 		bytesleft -= 4;			/* Tag, length, and 16 bit blocksize */
   1332 	}
   1333 	/*
   1334 	 * This one is special: Remaining options go in the ext file.
   1335 	 * Only the subnet_mask, bootsize, and gateway should precede.
   1336 	 */
   1337 	if (hp->flags.exten_file) {
   1338 		/*
   1339 		 * Check for room for exten_file.  Add 3 to account for
   1340 		 * TAG_EXTEN_FILE, length, and TAG_END.
   1341 		 */
   1342 		len = strlen(hp->exten_file->string);
   1343 		NEED((len + 3), "ef");
   1344 		*vp++ = TAG_EXTEN_FILE;
   1345 		*vp++ = (byte) (len & 0xFF);
   1346 		bcopy(hp->exten_file->string, vp, len);
   1347 		vp += len;
   1348 		*vp++ = TAG_END;
   1349 		bytesleft -= len + 3;
   1350 		return;					/* no more options here. */
   1351 	}
   1352 	/*
   1353 	 * The remaining options are inserted by the following
   1354 	 * function (which is shared with bootpef.c).
   1355 	 * Keep back one byte for the TAG_END.
   1356 	 */
   1357 	len = dovend_rfc1497(hp, vp, bytesleft - 1);
   1358 	vp += len;
   1359 	bytesleft -= len;
   1360 
   1361 	/* There should be at least one byte left. */
   1362 	NEED(1, "(end)");
   1363 	*vp++ = TAG_END;
   1364 	bytesleft--;
   1365 
   1366 	/* Log message done by caller. */
   1367 	if (bytesleft > 0) {
   1368 		/*
   1369 		 * Zero out any remaining part of the vendor area.
   1370 		 */
   1371 		bzero(vp, bytesleft);
   1372 	}
   1373 } /* dovend_rfc1048 */
   1374 #undef	NEED
   1375 
   1376 
   1378 /*
   1379  * Now in readfile.c:
   1380  * 	hwlookcmp()
   1381  *	iplookcmp()
   1382  */
   1383 
   1384 /* haddrtoa() - now in hwaddr.c */
   1385 /*
   1386  * Now in dovend.c:
   1387  * insert_ip()
   1388  * insert_generic()
   1389  * insert_u_long()
   1390  */
   1391 
   1392 /* get_errmsg() - now in report.c */
   1393 
   1394 /*
   1395  * Local Variables:
   1396  * tab-width: 4
   1397  * c-indent-level: 4
   1398  * c-argdecl-indent: 4
   1399  * c-continued-statement-offset: 4
   1400  * c-continued-brace-offset: -4
   1401  * c-label-offset: -4
   1402  * c-brace-offset: 0
   1403  * End:
   1404  */
   1405