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