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