bootpd.c revision 1.25 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.25 2014/03/29 18:23:00 apb 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 __dead 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 size_t clntpathmaxlen;
608 char *homedir, *bootfile;
609 int n;
610
611 /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
612
613 /*
614 * If the servername field is set, compare it against us.
615 * If we're not being addressed, ignore this request.
616 * If the server name field is null, throw in our name.
617 */
618 if (strlen(bp->bp_sname)) {
619 if (strcmp(bp->bp_sname, hostname)) {
620 if (debug)
621 report(LOG_INFO, "\
622 ignoring request for server %s from client at %s address %s",
623 bp->bp_sname, netname(bp->bp_htype),
624 haddrtoa(bp->bp_chaddr, bp->bp_hlen));
625 /* XXX - Is it correct to ignore such a request? -gwr */
626 return;
627 }
628 } else {
629 strlcpy(bp->bp_sname, hostname, sizeof(bp->bp_sname));
630 }
631
632 /* If it uses an unknown network type, ignore the request. */
633 if (bp->bp_htype >= hwinfocnt) {
634 if (debug)
635 report(LOG_INFO,
636 "Request with unknown network type %u",
637 bp->bp_htype);
638 return;
639 }
640
641 /* Convert the request into a reply. */
642 bp->bp_op = BOOTREPLY;
643 if (bp->bp_ciaddr.s_addr == 0) {
644 /*
645 * client doesnt know his IP address,
646 * search by hardware address.
647 */
648 if (debug > 1) {
649 report(LOG_INFO, "request from %s address %s",
650 netname(bp->bp_htype),
651 haddrtoa(bp->bp_chaddr, bp->bp_hlen));
652 }
653 hlen = haddrlength(bp->bp_htype);
654 if (hlen != bp->bp_hlen) {
655 report(LOG_NOTICE, "bad addr len from %s address %s",
656 netname(bp->bp_htype),
657 haddrtoa(bp->bp_chaddr, hlen));
658 }
659 dummyhost.htype = bp->bp_htype;
660 bcopy(bp->bp_chaddr, dummyhost.haddr, hlen);
661 hashcode = hash_HashFunction(bp->bp_chaddr, hlen);
662 hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp,
663 &dummyhost);
664 if (hp == NULL &&
665 bp->bp_htype == HTYPE_IEEE802)
666 {
667 /* Try again with address in "canonical" form. */
668 haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen);
669 if (debug > 1) {
670 report(LOG_INFO, "\
671 HW addr type is IEEE 802. convert to %s and check again\n",
672 haddrtoa(dummyhost.haddr, bp->bp_hlen));
673 }
674 hashcode = hash_HashFunction(dummyhost.haddr, hlen);
675 hp = (struct host *) hash_Lookup(hwhashtable, hashcode,
676 hwlookcmp, &dummyhost);
677 }
678 if (hp == NULL) {
679 /*
680 * XXX - Add dynamic IP address assignment?
681 */
682 if (debug > 1)
683 report(LOG_INFO, "unknown client %s address %s",
684 netname(bp->bp_htype),
685 haddrtoa(bp->bp_chaddr, bp->bp_hlen));
686 return; /* not found */
687 }
688 (bp->bp_yiaddr).s_addr = hp->iaddr.s_addr;
689
690 } else {
691
692 /*
693 * search by IP address.
694 */
695 if (debug > 1) {
696 report(LOG_INFO, "request from IP addr %s",
697 inet_ntoa(bp->bp_ciaddr));
698 }
699 dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr;
700 hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4);
701 hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp,
702 &dummyhost);
703 if (hp == NULL) {
704 if (debug > 1) {
705 report(LOG_NOTICE, "IP address not found: %s",
706 inet_ntoa(bp->bp_ciaddr));
707 }
708 return;
709 }
710 }
711
712 if (debug) {
713 report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr),
714 hp->hostname->string);
715 }
716
717 /*
718 * If there is a response delay threshold, ignore requests
719 * with a timestamp lower than the threshold.
720 */
721 if (hp->flags.min_wait) {
722 u_int32 t = (u_int32) ntohs(bp->bp_secs);
723 if (t < hp->min_wait) {
724 if (debug > 1)
725 report(LOG_INFO,
726 "ignoring request due to timestamp (%d < %d)",
727 t, hp->min_wait);
728 return;
729 }
730 }
731
732 #ifdef YORK_EX_OPTION
733 /*
734 * The need for the "ex" tag arose out of the need to empty
735 * shared networked drives on diskless PCs. This solution is
736 * not very clean but it does work fairly well.
737 * Written by Edmund J. Sutcliffe <edmund (at) york.ac.uk>
738 *
739 * XXX - This could compromise security if a non-trusted user
740 * managed to write an entry in the bootptab with :ex=trojan:
741 * so I would leave this turned off unless you need it. -gwr
742 */
743 /* Run a program, passing the client name as a parameter. */
744 if (hp->flags.exec_file) {
745 char tst[100];
746 /* XXX - Check string lengths? -gwr */
747 strlcpy(tst, hp->exec_file->string, sizeof(tst));
748 strlcat(tst, " ", sizeof(tst));
749 strlcat(tst, hp->hostname->string, sizeof(tst));
750 strlcat(tst, " &", sizeof(tst));
751 if (debug)
752 report(LOG_INFO, "executing %s", tst);
753 system(tst); /* Hope this finishes soon... */
754 }
755 #endif /* YORK_EX_OPTION */
756
757 /*
758 * If a specific TFTP server address was specified in the bootptab file,
759 * fill it in, otherwise zero it.
760 * XXX - Rather than zero it, should it be the bootpd address? -gwr
761 */
762 (bp->bp_siaddr).s_addr = (hp->flags.bootserver) ?
763 hp->bootserver.s_addr : 0L;
764
765 #ifdef STANFORD_PROM_COMPAT
766 /*
767 * Stanford bootp PROMs (for a Sun?) have no way to leave
768 * the boot file name field blank (because the boot file
769 * name is automatically generated from some index).
770 * As a work-around, this little hack allows those PROMs to
771 * specify "sunboot14" with the same effect as a NULL name.
772 * (The user specifies boot device 14 or some such magic.)
773 */
774 if (strcmp(bp->bp_file, "sunboot14") == 0)
775 bp->bp_file[0] = '\0'; /* treat it as unspecified */
776 #endif
777
778 /*
779 * Fill in the client's proper bootfile.
780 *
781 * If the client specifies an absolute path, try that file with a
782 * ".host" suffix and then without. If the file cannot be found, no
783 * reply is made at all.
784 *
785 * If the client specifies a null or relative file, use the following
786 * table to determine the appropriate action:
787 *
788 * Homedir Bootfile Client's file
789 * specified? specified? specification Action
790 * -------------------------------------------------------------------
791 * No No Null Send null filename
792 * No No Relative Discard request
793 * No Yes Null Send if absolute else null
794 * No Yes Relative Discard request *XXX
795 * Yes No Null Send null filename
796 * Yes No Relative Lookup with ".host"
797 * Yes Yes Null Send home/boot or bootfile
798 * Yes Yes Relative Lookup with ".host" *XXX
799 *
800 */
801
802 /*
803 * XXX - I don't like the policy of ignoring a client when the
804 * boot file is not accessible. The TFTP server might not be
805 * running on the same machine as the BOOTP server, in which
806 * case checking accessibility of the boot file is pointless.
807 *
808 * Therefore, file accessibility is now demanded ONLY if you
809 * define CHECK_FILE_ACCESS in the Makefile options. -gwr
810 */
811
812 /*
813 * The "real" path is as seen by the BOOTP daemon on this
814 * machine, while the client path is relative to the TFTP
815 * daemon chroot directory (i.e. /tftpboot).
816 */
817 if (hp->flags.tftpdir) {
818 strlcpy(lrealpath, hp->tftpdir->string, sizeof(lrealpath));
819 clntpath = &lrealpath[strlen(lrealpath)];
820 clntpathmaxlen = sizeof(lrealpath) + lrealpath - clntpath;
821 } else {
822 lrealpath[0] = '\0';
823 clntpath = lrealpath;
824 clntpathmaxlen = sizeof(lrealpath)
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 char *t;
834
835 homedir = bp->bp_file;
836
837 /* make sure that the file is nul terminated */
838 for (t = homedir; t - homedir < BP_FILE_LEN; t++)
839 if (*t == '\0')
840 break;
841 if (t - homedir < BP_FILE_LEN) {
842 report(LOG_INFO, "requested path length > BP_FILE_LEN file = \"%s\", nul terminating", homedir);
843 homedir[BP_FILE_LEN - 1] = '\0';
844 }
845
846 bootfile = strrchr(homedir, '/');
847 if (bootfile) {
848 if (homedir == bootfile)
849 homedir = NULL;
850 *bootfile++ = '\0';
851 } else {
852 /* no "/" in the string */
853 bootfile = homedir;
854 homedir = NULL;
855 }
856 if (debug > 2) {
857 report(LOG_INFO, "requested path=\"%s\" file=\"%s\"",
858 (homedir) ? homedir : "",
859 (bootfile) ? bootfile : "");
860 }
861 }
862
863 /*
864 * Specifications in bootptab override client requested values.
865 */
866 if (hp->flags.homedir)
867 homedir = hp->homedir->string;
868 if (hp->flags.bootfile)
869 bootfile = hp->bootfile->string;
870
871 /*
872 * Construct bootfile path.
873 */
874 if (homedir) {
875 if (homedir[0] != '/')
876 strlcat(lrealpath, "/", sizeof(lrealpath));
877 strlcat(lrealpath, homedir, sizeof(lrealpath));
878 homedir = NULL;
879 }
880 if (bootfile) {
881 if (bootfile[0] != '/') {
882 strlcat(lrealpath, "/", sizeof(lrealpath));
883 lrealpath[sizeof(lrealpath) - 1] = '\0';
884 }
885 strlcat(lrealpath, bootfile, sizeof(lrealpath));
886 lrealpath[sizeof(lrealpath) - 1] = '\0';
887 bootfile = NULL;
888 }
889
890 /*
891 * First try to find the file with a ".host" suffix
892 */
893 n = strlen(clntpath);
894 strlcat(clntpath, ".", clntpathmaxlen);
895 strlcat(clntpath, hp->hostname->string, clntpathmaxlen);
896 if (chk_access(lrealpath, &bootsize) < 0) {
897 clntpath[n] = 0; /* Try it without the suffix */
898 if (chk_access(lrealpath, &bootsize) < 0) {
899 /* neither "file.host" nor "file" was found */
900 #ifdef CHECK_FILE_ACCESS
901
902 if (bp->bp_file[0]) {
903 /*
904 * Client wanted specific file
905 * and we didn't have it.
906 */
907 report(LOG_NOTICE,
908 "requested file not found: \"%s\"", clntpath);
909 return;
910 }
911 /*
912 * Client didn't ask for a specific file and we couldn't
913 * access the default file, so just zero-out the bootfile
914 * field in the packet and continue processing the reply.
915 */
916 bzero(bp->bp_file, sizeof(bp->bp_file));
917 goto null_file_name;
918
919 #else /* CHECK_FILE_ACCESS */
920
921 /* Complain only if boot file size was needed. */
922 if (hp->flags.bootsize_auto) {
923 report(LOG_ERR, "can not determine size of file \"%s\"",
924 clntpath);
925 }
926
927 #endif /* CHECK_FILE_ACCESS */
928 }
929 }
930 strlcpy(bp->bp_file, clntpath, sizeof(bp->bp_file));
931 if (debug > 2)
932 report(LOG_INFO, "bootfile=\"%s\"", clntpath);
933
934 #ifdef CHECK_FILE_ACCESS
935 null_file_name:
936 #endif /* CHECK_FILE_ACCESS */
937
938
939 /*
941 * Handle vendor options based on magic number.
942 */
943
944 if (debug > 1) {
945 report(LOG_INFO, "vendor magic field is %d.%d.%d.%d",
946 (int) ((bp->bp_vend)[0]),
947 (int) ((bp->bp_vend)[1]),
948 (int) ((bp->bp_vend)[2]),
949 (int) ((bp->bp_vend)[3]));
950 }
951 /*
952 * If this host isn't set for automatic vendor info then copy the
953 * specific cookie into the bootp packet, thus forcing a certain
954 * reply format. Only force reply format if user specified it.
955 */
956 if (hp->flags.vm_cookie) {
957 /* Slam in the user specified magic number. */
958 bcopy(hp->vm_cookie, bp->bp_vend, 4);
959 }
960 /*
961 * Figure out the format for the vendor-specific info.
962 * Note that bp->bp_vend may have been set above.
963 */
964 if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) {
965 /* RFC1048 conformant bootp client */
966 dovend_rfc1048(bp, hp, bootsize);
967 if (debug > 1) {
968 report(LOG_INFO, "sending reply (with RFC1048 options)");
969 }
970 }
971 #ifdef VEND_CMU
972 else if (!bcmp(bp->bp_vend, vm_cmu, 4)) {
973 dovend_cmu(bp, hp);
974 if (debug > 1) {
975 report(LOG_INFO, "sending reply (with CMU options)");
976 }
977 }
978 #endif
979 else {
980 if (debug > 1) {
981 report(LOG_INFO, "sending reply (with no options)");
982 }
983 }
984
985 dest = (hp->flags.reply_addr) ?
986 hp->reply_addr.s_addr : 0L;
987
988 /* not forwarded */
989 sendreply(0, dest);
990 }
991
992
993 /*
994 * Process BOOTREPLY packet.
995 */
996 PRIVATE void
997 handle_reply(void)
998 {
999 if (debug) {
1000 report(LOG_INFO, "processing boot reply");
1001 }
1002 /* forwarded, no destination override */
1003 sendreply(1, 0);
1004 }
1005
1006
1007 /*
1008 * Send a reply packet to the client. 'forward' flag is set if we are
1009 * not the originator of this reply packet.
1010 */
1011 PRIVATE void
1012 sendreply(int forward, int32 dst_override)
1013 {
1014 struct bootp *bp = (struct bootp *) pktbuf;
1015 struct in_addr dst;
1016 u_short port = bootpc_port;
1017 unsigned char *ha;
1018 int len;
1019
1020 /*
1021 * XXX - Should honor bp_flags "broadcast" bit here.
1022 * Temporary workaround: use the :ra=ADDR: option to
1023 * set the reply address to the broadcast address.
1024 */
1025
1026 /*
1027 * If the destination address was specified explicitly
1028 * (i.e. the broadcast address for HP compatibility)
1029 * then send the response to that address. Otherwise,
1030 * act in accordance with RFC951:
1031 * If the client IP address is specified, use that
1032 * else if gateway IP address is specified, use that
1033 * else make a temporary arp cache entry for the client's
1034 * NEW IP/hardware address and use that.
1035 */
1036 if (dst_override) {
1037 dst.s_addr = dst_override;
1038 if (debug > 1) {
1039 report(LOG_INFO, "reply address override: %s",
1040 inet_ntoa(dst));
1041 }
1042 } else if (bp->bp_ciaddr.s_addr) {
1043 dst = bp->bp_ciaddr;
1044 } else if (bp->bp_giaddr.s_addr && forward == 0) {
1045 dst = bp->bp_giaddr;
1046 port = bootps_port;
1047 if (debug > 1) {
1048 report(LOG_INFO, "sending reply to gateway %s",
1049 inet_ntoa(dst));
1050 }
1051 } else {
1052 dst = bp->bp_yiaddr;
1053 ha = bp->bp_chaddr;
1054 len = bp->bp_hlen;
1055 if (len > MAXHADDRLEN)
1056 len = MAXHADDRLEN;
1057
1058 if (debug > 1)
1059 report(LOG_INFO, "setarp %s - %s",
1060 inet_ntoa(dst), haddrtoa(ha, len));
1061 setarp(s, &dst, ha, len);
1062 }
1063
1064 if ((forward == 0) &&
1065 (bp->bp_siaddr.s_addr == 0))
1066 {
1067 struct ifreq *ifr;
1068 struct in_addr siaddr;
1069 /*
1070 * If we are originating this reply, we
1071 * need to find our own interface address to
1072 * put in the bp_siaddr field of the reply.
1073 * If this server is multi-homed, pick the
1074 * 'best' interface (the one on the same net
1075 * as the client). Of course, the client may
1076 * be on the other side of a BOOTP gateway...
1077 */
1078 ifr = getif(s, &dst);
1079 if (ifr) {
1080 struct sockaddr_in *sip;
1081 sip = (struct sockaddr_in *) &(ifr->ifr_addr);
1082 siaddr = sip->sin_addr;
1083 } else {
1084 /* Just use my "official" IP address. */
1085 siaddr = my_ip_addr;
1086 }
1087
1088 /* XXX - No need to set bp_giaddr here. */
1089
1090 /* Finally, set the server address field. */
1091 bp->bp_siaddr = siaddr;
1092 }
1093 /* Set up socket address for send. */
1094 send_addr.sin_family = AF_INET;
1095 send_addr.sin_port = htons(port);
1096 send_addr.sin_addr = dst;
1097
1098 /* Send reply with same size packet as request used. */
1099 if (sendto(s, pktbuf, pktlen, 0,
1100 (struct sockaddr *) &send_addr,
1101 sizeof(send_addr)) < 0)
1102 {
1103 report(LOG_ERR, "sendto: %s", get_network_errmsg());
1104 }
1105 } /* sendreply */
1106
1107
1109 /* nmatch() - now in getif.c */
1110 /* setarp() - now in hwaddr.c */
1111
1112
1113 /*
1114 * This call checks read access to a file. It returns 0 if the file given
1115 * by "path" exists and is publically readable. A value of -1 is returned if
1116 * access is not permitted or an error occurs. Successful calls also
1117 * return the file size in bytes using the long pointer "filesize".
1118 *
1119 * The read permission bit for "other" users is checked. This bit must be
1120 * set for tftpd(8) to allow clients to read the file.
1121 */
1122
1123 PRIVATE int
1124 chk_access(char *path, int32 *filesize)
1125 {
1126 struct stat st;
1127
1128 if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) {
1129 *filesize = (int32) st.st_size;
1130 return 0;
1131 } else {
1132 return -1;
1133 }
1134 }
1135
1136
1138 /*
1139 * Now in dumptab.c :
1140 * dumptab()
1141 * dump_host()
1142 * list_ipaddresses()
1143 */
1144
1145 #ifdef VEND_CMU
1146
1147 /*
1148 * Insert the CMU "vendor" data for the host pointed to by "hp" into the
1149 * bootp packet pointed to by "bp".
1150 */
1151
1152 PRIVATE void
1153 dovend_cmu(struct bootp *bp, struct host *hp)
1154 {
1155 struct cmu_vend *vendp;
1156 struct in_addr_list *taddr;
1157
1158 /*
1159 * Initialize the entire vendor field to zeroes.
1160 */
1161 bzero(bp->bp_vend, sizeof(bp->bp_vend));
1162
1163 /*
1164 * Fill in vendor information. Subnet mask, default gateway,
1165 * domain name server, ien name server, time server
1166 */
1167 vendp = (struct cmu_vend *) bp->bp_vend;
1168 strlcpy(vendp->v_magic, (char *)vm_cmu, sizeof(vendp->v_magic));
1169 if (hp->flags.subnet_mask) {
1170 (vendp->v_smask).s_addr = hp->subnet_mask.s_addr;
1171 (vendp->v_flags) |= VF_SMASK;
1172 if (hp->flags.gateway) {
1173 (vendp->v_dgate).s_addr = hp->gateway->addr->s_addr;
1174 }
1175 }
1176 if (hp->flags.domain_server) {
1177 taddr = hp->domain_server;
1178 if (taddr->addrcount > 0) {
1179 (vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr;
1180 if (taddr->addrcount > 1) {
1181 (vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr;
1182 }
1183 }
1184 }
1185 if (hp->flags.name_server) {
1186 taddr = hp->name_server;
1187 if (taddr->addrcount > 0) {
1188 (vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr;
1189 if (taddr->addrcount > 1) {
1190 (vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr;
1191 }
1192 }
1193 }
1194 if (hp->flags.time_server) {
1195 taddr = hp->time_server;
1196 if (taddr->addrcount > 0) {
1197 (vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr;
1198 if (taddr->addrcount > 1) {
1199 (vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr;
1200 }
1201 }
1202 }
1203 /* Log message now done by caller. */
1204 } /* dovend_cmu */
1205
1206 #endif /* VEND_CMU */
1207
1208
1210
1211 /*
1212 * Insert the RFC1048 vendor data for the host pointed to by "hp" into the
1213 * bootp packet pointed to by "bp".
1214 */
1215 #define NEED(LEN, MSG) do \
1216 if (bytesleft < (LEN)) { \
1217 report(LOG_NOTICE, noroom, \
1218 hp->hostname->string, MSG); \
1219 return; \
1220 } while (0)
1221 PRIVATE void
1222 dovend_rfc1048(struct bootp *bp, struct host *hp, int32 bootsize)
1223 {
1224 int bytesleft, len;
1225 byte *vp;
1226
1227 static const char noroom[] = "%s: No room for \"%s\" option";
1228
1229 vp = bp->bp_vend;
1230
1231 if (hp->flags.msg_size) {
1232 pktlen = hp->msg_size;
1233 } else {
1234 /*
1235 * If the request was longer than the official length, build
1236 * a response of that same length where the additional length
1237 * is assumed to be part of the bp_vend (options) area.
1238 */
1239 if (pktlen > (int)sizeof(*bp)) {
1240 if (debug > 1)
1241 report(LOG_INFO, "request message length=%d", pktlen);
1242 }
1243 /*
1244 * Check whether the request contains the option:
1245 * Maximum DHCP Message Size (RFC1533 sec. 9.8)
1246 * and if so, override the response length with its value.
1247 * This request must lie within the first BP_VEND_LEN
1248 * bytes of the option space.
1249 */
1250 {
1251 byte *p, *ep;
1252 byte tag, llen;
1253 short msgsz = 0;
1254
1255 p = vp + 4;
1256 ep = p + BP_VEND_LEN - 4;
1257 while (p < ep) {
1258 tag = *p++;
1259 /* Check for tags with no data first. */
1260 if (tag == TAG_PAD)
1261 continue;
1262 if (tag == TAG_END)
1263 break;
1264 /* Now scan the length byte. */
1265 llen = *p++;
1266 switch (tag) {
1267 case TAG_MAX_MSGSZ:
1268 if (llen == 2) {
1269 bcopy(p, (char*)&msgsz, 2);
1270 msgsz = ntohs(msgsz);
1271 }
1272 break;
1273 case TAG_SUBNET_MASK:
1274 /* XXX - Should preserve this if given... */
1275 break;
1276 } /* swtich */
1277 p += llen;
1278 }
1279
1280 if (msgsz > (int)sizeof(*bp)) {
1281 if (debug > 1)
1282 report(LOG_INFO, "request has DHCP msglen=%d", msgsz);
1283 pktlen = msgsz;
1284 }
1285 }
1286 }
1287
1288 if (pktlen < (int)sizeof(*bp)) {
1289 report(LOG_ERR, "invalid response length=%d", pktlen);
1290 pktlen = sizeof(*bp);
1291 }
1292 bytesleft = ((byte*)bp + pktlen) - vp;
1293 if (pktlen > (int)sizeof(*bp)) {
1294 if (debug > 1)
1295 report(LOG_INFO, "extended reply, length=%d, options=%d",
1296 pktlen, bytesleft);
1297 }
1298
1299 /* Copy in the magic cookie */
1300 bcopy(vm_rfc1048, vp, 4);
1301 vp += 4;
1302 bytesleft -= 4;
1303
1304 if (hp->flags.subnet_mask) {
1305 /* always enough room here. */
1306 *vp++ = TAG_SUBNET_MASK;/* -1 byte */
1307 *vp++ = 4; /* -1 byte */
1308 insert_u_long(hp->subnet_mask.s_addr, &vp); /* -4 bytes */
1309 bytesleft -= 6; /* Fix real count */
1310 if (hp->flags.gateway) {
1311 (void) insert_ip(TAG_GATEWAY,
1312 hp->gateway,
1313 &vp, &bytesleft);
1314 }
1315 }
1316 if (hp->flags.bootsize) {
1317 /* always enough room here */
1318 bootsize = (hp->flags.bootsize_auto) ?
1319 ((bootsize + 511) / 512) : ((int32_t)hp->bootsize); /* Round up */
1320 *vp++ = TAG_BOOT_SIZE;
1321 *vp++ = 2;
1322 *vp++ = (byte) ((bootsize >> 8) & 0xFF);
1323 *vp++ = (byte) (bootsize & 0xFF);
1324 bytesleft -= 4; /* Tag, length, and 16 bit blocksize */
1325 }
1326 /*
1327 * This one is special: Remaining options go in the ext file.
1328 * Only the subnet_mask, bootsize, and gateway should precede.
1329 */
1330 if (hp->flags.exten_file) {
1331 /*
1332 * Check for room for exten_file. Add 3 to account for
1333 * TAG_EXTEN_FILE, length, and TAG_END.
1334 */
1335 len = strlen(hp->exten_file->string);
1336 NEED((len + 3), "ef");
1337 *vp++ = TAG_EXTEN_FILE;
1338 *vp++ = (byte) (len & 0xFF);
1339 bcopy(hp->exten_file->string, vp, len);
1340 vp += len;
1341 *vp++ = TAG_END;
1342 bytesleft -= len + 3;
1343 return; /* no more options here. */
1344 }
1345 /*
1346 * The remaining options are inserted by the following
1347 * function (which is shared with bootpef.c).
1348 * Keep back one byte for the TAG_END.
1349 */
1350 len = dovend_rfc1497(hp, vp, bytesleft - 1);
1351 vp += len;
1352 bytesleft -= len;
1353
1354 /* There should be at least one byte left. */
1355 NEED(1, "(end)");
1356 *vp++ = TAG_END;
1357 bytesleft--;
1358
1359 /* Log message done by caller. */
1360 if (bytesleft > 0) {
1361 /*
1362 * Zero out any remaining part of the vendor area.
1363 */
1364 bzero(vp, bytesleft);
1365 }
1366 } /* dovend_rfc1048 */
1367 #undef NEED
1368
1369
1371 /*
1372 * Now in readfile.c:
1373 * hwlookcmp()
1374 * iplookcmp()
1375 */
1376
1377 /* haddrtoa() - now in hwaddr.c */
1378 /*
1379 * Now in dovend.c:
1380 * insert_ip()
1381 * insert_generic()
1382 * insert_u_long()
1383 */
1384
1385 /* get_errmsg() - now in report.c */
1386
1387 /*
1388 * Local Variables:
1389 * tab-width: 4
1390 * c-indent-level: 4
1391 * c-argdecl-indent: 4
1392 * c-continued-statement-offset: 4
1393 * c-continued-brace-offset: -4
1394 * c-label-offset: -4
1395 * c-brace-offset: 0
1396 * End:
1397 */
1398