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