bootpd.c revision 116371
1105197Ssam/************************************************************************
2112758Ssam          Copyright 1988, 1991 by Carnegie Mellon University
3139823Simp
4112758Ssam                          All Rights Reserved
5112758Ssam
6112758SsamPermission to use, copy, modify, and distribute this software and its
7112758Ssamdocumentation for any purpose and without fee is hereby granted, provided
8112758Ssamthat the above copyright notice appear in all copies and that both that
9112758Ssamcopyright notice and this permission notice appear in supporting
10112758Ssamdocumentation, and that the name of Carnegie Mellon University not be used
11112758Ssamin advertising or publicity pertaining to distribution of the software
12112758Ssamwithout specific, written prior permission.
13112758Ssam
14112758SsamCARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
15112758SsamSOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
16112758SsamIN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
17112758SsamDAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
18112758SsamPROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
19112758SsamACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20112758SsamSOFTWARE.
21112758Ssam
22112758Ssam************************************************************************/
23112758Ssam
24112758Ssam/*
25112758Ssam * BOOTP (bootstrap protocol) server daemon.
26112758Ssam *
27112758Ssam * Answers BOOTP request packets from booting client machines.
28112758Ssam * See [SRI-NIC]<RFC>RFC951.TXT for a description of the protocol.
29112758Ssam * See [SRI-NIC]<RFC>RFC1048.TXT for vendor-information extensions.
30112758Ssam * See RFC 1395 for option tags 14-17.
31112758Ssam * See accompanying man page -- bootpd.8
32112758Ssam *
33112758Ssam * HISTORY
34112758Ssam *	See ./Changes
35112758Ssam *
36112758Ssam * BUGS
37112758Ssam *	See ./ToDo
38105197Ssam */
39105197Ssam
40105197Ssam#include <sys/cdefs.h>
41105197Ssam__FBSDID("$FreeBSD: head/libexec/bootpd/bootpd.c 116371 2003-06-15 03:08:37Z jmg $");
42105197Ssam
43105197Ssam#include <sys/types.h>
44105197Ssam#include <sys/param.h>
45105197Ssam#include <sys/socket.h>
46105197Ssam#include <sys/ioctl.h>
47105197Ssam#include <sys/file.h>
48105197Ssam#include <sys/time.h>
49105197Ssam#include <sys/stat.h>
50105197Ssam#include <sys/utsname.h>
51105197Ssam
52105197Ssam#include <net/if.h>
53105197Ssam#include <netinet/in.h>
54105197Ssam#include <arpa/inet.h>	/* inet_ntoa */
55291292Sae
56105197Ssam#ifndef	NO_UNISTD
57105197Ssam#include <unistd.h>
58105197Ssam#endif
59257176Sglebius
60291292Sae#include <stdlib.h>
61105197Ssam#include <signal.h>
62195699Srwatson#include <stdio.h>
63105197Ssam#include <string.h>
64105197Ssam#include <errno.h>
65105197Ssam#include <ctype.h>
66105197Ssam#include <netdb.h>
67105197Ssam#include <paths.h>
68105197Ssam#include <syslog.h>
69105197Ssam#include <assert.h>
70105197Ssam
71105197Ssam#ifdef	NO_SETSID
72105197Ssam# include <fcntl.h>		/* for O_RDONLY, etc */
73105197Ssam#endif
74105197Ssam
75105197Ssam#ifndef	USE_BFUNCS
76105197Ssam# include <memory.h>
77105197Ssam/* Yes, memcpy is OK here (no overlapped copies). */
78105197Ssam# define bcopy(a,b,c)    memcpy(b,a,c)
79105197Ssam# define bzero(p,l)      memset(p,0,l)
80105197Ssam# define bcmp(a,b,c)     memcmp(a,b,c)
81105197Ssam#endif
82105197Ssam
83105197Ssam#include "bootp.h"
84105197Ssam#include "hash.h"
85105197Ssam#include "hwaddr.h"
86105197Ssam#include "bootpd.h"
87105197Ssam#include "dovend.h"
88105197Ssam#include "getif.h"
89105197Ssam#include "readfile.h"
90105197Ssam#include "report.h"
91105197Ssam#include "tzone.h"
92105197Ssam#include "patchlevel.h"
93105197Ssam
94105197Ssam#ifndef CONFIG_FILE
95105197Ssam#define CONFIG_FILE		"/etc/bootptab"
96105197Ssam#endif
97181627Svanhu#ifndef DUMPTAB_FILE
98252028Sae#define DUMPTAB_FILE		"/tmp/bootpd.dump"
99252028Sae#endif
100252028Sae
101252028Sae
102252028Sae
103252028Sae/*
104252028Sae * Externals, forward declarations, and global variables
105252028Sae */
106105197Ssam
107193947Sbzextern void dumptab(char *);
108120585Ssam
109193947SbzPRIVATE void catcher(int);
110120585SsamPRIVATE int chk_access(char *, int32 *);
111105197Ssam#ifdef VEND_CMU
112105197SsamPRIVATE void dovend_cmu(struct bootp *, struct host *);
113214351Sthomas#endif
114170793SbzPRIVATE void dovend_rfc1048(struct bootp *, struct host *, int32);
115105197SsamPRIVATE void handle_reply(void);
116105197SsamPRIVATE void handle_request(void);
117285770SeriPRIVATE void sendreply(int forward, int32 dest_override);
118105197SsamPRIVATE void usage(void);
119105197Ssam
120281695Sae/*
121105197Ssam * IP port numbers for client and server obtained from /etc/services
122105197Ssam */
123105197Ssam
124119643Ssamu_short bootps_port, bootpc_port;
125221129Sbz
126194062Svanhu
127194062Svanhu/*
128194062Svanhu * Internet socket and interface config structures
129221129Sbz */
130105197Ssam
131252028Saestruct sockaddr_in bind_addr;	/* Listening */
132105197Ssamstruct sockaddr_in recv_addr;	/* Packet source */
133120585Ssamstruct sockaddr_in send_addr;	/*  destination */
134105197Ssam
135170792Sbz
136170792Sbz/*
137170792Sbz * option defaults
138170792Sbz */
139181803Sbzint debug = 0;					/* Debugging flag (level) */
140181803Sbzstruct timeval actualtimeout =
141181803Sbz{								/* fifteen minutes */
142105197Ssam	15 * 60L,					/* tv_sec */
143252028Sae	0							/* tv_usec */
144105197Ssam};
145105197Ssam
146105197Ssam/*
147105197Ssam * General
148105197Ssam */
149252028Sae
150120585Ssamint s;							/* Socket file descriptor */
151105197Ssamchar *pktbuf;					/* Receive packet buffer */
152105197Ssamint pktlen;
153105197Ssamchar *progname;
154105197Ssamchar *chdir_path;
155105197Ssamstruct in_addr my_ip_addr;
156105197Ssam
157105197Ssamstatic const char *hostname;
158105197Ssamstatic char default_hostname[MAXHOSTNAMELEN];
159105197Ssam
160105197Ssam/* Flags set by signal catcher. */
161105197SsamPRIVATE int do_readtab = 0;
162105197SsamPRIVATE int do_dumptab = 0;
163105197Ssam
164105197Ssam/*
165105197Ssam * Globals below are associated with the bootp database file (bootptab).
166105197Ssam */
167105197Ssam
168105197Ssamchar *bootptab = CONFIG_FILE;
169105197Ssamchar *bootpd_dump = DUMPTAB_FILE;
170105197Ssam
171105197Ssam
172105197Ssam
173105197Ssam/*
174105197Ssam * Initialization such as command-line processing is done and then the
175105197Ssam * main server loop is started.
176105197Ssam */
177105197Ssam
178105197Ssamint
179105197Ssammain(argc, argv)
180105197Ssam	int argc;
181194062Svanhu	char **argv;
182194062Svanhu{
183194062Svanhu	struct timeval *timeout;
184194062Svanhu	struct bootp *bp;
185194062Svanhu	struct servent *servp;
186194062Svanhu	struct hostent *hep;
187105197Ssam	char *stmp;
188105197Ssam	int n, ba_len, ra_len;
189105197Ssam	int nfound;
190105197Ssam	fd_set readfds;
191105197Ssam	int standalone;
192105197Ssam#ifdef	SA_NOCLDSTOP	/* Have POSIX sigaction(2). */
193105197Ssam	struct sigaction sa;
194105197Ssam#endif
195281693Sae
196281693Sae	progname = strrchr(argv[0], '/');
197281693Sae	if (progname) progname++;
198281693Sae	else progname = argv[0];
199281693Sae
200281693Sae	/*
201281693Sae	 * Initialize logging.
202105197Ssam	 */
203105197Ssam	report_init(0);				/* uses progname */
204105197Ssam
205120585Ssam	/*
206105197Ssam	 * Log startup
207252028Sae	 */
208105197Ssam	report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
209105197Ssam
210105197Ssam	/* Debugging for compilers with struct padding. */
211105197Ssam	assert(sizeof(struct bootp) == BP_MINPKTSZ);
212105197Ssam
213105197Ssam	/* Get space for receiving packets and composing replies. */
214120585Ssam	pktbuf = malloc(MAX_MSG_SIZE);
215281695Sae	if (!pktbuf) {
216281695Sae		report(LOG_ERR, "malloc failed");
217252028Sae		exit(1);
218105197Ssam	}
219105197Ssam	bp = (struct bootp *) pktbuf;
220105197Ssam
221105197Ssam	/*
222105197Ssam	 * Check to see if a socket was passed to us from inetd.
223120585Ssam	 *
224281695Sae	 * Use getsockname() to determine if descriptor 0 is indeed a socket
225281695Sae	 * (and thus we are probably a child of inetd) or if it is instead
226252028Sae	 * something else and we are running standalone.
227105197Ssam	 */
228105197Ssam	s = 0;
229105197Ssam	ba_len = sizeof(bind_addr);
230105197Ssam	bzero((char *) &bind_addr, ba_len);
231105197Ssam	errno = 0;
232105197Ssam	standalone = TRUE;
233105197Ssam	if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {
234105197Ssam		/*
235105197Ssam		 * Descriptor 0 is a socket.  Assume we are a child of inetd.
236105197Ssam		 */
237105197Ssam		if (bind_addr.sin_family == AF_INET) {
238105197Ssam			standalone = FALSE;
239105197Ssam			bootps_port = ntohs(bind_addr.sin_port);
240105197Ssam		} else {
241105197Ssam			/* Some other type of socket? */
242105197Ssam			report(LOG_ERR, "getsockname: not an INET socket");
243269699Skevlo		}
244106680Ssam	}
245269699Skevlo
246269699Skevlo	/*
247269699Skevlo	 * Set defaults that might be changed by option switches.
248269699Skevlo	 */
249269699Skevlo	stmp = NULL;
250269699Skevlo	timeout = &actualtimeout;
251269699Skevlo
252285770Seri	if (gethostname(default_hostname, sizeof(default_hostname) - 1) < 0) {
253285770Seri		report(LOG_ERR, "bootpd: can't get hostname\n");
254269699Skevlo		exit(1);
255106680Ssam	}
256120585Ssam	default_hostname[sizeof(default_hostname) - 1] = '\0';
257120585Ssam	hostname = default_hostname;
258120585Ssam
259120585Ssam	/*
260120585Ssam	 * Read switches.
261120585Ssam	 */
262120585Ssam	for (argc--, argv++; argc > 0; argc--, argv++) {
263106680Ssam		if (argv[0][0] != '-')
264269699Skevlo			break;
265269699Skevlo		switch (argv[0][1]) {
266106680Ssam
267269699Skevlo		case 'c':				/* chdir_path */
268269699Skevlo			if (argv[0][2]) {
269269699Skevlo				stmp = &(argv[0][2]);
270269699Skevlo			} else {
271269699Skevlo				argc--;
272269699Skevlo				argv++;
273269699Skevlo				stmp = argv[0];
274285770Seri			}
275285770Seri			if (!stmp || (stmp[0] != '/')) {
276269699Skevlo				report(LOG_ERR,
277106680Ssam						"bootpd: invalid chdir specification\n");
278269699Skevlo				break;
279120585Ssam			}
280120585Ssam			chdir_path = stmp;
281120585Ssam			break;
282120585Ssam
283120585Ssam		case 'd':				/* debug level */
284120585Ssam			if (argv[0][2]) {
285120585Ssam				stmp = &(argv[0][2]);
286106680Ssam			} else if (argv[1] && argv[1][0] == '-') {
287269699Skevlo				/*
288269699Skevlo				 * Backwards-compatible behavior:
289106680Ssam				 * no parameter, so just increment the debug flag.
290269699Skevlo				 */
291269699Skevlo				debug++;
292269699Skevlo				break;
293269699Skevlo			} else {
294269699Skevlo				argc--;
295269699Skevlo				argv++;
296269699Skevlo				stmp = argv[0];
297285770Seri			}
298285770Seri			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
299269699Skevlo				report(LOG_ERR,
300106680Ssam						"%s: invalid debug level\n", progname);
301106680Ssam				break;
302105197Ssam			}
303105197Ssam			debug = n;
304105197Ssam			break;
305105197Ssam
306105197Ssam		case 'h':				/* override hostname */
307105197Ssam			if (argv[0][2]) {
308105197Ssam				stmp = &(argv[0][2]);
309275707Sae			} else {
310275707Sae				argc--;
311105197Ssam				argv++;
312281695Sae				stmp = argv[0];
313291292Sae			}
314266800Svanhu			if (!stmp) {
315105197Ssam				report(LOG_ERR,
316105197Ssam						"bootpd: missing hostname\n");
317105197Ssam				break;
318105197Ssam			}
319105197Ssam			hostname = stmp;
320165222Sbz			break;
321165118Sbz
322165118Sbz		case 'i':				/* inetd mode */
323165118Sbz			standalone = FALSE;
324165118Sbz			break;
325105197Ssam
326120585Ssam		case 's':				/* standalone mode */
327120585Ssam			standalone = TRUE;
328120585Ssam			break;
329105197Ssam
330105197Ssam		case 't':				/* timeout */
331120585Ssam			if (argv[0][2]) {
332105197Ssam				stmp = &(argv[0][2]);
333120585Ssam			} else {
334105197Ssam				argc--;
335120585Ssam				argv++;
336105197Ssam				stmp = argv[0];
337105197Ssam			}
338105197Ssam			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
339120585Ssam				report(LOG_ERR,
340252028Sae						"%s: invalid timeout specification\n", progname);
341105197Ssam				break;
342105197Ssam			}
343105197Ssam			actualtimeout.tv_sec = (int32) (60 * n);
344105197Ssam			/*
345105197Ssam			 * If the actual timeout is zero, pass a NULL pointer
346241922Sglebius			 * to select so it blocks indefinitely, otherwise,
347241922Sglebius			 * point to the actual timeout value.
348241922Sglebius			 */
349241922Sglebius			timeout = (n > 0) ? &actualtimeout : NULL;
350105197Ssam			break;
351120585Ssam
352281695Sae		default:
353281695Sae			report(LOG_ERR, "%s: unknown switch: -%c\n",
354252028Sae					progname, argv[0][1]);
355105197Ssam			usage();
356105197Ssam			break;
357105197Ssam
358105197Ssam		} /* switch */
359105197Ssam	} /* for args */
360105197Ssam
361105197Ssam	/*
362105197Ssam	 * Override default file names if specified on the command line.
363105197Ssam	 */
364105197Ssam	if (argc > 0)
365105197Ssam		bootptab = argv[0];
366105197Ssam
367105197Ssam	if (argc > 1)
368291292Sae		bootpd_dump = argv[1];
369291292Sae
370291292Sae	/*
371282132Sae	 * Get my hostname and IP address.
372266800Svanhu	 */
373105197Ssam
374272394Sae	hep = gethostbyname(hostname);
375272394Sae	if (!hep) {
376105197Ssam		report(LOG_ERR, "Can not get my IP address\n");
377118888Ssam		exit(1);
378252028Sae	}
379118888Ssam	bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
380118888Ssam
381118888Ssam	if (standalone) {
382266800Svanhu		/*
383266800Svanhu		 * Go into background and disassociate from controlling terminal.
384105197Ssam		 */
385266800Svanhu		if (debug < 3) {
386105197Ssam			if (fork())
387105197Ssam				exit(0);
388105197Ssam#ifdef	NO_SETSID
389105197Ssam			setpgrp(0,0);
390105197Ssam#ifdef TIOCNOTTY
391105197Ssam			n = open(_PATH_TTY, O_RDWR);
392105197Ssam			if (n >= 0) {
393105197Ssam				ioctl(n, TIOCNOTTY, (char *) 0);
394105197Ssam				(void) close(n);
395105197Ssam			}
396105197Ssam#endif	/* TIOCNOTTY */
397105197Ssam#else	/* SETSID */
398105197Ssam			if (setsid() < 0)
399120585Ssam				perror("setsid");
400120585Ssam#endif	/* SETSID */
401120585Ssam		} /* if debug < 3 */
402105197Ssam
403105197Ssam		/*
404105197Ssam		 * Nuke any timeout value
405105197Ssam		 */
406105197Ssam		timeout = NULL;
407252028Sae
408105197Ssam	} /* if standalone (1st) */
409105197Ssam
410105197Ssam	/* Set the cwd (i.e. to /tftpboot) */
411266800Svanhu	if (chdir_path) {
412105197Ssam		if (chdir(chdir_path) < 0)
413159237Spjd			report(LOG_ERR, "%s: chdir failed", chdir_path);
414105197Ssam	}
415274193Sae
416272394Sae	/* Get the timezone. */
417105197Ssam	tzone_init();
418118888Ssam
419252028Sae	/* Allocate hash tables. */
420118888Ssam	rdtab_init();
421118888Ssam
422118888Ssam	/*
423266800Svanhu	 * Read the bootptab file.
424266800Svanhu	 */
425266800Svanhu	readtab(1);					/* force read */
426105197Ssam
427105197Ssam	if (standalone) {
428105197Ssam
429105197Ssam		/*
430105197Ssam		 * Create a socket.
431105197Ssam		 */
432105197Ssam		if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
433105197Ssam			report(LOG_ERR, "socket: %s", get_network_errmsg());
434105197Ssam			exit(1);
435105197Ssam		}
436105197Ssam
437120585Ssam		/*
438120585Ssam		 * Get server's listening port number
439120585Ssam		 */
440165118Sbz		servp = getservbyname("bootps", "udp");
441105197Ssam		if (servp) {
442105197Ssam			bootps_port = ntohs((u_short) servp->s_port);
443105197Ssam		} else {
444105197Ssam			bootps_port = (u_short) IPPORT_BOOTPS;
445252028Sae			report(LOG_ERR,
446105197Ssam				"bootps/udp: unknown service -- using port %d",
447105197Ssam				   bootps_port);
448105197Ssam		}
449266800Svanhu
450105197Ssam		/*
451105197Ssam		 * Bind socket to BOOTPS port.
452274193Sae		 */
453274193Sae		bind_addr.sin_family = AF_INET;
454274193Sae		bind_addr.sin_addr.s_addr = INADDR_ANY;
455274193Sae		bind_addr.sin_port = htons(bootps_port);
456274193Sae		if (bind(s, (struct sockaddr *) &bind_addr,
457274193Sae				 sizeof(bind_addr)) < 0)
458274193Sae		{
459274193Sae			report(LOG_ERR, "bind: %s", get_network_errmsg());
460274193Sae			exit(1);
461105197Ssam		}
462105197Ssam	} /* if standalone (2nd)*/
463105197Ssam
464275707Sae	/*
465105197Ssam	 * Get destination port number so we can reply to client
466275707Sae	 */
467105197Ssam	servp = getservbyname("bootpc", "udp");
468105197Ssam	if (servp) {
469105197Ssam		bootpc_port = ntohs(servp->s_port);
470120585Ssam	} else {
471252028Sae		report(LOG_ERR,
472105197Ssam			   "bootpc/udp: unknown service -- using port %d",
473105197Ssam			   IPPORT_BOOTPC);
474105197Ssam		bootpc_port = (u_short) IPPORT_BOOTPC;
475105197Ssam	}
476105197Ssam
477105197Ssam	/*
478105197Ssam	 * Set up signals to read or dump the table.
479105197Ssam	 */
480174054Sbz#ifdef	SA_NOCLDSTOP	/* Have POSIX sigaction(2). */
481174054Sbz	sa.sa_handler = catcher;
482174054Sbz	sigemptyset(&sa.sa_mask);
483105197Ssam	sa.sa_flags = 0;
484105197Ssam	if (sigaction(SIGHUP, &sa, NULL) < 0) {
485105197Ssam		report(LOG_ERR, "sigaction: %s", get_errmsg());
486105197Ssam		exit(1);
487105197Ssam	}
488105197Ssam	if (sigaction(SIGUSR1, &sa, NULL) < 0) {
489272394Sae		report(LOG_ERR, "sigaction: %s", get_errmsg());
490272394Sae		exit(1);
491272394Sae	}
492272394Sae#else	/* SA_NOCLDSTOP */
493272394Sae	/* Old-fashioned UNIX signals */
494272394Sae	if ((int) signal(SIGHUP, catcher) < 0) {
495105197Ssam		report(LOG_ERR, "signal: %s", get_errmsg());
496105197Ssam		exit(1);
497105197Ssam	}
498266800Svanhu	if ((int) signal(SIGUSR1, catcher) < 0) {
499266800Svanhu		report(LOG_ERR, "signal: %s", get_errmsg());
500266800Svanhu		exit(1);
501291292Sae	}
502266800Svanhu#endif	/* SA_NOCLDSTOP */
503266800Svanhu
504266800Svanhu	/*
505266800Svanhu	 * Process incoming requests.
506291292Sae	 */
507266800Svanhu	FD_ZERO(&readfds);
508266800Svanhu	for (;;) {
509266800Svanhu		struct timeval tv;
510266800Svanhu
511266800Svanhu		FD_SET(s, &readfds);
512266800Svanhu		if (timeout)
513266800Svanhu			tv = *timeout;
514266800Svanhu
515266800Svanhu		nfound = select(s + 1, &readfds, NULL, NULL,
516266800Svanhu						(timeout) ? &tv : NULL);
517291292Sae		if (nfound < 0) {
518291292Sae			if (errno != EINTR) {
519291292Sae				report(LOG_ERR, "select: %s", get_errmsg());
520266800Svanhu			}
521266800Svanhu			/*
522252028Sae			 * Call readtab() or dumptab() here to avoid the
523120585Ssam			 * dangers of doing I/O from a signal handler.
524120585Ssam			 */
525134391Sandre			if (do_readtab) {
526105197Ssam				do_readtab = 0;
527105197Ssam				readtab(1);		/* force read */
528105197Ssam			}
529105197Ssam			if (do_dumptab) {
530105197Ssam				do_dumptab = 0;
531105197Ssam				dumptab(bootpd_dump);
532120585Ssam			}
533120585Ssam			continue;
534120585Ssam		}
535120585Ssam		if (!FD_ISSET(s, &readfds)) {
536120585Ssam			if (debug > 1)
537120585Ssam				report(LOG_INFO, "exiting after %ld minutes of inactivity",
538105197Ssam					   actualtimeout.tv_sec / 60);
539105197Ssam			exit(0);
540105197Ssam		}
541105197Ssam		ra_len = sizeof(recv_addr);
542105197Ssam		n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
543105197Ssam					 (struct sockaddr *) &recv_addr, &ra_len);
544105197Ssam		if (n <= 0) {
545105197Ssam			continue;
546105197Ssam		}
547105197Ssam		if (debug > 1) {
548105197Ssam			report(LOG_INFO, "recvd pkt from IP addr %s",
549105197Ssam				   inet_ntoa(recv_addr.sin_addr));
550120585Ssam		}
551105197Ssam		if (n < sizeof(struct bootp)) {
552105197Ssam			if (debug) {
553105197Ssam				report(LOG_NOTICE, "received short packet");
554105197Ssam			}
555105197Ssam			continue;
556105197Ssam		}
557105197Ssam		pktlen = n;
558105197Ssam
559105197Ssam		readtab(0);				/* maybe re-read bootptab */
560105197Ssam
561105197Ssam		switch (bp->bp_op) {
562105197Ssam		case BOOTREQUEST:
563105197Ssam			handle_request();
564105197Ssam			break;
565105197Ssam		case BOOTREPLY:
566105197Ssam			handle_reply();
567120585Ssam			break;
568105197Ssam		}
569105197Ssam	}
570105197Ssam	return 0;
571105197Ssam}
572120585Ssam
573120585Ssam
574252028Sae
575105197Ssam
576105197Ssam/*
577105197Ssam * Print "usage" message and exit
578105197Ssam */
579105197Ssam
580105197SsamPRIVATE void
581105197Ssamusage()
582105197Ssam{
583105197Ssam	fprintf(stderr,
584105197Ssam			"usage:  bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n");
585105197Ssam	fprintf(stderr, "\t -c n\tset current directory\n");
586105197Ssam	fprintf(stderr, "\t -d n\tset debug level\n");
587105197Ssam	fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n");
588105197Ssam	fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n");
589105197Ssam	fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n");
590275707Sae	exit(1);
591275707Sae}
592105197Ssam
593281695Sae/* Signal catchers */
594291292SaePRIVATE void
595105197Ssamcatcher(sig)
596105197Ssam	int sig;
597105197Ssam{
598105197Ssam	if (sig == SIGHUP)
599105197Ssam		do_readtab = 1;
600281694Sae	if (sig == SIGUSR1)
601105197Ssam		do_dumptab = 1;
602105197Ssam#if	!defined(SA_NOCLDSTOP) && defined(SYSV)
603165118Sbz	/* For older "System V" derivatives with no sigaction(). */
604165118Sbz	signal(sig, catcher);
605165118Sbz#endif
606105197Ssam}
607120585Ssam
608120585Ssam
609120585Ssam
610105197Ssam/*
611105197Ssam * Process BOOTREQUEST packet.
612120585Ssam *
613105197Ssam * Note:  This version of the bootpd.c server never forwards
614120585Ssam * a request to another server.  That is the job of a gateway
615105197Ssam * program such as the "bootpgw" program included here.
616120585Ssam *
617105197Ssam * (Also this version does not interpret the hostname field of
618105197Ssam * the request packet;  it COULD do a name->address lookup and
619105197Ssam * forward the request there.)
620120585Ssam */
621252028SaePRIVATE void
622105197Ssamhandle_request()
623105197Ssam{
624105197Ssam	struct bootp *bp = (struct bootp *) pktbuf;
625105197Ssam	struct host *hp = NULL;
626105197Ssam	struct host dummyhost;
627105197Ssam	int32 bootsize = 0;
628105197Ssam	unsigned hlen, hashcode;
629105197Ssam	int32 dest;
630120585Ssam	char realpath[1024];
631281695Sae	char *clntpath;
632281695Sae	char *homedir, *bootfile;
633105197Ssam	int n;
634252028Sae
635105197Ssam	bp->bp_file[sizeof(bp->bp_file)-1] = '\0';
636105197Ssam
637105197Ssam	/* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
638105197Ssam
639105197Ssam	/*
640105197Ssam	 * If the servername field is set, compare it against us.
641105197Ssam	 * If we're not being addressed, ignore this request.
642291292Sae	 * If the server name field is null, throw in our name.
643291292Sae	 */
644291292Sae	if (strlen(bp->bp_sname)) {
645105197Ssam		if (strcmp(bp->bp_sname, hostname)) {
646274466Sae			if (debug)
647274466Sae				report(LOG_INFO, "\
648105197Ssamignoring request for server %s from client at %s address %s",
649274466Sae					   bp->bp_sname, netname(bp->bp_htype),
650274466Sae					   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
651274466Sae			/* XXX - Is it correct to ignore such a request? -gwr */
652274466Sae			return;
653252028Sae		}
654118888Ssam	} else {
655118888Ssam		strcpy(bp->bp_sname, hostname);
656118888Ssam	}
657274466Sae
658274466Sae	/* Convert the request into a reply. */
659266800Svanhu	bp->bp_op = BOOTREPLY;
660266800Svanhu	if (bp->bp_ciaddr.s_addr == 0) {
661105197Ssam		/*
662105197Ssam		 * client doesnt know his IP address,
663105197Ssam		 * search by hardware address.
664105197Ssam		 */
665274466Sae		if (debug > 1) {
666274466Sae			report(LOG_INFO, "request from %s address %s",
667274466Sae				   netname(bp->bp_htype),
668274466Sae				   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
669274466Sae		}
670105197Ssam		hlen = haddrlength(bp->bp_htype);
671105197Ssam		if (hlen != bp->bp_hlen) {
672120585Ssam			report(LOG_NOTICE, "bad addr len from %s address %s",
673120585Ssam				   netname(bp->bp_htype),
674120585Ssam				   haddrtoa(bp->bp_chaddr, hlen));
675274466Sae		}
676105197Ssam		dummyhost.htype = bp->bp_htype;
677105197Ssam		bcopy(bp->bp_chaddr, dummyhost.haddr, hlen);
678105197Ssam		hashcode = hash_HashFunction(bp->bp_chaddr, hlen);
679105197Ssam		hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp,
680252028Sae										 &dummyhost);
681105197Ssam		if (hp == NULL &&
682105197Ssam			bp->bp_htype == HTYPE_IEEE802)
683105197Ssam		{
684266800Svanhu			/* Try again with address in "canonical" form. */
685105197Ssam			haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen);
686274466Sae			if (debug > 1) {
687274466Sae				report(LOG_INFO, "\
688274466SaeHW addr type is IEEE 802.  convert to %s and check again\n",
689274466Sae					   haddrtoa(dummyhost.haddr, bp->bp_hlen));
690274466Sae			}
691252028Sae			hashcode = hash_HashFunction(dummyhost.haddr, hlen);
692118888Ssam			hp = (struct host *) hash_Lookup(hwhashtable, hashcode,
693118888Ssam											 hwlookcmp, &dummyhost);
694118888Ssam		}
695274466Sae		if (hp == NULL) {
696274466Sae			/*
697266800Svanhu			 * XXX - Add dynamic IP address assignment?
698266800Svanhu			 */
699105197Ssam			if (debug)
700105197Ssam				report(LOG_NOTICE, "unknown client %s address %s",
701105197Ssam					   netname(bp->bp_htype),
702105197Ssam					   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
703274466Sae			return; /* not found */
704274466Sae		}
705274466Sae		(bp->bp_yiaddr).s_addr = hp->iaddr.s_addr;
706274466Sae
707105197Ssam	} else {
708105197Ssam
709120585Ssam		/*
710120585Ssam		 * search by IP address.
711120585Ssam		 */
712274466Sae		if (debug > 1) {
713105197Ssam			report(LOG_INFO, "request from IP addr %s",
714105197Ssam				   inet_ntoa(bp->bp_ciaddr));
715105197Ssam		}
716105197Ssam		dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr;
717252028Sae		hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4);
718105197Ssam		hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp,
719105197Ssam										 &dummyhost);
720105197Ssam		if (hp == NULL) {
721266800Svanhu			if (debug) {
722159215Sgnn				report(LOG_NOTICE, "IP address not found: %s",
723274466Sae					   inet_ntoa(bp->bp_ciaddr));
724274466Sae			}
725274466Sae			return;
726274466Sae		}
727105197Ssam	}
728105197Ssam
729105197Ssam	if (debug) {
730275707Sae		report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr),
731105197Ssam			   hp->hostname->string);
732275707Sae	}
733105197Ssam
734105197Ssam	/*
735105197Ssam	 * If there is a response delay threshold, ignore requests
736120585Ssam	 * with a timestamp lower than the threshold.
737252028Sae	 */
738105197Ssam	if (hp->flags.min_wait) {
739105197Ssam		u_int32 t = (u_int32) ntohs(bp->bp_secs);
740105197Ssam		if (t < hp->min_wait) {
741105197Ssam			if (debug > 1)
742105197Ssam				report(LOG_INFO,
743105197Ssam					   "ignoring request due to timestamp (%d < %d)",
744105197Ssam					   t, hp->min_wait);
745105197Ssam			return;
746174054Sbz		}
747174054Sbz	}
748174054Sbz
749105197Ssam#ifdef	YORK_EX_OPTION
750105197Ssam	/*
751105197Ssam	 * The need for the "ex" tag arose out of the need to empty
752105197Ssam	 * shared networked drives on diskless PCs.  This solution is
753105197Ssam	 * not very clean but it does work fairly well.
754105197Ssam	 * Written by Edmund J. Sutcliffe <edmund@york.ac.uk>
755291292Sae	 *
756266800Svanhu	 * XXX - This could compromise security if a non-trusted user
757266800Svanhu	 * managed to write an entry in the bootptab with :ex=trojan:
758291292Sae	 * so I would leave this turned off unless you need it. -gwr
759291292Sae	 */
760174054Sbz	/* Run a program, passing the client name as a parameter. */
761291292Sae	if (hp->flags.exec_file) {
762291292Sae		char tst[100];
763291292Sae		/* XXX - Check string lengths? -gwr */
764291292Sae		strcpy (tst, hp->exec_file->string);
765281694Sae		strcat (tst, " ");
766281694Sae		strcat (tst, hp->hostname->string);
767281694Sae		strcat (tst, " &");
768281694Sae		if (debug)
769281694Sae			report(LOG_INFO, "executing %s", tst);
770281694Sae		system(tst);	/* Hope this finishes soon... */
771281694Sae	}
772281694Sae#endif	/* YORK_EX_OPTION */
773281694Sae
774281694Sae	/*
775281694Sae	 * If a specific TFTP server address was specified in the bootptab file,
776281694Sae	 * fill it in, otherwise zero it.
777281694Sae	 * XXX - Rather than zero it, should it be the bootpd address? -gwr
778281694Sae	 */
779281694Sae	(bp->bp_siaddr).s_addr = (hp->flags.bootserver) ?
780281694Sae		hp->bootserver.s_addr : 0L;
781281694Sae
782281694Sae#ifdef	STANFORD_PROM_COMPAT
783281694Sae	/*
784281694Sae	 * Stanford bootp PROMs (for a Sun?) have no way to leave
785281694Sae	 * the boot file name field blank (because the boot file
786281694Sae	 * name is automatically generated from some index).
787281694Sae	 * As a work-around, this little hack allows those PROMs to
788281694Sae	 * specify "sunboot14" with the same effect as a NULL name.
789281694Sae	 * (The user specifies boot device 14 or some such magic.)
790281694Sae	 */
791281694Sae	if (strcmp(bp->bp_file, "sunboot14") == 0)
792281694Sae		bp->bp_file[0] = '\0';	/* treat it as unspecified */
793281694Sae#endif
794105197Ssam
795105197Ssam	/*
796105197Ssam	 * Fill in the client's proper bootfile.
797105197Ssam	 *
798105197Ssam	 * If the client specifies an absolute path, try that file with a
799105197Ssam	 * ".host" suffix and then without.  If the file cannot be found, no
800105197Ssam	 * reply is made at all.
801181803Sbz	 *
802249294Sae	 * If the client specifies a null or relative file, use the following
803105197Ssam	 * table to determine the appropriate action:
804105197Ssam	 *
805105197Ssam	 *  Homedir      Bootfile    Client's file
806105197Ssam	 * specified?   specified?   specification   Action
807105197Ssam	 * -------------------------------------------------------------------
808105197Ssam	 *      No          No          Null         Send null filename
809105197Ssam	 *      No          No          Relative     Discard request
810105197Ssam	 *      No          Yes         Null         Send if absolute else null
811105197Ssam	 *      No          Yes         Relative     Discard request     *XXX
812249294Sae	 *      Yes         No          Null         Send null filename
813105197Ssam	 *      Yes         No          Relative     Lookup with ".host"
814105197Ssam	 *      Yes         Yes         Null         Send home/boot or bootfile
815105197Ssam	 *      Yes         Yes         Relative     Lookup with ".host" *XXX
816105197Ssam	 *
817105197Ssam	 */
818105197Ssam
819105197Ssam	/*
820105197Ssam	 * XXX - I don't like the policy of ignoring a client when the
821105197Ssam	 * boot file is not accessible.  The TFTP server might not be
822105197Ssam	 * running on the same machine as the BOOTP server, in which
823105197Ssam	 * case checking accessibility of the boot file is pointless.
824105197Ssam	 *
825105197Ssam	 * Therefore, file accessibility is now demanded ONLY if you
826105197Ssam	 * define CHECK_FILE_ACCESS in the Makefile options. -gwr
827105197Ssam	 */
828105197Ssam
829105197Ssam	/*
830105197Ssam	 * The "real" path is as seen by the BOOTP daemon on this
831105197Ssam	 * machine, while the client path is relative to the TFTP
832105197Ssam	 * daemon chroot directory (i.e. /tftpboot).
833105197Ssam	 */
834105197Ssam	if (hp->flags.tftpdir) {
835120585Ssam		snprintf(realpath, sizeof(realpath), "%s", hp->tftpdir->string);
836120585Ssam		clntpath = &realpath[strlen(realpath)];
837120585Ssam	} else {
838120585Ssam		realpath[0] = '\0';
839172149Sgnn		clntpath = realpath;
840172149Sgnn	}
841172149Sgnn
842172149Sgnn	/*
843172149Sgnn	 * Determine client's requested homedir and bootfile.
844120585Ssam	 */
845120585Ssam	homedir = NULL;
846120585Ssam	bootfile = NULL;
847120585Ssam	if (bp->bp_file[0]) {
848120585Ssam		homedir = bp->bp_file;
849120585Ssam		bootfile = strrchr(homedir, '/');
850120585Ssam		if (bootfile) {
851172149Sgnn			if (homedir == bootfile)
852172149Sgnn				homedir = NULL;
853172149Sgnn			*bootfile++ = '\0';
854172149Sgnn		} else {
855172149Sgnn			/* no "/" in the string */
856172149Sgnn			bootfile = homedir;
857172149Sgnn			homedir = NULL;
858172149Sgnn		}
859172149Sgnn		if (debug > 2) {
860172149Sgnn			report(LOG_INFO, "requested path=\"%s\"  file=\"%s\"",
861120585Ssam				   (homedir) ? homedir : "",
862172149Sgnn				   (bootfile) ? bootfile : "");
863172149Sgnn		}
864120585Ssam	}
865120585Ssam
866120585Ssam	/*
867120585Ssam	 * Specifications in bootptab override client requested values.
868120585Ssam	 */
869120585Ssam	if (hp->flags.homedir)
870120585Ssam		homedir = hp->homedir->string;
871120585Ssam	if (hp->flags.bootfile)
872120585Ssam		bootfile = hp->bootfile->string;
873120585Ssam
874120585Ssam	/*
875120585Ssam	 * Construct bootfile path.
876120585Ssam	 */
877120585Ssam	if (homedir) {
878120585Ssam		if (homedir[0] != '/')
879120585Ssam			strcat(clntpath, "/");
880120585Ssam		strcat(clntpath, homedir);
881120585Ssam		homedir = NULL;
882120585Ssam	}
883120585Ssam	if (bootfile) {
884120585Ssam		if (bootfile[0] != '/')
885120585Ssam			strcat(clntpath, "/");
886120585Ssam		strcat(clntpath, bootfile);
887120585Ssam		bootfile = NULL;
888120585Ssam	}
889120585Ssam
890120585Ssam	/*
891120585Ssam	 * First try to find the file with a ".host" suffix
892120585Ssam	 */
893120585Ssam	n = strlen(clntpath);
894120585Ssam	strcat(clntpath, ".");
895120585Ssam	strcat(clntpath, hp->hostname->string);
896120585Ssam	if (chk_access(realpath, &bootsize) < 0) {
897120585Ssam		clntpath[n] = 0;			/* Try it without the suffix */
898120585Ssam		if (chk_access(realpath, &bootsize) < 0) {
899120585Ssam			/* neither "file.host" nor "file" was found */
900120585Ssam#ifdef	CHECK_FILE_ACCESS
901120585Ssam
902120585Ssam			if (bp->bp_file[0]) {
903120585Ssam				/*
904120585Ssam				 * Client wanted specific file
905120585Ssam				 * and we didn't have it.
906120585Ssam				 */
907120585Ssam				report(LOG_NOTICE,
908120585Ssam					   "requested file not found: \"%s\"", clntpath);
909120585Ssam				return;
910120585Ssam			}
911120585Ssam			/*
912120585Ssam			 * Client didn't ask for a specific file and we couldn't
913120585Ssam			 * access the default file, so just zero-out the bootfile
914120585Ssam			 * field in the packet and continue processing the reply.
915120585Ssam			 */
916120585Ssam			bzero(bp->bp_file, sizeof(bp->bp_file));
917120585Ssam			goto null_file_name;
918120585Ssam
919120585Ssam#else	/* CHECK_FILE_ACCESS */
920120585Ssam
921120585Ssam			/* Complain only if boot file size was needed. */
922120585Ssam			if (hp->flags.bootsize_auto) {
923120585Ssam				report(LOG_ERR, "can not determine size of file \"%s\"",
924105197Ssam					   clntpath);
925			}
926
927#endif	/* CHECK_FILE_ACCESS */
928		}
929	}
930	strncpy(bp->bp_file, clntpath, BP_FILE_LEN);
931	if (debug > 2)
932		report(LOG_INFO, "bootfile=\"%s\"", clntpath);
933
934#ifdef	CHECK_FILE_ACCESS
935null_file_name:
936#endif	/* CHECK_FILE_ACCESS */
937
938
939	/*
940	 * Handle vendor options based on magic number.
941	 */
942
943	if (debug > 1) {
944		report(LOG_INFO, "vendor magic field is %d.%d.%d.%d",
945			   (int) ((bp->bp_vend)[0]),
946			   (int) ((bp->bp_vend)[1]),
947			   (int) ((bp->bp_vend)[2]),
948			   (int) ((bp->bp_vend)[3]));
949	}
950	/*
951	 * If this host isn't set for automatic vendor info then copy the
952	 * specific cookie into the bootp packet, thus forcing a certain
953	 * reply format.  Only force reply format if user specified it.
954	 */
955	if (hp->flags.vm_cookie) {
956		/* Slam in the user specified magic number. */
957		bcopy(hp->vm_cookie, bp->bp_vend, 4);
958	}
959	/*
960	 * Figure out the format for the vendor-specific info.
961	 * Note that bp->bp_vend may have been set above.
962	 */
963	if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) {
964		/* RFC1048 conformant bootp client */
965		dovend_rfc1048(bp, hp, bootsize);
966		if (debug > 1) {
967			report(LOG_INFO, "sending reply (with RFC1048 options)");
968		}
969	}
970#ifdef VEND_CMU
971	else if (!bcmp(bp->bp_vend, vm_cmu, 4)) {
972		dovend_cmu(bp, hp);
973		if (debug > 1) {
974			report(LOG_INFO, "sending reply (with CMU options)");
975		}
976	}
977#endif
978	else {
979		if (debug > 1) {
980			report(LOG_INFO, "sending reply (with no options)");
981		}
982	}
983
984	dest = (hp->flags.reply_addr) ?
985		hp->reply_addr.s_addr : 0L;
986
987	/* not forwarded */
988	sendreply(0, dest);
989}
990
991
992/*
993 * Process BOOTREPLY packet.
994 */
995PRIVATE void
996handle_reply()
997{
998	if (debug) {
999		report(LOG_INFO, "processing boot reply");
1000	}
1001	/* forwarded, no destination override */
1002	sendreply(1, 0);
1003}
1004
1005
1006/*
1007 * Send a reply packet to the client.  'forward' flag is set if we are
1008 * not the originator of this reply packet.
1009 */
1010PRIVATE void
1011sendreply(forward, dst_override)
1012	int forward;
1013	int32 dst_override;
1014{
1015	struct bootp *bp = (struct bootp *) pktbuf;
1016	struct in_addr dst;
1017	u_short port = bootpc_port;
1018	unsigned char *ha;
1019	int len, haf;
1020
1021	/*
1022	 * XXX - Should honor bp_flags "broadcast" bit here.
1023	 * Temporary workaround: use the :ra=ADDR: option to
1024	 * set the reply address to the broadcast address.
1025	 */
1026
1027	/*
1028	 * If the destination address was specified explicitly
1029	 * (i.e. the broadcast address for HP compatibility)
1030	 * then send the response to that address.  Otherwise,
1031	 * act in accordance with RFC951:
1032	 *   If the client IP address is specified, use that
1033	 * else if gateway IP address is specified, use that
1034	 * else make a temporary arp cache entry for the client's
1035	 * NEW IP/hardware address and use that.
1036	 */
1037	if (dst_override) {
1038		dst.s_addr = dst_override;
1039		if (debug > 1) {
1040			report(LOG_INFO, "reply address override: %s",
1041				   inet_ntoa(dst));
1042		}
1043	} else if (bp->bp_ciaddr.s_addr) {
1044		dst = bp->bp_ciaddr;
1045	} else if (bp->bp_giaddr.s_addr && forward == 0) {
1046		dst = bp->bp_giaddr;
1047		port = bootps_port;
1048		if (debug > 1) {
1049			report(LOG_INFO, "sending reply to gateway %s",
1050				   inet_ntoa(dst));
1051		}
1052	} else {
1053		dst = bp->bp_yiaddr;
1054		ha = bp->bp_chaddr;
1055		len = bp->bp_hlen;
1056		if (len > MAXHADDRLEN)
1057			len = MAXHADDRLEN;
1058		haf = (int) bp->bp_htype;
1059		if (haf == 0)
1060			haf = HTYPE_ETHERNET;
1061
1062		if (debug > 1)
1063			report(LOG_INFO, "setarp %s - %s",
1064				   inet_ntoa(dst), haddrtoa(ha, len));
1065		setarp(s, &dst, haf, ha, len);
1066	}
1067
1068	if ((forward == 0) &&
1069		(bp->bp_siaddr.s_addr == 0))
1070	{
1071		struct ifreq *ifr;
1072		struct in_addr siaddr;
1073		/*
1074		 * If we are originating this reply, we
1075		 * need to find our own interface address to
1076		 * put in the bp_siaddr field of the reply.
1077		 * If this server is multi-homed, pick the
1078		 * 'best' interface (the one on the same net
1079		 * as the client).  Of course, the client may
1080		 * be on the other side of a BOOTP gateway...
1081		 */
1082		ifr = getif(s, &dst);
1083		if (ifr) {
1084			struct sockaddr_in *sip;
1085			sip = (struct sockaddr_in *) &(ifr->ifr_addr);
1086			siaddr = sip->sin_addr;
1087		} else {
1088			/* Just use my "official" IP address. */
1089			siaddr = my_ip_addr;
1090		}
1091
1092		/* XXX - No need to set bp_giaddr here. */
1093
1094		/* Finally, set the server address field. */
1095		bp->bp_siaddr = siaddr;
1096	}
1097	/* Set up socket address for send. */
1098	send_addr.sin_family = AF_INET;
1099	send_addr.sin_port = htons(port);
1100	send_addr.sin_addr = dst;
1101
1102	/* Send reply with same size packet as request used. */
1103	if (sendto(s, pktbuf, pktlen, 0,
1104			   (struct sockaddr *) &send_addr,
1105			   sizeof(send_addr)) < 0)
1106	{
1107		report(LOG_ERR, "sendto: %s", get_network_errmsg());
1108	}
1109} /* sendreply */
1110
1111
1112/* nmatch() - now in getif.c */
1113/* setarp() - now in hwaddr.c */
1114
1115
1116/*
1117 * This call checks read access to a file.  It returns 0 if the file given
1118 * by "path" exists and is publically readable.  A value of -1 is returned if
1119 * access is not permitted or an error occurs.  Successful calls also
1120 * return the file size in bytes using the long pointer "filesize".
1121 *
1122 * The read permission bit for "other" users is checked.  This bit must be
1123 * set for tftpd(8) to allow clients to read the file.
1124 */
1125
1126PRIVATE int
1127chk_access(path, filesize)
1128	char *path;
1129	int32 *filesize;
1130{
1131	struct stat st;
1132
1133	if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) {
1134		*filesize = (int32) st.st_size;
1135		return 0;
1136	} else {
1137		return -1;
1138	}
1139}
1140
1141
1142/*
1143 * Now in dumptab.c :
1144 *	dumptab()
1145 *	dump_host()
1146 *	list_ipaddresses()
1147 */
1148
1149#ifdef VEND_CMU
1150
1151/*
1152 * Insert the CMU "vendor" data for the host pointed to by "hp" into the
1153 * bootp packet pointed to by "bp".
1154 */
1155
1156PRIVATE void
1157dovend_cmu(bp, hp)
1158	struct bootp *bp;
1159	struct host *hp;
1160{
1161	struct cmu_vend *vendp;
1162	struct in_addr_list *taddr;
1163
1164	/*
1165	 * Initialize the entire vendor field to zeroes.
1166	 */
1167	bzero(bp->bp_vend, sizeof(bp->bp_vend));
1168
1169	/*
1170	 * Fill in vendor information. Subnet mask, default gateway,
1171	 * domain name server, ien name server, time server
1172	 */
1173	vendp = (struct cmu_vend *) bp->bp_vend;
1174	strcpy(vendp->v_magic, (char *)vm_cmu);
1175	if (hp->flags.subnet_mask) {
1176		(vendp->v_smask).s_addr = hp->subnet_mask.s_addr;
1177		(vendp->v_flags) |= VF_SMASK;
1178		if (hp->flags.gateway) {
1179			(vendp->v_dgate).s_addr = hp->gateway->addr->s_addr;
1180		}
1181	}
1182	if (hp->flags.domain_server) {
1183		taddr = hp->domain_server;
1184		if (taddr->addrcount > 0) {
1185			(vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr;
1186			if (taddr->addrcount > 1) {
1187				(vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr;
1188			}
1189		}
1190	}
1191	if (hp->flags.name_server) {
1192		taddr = hp->name_server;
1193		if (taddr->addrcount > 0) {
1194			(vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr;
1195			if (taddr->addrcount > 1) {
1196				(vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr;
1197			}
1198		}
1199	}
1200	if (hp->flags.time_server) {
1201		taddr = hp->time_server;
1202		if (taddr->addrcount > 0) {
1203			(vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr;
1204			if (taddr->addrcount > 1) {
1205				(vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr;
1206			}
1207		}
1208	}
1209	/* Log message now done by caller. */
1210} /* dovend_cmu */
1211
1212#endif /* VEND_CMU */
1213
1214
1215
1216/*
1217 * Insert the RFC1048 vendor data for the host pointed to by "hp" into the
1218 * bootp packet pointed to by "bp".
1219 */
1220#define	NEED(LEN, MSG) do \
1221	if (bytesleft < (LEN)) { \
1222		report(LOG_NOTICE, noroom, \
1223			   hp->hostname->string, MSG); \
1224		return; \
1225	} while (0)
1226PRIVATE void
1227dovend_rfc1048(bp, hp, bootsize)
1228	struct bootp *bp;
1229	struct host *hp;
1230	int32 bootsize;
1231{
1232	int bytesleft, len;
1233	byte *vp;
1234
1235	static const char noroom[] = "%s: No room for \"%s\" option";
1236
1237	vp = bp->bp_vend;
1238
1239	if (hp->flags.msg_size) {
1240		pktlen = hp->msg_size;
1241	} else {
1242		/*
1243		 * If the request was longer than the official length, build
1244		 * a response of that same length where the additional length
1245		 * is assumed to be part of the bp_vend (options) area.
1246		 */
1247		if (pktlen > sizeof(*bp)) {
1248			if (debug > 1)
1249				report(LOG_INFO, "request message length=%d", pktlen);
1250		}
1251		/*
1252		 * Check whether the request contains the option:
1253		 * Maximum DHCP Message Size (RFC1533 sec. 9.8)
1254		 * and if so, override the response length with its value.
1255		 * This request must lie within the first BP_VEND_LEN
1256		 * bytes of the option space.
1257		 */
1258		{
1259			byte *p, *ep;
1260			byte tag, len;
1261			short msgsz = 0;
1262
1263			p = vp + 4;
1264			ep = p + BP_VEND_LEN - 4;
1265			while (p < ep) {
1266				tag = *p++;
1267				/* Check for tags with no data first. */
1268				if (tag == TAG_PAD)
1269					continue;
1270				if (tag == TAG_END)
1271					break;
1272				/* Now scan the length byte. */
1273				len = *p++;
1274				switch (tag) {
1275				case TAG_MAX_MSGSZ:
1276					if (len == 2) {
1277						bcopy(p, (char*)&msgsz, 2);
1278						msgsz = ntohs(msgsz);
1279					}
1280					break;
1281				case TAG_SUBNET_MASK:
1282					/* XXX - Should preserve this if given... */
1283					break;
1284				} /* swtich */
1285				p += len;
1286			}
1287
1288			if (msgsz > sizeof(*bp) + BP_MSG_OVERHEAD) {
1289				if (debug > 1)
1290					report(LOG_INFO, "request has DHCP msglen=%d", msgsz);
1291				pktlen = msgsz - BP_MSG_OVERHEAD;
1292			}
1293		}
1294	}
1295
1296	if (pktlen < sizeof(*bp)) {
1297		report(LOG_ERR, "invalid response length=%d", pktlen);
1298		pktlen = sizeof(*bp);
1299	}
1300	bytesleft = ((byte*)bp + pktlen) - vp;
1301	if (pktlen > sizeof(*bp)) {
1302		if (debug > 1)
1303			report(LOG_INFO, "extended reply, length=%d, options=%d",
1304				   pktlen, bytesleft);
1305	}
1306
1307	/* Copy in the magic cookie */
1308	bcopy(vm_rfc1048, vp, 4);
1309	vp += 4;
1310	bytesleft -= 4;
1311
1312	if (hp->flags.subnet_mask) {
1313		/* always enough room here. */
1314		*vp++ = TAG_SUBNET_MASK;/* -1 byte  */
1315		*vp++ = 4;				/* -1 byte  */
1316		insert_u_long(hp->subnet_mask.s_addr, &vp);	/* -4 bytes */
1317		bytesleft -= 6;			/* Fix real count */
1318		if (hp->flags.gateway) {
1319			(void) insert_ip(TAG_GATEWAY,
1320							 hp->gateway,
1321							 &vp, &bytesleft);
1322		}
1323	}
1324	if (hp->flags.bootsize) {
1325		/* always enough room here */
1326		bootsize = (hp->flags.bootsize_auto) ?
1327			((bootsize + 511) / 512) : (hp->bootsize);	/* Round up */
1328		*vp++ = TAG_BOOT_SIZE;
1329		*vp++ = 2;
1330		*vp++ = (byte) ((bootsize >> 8) & 0xFF);
1331		*vp++ = (byte) (bootsize & 0xFF);
1332		bytesleft -= 4;			/* Tag, length, and 16 bit blocksize */
1333	}
1334	/*
1335	 * This one is special: Remaining options go in the ext file.
1336	 * Only the subnet_mask, bootsize, and gateway should precede.
1337	 */
1338	if (hp->flags.exten_file) {
1339		/*
1340		 * Check for room for exten_file.  Add 3 to account for
1341		 * TAG_EXTEN_FILE, length, and TAG_END.
1342		 */
1343		len = strlen(hp->exten_file->string);
1344		NEED((len + 3), "ef");
1345		*vp++ = TAG_EXTEN_FILE;
1346		*vp++ = (byte) (len & 0xFF);
1347		bcopy(hp->exten_file->string, vp, len);
1348		vp += len;
1349		*vp++ = TAG_END;
1350		bytesleft -= len + 3;
1351		return;					/* no more options here. */
1352	}
1353	/*
1354	 * The remaining options are inserted by the following
1355	 * function (which is shared with bootpef.c).
1356	 * Keep back one byte for the TAG_END.
1357	 */
1358	len = dovend_rfc1497(hp, vp, bytesleft - 1);
1359	vp += len;
1360	bytesleft -= len;
1361
1362	/* There should be at least one byte left. */
1363	NEED(1, "(end)");
1364	*vp++ = TAG_END;
1365	bytesleft--;
1366
1367	/* Log message done by caller. */
1368	if (bytesleft > 0) {
1369		/*
1370		 * Zero out any remaining part of the vendor area.
1371		 */
1372		bzero(vp, bytesleft);
1373	}
1374} /* dovend_rfc1048 */
1375#undef	NEED
1376
1377
1378/*
1379 * Now in readfile.c:
1380 * 	hwlookcmp()
1381 *	iplookcmp()
1382 */
1383
1384/* haddrtoa() - now in hwaddr.c */
1385/*
1386 * Now in dovend.c:
1387 * insert_ip()
1388 * insert_generic()
1389 * insert_u_long()
1390 */
1391
1392/* get_errmsg() - now in report.c */
1393
1394/*
1395 * Local Variables:
1396 * tab-width: 4
1397 * c-indent-level: 4
1398 * c-argdecl-indent: 4
1399 * c-continued-statement-offset: 4
1400 * c-continued-brace-offset: -4
1401 * c-label-offset: -4
1402 * c-brace-offset: 0
1403 * End:
1404 */
1405