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