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