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