bootpd.c revision 1.1.1.1 1 1.1 gwr /************************************************************************
2 1.1 gwr Copyright 1988, 1991 by Carnegie Mellon University
3 1.1 gwr
4 1.1 gwr All Rights Reserved
5 1.1 gwr
6 1.1 gwr Permission to use, copy, modify, and distribute this software and its
7 1.1 gwr documentation for any purpose and without fee is hereby granted, provided
8 1.1 gwr that the above copyright notice appear in all copies and that both that
9 1.1 gwr copyright notice and this permission notice appear in supporting
10 1.1 gwr documentation, and that the name of Carnegie Mellon University not be used
11 1.1 gwr in advertising or publicity pertaining to distribution of the software
12 1.1 gwr without specific, written prior permission.
13 1.1 gwr
14 1.1 gwr CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
15 1.1 gwr SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
16 1.1 gwr IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
17 1.1 gwr DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
18 1.1 gwr PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
19 1.1 gwr ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20 1.1 gwr SOFTWARE.
21 1.1 gwr ************************************************************************/
22 1.1 gwr
23 1.1 gwr #ifndef lint
24 1.1 gwr static char rcsid[] = "$Id: bootpd.c,v 1.1.1.1 1994/06/27 21:25:49 gwr Exp $";
25 1.1 gwr #endif
26 1.1 gwr
27 1.1 gwr /*
28 1.1 gwr * BOOTP (bootstrap protocol) server daemon.
29 1.1 gwr *
30 1.1 gwr * Answers BOOTP request packets from booting client machines.
31 1.1 gwr * See [SRI-NIC]<RFC>RFC951.TXT for a description of the protocol.
32 1.1 gwr * See [SRI-NIC]<RFC>RFC1048.TXT for vendor-information extensions.
33 1.1 gwr * See RFC 1395 for option tags 14-17.
34 1.1 gwr * See accompanying man page -- bootpd.8
35 1.1 gwr *
36 1.1 gwr * HISTORY
37 1.1 gwr * See ./Changes
38 1.1 gwr *
39 1.1 gwr * BUGS
40 1.1 gwr * See ./ToDo
41 1.1 gwr */
42 1.1 gwr
43 1.1 gwr
44 1.1 gwr
46 1.1 gwr #include <sys/types.h>
47 1.1 gwr #include <sys/param.h>
48 1.1 gwr #include <sys/socket.h>
49 1.1 gwr #include <sys/ioctl.h>
50 1.1 gwr #include <sys/file.h>
51 1.1 gwr #include <sys/time.h>
52 1.1 gwr #include <sys/stat.h>
53 1.1 gwr
54 1.1 gwr #include <net/if.h>
55 1.1 gwr #include <netinet/in.h>
56 1.1 gwr #include <arpa/inet.h> /* inet_ntoa */
57 1.1 gwr
58 1.1 gwr #ifndef NO_UNISTD
59 1.1 gwr #include <unistd.h>
60 1.1 gwr #endif
61 1.1 gwr #include <stdlib.h>
62 1.1 gwr #include <signal.h>
63 1.1 gwr #include <stdio.h>
64 1.1 gwr #include <string.h>
65 1.1 gwr #include <errno.h>
66 1.1 gwr #include <ctype.h>
67 1.1 gwr #include <netdb.h>
68 1.1 gwr #include <syslog.h>
69 1.1 gwr #include <assert.h>
70 1.1 gwr
71 1.1 gwr #ifdef NO_SETSID
72 1.1 gwr # include <fcntl.h> /* for O_RDONLY, etc */
73 1.1 gwr #endif
74 1.1 gwr
75 1.1 gwr #ifdef SVR4
76 1.1 gwr /* Using sigset() avoids the need to re-arm each time. */
77 1.1 gwr #define signal sigset
78 1.1 gwr #endif
79 1.1 gwr
80 1.1 gwr #ifndef USE_BFUNCS
81 1.1 gwr # include <memory.h>
82 1.1 gwr /* Yes, memcpy is OK here (no overlapped copies). */
83 1.1 gwr # define bcopy(a,b,c) memcpy(b,a,c)
84 1.1 gwr # define bzero(p,l) memset(p,0,l)
85 1.1 gwr # define bcmp(a,b,c) memcmp(a,b,c)
86 1.1 gwr #endif
87 1.1 gwr
88 1.1 gwr #include "bootp.h"
89 1.1 gwr #include "hash.h"
90 1.1 gwr #include "hwaddr.h"
91 1.1 gwr #include "bootpd.h"
92 1.1 gwr #include "dovend.h"
93 1.1 gwr #include "getif.h"
94 1.1 gwr #include "readfile.h"
95 1.1 gwr #include "report.h"
96 1.1 gwr #include "tzone.h"
97 1.1 gwr #include "patchlevel.h"
98 1.1 gwr
99 1.1 gwr /* Local definitions: */
100 1.1 gwr #define MAXPKT (3*512) /* Maximum packet size */
101 1.1 gwr
102 1.1 gwr #ifndef CONFIG_FILE
103 1.1 gwr #define CONFIG_FILE "/etc/bootptab"
104 1.1 gwr #endif
105 1.1 gwr #ifndef DUMPTAB_FILE
106 1.1 gwr #define DUMPTAB_FILE "/tmp/bootpd.dump"
107 1.1 gwr #endif
108 1.1 gwr
109 1.1 gwr
110 1.1 gwr
112 1.1 gwr /*
113 1.1 gwr * Externals, forward declarations, and global variables
114 1.1 gwr */
115 1.1 gwr
116 1.1 gwr #ifdef __STDC__
117 1.1 gwr #define P(args) args
118 1.1 gwr #else
119 1.1 gwr #define P(args) ()
120 1.1 gwr #endif
121 1.1 gwr
122 1.1 gwr extern void dumptab P((char *));
123 1.1 gwr
124 1.1 gwr PRIVATE void catcher P((int));
125 1.1 gwr PRIVATE int chk_access P((char *, int32 *));
126 1.1 gwr #ifdef VEND_CMU
127 1.1 gwr PRIVATE void dovend_cmu P((struct bootp *, struct host *));
128 1.1 gwr #endif
129 1.1 gwr PRIVATE void dovend_rfc1048 P((struct bootp *, struct host *, int32));
130 1.1 gwr PRIVATE void handle_reply P((void));
131 1.1 gwr PRIVATE void handle_request P((void));
132 1.1 gwr PRIVATE void sendreply P((int forward, int32 dest_override));
133 1.1 gwr PRIVATE void usage P((void));
134 1.1 gwr
135 1.1 gwr #undef P
136 1.1 gwr
137 1.1 gwr /*
138 1.1 gwr * IP port numbers for client and server obtained from /etc/services
139 1.1 gwr */
140 1.1 gwr
141 1.1 gwr u_short bootps_port, bootpc_port;
142 1.1 gwr
143 1.1 gwr
144 1.1 gwr /*
145 1.1 gwr * Internet socket and interface config structures
146 1.1 gwr */
147 1.1 gwr
148 1.1 gwr struct sockaddr_in bind_addr; /* Listening */
149 1.1 gwr struct sockaddr_in recv_addr; /* Packet source */
150 1.1 gwr struct sockaddr_in send_addr; /* destination */
151 1.1 gwr
152 1.1 gwr
153 1.1 gwr /*
154 1.1 gwr * option defaults
155 1.1 gwr */
156 1.1 gwr int debug = 0; /* Debugging flag (level) */
157 1.1 gwr struct timeval actualtimeout =
158 1.1 gwr { /* fifteen minutes */
159 1.1 gwr 15 * 60L, /* tv_sec */
160 1.1 gwr 0 /* tv_usec */
161 1.1 gwr };
162 1.1 gwr
163 1.1 gwr /*
164 1.1 gwr * General
165 1.1 gwr */
166 1.1 gwr
167 1.1 gwr int s; /* Socket file descriptor */
168 1.1 gwr char *pktbuf; /* Receive packet buffer */
169 1.1 gwr int pktlen;
170 1.1 gwr char *progname;
171 1.1 gwr char *chdir_path;
172 1.1 gwr char hostname[MAXHOSTNAMELEN]; /* System host name */
173 1.1 gwr struct in_addr my_ip_addr;
174 1.1 gwr
175 1.1 gwr /* Flags set by signal catcher. */
176 1.1 gwr PRIVATE int do_readtab = 0;
177 1.1 gwr PRIVATE int do_dumptab = 0;
178 1.1 gwr
179 1.1 gwr /*
180 1.1 gwr * Globals below are associated with the bootp database file (bootptab).
181 1.1 gwr */
182 1.1 gwr
183 1.1 gwr char *bootptab = CONFIG_FILE;
184 1.1 gwr char *bootpd_dump = DUMPTAB_FILE;
185 1.1 gwr
186 1.1 gwr
187 1.1 gwr
189 1.1 gwr /*
190 1.1 gwr * Initialization such as command-line processing is done and then the
191 1.1 gwr * main server loop is started.
192 1.1 gwr */
193 1.1 gwr
194 1.1 gwr void
195 1.1 gwr main(argc, argv)
196 1.1 gwr int argc;
197 1.1 gwr char **argv;
198 1.1 gwr {
199 1.1 gwr struct timeval *timeout;
200 1.1 gwr struct bootp *bp;
201 1.1 gwr struct servent *servp;
202 1.1 gwr struct hostent *hep;
203 1.1 gwr char *stmp;
204 1.1 gwr int n, ba_len, ra_len;
205 1.1 gwr int nfound, readfds;
206 1.1 gwr int standalone;
207 1.1 gwr
208 1.1 gwr progname = strrchr(argv[0], '/');
209 1.1 gwr if (progname) progname++;
210 1.1 gwr else progname = argv[0];
211 1.1 gwr
212 1.1 gwr /*
213 1.1 gwr * Initialize logging.
214 1.1 gwr */
215 1.1 gwr report_init(0); /* uses progname */
216 1.1 gwr
217 1.1 gwr /*
218 1.1 gwr * Log startup
219 1.1 gwr */
220 1.1 gwr report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
221 1.1 gwr
222 1.1 gwr /* Debugging for compilers with struct padding. */
223 1.1 gwr assert(sizeof(struct bootp) == BP_MINPKTSZ);
224 1.1 gwr
225 1.1 gwr /* Get space for receiving packets and composing replies. */
226 1.1 gwr pktbuf = malloc(MAXPKT);
227 1.1 gwr if (!pktbuf) {
228 1.1 gwr report(LOG_ERR, "malloc failed");
229 1.1 gwr exit(1);
230 1.1 gwr }
231 1.1 gwr bp = (struct bootp *) pktbuf;
232 1.1 gwr
233 1.1 gwr /*
234 1.1 gwr * Check to see if a socket was passed to us from inetd.
235 1.1 gwr *
236 1.1 gwr * Use getsockname() to determine if descriptor 0 is indeed a socket
237 1.1 gwr * (and thus we are probably a child of inetd) or if it is instead
238 1.1 gwr * something else and we are running standalone.
239 1.1 gwr */
240 1.1 gwr s = 0;
241 1.1 gwr ba_len = sizeof(bind_addr);
242 1.1 gwr bzero((char *) &bind_addr, ba_len);
243 1.1 gwr errno = 0;
244 1.1 gwr standalone = TRUE;
245 1.1 gwr if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {
246 1.1 gwr /*
247 1.1 gwr * Descriptor 0 is a socket. Assume we are a child of inetd.
248 1.1 gwr */
249 1.1 gwr if (bind_addr.sin_family == AF_INET) {
250 1.1 gwr standalone = FALSE;
251 1.1 gwr bootps_port = ntohs(bind_addr.sin_port);
252 1.1 gwr } else {
253 1.1 gwr /* Some other type of socket? */
254 1.1 gwr report(LOG_ERR, "getsockname: not an INET socket");
255 1.1 gwr }
256 1.1 gwr }
257 1.1 gwr
258 1.1 gwr /*
259 1.1 gwr * Set defaults that might be changed by option switches.
260 1.1 gwr */
261 1.1 gwr stmp = NULL;
262 1.1 gwr timeout = &actualtimeout;
263 1.1 gwr
264 1.1 gwr /*
265 1.1 gwr * Read switches.
266 1.1 gwr */
267 1.1 gwr for (argc--, argv++; argc > 0; argc--, argv++) {
268 1.1 gwr if (argv[0][0] != '-')
269 1.1 gwr break;
270 1.1 gwr switch (argv[0][1]) {
271 1.1 gwr
272 1.1 gwr case 'c': /* chdir_path */
273 1.1 gwr if (argv[0][2]) {
274 1.1 gwr stmp = &(argv[0][2]);
275 1.1 gwr } else {
276 1.1 gwr argc--;
277 1.1 gwr argv++;
278 1.1 gwr stmp = argv[0];
279 1.1 gwr }
280 1.1 gwr if (!stmp || (stmp[0] != '/')) {
281 1.1 gwr fprintf(stderr,
282 1.1 gwr "bootpd: invalid chdir specification\n");
283 1.1 gwr break;
284 1.1 gwr }
285 1.1 gwr chdir_path = stmp;
286 1.1 gwr break;
287 1.1 gwr
288 1.1 gwr case 'd': /* debug level */
289 1.1 gwr if (argv[0][2]) {
290 1.1 gwr stmp = &(argv[0][2]);
291 1.1 gwr } else if (argv[1] && argv[1][0] == '-') {
292 1.1 gwr /*
293 1.1 gwr * Backwards-compatible behavior:
294 1.1 gwr * no parameter, so just increment the debug flag.
295 1.1 gwr */
296 1.1 gwr debug++;
297 1.1 gwr break;
298 1.1 gwr } else {
299 1.1 gwr argc--;
300 1.1 gwr argv++;
301 1.1 gwr stmp = argv[0];
302 1.1 gwr }
303 1.1 gwr if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
304 1.1 gwr fprintf(stderr,
305 1.1 gwr "%s: invalid debug level\n", progname);
306 1.1 gwr break;
307 1.1 gwr }
308 1.1 gwr debug = n;
309 1.1 gwr break;
310 1.1 gwr
311 1.1 gwr case 'h': /* override hostname */
312 1.1 gwr if (argv[0][2]) {
313 1.1 gwr stmp = &(argv[0][2]);
314 1.1 gwr } else {
315 1.1 gwr argc--;
316 1.1 gwr argv++;
317 1.1 gwr stmp = argv[0];
318 1.1 gwr }
319 1.1 gwr if (!stmp) {
320 1.1 gwr fprintf(stderr,
321 1.1 gwr "bootpd: missing hostname\n");
322 1.1 gwr break;
323 1.1 gwr }
324 1.1 gwr strncpy(hostname, stmp, sizeof(hostname)-1);
325 1.1 gwr break;
326 1.1 gwr
327 1.1 gwr case 'i': /* inetd mode */
328 1.1 gwr standalone = FALSE;
329 1.1 gwr break;
330 1.1 gwr
331 1.1 gwr case 's': /* standalone mode */
332 1.1 gwr standalone = TRUE;
333 1.1 gwr break;
334 1.1 gwr
335 1.1 gwr case 't': /* timeout */
336 1.1 gwr if (argv[0][2]) {
337 1.1 gwr stmp = &(argv[0][2]);
338 1.1 gwr } else {
339 1.1 gwr argc--;
340 1.1 gwr argv++;
341 1.1 gwr stmp = argv[0];
342 1.1 gwr }
343 1.1 gwr if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
344 1.1 gwr fprintf(stderr,
345 1.1 gwr "%s: invalid timeout specification\n", progname);
346 1.1 gwr break;
347 1.1 gwr }
348 1.1 gwr actualtimeout.tv_sec = (int32) (60 * n);
349 1.1 gwr /*
350 1.1 gwr * If the actual timeout is zero, pass a NULL pointer
351 1.1 gwr * to select so it blocks indefinitely, otherwise,
352 1.1 gwr * point to the actual timeout value.
353 1.1 gwr */
354 1.1 gwr timeout = (n > 0) ? &actualtimeout : NULL;
355 1.1 gwr break;
356 1.1 gwr
357 1.1 gwr default:
358 1.1 gwr fprintf(stderr, "%s: unknown switch: -%c\n",
359 1.1 gwr progname, argv[0][1]);
360 1.1 gwr usage();
361 1.1 gwr break;
362 1.1 gwr
363 1.1 gwr } /* switch */
364 1.1 gwr } /* for args */
365 1.1 gwr
366 1.1 gwr /*
367 1.1 gwr * Override default file names if specified on the command line.
368 1.1 gwr */
369 1.1 gwr if (argc > 0)
370 1.1 gwr bootptab = argv[0];
371 1.1 gwr
372 1.1 gwr if (argc > 1)
373 1.1 gwr bootpd_dump = argv[1];
374 1.1 gwr
375 1.1 gwr /*
376 1.1 gwr * Get my hostname and IP address.
377 1.1 gwr */
378 1.1 gwr if (hostname[0] == '\0') {
379 1.1 gwr if (gethostname(hostname, sizeof(hostname)) == -1) {
380 1.1 gwr fprintf(stderr, "bootpd: can't get hostname\n");
381 1.1 gwr exit(1);
382 1.1 gwr }
383 1.1 gwr }
384 1.1 gwr hep = gethostbyname(hostname);
385 1.1 gwr if (!hep) {
386 1.1 gwr fprintf(stderr, "Can not get my IP address\n");
387 1.1 gwr exit(1);
388 1.1 gwr }
389 1.1 gwr bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
390 1.1 gwr
391 1.1 gwr if (standalone) {
392 1.1 gwr /*
393 1.1 gwr * Go into background and disassociate from controlling terminal.
394 1.1 gwr */
395 1.1 gwr if (debug < 3) {
396 1.1 gwr if (fork())
397 1.1 gwr exit(0);
398 1.1 gwr #ifdef NO_SETSID
399 1.1 gwr setpgrp(0,0);
400 1.1 gwr #ifdef TIOCNOTTY
401 1.1 gwr n = open("/dev/tty", O_RDWR);
402 1.1 gwr if (n >= 0) {
403 1.1 gwr ioctl(n, TIOCNOTTY, (char *) 0);
404 1.1 gwr (void) close(n);
405 1.1 gwr }
406 1.1 gwr #endif /* TIOCNOTTY */
407 1.1 gwr #else /* SETSID */
408 1.1 gwr if (setsid() < 0)
409 1.1 gwr perror("setsid");
410 1.1 gwr #endif /* SETSID */
411 1.1 gwr } /* if debug < 3 */
412 1.1 gwr
413 1.1 gwr /*
414 1.1 gwr * Nuke any timeout value
415 1.1 gwr */
416 1.1 gwr timeout = NULL;
417 1.1 gwr
418 1.1 gwr } /* if standalone (1st) */
419 1.1 gwr
420 1.1 gwr /* Set the cwd (i.e. to /tftpboot) */
421 1.1 gwr if (chdir_path) {
422 1.1 gwr if (chdir(chdir_path) < 0)
423 1.1 gwr report(LOG_ERR, "%s: chdir failed", chdir_path);
424 1.1 gwr }
425 1.1 gwr
426 1.1 gwr /* Get the timezone. */
427 1.1 gwr tzone_init();
428 1.1 gwr
429 1.1 gwr /* Allocate hash tables. */
430 1.1 gwr rdtab_init();
431 1.1 gwr
432 1.1 gwr /*
433 1.1 gwr * Read the bootptab file.
434 1.1 gwr */
435 1.1 gwr readtab(1); /* force read */
436 1.1 gwr
437 1.1 gwr if (standalone) {
438 1.1 gwr
439 1.1 gwr /*
440 1.1 gwr * Create a socket.
441 1.1 gwr */
442 1.1 gwr if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
443 1.1 gwr report(LOG_ERR, "socket: %s", get_network_errmsg());
444 1.1 gwr exit(1);
445 1.1 gwr }
446 1.1 gwr
447 1.1 gwr /*
448 1.1 gwr * Get server's listening port number
449 1.1 gwr */
450 1.1 gwr servp = getservbyname("bootps", "udp");
451 1.1 gwr if (servp) {
452 1.1 gwr bootps_port = ntohs((u_short) servp->s_port);
453 1.1 gwr } else {
454 1.1 gwr bootps_port = (u_short) IPPORT_BOOTPS;
455 1.1 gwr report(LOG_ERR,
456 1.1 gwr "udp/bootps: unknown service -- assuming port %d",
457 1.1 gwr bootps_port);
458 1.1 gwr }
459 1.1 gwr
460 1.1 gwr /*
461 1.1 gwr * Bind socket to BOOTPS port.
462 1.1 gwr */
463 1.1 gwr bind_addr.sin_family = AF_INET;
464 1.1 gwr bind_addr.sin_addr.s_addr = INADDR_ANY;
465 1.1 gwr bind_addr.sin_port = htons(bootps_port);
466 1.1 gwr if (bind(s, (struct sockaddr *) &bind_addr,
467 1.1 gwr sizeof(bind_addr)) < 0)
468 1.1 gwr {
469 1.1 gwr report(LOG_ERR, "bind: %s", get_network_errmsg());
470 1.1 gwr exit(1);
471 1.1 gwr }
472 1.1 gwr } /* if standalone (2nd)*/
473 1.1 gwr
474 1.1 gwr /*
475 1.1 gwr * Get destination port number so we can reply to client
476 1.1 gwr */
477 1.1 gwr servp = getservbyname("bootpc", "udp");
478 1.1 gwr if (servp) {
479 1.1 gwr bootpc_port = ntohs(servp->s_port);
480 1.1 gwr } else {
481 1.1 gwr report(LOG_ERR,
482 1.1 gwr "udp/bootpc: unknown service -- assuming port %d",
483 1.1 gwr IPPORT_BOOTPC);
484 1.1 gwr bootpc_port = (u_short) IPPORT_BOOTPC;
485 1.1 gwr }
486 1.1 gwr
487 1.1 gwr /*
488 1.1 gwr * Set up signals to read or dump the table.
489 1.1 gwr */
490 1.1 gwr if ((int) signal(SIGHUP, catcher) < 0) {
491 1.1 gwr report(LOG_ERR, "signal: %s", get_errmsg());
492 1.1 gwr exit(1);
493 1.1 gwr }
494 1.1 gwr if ((int) signal(SIGUSR1, catcher) < 0) {
495 1.1 gwr report(LOG_ERR, "signal: %s", get_errmsg());
496 1.1 gwr exit(1);
497 1.1 gwr }
498 1.1 gwr
499 1.1 gwr /*
500 1.1 gwr * Process incoming requests.
501 1.1 gwr */
502 1.1 gwr for (;;) {
503 1.1 gwr readfds = 1 << s;
504 1.1 gwr nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL, timeout);
505 1.1 gwr if (nfound < 0) {
506 1.1 gwr if (errno != EINTR) {
507 1.1 gwr report(LOG_ERR, "select: %s", get_errmsg());
508 1.1 gwr }
509 1.1 gwr /*
510 1.1 gwr * Call readtab() or dumptab() here to avoid the
511 1.1 gwr * dangers of doing I/O from a signal handler.
512 1.1 gwr */
513 1.1 gwr if (do_readtab) {
514 1.1 gwr do_readtab = 0;
515 1.1 gwr readtab(1); /* force read */
516 1.1 gwr }
517 1.1 gwr if (do_dumptab) {
518 1.1 gwr do_dumptab = 0;
519 1.1 gwr dumptab(bootpd_dump);
520 1.1 gwr }
521 1.1 gwr continue;
522 1.1 gwr }
523 1.1 gwr if (!(readfds & (1 << s))) {
524 1.1 gwr if (debug > 1)
525 1.1 gwr report(LOG_INFO, "exiting after %ld minutes of inactivity",
526 1.1 gwr actualtimeout.tv_sec / 60);
527 1.1 gwr exit(0);
528 1.1 gwr }
529 1.1 gwr ra_len = sizeof(recv_addr);
530 1.1 gwr n = recvfrom(s, pktbuf, MAXPKT, 0,
531 1.1 gwr (struct sockaddr *) &recv_addr, &ra_len);
532 1.1 gwr if (n <= 0) {
533 1.1 gwr continue;
534 1.1 gwr }
535 1.1 gwr if (debug > 1) {
536 1.1 gwr report(LOG_INFO, "recvd pkt from IP addr %s",
537 1.1 gwr inet_ntoa(recv_addr.sin_addr));
538 1.1 gwr }
539 1.1 gwr if (n < sizeof(struct bootp)) {
540 1.1 gwr if (debug) {
541 1.1 gwr report(LOG_INFO, "received short packet");
542 1.1 gwr }
543 1.1 gwr continue;
544 1.1 gwr }
545 1.1 gwr pktlen = n;
546 1.1 gwr
547 1.1 gwr readtab(0); /* maybe re-read bootptab */
548 1.1 gwr
549 1.1 gwr switch (bp->bp_op) {
550 1.1 gwr case BOOTREQUEST:
551 1.1 gwr handle_request();
552 1.1 gwr break;
553 1.1 gwr case BOOTREPLY:
554 1.1 gwr handle_reply();
555 1.1 gwr break;
556 1.1 gwr }
557 1.1 gwr }
558 1.1 gwr }
559 1.1 gwr
560 1.1 gwr
561 1.1 gwr
563 1.1 gwr
564 1.1 gwr /*
565 1.1 gwr * Print "usage" message and exit
566 1.1 gwr */
567 1.1 gwr
568 1.1 gwr PRIVATE void
569 1.1 gwr usage()
570 1.1 gwr {
571 1.1 gwr fprintf(stderr,
572 1.1 gwr "usage: bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n");
573 1.1 gwr fprintf(stderr, "\t -c n\tset current directory\n");
574 1.1 gwr fprintf(stderr, "\t -d n\tset debug level\n");
575 1.1 gwr fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n");
576 1.1 gwr fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n");
577 1.1 gwr fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n");
578 1.1 gwr exit(1);
579 1.1 gwr }
580 1.1 gwr
581 1.1 gwr /* Signal catchers */
582 1.1 gwr PRIVATE void
583 1.1 gwr catcher(sig)
584 1.1 gwr int sig;
585 1.1 gwr {
586 1.1 gwr if (sig == SIGHUP)
587 1.1 gwr do_readtab = 1;
588 1.1 gwr if (sig == SIGUSR1)
589 1.1 gwr do_dumptab = 1;
590 1.1 gwr #ifdef SYSV
591 1.1 gwr /* For older "System V" derivatives with no sigset(). */
592 1.1 gwr /* XXX - Should just do it the POSIX way (sigaction). */
593 1.1 gwr signal(sig, catcher);
594 1.1 gwr #endif
595 1.1 gwr }
596 1.1 gwr
597 1.1 gwr
598 1.1 gwr
600 1.1 gwr /*
601 1.1 gwr * Process BOOTREQUEST packet.
602 1.1 gwr *
603 1.1 gwr * Note: This version of the bootpd.c server never forwards
604 1.1 gwr * a request to another server. That is the job of a gateway
605 1.1 gwr * program such as the "bootpgw" program included here.
606 1.1 gwr *
607 1.1 gwr * (Also this version does not interpret the hostname field of
608 1.1 gwr * the request packet; it COULD do a name->address lookup and
609 1.1 gwr * forward the request there.)
610 1.1 gwr */
611 1.1 gwr PRIVATE void
612 1.1 gwr handle_request()
613 1.1 gwr {
614 1.1 gwr struct bootp *bp = (struct bootp *) pktbuf;
615 1.1 gwr struct host *hp = NULL;
616 1.1 gwr struct host dummyhost;
617 1.1 gwr int32 bootsize = 0;
618 1.1 gwr unsigned hlen, hashcode;
619 1.1 gwr int32 dest;
620 1.1 gwr #ifdef CHECK_FILE_ACCESS
621 1.1 gwr char realpath[1024];
622 1.1 gwr char *path;
623 1.1 gwr int n;
624 1.1 gwr #endif
625 1.1 gwr
626 1.1 gwr /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
627 1.1 gwr
628 1.1 gwr /*
629 1.1 gwr * If the servername field is set, compare it against us.
630 1.1 gwr * If we're not being addressed, ignore this request.
631 1.1 gwr * If the server name field is null, throw in our name.
632 1.1 gwr */
633 1.1 gwr if (strlen(bp->bp_sname)) {
634 1.1 gwr if (strcmp(bp->bp_sname, hostname)) {
635 1.1 gwr if (debug)
636 1.1 gwr report(LOG_INFO, "\
637 1.1 gwr ignoring request for server %s from client at %s address %s",
638 1.1 gwr bp->bp_sname, netname(bp->bp_htype),
639 1.1 gwr haddrtoa(bp->bp_chaddr, bp->bp_hlen));
640 1.1 gwr /* XXX - Is it correct to ignore such a request? -gwr */
641 1.1 gwr return;
642 1.1 gwr }
643 1.1 gwr } else {
644 1.1 gwr strcpy(bp->bp_sname, hostname);
645 1.1 gwr }
646 1.1 gwr
647 1.1 gwr bp->bp_op = BOOTREPLY;
648 1.1 gwr if (bp->bp_ciaddr.s_addr == 0) {
649 1.1 gwr /*
650 1.1 gwr * client doesnt know his IP address,
651 1.1 gwr * search by hardware address.
652 1.1 gwr */
653 1.1 gwr if (debug > 1) {
654 1.1 gwr report(LOG_INFO, "request from %s address %s",
655 1.1 gwr netname(bp->bp_htype),
656 1.1 gwr haddrtoa(bp->bp_chaddr, bp->bp_hlen));
657 1.1 gwr }
658 1.1 gwr hlen = haddrlength(bp->bp_htype);
659 1.1 gwr if (hlen != bp->bp_hlen) {
660 1.1 gwr report(LOG_NOTICE, "bad addr len from from %s address %s",
661 1.1 gwr netname(bp->bp_htype),
662 1.1 gwr haddrtoa(bp->bp_chaddr, hlen));
663 1.1 gwr }
664 1.1 gwr dummyhost.htype = bp->bp_htype;
665 1.1 gwr bcopy(bp->bp_chaddr, dummyhost.haddr, hlen);
666 1.1 gwr hashcode = hash_HashFunction(bp->bp_chaddr, hlen);
667 1.1 gwr hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp,
668 1.1 gwr &dummyhost);
669 1.1 gwr if (hp == NULL &&
670 1.1 gwr bp->bp_htype == HTYPE_IEEE802)
671 1.1 gwr {
672 1.1 gwr /* Try again with address in "canonical" form. */
673 1.1 gwr haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen);
674 1.1 gwr if (debug > 1) {
675 1.1 gwr report(LOG_INFO, "\
676 1.1 gwr HW addr type is IEEE 802. convert to %s and check again\n",
677 1.1 gwr haddrtoa(dummyhost.haddr, bp->bp_hlen));
678 1.1 gwr }
679 1.1 gwr hashcode = hash_HashFunction(dummyhost.haddr, hlen);
680 1.1 gwr hp = (struct host *) hash_Lookup(hwhashtable, hashcode,
681 1.1 gwr hwlookcmp, &dummyhost);
682 1.1 gwr }
683 1.1 gwr if (hp == NULL) {
684 1.1 gwr /*
685 1.1 gwr * XXX - Add dynamic IP address assignment?
686 1.1 gwr */
687 1.1 gwr if (debug > 1)
688 1.1 gwr report(LOG_INFO, "unknown client %s address %s",
689 1.1 gwr netname(bp->bp_htype),
690 1.1 gwr haddrtoa(bp->bp_chaddr, bp->bp_hlen));
691 1.1 gwr return; /* not found */
692 1.1 gwr }
693 1.1 gwr (bp->bp_yiaddr).s_addr = hp->iaddr.s_addr;
694 1.1 gwr
695 1.1 gwr } else {
696 1.1 gwr
697 1.1 gwr /*
698 1.1 gwr * search by IP address.
699 1.1 gwr */
700 1.1 gwr if (debug > 1) {
701 1.1 gwr report(LOG_INFO, "request from IP addr %s",
702 1.1 gwr inet_ntoa(bp->bp_ciaddr));
703 1.1 gwr }
704 1.1 gwr dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr;
705 1.1 gwr hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4);
706 1.1 gwr hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp,
707 1.1 gwr &dummyhost);
708 1.1 gwr if (hp == NULL) {
709 1.1 gwr if (debug > 1) {
710 1.1 gwr report(LOG_NOTICE, "IP address not found: %s",
711 1.1 gwr inet_ntoa(bp->bp_ciaddr));
712 1.1 gwr }
713 1.1 gwr return;
714 1.1 gwr }
715 1.1 gwr }
716 1.1 gwr
717 1.1 gwr if (debug) {
718 1.1 gwr report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr),
719 1.1 gwr hp->hostname->string);
720 1.1 gwr }
721 1.1 gwr
722 1.1 gwr #ifdef YORK_EX_OPTION
723 1.1 gwr /*
724 1.1 gwr * The need for the "ex" tag arose out of the need to empty
725 1.1 gwr * shared networked drives on diskless PCs. This solution is
726 1.1 gwr * not very clean but it does work fairly well.
727 1.1 gwr * Written by Edmund J. Sutcliffe <edmund (at) york.ac.uk>
728 1.1 gwr *
729 1.1 gwr * XXX - This could compromise security if a non-trusted user
730 1.1 gwr * managed to write an entry in the bootptab with :ex=trojan:
731 1.1 gwr * so I would leave this turned off unless you need it. -gwr
732 1.1 gwr */
733 1.1 gwr /* Run a program, passing the client name as a parameter. */
734 1.1 gwr if (hp->flags.exec_file) {
735 1.1 gwr char tst[100];
736 1.1 gwr /* XXX - Check string lengths? -gwr */
737 1.1 gwr strcpy (tst, hp->exec_file->string);
738 1.1 gwr strcat (tst, " ");
739 1.1 gwr strcat (tst, hp->hostname->string);
740 1.1 gwr strcat (tst, " &");
741 1.1 gwr if (debug)
742 1.1 gwr report(LOG_INFO, "executing %s", tst);
743 1.1 gwr system(tst); /* Hope this finishes soon... */
744 1.1 gwr }
745 1.1 gwr #endif /* YORK_EX_OPTION */
746 1.1 gwr
747 1.1 gwr /*
748 1.1 gwr * If a specific TFTP server address was specified in the bootptab file,
749 1.1 gwr * fill it in, otherwise zero it.
750 1.1 gwr * XXX - Rather than zero it, should it be the bootpd address? -gwr
751 1.1 gwr */
752 1.1 gwr (bp->bp_siaddr).s_addr = (hp->flags.bootserver) ?
753 1.1 gwr hp->bootserver.s_addr : 0L;
754 1.1 gwr
755 1.1 gwr #ifdef STANFORD_PROM_COMPAT
756 1.1 gwr /*
757 1.1 gwr * Stanford bootp PROMs (for a Sun?) have no way to leave
758 1.1 gwr * the boot file name field blank (because the boot file
759 1.1 gwr * name is automatically generated from some index).
760 1.1 gwr * As a work-around, this little hack allows those PROMs to
761 1.1 gwr * specify "sunboot14" with the same effect as a NULL name.
762 1.1 gwr * (The user specifies boot device 14 or some such magic.)
763 1.1 gwr */
764 1.1 gwr if (strcmp(bp->bp_file, "sunboot14") == 0)
765 1.1 gwr bp->bp_file[0] = '\0'; /* treat it as unspecified */
766 1.1 gwr #endif
767 1.1 gwr
768 1.1 gwr /*
769 1.1 gwr * Fill in the client's proper bootfile.
770 1.1 gwr *
771 1.1 gwr * If the client specifies an absolute path, try that file with a
772 1.1 gwr * ".host" suffix and then without. If the file cannot be found, no
773 1.1 gwr * reply is made at all.
774 1.1 gwr *
775 1.1 gwr * If the client specifies a null or relative file, use the following
776 1.1 gwr * table to determine the appropriate action:
777 1.1 gwr *
778 1.1 gwr * Homedir Bootfile Client's file
779 1.1 gwr * specified? specified? specification Action
780 1.1 gwr * -------------------------------------------------------------------
781 1.1 gwr * No No Null Send null filename
782 1.1 gwr * No No Relative Discard request
783 1.1 gwr * No Yes Null Send if absolute else null
784 1.1 gwr * No Yes Relative Discard request *XXX
785 1.1 gwr * Yes No Null Send null filename
786 1.1 gwr * Yes No Relative Lookup with ".host"
787 1.1 gwr * Yes Yes Null Send home/boot or bootfile
788 1.1 gwr * Yes Yes Relative Lookup with ".host" *XXX
789 1.1 gwr *
790 1.1 gwr */
791 1.1 gwr
792 1.1 gwr /*
793 1.1 gwr * XXX - I think the above policy is too complicated. When the
794 1.1 gwr * boot file is missing, it is not obvious why bootpd will not
795 1.1 gwr * respond to client requests. Define CHECK_FILE_ACCESS if you
796 1.1 gwr * want the original complicated policy, otherwise bootpd will
797 1.1 gwr * no longer check for existence of the boot file. -gwr
798 1.1 gwr */
799 1.1 gwr
800 1.1 gwr #ifdef CHECK_FILE_ACCESS
801 1.1 gwr
802 1.1 gwr if (hp->flags.tftpdir) {
803 1.1 gwr strcpy(realpath, hp->tftpdir->string);
804 1.1 gwr path = &realpath[strlen(realpath)];
805 1.1 gwr } else {
806 1.1 gwr path = realpath;
807 1.1 gwr }
808 1.1 gwr
809 1.1 gwr if (bp->bp_file[0]) {
810 1.1 gwr /*
811 1.1 gwr * The client specified a file.
812 1.1 gwr */
813 1.1 gwr if (bp->bp_file[0] == '/') {
814 1.1 gwr strcpy(path, bp->bp_file); /* Absolute pathname */
815 1.1 gwr } else {
816 1.1 gwr if (hp->flags.homedir) {
817 1.1 gwr strcpy(path, hp->homedir->string);
818 1.1 gwr strcat(path, "/");
819 1.1 gwr strcat(path, bp->bp_file);
820 1.1 gwr } else {
821 1.1 gwr report(LOG_NOTICE,
822 1.1 gwr "requested file \"%s\" not found: hd unspecified",
823 1.1 gwr bp->bp_file);
824 1.1 gwr return;
825 1.1 gwr }
826 1.1 gwr }
827 1.1 gwr } else {
828 1.1 gwr /*
829 1.1 gwr * No file specified by the client.
830 1.1 gwr */
831 1.1 gwr if (hp->flags.bootfile && ((hp->bootfile->string)[0] == '/')) {
832 1.1 gwr strcpy(path, hp->bootfile->string);
833 1.1 gwr } else if (hp->flags.homedir && hp->flags.bootfile) {
834 1.1 gwr strcpy(path, hp->homedir->string);
835 1.1 gwr strcat(path, "/");
836 1.1 gwr strcat(path, hp->bootfile->string);
837 1.1 gwr } else {
838 1.1 gwr bzero(bp->bp_file, sizeof(bp->bp_file));
839 1.1 gwr goto skip_file; /* Don't bother trying to access the file */
840 1.1 gwr }
841 1.1 gwr }
842 1.1 gwr
843 1.1 gwr /*
844 1.1 gwr * First try to find the file with a ".host" suffix
845 1.1 gwr */
846 1.1 gwr n = strlen(path);
847 1.1 gwr strcat(path, ".");
848 1.1 gwr strcat(path, hp->hostname->string);
849 1.1 gwr if (chk_access(realpath, &bootsize) < 0) {
850 1.1 gwr path[n] = 0; /* Try it without the suffix */
851 1.1 gwr if (chk_access(realpath, &bootsize) < 0) {
852 1.1 gwr if (bp->bp_file[0]) {
853 1.1 gwr /*
854 1.1 gwr * Client wanted specific file
855 1.1 gwr * and we didn't have it.
856 1.1 gwr */
857 1.1 gwr report(LOG_NOTICE,
858 1.1 gwr "requested file not found: \"%s\"", path);
859 1.1 gwr return;
860 1.1 gwr } else {
861 1.1 gwr /*
862 1.1 gwr * Client didn't ask for a specific file and we couldn't
863 1.1 gwr * access the default file, so just zero-out the bootfile
864 1.1 gwr * field in the packet and continue processing the reply.
865 1.1 gwr */
866 1.1 gwr bzero(bp->bp_file, sizeof(bp->bp_file));
867 1.1 gwr goto skip_file;
868 1.1 gwr }
869 1.1 gwr }
870 1.1 gwr }
871 1.1 gwr strcpy(bp->bp_file, path);
872 1.1 gwr
873 1.1 gwr skip_file:
874 1.1 gwr ;
875 1.1 gwr
876 1.1 gwr #else /* CHECK_FILE_ACCESS */
878 1.1 gwr
879 1.1 gwr /*
880 1.1 gwr * This implements a simple response policy, where bootpd
881 1.1 gwr * will fail to respond only if it knows nothing about
882 1.1 gwr * the client that sent the request. This plugs in the
883 1.1 gwr * boot file name but does not demand that it exist.
884 1.1 gwr *
885 1.1 gwr * If either the client or the server specifies a boot file,
886 1.1 gwr * build the path name for it. Server boot file preferred.
887 1.1 gwr */
888 1.1 gwr if (bp->bp_file[0] || hp->flags.bootfile) {
889 1.1 gwr char requested_file[BP_FILE_LEN];
890 1.1 gwr char *given_file;
891 1.1 gwr char *p = bp->bp_file;
892 1.1 gwr int space = BP_FILE_LEN;
893 1.1 gwr int n;
894 1.1 gwr
895 1.1 gwr /* Save client's requested file name. */
896 1.1 gwr strncpy(requested_file, bp->bp_file, BP_FILE_LEN);
897 1.1 gwr
898 1.1 gwr /* If tftpdir is set, insert it. */
899 1.1 gwr if (hp->flags.tftpdir) {
900 1.1 gwr n = strlen(hp->tftpdir->string);
901 1.1 gwr if ((n+1) >= space)
902 1.1 gwr goto nospc;
903 1.1 gwr strcpy(p, hp->tftpdir->string);
904 1.1 gwr p += n;
905 1.1 gwr space -= n;
906 1.1 gwr }
907 1.1 gwr
908 1.1 gwr /* If homedir is set, insert it. */
909 1.1 gwr if (hp->flags.homedir) {
910 1.1 gwr n = strlen(hp->homedir->string);
911 1.1 gwr if ((n+1) >= space)
912 1.1 gwr goto nospc;
913 1.1 gwr strcpy(p, hp->homedir->string);
914 1.1 gwr p += n;
915 1.1 gwr space -= n;
916 1.1 gwr }
917 1.1 gwr
918 1.1 gwr /* Finally, append the boot file name. */
919 1.1 gwr if (hp->flags.bootfile)
920 1.1 gwr given_file = hp->bootfile->string;
921 1.1 gwr else
922 1.1 gwr given_file = requested_file;
923 1.1 gwr assert(given_file);
924 1.1 gwr n = strlen(given_file);
925 1.1 gwr if ((n+1) >= space)
926 1.1 gwr goto nospc;
927 1.1 gwr strcpy(p, given_file);
928 1.1 gwr p += n;
929 1.1 gwr space -= n;
930 1.1 gwr *p = '\0';
931 1.1 gwr
932 1.1 gwr if (space <= 0) {
933 1.1 gwr nospc:
934 1.1 gwr report(LOG_ERR, "boot file path too long (%s)",
935 1.1 gwr hp->hostname->string);
936 1.1 gwr }
937 1.1 gwr }
938 1.1 gwr
939 1.1 gwr /* Determine boot file size if requested. */
940 1.1 gwr if (hp->flags.bootsize_auto) {
941 1.1 gwr if (bp->bp_file[0] == '\0' ||
942 1.1 gwr chk_access(bp->bp_file, &bootsize) < 0)
943 1.1 gwr {
944 1.1 gwr report(LOG_ERR, "can not determine boot file size for %s",
945 1.1 gwr hp->hostname->string);
946 1.1 gwr }
947 1.1 gwr }
948 1.1 gwr
949 1.1 gwr #endif /* CHECK_FILE_ACCESS */
950 1.1 gwr
951 1.1 gwr
953 1.1 gwr if (debug > 1) {
954 1.1 gwr report(LOG_INFO, "vendor magic field is %d.%d.%d.%d",
955 1.1 gwr (int) ((bp->bp_vend)[0]),
956 1.1 gwr (int) ((bp->bp_vend)[1]),
957 1.1 gwr (int) ((bp->bp_vend)[2]),
958 1.1 gwr (int) ((bp->bp_vend)[3]));
959 1.1 gwr }
960 1.1 gwr /*
961 1.1 gwr * If this host isn't set for automatic vendor info then copy the
962 1.1 gwr * specific cookie into the bootp packet, thus forcing a certain
963 1.1 gwr * reply format. Only force reply format if user specified it.
964 1.1 gwr */
965 1.1 gwr if (hp->flags.vm_cookie) {
966 1.1 gwr /* Slam in the user specified magic number. */
967 1.1 gwr bcopy(hp->vm_cookie, bp->bp_vend, 4);
968 1.1 gwr }
969 1.1 gwr /*
970 1.1 gwr * Figure out the format for the vendor-specific info.
971 1.1 gwr * Note that bp->bp_vend may have been set above.
972 1.1 gwr */
973 1.1 gwr if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) {
974 1.1 gwr /* RFC1048 conformant bootp client */
975 1.1 gwr dovend_rfc1048(bp, hp, bootsize);
976 1.1 gwr if (debug > 1) {
977 1.1 gwr report(LOG_INFO, "sending reply (with RFC1048 options)");
978 1.1 gwr }
979 1.1 gwr }
980 1.1 gwr #ifdef VEND_CMU
981 1.1 gwr else if (!bcmp(bp->bp_vend, vm_cmu, 4)) {
982 1.1 gwr dovend_cmu(bp, hp);
983 1.1 gwr if (debug > 1) {
984 1.1 gwr report(LOG_INFO, "sending reply (with CMU options)");
985 1.1 gwr }
986 1.1 gwr }
987 1.1 gwr #endif
988 1.1 gwr else {
989 1.1 gwr if (debug > 1) {
990 1.1 gwr report(LOG_INFO, "sending reply (with no options)");
991 1.1 gwr }
992 1.1 gwr }
993 1.1 gwr
994 1.1 gwr dest = (hp->flags.reply_addr) ?
995 1.1 gwr hp->reply_addr.s_addr : 0L;
996 1.1 gwr
997 1.1 gwr /* not forwarded */
998 1.1 gwr sendreply(0, dest);
999 1.1 gwr }
1000 1.1 gwr
1001 1.1 gwr
1002 1.1 gwr /*
1003 1.1 gwr * Process BOOTREPLY packet.
1004 1.1 gwr */
1005 1.1 gwr PRIVATE void
1006 1.1 gwr handle_reply()
1007 1.1 gwr {
1008 1.1 gwr if (debug) {
1009 1.1 gwr report(LOG_INFO, "processing boot reply");
1010 1.1 gwr }
1011 1.1 gwr /* forwarded, no destination override */
1012 1.1 gwr sendreply(1, 0);
1013 1.1 gwr }
1014 1.1 gwr
1015 1.1 gwr
1016 1.1 gwr /*
1017 1.1 gwr * Send a reply packet to the client. 'forward' flag is set if we are
1018 1.1 gwr * not the originator of this reply packet.
1019 1.1 gwr */
1020 1.1 gwr PRIVATE void
1021 1.1 gwr sendreply(forward, dst_override)
1022 1.1 gwr int forward;
1023 1.1 gwr int32 dst_override;
1024 1.1 gwr {
1025 1.1 gwr struct bootp *bp = (struct bootp *) pktbuf;
1026 1.1 gwr struct in_addr dst;
1027 1.1 gwr u_short port = bootpc_port;
1028 1.1 gwr #if 0
1029 1.1 gwr u_char canon_haddr[MAXHADDRLEN];
1030 1.1 gwr #endif
1031 1.1 gwr unsigned char *ha;
1032 1.1 gwr int len;
1033 1.1 gwr
1034 1.1 gwr /*
1035 1.1 gwr * If the destination address was specified explicitly
1036 1.1 gwr * (i.e. the broadcast address for HP compatiblity)
1037 1.1 gwr * then send the response to that address. Otherwise,
1038 1.1 gwr * act in accordance with RFC951:
1039 1.1 gwr * If the client IP address is specified, use that
1040 1.1 gwr * else if gateway IP address is specified, use that
1041 1.1 gwr * else make a temporary arp cache entry for the client's
1042 1.1 gwr * NEW IP/hardware address and use that.
1043 1.1 gwr */
1044 1.1 gwr if (dst_override) {
1045 1.1 gwr dst.s_addr = dst_override;
1046 1.1 gwr if (debug > 1) {
1047 1.1 gwr report(LOG_INFO, "reply address override: %s",
1048 1.1 gwr inet_ntoa(dst));
1049 1.1 gwr }
1050 1.1 gwr } else if (bp->bp_ciaddr.s_addr) {
1051 1.1 gwr dst = bp->bp_ciaddr;
1052 1.1 gwr } else if (bp->bp_giaddr.s_addr && forward == 0) {
1053 1.1 gwr dst = bp->bp_giaddr;
1054 1.1 gwr port = bootps_port;
1055 1.1 gwr if (debug > 1) {
1056 1.1 gwr report(LOG_INFO, "sending reply to gateway %s",
1057 1.1 gwr inet_ntoa(dst));
1058 1.1 gwr }
1059 1.1 gwr } else {
1060 1.1 gwr dst = bp->bp_yiaddr;
1061 1.1 gwr ha = bp->bp_chaddr;
1062 1.1 gwr len = bp->bp_hlen;
1063 1.1 gwr if (len > MAXHADDRLEN)
1064 1.1 gwr len = MAXHADDRLEN;
1065 1.1 gwr #if 0
1066 1.1 gwr /*
1067 1.1 gwr * XXX - Is this necessary, given that the HW address
1068 1.1 gwr * in bp_chaddr was left as the client provided it?
1069 1.1 gwr * Does some DEC version of TCP/IP need this? -gwr
1070 1.1 gwr */
1071 1.1 gwr if (bp->bp_htype == HTYPE_IEEE802) {
1072 1.1 gwr haddr_conv802(ha, canon_haddr, len);
1073 1.1 gwr ha = canon_haddr;
1074 1.1 gwr }
1075 1.1 gwr #endif
1076 1.1 gwr if (debug > 1)
1077 1.1 gwr report(LOG_INFO, "setarp %s - %s",
1078 1.1 gwr inet_ntoa(dst), haddrtoa(ha, len));
1079 1.1 gwr setarp(s, &dst, ha, len);
1080 1.1 gwr }
1081 1.1 gwr
1082 1.1 gwr if ((forward == 0) &&
1083 1.1 gwr (bp->bp_siaddr.s_addr == 0))
1084 1.1 gwr {
1085 1.1 gwr struct ifreq *ifr;
1086 1.1 gwr struct in_addr siaddr;
1087 1.1 gwr /*
1088 1.1 gwr * If we are originating this reply, we
1089 1.1 gwr * need to find our own interface address to
1090 1.1 gwr * put in the bp_siaddr field of the reply.
1091 1.1 gwr * If this server is multi-homed, pick the
1092 1.1 gwr * 'best' interface (the one on the same net
1093 1.1 gwr * as the client). Of course, the client may
1094 1.1 gwr * be on the other side of a BOOTP gateway...
1095 1.1 gwr */
1096 1.1 gwr ifr = getif(s, &dst);
1097 1.1 gwr if (ifr) {
1098 1.1 gwr struct sockaddr_in *sip;
1099 1.1 gwr sip = (struct sockaddr_in *) &(ifr->ifr_addr);
1100 1.1 gwr siaddr = sip->sin_addr;
1101 1.1 gwr } else {
1102 1.1 gwr /* Just use my "official" IP address. */
1103 1.1 gwr siaddr = my_ip_addr;
1104 1.1 gwr }
1105 1.1 gwr
1106 1.1 gwr /* XXX - No need to set bp_giaddr here. */
1107 1.1 gwr
1108 1.1 gwr /* Finally, set the server address field. */
1109 1.1 gwr bp->bp_siaddr = siaddr;
1110 1.1 gwr }
1111 1.1 gwr /* Set up socket address for send. */
1112 1.1 gwr send_addr.sin_family = AF_INET;
1113 1.1 gwr send_addr.sin_port = htons(port);
1114 1.1 gwr send_addr.sin_addr = dst;
1115 1.1 gwr
1116 1.1 gwr /* Send reply with same size packet as request used. */
1117 1.1 gwr if (sendto(s, pktbuf, pktlen, 0,
1118 1.1 gwr (struct sockaddr *) &send_addr,
1119 1.1 gwr sizeof(send_addr)) < 0)
1120 1.1 gwr {
1121 1.1 gwr report(LOG_ERR, "sendto: %s", get_network_errmsg());
1122 1.1 gwr }
1123 1.1 gwr } /* sendreply */
1124 1.1 gwr
1125 1.1 gwr
1127 1.1 gwr /* nmatch() - now in getif.c */
1128 1.1 gwr /* setarp() - now in hwaddr.c */
1129 1.1 gwr
1130 1.1 gwr
1131 1.1 gwr /*
1132 1.1 gwr * This call checks read access to a file. It returns 0 if the file given
1133 1.1 gwr * by "path" exists and is publically readable. A value of -1 is returned if
1134 1.1 gwr * access is not permitted or an error occurs. Successful calls also
1135 1.1 gwr * return the file size in bytes using the long pointer "filesize".
1136 1.1 gwr *
1137 1.1 gwr * The read permission bit for "other" users is checked. This bit must be
1138 1.1 gwr * set for tftpd(8) to allow clients to read the file.
1139 1.1 gwr */
1140 1.1 gwr
1141 1.1 gwr PRIVATE int
1142 1.1 gwr chk_access(path, filesize)
1143 1.1 gwr char *path;
1144 1.1 gwr int32 *filesize;
1145 1.1 gwr {
1146 1.1 gwr struct stat st;
1147 1.1 gwr
1148 1.1 gwr if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) {
1149 1.1 gwr *filesize = (int32) st.st_size;
1150 1.1 gwr return 0;
1151 1.1 gwr } else {
1152 1.1 gwr return -1;
1153 1.1 gwr }
1154 1.1 gwr }
1155 1.1 gwr
1156 1.1 gwr
1158 1.1 gwr /*
1159 1.1 gwr * Now in dumptab.c :
1160 1.1 gwr * dumptab()
1161 1.1 gwr * dump_host()
1162 1.1 gwr * list_ipaddresses()
1163 1.1 gwr */
1164 1.1 gwr
1165 1.1 gwr #ifdef VEND_CMU
1166 1.1 gwr
1167 1.1 gwr /*
1168 1.1 gwr * Insert the CMU "vendor" data for the host pointed to by "hp" into the
1169 1.1 gwr * bootp packet pointed to by "bp".
1170 1.1 gwr */
1171 1.1 gwr
1172 1.1 gwr PRIVATE void
1173 1.1 gwr dovend_cmu(bp, hp)
1174 1.1 gwr struct bootp *bp;
1175 1.1 gwr struct host *hp;
1176 1.1 gwr {
1177 1.1 gwr struct cmu_vend *vendp;
1178 1.1 gwr struct in_addr_list *taddr;
1179 1.1 gwr
1180 1.1 gwr /*
1181 1.1 gwr * Initialize the entire vendor field to zeroes.
1182 1.1 gwr */
1183 1.1 gwr bzero(bp->bp_vend, sizeof(bp->bp_vend));
1184 1.1 gwr
1185 1.1 gwr /*
1186 1.1 gwr * Fill in vendor information. Subnet mask, default gateway,
1187 1.1 gwr * domain name server, ien name server, time server
1188 1.1 gwr */
1189 1.1 gwr vendp = (struct cmu_vend *) bp->bp_vend;
1190 1.1 gwr strcpy(vendp->v_magic, (char *)vm_cmu);
1191 1.1 gwr if (hp->flags.subnet_mask) {
1192 1.1 gwr (vendp->v_smask).s_addr = hp->subnet_mask.s_addr;
1193 1.1 gwr (vendp->v_flags) |= VF_SMASK;
1194 1.1 gwr if (hp->flags.gateway) {
1195 1.1 gwr (vendp->v_dgate).s_addr = hp->gateway->addr->s_addr;
1196 1.1 gwr }
1197 1.1 gwr }
1198 1.1 gwr if (hp->flags.domain_server) {
1199 1.1 gwr taddr = hp->domain_server;
1200 1.1 gwr if (taddr->addrcount > 0) {
1201 1.1 gwr (vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr;
1202 1.1 gwr if (taddr->addrcount > 1) {
1203 1.1 gwr (vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr;
1204 1.1 gwr }
1205 1.1 gwr }
1206 1.1 gwr }
1207 1.1 gwr if (hp->flags.name_server) {
1208 1.1 gwr taddr = hp->name_server;
1209 1.1 gwr if (taddr->addrcount > 0) {
1210 1.1 gwr (vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr;
1211 1.1 gwr if (taddr->addrcount > 1) {
1212 1.1 gwr (vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr;
1213 1.1 gwr }
1214 1.1 gwr }
1215 1.1 gwr }
1216 1.1 gwr if (hp->flags.time_server) {
1217 1.1 gwr taddr = hp->time_server;
1218 1.1 gwr if (taddr->addrcount > 0) {
1219 1.1 gwr (vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr;
1220 1.1 gwr if (taddr->addrcount > 1) {
1221 1.1 gwr (vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr;
1222 1.1 gwr }
1223 1.1 gwr }
1224 1.1 gwr }
1225 1.1 gwr /* Log message now done by caller. */
1226 1.1 gwr } /* dovend_cmu */
1227 1.1 gwr
1228 1.1 gwr #endif /* VEND_CMU */
1229 1.1 gwr
1230 1.1 gwr
1232 1.1 gwr
1233 1.1 gwr /*
1234 1.1 gwr * Insert the RFC1048 vendor data for the host pointed to by "hp" into the
1235 1.1 gwr * bootp packet pointed to by "bp".
1236 1.1 gwr */
1237 1.1 gwr #define NEED(LEN, MSG) do \
1238 1.1 gwr if (bytesleft < (LEN)) { \
1239 1.1 gwr report(LOG_NOTICE, noroom, \
1240 1.1 gwr hp->hostname->string, MSG); \
1241 1.1 gwr return; \
1242 1.1 gwr } while (0)
1243 1.1 gwr PRIVATE void
1244 1.1 gwr dovend_rfc1048(bp, hp, bootsize)
1245 1.1 gwr struct bootp *bp;
1246 1.1 gwr struct host *hp;
1247 1.1 gwr int32 bootsize;
1248 1.1 gwr {
1249 1.1 gwr int bytesleft, len;
1250 1.1 gwr byte *vp;
1251 1.1 gwr char *tmpstr;
1252 1.1 gwr
1253 1.1 gwr static char noroom[] = "%s: No room for \"%s\" option";
1254 1.1 gwr
1255 1.1 gwr vp = bp->bp_vend;
1256 1.1 gwr bytesleft = sizeof(bp->bp_vend); /* Initial vendor area size */
1257 1.1 gwr bcopy(vm_rfc1048, vp, 4); /* Copy in the magic cookie */
1258 1.1 gwr vp += 4;
1259 1.1 gwr bytesleft -= 4;
1260 1.1 gwr
1261 1.1 gwr if (hp->flags.subnet_mask) {
1262 1.1 gwr /* always enough room here. */
1263 1.1 gwr *vp++ = TAG_SUBNET_MASK;/* -1 byte */
1264 1.1 gwr *vp++ = 4; /* -1 byte */
1265 1.1 gwr insert_u_long(hp->subnet_mask.s_addr, &vp); /* -4 bytes */
1266 1.1 gwr bytesleft -= 6; /* Fix real count */
1267 1.1 gwr if (hp->flags.gateway) {
1268 1.1 gwr (void) insert_ip(TAG_GATEWAY,
1269 1.1 gwr hp->gateway,
1270 1.1 gwr &vp, &bytesleft);
1271 1.1 gwr }
1272 1.1 gwr }
1273 1.1 gwr if (hp->flags.bootsize) {
1274 1.1 gwr /* always enough room here */
1275 1.1 gwr bootsize = (hp->flags.bootsize_auto) ?
1276 1.1 gwr ((bootsize + 511) / 512) : (hp->bootsize); /* Round up */
1277 1.1 gwr *vp++ = TAG_BOOT_SIZE;
1278 1.1 gwr *vp++ = 2;
1279 1.1 gwr *vp++ = (byte) ((bootsize >> 8) & 0xFF);
1280 1.1 gwr *vp++ = (byte) (bootsize & 0xFF);
1281 1.1 gwr bytesleft -= 4; /* Tag, length, and 16 bit blocksize */
1282 1.1 gwr }
1283 1.1 gwr /*
1284 1.1 gwr * This one is special: Remaining options go in the ext file.
1285 1.1 gwr * Only the subnet_mask, bootsize, and gateway should precede.
1286 1.1 gwr */
1287 1.1 gwr if (hp->flags.exten_file) {
1288 1.1 gwr /*
1289 1.1 gwr * Check for room for exten_file. Add 3 to account for
1290 1.1 gwr * TAG_EXTEN_FILE, length, and TAG_END.
1291 1.1 gwr */
1292 1.1 gwr len = strlen(hp->exten_file->string);
1293 1.1 gwr NEED((len + 3), "ef");
1294 1.1 gwr *vp++ = TAG_EXTEN_FILE;
1295 1.1 gwr *vp++ = (byte) (len & 0xFF);
1296 1.1 gwr bcopy(hp->exten_file->string, vp, len);
1297 1.1 gwr vp += len;
1298 1.1 gwr *vp++ = TAG_END;
1299 1.1 gwr bytesleft -= len + 3;
1300 1.1 gwr return; /* no more options here. */
1301 1.1 gwr }
1302 1.1 gwr /*
1303 1.1 gwr * The remaining options are inserted by the following
1304 1.1 gwr * function (which is shared with bootpef.c).
1305 1.1 gwr * Keep back one byte for the TAG_END.
1306 1.1 gwr */
1307 1.1 gwr len = dovend_rfc1497(hp, vp, bytesleft - 1);
1308 1.1 gwr vp += len;
1309 1.1 gwr bytesleft -= len;
1310 1.1 gwr
1311 1.1 gwr /* There should be at least one byte left. */
1312 1.1 gwr NEED(1, "(end)");
1313 1.1 gwr *vp++ = TAG_END;
1314 1.1 gwr bytesleft--;
1315 1.1 gwr
1316 1.1 gwr /* Log message done by caller. */
1317 1.1 gwr if (bytesleft > 0) {
1318 1.1 gwr /*
1319 1.1 gwr * Zero out any remaining part of the vendor area.
1320 1.1 gwr */
1321 1.1 gwr bzero(vp, bytesleft);
1322 1.1 gwr }
1323 1.1 gwr } /* dovend_rfc1048 */
1324 1.1 gwr #undef NEED
1325 1.1 gwr
1326 1.1 gwr
1328 1.1 gwr /*
1329 1.1 gwr * Now in readfile.c:
1330 1.1 gwr * hwlookcmp()
1331 1.1 gwr * iplookcmp()
1332 1.1 gwr */
1333 1.1 gwr
1334 1.1 gwr /* haddrtoa() - now in hwaddr.c */
1335 1.1 gwr /*
1336 1.1 gwr * Now in dovend.c:
1337 1.1 gwr * insert_ip()
1338 1.1 gwr * insert_generic()
1339 1.1 gwr * insert_u_long()
1340 1.1 gwr */
1341 1.1 gwr
1342 1.1 gwr /* get_errmsg() - now in report.c */
1343 1.1 gwr
1344 /*
1345 * Local Variables:
1346 * tab-width: 4
1347 * c-indent-level: 4
1348 * c-argdecl-indent: 4
1349 * c-continued-statement-offset: 4
1350 * c-continued-brace-offset: -4
1351 * c-label-offset: -4
1352 * c-brace-offset: 0
1353 * End:
1354 */
1355