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