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