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