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/DnsContext.java
38924 views
1
/*
2
* Copyright (c) 2000, 2013, 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
29
import java.util.Enumeration;
30
import java.util.Hashtable;
31
32
import javax.naming.*;
33
import javax.naming.directory.*;
34
import javax.naming.spi.DirectoryManager;
35
36
import com.sun.jndi.toolkit.ctx.*;
37
38
39
/**
40
* A DnsContext is a directory context representing a DNS node.
41
*
42
* @author Scott Seligman
43
*/
44
45
46
public class DnsContext extends ComponentDirContext {
47
48
DnsName domain; // fully-qualified domain name of this context,
49
// with a root (empty) label at position 0
50
Hashtable<Object,Object> environment;
51
private boolean envShared; // true if environment is possibly shared
52
// and so must be copied on write
53
private boolean parentIsDns; // was this DnsContext created by
54
// another? see composeName()
55
private String[] servers;
56
private Resolver resolver;
57
58
private boolean authoritative; // must all responses be authoritative?
59
private boolean recursion; // request recursion on queries?
60
private int timeout; // initial timeout on UDP queries in ms
61
private int retries; // number of UDP retries
62
63
static final NameParser nameParser = new DnsNameParser();
64
65
// Timeouts for UDP queries use exponential backoff: each retry
66
// is for twice as long as the last. The following constants set
67
// the defaults for the initial timeout (in ms) and the number of
68
// retries, and name the environment properties used to override
69
// these defaults.
70
private static final int DEFAULT_INIT_TIMEOUT = 1000;
71
private static final int DEFAULT_RETRIES = 4;
72
private static final String INIT_TIMEOUT =
73
"com.sun.jndi.dns.timeout.initial";
74
private static final String RETRIES = "com.sun.jndi.dns.timeout.retries";
75
76
// The resource record type and class to use for lookups, and the
77
// property used to modify them
78
private CT lookupCT;
79
private static final String LOOKUP_ATTR = "com.sun.jndi.dns.lookup.attr";
80
81
// Property used to disallow recursion on queries
82
private static final String RECURSION = "com.sun.jndi.dns.recursion";
83
84
// ANY == ResourceRecord.QCLASS_STAR == ResourceRecord.QTYPE_STAR
85
private static final int ANY = ResourceRecord.QTYPE_STAR;
86
87
// The zone tree used for list operations
88
private static final ZoneNode zoneTree = new ZoneNode(null);
89
90
91
/**
92
* Returns a DNS context for a given domain and servers.
93
* Each server is of the form "server[:port]".
94
* IPv6 literal host names include delimiting brackets.
95
* There must be at least one server.
96
* The environment must not be null; it is cloned before being stored.
97
*/
98
@SuppressWarnings("unchecked")
99
public DnsContext(String domain, String[] servers, Hashtable<?,?> environment)
100
throws NamingException {
101
102
this.domain = new DnsName(domain.endsWith(".")
103
? domain
104
: domain + ".");
105
this.servers = (servers == null) ? null : servers.clone();
106
this.environment = (Hashtable<Object,Object>) environment.clone();
107
envShared = false;
108
parentIsDns = false;
109
resolver = null;
110
111
initFromEnvironment();
112
}
113
114
/*
115
* Returns a clone of a DNS context, just like DnsContext(DnsContext)
116
* but with a different domain name and with parentIsDns set to true.
117
*/
118
DnsContext(DnsContext ctx, DnsName domain) {
119
this(ctx);
120
this.domain = domain;
121
parentIsDns = true;
122
}
123
124
/*
125
* Returns a clone of a DNS context. The context's modifiable
126
* private state is independent of the original's (so closing one
127
* context, for example, won't close the other). The two contexts
128
* share <tt>environment</tt>, but it's copy-on-write so there's
129
* no conflict.
130
*/
131
private DnsContext(DnsContext ctx) {
132
environment = ctx.environment; // shared environment, copy-on-write
133
envShared = ctx.envShared = true;
134
parentIsDns = ctx.parentIsDns;
135
domain = ctx.domain;
136
servers = ctx.servers; // shared servers, no write operation
137
resolver = ctx.resolver;
138
authoritative = ctx.authoritative;
139
recursion = ctx.recursion;
140
timeout = ctx.timeout;
141
retries = ctx.retries;
142
lookupCT = ctx.lookupCT;
143
}
144
145
public void close() {
146
if (resolver != null) {
147
resolver.close();
148
resolver = null;
149
}
150
}
151
152
153
//---------- Environment operations
154
155
/*
156
* Override default with a noncloning version.
157
*/
158
protected Hashtable<?,?> p_getEnvironment() {
159
return environment;
160
}
161
162
public Hashtable<?,?> getEnvironment() throws NamingException {
163
return (Hashtable<?,?>) environment.clone();
164
}
165
166
@SuppressWarnings("unchecked")
167
public Object addToEnvironment(String propName, Object propVal)
168
throws NamingException {
169
170
if (propName.equals(LOOKUP_ATTR)) {
171
lookupCT = getLookupCT((String) propVal);
172
} else if (propName.equals(Context.AUTHORITATIVE)) {
173
authoritative = "true".equalsIgnoreCase((String) propVal);
174
} else if (propName.equals(RECURSION)) {
175
recursion = "true".equalsIgnoreCase((String) propVal);
176
} else if (propName.equals(INIT_TIMEOUT)) {
177
int val = Integer.parseInt((String) propVal);
178
if (timeout != val) {
179
timeout = val;
180
resolver = null;
181
}
182
} else if (propName.equals(RETRIES)) {
183
int val = Integer.parseInt((String) propVal);
184
if (retries != val) {
185
retries = val;
186
resolver = null;
187
}
188
}
189
190
if (!envShared) {
191
return environment.put(propName, propVal);
192
} else if (environment.get(propName) != propVal) {
193
// copy on write
194
environment = (Hashtable<Object,Object>) environment.clone();
195
envShared = false;
196
return environment.put(propName, propVal);
197
} else {
198
return propVal;
199
}
200
}
201
202
@SuppressWarnings("unchecked")
203
public Object removeFromEnvironment(String propName)
204
throws NamingException {
205
206
if (propName.equals(LOOKUP_ATTR)) {
207
lookupCT = getLookupCT(null);
208
} else if (propName.equals(Context.AUTHORITATIVE)) {
209
authoritative = false;
210
} else if (propName.equals(RECURSION)) {
211
recursion = true;
212
} else if (propName.equals(INIT_TIMEOUT)) {
213
if (timeout != DEFAULT_INIT_TIMEOUT) {
214
timeout = DEFAULT_INIT_TIMEOUT;
215
resolver = null;
216
}
217
} else if (propName.equals(RETRIES)) {
218
if (retries != DEFAULT_RETRIES) {
219
retries = DEFAULT_RETRIES;
220
resolver = null;
221
}
222
}
223
224
if (!envShared) {
225
return environment.remove(propName);
226
} else if (environment.get(propName) != null) {
227
// copy-on-write
228
environment = (Hashtable<Object,Object>) environment.clone();
229
envShared = false;
230
return environment.remove(propName);
231
} else {
232
return null;
233
}
234
}
235
236
/*
237
* Update PROVIDER_URL property. Call this only when environment
238
* is not being shared.
239
*/
240
void setProviderUrl(String url) {
241
// assert !envShared;
242
environment.put(Context.PROVIDER_URL, url);
243
}
244
245
/*
246
* Read environment properties and set parameters.
247
*/
248
private void initFromEnvironment()
249
throws InvalidAttributeIdentifierException {
250
251
lookupCT = getLookupCT((String) environment.get(LOOKUP_ATTR));
252
authoritative = "true".equalsIgnoreCase((String)
253
environment.get(Context.AUTHORITATIVE));
254
String val = (String) environment.get(RECURSION);
255
recursion = ((val == null) ||
256
"true".equalsIgnoreCase(val));
257
val = (String) environment.get(INIT_TIMEOUT);
258
timeout = (val == null)
259
? DEFAULT_INIT_TIMEOUT
260
: Integer.parseInt(val);
261
val = (String) environment.get(RETRIES);
262
retries = (val == null)
263
? DEFAULT_RETRIES
264
: Integer.parseInt(val);
265
}
266
267
private CT getLookupCT(String attrId)
268
throws InvalidAttributeIdentifierException {
269
return (attrId == null)
270
? new CT(ResourceRecord.CLASS_INTERNET, ResourceRecord.TYPE_TXT)
271
: fromAttrId(attrId);
272
}
273
274
275
//---------- Naming operations
276
277
public Object c_lookup(Name name, Continuation cont)
278
throws NamingException {
279
280
cont.setSuccess();
281
if (name.isEmpty()) {
282
DnsContext ctx = new DnsContext(this);
283
ctx.resolver = new Resolver(servers, timeout, retries);
284
// clone for parallelism
285
return ctx;
286
}
287
try {
288
DnsName fqdn = fullyQualify(name);
289
ResourceRecords rrs =
290
getResolver().query(fqdn, lookupCT.rrclass, lookupCT.rrtype,
291
recursion, authoritative);
292
Attributes attrs = rrsToAttrs(rrs, null);
293
DnsContext ctx = new DnsContext(this, fqdn);
294
return DirectoryManager.getObjectInstance(ctx, name, this,
295
environment, attrs);
296
} catch (NamingException e) {
297
cont.setError(this, name);
298
throw cont.fillInException(e);
299
} catch (Exception e) {
300
cont.setError(this, name);
301
NamingException ne = new NamingException(
302
"Problem generating object using object factory");
303
ne.setRootCause(e);
304
throw cont.fillInException(ne);
305
}
306
}
307
308
public Object c_lookupLink(Name name, Continuation cont)
309
throws NamingException {
310
return c_lookup(name, cont);
311
}
312
313
public NamingEnumeration<NameClassPair> c_list(Name name, Continuation cont)
314
throws NamingException {
315
cont.setSuccess();
316
try {
317
DnsName fqdn = fullyQualify(name);
318
NameNode nnode = getNameNode(fqdn);
319
DnsContext ctx = new DnsContext(this, fqdn);
320
return new NameClassPairEnumeration(ctx, nnode.getChildren());
321
322
} catch (NamingException e) {
323
cont.setError(this, name);
324
throw cont.fillInException(e);
325
}
326
}
327
328
public NamingEnumeration<Binding> c_listBindings(Name name, Continuation cont)
329
throws NamingException {
330
cont.setSuccess();
331
try {
332
DnsName fqdn = fullyQualify(name);
333
NameNode nnode = getNameNode(fqdn);
334
DnsContext ctx = new DnsContext(this, fqdn);
335
return new BindingEnumeration(ctx, nnode.getChildren());
336
337
} catch (NamingException e) {
338
cont.setError(this, name);
339
throw cont.fillInException(e);
340
}
341
}
342
343
public void c_bind(Name name, Object obj, Continuation cont)
344
throws NamingException {
345
cont.setError(this, name);
346
throw cont.fillInException(
347
new OperationNotSupportedException());
348
}
349
350
public void c_rebind(Name name, Object obj, Continuation cont)
351
throws NamingException {
352
cont.setError(this, name);
353
throw cont.fillInException(
354
new OperationNotSupportedException());
355
}
356
357
public void c_unbind(Name name, Continuation cont)
358
throws NamingException {
359
cont.setError(this, name);
360
throw cont.fillInException(
361
new OperationNotSupportedException());
362
}
363
364
public void c_rename(Name oldname, Name newname, Continuation cont)
365
throws NamingException {
366
cont.setError(this, oldname);
367
throw cont.fillInException(
368
new OperationNotSupportedException());
369
}
370
371
public Context c_createSubcontext(Name name, Continuation cont)
372
throws NamingException {
373
cont.setError(this, name);
374
throw cont.fillInException(
375
new OperationNotSupportedException());
376
}
377
378
public void c_destroySubcontext(Name name, Continuation cont)
379
throws NamingException {
380
cont.setError(this, name);
381
throw cont.fillInException(
382
new OperationNotSupportedException());
383
}
384
385
public NameParser c_getNameParser(Name name, Continuation cont)
386
throws NamingException {
387
cont.setSuccess();
388
return nameParser;
389
}
390
391
392
//---------- Directory operations
393
394
public void c_bind(Name name,
395
Object obj,
396
Attributes attrs,
397
Continuation cont)
398
throws NamingException {
399
cont.setError(this, name);
400
throw cont.fillInException(
401
new OperationNotSupportedException());
402
}
403
404
public void c_rebind(Name name,
405
Object obj,
406
Attributes attrs,
407
Continuation cont)
408
throws NamingException {
409
cont.setError(this, name);
410
throw cont.fillInException(
411
new OperationNotSupportedException());
412
}
413
414
public DirContext c_createSubcontext(Name name,
415
Attributes attrs,
416
Continuation cont)
417
throws NamingException {
418
cont.setError(this, name);
419
throw cont.fillInException(
420
new OperationNotSupportedException());
421
}
422
423
public Attributes c_getAttributes(Name name,
424
String[] attrIds,
425
Continuation cont)
426
throws NamingException {
427
428
cont.setSuccess();
429
try {
430
DnsName fqdn = fullyQualify(name);
431
CT[] cts = attrIdsToClassesAndTypes(attrIds);
432
CT ct = getClassAndTypeToQuery(cts);
433
ResourceRecords rrs =
434
getResolver().query(fqdn, ct.rrclass, ct.rrtype,
435
recursion, authoritative);
436
return rrsToAttrs(rrs, cts);
437
438
} catch (NamingException e) {
439
cont.setError(this, name);
440
throw cont.fillInException(e);
441
}
442
}
443
444
public void c_modifyAttributes(Name name,
445
int mod_op,
446
Attributes attrs,
447
Continuation cont)
448
throws NamingException {
449
cont.setError(this, name);
450
throw cont.fillInException(
451
new OperationNotSupportedException());
452
}
453
454
public void c_modifyAttributes(Name name,
455
ModificationItem[] mods,
456
Continuation cont)
457
throws NamingException {
458
cont.setError(this, name);
459
throw cont.fillInException(
460
new OperationNotSupportedException());
461
}
462
463
public NamingEnumeration<SearchResult> c_search(Name name,
464
Attributes matchingAttributes,
465
String[] attributesToReturn,
466
Continuation cont)
467
throws NamingException {
468
throw new OperationNotSupportedException();
469
}
470
471
public NamingEnumeration<SearchResult> c_search(Name name,
472
String filter,
473
SearchControls cons,
474
Continuation cont)
475
throws NamingException {
476
throw new OperationNotSupportedException();
477
}
478
479
public NamingEnumeration<SearchResult> c_search(Name name,
480
String filterExpr,
481
Object[] filterArgs,
482
SearchControls cons,
483
Continuation cont)
484
throws NamingException {
485
throw new OperationNotSupportedException();
486
}
487
488
public DirContext c_getSchema(Name name, Continuation cont)
489
throws NamingException {
490
cont.setError(this, name);
491
throw cont.fillInException(
492
new OperationNotSupportedException());
493
}
494
495
public DirContext c_getSchemaClassDefinition(Name name, Continuation cont)
496
throws NamingException {
497
cont.setError(this, name);
498
throw cont.fillInException(
499
new OperationNotSupportedException());
500
}
501
502
503
//---------- Name-related operations
504
505
public String getNameInNamespace() {
506
return domain.toString();
507
}
508
509
public Name composeName(Name name, Name prefix) throws NamingException {
510
Name result;
511
512
// Any name that's not a CompositeName is assumed to be a DNS
513
// compound name. Convert each to a DnsName for syntax checking.
514
if (!(prefix instanceof DnsName || prefix instanceof CompositeName)) {
515
prefix = (new DnsName()).addAll(prefix);
516
}
517
if (!(name instanceof DnsName || name instanceof CompositeName)) {
518
name = (new DnsName()).addAll(name);
519
}
520
521
// Each of prefix and name is now either a DnsName or a CompositeName.
522
523
// If we have two DnsNames, simply join them together.
524
if ((prefix instanceof DnsName) && (name instanceof DnsName)) {
525
result = (DnsName) (prefix.clone());
526
result.addAll(name);
527
return new CompositeName().add(result.toString());
528
}
529
530
// Wrap compound names in composite names.
531
Name prefixC = (prefix instanceof CompositeName)
532
? prefix
533
: new CompositeName().add(prefix.toString());
534
Name nameC = (name instanceof CompositeName)
535
? name
536
: new CompositeName().add(name.toString());
537
int prefixLast = prefixC.size() - 1;
538
539
// Let toolkit do the work at namespace boundaries.
540
if (nameC.isEmpty() || nameC.get(0).equals("") ||
541
prefixC.isEmpty() || prefixC.get(prefixLast).equals("")) {
542
return super.composeName(nameC, prefixC);
543
}
544
545
result = (prefix == prefixC)
546
? (CompositeName) prefixC.clone()
547
: prefixC; // prefixC is already a clone
548
result.addAll(nameC);
549
550
if (parentIsDns) {
551
DnsName dnsComp = (prefix instanceof DnsName)
552
? (DnsName) prefix.clone()
553
: new DnsName(prefixC.get(prefixLast));
554
dnsComp.addAll((name instanceof DnsName)
555
? name
556
: new DnsName(nameC.get(0)));
557
result.remove(prefixLast + 1);
558
result.remove(prefixLast);
559
result.add(prefixLast, dnsComp.toString());
560
}
561
return result;
562
}
563
564
565
//---------- Helper methods
566
567
/*
568
* Resolver is not created until needed, to allow time for updates
569
* to the environment.
570
*/
571
private synchronized Resolver getResolver() throws NamingException {
572
if (resolver == null) {
573
resolver = new Resolver(servers, timeout, retries);
574
}
575
return resolver;
576
}
577
578
/*
579
* Returns the fully-qualified domain name of a name given
580
* relative to this context. Result includes a root label (an
581
* empty component at position 0).
582
*/
583
DnsName fullyQualify(Name name) throws NamingException {
584
if (name.isEmpty()) {
585
return domain;
586
}
587
DnsName dnsName = (name instanceof CompositeName)
588
? new DnsName(name.get(0)) // parse name
589
: (DnsName) (new DnsName()).addAll(name); // clone & check syntax
590
591
if (dnsName.hasRootLabel()) {
592
// Be overly generous and allow root label if we're in root domain.
593
if (domain.size() == 1) {
594
return dnsName;
595
} else {
596
throw new InvalidNameException(
597
"DNS name " + dnsName + " not relative to " + domain);
598
}
599
}
600
return (DnsName) dnsName.addAll(0, domain);
601
}
602
603
/*
604
* Converts resource records to an attribute set. Only resource
605
* records in the answer section are used, and only those that
606
* match the classes and types in cts (see classAndTypeMatch()
607
* for matching rules).
608
*/
609
private static Attributes rrsToAttrs(ResourceRecords rrs, CT[] cts) {
610
611
BasicAttributes attrs = new BasicAttributes(true);
612
613
for (int i = 0; i < rrs.answer.size(); i++) {
614
ResourceRecord rr = rrs.answer.elementAt(i);
615
int rrtype = rr.getType();
616
int rrclass = rr.getRrclass();
617
618
if (!classAndTypeMatch(rrclass, rrtype, cts)) {
619
continue;
620
}
621
622
String attrId = toAttrId(rrclass, rrtype);
623
Attribute attr = attrs.get(attrId);
624
if (attr == null) {
625
attr = new BasicAttribute(attrId);
626
attrs.put(attr);
627
}
628
attr.add(rr.getRdata());
629
}
630
return attrs;
631
}
632
633
/*
634
* Returns true if rrclass and rrtype match some element of cts.
635
* A match occurs if corresponding classes and types are equal,
636
* or if the array value is ANY. If cts is null, then any class
637
* and type match.
638
*/
639
private static boolean classAndTypeMatch(int rrclass, int rrtype,
640
CT[] cts) {
641
if (cts == null) {
642
return true;
643
}
644
for (int i = 0; i < cts.length; i++) {
645
CT ct = cts[i];
646
boolean classMatch = (ct.rrclass == ANY) ||
647
(ct.rrclass == rrclass);
648
boolean typeMatch = (ct.rrtype == ANY) ||
649
(ct.rrtype == rrtype);
650
if (classMatch && typeMatch) {
651
return true;
652
}
653
}
654
return false;
655
}
656
657
/*
658
* Returns the attribute ID for a resource record given its class
659
* and type. If the record is in the internet class, the
660
* corresponding attribute ID is the record's type name (or the
661
* integer type value if the name is not known). If the record is
662
* not in the internet class, the class name (or integer class
663
* value) is prepended to the attribute ID, separated by a space.
664
*
665
* A class or type value of ANY represents an indeterminate class
666
* or type, and is represented within the attribute ID by "*".
667
* For example, the attribute ID "IN *" represents
668
* any type in the internet class, and "* NS" represents an NS
669
* record of any class.
670
*/
671
private static String toAttrId(int rrclass, int rrtype) {
672
String attrId = ResourceRecord.getTypeName(rrtype);
673
if (rrclass != ResourceRecord.CLASS_INTERNET) {
674
attrId = ResourceRecord.getRrclassName(rrclass) + " " + attrId;
675
}
676
return attrId;
677
}
678
679
/*
680
* Returns the class and type values corresponding to an attribute
681
* ID. An indeterminate class or type is represented by ANY. See
682
* toAttrId() for the format of attribute IDs.
683
*
684
* @throws InvalidAttributeIdentifierException
685
* if class or type is unknown
686
*/
687
private static CT fromAttrId(String attrId)
688
throws InvalidAttributeIdentifierException {
689
690
if (attrId.equals("")) {
691
throw new InvalidAttributeIdentifierException(
692
"Attribute ID cannot be empty");
693
}
694
int rrclass;
695
int rrtype;
696
int space = attrId.indexOf(' ');
697
698
// class
699
if (space < 0) {
700
rrclass = ResourceRecord.CLASS_INTERNET;
701
} else {
702
String className = attrId.substring(0, space);
703
rrclass = ResourceRecord.getRrclass(className);
704
if (rrclass < 0) {
705
throw new InvalidAttributeIdentifierException(
706
"Unknown resource record class '" + className + '\'');
707
}
708
}
709
710
// type
711
String typeName = attrId.substring(space + 1);
712
rrtype = ResourceRecord.getType(typeName);
713
if (rrtype < 0) {
714
throw new InvalidAttributeIdentifierException(
715
"Unknown resource record type '" + typeName + '\'');
716
}
717
718
return new CT(rrclass, rrtype);
719
}
720
721
/*
722
* Returns an array of the classes and types corresponding to a
723
* set of attribute IDs. See toAttrId() for the format of
724
* attribute IDs, and classAndTypeMatch() for the format of the
725
* array returned.
726
*/
727
private static CT[] attrIdsToClassesAndTypes(String[] attrIds)
728
throws InvalidAttributeIdentifierException {
729
if (attrIds == null) {
730
return null;
731
}
732
CT[] cts = new CT[attrIds.length];
733
734
for (int i = 0; i < attrIds.length; i++) {
735
cts[i] = fromAttrId(attrIds[i]);
736
}
737
return cts;
738
}
739
740
/*
741
* Returns the most restrictive resource record class and type
742
* that may be used to query for records matching cts.
743
* See classAndTypeMatch() for matching rules.
744
*/
745
private static CT getClassAndTypeToQuery(CT[] cts) {
746
int rrclass;
747
int rrtype;
748
749
if (cts == null) {
750
// Query all records.
751
rrclass = ANY;
752
rrtype = ANY;
753
} else if (cts.length == 0) {
754
// No records are requested, but we need to ask for something.
755
rrclass = ResourceRecord.CLASS_INTERNET;
756
rrtype = ANY;
757
} else {
758
rrclass = cts[0].rrclass;
759
rrtype = cts[0].rrtype;
760
for (int i = 1; i < cts.length; i++) {
761
if (rrclass != cts[i].rrclass) {
762
rrclass = ANY;
763
}
764
if (rrtype != cts[i].rrtype) {
765
rrtype = ANY;
766
}
767
}
768
}
769
return new CT(rrclass, rrtype);
770
}
771
772
773
//---------- Support for list operations
774
775
/*
776
* Synchronization notes:
777
*
778
* Any access to zoneTree that walks the tree, whether it modifies
779
* the tree or not, is synchronized on zoneTree.
780
* [%%% Note: a read/write lock would allow increased concurrency.]
781
* The depth of a ZoneNode can thereafter be accessed without
782
* further synchronization. Access to other fields and methods
783
* should be synchronized on the node itself.
784
*
785
* A zone's contents is a NameNode tree that, once created, is never
786
* modified. The only synchronization needed is to ensure that it
787
* gets flushed into shared memory after being created, which is
788
* accomplished by ZoneNode.populate(). The contents are accessed
789
* via a soft reference, so a ZoneNode may be seen to be populated
790
* one moment and unpopulated the next.
791
*/
792
793
/*
794
* Returns the node in the zone tree corresponding to a
795
* fully-qualified domain name. If the desired portion of the
796
* tree has not yet been populated or has been outdated, a zone
797
* transfer is done to populate the tree.
798
*/
799
private NameNode getNameNode(DnsName fqdn) throws NamingException {
800
dprint("getNameNode(" + fqdn + ")");
801
802
// Find deepest related zone in zone tree.
803
ZoneNode znode;
804
DnsName zone;
805
synchronized (zoneTree) {
806
znode = zoneTree.getDeepestPopulated(fqdn);
807
}
808
dprint("Deepest related zone in zone tree: " +
809
((znode != null) ? znode.getLabel() : "[none]"));
810
811
NameNode topOfZone;
812
NameNode nnode;
813
814
if (znode != null) {
815
synchronized (znode) {
816
topOfZone = znode.getContents();
817
}
818
// If fqdn is in znode's zone, is not at a zone cut, and
819
// is current, we're done.
820
if (topOfZone != null) {
821
nnode = topOfZone.get(fqdn, znode.depth() + 1); // +1 for root
822
823
if ((nnode != null) && !nnode.isZoneCut()) {
824
dprint("Found node " + fqdn + " in zone tree");
825
zone = (DnsName)
826
fqdn.getPrefix(znode.depth() + 1); // +1 for root
827
boolean current = isZoneCurrent(znode, zone);
828
boolean restart = false;
829
830
synchronized (znode) {
831
if (topOfZone != znode.getContents()) {
832
// Zone was modified while we were examining it.
833
// All bets are off.
834
restart = true;
835
} else if (!current) {
836
znode.depopulate();
837
} else {
838
return nnode; // cache hit!
839
}
840
}
841
dprint("Zone not current; discarding node");
842
if (restart) {
843
return getNameNode(fqdn);
844
}
845
}
846
}
847
}
848
849
// Cache miss... do it the expensive way.
850
dprint("Adding node " + fqdn + " to zone tree");
851
852
// Find fqdn's zone and add it to the tree.
853
zone = getResolver().findZoneName(fqdn, ResourceRecord.CLASS_INTERNET,
854
recursion);
855
dprint("Node's zone is " + zone);
856
synchronized (zoneTree) {
857
znode = (ZoneNode) zoneTree.add(zone, 1); // "1" to skip root
858
}
859
860
// If znode is now populated we know -- because the first half of
861
// getNodeName() didn't find it -- that it was populated by another
862
// thread during this method call. Assume then that it's current.
863
864
synchronized (znode) {
865
topOfZone = znode.isPopulated()
866
? znode.getContents()
867
: populateZone(znode, zone);
868
}
869
// Desired node should now be in znode's populated zone. Find it.
870
nnode = topOfZone.get(fqdn, zone.size());
871
if (nnode == null) {
872
throw new ConfigurationException(
873
"DNS error: node not found in its own zone");
874
}
875
dprint("Found node in newly-populated zone");
876
return nnode;
877
}
878
879
/*
880
* Does a zone transfer to [re]populate a zone in the zone tree.
881
* Returns the zone's new contents.
882
*/
883
private NameNode populateZone(ZoneNode znode, DnsName zone)
884
throws NamingException {
885
dprint("Populating zone " + zone);
886
// assert Thread.holdsLock(znode);
887
ResourceRecords rrs =
888
getResolver().queryZone(zone,
889
ResourceRecord.CLASS_INTERNET, recursion);
890
dprint("zone xfer complete: " + rrs.answer.size() + " records");
891
return znode.populate(zone, rrs);
892
}
893
894
/*
895
* Determine if a ZoneNode's data is current.
896
* We base this on a comparison between the cached serial
897
* number and the latest SOA record.
898
*
899
* If there is no SOA record, znode is not (or is no longer) a zone:
900
* depopulate znode and return false.
901
*
902
* Since this method may perform a network operation, it is best
903
* to call it with znode unlocked. Caller must then note that the
904
* result may be outdated by the time this method returns.
905
*/
906
private boolean isZoneCurrent(ZoneNode znode, DnsName zone)
907
throws NamingException {
908
// former version: return !znode.isExpired();
909
910
if (!znode.isPopulated()) {
911
return false;
912
}
913
ResourceRecord soa =
914
getResolver().findSoa(zone, ResourceRecord.CLASS_INTERNET,
915
recursion);
916
synchronized (znode) {
917
if (soa == null) {
918
znode.depopulate();
919
}
920
return (znode.isPopulated() &&
921
znode.compareSerialNumberTo(soa) >= 0);
922
}
923
}
924
925
926
//---------- Debugging
927
928
private static final boolean debug = false;
929
930
private static final void dprint(String msg) {
931
if (debug) {
932
System.err.println("** " + msg);
933
}
934
}
935
}
936
937
938
//----------
939
940
/*
941
* A pairing of a resource record class and a resource record type.
942
* A value of ANY in either field represents an indeterminate value.
943
*/
944
class CT {
945
int rrclass;
946
int rrtype;
947
948
CT(int rrclass, int rrtype) {
949
this.rrclass = rrclass;
950
this.rrtype = rrtype;
951
}
952
}
953
954
955
//----------
956
957
/*
958
* Common base class for NameClassPairEnumeration and BindingEnumeration.
959
*/
960
abstract class BaseNameClassPairEnumeration<T> implements NamingEnumeration<T> {
961
962
protected Enumeration<NameNode> nodes; // nodes to be enumerated, or null if none
963
protected DnsContext ctx; // context being enumerated
964
965
BaseNameClassPairEnumeration(DnsContext ctx, Hashtable<String,NameNode> nodes) {
966
this.ctx = ctx;
967
this.nodes = (nodes != null)
968
? nodes.elements()
969
: null;
970
}
971
972
/*
973
* ctx will be set to null when no longer needed by the enumeration.
974
*/
975
public final void close() {
976
nodes = null;
977
ctx = null;
978
}
979
980
public final boolean hasMore() {
981
boolean more = ((nodes != null) && nodes.hasMoreElements());
982
if (!more) {
983
close();
984
}
985
return more;
986
}
987
988
public final boolean hasMoreElements() {
989
return hasMore();
990
}
991
992
abstract public T next() throws NamingException;
993
994
public final T nextElement() {
995
try {
996
return next();
997
} catch (NamingException e) {
998
java.util.NoSuchElementException nsee =
999
new java.util.NoSuchElementException();
1000
nsee.initCause(e);
1001
throw nsee;
1002
}
1003
}
1004
}
1005
1006
/*
1007
* An enumeration of name/classname pairs.
1008
*
1009
* Nodes that have children or that are zone cuts are returned with
1010
* classname DirContext. Other nodes are returned with classname
1011
* Object even though they are DirContexts as well, since this might
1012
* make the namespace easier to browse.
1013
*/
1014
final class NameClassPairEnumeration
1015
extends BaseNameClassPairEnumeration<NameClassPair>
1016
implements NamingEnumeration<NameClassPair> {
1017
1018
NameClassPairEnumeration(DnsContext ctx, Hashtable<String,NameNode> nodes) {
1019
super(ctx, nodes);
1020
}
1021
1022
@Override
1023
public NameClassPair next() throws NamingException {
1024
if (!hasMore()) {
1025
throw new java.util.NoSuchElementException();
1026
}
1027
NameNode nnode = nodes.nextElement();
1028
String className = (nnode.isZoneCut() ||
1029
(nnode.getChildren() != null))
1030
? "javax.naming.directory.DirContext"
1031
: "java.lang.Object";
1032
1033
String label = nnode.getLabel();
1034
Name compName = (new DnsName()).add(label);
1035
Name cname = (new CompositeName()).add(compName.toString());
1036
1037
NameClassPair ncp = new NameClassPair(cname.toString(), className);
1038
ncp.setNameInNamespace(ctx.fullyQualify(cname).toString());
1039
return ncp;
1040
}
1041
}
1042
1043
/*
1044
* An enumeration of Bindings.
1045
*/
1046
final class BindingEnumeration extends BaseNameClassPairEnumeration<Binding>
1047
implements NamingEnumeration<Binding> {
1048
1049
BindingEnumeration(DnsContext ctx, Hashtable<String,NameNode> nodes) {
1050
super(ctx, nodes);
1051
}
1052
1053
// Finalizer not needed since it's safe to leave ctx unclosed.
1054
// protected void finalize() {
1055
// close();
1056
// }
1057
1058
@Override
1059
public Binding next() throws NamingException {
1060
if (!hasMore()) {
1061
throw (new java.util.NoSuchElementException());
1062
}
1063
NameNode nnode = nodes.nextElement();
1064
1065
String label = nnode.getLabel();
1066
Name compName = (new DnsName()).add(label);
1067
String compNameStr = compName.toString();
1068
Name cname = (new CompositeName()).add(compNameStr);
1069
String cnameStr = cname.toString();
1070
1071
DnsName fqdn = ctx.fullyQualify(compName);
1072
1073
// Clone ctx to create the child context.
1074
DnsContext child = new DnsContext(ctx, fqdn);
1075
1076
try {
1077
Object obj = DirectoryManager.getObjectInstance(
1078
child, cname, ctx, child.environment, null);
1079
Binding binding = new Binding(cnameStr, obj);
1080
binding.setNameInNamespace(ctx.fullyQualify(cname).toString());
1081
return binding;
1082
} catch (Exception e) {
1083
NamingException ne = new NamingException(
1084
"Problem generating object using object factory");
1085
ne.setRootCause(e);
1086
throw ne;
1087
}
1088
}
1089
}
1090
1091