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