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