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