xref: /freebsd/libexec/bootpd/bootpd.c (revision f19d047aec2b1d05cfb00498caff5141094d1124)
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$
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 <paths.h>
69 #include <syslog.h>
70 #include <assert.h>
71 
72 #ifdef	NO_SETSID
73 # include <fcntl.h>		/* for O_RDONLY, etc */
74 #endif
75 
76 #ifndef	USE_BFUNCS
77 # include <memory.h>
78 /* Yes, memcpy is OK here (no overlapped copies). */
79 # define bcopy(a,b,c)    memcpy(b,a,c)
80 # define bzero(p,l)      memset(p,0,l)
81 # define bcmp(a,b,c)     memcmp(a,b,c)
82 #endif
83 
84 #include "bootp.h"
85 #include "hash.h"
86 #include "hwaddr.h"
87 #include "bootpd.h"
88 #include "dovend.h"
89 #include "getif.h"
90 #include "readfile.h"
91 #include "report.h"
92 #include "tzone.h"
93 #include "patchlevel.h"
94 
95 #ifndef CONFIG_FILE
96 #define CONFIG_FILE		"/etc/bootptab"
97 #endif
98 #ifndef DUMPTAB_FILE
99 #define DUMPTAB_FILE		"/tmp/bootpd.dump"
100 #endif
101 
102 
103 
104 /*
105  * Externals, forward declarations, and global variables
106  */
107 
108 extern void dumptab(char *);
109 
110 PRIVATE void catcher(int);
111 PRIVATE int chk_access(char *, int32 *);
112 #ifdef VEND_CMU
113 PRIVATE void dovend_cmu(struct bootp *, struct host *);
114 #endif
115 PRIVATE void dovend_rfc1048(struct bootp *, struct host *, int32);
116 PRIVATE void handle_reply(void);
117 PRIVATE void handle_request(void);
118 PRIVATE void sendreply(int forward, int32 dest_override);
119 PRIVATE void usage(void);
120 
121 /*
122  * IP port numbers for client and server obtained from /etc/services
123  */
124 
125 u_short bootps_port, bootpc_port;
126 
127 
128 /*
129  * Internet socket and interface config structures
130  */
131 
132 struct sockaddr_in bind_addr;	/* Listening */
133 struct sockaddr_in recv_addr;	/* Packet source */
134 struct sockaddr_in send_addr;	/*  destination */
135 
136 
137 /*
138  * option defaults
139  */
140 int debug = 0;					/* Debugging flag (level) */
141 struct timeval actualtimeout =
142 {								/* fifteen minutes */
143 	15 * 60L,					/* tv_sec */
144 	0							/* tv_usec */
145 };
146 
147 /*
148  * General
149  */
150 
151 int s;							/* Socket file descriptor */
152 char *pktbuf;					/* Receive packet buffer */
153 int pktlen;
154 char *progname;
155 char *chdir_path;
156 struct in_addr my_ip_addr;
157 
158 static const char *hostname;
159 static char default_hostname[MAXHOSTNAMELEN];
160 
161 /* Flags set by signal catcher. */
162 PRIVATE int do_readtab = 0;
163 PRIVATE int do_dumptab = 0;
164 
165 /*
166  * Globals below are associated with the bootp database file (bootptab).
167  */
168 
169 char *bootptab = CONFIG_FILE;
170 char *bootpd_dump = DUMPTAB_FILE;
171 
172 
173 
174 /*
175  * Initialization such as command-line processing is done and then the
176  * main server loop is started.
177  */
178 
179 int
180 main(argc, argv)
181 	int argc;
182 	char **argv;
183 {
184 	struct timeval *timeout;
185 	struct bootp *bp;
186 	struct servent *servp;
187 	struct hostent *hep;
188 	char *stmp;
189 	int n, ba_len, ra_len;
190 	int nfound, readfds;
191 	int standalone;
192 #ifdef	SA_NOCLDSTOP	/* Have POSIX sigaction(2). */
193 	struct sigaction sa;
194 #endif
195 
196 	progname = strrchr(argv[0], '/');
197 	if (progname) progname++;
198 	else progname = argv[0];
199 
200 	/*
201 	 * Initialize logging.
202 	 */
203 	report_init(0);				/* uses progname */
204 
205 	/*
206 	 * Log startup
207 	 */
208 	report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
209 
210 	/* Debugging for compilers with struct padding. */
211 	assert(sizeof(struct bootp) == BP_MINPKTSZ);
212 
213 	/* Get space for receiving packets and composing replies. */
214 	pktbuf = malloc(MAX_MSG_SIZE);
215 	if (!pktbuf) {
216 		report(LOG_ERR, "malloc failed");
217 		exit(1);
218 	}
219 	bp = (struct bootp *) pktbuf;
220 
221 	/*
222 	 * Check to see if a socket was passed to us from inetd.
223 	 *
224 	 * Use getsockname() to determine if descriptor 0 is indeed a socket
225 	 * (and thus we are probably a child of inetd) or if it is instead
226 	 * something else and we are running standalone.
227 	 */
228 	s = 0;
229 	ba_len = sizeof(bind_addr);
230 	bzero((char *) &bind_addr, ba_len);
231 	errno = 0;
232 	standalone = TRUE;
233 	if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {
234 		/*
235 		 * Descriptor 0 is a socket.  Assume we are a child of inetd.
236 		 */
237 		if (bind_addr.sin_family == AF_INET) {
238 			standalone = FALSE;
239 			bootps_port = ntohs(bind_addr.sin_port);
240 		} else {
241 			/* Some other type of socket? */
242 			report(LOG_ERR, "getsockname: not an INET socket");
243 		}
244 	}
245 
246 	/*
247 	 * Set defaults that might be changed by option switches.
248 	 */
249 	stmp = NULL;
250 	timeout = &actualtimeout;
251 
252 	if (gethostname(default_hostname, sizeof(default_hostname) - 1) < 0) {
253 		report(LOG_ERR, "bootpd: can't get hostname\n");
254 		exit(1);
255 	}
256 	default_hostname[sizeof(default_hostname) - 1] = '\0';
257 	hostname = default_hostname;
258 
259 	/*
260 	 * Read switches.
261 	 */
262 	for (argc--, argv++; argc > 0; argc--, argv++) {
263 		if (argv[0][0] != '-')
264 			break;
265 		switch (argv[0][1]) {
266 
267 		case 'c':				/* chdir_path */
268 			if (argv[0][2]) {
269 				stmp = &(argv[0][2]);
270 			} else {
271 				argc--;
272 				argv++;
273 				stmp = argv[0];
274 			}
275 			if (!stmp || (stmp[0] != '/')) {
276 				report(LOG_ERR,
277 						"bootpd: invalid chdir specification\n");
278 				break;
279 			}
280 			chdir_path = stmp;
281 			break;
282 
283 		case 'd':				/* debug level */
284 			if (argv[0][2]) {
285 				stmp = &(argv[0][2]);
286 			} else if (argv[1] && argv[1][0] == '-') {
287 				/*
288 				 * Backwards-compatible behavior:
289 				 * no parameter, so just increment the debug flag.
290 				 */
291 				debug++;
292 				break;
293 			} else {
294 				argc--;
295 				argv++;
296 				stmp = argv[0];
297 			}
298 			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
299 				report(LOG_ERR,
300 						"%s: invalid debug level\n", progname);
301 				break;
302 			}
303 			debug = n;
304 			break;
305 
306 		case 'h':				/* override hostname */
307 			if (argv[0][2]) {
308 				stmp = &(argv[0][2]);
309 			} else {
310 				argc--;
311 				argv++;
312 				stmp = argv[0];
313 			}
314 			if (!stmp) {
315 				report(LOG_ERR,
316 						"bootpd: missing hostname\n");
317 				break;
318 			}
319 			hostname = stmp;
320 			break;
321 
322 		case 'i':				/* inetd mode */
323 			standalone = FALSE;
324 			break;
325 
326 		case 's':				/* standalone mode */
327 			standalone = TRUE;
328 			break;
329 
330 		case 't':				/* timeout */
331 			if (argv[0][2]) {
332 				stmp = &(argv[0][2]);
333 			} else {
334 				argc--;
335 				argv++;
336 				stmp = argv[0];
337 			}
338 			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
339 				report(LOG_ERR,
340 						"%s: invalid timeout specification\n", progname);
341 				break;
342 			}
343 			actualtimeout.tv_sec = (int32) (60 * n);
344 			/*
345 			 * If the actual timeout is zero, pass a NULL pointer
346 			 * to select so it blocks indefinitely, otherwise,
347 			 * point to the actual timeout value.
348 			 */
349 			timeout = (n > 0) ? &actualtimeout : NULL;
350 			break;
351 
352 		default:
353 			report(LOG_ERR, "%s: unknown switch: -%c\n",
354 					progname, argv[0][1]);
355 			usage();
356 			break;
357 
358 		} /* switch */
359 	} /* for args */
360 
361 	/*
362 	 * Override default file names if specified on the command line.
363 	 */
364 	if (argc > 0)
365 		bootptab = argv[0];
366 
367 	if (argc > 1)
368 		bootpd_dump = argv[1];
369 
370 	/*
371 	 * Get my hostname and IP address.
372 	 */
373 
374 	hep = gethostbyname(hostname);
375 	if (!hep) {
376 		report(LOG_ERR, "Can not get my IP address\n");
377 		exit(1);
378 	}
379 	bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
380 
381 	if (standalone) {
382 		/*
383 		 * Go into background and disassociate from controlling terminal.
384 		 */
385 		if (debug < 3) {
386 			if (fork())
387 				exit(0);
388 #ifdef	NO_SETSID
389 			setpgrp(0,0);
390 #ifdef TIOCNOTTY
391 			n = open(_PATH_TTY, O_RDWR);
392 			if (n >= 0) {
393 				ioctl(n, TIOCNOTTY, (char *) 0);
394 				(void) close(n);
395 			}
396 #endif	/* TIOCNOTTY */
397 #else	/* SETSID */
398 			if (setsid() < 0)
399 				perror("setsid");
400 #endif	/* SETSID */
401 		} /* if debug < 3 */
402 
403 		/*
404 		 * Nuke any timeout value
405 		 */
406 		timeout = NULL;
407 
408 	} /* if standalone (1st) */
409 
410 	/* Set the cwd (i.e. to /tftpboot) */
411 	if (chdir_path) {
412 		if (chdir(chdir_path) < 0)
413 			report(LOG_ERR, "%s: chdir failed", chdir_path);
414 	}
415 
416 	/* Get the timezone. */
417 	tzone_init();
418 
419 	/* Allocate hash tables. */
420 	rdtab_init();
421 
422 	/*
423 	 * Read the bootptab file.
424 	 */
425 	readtab(1);					/* force read */
426 
427 	if (standalone) {
428 
429 		/*
430 		 * Create a socket.
431 		 */
432 		if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
433 			report(LOG_ERR, "socket: %s", get_network_errmsg());
434 			exit(1);
435 		}
436 
437 		/*
438 		 * Get server's listening port number
439 		 */
440 		servp = getservbyname("bootps", "udp");
441 		if (servp) {
442 			bootps_port = ntohs((u_short) servp->s_port);
443 		} else {
444 			bootps_port = (u_short) IPPORT_BOOTPS;
445 			report(LOG_ERR,
446 				   "udp/bootps: unknown service -- assuming port %d",
447 				   bootps_port);
448 		}
449 
450 		/*
451 		 * Bind socket to BOOTPS port.
452 		 */
453 		bind_addr.sin_family = AF_INET;
454 		bind_addr.sin_addr.s_addr = INADDR_ANY;
455 		bind_addr.sin_port = htons(bootps_port);
456 		if (bind(s, (struct sockaddr *) &bind_addr,
457 				 sizeof(bind_addr)) < 0)
458 		{
459 			report(LOG_ERR, "bind: %s", get_network_errmsg());
460 			exit(1);
461 		}
462 	} /* if standalone (2nd)*/
463 
464 	/*
465 	 * Get destination port number so we can reply to client
466 	 */
467 	servp = getservbyname("bootpc", "udp");
468 	if (servp) {
469 		bootpc_port = ntohs(servp->s_port);
470 	} else {
471 		report(LOG_ERR,
472 			   "udp/bootpc: unknown service -- assuming port %d",
473 			   IPPORT_BOOTPC);
474 		bootpc_port = (u_short) IPPORT_BOOTPC;
475 	}
476 
477 	/*
478 	 * Set up signals to read or dump the table.
479 	 */
480 #ifdef	SA_NOCLDSTOP	/* Have POSIX sigaction(2). */
481 	sa.sa_handler = catcher;
482 	sigemptyset(&sa.sa_mask);
483 	sa.sa_flags = 0;
484 	if (sigaction(SIGHUP, &sa, NULL) < 0) {
485 		report(LOG_ERR, "sigaction: %s", get_errmsg());
486 		exit(1);
487 	}
488 	if (sigaction(SIGUSR1, &sa, NULL) < 0) {
489 		report(LOG_ERR, "sigaction: %s", get_errmsg());
490 		exit(1);
491 	}
492 #else	/* SA_NOCLDSTOP */
493 	/* Old-fashioned UNIX signals */
494 	if ((int) signal(SIGHUP, catcher) < 0) {
495 		report(LOG_ERR, "signal: %s", get_errmsg());
496 		exit(1);
497 	}
498 	if ((int) signal(SIGUSR1, catcher) < 0) {
499 		report(LOG_ERR, "signal: %s", get_errmsg());
500 		exit(1);
501 	}
502 #endif	/* SA_NOCLDSTOP */
503 
504 	/*
505 	 * Process incoming requests.
506 	 */
507 	for (;;) {
508 		struct timeval tv;
509 
510 		readfds = 1 << s;
511 		if (timeout)
512 			tv = *timeout;
513 
514 		nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL,
515 						(timeout) ? &tv : NULL);
516 		if (nfound < 0) {
517 			if (errno != EINTR) {
518 				report(LOG_ERR, "select: %s", get_errmsg());
519 			}
520 			/*
521 			 * Call readtab() or dumptab() here to avoid the
522 			 * dangers of doing I/O from a signal handler.
523 			 */
524 			if (do_readtab) {
525 				do_readtab = 0;
526 				readtab(1);		/* force read */
527 			}
528 			if (do_dumptab) {
529 				do_dumptab = 0;
530 				dumptab(bootpd_dump);
531 			}
532 			continue;
533 		}
534 		if (!(readfds & (1 << s))) {
535 			if (debug > 1)
536 				report(LOG_INFO, "exiting after %ld minutes of inactivity",
537 					   actualtimeout.tv_sec / 60);
538 			exit(0);
539 		}
540 		ra_len = sizeof(recv_addr);
541 		n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
542 					 (struct sockaddr *) &recv_addr, &ra_len);
543 		if (n <= 0) {
544 			continue;
545 		}
546 		if (debug > 1) {
547 			report(LOG_INFO, "recvd pkt from IP addr %s",
548 				   inet_ntoa(recv_addr.sin_addr));
549 		}
550 		if (n < sizeof(struct bootp)) {
551 			if (debug) {
552 				report(LOG_NOTICE, "received short packet");
553 			}
554 			continue;
555 		}
556 		pktlen = n;
557 
558 		readtab(0);				/* maybe re-read bootptab */
559 
560 		switch (bp->bp_op) {
561 		case BOOTREQUEST:
562 			handle_request();
563 			break;
564 		case BOOTREPLY:
565 			handle_reply();
566 			break;
567 		}
568 	}
569 	return 0;
570 }
571 
572 
573 
574 
575 /*
576  * Print "usage" message and exit
577  */
578 
579 PRIVATE void
580 usage()
581 {
582 	fprintf(stderr,
583 			"usage:  bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n");
584 	fprintf(stderr, "\t -c n\tset current directory\n");
585 	fprintf(stderr, "\t -d n\tset debug level\n");
586 	fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n");
587 	fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n");
588 	fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n");
589 	exit(1);
590 }
591 
592 /* Signal catchers */
593 PRIVATE void
594 catcher(sig)
595 	int sig;
596 {
597 	if (sig == SIGHUP)
598 		do_readtab = 1;
599 	if (sig == SIGUSR1)
600 		do_dumptab = 1;
601 #if	!defined(SA_NOCLDSTOP) && defined(SYSV)
602 	/* For older "System V" derivatives with no sigaction(). */
603 	signal(sig, catcher);
604 #endif
605 }
606 
607 
608 
609 /*
610  * Process BOOTREQUEST packet.
611  *
612  * Note:  This version of the bootpd.c server never forwards
613  * a request to another server.  That is the job of a gateway
614  * program such as the "bootpgw" program included here.
615  *
616  * (Also this version does not interpret the hostname field of
617  * the request packet;  it COULD do a name->address lookup and
618  * forward the request there.)
619  */
620 PRIVATE void
621 handle_request()
622 {
623 	struct bootp *bp = (struct bootp *) pktbuf;
624 	struct host *hp = NULL;
625 	struct host dummyhost;
626 	int32 bootsize = 0;
627 	unsigned hlen, hashcode;
628 	int32 dest;
629 	char realpath[1024];
630 	char *clntpath;
631 	char *homedir, *bootfile;
632 	int n;
633 
634 	bp->bp_file[sizeof(bp->bp_file)-1] = '\0';
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 		snprintf(realpath, sizeof(realpath), "%s", 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 const 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) + BP_MSG_OVERHEAD) {
1288 				if (debug > 1)
1289 					report(LOG_INFO, "request has DHCP msglen=%d", msgsz);
1290 				pktlen = msgsz - BP_MSG_OVERHEAD;
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