bootpd.c revision 22989
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	$Id$
23
24************************************************************************/
25
26/*
27 * BOOTP (bootstrap protocol) server daemon.
28 *
29 * Answers BOOTP request packets from booting client machines.
30 * See [SRI-NIC]<RFC>RFC951.TXT for a description of the protocol.
31 * See [SRI-NIC]<RFC>RFC1048.TXT for vendor-information extensions.
32 * See RFC 1395 for option tags 14-17.
33 * See accompanying man page -- bootpd.8
34 *
35 * HISTORY
36 *	See ./Changes
37 *
38 * BUGS
39 *	See ./ToDo
40 */
41
42
43
44#include <sys/types.h>
45#include <sys/param.h>
46#include <sys/socket.h>
47#include <sys/ioctl.h>
48#include <sys/file.h>
49#include <sys/time.h>
50#include <sys/stat.h>
51#include <sys/utsname.h>
52
53#include <net/if.h>
54#include <netinet/in.h>
55#include <arpa/inet.h>	/* inet_ntoa */
56
57#ifndef	NO_UNISTD
58#include <unistd.h>
59#endif
60
61#include <stdlib.h>
62#include <signal.h>
63#include <stdio.h>
64#include <string.h>
65#include <errno.h>
66#include <ctype.h>
67#include <netdb.h>
68#include <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
107#ifdef	__STDC__
108#define P(args) args
109#else
110#define P(args) ()
111#endif
112
113extern void dumptab P((char *));
114
115PRIVATE void catcher P((int));
116PRIVATE int chk_access P((char *, int32 *));
117#ifdef VEND_CMU
118PRIVATE void dovend_cmu P((struct bootp *, struct host *));
119#endif
120PRIVATE void dovend_rfc1048 P((struct bootp *, struct host *, int32));
121PRIVATE void handle_reply P((void));
122PRIVATE void handle_request P((void));
123PRIVATE void sendreply P((int forward, int32 dest_override));
124PRIVATE void usage P((void));
125
126#undef	P
127
128/*
129 * IP port numbers for client and server obtained from /etc/services
130 */
131
132u_short bootps_port, bootpc_port;
133
134
135/*
136 * Internet socket and interface config structures
137 */
138
139struct sockaddr_in bind_addr;	/* Listening */
140struct sockaddr_in recv_addr;	/* Packet source */
141struct sockaddr_in send_addr;	/*  destination */
142
143
144/*
145 * option defaults
146 */
147int debug = 0;					/* Debugging flag (level) */
148struct timeval actualtimeout =
149{								/* fifteen minutes */
150	15 * 60L,					/* tv_sec */
151	0							/* tv_usec */
152};
153
154/*
155 * General
156 */
157
158int s;							/* Socket file descriptor */
159char *pktbuf;					/* Receive packet buffer */
160int pktlen;
161char *progname;
162char *chdir_path;
163struct in_addr my_ip_addr;
164
165struct utsname my_uname;
166char *hostname;
167
168/* Flags set by signal catcher. */
169PRIVATE int do_readtab = 0;
170PRIVATE int do_dumptab = 0;
171
172/*
173 * Globals below are associated with the bootp database file (bootptab).
174 */
175
176char *bootptab = CONFIG_FILE;
177char *bootpd_dump = DUMPTAB_FILE;
178
179
180
181/*
182 * Initialization such as command-line processing is done and then the
183 * main server loop is started.
184 */
185
186void
187main(argc, argv)
188	int argc;
189	char **argv;
190{
191	struct timeval *timeout;
192	struct bootp *bp;
193	struct servent *servp;
194	struct hostent *hep;
195	char *stmp;
196	int n, ba_len, ra_len;
197	int nfound, readfds;
198	int standalone;
199#ifdef	SA_NOCLDSTOP	/* Have POSIX sigaction(2). */
200	struct sigaction sa;
201#endif
202
203	progname = strrchr(argv[0], '/');
204	if (progname) progname++;
205	else progname = argv[0];
206
207	/*
208	 * Initialize logging.
209	 */
210	report_init(0);				/* uses progname */
211
212	/*
213	 * Log startup
214	 */
215	report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
216
217	/* Debugging for compilers with struct padding. */
218	assert(sizeof(struct bootp) == BP_MINPKTSZ);
219
220	/* Get space for receiving packets and composing replies. */
221	pktbuf = malloc(MAX_MSG_SIZE);
222	if (!pktbuf) {
223		report(LOG_ERR, "malloc failed");
224		exit(1);
225	}
226	bp = (struct bootp *) pktbuf;
227
228	/*
229	 * Check to see if a socket was passed to us from inetd.
230	 *
231	 * Use getsockname() to determine if descriptor 0 is indeed a socket
232	 * (and thus we are probably a child of inetd) or if it is instead
233	 * something else and we are running standalone.
234	 */
235	s = 0;
236	ba_len = sizeof(bind_addr);
237	bzero((char *) &bind_addr, ba_len);
238	errno = 0;
239	standalone = TRUE;
240	if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {
241		/*
242		 * Descriptor 0 is a socket.  Assume we are a child of inetd.
243		 */
244		if (bind_addr.sin_family == AF_INET) {
245			standalone = FALSE;
246			bootps_port = ntohs(bind_addr.sin_port);
247		} else {
248			/* Some other type of socket? */
249			report(LOG_ERR, "getsockname: not an INET socket");
250		}
251	}
252
253	/*
254	 * Set defaults that might be changed by option switches.
255	 */
256	stmp = NULL;
257	timeout = &actualtimeout;
258
259	if (uname(&my_uname) < 0) {
260		fprintf(stderr, "bootpd: can't get hostname\n");
261		exit(1);
262	}
263	hostname = my_uname.nodename;
264
265	/*
266	 * Read switches.
267	 */
268	for (argc--, argv++; argc > 0; argc--, argv++) {
269		if (argv[0][0] != '-')
270			break;
271		switch (argv[0][1]) {
272
273		case 'c':				/* chdir_path */
274			if (argv[0][2]) {
275				stmp = &(argv[0][2]);
276			} else {
277				argc--;
278				argv++;
279				stmp = argv[0];
280			}
281			if (!stmp || (stmp[0] != '/')) {
282				fprintf(stderr,
283						"bootpd: invalid chdir specification\n");
284				break;
285			}
286			chdir_path = stmp;
287			break;
288
289		case 'd':				/* debug level */
290			if (argv[0][2]) {
291				stmp = &(argv[0][2]);
292			} else if (argv[1] && argv[1][0] == '-') {
293				/*
294				 * Backwards-compatible behavior:
295				 * no parameter, so just increment the debug flag.
296				 */
297				debug++;
298				break;
299			} else {
300				argc--;
301				argv++;
302				stmp = argv[0];
303			}
304			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
305				fprintf(stderr,
306						"%s: invalid debug level\n", progname);
307				break;
308			}
309			debug = n;
310			break;
311
312		case 'h':				/* override hostname */
313			if (argv[0][2]) {
314				stmp = &(argv[0][2]);
315			} else {
316				argc--;
317				argv++;
318				stmp = argv[0];
319			}
320			if (!stmp) {
321				fprintf(stderr,
322						"bootpd: missing hostname\n");
323				break;
324			}
325			hostname = stmp;
326			break;
327
328		case 'i':				/* inetd mode */
329			standalone = FALSE;
330			break;
331
332		case 's':				/* standalone mode */
333			standalone = TRUE;
334			break;
335
336		case 't':				/* timeout */
337			if (argv[0][2]) {
338				stmp = &(argv[0][2]);
339			} else {
340				argc--;
341				argv++;
342				stmp = argv[0];
343			}
344			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
345				fprintf(stderr,
346						"%s: invalid timeout specification\n", progname);
347				break;
348			}
349			actualtimeout.tv_sec = (int32) (60 * n);
350			/*
351			 * If the actual timeout is zero, pass a NULL pointer
352			 * to select so it blocks indefinitely, otherwise,
353			 * point to the actual timeout value.
354			 */
355			timeout = (n > 0) ? &actualtimeout : NULL;
356			break;
357
358		default:
359			fprintf(stderr, "%s: unknown switch: -%c\n",
360					progname, argv[0][1]);
361			usage();
362			break;
363
364		} /* switch */
365	} /* for args */
366
367	/*
368	 * Override default file names if specified on the command line.
369	 */
370	if (argc > 0)
371		bootptab = argv[0];
372
373	if (argc > 1)
374		bootpd_dump = argv[1];
375
376	/*
377	 * Get my hostname and IP address.
378	 */
379
380	hep = gethostbyname(hostname);
381	if (!hep) {
382		fprintf(stderr, "Can not get my IP address\n");
383		exit(1);
384	}
385	bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
386
387	if (standalone) {
388		/*
389		 * Go into background and disassociate from controlling terminal.
390		 */
391		if (debug < 3) {
392			if (fork())
393				exit(0);
394#ifdef	NO_SETSID
395			setpgrp(0,0);
396#ifdef TIOCNOTTY
397			n = open("/dev/tty", O_RDWR);
398			if (n >= 0) {
399				ioctl(n, TIOCNOTTY, (char *) 0);
400				(void) close(n);
401			}
402#endif	/* TIOCNOTTY */
403#else	/* SETSID */
404			if (setsid() < 0)
405				perror("setsid");
406#endif	/* SETSID */
407		} /* if debug < 3 */
408
409		/*
410		 * Nuke any timeout value
411		 */
412		timeout = NULL;
413
414	} /* if standalone (1st) */
415
416	/* Set the cwd (i.e. to /tftpboot) */
417	if (chdir_path) {
418		if (chdir(chdir_path) < 0)
419			report(LOG_ERR, "%s: chdir failed", chdir_path);
420	}
421
422	/* Get the timezone. */
423	tzone_init();
424
425	/* Allocate hash tables. */
426	rdtab_init();
427
428	/*
429	 * Read the bootptab file.
430	 */
431	readtab(1);					/* force read */
432
433	if (standalone) {
434
435		/*
436		 * Create a socket.
437		 */
438		if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
439			report(LOG_ERR, "socket: %s", get_network_errmsg());
440			exit(1);
441		}
442
443		/*
444		 * Get server's listening port number
445		 */
446		servp = getservbyname("bootps", "udp");
447		if (servp) {
448			bootps_port = ntohs((u_short) servp->s_port);
449		} else {
450			bootps_port = (u_short) IPPORT_BOOTPS;
451			report(LOG_ERR,
452				   "udp/bootps: unknown service -- assuming port %d",
453				   bootps_port);
454		}
455
456		/*
457		 * Bind socket to BOOTPS port.
458		 */
459		bind_addr.sin_family = AF_INET;
460		bind_addr.sin_addr.s_addr = INADDR_ANY;
461		bind_addr.sin_port = htons(bootps_port);
462		if (bind(s, (struct sockaddr *) &bind_addr,
463				 sizeof(bind_addr)) < 0)
464		{
465			report(LOG_ERR, "bind: %s", get_network_errmsg());
466			exit(1);
467		}
468	} /* if standalone (2nd)*/
469
470	/*
471	 * Get destination port number so we can reply to client
472	 */
473	servp = getservbyname("bootpc", "udp");
474	if (servp) {
475		bootpc_port = ntohs(servp->s_port);
476	} else {
477		report(LOG_ERR,
478			   "udp/bootpc: unknown service -- assuming port %d",
479			   IPPORT_BOOTPC);
480		bootpc_port = (u_short) IPPORT_BOOTPC;
481	}
482
483	/*
484	 * Set up signals to read or dump the table.
485	 */
486#ifdef	SA_NOCLDSTOP	/* Have POSIX sigaction(2). */
487	sa.sa_handler = catcher;
488	sigemptyset(&sa.sa_mask);
489	sa.sa_flags = 0;
490	if (sigaction(SIGHUP, &sa, NULL) < 0) {
491		report(LOG_ERR, "sigaction: %s", get_errmsg());
492		exit(1);
493	}
494	if (sigaction(SIGUSR1, &sa, NULL) < 0) {
495		report(LOG_ERR, "sigaction: %s", get_errmsg());
496		exit(1);
497	}
498#else	/* SA_NOCLDSTOP */
499	/* Old-fashioned UNIX signals */
500	if ((int) signal(SIGHUP, catcher) < 0) {
501		report(LOG_ERR, "signal: %s", get_errmsg());
502		exit(1);
503	}
504	if ((int) signal(SIGUSR1, catcher) < 0) {
505		report(LOG_ERR, "signal: %s", get_errmsg());
506		exit(1);
507	}
508#endif	/* SA_NOCLDSTOP */
509
510	/*
511	 * Process incoming requests.
512	 */
513	for (;;) {
514		struct timeval tv;
515
516		readfds = 1 << s;
517		if (timeout)
518			tv = *timeout;
519
520		nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL,
521						(timeout) ? &tv : NULL);
522		if (nfound < 0) {
523			if (errno != EINTR) {
524				report(LOG_ERR, "select: %s", get_errmsg());
525			}
526			/*
527			 * Call readtab() or dumptab() here to avoid the
528			 * dangers of doing I/O from a signal handler.
529			 */
530			if (do_readtab) {
531				do_readtab = 0;
532				readtab(1);		/* force read */
533			}
534			if (do_dumptab) {
535				do_dumptab = 0;
536				dumptab(bootpd_dump);
537			}
538			continue;
539		}
540		if (!(readfds & (1 << s))) {
541			if (debug > 1)
542				report(LOG_INFO, "exiting after %ld minutes of inactivity",
543					   actualtimeout.tv_sec / 60);
544			exit(0);
545		}
546		ra_len = sizeof(recv_addr);
547		n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
548					 (struct sockaddr *) &recv_addr, &ra_len);
549		if (n <= 0) {
550			continue;
551		}
552		if (debug > 1) {
553			report(LOG_INFO, "recvd pkt from IP addr %s",
554				   inet_ntoa(recv_addr.sin_addr));
555		}
556		if (n < sizeof(struct bootp)) {
557			if (debug) {
558				report(LOG_NOTICE, "received short packet");
559			}
560			continue;
561		}
562		pktlen = n;
563
564		readtab(0);				/* maybe re-read bootptab */
565
566		switch (bp->bp_op) {
567		case BOOTREQUEST:
568			handle_request();
569			break;
570		case BOOTREPLY:
571			handle_reply();
572			break;
573		}
574	}
575}
576
577
578
579
580/*
581 * Print "usage" message and exit
582 */
583
584PRIVATE void
585usage()
586{
587	fprintf(stderr,
588			"usage:  bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n");
589	fprintf(stderr, "\t -c n\tset current directory\n");
590	fprintf(stderr, "\t -d n\tset debug level\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	/* 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 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		strcpy(realpath, 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 compatiblity)
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 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)) {
1291				if (debug > 1)
1292					report(LOG_INFO, "request has DHCP msglen=%d", msgsz);
1293				pktlen = msgsz;
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