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