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