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