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