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