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