xref: /freebsd/libexec/bootpd/bootpd.c (revision 9e9a43bdec3ff0ad37e4799b209e0d9513150a6f)
144099b7bSPaul Traina /************************************************************************
244099b7bSPaul Traina           Copyright 1988, 1991 by Carnegie Mellon University
344099b7bSPaul Traina 
444099b7bSPaul Traina                           All Rights Reserved
544099b7bSPaul Traina 
644099b7bSPaul Traina Permission to use, copy, modify, and distribute this software and its
744099b7bSPaul Traina documentation for any purpose and without fee is hereby granted, provided
844099b7bSPaul Traina that the above copyright notice appear in all copies and that both that
944099b7bSPaul Traina copyright notice and this permission notice appear in supporting
1044099b7bSPaul Traina documentation, and that the name of Carnegie Mellon University not be used
1144099b7bSPaul Traina in advertising or publicity pertaining to distribution of the software
1244099b7bSPaul Traina without specific, written prior permission.
1344099b7bSPaul Traina 
1444099b7bSPaul Traina CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
1544099b7bSPaul Traina SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
1644099b7bSPaul Traina IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
1744099b7bSPaul Traina DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
1844099b7bSPaul Traina PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
1944099b7bSPaul Traina ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
2044099b7bSPaul Traina SOFTWARE.
21148531efSWolfram Schneider 
229e9a43bdSBrian Somers 	$Id: bootpd.c,v 1.10 1998/12/13 21:02:28 eivind Exp $
23148531efSWolfram Schneider 
2444099b7bSPaul Traina ************************************************************************/
2544099b7bSPaul Traina 
2644099b7bSPaul Traina /*
2744099b7bSPaul Traina  * BOOTP (bootstrap protocol) server daemon.
2844099b7bSPaul Traina  *
2944099b7bSPaul Traina  * Answers BOOTP request packets from booting client machines.
3044099b7bSPaul Traina  * See [SRI-NIC]<RFC>RFC951.TXT for a description of the protocol.
3144099b7bSPaul Traina  * See [SRI-NIC]<RFC>RFC1048.TXT for vendor-information extensions.
3244099b7bSPaul Traina  * See RFC 1395 for option tags 14-17.
3344099b7bSPaul Traina  * See accompanying man page -- bootpd.8
3444099b7bSPaul Traina  *
3544099b7bSPaul Traina  * HISTORY
3644099b7bSPaul Traina  *	See ./Changes
3744099b7bSPaul Traina  *
3844099b7bSPaul Traina  * BUGS
3944099b7bSPaul Traina  *	See ./ToDo
4044099b7bSPaul Traina  */
4144099b7bSPaul Traina 
4244099b7bSPaul Traina 
4344099b7bSPaul Traina 
4444099b7bSPaul Traina #include <sys/types.h>
4544099b7bSPaul Traina #include <sys/param.h>
4644099b7bSPaul Traina #include <sys/socket.h>
4744099b7bSPaul Traina #include <sys/ioctl.h>
4844099b7bSPaul Traina #include <sys/file.h>
4944099b7bSPaul Traina #include <sys/time.h>
5044099b7bSPaul Traina #include <sys/stat.h>
51e08ac58bSPaul Traina #include <sys/utsname.h>
5244099b7bSPaul Traina 
5344099b7bSPaul Traina #include <net/if.h>
5444099b7bSPaul Traina #include <netinet/in.h>
5544099b7bSPaul Traina #include <arpa/inet.h>	/* inet_ntoa */
5644099b7bSPaul Traina 
5744099b7bSPaul Traina #ifndef	NO_UNISTD
5844099b7bSPaul Traina #include <unistd.h>
5944099b7bSPaul Traina #endif
60e08ac58bSPaul Traina 
6144099b7bSPaul Traina #include <stdlib.h>
6244099b7bSPaul Traina #include <signal.h>
6344099b7bSPaul Traina #include <stdio.h>
6444099b7bSPaul Traina #include <string.h>
6544099b7bSPaul Traina #include <errno.h>
6644099b7bSPaul Traina #include <ctype.h>
6744099b7bSPaul Traina #include <netdb.h>
6844099b7bSPaul Traina #include <syslog.h>
6944099b7bSPaul Traina #include <assert.h>
7044099b7bSPaul Traina 
7144099b7bSPaul Traina #ifdef	NO_SETSID
7244099b7bSPaul Traina # include <fcntl.h>		/* for O_RDONLY, etc */
7344099b7bSPaul Traina #endif
7444099b7bSPaul Traina 
7544099b7bSPaul Traina #ifndef	USE_BFUNCS
7644099b7bSPaul Traina # include <memory.h>
7744099b7bSPaul Traina /* Yes, memcpy is OK here (no overlapped copies). */
7844099b7bSPaul Traina # define bcopy(a,b,c)    memcpy(b,a,c)
7944099b7bSPaul Traina # define bzero(p,l)      memset(p,0,l)
8044099b7bSPaul Traina # define bcmp(a,b,c)     memcmp(a,b,c)
8144099b7bSPaul Traina #endif
8244099b7bSPaul Traina 
8344099b7bSPaul Traina #include "bootp.h"
8444099b7bSPaul Traina #include "hash.h"
8544099b7bSPaul Traina #include "hwaddr.h"
8644099b7bSPaul Traina #include "bootpd.h"
8744099b7bSPaul Traina #include "dovend.h"
8844099b7bSPaul Traina #include "getif.h"
8944099b7bSPaul Traina #include "readfile.h"
9044099b7bSPaul Traina #include "report.h"
9144099b7bSPaul Traina #include "tzone.h"
9244099b7bSPaul Traina #include "patchlevel.h"
9344099b7bSPaul Traina 
9444099b7bSPaul Traina #ifndef CONFIG_FILE
9544099b7bSPaul Traina #define CONFIG_FILE		"/etc/bootptab"
9644099b7bSPaul Traina #endif
9744099b7bSPaul Traina #ifndef DUMPTAB_FILE
9844099b7bSPaul Traina #define DUMPTAB_FILE		"/tmp/bootpd.dump"
9944099b7bSPaul Traina #endif
10044099b7bSPaul Traina 
10144099b7bSPaul Traina 
10244099b7bSPaul Traina 
10344099b7bSPaul Traina /*
10444099b7bSPaul Traina  * Externals, forward declarations, and global variables
10544099b7bSPaul Traina  */
10644099b7bSPaul Traina 
10744099b7bSPaul Traina #ifdef	__STDC__
10844099b7bSPaul Traina #define P(args) args
10944099b7bSPaul Traina #else
11044099b7bSPaul Traina #define P(args) ()
11144099b7bSPaul Traina #endif
11244099b7bSPaul Traina 
11344099b7bSPaul Traina extern void dumptab P((char *));
11444099b7bSPaul Traina 
11544099b7bSPaul Traina PRIVATE void catcher P((int));
11644099b7bSPaul Traina PRIVATE int chk_access P((char *, int32 *));
11744099b7bSPaul Traina #ifdef VEND_CMU
11844099b7bSPaul Traina PRIVATE void dovend_cmu P((struct bootp *, struct host *));
11944099b7bSPaul Traina #endif
12044099b7bSPaul Traina PRIVATE void dovend_rfc1048 P((struct bootp *, struct host *, int32));
12144099b7bSPaul Traina PRIVATE void handle_reply P((void));
12244099b7bSPaul Traina PRIVATE void handle_request P((void));
12344099b7bSPaul Traina PRIVATE void sendreply P((int forward, int32 dest_override));
12444099b7bSPaul Traina PRIVATE void usage P((void));
12544099b7bSPaul Traina 
12644099b7bSPaul Traina #undef	P
12744099b7bSPaul Traina 
12844099b7bSPaul Traina /*
12944099b7bSPaul Traina  * IP port numbers for client and server obtained from /etc/services
13044099b7bSPaul Traina  */
13144099b7bSPaul Traina 
13244099b7bSPaul Traina u_short bootps_port, bootpc_port;
13344099b7bSPaul Traina 
13444099b7bSPaul Traina 
13544099b7bSPaul Traina /*
13644099b7bSPaul Traina  * Internet socket and interface config structures
13744099b7bSPaul Traina  */
13844099b7bSPaul Traina 
13944099b7bSPaul Traina struct sockaddr_in bind_addr;	/* Listening */
14044099b7bSPaul Traina struct sockaddr_in recv_addr;	/* Packet source */
14144099b7bSPaul Traina struct sockaddr_in send_addr;	/*  destination */
14244099b7bSPaul Traina 
14344099b7bSPaul Traina 
14444099b7bSPaul Traina /*
14544099b7bSPaul Traina  * option defaults
14644099b7bSPaul Traina  */
14744099b7bSPaul Traina int debug = 0;					/* Debugging flag (level) */
14844099b7bSPaul Traina struct timeval actualtimeout =
14944099b7bSPaul Traina {								/* fifteen minutes */
15044099b7bSPaul Traina 	15 * 60L,					/* tv_sec */
15144099b7bSPaul Traina 	0							/* tv_usec */
15244099b7bSPaul Traina };
15344099b7bSPaul Traina 
15444099b7bSPaul Traina /*
15544099b7bSPaul Traina  * General
15644099b7bSPaul Traina  */
15744099b7bSPaul Traina 
15844099b7bSPaul Traina int s;							/* Socket file descriptor */
15944099b7bSPaul Traina char *pktbuf;					/* Receive packet buffer */
16044099b7bSPaul Traina int pktlen;
16144099b7bSPaul Traina char *progname;
16244099b7bSPaul Traina char *chdir_path;
16344099b7bSPaul Traina struct in_addr my_ip_addr;
16444099b7bSPaul Traina 
1659e9a43bdSBrian Somers static const char *hostname;
1669e9a43bdSBrian Somers static char default_hostname[MAXHOSTNAMELEN];
167e08ac58bSPaul Traina 
16844099b7bSPaul Traina /* Flags set by signal catcher. */
16944099b7bSPaul Traina PRIVATE int do_readtab = 0;
17044099b7bSPaul Traina PRIVATE int do_dumptab = 0;
17144099b7bSPaul Traina 
17244099b7bSPaul Traina /*
17344099b7bSPaul Traina  * Globals below are associated with the bootp database file (bootptab).
17444099b7bSPaul Traina  */
17544099b7bSPaul Traina 
17644099b7bSPaul Traina char *bootptab = CONFIG_FILE;
17744099b7bSPaul Traina char *bootpd_dump = DUMPTAB_FILE;
17844099b7bSPaul Traina 
17944099b7bSPaul Traina 
18044099b7bSPaul Traina 
18144099b7bSPaul Traina /*
18244099b7bSPaul Traina  * Initialization such as command-line processing is done and then the
18344099b7bSPaul Traina  * main server loop is started.
18444099b7bSPaul Traina  */
18544099b7bSPaul Traina 
18644099b7bSPaul Traina void
18744099b7bSPaul Traina main(argc, argv)
18844099b7bSPaul Traina 	int argc;
18944099b7bSPaul Traina 	char **argv;
19044099b7bSPaul Traina {
19144099b7bSPaul Traina 	struct timeval *timeout;
19244099b7bSPaul Traina 	struct bootp *bp;
19344099b7bSPaul Traina 	struct servent *servp;
19444099b7bSPaul Traina 	struct hostent *hep;
19544099b7bSPaul Traina 	char *stmp;
19644099b7bSPaul Traina 	int n, ba_len, ra_len;
19744099b7bSPaul Traina 	int nfound, readfds;
19844099b7bSPaul Traina 	int standalone;
199e08ac58bSPaul Traina #ifdef	SA_NOCLDSTOP	/* Have POSIX sigaction(2). */
200e08ac58bSPaul Traina 	struct sigaction sa;
201e08ac58bSPaul Traina #endif
20244099b7bSPaul Traina 
20344099b7bSPaul Traina 	progname = strrchr(argv[0], '/');
20444099b7bSPaul Traina 	if (progname) progname++;
20544099b7bSPaul Traina 	else progname = argv[0];
20644099b7bSPaul Traina 
20744099b7bSPaul Traina 	/*
20844099b7bSPaul Traina 	 * Initialize logging.
20944099b7bSPaul Traina 	 */
21044099b7bSPaul Traina 	report_init(0);				/* uses progname */
21144099b7bSPaul Traina 
21244099b7bSPaul Traina 	/*
21344099b7bSPaul Traina 	 * Log startup
21444099b7bSPaul Traina 	 */
21544099b7bSPaul Traina 	report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
21644099b7bSPaul Traina 
21744099b7bSPaul Traina 	/* Debugging for compilers with struct padding. */
21844099b7bSPaul Traina 	assert(sizeof(struct bootp) == BP_MINPKTSZ);
21944099b7bSPaul Traina 
22044099b7bSPaul Traina 	/* Get space for receiving packets and composing replies. */
22144099b7bSPaul Traina 	pktbuf = malloc(MAX_MSG_SIZE);
22244099b7bSPaul Traina 	if (!pktbuf) {
22344099b7bSPaul Traina 		report(LOG_ERR, "malloc failed");
22444099b7bSPaul Traina 		exit(1);
22544099b7bSPaul Traina 	}
22644099b7bSPaul Traina 	bp = (struct bootp *) pktbuf;
22744099b7bSPaul Traina 
22844099b7bSPaul Traina 	/*
22944099b7bSPaul Traina 	 * Check to see if a socket was passed to us from inetd.
23044099b7bSPaul Traina 	 *
23144099b7bSPaul Traina 	 * Use getsockname() to determine if descriptor 0 is indeed a socket
23244099b7bSPaul Traina 	 * (and thus we are probably a child of inetd) or if it is instead
23344099b7bSPaul Traina 	 * something else and we are running standalone.
23444099b7bSPaul Traina 	 */
23544099b7bSPaul Traina 	s = 0;
23644099b7bSPaul Traina 	ba_len = sizeof(bind_addr);
23744099b7bSPaul Traina 	bzero((char *) &bind_addr, ba_len);
23844099b7bSPaul Traina 	errno = 0;
23944099b7bSPaul Traina 	standalone = TRUE;
24044099b7bSPaul Traina 	if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {
24144099b7bSPaul Traina 		/*
24244099b7bSPaul Traina 		 * Descriptor 0 is a socket.  Assume we are a child of inetd.
24344099b7bSPaul Traina 		 */
24444099b7bSPaul Traina 		if (bind_addr.sin_family == AF_INET) {
24544099b7bSPaul Traina 			standalone = FALSE;
24644099b7bSPaul Traina 			bootps_port = ntohs(bind_addr.sin_port);
24744099b7bSPaul Traina 		} else {
24844099b7bSPaul Traina 			/* Some other type of socket? */
24944099b7bSPaul Traina 			report(LOG_ERR, "getsockname: not an INET socket");
25044099b7bSPaul Traina 		}
25144099b7bSPaul Traina 	}
25244099b7bSPaul Traina 
25344099b7bSPaul Traina 	/*
25444099b7bSPaul Traina 	 * Set defaults that might be changed by option switches.
25544099b7bSPaul Traina 	 */
25644099b7bSPaul Traina 	stmp = NULL;
25744099b7bSPaul Traina 	timeout = &actualtimeout;
25844099b7bSPaul Traina 
2599e9a43bdSBrian Somers 	if (gethostname(default_hostname, sizeof(default_hostname) - 1) < 0) {
260798c69cdSPoul-Henning Kamp 		report(LOG_ERR, "bootpd: can't get hostname\n");
261e08ac58bSPaul Traina 		exit(1);
262e08ac58bSPaul Traina 	}
2639e9a43bdSBrian Somers 	default_hostname[sizeof(default_hostname) - 1] = '\0';
26409caeadeSJoerg Wunsch 	hostname = default_hostname;
265e08ac58bSPaul Traina 
26644099b7bSPaul Traina 	/*
26744099b7bSPaul Traina 	 * Read switches.
26844099b7bSPaul Traina 	 */
26944099b7bSPaul Traina 	for (argc--, argv++; argc > 0; argc--, argv++) {
27044099b7bSPaul Traina 		if (argv[0][0] != '-')
27144099b7bSPaul Traina 			break;
27244099b7bSPaul Traina 		switch (argv[0][1]) {
27344099b7bSPaul Traina 
27444099b7bSPaul Traina 		case 'c':				/* chdir_path */
27544099b7bSPaul Traina 			if (argv[0][2]) {
27644099b7bSPaul Traina 				stmp = &(argv[0][2]);
27744099b7bSPaul Traina 			} else {
27844099b7bSPaul Traina 				argc--;
27944099b7bSPaul Traina 				argv++;
28044099b7bSPaul Traina 				stmp = argv[0];
28144099b7bSPaul Traina 			}
28244099b7bSPaul Traina 			if (!stmp || (stmp[0] != '/')) {
283798c69cdSPoul-Henning Kamp 				report(LOG_ERR,
28444099b7bSPaul Traina 						"bootpd: invalid chdir specification\n");
28544099b7bSPaul Traina 				break;
28644099b7bSPaul Traina 			}
28744099b7bSPaul Traina 			chdir_path = stmp;
28844099b7bSPaul Traina 			break;
28944099b7bSPaul Traina 
29044099b7bSPaul Traina 		case 'd':				/* debug level */
29144099b7bSPaul Traina 			if (argv[0][2]) {
29244099b7bSPaul Traina 				stmp = &(argv[0][2]);
29344099b7bSPaul Traina 			} else if (argv[1] && argv[1][0] == '-') {
29444099b7bSPaul Traina 				/*
29544099b7bSPaul Traina 				 * Backwards-compatible behavior:
29644099b7bSPaul Traina 				 * no parameter, so just increment the debug flag.
29744099b7bSPaul Traina 				 */
29844099b7bSPaul Traina 				debug++;
29944099b7bSPaul Traina 				break;
30044099b7bSPaul Traina 			} else {
30144099b7bSPaul Traina 				argc--;
30244099b7bSPaul Traina 				argv++;
30344099b7bSPaul Traina 				stmp = argv[0];
30444099b7bSPaul Traina 			}
30544099b7bSPaul Traina 			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
306798c69cdSPoul-Henning Kamp 				report(LOG_ERR,
30744099b7bSPaul Traina 						"%s: invalid debug level\n", progname);
30844099b7bSPaul Traina 				break;
30944099b7bSPaul Traina 			}
31044099b7bSPaul Traina 			debug = n;
31144099b7bSPaul Traina 			break;
31244099b7bSPaul Traina 
31344099b7bSPaul Traina 		case 'h':				/* override hostname */
31444099b7bSPaul Traina 			if (argv[0][2]) {
31544099b7bSPaul Traina 				stmp = &(argv[0][2]);
31644099b7bSPaul Traina 			} else {
31744099b7bSPaul Traina 				argc--;
31844099b7bSPaul Traina 				argv++;
31944099b7bSPaul Traina 				stmp = argv[0];
32044099b7bSPaul Traina 			}
32144099b7bSPaul Traina 			if (!stmp) {
322798c69cdSPoul-Henning Kamp 				report(LOG_ERR,
32344099b7bSPaul Traina 						"bootpd: missing hostname\n");
32444099b7bSPaul Traina 				break;
32544099b7bSPaul Traina 			}
326e08ac58bSPaul Traina 			hostname = stmp;
32744099b7bSPaul Traina 			break;
32844099b7bSPaul Traina 
32944099b7bSPaul Traina 		case 'i':				/* inetd mode */
33044099b7bSPaul Traina 			standalone = FALSE;
33144099b7bSPaul Traina 			break;
33244099b7bSPaul Traina 
33344099b7bSPaul Traina 		case 's':				/* standalone mode */
33444099b7bSPaul Traina 			standalone = TRUE;
33544099b7bSPaul Traina 			break;
33644099b7bSPaul Traina 
33744099b7bSPaul Traina 		case 't':				/* timeout */
33844099b7bSPaul Traina 			if (argv[0][2]) {
33944099b7bSPaul Traina 				stmp = &(argv[0][2]);
34044099b7bSPaul Traina 			} else {
34144099b7bSPaul Traina 				argc--;
34244099b7bSPaul Traina 				argv++;
34344099b7bSPaul Traina 				stmp = argv[0];
34444099b7bSPaul Traina 			}
34544099b7bSPaul Traina 			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
346798c69cdSPoul-Henning Kamp 				report(LOG_ERR,
34744099b7bSPaul Traina 						"%s: invalid timeout specification\n", progname);
34844099b7bSPaul Traina 				break;
34944099b7bSPaul Traina 			}
35044099b7bSPaul Traina 			actualtimeout.tv_sec = (int32) (60 * n);
35144099b7bSPaul Traina 			/*
35244099b7bSPaul Traina 			 * If the actual timeout is zero, pass a NULL pointer
35344099b7bSPaul Traina 			 * to select so it blocks indefinitely, otherwise,
35444099b7bSPaul Traina 			 * point to the actual timeout value.
35544099b7bSPaul Traina 			 */
35644099b7bSPaul Traina 			timeout = (n > 0) ? &actualtimeout : NULL;
35744099b7bSPaul Traina 			break;
35844099b7bSPaul Traina 
35944099b7bSPaul Traina 		default:
360798c69cdSPoul-Henning Kamp 			report(LOG_ERR, "%s: unknown switch: -%c\n",
36144099b7bSPaul Traina 					progname, argv[0][1]);
36244099b7bSPaul Traina 			usage();
36344099b7bSPaul Traina 			break;
36444099b7bSPaul Traina 
36544099b7bSPaul Traina 		} /* switch */
36644099b7bSPaul Traina 	} /* for args */
36744099b7bSPaul Traina 
36844099b7bSPaul Traina 	/*
36944099b7bSPaul Traina 	 * Override default file names if specified on the command line.
37044099b7bSPaul Traina 	 */
37144099b7bSPaul Traina 	if (argc > 0)
37244099b7bSPaul Traina 		bootptab = argv[0];
37344099b7bSPaul Traina 
37444099b7bSPaul Traina 	if (argc > 1)
37544099b7bSPaul Traina 		bootpd_dump = argv[1];
37644099b7bSPaul Traina 
37744099b7bSPaul Traina 	/*
37844099b7bSPaul Traina 	 * Get my hostname and IP address.
37944099b7bSPaul Traina 	 */
380e08ac58bSPaul Traina 
38144099b7bSPaul Traina 	hep = gethostbyname(hostname);
38244099b7bSPaul Traina 	if (!hep) {
383798c69cdSPoul-Henning Kamp 		report(LOG_ERR, "Can not get my IP address\n");
38444099b7bSPaul Traina 		exit(1);
38544099b7bSPaul Traina 	}
38644099b7bSPaul Traina 	bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
38744099b7bSPaul Traina 
38844099b7bSPaul Traina 	if (standalone) {
38944099b7bSPaul Traina 		/*
39044099b7bSPaul Traina 		 * Go into background and disassociate from controlling terminal.
39144099b7bSPaul Traina 		 */
39244099b7bSPaul Traina 		if (debug < 3) {
39344099b7bSPaul Traina 			if (fork())
39444099b7bSPaul Traina 				exit(0);
39544099b7bSPaul Traina #ifdef	NO_SETSID
39644099b7bSPaul Traina 			setpgrp(0,0);
39744099b7bSPaul Traina #ifdef TIOCNOTTY
39844099b7bSPaul Traina 			n = open("/dev/tty", O_RDWR);
39944099b7bSPaul Traina 			if (n >= 0) {
40044099b7bSPaul Traina 				ioctl(n, TIOCNOTTY, (char *) 0);
40144099b7bSPaul Traina 				(void) close(n);
40244099b7bSPaul Traina 			}
40344099b7bSPaul Traina #endif	/* TIOCNOTTY */
40444099b7bSPaul Traina #else	/* SETSID */
40544099b7bSPaul Traina 			if (setsid() < 0)
40644099b7bSPaul Traina 				perror("setsid");
40744099b7bSPaul Traina #endif	/* SETSID */
40844099b7bSPaul Traina 		} /* if debug < 3 */
40944099b7bSPaul Traina 
41044099b7bSPaul Traina 		/*
41144099b7bSPaul Traina 		 * Nuke any timeout value
41244099b7bSPaul Traina 		 */
41344099b7bSPaul Traina 		timeout = NULL;
41444099b7bSPaul Traina 
41544099b7bSPaul Traina 	} /* if standalone (1st) */
41644099b7bSPaul Traina 
41744099b7bSPaul Traina 	/* Set the cwd (i.e. to /tftpboot) */
41844099b7bSPaul Traina 	if (chdir_path) {
41944099b7bSPaul Traina 		if (chdir(chdir_path) < 0)
42044099b7bSPaul Traina 			report(LOG_ERR, "%s: chdir failed", chdir_path);
42144099b7bSPaul Traina 	}
42244099b7bSPaul Traina 
42344099b7bSPaul Traina 	/* Get the timezone. */
42444099b7bSPaul Traina 	tzone_init();
42544099b7bSPaul Traina 
42644099b7bSPaul Traina 	/* Allocate hash tables. */
42744099b7bSPaul Traina 	rdtab_init();
42844099b7bSPaul Traina 
42944099b7bSPaul Traina 	/*
43044099b7bSPaul Traina 	 * Read the bootptab file.
43144099b7bSPaul Traina 	 */
43244099b7bSPaul Traina 	readtab(1);					/* force read */
43344099b7bSPaul Traina 
43444099b7bSPaul Traina 	if (standalone) {
43544099b7bSPaul Traina 
43644099b7bSPaul Traina 		/*
43744099b7bSPaul Traina 		 * Create a socket.
43844099b7bSPaul Traina 		 */
43944099b7bSPaul Traina 		if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
44044099b7bSPaul Traina 			report(LOG_ERR, "socket: %s", get_network_errmsg());
44144099b7bSPaul Traina 			exit(1);
44244099b7bSPaul Traina 		}
44344099b7bSPaul Traina 
44444099b7bSPaul Traina 		/*
44544099b7bSPaul Traina 		 * Get server's listening port number
44644099b7bSPaul Traina 		 */
44744099b7bSPaul Traina 		servp = getservbyname("bootps", "udp");
44844099b7bSPaul Traina 		if (servp) {
44944099b7bSPaul Traina 			bootps_port = ntohs((u_short) servp->s_port);
45044099b7bSPaul Traina 		} else {
45144099b7bSPaul Traina 			bootps_port = (u_short) IPPORT_BOOTPS;
45244099b7bSPaul Traina 			report(LOG_ERR,
45344099b7bSPaul Traina 				   "udp/bootps: unknown service -- assuming port %d",
45444099b7bSPaul Traina 				   bootps_port);
45544099b7bSPaul Traina 		}
45644099b7bSPaul Traina 
45744099b7bSPaul Traina 		/*
45844099b7bSPaul Traina 		 * Bind socket to BOOTPS port.
45944099b7bSPaul Traina 		 */
46044099b7bSPaul Traina 		bind_addr.sin_family = AF_INET;
46144099b7bSPaul Traina 		bind_addr.sin_addr.s_addr = INADDR_ANY;
46244099b7bSPaul Traina 		bind_addr.sin_port = htons(bootps_port);
46344099b7bSPaul Traina 		if (bind(s, (struct sockaddr *) &bind_addr,
46444099b7bSPaul Traina 				 sizeof(bind_addr)) < 0)
46544099b7bSPaul Traina 		{
46644099b7bSPaul Traina 			report(LOG_ERR, "bind: %s", get_network_errmsg());
46744099b7bSPaul Traina 			exit(1);
46844099b7bSPaul Traina 		}
46944099b7bSPaul Traina 	} /* if standalone (2nd)*/
47044099b7bSPaul Traina 
47144099b7bSPaul Traina 	/*
47244099b7bSPaul Traina 	 * Get destination port number so we can reply to client
47344099b7bSPaul Traina 	 */
47444099b7bSPaul Traina 	servp = getservbyname("bootpc", "udp");
47544099b7bSPaul Traina 	if (servp) {
47644099b7bSPaul Traina 		bootpc_port = ntohs(servp->s_port);
47744099b7bSPaul Traina 	} else {
47844099b7bSPaul Traina 		report(LOG_ERR,
47944099b7bSPaul Traina 			   "udp/bootpc: unknown service -- assuming port %d",
48044099b7bSPaul Traina 			   IPPORT_BOOTPC);
48144099b7bSPaul Traina 		bootpc_port = (u_short) IPPORT_BOOTPC;
48244099b7bSPaul Traina 	}
48344099b7bSPaul Traina 
48444099b7bSPaul Traina 	/*
48544099b7bSPaul Traina 	 * Set up signals to read or dump the table.
48644099b7bSPaul Traina 	 */
487e08ac58bSPaul Traina #ifdef	SA_NOCLDSTOP	/* Have POSIX sigaction(2). */
488e08ac58bSPaul Traina 	sa.sa_handler = catcher;
489e08ac58bSPaul Traina 	sigemptyset(&sa.sa_mask);
490e08ac58bSPaul Traina 	sa.sa_flags = 0;
491e08ac58bSPaul Traina 	if (sigaction(SIGHUP, &sa, NULL) < 0) {
492e08ac58bSPaul Traina 		report(LOG_ERR, "sigaction: %s", get_errmsg());
493e08ac58bSPaul Traina 		exit(1);
494e08ac58bSPaul Traina 	}
495e08ac58bSPaul Traina 	if (sigaction(SIGUSR1, &sa, NULL) < 0) {
496e08ac58bSPaul Traina 		report(LOG_ERR, "sigaction: %s", get_errmsg());
497e08ac58bSPaul Traina 		exit(1);
498e08ac58bSPaul Traina 	}
499e08ac58bSPaul Traina #else	/* SA_NOCLDSTOP */
500e08ac58bSPaul Traina 	/* Old-fashioned UNIX signals */
50144099b7bSPaul Traina 	if ((int) signal(SIGHUP, catcher) < 0) {
50244099b7bSPaul Traina 		report(LOG_ERR, "signal: %s", get_errmsg());
50344099b7bSPaul Traina 		exit(1);
50444099b7bSPaul Traina 	}
50544099b7bSPaul Traina 	if ((int) signal(SIGUSR1, catcher) < 0) {
50644099b7bSPaul Traina 		report(LOG_ERR, "signal: %s", get_errmsg());
50744099b7bSPaul Traina 		exit(1);
50844099b7bSPaul Traina 	}
509e08ac58bSPaul Traina #endif	/* SA_NOCLDSTOP */
51044099b7bSPaul Traina 
51144099b7bSPaul Traina 	/*
51244099b7bSPaul Traina 	 * Process incoming requests.
51344099b7bSPaul Traina 	 */
51444099b7bSPaul Traina 	for (;;) {
515e08ac58bSPaul Traina 		struct timeval tv;
516e08ac58bSPaul Traina 
51744099b7bSPaul Traina 		readfds = 1 << s;
518e08ac58bSPaul Traina 		if (timeout)
519e08ac58bSPaul Traina 			tv = *timeout;
520e08ac58bSPaul Traina 
521e08ac58bSPaul Traina 		nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL,
522e08ac58bSPaul Traina 						(timeout) ? &tv : NULL);
52344099b7bSPaul Traina 		if (nfound < 0) {
52444099b7bSPaul Traina 			if (errno != EINTR) {
52544099b7bSPaul Traina 				report(LOG_ERR, "select: %s", get_errmsg());
52644099b7bSPaul Traina 			}
52744099b7bSPaul Traina 			/*
52844099b7bSPaul Traina 			 * Call readtab() or dumptab() here to avoid the
52944099b7bSPaul Traina 			 * dangers of doing I/O from a signal handler.
53044099b7bSPaul Traina 			 */
53144099b7bSPaul Traina 			if (do_readtab) {
53244099b7bSPaul Traina 				do_readtab = 0;
53344099b7bSPaul Traina 				readtab(1);		/* force read */
53444099b7bSPaul Traina 			}
53544099b7bSPaul Traina 			if (do_dumptab) {
53644099b7bSPaul Traina 				do_dumptab = 0;
53744099b7bSPaul Traina 				dumptab(bootpd_dump);
53844099b7bSPaul Traina 			}
53944099b7bSPaul Traina 			continue;
54044099b7bSPaul Traina 		}
54144099b7bSPaul Traina 		if (!(readfds & (1 << s))) {
54244099b7bSPaul Traina 			if (debug > 1)
54344099b7bSPaul Traina 				report(LOG_INFO, "exiting after %ld minutes of inactivity",
54444099b7bSPaul Traina 					   actualtimeout.tv_sec / 60);
54544099b7bSPaul Traina 			exit(0);
54644099b7bSPaul Traina 		}
54744099b7bSPaul Traina 		ra_len = sizeof(recv_addr);
54844099b7bSPaul Traina 		n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
54944099b7bSPaul Traina 					 (struct sockaddr *) &recv_addr, &ra_len);
55044099b7bSPaul Traina 		if (n <= 0) {
55144099b7bSPaul Traina 			continue;
55244099b7bSPaul Traina 		}
55344099b7bSPaul Traina 		if (debug > 1) {
55444099b7bSPaul Traina 			report(LOG_INFO, "recvd pkt from IP addr %s",
55544099b7bSPaul Traina 				   inet_ntoa(recv_addr.sin_addr));
55644099b7bSPaul Traina 		}
55744099b7bSPaul Traina 		if (n < sizeof(struct bootp)) {
55844099b7bSPaul Traina 			if (debug) {
559e08ac58bSPaul Traina 				report(LOG_NOTICE, "received short packet");
56044099b7bSPaul Traina 			}
56144099b7bSPaul Traina 			continue;
56244099b7bSPaul Traina 		}
56344099b7bSPaul Traina 		pktlen = n;
56444099b7bSPaul Traina 
56544099b7bSPaul Traina 		readtab(0);				/* maybe re-read bootptab */
56644099b7bSPaul Traina 
56744099b7bSPaul Traina 		switch (bp->bp_op) {
56844099b7bSPaul Traina 		case BOOTREQUEST:
56944099b7bSPaul Traina 			handle_request();
57044099b7bSPaul Traina 			break;
57144099b7bSPaul Traina 		case BOOTREPLY:
57244099b7bSPaul Traina 			handle_reply();
57344099b7bSPaul Traina 			break;
57444099b7bSPaul Traina 		}
57544099b7bSPaul Traina 	}
57644099b7bSPaul Traina }
57744099b7bSPaul Traina 
57844099b7bSPaul Traina 
57944099b7bSPaul Traina 
58044099b7bSPaul Traina 
58144099b7bSPaul Traina /*
58244099b7bSPaul Traina  * Print "usage" message and exit
58344099b7bSPaul Traina  */
58444099b7bSPaul Traina 
58544099b7bSPaul Traina PRIVATE void
58644099b7bSPaul Traina usage()
58744099b7bSPaul Traina {
58844099b7bSPaul Traina 	fprintf(stderr,
58944099b7bSPaul Traina 			"usage:  bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n");
59044099b7bSPaul Traina 	fprintf(stderr, "\t -c n\tset current directory\n");
59144099b7bSPaul Traina 	fprintf(stderr, "\t -d n\tset debug level\n");
59244099b7bSPaul Traina 	fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n");
59344099b7bSPaul Traina 	fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n");
59444099b7bSPaul Traina 	fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n");
59544099b7bSPaul Traina 	exit(1);
59644099b7bSPaul Traina }
59744099b7bSPaul Traina 
59844099b7bSPaul Traina /* Signal catchers */
59944099b7bSPaul Traina PRIVATE void
60044099b7bSPaul Traina catcher(sig)
60144099b7bSPaul Traina 	int sig;
60244099b7bSPaul Traina {
60344099b7bSPaul Traina 	if (sig == SIGHUP)
60444099b7bSPaul Traina 		do_readtab = 1;
60544099b7bSPaul Traina 	if (sig == SIGUSR1)
60644099b7bSPaul Traina 		do_dumptab = 1;
607e08ac58bSPaul Traina #if	!defined(SA_NOCLDSTOP) && defined(SYSV)
608e08ac58bSPaul Traina 	/* For older "System V" derivatives with no sigaction(). */
60944099b7bSPaul Traina 	signal(sig, catcher);
61044099b7bSPaul Traina #endif
61144099b7bSPaul Traina }
61244099b7bSPaul Traina 
61344099b7bSPaul Traina 
61444099b7bSPaul Traina 
61544099b7bSPaul Traina /*
61644099b7bSPaul Traina  * Process BOOTREQUEST packet.
61744099b7bSPaul Traina  *
61844099b7bSPaul Traina  * Note:  This version of the bootpd.c server never forwards
61944099b7bSPaul Traina  * a request to another server.  That is the job of a gateway
62044099b7bSPaul Traina  * program such as the "bootpgw" program included here.
62144099b7bSPaul Traina  *
62244099b7bSPaul Traina  * (Also this version does not interpret the hostname field of
62344099b7bSPaul Traina  * the request packet;  it COULD do a name->address lookup and
62444099b7bSPaul Traina  * forward the request there.)
62544099b7bSPaul Traina  */
62644099b7bSPaul Traina PRIVATE void
62744099b7bSPaul Traina handle_request()
62844099b7bSPaul Traina {
62944099b7bSPaul Traina 	struct bootp *bp = (struct bootp *) pktbuf;
63044099b7bSPaul Traina 	struct host *hp = NULL;
63144099b7bSPaul Traina 	struct host dummyhost;
63244099b7bSPaul Traina 	int32 bootsize = 0;
63344099b7bSPaul Traina 	unsigned hlen, hashcode;
63444099b7bSPaul Traina 	int32 dest;
63544099b7bSPaul Traina 	char realpath[1024];
63644099b7bSPaul Traina 	char *clntpath;
63744099b7bSPaul Traina 	char *homedir, *bootfile;
63844099b7bSPaul Traina 	int n;
63944099b7bSPaul Traina 
640d284feaaSEivind Eklund 	bp->bp_file[sizeof(bp->bp_file)-1] = '\0';
641d284feaaSEivind Eklund 
64244099b7bSPaul Traina 	/* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
64344099b7bSPaul Traina 
64444099b7bSPaul Traina 	/*
64544099b7bSPaul Traina 	 * If the servername field is set, compare it against us.
64644099b7bSPaul Traina 	 * If we're not being addressed, ignore this request.
64744099b7bSPaul Traina 	 * If the server name field is null, throw in our name.
64844099b7bSPaul Traina 	 */
64944099b7bSPaul Traina 	if (strlen(bp->bp_sname)) {
65044099b7bSPaul Traina 		if (strcmp(bp->bp_sname, hostname)) {
65144099b7bSPaul Traina 			if (debug)
65244099b7bSPaul Traina 				report(LOG_INFO, "\
65344099b7bSPaul Traina ignoring request for server %s from client at %s address %s",
65444099b7bSPaul Traina 					   bp->bp_sname, netname(bp->bp_htype),
65544099b7bSPaul Traina 					   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
65644099b7bSPaul Traina 			/* XXX - Is it correct to ignore such a request? -gwr */
65744099b7bSPaul Traina 			return;
65844099b7bSPaul Traina 		}
65944099b7bSPaul Traina 	} else {
66044099b7bSPaul Traina 		strcpy(bp->bp_sname, hostname);
66144099b7bSPaul Traina 	}
66244099b7bSPaul Traina 
66344099b7bSPaul Traina 	/* Convert the request into a reply. */
66444099b7bSPaul Traina 	bp->bp_op = BOOTREPLY;
66544099b7bSPaul Traina 	if (bp->bp_ciaddr.s_addr == 0) {
66644099b7bSPaul Traina 		/*
66744099b7bSPaul Traina 		 * client doesnt know his IP address,
66844099b7bSPaul Traina 		 * search by hardware address.
66944099b7bSPaul Traina 		 */
67044099b7bSPaul Traina 		if (debug > 1) {
67144099b7bSPaul Traina 			report(LOG_INFO, "request from %s address %s",
67244099b7bSPaul Traina 				   netname(bp->bp_htype),
67344099b7bSPaul Traina 				   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
67444099b7bSPaul Traina 		}
67544099b7bSPaul Traina 		hlen = haddrlength(bp->bp_htype);
67644099b7bSPaul Traina 		if (hlen != bp->bp_hlen) {
67744099b7bSPaul Traina 			report(LOG_NOTICE, "bad addr len from from %s address %s",
67844099b7bSPaul Traina 				   netname(bp->bp_htype),
67944099b7bSPaul Traina 				   haddrtoa(bp->bp_chaddr, hlen));
68044099b7bSPaul Traina 		}
68144099b7bSPaul Traina 		dummyhost.htype = bp->bp_htype;
68244099b7bSPaul Traina 		bcopy(bp->bp_chaddr, dummyhost.haddr, hlen);
68344099b7bSPaul Traina 		hashcode = hash_HashFunction(bp->bp_chaddr, hlen);
68444099b7bSPaul Traina 		hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp,
68544099b7bSPaul Traina 										 &dummyhost);
68644099b7bSPaul Traina 		if (hp == NULL &&
68744099b7bSPaul Traina 			bp->bp_htype == HTYPE_IEEE802)
68844099b7bSPaul Traina 		{
68944099b7bSPaul Traina 			/* Try again with address in "canonical" form. */
69044099b7bSPaul Traina 			haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen);
69144099b7bSPaul Traina 			if (debug > 1) {
69244099b7bSPaul Traina 				report(LOG_INFO, "\
69344099b7bSPaul Traina HW addr type is IEEE 802.  convert to %s and check again\n",
69444099b7bSPaul Traina 					   haddrtoa(dummyhost.haddr, bp->bp_hlen));
69544099b7bSPaul Traina 			}
69644099b7bSPaul Traina 			hashcode = hash_HashFunction(dummyhost.haddr, hlen);
69744099b7bSPaul Traina 			hp = (struct host *) hash_Lookup(hwhashtable, hashcode,
69844099b7bSPaul Traina 											 hwlookcmp, &dummyhost);
69944099b7bSPaul Traina 		}
70044099b7bSPaul Traina 		if (hp == NULL) {
70144099b7bSPaul Traina 			/*
70244099b7bSPaul Traina 			 * XXX - Add dynamic IP address assignment?
70344099b7bSPaul Traina 			 */
704e08ac58bSPaul Traina 			if (debug)
705e08ac58bSPaul Traina 				report(LOG_NOTICE, "unknown client %s address %s",
70644099b7bSPaul Traina 					   netname(bp->bp_htype),
70744099b7bSPaul Traina 					   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
70844099b7bSPaul Traina 			return; /* not found */
70944099b7bSPaul Traina 		}
71044099b7bSPaul Traina 		(bp->bp_yiaddr).s_addr = hp->iaddr.s_addr;
71144099b7bSPaul Traina 
71244099b7bSPaul Traina 	} else {
71344099b7bSPaul Traina 
71444099b7bSPaul Traina 		/*
71544099b7bSPaul Traina 		 * search by IP address.
71644099b7bSPaul Traina 		 */
71744099b7bSPaul Traina 		if (debug > 1) {
71844099b7bSPaul Traina 			report(LOG_INFO, "request from IP addr %s",
71944099b7bSPaul Traina 				   inet_ntoa(bp->bp_ciaddr));
72044099b7bSPaul Traina 		}
72144099b7bSPaul Traina 		dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr;
72244099b7bSPaul Traina 		hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4);
72344099b7bSPaul Traina 		hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp,
72444099b7bSPaul Traina 										 &dummyhost);
72544099b7bSPaul Traina 		if (hp == NULL) {
726e08ac58bSPaul Traina 			if (debug) {
72744099b7bSPaul Traina 				report(LOG_NOTICE, "IP address not found: %s",
72844099b7bSPaul Traina 					   inet_ntoa(bp->bp_ciaddr));
72944099b7bSPaul Traina 			}
73044099b7bSPaul Traina 			return;
73144099b7bSPaul Traina 		}
73244099b7bSPaul Traina 	}
73344099b7bSPaul Traina 
73444099b7bSPaul Traina 	if (debug) {
73544099b7bSPaul Traina 		report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr),
73644099b7bSPaul Traina 			   hp->hostname->string);
73744099b7bSPaul Traina 	}
73844099b7bSPaul Traina 
73944099b7bSPaul Traina 	/*
74044099b7bSPaul Traina 	 * If there is a response delay threshold, ignore requests
74144099b7bSPaul Traina 	 * with a timestamp lower than the threshold.
74244099b7bSPaul Traina 	 */
74344099b7bSPaul Traina 	if (hp->flags.min_wait) {
74444099b7bSPaul Traina 		u_int32 t = (u_int32) ntohs(bp->bp_secs);
74544099b7bSPaul Traina 		if (t < hp->min_wait) {
74644099b7bSPaul Traina 			if (debug > 1)
74744099b7bSPaul Traina 				report(LOG_INFO,
74844099b7bSPaul Traina 					   "ignoring request due to timestamp (%d < %d)",
74944099b7bSPaul Traina 					   t, hp->min_wait);
75044099b7bSPaul Traina 			return;
75144099b7bSPaul Traina 		}
75244099b7bSPaul Traina 	}
75344099b7bSPaul Traina 
75444099b7bSPaul Traina #ifdef	YORK_EX_OPTION
75544099b7bSPaul Traina 	/*
75644099b7bSPaul Traina 	 * The need for the "ex" tag arose out of the need to empty
75744099b7bSPaul Traina 	 * shared networked drives on diskless PCs.  This solution is
75844099b7bSPaul Traina 	 * not very clean but it does work fairly well.
75944099b7bSPaul Traina 	 * Written by Edmund J. Sutcliffe <edmund@york.ac.uk>
76044099b7bSPaul Traina 	 *
76144099b7bSPaul Traina 	 * XXX - This could compromise security if a non-trusted user
76244099b7bSPaul Traina 	 * managed to write an entry in the bootptab with :ex=trojan:
76344099b7bSPaul Traina 	 * so I would leave this turned off unless you need it. -gwr
76444099b7bSPaul Traina 	 */
76544099b7bSPaul Traina 	/* Run a program, passing the client name as a parameter. */
76644099b7bSPaul Traina 	if (hp->flags.exec_file) {
76744099b7bSPaul Traina 		char tst[100];
76844099b7bSPaul Traina 		/* XXX - Check string lengths? -gwr */
76944099b7bSPaul Traina 		strcpy (tst, hp->exec_file->string);
77044099b7bSPaul Traina 		strcat (tst, " ");
77144099b7bSPaul Traina 		strcat (tst, hp->hostname->string);
77244099b7bSPaul Traina 		strcat (tst, " &");
77344099b7bSPaul Traina 		if (debug)
77444099b7bSPaul Traina 			report(LOG_INFO, "executing %s", tst);
77544099b7bSPaul Traina 		system(tst);	/* Hope this finishes soon... */
77644099b7bSPaul Traina 	}
77744099b7bSPaul Traina #endif	/* YORK_EX_OPTION */
77844099b7bSPaul Traina 
77944099b7bSPaul Traina 	/*
78044099b7bSPaul Traina 	 * If a specific TFTP server address was specified in the bootptab file,
78144099b7bSPaul Traina 	 * fill it in, otherwise zero it.
78244099b7bSPaul Traina 	 * XXX - Rather than zero it, should it be the bootpd address? -gwr
78344099b7bSPaul Traina 	 */
78444099b7bSPaul Traina 	(bp->bp_siaddr).s_addr = (hp->flags.bootserver) ?
78544099b7bSPaul Traina 		hp->bootserver.s_addr : 0L;
78644099b7bSPaul Traina 
78744099b7bSPaul Traina #ifdef	STANFORD_PROM_COMPAT
78844099b7bSPaul Traina 	/*
78944099b7bSPaul Traina 	 * Stanford bootp PROMs (for a Sun?) have no way to leave
79044099b7bSPaul Traina 	 * the boot file name field blank (because the boot file
79144099b7bSPaul Traina 	 * name is automatically generated from some index).
79244099b7bSPaul Traina 	 * As a work-around, this little hack allows those PROMs to
79344099b7bSPaul Traina 	 * specify "sunboot14" with the same effect as a NULL name.
79444099b7bSPaul Traina 	 * (The user specifies boot device 14 or some such magic.)
79544099b7bSPaul Traina 	 */
79644099b7bSPaul Traina 	if (strcmp(bp->bp_file, "sunboot14") == 0)
79744099b7bSPaul Traina 		bp->bp_file[0] = '\0';	/* treat it as unspecified */
79844099b7bSPaul Traina #endif
79944099b7bSPaul Traina 
80044099b7bSPaul Traina 	/*
80144099b7bSPaul Traina 	 * Fill in the client's proper bootfile.
80244099b7bSPaul Traina 	 *
80344099b7bSPaul Traina 	 * If the client specifies an absolute path, try that file with a
80444099b7bSPaul Traina 	 * ".host" suffix and then without.  If the file cannot be found, no
80544099b7bSPaul Traina 	 * reply is made at all.
80644099b7bSPaul Traina 	 *
80744099b7bSPaul Traina 	 * If the client specifies a null or relative file, use the following
80844099b7bSPaul Traina 	 * table to determine the appropriate action:
80944099b7bSPaul Traina 	 *
81044099b7bSPaul Traina 	 *  Homedir      Bootfile    Client's file
81144099b7bSPaul Traina 	 * specified?   specified?   specification   Action
81244099b7bSPaul Traina 	 * -------------------------------------------------------------------
81344099b7bSPaul Traina 	 *      No          No          Null         Send null filename
81444099b7bSPaul Traina 	 *      No          No          Relative     Discard request
81544099b7bSPaul Traina 	 *      No          Yes         Null         Send if absolute else null
81644099b7bSPaul Traina 	 *      No          Yes         Relative     Discard request     *XXX
81744099b7bSPaul Traina 	 *      Yes         No          Null         Send null filename
81844099b7bSPaul Traina 	 *      Yes         No          Relative     Lookup with ".host"
81944099b7bSPaul Traina 	 *      Yes         Yes         Null         Send home/boot or bootfile
82044099b7bSPaul Traina 	 *      Yes         Yes         Relative     Lookup with ".host" *XXX
82144099b7bSPaul Traina 	 *
82244099b7bSPaul Traina 	 */
82344099b7bSPaul Traina 
82444099b7bSPaul Traina 	/*
82544099b7bSPaul Traina 	 * XXX - I don't like the policy of ignoring a client when the
82644099b7bSPaul Traina 	 * boot file is not accessible.  The TFTP server might not be
82744099b7bSPaul Traina 	 * running on the same machine as the BOOTP server, in which
82844099b7bSPaul Traina 	 * case checking accessibility of the boot file is pointless.
82944099b7bSPaul Traina 	 *
83044099b7bSPaul Traina 	 * Therefore, file accessibility is now demanded ONLY if you
83144099b7bSPaul Traina 	 * define CHECK_FILE_ACCESS in the Makefile options. -gwr
83244099b7bSPaul Traina 	 */
83344099b7bSPaul Traina 
83444099b7bSPaul Traina 	/*
83544099b7bSPaul Traina 	 * The "real" path is as seen by the BOOTP daemon on this
83644099b7bSPaul Traina 	 * machine, while the client path is relative to the TFTP
83744099b7bSPaul Traina 	 * daemon chroot directory (i.e. /tftpboot).
83844099b7bSPaul Traina 	 */
83944099b7bSPaul Traina 	if (hp->flags.tftpdir) {
8403a3c0cf4SMatthew Dillon 		snprintf(realpath, sizeof(realpath), "%s", hp->tftpdir->string);
84144099b7bSPaul Traina 		clntpath = &realpath[strlen(realpath)];
84244099b7bSPaul Traina 	} else {
84344099b7bSPaul Traina 		realpath[0] = '\0';
84444099b7bSPaul Traina 		clntpath = realpath;
84544099b7bSPaul Traina 	}
84644099b7bSPaul Traina 
84744099b7bSPaul Traina 	/*
84844099b7bSPaul Traina 	 * Determine client's requested homedir and bootfile.
84944099b7bSPaul Traina 	 */
85044099b7bSPaul Traina 	homedir = NULL;
85144099b7bSPaul Traina 	bootfile = NULL;
85244099b7bSPaul Traina 	if (bp->bp_file[0]) {
85344099b7bSPaul Traina 		homedir = bp->bp_file;
85444099b7bSPaul Traina 		bootfile = strrchr(homedir, '/');
85544099b7bSPaul Traina 		if (bootfile) {
85644099b7bSPaul Traina 			if (homedir == bootfile)
85744099b7bSPaul Traina 				homedir = NULL;
85844099b7bSPaul Traina 			*bootfile++ = '\0';
85944099b7bSPaul Traina 		} else {
86044099b7bSPaul Traina 			/* no "/" in the string */
86144099b7bSPaul Traina 			bootfile = homedir;
86244099b7bSPaul Traina 			homedir = NULL;
86344099b7bSPaul Traina 		}
86444099b7bSPaul Traina 		if (debug > 2) {
86544099b7bSPaul Traina 			report(LOG_INFO, "requested path=\"%s\"  file=\"%s\"",
86644099b7bSPaul Traina 				   (homedir) ? homedir : "",
86744099b7bSPaul Traina 				   (bootfile) ? bootfile : "");
86844099b7bSPaul Traina 		}
86944099b7bSPaul Traina 	}
87044099b7bSPaul Traina 
87144099b7bSPaul Traina 	/*
87244099b7bSPaul Traina 	 * Specifications in bootptab override client requested values.
87344099b7bSPaul Traina 	 */
87444099b7bSPaul Traina 	if (hp->flags.homedir)
87544099b7bSPaul Traina 		homedir = hp->homedir->string;
87644099b7bSPaul Traina 	if (hp->flags.bootfile)
87744099b7bSPaul Traina 		bootfile = hp->bootfile->string;
87844099b7bSPaul Traina 
87944099b7bSPaul Traina 	/*
88044099b7bSPaul Traina 	 * Construct bootfile path.
88144099b7bSPaul Traina 	 */
88244099b7bSPaul Traina 	if (homedir) {
88344099b7bSPaul Traina 		if (homedir[0] != '/')
88444099b7bSPaul Traina 			strcat(clntpath, "/");
88544099b7bSPaul Traina 		strcat(clntpath, homedir);
88644099b7bSPaul Traina 		homedir = NULL;
88744099b7bSPaul Traina 	}
88844099b7bSPaul Traina 	if (bootfile) {
88944099b7bSPaul Traina 		if (bootfile[0] != '/')
89044099b7bSPaul Traina 			strcat(clntpath, "/");
89144099b7bSPaul Traina 		strcat(clntpath, bootfile);
89244099b7bSPaul Traina 		bootfile = NULL;
89344099b7bSPaul Traina 	}
89444099b7bSPaul Traina 
89544099b7bSPaul Traina 	/*
89644099b7bSPaul Traina 	 * First try to find the file with a ".host" suffix
89744099b7bSPaul Traina 	 */
89844099b7bSPaul Traina 	n = strlen(clntpath);
89944099b7bSPaul Traina 	strcat(clntpath, ".");
90044099b7bSPaul Traina 	strcat(clntpath, hp->hostname->string);
90144099b7bSPaul Traina 	if (chk_access(realpath, &bootsize) < 0) {
90244099b7bSPaul Traina 		clntpath[n] = 0;			/* Try it without the suffix */
90344099b7bSPaul Traina 		if (chk_access(realpath, &bootsize) < 0) {
90444099b7bSPaul Traina 			/* neither "file.host" nor "file" was found */
90544099b7bSPaul Traina #ifdef	CHECK_FILE_ACCESS
90644099b7bSPaul Traina 
90744099b7bSPaul Traina 			if (bp->bp_file[0]) {
90844099b7bSPaul Traina 				/*
90944099b7bSPaul Traina 				 * Client wanted specific file
91044099b7bSPaul Traina 				 * and we didn't have it.
91144099b7bSPaul Traina 				 */
91244099b7bSPaul Traina 				report(LOG_NOTICE,
91344099b7bSPaul Traina 					   "requested file not found: \"%s\"", clntpath);
91444099b7bSPaul Traina 				return;
91544099b7bSPaul Traina 			}
91644099b7bSPaul Traina 			/*
91744099b7bSPaul Traina 			 * Client didn't ask for a specific file and we couldn't
91844099b7bSPaul Traina 			 * access the default file, so just zero-out the bootfile
91944099b7bSPaul Traina 			 * field in the packet and continue processing the reply.
92044099b7bSPaul Traina 			 */
92144099b7bSPaul Traina 			bzero(bp->bp_file, sizeof(bp->bp_file));
92244099b7bSPaul Traina 			goto null_file_name;
92344099b7bSPaul Traina 
92444099b7bSPaul Traina #else	/* CHECK_FILE_ACCESS */
92544099b7bSPaul Traina 
92644099b7bSPaul Traina 			/* Complain only if boot file size was needed. */
92744099b7bSPaul Traina 			if (hp->flags.bootsize_auto) {
92844099b7bSPaul Traina 				report(LOG_ERR, "can not determine size of file \"%s\"",
92944099b7bSPaul Traina 					   clntpath);
93044099b7bSPaul Traina 			}
93144099b7bSPaul Traina 
93244099b7bSPaul Traina #endif	/* CHECK_FILE_ACCESS */
93344099b7bSPaul Traina 		}
93444099b7bSPaul Traina 	}
93544099b7bSPaul Traina 	strncpy(bp->bp_file, clntpath, BP_FILE_LEN);
93644099b7bSPaul Traina 	if (debug > 2)
93744099b7bSPaul Traina 		report(LOG_INFO, "bootfile=\"%s\"", clntpath);
93844099b7bSPaul Traina 
939e08ac58bSPaul Traina #ifdef	CHECK_FILE_ACCESS
94044099b7bSPaul Traina null_file_name:
941e08ac58bSPaul Traina #endif	/* CHECK_FILE_ACCESS */
94244099b7bSPaul Traina 
94344099b7bSPaul Traina 
94444099b7bSPaul Traina 	/*
94544099b7bSPaul Traina 	 * Handle vendor options based on magic number.
94644099b7bSPaul Traina 	 */
94744099b7bSPaul Traina 
94844099b7bSPaul Traina 	if (debug > 1) {
94944099b7bSPaul Traina 		report(LOG_INFO, "vendor magic field is %d.%d.%d.%d",
95044099b7bSPaul Traina 			   (int) ((bp->bp_vend)[0]),
95144099b7bSPaul Traina 			   (int) ((bp->bp_vend)[1]),
95244099b7bSPaul Traina 			   (int) ((bp->bp_vend)[2]),
95344099b7bSPaul Traina 			   (int) ((bp->bp_vend)[3]));
95444099b7bSPaul Traina 	}
95544099b7bSPaul Traina 	/*
95644099b7bSPaul Traina 	 * If this host isn't set for automatic vendor info then copy the
95744099b7bSPaul Traina 	 * specific cookie into the bootp packet, thus forcing a certain
95844099b7bSPaul Traina 	 * reply format.  Only force reply format if user specified it.
95944099b7bSPaul Traina 	 */
96044099b7bSPaul Traina 	if (hp->flags.vm_cookie) {
96144099b7bSPaul Traina 		/* Slam in the user specified magic number. */
96244099b7bSPaul Traina 		bcopy(hp->vm_cookie, bp->bp_vend, 4);
96344099b7bSPaul Traina 	}
96444099b7bSPaul Traina 	/*
96544099b7bSPaul Traina 	 * Figure out the format for the vendor-specific info.
96644099b7bSPaul Traina 	 * Note that bp->bp_vend may have been set above.
96744099b7bSPaul Traina 	 */
96844099b7bSPaul Traina 	if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) {
96944099b7bSPaul Traina 		/* RFC1048 conformant bootp client */
97044099b7bSPaul Traina 		dovend_rfc1048(bp, hp, bootsize);
97144099b7bSPaul Traina 		if (debug > 1) {
97244099b7bSPaul Traina 			report(LOG_INFO, "sending reply (with RFC1048 options)");
97344099b7bSPaul Traina 		}
97444099b7bSPaul Traina 	}
97544099b7bSPaul Traina #ifdef VEND_CMU
97644099b7bSPaul Traina 	else if (!bcmp(bp->bp_vend, vm_cmu, 4)) {
97744099b7bSPaul Traina 		dovend_cmu(bp, hp);
97844099b7bSPaul Traina 		if (debug > 1) {
97944099b7bSPaul Traina 			report(LOG_INFO, "sending reply (with CMU options)");
98044099b7bSPaul Traina 		}
98144099b7bSPaul Traina 	}
98244099b7bSPaul Traina #endif
98344099b7bSPaul Traina 	else {
98444099b7bSPaul Traina 		if (debug > 1) {
98544099b7bSPaul Traina 			report(LOG_INFO, "sending reply (with no options)");
98644099b7bSPaul Traina 		}
98744099b7bSPaul Traina 	}
98844099b7bSPaul Traina 
98944099b7bSPaul Traina 	dest = (hp->flags.reply_addr) ?
99044099b7bSPaul Traina 		hp->reply_addr.s_addr : 0L;
99144099b7bSPaul Traina 
99244099b7bSPaul Traina 	/* not forwarded */
99344099b7bSPaul Traina 	sendreply(0, dest);
99444099b7bSPaul Traina }
99544099b7bSPaul Traina 
99644099b7bSPaul Traina 
99744099b7bSPaul Traina /*
99844099b7bSPaul Traina  * Process BOOTREPLY packet.
99944099b7bSPaul Traina  */
100044099b7bSPaul Traina PRIVATE void
100144099b7bSPaul Traina handle_reply()
100244099b7bSPaul Traina {
100344099b7bSPaul Traina 	if (debug) {
100444099b7bSPaul Traina 		report(LOG_INFO, "processing boot reply");
100544099b7bSPaul Traina 	}
100644099b7bSPaul Traina 	/* forwarded, no destination override */
100744099b7bSPaul Traina 	sendreply(1, 0);
100844099b7bSPaul Traina }
100944099b7bSPaul Traina 
101044099b7bSPaul Traina 
101144099b7bSPaul Traina /*
101244099b7bSPaul Traina  * Send a reply packet to the client.  'forward' flag is set if we are
101344099b7bSPaul Traina  * not the originator of this reply packet.
101444099b7bSPaul Traina  */
101544099b7bSPaul Traina PRIVATE void
101644099b7bSPaul Traina sendreply(forward, dst_override)
101744099b7bSPaul Traina 	int forward;
101844099b7bSPaul Traina 	int32 dst_override;
101944099b7bSPaul Traina {
102044099b7bSPaul Traina 	struct bootp *bp = (struct bootp *) pktbuf;
102144099b7bSPaul Traina 	struct in_addr dst;
102244099b7bSPaul Traina 	u_short port = bootpc_port;
102344099b7bSPaul Traina 	unsigned char *ha;
1024e08ac58bSPaul Traina 	int len, haf;
102544099b7bSPaul Traina 
102644099b7bSPaul Traina 	/*
102744099b7bSPaul Traina 	 * XXX - Should honor bp_flags "broadcast" bit here.
102844099b7bSPaul Traina 	 * Temporary workaround: use the :ra=ADDR: option to
102944099b7bSPaul Traina 	 * set the reply address to the broadcast address.
103044099b7bSPaul Traina 	 */
103144099b7bSPaul Traina 
103244099b7bSPaul Traina 	/*
103344099b7bSPaul Traina 	 * If the destination address was specified explicitly
103444099b7bSPaul Traina 	 * (i.e. the broadcast address for HP compatiblity)
103544099b7bSPaul Traina 	 * then send the response to that address.  Otherwise,
103644099b7bSPaul Traina 	 * act in accordance with RFC951:
103744099b7bSPaul Traina 	 *   If the client IP address is specified, use that
103844099b7bSPaul Traina 	 * else if gateway IP address is specified, use that
103944099b7bSPaul Traina 	 * else make a temporary arp cache entry for the client's
104044099b7bSPaul Traina 	 * NEW IP/hardware address and use that.
104144099b7bSPaul Traina 	 */
104244099b7bSPaul Traina 	if (dst_override) {
104344099b7bSPaul Traina 		dst.s_addr = dst_override;
104444099b7bSPaul Traina 		if (debug > 1) {
104544099b7bSPaul Traina 			report(LOG_INFO, "reply address override: %s",
104644099b7bSPaul Traina 				   inet_ntoa(dst));
104744099b7bSPaul Traina 		}
104844099b7bSPaul Traina 	} else if (bp->bp_ciaddr.s_addr) {
104944099b7bSPaul Traina 		dst = bp->bp_ciaddr;
105044099b7bSPaul Traina 	} else if (bp->bp_giaddr.s_addr && forward == 0) {
105144099b7bSPaul Traina 		dst = bp->bp_giaddr;
105244099b7bSPaul Traina 		port = bootps_port;
105344099b7bSPaul Traina 		if (debug > 1) {
105444099b7bSPaul Traina 			report(LOG_INFO, "sending reply to gateway %s",
105544099b7bSPaul Traina 				   inet_ntoa(dst));
105644099b7bSPaul Traina 		}
105744099b7bSPaul Traina 	} else {
105844099b7bSPaul Traina 		dst = bp->bp_yiaddr;
105944099b7bSPaul Traina 		ha = bp->bp_chaddr;
106044099b7bSPaul Traina 		len = bp->bp_hlen;
106144099b7bSPaul Traina 		if (len > MAXHADDRLEN)
106244099b7bSPaul Traina 			len = MAXHADDRLEN;
1063e08ac58bSPaul Traina 		haf = (int) bp->bp_htype;
1064e08ac58bSPaul Traina 		if (haf == 0)
1065e08ac58bSPaul Traina 			haf = HTYPE_ETHERNET;
106644099b7bSPaul Traina 
106744099b7bSPaul Traina 		if (debug > 1)
106844099b7bSPaul Traina 			report(LOG_INFO, "setarp %s - %s",
106944099b7bSPaul Traina 				   inet_ntoa(dst), haddrtoa(ha, len));
1070e08ac58bSPaul Traina 		setarp(s, &dst, haf, ha, len);
107144099b7bSPaul Traina 	}
107244099b7bSPaul Traina 
107344099b7bSPaul Traina 	if ((forward == 0) &&
107444099b7bSPaul Traina 		(bp->bp_siaddr.s_addr == 0))
107544099b7bSPaul Traina 	{
107644099b7bSPaul Traina 		struct ifreq *ifr;
107744099b7bSPaul Traina 		struct in_addr siaddr;
107844099b7bSPaul Traina 		/*
107944099b7bSPaul Traina 		 * If we are originating this reply, we
108044099b7bSPaul Traina 		 * need to find our own interface address to
108144099b7bSPaul Traina 		 * put in the bp_siaddr field of the reply.
108244099b7bSPaul Traina 		 * If this server is multi-homed, pick the
108344099b7bSPaul Traina 		 * 'best' interface (the one on the same net
108444099b7bSPaul Traina 		 * as the client).  Of course, the client may
108544099b7bSPaul Traina 		 * be on the other side of a BOOTP gateway...
108644099b7bSPaul Traina 		 */
108744099b7bSPaul Traina 		ifr = getif(s, &dst);
108844099b7bSPaul Traina 		if (ifr) {
108944099b7bSPaul Traina 			struct sockaddr_in *sip;
109044099b7bSPaul Traina 			sip = (struct sockaddr_in *) &(ifr->ifr_addr);
109144099b7bSPaul Traina 			siaddr = sip->sin_addr;
109244099b7bSPaul Traina 		} else {
109344099b7bSPaul Traina 			/* Just use my "official" IP address. */
109444099b7bSPaul Traina 			siaddr = my_ip_addr;
109544099b7bSPaul Traina 		}
109644099b7bSPaul Traina 
109744099b7bSPaul Traina 		/* XXX - No need to set bp_giaddr here. */
109844099b7bSPaul Traina 
109944099b7bSPaul Traina 		/* Finally, set the server address field. */
110044099b7bSPaul Traina 		bp->bp_siaddr = siaddr;
110144099b7bSPaul Traina 	}
110244099b7bSPaul Traina 	/* Set up socket address for send. */
110344099b7bSPaul Traina 	send_addr.sin_family = AF_INET;
110444099b7bSPaul Traina 	send_addr.sin_port = htons(port);
110544099b7bSPaul Traina 	send_addr.sin_addr = dst;
110644099b7bSPaul Traina 
110744099b7bSPaul Traina 	/* Send reply with same size packet as request used. */
110844099b7bSPaul Traina 	if (sendto(s, pktbuf, pktlen, 0,
110944099b7bSPaul Traina 			   (struct sockaddr *) &send_addr,
111044099b7bSPaul Traina 			   sizeof(send_addr)) < 0)
111144099b7bSPaul Traina 	{
111244099b7bSPaul Traina 		report(LOG_ERR, "sendto: %s", get_network_errmsg());
111344099b7bSPaul Traina 	}
111444099b7bSPaul Traina } /* sendreply */
111544099b7bSPaul Traina 
111644099b7bSPaul Traina 
111744099b7bSPaul Traina /* nmatch() - now in getif.c */
111844099b7bSPaul Traina /* setarp() - now in hwaddr.c */
111944099b7bSPaul Traina 
112044099b7bSPaul Traina 
112144099b7bSPaul Traina /*
112244099b7bSPaul Traina  * This call checks read access to a file.  It returns 0 if the file given
112344099b7bSPaul Traina  * by "path" exists and is publically readable.  A value of -1 is returned if
112444099b7bSPaul Traina  * access is not permitted or an error occurs.  Successful calls also
112544099b7bSPaul Traina  * return the file size in bytes using the long pointer "filesize".
112644099b7bSPaul Traina  *
112744099b7bSPaul Traina  * The read permission bit for "other" users is checked.  This bit must be
112844099b7bSPaul Traina  * set for tftpd(8) to allow clients to read the file.
112944099b7bSPaul Traina  */
113044099b7bSPaul Traina 
113144099b7bSPaul Traina PRIVATE int
113244099b7bSPaul Traina chk_access(path, filesize)
113344099b7bSPaul Traina 	char *path;
113444099b7bSPaul Traina 	int32 *filesize;
113544099b7bSPaul Traina {
113644099b7bSPaul Traina 	struct stat st;
113744099b7bSPaul Traina 
113844099b7bSPaul Traina 	if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) {
113944099b7bSPaul Traina 		*filesize = (int32) st.st_size;
114044099b7bSPaul Traina 		return 0;
114144099b7bSPaul Traina 	} else {
114244099b7bSPaul Traina 		return -1;
114344099b7bSPaul Traina 	}
114444099b7bSPaul Traina }
114544099b7bSPaul Traina 
114644099b7bSPaul Traina 
114744099b7bSPaul Traina /*
114844099b7bSPaul Traina  * Now in dumptab.c :
114944099b7bSPaul Traina  *	dumptab()
115044099b7bSPaul Traina  *	dump_host()
115144099b7bSPaul Traina  *	list_ipaddresses()
115244099b7bSPaul Traina  */
115344099b7bSPaul Traina 
115444099b7bSPaul Traina #ifdef VEND_CMU
115544099b7bSPaul Traina 
115644099b7bSPaul Traina /*
115744099b7bSPaul Traina  * Insert the CMU "vendor" data for the host pointed to by "hp" into the
115844099b7bSPaul Traina  * bootp packet pointed to by "bp".
115944099b7bSPaul Traina  */
116044099b7bSPaul Traina 
116144099b7bSPaul Traina PRIVATE void
116244099b7bSPaul Traina dovend_cmu(bp, hp)
116344099b7bSPaul Traina 	struct bootp *bp;
116444099b7bSPaul Traina 	struct host *hp;
116544099b7bSPaul Traina {
116644099b7bSPaul Traina 	struct cmu_vend *vendp;
116744099b7bSPaul Traina 	struct in_addr_list *taddr;
116844099b7bSPaul Traina 
116944099b7bSPaul Traina 	/*
117044099b7bSPaul Traina 	 * Initialize the entire vendor field to zeroes.
117144099b7bSPaul Traina 	 */
117244099b7bSPaul Traina 	bzero(bp->bp_vend, sizeof(bp->bp_vend));
117344099b7bSPaul Traina 
117444099b7bSPaul Traina 	/*
117544099b7bSPaul Traina 	 * Fill in vendor information. Subnet mask, default gateway,
117644099b7bSPaul Traina 	 * domain name server, ien name server, time server
117744099b7bSPaul Traina 	 */
117844099b7bSPaul Traina 	vendp = (struct cmu_vend *) bp->bp_vend;
117944099b7bSPaul Traina 	strcpy(vendp->v_magic, (char *)vm_cmu);
118044099b7bSPaul Traina 	if (hp->flags.subnet_mask) {
118144099b7bSPaul Traina 		(vendp->v_smask).s_addr = hp->subnet_mask.s_addr;
118244099b7bSPaul Traina 		(vendp->v_flags) |= VF_SMASK;
118344099b7bSPaul Traina 		if (hp->flags.gateway) {
118444099b7bSPaul Traina 			(vendp->v_dgate).s_addr = hp->gateway->addr->s_addr;
118544099b7bSPaul Traina 		}
118644099b7bSPaul Traina 	}
118744099b7bSPaul Traina 	if (hp->flags.domain_server) {
118844099b7bSPaul Traina 		taddr = hp->domain_server;
118944099b7bSPaul Traina 		if (taddr->addrcount > 0) {
119044099b7bSPaul Traina 			(vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr;
119144099b7bSPaul Traina 			if (taddr->addrcount > 1) {
119244099b7bSPaul Traina 				(vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr;
119344099b7bSPaul Traina 			}
119444099b7bSPaul Traina 		}
119544099b7bSPaul Traina 	}
119644099b7bSPaul Traina 	if (hp->flags.name_server) {
119744099b7bSPaul Traina 		taddr = hp->name_server;
119844099b7bSPaul Traina 		if (taddr->addrcount > 0) {
119944099b7bSPaul Traina 			(vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr;
120044099b7bSPaul Traina 			if (taddr->addrcount > 1) {
120144099b7bSPaul Traina 				(vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr;
120244099b7bSPaul Traina 			}
120344099b7bSPaul Traina 		}
120444099b7bSPaul Traina 	}
120544099b7bSPaul Traina 	if (hp->flags.time_server) {
120644099b7bSPaul Traina 		taddr = hp->time_server;
120744099b7bSPaul Traina 		if (taddr->addrcount > 0) {
120844099b7bSPaul Traina 			(vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr;
120944099b7bSPaul Traina 			if (taddr->addrcount > 1) {
121044099b7bSPaul Traina 				(vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr;
121144099b7bSPaul Traina 			}
121244099b7bSPaul Traina 		}
121344099b7bSPaul Traina 	}
121444099b7bSPaul Traina 	/* Log message now done by caller. */
121544099b7bSPaul Traina } /* dovend_cmu */
121644099b7bSPaul Traina 
121744099b7bSPaul Traina #endif /* VEND_CMU */
121844099b7bSPaul Traina 
121944099b7bSPaul Traina 
122044099b7bSPaul Traina 
122144099b7bSPaul Traina /*
122244099b7bSPaul Traina  * Insert the RFC1048 vendor data for the host pointed to by "hp" into the
122344099b7bSPaul Traina  * bootp packet pointed to by "bp".
122444099b7bSPaul Traina  */
122544099b7bSPaul Traina #define	NEED(LEN, MSG) do \
122644099b7bSPaul Traina 	if (bytesleft < (LEN)) { \
122744099b7bSPaul Traina 		report(LOG_NOTICE, noroom, \
122844099b7bSPaul Traina 			   hp->hostname->string, MSG); \
122944099b7bSPaul Traina 		return; \
123044099b7bSPaul Traina 	} while (0)
123144099b7bSPaul Traina PRIVATE void
123244099b7bSPaul Traina dovend_rfc1048(bp, hp, bootsize)
123344099b7bSPaul Traina 	struct bootp *bp;
123444099b7bSPaul Traina 	struct host *hp;
123544099b7bSPaul Traina 	int32 bootsize;
123644099b7bSPaul Traina {
123744099b7bSPaul Traina 	int bytesleft, len;
123844099b7bSPaul Traina 	byte *vp;
123944099b7bSPaul Traina 
124044099b7bSPaul Traina 	static char noroom[] = "%s: No room for \"%s\" option";
124144099b7bSPaul Traina 
124244099b7bSPaul Traina 	vp = bp->bp_vend;
124344099b7bSPaul Traina 
124444099b7bSPaul Traina 	if (hp->flags.msg_size) {
124544099b7bSPaul Traina 		pktlen = hp->msg_size;
124644099b7bSPaul Traina 	} else {
124744099b7bSPaul Traina 		/*
124844099b7bSPaul Traina 		 * If the request was longer than the official length, build
124944099b7bSPaul Traina 		 * a response of that same length where the additional length
125044099b7bSPaul Traina 		 * is assumed to be part of the bp_vend (options) area.
125144099b7bSPaul Traina 		 */
125244099b7bSPaul Traina 		if (pktlen > sizeof(*bp)) {
125344099b7bSPaul Traina 			if (debug > 1)
125444099b7bSPaul Traina 				report(LOG_INFO, "request message length=%d", pktlen);
125544099b7bSPaul Traina 		}
125644099b7bSPaul Traina 		/*
125744099b7bSPaul Traina 		 * Check whether the request contains the option:
125844099b7bSPaul Traina 		 * Maximum DHCP Message Size (RFC1533 sec. 9.8)
125944099b7bSPaul Traina 		 * and if so, override the response length with its value.
126044099b7bSPaul Traina 		 * This request must lie within the first BP_VEND_LEN
126144099b7bSPaul Traina 		 * bytes of the option space.
126244099b7bSPaul Traina 		 */
126344099b7bSPaul Traina 		{
126444099b7bSPaul Traina 			byte *p, *ep;
126544099b7bSPaul Traina 			byte tag, len;
126644099b7bSPaul Traina 			short msgsz = 0;
126744099b7bSPaul Traina 
126844099b7bSPaul Traina 			p = vp + 4;
126944099b7bSPaul Traina 			ep = p + BP_VEND_LEN - 4;
127044099b7bSPaul Traina 			while (p < ep) {
127144099b7bSPaul Traina 				tag = *p++;
127244099b7bSPaul Traina 				/* Check for tags with no data first. */
127344099b7bSPaul Traina 				if (tag == TAG_PAD)
127444099b7bSPaul Traina 					continue;
127544099b7bSPaul Traina 				if (tag == TAG_END)
127644099b7bSPaul Traina 					break;
127744099b7bSPaul Traina 				/* Now scan the length byte. */
127844099b7bSPaul Traina 				len = *p++;
127944099b7bSPaul Traina 				switch (tag) {
128044099b7bSPaul Traina 				case TAG_MAX_MSGSZ:
128144099b7bSPaul Traina 					if (len == 2) {
128244099b7bSPaul Traina 						bcopy(p, (char*)&msgsz, 2);
128344099b7bSPaul Traina 						msgsz = ntohs(msgsz);
128444099b7bSPaul Traina 					}
128544099b7bSPaul Traina 					break;
128644099b7bSPaul Traina 				case TAG_SUBNET_MASK:
128744099b7bSPaul Traina 					/* XXX - Should preserve this if given... */
128844099b7bSPaul Traina 					break;
128944099b7bSPaul Traina 				} /* swtich */
129044099b7bSPaul Traina 				p += len;
129144099b7bSPaul Traina 			}
129244099b7bSPaul Traina 
129344099b7bSPaul Traina 			if (msgsz > sizeof(*bp)) {
129444099b7bSPaul Traina 				if (debug > 1)
129544099b7bSPaul Traina 					report(LOG_INFO, "request has DHCP msglen=%d", msgsz);
129644099b7bSPaul Traina 				pktlen = msgsz;
129744099b7bSPaul Traina 			}
129844099b7bSPaul Traina 		}
129944099b7bSPaul Traina 	}
130044099b7bSPaul Traina 
130144099b7bSPaul Traina 	if (pktlen < sizeof(*bp)) {
130244099b7bSPaul Traina 		report(LOG_ERR, "invalid response length=%d", pktlen);
130344099b7bSPaul Traina 		pktlen = sizeof(*bp);
130444099b7bSPaul Traina 	}
130544099b7bSPaul Traina 	bytesleft = ((byte*)bp + pktlen) - vp;
130644099b7bSPaul Traina 	if (pktlen > sizeof(*bp)) {
130744099b7bSPaul Traina 		if (debug > 1)
130844099b7bSPaul Traina 			report(LOG_INFO, "extended reply, length=%d, options=%d",
130944099b7bSPaul Traina 				   pktlen, bytesleft);
131044099b7bSPaul Traina 	}
131144099b7bSPaul Traina 
131244099b7bSPaul Traina 	/* Copy in the magic cookie */
131344099b7bSPaul Traina 	bcopy(vm_rfc1048, vp, 4);
131444099b7bSPaul Traina 	vp += 4;
131544099b7bSPaul Traina 	bytesleft -= 4;
131644099b7bSPaul Traina 
131744099b7bSPaul Traina 	if (hp->flags.subnet_mask) {
131844099b7bSPaul Traina 		/* always enough room here. */
131944099b7bSPaul Traina 		*vp++ = TAG_SUBNET_MASK;/* -1 byte  */
132044099b7bSPaul Traina 		*vp++ = 4;				/* -1 byte  */
132144099b7bSPaul Traina 		insert_u_long(hp->subnet_mask.s_addr, &vp);	/* -4 bytes */
132244099b7bSPaul Traina 		bytesleft -= 6;			/* Fix real count */
132344099b7bSPaul Traina 		if (hp->flags.gateway) {
132444099b7bSPaul Traina 			(void) insert_ip(TAG_GATEWAY,
132544099b7bSPaul Traina 							 hp->gateway,
132644099b7bSPaul Traina 							 &vp, &bytesleft);
132744099b7bSPaul Traina 		}
132844099b7bSPaul Traina 	}
132944099b7bSPaul Traina 	if (hp->flags.bootsize) {
133044099b7bSPaul Traina 		/* always enough room here */
133144099b7bSPaul Traina 		bootsize = (hp->flags.bootsize_auto) ?
133244099b7bSPaul Traina 			((bootsize + 511) / 512) : (hp->bootsize);	/* Round up */
133344099b7bSPaul Traina 		*vp++ = TAG_BOOT_SIZE;
133444099b7bSPaul Traina 		*vp++ = 2;
133544099b7bSPaul Traina 		*vp++ = (byte) ((bootsize >> 8) & 0xFF);
133644099b7bSPaul Traina 		*vp++ = (byte) (bootsize & 0xFF);
133744099b7bSPaul Traina 		bytesleft -= 4;			/* Tag, length, and 16 bit blocksize */
133844099b7bSPaul Traina 	}
133944099b7bSPaul Traina 	/*
134044099b7bSPaul Traina 	 * This one is special: Remaining options go in the ext file.
134144099b7bSPaul Traina 	 * Only the subnet_mask, bootsize, and gateway should precede.
134244099b7bSPaul Traina 	 */
134344099b7bSPaul Traina 	if (hp->flags.exten_file) {
134444099b7bSPaul Traina 		/*
134544099b7bSPaul Traina 		 * Check for room for exten_file.  Add 3 to account for
134644099b7bSPaul Traina 		 * TAG_EXTEN_FILE, length, and TAG_END.
134744099b7bSPaul Traina 		 */
134844099b7bSPaul Traina 		len = strlen(hp->exten_file->string);
134944099b7bSPaul Traina 		NEED((len + 3), "ef");
135044099b7bSPaul Traina 		*vp++ = TAG_EXTEN_FILE;
135144099b7bSPaul Traina 		*vp++ = (byte) (len & 0xFF);
135244099b7bSPaul Traina 		bcopy(hp->exten_file->string, vp, len);
135344099b7bSPaul Traina 		vp += len;
135444099b7bSPaul Traina 		*vp++ = TAG_END;
135544099b7bSPaul Traina 		bytesleft -= len + 3;
135644099b7bSPaul Traina 		return;					/* no more options here. */
135744099b7bSPaul Traina 	}
135844099b7bSPaul Traina 	/*
135944099b7bSPaul Traina 	 * The remaining options are inserted by the following
136044099b7bSPaul Traina 	 * function (which is shared with bootpef.c).
136144099b7bSPaul Traina 	 * Keep back one byte for the TAG_END.
136244099b7bSPaul Traina 	 */
136344099b7bSPaul Traina 	len = dovend_rfc1497(hp, vp, bytesleft - 1);
136444099b7bSPaul Traina 	vp += len;
136544099b7bSPaul Traina 	bytesleft -= len;
136644099b7bSPaul Traina 
136744099b7bSPaul Traina 	/* There should be at least one byte left. */
136844099b7bSPaul Traina 	NEED(1, "(end)");
136944099b7bSPaul Traina 	*vp++ = TAG_END;
137044099b7bSPaul Traina 	bytesleft--;
137144099b7bSPaul Traina 
137244099b7bSPaul Traina 	/* Log message done by caller. */
137344099b7bSPaul Traina 	if (bytesleft > 0) {
137444099b7bSPaul Traina 		/*
137544099b7bSPaul Traina 		 * Zero out any remaining part of the vendor area.
137644099b7bSPaul Traina 		 */
137744099b7bSPaul Traina 		bzero(vp, bytesleft);
137844099b7bSPaul Traina 	}
137944099b7bSPaul Traina } /* dovend_rfc1048 */
138044099b7bSPaul Traina #undef	NEED
138144099b7bSPaul Traina 
138244099b7bSPaul Traina 
138344099b7bSPaul Traina /*
138444099b7bSPaul Traina  * Now in readfile.c:
138544099b7bSPaul Traina  * 	hwlookcmp()
138644099b7bSPaul Traina  *	iplookcmp()
138744099b7bSPaul Traina  */
138844099b7bSPaul Traina 
138944099b7bSPaul Traina /* haddrtoa() - now in hwaddr.c */
139044099b7bSPaul Traina /*
139144099b7bSPaul Traina  * Now in dovend.c:
139244099b7bSPaul Traina  * insert_ip()
139344099b7bSPaul Traina  * insert_generic()
139444099b7bSPaul Traina  * insert_u_long()
139544099b7bSPaul Traina  */
139644099b7bSPaul Traina 
139744099b7bSPaul Traina /* get_errmsg() - now in report.c */
139844099b7bSPaul Traina 
139944099b7bSPaul Traina /*
140044099b7bSPaul Traina  * Local Variables:
140144099b7bSPaul Traina  * tab-width: 4
140244099b7bSPaul Traina  * c-indent-level: 4
140344099b7bSPaul Traina  * c-argdecl-indent: 4
140444099b7bSPaul Traina  * c-continued-statement-offset: 4
140544099b7bSPaul Traina  * c-continued-brace-offset: -4
140644099b7bSPaul Traina  * c-label-offset: -4
140744099b7bSPaul Traina  * c-brace-offset: 0
140844099b7bSPaul Traina  * End:
140944099b7bSPaul Traina  */
1410