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