Merge from vendor branch BINUTILS:
[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  $DragonFly: src/libexec/bootpd/bootpd.c,v 1.2 2003/06/17 04:27:07 dillon Exp $
24
25 ************************************************************************/
26
27 /*
28  * BOOTP (bootstrap protocol) server daemon.
29  *
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
35  *
36  * HISTORY
37  *      See ./Changes
38  *
39  * BUGS
40  *      See ./ToDo
41  */
42
43 \f
44
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>
53
54 #include <net/if.h>
55 #include <netinet/in.h>
56 #include <arpa/inet.h>  /* inet_ntoa */
57
58 #ifndef NO_UNISTD
59 #include <unistd.h>
60 #endif
61
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>
72
73 #ifdef  NO_SETSID
74 # include <fcntl.h>             /* for O_RDONLY, etc */
75 #endif
76
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
84
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"
95
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
102
103 \f
104
105 /*
106  * Externals, forward declarations, and global variables
107  */
108
109 #ifdef  __STDC__
110 #define P(args) args
111 #else
112 #define P(args) ()
113 #endif
114
115 extern void dumptab P((char *));
116
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));
127
128 #undef  P
129
130 /*
131  * IP port numbers for client and server obtained from /etc/services
132  */
133
134 u_short bootps_port, bootpc_port;
135
136
137 /*
138  * Internet socket and interface config structures
139  */
140
141 struct sockaddr_in bind_addr;   /* Listening */
142 struct sockaddr_in recv_addr;   /* Packet source */
143 struct sockaddr_in send_addr;   /*  destination */
144
145
146 /*
147  * option defaults
148  */
149 int debug = 0;                                  /* Debugging flag (level) */
150 struct timeval actualtimeout =
151 {                                                               /* fifteen minutes */
152         15 * 60L,                                       /* tv_sec */
153         0                                                       /* tv_usec */
154 };
155
156 /*
157  * General
158  */
159
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;
166
167 static const char *hostname;
168 static char default_hostname[MAXHOSTNAMELEN];
169
170 /* Flags set by signal catcher. */
171 PRIVATE int do_readtab = 0;
172 PRIVATE int do_dumptab = 0;
173
174 /*
175  * Globals below are associated with the bootp database file (bootptab).
176  */
177
178 char *bootptab = CONFIG_FILE;
179 char *bootpd_dump = DUMPTAB_FILE;
180
181 \f
182
183 /*
184  * Initialization such as command-line processing is done and then the
185  * main server loop is started.
186  */
187
188 int
189 main(argc, argv)
190         int argc;
191         char **argv;
192 {
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
204
205         progname = strrchr(argv[0], '/');
206         if (progname) progname++;
207         else progname = argv[0];
208
209         /*
210          * Initialize logging.
211          */
212         report_init(0);                         /* uses progname */
213
214         /*
215          * Log startup
216          */
217         report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
218
219         /* Debugging for compilers with struct padding. */
220         assert(sizeof(struct bootp) == BP_MINPKTSZ);
221
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);
227         }
228         bp = (struct bootp *) pktbuf;
229
230         /*
231          * Check to see if a socket was passed to us from inetd.
232          *
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.
236          */
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) {
243                 /*
244                  * Descriptor 0 is a socket.  Assume we are a child of inetd.
245                  */
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");
252                 }
253         }
254
255         /*
256          * Set defaults that might be changed by option switches.
257          */
258         stmp = NULL;
259         timeout = &actualtimeout;
260
261         if (gethostname(default_hostname, sizeof(default_hostname) - 1) < 0) {
262                 report(LOG_ERR, "bootpd: can't get hostname\n");
263                 exit(1);
264         }
265         default_hostname[sizeof(default_hostname) - 1] = '\0';
266         hostname = default_hostname;
267
268         /*
269          * Read switches.
270          */
271         for (argc--, argv++; argc > 0; argc--, argv++) {
272                 if (argv[0][0] != '-')
273                         break;
274                 switch (argv[0][1]) {
275
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];
283                         }
284                         if (!stmp || (stmp[0] != '/')) {
285                                 report(LOG_ERR,
286                                                 "bootpd: invalid chdir specification\n");
287                                 break;
288                         }
289                         chdir_path = stmp;
290                         break;
291
292                 case 'd':                               /* debug level */
293                         if (argv[0][2]) {
294                                 stmp = &(argv[0][2]);
295                         } else if (argv[1] && argv[1][0] == '-') {
296                                 /*
297                                  * Backwards-compatible behavior:
298                                  * no parameter, so just increment the debug flag.
299                                  */
300                                 debug++;
301                                 break;
302                         } else {
303                                 argc--;
304                                 argv++;
305                                 stmp = argv[0];
306                         }
307                         if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
308                                 report(LOG_ERR,
309                                                 "%s: invalid debug level\n", progname);
310                                 break;
311                         }
312                         debug = n;
313                         break;
314
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];
322                         }
323                         if (!stmp) {
324                                 report(LOG_ERR,
325                                                 "bootpd: missing hostname\n");
326                                 break;
327                         }
328                         hostname = stmp;
329                         break;
330
331                 case 'i':                               /* inetd mode */
332                         standalone = FALSE;
333                         break;
334
335                 case 's':                               /* standalone mode */
336                         standalone = TRUE;
337                         break;
338
339                 case 't':                               /* timeout */
340                         if (argv[0][2]) {
341                                 stmp = &(argv[0][2]);
342                         } else {
343                                 argc--;
344                                 argv++;
345                                 stmp = argv[0];
346                         }
347                         if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
348                                 report(LOG_ERR,
349                                                 "%s: invalid timeout specification\n", progname);
350                                 break;
351                         }
352                         actualtimeout.tv_sec = (int32) (60 * n);
353                         /*
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.
357                          */
358                         timeout = (n > 0) ? &actualtimeout : NULL;
359                         break;
360
361                 default:
362                         report(LOG_ERR, "%s: unknown switch: -%c\n",
363                                         progname, argv[0][1]);
364                         usage();
365                         break;
366
367                 } /* switch */
368         } /* for args */
369
370         /*
371          * Override default file names if specified on the command line.
372          */
373         if (argc > 0)
374                 bootptab = argv[0];
375
376         if (argc > 1)
377                 bootpd_dump = argv[1];
378
379         /*
380          * Get my hostname and IP address.
381          */
382
383         hep = gethostbyname(hostname);
384         if (!hep) {
385                 report(LOG_ERR, "Can not get my IP address\n");
386                 exit(1);
387         }
388         bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
389
390         if (standalone) {
391                 /*
392                  * Go into background and disassociate from controlling terminal.
393                  */
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);
404                         }
405 #endif  /* TIOCNOTTY */
406 #else   /* SETSID */
407                         if (setsid() < 0)
408                                 perror("setsid");
409 #endif  /* SETSID */
410                 } /* if debug < 3 */
411
412                 /*
413                  * Nuke any timeout value
414                  */
415                 timeout = NULL;
416
417         } /* if standalone (1st) */
418
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);
423         }
424
425         /* Get the timezone. */
426         tzone_init();
427
428         /* Allocate hash tables. */
429         rdtab_init();
430
431         /*
432          * Read the bootptab file.
433          */
434         readtab(1);                                     /* force read */
435
436         if (standalone) {
437
438                 /*
439                  * Create a socket.
440                  */
441                 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
442                         report(LOG_ERR, "socket: %s", get_network_errmsg());
443                         exit(1);
444                 }
445
446                 /*
447                  * Get server's listening port number
448                  */
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);
457                 }
458
459                 /*
460                  * Bind socket to BOOTPS port.
461                  */
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)
467                 {
468                         report(LOG_ERR, "bind: %s", get_network_errmsg());
469                         exit(1);
470                 }
471         } /* if standalone (2nd)*/
472
473         /*
474          * Get destination port number so we can reply to client
475          */
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;
484         }
485
486         /*
487          * Set up signals to read or dump the table.
488          */
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);
496         }
497         if (sigaction(SIGUSR1, &sa, NULL) < 0) {
498                 report(LOG_ERR, "sigaction: %s", get_errmsg());
499                 exit(1);
500         }
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);
506         }
507         if ((int) signal(SIGUSR1, catcher) < 0) {
508                 report(LOG_ERR, "signal: %s", get_errmsg());
509                 exit(1);
510         }
511 #endif  /* SA_NOCLDSTOP */
512
513         /*
514          * Process incoming requests.
515          */
516         for (;;) {
517                 struct timeval tv;
518
519                 readfds = 1 << s;
520                 if (timeout)
521                         tv = *timeout;
522
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());
528                         }
529                         /*
530                          * Call readtab() or dumptab() here to avoid the
531                          * dangers of doing I/O from a signal handler.
532                          */
533                         if (do_readtab) {
534                                 do_readtab = 0;
535                                 readtab(1);             /* force read */
536                         }
537                         if (do_dumptab) {
538                                 do_dumptab = 0;
539                                 dumptab(bootpd_dump);
540                         }
541                         continue;
542                 }
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);
548                 }
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;
554                 }
555                 if (debug > 1) {
556                         report(LOG_INFO, "recvd pkt from IP addr %s",
557                                    inet_ntoa(recv_addr.sin_addr));
558                 }
559                 if (n < sizeof(struct bootp)) {
560                         if (debug) {
561                                 report(LOG_NOTICE, "received short packet");
562                         }
563                         continue;
564                 }
565                 pktlen = n;
566
567                 readtab(0);                             /* maybe re-read bootptab */
568
569                 switch (bp->bp_op) {
570                 case BOOTREQUEST:
571                         handle_request();
572                         break;
573                 case BOOTREPLY:
574                         handle_reply();
575                         break;
576                 }
577         }
578         return 0;
579 }
580
581 \f
582
583
584 /*
585  * Print "usage" message and exit
586  */
587
588 PRIVATE void
589 usage()
590 {
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);
599 }
600
601 /* Signal catchers */
602 PRIVATE void
603 catcher(sig)
604         int sig;
605 {
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
614 }
615
616 \f
617
618 /*
619  * Process BOOTREQUEST packet.
620  *
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.
624  *
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.)
628  */
629 PRIVATE void
630 handle_request()
631 {
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;
642
643         bp->bp_file[sizeof(bp->bp_file)-1] = '\0';
644
645         /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
646
647         /*
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.
651          */
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;
661                 }
662         } else {
663                 strcpy(bp->bp_sname, hostname);
664         }
665
666         /* Convert the request into a reply. */
667         bp->bp_op = BOOTREPLY;
668         if (bp->bp_ciaddr.s_addr == 0) {
669                 /*
670                  * client doesnt know his IP address,
671                  * search by hardware address.
672                  */
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));
677                 }
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));
683                 }
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)
691                 {
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));
698                         }
699                         hashcode = hash_HashFunction(dummyhost.haddr, hlen);
700                         hp = (struct host *) hash_Lookup(hwhashtable, hashcode,
701                                                                                          hwlookcmp, &dummyhost);
702                 }
703                 if (hp == NULL) {
704                         /*
705                          * XXX - Add dynamic IP address assignment?
706                          */
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 */
712                 }
713                 (bp->bp_yiaddr).s_addr = hp->iaddr.s_addr;
714
715         } else {
716
717                 /*
718                  * search by IP address.
719                  */
720                 if (debug > 1) {
721                         report(LOG_INFO, "request from IP addr %s",
722                                    inet_ntoa(bp->bp_ciaddr));
723                 }
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));
732                         }
733                         return;
734                 }
735         }
736
737         if (debug) {
738                 report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr),
739                            hp->hostname->string);
740         }
741
742         /*
743          * If there is a response delay threshold, ignore requests
744          * with a timestamp lower than the threshold.
745          */
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;
754                 }
755         }
756
757 #ifdef  YORK_EX_OPTION
758         /*
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>
763          *
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
767          */
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... */
779         }
780 #endif  /* YORK_EX_OPTION */
781
782         /*
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
786          */
787         (bp->bp_siaddr).s_addr = (hp->flags.bootserver) ?
788                 hp->bootserver.s_addr : 0L;
789
790 #ifdef  STANFORD_PROM_COMPAT
791         /*
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.)
798          */
799         if (strcmp(bp->bp_file, "sunboot14") == 0)
800                 bp->bp_file[0] = '\0';  /* treat it as unspecified */
801 #endif
802
803         /*
804          * Fill in the client's proper bootfile.
805          *
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.
809          *
810          * If the client specifies a null or relative file, use the following
811          * table to determine the appropriate action:
812          *
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
824          *
825          */
826
827         /*
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.
832          *
833          * Therefore, file accessibility is now demanded ONLY if you
834          * define CHECK_FILE_ACCESS in the Makefile options. -gwr
835          */
836
837         /*
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).
841          */
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;
848         }
849
850         /*
851          * Determine client's requested homedir and bootfile.
852          */
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;
866                 }
867                 if (debug > 2) {
868                         report(LOG_INFO, "requested path=\"%s\"  file=\"%s\"",
869                                    (homedir) ? homedir : "",
870                                    (bootfile) ? bootfile : "");
871                 }
872         }
873
874         /*
875          * Specifications in bootptab override client requested values.
876          */
877         if (hp->flags.homedir)
878                 homedir = hp->homedir->string;
879         if (hp->flags.bootfile)
880                 bootfile = hp->bootfile->string;
881
882         /*
883          * Construct bootfile path.
884          */
885         if (homedir) {
886                 if (homedir[0] != '/')
887                         strcat(clntpath, "/");
888                 strcat(clntpath, homedir);
889                 homedir = NULL;
890         }
891         if (bootfile) {
892                 if (bootfile[0] != '/')
893                         strcat(clntpath, "/");
894                 strcat(clntpath, bootfile);
895                 bootfile = NULL;
896         }
897
898         /*
899          * First try to find the file with a ".host" suffix
900          */
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
909
910                         if (bp->bp_file[0]) {
911                                 /*
912                                  * Client wanted specific file
913                                  * and we didn't have it.
914                                  */
915                                 report(LOG_NOTICE,
916                                            "requested file not found: \"%s\"", clntpath);
917                                 return;
918                         }
919                         /*
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.
923                          */
924                         bzero(bp->bp_file, sizeof(bp->bp_file));
925                         goto null_file_name;
926
927 #else   /* CHECK_FILE_ACCESS */
928
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);
933                         }
934
935 #endif  /* CHECK_FILE_ACCESS */
936                 }
937         }
938         strncpy(bp->bp_file, clntpath, BP_FILE_LEN);
939         if (debug > 2)
940                 report(LOG_INFO, "bootfile=\"%s\"", clntpath);
941
942 #ifdef  CHECK_FILE_ACCESS
943 null_file_name:
944 #endif  /* CHECK_FILE_ACCESS */
945
946 \f
947         /*
948          * Handle vendor options based on magic number.
949          */
950
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]));
957         }
958         /*
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.
962          */
963         if (hp->flags.vm_cookie) {
964                 /* Slam in the user specified magic number. */
965                 bcopy(hp->vm_cookie, bp->bp_vend, 4);
966         }
967         /*
968          * Figure out the format for the vendor-specific info.
969          * Note that bp->bp_vend may have been set above.
970          */
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)");
976                 }
977         }
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)");
983                 }
984         }
985 #endif
986         else {
987                 if (debug > 1) {
988                         report(LOG_INFO, "sending reply (with no options)");
989                 }
990         }
991
992         dest = (hp->flags.reply_addr) ?
993                 hp->reply_addr.s_addr : 0L;
994
995         /* not forwarded */
996         sendreply(0, dest);
997 }
998
999
1000 /*
1001  * Process BOOTREPLY packet.
1002  */
1003 PRIVATE void
1004 handle_reply()
1005 {
1006         if (debug) {
1007                 report(LOG_INFO, "processing boot reply");
1008         }
1009         /* forwarded, no destination override */
1010         sendreply(1, 0);
1011 }
1012
1013
1014 /*
1015  * Send a reply packet to the client.  'forward' flag is set if we are
1016  * not the originator of this reply packet.
1017  */
1018 PRIVATE void
1019 sendreply(forward, dst_override)
1020         int forward;
1021         int32 dst_override;
1022 {
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;
1028
1029         /*
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.
1033          */
1034
1035         /*
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.
1044          */
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));
1050                 }
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));
1059                 }
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;
1069
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);
1074         }
1075
1076         if ((forward == 0) &&
1077                 (bp->bp_siaddr.s_addr == 0))
1078         {
1079                 struct ifreq *ifr;
1080                 struct in_addr siaddr;
1081                 /*
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...
1089                  */
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;
1098                 }
1099
1100                 /* XXX - No need to set bp_giaddr here. */
1101
1102                 /* Finally, set the server address field. */
1103                 bp->bp_siaddr = siaddr;
1104         }
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;
1109
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)
1114         {
1115                 report(LOG_ERR, "sendto: %s", get_network_errmsg());
1116         }
1117 } /* sendreply */
1118 \f
1119
1120 /* nmatch() - now in getif.c */
1121 /* setarp() - now in hwaddr.c */
1122
1123
1124 /*
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".
1129  *
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.
1132  */
1133
1134 PRIVATE int
1135 chk_access(path, filesize)
1136         char *path;
1137         int32 *filesize;
1138 {
1139         struct stat st;
1140
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;
1146         }
1147 }
1148 \f
1149
1150 /*
1151  * Now in dumptab.c :
1152  *      dumptab()
1153  *      dump_host()
1154  *      list_ipaddresses()
1155  */
1156
1157 #ifdef VEND_CMU
1158
1159 /*
1160  * Insert the CMU "vendor" data for the host pointed to by "hp" into the
1161  * bootp packet pointed to by "bp".
1162  */
1163
1164 PRIVATE void
1165 dovend_cmu(bp, hp)
1166         struct bootp *bp;
1167         struct host *hp;
1168 {
1169         struct cmu_vend *vendp;
1170         struct in_addr_list *taddr;
1171
1172         /*
1173          * Initialize the entire vendor field to zeroes.
1174          */
1175         bzero(bp->bp_vend, sizeof(bp->bp_vend));
1176
1177         /*
1178          * Fill in vendor information. Subnet mask, default gateway,
1179          * domain name server, ien name server, time server
1180          */
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;
1188                 }
1189         }
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;
1196                         }
1197                 }
1198         }
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;
1205                         }
1206                 }
1207         }
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;
1214                         }
1215                 }
1216         }
1217         /* Log message now done by caller. */
1218 } /* dovend_cmu */
1219
1220 #endif /* VEND_CMU */
1221 \f
1222
1223
1224 /*
1225  * Insert the RFC1048 vendor data for the host pointed to by "hp" into the
1226  * bootp packet pointed to by "bp".
1227  */
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;
1239 {
1240         int bytesleft, len;
1241         byte *vp;
1242
1243         static const char noroom[] = "%s: No room for \"%s\" option";
1244
1245         vp = bp->bp_vend;
1246
1247         if (hp->flags.msg_size) {
1248                 pktlen = hp->msg_size;
1249         } else {
1250                 /*
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.
1254                  */
1255                 if (pktlen > sizeof(*bp)) {
1256                         if (debug > 1)
1257                                 report(LOG_INFO, "request message length=%d", pktlen);
1258                 }
1259                 /*
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.
1265                  */
1266                 {
1267                         byte *p, *ep;
1268                         byte tag, len;
1269                         short msgsz = 0;
1270
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);
1287                                         }
1288                                         break;
1289                                 case TAG_SUBNET_MASK:
1290                                         /* XXX - Should preserve this if given... */
1291                                         break;
1292                                 } /* swtich */
1293                                 p += len;
1294                         }
1295
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;
1300                         }
1301                 }
1302         }
1303
1304         if (pktlen < sizeof(*bp)) {
1305                 report(LOG_ERR, "invalid response length=%d", pktlen);
1306                 pktlen = sizeof(*bp);
1307         }
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);
1313         }
1314
1315         /* Copy in the magic cookie */
1316         bcopy(vm_rfc1048, vp, 4);
1317         vp += 4;
1318         bytesleft -= 4;
1319
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);
1330                 }
1331         }
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 */
1341         }
1342         /*
1343          * This one is special: Remaining options go in the ext file.
1344          * Only the subnet_mask, bootsize, and gateway should precede.
1345          */
1346         if (hp->flags.exten_file) {
1347                 /*
1348                  * Check for room for exten_file.  Add 3 to account for
1349                  * TAG_EXTEN_FILE, length, and TAG_END.
1350                  */
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. */
1360         }
1361         /*
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.
1365          */
1366         len = dovend_rfc1497(hp, vp, bytesleft - 1);
1367         vp += len;
1368         bytesleft -= len;
1369
1370         /* There should be at least one byte left. */
1371         NEED(1, "(end)");
1372         *vp++ = TAG_END;
1373         bytesleft--;
1374
1375         /* Log message done by caller. */
1376         if (bytesleft > 0) {
1377                 /*
1378                  * Zero out any remaining part of the vendor area.
1379                  */
1380                 bzero(vp, bytesleft);
1381         }
1382 } /* dovend_rfc1048 */
1383 #undef  NEED
1384 \f
1385
1386 /*
1387  * Now in readfile.c:
1388  *      hwlookcmp()
1389  *      iplookcmp()
1390  */
1391
1392 /* haddrtoa() - now in hwaddr.c */
1393 /*
1394  * Now in dovend.c:
1395  * insert_ip()
1396  * insert_generic()
1397  * insert_u_long()
1398  */
1399
1400 /* get_errmsg() - now in report.c */
1401
1402 /*
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:
1412  */