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