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