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