bootpd.c revision 83941
1293734Sarybchik/************************************************************************
2300607Sarybchik          Copyright 1988, 1991 by Carnegie Mellon University
3293734Sarybchik
4293734Sarybchik                          All Rights Reserved
5293734Sarybchik
6293734SarybchikPermission to use, copy, modify, and distribute this software and its
7293734Sarybchikdocumentation for any purpose and without fee is hereby granted, provided
8293734Sarybchikthat the above copyright notice appear in all copies and that both that
9293734Sarybchikcopyright notice and this permission notice appear in supporting
10293734Sarybchikdocumentation, and that the name of Carnegie Mellon University not be used
11293734Sarybchikin advertising or publicity pertaining to distribution of the software
12293734Sarybchikwithout specific, written prior permission.
13293734Sarybchik
14293734SarybchikCARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
15293734SarybchikSOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
16293734SarybchikIN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
17293734SarybchikDAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
18293734SarybchikPROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
19293734SarybchikACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20293734SarybchikSOFTWARE.
21293734Sarybchik
22293734Sarybchik $FreeBSD: head/libexec/bootpd/bootpd.c 83941 2001-09-25 21:02:10Z iedowse $
23293734Sarybchik
24293734Sarybchik************************************************************************/
25293734Sarybchik
26293734Sarybchik/*
27293734Sarybchik * BOOTP (bootstrap protocol) server daemon.
28293734Sarybchik *
29293734Sarybchik * Answers BOOTP request packets from booting client machines.
30293734Sarybchik * See [SRI-NIC]<RFC>RFC951.TXT for a description of the protocol.
31293734Sarybchik * See [SRI-NIC]<RFC>RFC1048.TXT for vendor-information extensions.
32293734Sarybchik * See RFC 1395 for option tags 14-17.
33293734Sarybchik * See accompanying man page -- bootpd.8
34293734Sarybchik *
35293734Sarybchik * HISTORY
36293734Sarybchik *	See ./Changes
37293734Sarybchik *
38293734Sarybchik * BUGS
39293734Sarybchik *	See ./ToDo
40293748Sarybchik */
41293748Sarybchik
42293748Sarybchik
43293748Sarybchik
44293748Sarybchik#include <sys/types.h>
45293748Sarybchik#include <sys/param.h>
46293748Sarybchik#include <sys/socket.h>
47293734Sarybchik#include <sys/ioctl.h>
48299720Sarybchik#include <sys/file.h>
49299720Sarybchik#include <sys/time.h>
50299720Sarybchik#include <sys/stat.h>
51299720Sarybchik#include <sys/utsname.h>
52299720Sarybchik
53299720Sarybchik#include <net/if.h>
54299720Sarybchik#include <netinet/in.h>
55299720Sarybchik#include <arpa/inet.h>	/* inet_ntoa */
56299720Sarybchik
57299720Sarybchik#ifndef	NO_UNISTD
58299720Sarybchik#include <unistd.h>
59299720Sarybchik#endif
60299720Sarybchik
61299720Sarybchik#include <stdlib.h>
62299720Sarybchik#include <signal.h>
63299720Sarybchik#include <stdio.h>
64299720Sarybchik#include <string.h>
65299720Sarybchik#include <errno.h>
66299720Sarybchik#include <ctype.h>
67299720Sarybchik#include <netdb.h>
68299720Sarybchik#include <paths.h>
69299720Sarybchik#include <syslog.h>
70299720Sarybchik#include <assert.h>
71299720Sarybchik
72299720Sarybchik#ifdef	NO_SETSID
73299720Sarybchik# include <fcntl.h>		/* for O_RDONLY, etc */
74299720Sarybchik#endif
75299720Sarybchik
76299720Sarybchik#ifndef	USE_BFUNCS
77299720Sarybchik# include <memory.h>
78299720Sarybchik/* Yes, memcpy is OK here (no overlapped copies). */
79299720Sarybchik# define bcopy(a,b,c)    memcpy(b,a,c)
80299720Sarybchik# define bzero(p,l)      memset(p,0,l)
81299720Sarybchik# define bcmp(a,b,c)     memcmp(a,b,c)
82299720Sarybchik#endif
83299720Sarybchik
84299720Sarybchik#include "bootp.h"
85299720Sarybchik#include "hash.h"
86299720Sarybchik#include "hwaddr.h"
87301122Sarybchik#include "bootpd.h"
88310939Sarybchik#include "dovend.h"
89299720Sarybchik#include "getif.h"
90299720Sarybchik#include "readfile.h"
91299720Sarybchik#include "report.h"
92299720Sarybchik#include "tzone.h"
93299720Sarybchik#include "patchlevel.h"
94299720Sarybchik
95299720Sarybchik#ifndef CONFIG_FILE
96299720Sarybchik#define CONFIG_FILE		"/etc/bootptab"
97299720Sarybchik#endif
98299720Sarybchik#ifndef DUMPTAB_FILE
99299720Sarybchik#define DUMPTAB_FILE		"/tmp/bootpd.dump"
100299720Sarybchik#endif
101299720Sarybchik
102299720Sarybchik
103299720Sarybchik
104299720Sarybchik/*
105299720Sarybchik * Externals, forward declarations, and global variables
106299720Sarybchik */
107299720Sarybchik
108299720Sarybchik#ifdef	__STDC__
109299720Sarybchik#define P(args) args
110299720Sarybchik#else
111299720Sarybchik#define P(args) ()
112299720Sarybchik#endif
113299720Sarybchik
114299720Sarybchikextern void dumptab P((char *));
115299720Sarybchik
116299720SarybchikPRIVATE void catcher P((int));
117299720SarybchikPRIVATE int chk_access P((char *, int32 *));
118299720Sarybchik#ifdef VEND_CMU
119299720SarybchikPRIVATE void dovend_cmu P((struct bootp *, struct host *));
120299720Sarybchik#endif
121299720SarybchikPRIVATE void dovend_rfc1048 P((struct bootp *, struct host *, int32));
122299720SarybchikPRIVATE void handle_reply P((void));
123299720SarybchikPRIVATE void handle_request P((void));
124299720SarybchikPRIVATE void sendreply P((int forward, int32 dest_override));
125299720SarybchikPRIVATE void usage P((void));
126299720Sarybchik
127299720Sarybchik#undef	P
128299720Sarybchik
129299720Sarybchik/*
130299720Sarybchik * IP port numbers for client and server obtained from /etc/services
131299720Sarybchik */
132299720Sarybchik
133299720Sarybchiku_short bootps_port, bootpc_port;
134299720Sarybchik
135299720Sarybchik
136299720Sarybchik/*
137299720Sarybchik * Internet socket and interface config structures
138299720Sarybchik */
139299720Sarybchik
140299720Sarybchikstruct sockaddr_in bind_addr;	/* Listening */
141299720Sarybchikstruct sockaddr_in recv_addr;	/* Packet source */
142299720Sarybchikstruct sockaddr_in send_addr;	/*  destination */
143299720Sarybchik
144299720Sarybchik
145299720Sarybchik/*
146299720Sarybchik * option defaults
147299720Sarybchik */
148299720Sarybchikint debug = 0;					/* Debugging flag (level) */
149299720Sarybchikstruct timeval actualtimeout =
150299720Sarybchik{								/* fifteen minutes */
151299720Sarybchik	15 * 60L,					/* tv_sec */
152299720Sarybchik	0							/* tv_usec */
153299720Sarybchik};
154299720Sarybchik
155299720Sarybchik/*
156299720Sarybchik * General
157299720Sarybchik */
158299720Sarybchik
159299720Sarybchikint s;							/* Socket file descriptor */
160299720Sarybchikchar *pktbuf;					/* Receive packet buffer */
161299720Sarybchikint pktlen;
162299720Sarybchikchar *progname;
163299720Sarybchikchar *chdir_path;
164299720Sarybchikstruct in_addr my_ip_addr;
165299720Sarybchik
166299720Sarybchikstatic const char *hostname;
167299720Sarybchikstatic char default_hostname[MAXHOSTNAMELEN];
168299720Sarybchik
169299720Sarybchik/* Flags set by signal catcher. */
170299720SarybchikPRIVATE int do_readtab = 0;
171299720SarybchikPRIVATE int do_dumptab = 0;
172299720Sarybchik
173299720Sarybchik/*
174293887Sarybchik * Globals below are associated with the bootp database file (bootptab).
175299720Sarybchik */
176299720Sarybchik
177299720Sarybchikchar *bootptab = CONFIG_FILE;
178299720Sarybchikchar *bootpd_dump = DUMPTAB_FILE;
179299720Sarybchik
180299720Sarybchik
181299720Sarybchik
182299720Sarybchik/*
183299720Sarybchik * Initialization such as command-line processing is done and then the
184299720Sarybchik * main server loop is started.
185299720Sarybchik */
186299720Sarybchik
187299720Sarybchikint
188299720Sarybchikmain(argc, argv)
189299720Sarybchik	int argc;
190299720Sarybchik	char **argv;
191299720Sarybchik{
192299720Sarybchik	struct timeval *timeout;
193299720Sarybchik	struct bootp *bp;
194299720Sarybchik	struct servent *servp;
195299720Sarybchik	struct hostent *hep;
196299720Sarybchik	char *stmp;
197299720Sarybchik	int n, ba_len, ra_len;
198299720Sarybchik	int nfound, readfds;
199299720Sarybchik	int standalone;
200299720Sarybchik#ifdef	SA_NOCLDSTOP	/* Have POSIX sigaction(2). */
201299720Sarybchik	struct sigaction sa;
202299720Sarybchik#endif
203299720Sarybchik
204299720Sarybchik	progname = strrchr(argv[0], '/');
205299720Sarybchik	if (progname) progname++;
206299720Sarybchik	else progname = argv[0];
207299720Sarybchik
208299720Sarybchik	/*
209299720Sarybchik	 * Initialize logging.
210299720Sarybchik	 */
211299720Sarybchik	report_init(0);				/* uses progname */
212299720Sarybchik
213299720Sarybchik	/*
214299720Sarybchik	 * Log startup
215299720Sarybchik	 */
216299720Sarybchik	report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
217299720Sarybchik
218299720Sarybchik	/* Debugging for compilers with struct padding. */
219299720Sarybchik	assert(sizeof(struct bootp) == BP_MINPKTSZ);
220299720Sarybchik
221299720Sarybchik	/* Get space for receiving packets and composing replies. */
222299720Sarybchik	pktbuf = malloc(MAX_MSG_SIZE);
223299720Sarybchik	if (!pktbuf) {
224299720Sarybchik		report(LOG_ERR, "malloc failed");
225299720Sarybchik		exit(1);
226299720Sarybchik	}
227299720Sarybchik	bp = (struct bootp *) pktbuf;
228299720Sarybchik
229299720Sarybchik	/*
230299720Sarybchik	 * Check to see if a socket was passed to us from inetd.
231299720Sarybchik	 *
232299720Sarybchik	 * Use getsockname() to determine if descriptor 0 is indeed a socket
233299720Sarybchik	 * (and thus we are probably a child of inetd) or if it is instead
234299720Sarybchik	 * something else and we are running standalone.
235299720Sarybchik	 */
236299720Sarybchik	s = 0;
237299720Sarybchik	ba_len = sizeof(bind_addr);
238299720Sarybchik	bzero((char *) &bind_addr, ba_len);
239299720Sarybchik	errno = 0;
240299720Sarybchik	standalone = TRUE;
241300008Sarybchik	if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {
242300008Sarybchik		/*
243300008Sarybchik		 * Descriptor 0 is a socket.  Assume we are a child of inetd.
244300008Sarybchik		 */
245300008Sarybchik		if (bind_addr.sin_family == AF_INET) {
246299720Sarybchik			standalone = FALSE;
247299720Sarybchik			bootps_port = ntohs(bind_addr.sin_port);
248299720Sarybchik		} else {
249299720Sarybchik			/* Some other type of socket? */
250299720Sarybchik			report(LOG_ERR, "getsockname: not an INET socket");
251299720Sarybchik		}
252299720Sarybchik	}
253299720Sarybchik
254299720Sarybchik	/*
255299720Sarybchik	 * Set defaults that might be changed by option switches.
256299720Sarybchik	 */
257299720Sarybchik	stmp = NULL;
258299720Sarybchik	timeout = &actualtimeout;
259299720Sarybchik
260299720Sarybchik	if (gethostname(default_hostname, sizeof(default_hostname) - 1) < 0) {
261299720Sarybchik		report(LOG_ERR, "bootpd: can't get hostname\n");
262299720Sarybchik		exit(1);
263299720Sarybchik	}
264299720Sarybchik	default_hostname[sizeof(default_hostname) - 1] = '\0';
265299720Sarybchik	hostname = default_hostname;
266299720Sarybchik
267299720Sarybchik	/*
268299720Sarybchik	 * Read switches.
269299720Sarybchik	 */
270299720Sarybchik	for (argc--, argv++; argc > 0; argc--, argv++) {
271299720Sarybchik		if (argv[0][0] != '-')
272299720Sarybchik			break;
273299720Sarybchik		switch (argv[0][1]) {
274299720Sarybchik
275299720Sarybchik		case 'c':				/* chdir_path */
276311017Sarybchik			if (argv[0][2]) {
277311017Sarybchik				stmp = &(argv[0][2]);
278311017Sarybchik			} else {
279311017Sarybchik				argc--;
280311017Sarybchik				argv++;
281311017Sarybchik				stmp = argv[0];
282299720Sarybchik			}
283299720Sarybchik			if (!stmp || (stmp[0] != '/')) {
284299720Sarybchik				report(LOG_ERR,
285299720Sarybchik						"bootpd: invalid chdir specification\n");
286299720Sarybchik				break;
287299720Sarybchik			}
288299720Sarybchik			chdir_path = stmp;
289299720Sarybchik			break;
290299720Sarybchik
291299720Sarybchik		case 'd':				/* debug level */
292299720Sarybchik			if (argv[0][2]) {
293299720Sarybchik				stmp = &(argv[0][2]);
294299720Sarybchik			} else if (argv[1] && argv[1][0] == '-') {
295299720Sarybchik				/*
296299720Sarybchik				 * Backwards-compatible behavior:
297299720Sarybchik				 * no parameter, so just increment the debug flag.
298299720Sarybchik				 */
299299720Sarybchik				debug++;
300299720Sarybchik				break;
301299720Sarybchik			} else {
302299720Sarybchik				argc--;
303299720Sarybchik				argv++;
304299720Sarybchik				stmp = argv[0];
305299720Sarybchik			}
306310934Sarybchik			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
307310934Sarybchik				report(LOG_ERR,
308310934Sarybchik						"%s: invalid debug level\n", progname);
309310934Sarybchik				break;
310310934Sarybchik			}
311299720Sarybchik			debug = n;
312299720Sarybchik			break;
313299720Sarybchik
314299720Sarybchik		case 'h':				/* override hostname */
315299720Sarybchik			if (argv[0][2]) {
316299720Sarybchik				stmp = &(argv[0][2]);
317299720Sarybchik			} else {
318299720Sarybchik				argc--;
319299720Sarybchik				argv++;
320299720Sarybchik				stmp = argv[0];
321299720Sarybchik			}
322299720Sarybchik			if (!stmp) {
323299720Sarybchik				report(LOG_ERR,
324299720Sarybchik						"bootpd: missing hostname\n");
325299720Sarybchik				break;
326299720Sarybchik			}
327299720Sarybchik			hostname = stmp;
328299720Sarybchik			break;
329299720Sarybchik
330299720Sarybchik		case 'i':				/* inetd mode */
331299720Sarybchik			standalone = FALSE;
332299720Sarybchik			break;
333299720Sarybchik
334299720Sarybchik		case 's':				/* standalone mode */
335299720Sarybchik			standalone = TRUE;
336299720Sarybchik			break;
337299720Sarybchik
338299720Sarybchik		case 't':				/* timeout */
339299720Sarybchik			if (argv[0][2]) {
340299720Sarybchik				stmp = &(argv[0][2]);
341299720Sarybchik			} else {
342299720Sarybchik				argc--;
343299720Sarybchik				argv++;
344299720Sarybchik				stmp = argv[0];
345299720Sarybchik			}
346299720Sarybchik			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
347299720Sarybchik				report(LOG_ERR,
348299720Sarybchik						"%s: invalid timeout specification\n", progname);
349299720Sarybchik				break;
350299720Sarybchik			}
351299720Sarybchik			actualtimeout.tv_sec = (int32) (60 * n);
352299720Sarybchik			/*
353299720Sarybchik			 * If the actual timeout is zero, pass a NULL pointer
354299720Sarybchik			 * to select so it blocks indefinitely, otherwise,
355299720Sarybchik			 * point to the actual timeout value.
356299720Sarybchik			 */
357299720Sarybchik			timeout = (n > 0) ? &actualtimeout : NULL;
358299720Sarybchik			break;
359299720Sarybchik
360299720Sarybchik		default:
361299720Sarybchik			report(LOG_ERR, "%s: unknown switch: -%c\n",
362299720Sarybchik					progname, argv[0][1]);
363299720Sarybchik			usage();
364299720Sarybchik			break;
365299720Sarybchik
366299720Sarybchik		} /* switch */
367299720Sarybchik	} /* for args */
368299720Sarybchik
369299720Sarybchik	/*
370299720Sarybchik	 * Override default file names if specified on the command line.
371299720Sarybchik	 */
372299720Sarybchik	if (argc > 0)
373299720Sarybchik		bootptab = argv[0];
374299720Sarybchik
375299720Sarybchik	if (argc > 1)
376299720Sarybchik		bootpd_dump = argv[1];
377299720Sarybchik
378299720Sarybchik	/*
379299720Sarybchik	 * Get my hostname and IP address.
380299720Sarybchik	 */
381299720Sarybchik
382299720Sarybchik	hep = gethostbyname(hostname);
383299720Sarybchik	if (!hep) {
384299720Sarybchik		report(LOG_ERR, "Can not get my IP address\n");
385299720Sarybchik		exit(1);
386299720Sarybchik	}
387311481Sarybchik	bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
388299720Sarybchik
389299720Sarybchik	if (standalone) {
390299720Sarybchik		/*
391299720Sarybchik		 * Go into background and disassociate from controlling terminal.
392299720Sarybchik		 */
393299720Sarybchik		if (debug < 3) {
394299720Sarybchik			if (fork())
395299720Sarybchik				exit(0);
396299720Sarybchik#ifdef	NO_SETSID
397299720Sarybchik			setpgrp(0,0);
398299720Sarybchik#ifdef TIOCNOTTY
399299720Sarybchik			n = open(_PATH_TTY, O_RDWR);
400299720Sarybchik			if (n >= 0) {
401299720Sarybchik				ioctl(n, TIOCNOTTY, (char *) 0);
402299720Sarybchik				(void) close(n);
403299720Sarybchik			}
404299720Sarybchik#endif	/* TIOCNOTTY */
405299720Sarybchik#else	/* SETSID */
406299720Sarybchik			if (setsid() < 0)
407299720Sarybchik				perror("setsid");
408299720Sarybchik#endif	/* SETSID */
409299720Sarybchik		} /* if debug < 3 */
410299720Sarybchik
411299720Sarybchik		/*
412299720Sarybchik		 * Nuke any timeout value
413299720Sarybchik		 */
414299720Sarybchik		timeout = NULL;
415299720Sarybchik
416299720Sarybchik	} /* if standalone (1st) */
417299720Sarybchik
418299720Sarybchik	/* Set the cwd (i.e. to /tftpboot) */
419299720Sarybchik	if (chdir_path) {
420299720Sarybchik		if (chdir(chdir_path) < 0)
421299720Sarybchik			report(LOG_ERR, "%s: chdir failed", chdir_path);
422299720Sarybchik	}
423299720Sarybchik
424299720Sarybchik	/* Get the timezone. */
425299720Sarybchik	tzone_init();
426299720Sarybchik
427299720Sarybchik	/* Allocate hash tables. */
428299720Sarybchik	rdtab_init();
429299720Sarybchik
430299720Sarybchik	/*
431299720Sarybchik	 * Read the bootptab file.
432299720Sarybchik	 */
433299720Sarybchik	readtab(1);					/* force read */
434299720Sarybchik
435299720Sarybchik	if (standalone) {
436299720Sarybchik
437299720Sarybchik		/*
438299720Sarybchik		 * Create a socket.
439299720Sarybchik		 */
440299720Sarybchik		if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
441299720Sarybchik			report(LOG_ERR, "socket: %s", get_network_errmsg());
442299720Sarybchik			exit(1);
443299720Sarybchik		}
444299720Sarybchik
445299720Sarybchik		/*
446299720Sarybchik		 * Get server's listening port number
447299720Sarybchik		 */
448299720Sarybchik		servp = getservbyname("bootps", "udp");
449299720Sarybchik		if (servp) {
450299720Sarybchik			bootps_port = ntohs((u_short) servp->s_port);
451299720Sarybchik		} else {
452299720Sarybchik			bootps_port = (u_short) IPPORT_BOOTPS;
453299720Sarybchik			report(LOG_ERR,
454311481Sarybchik				   "udp/bootps: unknown service -- assuming port %d",
455299720Sarybchik				   bootps_port);
456299720Sarybchik		}
457299720Sarybchik
458299720Sarybchik		/*
459299720Sarybchik		 * Bind socket to BOOTPS port.
460299720Sarybchik		 */
461299720Sarybchik		bind_addr.sin_family = AF_INET;
462299720Sarybchik		bind_addr.sin_addr.s_addr = INADDR_ANY;
463299720Sarybchik		bind_addr.sin_port = htons(bootps_port);
464299720Sarybchik		if (bind(s, (struct sockaddr *) &bind_addr,
465299720Sarybchik				 sizeof(bind_addr)) < 0)
466299720Sarybchik		{
467299720Sarybchik			report(LOG_ERR, "bind: %s", get_network_errmsg());
468299720Sarybchik			exit(1);
469299720Sarybchik		}
470299720Sarybchik	} /* if standalone (2nd)*/
471299720Sarybchik
472299720Sarybchik	/*
473299720Sarybchik	 * Get destination port number so we can reply to client
474299720Sarybchik	 */
475299720Sarybchik	servp = getservbyname("bootpc", "udp");
476299720Sarybchik	if (servp) {
477299720Sarybchik		bootpc_port = ntohs(servp->s_port);
478299720Sarybchik	} else {
479299720Sarybchik		report(LOG_ERR,
480299720Sarybchik			   "udp/bootpc: unknown service -- assuming port %d",
481299720Sarybchik			   IPPORT_BOOTPC);
482299720Sarybchik		bootpc_port = (u_short) IPPORT_BOOTPC;
483299720Sarybchik	}
484299720Sarybchik
485299720Sarybchik	/*
486299720Sarybchik	 * Set up signals to read or dump the table.
487299720Sarybchik	 */
488299720Sarybchik#ifdef	SA_NOCLDSTOP	/* Have POSIX sigaction(2). */
489299720Sarybchik	sa.sa_handler = catcher;
490299720Sarybchik	sigemptyset(&sa.sa_mask);
491299720Sarybchik	sa.sa_flags = 0;
492299720Sarybchik	if (sigaction(SIGHUP, &sa, NULL) < 0) {
493299720Sarybchik		report(LOG_ERR, "sigaction: %s", get_errmsg());
494299720Sarybchik		exit(1);
495299720Sarybchik	}
496299720Sarybchik	if (sigaction(SIGUSR1, &sa, NULL) < 0) {
497299720Sarybchik		report(LOG_ERR, "sigaction: %s", get_errmsg());
498299720Sarybchik		exit(1);
499299720Sarybchik	}
500299720Sarybchik#else	/* SA_NOCLDSTOP */
501299720Sarybchik	/* Old-fashioned UNIX signals */
502299720Sarybchik	if ((int) signal(SIGHUP, catcher) < 0) {
503299720Sarybchik		report(LOG_ERR, "signal: %s", get_errmsg());
504299720Sarybchik		exit(1);
505299720Sarybchik	}
506299720Sarybchik	if ((int) signal(SIGUSR1, catcher) < 0) {
507299720Sarybchik		report(LOG_ERR, "signal: %s", get_errmsg());
508299720Sarybchik		exit(1);
509299720Sarybchik	}
510299720Sarybchik#endif	/* SA_NOCLDSTOP */
511299720Sarybchik
512299720Sarybchik	/*
513299720Sarybchik	 * Process incoming requests.
514299720Sarybchik	 */
515299720Sarybchik	for (;;) {
516299720Sarybchik		struct timeval tv;
517299720Sarybchik
518299720Sarybchik		readfds = 1 << s;
519299720Sarybchik		if (timeout)
520299720Sarybchik			tv = *timeout;
521299720Sarybchik
522299720Sarybchik		nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL,
523299720Sarybchik						(timeout) ? &tv : NULL);
524299720Sarybchik		if (nfound < 0) {
525299720Sarybchik			if (errno != EINTR) {
526299720Sarybchik				report(LOG_ERR, "select: %s", get_errmsg());
527299720Sarybchik			}
528299720Sarybchik			/*
529299720Sarybchik			 * Call readtab() or dumptab() here to avoid the
530299720Sarybchik			 * dangers of doing I/O from a signal handler.
531299720Sarybchik			 */
532299720Sarybchik			if (do_readtab) {
533299720Sarybchik				do_readtab = 0;
534299720Sarybchik				readtab(1);		/* force read */
535299720Sarybchik			}
536299720Sarybchik			if (do_dumptab) {
537299720Sarybchik				do_dumptab = 0;
538299720Sarybchik				dumptab(bootpd_dump);
539299720Sarybchik			}
540299720Sarybchik			continue;
541299720Sarybchik		}
542299720Sarybchik		if (!(readfds & (1 << s))) {
543299720Sarybchik			if (debug > 1)
544299720Sarybchik				report(LOG_INFO, "exiting after %ld minutes of inactivity",
545299720Sarybchik					   actualtimeout.tv_sec / 60);
546299720Sarybchik			exit(0);
547299720Sarybchik		}
548299720Sarybchik		ra_len = sizeof(recv_addr);
549299720Sarybchik		n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
550299720Sarybchik					 (struct sockaddr *) &recv_addr, &ra_len);
551299720Sarybchik		if (n <= 0) {
552299720Sarybchik			continue;
553299720Sarybchik		}
554299720Sarybchik		if (debug > 1) {
555299720Sarybchik			report(LOG_INFO, "recvd pkt from IP addr %s",
556299720Sarybchik				   inet_ntoa(recv_addr.sin_addr));
557299720Sarybchik		}
558299720Sarybchik		if (n < sizeof(struct bootp)) {
559299720Sarybchik			if (debug) {
560299720Sarybchik				report(LOG_NOTICE, "received short packet");
561299720Sarybchik			}
562299720Sarybchik			continue;
563299720Sarybchik		}
564299720Sarybchik		pktlen = n;
565299720Sarybchik
566299720Sarybchik		readtab(0);				/* maybe re-read bootptab */
567299720Sarybchik
568299720Sarybchik		switch (bp->bp_op) {
569299720Sarybchik		case BOOTREQUEST:
570299720Sarybchik			handle_request();
571299720Sarybchik			break;
572299720Sarybchik		case BOOTREPLY:
573299720Sarybchik			handle_reply();
574299720Sarybchik			break;
575299720Sarybchik		}
576299720Sarybchik	}
577299720Sarybchik	return 0;
578299720Sarybchik}
579299720Sarybchik
580299720Sarybchik
581299720Sarybchik
582299720Sarybchik
583299720Sarybchik/*
584299720Sarybchik * Print "usage" message and exit
585299720Sarybchik */
586299720Sarybchik
587299720SarybchikPRIVATE void
588299720Sarybchikusage()
589299720Sarybchik{
590299720Sarybchik	fprintf(stderr,
591299720Sarybchik			"usage:  bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n");
592299720Sarybchik	fprintf(stderr, "\t -c n\tset current directory\n");
593299720Sarybchik	fprintf(stderr, "\t -d n\tset debug level\n");
594299720Sarybchik	fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n");
595299720Sarybchik	fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n");
596299720Sarybchik	fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n");
597299720Sarybchik	exit(1);
598299720Sarybchik}
599299720Sarybchik
600299720Sarybchik/* Signal catchers */
601299720SarybchikPRIVATE void
602299720Sarybchikcatcher(sig)
603299720Sarybchik	int sig;
604299720Sarybchik{
605299720Sarybchik	if (sig == SIGHUP)
606299720Sarybchik		do_readtab = 1;
607299720Sarybchik	if (sig == SIGUSR1)
608299720Sarybchik		do_dumptab = 1;
609299720Sarybchik#if	!defined(SA_NOCLDSTOP) && defined(SYSV)
610299720Sarybchik	/* For older "System V" derivatives with no sigaction(). */
611310944Sarybchik	signal(sig, catcher);
612299720Sarybchik#endif
613310944Sarybchik}
614310944Sarybchik
615310944Sarybchik
616310944Sarybchik
617310944Sarybchik/*
618310944Sarybchik * Process BOOTREQUEST packet.
619310944Sarybchik *
620310944Sarybchik * Note:  This version of the bootpd.c server never forwards
621310944Sarybchik * a request to another server.  That is the job of a gateway
622310944Sarybchik * program such as the "bootpgw" program included here.
623310944Sarybchik *
624310944Sarybchik * (Also this version does not interpret the hostname field of
625310944Sarybchik * the request packet;  it COULD do a name->address lookup and
626310944Sarybchik * forward the request there.)
627310944Sarybchik */
628310944SarybchikPRIVATE void
629310944Sarybchikhandle_request()
630310944Sarybchik{
631310944Sarybchik	struct bootp *bp = (struct bootp *) pktbuf;
632310944Sarybchik	struct host *hp = NULL;
633310944Sarybchik	struct host dummyhost;
634310944Sarybchik	int32 bootsize = 0;
635310944Sarybchik	unsigned hlen, hashcode;
636310944Sarybchik	int32 dest;
637310944Sarybchik	char realpath[1024];
638310944Sarybchik	char *clntpath;
639310944Sarybchik	char *homedir, *bootfile;
640299720Sarybchik	int n;
641299720Sarybchik
642299720Sarybchik	bp->bp_file[sizeof(bp->bp_file)-1] = '\0';
643299720Sarybchik
644299720Sarybchik	/* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
645299720Sarybchik
646299720Sarybchik	/*
647299720Sarybchik	 * If the servername field is set, compare it against us.
648299720Sarybchik	 * If we're not being addressed, ignore this request.
649299720Sarybchik	 * If the server name field is null, throw in our name.
650299720Sarybchik	 */
651299720Sarybchik	if (strlen(bp->bp_sname)) {
652299720Sarybchik		if (strcmp(bp->bp_sname, hostname)) {
653299720Sarybchik			if (debug)
654299720Sarybchik				report(LOG_INFO, "\
655299720Sarybchikignoring request for server %s from client at %s address %s",
656299720Sarybchik					   bp->bp_sname, netname(bp->bp_htype),
657299720Sarybchik					   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
658299720Sarybchik			/* XXX - Is it correct to ignore such a request? -gwr */
659299720Sarybchik			return;
660299720Sarybchik		}
661299720Sarybchik	} else {
662299720Sarybchik		strcpy(bp->bp_sname, hostname);
663299720Sarybchik	}
664299720Sarybchik
665299720Sarybchik	/* Convert the request into a reply. */
666299720Sarybchik	bp->bp_op = BOOTREPLY;
667299720Sarybchik	if (bp->bp_ciaddr.s_addr == 0) {
668299720Sarybchik		/*
669299720Sarybchik		 * client doesnt know his IP address,
670299720Sarybchik		 * search by hardware address.
671299720Sarybchik		 */
672299720Sarybchik		if (debug > 1) {
673299720Sarybchik			report(LOG_INFO, "request from %s address %s",
674299720Sarybchik				   netname(bp->bp_htype),
675299720Sarybchik				   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
676299720Sarybchik		}
677299720Sarybchik		hlen = haddrlength(bp->bp_htype);
678299720Sarybchik		if (hlen != bp->bp_hlen) {
679299720Sarybchik			report(LOG_NOTICE, "bad addr len from from %s address %s",
680299720Sarybchik				   netname(bp->bp_htype),
681299720Sarybchik				   haddrtoa(bp->bp_chaddr, hlen));
682299720Sarybchik		}
683299720Sarybchik		dummyhost.htype = bp->bp_htype;
684299720Sarybchik		bcopy(bp->bp_chaddr, dummyhost.haddr, hlen);
685299720Sarybchik		hashcode = hash_HashFunction(bp->bp_chaddr, hlen);
686299720Sarybchik		hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp,
687299720Sarybchik										 &dummyhost);
688299720Sarybchik		if (hp == NULL &&
689299720Sarybchik			bp->bp_htype == HTYPE_IEEE802)
690299720Sarybchik		{
691299720Sarybchik			/* Try again with address in "canonical" form. */
692299720Sarybchik			haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen);
693299720Sarybchik			if (debug > 1) {
694299720Sarybchik				report(LOG_INFO, "\
695299720SarybchikHW addr type is IEEE 802.  convert to %s and check again\n",
696299720Sarybchik					   haddrtoa(dummyhost.haddr, bp->bp_hlen));
697299720Sarybchik			}
698299720Sarybchik			hashcode = hash_HashFunction(dummyhost.haddr, hlen);
699299720Sarybchik			hp = (struct host *) hash_Lookup(hwhashtable, hashcode,
700299720Sarybchik											 hwlookcmp, &dummyhost);
701299720Sarybchik		}
702299720Sarybchik		if (hp == NULL) {
703299720Sarybchik			/*
704299720Sarybchik			 * XXX - Add dynamic IP address assignment?
705299720Sarybchik			 */
706299720Sarybchik			if (debug)
707299720Sarybchik				report(LOG_NOTICE, "unknown client %s address %s",
708299720Sarybchik					   netname(bp->bp_htype),
709299720Sarybchik					   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
710299720Sarybchik			return; /* not found */
711299720Sarybchik		}
712299720Sarybchik		(bp->bp_yiaddr).s_addr = hp->iaddr.s_addr;
713299720Sarybchik
714299720Sarybchik	} else {
715299720Sarybchik
716299720Sarybchik		/*
717299720Sarybchik		 * search by IP address.
718299720Sarybchik		 */
719299720Sarybchik		if (debug > 1) {
720299720Sarybchik			report(LOG_INFO, "request from IP addr %s",
721299720Sarybchik				   inet_ntoa(bp->bp_ciaddr));
722299720Sarybchik		}
723299720Sarybchik		dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr;
724299720Sarybchik		hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4);
725299720Sarybchik		hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp,
726299720Sarybchik										 &dummyhost);
727299720Sarybchik		if (hp == NULL) {
728299720Sarybchik			if (debug) {
729299720Sarybchik				report(LOG_NOTICE, "IP address not found: %s",
730299720Sarybchik					   inet_ntoa(bp->bp_ciaddr));
731299720Sarybchik			}
732299720Sarybchik			return;
733299720Sarybchik		}
734299720Sarybchik	}
735299720Sarybchik
736299720Sarybchik	if (debug) {
737299720Sarybchik		report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr),
738299720Sarybchik			   hp->hostname->string);
739299720Sarybchik	}
740299720Sarybchik
741299720Sarybchik	/*
742299720Sarybchik	 * If there is a response delay threshold, ignore requests
743299720Sarybchik	 * with a timestamp lower than the threshold.
744299720Sarybchik	 */
745299720Sarybchik	if (hp->flags.min_wait) {
746299720Sarybchik		u_int32 t = (u_int32) ntohs(bp->bp_secs);
747299720Sarybchik		if (t < hp->min_wait) {
748299720Sarybchik			if (debug > 1)
749299720Sarybchik				report(LOG_INFO,
750299720Sarybchik					   "ignoring request due to timestamp (%d < %d)",
751299720Sarybchik					   t, hp->min_wait);
752299720Sarybchik			return;
753299720Sarybchik		}
754299720Sarybchik	}
755299720Sarybchik
756299720Sarybchik#ifdef	YORK_EX_OPTION
757299720Sarybchik	/*
758299720Sarybchik	 * The need for the "ex" tag arose out of the need to empty
759299720Sarybchik	 * shared networked drives on diskless PCs.  This solution is
760299720Sarybchik	 * not very clean but it does work fairly well.
761299720Sarybchik	 * Written by Edmund J. Sutcliffe <edmund@york.ac.uk>
762299720Sarybchik	 *
763299720Sarybchik	 * XXX - This could compromise security if a non-trusted user
764299720Sarybchik	 * managed to write an entry in the bootptab with :ex=trojan:
765299720Sarybchik	 * so I would leave this turned off unless you need it. -gwr
766299720Sarybchik	 */
767299720Sarybchik	/* Run a program, passing the client name as a parameter. */
768299720Sarybchik	if (hp->flags.exec_file) {
769299720Sarybchik		char tst[100];
770299720Sarybchik		/* XXX - Check string lengths? -gwr */
771299720Sarybchik		strcpy (tst, hp->exec_file->string);
772299720Sarybchik		strcat (tst, " ");
773299720Sarybchik		strcat (tst, hp->hostname->string);
774299720Sarybchik		strcat (tst, " &");
775299720Sarybchik		if (debug)
776299720Sarybchik			report(LOG_INFO, "executing %s", tst);
777299720Sarybchik		system(tst);	/* Hope this finishes soon... */
778299720Sarybchik	}
779299720Sarybchik#endif	/* YORK_EX_OPTION */
780299720Sarybchik
781299720Sarybchik	/*
782299720Sarybchik	 * If a specific TFTP server address was specified in the bootptab file,
783299720Sarybchik	 * fill it in, otherwise zero it.
784299720Sarybchik	 * XXX - Rather than zero it, should it be the bootpd address? -gwr
785299720Sarybchik	 */
786299720Sarybchik	(bp->bp_siaddr).s_addr = (hp->flags.bootserver) ?
787299720Sarybchik		hp->bootserver.s_addr : 0L;
788299720Sarybchik
789299720Sarybchik#ifdef	STANFORD_PROM_COMPAT
790299720Sarybchik	/*
791299720Sarybchik	 * Stanford bootp PROMs (for a Sun?) have no way to leave
792299720Sarybchik	 * the boot file name field blank (because the boot file
793299720Sarybchik	 * name is automatically generated from some index).
794299720Sarybchik	 * As a work-around, this little hack allows those PROMs to
795299720Sarybchik	 * specify "sunboot14" with the same effect as a NULL name.
796299720Sarybchik	 * (The user specifies boot device 14 or some such magic.)
797299720Sarybchik	 */
798299720Sarybchik	if (strcmp(bp->bp_file, "sunboot14") == 0)
799299720Sarybchik		bp->bp_file[0] = '\0';	/* treat it as unspecified */
800299720Sarybchik#endif
801299720Sarybchik
802299720Sarybchik	/*
803299720Sarybchik	 * Fill in the client's proper bootfile.
804299720Sarybchik	 *
805299720Sarybchik	 * If the client specifies an absolute path, try that file with a
806299720Sarybchik	 * ".host" suffix and then without.  If the file cannot be found, no
807299720Sarybchik	 * reply is made at all.
808299720Sarybchik	 *
809299720Sarybchik	 * If the client specifies a null or relative file, use the following
810299720Sarybchik	 * table to determine the appropriate action:
811299720Sarybchik	 *
812299720Sarybchik	 *  Homedir      Bootfile    Client's file
813299720Sarybchik	 * specified?   specified?   specification   Action
814299720Sarybchik	 * -------------------------------------------------------------------
815299720Sarybchik	 *      No          No          Null         Send null filename
816299720Sarybchik	 *      No          No          Relative     Discard request
817299720Sarybchik	 *      No          Yes         Null         Send if absolute else null
818299720Sarybchik	 *      No          Yes         Relative     Discard request     *XXX
819299720Sarybchik	 *      Yes         No          Null         Send null filename
820299720Sarybchik	 *      Yes         No          Relative     Lookup with ".host"
821299720Sarybchik	 *      Yes         Yes         Null         Send home/boot or bootfile
822299720Sarybchik	 *      Yes         Yes         Relative     Lookup with ".host" *XXX
823299720Sarybchik	 *
824299720Sarybchik	 */
825299720Sarybchik
826299720Sarybchik	/*
827299720Sarybchik	 * XXX - I don't like the policy of ignoring a client when the
828299720Sarybchik	 * boot file is not accessible.  The TFTP server might not be
829299720Sarybchik	 * running on the same machine as the BOOTP server, in which
830299720Sarybchik	 * case checking accessibility of the boot file is pointless.
831299720Sarybchik	 *
832299720Sarybchik	 * Therefore, file accessibility is now demanded ONLY if you
833299720Sarybchik	 * define CHECK_FILE_ACCESS in the Makefile options. -gwr
834299720Sarybchik	 */
835299720Sarybchik
836299720Sarybchik	/*
837299720Sarybchik	 * The "real" path is as seen by the BOOTP daemon on this
838299720Sarybchik	 * machine, while the client path is relative to the TFTP
839299720Sarybchik	 * daemon chroot directory (i.e. /tftpboot).
840299720Sarybchik	 */
841299720Sarybchik	if (hp->flags.tftpdir) {
842299720Sarybchik		snprintf(realpath, sizeof(realpath), "%s", hp->tftpdir->string);
843299720Sarybchik		clntpath = &realpath[strlen(realpath)];
844299720Sarybchik	} else {
845299720Sarybchik		realpath[0] = '\0';
846299720Sarybchik		clntpath = realpath;
847299720Sarybchik	}
848299720Sarybchik
849299720Sarybchik	/*
850299720Sarybchik	 * Determine client's requested homedir and bootfile.
851299720Sarybchik	 */
852299720Sarybchik	homedir = NULL;
853299720Sarybchik	bootfile = NULL;
854299720Sarybchik	if (bp->bp_file[0]) {
855299720Sarybchik		homedir = bp->bp_file;
856299720Sarybchik		bootfile = strrchr(homedir, '/');
857299720Sarybchik		if (bootfile) {
858299720Sarybchik			if (homedir == bootfile)
859299720Sarybchik				homedir = NULL;
860299720Sarybchik			*bootfile++ = '\0';
861299720Sarybchik		} else {
862299720Sarybchik			/* no "/" in the string */
863299720Sarybchik			bootfile = homedir;
864299720Sarybchik			homedir = NULL;
865299720Sarybchik		}
866299720Sarybchik		if (debug > 2) {
867299720Sarybchik			report(LOG_INFO, "requested path=\"%s\"  file=\"%s\"",
868299720Sarybchik				   (homedir) ? homedir : "",
869299720Sarybchik				   (bootfile) ? bootfile : "");
870299720Sarybchik		}
871299720Sarybchik	}
872299720Sarybchik
873299720Sarybchik	/*
874299720Sarybchik	 * Specifications in bootptab override client requested values.
875299720Sarybchik	 */
876299720Sarybchik	if (hp->flags.homedir)
877299720Sarybchik		homedir = hp->homedir->string;
878299720Sarybchik	if (hp->flags.bootfile)
879299720Sarybchik		bootfile = hp->bootfile->string;
880299720Sarybchik
881299720Sarybchik	/*
882299720Sarybchik	 * Construct bootfile path.
883299720Sarybchik	 */
884299720Sarybchik	if (homedir) {
885299720Sarybchik		if (homedir[0] != '/')
886299720Sarybchik			strcat(clntpath, "/");
887299720Sarybchik		strcat(clntpath, homedir);
888299720Sarybchik		homedir = NULL;
889299720Sarybchik	}
890299720Sarybchik	if (bootfile) {
891299720Sarybchik		if (bootfile[0] != '/')
892299720Sarybchik			strcat(clntpath, "/");
893299720Sarybchik		strcat(clntpath, bootfile);
894299720Sarybchik		bootfile = NULL;
895299720Sarybchik	}
896299720Sarybchik
897299720Sarybchik	/*
898299720Sarybchik	 * First try to find the file with a ".host" suffix
899299720Sarybchik	 */
900299720Sarybchik	n = strlen(clntpath);
901299720Sarybchik	strcat(clntpath, ".");
902299720Sarybchik	strcat(clntpath, hp->hostname->string);
903299720Sarybchik	if (chk_access(realpath, &bootsize) < 0) {
904299720Sarybchik		clntpath[n] = 0;			/* Try it without the suffix */
905299720Sarybchik		if (chk_access(realpath, &bootsize) < 0) {
906299720Sarybchik			/* neither "file.host" nor "file" was found */
907299720Sarybchik#ifdef	CHECK_FILE_ACCESS
908299720Sarybchik
909299720Sarybchik			if (bp->bp_file[0]) {
910299720Sarybchik				/*
911299720Sarybchik				 * Client wanted specific file
912299720Sarybchik				 * and we didn't have it.
913299720Sarybchik				 */
914299720Sarybchik				report(LOG_NOTICE,
915299720Sarybchik					   "requested file not found: \"%s\"", clntpath);
916299720Sarybchik				return;
917299720Sarybchik			}
918299720Sarybchik			/*
919299720Sarybchik			 * Client didn't ask for a specific file and we couldn't
920299720Sarybchik			 * access the default file, so just zero-out the bootfile
921299720Sarybchik			 * field in the packet and continue processing the reply.
922299720Sarybchik			 */
923299720Sarybchik			bzero(bp->bp_file, sizeof(bp->bp_file));
924299720Sarybchik			goto null_file_name;
925299720Sarybchik
926299720Sarybchik#else	/* CHECK_FILE_ACCESS */
927299720Sarybchik
928299720Sarybchik			/* Complain only if boot file size was needed. */
929299720Sarybchik			if (hp->flags.bootsize_auto) {
930299720Sarybchik				report(LOG_ERR, "can not determine size of file \"%s\"",
931299720Sarybchik					   clntpath);
932299720Sarybchik			}
933299720Sarybchik
934299720Sarybchik#endif	/* CHECK_FILE_ACCESS */
935299720Sarybchik		}
936299720Sarybchik	}
937299720Sarybchik	strncpy(bp->bp_file, clntpath, BP_FILE_LEN);
938299720Sarybchik	if (debug > 2)
939299720Sarybchik		report(LOG_INFO, "bootfile=\"%s\"", clntpath);
940299720Sarybchik
941299720Sarybchik#ifdef	CHECK_FILE_ACCESS
942299720Sarybchiknull_file_name:
943299720Sarybchik#endif	/* CHECK_FILE_ACCESS */
944299720Sarybchik
945299720Sarybchik
946299720Sarybchik	/*
947299720Sarybchik	 * Handle vendor options based on magic number.
948299720Sarybchik	 */
949299720Sarybchik
950299720Sarybchik	if (debug > 1) {
951299720Sarybchik		report(LOG_INFO, "vendor magic field is %d.%d.%d.%d",
952299720Sarybchik			   (int) ((bp->bp_vend)[0]),
953299720Sarybchik			   (int) ((bp->bp_vend)[1]),
954299720Sarybchik			   (int) ((bp->bp_vend)[2]),
955299720Sarybchik			   (int) ((bp->bp_vend)[3]));
956299720Sarybchik	}
957299720Sarybchik	/*
958299720Sarybchik	 * If this host isn't set for automatic vendor info then copy the
959299720Sarybchik	 * specific cookie into the bootp packet, thus forcing a certain
960299720Sarybchik	 * reply format.  Only force reply format if user specified it.
961299720Sarybchik	 */
962299720Sarybchik	if (hp->flags.vm_cookie) {
963299720Sarybchik		/* Slam in the user specified magic number. */
964299720Sarybchik		bcopy(hp->vm_cookie, bp->bp_vend, 4);
965299720Sarybchik	}
966299720Sarybchik	/*
967299720Sarybchik	 * Figure out the format for the vendor-specific info.
968299720Sarybchik	 * Note that bp->bp_vend may have been set above.
969299720Sarybchik	 */
970299720Sarybchik	if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) {
971299720Sarybchik		/* RFC1048 conformant bootp client */
972299720Sarybchik		dovend_rfc1048(bp, hp, bootsize);
973299720Sarybchik		if (debug > 1) {
974299720Sarybchik			report(LOG_INFO, "sending reply (with RFC1048 options)");
975299720Sarybchik		}
976299720Sarybchik	}
977299720Sarybchik#ifdef VEND_CMU
978299720Sarybchik	else if (!bcmp(bp->bp_vend, vm_cmu, 4)) {
979299720Sarybchik		dovend_cmu(bp, hp);
980299720Sarybchik		if (debug > 1) {
981299720Sarybchik			report(LOG_INFO, "sending reply (with CMU options)");
982299720Sarybchik		}
983299720Sarybchik	}
984299720Sarybchik#endif
985299720Sarybchik	else {
986299720Sarybchik		if (debug > 1) {
987299720Sarybchik			report(LOG_INFO, "sending reply (with no options)");
988310917Sarybchik		}
989299720Sarybchik	}
990299720Sarybchik
991299720Sarybchik	dest = (hp->flags.reply_addr) ?
992310938Sarybchik		hp->reply_addr.s_addr : 0L;
993299720Sarybchik
994299720Sarybchik	/* not forwarded */
995299720Sarybchik	sendreply(0, dest);
996299720Sarybchik}
997299720Sarybchik
998299720Sarybchik
999299720Sarybchik/*
1000299720Sarybchik * Process BOOTREPLY packet.
1001299720Sarybchik */
1002299720SarybchikPRIVATE void
1003299720Sarybchikhandle_reply()
1004299720Sarybchik{
1005299720Sarybchik	if (debug) {
1006299720Sarybchik		report(LOG_INFO, "processing boot reply");
1007299720Sarybchik	}
1008299720Sarybchik	/* forwarded, no destination override */
1009299720Sarybchik	sendreply(1, 0);
1010299720Sarybchik}
1011299720Sarybchik
1012299720Sarybchik
1013299720Sarybchik/*
1014299720Sarybchik * Send a reply packet to the client.  'forward' flag is set if we are
1015299720Sarybchik * not the originator of this reply packet.
1016299720Sarybchik */
1017299720SarybchikPRIVATE void
1018299720Sarybchiksendreply(forward, dst_override)
1019299720Sarybchik	int forward;
1020299720Sarybchik	int32 dst_override;
1021299720Sarybchik{
1022299720Sarybchik	struct bootp *bp = (struct bootp *) pktbuf;
1023299720Sarybchik	struct in_addr dst;
1024299720Sarybchik	u_short port = bootpc_port;
1025299720Sarybchik	unsigned char *ha;
1026299720Sarybchik	int len, haf;
1027299720Sarybchik
1028299720Sarybchik	/*
1029299720Sarybchik	 * XXX - Should honor bp_flags "broadcast" bit here.
1030299720Sarybchik	 * Temporary workaround: use the :ra=ADDR: option to
1031299720Sarybchik	 * set the reply address to the broadcast address.
1032299720Sarybchik	 */
1033299720Sarybchik
1034299720Sarybchik	/*
1035299720Sarybchik	 * If the destination address was specified explicitly
1036299720Sarybchik	 * (i.e. the broadcast address for HP compatiblity)
1037299720Sarybchik	 * then send the response to that address.  Otherwise,
1038299720Sarybchik	 * act in accordance with RFC951:
1039299720Sarybchik	 *   If the client IP address is specified, use that
1040299720Sarybchik	 * else if gateway IP address is specified, use that
1041299720Sarybchik	 * else make a temporary arp cache entry for the client's
1042299720Sarybchik	 * NEW IP/hardware address and use that.
1043299720Sarybchik	 */
1044299720Sarybchik	if (dst_override) {
1045299720Sarybchik		dst.s_addr = dst_override;
1046299720Sarybchik		if (debug > 1) {
1047299720Sarybchik			report(LOG_INFO, "reply address override: %s",
1048299720Sarybchik				   inet_ntoa(dst));
1049299720Sarybchik		}
1050299720Sarybchik	} else if (bp->bp_ciaddr.s_addr) {
1051299720Sarybchik		dst = bp->bp_ciaddr;
1052299720Sarybchik	} else if (bp->bp_giaddr.s_addr && forward == 0) {
1053299720Sarybchik		dst = bp->bp_giaddr;
1054299720Sarybchik		port = bootps_port;
1055299720Sarybchik		if (debug > 1) {
1056299720Sarybchik			report(LOG_INFO, "sending reply to gateway %s",
1057299720Sarybchik				   inet_ntoa(dst));
1058299720Sarybchik		}
1059299720Sarybchik	} else {
1060299720Sarybchik		dst = bp->bp_yiaddr;
1061299720Sarybchik		ha = bp->bp_chaddr;
1062299720Sarybchik		len = bp->bp_hlen;
1063299720Sarybchik		if (len > MAXHADDRLEN)
1064299720Sarybchik			len = MAXHADDRLEN;
1065299720Sarybchik		haf = (int) bp->bp_htype;
1066299720Sarybchik		if (haf == 0)
1067299720Sarybchik			haf = HTYPE_ETHERNET;
1068299720Sarybchik
1069299720Sarybchik		if (debug > 1)
1070299720Sarybchik			report(LOG_INFO, "setarp %s - %s",
1071299720Sarybchik				   inet_ntoa(dst), haddrtoa(ha, len));
1072293887Sarybchik		setarp(s, &dst, haf, ha, len);
1073293887Sarybchik	}
1074293887Sarybchik
1075293734Sarybchik	if ((forward == 0) &&
1076293887Sarybchik		(bp->bp_siaddr.s_addr == 0))
1077293887Sarybchik	{
1078293887Sarybchik		struct ifreq *ifr;
1079299904Sarybchik		struct in_addr siaddr;
1080299904Sarybchik		/*
1081293734Sarybchik		 * If we are originating this reply, we
1082293887Sarybchik		 * need to find our own interface address to
1083299904Sarybchik		 * put in the bp_siaddr field of the reply.
1084299904Sarybchik		 * If this server is multi-homed, pick the
1085299904Sarybchik		 * 'best' interface (the one on the same net
1086299904Sarybchik		 * as the client).  Of course, the client may
1087299904Sarybchik		 * be on the other side of a BOOTP gateway...
1088293887Sarybchik		 */
1089293887Sarybchik		ifr = getif(s, &dst);
1090293887Sarybchik		if (ifr) {
1091293887Sarybchik			struct sockaddr_in *sip;
1092293887Sarybchik			sip = (struct sockaddr_in *) &(ifr->ifr_addr);
1093293887Sarybchik			siaddr = sip->sin_addr;
1094293887Sarybchik		} else {
1095293887Sarybchik			/* Just use my "official" IP address. */
1096293887Sarybchik			siaddr = my_ip_addr;
1097293887Sarybchik		}
1098293887Sarybchik
1099293887Sarybchik		/* XXX - No need to set bp_giaddr here. */
1100300009Sarybchik
1101300009Sarybchik		/* Finally, set the server address field. */
1102293887Sarybchik		bp->bp_siaddr = siaddr;
1103300009Sarybchik	}
1104293887Sarybchik	/* Set up socket address for send. */
1105293887Sarybchik	send_addr.sin_family = AF_INET;
1106293887Sarybchik	send_addr.sin_port = htons(port);
1107293887Sarybchik	send_addr.sin_addr = dst;
1108293887Sarybchik
1109293887Sarybchik	/* Send reply with same size packet as request used. */
1110293887Sarybchik	if (sendto(s, pktbuf, pktlen, 0,
1111293887Sarybchik			   (struct sockaddr *) &send_addr,
1112293887Sarybchik			   sizeof(send_addr)) < 0)
1113293887Sarybchik	{
1114293887Sarybchik		report(LOG_ERR, "sendto: %s", get_network_errmsg());
1115294079Sarybchik	}
1116294079Sarybchik} /* sendreply */
1117294079Sarybchik
1118294079Sarybchik
1119294079Sarybchik/* nmatch() - now in getif.c */
1120293887Sarybchik/* setarp() - now in hwaddr.c */
1121293887Sarybchik
1122293887Sarybchik
1123293887Sarybchik/*
1124293887Sarybchik * This call checks read access to a file.  It returns 0 if the file given
1125293887Sarybchik * by "path" exists and is publically readable.  A value of -1 is returned if
1126293887Sarybchik * access is not permitted or an error occurs.  Successful calls also
1127293734Sarybchik * return the file size in bytes using the long pointer "filesize".
1128293734Sarybchik *
1129293734Sarybchik * The read permission bit for "other" users is checked.  This bit must be
1130293734Sarybchik * set for tftpd(8) to allow clients to read the file.
1131293734Sarybchik */
1132
1133PRIVATE int
1134chk_access(path, filesize)
1135	char *path;
1136	int32 *filesize;
1137{
1138	struct stat st;
1139
1140	if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) {
1141		*filesize = (int32) st.st_size;
1142		return 0;
1143	} else {
1144		return -1;
1145	}
1146}
1147
1148
1149/*
1150 * Now in dumptab.c :
1151 *	dumptab()
1152 *	dump_host()
1153 *	list_ipaddresses()
1154 */
1155
1156#ifdef VEND_CMU
1157
1158/*
1159 * Insert the CMU "vendor" data for the host pointed to by "hp" into the
1160 * bootp packet pointed to by "bp".
1161 */
1162
1163PRIVATE void
1164dovend_cmu(bp, hp)
1165	struct bootp *bp;
1166	struct host *hp;
1167{
1168	struct cmu_vend *vendp;
1169	struct in_addr_list *taddr;
1170
1171	/*
1172	 * Initialize the entire vendor field to zeroes.
1173	 */
1174	bzero(bp->bp_vend, sizeof(bp->bp_vend));
1175
1176	/*
1177	 * Fill in vendor information. Subnet mask, default gateway,
1178	 * domain name server, ien name server, time server
1179	 */
1180	vendp = (struct cmu_vend *) bp->bp_vend;
1181	strcpy(vendp->v_magic, (char *)vm_cmu);
1182	if (hp->flags.subnet_mask) {
1183		(vendp->v_smask).s_addr = hp->subnet_mask.s_addr;
1184		(vendp->v_flags) |= VF_SMASK;
1185		if (hp->flags.gateway) {
1186			(vendp->v_dgate).s_addr = hp->gateway->addr->s_addr;
1187		}
1188	}
1189	if (hp->flags.domain_server) {
1190		taddr = hp->domain_server;
1191		if (taddr->addrcount > 0) {
1192			(vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr;
1193			if (taddr->addrcount > 1) {
1194				(vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr;
1195			}
1196		}
1197	}
1198	if (hp->flags.name_server) {
1199		taddr = hp->name_server;
1200		if (taddr->addrcount > 0) {
1201			(vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr;
1202			if (taddr->addrcount > 1) {
1203				(vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr;
1204			}
1205		}
1206	}
1207	if (hp->flags.time_server) {
1208		taddr = hp->time_server;
1209		if (taddr->addrcount > 0) {
1210			(vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr;
1211			if (taddr->addrcount > 1) {
1212				(vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr;
1213			}
1214		}
1215	}
1216	/* Log message now done by caller. */
1217} /* dovend_cmu */
1218
1219#endif /* VEND_CMU */
1220
1221
1222
1223/*
1224 * Insert the RFC1048 vendor data for the host pointed to by "hp" into the
1225 * bootp packet pointed to by "bp".
1226 */
1227#define	NEED(LEN, MSG) do \
1228	if (bytesleft < (LEN)) { \
1229		report(LOG_NOTICE, noroom, \
1230			   hp->hostname->string, MSG); \
1231		return; \
1232	} while (0)
1233PRIVATE void
1234dovend_rfc1048(bp, hp, bootsize)
1235	struct bootp *bp;
1236	struct host *hp;
1237	int32 bootsize;
1238{
1239	int bytesleft, len;
1240	byte *vp;
1241
1242	static char noroom[] = "%s: No room for \"%s\" option";
1243
1244	vp = bp->bp_vend;
1245
1246	if (hp->flags.msg_size) {
1247		pktlen = hp->msg_size;
1248	} else {
1249		/*
1250		 * If the request was longer than the official length, build
1251		 * a response of that same length where the additional length
1252		 * is assumed to be part of the bp_vend (options) area.
1253		 */
1254		if (pktlen > sizeof(*bp)) {
1255			if (debug > 1)
1256				report(LOG_INFO, "request message length=%d", pktlen);
1257		}
1258		/*
1259		 * Check whether the request contains the option:
1260		 * Maximum DHCP Message Size (RFC1533 sec. 9.8)
1261		 * and if so, override the response length with its value.
1262		 * This request must lie within the first BP_VEND_LEN
1263		 * bytes of the option space.
1264		 */
1265		{
1266			byte *p, *ep;
1267			byte tag, len;
1268			short msgsz = 0;
1269
1270			p = vp + 4;
1271			ep = p + BP_VEND_LEN - 4;
1272			while (p < ep) {
1273				tag = *p++;
1274				/* Check for tags with no data first. */
1275				if (tag == TAG_PAD)
1276					continue;
1277				if (tag == TAG_END)
1278					break;
1279				/* Now scan the length byte. */
1280				len = *p++;
1281				switch (tag) {
1282				case TAG_MAX_MSGSZ:
1283					if (len == 2) {
1284						bcopy(p, (char*)&msgsz, 2);
1285						msgsz = ntohs(msgsz);
1286					}
1287					break;
1288				case TAG_SUBNET_MASK:
1289					/* XXX - Should preserve this if given... */
1290					break;
1291				} /* swtich */
1292				p += len;
1293			}
1294
1295			if (msgsz > sizeof(*bp) + BP_MSG_OVERHEAD) {
1296				if (debug > 1)
1297					report(LOG_INFO, "request has DHCP msglen=%d", msgsz);
1298				pktlen = msgsz - BP_MSG_OVERHEAD;
1299			}
1300		}
1301	}
1302
1303	if (pktlen < sizeof(*bp)) {
1304		report(LOG_ERR, "invalid response length=%d", pktlen);
1305		pktlen = sizeof(*bp);
1306	}
1307	bytesleft = ((byte*)bp + pktlen) - vp;
1308	if (pktlen > sizeof(*bp)) {
1309		if (debug > 1)
1310			report(LOG_INFO, "extended reply, length=%d, options=%d",
1311				   pktlen, bytesleft);
1312	}
1313
1314	/* Copy in the magic cookie */
1315	bcopy(vm_rfc1048, vp, 4);
1316	vp += 4;
1317	bytesleft -= 4;
1318
1319	if (hp->flags.subnet_mask) {
1320		/* always enough room here. */
1321		*vp++ = TAG_SUBNET_MASK;/* -1 byte  */
1322		*vp++ = 4;				/* -1 byte  */
1323		insert_u_long(hp->subnet_mask.s_addr, &vp);	/* -4 bytes */
1324		bytesleft -= 6;			/* Fix real count */
1325		if (hp->flags.gateway) {
1326			(void) insert_ip(TAG_GATEWAY,
1327							 hp->gateway,
1328							 &vp, &bytesleft);
1329		}
1330	}
1331	if (hp->flags.bootsize) {
1332		/* always enough room here */
1333		bootsize = (hp->flags.bootsize_auto) ?
1334			((bootsize + 511) / 512) : (hp->bootsize);	/* Round up */
1335		*vp++ = TAG_BOOT_SIZE;
1336		*vp++ = 2;
1337		*vp++ = (byte) ((bootsize >> 8) & 0xFF);
1338		*vp++ = (byte) (bootsize & 0xFF);
1339		bytesleft -= 4;			/* Tag, length, and 16 bit blocksize */
1340	}
1341	/*
1342	 * This one is special: Remaining options go in the ext file.
1343	 * Only the subnet_mask, bootsize, and gateway should precede.
1344	 */
1345	if (hp->flags.exten_file) {
1346		/*
1347		 * Check for room for exten_file.  Add 3 to account for
1348		 * TAG_EXTEN_FILE, length, and TAG_END.
1349		 */
1350		len = strlen(hp->exten_file->string);
1351		NEED((len + 3), "ef");
1352		*vp++ = TAG_EXTEN_FILE;
1353		*vp++ = (byte) (len & 0xFF);
1354		bcopy(hp->exten_file->string, vp, len);
1355		vp += len;
1356		*vp++ = TAG_END;
1357		bytesleft -= len + 3;
1358		return;					/* no more options here. */
1359	}
1360	/*
1361	 * The remaining options are inserted by the following
1362	 * function (which is shared with bootpef.c).
1363	 * Keep back one byte for the TAG_END.
1364	 */
1365	len = dovend_rfc1497(hp, vp, bytesleft - 1);
1366	vp += len;
1367	bytesleft -= len;
1368
1369	/* There should be at least one byte left. */
1370	NEED(1, "(end)");
1371	*vp++ = TAG_END;
1372	bytesleft--;
1373
1374	/* Log message done by caller. */
1375	if (bytesleft > 0) {
1376		/*
1377		 * Zero out any remaining part of the vendor area.
1378		 */
1379		bzero(vp, bytesleft);
1380	}
1381} /* dovend_rfc1048 */
1382#undef	NEED
1383
1384
1385/*
1386 * Now in readfile.c:
1387 * 	hwlookcmp()
1388 *	iplookcmp()
1389 */
1390
1391/* haddrtoa() - now in hwaddr.c */
1392/*
1393 * Now in dovend.c:
1394 * insert_ip()
1395 * insert_generic()
1396 * insert_u_long()
1397 */
1398
1399/* get_errmsg() - now in report.c */
1400
1401/*
1402 * Local Variables:
1403 * tab-width: 4
1404 * c-indent-level: 4
1405 * c-argdecl-indent: 4
1406 * c-continued-statement-offset: 4
1407 * c-continued-brace-offset: -4
1408 * c-label-offset: -4
1409 * c-brace-offset: 0
1410 * End:
1411 */
1412