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