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