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