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