bootpd.c revision 45422
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.10 1998/12/13 21:02:28 eivind 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
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 (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}
577
578
579
580
581/*
582 * Print "usage" message and exit
583 */
584
585PRIVATE void
586usage()
587{
588	fprintf(stderr,
589			"usage:  bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n");
590	fprintf(stderr, "\t -c n\tset current directory\n");
591	fprintf(stderr, "\t -d n\tset debug level\n");
592	fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n");
593	fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n");
594	fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n");
595	exit(1);
596}
597
598/* Signal catchers */
599PRIVATE void
600catcher(sig)
601	int sig;
602{
603	if (sig == SIGHUP)
604		do_readtab = 1;
605	if (sig == SIGUSR1)
606		do_dumptab = 1;
607#if	!defined(SA_NOCLDSTOP) && defined(SYSV)
608	/* For older "System V" derivatives with no sigaction(). */
609	signal(sig, catcher);
610#endif
611}
612
613
614
615/*
616 * Process BOOTREQUEST packet.
617 *
618 * Note:  This version of the bootpd.c server never forwards
619 * a request to another server.  That is the job of a gateway
620 * program such as the "bootpgw" program included here.
621 *
622 * (Also this version does not interpret the hostname field of
623 * the request packet;  it COULD do a name->address lookup and
624 * forward the request there.)
625 */
626PRIVATE void
627handle_request()
628{
629	struct bootp *bp = (struct bootp *) pktbuf;
630	struct host *hp = NULL;
631	struct host dummyhost;
632	int32 bootsize = 0;
633	unsigned hlen, hashcode;
634	int32 dest;
635	char realpath[1024];
636	char *clntpath;
637	char *homedir, *bootfile;
638	int n;
639
640	bp->bp_file[sizeof(bp->bp_file)-1] = '\0';
641
642	/* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
643
644	/*
645	 * If the servername field is set, compare it against us.
646	 * If we're not being addressed, ignore this request.
647	 * If the server name field is null, throw in our name.
648	 */
649	if (strlen(bp->bp_sname)) {
650		if (strcmp(bp->bp_sname, hostname)) {
651			if (debug)
652				report(LOG_INFO, "\
653ignoring request for server %s from client at %s address %s",
654					   bp->bp_sname, netname(bp->bp_htype),
655					   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
656			/* XXX - Is it correct to ignore such a request? -gwr */
657			return;
658		}
659	} else {
660		strcpy(bp->bp_sname, hostname);
661	}
662
663	/* Convert the request into a reply. */
664	bp->bp_op = BOOTREPLY;
665	if (bp->bp_ciaddr.s_addr == 0) {
666		/*
667		 * client doesnt know his IP address,
668		 * search by hardware address.
669		 */
670		if (debug > 1) {
671			report(LOG_INFO, "request from %s address %s",
672				   netname(bp->bp_htype),
673				   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
674		}
675		hlen = haddrlength(bp->bp_htype);
676		if (hlen != bp->bp_hlen) {
677			report(LOG_NOTICE, "bad addr len from from %s address %s",
678				   netname(bp->bp_htype),
679				   haddrtoa(bp->bp_chaddr, hlen));
680		}
681		dummyhost.htype = bp->bp_htype;
682		bcopy(bp->bp_chaddr, dummyhost.haddr, hlen);
683		hashcode = hash_HashFunction(bp->bp_chaddr, hlen);
684		hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp,
685										 &dummyhost);
686		if (hp == NULL &&
687			bp->bp_htype == HTYPE_IEEE802)
688		{
689			/* Try again with address in "canonical" form. */
690			haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen);
691			if (debug > 1) {
692				report(LOG_INFO, "\
693HW addr type is IEEE 802.  convert to %s and check again\n",
694					   haddrtoa(dummyhost.haddr, bp->bp_hlen));
695			}
696			hashcode = hash_HashFunction(dummyhost.haddr, hlen);
697			hp = (struct host *) hash_Lookup(hwhashtable, hashcode,
698											 hwlookcmp, &dummyhost);
699		}
700		if (hp == NULL) {
701			/*
702			 * XXX - Add dynamic IP address assignment?
703			 */
704			if (debug)
705				report(LOG_NOTICE, "unknown client %s address %s",
706					   netname(bp->bp_htype),
707					   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
708			return; /* not found */
709		}
710		(bp->bp_yiaddr).s_addr = hp->iaddr.s_addr;
711
712	} else {
713
714		/*
715		 * search by IP address.
716		 */
717		if (debug > 1) {
718			report(LOG_INFO, "request from IP addr %s",
719				   inet_ntoa(bp->bp_ciaddr));
720		}
721		dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr;
722		hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4);
723		hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp,
724										 &dummyhost);
725		if (hp == NULL) {
726			if (debug) {
727				report(LOG_NOTICE, "IP address not found: %s",
728					   inet_ntoa(bp->bp_ciaddr));
729			}
730			return;
731		}
732	}
733
734	if (debug) {
735		report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr),
736			   hp->hostname->string);
737	}
738
739	/*
740	 * If there is a response delay threshold, ignore requests
741	 * with a timestamp lower than the threshold.
742	 */
743	if (hp->flags.min_wait) {
744		u_int32 t = (u_int32) ntohs(bp->bp_secs);
745		if (t < hp->min_wait) {
746			if (debug > 1)
747				report(LOG_INFO,
748					   "ignoring request due to timestamp (%d < %d)",
749					   t, hp->min_wait);
750			return;
751		}
752	}
753
754#ifdef	YORK_EX_OPTION
755	/*
756	 * The need for the "ex" tag arose out of the need to empty
757	 * shared networked drives on diskless PCs.  This solution is
758	 * not very clean but it does work fairly well.
759	 * Written by Edmund J. Sutcliffe <edmund@york.ac.uk>
760	 *
761	 * XXX - This could compromise security if a non-trusted user
762	 * managed to write an entry in the bootptab with :ex=trojan:
763	 * so I would leave this turned off unless you need it. -gwr
764	 */
765	/* Run a program, passing the client name as a parameter. */
766	if (hp->flags.exec_file) {
767		char tst[100];
768		/* XXX - Check string lengths? -gwr */
769		strcpy (tst, hp->exec_file->string);
770		strcat (tst, " ");
771		strcat (tst, hp->hostname->string);
772		strcat (tst, " &");
773		if (debug)
774			report(LOG_INFO, "executing %s", tst);
775		system(tst);	/* Hope this finishes soon... */
776	}
777#endif	/* YORK_EX_OPTION */
778
779	/*
780	 * If a specific TFTP server address was specified in the bootptab file,
781	 * fill it in, otherwise zero it.
782	 * XXX - Rather than zero it, should it be the bootpd address? -gwr
783	 */
784	(bp->bp_siaddr).s_addr = (hp->flags.bootserver) ?
785		hp->bootserver.s_addr : 0L;
786
787#ifdef	STANFORD_PROM_COMPAT
788	/*
789	 * Stanford bootp PROMs (for a Sun?) have no way to leave
790	 * the boot file name field blank (because the boot file
791	 * name is automatically generated from some index).
792	 * As a work-around, this little hack allows those PROMs to
793	 * specify "sunboot14" with the same effect as a NULL name.
794	 * (The user specifies boot device 14 or some such magic.)
795	 */
796	if (strcmp(bp->bp_file, "sunboot14") == 0)
797		bp->bp_file[0] = '\0';	/* treat it as unspecified */
798#endif
799
800	/*
801	 * Fill in the client's proper bootfile.
802	 *
803	 * If the client specifies an absolute path, try that file with a
804	 * ".host" suffix and then without.  If the file cannot be found, no
805	 * reply is made at all.
806	 *
807	 * If the client specifies a null or relative file, use the following
808	 * table to determine the appropriate action:
809	 *
810	 *  Homedir      Bootfile    Client's file
811	 * specified?   specified?   specification   Action
812	 * -------------------------------------------------------------------
813	 *      No          No          Null         Send null filename
814	 *      No          No          Relative     Discard request
815	 *      No          Yes         Null         Send if absolute else null
816	 *      No          Yes         Relative     Discard request     *XXX
817	 *      Yes         No          Null         Send null filename
818	 *      Yes         No          Relative     Lookup with ".host"
819	 *      Yes         Yes         Null         Send home/boot or bootfile
820	 *      Yes         Yes         Relative     Lookup with ".host" *XXX
821	 *
822	 */
823
824	/*
825	 * XXX - I don't like the policy of ignoring a client when the
826	 * boot file is not accessible.  The TFTP server might not be
827	 * running on the same machine as the BOOTP server, in which
828	 * case checking accessibility of the boot file is pointless.
829	 *
830	 * Therefore, file accessibility is now demanded ONLY if you
831	 * define CHECK_FILE_ACCESS in the Makefile options. -gwr
832	 */
833
834	/*
835	 * The "real" path is as seen by the BOOTP daemon on this
836	 * machine, while the client path is relative to the TFTP
837	 * daemon chroot directory (i.e. /tftpboot).
838	 */
839	if (hp->flags.tftpdir) {
840		snprintf(realpath, sizeof(realpath), "%s", hp->tftpdir->string);
841		clntpath = &realpath[strlen(realpath)];
842	} else {
843		realpath[0] = '\0';
844		clntpath = realpath;
845	}
846
847	/*
848	 * Determine client's requested homedir and bootfile.
849	 */
850	homedir = NULL;
851	bootfile = NULL;
852	if (bp->bp_file[0]) {
853		homedir = bp->bp_file;
854		bootfile = strrchr(homedir, '/');
855		if (bootfile) {
856			if (homedir == bootfile)
857				homedir = NULL;
858			*bootfile++ = '\0';
859		} else {
860			/* no "/" in the string */
861			bootfile = homedir;
862			homedir = NULL;
863		}
864		if (debug > 2) {
865			report(LOG_INFO, "requested path=\"%s\"  file=\"%s\"",
866				   (homedir) ? homedir : "",
867				   (bootfile) ? bootfile : "");
868		}
869	}
870
871	/*
872	 * Specifications in bootptab override client requested values.
873	 */
874	if (hp->flags.homedir)
875		homedir = hp->homedir->string;
876	if (hp->flags.bootfile)
877		bootfile = hp->bootfile->string;
878
879	/*
880	 * Construct bootfile path.
881	 */
882	if (homedir) {
883		if (homedir[0] != '/')
884			strcat(clntpath, "/");
885		strcat(clntpath, homedir);
886		homedir = NULL;
887	}
888	if (bootfile) {
889		if (bootfile[0] != '/')
890			strcat(clntpath, "/");
891		strcat(clntpath, bootfile);
892		bootfile = NULL;
893	}
894
895	/*
896	 * First try to find the file with a ".host" suffix
897	 */
898	n = strlen(clntpath);
899	strcat(clntpath, ".");
900	strcat(clntpath, hp->hostname->string);
901	if (chk_access(realpath, &bootsize) < 0) {
902		clntpath[n] = 0;			/* Try it without the suffix */
903		if (chk_access(realpath, &bootsize) < 0) {
904			/* neither "file.host" nor "file" was found */
905#ifdef	CHECK_FILE_ACCESS
906
907			if (bp->bp_file[0]) {
908				/*
909				 * Client wanted specific file
910				 * and we didn't have it.
911				 */
912				report(LOG_NOTICE,
913					   "requested file not found: \"%s\"", clntpath);
914				return;
915			}
916			/*
917			 * Client didn't ask for a specific file and we couldn't
918			 * access the default file, so just zero-out the bootfile
919			 * field in the packet and continue processing the reply.
920			 */
921			bzero(bp->bp_file, sizeof(bp->bp_file));
922			goto null_file_name;
923
924#else	/* CHECK_FILE_ACCESS */
925
926			/* Complain only if boot file size was needed. */
927			if (hp->flags.bootsize_auto) {
928				report(LOG_ERR, "can not determine size of file \"%s\"",
929					   clntpath);
930			}
931
932#endif	/* CHECK_FILE_ACCESS */
933		}
934	}
935	strncpy(bp->bp_file, clntpath, BP_FILE_LEN);
936	if (debug > 2)
937		report(LOG_INFO, "bootfile=\"%s\"", clntpath);
938
939#ifdef	CHECK_FILE_ACCESS
940null_file_name:
941#endif	/* CHECK_FILE_ACCESS */
942
943
944	/*
945	 * Handle vendor options based on magic number.
946	 */
947
948	if (debug > 1) {
949		report(LOG_INFO, "vendor magic field is %d.%d.%d.%d",
950			   (int) ((bp->bp_vend)[0]),
951			   (int) ((bp->bp_vend)[1]),
952			   (int) ((bp->bp_vend)[2]),
953			   (int) ((bp->bp_vend)[3]));
954	}
955	/*
956	 * If this host isn't set for automatic vendor info then copy the
957	 * specific cookie into the bootp packet, thus forcing a certain
958	 * reply format.  Only force reply format if user specified it.
959	 */
960	if (hp->flags.vm_cookie) {
961		/* Slam in the user specified magic number. */
962		bcopy(hp->vm_cookie, bp->bp_vend, 4);
963	}
964	/*
965	 * Figure out the format for the vendor-specific info.
966	 * Note that bp->bp_vend may have been set above.
967	 */
968	if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) {
969		/* RFC1048 conformant bootp client */
970		dovend_rfc1048(bp, hp, bootsize);
971		if (debug > 1) {
972			report(LOG_INFO, "sending reply (with RFC1048 options)");
973		}
974	}
975#ifdef VEND_CMU
976	else if (!bcmp(bp->bp_vend, vm_cmu, 4)) {
977		dovend_cmu(bp, hp);
978		if (debug > 1) {
979			report(LOG_INFO, "sending reply (with CMU options)");
980		}
981	}
982#endif
983	else {
984		if (debug > 1) {
985			report(LOG_INFO, "sending reply (with no options)");
986		}
987	}
988
989	dest = (hp->flags.reply_addr) ?
990		hp->reply_addr.s_addr : 0L;
991
992	/* not forwarded */
993	sendreply(0, dest);
994}
995
996
997/*
998 * Process BOOTREPLY packet.
999 */
1000PRIVATE void
1001handle_reply()
1002{
1003	if (debug) {
1004		report(LOG_INFO, "processing boot reply");
1005	}
1006	/* forwarded, no destination override */
1007	sendreply(1, 0);
1008}
1009
1010
1011/*
1012 * Send a reply packet to the client.  'forward' flag is set if we are
1013 * not the originator of this reply packet.
1014 */
1015PRIVATE void
1016sendreply(forward, dst_override)
1017	int forward;
1018	int32 dst_override;
1019{
1020	struct bootp *bp = (struct bootp *) pktbuf;
1021	struct in_addr dst;
1022	u_short port = bootpc_port;
1023	unsigned char *ha;
1024	int len, haf;
1025
1026	/*
1027	 * XXX - Should honor bp_flags "broadcast" bit here.
1028	 * Temporary workaround: use the :ra=ADDR: option to
1029	 * set the reply address to the broadcast address.
1030	 */
1031
1032	/*
1033	 * If the destination address was specified explicitly
1034	 * (i.e. the broadcast address for HP compatiblity)
1035	 * then send the response to that address.  Otherwise,
1036	 * act in accordance with RFC951:
1037	 *   If the client IP address is specified, use that
1038	 * else if gateway IP address is specified, use that
1039	 * else make a temporary arp cache entry for the client's
1040	 * NEW IP/hardware address and use that.
1041	 */
1042	if (dst_override) {
1043		dst.s_addr = dst_override;
1044		if (debug > 1) {
1045			report(LOG_INFO, "reply address override: %s",
1046				   inet_ntoa(dst));
1047		}
1048	} else if (bp->bp_ciaddr.s_addr) {
1049		dst = bp->bp_ciaddr;
1050	} else if (bp->bp_giaddr.s_addr && forward == 0) {
1051		dst = bp->bp_giaddr;
1052		port = bootps_port;
1053		if (debug > 1) {
1054			report(LOG_INFO, "sending reply to gateway %s",
1055				   inet_ntoa(dst));
1056		}
1057	} else {
1058		dst = bp->bp_yiaddr;
1059		ha = bp->bp_chaddr;
1060		len = bp->bp_hlen;
1061		if (len > MAXHADDRLEN)
1062			len = MAXHADDRLEN;
1063		haf = (int) bp->bp_htype;
1064		if (haf == 0)
1065			haf = HTYPE_ETHERNET;
1066
1067		if (debug > 1)
1068			report(LOG_INFO, "setarp %s - %s",
1069				   inet_ntoa(dst), haddrtoa(ha, len));
1070		setarp(s, &dst, haf, ha, len);
1071	}
1072
1073	if ((forward == 0) &&
1074		(bp->bp_siaddr.s_addr == 0))
1075	{
1076		struct ifreq *ifr;
1077		struct in_addr siaddr;
1078		/*
1079		 * If we are originating this reply, we
1080		 * need to find our own interface address to
1081		 * put in the bp_siaddr field of the reply.
1082		 * If this server is multi-homed, pick the
1083		 * 'best' interface (the one on the same net
1084		 * as the client).  Of course, the client may
1085		 * be on the other side of a BOOTP gateway...
1086		 */
1087		ifr = getif(s, &dst);
1088		if (ifr) {
1089			struct sockaddr_in *sip;
1090			sip = (struct sockaddr_in *) &(ifr->ifr_addr);
1091			siaddr = sip->sin_addr;
1092		} else {
1093			/* Just use my "official" IP address. */
1094			siaddr = my_ip_addr;
1095		}
1096
1097		/* XXX - No need to set bp_giaddr here. */
1098
1099		/* Finally, set the server address field. */
1100		bp->bp_siaddr = siaddr;
1101	}
1102	/* Set up socket address for send. */
1103	send_addr.sin_family = AF_INET;
1104	send_addr.sin_port = htons(port);
1105	send_addr.sin_addr = dst;
1106
1107	/* Send reply with same size packet as request used. */
1108	if (sendto(s, pktbuf, pktlen, 0,
1109			   (struct sockaddr *) &send_addr,
1110			   sizeof(send_addr)) < 0)
1111	{
1112		report(LOG_ERR, "sendto: %s", get_network_errmsg());
1113	}
1114} /* sendreply */
1115
1116
1117/* nmatch() - now in getif.c */
1118/* setarp() - now in hwaddr.c */
1119
1120
1121/*
1122 * This call checks read access to a file.  It returns 0 if the file given
1123 * by "path" exists and is publically readable.  A value of -1 is returned if
1124 * access is not permitted or an error occurs.  Successful calls also
1125 * return the file size in bytes using the long pointer "filesize".
1126 *
1127 * The read permission bit for "other" users is checked.  This bit must be
1128 * set for tftpd(8) to allow clients to read the file.
1129 */
1130
1131PRIVATE int
1132chk_access(path, filesize)
1133	char *path;
1134	int32 *filesize;
1135{
1136	struct stat st;
1137
1138	if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) {
1139		*filesize = (int32) st.st_size;
1140		return 0;
1141	} else {
1142		return -1;
1143	}
1144}
1145
1146
1147/*
1148 * Now in dumptab.c :
1149 *	dumptab()
1150 *	dump_host()
1151 *	list_ipaddresses()
1152 */
1153
1154#ifdef VEND_CMU
1155
1156/*
1157 * Insert the CMU "vendor" data for the host pointed to by "hp" into the
1158 * bootp packet pointed to by "bp".
1159 */
1160
1161PRIVATE void
1162dovend_cmu(bp, hp)
1163	struct bootp *bp;
1164	struct host *hp;
1165{
1166	struct cmu_vend *vendp;
1167	struct in_addr_list *taddr;
1168
1169	/*
1170	 * Initialize the entire vendor field to zeroes.
1171	 */
1172	bzero(bp->bp_vend, sizeof(bp->bp_vend));
1173
1174	/*
1175	 * Fill in vendor information. Subnet mask, default gateway,
1176	 * domain name server, ien name server, time server
1177	 */
1178	vendp = (struct cmu_vend *) bp->bp_vend;
1179	strcpy(vendp->v_magic, (char *)vm_cmu);
1180	if (hp->flags.subnet_mask) {
1181		(vendp->v_smask).s_addr = hp->subnet_mask.s_addr;
1182		(vendp->v_flags) |= VF_SMASK;
1183		if (hp->flags.gateway) {
1184			(vendp->v_dgate).s_addr = hp->gateway->addr->s_addr;
1185		}
1186	}
1187	if (hp->flags.domain_server) {
1188		taddr = hp->domain_server;
1189		if (taddr->addrcount > 0) {
1190			(vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr;
1191			if (taddr->addrcount > 1) {
1192				(vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr;
1193			}
1194		}
1195	}
1196	if (hp->flags.name_server) {
1197		taddr = hp->name_server;
1198		if (taddr->addrcount > 0) {
1199			(vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr;
1200			if (taddr->addrcount > 1) {
1201				(vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr;
1202			}
1203		}
1204	}
1205	if (hp->flags.time_server) {
1206		taddr = hp->time_server;
1207		if (taddr->addrcount > 0) {
1208			(vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr;
1209			if (taddr->addrcount > 1) {
1210				(vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr;
1211			}
1212		}
1213	}
1214	/* Log message now done by caller. */
1215} /* dovend_cmu */
1216
1217#endif /* VEND_CMU */
1218
1219
1220
1221/*
1222 * Insert the RFC1048 vendor data for the host pointed to by "hp" into the
1223 * bootp packet pointed to by "bp".
1224 */
1225#define	NEED(LEN, MSG) do \
1226	if (bytesleft < (LEN)) { \
1227		report(LOG_NOTICE, noroom, \
1228			   hp->hostname->string, MSG); \
1229		return; \
1230	} while (0)
1231PRIVATE void
1232dovend_rfc1048(bp, hp, bootsize)
1233	struct bootp *bp;
1234	struct host *hp;
1235	int32 bootsize;
1236{
1237	int bytesleft, len;
1238	byte *vp;
1239
1240	static char noroom[] = "%s: No room for \"%s\" option";
1241
1242	vp = bp->bp_vend;
1243
1244	if (hp->flags.msg_size) {
1245		pktlen = hp->msg_size;
1246	} else {
1247		/*
1248		 * If the request was longer than the official length, build
1249		 * a response of that same length where the additional length
1250		 * is assumed to be part of the bp_vend (options) area.
1251		 */
1252		if (pktlen > sizeof(*bp)) {
1253			if (debug > 1)
1254				report(LOG_INFO, "request message length=%d", pktlen);
1255		}
1256		/*
1257		 * Check whether the request contains the option:
1258		 * Maximum DHCP Message Size (RFC1533 sec. 9.8)
1259		 * and if so, override the response length with its value.
1260		 * This request must lie within the first BP_VEND_LEN
1261		 * bytes of the option space.
1262		 */
1263		{
1264			byte *p, *ep;
1265			byte tag, len;
1266			short msgsz = 0;
1267
1268			p = vp + 4;
1269			ep = p + BP_VEND_LEN - 4;
1270			while (p < ep) {
1271				tag = *p++;
1272				/* Check for tags with no data first. */
1273				if (tag == TAG_PAD)
1274					continue;
1275				if (tag == TAG_END)
1276					break;
1277				/* Now scan the length byte. */
1278				len = *p++;
1279				switch (tag) {
1280				case TAG_MAX_MSGSZ:
1281					if (len == 2) {
1282						bcopy(p, (char*)&msgsz, 2);
1283						msgsz = ntohs(msgsz);
1284					}
1285					break;
1286				case TAG_SUBNET_MASK:
1287					/* XXX - Should preserve this if given... */
1288					break;
1289				} /* swtich */
1290				p += len;
1291			}
1292
1293			if (msgsz > sizeof(*bp)) {
1294				if (debug > 1)
1295					report(LOG_INFO, "request has DHCP msglen=%d", msgsz);
1296				pktlen = msgsz;
1297			}
1298		}
1299	}
1300
1301	if (pktlen < sizeof(*bp)) {
1302		report(LOG_ERR, "invalid response length=%d", pktlen);
1303		pktlen = sizeof(*bp);
1304	}
1305	bytesleft = ((byte*)bp + pktlen) - vp;
1306	if (pktlen > sizeof(*bp)) {
1307		if (debug > 1)
1308			report(LOG_INFO, "extended reply, length=%d, options=%d",
1309				   pktlen, bytesleft);
1310	}
1311
1312	/* Copy in the magic cookie */
1313	bcopy(vm_rfc1048, vp, 4);
1314	vp += 4;
1315	bytesleft -= 4;
1316
1317	if (hp->flags.subnet_mask) {
1318		/* always enough room here. */
1319		*vp++ = TAG_SUBNET_MASK;/* -1 byte  */
1320		*vp++ = 4;				/* -1 byte  */
1321		insert_u_long(hp->subnet_mask.s_addr, &vp);	/* -4 bytes */
1322		bytesleft -= 6;			/* Fix real count */
1323		if (hp->flags.gateway) {
1324			(void) insert_ip(TAG_GATEWAY,
1325							 hp->gateway,
1326							 &vp, &bytesleft);
1327		}
1328	}
1329	if (hp->flags.bootsize) {
1330		/* always enough room here */
1331		bootsize = (hp->flags.bootsize_auto) ?
1332			((bootsize + 511) / 512) : (hp->bootsize);	/* Round up */
1333		*vp++ = TAG_BOOT_SIZE;
1334		*vp++ = 2;
1335		*vp++ = (byte) ((bootsize >> 8) & 0xFF);
1336		*vp++ = (byte) (bootsize & 0xFF);
1337		bytesleft -= 4;			/* Tag, length, and 16 bit blocksize */
1338	}
1339	/*
1340	 * This one is special: Remaining options go in the ext file.
1341	 * Only the subnet_mask, bootsize, and gateway should precede.
1342	 */
1343	if (hp->flags.exten_file) {
1344		/*
1345		 * Check for room for exten_file.  Add 3 to account for
1346		 * TAG_EXTEN_FILE, length, and TAG_END.
1347		 */
1348		len = strlen(hp->exten_file->string);
1349		NEED((len + 3), "ef");
1350		*vp++ = TAG_EXTEN_FILE;
1351		*vp++ = (byte) (len & 0xFF);
1352		bcopy(hp->exten_file->string, vp, len);
1353		vp += len;
1354		*vp++ = TAG_END;
1355		bytesleft -= len + 3;
1356		return;					/* no more options here. */
1357	}
1358	/*
1359	 * The remaining options are inserted by the following
1360	 * function (which is shared with bootpef.c).
1361	 * Keep back one byte for the TAG_END.
1362	 */
1363	len = dovend_rfc1497(hp, vp, bytesleft - 1);
1364	vp += len;
1365	bytesleft -= len;
1366
1367	/* There should be at least one byte left. */
1368	NEED(1, "(end)");
1369	*vp++ = TAG_END;
1370	bytesleft--;
1371
1372	/* Log message done by caller. */
1373	if (bytesleft > 0) {
1374		/*
1375		 * Zero out any remaining part of the vendor area.
1376		 */
1377		bzero(vp, bytesleft);
1378	}
1379} /* dovend_rfc1048 */
1380#undef	NEED
1381
1382
1383/*
1384 * Now in readfile.c:
1385 * 	hwlookcmp()
1386 *	iplookcmp()
1387 */
1388
1389/* haddrtoa() - now in hwaddr.c */
1390/*
1391 * Now in dovend.c:
1392 * insert_ip()
1393 * insert_generic()
1394 * insert_u_long()
1395 */
1396
1397/* get_errmsg() - now in report.c */
1398
1399/*
1400 * Local Variables:
1401 * tab-width: 4
1402 * c-indent-level: 4
1403 * c-argdecl-indent: 4
1404 * c-continued-statement-offset: 4
1405 * c-continued-brace-offset: -4
1406 * c-label-offset: -4
1407 * c-brace-offset: 0
1408 * End:
1409 */
1410