bootpd.c revision 18471
13229Spst/************************************************************************
23229Spst          Copyright 1988, 1991 by Carnegie Mellon University
33229Spst
43229Spst                          All Rights Reserved
53229Spst
63229SpstPermission to use, copy, modify, and distribute this software and its
73229Spstdocumentation for any purpose and without fee is hereby granted, provided
83229Spstthat the above copyright notice appear in all copies and that both that
93229Spstcopyright notice and this permission notice appear in supporting
103229Spstdocumentation, and that the name of Carnegie Mellon University not be used
113229Spstin advertising or publicity pertaining to distribution of the software
123229Spstwithout specific, written prior permission.
133229Spst
143229SpstCARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
153229SpstSOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
163229SpstIN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
173229SpstDAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
183229SpstPROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
193229SpstACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
203229SpstSOFTWARE.
2118471Swosch
2218471Swosch	$Id$
2318471Swosch
243229Spst************************************************************************/
253229Spst
263229Spst/*
273229Spst * BOOTP (bootstrap protocol) server daemon.
283229Spst *
293229Spst * Answers BOOTP request packets from booting client machines.
303229Spst * See [SRI-NIC]<RFC>RFC951.TXT for a description of the protocol.
313229Spst * See [SRI-NIC]<RFC>RFC1048.TXT for vendor-information extensions.
323229Spst * See RFC 1395 for option tags 14-17.
333229Spst * See accompanying man page -- bootpd.8
343229Spst *
353229Spst * HISTORY
363229Spst *	See ./Changes
373229Spst *
383229Spst * BUGS
393229Spst *	See ./ToDo
403229Spst */
413229Spst
423229Spst
433229Spst
443229Spst#include <sys/types.h>
453229Spst#include <sys/param.h>
463229Spst#include <sys/socket.h>
473229Spst#include <sys/ioctl.h>
483229Spst#include <sys/file.h>
493229Spst#include <sys/time.h>
503229Spst#include <sys/stat.h>
5113575Spst#include <sys/utsname.h>
523229Spst
533229Spst#include <net/if.h>
543229Spst#include <netinet/in.h>
553229Spst#include <arpa/inet.h>	/* inet_ntoa */
563229Spst
573229Spst#ifndef	NO_UNISTD
583229Spst#include <unistd.h>
593229Spst#endif
6013575Spst
613229Spst#include <stdlib.h>
623229Spst#include <signal.h>
633229Spst#include <stdio.h>
643229Spst#include <string.h>
653229Spst#include <errno.h>
663229Spst#include <ctype.h>
673229Spst#include <netdb.h>
683229Spst#include <syslog.h>
693229Spst#include <assert.h>
703229Spst
713229Spst#ifdef	NO_SETSID
723229Spst# include <fcntl.h>		/* for O_RDONLY, etc */
733229Spst#endif
743229Spst
753229Spst#ifndef	USE_BFUNCS
763229Spst# include <memory.h>
773229Spst/* Yes, memcpy is OK here (no overlapped copies). */
783229Spst# define bcopy(a,b,c)    memcpy(b,a,c)
793229Spst# define bzero(p,l)      memset(p,0,l)
803229Spst# define bcmp(a,b,c)     memcmp(a,b,c)
813229Spst#endif
823229Spst
833229Spst#include "bootp.h"
843229Spst#include "hash.h"
853229Spst#include "hwaddr.h"
863229Spst#include "bootpd.h"
873229Spst#include "dovend.h"
883229Spst#include "getif.h"
893229Spst#include "readfile.h"
903229Spst#include "report.h"
913229Spst#include "tzone.h"
923229Spst#include "patchlevel.h"
933229Spst
943229Spst#ifndef CONFIG_FILE
953229Spst#define CONFIG_FILE		"/etc/bootptab"
963229Spst#endif
973229Spst#ifndef DUMPTAB_FILE
983229Spst#define DUMPTAB_FILE		"/tmp/bootpd.dump"
993229Spst#endif
1003229Spst
1013229Spst
1023229Spst
1033229Spst/*
1043229Spst * Externals, forward declarations, and global variables
1053229Spst */
1063229Spst
1073229Spst#ifdef	__STDC__
1083229Spst#define P(args) args
1093229Spst#else
1103229Spst#define P(args) ()
1113229Spst#endif
1123229Spst
1133229Spstextern void dumptab P((char *));
1143229Spst
1153229SpstPRIVATE void catcher P((int));
1163229SpstPRIVATE int chk_access P((char *, int32 *));
1173229Spst#ifdef VEND_CMU
1183229SpstPRIVATE void dovend_cmu P((struct bootp *, struct host *));
1193229Spst#endif
1203229SpstPRIVATE void dovend_rfc1048 P((struct bootp *, struct host *, int32));
1213229SpstPRIVATE void handle_reply P((void));
1223229SpstPRIVATE void handle_request P((void));
1233229SpstPRIVATE void sendreply P((int forward, int32 dest_override));
1243229SpstPRIVATE void usage P((void));
1253229Spst
1263229Spst#undef	P
1273229Spst
1283229Spst/*
1293229Spst * IP port numbers for client and server obtained from /etc/services
1303229Spst */
1313229Spst
1323229Spstu_short bootps_port, bootpc_port;
1333229Spst
1343229Spst
1353229Spst/*
1363229Spst * Internet socket and interface config structures
1373229Spst */
1383229Spst
1393229Spststruct sockaddr_in bind_addr;	/* Listening */
1403229Spststruct sockaddr_in recv_addr;	/* Packet source */
1413229Spststruct sockaddr_in send_addr;	/*  destination */
1423229Spst
1433229Spst
1443229Spst/*
1453229Spst * option defaults
1463229Spst */
1473229Spstint debug = 0;					/* Debugging flag (level) */
1483229Spststruct timeval actualtimeout =
1493229Spst{								/* fifteen minutes */
1503229Spst	15 * 60L,					/* tv_sec */
1513229Spst	0							/* tv_usec */
1523229Spst};
1533229Spst
1543229Spst/*
1553229Spst * General
1563229Spst */
1573229Spst
1583229Spstint s;							/* Socket file descriptor */
1593229Spstchar *pktbuf;					/* Receive packet buffer */
1603229Spstint pktlen;
1613229Spstchar *progname;
1623229Spstchar *chdir_path;
1633229Spststruct in_addr my_ip_addr;
1643229Spst
16513575Spststruct utsname my_uname;
16613575Spstchar *hostname;
16713575Spst
1683229Spst/* Flags set by signal catcher. */
1693229SpstPRIVATE int do_readtab = 0;
1703229SpstPRIVATE int do_dumptab = 0;
1713229Spst
1723229Spst/*
1733229Spst * Globals below are associated with the bootp database file (bootptab).
1743229Spst */
1753229Spst
1763229Spstchar *bootptab = CONFIG_FILE;
1773229Spstchar *bootpd_dump = DUMPTAB_FILE;
1783229Spst
1793229Spst
1803229Spst
1813229Spst/*
1823229Spst * Initialization such as command-line processing is done and then the
1833229Spst * main server loop is started.
1843229Spst */
1853229Spst
1863229Spstvoid
1873229Spstmain(argc, argv)
1883229Spst	int argc;
1893229Spst	char **argv;
1903229Spst{
1913229Spst	struct timeval *timeout;
1923229Spst	struct bootp *bp;
1933229Spst	struct servent *servp;
1943229Spst	struct hostent *hep;
1953229Spst	char *stmp;
1963229Spst	int n, ba_len, ra_len;
1973229Spst	int nfound, readfds;
1983229Spst	int standalone;
19913575Spst#ifdef	SA_NOCLDSTOP	/* Have POSIX sigaction(2). */
20013575Spst	struct sigaction sa;
20113575Spst#endif
2023229Spst
2033229Spst	progname = strrchr(argv[0], '/');
2043229Spst	if (progname) progname++;
2053229Spst	else progname = argv[0];
2063229Spst
2073229Spst	/*
2083229Spst	 * Initialize logging.
2093229Spst	 */
2103229Spst	report_init(0);				/* uses progname */
2113229Spst
2123229Spst	/*
2133229Spst	 * Log startup
2143229Spst	 */
2153229Spst	report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
2163229Spst
2173229Spst	/* Debugging for compilers with struct padding. */
2183229Spst	assert(sizeof(struct bootp) == BP_MINPKTSZ);
2193229Spst
2203229Spst	/* Get space for receiving packets and composing replies. */
2213229Spst	pktbuf = malloc(MAX_MSG_SIZE);
2223229Spst	if (!pktbuf) {
2233229Spst		report(LOG_ERR, "malloc failed");
2243229Spst		exit(1);
2253229Spst	}
2263229Spst	bp = (struct bootp *) pktbuf;
2273229Spst
2283229Spst	/*
2293229Spst	 * Check to see if a socket was passed to us from inetd.
2303229Spst	 *
2313229Spst	 * Use getsockname() to determine if descriptor 0 is indeed a socket
2323229Spst	 * (and thus we are probably a child of inetd) or if it is instead
2333229Spst	 * something else and we are running standalone.
2343229Spst	 */
2353229Spst	s = 0;
2363229Spst	ba_len = sizeof(bind_addr);
2373229Spst	bzero((char *) &bind_addr, ba_len);
2383229Spst	errno = 0;
2393229Spst	standalone = TRUE;
2403229Spst	if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {
2413229Spst		/*
2423229Spst		 * Descriptor 0 is a socket.  Assume we are a child of inetd.
2433229Spst		 */
2443229Spst		if (bind_addr.sin_family == AF_INET) {
2453229Spst			standalone = FALSE;
2463229Spst			bootps_port = ntohs(bind_addr.sin_port);
2473229Spst		} else {
2483229Spst			/* Some other type of socket? */
2493229Spst			report(LOG_ERR, "getsockname: not an INET socket");
2503229Spst		}
2513229Spst	}
2523229Spst
2533229Spst	/*
2543229Spst	 * Set defaults that might be changed by option switches.
2553229Spst	 */
2563229Spst	stmp = NULL;
2573229Spst	timeout = &actualtimeout;
2583229Spst
25913575Spst	if (uname(&my_uname) < 0) {
26013575Spst		fprintf(stderr, "bootpd: can't get hostname\n");
26113575Spst		exit(1);
26213575Spst	}
26313575Spst	hostname = my_uname.nodename;
26413575Spst
2653229Spst	/*
2663229Spst	 * Read switches.
2673229Spst	 */
2683229Spst	for (argc--, argv++; argc > 0; argc--, argv++) {
2693229Spst		if (argv[0][0] != '-')
2703229Spst			break;
2713229Spst		switch (argv[0][1]) {
2723229Spst
2733229Spst		case 'c':				/* chdir_path */
2743229Spst			if (argv[0][2]) {
2753229Spst				stmp = &(argv[0][2]);
2763229Spst			} else {
2773229Spst				argc--;
2783229Spst				argv++;
2793229Spst				stmp = argv[0];
2803229Spst			}
2813229Spst			if (!stmp || (stmp[0] != '/')) {
2823229Spst				fprintf(stderr,
2833229Spst						"bootpd: invalid chdir specification\n");
2843229Spst				break;
2853229Spst			}
2863229Spst			chdir_path = stmp;
2873229Spst			break;
2883229Spst
2893229Spst		case 'd':				/* debug level */
2903229Spst			if (argv[0][2]) {
2913229Spst				stmp = &(argv[0][2]);
2923229Spst			} else if (argv[1] && argv[1][0] == '-') {
2933229Spst				/*
2943229Spst				 * Backwards-compatible behavior:
2953229Spst				 * no parameter, so just increment the debug flag.
2963229Spst				 */
2973229Spst				debug++;
2983229Spst				break;
2993229Spst			} else {
3003229Spst				argc--;
3013229Spst				argv++;
3023229Spst				stmp = argv[0];
3033229Spst			}
3043229Spst			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
3053229Spst				fprintf(stderr,
3063229Spst						"%s: invalid debug level\n", progname);
3073229Spst				break;
3083229Spst			}
3093229Spst			debug = n;
3103229Spst			break;
3113229Spst
3123229Spst		case 'h':				/* override hostname */
3133229Spst			if (argv[0][2]) {
3143229Spst				stmp = &(argv[0][2]);
3153229Spst			} else {
3163229Spst				argc--;
3173229Spst				argv++;
3183229Spst				stmp = argv[0];
3193229Spst			}
3203229Spst			if (!stmp) {
3213229Spst				fprintf(stderr,
3223229Spst						"bootpd: missing hostname\n");
3233229Spst				break;
3243229Spst			}
32513575Spst			hostname = stmp;
3263229Spst			break;
3273229Spst
3283229Spst		case 'i':				/* inetd mode */
3293229Spst			standalone = FALSE;
3303229Spst			break;
3313229Spst
3323229Spst		case 's':				/* standalone mode */
3333229Spst			standalone = TRUE;
3343229Spst			break;
3353229Spst
3363229Spst		case 't':				/* timeout */
3373229Spst			if (argv[0][2]) {
3383229Spst				stmp = &(argv[0][2]);
3393229Spst			} else {
3403229Spst				argc--;
3413229Spst				argv++;
3423229Spst				stmp = argv[0];
3433229Spst			}
3443229Spst			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
3453229Spst				fprintf(stderr,
3463229Spst						"%s: invalid timeout specification\n", progname);
3473229Spst				break;
3483229Spst			}
3493229Spst			actualtimeout.tv_sec = (int32) (60 * n);
3503229Spst			/*
3513229Spst			 * If the actual timeout is zero, pass a NULL pointer
3523229Spst			 * to select so it blocks indefinitely, otherwise,
3533229Spst			 * point to the actual timeout value.
3543229Spst			 */
3553229Spst			timeout = (n > 0) ? &actualtimeout : NULL;
3563229Spst			break;
3573229Spst
3583229Spst		default:
3593229Spst			fprintf(stderr, "%s: unknown switch: -%c\n",
3603229Spst					progname, argv[0][1]);
3613229Spst			usage();
3623229Spst			break;
3633229Spst
3643229Spst		} /* switch */
3653229Spst	} /* for args */
3663229Spst
3673229Spst	/*
3683229Spst	 * Override default file names if specified on the command line.
3693229Spst	 */
3703229Spst	if (argc > 0)
3713229Spst		bootptab = argv[0];
3723229Spst
3733229Spst	if (argc > 1)
3743229Spst		bootpd_dump = argv[1];
3753229Spst
3763229Spst	/*
3773229Spst	 * Get my hostname and IP address.
3783229Spst	 */
37913575Spst
3803229Spst	hep = gethostbyname(hostname);
3813229Spst	if (!hep) {
3823229Spst		fprintf(stderr, "Can not get my IP address\n");
3833229Spst		exit(1);
3843229Spst	}
3853229Spst	bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
3863229Spst
3873229Spst	if (standalone) {
3883229Spst		/*
3893229Spst		 * Go into background and disassociate from controlling terminal.
3903229Spst		 */
3913229Spst		if (debug < 3) {
3923229Spst			if (fork())
3933229Spst				exit(0);
3943229Spst#ifdef	NO_SETSID
3953229Spst			setpgrp(0,0);
3963229Spst#ifdef TIOCNOTTY
3973229Spst			n = open("/dev/tty", O_RDWR);
3983229Spst			if (n >= 0) {
3993229Spst				ioctl(n, TIOCNOTTY, (char *) 0);
4003229Spst				(void) close(n);
4013229Spst			}
4023229Spst#endif	/* TIOCNOTTY */
4033229Spst#else	/* SETSID */
4043229Spst			if (setsid() < 0)
4053229Spst				perror("setsid");
4063229Spst#endif	/* SETSID */
4073229Spst		} /* if debug < 3 */
4083229Spst
4093229Spst		/*
4103229Spst		 * Nuke any timeout value
4113229Spst		 */
4123229Spst		timeout = NULL;
4133229Spst
4143229Spst	} /* if standalone (1st) */
4153229Spst
4163229Spst	/* Set the cwd (i.e. to /tftpboot) */
4173229Spst	if (chdir_path) {
4183229Spst		if (chdir(chdir_path) < 0)
4193229Spst			report(LOG_ERR, "%s: chdir failed", chdir_path);
4203229Spst	}
4213229Spst
4223229Spst	/* Get the timezone. */
4233229Spst	tzone_init();
4243229Spst
4253229Spst	/* Allocate hash tables. */
4263229Spst	rdtab_init();
4273229Spst
4283229Spst	/*
4293229Spst	 * Read the bootptab file.
4303229Spst	 */
4313229Spst	readtab(1);					/* force read */
4323229Spst
4333229Spst	if (standalone) {
4343229Spst
4353229Spst		/*
4363229Spst		 * Create a socket.
4373229Spst		 */
4383229Spst		if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
4393229Spst			report(LOG_ERR, "socket: %s", get_network_errmsg());
4403229Spst			exit(1);
4413229Spst		}
4423229Spst
4433229Spst		/*
4443229Spst		 * Get server's listening port number
4453229Spst		 */
4463229Spst		servp = getservbyname("bootps", "udp");
4473229Spst		if (servp) {
4483229Spst			bootps_port = ntohs((u_short) servp->s_port);
4493229Spst		} else {
4503229Spst			bootps_port = (u_short) IPPORT_BOOTPS;
4513229Spst			report(LOG_ERR,
4523229Spst				   "udp/bootps: unknown service -- assuming port %d",
4533229Spst				   bootps_port);
4543229Spst		}
4553229Spst
4563229Spst		/*
4573229Spst		 * Bind socket to BOOTPS port.
4583229Spst		 */
4593229Spst		bind_addr.sin_family = AF_INET;
4603229Spst		bind_addr.sin_addr.s_addr = INADDR_ANY;
4613229Spst		bind_addr.sin_port = htons(bootps_port);
4623229Spst		if (bind(s, (struct sockaddr *) &bind_addr,
4633229Spst				 sizeof(bind_addr)) < 0)
4643229Spst		{
4653229Spst			report(LOG_ERR, "bind: %s", get_network_errmsg());
4663229Spst			exit(1);
4673229Spst		}
4683229Spst	} /* if standalone (2nd)*/
4693229Spst
4703229Spst	/*
4713229Spst	 * Get destination port number so we can reply to client
4723229Spst	 */
4733229Spst	servp = getservbyname("bootpc", "udp");
4743229Spst	if (servp) {
4753229Spst		bootpc_port = ntohs(servp->s_port);
4763229Spst	} else {
4773229Spst		report(LOG_ERR,
4783229Spst			   "udp/bootpc: unknown service -- assuming port %d",
4793229Spst			   IPPORT_BOOTPC);
4803229Spst		bootpc_port = (u_short) IPPORT_BOOTPC;
4813229Spst	}
4823229Spst
4833229Spst	/*
4843229Spst	 * Set up signals to read or dump the table.
4853229Spst	 */
48613575Spst#ifdef	SA_NOCLDSTOP	/* Have POSIX sigaction(2). */
48713575Spst	sa.sa_handler = catcher;
48813575Spst	sigemptyset(&sa.sa_mask);
48913575Spst	sa.sa_flags = 0;
49013575Spst	if (sigaction(SIGHUP, &sa, NULL) < 0) {
49113575Spst		report(LOG_ERR, "sigaction: %s", get_errmsg());
49213575Spst		exit(1);
49313575Spst	}
49413575Spst	if (sigaction(SIGUSR1, &sa, NULL) < 0) {
49513575Spst		report(LOG_ERR, "sigaction: %s", get_errmsg());
49613575Spst		exit(1);
49713575Spst	}
49813575Spst#else	/* SA_NOCLDSTOP */
49913575Spst	/* Old-fashioned UNIX signals */
5003229Spst	if ((int) signal(SIGHUP, catcher) < 0) {
5013229Spst		report(LOG_ERR, "signal: %s", get_errmsg());
5023229Spst		exit(1);
5033229Spst	}
5043229Spst	if ((int) signal(SIGUSR1, catcher) < 0) {
5053229Spst		report(LOG_ERR, "signal: %s", get_errmsg());
5063229Spst		exit(1);
5073229Spst	}
50813575Spst#endif	/* SA_NOCLDSTOP */
5093229Spst
5103229Spst	/*
5113229Spst	 * Process incoming requests.
5123229Spst	 */
5133229Spst	for (;;) {
51413575Spst		struct timeval tv;
51513575Spst
5163229Spst		readfds = 1 << s;
51713575Spst		if (timeout)
51813575Spst			tv = *timeout;
51913575Spst
52013575Spst		nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL,
52113575Spst						(timeout) ? &tv : NULL);
5223229Spst		if (nfound < 0) {
5233229Spst			if (errno != EINTR) {
5243229Spst				report(LOG_ERR, "select: %s", get_errmsg());
5253229Spst			}
5263229Spst			/*
5273229Spst			 * Call readtab() or dumptab() here to avoid the
5283229Spst			 * dangers of doing I/O from a signal handler.
5293229Spst			 */
5303229Spst			if (do_readtab) {
5313229Spst				do_readtab = 0;
5323229Spst				readtab(1);		/* force read */
5333229Spst			}
5343229Spst			if (do_dumptab) {
5353229Spst				do_dumptab = 0;
5363229Spst				dumptab(bootpd_dump);
5373229Spst			}
5383229Spst			continue;
5393229Spst		}
5403229Spst		if (!(readfds & (1 << s))) {
5413229Spst			if (debug > 1)
5423229Spst				report(LOG_INFO, "exiting after %ld minutes of inactivity",
5433229Spst					   actualtimeout.tv_sec / 60);
5443229Spst			exit(0);
5453229Spst		}
5463229Spst		ra_len = sizeof(recv_addr);
5473229Spst		n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
5483229Spst					 (struct sockaddr *) &recv_addr, &ra_len);
5493229Spst		if (n <= 0) {
5503229Spst			continue;
5513229Spst		}
5523229Spst		if (debug > 1) {
5533229Spst			report(LOG_INFO, "recvd pkt from IP addr %s",
5543229Spst				   inet_ntoa(recv_addr.sin_addr));
5553229Spst		}
5563229Spst		if (n < sizeof(struct bootp)) {
5573229Spst			if (debug) {
55813575Spst				report(LOG_NOTICE, "received short packet");
5593229Spst			}
5603229Spst			continue;
5613229Spst		}
5623229Spst		pktlen = n;
5633229Spst
5643229Spst		readtab(0);				/* maybe re-read bootptab */
5653229Spst
5663229Spst		switch (bp->bp_op) {
5673229Spst		case BOOTREQUEST:
5683229Spst			handle_request();
5693229Spst			break;
5703229Spst		case BOOTREPLY:
5713229Spst			handle_reply();
5723229Spst			break;
5733229Spst		}
5743229Spst	}
5753229Spst}
5763229Spst
5773229Spst
5783229Spst
5793229Spst
5803229Spst/*
5813229Spst * Print "usage" message and exit
5823229Spst */
5833229Spst
5843229SpstPRIVATE void
5853229Spstusage()
5863229Spst{
5873229Spst	fprintf(stderr,
5883229Spst			"usage:  bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n");
5893229Spst	fprintf(stderr, "\t -c n\tset current directory\n");
5903229Spst	fprintf(stderr, "\t -d n\tset debug level\n");
5913229Spst	fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n");
5923229Spst	fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n");
5933229Spst	fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n");
5943229Spst	exit(1);
5953229Spst}
5963229Spst
5973229Spst/* Signal catchers */
5983229SpstPRIVATE void
5993229Spstcatcher(sig)
6003229Spst	int sig;
6013229Spst{
6023229Spst	if (sig == SIGHUP)
6033229Spst		do_readtab = 1;
6043229Spst	if (sig == SIGUSR1)
6053229Spst		do_dumptab = 1;
60613575Spst#if	!defined(SA_NOCLDSTOP) && defined(SYSV)
60713575Spst	/* For older "System V" derivatives with no sigaction(). */
6083229Spst	signal(sig, catcher);
6093229Spst#endif
6103229Spst}
6113229Spst
6123229Spst
6133229Spst
6143229Spst/*
6153229Spst * Process BOOTREQUEST packet.
6163229Spst *
6173229Spst * Note:  This version of the bootpd.c server never forwards
6183229Spst * a request to another server.  That is the job of a gateway
6193229Spst * program such as the "bootpgw" program included here.
6203229Spst *
6213229Spst * (Also this version does not interpret the hostname field of
6223229Spst * the request packet;  it COULD do a name->address lookup and
6233229Spst * forward the request there.)
6243229Spst */
6253229SpstPRIVATE void
6263229Spsthandle_request()
6273229Spst{
6283229Spst	struct bootp *bp = (struct bootp *) pktbuf;
6293229Spst	struct host *hp = NULL;
6303229Spst	struct host dummyhost;
6313229Spst	int32 bootsize = 0;
6323229Spst	unsigned hlen, hashcode;
6333229Spst	int32 dest;
6343229Spst	char realpath[1024];
6353229Spst	char *clntpath;
6363229Spst	char *homedir, *bootfile;
6373229Spst	int n;
6383229Spst
6393229Spst	/* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
6403229Spst
6413229Spst	/*
6423229Spst	 * If the servername field is set, compare it against us.
6433229Spst	 * If we're not being addressed, ignore this request.
6443229Spst	 * If the server name field is null, throw in our name.
6453229Spst	 */
6463229Spst	if (strlen(bp->bp_sname)) {
6473229Spst		if (strcmp(bp->bp_sname, hostname)) {
6483229Spst			if (debug)
6493229Spst				report(LOG_INFO, "\
6503229Spstignoring request for server %s from client at %s address %s",
6513229Spst					   bp->bp_sname, netname(bp->bp_htype),
6523229Spst					   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
6533229Spst			/* XXX - Is it correct to ignore such a request? -gwr */
6543229Spst			return;
6553229Spst		}
6563229Spst	} else {
6573229Spst		strcpy(bp->bp_sname, hostname);
6583229Spst	}
6593229Spst
6603229Spst	/* Convert the request into a reply. */
6613229Spst	bp->bp_op = BOOTREPLY;
6623229Spst	if (bp->bp_ciaddr.s_addr == 0) {
6633229Spst		/*
6643229Spst		 * client doesnt know his IP address,
6653229Spst		 * search by hardware address.
6663229Spst		 */
6673229Spst		if (debug > 1) {
6683229Spst			report(LOG_INFO, "request from %s address %s",
6693229Spst				   netname(bp->bp_htype),
6703229Spst				   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
6713229Spst		}
6723229Spst		hlen = haddrlength(bp->bp_htype);
6733229Spst		if (hlen != bp->bp_hlen) {
6743229Spst			report(LOG_NOTICE, "bad addr len from from %s address %s",
6753229Spst				   netname(bp->bp_htype),
6763229Spst				   haddrtoa(bp->bp_chaddr, hlen));
6773229Spst		}
6783229Spst		dummyhost.htype = bp->bp_htype;
6793229Spst		bcopy(bp->bp_chaddr, dummyhost.haddr, hlen);
6803229Spst		hashcode = hash_HashFunction(bp->bp_chaddr, hlen);
6813229Spst		hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp,
6823229Spst										 &dummyhost);
6833229Spst		if (hp == NULL &&
6843229Spst			bp->bp_htype == HTYPE_IEEE802)
6853229Spst		{
6863229Spst			/* Try again with address in "canonical" form. */
6873229Spst			haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen);
6883229Spst			if (debug > 1) {
6893229Spst				report(LOG_INFO, "\
6903229SpstHW addr type is IEEE 802.  convert to %s and check again\n",
6913229Spst					   haddrtoa(dummyhost.haddr, bp->bp_hlen));
6923229Spst			}
6933229Spst			hashcode = hash_HashFunction(dummyhost.haddr, hlen);
6943229Spst			hp = (struct host *) hash_Lookup(hwhashtable, hashcode,
6953229Spst											 hwlookcmp, &dummyhost);
6963229Spst		}
6973229Spst		if (hp == NULL) {
6983229Spst			/*
6993229Spst			 * XXX - Add dynamic IP address assignment?
7003229Spst			 */
70113575Spst			if (debug)
70213575Spst				report(LOG_NOTICE, "unknown client %s address %s",
7033229Spst					   netname(bp->bp_htype),
7043229Spst					   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
7053229Spst			return; /* not found */
7063229Spst		}
7073229Spst		(bp->bp_yiaddr).s_addr = hp->iaddr.s_addr;
7083229Spst
7093229Spst	} else {
7103229Spst
7113229Spst		/*
7123229Spst		 * search by IP address.
7133229Spst		 */
7143229Spst		if (debug > 1) {
7153229Spst			report(LOG_INFO, "request from IP addr %s",
7163229Spst				   inet_ntoa(bp->bp_ciaddr));
7173229Spst		}
7183229Spst		dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr;
7193229Spst		hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4);
7203229Spst		hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp,
7213229Spst										 &dummyhost);
7223229Spst		if (hp == NULL) {
72313575Spst			if (debug) {
7243229Spst				report(LOG_NOTICE, "IP address not found: %s",
7253229Spst					   inet_ntoa(bp->bp_ciaddr));
7263229Spst			}
7273229Spst			return;
7283229Spst		}
7293229Spst	}
7303229Spst
7313229Spst	if (debug) {
7323229Spst		report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr),
7333229Spst			   hp->hostname->string);
7343229Spst	}
7353229Spst
7363229Spst	/*
7373229Spst	 * If there is a response delay threshold, ignore requests
7383229Spst	 * with a timestamp lower than the threshold.
7393229Spst	 */
7403229Spst	if (hp->flags.min_wait) {
7413229Spst		u_int32 t = (u_int32) ntohs(bp->bp_secs);
7423229Spst		if (t < hp->min_wait) {
7433229Spst			if (debug > 1)
7443229Spst				report(LOG_INFO,
7453229Spst					   "ignoring request due to timestamp (%d < %d)",
7463229Spst					   t, hp->min_wait);
7473229Spst			return;
7483229Spst		}
7493229Spst	}
7503229Spst
7513229Spst#ifdef	YORK_EX_OPTION
7523229Spst	/*
7533229Spst	 * The need for the "ex" tag arose out of the need to empty
7543229Spst	 * shared networked drives on diskless PCs.  This solution is
7553229Spst	 * not very clean but it does work fairly well.
7563229Spst	 * Written by Edmund J. Sutcliffe <edmund@york.ac.uk>
7573229Spst	 *
7583229Spst	 * XXX - This could compromise security if a non-trusted user
7593229Spst	 * managed to write an entry in the bootptab with :ex=trojan:
7603229Spst	 * so I would leave this turned off unless you need it. -gwr
7613229Spst	 */
7623229Spst	/* Run a program, passing the client name as a parameter. */
7633229Spst	if (hp->flags.exec_file) {
7643229Spst		char tst[100];
7653229Spst		/* XXX - Check string lengths? -gwr */
7663229Spst		strcpy (tst, hp->exec_file->string);
7673229Spst		strcat (tst, " ");
7683229Spst		strcat (tst, hp->hostname->string);
7693229Spst		strcat (tst, " &");
7703229Spst		if (debug)
7713229Spst			report(LOG_INFO, "executing %s", tst);
7723229Spst		system(tst);	/* Hope this finishes soon... */
7733229Spst	}
7743229Spst#endif	/* YORK_EX_OPTION */
7753229Spst
7763229Spst	/*
7773229Spst	 * If a specific TFTP server address was specified in the bootptab file,
7783229Spst	 * fill it in, otherwise zero it.
7793229Spst	 * XXX - Rather than zero it, should it be the bootpd address? -gwr
7803229Spst	 */
7813229Spst	(bp->bp_siaddr).s_addr = (hp->flags.bootserver) ?
7823229Spst		hp->bootserver.s_addr : 0L;
7833229Spst
7843229Spst#ifdef	STANFORD_PROM_COMPAT
7853229Spst	/*
7863229Spst	 * Stanford bootp PROMs (for a Sun?) have no way to leave
7873229Spst	 * the boot file name field blank (because the boot file
7883229Spst	 * name is automatically generated from some index).
7893229Spst	 * As a work-around, this little hack allows those PROMs to
7903229Spst	 * specify "sunboot14" with the same effect as a NULL name.
7913229Spst	 * (The user specifies boot device 14 or some such magic.)
7923229Spst	 */
7933229Spst	if (strcmp(bp->bp_file, "sunboot14") == 0)
7943229Spst		bp->bp_file[0] = '\0';	/* treat it as unspecified */
7953229Spst#endif
7963229Spst
7973229Spst	/*
7983229Spst	 * Fill in the client's proper bootfile.
7993229Spst	 *
8003229Spst	 * If the client specifies an absolute path, try that file with a
8013229Spst	 * ".host" suffix and then without.  If the file cannot be found, no
8023229Spst	 * reply is made at all.
8033229Spst	 *
8043229Spst	 * If the client specifies a null or relative file, use the following
8053229Spst	 * table to determine the appropriate action:
8063229Spst	 *
8073229Spst	 *  Homedir      Bootfile    Client's file
8083229Spst	 * specified?   specified?   specification   Action
8093229Spst	 * -------------------------------------------------------------------
8103229Spst	 *      No          No          Null         Send null filename
8113229Spst	 *      No          No          Relative     Discard request
8123229Spst	 *      No          Yes         Null         Send if absolute else null
8133229Spst	 *      No          Yes         Relative     Discard request     *XXX
8143229Spst	 *      Yes         No          Null         Send null filename
8153229Spst	 *      Yes         No          Relative     Lookup with ".host"
8163229Spst	 *      Yes         Yes         Null         Send home/boot or bootfile
8173229Spst	 *      Yes         Yes         Relative     Lookup with ".host" *XXX
8183229Spst	 *
8193229Spst	 */
8203229Spst
8213229Spst	/*
8223229Spst	 * XXX - I don't like the policy of ignoring a client when the
8233229Spst	 * boot file is not accessible.  The TFTP server might not be
8243229Spst	 * running on the same machine as the BOOTP server, in which
8253229Spst	 * case checking accessibility of the boot file is pointless.
8263229Spst	 *
8273229Spst	 * Therefore, file accessibility is now demanded ONLY if you
8283229Spst	 * define CHECK_FILE_ACCESS in the Makefile options. -gwr
8293229Spst	 */
8303229Spst
8313229Spst	/*
8323229Spst	 * The "real" path is as seen by the BOOTP daemon on this
8333229Spst	 * machine, while the client path is relative to the TFTP
8343229Spst	 * daemon chroot directory (i.e. /tftpboot).
8353229Spst	 */
8363229Spst	if (hp->flags.tftpdir) {
8373229Spst		strcpy(realpath, hp->tftpdir->string);
8383229Spst		clntpath = &realpath[strlen(realpath)];
8393229Spst	} else {
8403229Spst		realpath[0] = '\0';
8413229Spst		clntpath = realpath;
8423229Spst	}
8433229Spst
8443229Spst	/*
8453229Spst	 * Determine client's requested homedir and bootfile.
8463229Spst	 */
8473229Spst	homedir = NULL;
8483229Spst	bootfile = NULL;
8493229Spst	if (bp->bp_file[0]) {
8503229Spst		homedir = bp->bp_file;
8513229Spst		bootfile = strrchr(homedir, '/');
8523229Spst		if (bootfile) {
8533229Spst			if (homedir == bootfile)
8543229Spst				homedir = NULL;
8553229Spst			*bootfile++ = '\0';
8563229Spst		} else {
8573229Spst			/* no "/" in the string */
8583229Spst			bootfile = homedir;
8593229Spst			homedir = NULL;
8603229Spst		}
8613229Spst		if (debug > 2) {
8623229Spst			report(LOG_INFO, "requested path=\"%s\"  file=\"%s\"",
8633229Spst				   (homedir) ? homedir : "",
8643229Spst				   (bootfile) ? bootfile : "");
8653229Spst		}
8663229Spst	}
8673229Spst
8683229Spst	/*
8693229Spst	 * Specifications in bootptab override client requested values.
8703229Spst	 */
8713229Spst	if (hp->flags.homedir)
8723229Spst		homedir = hp->homedir->string;
8733229Spst	if (hp->flags.bootfile)
8743229Spst		bootfile = hp->bootfile->string;
8753229Spst
8763229Spst	/*
8773229Spst	 * Construct bootfile path.
8783229Spst	 */
8793229Spst	if (homedir) {
8803229Spst		if (homedir[0] != '/')
8813229Spst			strcat(clntpath, "/");
8823229Spst		strcat(clntpath, homedir);
8833229Spst		homedir = NULL;
8843229Spst	}
8853229Spst	if (bootfile) {
8863229Spst		if (bootfile[0] != '/')
8873229Spst			strcat(clntpath, "/");
8883229Spst		strcat(clntpath, bootfile);
8893229Spst		bootfile = NULL;
8903229Spst	}
8913229Spst
8923229Spst	/*
8933229Spst	 * First try to find the file with a ".host" suffix
8943229Spst	 */
8953229Spst	n = strlen(clntpath);
8963229Spst	strcat(clntpath, ".");
8973229Spst	strcat(clntpath, hp->hostname->string);
8983229Spst	if (chk_access(realpath, &bootsize) < 0) {
8993229Spst		clntpath[n] = 0;			/* Try it without the suffix */
9003229Spst		if (chk_access(realpath, &bootsize) < 0) {
9013229Spst			/* neither "file.host" nor "file" was found */
9023229Spst#ifdef	CHECK_FILE_ACCESS
9033229Spst
9043229Spst			if (bp->bp_file[0]) {
9053229Spst				/*
9063229Spst				 * Client wanted specific file
9073229Spst				 * and we didn't have it.
9083229Spst				 */
9093229Spst				report(LOG_NOTICE,
9103229Spst					   "requested file not found: \"%s\"", clntpath);
9113229Spst				return;
9123229Spst			}
9133229Spst			/*
9143229Spst			 * Client didn't ask for a specific file and we couldn't
9153229Spst			 * access the default file, so just zero-out the bootfile
9163229Spst			 * field in the packet and continue processing the reply.
9173229Spst			 */
9183229Spst			bzero(bp->bp_file, sizeof(bp->bp_file));
9193229Spst			goto null_file_name;
9203229Spst
9213229Spst#else	/* CHECK_FILE_ACCESS */
9223229Spst
9233229Spst			/* Complain only if boot file size was needed. */
9243229Spst			if (hp->flags.bootsize_auto) {
9253229Spst				report(LOG_ERR, "can not determine size of file \"%s\"",
9263229Spst					   clntpath);
9273229Spst			}
9283229Spst
9293229Spst#endif	/* CHECK_FILE_ACCESS */
9303229Spst		}
9313229Spst	}
9323229Spst	strncpy(bp->bp_file, clntpath, BP_FILE_LEN);
9333229Spst	if (debug > 2)
9343229Spst		report(LOG_INFO, "bootfile=\"%s\"", clntpath);
9353229Spst
93613575Spst#ifdef	CHECK_FILE_ACCESS
9373229Spstnull_file_name:
93813575Spst#endif	/* CHECK_FILE_ACCESS */
9393229Spst
9403229Spst
9413229Spst	/*
9423229Spst	 * Handle vendor options based on magic number.
9433229Spst	 */
9443229Spst
9453229Spst	if (debug > 1) {
9463229Spst		report(LOG_INFO, "vendor magic field is %d.%d.%d.%d",
9473229Spst			   (int) ((bp->bp_vend)[0]),
9483229Spst			   (int) ((bp->bp_vend)[1]),
9493229Spst			   (int) ((bp->bp_vend)[2]),
9503229Spst			   (int) ((bp->bp_vend)[3]));
9513229Spst	}
9523229Spst	/*
9533229Spst	 * If this host isn't set for automatic vendor info then copy the
9543229Spst	 * specific cookie into the bootp packet, thus forcing a certain
9553229Spst	 * reply format.  Only force reply format if user specified it.
9563229Spst	 */
9573229Spst	if (hp->flags.vm_cookie) {
9583229Spst		/* Slam in the user specified magic number. */
9593229Spst		bcopy(hp->vm_cookie, bp->bp_vend, 4);
9603229Spst	}
9613229Spst	/*
9623229Spst	 * Figure out the format for the vendor-specific info.
9633229Spst	 * Note that bp->bp_vend may have been set above.
9643229Spst	 */
9653229Spst	if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) {
9663229Spst		/* RFC1048 conformant bootp client */
9673229Spst		dovend_rfc1048(bp, hp, bootsize);
9683229Spst		if (debug > 1) {
9693229Spst			report(LOG_INFO, "sending reply (with RFC1048 options)");
9703229Spst		}
9713229Spst	}
9723229Spst#ifdef VEND_CMU
9733229Spst	else if (!bcmp(bp->bp_vend, vm_cmu, 4)) {
9743229Spst		dovend_cmu(bp, hp);
9753229Spst		if (debug > 1) {
9763229Spst			report(LOG_INFO, "sending reply (with CMU options)");
9773229Spst		}
9783229Spst	}
9793229Spst#endif
9803229Spst	else {
9813229Spst		if (debug > 1) {
9823229Spst			report(LOG_INFO, "sending reply (with no options)");
9833229Spst		}
9843229Spst	}
9853229Spst
9863229Spst	dest = (hp->flags.reply_addr) ?
9873229Spst		hp->reply_addr.s_addr : 0L;
9883229Spst
9893229Spst	/* not forwarded */
9903229Spst	sendreply(0, dest);
9913229Spst}
9923229Spst
9933229Spst
9943229Spst/*
9953229Spst * Process BOOTREPLY packet.
9963229Spst */
9973229SpstPRIVATE void
9983229Spsthandle_reply()
9993229Spst{
10003229Spst	if (debug) {
10013229Spst		report(LOG_INFO, "processing boot reply");
10023229Spst	}
10033229Spst	/* forwarded, no destination override */
10043229Spst	sendreply(1, 0);
10053229Spst}
10063229Spst
10073229Spst
10083229Spst/*
10093229Spst * Send a reply packet to the client.  'forward' flag is set if we are
10103229Spst * not the originator of this reply packet.
10113229Spst */
10123229SpstPRIVATE void
10133229Spstsendreply(forward, dst_override)
10143229Spst	int forward;
10153229Spst	int32 dst_override;
10163229Spst{
10173229Spst	struct bootp *bp = (struct bootp *) pktbuf;
10183229Spst	struct in_addr dst;
10193229Spst	u_short port = bootpc_port;
10203229Spst	unsigned char *ha;
102113575Spst	int len, haf;
10223229Spst
10233229Spst	/*
10243229Spst	 * XXX - Should honor bp_flags "broadcast" bit here.
10253229Spst	 * Temporary workaround: use the :ra=ADDR: option to
10263229Spst	 * set the reply address to the broadcast address.
10273229Spst	 */
10283229Spst
10293229Spst	/*
10303229Spst	 * If the destination address was specified explicitly
10313229Spst	 * (i.e. the broadcast address for HP compatiblity)
10323229Spst	 * then send the response to that address.  Otherwise,
10333229Spst	 * act in accordance with RFC951:
10343229Spst	 *   If the client IP address is specified, use that
10353229Spst	 * else if gateway IP address is specified, use that
10363229Spst	 * else make a temporary arp cache entry for the client's
10373229Spst	 * NEW IP/hardware address and use that.
10383229Spst	 */
10393229Spst	if (dst_override) {
10403229Spst		dst.s_addr = dst_override;
10413229Spst		if (debug > 1) {
10423229Spst			report(LOG_INFO, "reply address override: %s",
10433229Spst				   inet_ntoa(dst));
10443229Spst		}
10453229Spst	} else if (bp->bp_ciaddr.s_addr) {
10463229Spst		dst = bp->bp_ciaddr;
10473229Spst	} else if (bp->bp_giaddr.s_addr && forward == 0) {
10483229Spst		dst = bp->bp_giaddr;
10493229Spst		port = bootps_port;
10503229Spst		if (debug > 1) {
10513229Spst			report(LOG_INFO, "sending reply to gateway %s",
10523229Spst				   inet_ntoa(dst));
10533229Spst		}
10543229Spst	} else {
10553229Spst		dst = bp->bp_yiaddr;
10563229Spst		ha = bp->bp_chaddr;
10573229Spst		len = bp->bp_hlen;
10583229Spst		if (len > MAXHADDRLEN)
10593229Spst			len = MAXHADDRLEN;
106013575Spst		haf = (int) bp->bp_htype;
106113575Spst		if (haf == 0)
106213575Spst			haf = HTYPE_ETHERNET;
10633229Spst
10643229Spst		if (debug > 1)
10653229Spst			report(LOG_INFO, "setarp %s - %s",
10663229Spst				   inet_ntoa(dst), haddrtoa(ha, len));
106713575Spst		setarp(s, &dst, haf, ha, len);
10683229Spst	}
10693229Spst
10703229Spst	if ((forward == 0) &&
10713229Spst		(bp->bp_siaddr.s_addr == 0))
10723229Spst	{
10733229Spst		struct ifreq *ifr;
10743229Spst		struct in_addr siaddr;
10753229Spst		/*
10763229Spst		 * If we are originating this reply, we
10773229Spst		 * need to find our own interface address to
10783229Spst		 * put in the bp_siaddr field of the reply.
10793229Spst		 * If this server is multi-homed, pick the
10803229Spst		 * 'best' interface (the one on the same net
10813229Spst		 * as the client).  Of course, the client may
10823229Spst		 * be on the other side of a BOOTP gateway...
10833229Spst		 */
10843229Spst		ifr = getif(s, &dst);
10853229Spst		if (ifr) {
10863229Spst			struct sockaddr_in *sip;
10873229Spst			sip = (struct sockaddr_in *) &(ifr->ifr_addr);
10883229Spst			siaddr = sip->sin_addr;
10893229Spst		} else {
10903229Spst			/* Just use my "official" IP address. */
10913229Spst			siaddr = my_ip_addr;
10923229Spst		}
10933229Spst
10943229Spst		/* XXX - No need to set bp_giaddr here. */
10953229Spst
10963229Spst		/* Finally, set the server address field. */
10973229Spst		bp->bp_siaddr = siaddr;
10983229Spst	}
10993229Spst	/* Set up socket address for send. */
11003229Spst	send_addr.sin_family = AF_INET;
11013229Spst	send_addr.sin_port = htons(port);
11023229Spst	send_addr.sin_addr = dst;
11033229Spst
11043229Spst	/* Send reply with same size packet as request used. */
11053229Spst	if (sendto(s, pktbuf, pktlen, 0,
11063229Spst			   (struct sockaddr *) &send_addr,
11073229Spst			   sizeof(send_addr)) < 0)
11083229Spst	{
11093229Spst		report(LOG_ERR, "sendto: %s", get_network_errmsg());
11103229Spst	}
11113229Spst} /* sendreply */
11123229Spst
11133229Spst
11143229Spst/* nmatch() - now in getif.c */
11153229Spst/* setarp() - now in hwaddr.c */
11163229Spst
11173229Spst
11183229Spst/*
11193229Spst * This call checks read access to a file.  It returns 0 if the file given
11203229Spst * by "path" exists and is publically readable.  A value of -1 is returned if
11213229Spst * access is not permitted or an error occurs.  Successful calls also
11223229Spst * return the file size in bytes using the long pointer "filesize".
11233229Spst *
11243229Spst * The read permission bit for "other" users is checked.  This bit must be
11253229Spst * set for tftpd(8) to allow clients to read the file.
11263229Spst */
11273229Spst
11283229SpstPRIVATE int
11293229Spstchk_access(path, filesize)
11303229Spst	char *path;
11313229Spst	int32 *filesize;
11323229Spst{
11333229Spst	struct stat st;
11343229Spst
11353229Spst	if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) {
11363229Spst		*filesize = (int32) st.st_size;
11373229Spst		return 0;
11383229Spst	} else {
11393229Spst		return -1;
11403229Spst	}
11413229Spst}
11423229Spst
11433229Spst
11443229Spst/*
11453229Spst * Now in dumptab.c :
11463229Spst *	dumptab()
11473229Spst *	dump_host()
11483229Spst *	list_ipaddresses()
11493229Spst */
11503229Spst
11513229Spst#ifdef VEND_CMU
11523229Spst
11533229Spst/*
11543229Spst * Insert the CMU "vendor" data for the host pointed to by "hp" into the
11553229Spst * bootp packet pointed to by "bp".
11563229Spst */
11573229Spst
11583229SpstPRIVATE void
11593229Spstdovend_cmu(bp, hp)
11603229Spst	struct bootp *bp;
11613229Spst	struct host *hp;
11623229Spst{
11633229Spst	struct cmu_vend *vendp;
11643229Spst	struct in_addr_list *taddr;
11653229Spst
11663229Spst	/*
11673229Spst	 * Initialize the entire vendor field to zeroes.
11683229Spst	 */
11693229Spst	bzero(bp->bp_vend, sizeof(bp->bp_vend));
11703229Spst
11713229Spst	/*
11723229Spst	 * Fill in vendor information. Subnet mask, default gateway,
11733229Spst	 * domain name server, ien name server, time server
11743229Spst	 */
11753229Spst	vendp = (struct cmu_vend *) bp->bp_vend;
11763229Spst	strcpy(vendp->v_magic, (char *)vm_cmu);
11773229Spst	if (hp->flags.subnet_mask) {
11783229Spst		(vendp->v_smask).s_addr = hp->subnet_mask.s_addr;
11793229Spst		(vendp->v_flags) |= VF_SMASK;
11803229Spst		if (hp->flags.gateway) {
11813229Spst			(vendp->v_dgate).s_addr = hp->gateway->addr->s_addr;
11823229Spst		}
11833229Spst	}
11843229Spst	if (hp->flags.domain_server) {
11853229Spst		taddr = hp->domain_server;
11863229Spst		if (taddr->addrcount > 0) {
11873229Spst			(vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr;
11883229Spst			if (taddr->addrcount > 1) {
11893229Spst				(vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr;
11903229Spst			}
11913229Spst		}
11923229Spst	}
11933229Spst	if (hp->flags.name_server) {
11943229Spst		taddr = hp->name_server;
11953229Spst		if (taddr->addrcount > 0) {
11963229Spst			(vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr;
11973229Spst			if (taddr->addrcount > 1) {
11983229Spst				(vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr;
11993229Spst			}
12003229Spst		}
12013229Spst	}
12023229Spst	if (hp->flags.time_server) {
12033229Spst		taddr = hp->time_server;
12043229Spst		if (taddr->addrcount > 0) {
12053229Spst			(vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr;
12063229Spst			if (taddr->addrcount > 1) {
12073229Spst				(vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr;
12083229Spst			}
12093229Spst		}
12103229Spst	}
12113229Spst	/* Log message now done by caller. */
12123229Spst} /* dovend_cmu */
12133229Spst
12143229Spst#endif /* VEND_CMU */
12153229Spst
12163229Spst
12173229Spst
12183229Spst/*
12193229Spst * Insert the RFC1048 vendor data for the host pointed to by "hp" into the
12203229Spst * bootp packet pointed to by "bp".
12213229Spst */
12223229Spst#define	NEED(LEN, MSG) do \
12233229Spst	if (bytesleft < (LEN)) { \
12243229Spst		report(LOG_NOTICE, noroom, \
12253229Spst			   hp->hostname->string, MSG); \
12263229Spst		return; \
12273229Spst	} while (0)
12283229SpstPRIVATE void
12293229Spstdovend_rfc1048(bp, hp, bootsize)
12303229Spst	struct bootp *bp;
12313229Spst	struct host *hp;
12323229Spst	int32 bootsize;
12333229Spst{
12343229Spst	int bytesleft, len;
12353229Spst	byte *vp;
12363229Spst
12373229Spst	static char noroom[] = "%s: No room for \"%s\" option";
12383229Spst
12393229Spst	vp = bp->bp_vend;
12403229Spst
12413229Spst	if (hp->flags.msg_size) {
12423229Spst		pktlen = hp->msg_size;
12433229Spst	} else {
12443229Spst		/*
12453229Spst		 * If the request was longer than the official length, build
12463229Spst		 * a response of that same length where the additional length
12473229Spst		 * is assumed to be part of the bp_vend (options) area.
12483229Spst		 */
12493229Spst		if (pktlen > sizeof(*bp)) {
12503229Spst			if (debug > 1)
12513229Spst				report(LOG_INFO, "request message length=%d", pktlen);
12523229Spst		}
12533229Spst		/*
12543229Spst		 * Check whether the request contains the option:
12553229Spst		 * Maximum DHCP Message Size (RFC1533 sec. 9.8)
12563229Spst		 * and if so, override the response length with its value.
12573229Spst		 * This request must lie within the first BP_VEND_LEN
12583229Spst		 * bytes of the option space.
12593229Spst		 */
12603229Spst		{
12613229Spst			byte *p, *ep;
12623229Spst			byte tag, len;
12633229Spst			short msgsz = 0;
12648870Srgrimes
12653229Spst			p = vp + 4;
12663229Spst			ep = p + BP_VEND_LEN - 4;
12673229Spst			while (p < ep) {
12683229Spst				tag = *p++;
12693229Spst				/* Check for tags with no data first. */
12703229Spst				if (tag == TAG_PAD)
12713229Spst					continue;
12723229Spst				if (tag == TAG_END)
12733229Spst					break;
12743229Spst				/* Now scan the length byte. */
12753229Spst				len = *p++;
12763229Spst				switch (tag) {
12773229Spst				case TAG_MAX_MSGSZ:
12783229Spst					if (len == 2) {
12793229Spst						bcopy(p, (char*)&msgsz, 2);
12803229Spst						msgsz = ntohs(msgsz);
12813229Spst					}
12823229Spst					break;
12833229Spst				case TAG_SUBNET_MASK:
12843229Spst					/* XXX - Should preserve this if given... */
12853229Spst					break;
12863229Spst				} /* swtich */
12873229Spst				p += len;
12883229Spst			}
12893229Spst
12903229Spst			if (msgsz > sizeof(*bp)) {
12913229Spst				if (debug > 1)
12923229Spst					report(LOG_INFO, "request has DHCP msglen=%d", msgsz);
12933229Spst				pktlen = msgsz;
12943229Spst			}
12953229Spst		}
12963229Spst	}
12973229Spst
12983229Spst	if (pktlen < sizeof(*bp)) {
12993229Spst		report(LOG_ERR, "invalid response length=%d", pktlen);
13003229Spst		pktlen = sizeof(*bp);
13013229Spst	}
13023229Spst	bytesleft = ((byte*)bp + pktlen) - vp;
13033229Spst	if (pktlen > sizeof(*bp)) {
13043229Spst		if (debug > 1)
13053229Spst			report(LOG_INFO, "extended reply, length=%d, options=%d",
13063229Spst				   pktlen, bytesleft);
13073229Spst	}
13083229Spst
13093229Spst	/* Copy in the magic cookie */
13103229Spst	bcopy(vm_rfc1048, vp, 4);
13113229Spst	vp += 4;
13123229Spst	bytesleft -= 4;
13133229Spst
13143229Spst	if (hp->flags.subnet_mask) {
13153229Spst		/* always enough room here. */
13163229Spst		*vp++ = TAG_SUBNET_MASK;/* -1 byte  */
13173229Spst		*vp++ = 4;				/* -1 byte  */
13183229Spst		insert_u_long(hp->subnet_mask.s_addr, &vp);	/* -4 bytes */
13193229Spst		bytesleft -= 6;			/* Fix real count */
13203229Spst		if (hp->flags.gateway) {
13213229Spst			(void) insert_ip(TAG_GATEWAY,
13223229Spst							 hp->gateway,
13233229Spst							 &vp, &bytesleft);
13243229Spst		}
13253229Spst	}
13263229Spst	if (hp->flags.bootsize) {
13273229Spst		/* always enough room here */
13283229Spst		bootsize = (hp->flags.bootsize_auto) ?
13293229Spst			((bootsize + 511) / 512) : (hp->bootsize);	/* Round up */
13303229Spst		*vp++ = TAG_BOOT_SIZE;
13313229Spst		*vp++ = 2;
13323229Spst		*vp++ = (byte) ((bootsize >> 8) & 0xFF);
13333229Spst		*vp++ = (byte) (bootsize & 0xFF);
13343229Spst		bytesleft -= 4;			/* Tag, length, and 16 bit blocksize */
13353229Spst	}
13363229Spst	/*
13373229Spst	 * This one is special: Remaining options go in the ext file.
13383229Spst	 * Only the subnet_mask, bootsize, and gateway should precede.
13393229Spst	 */
13403229Spst	if (hp->flags.exten_file) {
13413229Spst		/*
13423229Spst		 * Check for room for exten_file.  Add 3 to account for
13433229Spst		 * TAG_EXTEN_FILE, length, and TAG_END.
13443229Spst		 */
13453229Spst		len = strlen(hp->exten_file->string);
13463229Spst		NEED((len + 3), "ef");
13473229Spst		*vp++ = TAG_EXTEN_FILE;
13483229Spst		*vp++ = (byte) (len & 0xFF);
13493229Spst		bcopy(hp->exten_file->string, vp, len);
13503229Spst		vp += len;
13513229Spst		*vp++ = TAG_END;
13523229Spst		bytesleft -= len + 3;
13533229Spst		return;					/* no more options here. */
13543229Spst	}
13553229Spst	/*
13563229Spst	 * The remaining options are inserted by the following
13573229Spst	 * function (which is shared with bootpef.c).
13583229Spst	 * Keep back one byte for the TAG_END.
13593229Spst	 */
13603229Spst	len = dovend_rfc1497(hp, vp, bytesleft - 1);
13613229Spst	vp += len;
13623229Spst	bytesleft -= len;
13633229Spst
13643229Spst	/* There should be at least one byte left. */
13653229Spst	NEED(1, "(end)");
13663229Spst	*vp++ = TAG_END;
13673229Spst	bytesleft--;
13683229Spst
13693229Spst	/* Log message done by caller. */
13703229Spst	if (bytesleft > 0) {
13713229Spst		/*
13723229Spst		 * Zero out any remaining part of the vendor area.
13733229Spst		 */
13743229Spst		bzero(vp, bytesleft);
13753229Spst	}
13763229Spst} /* dovend_rfc1048 */
13773229Spst#undef	NEED
13783229Spst
13793229Spst
13803229Spst/*
13813229Spst * Now in readfile.c:
13823229Spst * 	hwlookcmp()
13833229Spst *	iplookcmp()
13843229Spst */
13853229Spst
13863229Spst/* haddrtoa() - now in hwaddr.c */
13873229Spst/*
13883229Spst * Now in dovend.c:
13893229Spst * insert_ip()
13903229Spst * insert_generic()
13913229Spst * insert_u_long()
13923229Spst */
13933229Spst
13943229Spst/* get_errmsg() - now in report.c */
13953229Spst
13963229Spst/*
13973229Spst * Local Variables:
13983229Spst * tab-width: 4
13993229Spst * c-indent-level: 4
14003229Spst * c-argdecl-indent: 4
14013229Spst * c-continued-statement-offset: 4
14023229Spst * c-continued-brace-offset: -4
14033229Spst * c-label-offset: -4
14043229Spst * c-brace-offset: 0
14053229Spst * End:
14063229Spst */
1407