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