bootpd.c revision 1.27.22.1 1 /************************************************************************
2 Copyright 1988, 1991 by Carnegie Mellon University
3
4 All Rights Reserved
5
6 Permission to use, copy, modify, and distribute this software and its
7 documentation for any purpose and without fee is hereby granted, provided
8 that the above copyright notice appear in all copies and that both that
9 copyright notice and this permission notice appear in supporting
10 documentation, and that the name of Carnegie Mellon University not be used
11 in advertising or publicity pertaining to distribution of the software
12 without specific, written prior permission.
13
14 CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
15 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
16 IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
17 DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
18 PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
19 ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20 SOFTWARE.
21 ************************************************************************/
22
23 #include <sys/cdefs.h>
24 #ifndef lint
25 __RCSID("$NetBSD: bootpd.c,v 1.27.22.1 2025/08/02 05:58:48 perseant 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
121 /*
122 * IP port numbers for client and server obtained from /etc/services
123 */
124
125 u_short bootps_port, bootpc_port;
126
127
128 /*
129 * Internet socket and interface config structures
130 */
131
132 struct sockaddr_in bind_addr; /* Listening */
133 struct sockaddr_in recv_addr; /* Packet source */
134 struct sockaddr_in send_addr; /* destination */
135
136
137 /*
138 * option defaults
139 */
140 int debug = 0; /* Debugging flag (level) */
141 int actualtimeout = 15 * 60000; /* fifteen minutes */
142
143 /*
144 * General
145 */
146
147 int s; /* Socket file descriptor */
148 char *pktbuf; /* Receive packet buffer */
149 int pktlen;
150 const char *progname;
151 char *chdir_path;
152 char hostname[MAXHOSTNAMELEN + 1]; /* System host name */
153 struct in_addr my_ip_addr;
154
155 /* Flags set by signal catcher. */
156 PRIVATE int do_readtab = 0;
157 PRIVATE int do_dumptab = 0;
158
159 /*
160 * Globals below are associated with the bootp database file (bootptab).
161 */
162
163 const char *bootptab = CONFIG_FILE;
164 const char *bootpd_dump = DUMPTAB_FILE;
165
166
167
169 /*
170 * Initialization such as command-line processing is done and then the
171 * main server loop is started.
172 */
173
174 int
175 main(int argc, char **argv)
176 {
177 int timeout;
178 struct bootp *bp;
179 struct servent *servp;
180 struct hostent *hep;
181 char *stmp;
182 socklen_t ba_len, ra_len;
183 int n;
184 int nfound;
185 struct pollfd set[1];
186 int standalone;
187
188 progname = strrchr(argv[0], '/');
189 if (progname)
190 progname++;
191 else
192 progname = argv[0];
193
194 /*
195 * Initialize logging.
196 */
197 report_init(0); /* uses progname */
198
199 /*
200 * Log startup
201 */
202 report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
203
204 /* Debugging for compilers with struct padding. */
205 assert(sizeof(struct bootp) == BP_MINPKTSZ);
206
207 /* Get space for receiving packets and composing replies. */
208 pktbuf = malloc(MAX_MSG_SIZE);
209 if (!pktbuf) {
210 report(LOG_ERR, "malloc failed");
211 exit(1);
212 }
213 bp = (struct bootp *) pktbuf;
214
215 /*
216 * Check to see if a socket was passed to us from inetd.
217 *
218 * Use getsockname() to determine if descriptor 0 is indeed a socket
219 * (and thus we are probably a child of inetd) or if it is instead
220 * something else and we are running standalone.
221 */
222 s = 0;
223 ba_len = sizeof(bind_addr);
224 bzero((char *) &bind_addr, ba_len);
225 errno = 0;
226 standalone = TRUE;
227 if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {
228 /*
229 * Descriptor 0 is a socket. Assume we are a child of inetd.
230 */
231 if (bind_addr.sin_family == AF_INET) {
232 standalone = FALSE;
233 bootps_port = ntohs(bind_addr.sin_port);
234 } else {
235 /* Some other type of socket? */
236 report(LOG_ERR, "getsockname: not an INET socket");
237 }
238 }
239
240 /*
241 * Set defaults that might be changed by option switches.
242 */
243 stmp = NULL;
244 timeout = actualtimeout;
245
246 /*
247 * Read switches.
248 */
249 for (argc--, argv++; argc > 0; argc--, argv++) {
250 if (argv[0][0] != '-')
251 break;
252 switch (argv[0][1]) {
253
254 case 'c': /* chdir_path */
255 if (argv[0][2]) {
256 stmp = &(argv[0][2]);
257 } else {
258 argc--;
259 argv++;
260 stmp = argv[0];
261 }
262 if (!stmp || (stmp[0] != '/')) {
263 fprintf(stderr,
264 "bootpd: invalid chdir specification\n");
265 break;
266 }
267 chdir_path = stmp;
268 break;
269
270 case 'd': /* debug level */
271 if (argv[0][2]) {
272 stmp = &(argv[0][2]);
273 } else if (argv[1] && argv[1][0] == '-') {
274 /*
275 * Backwards-compatible behavior:
276 * no parameter, so just increment the debug flag.
277 */
278 debug++;
279 break;
280 } else {
281 argc--;
282 argv++;
283 stmp = argv[0];
284 }
285 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
286 fprintf(stderr,
287 "%s: invalid debug level\n", progname);
288 break;
289 }
290 debug = n;
291 break;
292
293 case 'h': /* override hostname */
294 if (argv[0][2]) {
295 stmp = &(argv[0][2]);
296 } else {
297 argc--;
298 argv++;
299 stmp = argv[0];
300 }
301 if (!stmp) {
302 fprintf(stderr,
303 "bootpd: missing hostname\n");
304 break;
305 }
306 strlcpy(hostname, stmp, sizeof(hostname));
307 break;
308
309 case 'i': /* inetd mode */
310 standalone = FALSE;
311 break;
312
313 case 's': /* standalone mode */
314 standalone = TRUE;
315 break;
316
317 case 't': /* timeout */
318 if (argv[0][2]) {
319 stmp = &(argv[0][2]);
320 } else {
321 argc--;
322 argv++;
323 stmp = argv[0];
324 }
325 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
326 fprintf(stderr,
327 "%s: invalid timeout specification\n", progname);
328 break;
329 }
330 actualtimeout = n * 60000;
331 /*
332 * If the actual timeout is zero, pass INFTIM
333 * to poll so it blocks indefinitely, otherwise,
334 * use the actual timeout value.
335 */
336 timeout = (n > 0) ? actualtimeout : INFTIM;
337 break;
338
339 default:
340 fprintf(stderr, "%s: unknown switch: -%c\n",
341 progname, argv[0][1]);
342 usage();
343 break;
344
345 } /* switch */
346 } /* for args */
347
348 /*
349 * Override default file names if specified on the command line.
350 */
351 if (argc > 0)
352 bootptab = argv[0];
353
354 if (argc > 1)
355 bootpd_dump = argv[1];
356
357 /*
358 * Get my hostname and IP address.
359 */
360 if (hostname[0] == '\0') {
361 if (gethostname(hostname, sizeof(hostname)) == -1) {
362 fprintf(stderr, "bootpd: can't get hostname\n");
363 exit(1);
364 }
365 hostname[sizeof(hostname) - 1] = '\0';
366 }
367 hep = gethostbyname(hostname);
368 if (!hep) {
369 fprintf(stderr, "Can not get my IP address\n");
370 exit(1);
371 }
372 bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
373
374 if (standalone) {
375 /*
376 * Go into background and disassociate from controlling terminal.
377 */
378 if (debug < 3) {
379 if (fork())
380 exit(0);
381 #ifdef NO_SETSID
382 setpgrp(0,0);
383 #ifdef TIOCNOTTY
384 n = open("/dev/tty", O_RDWR);
385 if (n >= 0) {
386 ioctl(n, TIOCNOTTY, (char *) 0);
387 (void) close(n);
388 }
389 #endif /* TIOCNOTTY */
390 #else /* SETSID */
391 if (setsid() < 0)
392 perror("setsid");
393 #endif /* SETSID */
394 } /* if debug < 3 */
395
396 /*
397 * Nuke any timeout value
398 */
399 timeout = INFTIM;
400
401 } /* if standalone (1st) */
402
403 /* Set the cwd (i.e. to /tftpboot) */
404 if (chdir_path) {
405 if (chdir(chdir_path) < 0)
406 report(LOG_ERR, "%s: chdir failed", chdir_path);
407 }
408
409 /* Get the timezone. */
410 tzone_init();
411
412 /* Allocate hash tables. */
413 rdtab_init();
414
415 /*
416 * Read the bootptab file.
417 */
418 readtab(1); /* force read */
419
420 if (standalone) {
421
422 /*
423 * Create a socket.
424 */
425 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
426 report(LOG_ERR, "socket: %s", get_network_errmsg());
427 exit(1);
428 }
429
430 /*
431 * Get server's listening port number
432 */
433 servp = getservbyname("bootps", "udp");
434 if (servp) {
435 bootps_port = ntohs((u_short) servp->s_port);
436 } else {
437 bootps_port = (u_short) IPPORT_BOOTPS;
438 report(LOG_ERR,
439 "udp/bootps: unknown service -- assuming port %d",
440 bootps_port);
441 }
442
443 /*
444 * Bind socket to BOOTPS port.
445 */
446 bind_addr.sin_family = AF_INET;
447 bind_addr.sin_addr.s_addr = INADDR_ANY;
448 bind_addr.sin_port = htons(bootps_port);
449 if (bind(s, (struct sockaddr *) &bind_addr,
450 sizeof(bind_addr)) < 0)
451 {
452 report(LOG_ERR, "bind: %s", get_network_errmsg());
453 exit(1);
454 }
455 } /* if standalone (2nd)*/
456
457 /*
458 * Get destination port number so we can reply to client
459 */
460 servp = getservbyname("bootpc", "udp");
461 if (servp) {
462 bootpc_port = ntohs(servp->s_port);
463 } else {
464 report(LOG_ERR,
465 "udp/bootpc: unknown service -- assuming port %d",
466 IPPORT_BOOTPC);
467 bootpc_port = (u_short) IPPORT_BOOTPC;
468 }
469
470 /*
471 * Set up signals to read or dump the table.
472 */
473 if ((long) signal(SIGHUP, catcher) < 0) {
474 report(LOG_ERR, "signal: %s", get_errmsg());
475 exit(1);
476 }
477 if ((long) signal(SIGUSR1, catcher) < 0) {
478 report(LOG_ERR, "signal: %s", get_errmsg());
479 exit(1);
480 }
481
482 /*
483 * Process incoming requests.
484 */
485 set[0].fd = s;
486 set[0].events = POLLIN;
487 for (;;) {
488 nfound = poll(set, 1, timeout);
489 if (nfound < 0) {
490 if (errno != EINTR) {
491 report(LOG_ERR, "poll: %s", get_errmsg());
492 }
493 /*
494 * Call readtab() or dumptab() here to avoid the
495 * dangers of doing I/O from a signal handler.
496 */
497 if (do_readtab) {
498 do_readtab = 0;
499 readtab(1); /* force read */
500 }
501 if (do_dumptab) {
502 do_dumptab = 0;
503 dumptab(bootpd_dump);
504 }
505 continue;
506 }
507 if (nfound == 0) {
508 if (debug > 1)
509 report(LOG_INFO, "exiting after %d minute%s of inactivity",
510 actualtimeout / 60000,
511 actualtimeout == 60000 ? "" : "s");
512 exit(0);
513 }
514 ra_len = sizeof(recv_addr);
515 n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
516 (struct sockaddr *) &recv_addr, &ra_len);
517 if (n <= 0) {
518 continue;
519 }
520 if (debug > 1) {
521 report(LOG_INFO, "recvd pkt from IP addr %s",
522 inet_ntoa(recv_addr.sin_addr));
523 }
524 if (n < (int)sizeof(struct bootp)) {
525 if (debug) {
526 report(LOG_INFO, "received short packet");
527 }
528 continue;
529 }
530 pktlen = n;
531
532 readtab(0); /* maybe re-read bootptab */
533
534 switch (bp->bp_op) {
535 case BOOTREQUEST:
536 handle_request();
537 break;
538 case BOOTREPLY:
539 handle_reply();
540 break;
541 }
542 }
543 }
544
545
546
548
549 /*
550 * Print "usage" message and exit
551 */
552
553 PRIVATE void
554 usage(void)
555 {
556 fprintf(stderr,
557 "usage: bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n");
558 fprintf(stderr, "\t -c n\tset current directory\n");
559 fprintf(stderr, "\t -d n\tset debug level\n");
560 fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n");
561 fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n");
562 fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n");
563 exit(1);
564 }
565
566 /* Signal catchers */
567 PRIVATE void
568 catcher(int sig)
569 {
570 if (sig == SIGHUP)
571 do_readtab = 1;
572 if (sig == SIGUSR1)
573 do_dumptab = 1;
574 #ifdef SYSV
575 /* For older "System V" derivatives with no sigset(). */
576 /* XXX - Should just do it the POSIX way (sigaction). */
577 signal(sig, catcher);
578 #endif
579 }
580
581
582
584 /*
585 * Process BOOTREQUEST packet.
586 *
587 * Note: This version of the bootpd.c server never forwards
588 * a request to another server. That is the job of a gateway
589 * program such as the "bootpgw" program included here.
590 *
591 * (Also this version does not interpret the hostname field of
592 * the request packet; it COULD do a name->address lookup and
593 * forward the request there.)
594 */
595 PRIVATE void
596 handle_request(void)
597 {
598 struct bootp *bp = (struct bootp *) pktbuf;
599 struct host *hp = NULL;
600 struct host dummyhost;
601 int32 bootsize = 0;
602 unsigned hlen, hashcode;
603 int32 dest;
604 char lrealpath[1024];
605 char *clntpath;
606 size_t clntpathmaxlen;
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 clntpathmaxlen = sizeof(lrealpath) + lrealpath - clntpath;
820 } else {
821 lrealpath[0] = '\0';
822 clntpath = lrealpath;
823 clntpathmaxlen = sizeof(lrealpath);
824 }
825
826 /*
827 * Determine client's requested homedir and bootfile.
828 */
829 homedir = NULL;
830 bootfile = NULL;
831 if (bp->bp_file[0]) {
832 char *t;
833
834 homedir = bp->bp_file;
835
836 /* make sure that the file is nul terminated */
837 for (t = homedir; t - homedir < BP_FILE_LEN; t++)
838 if (*t == '\0')
839 break;
840 if (t - homedir < BP_FILE_LEN) {
841 report(LOG_INFO, "requested path length > BP_FILE_LEN file = \"%s\", nul terminating", homedir);
842 homedir[BP_FILE_LEN - 1] = '\0';
843 }
844
845 bootfile = strrchr(homedir, '/');
846 if (bootfile) {
847 if (homedir == bootfile)
848 homedir = NULL;
849 *bootfile++ = '\0';
850 } else {
851 /* no "/" in the string */
852 bootfile = homedir;
853 homedir = NULL;
854 }
855 if (debug > 2) {
856 report(LOG_INFO, "requested path=\"%s\" file=\"%s\"",
857 (homedir) ? homedir : "",
858 (bootfile) ? bootfile : "");
859 }
860 }
861
862 /*
863 * Specifications in bootptab override client requested values.
864 */
865 if (hp->flags.homedir)
866 homedir = hp->homedir->string;
867 if (hp->flags.bootfile)
868 bootfile = hp->bootfile->string;
869
870 /*
871 * Construct bootfile path.
872 */
873 if (homedir) {
874 if (homedir[0] != '/')
875 strlcat(lrealpath, "/", sizeof(lrealpath));
876 strlcat(lrealpath, homedir, sizeof(lrealpath));
877 homedir = NULL;
878 }
879 if (bootfile) {
880 if (bootfile[0] != '/') {
881 strlcat(lrealpath, "/", sizeof(lrealpath));
882 lrealpath[sizeof(lrealpath) - 1] = '\0';
883 }
884 strlcat(lrealpath, bootfile, sizeof(lrealpath));
885 lrealpath[sizeof(lrealpath) - 1] = '\0';
886 bootfile = NULL;
887 }
888
889 /*
890 * First try to find the file with a ".host" suffix
891 */
892 n = strlen(clntpath);
893 strlcat(clntpath, ".", clntpathmaxlen);
894 strlcat(clntpath, hp->hostname->string, clntpathmaxlen);
895 if (chk_access(lrealpath, &bootsize) < 0) {
896 clntpath[n] = 0; /* Try it without the suffix */
897 if (chk_access(lrealpath, &bootsize) < 0) {
898 /* neither "file.host" nor "file" was found */
899 #ifdef CHECK_FILE_ACCESS
900
901 if (bp->bp_file[0]) {
902 /*
903 * Client wanted specific file
904 * and we didn't have it.
905 */
906 report(LOG_NOTICE,
907 "requested file not found: \"%s\"", clntpath);
908 return;
909 }
910 /*
911 * Client didn't ask for a specific file and we couldn't
912 * access the default file, so just zero-out the bootfile
913 * field in the packet and continue processing the reply.
914 */
915 bzero(bp->bp_file, sizeof(bp->bp_file));
916 goto null_file_name;
917
918 #else /* CHECK_FILE_ACCESS */
919
920 /* Complain only if boot file size was needed. */
921 if (hp->flags.bootsize_auto) {
922 report(LOG_ERR, "can not determine size of file \"%s\"",
923 clntpath);
924 }
925
926 #endif /* CHECK_FILE_ACCESS */
927 }
928 }
929 strlcpy(bp->bp_file, clntpath, sizeof(bp->bp_file));
930 if (debug > 2)
931 report(LOG_INFO, "bootfile=\"%s\"", clntpath);
932
933 #ifdef CHECK_FILE_ACCESS
934 null_file_name:
935 #endif /* CHECK_FILE_ACCESS */
936
937
938 /*
940 * Handle vendor options based on magic number.
941 */
942
943 if (debug > 1) {
944 report(LOG_INFO, "vendor magic field is %d.%d.%d.%d",
945 (int) ((bp->bp_vend)[0]),
946 (int) ((bp->bp_vend)[1]),
947 (int) ((bp->bp_vend)[2]),
948 (int) ((bp->bp_vend)[3]));
949 }
950 /*
951 * If this host isn't set for automatic vendor info then copy the
952 * specific cookie into the bootp packet, thus forcing a certain
953 * reply format. Only force reply format if user specified it.
954 */
955 if (hp->flags.vm_cookie) {
956 /* Slam in the user specified magic number. */
957 bcopy(hp->vm_cookie, bp->bp_vend, 4);
958 }
959 /*
960 * Figure out the format for the vendor-specific info.
961 * Note that bp->bp_vend may have been set above.
962 */
963 if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) {
964 /* RFC1048 conformant bootp client */
965 dovend_rfc1048(bp, hp, bootsize);
966 if (debug > 1) {
967 report(LOG_INFO, "sending reply (with RFC1048 options)");
968 }
969 }
970 #ifdef VEND_CMU
971 else if (!bcmp(bp->bp_vend, vm_cmu, 4)) {
972 dovend_cmu(bp, hp);
973 if (debug > 1) {
974 report(LOG_INFO, "sending reply (with CMU options)");
975 }
976 }
977 #endif
978 else {
979 if (debug > 1) {
980 report(LOG_INFO, "sending reply (with no options)");
981 }
982 }
983
984 dest = (hp->flags.reply_addr) ?
985 hp->reply_addr.s_addr : 0L;
986
987 /* not forwarded */
988 sendreply(0, dest);
989 }
990
991
992 /*
993 * Process BOOTREPLY packet.
994 */
995 PRIVATE void
996 handle_reply(void)
997 {
998 if (debug) {
999 report(LOG_INFO, "processing boot reply");
1000 }
1001 /* forwarded, no destination override */
1002 sendreply(1, 0);
1003 }
1004
1005
1006 /*
1007 * Send a reply packet to the client. 'forward' flag is set if we are
1008 * not the originator of this reply packet.
1009 */
1010 PRIVATE void
1011 sendreply(int forward, int32 dst_override)
1012 {
1013 struct bootp *bp = (struct bootp *) pktbuf;
1014 struct in_addr dst;
1015 u_short port = bootpc_port;
1016 unsigned char *ha;
1017 int len;
1018
1019 /*
1020 * XXX - Should honor bp_flags "broadcast" bit here.
1021 * Temporary workaround: use the :ra=ADDR: option to
1022 * set the reply address to the broadcast address.
1023 */
1024
1025 /*
1026 * If the destination address was specified explicitly
1027 * (i.e. the broadcast address for HP compatibility)
1028 * then send the response to that address. Otherwise,
1029 * act in accordance with RFC951:
1030 * If the client IP address is specified, use that
1031 * else if gateway IP address is specified, use that
1032 * else make a temporary arp cache entry for the client's
1033 * NEW IP/hardware address and use that.
1034 */
1035 if (dst_override) {
1036 dst.s_addr = dst_override;
1037 if (debug > 1) {
1038 report(LOG_INFO, "reply address override: %s",
1039 inet_ntoa(dst));
1040 }
1041 } else if (bp->bp_ciaddr.s_addr) {
1042 dst = bp->bp_ciaddr;
1043 } else if (bp->bp_giaddr.s_addr && forward == 0) {
1044 dst = bp->bp_giaddr;
1045 port = bootps_port;
1046 if (debug > 1) {
1047 report(LOG_INFO, "sending reply to gateway %s",
1048 inet_ntoa(dst));
1049 }
1050 } else {
1051 dst = bp->bp_yiaddr;
1052 ha = bp->bp_chaddr;
1053 len = bp->bp_hlen;
1054 if (len > MAXHADDRLEN)
1055 len = MAXHADDRLEN;
1056
1057 if (debug > 1)
1058 report(LOG_INFO, "setarp %s - %s",
1059 inet_ntoa(dst), haddrtoa(ha, len));
1060 setarp(s, &dst, ha, len);
1061 }
1062
1063 if ((forward == 0) &&
1064 (bp->bp_siaddr.s_addr == 0))
1065 {
1066 struct ifreq *ifr;
1067 struct in_addr siaddr;
1068 /*
1069 * If we are originating this reply, we
1070 * need to find our own interface address to
1071 * put in the bp_siaddr field of the reply.
1072 * If this server is multi-homed, pick the
1073 * 'best' interface (the one on the same net
1074 * as the client). Of course, the client may
1075 * be on the other side of a BOOTP gateway...
1076 */
1077 ifr = getif(s, &dst);
1078 if (ifr) {
1079 struct sockaddr_in *sip;
1080 sip = (struct sockaddr_in *) &(ifr->ifr_addr);
1081 siaddr = sip->sin_addr;
1082 } else {
1083 /* Just use my "official" IP address. */
1084 siaddr = my_ip_addr;
1085 }
1086
1087 /* XXX - No need to set bp_giaddr here. */
1088
1089 /* Finally, set the server address field. */
1090 bp->bp_siaddr = siaddr;
1091 }
1092 /* Set up socket address for send. */
1093 send_addr.sin_family = AF_INET;
1094 send_addr.sin_port = htons(port);
1095 send_addr.sin_addr = dst;
1096
1097 /* Send reply with same size packet as request used. */
1098 if (sendto(s, pktbuf, pktlen, 0,
1099 (struct sockaddr *) &send_addr,
1100 sizeof(send_addr)) < 0)
1101 {
1102 report(LOG_ERR, "sendto: %s", get_network_errmsg());
1103 }
1104 } /* sendreply */
1105
1106
1108 /* nmatch() - now in getif.c */
1109 /* setarp() - now in hwaddr.c */
1110
1111
1112 /*
1113 * This call checks read access to a file. It returns 0 if the file given
1114 * by "path" exists and is publically readable. A value of -1 is returned if
1115 * access is not permitted or an error occurs. Successful calls also
1116 * return the file size in bytes using the long pointer "filesize".
1117 *
1118 * The read permission bit for "other" users is checked. This bit must be
1119 * set for tftpd(8) to allow clients to read the file.
1120 */
1121
1122 PRIVATE int
1123 chk_access(char *path, int32 *filesize)
1124 {
1125 struct stat st;
1126
1127 if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) {
1128 *filesize = (int32) st.st_size;
1129 return 0;
1130 } else {
1131 return -1;
1132 }
1133 }
1134
1135
1137 /*
1138 * Now in dumptab.c :
1139 * dumptab()
1140 * dump_host()
1141 * list_ipaddresses()
1142 */
1143
1144 #ifdef VEND_CMU
1145
1146 /*
1147 * Insert the CMU "vendor" data for the host pointed to by "hp" into the
1148 * bootp packet pointed to by "bp".
1149 */
1150
1151 PRIVATE void
1152 dovend_cmu(struct bootp *bp, struct host *hp)
1153 {
1154 struct cmu_vend *vendp;
1155 struct in_addr_list *taddr;
1156
1157 /*
1158 * Initialize the entire vendor field to zeroes.
1159 */
1160 bzero(bp->bp_vend, sizeof(bp->bp_vend));
1161
1162 /*
1163 * Fill in vendor information. Subnet mask, default gateway,
1164 * domain name server, ien name server, time server
1165 */
1166 vendp = (struct cmu_vend *) bp->bp_vend;
1167 strlcpy(vendp->v_magic, (char *)vm_cmu, sizeof(vendp->v_magic));
1168 if (hp->flags.subnet_mask) {
1169 (vendp->v_smask).s_addr = hp->subnet_mask.s_addr;
1170 (vendp->v_flags) |= VF_SMASK;
1171 if (hp->flags.gateway) {
1172 (vendp->v_dgate).s_addr = hp->gateway->addr->s_addr;
1173 }
1174 }
1175 if (hp->flags.domain_server) {
1176 taddr = hp->domain_server;
1177 if (taddr->addrcount > 0) {
1178 (vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr;
1179 if (taddr->addrcount > 1) {
1180 (vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr;
1181 }
1182 }
1183 }
1184 if (hp->flags.name_server) {
1185 taddr = hp->name_server;
1186 if (taddr->addrcount > 0) {
1187 (vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr;
1188 if (taddr->addrcount > 1) {
1189 (vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr;
1190 }
1191 }
1192 }
1193 if (hp->flags.time_server) {
1194 taddr = hp->time_server;
1195 if (taddr->addrcount > 0) {
1196 (vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr;
1197 if (taddr->addrcount > 1) {
1198 (vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr;
1199 }
1200 }
1201 }
1202 /* Log message now done by caller. */
1203 } /* dovend_cmu */
1204
1205 #endif /* VEND_CMU */
1206
1207
1209
1210 /*
1211 * Insert the RFC1048 vendor data for the host pointed to by "hp" into the
1212 * bootp packet pointed to by "bp".
1213 */
1214 #define NEED(LEN, MSG) do \
1215 if (bytesleft < (LEN)) { \
1216 report(LOG_NOTICE, noroom, \
1217 hp->hostname->string, MSG); \
1218 return; \
1219 } while (0)
1220 PRIVATE void
1221 dovend_rfc1048(struct bootp *bp, struct host *hp, int32 bootsize)
1222 {
1223 int bytesleft, len;
1224 byte *vp;
1225
1226 static const char noroom[] = "%s: No room for \"%s\" option";
1227
1228 vp = bp->bp_vend;
1229
1230 if (hp->flags.msg_size) {
1231 pktlen = hp->msg_size;
1232 } else {
1233 /*
1234 * If the request was longer than the official length, build
1235 * a response of that same length where the additional length
1236 * is assumed to be part of the bp_vend (options) area.
1237 */
1238 if (pktlen > (int)sizeof(*bp)) {
1239 if (debug > 1)
1240 report(LOG_INFO, "request message length=%d", pktlen);
1241 }
1242 /*
1243 * Check whether the request contains the option:
1244 * Maximum DHCP Message Size (RFC1533 sec. 9.8)
1245 * and if so, override the response length with its value.
1246 * This request must lie within the first BP_VEND_LEN
1247 * bytes of the option space.
1248 */
1249 {
1250 byte *p, *ep;
1251 byte tag, llen;
1252 short msgsz = 0;
1253
1254 p = vp + 4;
1255 ep = p + BP_VEND_LEN - 4;
1256 while (p < ep) {
1257 tag = *p++;
1258 /* Check for tags with no data first. */
1259 if (tag == TAG_PAD)
1260 continue;
1261 if (tag == TAG_END)
1262 break;
1263 /* Now scan the length byte. */
1264 llen = *p++;
1265 switch (tag) {
1266 case TAG_MAX_MSGSZ:
1267 if (llen == 2) {
1268 bcopy(p, (char*)&msgsz, 2);
1269 msgsz = ntohs(msgsz);
1270 }
1271 break;
1272 case TAG_SUBNET_MASK:
1273 /* XXX - Should preserve this if given... */
1274 break;
1275 } /* switch */
1276 p += llen;
1277 }
1278
1279 if (msgsz > (int)sizeof(*bp)) {
1280 if (debug > 1)
1281 report(LOG_INFO, "request has DHCP msglen=%d", msgsz);
1282 pktlen = msgsz;
1283 }
1284 }
1285 }
1286
1287 if (pktlen < (int)sizeof(*bp)) {
1288 report(LOG_ERR, "invalid response length=%d", pktlen);
1289 pktlen = sizeof(*bp);
1290 }
1291 bytesleft = ((byte*)bp + pktlen) - vp;
1292 if (pktlen > (int)sizeof(*bp)) {
1293 if (debug > 1)
1294 report(LOG_INFO, "extended reply, length=%d, options=%d",
1295 pktlen, bytesleft);
1296 }
1297
1298 /* Copy in the magic cookie */
1299 bcopy(vm_rfc1048, vp, 4);
1300 vp += 4;
1301 bytesleft -= 4;
1302
1303 if (hp->flags.subnet_mask) {
1304 /* always enough room here. */
1305 *vp++ = TAG_SUBNET_MASK;/* -1 byte */
1306 *vp++ = 4; /* -1 byte */
1307 insert_u_long(hp->subnet_mask.s_addr, &vp); /* -4 bytes */
1308 bytesleft -= 6; /* Fix real count */
1309 if (hp->flags.gateway) {
1310 (void) insert_ip(TAG_GATEWAY,
1311 hp->gateway,
1312 &vp, &bytesleft);
1313 }
1314 }
1315 if (hp->flags.bootsize) {
1316 /* always enough room here */
1317 bootsize = (hp->flags.bootsize_auto) ?
1318 ((bootsize + 511) / 512) : ((int32_t)hp->bootsize); /* Round up */
1319 *vp++ = TAG_BOOT_SIZE;
1320 *vp++ = 2;
1321 *vp++ = (byte) ((bootsize >> 8) & 0xFF);
1322 *vp++ = (byte) (bootsize & 0xFF);
1323 bytesleft -= 4; /* Tag, length, and 16 bit blocksize */
1324 }
1325 /*
1326 * This one is special: Remaining options go in the ext file.
1327 * Only the subnet_mask, bootsize, and gateway should precede.
1328 */
1329 if (hp->flags.exten_file) {
1330 /*
1331 * Check for room for exten_file. Add 3 to account for
1332 * TAG_EXTEN_FILE, length, and TAG_END.
1333 */
1334 len = strlen(hp->exten_file->string);
1335 NEED((len + 3), "ef");
1336 *vp++ = TAG_EXTEN_FILE;
1337 *vp++ = (byte) (len & 0xFF);
1338 bcopy(hp->exten_file->string, vp, len);
1339 vp += len;
1340 *vp++ = TAG_END;
1341 bytesleft -= len + 3;
1342 return; /* no more options here. */
1343 }
1344 /*
1345 * The remaining options are inserted by the following
1346 * function (which is shared with bootpef.c).
1347 * Keep back one byte for the TAG_END.
1348 */
1349 len = dovend_rfc1497(hp, vp, bytesleft - 1);
1350 vp += len;
1351 bytesleft -= len;
1352
1353 /* There should be at least one byte left. */
1354 NEED(1, "(end)");
1355 *vp++ = TAG_END;
1356 bytesleft--;
1357
1358 /* Log message done by caller. */
1359 if (bytesleft > 0) {
1360 /*
1361 * Zero out any remaining part of the vendor area.
1362 */
1363 bzero(vp, bytesleft);
1364 }
1365 } /* dovend_rfc1048 */
1366 #undef NEED
1367
1368
1370 /*
1371 * Now in readfile.c:
1372 * hwlookcmp()
1373 * iplookcmp()
1374 */
1375
1376 /* haddrtoa() - now in hwaddr.c */
1377 /*
1378 * Now in dovend.c:
1379 * insert_ip()
1380 * insert_generic()
1381 * insert_u_long()
1382 */
1383
1384 /* get_errmsg() - now in report.c */
1385
1386 /*
1387 * Local Variables:
1388 * tab-width: 4
1389 * c-indent-level: 4
1390 * c-argdecl-indent: 4
1391 * c-continued-statement-offset: 4
1392 * c-continued-brace-offset: -4
1393 * c-label-offset: -4
1394 * c-brace-offset: 0
1395 * End:
1396 */
1397