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