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