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