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