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: stable/11/libexec/bootpd/bootpd.c 342229 2018-12-19 18:19:15Z emaste $");
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
639342229Semaste	if (bp->bp_htype >= hwinfocnt) {
640342229Semaste		report(LOG_NOTICE, "bad hw addr type %u", bp->bp_htype);
641342229Semaste		return;
642342229Semaste	}
64341734Seivind	bp->bp_file[sizeof(bp->bp_file)-1] = '\0';
64441734Seivind
6453229Spst	/* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
6463229Spst
6473229Spst	/*
6483229Spst	 * If the servername field is set, compare it against us.
6493229Spst	 * If we're not being addressed, ignore this request.
6503229Spst	 * If the server name field is null, throw in our name.
6513229Spst	 */
6523229Spst	if (strlen(bp->bp_sname)) {
6533229Spst		if (strcmp(bp->bp_sname, hostname)) {
6543229Spst			if (debug)
6553229Spst				report(LOG_INFO, "\
6563229Spstignoring request for server %s from client at %s address %s",
6573229Spst					   bp->bp_sname, netname(bp->bp_htype),
6583229Spst					   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
6593229Spst			/* XXX - Is it correct to ignore such a request? -gwr */
6603229Spst			return;
6613229Spst		}
6623229Spst	} else {
6633229Spst		strcpy(bp->bp_sname, hostname);
6643229Spst	}
6653229Spst
6663229Spst	/* Convert the request into a reply. */
6673229Spst	bp->bp_op = BOOTREPLY;
6683229Spst	if (bp->bp_ciaddr.s_addr == 0) {
6693229Spst		/*
670229780Suqs		 * client doesn't know his IP address,
6713229Spst		 * search by hardware address.
6723229Spst		 */
6733229Spst		if (debug > 1) {
6743229Spst			report(LOG_INFO, "request from %s address %s",
6753229Spst				   netname(bp->bp_htype),
6763229Spst				   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
6773229Spst		}
6783229Spst		hlen = haddrlength(bp->bp_htype);
6793229Spst		if (hlen != bp->bp_hlen) {
680116371Sjmg			report(LOG_NOTICE, "bad addr len from %s address %s",
6813229Spst				   netname(bp->bp_htype),
6823229Spst				   haddrtoa(bp->bp_chaddr, hlen));
6833229Spst		}
6843229Spst		dummyhost.htype = bp->bp_htype;
6853229Spst		bcopy(bp->bp_chaddr, dummyhost.haddr, hlen);
6863229Spst		hashcode = hash_HashFunction(bp->bp_chaddr, hlen);
6873229Spst		hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp,
6883229Spst										 &dummyhost);
6893229Spst		if (hp == NULL &&
6903229Spst			bp->bp_htype == HTYPE_IEEE802)
6913229Spst		{
6923229Spst			/* Try again with address in "canonical" form. */
6933229Spst			haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen);
6943229Spst			if (debug > 1) {
6953229Spst				report(LOG_INFO, "\
6963229SpstHW addr type is IEEE 802.  convert to %s and check again\n",
6973229Spst					   haddrtoa(dummyhost.haddr, bp->bp_hlen));
6983229Spst			}
6993229Spst			hashcode = hash_HashFunction(dummyhost.haddr, hlen);
7003229Spst			hp = (struct host *) hash_Lookup(hwhashtable, hashcode,
7013229Spst											 hwlookcmp, &dummyhost);
7023229Spst		}
7033229Spst		if (hp == NULL) {
7043229Spst			/*
7053229Spst			 * XXX - Add dynamic IP address assignment?
7063229Spst			 */
70713575Spst			if (debug)
70813575Spst				report(LOG_NOTICE, "unknown client %s address %s",
7093229Spst					   netname(bp->bp_htype),
7103229Spst					   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
7113229Spst			return; /* not found */
7123229Spst		}
7133229Spst		(bp->bp_yiaddr).s_addr = hp->iaddr.s_addr;
7143229Spst
7153229Spst	} else {
7163229Spst
7173229Spst		/*
7183229Spst		 * search by IP address.
7193229Spst		 */
7203229Spst		if (debug > 1) {
7213229Spst			report(LOG_INFO, "request from IP addr %s",
7223229Spst				   inet_ntoa(bp->bp_ciaddr));
7233229Spst		}
7243229Spst		dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr;
7253229Spst		hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4);
7263229Spst		hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp,
7273229Spst										 &dummyhost);
7283229Spst		if (hp == NULL) {
72913575Spst			if (debug) {
7303229Spst				report(LOG_NOTICE, "IP address not found: %s",
7313229Spst					   inet_ntoa(bp->bp_ciaddr));
7323229Spst			}
7333229Spst			return;
7343229Spst		}
7353229Spst	}
7363229Spst
7373229Spst	if (debug) {
7383229Spst		report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr),
7393229Spst			   hp->hostname->string);
7403229Spst	}
7413229Spst
7423229Spst	/*
7433229Spst	 * If there is a response delay threshold, ignore requests
7443229Spst	 * with a timestamp lower than the threshold.
7453229Spst	 */
7463229Spst	if (hp->flags.min_wait) {
7473229Spst		u_int32 t = (u_int32) ntohs(bp->bp_secs);
7483229Spst		if (t < hp->min_wait) {
7493229Spst			if (debug > 1)
7503229Spst				report(LOG_INFO,
7513229Spst					   "ignoring request due to timestamp (%d < %d)",
7523229Spst					   t, hp->min_wait);
7533229Spst			return;
7543229Spst		}
7553229Spst	}
7563229Spst
7573229Spst#ifdef	YORK_EX_OPTION
7583229Spst	/*
7593229Spst	 * The need for the "ex" tag arose out of the need to empty
7603229Spst	 * shared networked drives on diskless PCs.  This solution is
7613229Spst	 * not very clean but it does work fairly well.
7623229Spst	 * Written by Edmund J. Sutcliffe <edmund@york.ac.uk>
7633229Spst	 *
7643229Spst	 * XXX - This could compromise security if a non-trusted user
7653229Spst	 * managed to write an entry in the bootptab with :ex=trojan:
7663229Spst	 * so I would leave this turned off unless you need it. -gwr
7673229Spst	 */
7683229Spst	/* Run a program, passing the client name as a parameter. */
7693229Spst	if (hp->flags.exec_file) {
7703229Spst		char tst[100];
7713229Spst		/* XXX - Check string lengths? -gwr */
7723229Spst		strcpy (tst, hp->exec_file->string);
7733229Spst		strcat (tst, " ");
7743229Spst		strcat (tst, hp->hostname->string);
7753229Spst		strcat (tst, " &");
7763229Spst		if (debug)
7773229Spst			report(LOG_INFO, "executing %s", tst);
7783229Spst		system(tst);	/* Hope this finishes soon... */
7793229Spst	}
7803229Spst#endif	/* YORK_EX_OPTION */
7813229Spst
7823229Spst	/*
7833229Spst	 * If a specific TFTP server address was specified in the bootptab file,
7843229Spst	 * fill it in, otherwise zero it.
7853229Spst	 * XXX - Rather than zero it, should it be the bootpd address? -gwr
7863229Spst	 */
7873229Spst	(bp->bp_siaddr).s_addr = (hp->flags.bootserver) ?
7883229Spst		hp->bootserver.s_addr : 0L;
7893229Spst
7903229Spst#ifdef	STANFORD_PROM_COMPAT
7913229Spst	/*
7923229Spst	 * Stanford bootp PROMs (for a Sun?) have no way to leave
7933229Spst	 * the boot file name field blank (because the boot file
7943229Spst	 * name is automatically generated from some index).
7953229Spst	 * As a work-around, this little hack allows those PROMs to
7963229Spst	 * specify "sunboot14" with the same effect as a NULL name.
7973229Spst	 * (The user specifies boot device 14 or some such magic.)
7983229Spst	 */
7993229Spst	if (strcmp(bp->bp_file, "sunboot14") == 0)
8003229Spst		bp->bp_file[0] = '\0';	/* treat it as unspecified */
8013229Spst#endif
8023229Spst
8033229Spst	/*
8043229Spst	 * Fill in the client's proper bootfile.
8053229Spst	 *
8063229Spst	 * If the client specifies an absolute path, try that file with a
8073229Spst	 * ".host" suffix and then without.  If the file cannot be found, no
8083229Spst	 * reply is made at all.
8093229Spst	 *
8103229Spst	 * If the client specifies a null or relative file, use the following
8113229Spst	 * table to determine the appropriate action:
8123229Spst	 *
8133229Spst	 *  Homedir      Bootfile    Client's file
8143229Spst	 * specified?   specified?   specification   Action
8153229Spst	 * -------------------------------------------------------------------
8163229Spst	 *      No          No          Null         Send null filename
8173229Spst	 *      No          No          Relative     Discard request
8183229Spst	 *      No          Yes         Null         Send if absolute else null
8193229Spst	 *      No          Yes         Relative     Discard request     *XXX
8203229Spst	 *      Yes         No          Null         Send null filename
8213229Spst	 *      Yes         No          Relative     Lookup with ".host"
8223229Spst	 *      Yes         Yes         Null         Send home/boot or bootfile
8233229Spst	 *      Yes         Yes         Relative     Lookup with ".host" *XXX
8243229Spst	 *
8253229Spst	 */
8263229Spst
8273229Spst	/*
8283229Spst	 * XXX - I don't like the policy of ignoring a client when the
8293229Spst	 * boot file is not accessible.  The TFTP server might not be
8303229Spst	 * running on the same machine as the BOOTP server, in which
8313229Spst	 * case checking accessibility of the boot file is pointless.
8323229Spst	 *
8333229Spst	 * Therefore, file accessibility is now demanded ONLY if you
8343229Spst	 * define CHECK_FILE_ACCESS in the Makefile options. -gwr
8353229Spst	 */
8363229Spst
8373229Spst	/*
8383229Spst	 * The "real" path is as seen by the BOOTP daemon on this
8393229Spst	 * machine, while the client path is relative to the TFTP
8403229Spst	 * daemon chroot directory (i.e. /tftpboot).
8413229Spst	 */
8423229Spst	if (hp->flags.tftpdir) {
84341699Sdillon		snprintf(realpath, sizeof(realpath), "%s", hp->tftpdir->string);
8443229Spst		clntpath = &realpath[strlen(realpath)];
8453229Spst	} else {
8463229Spst		realpath[0] = '\0';
8473229Spst		clntpath = realpath;
8483229Spst	}
8493229Spst
8503229Spst	/*
8513229Spst	 * Determine client's requested homedir and bootfile.
8523229Spst	 */
8533229Spst	homedir = NULL;
8543229Spst	bootfile = NULL;
8553229Spst	if (bp->bp_file[0]) {
8563229Spst		homedir = bp->bp_file;
8573229Spst		bootfile = strrchr(homedir, '/');
8583229Spst		if (bootfile) {
8593229Spst			if (homedir == bootfile)
8603229Spst				homedir = NULL;
8613229Spst			*bootfile++ = '\0';
8623229Spst		} else {
8633229Spst			/* no "/" in the string */
8643229Spst			bootfile = homedir;
8653229Spst			homedir = NULL;
8663229Spst		}
8673229Spst		if (debug > 2) {
8683229Spst			report(LOG_INFO, "requested path=\"%s\"  file=\"%s\"",
8693229Spst				   (homedir) ? homedir : "",
8703229Spst				   (bootfile) ? bootfile : "");
8713229Spst		}
8723229Spst	}
8733229Spst
8743229Spst	/*
8753229Spst	 * Specifications in bootptab override client requested values.
8763229Spst	 */
8773229Spst	if (hp->flags.homedir)
8783229Spst		homedir = hp->homedir->string;
8793229Spst	if (hp->flags.bootfile)
8803229Spst		bootfile = hp->bootfile->string;
8813229Spst
8823229Spst	/*
8833229Spst	 * Construct bootfile path.
8843229Spst	 */
8853229Spst	if (homedir) {
8863229Spst		if (homedir[0] != '/')
8873229Spst			strcat(clntpath, "/");
8883229Spst		strcat(clntpath, homedir);
8893229Spst		homedir = NULL;
8903229Spst	}
8913229Spst	if (bootfile) {
8923229Spst		if (bootfile[0] != '/')
8933229Spst			strcat(clntpath, "/");
8943229Spst		strcat(clntpath, bootfile);
8953229Spst		bootfile = NULL;
8963229Spst	}
8973229Spst
8983229Spst	/*
8993229Spst	 * First try to find the file with a ".host" suffix
9003229Spst	 */
9013229Spst	n = strlen(clntpath);
9023229Spst	strcat(clntpath, ".");
9033229Spst	strcat(clntpath, hp->hostname->string);
9043229Spst	if (chk_access(realpath, &bootsize) < 0) {
9053229Spst		clntpath[n] = 0;			/* Try it without the suffix */
9063229Spst		if (chk_access(realpath, &bootsize) < 0) {
9073229Spst			/* neither "file.host" nor "file" was found */
9083229Spst#ifdef	CHECK_FILE_ACCESS
9093229Spst
9103229Spst			if (bp->bp_file[0]) {
9113229Spst				/*
9123229Spst				 * Client wanted specific file
9133229Spst				 * and we didn't have it.
9143229Spst				 */
9153229Spst				report(LOG_NOTICE,
9163229Spst					   "requested file not found: \"%s\"", clntpath);
9173229Spst				return;
9183229Spst			}
9193229Spst			/*
9203229Spst			 * Client didn't ask for a specific file and we couldn't
9213229Spst			 * access the default file, so just zero-out the bootfile
9223229Spst			 * field in the packet and continue processing the reply.
9233229Spst			 */
9243229Spst			bzero(bp->bp_file, sizeof(bp->bp_file));
9253229Spst			goto null_file_name;
9263229Spst
9273229Spst#else	/* CHECK_FILE_ACCESS */
9283229Spst
9293229Spst			/* Complain only if boot file size was needed. */
9303229Spst			if (hp->flags.bootsize_auto) {
9313229Spst				report(LOG_ERR, "can not determine size of file \"%s\"",
9323229Spst					   clntpath);
9333229Spst			}
9343229Spst
9353229Spst#endif	/* CHECK_FILE_ACCESS */
9363229Spst		}
9373229Spst	}
9383229Spst	strncpy(bp->bp_file, clntpath, BP_FILE_LEN);
9393229Spst	if (debug > 2)
9403229Spst		report(LOG_INFO, "bootfile=\"%s\"", clntpath);
9413229Spst
94213575Spst#ifdef	CHECK_FILE_ACCESS
9433229Spstnull_file_name:
94413575Spst#endif	/* CHECK_FILE_ACCESS */
9453229Spst
9463229Spst
9473229Spst	/*
9483229Spst	 * Handle vendor options based on magic number.
9493229Spst	 */
9503229Spst
9513229Spst	if (debug > 1) {
9523229Spst		report(LOG_INFO, "vendor magic field is %d.%d.%d.%d",
9533229Spst			   (int) ((bp->bp_vend)[0]),
9543229Spst			   (int) ((bp->bp_vend)[1]),
9553229Spst			   (int) ((bp->bp_vend)[2]),
9563229Spst			   (int) ((bp->bp_vend)[3]));
9573229Spst	}
9583229Spst	/*
9593229Spst	 * If this host isn't set for automatic vendor info then copy the
9603229Spst	 * specific cookie into the bootp packet, thus forcing a certain
9613229Spst	 * reply format.  Only force reply format if user specified it.
9623229Spst	 */
9633229Spst	if (hp->flags.vm_cookie) {
9643229Spst		/* Slam in the user specified magic number. */
9653229Spst		bcopy(hp->vm_cookie, bp->bp_vend, 4);
9663229Spst	}
9673229Spst	/*
9683229Spst	 * Figure out the format for the vendor-specific info.
9693229Spst	 * Note that bp->bp_vend may have been set above.
9703229Spst	 */
9713229Spst	if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) {
9723229Spst		/* RFC1048 conformant bootp client */
9733229Spst		dovend_rfc1048(bp, hp, bootsize);
9743229Spst		if (debug > 1) {
9753229Spst			report(LOG_INFO, "sending reply (with RFC1048 options)");
9763229Spst		}
9773229Spst	}
9783229Spst#ifdef VEND_CMU
9793229Spst	else if (!bcmp(bp->bp_vend, vm_cmu, 4)) {
9803229Spst		dovend_cmu(bp, hp);
9813229Spst		if (debug > 1) {
9823229Spst			report(LOG_INFO, "sending reply (with CMU options)");
9833229Spst		}
9843229Spst	}
9853229Spst#endif
9863229Spst	else {
9873229Spst		if (debug > 1) {
9883229Spst			report(LOG_INFO, "sending reply (with no options)");
9893229Spst		}
9903229Spst	}
9913229Spst
9923229Spst	dest = (hp->flags.reply_addr) ?
9933229Spst		hp->reply_addr.s_addr : 0L;
9943229Spst
9953229Spst	/* not forwarded */
9963229Spst	sendreply(0, dest);
9973229Spst}
9983229Spst
9993229Spst
10003229Spst/*
10013229Spst * Process BOOTREPLY packet.
10023229Spst */
10033229SpstPRIVATE void
10043229Spsthandle_reply()
10053229Spst{
10063229Spst	if (debug) {
10073229Spst		report(LOG_INFO, "processing boot reply");
10083229Spst	}
10093229Spst	/* forwarded, no destination override */
10103229Spst	sendreply(1, 0);
10113229Spst}
10123229Spst
10133229Spst
10143229Spst/*
10153229Spst * Send a reply packet to the client.  'forward' flag is set if we are
10163229Spst * not the originator of this reply packet.
10173229Spst */
10183229SpstPRIVATE void
10193229Spstsendreply(forward, dst_override)
10203229Spst	int forward;
10213229Spst	int32 dst_override;
10223229Spst{
10233229Spst	struct bootp *bp = (struct bootp *) pktbuf;
10243229Spst	struct in_addr dst;
10253229Spst	u_short port = bootpc_port;
10263229Spst	unsigned char *ha;
102713575Spst	int len, haf;
10283229Spst
10293229Spst	/*
10303229Spst	 * XXX - Should honor bp_flags "broadcast" bit here.
10313229Spst	 * Temporary workaround: use the :ra=ADDR: option to
10323229Spst	 * set the reply address to the broadcast address.
10333229Spst	 */
10343229Spst
10353229Spst	/*
10363229Spst	 * If the destination address was specified explicitly
1037116371Sjmg	 * (i.e. the broadcast address for HP compatibility)
10383229Spst	 * then send the response to that address.  Otherwise,
10393229Spst	 * act in accordance with RFC951:
10403229Spst	 *   If the client IP address is specified, use that
10413229Spst	 * else if gateway IP address is specified, use that
10423229Spst	 * else make a temporary arp cache entry for the client's
10433229Spst	 * NEW IP/hardware address and use that.
10443229Spst	 */
10453229Spst	if (dst_override) {
10463229Spst		dst.s_addr = dst_override;
10473229Spst		if (debug > 1) {
10483229Spst			report(LOG_INFO, "reply address override: %s",
10493229Spst				   inet_ntoa(dst));
10503229Spst		}
10513229Spst	} else if (bp->bp_ciaddr.s_addr) {
10523229Spst		dst = bp->bp_ciaddr;
10533229Spst	} else if (bp->bp_giaddr.s_addr && forward == 0) {
10543229Spst		dst = bp->bp_giaddr;
10553229Spst		port = bootps_port;
10563229Spst		if (debug > 1) {
10573229Spst			report(LOG_INFO, "sending reply to gateway %s",
10583229Spst				   inet_ntoa(dst));
10593229Spst		}
10603229Spst	} else {
10613229Spst		dst = bp->bp_yiaddr;
10623229Spst		ha = bp->bp_chaddr;
10633229Spst		len = bp->bp_hlen;
10643229Spst		if (len > MAXHADDRLEN)
10653229Spst			len = MAXHADDRLEN;
106613575Spst		haf = (int) bp->bp_htype;
106713575Spst		if (haf == 0)
106813575Spst			haf = HTYPE_ETHERNET;
10693229Spst
10703229Spst		if (debug > 1)
10713229Spst			report(LOG_INFO, "setarp %s - %s",
10723229Spst				   inet_ntoa(dst), haddrtoa(ha, len));
107313575Spst		setarp(s, &dst, haf, ha, len);
10743229Spst	}
10753229Spst
10763229Spst	if ((forward == 0) &&
10773229Spst		(bp->bp_siaddr.s_addr == 0))
10783229Spst	{
10793229Spst		struct ifreq *ifr;
10803229Spst		struct in_addr siaddr;
10813229Spst		/*
10823229Spst		 * If we are originating this reply, we
10833229Spst		 * need to find our own interface address to
10843229Spst		 * put in the bp_siaddr field of the reply.
10853229Spst		 * If this server is multi-homed, pick the
10863229Spst		 * 'best' interface (the one on the same net
10873229Spst		 * as the client).  Of course, the client may
10883229Spst		 * be on the other side of a BOOTP gateway...
10893229Spst		 */
10903229Spst		ifr = getif(s, &dst);
10913229Spst		if (ifr) {
10923229Spst			struct sockaddr_in *sip;
10933229Spst			sip = (struct sockaddr_in *) &(ifr->ifr_addr);
10943229Spst			siaddr = sip->sin_addr;
10953229Spst		} else {
10963229Spst			/* Just use my "official" IP address. */
10973229Spst			siaddr = my_ip_addr;
10983229Spst		}
10993229Spst
11003229Spst		/* XXX - No need to set bp_giaddr here. */
11013229Spst
11023229Spst		/* Finally, set the server address field. */
11033229Spst		bp->bp_siaddr = siaddr;
11043229Spst	}
11053229Spst	/* Set up socket address for send. */
11063229Spst	send_addr.sin_family = AF_INET;
11073229Spst	send_addr.sin_port = htons(port);
11083229Spst	send_addr.sin_addr = dst;
11093229Spst
11103229Spst	/* Send reply with same size packet as request used. */
11113229Spst	if (sendto(s, pktbuf, pktlen, 0,
11123229Spst			   (struct sockaddr *) &send_addr,
11133229Spst			   sizeof(send_addr)) < 0)
11143229Spst	{
11153229Spst		report(LOG_ERR, "sendto: %s", get_network_errmsg());
11163229Spst	}
11173229Spst} /* sendreply */
11183229Spst
11193229Spst
11203229Spst/* nmatch() - now in getif.c */
11213229Spst/* setarp() - now in hwaddr.c */
11223229Spst
11233229Spst
11243229Spst/*
11253229Spst * This call checks read access to a file.  It returns 0 if the file given
1126229780Suqs * by "path" exists and is publicly readable.  A value of -1 is returned if
11273229Spst * access is not permitted or an error occurs.  Successful calls also
11283229Spst * return the file size in bytes using the long pointer "filesize".
11293229Spst *
11303229Spst * The read permission bit for "other" users is checked.  This bit must be
11313229Spst * set for tftpd(8) to allow clients to read the file.
11323229Spst */
11333229Spst
11343229SpstPRIVATE int
11353229Spstchk_access(path, filesize)
11363229Spst	char *path;
11373229Spst	int32 *filesize;
11383229Spst{
11393229Spst	struct stat st;
11403229Spst
11413229Spst	if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) {
11423229Spst		*filesize = (int32) st.st_size;
11433229Spst		return 0;
11443229Spst	} else {
11453229Spst		return -1;
11463229Spst	}
11473229Spst}
11483229Spst
11493229Spst
11503229Spst/*
11513229Spst * Now in dumptab.c :
11523229Spst *	dumptab()
11533229Spst *	dump_host()
11543229Spst *	list_ipaddresses()
11553229Spst */
11563229Spst
11573229Spst#ifdef VEND_CMU
11583229Spst
11593229Spst/*
11603229Spst * Insert the CMU "vendor" data for the host pointed to by "hp" into the
11613229Spst * bootp packet pointed to by "bp".
11623229Spst */
11633229Spst
11643229SpstPRIVATE void
11653229Spstdovend_cmu(bp, hp)
11663229Spst	struct bootp *bp;
11673229Spst	struct host *hp;
11683229Spst{
11693229Spst	struct cmu_vend *vendp;
11703229Spst	struct in_addr_list *taddr;
11713229Spst
11723229Spst	/*
11733229Spst	 * Initialize the entire vendor field to zeroes.
11743229Spst	 */
11753229Spst	bzero(bp->bp_vend, sizeof(bp->bp_vend));
11763229Spst
11773229Spst	/*
11783229Spst	 * Fill in vendor information. Subnet mask, default gateway,
11793229Spst	 * domain name server, ien name server, time server
11803229Spst	 */
11813229Spst	vendp = (struct cmu_vend *) bp->bp_vend;
11823229Spst	strcpy(vendp->v_magic, (char *)vm_cmu);
11833229Spst	if (hp->flags.subnet_mask) {
11843229Spst		(vendp->v_smask).s_addr = hp->subnet_mask.s_addr;
11853229Spst		(vendp->v_flags) |= VF_SMASK;
11863229Spst		if (hp->flags.gateway) {
11873229Spst			(vendp->v_dgate).s_addr = hp->gateway->addr->s_addr;
11883229Spst		}
11893229Spst	}
11903229Spst	if (hp->flags.domain_server) {
11913229Spst		taddr = hp->domain_server;
11923229Spst		if (taddr->addrcount > 0) {
11933229Spst			(vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr;
11943229Spst			if (taddr->addrcount > 1) {
11953229Spst				(vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr;
11963229Spst			}
11973229Spst		}
11983229Spst	}
11993229Spst	if (hp->flags.name_server) {
12003229Spst		taddr = hp->name_server;
12013229Spst		if (taddr->addrcount > 0) {
12023229Spst			(vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr;
12033229Spst			if (taddr->addrcount > 1) {
12043229Spst				(vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr;
12053229Spst			}
12063229Spst		}
12073229Spst	}
12083229Spst	if (hp->flags.time_server) {
12093229Spst		taddr = hp->time_server;
12103229Spst		if (taddr->addrcount > 0) {
12113229Spst			(vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr;
12123229Spst			if (taddr->addrcount > 1) {
12133229Spst				(vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr;
12143229Spst			}
12153229Spst		}
12163229Spst	}
12173229Spst	/* Log message now done by caller. */
12183229Spst} /* dovend_cmu */
12193229Spst
12203229Spst#endif /* VEND_CMU */
12213229Spst
12223229Spst
12233229Spst
12243229Spst/*
12253229Spst * Insert the RFC1048 vendor data for the host pointed to by "hp" into the
12263229Spst * bootp packet pointed to by "bp".
12273229Spst */
12283229Spst#define	NEED(LEN, MSG) do \
12293229Spst	if (bytesleft < (LEN)) { \
12303229Spst		report(LOG_NOTICE, noroom, \
12313229Spst			   hp->hostname->string, MSG); \
12323229Spst		return; \
12333229Spst	} while (0)
12343229SpstPRIVATE void
12353229Spstdovend_rfc1048(bp, hp, bootsize)
12363229Spst	struct bootp *bp;
12373229Spst	struct host *hp;
12383229Spst	int32 bootsize;
12393229Spst{
12403229Spst	int bytesleft, len;
12413229Spst	byte *vp;
12423229Spst
124390159Skris	static const char noroom[] = "%s: No room for \"%s\" option";
12443229Spst
12453229Spst	vp = bp->bp_vend;
12463229Spst
12473229Spst	if (hp->flags.msg_size) {
12483229Spst		pktlen = hp->msg_size;
12493229Spst	} else {
12503229Spst		/*
12513229Spst		 * If the request was longer than the official length, build
12523229Spst		 * a response of that same length where the additional length
12533229Spst		 * is assumed to be part of the bp_vend (options) area.
12543229Spst		 */
12553229Spst		if (pktlen > sizeof(*bp)) {
12563229Spst			if (debug > 1)
12573229Spst				report(LOG_INFO, "request message length=%d", pktlen);
12583229Spst		}
12593229Spst		/*
12603229Spst		 * Check whether the request contains the option:
12613229Spst		 * Maximum DHCP Message Size (RFC1533 sec. 9.8)
12623229Spst		 * and if so, override the response length with its value.
12633229Spst		 * This request must lie within the first BP_VEND_LEN
12643229Spst		 * bytes of the option space.
12653229Spst		 */
12663229Spst		{
12673229Spst			byte *p, *ep;
12683229Spst			byte tag, len;
12693229Spst			short msgsz = 0;
12708870Srgrimes
12713229Spst			p = vp + 4;
12723229Spst			ep = p + BP_VEND_LEN - 4;
12733229Spst			while (p < ep) {
12743229Spst				tag = *p++;
12753229Spst				/* Check for tags with no data first. */
12763229Spst				if (tag == TAG_PAD)
12773229Spst					continue;
12783229Spst				if (tag == TAG_END)
12793229Spst					break;
12803229Spst				/* Now scan the length byte. */
12813229Spst				len = *p++;
12823229Spst				switch (tag) {
12833229Spst				case TAG_MAX_MSGSZ:
12843229Spst					if (len == 2) {
12853229Spst						bcopy(p, (char*)&msgsz, 2);
12863229Spst						msgsz = ntohs(msgsz);
12873229Spst					}
12883229Spst					break;
12893229Spst				case TAG_SUBNET_MASK:
12903229Spst					/* XXX - Should preserve this if given... */
12913229Spst					break;
12923229Spst				} /* swtich */
12933229Spst				p += len;
12943229Spst			}
12953229Spst
129683941Siedowse			if (msgsz > sizeof(*bp) + BP_MSG_OVERHEAD) {
12973229Spst				if (debug > 1)
12983229Spst					report(LOG_INFO, "request has DHCP msglen=%d", msgsz);
129983941Siedowse				pktlen = msgsz - BP_MSG_OVERHEAD;
13003229Spst			}
13013229Spst		}
13023229Spst	}
13033229Spst
13043229Spst	if (pktlen < sizeof(*bp)) {
13053229Spst		report(LOG_ERR, "invalid response length=%d", pktlen);
13063229Spst		pktlen = sizeof(*bp);
13073229Spst	}
13083229Spst	bytesleft = ((byte*)bp + pktlen) - vp;
13093229Spst	if (pktlen > sizeof(*bp)) {
13103229Spst		if (debug > 1)
13113229Spst			report(LOG_INFO, "extended reply, length=%d, options=%d",
13123229Spst				   pktlen, bytesleft);
13133229Spst	}
13143229Spst
13153229Spst	/* Copy in the magic cookie */
13163229Spst	bcopy(vm_rfc1048, vp, 4);
13173229Spst	vp += 4;
13183229Spst	bytesleft -= 4;
13193229Spst
13203229Spst	if (hp->flags.subnet_mask) {
13213229Spst		/* always enough room here. */
13223229Spst		*vp++ = TAG_SUBNET_MASK;/* -1 byte  */
13233229Spst		*vp++ = 4;				/* -1 byte  */
13243229Spst		insert_u_long(hp->subnet_mask.s_addr, &vp);	/* -4 bytes */
13253229Spst		bytesleft -= 6;			/* Fix real count */
13263229Spst		if (hp->flags.gateway) {
13273229Spst			(void) insert_ip(TAG_GATEWAY,
13283229Spst							 hp->gateway,
13293229Spst							 &vp, &bytesleft);
13303229Spst		}
13313229Spst	}
13323229Spst	if (hp->flags.bootsize) {
13333229Spst		/* always enough room here */
13343229Spst		bootsize = (hp->flags.bootsize_auto) ?
13353229Spst			((bootsize + 511) / 512) : (hp->bootsize);	/* Round up */
13363229Spst		*vp++ = TAG_BOOT_SIZE;
13373229Spst		*vp++ = 2;
13383229Spst		*vp++ = (byte) ((bootsize >> 8) & 0xFF);
13393229Spst		*vp++ = (byte) (bootsize & 0xFF);
13403229Spst		bytesleft -= 4;			/* Tag, length, and 16 bit blocksize */
13413229Spst	}
13423229Spst	/*
13433229Spst	 * This one is special: Remaining options go in the ext file.
13443229Spst	 * Only the subnet_mask, bootsize, and gateway should precede.
13453229Spst	 */
13463229Spst	if (hp->flags.exten_file) {
13473229Spst		/*
13483229Spst		 * Check for room for exten_file.  Add 3 to account for
13493229Spst		 * TAG_EXTEN_FILE, length, and TAG_END.
13503229Spst		 */
13513229Spst		len = strlen(hp->exten_file->string);
13523229Spst		NEED((len + 3), "ef");
13533229Spst		*vp++ = TAG_EXTEN_FILE;
13543229Spst		*vp++ = (byte) (len & 0xFF);
13553229Spst		bcopy(hp->exten_file->string, vp, len);
13563229Spst		vp += len;
13573229Spst		*vp++ = TAG_END;
13583229Spst		bytesleft -= len + 3;
13593229Spst		return;					/* no more options here. */
13603229Spst	}
13613229Spst	/*
13623229Spst	 * The remaining options are inserted by the following
13633229Spst	 * function (which is shared with bootpef.c).
13643229Spst	 * Keep back one byte for the TAG_END.
13653229Spst	 */
13663229Spst	len = dovend_rfc1497(hp, vp, bytesleft - 1);
13673229Spst	vp += len;
13683229Spst	bytesleft -= len;
13693229Spst
13703229Spst	/* There should be at least one byte left. */
13713229Spst	NEED(1, "(end)");
13723229Spst	*vp++ = TAG_END;
13733229Spst	bytesleft--;
13743229Spst
13753229Spst	/* Log message done by caller. */
13763229Spst	if (bytesleft > 0) {
13773229Spst		/*
13783229Spst		 * Zero out any remaining part of the vendor area.
13793229Spst		 */
13803229Spst		bzero(vp, bytesleft);
13813229Spst	}
13823229Spst} /* dovend_rfc1048 */
13833229Spst#undef	NEED
13843229Spst
13853229Spst
13863229Spst/*
13873229Spst * Now in readfile.c:
13883229Spst * 	hwlookcmp()
13893229Spst *	iplookcmp()
13903229Spst */
13913229Spst
13923229Spst/* haddrtoa() - now in hwaddr.c */
13933229Spst/*
13943229Spst * Now in dovend.c:
13953229Spst * insert_ip()
13963229Spst * insert_generic()
13973229Spst * insert_u_long()
13983229Spst */
13993229Spst
14003229Spst/* get_errmsg() - now in report.c */
14013229Spst
14023229Spst/*
14033229Spst * Local Variables:
14043229Spst * tab-width: 4
14053229Spst * c-indent-level: 4
14063229Spst * c-argdecl-indent: 4
14073229Spst * c-continued-statement-offset: 4
14083229Spst * c-continued-brace-offset: -4
14093229Spst * c-label-offset: -4
14103229Spst * c-brace-offset: 0
14113229Spst * End:
14123229Spst */
1413