2
Last updated : Mon Sep 16 15:20:57 1996
3
Modified by JSP from code by Charles Hawkins <ceh@eng.cam.ac.uk>,
5
J.S.Peatfield@damtp.cam.ac.uk
7
Copyright (c) University of Cambridge, 1993-1996
8
See the file NOTICE for conditions of use and distribution.
11
$Date: 1998/12/15 03:42:28 $
14
/* Standard headers */
16
#include <sys/types.h>
17
#include <sys/socket.h>
18
#include <sys/ioctl.h>
20
#include <netinet/in.h>
21
#include <netinet/ip.h>
22
#include <netinet/ip_icmp.h>
24
#include <arpa/inet.h>
26
#include <net/if_arp.h>
38
/* My global variables */
39
int bootp_verbose = 0 ; /* verbose mode or not 10/02/94 JSP */
40
int bootp_debug = 0 ; /* debug mode or not 14/02/94 JSP */
42
static int returniffail ; /* Return to the user if we fail */
43
static int printflag; /* Print control */
46
int performBootp(char *device,
51
struct ifreq *their_ifr,
58
struct sockaddr_in cli_addr, serv_addr;
59
struct bootp *bootp_xmit, *bootp_recv;
60
fd_set rfds, wfds, xfds;
61
struct timeval timeout ;
65
int retry_wait, waited=0 ;
68
int received_packet = 0 ;
69
/* See RFC1497, RFC1542 09/02/94 JSP */
70
unsigned char mincookie[] = {99,130,83,99,255} ;
75
/* zero structure before use */
76
memset((char *) &serv_addr, 0, sizeof(serv_addr));
78
serv_addr.sin_family = AF_INET;
79
serv_addr.sin_addr.s_addr = inet_addr(server) ;
80
serv_addr.sin_port = htons(IPPORT_BOOTPS);
82
if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
83
perror("bootpc: socket failed");
87
if (setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&one,sizeof(one))==-1) {
88
perror("bootpc: setsockopt failed");
92
memset((char *) &cli_addr, 0, sizeof(cli_addr));
93
cli_addr.sin_family = AF_INET;
94
cli_addr.sin_addr.s_addr = htonl(INADDR_ANY);
95
cli_addr.sin_port = htons(IPPORT_BOOTPC);
97
if(bind(sockfd, (struct sockaddr *) &cli_addr, sizeof(cli_addr)) < 0) {
98
perror("bootpc: bind failed");
102
/* allocate bootp packet before we use it */
103
bootp_xmit = (struct bootp *) malloc(BUFSIZ) ;
104
memset((char *) bootp_xmit, 0, BUFSIZ) ;
106
bootp_recv = (struct bootp *) malloc(BUFSIZ) ;
107
memset((char *) bootp_recv, 0, BUFSIZ) ;
109
/* Server needs to broadcast for me to see it */
110
if (broadcast || givenhwaddr)
111
bootp_xmit->bp_flags |= htons(BPFLAG_BROADCAST);
113
/* Don't do this if we were given the MAC address to use. 27/09/94 JSP */
115
/* Assuming ETHER if given HW */
116
ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER ;
117
ifr.ifr_hwaddr = their_ifr->ifr_hwaddr ;
119
/* Get the hardware address, and family information */
120
memcpy(ifr.ifr_name, device, strlen(device)+1);
121
if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) < 0) {
122
perror("bootpc: ioctl failed");
127
/* Check the network family if in NET3 or later, before NET3 you couldn't
128
examine this info (as far as I know.) */
130
/* set the htype field from the sa.family of the hardware address, if
131
this doesn't work for your interface type let me know please. */
133
bootp_xmit->bp_htype = ifr.ifr_hwaddr.sa_family;
134
bootp_xmit->bp_hlen = IFHWADDRLEN ; /* All MAC addresses are the same length */
137
logMessage("Got family=%d (Ether=%d)",
138
bootp_xmit->bp_htype, ARPHRD_ETHER);
140
/* If we have the time seed with it xor the hardware address, otherwise
141
use the hardware address 12/02/94 JSP */
142
if (gettimeofday(&tp, NULL) == -1)
145
rancopy = tp.tv_sec + tp.tv_usec ;
148
for (i=0; i < IFHWADDRLEN ; ++i) {
149
((unsigned char *)&rancopy)[ i % sizeof(rancopy) ] ^=
150
((unsigned char *)(ifr.use_hwaddr))[i] ;
152
/* and set the seed */
156
logMessage("hardware addr is :") ;
157
for (i=0; i < bootp_xmit->bp_hlen ; ++i)
158
logMessage("%2.2X ", ((unsigned char *)(ifr.use_hwaddr))[i]) ;
161
/* Now fill in the packet. */
162
bootp_xmit->bp_op = BOOTREQUEST ;
164
/* Now with my understanding of the bootp protocol we *should* just
165
need to copy the hwaddr over, but it seems that at least ARCNET
166
bootb servers are wird in this respect. So here is a switch in
167
case of other weirdness. JSP */
169
switch(bootp_xmit->bp_htype) {
170
/* ARCNET uses a "fake" ethernet address, with the ARCNET address at
171
the wrong end. At least the Novell bootp server on ARCNET assumes
172
this. Thanks to Tomasz Motylewski <motyl@tichy.ch.uj.edu.pl> for
175
memcpy(bootp_xmit->bp_chaddr+IFHWADDRLEN-1, (char *)(ifr.use_hwaddr), 1) ;
176
bootp_xmit->bp_htype=ARPHRD_ETHER;
177
bootp_xmit->bp_hlen=IFHWADDRLEN;
180
/* Add other network weirdness here */
182
/* For sensible networks the rest is normal */
184
memcpy(bootp_xmit->bp_chaddr,
185
(char *)(ifr.use_hwaddr),
186
bootp_xmit->bp_hlen) ;
189
/* Must start with zero here, see RFC1542 09/02/94 JSP */
190
bootp_xmit->bp_secs = 0;
192
/* Put in the minimal RFC1497 Magic cookie 09/02/94 JSP */
193
memcpy(bootp_xmit->bp_vend, mincookie, sizeof(mincookie));
195
/* Put the user precified bootfile name in place 12/02/94 */
196
memcpy(bootp_xmit->bp_file, bootfile, strlen(bootfile)+1);
198
/* put a random value in here, but keep a copy to check later 09/02/94 JSP */
199
bootp_xmit->bp_xid = rancopy = rand() ;
203
logMessage("BOOTPclient broadcast...");
205
while (((waited <= timeout_wait) && !received_packet) ||
206
((waited <= waitformore) && received_packet)) {
208
if (!received_packet) { /* Move this to a sendpacket function */
209
/* set time of this timeout 09/02/94 JSP */
210
bootp_xmit->bp_secs = waited ;
212
logMessage("."); fflush(stderr);
215
logMessage("Size = %ld", (long)sizeof(struct bootp)) ;
218
if(sendto(sockfd, bootp_xmit, sizeof(struct bootp), 0,
219
(struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
220
perror("bootpc: sendto");
225
/* Move rest of this loop to a receivepacket function */
227
/* The above was missing, thanks to
228
Gilles Detillieux <grdetil@cliff.scrc.UManitoba.CA> for pointing it out */
229
FD_SET(sockfd,&rfds);
233
/* Randomise the delays a little as suggested in RFC1542 09/02/94 JSP */
234
timeout.tv_sec = retry_wait + (1+(rand() & (retry_wait-1))) ;
236
waited += timeout.tv_sec ; /* Add this to the total time we have waited */
238
if(select(sockfd+1, &rfds, &wfds, &xfds, &timeout)<0) {
239
perror("bootpc: select");
243
if(!FD_ISSET(sockfd, &rfds)) {
244
retry_wait = retry_wait*2;
246
if ((plen = recvfrom(sockfd, bootp_recv, BUFSIZ, 0,
247
(struct sockaddr *)NULL, (int *)NULL)) < 0){
248
perror("bootpc: recvfrom");
253
logMessage("plen = %ld plen - sizeof(struct bootp) = %ld",
254
(long)plen, (long)(plen - sizeof(struct bootp))) ;
256
cookielength = 64 + plen - sizeof(struct bootp) ;
258
if (bootp_recv->bp_xid == rancopy) { /* is xid right */
259
if (!received_packet) {
260
/* If we haven't already recieved a packet then set the time to wait
261
further to be now + time user specified */
262
waitformore += waited ;
263
received_packet = 1 ;
265
/* To make it look a bit prettier */
266
if (printflag & BP_PRINT_OUT)
269
/* Pass the cookie info, the mincookie to look for and our address to
270
the cookie parser. It needs our address to get the network and
271
broadcast bits right if the SUBNET is defined in the cookie.
273
ParsePacket(bootp_recv,
277
/* xid mismatch so normally silently ignore */
279
logMessage("WARNING bp_xid mismatch got 0x%lx sent 0x%lx",
280
(long)bootp_recv->bp_xid, (long)rancopy) ;
285
if (!received_packet) {
286
logMessage("No response from BOOTP server");
292
return 0 ; /* Normal exit */
301
logMessage("In BootpFatal(), errno was %d", errno) ;
304
logMessage("bootpc failed to locate a network address") ;
308
logMessage(" Unable to locate an IP address for this host.\n"
309
" ***Please report this problem**\n\n"
310
" [Unable to continue]\n");
313
logMessage("Will now loop forerver, break out of this to fix") ;
316
/* your eyes are getting heavy.... */
321
/* Parse Magic cookies as specified in RFC1497, well only the bits we
322
are actually interested in... 09/02/94 JSP
324
void ParsePacket(struct bootp * bootp_recv,
326
unsigned char *match)
330
struct in_addr temp ;
331
unsigned char *cookie = (unsigned char *)(bootp_recv->bp_vend) ;
332
struct in_addr temp_addr, my_addr ;
334
temp_addr.s_addr = bootp_recv->bp_siaddr.s_addr ;
335
OutString("SERVER", (unsigned char *)inet_ntoa(temp_addr), -1);
336
my_addr.s_addr = bootp_recv->bp_yiaddr.s_addr ;
337
OutString("IPADDR", (unsigned char *)inet_ntoa(my_addr), -1);
339
logMessage("bp_file len is %d", strlen(bootp_recv->bp_file)) ;
341
OutString("BOOTFILE",
342
(unsigned char *)bootp_recv->bp_file, -1) ;
344
if (bootp_debug) { /* dump cookie contents in HEX 10/02/94 JSP */
345
for (i=0; i<cookielength; i++) {
347
logMessage("\n %2.2d :", i) ;
348
logMessage(" 0x%2.2X", cookie[i]) ;
353
/* Must get the same cookie back as we sent 09/02/94 JSP */
354
for (i=0; i < 4; ++i) {
355
if (cookie[i] != match[i]) {
357
logMessage("RFC1497 Cookie mismatch at offset %d", i) ;
363
logMessage("found valid RFC1497 cookie, parsing...") ;
365
/* Carry on after the cookie for other data 09/02/94 JSP */
366
while (i < cookielength) {
370
logMessage("cookie position %d is %d", i, tag) ;
372
/* If we arn't at the end of the cookie and we will need it extract len */
373
if ((i < cookielength - 1) && (tag != TAG_PAD) && (tag != TAG_END))
378
/* Warn if the "length" takes us out of the cookie and truncate */
379
if (len + i > cookielength) {
381
logMessage("TAG %d at %d. len %d, overrun %d",
382
cookie[i], i, len, i + len - cookielength) ;
383
/* And truncate in any case even with no warning */
384
len = cookielength - i ;
387
switch (cookie[i]) { /* The monster switch statement ... */
393
/* SUBNET we are in */
394
case TAG_SUBNET_MASK :
395
if (bootp_verbose && len != 4)
396
logMessage("WARNING len of tag 1 is %d not 4", len) ;
397
memcpy((char *)&temp, cookie + i + 2, 4) ;
398
OutString("NETMASK", (unsigned char *)inet_ntoa(temp), -1) ;
400
/* Both values are in network order so this doesn't care about the
401
ordering 10/02/94 JSP */
402
my_addr.s_addr &= temp.s_addr ;
403
OutString("NETWORK", (unsigned char *)inet_ntoa(my_addr), -1) ;
404
my_addr.s_addr |= ~temp.s_addr ;
405
OutString("BROADCAST", (unsigned char *)inet_ntoa(my_addr), -1) ;
407
/* defined so we know later that subnet info has been printed 11/02/94 JSP */
413
case TAG_TIME_OFFSET :
418
/* IP Gateways (routers) */
420
OutList("GATEWAYS", cookie+i+2, len) ;
424
/* Timeservers (see RFC-868) */
425
case TAG_TIME_SERVER :
426
OutList("TIMESRVS", cookie+i+2, len) ;
430
/* IEN-116 Nameservers */
431
case TAG_NAME_SERVER :
432
OutList("IEN116SRVS", cookie+i+2, len) ;
436
/* DNS Nameservers */
437
case TAG_DOMAIN_SERVER :
438
OutList("DNSSRVS", cookie+i+2, len) ;
442
/* LOGGING servers */
443
case TAG_LOG_SERVER :
444
OutList("LOGSRVS", cookie+i+2, len) ;
448
/* Quote of day/Cookie servers */
449
case TAG_COOKIE_SERVER :
450
OutList("QODSRVS", cookie+i+2, len) ;
455
case TAG_LPR_SERVER :
456
OutList("LPRSRVS", cookie+i+2, len) ;
460
/* Impress (Imogen) servers */
461
case TAG_IMPRESS_SERVER :
462
OutList("IMPRESSSRVS", cookie+i+2, len) ;
466
/* Remote Location Protocol servers */
467
case TAG_RLP_SERVER :
468
OutList("RLPSRVS", cookie+i+2, len) ;
472
/* HOSTNAME (may be fqdn or leaf) */
474
OutString("HOSTNAME", cookie+i+2, len) ;
478
/* BOOT File Size (ignored) */
483
/* Merit DUMP File name (ignored) */
489
case TAG_DOMAIN_NAME :
490
OutString("DOMAIN", cookie+i+2, len) ;
491
OutSearch("SEARCH", cookie+i+2, len) ;
495
/* SWAPServer address */
496
case TAG_SWAP_SERVER :
497
OutList("SWAPSRVR", cookie+i+2, len) ;
501
/* Root pathname to mount as root filesystem */
503
OutString("ROOT_PATH", cookie+i+2, len) ;
507
/* Extensions. Name of further Cookie data */
508
case TAG_EXTEN_FILE :
509
OutString("EXTEN_FILE", cookie+i+2, len) ;
513
/* NIS (formerly YP) domain name */
514
case TAG_NIS_DOMAIN :
515
OutString("YPDOMAIN", cookie+i+2, len) ;
519
/* NIS (formerly YP) server */
520
case TAG_NIS_SERVER :
521
OutList("YPSRVR", cookie+i+2, len) ;
526
case TAG_NTP_SERVER :
527
OutList("NTPSRVS", cookie+i+2, len) ;
531
/* END of cookie (phew) */
534
logMessage("end of cookie parsing, END tag found") ;
540
if (tag >= 128 && tag <= 254) /* reserved */
541
logMessage("Reserved TAG %d at %d (len %d)", tag, i, len) ;
543
logMessage("Unknown TAG %d at %d (len %d)", tag, i, len) ;
545
sprintf(name, "T%3.3d", tag) ;
546
OutString(name, cookie+i+2, len) ;
553
/* No SUBNET TAG in the cookie so we fake guess here, if this is wrong
554
then fix your bootp server to tell us the answer rather than
555
hacking this code. */
558
struct in_addr netmask ;
562
logMessage("Guessing netmask from IP address range") ;
564
type = ntohl(temp_addr.s_addr) ;
565
if ((type & 0x80000000) == 0) {
567
netmask.s_addr = htonl(0xFF000000) ;
568
} else if ((type & 0x40000000) == 0) {
570
netmask.s_addr = htonl(0xFFFF0000) ;
571
} else if ((type & 0x20000000) == 0) {
573
netmask.s_addr = htonl(0xFFFFFF00) ;
574
} else { /* GOD KNOWS... other classes are weird */
576
logMessage("IP number not Class A,B or C. Setting NETMASK to zero") ;
577
netmask.s_addr = htonl(0x00000000) ;
579
OutString("NETMASK", (unsigned char *)inet_ntoa(netmask), -1);
580
temp_addr.s_addr &= netmask.s_addr ;
581
OutString("NETWORK", (unsigned char *)inet_ntoa(temp_addr), -1);
582
temp_addr.s_addr |= ~netmask.s_addr ;
583
OutString("BROADCAST", (unsigned char *)inet_ntoa(temp_addr), -1);
588
/* Print out a list of IP addresses */
589
void OutList(char *name,
590
unsigned char *cookie,
593
struct in_addr temp ;
594
char lenv[BUFSIZ], *ptr ;
598
logMessage("%s found len=%d", name, len) ;
600
if ((len % 4) != 0) {
602
logMessage("ERROR %s length (%d) not 4 div", name, len) ;
605
if (len == 0) /* Nothing to do 10/02/94 JSP */
608
for (n=0,i=1 ; len; len -= 4, cookie += 4, i++) {
610
memcpy((char *)&temp, cookie, 4) ;
611
ptr = inet_ntoa(temp) ;
613
sprintf(lbuf, "%s_%d", name, i) ;
614
OutString(lbuf, (unsigned char *)ptr, c) ;
615
strncpy(lenv+n, ptr, c) ;
625
/* Prints the string passed */
626
void OutString(char *name,
627
unsigned char *cookie,
632
len = strlen((char *)cookie) ;
634
safecopy((unsigned char *)lenv, cookie, len);
638
/* Prints the string as usable in a DNS search. This is doing the
639
same as the old default BIND (pre 4.9.3) did with a DOMAIN line,
640
for backwards compatibility, and since BOOTP doesn't allow a way to
641
specify the search path explicitly */
642
void OutSearch(char *name,
643
unsigned char *cookie,
646
unsigned char *ptr, *nptr ;
647
unsigned char buf[258] ; /* Max len is 255 */
651
strncpy((char *)buf, (char *)(cookie), len) ;
652
buf[ len + 1 ] = 0 ; /* Null terminate it */
656
safecopy((unsigned char *)(lenv+n), ptr, len) ;
659
nptr = (unsigned char *)strchr((char *)ptr, '.') ; /* Cast cast cast */
661
len = 0 ; /* End of string I hope */
663
if (strchr((char *)nptr + 1, '.') == NULL) {
664
/* Trad to not use last component */
667
len -= (nptr - ptr) + 1 ;
678
/* Takes an address and returns useful bits of the name after lookup,
679
this was a seperate program, but it is more compact to have both
680
together. 17/02/94 JSP */
682
int in2host(char *address,
685
struct in_addr sin_addr;
691
/* convert to standard network form */
692
sin_addr.s_addr = inet_addr(address);
694
/* perform lookup, must have DNS running or have local hosts file at
697
hp = gethostbyaddr((char *)&sin_addr, sizeof(sin_addr), AF_INET) ;
700
perror ("bootpc: gethostbyaddr") ;
704
/* Print out a known name to stop repeated calls */
705
OutString("DONEIN2HOST",(unsigned char *)"1", -1) ;
707
/* Print out full name as returned by the call */
708
OutString("HOSTFULL", (unsigned char *)(hp->h_name), -1) ;
710
for(c=(char *)hp->h_name; *c ; ++c)
712
/* Zap first 'dot' to give leaf and domain names */
713
OutString("HOSTDOMAIN", (unsigned char *)(c+1), -1) ;
714
OutSearch("HOSTSEARCH", (unsigned char *)(c+1), strlen(c+1)) ;
716
OutString("HOSTLEAF", (unsigned char *)(hp->h_name), -1) ;
722
/* Copy those bits of a string which are alphanumeric or in a
723
"safe" list of characters. */
724
void safecopy(unsigned char *out,
725
unsigned char *string,
728
char safe[] = "./:-_=+[]~()%&*^#@! " ;
731
for (i =0 ; i < len; ++i) {
734
out[i] = c ; /* alphanumeric */
735
else { /* Not alphanumeric */
736
if (strchr(safe, c) != NULL) {
737
out[i] = c ; /* but safe */
739
out[i] = '?' ; /* NOT safe */
741
logMessage("Illegal char 0x%2.2X", c) ;
748
void doOut(char *name,
751
if (printflag & BP_PRINT_OUT) {
752
printf("%s='%s'\n", name, lenv) ;
754
if (printflag & BP_PUT_ENV) {
755
char envb[BUFSIZ], *envp ;
756
sprintf(envb, "BOOTP_%s=%s", name, lenv) ;
757
envp = strdup(envb) ;
759
logMessage("ENV setting :%s:", envp) ;