1/************************************************************************
2          Copyright 1988, 1991 by Carnegie Mellon University
3
4                          All Rights Reserved
5
6Permission to use, copy, modify, and distribute this software and its
7documentation for any purpose and without fee is hereby granted, provided
8that the above copyright notice appear in all copies and that both that
9copyright notice and this permission notice appear in supporting
10documentation, and that the name of Carnegie Mellon University not be used
11in advertising or publicity pertaining to distribution of the software
12without specific, written prior permission.
13
14CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
15SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
16IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
17DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
18PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
19ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20SOFTWARE.
21
22************************************************************************/
23
24/*
25 * BOOTP (bootstrap protocol) server daemon.
26 *
27 * Answers BOOTP request packets from booting client machines.
28 * See [SRI-NIC]<RFC>RFC951.TXT for a description of the protocol.
29 * See [SRI-NIC]<RFC>RFC1048.TXT for vendor-information extensions.
30 * See RFC 1395 for option tags 14-17.
31 * See accompanying man page -- bootpd.8
32 *
33 * HISTORY
34 *	See ./Changes
35 *
36 * BUGS
37 *	See ./ToDo
38 */
39
40#include <sys/types.h>
41#include <sys/param.h>
42#include <sys/socket.h>
43#include <sys/ioctl.h>
44#include <sys/file.h>
45#include <sys/time.h>
46#include <sys/stat.h>
47#include <sys/utsname.h>
48
49#include <net/if.h>
50#include <netinet/in.h>
51#include <arpa/inet.h>	/* inet_ntoa */
52
53#ifndef	NO_UNISTD
54#include <unistd.h>
55#endif
56
57#include <stdlib.h>
58#include <signal.h>
59#include <stdio.h>
60#include <string.h>
61#include <errno.h>
62#include <ctype.h>
63#include <netdb.h>
64#include <paths.h>
65#include <syslog.h>
66#include <assert.h>
67#include <inttypes.h>
68
69#ifdef	NO_SETSID
70# include <fcntl.h>		/* for O_RDONLY, etc */
71#endif
72
73#include "bootp.h"
74#include "hash.h"
75#include "hwaddr.h"
76#include "bootpd.h"
77#include "dovend.h"
78#include "getif.h"
79#include "readfile.h"
80#include "report.h"
81#include "tzone.h"
82#include "patchlevel.h"
83
84#ifndef CONFIG_FILE
85#define CONFIG_FILE		"/etc/bootptab"
86#endif
87#ifndef DUMPTAB_FILE
88#define DUMPTAB_FILE		"/tmp/bootpd.dump"
89#endif
90
91
92
93/*
94 * Externals, forward declarations, and global variables
95 */
96
97extern void dumptab(char *);
98
99PRIVATE void catcher(int);
100PRIVATE int chk_access(char *, int32 *);
101#ifdef VEND_CMU
102PRIVATE void dovend_cmu(struct bootp *, struct host *);
103#endif
104PRIVATE void dovend_rfc1048(struct bootp *, struct host *, int32);
105PRIVATE void handle_reply(void);
106PRIVATE void handle_request(void);
107PRIVATE void sendreply(int forward, int32 dest_override);
108PRIVATE void usage(void);
109
110/*
111 * IP port numbers for client and server obtained from /etc/services
112 */
113
114u_short bootps_port, bootpc_port;
115
116
117/*
118 * Internet socket and interface config structures
119 */
120
121struct sockaddr_in bind_addr;	/* Listening */
122struct sockaddr_in recv_addr;	/* Packet source */
123struct sockaddr_in send_addr;	/*  destination */
124
125
126/*
127 * option defaults
128 */
129int debug = 0;					/* Debugging flag (level) */
130struct timeval actualtimeout =
131{								/* fifteen minutes */
132	15 * 60L,					/* tv_sec */
133	0							/* tv_usec */
134};
135int arpmod = TRUE;				/* modify the ARP table */
136
137/*
138 * General
139 */
140
141int s;							/* Socket file descriptor */
142char *pktbuf;					/* Receive packet buffer */
143int pktlen;
144char *progname;
145char *chdir_path;
146struct in_addr my_ip_addr;
147
148static const char *hostname;
149static char default_hostname[MAXHOSTNAMELEN];
150
151/* Flags set by signal catcher. */
152PRIVATE int do_readtab = 0;
153PRIVATE int do_dumptab = 0;
154
155/*
156 * Globals below are associated with the bootp database file (bootptab).
157 */
158
159char *bootptab = CONFIG_FILE;
160char *bootpd_dump = DUMPTAB_FILE;
161
162
163
164/*
165 * Initialization such as command-line processing is done and then the
166 * main server loop is started.
167 */
168
169int
170main(int argc, char **argv)
171{
172	struct timeval *timeout;
173	struct bootp *bp;
174	struct servent *servp;
175	struct hostent *hep;
176	char *stmp;
177	socklen_t ba_len, ra_len;
178	int n;
179	int nfound;
180	fd_set readfds;
181	int standalone;
182#ifdef	SA_NOCLDSTOP	/* Have POSIX sigaction(2). */
183	struct sigaction sa;
184#endif
185
186	progname = strrchr(argv[0], '/');
187	if (progname) progname++;
188	else progname = argv[0];
189
190	/*
191	 * Initialize logging.
192	 */
193	report_init(0);				/* uses progname */
194
195	/*
196	 * Log startup
197	 */
198	report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
199
200	/* Debugging for compilers with struct padding. */
201	assert(sizeof(struct bootp) == BP_MINPKTSZ);
202
203	/* Get space for receiving packets and composing replies. */
204	pktbuf = malloc(MAX_MSG_SIZE);
205	if (!pktbuf) {
206		report(LOG_ERR, "malloc failed");
207		exit(1);
208	}
209	bp = (struct bootp *) pktbuf;
210
211	/*
212	 * Check to see if a socket was passed to us from inetd.
213	 *
214	 * Use getsockname() to determine if descriptor 0 is indeed a socket
215	 * (and thus we are probably a child of inetd) or if it is instead
216	 * something else and we are running standalone.
217	 */
218	s = 0;
219	ba_len = sizeof(bind_addr);
220	bzero((char *) &bind_addr, ba_len);
221	errno = 0;
222	standalone = TRUE;
223	if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {
224		/*
225		 * Descriptor 0 is a socket.  Assume we are a child of inetd.
226		 */
227		if (bind_addr.sin_family == AF_INET) {
228			standalone = FALSE;
229			bootps_port = ntohs(bind_addr.sin_port);
230		} else {
231			/* Some other type of socket? */
232			report(LOG_ERR, "getsockname: not an INET socket");
233		}
234	}
235
236	/*
237	 * Set defaults that might be changed by option switches.
238	 */
239	stmp = NULL;
240	timeout = &actualtimeout;
241
242	if (gethostname(default_hostname, sizeof(default_hostname) - 1) < 0) {
243		report(LOG_ERR, "bootpd: can't get hostname\n");
244		exit(1);
245	}
246	default_hostname[sizeof(default_hostname) - 1] = '\0';
247	hostname = default_hostname;
248
249	/*
250	 * Read switches.
251	 */
252	for (argc--, argv++; argc > 0; argc--, argv++) {
253		if (argv[0][0] != '-')
254			break;
255		switch (argv[0][1]) {
256
257		case 'a':				/* don't modify the ARP table */
258			arpmod = FALSE;
259			break;
260		case 'c':				/* chdir_path */
261			if (argv[0][2]) {
262				stmp = &(argv[0][2]);
263			} else {
264				argc--;
265				argv++;
266				stmp = argv[0];
267			}
268			if (!stmp || (stmp[0] != '/')) {
269				report(LOG_ERR,
270						"bootpd: invalid chdir specification\n");
271				break;
272			}
273			chdir_path = stmp;
274			break;
275
276		case 'd':				/* debug level */
277			if (argv[0][2]) {
278				stmp = &(argv[0][2]);
279			} else if (argv[1] && argv[1][0] == '-') {
280				/*
281				 * Backwards-compatible behavior:
282				 * no parameter, so just increment the debug flag.
283				 */
284				debug++;
285				break;
286			} else {
287				argc--;
288				argv++;
289				stmp = argv[0];
290			}
291			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
292				report(LOG_ERR,
293						"%s: invalid debug level\n", progname);
294				break;
295			}
296			debug = n;
297			break;
298
299		case 'h':				/* override hostname */
300			if (argv[0][2]) {
301				stmp = &(argv[0][2]);
302			} else {
303				argc--;
304				argv++;
305				stmp = argv[0];
306			}
307			if (!stmp) {
308				report(LOG_ERR,
309						"bootpd: missing hostname\n");
310				break;
311			}
312			hostname = stmp;
313			break;
314
315		case 'i':				/* inetd mode */
316			standalone = FALSE;
317			break;
318
319		case 's':				/* standalone mode */
320			standalone = TRUE;
321			break;
322
323		case 't':				/* timeout */
324			if (argv[0][2]) {
325				stmp = &(argv[0][2]);
326			} else {
327				argc--;
328				argv++;
329				stmp = argv[0];
330			}
331			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
332				report(LOG_ERR,
333						"%s: invalid timeout specification\n", progname);
334				break;
335			}
336			actualtimeout.tv_sec = (int32) (60 * n);
337			/*
338			 * If the actual timeout is zero, pass a NULL pointer
339			 * to select so it blocks indefinitely, otherwise,
340			 * point to the actual timeout value.
341			 */
342			timeout = (n > 0) ? &actualtimeout : NULL;
343			break;
344
345		default:
346			report(LOG_ERR, "%s: unknown switch: -%c\n",
347					progname, argv[0][1]);
348			usage();
349			break;
350
351		} /* switch */
352	} /* for args */
353
354	/*
355	 * Override default file names if specified on the command line.
356	 */
357	if (argc > 0)
358		bootptab = argv[0];
359
360	if (argc > 1)
361		bootpd_dump = argv[1];
362
363	/*
364	 * Get my hostname and IP address.
365	 */
366
367	hep = gethostbyname(hostname);
368	if (!hep) {
369		report(LOG_ERR, "Can not get my IP address\n");
370		exit(1);
371	}
372	bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
373
374	if (standalone) {
375		/*
376		 * Go into background and disassociate from controlling terminal.
377		 */
378		if (debug < 3) {
379			if (fork())
380				exit(0);
381#ifdef	NO_SETSID
382			setpgrp(0,0);
383#ifdef TIOCNOTTY
384			n = open(_PATH_TTY, O_RDWR);
385			if (n >= 0) {
386				ioctl(n, TIOCNOTTY, (char *) 0);
387				(void) close(n);
388			}
389#endif	/* TIOCNOTTY */
390#else	/* SETSID */
391			if (setsid() < 0)
392				perror("setsid");
393#endif	/* SETSID */
394		} /* if debug < 3 */
395
396		/*
397		 * Nuke any timeout value
398		 */
399		timeout = NULL;
400
401	} /* if standalone (1st) */
402
403	/* Set the cwd (i.e. to /tftpboot) */
404	if (chdir_path) {
405		if (chdir(chdir_path) < 0)
406			report(LOG_ERR, "%s: chdir failed", chdir_path);
407	}
408
409	/* Get the timezone. */
410	tzone_init();
411
412	/* Allocate hash tables. */
413	rdtab_init();
414
415	/*
416	 * Read the bootptab file.
417	 */
418	readtab(1);					/* force read */
419
420	if (standalone) {
421
422		/*
423		 * Create a socket.
424		 */
425		if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
426			report(LOG_ERR, "socket: %s", get_network_errmsg());
427			exit(1);
428		}
429
430		/*
431		 * Get server's listening port number
432		 */
433		servp = getservbyname("bootps", "udp");
434		if (servp) {
435			bootps_port = ntohs((u_short) servp->s_port);
436		} else {
437			bootps_port = (u_short) IPPORT_BOOTPS;
438			report(LOG_ERR,
439				"bootps/udp: unknown service -- using port %d",
440				   bootps_port);
441		}
442
443		/*
444		 * Bind socket to BOOTPS port.
445		 */
446		bind_addr.sin_family = AF_INET;
447		bind_addr.sin_addr.s_addr = INADDR_ANY;
448		bind_addr.sin_port = htons(bootps_port);
449		if (bind(s, (struct sockaddr *) &bind_addr,
450				 sizeof(bind_addr)) < 0)
451		{
452			report(LOG_ERR, "bind: %s", get_network_errmsg());
453			exit(1);
454		}
455	} /* if standalone (2nd)*/
456
457	/*
458	 * Get destination port number so we can reply to client
459	 */
460	servp = getservbyname("bootpc", "udp");
461	if (servp) {
462		bootpc_port = ntohs(servp->s_port);
463	} else {
464		report(LOG_ERR,
465			   "bootpc/udp: unknown service -- using port %d",
466			   IPPORT_BOOTPC);
467		bootpc_port = (u_short) IPPORT_BOOTPC;
468	}
469
470	/*
471	 * Set up signals to read or dump the table.
472	 */
473#ifdef	SA_NOCLDSTOP	/* Have POSIX sigaction(2). */
474	sa.sa_handler = catcher;
475	sigemptyset(&sa.sa_mask);
476	sa.sa_flags = 0;
477	if (sigaction(SIGHUP, &sa, NULL) < 0) {
478		report(LOG_ERR, "sigaction: %s", get_errmsg());
479		exit(1);
480	}
481	if (sigaction(SIGUSR1, &sa, NULL) < 0) {
482		report(LOG_ERR, "sigaction: %s", get_errmsg());
483		exit(1);
484	}
485#else	/* SA_NOCLDSTOP */
486	/* Old-fashioned UNIX signals */
487	if ((int) signal(SIGHUP, catcher) < 0) {
488		report(LOG_ERR, "signal: %s", get_errmsg());
489		exit(1);
490	}
491	if ((int) signal(SIGUSR1, catcher) < 0) {
492		report(LOG_ERR, "signal: %s", get_errmsg());
493		exit(1);
494	}
495#endif	/* SA_NOCLDSTOP */
496
497	/*
498	 * Process incoming requests.
499	 */
500	FD_ZERO(&readfds);
501	for (;;) {
502		struct timeval tv;
503
504		FD_SET(s, &readfds);
505		if (timeout)
506			tv = *timeout;
507
508		nfound = select(s + 1, &readfds, NULL, NULL,
509						(timeout) ? &tv : NULL);
510		if (nfound < 0) {
511			if (errno != EINTR) {
512				report(LOG_ERR, "select: %s", get_errmsg());
513			}
514			/*
515			 * Call readtab() or dumptab() here to avoid the
516			 * dangers of doing I/O from a signal handler.
517			 */
518			if (do_readtab) {
519				do_readtab = 0;
520				readtab(1);		/* force read */
521			}
522			if (do_dumptab) {
523				do_dumptab = 0;
524				dumptab(bootpd_dump);
525			}
526			continue;
527		}
528		if (!FD_ISSET(s, &readfds)) {
529			if (debug > 1)
530				report(LOG_INFO, "exiting after %jd minutes of inactivity",
531					   (intmax_t)actualtimeout.tv_sec / 60);
532			exit(0);
533		}
534		ra_len = sizeof(recv_addr);
535		n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
536					 (struct sockaddr *) &recv_addr, &ra_len);
537		if (n <= 0) {
538			continue;
539		}
540		if (debug > 1) {
541			report(LOG_INFO, "recvd pkt from IP addr %s",
542				   inet_ntoa(recv_addr.sin_addr));
543		}
544		if (n < sizeof(struct bootp)) {
545			if (debug) {
546				report(LOG_NOTICE, "received short packet");
547			}
548			continue;
549		}
550		pktlen = n;
551
552		readtab(0);				/* maybe re-read bootptab */
553
554		switch (bp->bp_op) {
555		case BOOTREQUEST:
556			handle_request();
557			break;
558		case BOOTREPLY:
559			handle_reply();
560			break;
561		}
562	}
563	return 0;
564}
565
566
567
568
569/*
570 * Print "usage" message and exit
571 */
572
573PRIVATE void
574usage()
575{
576	fprintf(stderr,
577		"usage: bootpd [-a] [-i | -s] [-c chdir-path] [-d level] [-h hostname]\n"
578		"              [-t timeout] [bootptab [dumpfile]]\n");
579	fprintf(stderr, "       -a\tdon't modify ARP table\n");
580	fprintf(stderr, "       -c n\tset current directory\n");
581	fprintf(stderr, "       -d n\tset debug level\n");
582	fprintf(stderr, "       -h n\tset the hostname to listen on\n");
583	fprintf(stderr, "       -i\tforce inetd mode (run as child of inetd)\n");
584	fprintf(stderr, "       -s\tforce standalone mode (run without inetd)\n");
585	fprintf(stderr, "       -t n\tset inetd exit timeout to n minutes\n");
586	exit(1);
587}
588
589/* Signal catchers */
590PRIVATE void
591catcher(int sig)
592{
593	if (sig == SIGHUP)
594		do_readtab = 1;
595	if (sig == SIGUSR1)
596		do_dumptab = 1;
597#if	!defined(SA_NOCLDSTOP) && defined(SYSV)
598	/* For older "System V" derivatives with no sigaction(). */
599	signal(sig, catcher);
600#endif
601}
602
603
604
605/*
606 * Process BOOTREQUEST packet.
607 *
608 * Note:  This version of the bootpd.c server never forwards
609 * a request to another server.  That is the job of a gateway
610 * program such as the "bootpgw" program included here.
611 *
612 * (Also this version does not interpret the hostname field of
613 * the request packet;  it COULD do a name->address lookup and
614 * forward the request there.)
615 */
616PRIVATE void
617handle_request(void)
618{
619	struct bootp *bp = (struct bootp *) pktbuf;
620	struct host *hp = NULL;
621	struct host dummyhost;
622	int32 bootsize = 0;
623	unsigned hlen, hashcode;
624	int32 dest;
625	char realpath[1024];
626	char *clntpath;
627	char *homedir, *bootfile;
628	int n;
629
630	if (bp->bp_htype >= hwinfocnt) {
631		report(LOG_NOTICE, "bad hw addr type %u", bp->bp_htype);
632		return;
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 doesn't 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 %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(void)
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(int forward, int32 dst_override)
1011{
1012	struct bootp *bp = (struct bootp *) pktbuf;
1013	struct in_addr dst;
1014	u_short port = bootpc_port;
1015	unsigned char *ha;
1016	int len, haf;
1017
1018	/*
1019	 * XXX - Should honor bp_flags "broadcast" bit here.
1020	 * Temporary workaround: use the :ra=ADDR: option to
1021	 * set the reply address to the broadcast address.
1022	 */
1023
1024	/*
1025	 * If the destination address was specified explicitly
1026	 * (i.e. the broadcast address for HP compatibility)
1027	 * then send the response to that address.  Otherwise,
1028	 * act in accordance with RFC951:
1029	 *   If the client IP address is specified, use that
1030	 * else if gateway IP address is specified, use that
1031	 * else make a temporary arp cache entry for the client's
1032	 * NEW IP/hardware address and use that.
1033	 */
1034	if (dst_override) {
1035		dst.s_addr = dst_override;
1036		if (debug > 1) {
1037			report(LOG_INFO, "reply address override: %s",
1038				   inet_ntoa(dst));
1039		}
1040	} else if (bp->bp_ciaddr.s_addr) {
1041		dst = bp->bp_ciaddr;
1042	} else if (bp->bp_giaddr.s_addr && forward == 0) {
1043		dst = bp->bp_giaddr;
1044		port = bootps_port;
1045		if (debug > 1) {
1046			report(LOG_INFO, "sending reply to gateway %s",
1047				   inet_ntoa(dst));
1048		}
1049	} else {
1050		dst = bp->bp_yiaddr;
1051		ha = bp->bp_chaddr;
1052		len = bp->bp_hlen;
1053		if (len > MAXHADDRLEN)
1054			len = MAXHADDRLEN;
1055		haf = (int) bp->bp_htype;
1056		if (haf == 0)
1057			haf = HTYPE_ETHERNET;
1058
1059		if (arpmod) {
1060			if (debug > 1)
1061				report(LOG_INFO, "setarp %s - %s",
1062					   inet_ntoa(dst), haddrtoa(ha, len));
1063			setarp(s, &dst, haf, ha, len);
1064		}
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 publicly 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(char *path, int32 *filesize)
1127{
1128	struct stat st;
1129
1130	if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) {
1131		*filesize = (int32) st.st_size;
1132		return 0;
1133	} else {
1134		return -1;
1135	}
1136}
1137
1138
1139/*
1140 * Now in dumptab.c :
1141 *	dumptab()
1142 *	dump_host()
1143 *	list_ipaddresses()
1144 */
1145
1146#ifdef VEND_CMU
1147
1148/*
1149 * Insert the CMU "vendor" data for the host pointed to by "hp" into the
1150 * bootp packet pointed to by "bp".
1151 */
1152
1153PRIVATE void
1154dovend_cmu(struct bootp *bp, struct host *hp)
1155{
1156	struct cmu_vend *vendp;
1157	struct in_addr_list *taddr;
1158
1159	/*
1160	 * Initialize the entire vendor field to zeroes.
1161	 */
1162	bzero(bp->bp_vend, sizeof(bp->bp_vend));
1163
1164	/*
1165	 * Fill in vendor information. Subnet mask, default gateway,
1166	 * domain name server, ien name server, time server
1167	 */
1168	vendp = (struct cmu_vend *) bp->bp_vend;
1169	strcpy(vendp->v_magic, (char *)vm_cmu);
1170	if (hp->flags.subnet_mask) {
1171		(vendp->v_smask).s_addr = hp->subnet_mask.s_addr;
1172		(vendp->v_flags) |= VF_SMASK;
1173		if (hp->flags.gateway) {
1174			(vendp->v_dgate).s_addr = hp->gateway->addr->s_addr;
1175		}
1176	}
1177	if (hp->flags.domain_server) {
1178		taddr = hp->domain_server;
1179		if (taddr->addrcount > 0) {
1180			(vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr;
1181			if (taddr->addrcount > 1) {
1182				(vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr;
1183			}
1184		}
1185	}
1186	if (hp->flags.name_server) {
1187		taddr = hp->name_server;
1188		if (taddr->addrcount > 0) {
1189			(vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr;
1190			if (taddr->addrcount > 1) {
1191				(vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr;
1192			}
1193		}
1194	}
1195	if (hp->flags.time_server) {
1196		taddr = hp->time_server;
1197		if (taddr->addrcount > 0) {
1198			(vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr;
1199			if (taddr->addrcount > 1) {
1200				(vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr;
1201			}
1202		}
1203	}
1204	/* Log message now done by caller. */
1205} /* dovend_cmu */
1206
1207#endif /* VEND_CMU */
1208
1209
1210
1211/*
1212 * Insert the RFC1048 vendor data for the host pointed to by "hp" into the
1213 * bootp packet pointed to by "bp".
1214 */
1215#define	NEED(LEN, MSG) do \
1216	if (bytesleft < (LEN)) { \
1217		report(LOG_NOTICE, noroom, \
1218			   hp->hostname->string, MSG); \
1219		return; \
1220	} while (0)
1221PRIVATE void
1222dovend_rfc1048(struct bootp *bp, struct host *hp, int32 bootsize)
1223{
1224	int bytesleft, len;
1225	byte *vp;
1226
1227	static const char noroom[] = "%s: No room for \"%s\" option";
1228
1229	vp = bp->bp_vend;
1230
1231	if (hp->flags.msg_size) {
1232		pktlen = hp->msg_size;
1233	} else {
1234		/*
1235		 * If the request was longer than the official length, build
1236		 * a response of that same length where the additional length
1237		 * is assumed to be part of the bp_vend (options) area.
1238		 */
1239		if (pktlen > sizeof(*bp)) {
1240			if (debug > 1)
1241				report(LOG_INFO, "request message length=%d", pktlen);
1242		}
1243		/*
1244		 * Check whether the request contains the option:
1245		 * Maximum DHCP Message Size (RFC1533 sec. 9.8)
1246		 * and if so, override the response length with its value.
1247		 * This request must lie within the first BP_VEND_LEN
1248		 * bytes of the option space.
1249		 */
1250		{
1251			byte *p, *ep;
1252			byte tag, len;
1253			short msgsz = 0;
1254
1255			p = vp + 4;
1256			ep = p + BP_VEND_LEN - 4;
1257			while (p < ep) {
1258				tag = *p++;
1259				/* Check for tags with no data first. */
1260				if (tag == TAG_PAD)
1261					continue;
1262				if (tag == TAG_END)
1263					break;
1264				/* Now scan the length byte. */
1265				len = *p++;
1266				switch (tag) {
1267				case TAG_MAX_MSGSZ:
1268					if (len == 2) {
1269						bcopy(p, (char*)&msgsz, 2);
1270						msgsz = ntohs(msgsz);
1271					}
1272					break;
1273				case TAG_SUBNET_MASK:
1274					/* XXX - Should preserve this if given... */
1275					break;
1276				} /* swtich */
1277				p += len;
1278			}
1279
1280			if (msgsz > sizeof(*bp) + BP_MSG_OVERHEAD) {
1281				if (debug > 1)
1282					report(LOG_INFO, "request has DHCP msglen=%d", msgsz);
1283				pktlen = msgsz - BP_MSG_OVERHEAD;
1284			}
1285		}
1286	}
1287
1288	if (pktlen < sizeof(*bp)) {
1289		report(LOG_ERR, "invalid response length=%d", pktlen);
1290		pktlen = sizeof(*bp);
1291	}
1292	bytesleft = ((byte*)bp + pktlen) - vp;
1293	if (pktlen > sizeof(*bp)) {
1294		if (debug > 1)
1295			report(LOG_INFO, "extended reply, length=%d, options=%d",
1296				   pktlen, bytesleft);
1297	}
1298
1299	/* Copy in the magic cookie */
1300	bcopy(vm_rfc1048, vp, 4);
1301	vp += 4;
1302	bytesleft -= 4;
1303
1304	if (hp->flags.subnet_mask) {
1305		/* always enough room here. */
1306		*vp++ = TAG_SUBNET_MASK;/* -1 byte  */
1307		*vp++ = 4;				/* -1 byte  */
1308		insert_u_long(hp->subnet_mask.s_addr, &vp);	/* -4 bytes */
1309		bytesleft -= 6;			/* Fix real count */
1310		if (hp->flags.gateway) {
1311			(void) insert_ip(TAG_GATEWAY,
1312							 hp->gateway,
1313							 &vp, &bytesleft);
1314		}
1315	}
1316	if (hp->flags.bootsize) {
1317		/* always enough room here */
1318		bootsize = (hp->flags.bootsize_auto) ?
1319			((bootsize + 511) / 512) : (hp->bootsize);	/* Round up */
1320		*vp++ = TAG_BOOT_SIZE;
1321		*vp++ = 2;
1322		*vp++ = (byte) ((bootsize >> 8) & 0xFF);
1323		*vp++ = (byte) (bootsize & 0xFF);
1324		bytesleft -= 4;			/* Tag, length, and 16 bit blocksize */
1325	}
1326	/*
1327	 * This one is special: Remaining options go in the ext file.
1328	 * Only the subnet_mask, bootsize, and gateway should precede.
1329	 */
1330	if (hp->flags.exten_file) {
1331		/*
1332		 * Check for room for exten_file.  Add 3 to account for
1333		 * TAG_EXTEN_FILE, length, and TAG_END.
1334		 */
1335		len = strlen(hp->exten_file->string);
1336		NEED((len + 3), "ef");
1337		*vp++ = TAG_EXTEN_FILE;
1338		*vp++ = (byte) (len & 0xFF);
1339		bcopy(hp->exten_file->string, vp, len);
1340		vp += len;
1341		*vp++ = TAG_END;
1342		bytesleft -= len + 3;
1343		return;					/* no more options here. */
1344	}
1345	/*
1346	 * The remaining options are inserted by the following
1347	 * function (which is shared with bootpef.c).
1348	 * Keep back one byte for the TAG_END.
1349	 */
1350	len = dovend_rfc1497(hp, vp, bytesleft - 1);
1351	vp += len;
1352	bytesleft -= len;
1353
1354	/* There should be at least one byte left. */
1355	NEED(1, "(end)");
1356	*vp++ = TAG_END;
1357	bytesleft--;
1358
1359	/* Log message done by caller. */
1360	if (bytesleft > 0) {
1361		/*
1362		 * Zero out any remaining part of the vendor area.
1363		 */
1364		bzero(vp, bytesleft);
1365	}
1366} /* dovend_rfc1048 */
1367#undef	NEED
1368
1369
1370/*
1371 * Now in readfile.c:
1372 * 	hwlookcmp()
1373 *	iplookcmp()
1374 */
1375
1376/* haddrtoa() - now in hwaddr.c */
1377/*
1378 * Now in dovend.c:
1379 * insert_ip()
1380 * insert_generic()
1381 * insert_u_long()
1382 */
1383
1384/* get_errmsg() - now in report.c */
1385
1386/*
1387 * Local Variables:
1388 * tab-width: 4
1389 * c-indent-level: 4
1390 * c-argdecl-indent: 4
1391 * c-continued-statement-offset: 4
1392 * c-continued-brace-offset: -4
1393 * c-label-offset: -4
1394 * c-brace-offset: 0
1395 * End:
1396 */
1397