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