bootpd.c revision 13575
190792Sgshapiro/************************************************************************
2261194Sgshapiro          Copyright 1988, 1991 by Carnegie Mellon University
390792Sgshapiro
490792Sgshapiro                          All Rights Reserved
590792Sgshapiro
690792SgshapiroPermission to use, copy, modify, and distribute this software and its
790792Sgshapirodocumentation for any purpose and without fee is hereby granted, provided
890792Sgshapirothat the above copyright notice appear in all copies and that both that
990792Sgshapirocopyright notice and this permission notice appear in supporting
1090792Sgshapirodocumentation, and that the name of Carnegie Mellon University not be used
1190792Sgshapiroin advertising or publicity pertaining to distribution of the software
1290792Sgshapirowithout specific, written prior permission.
1390792Sgshapiro
1490792SgshapiroCARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
1590792SgshapiroSOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
16266527SgshapiroIN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
1790792SgshapiroDAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
1890792SgshapiroPROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
1990792SgshapiroACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
2090792SgshapiroSOFTWARE.
2190792Sgshapiro************************************************************************/
2290792Sgshapiro
2390792Sgshapiro/*
2490792Sgshapiro * BOOTP (bootstrap protocol) server daemon.
2590792Sgshapiro *
2690792Sgshapiro * Answers BOOTP request packets from booting client machines.
2790792Sgshapiro * See [SRI-NIC]<RFC>RFC951.TXT for a description of the protocol.
2890792Sgshapiro * See [SRI-NIC]<RFC>RFC1048.TXT for vendor-information extensions.
2990792Sgshapiro * See RFC 1395 for option tags 14-17.
3090792Sgshapiro * See accompanying man page -- bootpd.8
3190792Sgshapiro *
3290792Sgshapiro * HISTORY
3390792Sgshapiro *	See ./Changes
3490792Sgshapiro *
3590792Sgshapiro * BUGS
3690792Sgshapiro *	See ./ToDo
3790792Sgshapiro */
3890792Sgshapiro
3990792Sgshapiro
4090792Sgshapiro
4190792Sgshapiro#include <sys/types.h>
4290792Sgshapiro#include <sys/param.h>
4390792Sgshapiro#include <sys/socket.h>
4490792Sgshapiro#include <sys/ioctl.h>
4590792Sgshapiro#include <sys/file.h>
4690792Sgshapiro#include <sys/time.h>
4790792Sgshapiro#include <sys/stat.h>
4890792Sgshapiro#include <sys/utsname.h>
4990792Sgshapiro
5090792Sgshapiro#include <net/if.h>
5190792Sgshapiro#include <netinet/in.h>
5290792Sgshapiro#include <arpa/inet.h>	/* inet_ntoa */
5390792Sgshapiro
5490792Sgshapiro#ifndef	NO_UNISTD
5590792Sgshapiro#include <unistd.h>
5690792Sgshapiro#endif
5790792Sgshapiro
5890792Sgshapiro#include <stdlib.h>
5990792Sgshapiro#include <signal.h>
6090792Sgshapiro#include <stdio.h>
6190792Sgshapiro#include <string.h>
6290792Sgshapiro#include <errno.h>
6390792Sgshapiro#include <ctype.h>
6490792Sgshapiro#include <netdb.h>
6590792Sgshapiro#include <syslog.h>
6690792Sgshapiro#include <assert.h>
6790792Sgshapiro
6890792Sgshapiro#ifdef	NO_SETSID
6990792Sgshapiro# include <fcntl.h>		/* for O_RDONLY, etc */
7090792Sgshapiro#endif
7190792Sgshapiro
7290792Sgshapiro#ifndef	USE_BFUNCS
7390792Sgshapiro# include <memory.h>
7490792Sgshapiro/* Yes, memcpy is OK here (no overlapped copies). */
7590792Sgshapiro# define bcopy(a,b,c)    memcpy(b,a,c)
7690792Sgshapiro# define bzero(p,l)      memset(p,0,l)
7790792Sgshapiro# define bcmp(a,b,c)     memcmp(a,b,c)
7890792Sgshapiro#endif
7990792Sgshapiro
8090792Sgshapiro#include "bootp.h"
8190792Sgshapiro#include "hash.h"
8290792Sgshapiro#include "hwaddr.h"
8390792Sgshapiro#include "bootpd.h"
8490792Sgshapiro#include "dovend.h"
8590792Sgshapiro#include "getif.h"
8690792Sgshapiro#include "readfile.h"
8790792Sgshapiro#include "report.h"
8890792Sgshapiro#include "tzone.h"
89#include "patchlevel.h"
90
91#ifndef CONFIG_FILE
92#define CONFIG_FILE		"/etc/bootptab"
93#endif
94#ifndef DUMPTAB_FILE
95#define DUMPTAB_FILE		"/tmp/bootpd.dump"
96#endif
97
98
99
100/*
101 * Externals, forward declarations, and global variables
102 */
103
104#ifdef	__STDC__
105#define P(args) args
106#else
107#define P(args) ()
108#endif
109
110extern void dumptab P((char *));
111
112PRIVATE void catcher P((int));
113PRIVATE int chk_access P((char *, int32 *));
114#ifdef VEND_CMU
115PRIVATE void dovend_cmu P((struct bootp *, struct host *));
116#endif
117PRIVATE void dovend_rfc1048 P((struct bootp *, struct host *, int32));
118PRIVATE void handle_reply P((void));
119PRIVATE void handle_request P((void));
120PRIVATE void sendreply P((int forward, int32 dest_override));
121PRIVATE void usage P((void));
122
123#undef	P
124
125/*
126 * IP port numbers for client and server obtained from /etc/services
127 */
128
129u_short bootps_port, bootpc_port;
130
131
132/*
133 * Internet socket and interface config structures
134 */
135
136struct sockaddr_in bind_addr;	/* Listening */
137struct sockaddr_in recv_addr;	/* Packet source */
138struct sockaddr_in send_addr;	/*  destination */
139
140
141/*
142 * option defaults
143 */
144int debug = 0;					/* Debugging flag (level) */
145struct timeval actualtimeout =
146{								/* fifteen minutes */
147	15 * 60L,					/* tv_sec */
148	0							/* tv_usec */
149};
150
151/*
152 * General
153 */
154
155int s;							/* Socket file descriptor */
156char *pktbuf;					/* Receive packet buffer */
157int pktlen;
158char *progname;
159char *chdir_path;
160struct in_addr my_ip_addr;
161
162struct utsname my_uname;
163char *hostname;
164
165/* Flags set by signal catcher. */
166PRIVATE int do_readtab = 0;
167PRIVATE int do_dumptab = 0;
168
169/*
170 * Globals below are associated with the bootp database file (bootptab).
171 */
172
173char *bootptab = CONFIG_FILE;
174char *bootpd_dump = DUMPTAB_FILE;
175
176
177
178/*
179 * Initialization such as command-line processing is done and then the
180 * main server loop is started.
181 */
182
183void
184main(argc, argv)
185	int argc;
186	char **argv;
187{
188	struct timeval *timeout;
189	struct bootp *bp;
190	struct servent *servp;
191	struct hostent *hep;
192	char *stmp;
193	int n, ba_len, ra_len;
194	int nfound, readfds;
195	int standalone;
196#ifdef	SA_NOCLDSTOP	/* Have POSIX sigaction(2). */
197	struct sigaction sa;
198#endif
199
200	progname = strrchr(argv[0], '/');
201	if (progname) progname++;
202	else progname = argv[0];
203
204	/*
205	 * Initialize logging.
206	 */
207	report_init(0);				/* uses progname */
208
209	/*
210	 * Log startup
211	 */
212	report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
213
214	/* Debugging for compilers with struct padding. */
215	assert(sizeof(struct bootp) == BP_MINPKTSZ);
216
217	/* Get space for receiving packets and composing replies. */
218	pktbuf = malloc(MAX_MSG_SIZE);
219	if (!pktbuf) {
220		report(LOG_ERR, "malloc failed");
221		exit(1);
222	}
223	bp = (struct bootp *) pktbuf;
224
225	/*
226	 * Check to see if a socket was passed to us from inetd.
227	 *
228	 * Use getsockname() to determine if descriptor 0 is indeed a socket
229	 * (and thus we are probably a child of inetd) or if it is instead
230	 * something else and we are running standalone.
231	 */
232	s = 0;
233	ba_len = sizeof(bind_addr);
234	bzero((char *) &bind_addr, ba_len);
235	errno = 0;
236	standalone = TRUE;
237	if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {
238		/*
239		 * Descriptor 0 is a socket.  Assume we are a child of inetd.
240		 */
241		if (bind_addr.sin_family == AF_INET) {
242			standalone = FALSE;
243			bootps_port = ntohs(bind_addr.sin_port);
244		} else {
245			/* Some other type of socket? */
246			report(LOG_ERR, "getsockname: not an INET socket");
247		}
248	}
249
250	/*
251	 * Set defaults that might be changed by option switches.
252	 */
253	stmp = NULL;
254	timeout = &actualtimeout;
255
256	if (uname(&my_uname) < 0) {
257		fprintf(stderr, "bootpd: can't get hostname\n");
258		exit(1);
259	}
260	hostname = my_uname.nodename;
261
262	/*
263	 * Read switches.
264	 */
265	for (argc--, argv++; argc > 0; argc--, argv++) {
266		if (argv[0][0] != '-')
267			break;
268		switch (argv[0][1]) {
269
270		case 'c':				/* chdir_path */
271			if (argv[0][2]) {
272				stmp = &(argv[0][2]);
273			} else {
274				argc--;
275				argv++;
276				stmp = argv[0];
277			}
278			if (!stmp || (stmp[0] != '/')) {
279				fprintf(stderr,
280						"bootpd: invalid chdir specification\n");
281				break;
282			}
283			chdir_path = stmp;
284			break;
285
286		case 'd':				/* debug level */
287			if (argv[0][2]) {
288				stmp = &(argv[0][2]);
289			} else if (argv[1] && argv[1][0] == '-') {
290				/*
291				 * Backwards-compatible behavior:
292				 * no parameter, so just increment the debug flag.
293				 */
294				debug++;
295				break;
296			} else {
297				argc--;
298				argv++;
299				stmp = argv[0];
300			}
301			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
302				fprintf(stderr,
303						"%s: invalid debug level\n", progname);
304				break;
305			}
306			debug = n;
307			break;
308
309		case 'h':				/* override hostname */
310			if (argv[0][2]) {
311				stmp = &(argv[0][2]);
312			} else {
313				argc--;
314				argv++;
315				stmp = argv[0];
316			}
317			if (!stmp) {
318				fprintf(stderr,
319						"bootpd: missing hostname\n");
320				break;
321			}
322			hostname = stmp;
323			break;
324
325		case 'i':				/* inetd mode */
326			standalone = FALSE;
327			break;
328
329		case 's':				/* standalone mode */
330			standalone = TRUE;
331			break;
332
333		case 't':				/* timeout */
334			if (argv[0][2]) {
335				stmp = &(argv[0][2]);
336			} else {
337				argc--;
338				argv++;
339				stmp = argv[0];
340			}
341			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
342				fprintf(stderr,
343						"%s: invalid timeout specification\n", progname);
344				break;
345			}
346			actualtimeout.tv_sec = (int32) (60 * n);
347			/*
348			 * If the actual timeout is zero, pass a NULL pointer
349			 * to select so it blocks indefinitely, otherwise,
350			 * point to the actual timeout value.
351			 */
352			timeout = (n > 0) ? &actualtimeout : NULL;
353			break;
354
355		default:
356			fprintf(stderr, "%s: unknown switch: -%c\n",
357					progname, argv[0][1]);
358			usage();
359			break;
360
361		} /* switch */
362	} /* for args */
363
364	/*
365	 * Override default file names if specified on the command line.
366	 */
367	if (argc > 0)
368		bootptab = argv[0];
369
370	if (argc > 1)
371		bootpd_dump = argv[1];
372
373	/*
374	 * Get my hostname and IP address.
375	 */
376
377	hep = gethostbyname(hostname);
378	if (!hep) {
379		fprintf(stderr, "Can not get my IP address\n");
380		exit(1);
381	}
382	bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
383
384	if (standalone) {
385		/*
386		 * Go into background and disassociate from controlling terminal.
387		 */
388		if (debug < 3) {
389			if (fork())
390				exit(0);
391#ifdef	NO_SETSID
392			setpgrp(0,0);
393#ifdef TIOCNOTTY
394			n = open("/dev/tty", O_RDWR);
395			if (n >= 0) {
396				ioctl(n, TIOCNOTTY, (char *) 0);
397				(void) close(n);
398			}
399#endif	/* TIOCNOTTY */
400#else	/* SETSID */
401			if (setsid() < 0)
402				perror("setsid");
403#endif	/* SETSID */
404		} /* if debug < 3 */
405
406		/*
407		 * Nuke any timeout value
408		 */
409		timeout = NULL;
410
411	} /* if standalone (1st) */
412
413	/* Set the cwd (i.e. to /tftpboot) */
414	if (chdir_path) {
415		if (chdir(chdir_path) < 0)
416			report(LOG_ERR, "%s: chdir failed", chdir_path);
417	}
418
419	/* Get the timezone. */
420	tzone_init();
421
422	/* Allocate hash tables. */
423	rdtab_init();
424
425	/*
426	 * Read the bootptab file.
427	 */
428	readtab(1);					/* force read */
429
430	if (standalone) {
431
432		/*
433		 * Create a socket.
434		 */
435		if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
436			report(LOG_ERR, "socket: %s", get_network_errmsg());
437			exit(1);
438		}
439
440		/*
441		 * Get server's listening port number
442		 */
443		servp = getservbyname("bootps", "udp");
444		if (servp) {
445			bootps_port = ntohs((u_short) servp->s_port);
446		} else {
447			bootps_port = (u_short) IPPORT_BOOTPS;
448			report(LOG_ERR,
449				   "udp/bootps: unknown service -- assuming port %d",
450				   bootps_port);
451		}
452
453		/*
454		 * Bind socket to BOOTPS port.
455		 */
456		bind_addr.sin_family = AF_INET;
457		bind_addr.sin_addr.s_addr = INADDR_ANY;
458		bind_addr.sin_port = htons(bootps_port);
459		if (bind(s, (struct sockaddr *) &bind_addr,
460				 sizeof(bind_addr)) < 0)
461		{
462			report(LOG_ERR, "bind: %s", get_network_errmsg());
463			exit(1);
464		}
465	} /* if standalone (2nd)*/
466
467	/*
468	 * Get destination port number so we can reply to client
469	 */
470	servp = getservbyname("bootpc", "udp");
471	if (servp) {
472		bootpc_port = ntohs(servp->s_port);
473	} else {
474		report(LOG_ERR,
475			   "udp/bootpc: unknown service -- assuming port %d",
476			   IPPORT_BOOTPC);
477		bootpc_port = (u_short) IPPORT_BOOTPC;
478	}
479
480	/*
481	 * Set up signals to read or dump the table.
482	 */
483#ifdef	SA_NOCLDSTOP	/* Have POSIX sigaction(2). */
484	sa.sa_handler = catcher;
485	sigemptyset(&sa.sa_mask);
486	sa.sa_flags = 0;
487	if (sigaction(SIGHUP, &sa, NULL) < 0) {
488		report(LOG_ERR, "sigaction: %s", get_errmsg());
489		exit(1);
490	}
491	if (sigaction(SIGUSR1, &sa, NULL) < 0) {
492		report(LOG_ERR, "sigaction: %s", get_errmsg());
493		exit(1);
494	}
495#else	/* SA_NOCLDSTOP */
496	/* Old-fashioned UNIX signals */
497	if ((int) signal(SIGHUP, catcher) < 0) {
498		report(LOG_ERR, "signal: %s", get_errmsg());
499		exit(1);
500	}
501	if ((int) signal(SIGUSR1, catcher) < 0) {
502		report(LOG_ERR, "signal: %s", get_errmsg());
503		exit(1);
504	}
505#endif	/* SA_NOCLDSTOP */
506
507	/*
508	 * Process incoming requests.
509	 */
510	for (;;) {
511		struct timeval tv;
512
513		readfds = 1 << s;
514		if (timeout)
515			tv = *timeout;
516
517		nfound = select(s + 1, (fd_set *)&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 (!(readfds & (1 << s))) {
538			if (debug > 1)
539				report(LOG_INFO, "exiting after %ld minutes of inactivity",
540					   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}
573
574
575
576
577/*
578 * Print "usage" message and exit
579 */
580
581PRIVATE void
582usage()
583{
584	fprintf(stderr,
585			"usage:  bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n");
586	fprintf(stderr, "\t -c n\tset current directory\n");
587	fprintf(stderr, "\t -d n\tset debug level\n");
588	fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n");
589	fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n");
590	fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n");
591	exit(1);
592}
593
594/* Signal catchers */
595PRIVATE void
596catcher(sig)
597	int sig;
598{
599	if (sig == SIGHUP)
600		do_readtab = 1;
601	if (sig == SIGUSR1)
602		do_dumptab = 1;
603#if	!defined(SA_NOCLDSTOP) && defined(SYSV)
604	/* For older "System V" derivatives with no sigaction(). */
605	signal(sig, catcher);
606#endif
607}
608
609
610
611/*
612 * Process BOOTREQUEST packet.
613 *
614 * Note:  This version of the bootpd.c server never forwards
615 * a request to another server.  That is the job of a gateway
616 * program such as the "bootpgw" program included here.
617 *
618 * (Also this version does not interpret the hostname field of
619 * the request packet;  it COULD do a name->address lookup and
620 * forward the request there.)
621 */
622PRIVATE void
623handle_request()
624{
625	struct bootp *bp = (struct bootp *) pktbuf;
626	struct host *hp = NULL;
627	struct host dummyhost;
628	int32 bootsize = 0;
629	unsigned hlen, hashcode;
630	int32 dest;
631	char realpath[1024];
632	char *clntpath;
633	char *homedir, *bootfile;
634	int n;
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		strcpy(realpath, 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 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)) {
1288				if (debug > 1)
1289					report(LOG_INFO, "request has DHCP msglen=%d", msgsz);
1290				pktlen = msgsz;
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