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