Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/com/sun/jndi/dns/DnsClient.java
38924 views
1
/*
2
* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package com.sun.jndi.dns;
27
28
import java.io.IOException;
29
import java.net.DatagramSocket;
30
import java.net.DatagramPacket;
31
import java.net.InetAddress;
32
import java.net.Socket;
33
import java.security.SecureRandom;
34
import javax.naming.*;
35
36
import java.util.Collections;
37
import java.util.Map;
38
import java.util.HashMap;
39
40
import sun.security.jca.JCAUtil;
41
42
// Some of this code began life as part of sun.javaos.net.DnsClient
43
// originally by sritchie@eng 1/96. It was first hacked up for JNDI
44
// use by caveh@eng 6/97.
45
46
47
/**
48
* The DnsClient class performs DNS client operations in support of DnsContext.
49
*
50
*/
51
52
public class DnsClient {
53
54
// DNS packet header field offsets
55
private static final int IDENT_OFFSET = 0;
56
private static final int FLAGS_OFFSET = 2;
57
private static final int NUMQ_OFFSET = 4;
58
private static final int NUMANS_OFFSET = 6;
59
private static final int NUMAUTH_OFFSET = 8;
60
private static final int NUMADD_OFFSET = 10;
61
private static final int DNS_HDR_SIZE = 12;
62
63
// DNS response codes
64
private static final int NO_ERROR = 0;
65
private static final int FORMAT_ERROR = 1;
66
private static final int SERVER_FAILURE = 2;
67
private static final int NAME_ERROR = 3;
68
private static final int NOT_IMPL = 4;
69
private static final int REFUSED = 5;
70
71
private static final String[] rcodeDescription = {
72
"No error",
73
"DNS format error",
74
"DNS server failure",
75
"DNS name not found",
76
"DNS operation not supported",
77
"DNS service refused"
78
};
79
80
private static final int DEFAULT_PORT = 53;
81
private static final int TRANSACTION_ID_BOUND = 0x10000;
82
private static final SecureRandom random = JCAUtil.getSecureRandom();
83
private InetAddress[] servers;
84
private int[] serverPorts;
85
private int timeout; // initial timeout on UDP queries in ms
86
private int retries; // number of UDP retries
87
88
private final Object udpSocketLock = new Object();
89
private static final DNSDatagramSocketFactory factory =
90
new DNSDatagramSocketFactory(random);
91
92
// Requests sent
93
private Map<Integer, ResourceRecord> reqs;
94
95
// Responses received
96
private Map<Integer, byte[]> resps;
97
98
//-------------------------------------------------------------------------
99
100
/*
101
* Each server is of the form "server[:port]". IPv6 literal host names
102
* include delimiting brackets.
103
* "timeout" is the initial timeout interval (in ms) for UDP queries,
104
* and "retries" gives the number of retries per server.
105
*/
106
public DnsClient(String[] servers, int timeout, int retries)
107
throws NamingException {
108
this.timeout = timeout;
109
this.retries = retries;
110
this.servers = new InetAddress[servers.length];
111
serverPorts = new int[servers.length];
112
113
for (int i = 0; i < servers.length; i++) {
114
115
// Is optional port given?
116
int colon = servers[i].indexOf(':',
117
servers[i].indexOf(']') + 1);
118
119
serverPorts[i] = (colon < 0)
120
? DEFAULT_PORT
121
: Integer.parseInt(servers[i].substring(colon + 1));
122
String server = (colon < 0)
123
? servers[i]
124
: servers[i].substring(0, colon);
125
try {
126
this.servers[i] = InetAddress.getByName(server);
127
} catch (java.net.UnknownHostException e) {
128
NamingException ne = new ConfigurationException(
129
"Unknown DNS server: " + server);
130
ne.setRootCause(e);
131
throw ne;
132
}
133
}
134
reqs = Collections.synchronizedMap(
135
new HashMap<Integer, ResourceRecord>());
136
resps = Collections.synchronizedMap(new HashMap<Integer, byte[]>());
137
}
138
139
DatagramSocket getDatagramSocket() throws NamingException {
140
try {
141
return factory.open();
142
} catch (java.net.SocketException e) {
143
NamingException ne = new ConfigurationException();
144
ne.setRootCause(e);
145
throw ne;
146
}
147
}
148
149
protected void finalize() {
150
close();
151
}
152
153
// A lock to access the request and response queues in tandem.
154
private Object queuesLock = new Object();
155
156
public void close() {
157
synchronized (queuesLock) {
158
reqs.clear();
159
resps.clear();
160
}
161
}
162
163
/*
164
* If recursion is true, recursion is requested on the query.
165
* If auth is true, only authoritative responses are accepted; other
166
* responses throw NameNotFoundException.
167
*/
168
ResourceRecords query(DnsName fqdn, int qclass, int qtype,
169
boolean recursion, boolean auth)
170
throws NamingException {
171
172
int xid;
173
Packet pkt;
174
ResourceRecord collision;
175
176
do {
177
// Generate a random transaction ID
178
xid = random.nextInt(TRANSACTION_ID_BOUND);
179
pkt = makeQueryPacket(fqdn, xid, qclass, qtype, recursion);
180
181
// enqueue the outstanding request
182
collision = reqs.putIfAbsent(xid, new ResourceRecord(pkt.getData(),
183
pkt.length(), Header.HEADER_SIZE, true, false));
184
185
} while (collision != null);
186
187
Exception caughtException = null;
188
boolean[] doNotRetry = new boolean[servers.length];
189
190
try {
191
//
192
// The UDP retry strategy is to try the 1st server, and then
193
// each server in order. If no answer, double the timeout
194
// and try each server again.
195
//
196
for (int retry = 0; retry < retries; retry++) {
197
198
// Try each name server.
199
for (int i = 0; i < servers.length; i++) {
200
if (doNotRetry[i]) {
201
continue;
202
}
203
204
// send the request packet and wait for a response.
205
try {
206
if (debug) {
207
dprint("SEND ID (" + (retry + 1) + "): " + xid);
208
}
209
210
byte[] msg = null;
211
msg = doUdpQuery(pkt, servers[i], serverPorts[i],
212
retry, xid);
213
//
214
// If the matching response is not got within the
215
// given timeout, check if the response was enqueued
216
// by some other thread, if not proceed with the next
217
// server or retry.
218
//
219
if (msg == null) {
220
if (resps.size() > 0) {
221
msg = lookupResponse(xid);
222
}
223
if (msg == null) { // try next server or retry
224
continue;
225
}
226
}
227
Header hdr = new Header(msg, msg.length);
228
229
if (auth && !hdr.authoritative) {
230
caughtException = new NameNotFoundException(
231
"DNS response not authoritative");
232
doNotRetry[i] = true;
233
continue;
234
}
235
if (hdr.truncated) { // message is truncated -- try TCP
236
237
// Try each server, starting with the one that just
238
// provided the truncated message.
239
for (int j = 0; j < servers.length; j++) {
240
int ij = (i + j) % servers.length;
241
if (doNotRetry[ij]) {
242
continue;
243
}
244
try {
245
Tcp tcp =
246
new Tcp(servers[ij], serverPorts[ij]);
247
byte[] msg2;
248
try {
249
msg2 = doTcpQuery(tcp, pkt);
250
} finally {
251
tcp.close();
252
}
253
Header hdr2 = new Header(msg2, msg2.length);
254
if (hdr2.query) {
255
throw new CommunicationException(
256
"DNS error: expecting response");
257
}
258
checkResponseCode(hdr2);
259
260
if (!auth || hdr2.authoritative) {
261
// Got a valid response
262
hdr = hdr2;
263
msg = msg2;
264
break;
265
} else {
266
doNotRetry[ij] = true;
267
}
268
} catch (Exception e) {
269
// Try next server, or use UDP response
270
}
271
} // servers
272
}
273
return new ResourceRecords(msg, msg.length, hdr, false);
274
275
} catch (IOException e) {
276
if (debug) {
277
dprint("Caught IOException:" + e);
278
}
279
if (caughtException == null) {
280
caughtException = e;
281
}
282
// Use reflection to allow pre-1.4 compilation.
283
// This won't be needed much longer.
284
if (e.getClass().getName().equals(
285
"java.net.PortUnreachableException")) {
286
doNotRetry[i] = true;
287
}
288
} catch (NameNotFoundException e) {
289
// This is authoritative, so return immediately
290
throw e;
291
} catch (CommunicationException e) {
292
if (caughtException == null) {
293
caughtException = e;
294
}
295
} catch (NamingException e) {
296
if (caughtException == null) {
297
caughtException = e;
298
}
299
doNotRetry[i] = true;
300
}
301
} // servers
302
} // retries
303
304
} finally {
305
reqs.remove(xid); // cleanup
306
}
307
308
if (caughtException instanceof NamingException) {
309
throw (NamingException) caughtException;
310
}
311
// A network timeout or other error occurred.
312
NamingException ne = new CommunicationException("DNS error");
313
ne.setRootCause(caughtException);
314
throw ne;
315
}
316
317
ResourceRecords queryZone(DnsName zone, int qclass, boolean recursion)
318
throws NamingException {
319
320
int xid = random.nextInt(TRANSACTION_ID_BOUND);
321
322
Packet pkt = makeQueryPacket(zone, xid, qclass,
323
ResourceRecord.QTYPE_AXFR, recursion);
324
Exception caughtException = null;
325
326
// Try each name server.
327
for (int i = 0; i < servers.length; i++) {
328
try {
329
Tcp tcp = new Tcp(servers[i], serverPorts[i]);
330
byte[] msg;
331
try {
332
msg = doTcpQuery(tcp, pkt);
333
Header hdr = new Header(msg, msg.length);
334
// Check only rcode as per
335
// draft-ietf-dnsext-axfr-clarify-04
336
checkResponseCode(hdr);
337
ResourceRecords rrs =
338
new ResourceRecords(msg, msg.length, hdr, true);
339
if (rrs.getFirstAnsType() != ResourceRecord.TYPE_SOA) {
340
throw new CommunicationException(
341
"DNS error: zone xfer doesn't begin with SOA");
342
}
343
344
if (rrs.answer.size() == 1 ||
345
rrs.getLastAnsType() != ResourceRecord.TYPE_SOA) {
346
// The response is split into multiple DNS messages.
347
do {
348
msg = continueTcpQuery(tcp);
349
if (msg == null) {
350
throw new CommunicationException(
351
"DNS error: incomplete zone transfer");
352
}
353
hdr = new Header(msg, msg.length);
354
checkResponseCode(hdr);
355
rrs.add(msg, msg.length, hdr);
356
} while (rrs.getLastAnsType() !=
357
ResourceRecord.TYPE_SOA);
358
}
359
360
// Delete the duplicate SOA record.
361
rrs.answer.removeElementAt(rrs.answer.size() - 1);
362
return rrs;
363
364
} finally {
365
tcp.close();
366
}
367
368
} catch (IOException e) {
369
caughtException = e;
370
} catch (NameNotFoundException e) {
371
throw e;
372
} catch (NamingException e) {
373
caughtException = e;
374
}
375
}
376
if (caughtException instanceof NamingException) {
377
throw (NamingException) caughtException;
378
}
379
NamingException ne = new CommunicationException(
380
"DNS error during zone transfer");
381
ne.setRootCause(caughtException);
382
throw ne;
383
}
384
385
386
/**
387
* Tries to retreive an UDP packet matching the given xid
388
* received within the timeout.
389
* If a packet with different xid is received, the received packet
390
* is enqueued with the corresponding xid in 'resps'.
391
*/
392
private byte[] doUdpQuery(Packet pkt, InetAddress server,
393
int port, int retry, int xid)
394
throws IOException, NamingException {
395
396
int minTimeout = 50; // msec after which there are no retries.
397
398
synchronized (udpSocketLock) {
399
try (DatagramSocket udpSocket = getDatagramSocket()) {
400
DatagramPacket opkt = new DatagramPacket(
401
pkt.getData(), pkt.length(), server, port);
402
DatagramPacket ipkt = new DatagramPacket(new byte[8000], 8000);
403
// Packets may only be sent to or received from this server address
404
udpSocket.connect(server, port);
405
int pktTimeout = (timeout * (1 << retry));
406
try {
407
udpSocket.send(opkt);
408
409
// timeout remaining after successive 'receive()'
410
int timeoutLeft = pktTimeout;
411
int cnt = 0;
412
do {
413
if (debug) {
414
cnt++;
415
dprint("Trying RECEIVE(" +
416
cnt + ") retry(" + (retry + 1) +
417
") for:" + xid + " sock-timeout:" +
418
timeoutLeft + " ms.");
419
}
420
udpSocket.setSoTimeout(timeoutLeft);
421
long start = System.currentTimeMillis();
422
udpSocket.receive(ipkt);
423
long end = System.currentTimeMillis();
424
425
byte[] data = ipkt.getData();
426
if (isMatchResponse(data, xid)) {
427
return data;
428
}
429
timeoutLeft = pktTimeout - ((int) (end - start));
430
} while (timeoutLeft > minTimeout);
431
432
} finally {
433
udpSocket.disconnect();
434
}
435
return null; // no matching packet received within the timeout
436
}
437
}
438
}
439
440
/*
441
* Sends a TCP query, and returns the first DNS message in the response.
442
*/
443
private byte[] doTcpQuery(Tcp tcp, Packet pkt) throws IOException {
444
445
int len = pkt.length();
446
// Send 2-byte message length, then send message.
447
tcp.out.write(len >> 8);
448
tcp.out.write(len);
449
tcp.out.write(pkt.getData(), 0, len);
450
tcp.out.flush();
451
452
byte[] msg = continueTcpQuery(tcp);
453
if (msg == null) {
454
throw new IOException("DNS error: no response");
455
}
456
return msg;
457
}
458
459
/*
460
* Returns the next DNS message from the TCP socket, or null on EOF.
461
*/
462
private byte[] continueTcpQuery(Tcp tcp) throws IOException {
463
464
int lenHi = tcp.in.read(); // high-order byte of response length
465
if (lenHi == -1) {
466
return null; // EOF
467
}
468
int lenLo = tcp.in.read(); // low-order byte of response length
469
if (lenLo == -1) {
470
throw new IOException("Corrupted DNS response: bad length");
471
}
472
int len = (lenHi << 8) | lenLo;
473
byte[] msg = new byte[len];
474
int pos = 0; // next unfilled position in msg
475
while (len > 0) {
476
int n = tcp.in.read(msg, pos, len);
477
if (n == -1) {
478
throw new IOException(
479
"Corrupted DNS response: too little data");
480
}
481
len -= n;
482
pos += n;
483
}
484
return msg;
485
}
486
487
private Packet makeQueryPacket(DnsName fqdn, int xid,
488
int qclass, int qtype, boolean recursion) {
489
int qnameLen = fqdn.getOctets();
490
int pktLen = DNS_HDR_SIZE + qnameLen + 4;
491
Packet pkt = new Packet(pktLen);
492
493
short flags = recursion ? Header.RD_BIT : 0;
494
495
pkt.putShort(xid, IDENT_OFFSET);
496
pkt.putShort(flags, FLAGS_OFFSET);
497
pkt.putShort(1, NUMQ_OFFSET);
498
pkt.putShort(0, NUMANS_OFFSET);
499
pkt.putInt(0, NUMAUTH_OFFSET);
500
501
makeQueryName(fqdn, pkt, DNS_HDR_SIZE);
502
pkt.putShort(qtype, DNS_HDR_SIZE + qnameLen);
503
pkt.putShort(qclass, DNS_HDR_SIZE + qnameLen + 2);
504
505
return pkt;
506
}
507
508
// Builds a query name in pkt according to the RFC spec.
509
private void makeQueryName(DnsName fqdn, Packet pkt, int off) {
510
511
// Loop through labels, least-significant first.
512
for (int i = fqdn.size() - 1; i >= 0; i--) {
513
String label = fqdn.get(i);
514
int len = label.length();
515
516
pkt.putByte(len, off++);
517
for (int j = 0; j < len; j++) {
518
pkt.putByte(label.charAt(j), off++);
519
}
520
}
521
if (!fqdn.hasRootLabel()) {
522
pkt.putByte(0, off);
523
}
524
}
525
526
//-------------------------------------------------------------------------
527
528
private byte[] lookupResponse(Integer xid) throws NamingException {
529
//
530
// Check the queued responses: some other thread in between
531
// received the response for this request.
532
//
533
if (debug) {
534
dprint("LOOKUP for: " + xid +
535
"\tResponse Q:" + resps);
536
}
537
byte[] pkt;
538
if ((pkt = resps.get(xid)) != null) {
539
checkResponseCode(new Header(pkt, pkt.length));
540
synchronized (queuesLock) {
541
resps.remove(xid);
542
reqs.remove(xid);
543
}
544
545
if (debug) {
546
dprint("FOUND (" + Thread.currentThread() +
547
") for:" + xid);
548
}
549
}
550
return pkt;
551
}
552
553
/*
554
* Checks the header of an incoming DNS response.
555
* Returns true if it matches the given xid and throws a naming
556
* exception, if appropriate, based on the response code.
557
*
558
* Also checks that the domain name, type and class in the response
559
* match those in the original query.
560
*/
561
private boolean isMatchResponse(byte[] pkt, int xid)
562
throws NamingException {
563
564
Header hdr = new Header(pkt, pkt.length);
565
if (hdr.query) {
566
throw new CommunicationException("DNS error: expecting response");
567
}
568
569
if (!reqs.containsKey(xid)) { // already received, ignore the response
570
return false;
571
}
572
573
// common case- the request sent matches the subsequent response read
574
if (hdr.xid == xid) {
575
if (debug) {
576
dprint("XID MATCH:" + xid);
577
}
578
checkResponseCode(hdr);
579
if (!hdr.query && hdr.numQuestions == 1) {
580
581
ResourceRecord rr = new ResourceRecord(pkt, pkt.length,
582
Header.HEADER_SIZE, true, false);
583
584
// Retrieve the original query
585
ResourceRecord query = reqs.get(xid);
586
int qtype = query.getType();
587
int qclass = query.getRrclass();
588
DnsName qname = query.getName();
589
590
// Check that the type/class/name in the query section of the
591
// response match those in the original query
592
if ((qtype == ResourceRecord.QTYPE_STAR ||
593
qtype == rr.getType()) &&
594
(qclass == ResourceRecord.QCLASS_STAR ||
595
qclass == rr.getRrclass()) &&
596
qname.equals(rr.getName())) {
597
598
if (debug) {
599
dprint("MATCH NAME:" + qname + " QTYPE:" + qtype +
600
" QCLASS:" + qclass);
601
}
602
603
// Remove the response for the xid if received by some other
604
// thread.
605
synchronized (queuesLock) {
606
resps.remove(xid);
607
reqs.remove(xid);
608
}
609
return true;
610
611
} else {
612
if (debug) {
613
dprint("NO-MATCH NAME:" + qname + " QTYPE:" + qtype +
614
" QCLASS:" + qclass);
615
}
616
}
617
}
618
return false;
619
}
620
621
//
622
// xid mis-match: enqueue the response, it may belong to some other
623
// thread that has not yet had a chance to read its response.
624
// enqueue only the first response, responses for retries are ignored.
625
//
626
synchronized (queuesLock) {
627
if (reqs.containsKey(hdr.xid)) { // enqueue only the first response
628
resps.put(hdr.xid, pkt);
629
}
630
}
631
632
if (debug) {
633
dprint("NO-MATCH SEND ID:" +
634
xid + " RECVD ID:" + hdr.xid +
635
" Response Q:" + resps +
636
" Reqs size:" + reqs.size());
637
}
638
return false;
639
}
640
641
/*
642
* Throws an exception if appropriate for the response code of a
643
* given header.
644
*/
645
private void checkResponseCode(Header hdr) throws NamingException {
646
647
int rcode = hdr.rcode;
648
if (rcode == NO_ERROR) {
649
return;
650
}
651
String msg = (rcode < rcodeDescription.length)
652
? rcodeDescription[rcode]
653
: "DNS error";
654
msg += " [response code " + rcode + "]";
655
656
switch (rcode) {
657
case SERVER_FAILURE:
658
throw new ServiceUnavailableException(msg);
659
case NAME_ERROR:
660
throw new NameNotFoundException(msg);
661
case NOT_IMPL:
662
case REFUSED:
663
throw new OperationNotSupportedException(msg);
664
case FORMAT_ERROR:
665
default:
666
throw new NamingException(msg);
667
}
668
}
669
670
//-------------------------------------------------------------------------
671
672
private static final boolean debug = false;
673
674
private static void dprint(String mess) {
675
if (debug) {
676
System.err.println("DNS: " + mess);
677
}
678
}
679
680
}
681
682
class Tcp {
683
684
private Socket sock;
685
java.io.InputStream in;
686
java.io.OutputStream out;
687
688
Tcp(InetAddress server, int port) throws IOException {
689
sock = new Socket(server, port);
690
sock.setTcpNoDelay(true);
691
out = new java.io.BufferedOutputStream(sock.getOutputStream());
692
in = new java.io.BufferedInputStream(sock.getInputStream());
693
}
694
695
void close() throws IOException {
696
sock.close();
697
}
698
}
699
700
/*
701
* javaos emulation -cj
702
*/
703
class Packet {
704
byte buf[];
705
706
Packet(int len) {
707
buf = new byte[len];
708
}
709
710
Packet(byte data[], int len) {
711
buf = new byte[len];
712
System.arraycopy(data, 0, buf, 0, len);
713
}
714
715
void putInt(int x, int off) {
716
buf[off + 0] = (byte)(x >> 24);
717
buf[off + 1] = (byte)(x >> 16);
718
buf[off + 2] = (byte)(x >> 8);
719
buf[off + 3] = (byte)x;
720
}
721
722
void putShort(int x, int off) {
723
buf[off + 0] = (byte)(x >> 8);
724
buf[off + 1] = (byte)x;
725
}
726
727
void putByte(int x, int off) {
728
buf[off] = (byte)x;
729
}
730
731
void putBytes(byte src[], int src_offset, int dst_offset, int len) {
732
System.arraycopy(src, src_offset, buf, dst_offset, len);
733
}
734
735
int length() {
736
return buf.length;
737
}
738
739
byte[] getData() {
740
return buf;
741
}
742
}
743
744