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