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