- Test m_pkthdr.fw_flags against DUMMYNET_MBUF_TAGGED before trying to locate
[dragonfly/netmp.git] / libexec / bootpd / bootpd.c
blob9f6a3478f2f6746ec7d46a04db0ba3dc083f63ea
1 /************************************************************************
2 Copyright 1988, 1991 by Carnegie Mellon University
4 All Rights Reserved
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.
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.
22 $FreeBSD: src/libexec/bootpd/bootpd.c,v 1.13.2.3 2003/02/15 05:36:01 kris Exp $
23 $DragonFly: src/libexec/bootpd/bootpd.c,v 1.2 2003/06/17 04:27:07 dillon Exp $
25 ************************************************************************/
28 * BOOTP (bootstrap protocol) server daemon.
30 * Answers BOOTP request packets from booting client machines.
31 * See [SRI-NIC]<RFC>RFC951.TXT for a description of the protocol.
32 * See [SRI-NIC]<RFC>RFC1048.TXT for vendor-information extensions.
33 * See RFC 1395 for option tags 14-17.
34 * See accompanying man page -- bootpd.8
36 * HISTORY
37 * See ./Changes
39 * BUGS
40 * See ./ToDo
45 #include <sys/types.h>
46 #include <sys/param.h>
47 #include <sys/socket.h>
48 #include <sys/ioctl.h>
49 #include <sys/file.h>
50 #include <sys/time.h>
51 #include <sys/stat.h>
52 #include <sys/utsname.h>
54 #include <net/if.h>
55 #include <netinet/in.h>
56 #include <arpa/inet.h> /* inet_ntoa */
58 #ifndef NO_UNISTD
59 #include <unistd.h>
60 #endif
62 #include <stdlib.h>
63 #include <signal.h>
64 #include <stdio.h>
65 #include <string.h>
66 #include <errno.h>
67 #include <ctype.h>
68 #include <netdb.h>
69 #include <paths.h>
70 #include <syslog.h>
71 #include <assert.h>
73 #ifdef NO_SETSID
74 # include <fcntl.h> /* for O_RDONLY, etc */
75 #endif
77 #ifndef USE_BFUNCS
78 # include <memory.h>
79 /* Yes, memcpy is OK here (no overlapped copies). */
80 # define bcopy(a,b,c) memcpy(b,a,c)
81 # define bzero(p,l) memset(p,0,l)
82 # define bcmp(a,b,c) memcmp(a,b,c)
83 #endif
85 #include "bootp.h"
86 #include "hash.h"
87 #include "hwaddr.h"
88 #include "bootpd.h"
89 #include "dovend.h"
90 #include "getif.h"
91 #include "readfile.h"
92 #include "report.h"
93 #include "tzone.h"
94 #include "patchlevel.h"
96 #ifndef CONFIG_FILE
97 #define CONFIG_FILE "/etc/bootptab"
98 #endif
99 #ifndef DUMPTAB_FILE
100 #define DUMPTAB_FILE "/tmp/bootpd.dump"
101 #endif
106 * Externals, forward declarations, and global variables
109 #ifdef __STDC__
110 #define P(args) args
111 #else
112 #define P(args) ()
113 #endif
115 extern void dumptab P((char *));
117 PRIVATE void catcher P((int));
118 PRIVATE int chk_access P((char *, int32 *));
119 #ifdef VEND_CMU
120 PRIVATE void dovend_cmu P((struct bootp *, struct host *));
121 #endif
122 PRIVATE void dovend_rfc1048 P((struct bootp *, struct host *, int32));
123 PRIVATE void handle_reply P((void));
124 PRIVATE void handle_request P((void));
125 PRIVATE void sendreply P((int forward, int32 dest_override));
126 PRIVATE void usage P((void));
128 #undef P
131 * IP port numbers for client and server obtained from /etc/services
134 u_short bootps_port, bootpc_port;
138 * Internet socket and interface config structures
141 struct sockaddr_in bind_addr; /* Listening */
142 struct sockaddr_in recv_addr; /* Packet source */
143 struct sockaddr_in send_addr; /* destination */
147 * option defaults
149 int debug = 0; /* Debugging flag (level) */
150 struct timeval actualtimeout =
151 { /* fifteen minutes */
152 15 * 60L, /* tv_sec */
153 0 /* tv_usec */
157 * General
160 int s; /* Socket file descriptor */
161 char *pktbuf; /* Receive packet buffer */
162 int pktlen;
163 char *progname;
164 char *chdir_path;
165 struct in_addr my_ip_addr;
167 static const char *hostname;
168 static char default_hostname[MAXHOSTNAMELEN];
170 /* Flags set by signal catcher. */
171 PRIVATE int do_readtab = 0;
172 PRIVATE int do_dumptab = 0;
175 * Globals below are associated with the bootp database file (bootptab).
178 char *bootptab = CONFIG_FILE;
179 char *bootpd_dump = DUMPTAB_FILE;
184 * Initialization such as command-line processing is done and then the
185 * main server loop is started.
189 main(argc, argv)
190 int argc;
191 char **argv;
193 struct timeval *timeout;
194 struct bootp *bp;
195 struct servent *servp;
196 struct hostent *hep;
197 char *stmp;
198 int n, ba_len, ra_len;
199 int nfound, readfds;
200 int standalone;
201 #ifdef SA_NOCLDSTOP /* Have POSIX sigaction(2). */
202 struct sigaction sa;
203 #endif
205 progname = strrchr(argv[0], '/');
206 if (progname) progname++;
207 else progname = argv[0];
210 * Initialize logging.
212 report_init(0); /* uses progname */
215 * Log startup
217 report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
219 /* Debugging for compilers with struct padding. */
220 assert(sizeof(struct bootp) == BP_MINPKTSZ);
222 /* Get space for receiving packets and composing replies. */
223 pktbuf = malloc(MAX_MSG_SIZE);
224 if (!pktbuf) {
225 report(LOG_ERR, "malloc failed");
226 exit(1);
228 bp = (struct bootp *) pktbuf;
231 * Check to see if a socket was passed to us from inetd.
233 * Use getsockname() to determine if descriptor 0 is indeed a socket
234 * (and thus we are probably a child of inetd) or if it is instead
235 * something else and we are running standalone.
237 s = 0;
238 ba_len = sizeof(bind_addr);
239 bzero((char *) &bind_addr, ba_len);
240 errno = 0;
241 standalone = TRUE;
242 if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {
244 * Descriptor 0 is a socket. Assume we are a child of inetd.
246 if (bind_addr.sin_family == AF_INET) {
247 standalone = FALSE;
248 bootps_port = ntohs(bind_addr.sin_port);
249 } else {
250 /* Some other type of socket? */
251 report(LOG_ERR, "getsockname: not an INET socket");
256 * Set defaults that might be changed by option switches.
258 stmp = NULL;
259 timeout = &actualtimeout;
261 if (gethostname(default_hostname, sizeof(default_hostname) - 1) < 0) {
262 report(LOG_ERR, "bootpd: can't get hostname\n");
263 exit(1);
265 default_hostname[sizeof(default_hostname) - 1] = '\0';
266 hostname = default_hostname;
269 * Read switches.
271 for (argc--, argv++; argc > 0; argc--, argv++) {
272 if (argv[0][0] != '-')
273 break;
274 switch (argv[0][1]) {
276 case 'c': /* chdir_path */
277 if (argv[0][2]) {
278 stmp = &(argv[0][2]);
279 } else {
280 argc--;
281 argv++;
282 stmp = argv[0];
284 if (!stmp || (stmp[0] != '/')) {
285 report(LOG_ERR,
286 "bootpd: invalid chdir specification\n");
287 break;
289 chdir_path = stmp;
290 break;
292 case 'd': /* debug level */
293 if (argv[0][2]) {
294 stmp = &(argv[0][2]);
295 } else if (argv[1] && argv[1][0] == '-') {
297 * Backwards-compatible behavior:
298 * no parameter, so just increment the debug flag.
300 debug++;
301 break;
302 } else {
303 argc--;
304 argv++;
305 stmp = argv[0];
307 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
308 report(LOG_ERR,
309 "%s: invalid debug level\n", progname);
310 break;
312 debug = n;
313 break;
315 case 'h': /* override hostname */
316 if (argv[0][2]) {
317 stmp = &(argv[0][2]);
318 } else {
319 argc--;
320 argv++;
321 stmp = argv[0];
323 if (!stmp) {
324 report(LOG_ERR,
325 "bootpd: missing hostname\n");
326 break;
328 hostname = stmp;
329 break;
331 case 'i': /* inetd mode */
332 standalone = FALSE;
333 break;
335 case 's': /* standalone mode */
336 standalone = TRUE;
337 break;
339 case 't': /* timeout */
340 if (argv[0][2]) {
341 stmp = &(argv[0][2]);
342 } else {
343 argc--;
344 argv++;
345 stmp = argv[0];
347 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
348 report(LOG_ERR,
349 "%s: invalid timeout specification\n", progname);
350 break;
352 actualtimeout.tv_sec = (int32) (60 * n);
354 * If the actual timeout is zero, pass a NULL pointer
355 * to select so it blocks indefinitely, otherwise,
356 * point to the actual timeout value.
358 timeout = (n > 0) ? &actualtimeout : NULL;
359 break;
361 default:
362 report(LOG_ERR, "%s: unknown switch: -%c\n",
363 progname, argv[0][1]);
364 usage();
365 break;
367 } /* switch */
368 } /* for args */
371 * Override default file names if specified on the command line.
373 if (argc > 0)
374 bootptab = argv[0];
376 if (argc > 1)
377 bootpd_dump = argv[1];
380 * Get my hostname and IP address.
383 hep = gethostbyname(hostname);
384 if (!hep) {
385 report(LOG_ERR, "Can not get my IP address\n");
386 exit(1);
388 bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
390 if (standalone) {
392 * Go into background and disassociate from controlling terminal.
394 if (debug < 3) {
395 if (fork())
396 exit(0);
397 #ifdef NO_SETSID
398 setpgrp(0,0);
399 #ifdef TIOCNOTTY
400 n = open(_PATH_TTY, O_RDWR);
401 if (n >= 0) {
402 ioctl(n, TIOCNOTTY, (char *) 0);
403 (void) close(n);
405 #endif /* TIOCNOTTY */
406 #else /* SETSID */
407 if (setsid() < 0)
408 perror("setsid");
409 #endif /* SETSID */
410 } /* if debug < 3 */
413 * Nuke any timeout value
415 timeout = NULL;
417 } /* if standalone (1st) */
419 /* Set the cwd (i.e. to /tftpboot) */
420 if (chdir_path) {
421 if (chdir(chdir_path) < 0)
422 report(LOG_ERR, "%s: chdir failed", chdir_path);
425 /* Get the timezone. */
426 tzone_init();
428 /* Allocate hash tables. */
429 rdtab_init();
432 * Read the bootptab file.
434 readtab(1); /* force read */
436 if (standalone) {
439 * Create a socket.
441 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
442 report(LOG_ERR, "socket: %s", get_network_errmsg());
443 exit(1);
447 * Get server's listening port number
449 servp = getservbyname("bootps", "udp");
450 if (servp) {
451 bootps_port = ntohs((u_short) servp->s_port);
452 } else {
453 bootps_port = (u_short) IPPORT_BOOTPS;
454 report(LOG_ERR,
455 "udp/bootps: unknown service -- assuming port %d",
456 bootps_port);
460 * Bind socket to BOOTPS port.
462 bind_addr.sin_family = AF_INET;
463 bind_addr.sin_addr.s_addr = INADDR_ANY;
464 bind_addr.sin_port = htons(bootps_port);
465 if (bind(s, (struct sockaddr *) &bind_addr,
466 sizeof(bind_addr)) < 0)
468 report(LOG_ERR, "bind: %s", get_network_errmsg());
469 exit(1);
471 } /* if standalone (2nd)*/
474 * Get destination port number so we can reply to client
476 servp = getservbyname("bootpc", "udp");
477 if (servp) {
478 bootpc_port = ntohs(servp->s_port);
479 } else {
480 report(LOG_ERR,
481 "udp/bootpc: unknown service -- assuming port %d",
482 IPPORT_BOOTPC);
483 bootpc_port = (u_short) IPPORT_BOOTPC;
487 * Set up signals to read or dump the table.
489 #ifdef SA_NOCLDSTOP /* Have POSIX sigaction(2). */
490 sa.sa_handler = catcher;
491 sigemptyset(&sa.sa_mask);
492 sa.sa_flags = 0;
493 if (sigaction(SIGHUP, &sa, NULL) < 0) {
494 report(LOG_ERR, "sigaction: %s", get_errmsg());
495 exit(1);
497 if (sigaction(SIGUSR1, &sa, NULL) < 0) {
498 report(LOG_ERR, "sigaction: %s", get_errmsg());
499 exit(1);
501 #else /* SA_NOCLDSTOP */
502 /* Old-fashioned UNIX signals */
503 if ((int) signal(SIGHUP, catcher) < 0) {
504 report(LOG_ERR, "signal: %s", get_errmsg());
505 exit(1);
507 if ((int) signal(SIGUSR1, catcher) < 0) {
508 report(LOG_ERR, "signal: %s", get_errmsg());
509 exit(1);
511 #endif /* SA_NOCLDSTOP */
514 * Process incoming requests.
516 for (;;) {
517 struct timeval tv;
519 readfds = 1 << s;
520 if (timeout)
521 tv = *timeout;
523 nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL,
524 (timeout) ? &tv : NULL);
525 if (nfound < 0) {
526 if (errno != EINTR) {
527 report(LOG_ERR, "select: %s", get_errmsg());
530 * Call readtab() or dumptab() here to avoid the
531 * dangers of doing I/O from a signal handler.
533 if (do_readtab) {
534 do_readtab = 0;
535 readtab(1); /* force read */
537 if (do_dumptab) {
538 do_dumptab = 0;
539 dumptab(bootpd_dump);
541 continue;
543 if (!(readfds & (1 << s))) {
544 if (debug > 1)
545 report(LOG_INFO, "exiting after %ld minutes of inactivity",
546 actualtimeout.tv_sec / 60);
547 exit(0);
549 ra_len = sizeof(recv_addr);
550 n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
551 (struct sockaddr *) &recv_addr, &ra_len);
552 if (n <= 0) {
553 continue;
555 if (debug > 1) {
556 report(LOG_INFO, "recvd pkt from IP addr %s",
557 inet_ntoa(recv_addr.sin_addr));
559 if (n < sizeof(struct bootp)) {
560 if (debug) {
561 report(LOG_NOTICE, "received short packet");
563 continue;
565 pktlen = n;
567 readtab(0); /* maybe re-read bootptab */
569 switch (bp->bp_op) {
570 case BOOTREQUEST:
571 handle_request();
572 break;
573 case BOOTREPLY:
574 handle_reply();
575 break;
578 return 0;
585 * Print "usage" message and exit
588 PRIVATE void
589 usage()
591 fprintf(stderr,
592 "usage: bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n");
593 fprintf(stderr, "\t -c n\tset current directory\n");
594 fprintf(stderr, "\t -d n\tset debug level\n");
595 fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n");
596 fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n");
597 fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n");
598 exit(1);
601 /* Signal catchers */
602 PRIVATE void
603 catcher(sig)
604 int sig;
606 if (sig == SIGHUP)
607 do_readtab = 1;
608 if (sig == SIGUSR1)
609 do_dumptab = 1;
610 #if !defined(SA_NOCLDSTOP) && defined(SYSV)
611 /* For older "System V" derivatives with no sigaction(). */
612 signal(sig, catcher);
613 #endif
619 * Process BOOTREQUEST packet.
621 * Note: This version of the bootpd.c server never forwards
622 * a request to another server. That is the job of a gateway
623 * program such as the "bootpgw" program included here.
625 * (Also this version does not interpret the hostname field of
626 * the request packet; it COULD do a name->address lookup and
627 * forward the request there.)
629 PRIVATE void
630 handle_request()
632 struct bootp *bp = (struct bootp *) pktbuf;
633 struct host *hp = NULL;
634 struct host dummyhost;
635 int32 bootsize = 0;
636 unsigned hlen, hashcode;
637 int32 dest;
638 char realpath[1024];
639 char *clntpath;
640 char *homedir, *bootfile;
641 int n;
643 bp->bp_file[sizeof(bp->bp_file)-1] = '\0';
645 /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
648 * If the servername field is set, compare it against us.
649 * If we're not being addressed, ignore this request.
650 * If the server name field is null, throw in our name.
652 if (strlen(bp->bp_sname)) {
653 if (strcmp(bp->bp_sname, hostname)) {
654 if (debug)
655 report(LOG_INFO, "\
656 ignoring request for server %s from client at %s address %s",
657 bp->bp_sname, netname(bp->bp_htype),
658 haddrtoa(bp->bp_chaddr, bp->bp_hlen));
659 /* XXX - Is it correct to ignore such a request? -gwr */
660 return;
662 } else {
663 strcpy(bp->bp_sname, hostname);
666 /* Convert the request into a reply. */
667 bp->bp_op = BOOTREPLY;
668 if (bp->bp_ciaddr.s_addr == 0) {
670 * client doesnt know his IP address,
671 * search by hardware address.
673 if (debug > 1) {
674 report(LOG_INFO, "request from %s address %s",
675 netname(bp->bp_htype),
676 haddrtoa(bp->bp_chaddr, bp->bp_hlen));
678 hlen = haddrlength(bp->bp_htype);
679 if (hlen != bp->bp_hlen) {
680 report(LOG_NOTICE, "bad addr len from from %s address %s",
681 netname(bp->bp_htype),
682 haddrtoa(bp->bp_chaddr, hlen));
684 dummyhost.htype = bp->bp_htype;
685 bcopy(bp->bp_chaddr, dummyhost.haddr, hlen);
686 hashcode = hash_HashFunction(bp->bp_chaddr, hlen);
687 hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp,
688 &dummyhost);
689 if (hp == NULL &&
690 bp->bp_htype == HTYPE_IEEE802)
692 /* Try again with address in "canonical" form. */
693 haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen);
694 if (debug > 1) {
695 report(LOG_INFO, "\
696 HW addr type is IEEE 802. convert to %s and check again\n",
697 haddrtoa(dummyhost.haddr, bp->bp_hlen));
699 hashcode = hash_HashFunction(dummyhost.haddr, hlen);
700 hp = (struct host *) hash_Lookup(hwhashtable, hashcode,
701 hwlookcmp, &dummyhost);
703 if (hp == NULL) {
705 * XXX - Add dynamic IP address assignment?
707 if (debug)
708 report(LOG_NOTICE, "unknown client %s address %s",
709 netname(bp->bp_htype),
710 haddrtoa(bp->bp_chaddr, bp->bp_hlen));
711 return; /* not found */
713 (bp->bp_yiaddr).s_addr = hp->iaddr.s_addr;
715 } else {
718 * search by IP address.
720 if (debug > 1) {
721 report(LOG_INFO, "request from IP addr %s",
722 inet_ntoa(bp->bp_ciaddr));
724 dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr;
725 hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4);
726 hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp,
727 &dummyhost);
728 if (hp == NULL) {
729 if (debug) {
730 report(LOG_NOTICE, "IP address not found: %s",
731 inet_ntoa(bp->bp_ciaddr));
733 return;
737 if (debug) {
738 report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr),
739 hp->hostname->string);
743 * If there is a response delay threshold, ignore requests
744 * with a timestamp lower than the threshold.
746 if (hp->flags.min_wait) {
747 u_int32 t = (u_int32) ntohs(bp->bp_secs);
748 if (t < hp->min_wait) {
749 if (debug > 1)
750 report(LOG_INFO,
751 "ignoring request due to timestamp (%d < %d)",
752 t, hp->min_wait);
753 return;
757 #ifdef YORK_EX_OPTION
759 * The need for the "ex" tag arose out of the need to empty
760 * shared networked drives on diskless PCs. This solution is
761 * not very clean but it does work fairly well.
762 * Written by Edmund J. Sutcliffe <edmund@york.ac.uk>
764 * XXX - This could compromise security if a non-trusted user
765 * managed to write an entry in the bootptab with :ex=trojan:
766 * so I would leave this turned off unless you need it. -gwr
768 /* Run a program, passing the client name as a parameter. */
769 if (hp->flags.exec_file) {
770 char tst[100];
771 /* XXX - Check string lengths? -gwr */
772 strcpy (tst, hp->exec_file->string);
773 strcat (tst, " ");
774 strcat (tst, hp->hostname->string);
775 strcat (tst, " &");
776 if (debug)
777 report(LOG_INFO, "executing %s", tst);
778 system(tst); /* Hope this finishes soon... */
780 #endif /* YORK_EX_OPTION */
783 * If a specific TFTP server address was specified in the bootptab file,
784 * fill it in, otherwise zero it.
785 * XXX - Rather than zero it, should it be the bootpd address? -gwr
787 (bp->bp_siaddr).s_addr = (hp->flags.bootserver) ?
788 hp->bootserver.s_addr : 0L;
790 #ifdef STANFORD_PROM_COMPAT
792 * Stanford bootp PROMs (for a Sun?) have no way to leave
793 * the boot file name field blank (because the boot file
794 * name is automatically generated from some index).
795 * As a work-around, this little hack allows those PROMs to
796 * specify "sunboot14" with the same effect as a NULL name.
797 * (The user specifies boot device 14 or some such magic.)
799 if (strcmp(bp->bp_file, "sunboot14") == 0)
800 bp->bp_file[0] = '\0'; /* treat it as unspecified */
801 #endif
804 * Fill in the client's proper bootfile.
806 * If the client specifies an absolute path, try that file with a
807 * ".host" suffix and then without. If the file cannot be found, no
808 * reply is made at all.
810 * If the client specifies a null or relative file, use the following
811 * table to determine the appropriate action:
813 * Homedir Bootfile Client's file
814 * specified? specified? specification Action
815 * -------------------------------------------------------------------
816 * No No Null Send null filename
817 * No No Relative Discard request
818 * No Yes Null Send if absolute else null
819 * No Yes Relative Discard request *XXX
820 * Yes No Null Send null filename
821 * Yes No Relative Lookup with ".host"
822 * Yes Yes Null Send home/boot or bootfile
823 * Yes Yes Relative Lookup with ".host" *XXX
828 * XXX - I don't like the policy of ignoring a client when the
829 * boot file is not accessible. The TFTP server might not be
830 * running on the same machine as the BOOTP server, in which
831 * case checking accessibility of the boot file is pointless.
833 * Therefore, file accessibility is now demanded ONLY if you
834 * define CHECK_FILE_ACCESS in the Makefile options. -gwr
838 * The "real" path is as seen by the BOOTP daemon on this
839 * machine, while the client path is relative to the TFTP
840 * daemon chroot directory (i.e. /tftpboot).
842 if (hp->flags.tftpdir) {
843 snprintf(realpath, sizeof(realpath), "%s", hp->tftpdir->string);
844 clntpath = &realpath[strlen(realpath)];
845 } else {
846 realpath[0] = '\0';
847 clntpath = realpath;
851 * Determine client's requested homedir and bootfile.
853 homedir = NULL;
854 bootfile = NULL;
855 if (bp->bp_file[0]) {
856 homedir = bp->bp_file;
857 bootfile = strrchr(homedir, '/');
858 if (bootfile) {
859 if (homedir == bootfile)
860 homedir = NULL;
861 *bootfile++ = '\0';
862 } else {
863 /* no "/" in the string */
864 bootfile = homedir;
865 homedir = NULL;
867 if (debug > 2) {
868 report(LOG_INFO, "requested path=\"%s\" file=\"%s\"",
869 (homedir) ? homedir : "",
870 (bootfile) ? bootfile : "");
875 * Specifications in bootptab override client requested values.
877 if (hp->flags.homedir)
878 homedir = hp->homedir->string;
879 if (hp->flags.bootfile)
880 bootfile = hp->bootfile->string;
883 * Construct bootfile path.
885 if (homedir) {
886 if (homedir[0] != '/')
887 strcat(clntpath, "/");
888 strcat(clntpath, homedir);
889 homedir = NULL;
891 if (bootfile) {
892 if (bootfile[0] != '/')
893 strcat(clntpath, "/");
894 strcat(clntpath, bootfile);
895 bootfile = NULL;
899 * First try to find the file with a ".host" suffix
901 n = strlen(clntpath);
902 strcat(clntpath, ".");
903 strcat(clntpath, hp->hostname->string);
904 if (chk_access(realpath, &bootsize) < 0) {
905 clntpath[n] = 0; /* Try it without the suffix */
906 if (chk_access(realpath, &bootsize) < 0) {
907 /* neither "file.host" nor "file" was found */
908 #ifdef CHECK_FILE_ACCESS
910 if (bp->bp_file[0]) {
912 * Client wanted specific file
913 * and we didn't have it.
915 report(LOG_NOTICE,
916 "requested file not found: \"%s\"", clntpath);
917 return;
920 * Client didn't ask for a specific file and we couldn't
921 * access the default file, so just zero-out the bootfile
922 * field in the packet and continue processing the reply.
924 bzero(bp->bp_file, sizeof(bp->bp_file));
925 goto null_file_name;
927 #else /* CHECK_FILE_ACCESS */
929 /* Complain only if boot file size was needed. */
930 if (hp->flags.bootsize_auto) {
931 report(LOG_ERR, "can not determine size of file \"%s\"",
932 clntpath);
935 #endif /* CHECK_FILE_ACCESS */
938 strncpy(bp->bp_file, clntpath, BP_FILE_LEN);
939 if (debug > 2)
940 report(LOG_INFO, "bootfile=\"%s\"", clntpath);
942 #ifdef CHECK_FILE_ACCESS
943 null_file_name:
944 #endif /* CHECK_FILE_ACCESS */
948 * Handle vendor options based on magic number.
951 if (debug > 1) {
952 report(LOG_INFO, "vendor magic field is %d.%d.%d.%d",
953 (int) ((bp->bp_vend)[0]),
954 (int) ((bp->bp_vend)[1]),
955 (int) ((bp->bp_vend)[2]),
956 (int) ((bp->bp_vend)[3]));
959 * If this host isn't set for automatic vendor info then copy the
960 * specific cookie into the bootp packet, thus forcing a certain
961 * reply format. Only force reply format if user specified it.
963 if (hp->flags.vm_cookie) {
964 /* Slam in the user specified magic number. */
965 bcopy(hp->vm_cookie, bp->bp_vend, 4);
968 * Figure out the format for the vendor-specific info.
969 * Note that bp->bp_vend may have been set above.
971 if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) {
972 /* RFC1048 conformant bootp client */
973 dovend_rfc1048(bp, hp, bootsize);
974 if (debug > 1) {
975 report(LOG_INFO, "sending reply (with RFC1048 options)");
978 #ifdef VEND_CMU
979 else if (!bcmp(bp->bp_vend, vm_cmu, 4)) {
980 dovend_cmu(bp, hp);
981 if (debug > 1) {
982 report(LOG_INFO, "sending reply (with CMU options)");
985 #endif
986 else {
987 if (debug > 1) {
988 report(LOG_INFO, "sending reply (with no options)");
992 dest = (hp->flags.reply_addr) ?
993 hp->reply_addr.s_addr : 0L;
995 /* not forwarded */
996 sendreply(0, dest);
1001 * Process BOOTREPLY packet.
1003 PRIVATE void
1004 handle_reply()
1006 if (debug) {
1007 report(LOG_INFO, "processing boot reply");
1009 /* forwarded, no destination override */
1010 sendreply(1, 0);
1015 * Send a reply packet to the client. 'forward' flag is set if we are
1016 * not the originator of this reply packet.
1018 PRIVATE void
1019 sendreply(forward, dst_override)
1020 int forward;
1021 int32 dst_override;
1023 struct bootp *bp = (struct bootp *) pktbuf;
1024 struct in_addr dst;
1025 u_short port = bootpc_port;
1026 unsigned char *ha;
1027 int len, haf;
1030 * XXX - Should honor bp_flags "broadcast" bit here.
1031 * Temporary workaround: use the :ra=ADDR: option to
1032 * set the reply address to the broadcast address.
1036 * If the destination address was specified explicitly
1037 * (i.e. the broadcast address for HP compatiblity)
1038 * then send the response to that address. Otherwise,
1039 * act in accordance with RFC951:
1040 * If the client IP address is specified, use that
1041 * else if gateway IP address is specified, use that
1042 * else make a temporary arp cache entry for the client's
1043 * NEW IP/hardware address and use that.
1045 if (dst_override) {
1046 dst.s_addr = dst_override;
1047 if (debug > 1) {
1048 report(LOG_INFO, "reply address override: %s",
1049 inet_ntoa(dst));
1051 } else if (bp->bp_ciaddr.s_addr) {
1052 dst = bp->bp_ciaddr;
1053 } else if (bp->bp_giaddr.s_addr && forward == 0) {
1054 dst = bp->bp_giaddr;
1055 port = bootps_port;
1056 if (debug > 1) {
1057 report(LOG_INFO, "sending reply to gateway %s",
1058 inet_ntoa(dst));
1060 } else {
1061 dst = bp->bp_yiaddr;
1062 ha = bp->bp_chaddr;
1063 len = bp->bp_hlen;
1064 if (len > MAXHADDRLEN)
1065 len = MAXHADDRLEN;
1066 haf = (int) bp->bp_htype;
1067 if (haf == 0)
1068 haf = HTYPE_ETHERNET;
1070 if (debug > 1)
1071 report(LOG_INFO, "setarp %s - %s",
1072 inet_ntoa(dst), haddrtoa(ha, len));
1073 setarp(s, &dst, haf, ha, len);
1076 if ((forward == 0) &&
1077 (bp->bp_siaddr.s_addr == 0))
1079 struct ifreq *ifr;
1080 struct in_addr siaddr;
1082 * If we are originating this reply, we
1083 * need to find our own interface address to
1084 * put in the bp_siaddr field of the reply.
1085 * If this server is multi-homed, pick the
1086 * 'best' interface (the one on the same net
1087 * as the client). Of course, the client may
1088 * be on the other side of a BOOTP gateway...
1090 ifr = getif(s, &dst);
1091 if (ifr) {
1092 struct sockaddr_in *sip;
1093 sip = (struct sockaddr_in *) &(ifr->ifr_addr);
1094 siaddr = sip->sin_addr;
1095 } else {
1096 /* Just use my "official" IP address. */
1097 siaddr = my_ip_addr;
1100 /* XXX - No need to set bp_giaddr here. */
1102 /* Finally, set the server address field. */
1103 bp->bp_siaddr = siaddr;
1105 /* Set up socket address for send. */
1106 send_addr.sin_family = AF_INET;
1107 send_addr.sin_port = htons(port);
1108 send_addr.sin_addr = dst;
1110 /* Send reply with same size packet as request used. */
1111 if (sendto(s, pktbuf, pktlen, 0,
1112 (struct sockaddr *) &send_addr,
1113 sizeof(send_addr)) < 0)
1115 report(LOG_ERR, "sendto: %s", get_network_errmsg());
1117 } /* sendreply */
1120 /* nmatch() - now in getif.c */
1121 /* setarp() - now in hwaddr.c */
1125 * This call checks read access to a file. It returns 0 if the file given
1126 * by "path" exists and is publically readable. A value of -1 is returned if
1127 * access is not permitted or an error occurs. Successful calls also
1128 * return the file size in bytes using the long pointer "filesize".
1130 * The read permission bit for "other" users is checked. This bit must be
1131 * set for tftpd(8) to allow clients to read the file.
1134 PRIVATE int
1135 chk_access(path, filesize)
1136 char *path;
1137 int32 *filesize;
1139 struct stat st;
1141 if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) {
1142 *filesize = (int32) st.st_size;
1143 return 0;
1144 } else {
1145 return -1;
1151 * Now in dumptab.c :
1152 * dumptab()
1153 * dump_host()
1154 * list_ipaddresses()
1157 #ifdef VEND_CMU
1160 * Insert the CMU "vendor" data for the host pointed to by "hp" into the
1161 * bootp packet pointed to by "bp".
1164 PRIVATE void
1165 dovend_cmu(bp, hp)
1166 struct bootp *bp;
1167 struct host *hp;
1169 struct cmu_vend *vendp;
1170 struct in_addr_list *taddr;
1173 * Initialize the entire vendor field to zeroes.
1175 bzero(bp->bp_vend, sizeof(bp->bp_vend));
1178 * Fill in vendor information. Subnet mask, default gateway,
1179 * domain name server, ien name server, time server
1181 vendp = (struct cmu_vend *) bp->bp_vend;
1182 strcpy(vendp->v_magic, (char *)vm_cmu);
1183 if (hp->flags.subnet_mask) {
1184 (vendp->v_smask).s_addr = hp->subnet_mask.s_addr;
1185 (vendp->v_flags) |= VF_SMASK;
1186 if (hp->flags.gateway) {
1187 (vendp->v_dgate).s_addr = hp->gateway->addr->s_addr;
1190 if (hp->flags.domain_server) {
1191 taddr = hp->domain_server;
1192 if (taddr->addrcount > 0) {
1193 (vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr;
1194 if (taddr->addrcount > 1) {
1195 (vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr;
1199 if (hp->flags.name_server) {
1200 taddr = hp->name_server;
1201 if (taddr->addrcount > 0) {
1202 (vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr;
1203 if (taddr->addrcount > 1) {
1204 (vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr;
1208 if (hp->flags.time_server) {
1209 taddr = hp->time_server;
1210 if (taddr->addrcount > 0) {
1211 (vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr;
1212 if (taddr->addrcount > 1) {
1213 (vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr;
1217 /* Log message now done by caller. */
1218 } /* dovend_cmu */
1220 #endif /* VEND_CMU */
1225 * Insert the RFC1048 vendor data for the host pointed to by "hp" into the
1226 * bootp packet pointed to by "bp".
1228 #define NEED(LEN, MSG) do \
1229 if (bytesleft < (LEN)) { \
1230 report(LOG_NOTICE, noroom, \
1231 hp->hostname->string, MSG); \
1232 return; \
1233 } while (0)
1234 PRIVATE void
1235 dovend_rfc1048(bp, hp, bootsize)
1236 struct bootp *bp;
1237 struct host *hp;
1238 int32 bootsize;
1240 int bytesleft, len;
1241 byte *vp;
1243 static const char noroom[] = "%s: No room for \"%s\" option";
1245 vp = bp->bp_vend;
1247 if (hp->flags.msg_size) {
1248 pktlen = hp->msg_size;
1249 } else {
1251 * If the request was longer than the official length, build
1252 * a response of that same length where the additional length
1253 * is assumed to be part of the bp_vend (options) area.
1255 if (pktlen > sizeof(*bp)) {
1256 if (debug > 1)
1257 report(LOG_INFO, "request message length=%d", pktlen);
1260 * Check whether the request contains the option:
1261 * Maximum DHCP Message Size (RFC1533 sec. 9.8)
1262 * and if so, override the response length with its value.
1263 * This request must lie within the first BP_VEND_LEN
1264 * bytes of the option space.
1267 byte *p, *ep;
1268 byte tag, len;
1269 short msgsz = 0;
1271 p = vp + 4;
1272 ep = p + BP_VEND_LEN - 4;
1273 while (p < ep) {
1274 tag = *p++;
1275 /* Check for tags with no data first. */
1276 if (tag == TAG_PAD)
1277 continue;
1278 if (tag == TAG_END)
1279 break;
1280 /* Now scan the length byte. */
1281 len = *p++;
1282 switch (tag) {
1283 case TAG_MAX_MSGSZ:
1284 if (len == 2) {
1285 bcopy(p, (char*)&msgsz, 2);
1286 msgsz = ntohs(msgsz);
1288 break;
1289 case TAG_SUBNET_MASK:
1290 /* XXX - Should preserve this if given... */
1291 break;
1292 } /* swtich */
1293 p += len;
1296 if (msgsz > sizeof(*bp) + BP_MSG_OVERHEAD) {
1297 if (debug > 1)
1298 report(LOG_INFO, "request has DHCP msglen=%d", msgsz);
1299 pktlen = msgsz - BP_MSG_OVERHEAD;
1304 if (pktlen < sizeof(*bp)) {
1305 report(LOG_ERR, "invalid response length=%d", pktlen);
1306 pktlen = sizeof(*bp);
1308 bytesleft = ((byte*)bp + pktlen) - vp;
1309 if (pktlen > sizeof(*bp)) {
1310 if (debug > 1)
1311 report(LOG_INFO, "extended reply, length=%d, options=%d",
1312 pktlen, bytesleft);
1315 /* Copy in the magic cookie */
1316 bcopy(vm_rfc1048, vp, 4);
1317 vp += 4;
1318 bytesleft -= 4;
1320 if (hp->flags.subnet_mask) {
1321 /* always enough room here. */
1322 *vp++ = TAG_SUBNET_MASK;/* -1 byte */
1323 *vp++ = 4; /* -1 byte */
1324 insert_u_long(hp->subnet_mask.s_addr, &vp); /* -4 bytes */
1325 bytesleft -= 6; /* Fix real count */
1326 if (hp->flags.gateway) {
1327 (void) insert_ip(TAG_GATEWAY,
1328 hp->gateway,
1329 &vp, &bytesleft);
1332 if (hp->flags.bootsize) {
1333 /* always enough room here */
1334 bootsize = (hp->flags.bootsize_auto) ?
1335 ((bootsize + 511) / 512) : (hp->bootsize); /* Round up */
1336 *vp++ = TAG_BOOT_SIZE;
1337 *vp++ = 2;
1338 *vp++ = (byte) ((bootsize >> 8) & 0xFF);
1339 *vp++ = (byte) (bootsize & 0xFF);
1340 bytesleft -= 4; /* Tag, length, and 16 bit blocksize */
1343 * This one is special: Remaining options go in the ext file.
1344 * Only the subnet_mask, bootsize, and gateway should precede.
1346 if (hp->flags.exten_file) {
1348 * Check for room for exten_file. Add 3 to account for
1349 * TAG_EXTEN_FILE, length, and TAG_END.
1351 len = strlen(hp->exten_file->string);
1352 NEED((len + 3), "ef");
1353 *vp++ = TAG_EXTEN_FILE;
1354 *vp++ = (byte) (len & 0xFF);
1355 bcopy(hp->exten_file->string, vp, len);
1356 vp += len;
1357 *vp++ = TAG_END;
1358 bytesleft -= len + 3;
1359 return; /* no more options here. */
1362 * The remaining options are inserted by the following
1363 * function (which is shared with bootpef.c).
1364 * Keep back one byte for the TAG_END.
1366 len = dovend_rfc1497(hp, vp, bytesleft - 1);
1367 vp += len;
1368 bytesleft -= len;
1370 /* There should be at least one byte left. */
1371 NEED(1, "(end)");
1372 *vp++ = TAG_END;
1373 bytesleft--;
1375 /* Log message done by caller. */
1376 if (bytesleft > 0) {
1378 * Zero out any remaining part of the vendor area.
1380 bzero(vp, bytesleft);
1382 } /* dovend_rfc1048 */
1383 #undef NEED
1387 * Now in readfile.c:
1388 * hwlookcmp()
1389 * iplookcmp()
1392 /* haddrtoa() - now in hwaddr.c */
1394 * Now in dovend.c:
1395 * insert_ip()
1396 * insert_generic()
1397 * insert_u_long()
1400 /* get_errmsg() - now in report.c */
1403 * Local Variables:
1404 * tab-width: 4
1405 * c-indent-level: 4
1406 * c-argdecl-indent: 4
1407 * c-continued-statement-offset: 4
1408 * c-continued-brace-offset: -4
1409 * c-label-offset: -4
1410 * c-brace-offset: 0
1411 * End: