feat(jdk8): move files to new folder to avoid resources compiled.

This commit is contained in:
2025-09-07 15:25:52 +08:00
parent 3f0047bf6f
commit 8c35cfb1c0
17415 changed files with 217 additions and 213 deletions

View File

@@ -0,0 +1,240 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.cosnaming;
import javax.naming.*;
import javax.naming.spi.NamingManager;
import java.util.NoSuchElementException;
import java.util.Hashtable;
import org.omg.CosNaming.*;
import com.sun.jndi.toolkit.corba.CorbaUtils;
/**
* Implements the JNDI NamingEnumeration interface for COS
* Naming. Gets hold of a list of bindings from the COS Naming Server
* and allows the client to iterate through them.
*
* @author Raj Krishnamurthy
* @author Rosanna Lee
*/
final class CNBindingEnumeration
implements NamingEnumeration<javax.naming.Binding> {
private static final int DEFAULT_BATCHSIZE = 100;
private BindingListHolder _bindingList; // list of bindings
private BindingIterator _bindingIter; // iterator for getting list of bindings
private int counter; // pointer in _bindingList
private int batchsize = DEFAULT_BATCHSIZE; // how many to ask for each time
private CNCtx _ctx; // ctx to list
private Hashtable<?,?> _env; // environment for getObjectInstance
private boolean more = false; // iterator done?
private boolean isLookedUpCtx = false; // iterating on a context beneath this context ?
/**
* Creates a CNBindingEnumeration object.
* @param ctx Context to enumerate
*/
CNBindingEnumeration(CNCtx ctx, boolean isLookedUpCtx, Hashtable<?,?> env) {
// Get batch size to use
String batch = (env != null ?
(String)env.get(javax.naming.Context.BATCHSIZE) : null);
if (batch != null) {
try {
batchsize = Integer.parseInt(batch);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Batch size not numeric: " + batch);
}
}
_ctx = ctx;
_ctx.incEnumCount();
this.isLookedUpCtx = isLookedUpCtx;
_env = env;
_bindingList = new BindingListHolder();
BindingIteratorHolder _bindingIterH = new BindingIteratorHolder();
// Perform listing and request that bindings be returned in _bindingIter
// Upon return,_bindingList returns a zero length list
_ctx._nc.list(0, _bindingList, _bindingIterH);
_bindingIter = _bindingIterH.value;
// Get first batch using _bindingIter
if (_bindingIter != null) {
more = _bindingIter.next_n(batchsize, _bindingList);
} else {
more = false;
}
counter = 0;
}
/**
* Returns the next binding in the list.
* @exception NamingException any naming exception.
*/
public javax.naming.Binding next() throws NamingException {
if (more && counter >= _bindingList.value.length) {
getMore();
}
if (more && counter < _bindingList.value.length) {
org.omg.CosNaming.Binding bndg = _bindingList.value[counter];
counter++;
return mapBinding(bndg);
} else {
throw new NoSuchElementException();
}
}
/**
* Returns true or false depending on whether there are more bindings.
* @return boolean value
*/
public boolean hasMore() throws NamingException {
// If there's more, check whether current bindingList has been exhausted,
// and if so, try to get more.
// If no more, just say so.
return more ? (counter < _bindingList.value.length || getMore()) : false;
}
/**
* Returns true or false depending on whether there are more bindings.
* Need to define this to satisfy the Enumeration api requirement.
* @return boolean value
*/
public boolean hasMoreElements() {
try {
return hasMore();
} catch (NamingException e) {
return false;
}
}
/**
* Returns the next binding in the list.
* @exception NoSuchElementException Thrown when the end of the
* list is reached.
*/
public javax.naming.Binding nextElement() {
try {
return next();
} catch (NamingException ne) {
throw new NoSuchElementException();
}
}
public void close() throws NamingException {
more = false;
if (_bindingIter != null) {
_bindingIter.destroy();
_bindingIter = null;
}
if (_ctx != null) {
_ctx.decEnumCount();
/**
* context was obtained by CNCtx, the user doesn't have a handle to
* it, close it as we are done enumerating through the context
*/
if (isLookedUpCtx) {
_ctx.close();
}
_ctx = null;
}
}
protected void finalize() {
try {
close();
} catch (NamingException e) {
// ignore failures
}
}
/**
* Get the next batch using _bindingIter. Update the 'more' field.
*/
private boolean getMore() throws NamingException {
try {
more = _bindingIter.next_n(batchsize, _bindingList);
counter = 0; // reset
} catch (Exception e) {
more = false;
NamingException ne = new NamingException(
"Problem getting binding list");
ne.setRootCause(e);
throw ne;
}
return more;
}
/**
* Constructs a JNDI Binding object from the COS Naming binding
* object.
* @exception NameNotFound No objects under the name.
* @exception CannotProceed Unable to obtain a continuation context
* @exception InvalidName Name not understood.
* @exception NamingException One of the above.
*/
private javax.naming.Binding mapBinding(org.omg.CosNaming.Binding bndg)
throws NamingException {
java.lang.Object obj = _ctx.callResolve(bndg.binding_name);
Name cname = CNNameParser.cosNameToName(bndg.binding_name);
try {
// Check whether object factory codebase is trusted
if (CorbaUtils.isObjectFactoryTrusted(obj)) {
obj = NamingManager.getObjectInstance(obj, cname, _ctx, _env);
}
} catch (NamingException e) {
throw e;
} catch (Exception e) {
NamingException ne = new NamingException(
"problem generating object using object factory");
ne.setRootCause(e);
throw ne;
}
// Use cname.toString() instead of bindingName because the name
// in the binding should be a composite name
String cnameStr = cname.toString();
javax.naming.Binding jbndg = new javax.naming.Binding(cnameStr, obj);
NameComponent[] comps = _ctx.makeFullName(bndg.binding_name);
String fullName = CNNameParser.cosNameToInsString(comps);
jbndg.setNameInNamespace(fullName);
return jbndg;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 1999, 2004, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.cosnaming;
import javax.naming.spi.InitialContextFactory;
import javax.naming.*;
import java.util.Hashtable;
/**
* Implements the JNDI SPI InitialContextFactory interface used to
* create the InitialContext objects.
*
* @author Raj Krishnamurthy
*/
public class CNCtxFactory implements InitialContextFactory {
/**
* Creates the InitialContext object. Properties parameter should
* should contain the ORB object for the value jndi.corba.orb.
* @param env Properties object
*/
public Context getInitialContext(Hashtable<?,?> env) throws NamingException {
return new CNCtx(env);
}
}

View File

@@ -0,0 +1,500 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.cosnaming;
import javax.naming.*;
import java.util.Properties;
import java.util.Vector;
import java.util.Enumeration;
import org.omg.CosNaming.NameComponent;
/**
* Parsing routines for NameParser as well as COS Naming stringified names.
* This is used by CNCtx to create a NameComponent[] object and vice versa.
* It follows Section 4.5 of Interoperable Naming Service (INS) 98-10-11.
* In summary, the stringified form is a left-to-right, forward-slash
* separated name. id and kinds are separated by '.'. backslash is the
* escape character.
*
* @author Rosanna Lee
*/
final public class CNNameParser implements NameParser {
private static final Properties mySyntax = new Properties();
private static final char kindSeparator = '.';
private static final char compSeparator = '/';
private static final char escapeChar = '\\';
static {
mySyntax.put("jndi.syntax.direction", "left_to_right");
mySyntax.put("jndi.syntax.separator", ""+compSeparator);
mySyntax.put("jndi.syntax.escape", ""+escapeChar);
};
/**
* Constructs a new name parser for parsing names in INS syntax.
*/
public CNNameParser() {
}
/**
* Returns a CompoundName given a string in INS syntax.
* @param name The non-null string representation of the name.
* @return a non-null CompoundName
*/
public Name parse(String name) throws NamingException {
Vector<String> comps = insStringToStringifiedComps(name);
return new CNCompoundName(comps.elements());
}
/**
* Creates a NameComponent[] from a Name structure.
* Used by CNCtx to convert the input Name arg into a NameComponent[].
* @param a CompoundName or a CompositeName;
* each component must be the stringified form of a NameComponent.
*/
static NameComponent[] nameToCosName(Name name)
throws InvalidNameException {
int len = name.size();
if (len == 0) {
return new NameComponent[0];
}
NameComponent[] answer = new NameComponent[len];
for (int i = 0; i < len; i++) {
answer[i] = parseComponent(name.get(i));
}
return answer;
}
/**
* Returns the INS stringified form of a NameComponent[].
* Used by CNCtx.getNameInNamespace(), CNCompoundName.toString().
*/
static String cosNameToInsString(NameComponent[] cname) {
StringBuffer str = new StringBuffer();
for ( int i = 0; i < cname.length; i++) {
if ( i > 0) {
str.append(compSeparator);
}
str.append(stringifyComponent(cname[i]));
}
return str.toString();
}
/**
* Creates a CompositeName from a NameComponent[].
* Used by ExceptionMapper and CNBindingEnumeration to convert
* a NameComponent[] into a composite name.
*/
static Name cosNameToName(NameComponent[] cname) {
Name nm = new CompositeName();
for ( int i = 0; cname != null && i < cname.length; i++) {
try {
nm.add(stringifyComponent(cname[i]));
} catch (InvalidNameException e) {
// ignore
}
}
return nm;
}
/**
* Converts an INS-syntax string name into a Vector in which
* each element of the vector contains a stringified form of
* a NameComponent.
*/
private static Vector<String> insStringToStringifiedComps(String str)
throws InvalidNameException {
int len = str.length();
Vector<String> components = new Vector<>(10);
char[] id = new char[len];
char[] kind = new char[len];
int idCount, kindCount;
boolean idMode;
for (int i = 0; i < len; ) {
idCount = kindCount = 0; // reset for new component
idMode = true; // always start off parsing id
while (i < len) {
if (str.charAt(i) == compSeparator) {
break;
} else if (str.charAt(i) == escapeChar) {
if (i + 1 >= len) {
throw new InvalidNameException(str +
": unescaped \\ at end of component");
} else if (isMeta(str.charAt(i+1))) {
++i; // skip escape and let meta through
if (idMode) {
id[idCount++] = str.charAt(i++);
} else {
kind[kindCount++] = str.charAt(i++);
}
} else {
throw new InvalidNameException(str +
": invalid character being escaped");
}
} else if (idMode && str.charAt(i) == kindSeparator) {
// just look for the first kindSeparator
++i; // skip kind separator
idMode = false;
} else {
if (idMode) {
id[idCount++] = str.charAt(i++);
} else {
kind[kindCount++] = str.charAt(i++);
}
}
}
components.addElement(stringifyComponent(
new NameComponent(new String(id, 0, idCount),
new String(kind, 0, kindCount))));
if (i < len) {
++i; // skip separator
}
}
return components;
}
/**
* Return a NameComponent given its stringified form.
*/
private static NameComponent parseComponent(String compStr)
throws InvalidNameException {
NameComponent comp = new NameComponent();
int kindSep = -1;
int len = compStr.length();
int j = 0;
char[] newStr = new char[len];
boolean escaped = false;
// Find the kind separator
for (int i = 0; i < len && kindSep < 0; i++) {
if (escaped) {
newStr[j++] = compStr.charAt(i);
escaped = false;
} else if (compStr.charAt(i) == escapeChar) {
if (i + 1 >= len) {
throw new InvalidNameException(compStr +
": unescaped \\ at end of component");
} else if (isMeta(compStr.charAt(i+1))) {
escaped = true;
} else {
throw new InvalidNameException(compStr +
": invalid character being escaped");
}
} else if (compStr.charAt(i) == kindSeparator) {
kindSep = i;
} else {
newStr[j++] = compStr.charAt(i);
}
}
// Set id
comp.id = new String(newStr, 0, j);
// Set kind
if (kindSep < 0) {
comp.kind = ""; // no kind separator
} else {
// unescape kind
j = 0;
escaped = false;
for (int i = kindSep+1; i < len; i++) {
if (escaped) {
newStr[j++] = compStr.charAt(i);
escaped = false;
} else if (compStr.charAt(i) == escapeChar) {
if (i + 1 >= len) {
throw new InvalidNameException(compStr +
": unescaped \\ at end of component");
} else if (isMeta(compStr.charAt(i+1))) {
escaped = true;
} else {
throw new InvalidNameException(compStr +
": invalid character being escaped");
}
} else {
newStr[j++] = compStr.charAt(i);
}
}
comp.kind = new String(newStr, 0, j);
}
return comp;
}
private static String stringifyComponent(NameComponent comp) {
StringBuffer one = new StringBuffer(escape(comp.id));
if (comp.kind != null && !comp.kind.equals("")) {
one.append(kindSeparator + escape(comp.kind));
}
if (one.length() == 0) {
return ""+kindSeparator; // if neither id nor kind specified
} else {
return one.toString();
}
}
/**
* Returns a string with '.', '\', '/' escaped. Used when
* stringifying the name into its INS stringified form.
*/
private static String escape(String str) {
if (str.indexOf(kindSeparator) < 0 &&
str.indexOf(compSeparator) < 0 &&
str.indexOf(escapeChar) < 0) {
return str; // no meta characters to escape
} else {
int len = str.length();
int j = 0;
char[] newStr = new char[len+len];
for (int i = 0; i < len; i++) {
if (isMeta(str.charAt(i))) {
newStr[j++] = escapeChar; // escape meta character
}
newStr[j++] = str.charAt(i);
}
return new String(newStr, 0, j);
}
}
/**
* In INS, there are three meta characters: '.', '/' and '\'.
*/
private static boolean isMeta(char ch) {
switch (ch) {
case kindSeparator:
case compSeparator:
case escapeChar:
return true;
}
return false;
}
/**
* An implementation of CompoundName that bypasses the parsing
* and stringifying code of the default CompoundName.
*/
static final class CNCompoundName extends CompoundName {
CNCompoundName(Enumeration<String> enum_) {
super(enum_, CNNameParser.mySyntax);
}
public Object clone() {
return new CNCompoundName(getAll());
}
public Name getPrefix(int posn) {
Enumeration<String> comps = super.getPrefix(posn).getAll();
return new CNCompoundName(comps);
}
public Name getSuffix(int posn) {
Enumeration<String> comps = super.getSuffix(posn).getAll();
return new CNCompoundName(comps);
}
public String toString() {
try {
// Convert Name to NameComponent[] then stringify
return cosNameToInsString(nameToCosName(this));
} catch (InvalidNameException e) {
return super.toString();
}
}
private static final long serialVersionUID = -6599252802678482317L;
}
// for testing only
/*
private static void print(String input) {
try {
System.out.println("\n >>>>>> input: " + input);
System.out.println("--Compound Name: ");
NameParser parser = new CNNameParser();
Name name = parser.parse(input);
for (int i = 0; i < name.size(); i++) {
System.out.println("\t" + i + ": " + name.get(i));
NameComponent cp = parseComponent(name.get(i));
System.out.println("\t\t" + "id: " + cp.id + ";kind: " + cp.kind);
}
System.out.println("\t" + name.toString());
System.out.println("--Composite Name: ");
Name composite = new CompositeName(input);
for (int i = 0; i < composite.size(); i++) {
System.out.println("\t" + i+": " + composite.get(i));
}
System.out.println("\t" + composite.toString());
System.out.println("--Composite To NameComponent");
NameComponent[] names = nameToCosName(composite);
for (int i = 0; i < composite.size(); i++) {
System.out.println("\t" + i+": id: " + names[i].id + "; kind: " + names[i].kind);
}
System.out.println("\t" + cosNameToInsString(names));
} catch (NamingException e) {
System.out.println(e);
}
}
private static void checkName(Name name, String[] comps) throws Exception {
if (name.size() != comps.length) {
throw new Exception(
"test failed; incorrect component count in " + name + "; " +
"expecting " + comps.length + " got " + name.size());
}
for (int i = 0; i < name.size(); i++) {
if (!comps[i].equals(name.get(i))) {
throw new Exception (
"test failed; invalid component in " + name + "; " +
"expecting '" + comps[i] + "' got '" + name.get(i) + "'");
}
}
}
private static void checkCompound(NameParser parser,
String input, String[] comps) throws Exception {
checkName(parser.parse(input), comps);
}
private static void checkComposite(String input, String[] comps)
throws Exception {
checkName(new CompositeName(input), comps);
}
private static String[] compounds = {
"a/b/c",
"a.b/c.d",
"a",
".",
"a.",
"c.d",
".e",
"a/x\\/y\\/z/b",
"a\\.b.c\\.d/e.f",
"a/b\\\\/c",
"x\\\\.y",
"x\\.y",
"x.\\\\y",
"x.y\\\\",
"\\\\x.y",
"a.b\\.c/d"
};
private static String[][] compoundComps = {
{"a", "b", "c"},
{"a.b", "c.d"},
{"a"},
{"."},
{"a"},
{"c.d"},
{".e"},
{"a", "x\\/y\\/z", "b"},
{"a\\.b.c\\.d", "e.f"},
{"a", "b\\\\", "c"},
{"x\\\\.y"},
{"x\\.y"},
{"x.\\\\y"},
{"x.y\\\\"},
{"\\\\x.y"},
{"a.b\\.c", "d"},
};
private static String[] composites = {
"a/b/c",
"a.b/c.d",
"a",
".",
"a.",
"c.d",
".e",
"a/x\\\\\\/y\\\\\\/z/b",
"a\\\\.b.c\\\\.d/e.f",
"a/b\\\\\\\\/c",
"x\\\\\\.y",
"x\\\\.y",
"x.\\\\\\\\y",
"x.y\\\\\\\\",
"\\\\\\\\x.y"
};
private static String[][] compositeComps = {
{"a", "b", "c"},
{"a.b", "c.d"},
{"a"},
{"."},
{"a."}, // unlike compound, kind sep is not consumed
{"c.d"},
{".e"},
{"a", "x\\/y\\/z", "b"},
{"a\\.b.c\\.d", "e.f"},
{"a", "b\\\\", "c"},
{"x\\\\.y"},
{"x\\.y"},
{"x.\\\\y"},
{"x.y\\\\"},
{"\\\\x.y"}
};
public static void main(String[] args) throws Exception {
if (args.length > 0) {
for (int i = 0; i < args.length; i++) {
print(args[0]);
}
} else {
print("x\\\\.y");
print("x\\.y");
print("x.\\\\y");
print("x.y\\\\");
print("\\\\x.y");
}
NameParser parser = new com.sun.jndi.cosnaming.CNNameParser();
for (int i = 0; i < compounds.length; i++) {
checkCompound(parser, compounds[i], compoundComps[i]);
}
for (int i = 0; i < composites.length; i++) {
checkComposite(composites[i], compositeComps[i]);
}
System.out.println("hardwire");
NameComponent[] foo = new NameComponent[1];
foo[0] = new NameComponent("foo\\", "bar");
System.out.println(cosNameToInsString(foo));
System.out.println(cosNameToName(foo));
}
*/
}

View File

@@ -0,0 +1,128 @@
/*
* Copyright (c) 2000, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.cosnaming;
import javax.naming.Name;
import javax.naming.NamingException;
import java.net.MalformedURLException;
import com.sun.jndi.toolkit.url.UrlUtil;
/**
* Extract components of a "corbaname" URL.
*
* The format of an corbaname URL is defined in INS 99-12-03 as follows.
*<p>
* corbaname url = "corbaname:" <corbaloc_obj> ["#" <string_name>]
* corbaloc_obj = <obj_addr_list> ["/" <key_string>]
* obj_addr_list = as defined in a corbaloc URL
* key_string = as defined in a corbaloc URL
* string_name = stringified COS name | empty_string
*<p>
* Characters in <string_name> are escaped as follows.
* US-ASCII alphanumeric characters are not escaped. Any characters outside
* of this range are escaped except for the following:
* ; / : ? @ & = + $ , - _ . ! ~ * ; ( )
* Escaped characters is escaped by using a % followed by its 2 hexadecimal
* numbers representing the octet.
*<p>
* The corbaname URL is parsed into two parts: a corbaloc URL and a COS name.
* The corbaloc URL is constructed by concatenation "corbaloc:" with
* <corbaloc_obj>.
* The COS name is <string_name> with the escaped characters resolved.
*<p>
* A corbaname URL is resolved by:
*<ol>
*<li>Construct a corbaloc URL by concatenating "corbaloc:" and <corbaloc_obj>.
*<li>Resolve the corbaloc URL to a NamingContext by using
* nctx = ORB.string_to_object(corbalocUrl);
*<li>Resolve <string_name> in the NamingContext.
*</ol>
*
* @author Rosanna Lee
*/
public final class CorbanameUrl {
private String stringName;
private String location;
/**
* Returns a possibly empty but non-null string that is the "string_name"
* portion of the URL.
*/
public String getStringName() {
return stringName;
}
public Name getCosName() throws NamingException {
return CNCtx.parser.parse(stringName);
}
public String getLocation() {
return "corbaloc:" + location;
}
public CorbanameUrl(String url) throws MalformedURLException {
if (!url.startsWith("corbaname:")) {
throw new MalformedURLException("Invalid corbaname URL: " + url);
}
int addrStart = 10; // "corbaname:"
int addrEnd = url.indexOf('#', addrStart);
if (addrEnd < 0) {
addrEnd = url.length();
stringName = "";
} else {
stringName = UrlUtil.decode(url.substring(addrEnd+1));
}
location = url.substring(addrStart, addrEnd);
int keyStart = location.indexOf("/");
if (keyStart >= 0) {
// Has key string
if (keyStart == (location.length() -1)) {
location += "NameService";
}
} else {
location += "/NameService";
}
}
/*
// for testing only
public static void main(String[] args) {
try {
CorbanameUrl url = new CorbanameUrl(args[0]);
System.out.println("location: " + url.getLocation());
System.out.println("string name: " + url.getStringName());
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
*/
}

View File

@@ -0,0 +1,247 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.cosnaming;
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.spi.*;
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*;
import org.omg.CORBA.*;
import com.sun.jndi.toolkit.corba.CorbaUtils;
/**
* A convenience class to map the COS Naming exceptions to the JNDI exceptions.
* @author Raj Krishnamurthy
*/
public final class ExceptionMapper {
private ExceptionMapper() {} // ensure no instance
private static final boolean debug = false;
public static final NamingException mapException(Exception e,
CNCtx ctx, NameComponent[] inputName) throws NamingException {
if (e instanceof NamingException) {
return (NamingException)e;
}
if (e instanceof RuntimeException) {
throw (RuntimeException)e;
}
NamingException ne;
if (e instanceof NotFound) {
if (ctx.federation) {
return tryFed((NotFound)e, ctx, inputName);
} else {
ne = new NameNotFoundException();
}
} else if (e instanceof CannotProceed) {
ne = new CannotProceedException();
NamingContext nc = ((CannotProceed) e).cxt;
NameComponent[] rest = ((CannotProceed) e).rest_of_name;
// %%% We assume that rest returns *all* unprocessed components.
// Don't' know if that is a good assumption, given
// NotFound doesn't set rest as expected. -RL
if (inputName != null && (inputName.length > rest.length)) {
NameComponent[] resolvedName =
new NameComponent[inputName.length - rest.length];
System.arraycopy(inputName, 0, resolvedName, 0, resolvedName.length);
// Wrap resolved NamingContext inside a CNCtx
// Guess that its name (which is relative to ctx)
// is the part of inputName minus rest_of_name
ne.setResolvedObj(new CNCtx(ctx._orb, ctx.orbTracker, nc,
ctx._env,
ctx.makeFullName(resolvedName)));
} else {
ne.setResolvedObj(ctx);
}
ne.setRemainingName(CNNameParser.cosNameToName(rest));
} else if (e instanceof InvalidName) {
ne = new InvalidNameException();
} else if (e instanceof AlreadyBound) {
ne = new NameAlreadyBoundException();
} else if (e instanceof NotEmpty) {
ne = new ContextNotEmptyException();
} else {
ne = new NamingException("Unknown reasons");
}
ne.setRootCause(e);
return ne;
}
private static final NamingException tryFed(NotFound e, CNCtx ctx,
NameComponent[] inputName) throws NamingException {
NameComponent[] rest = e.rest_of_name;
if (debug) {
System.out.println(e.why.value());
System.out.println(rest.length);
}
// %%% Using 1.2 & 1.3 Sun's tnameserv, 'rest' contains only the first
// component that failed, not *rest* as advertized. This is useless
// because what if you have something like aa/aa/aa/aa/aa.
// If one of those is not found, you get "aa" as 'rest'.
if (rest.length == 1 && inputName != null) {
// Check that we're not talking to 1.2/1.3 Sun tnameserv
NameComponent lastIn = inputName[inputName.length-1];
if (rest[0].id.equals(lastIn.id) &&
rest[0].kind != null &&
rest[0].kind.equals(lastIn.kind)) {
// Might be legit
;
} else {
// Due to 1.2/1.3 bug that always returns single-item 'rest'
NamingException ne = new NameNotFoundException();
ne.setRemainingName(CNNameParser.cosNameToName(rest));
ne.setRootCause(e);
throw ne;
}
}
// Fixed in 1.4; perform calculations based on correct (1.4) behavior
// Calculate the components of the name that has been resolved
NameComponent[] resolvedName = null;
int len = 0;
if (inputName != null && (inputName.length >= rest.length)) {
if (e.why == NotFoundReason.not_context) {
// First component of rest is found but not a context; keep it
// as part of resolved name
len = inputName.length - (rest.length - 1);
// Remove resolved component from rest
if (rest.length == 1) {
// No more remaining
rest = null;
} else {
NameComponent[] tmp = new NameComponent[rest.length-1];
System.arraycopy(rest, 1, tmp, 0, tmp.length);
rest = tmp;
}
} else {
len = inputName.length - rest.length;
}
if (len > 0) {
resolvedName = new NameComponent[len];
System.arraycopy(inputName, 0, resolvedName, 0, len);
}
}
// Create CPE and set common fields
CannotProceedException cpe = new CannotProceedException();
cpe.setRootCause(e);
if (rest != null && rest.length > 0) {
cpe.setRemainingName(CNNameParser.cosNameToName(rest));
}
cpe.setEnvironment(ctx._env);
if (debug) {
System.out.println("rest of name: " + cpe.getRemainingName());
}
// Lookup resolved name to get resolved object
final java.lang.Object resolvedObj =
(resolvedName != null) ? ctx.callResolve(resolvedName) : ctx;
if (resolvedObj instanceof javax.naming.Context) {
// obj is a context and child is not found
// try getting its nns dynamically by constructing
// a Reference containing obj.
RefAddr addr = new RefAddr("nns") {
public java.lang.Object getContent() {
return resolvedObj;
}
private static final long serialVersionUID =
669984699392133792L;
};
Reference ref = new Reference("java.lang.Object", addr);
// Resolved name has trailing slash to indicate nns
CompositeName cname = new CompositeName();
cname.add(""); // add trailing slash
cpe.setResolvedObj(ref);
cpe.setAltName(cname);
cpe.setAltNameCtx((javax.naming.Context)resolvedObj);
return cpe;
} else {
// Not a context, use object factory to transform object.
Name cname = CNNameParser.cosNameToName(resolvedName);
java.lang.Object resolvedObj2 = null;
try {
// Check whether object factory codebase is trusted
if (CorbaUtils.isObjectFactoryTrusted(resolvedObj)) {
resolvedObj2 = NamingManager.getObjectInstance(resolvedObj,
cname, ctx, ctx._env);
}
} catch (NamingException ge) {
throw ge;
} catch (Exception ge) {
NamingException ne = new NamingException(
"problem generating object using object factory");
ne.setRootCause(ge);
throw ne;
}
// If a context, continue operation with context
if (resolvedObj2 instanceof javax.naming.Context) {
cpe.setResolvedObj(resolvedObj2);
} else {
// Add trailing slash
cname.add("");
cpe.setAltName(cname);
// Create nns reference
final java.lang.Object rf2 = resolvedObj2;
RefAddr addr = new RefAddr("nns") {
public java.lang.Object getContent() {
return rf2;
}
private static final long serialVersionUID =
-785132553978269772L;
};
Reference ref = new Reference("java.lang.Object", addr);
cpe.setResolvedObj(ref);
cpe.setAltNameCtx(ctx);
}
return cpe;
}
}
}

View File

@@ -0,0 +1,223 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.cosnaming;
import javax.naming.Name;
import javax.naming.NamingException;
import java.net.MalformedURLException;
import java.util.Vector;
import java.util.StringTokenizer;
import com.sun.jndi.toolkit.url.UrlUtil;
/**
* Extract components of an "iiop" or "iiopname" URL.
*
* The format of an iiopname URL is defined in INS 98-10-11 as follows:
*
* iiopname url = "iiopname://" [addr_list]["/" string_name]
* addr_list = [address ","]* address
* address = [version host [":" port]]
* host = DNS style host name | IP address
* version = major "." minor "@" | empty_string
* port = number
* major = number
* minor = number
* string_name = stringified name | empty_string
*
* The default port is 9999. The default version is "1.0"
* US-ASCII alphanumeric characters are not escaped. Any characters outside
* of this range are escaped except for the following:
* ; / : ? : @ & = + $ , - _ . ! ~ * ' ( )
* Escaped characters is escaped by using a % followed by its 2 hexadecimal
* numbers representing the octet.
*
* For backward compatibility, the "iiop" URL as defined in INS 97-6-6
* is also supported:
*
* iiop url = "iiop://" [host [":" port]] ["/" string_name]
* The default port is 900.
*
* @author Rosanna Lee
*/
public final class IiopUrl {
static final private int DEFAULT_IIOPNAME_PORT = 9999;
static final private int DEFAULT_IIOP_PORT = 900;
static final private String DEFAULT_HOST = "localhost";
private Vector<Address> addresses;
private String stringName;
public static class Address {
public int port = -1;
public int major, minor;
public String host;
public Address(String hostPortVers, boolean oldFormat)
throws MalformedURLException {
// [version host [":" port]]
int start;
// Parse version
int at;
if (oldFormat || (at = hostPortVers.indexOf('@')) < 0) {
major = 1;
minor = 0;
start = 0; // start at the beginning
} else {
int dot = hostPortVers.indexOf('.');
if (dot < 0) {
throw new MalformedURLException(
"invalid version: " + hostPortVers);
}
try {
major = Integer.parseInt(hostPortVers.substring(0, dot));
minor = Integer.parseInt(hostPortVers.substring(dot+1, at));
} catch (NumberFormatException e) {
throw new MalformedURLException(
"Nonnumeric version: " + hostPortVers);
}
start = at + 1; // skip '@' sign
}
// Parse host and port
int slash = hostPortVers.indexOf('/', start);
if (slash < 0) {
slash = hostPortVers.length();
}
if (hostPortVers.startsWith("[", start)) { // at IPv6 literal
int brac = hostPortVers.indexOf(']', start + 1);
if (brac < 0 || brac > slash) {
throw new IllegalArgumentException(
"IiopURL: name is an Invalid URL: " + hostPortVers);
}
// include brackets
host = hostPortVers.substring(start, brac + 1);
start = brac + 1;
} else { // at hostname or IPv4
int colon = hostPortVers.indexOf(':', start);
int hostEnd = (colon < 0 || colon > slash)
? slash
: colon;
if (start < hostEnd) {
host = hostPortVers.substring(start, hostEnd);
}
start = hostEnd; // skip past host
}
if ((start + 1 < slash)) {
if ( hostPortVers.startsWith(":", start)) { // parse port
start++; // skip past ":"
port = Integer.parseInt(hostPortVers.
substring(start, slash));
} else {
throw new IllegalArgumentException(
"IiopURL: name is an Invalid URL: " + hostPortVers);
}
}
start = slash;
if ("".equals(host) || host == null) {
host = DEFAULT_HOST ;
}
if (port == -1) {
port = (oldFormat ? DEFAULT_IIOP_PORT :
DEFAULT_IIOPNAME_PORT);
}
}
}
public Vector<Address> getAddresses() {
return addresses;
}
/**
* Returns a possibly empty but non-null string that is the "string_name"
* portion of the URL.
*/
public String getStringName() {
return stringName;
}
public Name getCosName() throws NamingException {
return CNCtx.parser.parse(stringName);
}
public IiopUrl(String url) throws MalformedURLException {
int addrStart;
boolean oldFormat;
if (url.startsWith("iiopname://")) {
oldFormat = false;
addrStart = 11;
} else if (url.startsWith("iiop://")) {
oldFormat = true;
addrStart = 7;
} else {
throw new MalformedURLException("Invalid iiop/iiopname URL: " + url);
}
int addrEnd = url.indexOf('/', addrStart);
if (addrEnd < 0) {
addrEnd = url.length();
stringName = "";
} else {
stringName = UrlUtil.decode(url.substring(addrEnd+1));
}
addresses = new Vector<>(3);
if (oldFormat) {
// Only one host:port part, not multiple
addresses.addElement(
new Address(url.substring(addrStart, addrEnd), oldFormat));
} else {
StringTokenizer tokens =
new StringTokenizer(url.substring(addrStart, addrEnd), ",");
while (tokens.hasMoreTokens()) {
addresses.addElement(new Address(tokens.nextToken(), oldFormat));
}
if (addresses.size() == 0) {
addresses.addElement(new Address("", oldFormat));
}
}
}
// for testing only
/*public static void main(String[] args) {
try {
IiopUrl url = new IiopUrl(args[0]);
Vector addrs = url.getAddresses();
String name = url.getStringName();
for (int i = 0; i < addrs.size(); i++) {
Address addr = (Address)addrs.elementAt(i);
System.out.println("host: " + addr.host);
System.out.println("port: " + addr.port);
System.out.println("version: " + addr.major + " " + addr.minor);
}
System.out.println("name: " + name);
} catch (MalformedURLException e) {
e.printStackTrace();
}
} */
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.cosnaming;
import org.omg.CORBA.ORB;
/**
* This class keeps track of references to the shared ORB object
* and destroys it when no more references are made to the ORB
* object. This object is created for each ORB object that CNCtx
* creates.
*/
class OrbReuseTracker {
int referenceCnt;
ORB orb;
private static final boolean debug = false;
OrbReuseTracker(ORB orb) {
this.orb = orb;
referenceCnt++;
if (debug) {
System.out.println("New OrbReuseTracker created");
}
}
synchronized void incRefCount() {
referenceCnt++;
if (debug) {
System.out.println("Increment orb ref count to:" + referenceCnt);
}
}
synchronized void decRefCount() {
referenceCnt--;
if (debug) {
System.out.println("Decrement orb ref count to:" + referenceCnt);
}
if ((referenceCnt == 0)) {
if (debug) {
System.out.println("Destroying the ORB");
}
orb.destroy();
}
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright (c) 1999, 2004, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.cosnaming;
import javax.naming.*;
import javax.naming.spi.StateFactory;
import java.util.Hashtable;
import org.omg.CORBA.ORB;
import java.rmi.Remote;
import java.rmi.server.ExportException;
import com.sun.jndi.toolkit.corba.CorbaUtils; // for RMI-IIOP
/**
* StateFactory that turns java.rmi.Remote objects to org.omg.CORBA.Object.
*
* @author Rosanna Lee
*/
public class RemoteToCorba implements StateFactory {
public RemoteToCorba() {
}
/**
* Returns the CORBA object for a Remote object.
* If input is not a Remote object, or if Remote object uses JRMP, return null.
* If the RMI-IIOP library is not available, throw ConfigurationException.
*
* @param orig The object to turn into a CORBA object. If not Remote,
* or if is a JRMP stub or impl, return null.
* @param name Ignored
* @param ctx The non-null CNCtx whose ORB to use.
* @param env Ignored
* @return The CORBA object for <tt>orig</tt> or null.
* @exception ConfigurationException If the CORBA object cannot be obtained
* due to configuration problems, for instance, if RMI-IIOP not available.
* @exception NamingException If some other problem prevented a CORBA
* object from being obtained from the Remote object.
*/
public Object getStateToBind(Object orig, Name name, Context ctx,
Hashtable<?,?> env) throws NamingException {
if (orig instanceof org.omg.CORBA.Object) {
// Already a CORBA object, just use it
return null;
}
if (orig instanceof Remote) {
// Turn remote object into org.omg.CORBA.Object
try {
// Returns null if JRMP; let next factory try
// CNCtx will eventually throw IllegalArgumentException if
// no CORBA object gotten
return
CorbaUtils.remoteToCorba((Remote)orig, ((CNCtx)ctx)._orb);
} catch (ClassNotFoundException e) {
// RMI-IIOP library not available
throw new ConfigurationException(
"javax.rmi packages not available");
}
}
return null; // pass and let next state factory try
}
}

View File

@@ -0,0 +1,295 @@
/*
* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.dns;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.ProtocolFamily;
import java.net.SocketException;
import java.net.InetSocketAddress;
import java.nio.channels.DatagramChannel;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.util.Objects;
import java.util.Random;
class DNSDatagramSocketFactory {
static final int DEVIATION = 3;
static final int THRESHOLD = 6;
static final int BIT_DEVIATION = 2;
static final int HISTORY = 32;
static final int MAX_RANDOM_TRIES = 5;
/**
* The dynamic allocation port range (aka ephemeral ports), as configured
* on the system. Use nested class for lazy evaluation.
*/
static final class EphemeralPortRange {
private EphemeralPortRange() {}
static final int LOWER = sun.net.PortConfig.getLower();
static final int UPPER = sun.net.PortConfig.getUpper();
static final int RANGE = UPPER - LOWER + 1;
}
private static int findFirstFreePort() {
PrivilegedExceptionAction<DatagramSocket> action = () -> new DatagramSocket(0);
int port;
try {
@SuppressWarnings({"deprecated", "removal"})
DatagramSocket ds = AccessController.doPrivileged(action);
try (DatagramSocket ds1 = ds) {
port = ds1.getLocalPort();
}
} catch (Exception x) {
port = 0;
}
return port;
}
// Records a subset of max {@code capacity} previously used ports
static final class PortHistory {
final int capacity;
final int[] ports;
final Random random;
int index;
PortHistory(int capacity, Random random) {
this.random = random;
this.capacity = capacity;
this.ports = new int[capacity];
}
// returns true if the history contains the specified port.
public boolean contains(int port) {
int p = 0;
for (int i=0; i<capacity; i++) {
if ((p = ports[i]) == 0 || p == port) break;
}
return p == port;
}
// Adds the port to the history - doesn't check whether the port
// is already present. Always adds the port and always return true.
public boolean add(int port) {
if (ports[index] != 0) { // at max capacity
// remove one port at random and store the new port there
// don't remove the last port
int remove = random.nextInt(capacity);
if ((remove +1) % capacity == index) remove = index;
ports[index = remove] = port;
} else { // there's a free slot
ports[index] = port;
}
if (++index == capacity) index = 0;
return true;
}
// Adds the port to the history if not already present.
// Return true if the port was added, false if the port was already
// present.
public boolean offer(int port) {
if (contains(port)) return false;
else return add(port);
}
}
int lastport = findFirstFreePort();
int lastSystemAllocated = lastport;
int suitablePortCount;
int unsuitablePortCount;
final ProtocolFamily family; // null (default) means dual stack
final int thresholdCount; // decision point
final int deviation;
final Random random;
final PortHistory history;
DNSDatagramSocketFactory() {
this(new Random());
}
DNSDatagramSocketFactory(Random random) {
this(Objects.requireNonNull(random), null, DEVIATION, THRESHOLD);
}
DNSDatagramSocketFactory(Random random,
ProtocolFamily family,
int deviation,
int threshold) {
this.random = Objects.requireNonNull(random);
this.history = new PortHistory(HISTORY, random);
this.family = family;
this.deviation = Math.max(1, deviation);
this.thresholdCount = Math.max(2, threshold);
}
/**
* Opens a datagram socket listening to the wildcard address on a
* random port. If the underlying OS supports UDP port randomization
* out of the box (if binding a socket to port 0 binds it to a random
* port) then the underlying OS implementation is used. Otherwise, this
* method will allocate and bind a socket on a randomly selected ephemeral
* port in the dynamic range.
* @return A new DatagramSocket bound to a random port.
* @throws SocketException if the socket cannot be created.
*/
public synchronized DatagramSocket open() throws SocketException {
int lastseen = lastport;
DatagramSocket s;
boolean thresholdCrossed = unsuitablePortCount > thresholdCount;
if (thresholdCrossed) {
// Underlying stack does not support random UDP port out of the box.
// Use our own algorithm to allocate a random UDP port
s = openRandom();
if (s != null) return s;
// couldn't allocate a random port: reset all counters and fall
// through.
unsuitablePortCount = 0; suitablePortCount = 0; lastseen = 0;
}
// Allocate an ephemeral port (port 0)
s = openDefault();
lastport = s.getLocalPort();
if (lastseen == 0) {
lastSystemAllocated = lastport;
history.offer(lastport);
return s;
}
thresholdCrossed = suitablePortCount > thresholdCount;
boolean farEnough = farEnough(lastseen);
if (farEnough && lastSystemAllocated > 0) {
farEnough = farEnough(lastSystemAllocated);
}
boolean recycled = history.contains(lastport);
boolean suitable = (thresholdCrossed || farEnough && !recycled);
if (suitable && !recycled) history.add(lastport);
if (suitable) {
if (!thresholdCrossed) {
suitablePortCount++;
} else if (!farEnough || recycled) {
unsuitablePortCount = 1;
suitablePortCount = thresholdCount/2;
}
// Either the underlying stack supports random UDP port allocation,
// or the new port is sufficiently distant from last port to make
// it look like it is. Let's use it.
lastSystemAllocated = lastport;
return s;
}
// Undecided... the new port was too close. Let's allocate a random
// port using our own algorithm
assert !thresholdCrossed;
DatagramSocket ss = openRandom();
if (ss == null) return s;
unsuitablePortCount++;
s.close();
return ss;
}
private DatagramSocket openDefault() throws SocketException {
if (family != null) {
try {
DatagramChannel c = DatagramChannel.open(family);
try {
DatagramSocket s = c.socket();
s.bind(null);
return s;
} catch (Throwable x) {
c.close();
throw x;
}
} catch (SocketException x) {
throw x;
} catch (IOException x) {
SocketException e = new SocketException(x.getMessage());
e.initCause(x);
throw e;
}
}
return new DatagramSocket();
}
synchronized boolean isUsingNativePortRandomization() {
return unsuitablePortCount <= thresholdCount
&& suitablePortCount > thresholdCount;
}
synchronized boolean isUsingJavaPortRandomization() {
return unsuitablePortCount > thresholdCount ;
}
synchronized boolean isUndecided() {
return !isUsingJavaPortRandomization()
&& !isUsingNativePortRandomization();
}
private boolean farEnough(int port) {
return Integer.bitCount(port ^ lastport) > BIT_DEVIATION
&& Math.abs(port - lastport) > deviation;
}
private DatagramSocket openRandom() {
int maxtries = MAX_RANDOM_TRIES;
while (maxtries-- > 0) {
int port;
boolean suitable;
boolean recycled;
int maxrandom = MAX_RANDOM_TRIES;
do {
port = EphemeralPortRange.LOWER
+ random.nextInt(EphemeralPortRange.RANGE);
recycled = history.contains(port);
suitable = lastport == 0 || (farEnough(port) && !recycled);
} while (maxrandom-- > 0 && !suitable);
// if no suitable port was found, try again
// this means we might call random MAX_RANDOM_TRIES x MAX_RANDOM_TRIES
// times - but that should be OK with MAX_RANDOM_TRIES = 5.
if (!suitable) continue;
try {
if (family != null) {
DatagramChannel c = DatagramChannel.open(family);
try {
DatagramSocket s = c.socket();
s.bind(new InetSocketAddress(port));
lastport = s.getLocalPort();
if (!recycled) history.add(port);
return s;
} catch (Throwable x) {
c.close();
throw x;
}
}
DatagramSocket s = new DatagramSocket(port);
lastport = s.getLocalPort();
if (!recycled) history.add(port);
return s;
} catch (IOException x) {
// try again until maxtries == 0;
}
}
return null;
}
}

View File

@@ -0,0 +1,742 @@
/*
* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.dns;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.Socket;
import java.security.SecureRandom;
import javax.naming.*;
import java.util.Collections;
import java.util.Map;
import java.util.HashMap;
import sun.security.jca.JCAUtil;
// Some of this code began life as part of sun.javaos.net.DnsClient
// originally by sritchie@eng 1/96. It was first hacked up for JNDI
// use by caveh@eng 6/97.
/**
* The DnsClient class performs DNS client operations in support of DnsContext.
*
*/
public class DnsClient {
// DNS packet header field offsets
private static final int IDENT_OFFSET = 0;
private static final int FLAGS_OFFSET = 2;
private static final int NUMQ_OFFSET = 4;
private static final int NUMANS_OFFSET = 6;
private static final int NUMAUTH_OFFSET = 8;
private static final int NUMADD_OFFSET = 10;
private static final int DNS_HDR_SIZE = 12;
// DNS response codes
private static final int NO_ERROR = 0;
private static final int FORMAT_ERROR = 1;
private static final int SERVER_FAILURE = 2;
private static final int NAME_ERROR = 3;
private static final int NOT_IMPL = 4;
private static final int REFUSED = 5;
private static final String[] rcodeDescription = {
"No error",
"DNS format error",
"DNS server failure",
"DNS name not found",
"DNS operation not supported",
"DNS service refused"
};
private static final int DEFAULT_PORT = 53;
private static final int TRANSACTION_ID_BOUND = 0x10000;
private static final SecureRandom random = JCAUtil.getSecureRandom();
private InetAddress[] servers;
private int[] serverPorts;
private int timeout; // initial timeout on UDP queries in ms
private int retries; // number of UDP retries
private final Object udpSocketLock = new Object();
private static final DNSDatagramSocketFactory factory =
new DNSDatagramSocketFactory(random);
// Requests sent
private Map<Integer, ResourceRecord> reqs;
// Responses received
private Map<Integer, byte[]> resps;
//-------------------------------------------------------------------------
/*
* Each server is of the form "server[:port]". IPv6 literal host names
* include delimiting brackets.
* "timeout" is the initial timeout interval (in ms) for UDP queries,
* and "retries" gives the number of retries per server.
*/
public DnsClient(String[] servers, int timeout, int retries)
throws NamingException {
this.timeout = timeout;
this.retries = retries;
this.servers = new InetAddress[servers.length];
serverPorts = new int[servers.length];
for (int i = 0; i < servers.length; i++) {
// Is optional port given?
int colon = servers[i].indexOf(':',
servers[i].indexOf(']') + 1);
serverPorts[i] = (colon < 0)
? DEFAULT_PORT
: Integer.parseInt(servers[i].substring(colon + 1));
String server = (colon < 0)
? servers[i]
: servers[i].substring(0, colon);
try {
this.servers[i] = InetAddress.getByName(server);
} catch (java.net.UnknownHostException e) {
NamingException ne = new ConfigurationException(
"Unknown DNS server: " + server);
ne.setRootCause(e);
throw ne;
}
}
reqs = Collections.synchronizedMap(
new HashMap<Integer, ResourceRecord>());
resps = Collections.synchronizedMap(new HashMap<Integer, byte[]>());
}
DatagramSocket getDatagramSocket() throws NamingException {
try {
return factory.open();
} catch (java.net.SocketException e) {
NamingException ne = new ConfigurationException();
ne.setRootCause(e);
throw ne;
}
}
protected void finalize() {
close();
}
// A lock to access the request and response queues in tandem.
private Object queuesLock = new Object();
public void close() {
synchronized (queuesLock) {
reqs.clear();
resps.clear();
}
}
/*
* If recursion is true, recursion is requested on the query.
* If auth is true, only authoritative responses are accepted; other
* responses throw NameNotFoundException.
*/
ResourceRecords query(DnsName fqdn, int qclass, int qtype,
boolean recursion, boolean auth)
throws NamingException {
int xid;
Packet pkt;
ResourceRecord collision;
do {
// Generate a random transaction ID
xid = random.nextInt(TRANSACTION_ID_BOUND);
pkt = makeQueryPacket(fqdn, xid, qclass, qtype, recursion);
// enqueue the outstanding request
collision = reqs.putIfAbsent(xid, new ResourceRecord(pkt.getData(),
pkt.length(), Header.HEADER_SIZE, true, false));
} while (collision != null);
Exception caughtException = null;
boolean[] doNotRetry = new boolean[servers.length];
try {
//
// The UDP retry strategy is to try the 1st server, and then
// each server in order. If no answer, double the timeout
// and try each server again.
//
for (int retry = 0; retry < retries; retry++) {
// Try each name server.
for (int i = 0; i < servers.length; i++) {
if (doNotRetry[i]) {
continue;
}
// send the request packet and wait for a response.
try {
if (debug) {
dprint("SEND ID (" + (retry + 1) + "): " + xid);
}
byte[] msg = null;
msg = doUdpQuery(pkt, servers[i], serverPorts[i],
retry, xid);
//
// If the matching response is not got within the
// given timeout, check if the response was enqueued
// by some other thread, if not proceed with the next
// server or retry.
//
if (msg == null) {
if (resps.size() > 0) {
msg = lookupResponse(xid);
}
if (msg == null) { // try next server or retry
continue;
}
}
Header hdr = new Header(msg, msg.length);
if (auth && !hdr.authoritative) {
caughtException = new NameNotFoundException(
"DNS response not authoritative");
doNotRetry[i] = true;
continue;
}
if (hdr.truncated) { // message is truncated -- try TCP
// Try each server, starting with the one that just
// provided the truncated message.
for (int j = 0; j < servers.length; j++) {
int ij = (i + j) % servers.length;
if (doNotRetry[ij]) {
continue;
}
try {
Tcp tcp =
new Tcp(servers[ij], serverPorts[ij]);
byte[] msg2;
try {
msg2 = doTcpQuery(tcp, pkt);
} finally {
tcp.close();
}
Header hdr2 = new Header(msg2, msg2.length);
if (hdr2.query) {
throw new CommunicationException(
"DNS error: expecting response");
}
checkResponseCode(hdr2);
if (!auth || hdr2.authoritative) {
// Got a valid response
hdr = hdr2;
msg = msg2;
break;
} else {
doNotRetry[ij] = true;
}
} catch (Exception e) {
// Try next server, or use UDP response
}
} // servers
}
return new ResourceRecords(msg, msg.length, hdr, false);
} catch (IOException e) {
if (debug) {
dprint("Caught IOException:" + e);
}
if (caughtException == null) {
caughtException = e;
}
// Use reflection to allow pre-1.4 compilation.
// This won't be needed much longer.
if (e.getClass().getName().equals(
"java.net.PortUnreachableException")) {
doNotRetry[i] = true;
}
} catch (NameNotFoundException e) {
// This is authoritative, so return immediately
throw e;
} catch (CommunicationException e) {
if (caughtException == null) {
caughtException = e;
}
} catch (NamingException e) {
if (caughtException == null) {
caughtException = e;
}
doNotRetry[i] = true;
}
} // servers
} // retries
} finally {
reqs.remove(xid); // cleanup
}
if (caughtException instanceof NamingException) {
throw (NamingException) caughtException;
}
// A network timeout or other error occurred.
NamingException ne = new CommunicationException("DNS error");
ne.setRootCause(caughtException);
throw ne;
}
ResourceRecords queryZone(DnsName zone, int qclass, boolean recursion)
throws NamingException {
int xid = random.nextInt(TRANSACTION_ID_BOUND);
Packet pkt = makeQueryPacket(zone, xid, qclass,
ResourceRecord.QTYPE_AXFR, recursion);
Exception caughtException = null;
// Try each name server.
for (int i = 0; i < servers.length; i++) {
try {
Tcp tcp = new Tcp(servers[i], serverPorts[i]);
byte[] msg;
try {
msg = doTcpQuery(tcp, pkt);
Header hdr = new Header(msg, msg.length);
// Check only rcode as per
// draft-ietf-dnsext-axfr-clarify-04
checkResponseCode(hdr);
ResourceRecords rrs =
new ResourceRecords(msg, msg.length, hdr, true);
if (rrs.getFirstAnsType() != ResourceRecord.TYPE_SOA) {
throw new CommunicationException(
"DNS error: zone xfer doesn't begin with SOA");
}
if (rrs.answer.size() == 1 ||
rrs.getLastAnsType() != ResourceRecord.TYPE_SOA) {
// The response is split into multiple DNS messages.
do {
msg = continueTcpQuery(tcp);
if (msg == null) {
throw new CommunicationException(
"DNS error: incomplete zone transfer");
}
hdr = new Header(msg, msg.length);
checkResponseCode(hdr);
rrs.add(msg, msg.length, hdr);
} while (rrs.getLastAnsType() !=
ResourceRecord.TYPE_SOA);
}
// Delete the duplicate SOA record.
rrs.answer.removeElementAt(rrs.answer.size() - 1);
return rrs;
} finally {
tcp.close();
}
} catch (IOException e) {
caughtException = e;
} catch (NameNotFoundException e) {
throw e;
} catch (NamingException e) {
caughtException = e;
}
}
if (caughtException instanceof NamingException) {
throw (NamingException) caughtException;
}
NamingException ne = new CommunicationException(
"DNS error during zone transfer");
ne.setRootCause(caughtException);
throw ne;
}
/**
* Tries to retreive an UDP packet matching the given xid
* received within the timeout.
* If a packet with different xid is received, the received packet
* is enqueued with the corresponding xid in 'resps'.
*/
private byte[] doUdpQuery(Packet pkt, InetAddress server,
int port, int retry, int xid)
throws IOException, NamingException {
int minTimeout = 50; // msec after which there are no retries.
synchronized (udpSocketLock) {
try (DatagramSocket udpSocket = getDatagramSocket()) {
DatagramPacket opkt = new DatagramPacket(
pkt.getData(), pkt.length(), server, port);
DatagramPacket ipkt = new DatagramPacket(new byte[8000], 8000);
// Packets may only be sent to or received from this server address
udpSocket.connect(server, port);
int pktTimeout = (timeout * (1 << retry));
try {
udpSocket.send(opkt);
// timeout remaining after successive 'receive()'
int timeoutLeft = pktTimeout;
int cnt = 0;
do {
if (debug) {
cnt++;
dprint("Trying RECEIVE(" +
cnt + ") retry(" + (retry + 1) +
") for:" + xid + " sock-timeout:" +
timeoutLeft + " ms.");
}
udpSocket.setSoTimeout(timeoutLeft);
long start = System.currentTimeMillis();
udpSocket.receive(ipkt);
long end = System.currentTimeMillis();
byte[] data = ipkt.getData();
if (isMatchResponse(data, xid)) {
return data;
}
timeoutLeft = pktTimeout - ((int) (end - start));
} while (timeoutLeft > minTimeout);
} finally {
udpSocket.disconnect();
}
return null; // no matching packet received within the timeout
}
}
}
/*
* Sends a TCP query, and returns the first DNS message in the response.
*/
private byte[] doTcpQuery(Tcp tcp, Packet pkt) throws IOException {
int len = pkt.length();
// Send 2-byte message length, then send message.
tcp.out.write(len >> 8);
tcp.out.write(len);
tcp.out.write(pkt.getData(), 0, len);
tcp.out.flush();
byte[] msg = continueTcpQuery(tcp);
if (msg == null) {
throw new IOException("DNS error: no response");
}
return msg;
}
/*
* Returns the next DNS message from the TCP socket, or null on EOF.
*/
private byte[] continueTcpQuery(Tcp tcp) throws IOException {
int lenHi = tcp.in.read(); // high-order byte of response length
if (lenHi == -1) {
return null; // EOF
}
int lenLo = tcp.in.read(); // low-order byte of response length
if (lenLo == -1) {
throw new IOException("Corrupted DNS response: bad length");
}
int len = (lenHi << 8) | lenLo;
byte[] msg = new byte[len];
int pos = 0; // next unfilled position in msg
while (len > 0) {
int n = tcp.in.read(msg, pos, len);
if (n == -1) {
throw new IOException(
"Corrupted DNS response: too little data");
}
len -= n;
pos += n;
}
return msg;
}
private Packet makeQueryPacket(DnsName fqdn, int xid,
int qclass, int qtype, boolean recursion) {
int qnameLen = fqdn.getOctets();
int pktLen = DNS_HDR_SIZE + qnameLen + 4;
Packet pkt = new Packet(pktLen);
short flags = recursion ? Header.RD_BIT : 0;
pkt.putShort(xid, IDENT_OFFSET);
pkt.putShort(flags, FLAGS_OFFSET);
pkt.putShort(1, NUMQ_OFFSET);
pkt.putShort(0, NUMANS_OFFSET);
pkt.putInt(0, NUMAUTH_OFFSET);
makeQueryName(fqdn, pkt, DNS_HDR_SIZE);
pkt.putShort(qtype, DNS_HDR_SIZE + qnameLen);
pkt.putShort(qclass, DNS_HDR_SIZE + qnameLen + 2);
return pkt;
}
// Builds a query name in pkt according to the RFC spec.
private void makeQueryName(DnsName fqdn, Packet pkt, int off) {
// Loop through labels, least-significant first.
for (int i = fqdn.size() - 1; i >= 0; i--) {
String label = fqdn.get(i);
int len = label.length();
pkt.putByte(len, off++);
for (int j = 0; j < len; j++) {
pkt.putByte(label.charAt(j), off++);
}
}
if (!fqdn.hasRootLabel()) {
pkt.putByte(0, off);
}
}
//-------------------------------------------------------------------------
private byte[] lookupResponse(Integer xid) throws NamingException {
//
// Check the queued responses: some other thread in between
// received the response for this request.
//
if (debug) {
dprint("LOOKUP for: " + xid +
"\tResponse Q:" + resps);
}
byte[] pkt;
if ((pkt = resps.get(xid)) != null) {
checkResponseCode(new Header(pkt, pkt.length));
synchronized (queuesLock) {
resps.remove(xid);
reqs.remove(xid);
}
if (debug) {
dprint("FOUND (" + Thread.currentThread() +
") for:" + xid);
}
}
return pkt;
}
/*
* Checks the header of an incoming DNS response.
* Returns true if it matches the given xid and throws a naming
* exception, if appropriate, based on the response code.
*
* Also checks that the domain name, type and class in the response
* match those in the original query.
*/
private boolean isMatchResponse(byte[] pkt, int xid)
throws NamingException {
Header hdr = new Header(pkt, pkt.length);
if (hdr.query) {
throw new CommunicationException("DNS error: expecting response");
}
if (!reqs.containsKey(xid)) { // already received, ignore the response
return false;
}
// common case- the request sent matches the subsequent response read
if (hdr.xid == xid) {
if (debug) {
dprint("XID MATCH:" + xid);
}
checkResponseCode(hdr);
if (!hdr.query && hdr.numQuestions == 1) {
ResourceRecord rr = new ResourceRecord(pkt, pkt.length,
Header.HEADER_SIZE, true, false);
// Retrieve the original query
ResourceRecord query = reqs.get(xid);
int qtype = query.getType();
int qclass = query.getRrclass();
DnsName qname = query.getName();
// Check that the type/class/name in the query section of the
// response match those in the original query
if ((qtype == ResourceRecord.QTYPE_STAR ||
qtype == rr.getType()) &&
(qclass == ResourceRecord.QCLASS_STAR ||
qclass == rr.getRrclass()) &&
qname.equals(rr.getName())) {
if (debug) {
dprint("MATCH NAME:" + qname + " QTYPE:" + qtype +
" QCLASS:" + qclass);
}
// Remove the response for the xid if received by some other
// thread.
synchronized (queuesLock) {
resps.remove(xid);
reqs.remove(xid);
}
return true;
} else {
if (debug) {
dprint("NO-MATCH NAME:" + qname + " QTYPE:" + qtype +
" QCLASS:" + qclass);
}
}
}
return false;
}
//
// xid mis-match: enqueue the response, it may belong to some other
// thread that has not yet had a chance to read its response.
// enqueue only the first response, responses for retries are ignored.
//
synchronized (queuesLock) {
if (reqs.containsKey(hdr.xid)) { // enqueue only the first response
resps.put(hdr.xid, pkt);
}
}
if (debug) {
dprint("NO-MATCH SEND ID:" +
xid + " RECVD ID:" + hdr.xid +
" Response Q:" + resps +
" Reqs size:" + reqs.size());
}
return false;
}
/*
* Throws an exception if appropriate for the response code of a
* given header.
*/
private void checkResponseCode(Header hdr) throws NamingException {
int rcode = hdr.rcode;
if (rcode == NO_ERROR) {
return;
}
String msg = (rcode < rcodeDescription.length)
? rcodeDescription[rcode]
: "DNS error";
msg += " [response code " + rcode + "]";
switch (rcode) {
case SERVER_FAILURE:
throw new ServiceUnavailableException(msg);
case NAME_ERROR:
throw new NameNotFoundException(msg);
case NOT_IMPL:
case REFUSED:
throw new OperationNotSupportedException(msg);
case FORMAT_ERROR:
default:
throw new NamingException(msg);
}
}
//-------------------------------------------------------------------------
private static final boolean debug = false;
private static void dprint(String mess) {
if (debug) {
System.err.println("DNS: " + mess);
}
}
}
class Tcp {
private Socket sock;
java.io.InputStream in;
java.io.OutputStream out;
Tcp(InetAddress server, int port) throws IOException {
sock = new Socket(server, port);
sock.setTcpNoDelay(true);
out = new java.io.BufferedOutputStream(sock.getOutputStream());
in = new java.io.BufferedInputStream(sock.getInputStream());
}
void close() throws IOException {
sock.close();
}
}
/*
* javaos emulation -cj
*/
class Packet {
byte buf[];
Packet(int len) {
buf = new byte[len];
}
Packet(byte data[], int len) {
buf = new byte[len];
System.arraycopy(data, 0, buf, 0, len);
}
void putInt(int x, int off) {
buf[off + 0] = (byte)(x >> 24);
buf[off + 1] = (byte)(x >> 16);
buf[off + 2] = (byte)(x >> 8);
buf[off + 3] = (byte)x;
}
void putShort(int x, int off) {
buf[off + 0] = (byte)(x >> 8);
buf[off + 1] = (byte)x;
}
void putByte(int x, int off) {
buf[off] = (byte)x;
}
void putBytes(byte src[], int src_offset, int dst_offset, int len) {
System.arraycopy(src, src_offset, buf, dst_offset, len);
}
int length() {
return buf.length;
}
byte[] getData() {
return buf;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,256 @@
/*
* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.dns;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import javax.naming.*;
import javax.naming.spi.*;
import com.sun.jndi.toolkit.url.UrlUtil;
import sun.net.dns.ResolverConfiguration; // available since 1.4.1
/**
* A DnsContextFactory serves as the initial context factory for DNS.
*
* <p> When an initial context is being created, the environment
* property "java.naming.provider.url" should contain a DNS pseudo-URL
* (see DnsUrl) or a space-separated list of them. Multiple URLs must
* all have the same domain value.
* If the property is not set, the default "dns:" is used.
*
* @author Scott Seligman
*/
public class DnsContextFactory implements InitialContextFactory {
private static final String DEFAULT_URL = "dns:";
private static final int DEFAULT_PORT = 53;
public Context getInitialContext(Hashtable<?,?> env) throws NamingException {
if (env == null) {
env = new Hashtable<>(5);
}
return urlToContext(getInitCtxUrl(env), env);
}
public static DnsContext getContext(String domain,
String[] servers, Hashtable<?,?> env)
throws NamingException {
return new DnsContext(domain, servers, env);
}
/*
* "urls" are used to determine the servers, but any domain
* components are overridden by "domain".
*/
public static DnsContext getContext(String domain,
DnsUrl[] urls, Hashtable<?,?> env)
throws NamingException {
String[] servers = serversForUrls(urls);
DnsContext ctx = getContext(domain, servers, env);
if (platformServersUsed(urls)) {
ctx.setProviderUrl(constructProviderUrl(domain, servers));
}
return ctx;
}
/*
* Public for use by product test suite.
*/
public static boolean platformServersAvailable() {
return !filterNameServers(
ResolverConfiguration.open().nameservers(), true
).isEmpty();
}
private static Context urlToContext(String url, Hashtable<?,?> env)
throws NamingException {
DnsUrl[] urls;
try {
urls = DnsUrl.fromList(url);
} catch (MalformedURLException e) {
throw new ConfigurationException(e.getMessage());
}
if (urls.length == 0) {
throw new ConfigurationException(
"Invalid DNS pseudo-URL(s): " + url);
}
String domain = urls[0].getDomain();
// If multiple urls, all must have the same domain.
for (int i = 1; i < urls.length; i++) {
if (!domain.equalsIgnoreCase(urls[i].getDomain())) {
throw new ConfigurationException(
"Conflicting domains: " + url);
}
}
return getContext(domain, urls, env);
}
/*
* Returns all the servers specified in a set of URLs.
* If a URL has no host (or port), the servers configured on the
* underlying platform are used if possible. If no configured
* servers can be found, then fall back to the old behavior of
* using "localhost".
* There must be at least one URL.
*/
private static String[] serversForUrls(DnsUrl[] urls)
throws NamingException {
if (urls.length == 0) {
throw new ConfigurationException("DNS pseudo-URL required");
}
List<String> servers = new ArrayList<>();
for (int i = 0; i < urls.length; i++) {
String server = urls[i].getHost();
int port = urls[i].getPort();
if (server == null && port < 0) {
// No server or port given, so look to underlying platform.
// ResolverConfiguration does some limited caching, so the
// following is reasonably efficient even if called rapid-fire.
List<String> platformServers = filterNameServers(
ResolverConfiguration.open().nameservers(), false);
if (!platformServers.isEmpty()) {
servers.addAll(platformServers);
continue; // on to next URL (if any, which is unlikely)
}
}
if (server == null) {
server = "localhost";
}
servers.add((port < 0)
? server
: server + ":" + port);
}
return servers.toArray(new String[servers.size()]);
}
/*
* Returns true if serversForUrls(urls) would make use of servers
* from the underlying platform.
*/
private static boolean platformServersUsed(DnsUrl[] urls) {
if (!platformServersAvailable()) {
return false;
}
for (int i = 0; i < urls.length; i++) {
if (urls[i].getHost() == null &&
urls[i].getPort() < 0) {
return true;
}
}
return false;
}
/*
* Returns a value for the PROVIDER_URL property (space-separated URL
* Strings) that reflects the given domain and servers.
* Each server is of the form "server[:port]".
* There must be at least one server.
* IPv6 literal host names include delimiting brackets.
*/
private static String constructProviderUrl(String domain,
String[] servers) {
String path = "";
if (!domain.equals(".")) {
try {
path = "/" + UrlUtil.encode(domain, "ISO-8859-1");
} catch (java.io.UnsupportedEncodingException e) {
// assert false : "ISO-Latin-1 charset unavailable";
}
}
StringBuffer buf = new StringBuffer();
for (int i = 0; i < servers.length; i++) {
if (i > 0) {
buf.append(' ');
}
buf.append("dns://").append(servers[i]).append(path);
}
return buf.toString();
}
/*
* Reads environment to find URL(s) of initial context.
* Default URL is "dns:".
*/
private static String getInitCtxUrl(Hashtable<?,?> env) {
String url = (String) env.get(Context.PROVIDER_URL);
return ((url != null) ? url : DEFAULT_URL);
}
/**
* Removes any DNS server that's not permitted to access
* @param input the input server[:port] list, must not be null
* @param oneIsEnough return output once there exists one ok
* @return the filtered list, all non-permitted input removed
*/
private static List<String> filterNameServers(List<String> input, boolean oneIsEnough) {
SecurityManager security = System.getSecurityManager();
if (security == null || input == null || input.isEmpty()) {
return input;
} else {
List<String> output = new ArrayList<>();
for (String platformServer: input) {
int colon = platformServer.indexOf(':',
platformServer.indexOf(']') + 1);
int p = (colon < 0)
? DEFAULT_PORT
: Integer.parseInt(
platformServer.substring(colon + 1));
String s = (colon < 0)
? platformServer
: platformServer.substring(0, colon);
try {
security.checkConnect(s, p);
output.add(platformServer);
if (oneIsEnough) {
return output;
}
} catch (SecurityException se) {
continue;
}
}
return output;
}
}
}

View File

@@ -0,0 +1,602 @@
/*
* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.dns;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Enumeration;
import javax.naming.*;
/**
* <tt>DnsName</tt> implements compound names for DNS as specified by
* RFCs 1034 and 1035, and as updated and clarified by RFCs 1123 and 2181.
*
* <p> The labels in a domain name correspond to JNDI atomic names.
* Each label must be less than 64 octets in length, and only the
* optional root label at the end of the name may be 0 octets long.
* The sum of the lengths of all labels in a name, plus the number of
* non-root labels plus 1, must be less than 256. The textual
* representation of a domain name consists of the labels, escaped as
* needed, dot-separated, and ordered right-to-left.
*
* <p> A label consists of a sequence of octets, each of which may
* have any value from 0 to 255.
*
* <p> <em>Host names</em> are a subset of domain names.
* Their labels contain only ASCII letters, digits, and hyphens, and
* none may begin or end with a hyphen. While names not conforming to
* these rules may be valid domain names, they will not be usable by a
* number of DNS applications, and should in most cases be avoided.
*
* <p> DNS does not specify an encoding (such as UTF-8) to use for
* octets with non-ASCII values. As of this writing there is some
* work going on in this area, but it is not yet finalized.
* <tt>DnsName</tt> currently converts any non-ASCII octets into
* characters using ISO-LATIN-1 encoding, in effect taking the
* value of each octet and storing it directly into the low-order byte
* of a Java character and <i>vice versa</i>. As a consequence, no
* character in a DNS name will ever have a non-zero high-order byte.
* When the work on internationalizing domain names has stabilized
* (see for example <i>draft-ietf-idn-idna-10.txt</i>), <tt>DnsName</tt>
* may be updated to conform to that work.
*
* <p> Backslash (<tt>\</tt>) is used as the escape character in the
* textual representation of a domain name. The character sequence
* `<tt>\DDD</tt>', where <tt>DDD</tt> is a 3-digit decimal number
* (with leading zeros if needed), represents the octet whose value
* is <tt>DDD</tt>. The character sequence `<tt>\C</tt>', where
* <tt>C</tt> is a character other than <tt>'0'</tt> through
* <tt>'9'</tt>, represents the octet whose value is that of
* <tt>C</tt> (again using ISO-LATIN-1 encoding); this is particularly
* useful for escaping <tt>'.'</tt> or backslash itself. Backslash is
* otherwise not allowed in a domain name. Note that escape characters
* are interpreted when a name is parsed. So, for example, the character
* sequences `<tt>S</tt>', `<tt>\S</tt>', and `<tt>\083</tt>' each
* represent the same one-octet name. The <tt>toString()</tt> method
* does not generally insert escape sequences except where necessary.
* If, however, the <tt>DnsName</tt> was constructed using unneeded
* escapes, those escapes may appear in the <tt>toString</tt> result.
*
* <p> Atomic names passed as parameters to methods of
* <tt>DnsName</tt>, and those returned by them, are unescaped. So,
* for example, <tt>(new&nbsp;DnsName()).add("a.b")</tt> creates an
* object representing the one-label domain name <tt>a\.b</tt>, and
* calling <tt>get(0)</tt> on this object returns <tt>"a.b"</tt>.
*
* <p> While DNS names are case-preserving, comparisons between them
* are case-insensitive. When comparing names containing non-ASCII
* octets, <tt>DnsName</tt> uses case-insensitive comparison
* between pairs of ASCII values, and exact binary comparison
* otherwise.
* <p> A <tt>DnsName</tt> instance is not synchronized against
* concurrent access by multiple threads.
*
* @author Scott Seligman
*/
public final class DnsName implements Name {
// If non-null, the domain name represented by this DnsName.
private String domain = "";
// The labels of this domain name, as a list of strings. Index 0
// corresponds to the leftmost (least significant) label: note that
// this is the reverse of the ordering used by the Name interface.
private ArrayList<String> labels = new ArrayList<>();
// The number of octets needed to carry this domain name in a DNS
// packet. Equal to the sum of the lengths of each label, plus the
// number of non-root labels, plus 1. Must remain less than 256.
private short octets = 1;
/**
* Constructs a <tt>DnsName</tt> representing the empty domain name.
*/
public DnsName() {
}
/**
* Constructs a <tt>DnsName</tt> representing a given domain name.
*
* @param name the domain name to parse
* @throws InvalidNameException if <tt>name</tt> does not conform
* to DNS syntax.
*/
public DnsName(String name) throws InvalidNameException {
parse(name);
}
/*
* Returns a new DnsName with its name components initialized to
* the components of "n" in the range [beg,end). Indexing is as
* for the Name interface, with 0 being the most significant.
*/
private DnsName(DnsName n, int beg, int end) {
// Compute indexes into "labels", which has least-significant label
// at index 0 (opposite to the convention used for "beg" and "end").
int b = n.size() - end;
int e = n.size() - beg;
labels.addAll(n.labels.subList(b, e));
if (size() == n.size()) {
domain = n.domain;
octets = n.octets;
} else {
for (String label: labels) {
if (label.length() > 0) {
octets += (short) (label.length() + 1);
}
}
}
}
public String toString() {
if (domain == null) {
StringBuilder buf = new StringBuilder();
for (String label: labels) {
if (buf.length() > 0 || label.length() == 0) {
buf.append('.');
}
escape(buf, label);
}
domain = buf.toString();
}
return domain;
}
/**
* Does this domain name follow <em>host name</em> syntax?
*/
public boolean isHostName() {
for (String label: labels) {
if (!isHostNameLabel(label)) {
return false;
}
}
return true;
}
public short getOctets() {
return octets;
}
public int size() {
return labels.size();
}
public boolean isEmpty() {
return (size() == 0);
}
public int hashCode() {
int h = 0;
for (int i = 0; i < size(); i++) {
h = 31 * h + getKey(i).hashCode();
}
return h;
}
public boolean equals(Object obj) {
if (!(obj instanceof Name) || (obj instanceof CompositeName)) {
return false;
}
Name n = (Name) obj;
return ((size() == n.size()) && // shortcut: do sizes differ?
(compareTo(obj) == 0));
}
public int compareTo(Object obj) {
Name n = (Name) obj;
return compareRange(0, size(), n); // never 0 if sizes differ
}
public boolean startsWith(Name n) {
return ((size() >= n.size()) &&
(compareRange(0, n.size(), n) == 0));
}
public boolean endsWith(Name n) {
return ((size() >= n.size()) &&
(compareRange(size() - n.size(), size(), n) == 0));
}
public String get(int pos) {
if (pos < 0 || pos >= size()) {
throw new ArrayIndexOutOfBoundsException();
}
int i = size() - pos - 1; // index of "pos" component in "labels"
return labels.get(i);
}
public Enumeration<String> getAll() {
return new Enumeration<String>() {
int pos = 0;
public boolean hasMoreElements() {
return (pos < size());
}
public String nextElement() {
if (pos < size()) {
return get(pos++);
}
throw new java.util.NoSuchElementException();
}
};
}
public Name getPrefix(int pos) {
return new DnsName(this, 0, pos);
}
public Name getSuffix(int pos) {
return new DnsName(this, pos, size());
}
public Object clone() {
return new DnsName(this, 0, size());
}
public Object remove(int pos) {
if (pos < 0 || pos >= size()) {
throw new ArrayIndexOutOfBoundsException();
}
int i = size() - pos - 1; // index of element to remove in "labels"
String label = labels.remove(i);
int len = label.length();
if (len > 0) {
octets -= (short) (len + 1);
}
domain = null; // invalidate "domain"
return label;
}
public Name add(String comp) throws InvalidNameException {
return add(size(), comp);
}
public Name add(int pos, String comp) throws InvalidNameException {
if (pos < 0 || pos > size()) {
throw new ArrayIndexOutOfBoundsException();
}
// Check for empty labels: may have only one, and only at end.
int len = comp.length();
if ((pos > 0 && len == 0) ||
(pos == 0 && hasRootLabel())) {
throw new InvalidNameException(
"Empty label must be the last label in a domain name");
}
// Check total name length.
if (len > 0) {
if (octets + len + 1 >= 256) {
throw new InvalidNameException("Name too long");
}
octets += (short) (len + 1);
}
int i = size() - pos; // index for insertion into "labels"
verifyLabel(comp);
labels.add(i, comp);
domain = null; // invalidate "domain"
return this;
}
public Name addAll(Name suffix) throws InvalidNameException {
return addAll(size(), suffix);
}
public Name addAll(int pos, Name n) throws InvalidNameException {
if (n instanceof DnsName) {
// "n" is a DnsName so we can insert it as a whole, rather than
// verifying and inserting it component-by-component.
// More code, but less work.
DnsName dn = (DnsName) n;
if (dn.isEmpty()) {
return this;
}
// Check for empty labels: may have only one, and only at end.
if ((pos > 0 && dn.hasRootLabel()) ||
(pos == 0 && hasRootLabel())) {
throw new InvalidNameException(
"Empty label must be the last label in a domain name");
}
short newOctets = (short) (octets + dn.octets - 1);
if (newOctets > 255) {
throw new InvalidNameException("Name too long");
}
octets = newOctets;
int i = size() - pos; // index for insertion into "labels"
labels.addAll(i, dn.labels);
// Preserve "domain" if we're appending or prepending,
// otherwise invalidate it.
if (isEmpty()) {
domain = dn.domain;
} else if (domain == null || dn.domain == null) {
domain = null;
} else if (pos == 0) {
domain += (dn.domain.equals(".") ? "" : ".") + dn.domain;
} else if (pos == size()) {
domain = dn.domain + (domain.equals(".") ? "" : ".") + domain;
} else {
domain = null;
}
} else if (n instanceof CompositeName) {
n = (DnsName) n; // force ClassCastException
} else { // "n" is a compound name, but not a DnsName.
// Add labels least-significant first: sometimes more efficient.
for (int i = n.size() - 1; i >= 0; i--) {
add(pos, n.get(i));
}
}
return this;
}
boolean hasRootLabel() {
return (!isEmpty() &&
get(0).equals(""));
}
/*
* Helper method for public comparison methods. Lexicographically
* compares components of this name in the range [beg,end) with
* all components of "n". Indexing is as for the Name interface,
* with 0 being the most significant. Returns negative, zero, or
* positive as these name components are less than, equal to, or
* greater than those of "n".
*/
private int compareRange(int beg, int end, Name n) {
if (n instanceof CompositeName) {
n = (DnsName) n; // force ClassCastException
}
// Loop through labels, starting with most significant.
int minSize = Math.min(end - beg, n.size());
for (int i = 0; i < minSize; i++) {
String label1 = get(i + beg);
String label2 = n.get(i);
int j = size() - (i + beg) - 1; // index of label1 in "labels"
// assert (label1 == labels.get(j));
int c = compareLabels(label1, label2);
if (c != 0) {
return c;
}
}
return ((end - beg) - n.size()); // longer range wins
}
/*
* Returns a key suitable for hashing the label at index i.
* Indexing is as for the Name interface, with 0 being the most
* significant.
*/
String getKey(int i) {
return keyForLabel(get(i));
}
/*
* Parses a domain name, setting the values of instance vars accordingly.
*/
private void parse(String name) throws InvalidNameException {
StringBuffer label = new StringBuffer(); // label being parsed
for (int i = 0; i < name.length(); i++) {
char c = name.charAt(i);
if (c == '\\') { // found an escape sequence
c = getEscapedOctet(name, i++);
if (isDigit(name.charAt(i))) { // sequence is \DDD
i += 2; // consume remaining digits
}
label.append(c);
} else if (c != '.') { // an unescaped octet
label.append(c);
} else { // found '.' separator
add(0, label.toString()); // check syntax, then add label
// to end of name
label.delete(0, i); // clear buffer for next label
}
}
// If name is neither "." nor "", the octets (zero or more)
// from the rightmost dot onward are now added as the final
// label of the name. Those two are special cases in that for
// all other domain names, the number of labels is one greater
// than the number of dot separators.
if (!name.equals("") && !name.equals(".")) {
add(0, label.toString());
}
domain = name; // do this last, since add() sets it to null
}
/*
* Returns (as a char) the octet indicated by the escape sequence
* at a given position within a domain name.
* @throws InvalidNameException if a valid escape sequence is not found.
*/
private static char getEscapedOctet(String name, int pos)
throws InvalidNameException {
try {
// assert (name.charAt(pos) == '\\');
char c1 = name.charAt(++pos);
if (isDigit(c1)) { // sequence is `\DDD'
char c2 = name.charAt(++pos);
char c3 = name.charAt(++pos);
if (isDigit(c2) && isDigit(c3)) {
return (char)
((c1 - '0') * 100 + (c2 - '0') * 10 + (c3 - '0'));
} else {
throw new InvalidNameException(
"Invalid escape sequence in " + name);
}
} else { // sequence is `\C'
return c1;
}
} catch (IndexOutOfBoundsException e) {
throw new InvalidNameException(
"Invalid escape sequence in " + name);
}
}
/*
* Checks that this label is valid.
* @throws InvalidNameException if label is not valid.
*/
private static void verifyLabel(String label) throws InvalidNameException {
if (label.length() > 63) {
throw new InvalidNameException(
"Label exceeds 63 octets: " + label);
}
// Check for two-byte characters.
for (int i = 0; i < label.length(); i++) {
char c = label.charAt(i);
if ((c & 0xFF00) != 0) {
throw new InvalidNameException(
"Label has two-byte char: " + label);
}
}
}
/*
* Does this label conform to host name syntax?
*/
private static boolean isHostNameLabel(String label) {
for (int i = 0; i < label.length(); i++) {
char c = label.charAt(i);
if (!isHostNameChar(c)) {
return false;
}
}
return !(label.startsWith("-") || label.endsWith("-"));
}
private static boolean isHostNameChar(char c) {
return (c == '-' ||
c >= 'a' && c <= 'z' ||
c >= 'A' && c <= 'Z' ||
c >= '0' && c <= '9');
}
private static boolean isDigit(char c) {
return (c >= '0' && c <= '9');
}
/*
* Append a label to buf, escaping as needed.
*/
private static void escape(StringBuilder buf, String label) {
for (int i = 0; i < label.length(); i++) {
char c = label.charAt(i);
if (c == '.' || c == '\\') {
buf.append('\\');
}
buf.append(c);
}
}
/*
* Compares two labels, ignoring case for ASCII values.
* Returns negative, zero, or positive as the first label
* is less than, equal to, or greater than the second.
* See keyForLabel().
*/
private static int compareLabels(String label1, String label2) {
int min = Math.min(label1.length(), label2.length());
for (int i = 0; i < min; i++) {
char c1 = label1.charAt(i);
char c2 = label2.charAt(i);
if (c1 >= 'A' && c1 <= 'Z') {
c1 += 'a' - 'A'; // to lower case
}
if (c2 >= 'A' && c2 <= 'Z') {
c2 += 'a' - 'A'; // to lower case
}
if (c1 != c2) {
return (c1 - c2);
}
}
return (label1.length() - label2.length()); // the longer one wins
}
/*
* Returns a key suitable for hashing a label. Two labels map to
* the same key iff they are equal, taking possible case-folding
* into account. See compareLabels().
*/
private static String keyForLabel(String label) {
StringBuffer buf = new StringBuffer(label.length());
for (int i = 0; i < label.length(); i++) {
char c = label.charAt(i);
if (c >= 'A' && c <= 'Z') {
c += 'a' - 'A'; // to lower case
}
buf.append(c);
}
return buf.toString();
}
/**
* Serializes only the domain name string, for compactness and to avoid
* any implementation dependency.
*
* @serialdata The domain name string.
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
s.writeObject(toString());
}
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
try {
parse((String) s.readObject());
} catch (InvalidNameException e) {
// shouldn't happen
throw new java.io.StreamCorruptedException(
"Invalid name: " + domain);
}
}
private static final long serialVersionUID = 7040187611324710271L;
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2000, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.dns;
import javax.naming.*;
/**
* A name parser for DNS names.
*
* @author Scott Seligman
*/
class DnsNameParser implements NameParser {
public Name parse(String name) throws NamingException {
return new DnsName(name);
}
// Every DnsNameParser is created equal.
public boolean equals(Object obj) {
return (obj instanceof DnsNameParser);
}
public int hashCode() {
return DnsNameParser.class.hashCode() + 1;
}
}

View File

@@ -0,0 +1,180 @@
/*
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.dns;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import sun.security.action.GetPropertyAction;
import java.util.Locale;
import java.util.StringTokenizer;
import com.sun.jndi.toolkit.url.Uri;
import com.sun.jndi.toolkit.url.UrlUtil;
/**
* A DnsUrl represents a DNS pseudo-URL of the form
* <pre>
* dns://[host][:port][/[domain]]
* or
* dns:[/][domain]
* </pre>
* The host names a DNS server. If the host is not provided, it
* indicates that the underlying platform's DNS server(s) should be
* used if possible, or that "localhost" should be used otherwise. If
* the port is not provided, the DNS default port 53 will be used.
* The domain indicates the domain name of the context, and is not
* necessarily related to the domain of the server; if it is not
* provided, the root domain "." is used. Special characters in
* the domain name must be %-escaped as described in RFC 2396.
*
* @author Scott Seligman
*/
public class DnsUrl extends Uri {
private static final String PARSE_MODE_PROP = "com.sun.jndi.dnsURLParsing";
private static final ParseMode DEFAULT_PARSE_MODE = ParseMode.COMPAT;
public static final ParseMode PARSE_MODE;
static {
PrivilegedAction<String> action =
new GetPropertyAction(PARSE_MODE_PROP, DEFAULT_PARSE_MODE.toString());
ParseMode parseMode = DEFAULT_PARSE_MODE;
try {
String mode = AccessController.doPrivileged(action);
parseMode = ParseMode.valueOf(mode.toUpperCase(Locale.ROOT));
} catch (Throwable t) {
parseMode = DEFAULT_PARSE_MODE;
} finally {
PARSE_MODE = parseMode;
}
}
private String domain; // domain name of the context
/**
* Given a space-separated list of DNS URLs, returns an array of DnsUrl
* objects.
*/
public static DnsUrl[] fromList(String urlList)
throws MalformedURLException {
DnsUrl[] urls = new DnsUrl[(urlList.length() + 1) / 2];
int i = 0; // next available index in urls
StringTokenizer st = new StringTokenizer(urlList, " ");
while (st.hasMoreTokens()) {
try {
urls[i++] = new DnsUrl(validateURI(st.nextToken()));
} catch (URISyntaxException e) {
MalformedURLException mue = new MalformedURLException(e.getMessage());
mue.initCause(e);
throw mue;
}
}
DnsUrl[] trimmed = new DnsUrl[i];
System.arraycopy(urls, 0, trimmed, 0, i);
return trimmed;
}
@Override
protected ParseMode parseMode() {
return PARSE_MODE;
}
@Override
protected final boolean isSchemeOnly(String uri) {
return isDnsSchemeOnly(uri);
}
@Override
protected boolean checkSchemeOnly(String uri, String scheme) {
return uri.equals(scheme + ":") || uri.equals(scheme + "://");
}
@Override
protected final MalformedURLException newInvalidURISchemeException(String uri) {
return new MalformedURLException(
uri + " is not a valid DNS pseudo-URL");
}
private static boolean isDnsSchemeOnly(String uri) {
return "dns:".equals(uri) || "dns://".equals(uri);
}
private static String validateURI(String uri) throws URISyntaxException {
// no validation in legacy parsing mode
if (PARSE_MODE == ParseMode.LEGACY) return uri;
// special case of scheme-only URIs
if (isDnsSchemeOnly(uri)) return uri;
// use java.net.URI to validate the uri syntax
return new URI(uri).toString();
}
public DnsUrl(String url) throws MalformedURLException {
super(url);
if (!scheme.equals("dns")) {
throw newInvalidURISchemeException(url);
}
domain = path.startsWith("/")
? path.substring(1)
: path;
domain = domain.equals("")
? "."
: UrlUtil.decode(domain);
// Debug
// System.out.println("host=" + host + " port=" + port +
// " domain=" + domain);
}
/**
* Returns the domain of this URL, or "." if none is provided.
* Never null.
*/
public String getDomain() {
return domain;
}
/*
// Debug
public static void main(String args[]) throws MalformedURLException {
DnsUrl[] urls = fromList(args[0]);
for (int i = 0; i < urls.length; i++) {
System.out.println(urls[i].toString());
}
}
*/
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright (c) 2000, 2002, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.dns;
import javax.naming.*;
/**
* The Header class represents the header of a DNS message.
*
* @author Scott Seligman
*/
class Header {
static final int HEADER_SIZE = 12; // octets in a DNS header
// Masks and shift amounts for DNS header flag fields.
static final short QR_BIT = (short) 0x8000;
static final short OPCODE_MASK = (short) 0x7800;
static final int OPCODE_SHIFT = 11;
static final short AA_BIT = (short) 0x0400;
static final short TC_BIT = (short) 0x0200;
static final short RD_BIT = (short) 0x0100;
static final short RA_BIT = (short) 0x0080;
static final short RCODE_MASK = (short) 0x000F;
int xid; // ID: 16-bit query identifier
boolean query; // QR: true if query, false if response
int opcode; // OPCODE: 4-bit opcode
boolean authoritative; // AA
boolean truncated; // TC
boolean recursionDesired; // RD
boolean recursionAvail; // RA
int rcode; // RCODE: 4-bit response code
int numQuestions;
int numAnswers;
int numAuthorities;
int numAdditionals;
/*
* Returns a representation of a decoded DNS message header.
* Does not modify or store a reference to the msg array.
*/
Header(byte[] msg, int msgLen) throws NamingException {
decode(msg, msgLen);
}
/*
* Decodes a DNS message header. Does not modify or store a
* reference to the msg array.
*/
private void decode(byte[] msg, int msgLen) throws NamingException {
try {
int pos = 0; // current offset into msg
if (msgLen < HEADER_SIZE) {
throw new CommunicationException(
"DNS error: corrupted message header");
}
xid = getShort(msg, pos);
pos += 2;
// Flags
short flags = (short) getShort(msg, pos);
pos += 2;
query = (flags & QR_BIT) == 0;
opcode = (flags & OPCODE_MASK) >>> OPCODE_SHIFT;
authoritative = (flags & AA_BIT) != 0;
truncated = (flags & TC_BIT) != 0;
recursionDesired = (flags & RD_BIT) != 0;
recursionAvail = (flags & RA_BIT) != 0;
rcode = (flags & RCODE_MASK);
// RR counts
numQuestions = getShort(msg, pos);
pos += 2;
numAnswers = getShort(msg, pos);
pos += 2;
numAuthorities = getShort(msg, pos);
pos += 2;
numAdditionals = getShort(msg, pos);
pos += 2;
} catch (IndexOutOfBoundsException e) {
throw new CommunicationException(
"DNS error: corrupted message header");
}
}
/*
* Returns the 2-byte unsigned value at msg[pos]. The high
* order byte comes first.
*/
private static int getShort(byte[] msg, int pos) {
return (((msg[pos] & 0xFF) << 8) |
(msg[pos + 1] & 0xFF));
}
}

View File

@@ -0,0 +1,156 @@
/*
* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.dns;
import java.util.Hashtable;
/**
* A NameNode represents a node in the DNS namespace. Each node
* has a label, which is its name relative to its parent (so the
* node at Sun.COM has label "Sun"). Each node has a hashtable of
* children indexed by their labels converted to lower-case.
*
* <p> A node may be addressed from another by giving a DnsName
* consisting of the sequence of labels from one node to the other.
*
* <p> Each node also has an <tt>isZoneCut</tt> flag, used to indicate
* if the node is a zone cut. A zone cut is a node with an NS record
* that is contained in one zone, but that actually belongs to a child zone.
*
* <p> All access is unsynchronized.
*
* @author Scott Seligman
*/
class NameNode {
private String label; // name of this node relative to its
// parent, or null for root of a tree
private Hashtable<String,NameNode> children = null; // child nodes
private boolean isZoneCut = false; // true if this node is a zone cut
private int depth = 0; // depth in tree (0 for root)
NameNode(String label) {
this.label = label;
}
/*
* Returns a newly-allocated NameNode. Used to allocate new nodes
* in a tree. Should be overridden in a subclass to return an object
* of the subclass's type.
*/
protected NameNode newNameNode(String label) {
return new NameNode(label);
}
/*
* Returns the name of this node relative to its parent, or null for
* the root of a tree.
*/
String getLabel() {
return label;
}
/*
* Returns the depth of this node in the tree. The depth of the root
* is 0.
*/
int depth() {
return depth;
}
boolean isZoneCut() {
return isZoneCut;
}
void setZoneCut(boolean isZoneCut) {
this.isZoneCut = isZoneCut;
}
/*
* Returns the children of this node, or null if there are none.
* The caller must not modify the Hashtable returned.
*/
Hashtable<String,NameNode> getChildren() {
return children;
}
/*
* Returns the child node given the hash key (the down-cased label)
* for its name relative to this node, or null if there is no such
* child.
*/
NameNode get(String key) {
return (children != null)
? children.get(key)
: null;
}
/*
* Returns the node at the end of a path, or null if the
* node does not exist.
* The path is specified by the labels of <tt>name</tt>, beginning
* at index idx.
*/
NameNode get(DnsName name, int idx) {
NameNode node = this;
for (int i = idx; i < name.size() && node != null; i++) {
node = node.get(name.getKey(i));
}
return node;
}
/*
* Returns the node at the end of a path, creating it and any
* intermediate nodes as needed.
* The path is specified by the labels of <tt>name</tt>, beginning
* at index idx.
*/
NameNode add(DnsName name, int idx) {
NameNode node = this;
for (int i = idx; i < name.size(); i++) {
String label = name.get(i);
String key = name.getKey(i);
NameNode child = null;
if (node.children == null) {
node.children = new Hashtable<>();
} else {
child = node.children.get(key);
}
if (child == null) {
child = newNameNode(label);
child.depth = node.depth + 1;
node.children.put(key, child);
}
node = child;
}
return node;
}
}

View File

@@ -0,0 +1,190 @@
/*
* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.dns;
import javax.naming.*;
/**
* The Resolver class performs DNS client operations in support of DnsContext.
*
* <p> Every DnsName instance passed to or returned from a method of
* this class should be fully-qualified and contain a root label (an
* empty component at position 0).
*
* @author Scott Seligman
*/
class Resolver {
private DnsClient dnsClient;
private int timeout; // initial timeout on UDP queries in ms
private int retries; // number of UDP retries
/*
* Constructs a new Resolver given its servers and timeout parameters.
* Each server is of the form "server[:port]".
* IPv6 literal host names include delimiting brackets.
* There must be at least one server.
* "timeout" is the initial timeout interval (in ms) for UDP queries,
* and "retries" gives the number of retries per server.
*/
Resolver(String[] servers, int timeout, int retries)
throws NamingException {
this.timeout = timeout;
this.retries = retries;
dnsClient = new DnsClient(servers, timeout, retries);
}
public void close() {
dnsClient.close();
dnsClient = null;
}
/*
* Queries resource records of a particular class and type for a
* given domain name.
* Useful values of rrclass are ResourceRecord.[Q]CLASS_xxx.
* Useful values of rrtype are ResourceRecord.[Q]TYPE_xxx.
* If recursion is true, recursion is requested on the query.
* If auth is true, only authoritative responses are accepted.
*/
ResourceRecords query(DnsName fqdn, int rrclass, int rrtype,
boolean recursion, boolean auth)
throws NamingException {
return dnsClient.query(fqdn, rrclass, rrtype, recursion, auth);
}
/*
* Queries all resource records of a zone given its domain name and class.
* If recursion is true, recursion is requested on the query to find
* the name server (and also on the zone transfer, but it won't matter).
*/
ResourceRecords queryZone(DnsName zone, int rrclass, boolean recursion)
throws NamingException {
DnsClient cl =
new DnsClient(findNameServers(zone, recursion), timeout, retries);
try {
return cl.queryZone(zone, rrclass, recursion);
} finally {
cl.close();
}
}
/*
* Finds the zone of a given domain name. The method is to look
* for the first SOA record on the path from the given domain to
* the root. This search may be partially bypassed if the zone's
* SOA record is received in the authority section of a response.
* If recursion is true, recursion is requested on any queries.
*/
DnsName findZoneName(DnsName fqdn, int rrclass, boolean recursion)
throws NamingException {
fqdn = (DnsName) fqdn.clone();
while (fqdn.size() > 1) { // while below root
ResourceRecords rrs = null;
try {
rrs = query(fqdn, rrclass, ResourceRecord.TYPE_SOA,
recursion, false);
} catch (NameNotFoundException e) {
throw e;
} catch (NamingException e) {
// Ignore error and keep searching up the tree.
}
if (rrs != null) {
if (rrs.answer.size() > 0) { // found zone's SOA
return fqdn;
}
// Look for an SOA record giving the zone's top node.
for (int i = 0; i < rrs.authority.size(); i++) {
ResourceRecord rr = rrs.authority.elementAt(i);
if (rr.getType() == ResourceRecord.TYPE_SOA) {
DnsName zone = rr.getName();
if (fqdn.endsWith(zone)) {
return zone;
}
}
}
}
fqdn.remove(fqdn.size() - 1); // one step rootward
}
return fqdn; // no SOA found below root, so
// return root
}
/*
* Finds a zone's SOA record. Returns null if no SOA is found (in
* which case "zone" is not actually a zone).
* If recursion is true, recursion is requested on the query.
*/
ResourceRecord findSoa(DnsName zone, int rrclass, boolean recursion)
throws NamingException {
ResourceRecords rrs = query(zone, rrclass, ResourceRecord.TYPE_SOA,
recursion, false);
for (int i = 0; i < rrs.answer.size(); i++) {
ResourceRecord rr = rrs.answer.elementAt(i);
if (rr.getType() == ResourceRecord.TYPE_SOA) {
return rr;
}
}
return null;
}
/*
* Finds the name servers of a zone. <tt>zone</tt> is a fully-qualified
* domain name at the top of a zone.
* If recursion is true, recursion is requested on the query.
*/
private String[] findNameServers(DnsName zone, boolean recursion)
throws NamingException {
// %%% As an optimization, could look in authority section of
// findZoneName() response first.
ResourceRecords rrs =
query(zone, ResourceRecord.CLASS_INTERNET, ResourceRecord.TYPE_NS,
recursion, false);
String[] ns = new String[rrs.answer.size()];
for (int i = 0; i < ns.length; i++) {
ResourceRecord rr = rrs.answer.elementAt(i);
if (rr.getType() != ResourceRecord.TYPE_NS) {
throw new CommunicationException("Corrupted DNS message");
}
ns[i] = (String) rr.getRdata();
// Server name will be passed to InetAddress.getByName(), which
// may not be able to handle a trailing dot.
// assert ns[i].endsWith(".");
ns[i] = ns[i].substring(0, ns[i].length() - 1);
}
return ns;
}
}

View File

@@ -0,0 +1,643 @@
/*
* Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.dns;
import javax.naming.CommunicationException;
import javax.naming.InvalidNameException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* The ResourceRecord class represents a DNS resource record.
* The string format is based on the master file representation in
* RFC 1035.
*
* @author Scott Seligman
*/
public class ResourceRecord {
/*
* Resource record type codes
*/
static final int TYPE_A = 1;
static final int TYPE_NS = 2;
static final int TYPE_CNAME = 5;
static final int TYPE_SOA = 6;
static final int TYPE_PTR = 12;
static final int TYPE_HINFO = 13;
static final int TYPE_MX = 15;
static final int TYPE_TXT = 16;
static final int TYPE_AAAA = 28;
static final int TYPE_SRV = 33;
static final int TYPE_NAPTR = 35;
static final int QTYPE_AXFR = 252; // zone transfer
static final int QTYPE_STAR = 255; // query type "*"
/*
* Mapping from resource record type codes to type name strings.
*/
static final String rrTypeNames[] = {
null, "A", "NS", null, null,
"CNAME", "SOA", null, null, null,
null, null, "PTR", "HINFO", null,
"MX", "TXT", null, null, null,
null, null, null, null, null,
null, null, null, "AAAA", null,
null, null, null, "SRV", null,
"NAPTR"
};
/*
* Resource record class codes
*/
static final int CLASS_INTERNET = 1;
static final int CLASS_HESIOD = 2;
static final int QCLASS_STAR = 255; // query class "*"
/*
* Mapping from resource record type codes to class name strings.
*/
static final String rrClassNames[] = {
null, "IN", null, null, "HS"
};
/*
* Maximum number of compression references in labels.
* Used to detect compression loops.
*/
private static final int MAXIMUM_COMPRESSION_REFERENCES = 16;
byte[] msg; // DNS message
int msgLen; // msg size (in octets)
boolean qSection; // true if this RR is part of question section
// and therefore has no ttl or rdata
int offset; // offset of RR w/in msg
int rrlen; // number of octets in encoded RR
DnsName name; // name field of RR, including root label
int rrtype; // type field of RR
String rrtypeName; // name of of rrtype
int rrclass; // class field of RR
String rrclassName; // name of rrclass
int ttl = 0; // ttl field of RR
int rdlen = 0; // number of octets of rdata
Object rdata = null; // rdata -- most are String, unknown are byte[]
/*
* Constructs a new ResourceRecord. The encoded data of the DNS
* message is contained in msg; data for this RR begins at msg[offset].
* If qSection is true this RR is part of a question section. It's
* not a true resource record in that case, but is treated as if it
* were a shortened one (with no ttl or rdata). If decodeRdata is
* false, the rdata is not decoded (and getRdata() will return null)
* unless this is an SOA record.
*
* @throws CommunicationException if a decoded domain name isn't valid.
* @throws ArrayIndexOutOfBoundsException given certain other corrupt data.
*/
ResourceRecord(byte[] msg, int msgLen, int offset,
boolean qSection, boolean decodeRdata)
throws CommunicationException {
this.msg = msg;
this.msgLen = msgLen;
this.offset = offset;
this.qSection = qSection;
decode(decodeRdata);
}
public String toString() {
String text = name + " " + rrclassName + " " + rrtypeName;
if (!qSection) {
text += " " + ttl + " " +
((rdata != null) ? rdata : "[n/a]");
}
return text;
}
/*
* Returns the name field of this RR, including the root label.
*/
public DnsName getName() {
return name;
}
/*
* Returns the number of octets in the encoded RR.
*/
public int size() {
return rrlen;
}
public int getType() {
return rrtype;
}
public int getRrclass() {
return rrclass;
}
public Object getRdata() {
return rdata;
}
public static String getTypeName(int rrtype) {
return valueToName(rrtype, rrTypeNames);
}
public static int getType(String typeName) {
return nameToValue(typeName, rrTypeNames);
}
public static String getRrclassName(int rrclass) {
return valueToName(rrclass, rrClassNames);
}
public static int getRrclass(String className) {
return nameToValue(className, rrClassNames);
}
private static String valueToName(int val, String[] names) {
String name = null;
if ((val > 0) && (val < names.length)) {
name = names[val];
} else if (val == QTYPE_STAR) { // QTYPE_STAR == QCLASS_STAR
name = "*";
}
if (name == null) {
name = Integer.toString(val);
}
return name;
}
private static int nameToValue(String name, String[] names) {
if (name.equals("")) {
return -1; // invalid name
} else if (name.equals("*")) {
return QTYPE_STAR; // QTYPE_STAR == QCLASS_STAR
}
if (Character.isDigit(name.charAt(0))) {
try {
return Integer.parseInt(name);
} catch (NumberFormatException e) {
}
}
for (int i = 1; i < names.length; i++) {
if ((names[i] != null) &&
name.equalsIgnoreCase(names[i])) {
return i;
}
}
return -1; // unknown name
}
/*
* Compares two SOA record serial numbers using 32-bit serial number
* arithmetic as defined in RFC 1982. Serial numbers are unsigned
* 32-bit quantities. Returns a negative, zero, or positive value
* as the first serial number is less than, equal to, or greater
* than the second. If the serial numbers are not comparable the
* result is undefined. Note that the relation is not transitive.
*/
public static int compareSerialNumbers(long s1, long s2) {
long diff = s2 - s1;
if (diff == 0) {
return 0;
} else if ((diff > 0 && diff <= 0x7FFFFFFF) ||
(diff < 0 && -diff > 0x7FFFFFFF)) {
return -1;
} else {
return 1;
}
}
/*
* Decodes the binary format of the RR.
* May throw ArrayIndexOutOfBoundsException given corrupt data.
*/
private void decode(boolean decodeRdata) throws CommunicationException {
int pos = offset; // index of next unread octet
name = new DnsName(); // NAME
pos = decodeName(pos, name);
rrtype = getUShort(pos); // TYPE
rrtypeName = (rrtype < rrTypeNames.length)
? rrTypeNames[rrtype]
: null;
if (rrtypeName == null) {
rrtypeName = Integer.toString(rrtype);
}
pos += 2;
rrclass = getUShort(pos); // CLASS
rrclassName = (rrclass < rrClassNames.length)
? rrClassNames[rrclass]
: null;
if (rrclassName == null) {
rrclassName = Integer.toString(rrclass);
}
pos += 2;
if (!qSection) {
ttl = getInt(pos); // TTL
pos += 4;
rdlen = getUShort(pos); // RDLENGTH
pos += 2;
rdata = (decodeRdata || // RDATA
(rrtype == TYPE_SOA))
? decodeRdata(pos)
: null;
if (rdata instanceof DnsName) {
rdata = rdata.toString();
}
pos += rdlen;
}
rrlen = pos - offset;
msg = null; // free up for GC
}
/*
* Returns the 1-byte unsigned value at msg[pos].
*/
private int getUByte(int pos) {
return (msg[pos] & 0xFF);
}
/*
* Returns the 2-byte unsigned value at msg[pos]. The high
* order byte comes first.
*/
private int getUShort(int pos) {
return (((msg[pos] & 0xFF) << 8) |
(msg[pos + 1] & 0xFF));
}
/*
* Returns the 4-byte signed value at msg[pos]. The high
* order byte comes first.
*/
private int getInt(int pos) {
return ((getUShort(pos) << 16) | getUShort(pos + 2));
}
/*
* Returns the 4-byte unsigned value at msg[pos]. The high
* order byte comes first.
*/
private long getUInt(int pos) {
return (getInt(pos) & 0xffffffffL);
}
/*
* Returns the name encoded at msg[pos], including the root label.
*/
private DnsName decodeName(int pos) throws CommunicationException {
DnsName n = new DnsName();
decodeName(pos, n);
return n;
}
/*
* Prepends to "n" the domain name encoded at msg[pos], including the root
* label. Returns the index into "msg" following the name.
*/
private int decodeName(int pos, DnsName n) throws CommunicationException {
int endPos = -1;
int level = 0;
try {
while (true) {
if (level > MAXIMUM_COMPRESSION_REFERENCES)
throw new IOException("Too many compression references");
int typeAndLen = msg[pos] & 0xFF;
if (typeAndLen == 0) { // end of name
++pos;
n.add(0, "");
break;
} else if (typeAndLen <= 63) { // regular label
++pos;
n.add(0, new String(msg, pos, typeAndLen,
StandardCharsets.ISO_8859_1));
pos += typeAndLen;
} else if ((typeAndLen & 0xC0) == 0xC0) { // name compression
++level;
// cater for the case where the name pointed to is itself
// compressed: we don't want endPos to be reset by the second
// compression level.
int ppos = pos;
if (endPos == -1) endPos = pos + 2;
pos = getUShort(pos) & 0x3FFF;
if (debug) {
dprint("decode: name compression at " + ppos
+ " -> " + pos + " endPos=" + endPos);
assert endPos > 0;
assert pos < ppos;
assert pos >= Header.HEADER_SIZE;
}
} else
throw new IOException("Invalid label type: " + typeAndLen);
}
} catch (IOException | InvalidNameException e) {
CommunicationException ce =new CommunicationException(
"DNS error: malformed packet");
ce.initCause(e);
throw ce;
}
if (endPos == -1)
endPos = pos;
return endPos;
}
/*
* Returns the rdata encoded at msg[pos]. The format is dependent
* on the rrtype and rrclass values, which have already been set.
* The length of the encoded data is rdlen, which has already been
* set.
* The rdata of records with unknown type/class combinations is
* returned in a newly-allocated byte array.
*/
private Object decodeRdata(int pos) throws CommunicationException {
if (rrclass == CLASS_INTERNET) {
switch (rrtype) {
case TYPE_A:
return decodeA(pos);
case TYPE_AAAA:
return decodeAAAA(pos);
case TYPE_CNAME:
case TYPE_NS:
case TYPE_PTR:
return decodeName(pos);
case TYPE_MX:
return decodeMx(pos);
case TYPE_SOA:
return decodeSoa(pos);
case TYPE_SRV:
return decodeSrv(pos);
case TYPE_NAPTR:
return decodeNaptr(pos);
case TYPE_TXT:
return decodeTxt(pos);
case TYPE_HINFO:
return decodeHinfo(pos);
}
}
// Unknown RR type/class
if (debug) {
dprint("Unknown RR type for RR data: " + rrtype + " rdlen=" + rdlen
+ ", pos=" + pos +", msglen=" + msg.length + ", remaining="
+ (msg.length-pos));
}
byte[] rd = new byte[rdlen];
System.arraycopy(msg, pos, rd, 0, rdlen);
return rd;
}
/*
* Returns the rdata of an MX record that is encoded at msg[pos].
*/
private String decodeMx(int pos) throws CommunicationException {
int preference = getUShort(pos);
pos += 2;
DnsName name = decodeName(pos);
return (preference + " " + name);
}
/*
* Returns the rdata of an SOA record that is encoded at msg[pos].
*/
private String decodeSoa(int pos) throws CommunicationException {
DnsName mname = new DnsName();
pos = decodeName(pos, mname);
DnsName rname = new DnsName();
pos = decodeName(pos, rname);
long serial = getUInt(pos);
pos += 4;
long refresh = getUInt(pos);
pos += 4;
long retry = getUInt(pos);
pos += 4;
long expire = getUInt(pos);
pos += 4;
long minimum = getUInt(pos); // now used as negative TTL
pos += 4;
return (mname + " " + rname + " " + serial + " " +
refresh + " " + retry + " " + expire + " " + minimum);
}
/*
* Returns the rdata of an SRV record that is encoded at msg[pos].
* See RFC 2782.
*/
private String decodeSrv(int pos) throws CommunicationException {
int priority = getUShort(pos);
pos += 2;
int weight = getUShort(pos);
pos += 2;
int port = getUShort(pos);
pos += 2;
DnsName target = decodeName(pos);
return (priority + " " + weight + " " + port + " " + target);
}
/*
* Returns the rdata of an NAPTR record that is encoded at msg[pos].
* See RFC 2915.
*/
private String decodeNaptr(int pos) throws CommunicationException {
int order = getUShort(pos);
pos += 2;
int preference = getUShort(pos);
pos += 2;
StringBuffer flags = new StringBuffer();
pos += decodeCharString(pos, flags);
StringBuffer services = new StringBuffer();
pos += decodeCharString(pos, services);
StringBuffer regexp = new StringBuffer(rdlen);
pos += decodeCharString(pos, regexp);
DnsName replacement = decodeName(pos);
return (order + " " + preference + " " + flags + " " +
services + " " + regexp + " " + replacement);
}
/*
* Returns the rdata of a TXT record that is encoded at msg[pos].
* The rdata consists of one or more <character-string>s.
*/
private String decodeTxt(int pos) {
StringBuffer buf = new StringBuffer(rdlen);
int end = pos + rdlen;
while (pos < end) {
pos += decodeCharString(pos, buf);
if (pos < end) {
buf.append(' ');
}
}
return buf.toString();
}
/*
* Returns the rdata of an HINFO record that is encoded at msg[pos].
* The rdata consists of two <character-string>s.
*/
private String decodeHinfo(int pos) {
StringBuffer buf = new StringBuffer(rdlen);
pos += decodeCharString(pos, buf);
buf.append(' ');
pos += decodeCharString(pos, buf);
return buf.toString();
}
/*
* Decodes the <character-string> at msg[pos] and adds it to buf.
* If the string contains one of the meta-characters ' ', '\\', or
* '"', then the result is quoted and any embedded '\\' or '"'
* chars are escaped with '\\'. Empty strings are also quoted.
* Returns the size of the encoded string, including the initial
* length octet.
*/
private int decodeCharString(int pos, StringBuffer buf) {
int start = buf.length(); // starting index of this string
int len = getUByte(pos++); // encoded string length
boolean quoted = (len == 0); // quote string if empty
for (int i = 0; i < len; i++) {
int c = getUByte(pos++);
quoted |= (c == ' ');
if ((c == '\\') || (c == '"')) {
quoted = true;
buf.append('\\');
}
buf.append((char) c);
}
if (quoted) {
buf.insert(start, '"');
buf.append('"');
}
return (len + 1); // size includes initial octet
}
/*
* Returns the rdata of an A record, in dotted-decimal format,
* that is encoded at msg[pos].
*/
private String decodeA(int pos) {
return ((msg[pos] & 0xff) + "." +
(msg[pos + 1] & 0xff) + "." +
(msg[pos + 2] & 0xff) + "." +
(msg[pos + 3] & 0xff));
}
/*
* Returns the rdata of an AAAA record, in colon-separated format,
* that is encoded at msg[pos]. For example: 4321:0:1:2:3:4:567:89ab.
* See RFCs 1886 and 2373.
*/
private String decodeAAAA(int pos) {
int[] addr6 = new int[8]; // the unsigned 16-bit words of the address
for (int i = 0; i < 8; i++) {
addr6[i] = getUShort(pos);
pos += 2;
}
// Find longest sequence of two or more zeros, to compress them.
int curBase = -1;
int curLen = 0;
int bestBase = -1;
int bestLen = 0;
for (int i = 0; i < 8; i++) {
if (addr6[i] == 0) {
if (curBase == -1) { // new sequence
curBase = i;
curLen = 1;
} else { // extend sequence
++curLen;
if ((curLen >= 2) && (curLen > bestLen)) {
bestBase = curBase;
bestLen = curLen;
}
}
} else { // not in sequence
curBase = -1;
}
}
// If addr begins with at least 6 zeros and is not :: or ::1,
// or with 5 zeros followed by 0xffff, use the text format for
// IPv4-compatible or IPv4-mapped addresses.
if (bestBase == 0) {
if ((bestLen == 6) ||
((bestLen == 7) && (addr6[7] > 1))) {
return ("::" + decodeA(pos - 4));
} else if ((bestLen == 5) && (addr6[5] == 0xffff)) {
return ("::ffff:" + decodeA(pos - 4));
}
}
// If bestBase != -1, compress zeros in [bestBase, bestBase+bestLen)
boolean compress = (bestBase != -1);
StringBuffer buf = new StringBuffer(40);
if (bestBase == 0) {
buf.append(':');
}
for (int i = 0; i < 8; i++) {
if (!compress || (i < bestBase) || (i >= bestBase + bestLen)) {
buf.append(Integer.toHexString(addr6[i]));
if (i < 7) {
buf.append(':');
}
} else if (compress && (i == bestBase)) { // first compressed zero
buf.append(':');
}
}
return buf.toString();
}
//-------------------------------------------------------------------------
private static final boolean debug = false;
private static void dprint(String mess) {
if (debug) {
System.err.println("DNS: " + mess);
}
}
}

View File

@@ -0,0 +1,140 @@
/*
* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.dns;
import java.util.Vector;
import javax.naming.*;
/**
* The ResourceRecords class represents the resource records in the
* four sections of a DNS message.
*
* The additional records section is currently ignored.
*
* @author Scott Seligman
*/
class ResourceRecords {
// Four sections: question, answer, authority, additional.
// The question section is treated as being made up of (shortened)
// resource records, although this isn't technically how it's defined.
Vector<ResourceRecord> question = new Vector<>();
Vector<ResourceRecord> answer = new Vector<>();
Vector<ResourceRecord> authority = new Vector<>();
Vector<ResourceRecord> additional = new Vector<>();
/*
* True if these resource records are from a zone transfer. In
* that case only answer records are read (as per
* draft-ietf-dnsext-axfr-clarify-02.txt). Also, the rdata of
* those answer records is not decoded (for efficiency) except
* for SOA records.
*/
boolean zoneXfer;
/*
* Returns a representation of the resource records in a DNS message.
* Does not modify or store a reference to the msg array.
*/
ResourceRecords(byte[] msg, int msgLen, Header hdr, boolean zoneXfer)
throws NamingException {
if (zoneXfer) {
answer.ensureCapacity(8192); // an arbitrary "large" number
}
this.zoneXfer = zoneXfer;
add(msg, msgLen, hdr);
}
/*
* Returns the type field of the first answer record, or -1 if
* there are no answer records.
*/
int getFirstAnsType() {
if (answer.size() == 0) {
return -1;
}
return answer.firstElement().getType();
}
/*
* Returns the type field of the last answer record, or -1 if
* there are no answer records.
*/
int getLastAnsType() {
if (answer.size() == 0) {
return -1;
}
return answer.lastElement().getType();
}
/*
* Decodes the resource records in a DNS message and adds
* them to this object.
* Does not modify or store a reference to the msg array.
*/
void add(byte[] msg, int msgLen, Header hdr) throws NamingException {
ResourceRecord rr;
int pos = Header.HEADER_SIZE; // current offset into msg
try {
for (int i = 0; i < hdr.numQuestions; i++) {
rr = new ResourceRecord(msg, msgLen, pos, true, false);
if (!zoneXfer) {
question.addElement(rr);
}
pos += rr.size();
}
for (int i = 0; i < hdr.numAnswers; i++) {
rr = new ResourceRecord(
msg, msgLen, pos, false, !zoneXfer);
answer.addElement(rr);
pos += rr.size();
}
if (zoneXfer) {
return;
}
for (int i = 0; i < hdr.numAuthorities; i++) {
rr = new ResourceRecord(msg, msgLen, pos, false, true);
authority.addElement(rr);
pos += rr.size();
}
// The additional records section is currently ignored.
} catch (IndexOutOfBoundsException e) {
throw new CommunicationException(
"DNS error: corrupted message");
}
}
}

View File

@@ -0,0 +1,205 @@
/*
* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.dns;
import java.lang.ref.SoftReference;
import java.util.Date;
import java.util.Vector;
/**
* ZoneNode extends NameNode to represent a tree of the zones in the
* DNS namespace, along with any intermediate nodes between zones.
* A ZoneNode that represents a zone may be "populated" with a
* NameNode tree containing the zone's contents.
*
* <p> A populated zone's contents will be flagged as having expired after
* the time specified by the minimum TTL value in the zone's SOA record.
*
* <p> Since zone cuts aren't directly modeled by a tree of ZoneNodes,
* ZoneNode.isZoneCut() always returns false.
*
* <p> The synchronization strategy is documented in DnsContext.java.
*
* <p> The zone's contents are accessed via a soft reference, so its
* heap space may be reclaimed when necessary. The zone may be
* repopulated later.
*
* @author Scott Seligman
*/
class ZoneNode extends NameNode {
private SoftReference<NameNode> contentsRef = null; // the zone's namespace
private long serialNumber = -1; // the zone data's serial number
private Date expiration = null; // time when the zone's data expires
ZoneNode(String label) {
super(label);
}
protected NameNode newNameNode(String label) {
return new ZoneNode(label);
}
/*
* Clears the contents of this node. If the node was flagged as
* expired, it remains so.
*/
synchronized void depopulate() {
contentsRef = null;
serialNumber = -1;
}
/*
* Is this node currently populated?
*/
synchronized boolean isPopulated() {
return (getContents() != null);
}
/*
* Returns the zone's contents, or null if the zone is not populated.
*/
synchronized NameNode getContents() {
return (contentsRef != null)
? contentsRef.get()
: null;
}
/*
* Has this zone's data expired?
*/
synchronized boolean isExpired() {
return ((expiration != null) && expiration.before(new Date()));
}
/*
* Returns the deepest populated zone on the path specified by a
* fully-qualified domain name, or null if there is no populated
* zone on that path. Note that a node may be depopulated after
* being returned.
*/
ZoneNode getDeepestPopulated(DnsName fqdn) {
ZoneNode znode = this;
ZoneNode popNode = isPopulated() ? this : null;
for (int i = 1; i < fqdn.size(); i++) { // "i=1" to skip root label
znode = (ZoneNode) znode.get(fqdn.getKey(i));
if (znode == null) {
break;
} else if (znode.isPopulated()) {
popNode = znode;
}
}
return popNode;
}
/*
* Populates (or repopulates) a zone given its own fully-qualified
* name and its resource records. Returns the zone's new contents.
*/
NameNode populate(DnsName zone, ResourceRecords rrs) {
// assert zone.get(0).equals(""); // zone has root label
// assert (zone.size() == (depth() + 1)); // +1 due to root label
NameNode newContents = new NameNode(null);
for (int i = 0; i < rrs.answer.size(); i++) {
ResourceRecord rr = rrs.answer.elementAt(i);
DnsName n = rr.getName();
// Ignore resource records whose names aren't within the zone's
// domain. Also skip records of the zone's top node, since
// the zone's root NameNode is already in place.
if ((n.size() > zone.size()) && n.startsWith(zone)) {
NameNode nnode = newContents.add(n, zone.size());
if (rr.getType() == ResourceRecord.TYPE_NS) {
nnode.setZoneCut(true);
}
}
}
// The zone's SOA record is the first record in the answer section.
ResourceRecord soa = rrs.answer.firstElement();
synchronized (this) {
contentsRef = new SoftReference<NameNode>(newContents);
serialNumber = getSerialNumber(soa);
setExpiration(getMinimumTtl(soa));
return newContents;
}
}
/*
* Set this zone's data to expire in <tt>secsToExpiration</tt> seconds.
*/
private void setExpiration(long secsToExpiration) {
expiration = new Date(System.currentTimeMillis() +
1000 * secsToExpiration);
}
/*
* Returns an SOA record's minimum TTL field.
*/
private static long getMinimumTtl(ResourceRecord soa) {
String rdata = (String) soa.getRdata();
int pos = rdata.lastIndexOf(' ') + 1;
return Long.parseLong(rdata.substring(pos));
}
/*
* Compares this zone's serial number with that of an SOA record.
* Zone must be populated.
* Returns a negative, zero, or positive integer as this zone's
* serial number is less than, equal to, or greater than the SOA
* record's.
* See ResourceRecord.compareSerialNumbers() for a description of
* serial number arithmetic.
*/
int compareSerialNumberTo(ResourceRecord soa) {
// assert isPopulated();
return ResourceRecord.compareSerialNumbers(serialNumber,
getSerialNumber(soa));
}
/*
* Returns an SOA record's serial number.
*/
private static long getSerialNumber(ResourceRecord soa) {
String rdata = (String) soa.getRdata();
// An SOA record ends with: serial refresh retry expire minimum.
// Set "beg" to the space before serial, and "end" to the space after.
// We go "backward" to avoid dealing with escaped spaces in names.
int beg = rdata.length();
int end = -1;
for (int i = 0; i < 5; i++) {
end = beg;
beg = rdata.lastIndexOf(' ', end - 1);
}
return Long.parseLong(rdata.substring(beg + 1, end));
}
}

View File

@@ -0,0 +1,408 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import com.sun.jndi.toolkit.ctx.Continuation;
import java.util.NoSuchElementException;
import java.util.Vector;
import javax.naming.*;
import javax.naming.directory.Attributes;
import javax.naming.ldap.Control;
/**
* Basic enumeration for NameClassPair, Binding, and SearchResults.
*/
abstract class AbstractLdapNamingEnumeration<T extends NameClassPair>
implements NamingEnumeration<T>, ReferralEnumeration<T> {
protected Name listArg;
private boolean cleaned = false;
private LdapResult res;
private LdapClient enumClnt;
private Continuation cont; // used to fill in exceptions
private Vector<LdapEntry> entries = null;
private int limit = 0;
private int posn = 0;
protected LdapCtx homeCtx;
private LdapReferralException refEx = null;
private NamingException errEx = null;
/*
* Record the next set of entries and/or referrals.
*/
AbstractLdapNamingEnumeration(LdapCtx homeCtx, LdapResult answer, Name listArg,
Continuation cont) throws NamingException {
// These checks are to accommodate referrals and limit exceptions
// which will generate an enumeration and defer the exception
// to be thrown at the end of the enumeration.
// All other exceptions are thrown immediately.
// Exceptions shouldn't be thrown here anyhow because
// process_return_code() is called before the constructor
// is called, so these are just safety checks.
if ((answer.status != LdapClient.LDAP_SUCCESS) &&
(answer.status != LdapClient.LDAP_SIZE_LIMIT_EXCEEDED) &&
(answer.status != LdapClient.LDAP_TIME_LIMIT_EXCEEDED) &&
(answer.status != LdapClient.LDAP_ADMIN_LIMIT_EXCEEDED) &&
(answer.status != LdapClient.LDAP_REFERRAL) &&
(answer.status != LdapClient.LDAP_PARTIAL_RESULTS)) {
// %%% need to deal with referral
NamingException e = new NamingException(
LdapClient.getErrorMessage(
answer.status, answer.errorMessage));
throw cont.fillInException(e);
}
// otherwise continue
res = answer;
entries = answer.entries;
limit = (entries == null) ? 0 : entries.size(); // handle empty set
this.listArg = listArg;
this.cont = cont;
if (answer.refEx != null) {
refEx = answer.refEx;
}
// Ensures that context won't get closed from underneath us
this.homeCtx = homeCtx;
homeCtx.incEnumCount();
enumClnt = homeCtx.clnt; // remember
}
@Override
public final T nextElement() {
try {
return next();
} catch (NamingException e) {
// can't throw exception
cleanup();
return null;
}
}
@Override
public final boolean hasMoreElements() {
try {
return hasMore();
} catch (NamingException e) {
// can't throw exception
cleanup();
return false;
}
}
/*
* Retrieve the next set of entries and/or referrals.
*/
private void getNextBatch() throws NamingException {
res = homeCtx.getSearchReply(enumClnt, res);
if (res == null) {
limit = posn = 0;
return;
}
entries = res.entries;
limit = (entries == null) ? 0 : entries.size(); // handle empty set
posn = 0; // reset
// mimimize the number of calls to processReturnCode()
// (expensive when batchSize is small and there are many results)
if ((res.status != LdapClient.LDAP_SUCCESS) ||
((res.status == LdapClient.LDAP_SUCCESS) &&
(res.referrals != null))) {
try {
// convert referrals into a chain of LdapReferralException
homeCtx.processReturnCode(res, listArg);
} catch (LimitExceededException | PartialResultException e) {
setNamingException(e);
}
}
// merge any newly received referrals with any current referrals
if (res.refEx != null) {
if (refEx == null) {
refEx = res.refEx;
} else {
refEx = refEx.appendUnprocessedReferrals(res.refEx);
}
res.refEx = null; // reset
}
if (res.resControls != null) {
homeCtx.respCtls = res.resControls;
}
}
private boolean more = true; // assume we have something to start with
private boolean hasMoreCalled = false;
/*
* Test if unprocessed entries or referrals exist.
*/
@Override
public final boolean hasMore() throws NamingException {
if (hasMoreCalled) {
return more;
}
hasMoreCalled = true;
if (!more) {
return false;
} else {
return (more = hasMoreImpl());
}
}
/*
* Retrieve the next entry.
*/
@Override
public final T next() throws NamingException {
if (!hasMoreCalled) {
hasMore();
}
hasMoreCalled = false;
return nextImpl();
}
/*
* Test if unprocessed entries or referrals exist.
*/
private boolean hasMoreImpl() throws NamingException {
// when page size is supported, this
// might generate an exception while attempting
// to fetch the next batch to determine
// whether there are any more elements
// test if the current set of entries has been processed
if (posn == limit) {
getNextBatch();
}
// test if any unprocessed entries exist
if (posn < limit) {
return true;
} else {
try {
// try to process another referral
return hasMoreReferrals();
} catch (LdapReferralException |
LimitExceededException |
PartialResultException e) {
cleanup();
throw e;
} catch (NamingException e) {
cleanup();
PartialResultException pre = new PartialResultException();
pre.setRootCause(e);
throw pre;
}
}
}
/*
* Retrieve the next entry.
*/
private T nextImpl() throws NamingException {
try {
return nextAux();
} catch (NamingException e) {
cleanup();
throw cont.fillInException(e);
}
}
private T nextAux() throws NamingException {
if (posn == limit) {
getNextBatch(); // updates posn and limit
}
if (posn >= limit) {
cleanup();
throw new NoSuchElementException("invalid enumeration handle");
}
LdapEntry result = entries.elementAt(posn++);
// gets and outputs DN from the entry
return createItem(result.DN, result.attributes, result.respCtls);
}
protected final String getAtom(String dn) {
// need to strip off all but lowest component of dn
// so that is relative to current context (currentDN)
try {
Name parsed = new LdapName(dn);
return parsed.get(parsed.size() - 1);
} catch (NamingException e) {
return dn;
}
}
protected abstract T createItem(String dn, Attributes attrs,
Vector<Control> respCtls) throws NamingException;
/*
* Append the supplied (chain of) referrals onto the
* end of the current (chain of) referrals.
*/
@Override
public void appendUnprocessedReferrals(LdapReferralException ex) {
if (refEx != null) {
refEx = refEx.appendUnprocessedReferrals(ex);
} else {
refEx = ex.appendUnprocessedReferrals(refEx);
}
}
final void setNamingException(NamingException e) {
errEx = e;
}
protected abstract AbstractLdapNamingEnumeration<? extends NameClassPair> getReferredResults(
LdapReferralContext refCtx) throws NamingException;
/*
* Iterate through the URLs of a referral. If successful then perform
* a search operation and merge the received results with the current
* results.
*/
protected final boolean hasMoreReferrals() throws NamingException {
if ((refEx != null) &&
(refEx.hasMoreReferrals() ||
refEx.hasMoreReferralExceptions())) {
if (homeCtx.handleReferrals == LdapClient.LDAP_REF_THROW) {
throw (NamingException)(refEx.fillInStackTrace());
}
// process the referrals sequentially
while (true) {
LdapReferralContext refCtx =
(LdapReferralContext)refEx.getReferralContext(
homeCtx.envprops, homeCtx.reqCtls);
try {
update(getReferredResults(refCtx));
break;
} catch (LdapReferralException re) {
// record a previous exception
if (errEx == null) {
errEx = re.getNamingException();
}
refEx = re;
continue;
} finally {
// Make sure we close referral context
refCtx.close();
}
}
return hasMoreImpl();
} else {
cleanup();
if (errEx != null) {
throw errEx;
}
return (false);
}
}
/*
* Merge the entries and/or referrals from the supplied enumeration
* with those of the current enumeration.
*/
protected void update(AbstractLdapNamingEnumeration<? extends NameClassPair> ne) {
// Cleanup previous context first
homeCtx.decEnumCount();
// New enum will have already incremented enum count and recorded clnt
homeCtx = ne.homeCtx;
enumClnt = ne.enumClnt;
// Do this to prevent referral enumeration (ne) from decrementing
// enum count because we'll be doing that here from this
// enumeration.
ne.homeCtx = null;
// Record rest of information from new enum
posn = ne.posn;
limit = ne.limit;
res = ne.res;
entries = ne.entries;
refEx = ne.refEx;
listArg = ne.listArg;
}
protected final void finalize() {
cleanup();
}
protected final void cleanup() {
if (cleaned) return; // been there; done that
if(enumClnt != null) {
enumClnt.clearSearchReply(res, homeCtx.reqCtls);
}
enumClnt = null;
cleaned = true;
if (homeCtx != null) {
homeCtx.decEnumCount();
homeCtx = null;
}
}
@Override
public final void close() {
cleanup();
}
}

View File

@@ -0,0 +1,118 @@
/*
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import javax.naming.ldap.*;
/**
* This class provides a basic implementation of the <tt>Control</tt>
* interface. It represents an LDAPv3 Control as defined in RFC-2251.
*
* @author Vincent Ryan
*/
public class BasicControl implements Control {
/**
* The control's object identifier string.
*
* @serial
*/
protected String id;
/**
* The control's criticality.
*
* @serial
*/
protected boolean criticality = false; // default
/**
* The control's ASN.1 BER encoded value.
*
* @serial
*/
protected byte[] value = null;
private static final long serialVersionUID = -5914033725246428413L;
/**
* Constructs a new instance of BasicControl.
* It is a non-critical control.
*
* @param id The control's object identifier string.
*
*/
public BasicControl(String id) {
this.id = id;
}
/**
* Constructs a new instance of BasicControl.
*
* @param id The control's object identifier string.
* @param criticality The control's criticality.
* @param value The control's ASN.1 BER encoded value.
* May be null.
*/
public BasicControl(String id, boolean criticality, byte[] value) {
this.id = id;
this.criticality = criticality;
if (value != null) {
this.value = value.clone();
}
}
/**
* Retrieves the control's object identifier string.
*
* @return The non-null object identifier string.
*/
public String getID() {
return id;
}
/**
* Determines the control's criticality.
*
* @return true if the control is critical; false otherwise.
*/
public boolean isCritical() {
return criticality;
}
/**
* Retrieves the control's ASN.1 BER encoded value.
* The result is the raw BER bytes including the tag and length of
* the control's value. It does not include the control's object
* identifier string or criticality.
*
* @return A possibly null byte array representing the control's
* ASN.1 BER encoded value.
*/
public byte[] getEncodedValue() {
return value == null ? null : value.clone();
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import java.io.OutputStream;
import java.io.IOException;
import java.io.ByteArrayInputStream;
import sun.misc.HexDumpEncoder;
/**
* Base class that defines common fields, constants, and debug method.
*
* @author Jagane Sundar
*/
public abstract class Ber {
protected byte buf[];
protected int offset;
protected int bufsize;
protected Ber() {
}
public static void dumpBER(OutputStream outStream, String tag, byte[] bytes,
int from, int to) {
try {
outStream.write('\n');
outStream.write(tag.getBytes("UTF8"));
new HexDumpEncoder().encodeBuffer(
new ByteArrayInputStream(bytes, from, to),
outStream);
outStream.write('\n');
} catch (IOException e) {
try {
outStream.write(
"Ber.dumpBER(): error encountered\n".getBytes("UTF8"));
} catch (IOException e2) {
// ignore
}
}
}
////////////////////////////////////////////////////////////////////////////
//
// some ASN defines
//
////////////////////////////////////////////////////////////////////////////
public static final int ASN_BOOLEAN = 0x01;
public static final int ASN_INTEGER = 0x02;
public static final int ASN_BIT_STRING = 0x03;
public static final int ASN_SIMPLE_STRING = 0x04;
public static final int ASN_OCTET_STR = 0x04;
public static final int ASN_NULL = 0x05;
public static final int ASN_OBJECT_ID = 0x06;
public static final int ASN_SEQUENCE = 0x10;
public static final int ASN_SET = 0x11;
public static final int ASN_PRIMITIVE = 0x00;
public static final int ASN_UNIVERSAL = 0x00;
public static final int ASN_CONSTRUCTOR = 0x20;
public static final int ASN_APPLICATION = 0x40;
public static final int ASN_CONTEXT = 0x80;
public static final int ASN_PRIVATE = 0xC0;
public static final int ASN_ENUMERATED = 0x0a;
final static class EncodeException extends IOException {
private static final long serialVersionUID = -5247359637775781768L;
EncodeException(String msg) {
super(msg);
}
}
final static class DecodeException extends IOException {
private static final long serialVersionUID = 8735036969244425583L;
DecodeException(String msg) {
super(msg);
}
}
}

View File

@@ -0,0 +1,341 @@
/*
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import java.io.UnsupportedEncodingException;
/**
* A BER decoder. Contains methods to parse a BER buffer.
*
* @author Jagane Sundar
* @author Vincent Ryan
*/
public final class BerDecoder extends Ber {
private int origOffset; // The start point in buf to decode
/**
* Creates a BER decoder that reads bytes from the specified buffer.
*/
public BerDecoder(byte buf[], int offset, int bufsize) {
this.buf = buf; // shared buffer, be careful to use this class
this.bufsize = bufsize;
this.origOffset = offset;
reset();
}
/**
* Resets this decode to start parsing from the initial offset
* (ie., same state as after calling the constructor).
*/
public void reset() {
offset = origOffset;
}
/**
* Returns the current parse position.
* It points to the byte that will be parsed next.
* Useful for parsing sequences.
*/
public int getParsePosition() {
return offset;
}
/**
* Parses a possibly variable length field.
*/
public int parseLength() throws DecodeException {
int lengthbyte = parseByte();
if ((lengthbyte & 0x80) == 0x80) {
lengthbyte &= 0x7f;
if (lengthbyte == 0) {
throw new DecodeException(
"Indefinite length not supported");
}
if (lengthbyte > 4) {
throw new DecodeException("encoding too long");
}
if (bufsize - offset < lengthbyte) {
throw new DecodeException("Insufficient data");
}
int retval = 0;
for( int i = 0; i < lengthbyte; i++) {
retval = (retval << 8) + (buf[offset++] & 0xff);
}
if (retval < 0) {
throw new DecodeException("Invalid length bytes");
}
return retval;
} else {
return lengthbyte;
}
}
/**
* Parses the next sequence in this BER buffer.
* @param rlen An array for returning size of the sequence in bytes. If null,
* the size is not returned.
* @return The sequence's tag.
*/
public int parseSeq(int rlen[]) throws DecodeException {
int seq = parseByte();
int len = parseLength();
if (rlen != null) {
rlen[0] = len;
}
return seq;
}
/**
* Used to skip bytes. Usually used when trying to recover from parse error.
* Don't need to be public right now?
* @param i The number of bytes to skip
*/
void seek(int i) throws DecodeException {
if (offset + i > bufsize || offset + i < 0) {
throw new DecodeException("array index out of bounds");
}
offset += i;
}
/**
* Parses the next byte in this BER buffer.
* @return The byte parsed.
*/
public int parseByte() throws DecodeException {
if (bufsize - offset < 1) {
throw new DecodeException("Insufficient data");
}
return buf[offset++] & 0xff;
}
/**
* Returns the next byte in this BER buffer without consuming it.
* @return The next byte.
*/
public int peekByte() throws DecodeException {
if (bufsize - offset < 1) {
throw new DecodeException("Insufficient data");
}
return buf[offset] & 0xff;
}
/**
* Parses an ASN_BOOLEAN tagged integer from this BER buffer.
* @return true if the tagged integer is 0; false otherwise.
*/
public boolean parseBoolean() throws DecodeException {
return ((parseIntWithTag(ASN_BOOLEAN) == 0x00) ? false : true);
}
/**
* Parses an ASN_ENUMERATED tagged integer from this BER buffer.
* @return The tag of enumeration.
*/
public int parseEnumeration() throws DecodeException {
return parseIntWithTag(ASN_ENUMERATED);
}
/**
* Parses an ASN_INTEGER tagged integer from this BER buffer.
* @return The value of the integer.
*/
public int parseInt() throws DecodeException {
return parseIntWithTag(ASN_INTEGER);
}
/**
* Parses an integer that's preceded by a tag.
*<blockquote><pre>
* BER integer ::= tag length byte {byte}*
*</pre></blockquote>
*/
private int parseIntWithTag(int tag) throws DecodeException {
if (parseByte() != tag) {
// Ber could have been reset;
String s;
if (offset > 0) {
s = Integer.toString(buf[offset - 1] & 0xff);
} else {
s = "Empty tag";
}
throw new DecodeException("Encountered ASN.1 tag " +
s + " (expected tag " + Integer.toString(tag) + ")");
}
int len = parseLength();
if (len > 4) {
throw new DecodeException("INTEGER too long");
} else if (len > bufsize - offset) {
throw new DecodeException("Insufficient data");
}
byte fb = buf[offset++];
int value = 0;
value = fb & 0x7F;
for( int i = 1 /* first byte already read */ ; i < len; i++) {
value <<= 8;
value |= (buf[offset++] & 0xff);
}
if ((fb & 0x80) == 0x80) {
value = -value;
}
return value;
}
/**
* Parses a string.
*/
public String parseString(boolean decodeUTF8) throws DecodeException {
return parseStringWithTag(ASN_SIMPLE_STRING, decodeUTF8, null);
}
/**
* Parses a string of a given tag from this BER buffer.
*<blockquote><pre>
*BER simple string ::= tag length {byte}*
*</pre></blockquote>
* @param rlen An array for holding the relative parsed offset; if null
* offset not set.
* @param decodeUTF8 If true, use UTF-8 when decoding the string; otherwise
* use ISO-Latin-1 (8859_1). Use true for LDAPv3; false for LDAPv2.
* @param tag The tag that precedes the string.
* @return The non-null parsed string.
*/
public String parseStringWithTag(int tag, boolean decodeUTF8, int rlen[])
throws DecodeException {
int st;
int origOffset = offset;
if ((st = parseByte()) != tag) {
throw new DecodeException("Encountered ASN.1 tag " +
Integer.toString((byte)st) + " (expected tag " + tag + ")");
}
int len = parseLength();
if (len > bufsize - offset) {
throw new DecodeException("Insufficient data");
}
String retstr;
if (len == 0) {
retstr = "";
} else {
byte[] buf2 = new byte[len];
System.arraycopy(buf, offset, buf2, 0, len);
if (decodeUTF8) {
try {
retstr = new String(buf2, "UTF8");
} catch (UnsupportedEncodingException e) {
throw new DecodeException("UTF8 not available on platform");
}
} else {
try {
retstr = new String(buf2, "8859_1");
} catch (UnsupportedEncodingException e) {
throw new DecodeException("8859_1 not available on platform");
}
}
offset += len;
}
if (rlen != null) {
rlen[0] = offset - origOffset;
}
return retstr;
}
/**
* Parses an octet string of a given type(tag) from this BER buffer.
* <blockquote><pre>
* BER Binary Data of type "tag" ::= tag length {byte}*
*</pre></blockquote>
*
* @param tag The tag to look for.
* @param rlen An array for returning the relative parsed position. If null,
* the relative parsed position is not returned.
* @return A non-null array containing the octet string.
* @throws DecodeException If the next byte in the BER buffer is not
* <tt>tag</tt>, or if length specified in the BER buffer exceeds the
* number of bytes left in the buffer.
*/
public byte[] parseOctetString(int tag, int rlen[]) throws DecodeException {
int origOffset = offset;
int st;
if ((st = parseByte()) != tag) {
throw new DecodeException("Encountered ASN.1 tag " +
Integer.toString(st) +
" (expected tag " + Integer.toString(tag) + ")");
}
int len = parseLength();
if (len > bufsize - offset) {
throw new DecodeException("Insufficient data");
}
byte retarr[] = new byte[len];
if (len > 0) {
System.arraycopy(buf, offset, retarr, 0, len);
offset += len;
}
if (rlen != null) {
rlen[0] = offset - origOffset;
}
return retarr;
}
/**
* Returns the number of unparsed bytes in this BER buffer.
*/
public int bytesLeft() {
return bufsize - offset;
}
}

View File

@@ -0,0 +1,429 @@
/*
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import java.io.UnsupportedEncodingException;
/**
* A BER encoder.
*
* @author Jagane Sundar
* @author Scott Seligman
* @author Vincent Ryan
*/
public final class BerEncoder extends Ber {
private int curSeqIndex;
private int seqOffset[];
private static final int INITIAL_SEQUENCES = 16;
private static final int DEFAULT_BUFSIZE = 1024;
// When buf is full, expand its size by the following factor.
private static final int BUF_GROWTH_FACTOR = 8;
/**
* Creates a BER buffer for encoding.
*/
public BerEncoder() {
this(DEFAULT_BUFSIZE);
}
/**
* Creates a BER buffer of a specified size for encoding.
* Specify the initial bufsize. Buffer will be expanded as needed.
* @param bufsize The number of bytes for the buffer.
*/
public BerEncoder(int bufsize) {
buf = new byte[bufsize];
this.bufsize = bufsize;
offset = 0;
seqOffset = new int[INITIAL_SEQUENCES];
curSeqIndex = 0;
}
/**
* Resets encoder to state when newly constructed. Zeros out
* internal data structures.
*/
public void reset() {
while (offset > 0) {
buf[--offset] = 0;
}
while (curSeqIndex > 0) {
seqOffset[--curSeqIndex] = 0;
}
}
// ------------------ Accessor methods ------------
/**
* Gets the number of encoded bytes in this BER buffer.
*/
public int getDataLen() {
return offset;
}
/**
* Gets the buffer that contains the BER encoding. Throws an
* exception if unmatched beginSeq() and endSeq() pairs were
* encountered. Not entire buffer contains encoded bytes.
* Use getDataLen() to determine number of encoded bytes.
* Use getBuffer(true) to get rid of excess bytes in array.
* @throws IllegalStateException If buffer contains unbalanced sequence.
*/
public byte[] getBuf() {
if (curSeqIndex != 0) {
throw new IllegalStateException("BER encode error: Unbalanced SEQUENCEs.");
}
return buf; // shared buffer, be careful to use this method.
}
/**
* Gets the buffer that contains the BER encoding, trimming unused bytes.
*
* @throws IllegalStateException If buffer contains unbalanced sequence.
*/
public byte[] getTrimmedBuf() {
int len = getDataLen();
byte[] trimBuf = new byte[len];
System.arraycopy(getBuf(), 0, trimBuf, 0, len);
return trimBuf;
}
// -------------- encoding methods -------------
/**
* Begin encoding a sequence with a tag.
*/
public void beginSeq(int tag) {
// Double the size of the SEQUENCE array if it overflows
if (curSeqIndex >= seqOffset.length) {
int[] seqOffsetTmp = new int[seqOffset.length * 2];
for (int i = 0; i < seqOffset.length; i++) {
seqOffsetTmp[i] = seqOffset[i];
}
seqOffset = seqOffsetTmp;
}
encodeByte(tag);
seqOffset[curSeqIndex] = offset;
// Save space for sequence length.
// %%% Currently we save enough space for sequences up to 64k.
// For larger sequences we'll need to shift the data to the right
// in endSeq(). If we could instead pad the length field with
// zeros, it would be a big win.
ensureFreeBytes(3);
offset += 3;
curSeqIndex++;
}
/**
* Terminate a BER sequence.
*/
public void endSeq() throws EncodeException {
curSeqIndex--;
if (curSeqIndex < 0) {
throw new IllegalStateException("BER encode error: Unbalanced SEQUENCEs.");
}
int start = seqOffset[curSeqIndex] + 3; // index beyond length field
int len = offset - start;
if (len <= 0x7f) {
shiftSeqData(start, len, -2);
buf[seqOffset[curSeqIndex]] = (byte) len;
} else if (len <= 0xff) {
shiftSeqData(start, len, -1);
buf[seqOffset[curSeqIndex]] = (byte) 0x81;
buf[seqOffset[curSeqIndex] + 1] = (byte) len;
} else if (len <= 0xffff) {
buf[seqOffset[curSeqIndex]] = (byte) 0x82;
buf[seqOffset[curSeqIndex] + 1] = (byte) (len >> 8);
buf[seqOffset[curSeqIndex] + 2] = (byte) len;
} else if (len <= 0xffffff) {
shiftSeqData(start, len, 1);
buf[seqOffset[curSeqIndex]] = (byte) 0x83;
buf[seqOffset[curSeqIndex] + 1] = (byte) (len >> 16);
buf[seqOffset[curSeqIndex] + 2] = (byte) (len >> 8);
buf[seqOffset[curSeqIndex] + 3] = (byte) len;
} else {
throw new EncodeException("SEQUENCE too long");
}
}
/**
* Shifts contents of buf in the range [start,start+len) a specified amount.
* Positive shift value means shift to the right.
*/
private void shiftSeqData(int start, int len, int shift) {
if (shift > 0) {
ensureFreeBytes(shift);
}
System.arraycopy(buf, start, buf, start + shift, len);
offset += shift;
}
/**
* Encode a single byte.
*/
public void encodeByte(int b) {
ensureFreeBytes(1);
buf[offset++] = (byte) b;
}
/*
private void deleteByte() {
offset--;
}
*/
/*
* Encodes an int.
*<blockquote><pre>
* BER integer ::= 0x02 berlength byte {byte}*
*</pre></blockquote>
*/
public void encodeInt(int i) {
encodeInt(i, 0x02);
}
/**
* Encodes an int and a tag.
*<blockquote><pre>
* BER integer w tag ::= tag berlength byte {byte}*
*</pre></blockquote>
*/
public void encodeInt(int i, int tag) {
int mask = 0xff800000;
int intsize = 4;
while( (((i & mask) == 0) || ((i & mask) == mask)) && (intsize > 1) ) {
intsize--;
i <<= 8;
}
encodeInt(i, tag, intsize);
}
//
// encodes an int using numbytes for the actual encoding.
//
private void encodeInt(int i, int tag, int intsize) {
//
// integer ::= 0x02 asnlength byte {byte}*
//
if (intsize > 4) {
throw new IllegalArgumentException("BER encode error: INTEGER too long.");
}
ensureFreeBytes(2 + intsize);
buf[offset++] = (byte) tag;
buf[offset++] = (byte) intsize;
int mask = 0xff000000;
while (intsize-- > 0) {
buf[offset++] = (byte) ((i & mask) >> 24);
i <<= 8;
}
}
/**
* Encodes a boolean.
*<blockquote><pre>
* BER boolean ::= 0x01 0x01 {0xff|0x00}
*</pre></blockquote>
*/
public void encodeBoolean(boolean b) {
encodeBoolean(b, ASN_BOOLEAN);
}
/**
* Encodes a boolean and a tag
*<blockquote><pre>
* BER boolean w TAG ::= tag 0x01 {0xff|0x00}
*</pre></blockquote>
*/
public void encodeBoolean(boolean b, int tag) {
ensureFreeBytes(3);
buf[offset++] = (byte) tag;
buf[offset++] = 0x01;
buf[offset++] = b ? (byte) 0xff : (byte) 0x00;
}
/**
* Encodes a string.
*<blockquote><pre>
* BER string ::= 0x04 strlen byte1 byte2...
*</pre></blockquote>
* The string is converted into bytes using UTF-8 or ISO-Latin-1.
*/
public void encodeString(String str, boolean encodeUTF8)
throws EncodeException {
encodeString(str, ASN_OCTET_STR, encodeUTF8);
}
/**
* Encodes a string and a tag.
*<blockquote><pre>
* BER string w TAG ::= tag strlen byte1 byte2...
*</pre></blockquote>
*/
public void encodeString(String str, int tag, boolean encodeUTF8)
throws EncodeException {
encodeByte(tag);
int i = 0;
int count;
byte[] bytes = null;
if (str == null) {
count = 0;
} else if (encodeUTF8) {
try {
bytes = str.getBytes("UTF8");
count = bytes.length;
} catch (UnsupportedEncodingException e) {
throw new EncodeException("UTF8 not available on platform");
}
} else {
try {
bytes = str.getBytes("8859_1");
count = bytes.length;
} catch (UnsupportedEncodingException e) {
throw new EncodeException("8859_1 not available on platform");
}
}
encodeLength(count);
ensureFreeBytes(count);
while (i < count) {
buf[offset++] = bytes[i++];
}
}
/**
* Encodes a portion of an octet string and a tag.
*/
public void encodeOctetString(byte tb[], int tag, int tboffset, int length)
throws EncodeException {
encodeByte(tag);
encodeLength(length);
if (length > 0) {
ensureFreeBytes(length);
System.arraycopy(tb, tboffset, buf, offset, length);
offset += length;
}
}
/**
* Encodes an octet string and a tag.
*/
public void encodeOctetString(byte tb[], int tag) throws EncodeException {
encodeOctetString(tb, tag, 0, tb.length);
}
private void encodeLength(int len) throws EncodeException {
ensureFreeBytes(4); // worst case
if (len < 128) {
buf[offset++] = (byte) len;
} else if (len <= 0xff) {
buf[offset++] = (byte) 0x81;
buf[offset++] = (byte) len;
} else if (len <= 0xffff) {
buf[offset++] = (byte) 0x82;
buf[offset++] = (byte) (len >> 8);
buf[offset++] = (byte) (len & 0xff);
} else if (len <= 0xffffff) {
buf[offset++] = (byte) 0x83;
buf[offset++] = (byte) (len >> 16);
buf[offset++] = (byte) (len >> 8);
buf[offset++] = (byte) (len & 0xff);
} else {
throw new EncodeException("string too long");
}
}
/**
* Encodes an array of strings.
*/
public void encodeStringArray(String strs[], boolean encodeUTF8)
throws EncodeException {
if (strs == null)
return;
for (int i = 0; i < strs.length; i++) {
encodeString(strs[i], encodeUTF8);
}
}
/*
private void encodeNull() {
//
// NULL ::= 0x05 0x00
//
encodeByte(0x05);
encodeByte(0x00);
}
*/
/**
* Ensures that there are at least "len" unused bytes in "buf".
* When more space is needed "buf" is expanded by a factor of
* BUF_GROWTH_FACTOR, then "len" bytes are added if "buf" still
* isn't large enough.
*/
private void ensureFreeBytes(int len) {
if (bufsize - offset < len) {
int newsize = bufsize * BUF_GROWTH_FACTOR;
if (newsize - offset < len) {
newsize += len;
}
byte newbuf[] = new byte[newsize];
// Only copy bytes in the range [0, offset)
System.arraycopy(buf, 0, newbuf, 0, offset);
buf = newbuf;
bufsize = newsize;
}
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 1999, 2002, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import javax.naming.*;
import javax.naming.ldap.*;
class BindingWithControls extends Binding implements HasControls {
private Control[] controls;
public BindingWithControls(String name, Object obj, Control[] controls) {
super(name, obj);
this.controls = controls;
}
public Control[] getControls() throws NamingException {
return controls;
}
private static final long serialVersionUID = 9117274533692320040L;
}

View File

@@ -0,0 +1,227 @@
/*
* Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import java.util.Locale;
import java.util.Arrays; // JDK 1.2
import java.io.OutputStream;
import javax.naming.ldap.Control;
import java.lang.reflect.Method;
import javax.net.SocketFactory;
/**
* Represents identity information about an anonymous LDAP connection.
* This base class contains the following information:
* - protocol version number
* - server's hostname (case-insensitive)
* - server's port number
* - prototype type (plain or ssl)
* - controls to be sent with the LDAP bind request
*
* All other identity classes must be a subclass of ClientId.
* Identity subclasses would add more distinguishing information, depending
* on the type of authentication that the connection is to have.
*
* The equals() and hashCode() methods of this class and its subclasses are
* important because they are used to determine whether two requests for
* the same connection are identical, and thus whether the same connection
* may be shared. This is especially important for authenticated connections
* because a mistake would result in a serious security violation.
*
* @author Rosanna Lee
*/
class ClientId {
final private int version;
final private String hostname;
final private int port;
final private String protocol;
final private Control[] bindCtls;
final private OutputStream trace;
final private String socketFactory;
final private int myHash;
final private int ctlHash;
private SocketFactory factory = null;
private Method sockComparator = null;
private boolean isDefaultSockFactory = false;
final public static boolean debug = false;
ClientId(int version, String hostname, int port, String protocol,
Control[] bindCtls, OutputStream trace, String socketFactory) {
this.version = version;
this.hostname = hostname.toLowerCase(Locale.ENGLISH); // ignore case
this.port = port;
this.protocol = protocol;
this.bindCtls = (bindCtls != null ? bindCtls.clone() : null);
this.trace = trace;
//
// Needed for custom socket factory pooling
//
this.socketFactory = socketFactory;
if ((socketFactory != null) &&
!socketFactory.equals(LdapCtx.DEFAULT_SSL_FACTORY)) {
try {
Class<?> socketFactoryClass =
Obj.helper.loadClass(socketFactory);
Class<?> objClass = Class.forName("java.lang.Object");
this.sockComparator = socketFactoryClass.getMethod(
"compare", new Class<?>[]{objClass, objClass});
Method getDefault = socketFactoryClass.getMethod(
"getDefault", new Class<?>[]{});
this.factory =
(SocketFactory)getDefault.invoke(null, new Object[]{});
} catch (Exception e) {
// Ignore it here, the same exceptions are/will be handled by
// LdapPoolManager and Connection classes.
if (debug) {
System.out.println("ClientId received an exception");
e.printStackTrace();
}
}
} else {
isDefaultSockFactory = true;
}
// The SocketFactory field is not used in the myHash
// computation as there is no right way to compute the hash code
// for this field. There is no harm in skipping it from the hash
// computation
myHash = version + port
+ (trace != null ? trace.hashCode() : 0)
+ (this.hostname != null ? this.hostname.hashCode() : 0)
+ (protocol != null ? protocol.hashCode() : 0)
+ (ctlHash=hashCodeControls(bindCtls));
}
public boolean equals(Object obj) {
if (!(obj instanceof ClientId)) {
return false;
}
ClientId other = (ClientId)obj;
return myHash == other.myHash
&& version == other.version
&& port == other.port
&& trace == other.trace
&& (hostname == other.hostname // null OK
|| (hostname != null && hostname.equals(other.hostname)))
&& (protocol == other.protocol // null OK
|| (protocol != null && protocol.equals(other.protocol)))
&& ctlHash == other.ctlHash
&& (equalsControls(bindCtls, other.bindCtls))
&& (equalsSockFactory(other));
}
public int hashCode() {
return myHash;
}
private static int hashCodeControls(Control[] c) {
if (c == null) {
return 0;
}
int code = 0;
for (int i = 0; i < c.length; i++) {
code = code * 31 + c[i].getID().hashCode();
}
return code;
}
private static boolean equalsControls(Control[] a, Control[] b) {
if (a == b) {
return true; // both null or same
}
if (a == null || b == null) {
return false; // one is non-null
}
if (a.length != b.length) {
return false;
}
for (int i = 0; i < a.length; i++) {
if (!a[i].getID().equals(b[i].getID())
|| a[i].isCritical() != b[i].isCritical()
|| !Arrays.equals(a[i].getEncodedValue(),
b[i].getEncodedValue())) {
return false;
}
}
return true;
}
private boolean equalsSockFactory(ClientId other) {
if (this.isDefaultSockFactory && other.isDefaultSockFactory) {
return true;
}
else if (!other.isDefaultSockFactory) {
return invokeComparator(other, this);
} else {
return invokeComparator(this, other);
}
}
// delegate the comparison work to the SocketFactory class
// as there is no enough information here, to do the comparison
private boolean invokeComparator(ClientId c1, ClientId c2) {
Object ret;
try {
ret = (c1.sockComparator).invoke(
c1.factory, c1.socketFactory, c2.socketFactory);
} catch(Exception e) {
if (debug) {
System.out.println("ClientId received an exception");
e.printStackTrace();
}
// Failed to invoke the comparator; flag unequality
return false;
}
if (((Integer) ret) == 0) {
return true;
}
return false;
}
private static String toStringControls(Control[] ctls) {
if (ctls == null) {
return "";
}
StringBuffer str = new StringBuffer();
for (int i = 0; i < ctls.length; i++) {
str.append(ctls[i].getID());
str.append(' ');
}
return str.toString();
}
public String toString() {
return (hostname + ":" + port + ":" +
(protocol != null ? protocol : "") + ":" +
toStringControls(bindCtls) + ":" +
socketFactory);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,86 @@
/*
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import com.sun.jndi.ldap.spi.LdapDnsProvider;
import com.sun.jndi.ldap.spi.LdapDnsProviderResult;
import javax.naming.NamingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class DefaultLdapDnsProvider {
public Optional<LdapDnsProviderResult> lookupEndpoints(String url,
Map<?,?> env)
throws NamingException
{
if (url == null || env == null) {
throw new NullPointerException();
}
String domainName;
List<String> endpoints = new ArrayList<>();
LdapURL ldapUrl = new LdapURL(url);
String dn = ldapUrl.getDN();
String host = ldapUrl.getHost();
int port = ldapUrl.getPort();
String[] hostports;
// handle a URL with no hostport (ldap:/// or ldaps:///)
// locate the LDAP service using the URL's distinguished name
if (host == null
&& port == -1
&& dn != null
&& (domainName = ServiceLocator.mapDnToDomainName(dn)) != null
&& (hostports = ServiceLocator.getLdapService(domainName, env)) != null) {
// Generate new URLs that include the discovered hostports.
// Reuse the original URL scheme.
String scheme = ldapUrl.getScheme() + "://";
String query = ldapUrl.getQuery();
String urlSuffix = ldapUrl.getPath() + (query != null ? query : "");
for (String hostPort : hostports) {
// the hostports come from the DNS SRV records
// we assume the SRV record is scheme aware
endpoints.add(scheme + hostPort + urlSuffix);
}
} else {
// we don't have enough information to set the domain name
// correctly
domainName = "";
endpoints.add(url);
}
LdapDnsProviderResult res = new LdapDnsProviderResult(domainName, endpoints);
if (res.getEndpoints().isEmpty() && res.getDomainName().isEmpty()) {
return Optional.empty();
} else {
return Optional.of(res);
}
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright (c) 1999, 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import java.io.IOException;
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.ldap.*;
/**
* This class represents a factory for creating LDAPv3 response controls.
* The following response controls are supported:
* <ul>
* <li>
* Paged results, as defined in
* <a href="http://www.ietf.org/rfc/rfc2696.txt">RFC 2696</a>.
* <li>
* Server-side sorting, as defined in
* <a href="http://www.ietf.org/rfc/rfc2891.txt">RFC 2891</a>.
* <li>
* Entry change response control, as defined in
* <a href="http://www.ietf.org/internet-drafts/draft-ietf-ldapext-psearch-02.txt">draft-ietf-ldapext-psearch-02.txt</a>.
* </ul>
*
* @see javax.naming.ldap.SortResponseControl
* @see javax.naming.ldap.PagedResultsResponseControl
* @see PersistentSearchControl
* @see EntryChangeResponseControl
* @author Vincent Ryan
*/
public class DefaultResponseControlFactory extends ControlFactory {
/**
* Constructs a new instance of the response control factory.
*/
public DefaultResponseControlFactory() {
}
/**
* Creates an instance of a response control class from a more
* generic control class (BasicControl).
*
* @param ctl A non-null control.
* @return The LDAP control created or null if it cannot be created.
* Null indicates that another factory should be attempted.
* @exception NamingException if this control factory encountered an
* error condition while attempting to create the LDAP control,
* and no other control factories are to be tried.
*/
public Control getControlInstance(Control ctl)
throws NamingException {
String id = ctl.getID();
//System.out.println(id);
try {
if (id.equals(SortResponseControl.OID)) {
return new SortResponseControl(id, ctl.isCritical(),
ctl.getEncodedValue());
} else if (id.equals(PagedResultsResponseControl.OID)) {
return new PagedResultsResponseControl(id, ctl.isCritical(),
ctl.getEncodedValue());
} else if (id.equals(EntryChangeResponseControl.OID)) {
return new EntryChangeResponseControl(id, ctl.isCritical(),
ctl.getEncodedValue());
}
} catch (IOException e) {
NamingException ne = new NamingException();
ne.setRootCause(e);
throw ne;
}
return null;
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import java.util.Arrays; // JDK 1.2
import java.util.Hashtable;
import java.io.OutputStream;
import javax.naming.ldap.Control;
/**
* Extends SimpleClientId to add property values specific for Digest-MD5.
* This includes:
* realm, authzid, qop, strength, maxbuffer, mutual-auth, reuse,
* all policy-related selection properties.
* Two DigestClientIds are identical iff they pass the SimpleClientId
* equals() test and that all of these property values are the same.
*
* @author Rosanna Lee
*/
class DigestClientId extends SimpleClientId {
private static final String[] SASL_PROPS = {
"java.naming.security.sasl.authorizationId",
"java.naming.security.sasl.realm",
"javax.security.sasl.qop",
"javax.security.sasl.strength",
"javax.security.sasl.reuse",
"javax.security.sasl.server.authentication",
"javax.security.sasl.maxbuffer",
"javax.security.sasl.policy.noplaintext",
"javax.security.sasl.policy.noactive",
"javax.security.sasl.policy.nodictionary",
"javax.security.sasl.policy.noanonymous",
"javax.security.sasl.policy.forward",
"javax.security.sasl.policy.credentials",
};
final private String[] propvals;
final private int myHash;
DigestClientId(int version, String hostname, int port,
String protocol, Control[] bindCtls, OutputStream trace,
String socketFactory, String username,
Object passwd, Hashtable<?,?> env) {
super(version, hostname, port, protocol, bindCtls, trace,
socketFactory, username, passwd);
if (env == null) {
propvals = null;
} else {
// Could be smarter and apply default values for props
// but for now, we just record and check exact matches
propvals = new String[SASL_PROPS.length];
for (int i = 0; i < SASL_PROPS.length; i++) {
propvals[i] = (String) env.get(SASL_PROPS[i]);
}
}
myHash = super.hashCode() ^ Arrays.hashCode(propvals);
}
public boolean equals(Object obj) {
if (!(obj instanceof DigestClientId)) {
return false;
}
DigestClientId other = (DigestClientId)obj;
return myHash == other.myHash
&& super.equals(obj)
&& Arrays.equals(propvals, other.propvals);
}
public int hashCode() {
return myHash;
}
public String toString() {
if (propvals != null) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < propvals.length; i++) {
buf.append(':');
if (propvals[i] != null) {
buf.append(propvals[i]);
}
}
return super.toString() + buf.toString();
} else {
return super.toString();
}
}
}

View File

@@ -0,0 +1,167 @@
/*
* Copyright (c) 1999, 2002, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import java.io.IOException;
import javax.naming.*;
import javax.naming.directory.*;
/**
* This class implements the LDAPv3 Response Control for entry-change
* notification as defined in
* <a href="http://www.ietf.org/internet-drafts/draft-ietf-ldapext-psearch-02.txt">draft-ietf-ldapext-psearch-02.txt</a>.
*
* The control's value has the following ASN.1 definition:
* <pre>
*
* EntryChangeNotification ::= SEQUENCE {
* changeType ENUMERATED {
* add (1),
* delete (2),
* modify (4),
* modDN (8)
* },
* previousDN LDAPDN OPTIONAL, -- modifyDN ops. only
* changeNumber INTEGER OPTIONAL, -- if supported
* }
*
* </pre>
*
* @see PersistentSearchControl
* @see com.sun.jndi.ldap.ctl.ResponseControlFactory ResponseControlFactory
* @author Vincent Ryan
*/
final public class EntryChangeResponseControl extends BasicControl {
/**
* The entry-change response control's assigned object identifier
* is 2.16.840.1.113730.3.4.7.
*/
public static final String OID = "2.16.840.1.113730.3.4.7";
/**
* Indicates an entry which has been added.
*/
public static final int ADD = 1;
/**
* Indicates an entry which has been deleted.
*/
public static final int DELETE = 2;
/**
* Indicates an entry which has been modified.
*/
public static final int MODIFY = 4;
/**
* Indicates an entry which has been renamed.
*/
public static final int RENAME = 8;
/**
* The type of change that occurred.
*
* @serial
*/
private int changeType;
/**
* The previous distinguished name (only applies to RENAME changes).
*
* @serial
*/
private String previousDN = null;
/**
* The change number (if supported by the server).
*
* @serial
*/
private long changeNumber = -1L;
private static final long serialVersionUID = -2087354136750180511L;
/**
* Constructs a new instance of EntryChangeResponseControl.
*
* @param id The control's object identifier string.
* @param criticality The control's criticality.
* @param value The control's ASN.1 BER encoded value.
* May be null.
* @exception IOException if an error is encountered
* while decoding the control's value.
*/
public EntryChangeResponseControl(String id, boolean criticality,
byte[] value) throws IOException {
super(id, criticality, value);
// decode value
if ((value != null) && (value.length > 0)) {
BerDecoder ber = new BerDecoder(value, 0, value.length);
ber.parseSeq(null);
changeType = ber.parseEnumeration();
if ((ber.bytesLeft() > 0) && (ber.peekByte() == Ber.ASN_OCTET_STR)){
previousDN = ber.parseString(true);
}
if ((ber.bytesLeft() > 0) && (ber.peekByte() == Ber.ASN_INTEGER)) {
changeNumber = ber.parseInt();
}
}
}
/**
* Retrieves the type of change that occurred.
*
* @return The type of change.
*/
public int getChangeType() {
return changeType;
}
/**
* Retrieves the previous distinguished name of the entry before it was
* renamed and/or moved. This method applies only to RENAME changes.
*
* @return The previous distinguished name or null if not applicable.
*/
public String getPreviousDN() {
return previousDN;
}
/**
* Retrieves the change number assigned by the server for this change.
* Returns -1 if this feature is not supported by the server.
*
* @return The change number or -1 if unsupported.
*/
public long getChangeNumber() {
return changeNumber;
}
}

View File

@@ -0,0 +1,176 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import java.util.Vector;
import java.util.EventObject;
import javax.naming.event.NamingEvent;
import javax.naming.event.NamingExceptionEvent;
import javax.naming.event.NamingListener;
import javax.naming.ldap.UnsolicitedNotificationEvent;
import javax.naming.ldap.UnsolicitedNotificationListener;
/**
* Package private class used by EventSupport to dispatch events.
* This class implements an event queue, and a dispatcher thread that
* dequeues and dispatches events from the queue.
*
* Pieces stolen from sun.misc.Queue.
*
* @author Bill Shannon (from javax.mail.event)
* @author Rosanna Lee (modified for JNDI-related events)
*/
final class EventQueue implements Runnable {
final static private boolean debug = false;
private static class QueueElement {
QueueElement next = null;
QueueElement prev = null;
EventObject event = null;
Vector<NamingListener> vector = null;
QueueElement(EventObject event, Vector<NamingListener> vector) {
this.event = event;
this.vector = vector;
}
}
private QueueElement head = null;
private QueueElement tail = null;
private Thread qThread;
// package private
EventQueue() {
qThread = Obj.helper.createThread(this);
qThread.setDaemon(true); // not a user thread
qThread.start();
}
// package private;
/**
* Enqueue an event.
* @param event Either a <tt>NamingExceptionEvent</tt> or a subclass
* of <tt>NamingEvent</tt> or
* <tt>UnsolicitedNotificatoniEvent</tt>.
* If it is a subclass of <tt>NamingEvent</tt>, all listeners must implement
* the corresponding subinterface of <tt>NamingListener</tt>.
* For example, for a <tt>ObjectAddedEvent</tt>, all listeners <em>must</em>
* implement the <tt>ObjectAddedListener</tt> interface.
* <em>The current implementation does not check this before dispatching
* the event.</em>
* If the event is a <tt>NamingExceptionEvent</tt>, then all listeners
* are notified.
* @param vector List of NamingListeners that will be notified of event.
*/
synchronized void enqueue(EventObject event, Vector<NamingListener> vector) {
QueueElement newElt = new QueueElement(event, vector);
if (head == null) {
head = newElt;
tail = newElt;
} else {
newElt.next = head;
head.prev = newElt;
head = newElt;
}
notify();
}
/**
* Dequeue the oldest object on the queue.
* Used only by the run() method.
*
* @return the oldest object on the queue.
* @exception java.lang.InterruptedException if any thread has
* interrupted this thread.
*/
private synchronized QueueElement dequeue()
throws InterruptedException {
while (tail == null)
wait();
QueueElement elt = tail;
tail = elt.prev;
if (tail == null) {
head = null;
} else {
tail.next = null;
}
elt.prev = elt.next = null;
return elt;
}
/**
* Pull events off the queue and dispatch them.
*/
public void run() {
QueueElement qe;
try {
while ((qe = dequeue()) != null) {
EventObject e = qe.event;
Vector<NamingListener> v = qe.vector;
for (int i = 0; i < v.size(); i++) {
// Dispatch to corresponding NamingListener
// The listener should only be getting the event that
// it is interested in. (No need to check mask or
// instanceof subinterfaces.)
// It is the responsibility of the enqueuer to
// only enqueue events with listseners of the correct type.
if (e instanceof NamingEvent) {
((NamingEvent)e).dispatch(v.elementAt(i));
// An exception occurred: if notify all naming listeners
} else if (e instanceof NamingExceptionEvent) {
((NamingExceptionEvent)e).dispatch(v.elementAt(i));
} else if (e instanceof UnsolicitedNotificationEvent) {
((UnsolicitedNotificationEvent)e).dispatch(
(UnsolicitedNotificationListener)v.elementAt(i));
}
}
qe = null; e = null; v = null;
}
} catch (InterruptedException e) {
// just die
}
}
// package private; used by EventSupport;
/**
* Stop the dispatcher so we can be destroyed.
*/
void stop() {
if (debug) System.err.println("EventQueue stopping");
if (qThread != null) {
qThread.interrupt(); // kill our thread
qThread = null;
}
}
}

View File

@@ -0,0 +1,352 @@
/*
* Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import java.util.Hashtable;
import java.util.Vector;
import java.util.EventObject;
import java.util.Iterator;
import java.util.Map;
import javax.naming.*;
import javax.naming.event.*;
import javax.naming.directory.SearchControls;
import javax.naming.ldap.UnsolicitedNotificationListener;
import javax.naming.ldap.UnsolicitedNotificationEvent;
import javax.naming.ldap.UnsolicitedNotification;
/**
* This is a utility class that can be used by a context that supports
* event notification. You can use an instance of this class as a member field
* of your context and delegate various work to it.
* It is currently structured so that each context should have its own
* EventSupport (instead of static version shared by all contexts
* of a service provider).
*<p>
* This class supports two types of listeners: those that register for
* NamingEvents, and those for UnsolicitedNotificationEvents (they can be mixed
* into the same listener).
* For NamingEvent listeners, it maintains a hashtable that maps
* registration requests--the key--to
* <em>notifiers</em>--the value. Each registration request consists of:
*<ul>
*<li>The name argument of the registration.
*<li>The filter (default is "(objectclass=*)").
*<li>The search controls (default is null SearchControls).
*<li>The events that the listener is interested in. This is determined by
* finding out which <tt>NamingListener</tt> interface the listener supports.
*</ul>
*<p>
*A notifier (<tt>NamingEventNotifier</tt>) is a worker thread that is responsible
*for gathering information for generating events requested by its listeners.
*Each notifier maintains its own list of listeners; these listeners have
*all made the same registration request (at different times) and implements
*the same <tt>NamingListener</tt> interfaces.
*<p>
*For unsolicited listeners, this class maintains a vector, unsolicited.
*When an unsolicited listener is registered, this class adds itself
*to the context's LdapClient. When LdapClient receives an unsolicited
*notification, it notifies this EventSupport to fire an event to the
*the listeners. Special handling in LdapClient is done for the DISCONNECT
*notification. [It results in the EventSupport firing also a
*NamingExceptionEvent to the unsolicited listeners.]
*<p>
*
*When a context no longer needs this EventSupport, it should invoke
*cleanup() on it.
*<p>
*<h4>Registration</h4>
*When a registration request is made, this class attempts to find an
*existing notifier that's already working on the request. If one is
*found, the listener is added to the notifier's list. If one is not found,
*a new notifier is created for the listener.
*
*<h4>Deregistration</h4>
*When a deregistration request is made, this class attemps to find its
*corresponding notifier. If the notifier is found, the listener is removed
*from the notifier's list. If the listener is the last listener on the list,
*the notifier's thread is terminated and removed from this class's hashtable.
*Nothing happens if the notifier is not found.
*
*<h4>Event Dispatching</h4>
*The notifiers are responsible for gather information for generating events
*requested by their respective listeners. When a notifier gets sufficient
*information to generate an event, it creates invokes the
*appropriate <tt>fireXXXEvent</tt> on this class with the information and list of
*listeners. This causes an event and the list of listeners to be added
*to the <em>event queue</em>.
*This class maintains an event queue and a dispatching thread that dequeues
*events from the queue and dispatches them to the listeners.
*
*<h4>Synchronization</h4>
*This class is used by the main thread (LdapCtx) to add/remove listeners.
*It is also used asynchronously by NamingEventNotifiers threads and
*the context's Connection thread. It is used by the notifier threads to
*queue events and to update the notifiers list when the notifiers exit.
*It is used by the Connection thread to fire unsolicited notifications.
*Methods that access/update the 'unsolicited' and 'notifiers' lists are
*thread-safe.
*
* @author Rosanna Lee
*/
final class EventSupport {
final static private boolean debug = false;
private LdapCtx ctx;
/**
* NamingEventNotifiers; hashed by search arguments;
*/
private Hashtable<NotifierArgs, NamingEventNotifier> notifiers =
new Hashtable<>(11);
/**
* List of unsolicited notification listeners.
*/
private Vector<UnsolicitedNotificationListener> unsolicited = null;
/**
* Constructs EventSupport for ctx.
* <em>Do we need to record the name of the target context?
* Or can we assume that EventSupport is called on a resolved
* context? Do we need other add/remove-NamingListener methods?
* package private;
*/
EventSupport(LdapCtx ctx) {
this.ctx = ctx;
}
/**
* Adds <tt>l</tt> to list of listeners interested in <tt>nm</tt>.
*/
/*
* Make the add/removeNamingListeners synchronized to:
* 1. protect usage of 'unsolicited', which may be read by
* the Connection thread when dispatching unsolicited notification.
* 2. ensure that NamingEventNotifier thread's access to 'notifiers'
* is safe
*/
synchronized void addNamingListener(String nm, int scope,
NamingListener l) throws NamingException {
if (l instanceof ObjectChangeListener ||
l instanceof NamespaceChangeListener) {
NotifierArgs args = new NotifierArgs(nm, scope, l);
NamingEventNotifier notifier = notifiers.get(args);
if (notifier == null) {
notifier = new NamingEventNotifier(this, ctx, args, l);
notifiers.put(args, notifier);
} else {
notifier.addNamingListener(l);
}
}
if (l instanceof UnsolicitedNotificationListener) {
// Add listener to this's list of unsolicited notifiers
if (unsolicited == null) {
unsolicited = new Vector<>(3);
}
unsolicited.addElement((UnsolicitedNotificationListener)l);
}
}
/**
* Adds <tt>l</tt> to list of listeners interested in <tt>nm</tt>
* and filter.
*/
synchronized void addNamingListener(String nm, String filter,
SearchControls ctls, NamingListener l) throws NamingException {
if (l instanceof ObjectChangeListener ||
l instanceof NamespaceChangeListener) {
NotifierArgs args = new NotifierArgs(nm, filter, ctls, l);
NamingEventNotifier notifier = notifiers.get(args);
if (notifier == null) {
notifier = new NamingEventNotifier(this, ctx, args, l);
notifiers.put(args, notifier);
} else {
notifier.addNamingListener(l);
}
}
if (l instanceof UnsolicitedNotificationListener) {
// Add listener to this's list of unsolicited notifiers
if (unsolicited == null) {
unsolicited = new Vector<>(3);
}
unsolicited.addElement((UnsolicitedNotificationListener)l);
}
}
/**
* Removes <tt>l</tt> from all notifiers in this context.
*/
synchronized void removeNamingListener(NamingListener l) {
if (debug) {
System.err.println("EventSupport removing listener");
}
// Go through list of notifiers, remove 'l' from each.
// If 'l' is notifier's only listener, remove notifier too.
Iterator<NamingEventNotifier> iterator = notifiers.values().iterator();
while (iterator.hasNext()) {
NamingEventNotifier notifier = iterator.next();
if (notifier != null) {
if (debug) {
System.err.println("EventSupport removing listener from notifier");
}
notifier.removeNamingListener(l);
if (!notifier.hasNamingListeners()) {
if (debug) {
System.err.println("EventSupport stopping notifier");
}
notifier.stop();
iterator.remove();
}
}
}
// Remove from list of unsolicited notifier
if (debug) {
System.err.println("EventSupport removing unsolicited: " + unsolicited);
}
if (unsolicited != null) {
unsolicited.removeElement(l);
}
}
synchronized boolean hasUnsolicited() {
return (unsolicited != null && unsolicited.size() > 0);
}
/**
* package private;
* Called by NamingEventNotifier to remove itself when it encounters
* a NamingException.
*/
synchronized void removeDeadNotifier(NotifierArgs info) {
if (debug) {
System.err.println("EventSupport.removeDeadNotifier: " + info.name);
}
notifiers.remove(info);
}
/**
* Fire an event to unsolicited listeners.
* package private;
* Called by LdapCtx when its clnt receives an unsolicited notification.
*/
synchronized void fireUnsolicited(Object obj) {
if (debug) {
System.err.println("EventSupport.fireUnsolicited: " + obj + " "
+ unsolicited);
}
if (unsolicited == null || unsolicited.size() == 0) {
// This shouldn't really happen, but might in case
// there is a timing problem that removes a listener
// before a fired event event reaches here.
return;
}
if (obj instanceof UnsolicitedNotification) {
// Fire UnsolicitedNotification to unsolicited listeners
UnsolicitedNotificationEvent evt =
new UnsolicitedNotificationEvent(ctx, (UnsolicitedNotification)obj);
queueEvent(evt, unsolicited);
} else if (obj instanceof NamingException) {
// Fire NamingExceptionEvent to unsolicited listeners.
NamingExceptionEvent evt =
new NamingExceptionEvent(ctx, (NamingException)obj);
queueEvent(evt, unsolicited);
// When an exception occurs, the unsolicited listeners
// are automatically deregistered.
// When LdapClient.processUnsolicited() fires a NamingException,
// it will update its listener list so we don't have to.
// Likewise for LdapCtx.
unsolicited = null;
}
}
/**
* Stops notifier threads that are collecting event data and
* stops the event queue from dispatching events.
* Package private; used by LdapCtx.
*/
synchronized void cleanup() {
if (debug) System.err.println("EventSupport clean up");
if (notifiers != null) {
for (NamingEventNotifier notifier : notifiers.values()) {
notifier.stop();
}
notifiers = null;
}
if (eventQueue != null) {
eventQueue.stop();
eventQueue = null;
}
// %%% Should we fire NamingExceptionEvents to unsolicited listeners?
}
/*
* The queue of events to be delivered.
*/
private EventQueue eventQueue;
/**
* Add the event and vector of listeners to the queue to be delivered.
* An event dispatcher thread dequeues events from the queue and dispatches
* them to the registered listeners.
* Package private; used by NamingEventNotifier to fire events
*/
synchronized void queueEvent(EventObject event,
Vector<? extends NamingListener> vector) {
if (eventQueue == null)
eventQueue = new EventQueue();
/*
* Copy the vector in order to freeze the state of the set
* of EventListeners the event should be delivered to prior
* to delivery. This ensures that any changes made to the
* Vector from a target listener's method during the delivery
* of this event will not take effect until after the event is
* delivered.
*/
@SuppressWarnings("unchecked") // clone()
Vector<NamingListener> v =
(Vector<NamingListener>)vector.clone();
eventQueue.enqueue(event, v);
}
// No finalize() needed because EventSupport is always owned by
// an LdapCtx. LdapCtx's finalize() and close() always call cleanup() so
// there is no need for EventSupport to have a finalize().
}

View File

@@ -0,0 +1,870 @@
/*
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import javax.naming.NamingException;
import javax.naming.directory.InvalidSearchFilterException;
import java.io.IOException;
/**
* LDAP (RFC-1960) and LDAPv3 (RFC-2254) search filters.
*
* @author Xuelei Fan
* @author Vincent Ryan
* @author Jagane Sundar
* @author Rosanna Lee
*/
final class Filter {
/**
* First convert filter string into byte[].
* For LDAP v3, the conversion uses Unicode -> UTF8
* For LDAP v2, the conversion uses Unicode -> ISO 8859 (Latin-1)
*
* Then parse the byte[] as a filter, converting \hh to
* a single byte, and encoding the resulting filter
* into the supplied BER buffer
*/
static void encodeFilterString(BerEncoder ber, String filterStr,
boolean isLdapv3) throws IOException, NamingException {
if ((filterStr == null) || (filterStr.equals(""))) {
throw new InvalidSearchFilterException("Empty filter");
}
byte[] filter;
int filterLen;
if (isLdapv3) {
filter = filterStr.getBytes("UTF8");
} else {
filter = filterStr.getBytes("8859_1");
}
filterLen = filter.length;
if (dbg) {
dbgIndent = 0;
System.err.println("String filter: " + filterStr);
System.err.println("size: " + filterLen);
dprint("original: ", filter, 0, filterLen);
}
encodeFilter(ber, filter, 0, filterLen);
}
private static void encodeFilter(BerEncoder ber, byte[] filter,
int filterStart, int filterEnd) throws IOException, NamingException {
if (dbg) {
dprint("encFilter: ", filter, filterStart, filterEnd);
dbgIndent++;
}
if ((filterEnd - filterStart) <= 0) {
throw new InvalidSearchFilterException("Empty filter");
}
int nextOffset;
int parens, balance;
boolean escape;
parens = 0;
int filtOffset[] = new int[1];
for (filtOffset[0] = filterStart; filtOffset[0] < filterEnd;) {
switch (filter[filtOffset[0]]) {
case '(':
filtOffset[0]++;
parens++;
switch (filter[filtOffset[0]]) {
case '&':
encodeComplexFilter(ber, filter,
LDAP_FILTER_AND, filtOffset, filterEnd);
// filtOffset[0] has pointed to char after right paren
parens--;
break;
case '|':
encodeComplexFilter(ber, filter,
LDAP_FILTER_OR, filtOffset, filterEnd);
// filtOffset[0] has pointed to char after right paren
parens--;
break;
case '!':
encodeComplexFilter(ber, filter,
LDAP_FILTER_NOT, filtOffset, filterEnd);
// filtOffset[0] has pointed to char after right paren
parens--;
break;
default:
balance = 1;
escape = false;
nextOffset = filtOffset[0];
while (nextOffset < filterEnd && balance > 0) {
if (!escape) {
if (filter[nextOffset] == '(')
balance++;
else if (filter[nextOffset] == ')')
balance--;
}
if (filter[nextOffset] == '\\' && !escape)
escape = true;
else
escape = false;
if (balance > 0)
nextOffset++;
}
if (balance != 0)
throw new InvalidSearchFilterException(
"Unbalanced parenthesis");
encodeSimpleFilter(ber, filter, filtOffset[0], nextOffset);
// points to the char after right paren.
filtOffset[0] = nextOffset + 1;
parens--;
break;
}
break;
case ')':
//
// End of sequence
//
ber.endSeq();
filtOffset[0]++;
parens--;
break;
case ' ':
filtOffset[0]++;
break;
default: // assume simple type=value filter
encodeSimpleFilter(ber, filter, filtOffset[0], filterEnd);
filtOffset[0] = filterEnd; // force break from outer
break;
}
if (parens < 0) {
throw new InvalidSearchFilterException(
"Unbalanced parenthesis");
}
}
if (parens != 0) {
throw new InvalidSearchFilterException("Unbalanced parenthesis");
}
if (dbg) {
dbgIndent--;
}
}
/**
* convert character 'c' that represents a hexadecimal digit to an integer.
* if 'c' is not a hexadecimal digit [0-9A-Fa-f], -1 is returned.
* otherwise the converted value is returned.
*/
private static int hexchar2int( byte c ) {
if ( c >= '0' && c <= '9' ) {
return( c - '0' );
}
if ( c >= 'A' && c <= 'F' ) {
return( c - 'A' + 10 );
}
if ( c >= 'a' && c <= 'f' ) {
return( c - 'a' + 10 );
}
return( -1 );
}
// called by the LdapClient.compare method
static byte[] unescapeFilterValue(byte[] orig, int start, int end)
throws NamingException {
boolean escape = false, escStart = false;
int ival;
byte ch;
if (dbg) {
dprint("unescape: " , orig, start, end);
}
int len = end - start;
byte tbuf[] = new byte[len];
int j = 0;
for (int i = start; i < end; i++) {
ch = orig[i];
if (escape) {
// Try LDAP V3 escape (\xx)
if ((ival = hexchar2int(ch)) < 0) {
/**
* If there is no hex char following a '\' when
* parsing a LDAP v3 filter (illegal by v3 way)
* we fallback to the way we unescape in v2.
*/
if (escStart) {
// V2: \* \( \)
escape = false;
tbuf[j++] = ch;
} else {
// escaping already started but we can't find 2nd hex
throw new InvalidSearchFilterException("invalid escape sequence: " + orig);
}
} else {
if (escStart) {
tbuf[j] = (byte)(ival<<4);
escStart = false;
} else {
tbuf[j++] |= (byte)ival;
escape = false;
}
}
} else if (ch != '\\') {
tbuf[j++] = ch;
escape = false;
} else {
escStart = escape = true;
}
}
byte[] answer = new byte[j];
System.arraycopy(tbuf, 0, answer, 0, j);
if (dbg) {
Ber.dumpBER(System.err, "", answer, 0, j);
}
return answer;
}
private static int indexOf(byte[] str, char ch, int start, int end) {
for (int i = start; i < end; i++) {
if (str[i] == ch)
return i;
}
return -1;
}
private static int indexOf(byte[] str, String target, int start, int end) {
int where = indexOf(str, target.charAt(0), start, end);
if (where >= 0) {
for (int i = 1; i < target.length(); i++) {
if (str[where+i] != target.charAt(i)) {
return -1;
}
}
}
return where;
}
private static int findUnescaped(byte[] str, char ch, int start, int end) {
while (start < end) {
int where = indexOf(str, ch, start, end);
/*
* Count the immediate preceding '\' to find out if
* this is an escaped '*'. This is a made-up way for
* parsing an escaped '*' in v2. This is how the other leading
* SDK vendors interpret v2.
* For v3 we fallback to the way we parse "\*" in v2.
* It's not legal in v3 to use "\*" to escape '*'; the right
* way is to use "\2a" instead.
*/
int backSlashPos;
int backSlashCnt = 0;
for (backSlashPos = where - 1;
((backSlashPos >= start) && (str[backSlashPos] == '\\'));
backSlashPos--, backSlashCnt++);
// if at start of string, or not there at all, or if not escaped
if (where == start || where == -1 || ((backSlashCnt % 2) == 0))
return where;
// start search after escaped star
start = where + 1;
}
return -1;
}
private static void encodeSimpleFilter(BerEncoder ber, byte[] filter,
int filtStart, int filtEnd) throws IOException, NamingException {
if (dbg) {
dprint("encSimpleFilter: ", filter, filtStart, filtEnd);
dbgIndent++;
}
String type, value;
int valueStart, valueEnd, typeStart, typeEnd;
int eq;
if ((eq = indexOf(filter, '=', filtStart, filtEnd)) == -1) {
throw new InvalidSearchFilterException("Missing 'equals'");
}
valueStart = eq + 1; // value starts after equal sign
valueEnd = filtEnd;
typeStart = filtStart; // beginning of string
int ftype;
switch (filter[eq - 1]) {
case '<':
ftype = LDAP_FILTER_LE;
typeEnd = eq - 1;
break;
case '>':
ftype = LDAP_FILTER_GE;
typeEnd = eq - 1;
break;
case '~':
ftype = LDAP_FILTER_APPROX;
typeEnd = eq - 1;
break;
case ':':
ftype = LDAP_FILTER_EXT;
typeEnd = eq - 1;
break;
default:
typeEnd = eq;
//initializing ftype to make the compiler happy
ftype = 0x00;
break;
}
if (dbg) {
System.err.println("type: " + typeStart + ", " + typeEnd);
System.err.println("value: " + valueStart + ", " + valueEnd);
}
// check validity of type
//
// RFC4512 defines the type as the following ABNF:
// attr = attributedescription
// attributedescription = attributetype options
// attributetype = oid
// oid = descr / numericoid
// descr = keystring
// keystring = leadkeychar *keychar
// leadkeychar = ALPHA
// keychar = ALPHA / DIGIT / HYPHEN
// numericoid = number 1*( DOT number )
// number = DIGIT / ( LDIGIT 1*DIGIT )
// options = *( SEMI option )
// option = 1*keychar
//
// And RFC4515 defines the extensible type as the following ABNF:
// attr [dnattrs] [matchingrule] / [dnattrs] matchingrule
int optionsStart = -1;
int extensibleStart = -1;
if ((filter[typeStart] >= '0' && filter[typeStart] <= '9') ||
(filter[typeStart] >= 'A' && filter[typeStart] <= 'Z') ||
(filter[typeStart] >= 'a' && filter[typeStart] <= 'z')) {
boolean isNumericOid =
filter[typeStart] >= '0' && filter[typeStart] <= '9';
for (int i = typeStart + 1; i < typeEnd; i++) {
// ';' is an indicator of attribute options
if (filter[i] == ';') {
if (isNumericOid && filter[i - 1] == '.') {
throw new InvalidSearchFilterException(
"invalid attribute description");
}
// attribute options
optionsStart = i;
break;
}
// ':' is an indicator of extensible rules
if (filter[i] == ':' && ftype == LDAP_FILTER_EXT) {
if (isNumericOid && filter[i - 1] == '.') {
throw new InvalidSearchFilterException(
"invalid attribute description");
}
// extensible matching
extensibleStart = i;
break;
}
if (isNumericOid) {
// numeric object identifier
if ((filter[i] == '.' && filter[i - 1] == '.') ||
(filter[i] != '.' &&
!(filter[i] >= '0' && filter[i] <= '9'))) {
throw new InvalidSearchFilterException(
"invalid attribute description");
}
} else {
// descriptor
// The underscore ("_") character is not allowed by
// the LDAP specification. We allow it here to
// tolerate the incorrect use in practice.
if (filter[i] != '-' && filter[i] != '_' &&
!(filter[i] >= '0' && filter[i] <= '9') &&
!(filter[i] >= 'A' && filter[i] <= 'Z') &&
!(filter[i] >= 'a' && filter[i] <= 'z')) {
throw new InvalidSearchFilterException(
"invalid attribute description");
}
}
}
} else if (ftype == LDAP_FILTER_EXT && filter[typeStart] == ':') {
// extensible matching
extensibleStart = typeStart;
} else {
throw new InvalidSearchFilterException(
"invalid attribute description");
}
// check attribute options
if (optionsStart > 0) {
for (int i = optionsStart + 1; i < typeEnd; i++) {
if (filter[i] == ';') {
if (filter[i - 1] == ';') {
throw new InvalidSearchFilterException(
"invalid attribute description");
}
continue;
}
// ':' is an indicator of extensible rules
if (filter[i] == ':' && ftype == LDAP_FILTER_EXT) {
if (filter[i - 1] == ';') {
throw new InvalidSearchFilterException(
"invalid attribute description");
}
// extensible matching
extensibleStart = i;
break;
}
// The underscore ("_") character is not allowed by
// the LDAP specification. We allow it here to
// tolerate the incorrect use in practice.
if (filter[i] != '-' && filter[i] != '_' &&
!(filter[i] >= '0' && filter[i] <= '9') &&
!(filter[i] >= 'A' && filter[i] <= 'Z') &&
!(filter[i] >= 'a' && filter[i] <= 'z')) {
throw new InvalidSearchFilterException(
"invalid attribute description");
}
}
}
// check extensible matching
if (extensibleStart > 0) {
boolean isMatchingRule = false;
for (int i = extensibleStart + 1; i < typeEnd; i++) {
if (filter[i] == ':') {
throw new InvalidSearchFilterException(
"invalid attribute description");
} else if ((filter[i] >= '0' && filter[i] <= '9') ||
(filter[i] >= 'A' && filter[i] <= 'Z') ||
(filter[i] >= 'a' && filter[i] <= 'z')) {
boolean isNumericOid = filter[i] >= '0' && filter[i] <= '9';
i++;
for (int j = i; j < typeEnd; j++, i++) {
// allows no more than two extensible rules
if (filter[j] == ':') {
if (isMatchingRule) {
throw new InvalidSearchFilterException(
"invalid attribute description");
}
if (isNumericOid && filter[j - 1] == '.') {
throw new InvalidSearchFilterException(
"invalid attribute description");
}
isMatchingRule = true;
break;
}
if (isNumericOid) {
// numeric object identifier
if ((filter[j] == '.' && filter[j - 1] == '.') ||
(filter[j] != '.' &&
!(filter[j] >= '0' && filter[j] <= '9'))) {
throw new InvalidSearchFilterException(
"invalid attribute description");
}
} else {
// descriptor
// The underscore ("_") character is not allowed by
// the LDAP specification. We allow it here to
// tolerate the incorrect use in practice.
if (filter[j] != '-' && filter[j] != '_' &&
!(filter[j] >= '0' && filter[j] <= '9') &&
!(filter[j] >= 'A' && filter[j] <= 'Z') &&
!(filter[j] >= 'a' && filter[j] <= 'z')) {
throw new InvalidSearchFilterException(
"invalid attribute description");
}
}
}
} else {
throw new InvalidSearchFilterException(
"invalid attribute description");
}
}
}
// ensure the latest byte is not isolated
if (filter[typeEnd - 1] == '.' || filter[typeEnd - 1] == ';' ||
filter[typeEnd - 1] == ':') {
throw new InvalidSearchFilterException(
"invalid attribute description");
}
if (typeEnd == eq) { // filter type is of "equal"
if (findUnescaped(filter, '*', valueStart, valueEnd) == -1) {
ftype = LDAP_FILTER_EQUALITY;
} else if (filter[valueStart] == '*' &&
valueStart == (valueEnd - 1)) {
ftype = LDAP_FILTER_PRESENT;
} else {
encodeSubstringFilter(ber, filter,
typeStart, typeEnd, valueStart, valueEnd);
return;
}
}
if (ftype == LDAP_FILTER_PRESENT) {
ber.encodeOctetString(filter, ftype, typeStart, typeEnd-typeStart);
} else if (ftype == LDAP_FILTER_EXT) {
encodeExtensibleMatch(ber, filter,
typeStart, typeEnd, valueStart, valueEnd);
} else {
ber.beginSeq(ftype);
ber.encodeOctetString(filter, Ber.ASN_OCTET_STR,
typeStart, typeEnd - typeStart);
ber.encodeOctetString(
unescapeFilterValue(filter, valueStart, valueEnd),
Ber.ASN_OCTET_STR);
ber.endSeq();
}
if (dbg) {
dbgIndent--;
}
}
private static void encodeSubstringFilter(BerEncoder ber, byte[] filter,
int typeStart, int typeEnd, int valueStart, int valueEnd)
throws IOException, NamingException {
if (dbg) {
dprint("encSubstringFilter: type ", filter, typeStart, typeEnd);
dprint(", val : ", filter, valueStart, valueEnd);
dbgIndent++;
}
ber.beginSeq(LDAP_FILTER_SUBSTRINGS);
ber.encodeOctetString(filter, Ber.ASN_OCTET_STR,
typeStart, typeEnd-typeStart);
ber.beginSeq(LdapClient.LBER_SEQUENCE);
int index;
int previndex = valueStart;
while ((index = findUnescaped(filter, '*', previndex, valueEnd)) != -1) {
if (previndex == valueStart) {
if (previndex < index) {
if (dbg)
System.err.println(
"initial: " + previndex + "," + index);
ber.encodeOctetString(
unescapeFilterValue(filter, previndex, index),
LDAP_SUBSTRING_INITIAL);
}
} else {
if (previndex < index) {
if (dbg)
System.err.println("any: " + previndex + "," + index);
ber.encodeOctetString(
unescapeFilterValue(filter, previndex, index),
LDAP_SUBSTRING_ANY);
}
}
previndex = index + 1;
}
if (previndex < valueEnd) {
if (dbg)
System.err.println("final: " + previndex + "," + valueEnd);
ber.encodeOctetString(
unescapeFilterValue(filter, previndex, valueEnd),
LDAP_SUBSTRING_FINAL);
}
ber.endSeq();
ber.endSeq();
if (dbg) {
dbgIndent--;
}
}
// The complex filter types look like:
// "&(type=val)(type=val)"
// "|(type=val)(type=val)"
// "!(type=val)"
//
// The filtOffset[0] pointing to the '&', '|', or '!'.
//
private static void encodeComplexFilter(BerEncoder ber, byte[] filter,
int filterType, int filtOffset[], int filtEnd)
throws IOException, NamingException {
if (dbg) {
dprint("encComplexFilter: ", filter, filtOffset[0], filtEnd);
dprint(", type: " + Integer.toString(filterType, 16));
dbgIndent++;
}
filtOffset[0]++;
ber.beginSeq(filterType);
int[] parens = findRightParen(filter, filtOffset, filtEnd);
encodeFilterList(ber, filter, filterType, parens[0], parens[1]);
ber.endSeq();
if (dbg) {
dbgIndent--;
}
}
//
// filter at filtOffset[0] - 1 points to a (. Find ) that matches it
// and return substring between the parens. Adjust filtOffset[0] to
// point to char after right paren
//
private static int[] findRightParen(byte[] filter, int filtOffset[], int end)
throws IOException, NamingException {
int balance = 1;
boolean escape = false;
int nextOffset = filtOffset[0];
while (nextOffset < end && balance > 0) {
if (!escape) {
if (filter[nextOffset] == '(')
balance++;
else if (filter[nextOffset] == ')')
balance--;
}
if (filter[nextOffset] == '\\' && !escape)
escape = true;
else
escape = false;
if (balance > 0)
nextOffset++;
}
if (balance != 0) {
throw new InvalidSearchFilterException("Unbalanced parenthesis");
}
// String tmp = filter.substring(filtOffset[0], nextOffset);
int[] tmp = new int[] {filtOffset[0], nextOffset};
filtOffset[0] = nextOffset + 1;
return tmp;
}
//
// Encode filter list of type "(filter1)(filter2)..."
//
private static void encodeFilterList(BerEncoder ber, byte[] filter,
int filterType, int start, int end) throws IOException, NamingException {
if (dbg) {
dprint("encFilterList: ", filter, start, end);
dbgIndent++;
}
int filtOffset[] = new int[1];
int listNumber = 0;
for (filtOffset[0] = start; filtOffset[0] < end; filtOffset[0]++) {
if (Character.isSpaceChar((char)filter[filtOffset[0]]))
continue;
if ((filterType == LDAP_FILTER_NOT) && (listNumber > 0)) {
throw new InvalidSearchFilterException(
"Filter (!) cannot be followed by more than one filters");
}
if (filter[filtOffset[0]] == '(') {
continue;
}
int[] parens = findRightParen(filter, filtOffset, end);
// add enclosing parens
int len = parens[1]-parens[0];
byte[] newfilter = new byte[len+2];
System.arraycopy(filter, parens[0], newfilter, 1, len);
newfilter[0] = (byte)'(';
newfilter[len+1] = (byte)')';
encodeFilter(ber, newfilter, 0, newfilter.length);
listNumber++;
}
if (dbg) {
dbgIndent--;
}
}
//
// Encode extensible match
//
private static void encodeExtensibleMatch(BerEncoder ber, byte[] filter,
int matchStart, int matchEnd, int valueStart, int valueEnd)
throws IOException, NamingException {
boolean matchDN = false;
int colon;
int colon2;
int i;
ber.beginSeq(LDAP_FILTER_EXT);
// test for colon separator
if ((colon = indexOf(filter, ':', matchStart, matchEnd)) >= 0) {
// test for match DN
if ((i = indexOf(filter, ":dn", colon, matchEnd)) >= 0) {
matchDN = true;
}
// test for matching rule
if (((colon2 = indexOf(filter, ':', colon + 1, matchEnd)) >= 0)
|| (i == -1)) {
if (i == colon) {
ber.encodeOctetString(filter, LDAP_FILTER_EXT_RULE,
colon2 + 1, matchEnd - (colon2 + 1));
} else if ((i == colon2) && (i >= 0)) {
ber.encodeOctetString(filter, LDAP_FILTER_EXT_RULE,
colon + 1, colon2 - (colon + 1));
} else {
ber.encodeOctetString(filter, LDAP_FILTER_EXT_RULE,
colon + 1, matchEnd - (colon + 1));
}
}
// test for attribute type
if (colon > matchStart) {
ber.encodeOctetString(filter,
LDAP_FILTER_EXT_TYPE, matchStart, colon - matchStart);
}
} else {
ber.encodeOctetString(filter, LDAP_FILTER_EXT_TYPE, matchStart,
matchEnd - matchStart);
}
ber.encodeOctetString(
unescapeFilterValue(filter, valueStart, valueEnd),
LDAP_FILTER_EXT_VAL);
/*
* This element is defined in RFC-2251 with an ASN.1 DEFAULT tag.
* However, for Active Directory interoperability it is transmitted
* even when FALSE.
*/
ber.encodeBoolean(matchDN, LDAP_FILTER_EXT_DN);
ber.endSeq();
}
////////////////////////////////////////////////////////////////////////////
//
// some debug print code that does indenting. Useful for debugging
// the filter generation code
//
////////////////////////////////////////////////////////////////////////////
private static final boolean dbg = false;
private static int dbgIndent = 0;
private static void dprint(String msg) {
dprint(msg, new byte[0], 0, 0);
}
private static void dprint(String msg, byte[] str) {
dprint(msg, str, 0, str.length);
}
private static void dprint(String msg, byte[] str, int start, int end) {
String dstr = " ";
int i = dbgIndent;
while (i-- > 0) {
dstr += " ";
}
dstr += msg;
System.err.print(dstr);
for (int j = start; j < end; j++) {
System.err.print((char)str[j]);
}
System.err.println();
}
/////////////// Constants used for encoding filter //////////////
static final int LDAP_FILTER_AND = 0xa0;
static final int LDAP_FILTER_OR = 0xa1;
static final int LDAP_FILTER_NOT = 0xa2;
static final int LDAP_FILTER_EQUALITY = 0xa3;
static final int LDAP_FILTER_SUBSTRINGS = 0xa4;
static final int LDAP_FILTER_GE = 0xa5;
static final int LDAP_FILTER_LE = 0xa6;
static final int LDAP_FILTER_PRESENT = 0x87;
static final int LDAP_FILTER_APPROX = 0xa8;
static final int LDAP_FILTER_EXT = 0xa9; // LDAPv3
static final int LDAP_FILTER_EXT_RULE = 0x81; // LDAPv3
static final int LDAP_FILTER_EXT_TYPE = 0x82; // LDAPv3
static final int LDAP_FILTER_EXT_VAL = 0x83; // LDAPv3
static final int LDAP_FILTER_EXT_DN = 0x84; // LDAPv3
static final int LDAP_SUBSTRING_INITIAL = 0x80;
static final int LDAP_SUBSTRING_ANY = 0x81;
static final int LDAP_SUBSTRING_FINAL = 0x82;
}

View File

@@ -0,0 +1,212 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import java.io.IOException;
import java.util.Hashtable;
import java.util.Vector;
import javax.naming.*;
import javax.naming.directory.*;
/**
* This subclass is used by LDAP to implement the schema calls.
* Basically, it keeps track of which context it is an attribute of
* so it can get the schema for that cotnext.
*
* @author Jon Ruiz
*/
final class LdapAttribute extends BasicAttribute {
static final long serialVersionUID = -4288716561020779584L;
private transient DirContext baseCtx = null;
private Name rdn = new CompositeName();
// these two are used to reconstruct the baseCtx if this attribute has
// been serialized (
private String baseCtxURL;
private Hashtable<String, ? super String> baseCtxEnv;
@SuppressWarnings("unchecked") // clone()
public Object clone() {
LdapAttribute attr = new LdapAttribute(this.attrID, baseCtx, rdn);
attr.values = (Vector<Object>)values.clone();
return attr;
}
/**
* Adds a new value to this attribute.
*
* @param attrVal The value to be added. If null, a null value is added to
* the attribute.
* @return true Always returns true.
*/
public boolean add(Object attrVal) {
// LDAP attributes don't contain duplicate values so there's no need
// to check if the value already exists before adding it.
values.addElement(attrVal);
return true;
}
/**
* Constructs a new instance of an attribute.
*
* @param id The attribute's id. It cannot be null.
*/
LdapAttribute(String id) {
super(id);
}
/**
* Constructs a new instance of an attribute.
*
* @param id The attribute's id. It cannot be null.
* @param baseCtx the baseCtx object of this attribute
* @param rdn the RDN of the entry (relative to baseCtx)
*/
private LdapAttribute(String id, DirContext baseCtx, Name rdn) {
super(id);
this.baseCtx = baseCtx;
this.rdn = rdn;
}
/**
* Sets the baseCtx and rdn used to find the attribute's schema
* Used by LdapCtx.setParents().
*/
void setParent(DirContext baseCtx, Name rdn) {
this.baseCtx = baseCtx;
this.rdn = rdn;
}
/**
* returns the ctx this attribute came from. This call allows
* LDAPAttribute to be serializable. 'baseCtx' is transient so if
* it is null, the `baseCtxURL` is used to reconstruct the context
* to which calls are made.
*/
private DirContext getBaseCtx() throws NamingException {
if(baseCtx == null) {
if (baseCtxEnv == null) {
baseCtxEnv = new Hashtable<String, String>(3);
}
baseCtxEnv.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
baseCtxEnv.put(Context.PROVIDER_URL,baseCtxURL);
baseCtx = (new InitialDirContext(baseCtxEnv));
}
return baseCtx;
}
/**
* This is called when the object is serialized. It is
* overridden so that the appropriate class variables can be set
* to re-construct the baseCtx when deserialized. Setting these
* variables is costly, so it is only done if the object
* is actually serialized.
*/
private void writeObject(java.io.ObjectOutputStream out)
throws IOException {
// setup internal state
this.setBaseCtxInfo();
// let the ObjectOutpurStream do the real work of serialization
out.defaultWriteObject();
}
/**
* sets the information needed to reconstruct the baseCtx if
* we are serialized. This must be called _before_ the object is
* serialized!!!
*/
@SuppressWarnings("unchecked") // clone()
private void setBaseCtxInfo() {
Hashtable<String, Object> realEnv = null;
Hashtable<String, Object> secureEnv = null;
if (baseCtx != null) {
realEnv = ((LdapCtx)baseCtx).envprops;
this.baseCtxURL = ((LdapCtx)baseCtx).getURL();
}
if(realEnv != null && realEnv.size() > 0 ) {
// remove any security credentials - otherwise the serialized form
// would store them in the clear
for (String key : realEnv.keySet()){
if (key.indexOf("security") != -1 ) {
//if we need to remove props, we must do it to a clone
//of the environment. cloning is expensive, so we only do
//it if we have to.
if(secureEnv == null) {
secureEnv = (Hashtable<String, Object>)realEnv.clone();
}
secureEnv.remove(key);
}
}
}
// set baseCtxEnv depending on whether we removed props or not
this.baseCtxEnv = (secureEnv == null ? realEnv : secureEnv);
}
/**
* Retrieves the syntax definition associated with this attribute.
* @return This attribute's syntax definition.
*/
public DirContext getAttributeSyntaxDefinition() throws NamingException {
// get the syntax id from the attribute def
DirContext schema = getBaseCtx().getSchema(rdn);
DirContext attrDef = (DirContext)schema.lookup(
LdapSchemaParser.ATTRIBUTE_DEFINITION_NAME + "/" + getID());
Attribute syntaxAttr = attrDef.getAttributes("").get("SYNTAX");
if(syntaxAttr == null || syntaxAttr.size() == 0) {
throw new NameNotFoundException(
getID() + "does not have a syntax associated with it");
}
String syntaxName = (String)syntaxAttr.get();
// look in the schema tree for the syntax definition
return (DirContext)schema.lookup(
LdapSchemaParser.SYNTAX_DEFINITION_NAME + "/" + syntaxName);
}
/**
* Retrieves this attribute's schema definition.
*
* @return This attribute's schema definition.
*/
public DirContext getAttributeDefinition() throws NamingException {
DirContext schema = getBaseCtx().getSchema(rdn);
return (DirContext)schema.lookup(
LdapSchemaParser.ATTRIBUTE_DEFINITION_NAME + "/" + getID());
}
}

View File

@@ -0,0 +1,112 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Vector;
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.ldap.Control;
import javax.naming.spi.*;
import com.sun.jndi.toolkit.ctx.Continuation;
final class LdapBindingEnumeration
extends AbstractLdapNamingEnumeration<Binding> {
private final AccessControlContext acc = AccessController.getContext();
LdapBindingEnumeration(LdapCtx homeCtx, LdapResult answer, Name remain,
Continuation cont) throws NamingException
{
super(homeCtx, answer, remain, cont);
}
@Override
protected Binding
createItem(String dn, Attributes attrs, Vector<Control> respCtls)
throws NamingException {
Object obj = null;
String atom = getAtom(dn);
if (attrs.get(Obj.JAVA_ATTRIBUTES[Obj.CLASSNAME]) != null) {
// serialized object or object reference
try {
obj = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws NamingException {
return Obj.decodeObject(attrs);
}
}, acc);
} catch (PrivilegedActionException e) {
throw (NamingException)e.getException();
}
}
if (obj == null) {
// DirContext object
obj = new LdapCtx(homeCtx, dn);
}
CompositeName cn = new CompositeName();
cn.add(atom);
try {
obj = DirectoryManager.getObjectInstance(obj, cn, homeCtx,
homeCtx.envprops, attrs);
} catch (NamingException e) {
throw e;
} catch (Exception e) {
NamingException ne =
new NamingException(
"problem generating object using object factory");
ne.setRootCause(e);
throw ne;
}
Binding binding;
if (respCtls != null) {
binding = new BindingWithControls(cn.toString(), obj,
homeCtx.convertControls(respCtls));
} else {
binding = new Binding(cn.toString(), obj);
}
binding.setNameInNamespace(dn);
return binding;
}
@Override
protected AbstractLdapNamingEnumeration<? extends NameClassPair> getReferredResults(
LdapReferralContext refCtx) throws NamingException{
// repeat the original operation at the new context
return (AbstractLdapNamingEnumeration<? extends NameClassPair>)refCtx.listBindings(listArg);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,71 @@
/*
* Copyright (c) 2002, 2005, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import java.io.OutputStream;
import javax.naming.InterruptedNamingException;
import javax.naming.CommunicationException;
import javax.naming.NamingException;
import com.sun.jndi.ldap.pool.PoolCallback;
import com.sun.jndi.ldap.pool.PooledConnection;
import com.sun.jndi.ldap.pool.PooledConnectionFactory;
/**
* Creates an LdapClient. Encapsulates the parameters required to create
* an LdapClient and provides methods for returning appropriate exceptions
* to throw when acquiring a pooled LdapClient fails.
*
* @author Rosanna Lee
*/
final class LdapClientFactory implements PooledConnectionFactory {
final private String host;
final private int port;
final private String socketFactory;
final private int connTimeout;
final private int readTimeout;
final private OutputStream trace;
LdapClientFactory(String host, int port, String socketFactory,
int connTimeout, int readTimeout, OutputStream trace) {
this.host = host;
this.port = port;
this.socketFactory = socketFactory;
this.connTimeout = connTimeout;
this.readTimeout = readTimeout;
this.trace = trace;
}
public PooledConnection createPooledConnection(PoolCallback pcb)
throws NamingException {
return new LdapClient(host, port, socketFactory,
connTimeout, readTimeout, trace, pcb);
}
public String toString() {
return host + ":" + port;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,303 @@
/*
* Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Enumeration;
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.spi.ObjectFactory;
import javax.naming.spi.InitialContextFactory;
import javax.naming.ldap.Control;
import com.sun.jndi.ldap.spi.LdapDnsProviderResult;
import com.sun.jndi.url.ldap.ldapURLContextFactory;
final public class LdapCtxFactory implements ObjectFactory, InitialContextFactory {
/**
* The type of each address in an LDAP reference.
*/
public final static String ADDRESS_TYPE = "URL";
// ----------------- ObjectFactory interface --------------------
public Object getObjectInstance(Object ref, Name name, Context nameCtx,
Hashtable<?,?> env) throws Exception {
if (!isLdapRef(ref)) {
return null;
}
ObjectFactory factory = new ldapURLContextFactory();
String[] urls = getURLs((Reference)ref);
return factory.getObjectInstance(urls, name, nameCtx, env);
}
// ----------------- InitialContext interface --------------------
public Context getInitialContext(Hashtable<?,?> envprops)
throws NamingException {
try {
String providerUrl = (envprops != null) ?
(String)envprops.get(Context.PROVIDER_URL) : null;
// If URL not in environment, use defaults
if (providerUrl == null) {
return new LdapCtx("", LdapCtx.DEFAULT_HOST,
LdapCtx.DEFAULT_PORT, envprops, false);
}
// Extract URL(s)
String[] urls = LdapURL.fromList(providerUrl);
if (urls.length == 0) {
throw new ConfigurationException(Context.PROVIDER_URL +
" property does not contain a URL");
}
// Generate an LDAP context
return getLdapCtxInstance(urls, envprops);
} catch (LdapReferralException e) {
if (envprops != null &&
"throw".equals(envprops.get(Context.REFERRAL))) {
throw e;
}
Control[] bindCtls = (envprops != null)?
(Control[])envprops.get(LdapCtx.BIND_CONTROLS) : null;
return (LdapCtx)e.getReferralContext(envprops, bindCtls);
}
}
/**
* Returns true if argument is an LDAP reference.
*/
private static boolean isLdapRef(Object obj) {
if (!(obj instanceof Reference)) {
return false;
}
String thisClassName = LdapCtxFactory.class.getName();
Reference ref = (Reference)obj;
return thisClassName.equals(ref.getFactoryClassName());
}
/**
* Returns the URLs contained within an LDAP reference.
*/
private static String[] getURLs(Reference ref) throws NamingException {
int size = 0; // number of URLs
String[] urls = new String[ref.size()];
Enumeration<RefAddr> addrs = ref.getAll();
while (addrs.hasMoreElements()) {
RefAddr addr = addrs.nextElement();
if ((addr instanceof StringRefAddr) &&
addr.getType().equals(ADDRESS_TYPE)) {
urls[size++] = (String)addr.getContent();
}
}
if (size == 0) {
throw (new ConfigurationException(
"Reference contains no valid addresses"));
}
// Trim URL array down to size.
if (size == ref.size()) {
return urls;
}
String[] urls2 = new String[size];
System.arraycopy(urls, 0, urls2, 0, size);
return urls2;
}
// ------------ Utilities used by other classes ----------------
public static DirContext getLdapCtxInstance(Object urlInfo, Hashtable<?,?> env)
throws NamingException {
if (urlInfo instanceof String) {
return getUsingURL((String)urlInfo, env);
} else if (urlInfo instanceof String[]) {
return getUsingURLs((String[])urlInfo, env);
} else {
throw new IllegalArgumentException(
"argument must be an LDAP URL String or array of them");
}
}
private static DirContext getUsingURL(String url, Hashtable<?,?> env)
throws NamingException
{
try {
LdapDnsProviderResult r =
LdapDnsProviderService.getInstance().lookupEndpoints(url, env);
LdapCtx ctx;
NamingException lastException = null;
/*
* Prior to this change we had been assuming that the url.getDN()
* should be converted to a domain name via
* ServiceLocator.mapDnToDomainName(url.getDN())
*
* However this is incorrect as we can't assume that the supplied
* url.getDN() is the same as the dns domain for the directory
* server.
*
* This means that we depend on the dnsProvider to return both
* the list of urls of individual hosts from which we attempt to
* create an LdapCtx from *AND* the domain name that they serve
*
* In order to do this the dnsProvider must return an
* {@link LdapDnsProviderResult}.
*
*/
for (String u : r.getEndpoints()) {
try {
ctx = getLdapCtxFromUrl(
r.getDomainName(), url, new LdapURL(u), env);
return ctx;
} catch (AuthenticationException e) {
// do not retry on a different endpoint to avoid blocking
// the user if authentication credentials are wrong.
throw e;
} catch (NamingException e) {
// try the next element
lastException = e;
}
}
if (lastException != null) {
throw lastException;
}
// lookupEndpoints returned an LdapDnsProviderResult with an empty
// list of endpoints
throw new NamingException("Could not resolve a valid ldap host");
} catch (NamingException e) {
// lookupEndpoints(url, env) may throw a NamingException, which
// there is no need to wrap.
throw e;
} catch (Exception e) {
NamingException ex = new NamingException();
ex.setRootCause(e);
throw ex;
}
}
private static LdapCtx getLdapCtxFromUrl(String domain,
String url,
LdapURL u,
Hashtable<?,?> env)
throws NamingException
{
String dn = u.getDN();
String host = u.getHost();
int port = u.getPort();
LdapCtx ctx = new LdapCtx(dn, host, port, env, u.useSsl());
ctx.setDomainName(domain);
// Record the URL that created the context
ctx.setProviderUrl(url);
return ctx;
}
/*
* Try each URL until one of them succeeds.
* If all URLs fail, throw one of the exceptions arbitrarily.
* Not pretty, but potentially more informative than returning null.
*/
private static DirContext getUsingURLs(String[] urls, Hashtable<?,?> env)
throws NamingException
{
NamingException ex = null;
for (String u : urls) {
try {
return getUsingURL(u, env);
} catch (AuthenticationException e) {
// do not retry on a different endpoint to avoid blocking
// the user if authentication credentials are wrong.
throw e;
} catch (NamingException e) {
ex = e;
}
}
throw ex;
}
/**
* Used by Obj and obj/RemoteToAttrs too so must be public
*/
public static Attribute createTypeNameAttr(Class<?> cl) {
Vector<String> v = new Vector<>(10);
String[] types = getTypeNames(cl, v);
if (types.length > 0) {
BasicAttribute tAttr =
new BasicAttribute(Obj.JAVA_ATTRIBUTES[Obj.TYPENAME]);
for (int i = 0; i < types.length; i++) {
tAttr.add(types[i]);
}
return tAttr;
}
return null;
}
private static String[] getTypeNames(Class<?> currentClass, Vector<String> v) {
getClassesAux(currentClass, v);
Class<?>[] members = currentClass.getInterfaces();
for (int i = 0; i < members.length; i++) {
getClassesAux(members[i], v);
}
String[] ret = new String[v.size()];
int i = 0;
for (String name : v) {
ret[i++] = name;
}
return ret;
}
private static void getClassesAux(Class<?> currentClass, Vector<String> v) {
if (!v.contains(currentClass.getName())) {
v.addElement(currentClass.getName());
}
currentClass = currentClass.getSuperclass();
while (currentClass != null) {
getTypeNames(currentClass, v);
currentClass = currentClass.getSuperclass();
}
}
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.*;
import javax.naming.NamingException;
import com.sun.jndi.ldap.spi.LdapDnsProvider;
import com.sun.jndi.ldap.spi.LdapDnsProviderResult;
import sun.security.util.SecurityConstants;
/**
* The {@code LdapDnsProviderService} is responsible for creating and providing
* access to the registered {@code LdapDnsProvider}s. The {@link ServiceLoader}
* is used to find and register any implementations of {@link LdapDnsProvider}.
*
* <p> Instances of this class are safe for use by multiple threads.
*/
final class LdapDnsProviderService {
private static volatile LdapDnsProviderService service;
private static final Object LOCK = new int[0];
private final ServiceLoader<LdapDnsProvider> providers;
/**
* Creates a new instance of LdapDnsProviderService
*/
private LdapDnsProviderService() {
SecurityManager sm = System.getSecurityManager();
if (sm == null) {
providers = ServiceLoader.load(
LdapDnsProvider.class,
ClassLoader.getSystemClassLoader());
} else {
final PrivilegedAction<ServiceLoader<LdapDnsProvider>> pa =
() -> ServiceLoader.load(
LdapDnsProvider.class,
ClassLoader.getSystemClassLoader());
providers = AccessController.doPrivileged(
pa,
null,
new RuntimePermission("ldapDnsProvider"),
SecurityConstants.GET_CLASSLOADER_PERMISSION);
}
}
/**
* Retrieves the singleton instance of LdapDnsProviderService.
*/
static LdapDnsProviderService getInstance() {
if (service != null) return service;
synchronized (LOCK) {
if (service != null) return service;
service = new LdapDnsProviderService();
}
return service;
}
/**
* Retrieves result from the first provider that successfully resolves
* the endpoints. If no results are found when calling installed
* subclasses of {@code LdapDnsProvider} then this method will fall back
* to the {@code DefaultLdapDnsProvider}.
*
* @throws NamingException if the {@code url} in not valid or an error
* occurred while performing the lookup.
*/
LdapDnsProviderResult lookupEndpoints(String url, Hashtable<?,?> env)
throws NamingException
{
LdapDnsProviderResult result = null;
Hashtable<?, ?> envCopy = new Hashtable<>(env);
synchronized (LOCK) {
Iterator<LdapDnsProvider> iterator = providers.iterator();
while (result == null && iterator.hasNext()) {
result = iterator.next().lookupEndpoints(url, envCopy)
.filter(r -> !r.getEndpoints().isEmpty())
.orElse(null);
}
}
if (result == null) {
return new DefaultLdapDnsProvider().lookupEndpoints(url, env)
.orElse(new LdapDnsProviderResult("", Collections.emptyList()));
}
return result;
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import java.util.Vector;
import javax.naming.directory.Attributes;
import javax.naming.ldap.Control;
/**
* A holder for an LDAP entry read from an LDAP server.
*
* @author Jagane Sundar
* @author Vincent Ryan
*/
final class LdapEntry {
String DN;
Attributes attributes;
Vector<Control> respCtls = null;
LdapEntry(String DN, Attributes attrs) {
this.DN = DN;
this.attributes = attrs;
}
LdapEntry(String DN, Attributes attrs, Vector<Control> respCtls) {
this.DN = DN;
this.attributes = attrs;
this.respCtls = respCtls;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 1999, 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import javax.naming.*;
import javax.naming.ldap.LdapName;
class LdapNameParser implements NameParser {
public LdapNameParser() {
}
public Name parse(String name) throws NamingException {
return new LdapName(name);
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import javax.naming.*;
import javax.naming.directory.*;
import com.sun.jndi.toolkit.ctx.Continuation;
import java.util.Vector;
import javax.naming.ldap.Control;
final class LdapNamingEnumeration
extends AbstractLdapNamingEnumeration<NameClassPair> {
private static final String defaultClassName = DirContext.class.getName();
LdapNamingEnumeration(LdapCtx homeCtx, LdapResult answer, Name listArg,
Continuation cont) throws NamingException {
super(homeCtx, answer, listArg, cont);
}
@Override
protected NameClassPair createItem(String dn, Attributes attrs,
Vector<Control> respCtls) throws NamingException {
Attribute attr;
String className = null;
// use the Java classname if present
if ((attr = attrs.get(Obj.JAVA_ATTRIBUTES[Obj.CLASSNAME])) != null) {
className = (String)attr.get();
} else {
className = defaultClassName;
}
CompositeName cn = new CompositeName();
cn.add(getAtom(dn));
NameClassPair ncp;
if (respCtls != null) {
ncp = new NameClassPairWithControls(
cn.toString(), className,
homeCtx.convertControls(respCtls));
} else {
ncp = new NameClassPair(cn.toString(), className);
}
ncp.setNameInNamespace(dn);
return ncp;
}
@Override
protected AbstractLdapNamingEnumeration<? extends NameClassPair> getReferredResults(
LdapReferralContext refCtx) throws NamingException {
// repeat the original operation at the new context
return (AbstractLdapNamingEnumeration<? extends NameClassPair>)refCtx.list(listArg);
}
}

View File

@@ -0,0 +1,441 @@
/*
* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import java.io.PrintStream;
import java.io.OutputStream;
import java.util.Hashtable;
import java.util.Locale;
import java.util.StringTokenizer;
import javax.naming.ldap.Control;
import javax.naming.NamingException;
import javax.naming.CommunicationException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import com.sun.jndi.ldap.pool.PoolCleaner;
import com.sun.jndi.ldap.pool.Pool;
import sun.misc.InnocuousThread;
/**
* Contains utilities for managing connection pools of LdapClient.
* Contains method for
* - checking whether attempted connection creation may be pooled
* - creating a pooled connection
* - closing idle connections.
*
* If a timeout period has been configured, then it will automatically
* close and remove idle connections (those that have not been
* used for the duration of the timeout period).
*
* @author Rosanna Lee
*/
public final class LdapPoolManager {
private static final String DEBUG =
"com.sun.jndi.ldap.connect.pool.debug";
public static final boolean debug =
"all".equalsIgnoreCase(getProperty(DEBUG, null));
public static final boolean trace = debug ||
"fine".equalsIgnoreCase(getProperty(DEBUG, null));
// ---------- System properties for connection pooling
// Authentication mechanisms of connections that may be pooled
private static final String POOL_AUTH =
"com.sun.jndi.ldap.connect.pool.authentication";
// Protocol types of connections that may be pooled
private static final String POOL_PROTOCOL =
"com.sun.jndi.ldap.connect.pool.protocol";
// Maximum number of identical connections per pool
private static final String MAX_POOL_SIZE =
"com.sun.jndi.ldap.connect.pool.maxsize";
// Preferred number of identical connections per pool
private static final String PREF_POOL_SIZE =
"com.sun.jndi.ldap.connect.pool.prefsize";
// Initial number of identical connections per pool
private static final String INIT_POOL_SIZE =
"com.sun.jndi.ldap.connect.pool.initsize";
// Milliseconds to wait before closing idle connections
private static final String POOL_TIMEOUT =
"com.sun.jndi.ldap.connect.pool.timeout";
// Properties for DIGEST
private static final String SASL_CALLBACK =
"java.naming.security.sasl.callback";
// --------- Constants
private static final int DEFAULT_MAX_POOL_SIZE = 0;
private static final int DEFAULT_PREF_POOL_SIZE = 0;
private static final int DEFAULT_INIT_POOL_SIZE = 1;
private static final int DEFAULT_TIMEOUT = 0; // no timeout
private static final String DEFAULT_AUTH_MECHS = "none simple";
private static final String DEFAULT_PROTOCOLS = "plain";
private static final int NONE = 0; // indices into pools
private static final int SIMPLE = 1;
private static final int DIGEST = 2;
// --------- static fields
private static final long idleTimeout;// ms to wait before closing idle conn
private static final int maxSize; // max num of identical conns/pool
private static final int prefSize; // preferred num of identical conns/pool
private static final int initSize; // initial num of identical conns/pool
private static boolean supportPlainProtocol = false;
private static boolean supportSslProtocol = false;
// List of pools used for different auth types
private static final Pool[] pools = new Pool[3];
static {
maxSize = getInteger(MAX_POOL_SIZE, DEFAULT_MAX_POOL_SIZE);
prefSize = getInteger(PREF_POOL_SIZE, DEFAULT_PREF_POOL_SIZE);
initSize = getInteger(INIT_POOL_SIZE, DEFAULT_INIT_POOL_SIZE);
idleTimeout = getLong(POOL_TIMEOUT, DEFAULT_TIMEOUT);
// Determine supported authentication mechanisms
String str = getProperty(POOL_AUTH, DEFAULT_AUTH_MECHS);
StringTokenizer parser = new StringTokenizer(str);
int count = parser.countTokens();
String mech;
int p;
for (int i = 0; i < count; i++) {
mech = parser.nextToken().toLowerCase(Locale.ENGLISH);
if (mech.equals("anonymous")) {
mech = "none";
}
p = findPool(mech);
if (p >= 0 && pools[p] == null) {
pools[p] = new Pool(initSize, prefSize, maxSize);
}
}
// Determine supported protocols
str= getProperty(POOL_PROTOCOL, DEFAULT_PROTOCOLS);
parser = new StringTokenizer(str);
count = parser.countTokens();
String proto;
for (int i = 0; i < count; i++) {
proto = parser.nextToken();
if ("plain".equalsIgnoreCase(proto)) {
supportPlainProtocol = true;
} else if ("ssl".equalsIgnoreCase(proto)) {
supportSslProtocol = true;
} else {
// ignore
}
}
if (idleTimeout > 0) {
// Create cleaner to expire idle connections
PrivilegedAction<Void> pa = new PrivilegedAction<Void>() {
public Void run() {
Thread t = InnocuousThread.newSystemThread(
"LDAP PoolCleaner",
new PoolCleaner(idleTimeout, pools));
assert t.getContextClassLoader() == null;
t.setDaemon(true);
t.start();
return null;
}};
AccessController.doPrivileged(pa);
}
if (debug) {
showStats(System.err);
}
}
// Cannot instantiate one of these
private LdapPoolManager() {
}
/**
* Find the index of the pool for the specified mechanism. If not
* one of "none", "simple", "DIGEST-MD5", or "GSSAPI",
* return -1.
* @param mech mechanism type
*/
private static int findPool(String mech) {
if ("none".equalsIgnoreCase(mech)) {
return NONE;
} else if ("simple".equalsIgnoreCase(mech)) {
return SIMPLE;
} else if ("digest-md5".equalsIgnoreCase(mech)) {
return DIGEST;
}
return -1;
}
/**
* Determines whether pooling is allowed given information on how
* the connection will be used.
*
* Non-configurable rejections:
* - nonstandard socketFactory has been specified: the pool manager
* cannot track input or parameters used by the socket factory and
* thus has no way of determining whether two connection requests
* are equivalent. Maybe in the future it might add a list of allowed
* socket factories to be configured
* - trace enabled (except when debugging)
* - for Digest authentication, if a callback handler has been specified:
* the pool manager cannot track input collected by the handler
* and thus has no way of determining whether two connection requests are
* equivalent. Maybe in the future it might add a list of allowed
* callback handlers.
*
* Configurable tests:
* - Pooling for the requested protocol (plain or ssl) is supported
* - Pooling for the requested authentication mechanism is supported
*
*/
static boolean isPoolingAllowed(String socketFactory, OutputStream trace,
String authMech, String protocol, Hashtable<?,?> env)
throws NamingException {
if (trace != null && !debug
// Requesting plain protocol but it is not supported
|| (protocol == null && !supportPlainProtocol)
// Requesting ssl protocol but it is not supported
|| ("ssl".equalsIgnoreCase(protocol) && !supportSslProtocol)) {
d("Pooling disallowed due to tracing or unsupported pooling of protocol");
return false;
}
// pooling of custom socket factory is possible only if the
// socket factory interface implements java.util.comparator
String COMPARATOR = "java.util.Comparator";
boolean foundSockCmp = false;
if ((socketFactory != null) &&
!socketFactory.equals(LdapCtx.DEFAULT_SSL_FACTORY)) {
try {
Class<?> socketFactoryClass = Obj.helper.loadClass(socketFactory);
Class<?>[] interfaces = socketFactoryClass.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
if (interfaces[i].getCanonicalName().equals(COMPARATOR)) {
foundSockCmp = true;
}
}
} catch (Exception e) {
CommunicationException ce =
new CommunicationException("Loading the socket factory");
ce.setRootCause(e);
throw ce;
}
if (!foundSockCmp) {
return false;
}
}
// Cannot use pooling if authMech is not a supported mechs
// Cannot use pooling if authMech contains multiple mechs
int p = findPool(authMech);
if (p < 0 || pools[p] == null) {
d("authmech not found: ", authMech);
return false;
}
d("using authmech: ", authMech);
switch (p) {
case NONE:
case SIMPLE:
return true;
case DIGEST:
// Provider won't be able to determine connection identity
// if an alternate callback handler is used
return (env == null || env.get(SASL_CALLBACK) == null);
}
return false;
}
/**
* Obtains a pooled connection that either already exists or is
* newly created using the parameters supplied. If it is newly
* created, it needs to go through the authentication checks to
* determine whether an LDAP bind is necessary.
*
* Caller needs to invoke ldapClient.authenticateCalled() to
* determine whether ldapClient.authenticate() needs to be invoked.
* Caller has that responsibility because caller needs to deal
* with the LDAP bind response, which might involve referrals,
* response controls, errors, etc. This method is responsible only
* for establishing the connection.
*
* @return an LdapClient that is pooled.
*/
static LdapClient getLdapClient(String host, int port, String socketFactory,
int connTimeout, int readTimeout, OutputStream trace, int version,
String authMech, Control[] ctls, String protocol, String user,
Object passwd, Hashtable<?,?> env) throws NamingException {
// Create base identity for LdapClient
ClientId id = null;
Pool pool;
int p = findPool(authMech);
if (p < 0 || (pool=pools[p]) == null) {
throw new IllegalArgumentException(
"Attempting to use pooling for an unsupported mechanism: " +
authMech);
}
switch (p) {
case NONE:
id = new ClientId(version, host, port, protocol,
ctls, trace, socketFactory);
break;
case SIMPLE:
// Add identity information used in simple authentication
id = new SimpleClientId(version, host, port, protocol,
ctls, trace, socketFactory, user, passwd);
break;
case DIGEST:
// Add user/passwd/realm/authzid/qop/strength/maxbuf/mutual/policy*
id = new DigestClientId(version, host, port, protocol,
ctls, trace, socketFactory, user, passwd, env);
break;
}
return (LdapClient) pool.getPooledConnection(id, connTimeout,
new LdapClientFactory(host, port, socketFactory, connTimeout,
readTimeout, trace));
}
public static void showStats(PrintStream out) {
out.println("***** start *****");
out.println("idle timeout: " + idleTimeout);
out.println("maximum pool size: " + maxSize);
out.println("preferred pool size: " + prefSize);
out.println("initial pool size: " + initSize);
out.println("protocol types: " + (supportPlainProtocol ? "plain " : "") +
(supportSslProtocol ? "ssl" : ""));
out.println("authentication types: " +
(pools[NONE] != null ? "none " : "") +
(pools[SIMPLE] != null ? "simple " : "") +
(pools[DIGEST] != null ? "DIGEST-MD5 " : ""));
for (int i = 0; i < pools.length; i++) {
if (pools[i] != null) {
out.println(
(i == NONE ? "anonymous pools" :
i == SIMPLE ? "simple auth pools" :
i == DIGEST ? "digest pools" : "")
+ ":");
pools[i].showStats(out);
}
}
out.println("***** end *****");
}
/**
* Closes idle connections idle since specified time.
*
* @param threshold Close connections idle since this time, as
* specified in milliseconds since "the epoch".
* @see java.util.Date
*/
public static void expire(long threshold) {
for (int i = 0; i < pools.length; i++) {
if (pools[i] != null) {
pools[i].expire(threshold);
}
}
}
private static void d(String msg) {
if (debug) {
System.err.println("LdapPoolManager: " + msg);
}
}
private static void d(String msg, String o) {
if (debug) {
System.err.println("LdapPoolManager: " + msg + o);
}
}
private static final String getProperty(final String propName,
final String defVal) {
return AccessController.doPrivileged(
new PrivilegedAction<String>() {
public String run() {
try {
return System.getProperty(propName, defVal);
} catch (SecurityException e) {
return defVal;
}
}
});
}
private static final int getInteger(final String propName,
final int defVal) {
Integer val = AccessController.doPrivileged(
new PrivilegedAction<Integer>() {
public Integer run() {
try {
return Integer.getInteger(propName, defVal);
} catch (SecurityException e) {
return new Integer(defVal);
}
}
});
return val.intValue();
}
private static final long getLong(final String propName,
final long defVal) {
Long val = AccessController.doPrivileged(
new PrivilegedAction<Long>() {
public Long run() {
try {
return Long.getLong(propName, defVal);
} catch (SecurityException e) {
return new Long(defVal);
}
}
});
return val.longValue();
}
}

View File

@@ -0,0 +1,953 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.spi.*;
import javax.naming.ldap.*;
import java.util.Hashtable;
import java.util.StringTokenizer;
import com.sun.jndi.toolkit.dir.SearchFilter;
/**
* A context for handling referrals.
*
* @author Vincent Ryan
*/
final class LdapReferralContext implements DirContext, LdapContext {
private DirContext refCtx = null;
private Name urlName = null; // override the supplied name
private String urlAttrs = null; // override attributes
private String urlScope = null; // override scope
private String urlFilter = null; // override filter
private LdapReferralException refEx = null;
private boolean skipThisReferral = false;
private int hopCount = 1;
private NamingException previousEx = null;
@SuppressWarnings("unchecked") // clone()
LdapReferralContext(LdapReferralException ex,
Hashtable<?,?> env,
Control[] connCtls,
Control[] reqCtls,
String nextName,
boolean skipThisReferral,
int handleReferrals) throws NamingException {
refEx = ex;
if (this.skipThisReferral = skipThisReferral) {
return; // don't create a DirContext for this referral
}
String referral;
// Make copies of environment and connect controls for our own use.
if (env != null) {
env = (Hashtable<?,?>) env.clone();
// Remove old connect controls from environment, unless we have new
// ones that will override them anyway.
if (connCtls == null) {
env.remove(LdapCtx.BIND_CONTROLS);
}
} else if (connCtls != null) {
env = new Hashtable<String, Control[]>(5);
}
if (connCtls != null) {
Control[] copiedCtls = new Control[connCtls.length];
System.arraycopy(connCtls, 0, copiedCtls, 0, connCtls.length);
// Add copied controls to environment, replacing any old ones.
((Hashtable<? super String, ? super Control[]>)env)
.put(LdapCtx.BIND_CONTROLS, copiedCtls);
}
while (true) {
try {
referral = refEx.getNextReferral();
if (referral == null) {
if (previousEx != null) {
throw (NamingException)(previousEx.fillInStackTrace());
} else {
throw new NamingException(
"Illegal encoding: referral is empty");
}
}
} catch (LdapReferralException e) {
if (handleReferrals == LdapClient.LDAP_REF_THROW) {
throw e;
} else {
refEx = e;
continue;
}
}
// Create a Reference containing the referral URL.
Reference ref = new Reference("javax.naming.directory.DirContext",
new StringRefAddr("URL", referral));
Object obj;
try {
obj = NamingManager.getObjectInstance(ref, null, null, env);
} catch (NamingException e) {
if (handleReferrals == LdapClient.LDAP_REF_THROW) {
throw e;
}
// mask the exception and save it for later
previousEx = e;
// follow another referral
continue;
} catch (Exception e) {
NamingException e2 =
new NamingException(
"problem generating object using object factory");
e2.setRootCause(e);
throw e2;
}
if (obj instanceof DirContext) {
refCtx = (DirContext)obj;
if (refCtx instanceof LdapContext && reqCtls != null) {
((LdapContext)refCtx).setRequestControls(reqCtls);
}
initDefaults(referral, nextName);
break;
} else {
NamingException ne = new NotContextException(
"Cannot create context for: " + referral);
ne.setRemainingName((new CompositeName()).add(nextName));
throw ne;
}
}
}
private void initDefaults(String referral, String nextName)
throws NamingException {
String urlString;
try {
// parse URL
LdapURL url = new LdapURL(referral);
urlString = url.getDN();
urlAttrs = url.getAttributes();
urlScope = url.getScope();
urlFilter = url.getFilter();
} catch (NamingException e) {
// Not an LDAP URL; use original URL
urlString = referral;
urlAttrs = urlScope = urlFilter = null;
}
// reuse original name if URL DN is absent
if (urlString == null) {
urlString = nextName;
} else {
// concatenate with remaining name if URL DN is present
urlString = "";
}
if (urlString == null) {
urlName = null;
} else {
urlName = urlString.equals("") ? new CompositeName() :
new CompositeName().add(urlString);
}
}
public void close() throws NamingException {
if (refCtx != null) {
refCtx.close();
refCtx = null;
}
refEx = null;
}
void setHopCount(int hopCount) {
this.hopCount = hopCount;
if ((refCtx != null) && (refCtx instanceof LdapCtx)) {
((LdapCtx)refCtx).setHopCount(hopCount);
}
}
public Object lookup(String name) throws NamingException {
return lookup(toName(name));
}
public Object lookup(Name name) throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
return refCtx.lookup(overrideName(name));
}
public void bind(String name, Object obj) throws NamingException {
bind(toName(name), obj);
}
public void bind(Name name, Object obj) throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
refCtx.bind(overrideName(name), obj);
}
public void rebind(String name, Object obj) throws NamingException {
rebind(toName(name), obj);
}
public void rebind(Name name, Object obj) throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
refCtx.rebind(overrideName(name), obj);
}
public void unbind(String name) throws NamingException {
unbind(toName(name));
}
public void unbind(Name name) throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
refCtx.unbind(overrideName(name));
}
public void rename(String oldName, String newName) throws NamingException {
rename(toName(oldName), toName(newName));
}
public void rename(Name oldName, Name newName) throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
refCtx.rename(overrideName(oldName), toName(refEx.getNewRdn()));
}
public NamingEnumeration<NameClassPair> list(String name) throws NamingException {
return list(toName(name));
}
@SuppressWarnings("unchecked")
public NamingEnumeration<NameClassPair> list(Name name) throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
try {
NamingEnumeration<NameClassPair> ne = null;
if (urlScope != null && urlScope.equals("base")) {
SearchControls cons = new SearchControls();
cons.setReturningObjFlag(true);
cons.setSearchScope(SearchControls.OBJECT_SCOPE);
ne = (NamingEnumeration)
refCtx.search(overrideName(name), "(objectclass=*)", cons);
} else {
ne = refCtx.list(overrideName(name));
}
refEx.setNameResolved(true);
// append (referrals from) the exception that generated this
// context to the new search results, so that referral processing
// can continue
((ReferralEnumeration)ne).appendUnprocessedReferrals(refEx);
return (ne);
} catch (LdapReferralException e) {
// append (referrals from) the exception that generated this
// context to the new exception, so that referral processing
// can continue
e.appendUnprocessedReferrals(refEx);
throw (NamingException)(e.fillInStackTrace());
} catch (NamingException e) {
// record the exception if there are no remaining referrals
if ((refEx != null) && (! refEx.hasMoreReferrals())) {
refEx.setNamingException(e);
}
if ((refEx != null) &&
(refEx.hasMoreReferrals() ||
refEx.hasMoreReferralExceptions())) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
} else {
throw e;
}
}
}
public NamingEnumeration<Binding> listBindings(String name) throws
NamingException {
return listBindings(toName(name));
}
@SuppressWarnings("unchecked")
public NamingEnumeration<Binding> listBindings(Name name) throws
NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
try {
NamingEnumeration<Binding> be = null;
if (urlScope != null && urlScope.equals("base")) {
SearchControls cons = new SearchControls();
cons.setReturningObjFlag(true);
cons.setSearchScope(SearchControls.OBJECT_SCOPE);
be = (NamingEnumeration)refCtx.search(overrideName(name),
"(objectclass=*)", cons);
} else {
be = refCtx.listBindings(overrideName(name));
}
refEx.setNameResolved(true);
// append (referrals from) the exception that generated this
// context to the new search results, so that referral processing
// can continue
((ReferralEnumeration<Binding>)be).appendUnprocessedReferrals(refEx);
return (be);
} catch (LdapReferralException e) {
// append (referrals from) the exception that generated this
// context to the new exception, so that referral processing
// can continue
e.appendUnprocessedReferrals(refEx);
throw (NamingException)(e.fillInStackTrace());
} catch (NamingException e) {
// record the exception if there are no remaining referrals
if ((refEx != null) && (! refEx.hasMoreReferrals())) {
refEx.setNamingException(e);
}
if ((refEx != null) &&
(refEx.hasMoreReferrals() ||
refEx.hasMoreReferralExceptions())) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
} else {
throw e;
}
}
}
public void destroySubcontext(String name) throws NamingException {
destroySubcontext(toName(name));
}
public void destroySubcontext(Name name) throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
refCtx.destroySubcontext(overrideName(name));
}
public Context createSubcontext(String name) throws NamingException {
return createSubcontext(toName(name));
}
public Context createSubcontext(Name name) throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
return refCtx.createSubcontext(overrideName(name));
}
public Object lookupLink(String name) throws NamingException {
return lookupLink(toName(name));
}
public Object lookupLink(Name name) throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
return refCtx.lookupLink(overrideName(name));
}
public NameParser getNameParser(String name) throws NamingException {
return getNameParser(toName(name));
}
public NameParser getNameParser(Name name) throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
return refCtx.getNameParser(overrideName(name));
}
public String composeName(String name, String prefix)
throws NamingException {
return composeName(toName(name), toName(prefix)).toString();
}
public Name composeName(Name name, Name prefix) throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
return refCtx.composeName(name, prefix);
}
public Object addToEnvironment(String propName, Object propVal)
throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
return refCtx.addToEnvironment(propName, propVal);
}
public Object removeFromEnvironment(String propName)
throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
return refCtx.removeFromEnvironment(propName);
}
public Hashtable<?,?> getEnvironment() throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
return refCtx.getEnvironment();
}
public Attributes getAttributes(String name) throws NamingException {
return getAttributes(toName(name));
}
public Attributes getAttributes(Name name) throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
return refCtx.getAttributes(overrideName(name));
}
public Attributes getAttributes(String name, String[] attrIds)
throws NamingException {
return getAttributes(toName(name), attrIds);
}
public Attributes getAttributes(Name name, String[] attrIds)
throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
return refCtx.getAttributes(overrideName(name), attrIds);
}
public void modifyAttributes(String name, int mod_op, Attributes attrs)
throws NamingException {
modifyAttributes(toName(name), mod_op, attrs);
}
public void modifyAttributes(Name name, int mod_op, Attributes attrs)
throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
refCtx.modifyAttributes(overrideName(name), mod_op, attrs);
}
public void modifyAttributes(String name, ModificationItem[] mods)
throws NamingException {
modifyAttributes(toName(name), mods);
}
public void modifyAttributes(Name name, ModificationItem[] mods)
throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
refCtx.modifyAttributes(overrideName(name), mods);
}
public void bind(String name, Object obj, Attributes attrs)
throws NamingException {
bind(toName(name), obj, attrs);
}
public void bind(Name name, Object obj, Attributes attrs)
throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
refCtx.bind(overrideName(name), obj, attrs);
}
public void rebind(String name, Object obj, Attributes attrs)
throws NamingException {
rebind(toName(name), obj, attrs);
}
public void rebind(Name name, Object obj, Attributes attrs)
throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
refCtx.rebind(overrideName(name), obj, attrs);
}
public DirContext createSubcontext(String name, Attributes attrs)
throws NamingException {
return createSubcontext(toName(name), attrs);
}
public DirContext createSubcontext(Name name, Attributes attrs)
throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
return refCtx.createSubcontext(overrideName(name), attrs);
}
public DirContext getSchema(String name) throws NamingException {
return getSchema(toName(name));
}
public DirContext getSchema(Name name) throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
return refCtx.getSchema(overrideName(name));
}
public DirContext getSchemaClassDefinition(String name)
throws NamingException {
return getSchemaClassDefinition(toName(name));
}
public DirContext getSchemaClassDefinition(Name name)
throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
return refCtx.getSchemaClassDefinition(overrideName(name));
}
public NamingEnumeration<SearchResult> search(String name,
Attributes matchingAttributes)
throws NamingException {
return search(toName(name), SearchFilter.format(matchingAttributes),
new SearchControls());
}
public NamingEnumeration<SearchResult> search(Name name,
Attributes matchingAttributes)
throws NamingException {
return search(name, SearchFilter.format(matchingAttributes),
new SearchControls());
}
public NamingEnumeration<SearchResult> search(String name,
Attributes matchingAttributes,
String[] attributesToReturn)
throws NamingException {
SearchControls cons = new SearchControls();
cons.setReturningAttributes(attributesToReturn);
return search(toName(name), SearchFilter.format(matchingAttributes),
cons);
}
public NamingEnumeration<SearchResult> search(Name name,
Attributes matchingAttributes,
String[] attributesToReturn)
throws NamingException {
SearchControls cons = new SearchControls();
cons.setReturningAttributes(attributesToReturn);
return search(name, SearchFilter.format(matchingAttributes), cons);
}
public NamingEnumeration<SearchResult> search(String name,
String filter,
SearchControls cons)
throws NamingException {
return search(toName(name), filter, cons);
}
public NamingEnumeration<SearchResult> search(Name name,
String filter,
SearchControls cons) throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
try {
NamingEnumeration<SearchResult> se =
refCtx.search(overrideName(name),
overrideFilter(filter),
overrideAttributesAndScope(cons));
refEx.setNameResolved(true);
// append (referrals from) the exception that generated this
// context to the new search results, so that referral processing
// can continue
((ReferralEnumeration)se).appendUnprocessedReferrals(refEx);
return (se);
} catch (LdapReferralException e) {
// %%% VR - setNameResolved(true);
// append (referrals from) the exception that generated this
// context to the new exception, so that referral processing
// can continue
e.appendUnprocessedReferrals(refEx);
throw (NamingException)(e.fillInStackTrace());
} catch (NamingException e) {
// record the exception if there are no remaining referrals
if ((refEx != null) && (! refEx.hasMoreReferrals())) {
refEx.setNamingException(e);
}
if ((refEx != null) &&
(refEx.hasMoreReferrals() ||
refEx.hasMoreReferralExceptions())) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
} else {
throw e;
}
}
}
public NamingEnumeration<SearchResult> search(String name,
String filterExpr,
Object[] filterArgs,
SearchControls cons)
throws NamingException {
return search(toName(name), filterExpr, filterArgs, cons);
}
public NamingEnumeration<SearchResult> search(Name name,
String filterExpr,
Object[] filterArgs,
SearchControls cons) throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
try {
NamingEnumeration<SearchResult> se;
if (urlFilter != null) {
se = refCtx.search(overrideName(name), urlFilter,
overrideAttributesAndScope(cons));
} else {
se = refCtx.search(overrideName(name), filterExpr,
filterArgs, overrideAttributesAndScope(cons));
}
refEx.setNameResolved(true);
// append (referrals from) the exception that generated this
// context to the new search results, so that referral processing
// can continue
((ReferralEnumeration)se).appendUnprocessedReferrals(refEx);
return (se);
} catch (LdapReferralException e) {
// append (referrals from) the exception that generated this
// context to the new exception, so that referral processing
// can continue
e.appendUnprocessedReferrals(refEx);
throw (NamingException)(e.fillInStackTrace());
} catch (NamingException e) {
// record the exception if there are no remaining referrals
if ((refEx != null) && (! refEx.hasMoreReferrals())) {
refEx.setNamingException(e);
}
if ((refEx != null) &&
(refEx.hasMoreReferrals() ||
refEx.hasMoreReferralExceptions())) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
} else {
throw e;
}
}
}
public String getNameInNamespace() throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
return urlName != null && !urlName.isEmpty() ? urlName.get(0) : "";
}
// ---------------------- LdapContext ---------------------
public ExtendedResponse extendedOperation(ExtendedRequest request)
throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
if (!(refCtx instanceof LdapContext)) {
throw new NotContextException(
"Referral context not an instance of LdapContext");
}
return ((LdapContext)refCtx).extendedOperation(request);
}
public LdapContext newInstance(Control[] requestControls)
throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
if (!(refCtx instanceof LdapContext)) {
throw new NotContextException(
"Referral context not an instance of LdapContext");
}
return ((LdapContext)refCtx).newInstance(requestControls);
}
public void reconnect(Control[] connCtls) throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
if (!(refCtx instanceof LdapContext)) {
throw new NotContextException(
"Referral context not an instance of LdapContext");
}
((LdapContext)refCtx).reconnect(connCtls);
}
public Control[] getConnectControls() throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
if (!(refCtx instanceof LdapContext)) {
throw new NotContextException(
"Referral context not an instance of LdapContext");
}
return ((LdapContext)refCtx).getConnectControls();
}
public void setRequestControls(Control[] requestControls)
throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
if (!(refCtx instanceof LdapContext)) {
throw new NotContextException(
"Referral context not an instance of LdapContext");
}
((LdapContext)refCtx).setRequestControls(requestControls);
}
public Control[] getRequestControls() throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
if (!(refCtx instanceof LdapContext)) {
throw new NotContextException(
"Referral context not an instance of LdapContext");
}
return ((LdapContext)refCtx).getRequestControls();
}
public Control[] getResponseControls() throws NamingException {
if (skipThisReferral) {
throw (NamingException)
((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
}
if (!(refCtx instanceof LdapContext)) {
throw new NotContextException(
"Referral context not an instance of LdapContext");
}
return ((LdapContext)refCtx).getResponseControls();
}
// ---------------------- Private methods ---------------------
private Name toName(String name) throws InvalidNameException {
return name.equals("") ? new CompositeName() :
new CompositeName().add(name);
}
/*
* Use the DN component from the LDAP URL (if present) to override the
* supplied DN.
*/
private Name overrideName(Name name) throws InvalidNameException {
return (urlName == null ? name : urlName);
}
/*
* Use the attributes and scope components from the LDAP URL (if present)
* to override the corrpesonding components supplied in SearchControls.
*/
private SearchControls overrideAttributesAndScope(SearchControls cons) {
SearchControls urlCons;
if ((urlScope != null) || (urlAttrs != null)) {
urlCons = new SearchControls(cons.getSearchScope(),
cons.getCountLimit(),
cons.getTimeLimit(),
cons.getReturningAttributes(),
cons.getReturningObjFlag(),
cons.getDerefLinkFlag());
if (urlScope != null) {
if (urlScope.equals("base")) {
urlCons.setSearchScope(SearchControls.OBJECT_SCOPE);
} else if (urlScope.equals("one")) {
urlCons.setSearchScope(SearchControls.ONELEVEL_SCOPE);
} else if (urlScope.equals("sub")) {
urlCons.setSearchScope(SearchControls.SUBTREE_SCOPE);
}
}
if (urlAttrs != null) {
StringTokenizer tokens = new StringTokenizer(urlAttrs, ",");
int count = tokens.countTokens();
String[] attrs = new String[count];
for (int i = 0; i < count; i ++) {
attrs[i] = tokens.nextToken();
}
urlCons.setReturningAttributes(attrs);
}
return urlCons;
} else {
return cons;
}
}
/*
* Use the filter component from the LDAP URL (if present) to override the
* supplied filter.
*/
private String overrideFilter(String filter) {
return (urlFilter == null ? filter : urlFilter);
}
}

View File

@@ -0,0 +1,434 @@
/*
* Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import javax.naming.*;
import javax.naming.ldap.Control;
import java.util.Hashtable;
import java.util.Vector;
/**
* This exception is raised when a referral to an alternative context
* is encountered.
* <p>
* An <tt>LdapReferralException</tt> object contains one or more referrals.
* Each referral is an alternative location for the same target entry.
* For example, a referral may be an LDAP URL.
* The referrals are attempted in sequence until one is successful or
* all have failed. In the case of the latter then the exception generated
* by the final referral is recorded and presented later.
* <p>
* A referral may be skipped or may be retried. For example, in the case
* of an authentication error, a referral may be retried with different
* environment properties.
* <p>
* An <tt>LdapReferralException</tt> object may also contain a reference
* to a chain of unprocessed <tt>LdapReferralException</tt> objects.
* Once the current set of referrals have been exhausted and unprocessed
* <tt>LdapReferralException</tt> objects remain, then the
* <tt>LdapReferralException</tt> object referenced by the current
* object is thrown and the cycle continues.
* <p>
* If new <tt>LdapReferralException</tt> objects are generated while
* following an existing referral then these new objects are appended
* to the end of the chain of unprocessed <tt>LdapReferralException</tt>
* objects.
* <p>
* If an exception was recorded while processing a chain of
* <tt>LdapReferralException</tt> objects then is is throw once
* processing has completed.
*
* @author Vincent Ryan
*/
final public class LdapReferralException extends
javax.naming.ldap.LdapReferralException {
private static final long serialVersionUID = 627059076356906399L;
// ----------- fields initialized in constructor ---------------
private int handleReferrals;
private Hashtable<?,?> envprops;
private String nextName;
private Control[] reqCtls;
// ----------- fields that have defaults -----------------------
private Vector<?> referrals = null; // alternatives,set by setReferralInfo()
private int referralIndex = 0; // index into referrals
private int referralCount = 0; // count of referrals
private boolean foundEntry = false; // will stop when entry is found
private boolean skipThisReferral = false;
private int hopCount = 1;
private NamingException errorEx = null;
private String newRdn = null;
private boolean debug = false;
LdapReferralException nextReferralEx = null; // referral ex. chain
/**
* Constructs a new instance of LdapReferralException.
* @param resolvedName The part of the name that has been successfully
* resolved.
* @param resolvedObj The object to which resolution was successful.
* @param remainingName The remaining unresolved portion of the name.
* @param explanation Additional detail about this exception.
*/
LdapReferralException(Name resolvedName,
Object resolvedObj,
Name remainingName,
String explanation,
Hashtable<?,?> envprops,
String nextName,
int handleReferrals,
Control[] reqCtls) {
super(explanation);
if (debug)
System.out.println("LdapReferralException constructor");
setResolvedName(resolvedName);
setResolvedObj(resolvedObj);
setRemainingName(remainingName);
this.envprops = envprops;
this.nextName = nextName;
this.handleReferrals = handleReferrals;
// If following referral, request controls are passed to referral ctx
this.reqCtls =
(handleReferrals == LdapClient.LDAP_REF_FOLLOW ||
handleReferrals == LdapClient.LDAP_REF_FOLLOW_SCHEME ? reqCtls : null);
}
/**
* Gets a context at which to continue processing.
* The current environment properties are re-used.
*/
public Context getReferralContext() throws NamingException {
return getReferralContext(envprops, null);
}
/**
* Gets a context at which to continue processing.
* The supplied environment properties are used.
*/
public Context getReferralContext(Hashtable<?,?> newProps) throws
NamingException {
return getReferralContext(newProps, null);
}
/**
* Gets a context at which to continue processing.
* The supplied environment properties and connection controls are used.
*/
public Context getReferralContext(Hashtable<?,?> newProps, Control[] connCtls)
throws NamingException {
if (debug)
System.out.println("LdapReferralException.getReferralContext");
LdapReferralContext refCtx = new LdapReferralContext(
this, newProps, connCtls, reqCtls,
nextName, skipThisReferral, handleReferrals);
refCtx.setHopCount(hopCount + 1);
if (skipThisReferral) {
skipThisReferral = false; // reset
}
return (Context)refCtx;
}
/**
* Gets referral information.
*/
public Object getReferralInfo() {
if (debug) {
System.out.println("LdapReferralException.getReferralInfo");
System.out.println(" referralIndex=" + referralIndex);
}
if (hasMoreReferrals()) {
return referrals.elementAt(referralIndex);
} else {
return null;
}
}
/**
* Marks the current referral as one to be retried.
*/
public void retryReferral() {
if (debug)
System.out.println("LdapReferralException.retryReferral");
if (referralIndex > 0)
referralIndex--; // decrement index
}
/**
* Marks the current referral as one to be ignored.
* Returns false when there are no referrals remaining to be processed.
*/
public boolean skipReferral() {
if (debug)
System.out.println("LdapReferralException.skipReferral");
skipThisReferral = true;
// advance to next referral
try {
getNextReferral();
} catch (ReferralException e) {
// mask the referral exception
}
return (hasMoreReferrals() || hasMoreReferralExceptions());
}
/**
* Sets referral information.
*/
void setReferralInfo(Vector<?> referrals, boolean continuationRef) {
// %%% continuationRef is currently ignored
if (debug)
System.out.println("LdapReferralException.setReferralInfo");
this.referrals = referrals;
referralCount = (referrals == null) ? 0 : referrals.size();
if (debug) {
if (referrals != null) {
for (int i = 0; i < referralCount; i++) {
System.out.println(" [" + i + "] " + referrals.elementAt(i));
}
} else {
System.out.println("setReferralInfo : referrals == null");
}
}
}
/**
* Gets the next referral. When the current set of referrals have
* been exhausted then the next referral exception is thrown, if available.
*/
String getNextReferral() throws ReferralException {
if (debug)
System.out.println("LdapReferralException.getNextReferral");
if (hasMoreReferrals()) {
return (String)referrals.elementAt(referralIndex++);
} else if (hasMoreReferralExceptions()) {
throw nextReferralEx;
} else {
return null;
}
}
/**
* Appends the supplied (chain of) referral exception onto the end of
* the current (chain of) referral exception. Spent referral exceptions
* are trimmed off.
*/
LdapReferralException
appendUnprocessedReferrals(LdapReferralException back) {
if (debug) {
System.out.println(
"LdapReferralException.appendUnprocessedReferrals");
dump();
if (back != null) {
back.dump();
}
}
LdapReferralException front = this;
if (! front.hasMoreReferrals()) {
front = nextReferralEx; // trim
if ((errorEx != null) && (front != null)) {
front.setNamingException(errorEx); //advance the saved exception
}
}
// don't append onto itself
if (this == back) {
return front;
}
if ((back != null) && (! back.hasMoreReferrals())) {
back = back.nextReferralEx; // trim
}
if (back == null) {
return front;
}
// Locate the end of the current chain
LdapReferralException ptr = front;
while (ptr.nextReferralEx != null) {
ptr = ptr.nextReferralEx;
}
ptr.nextReferralEx = back; // append
return front;
}
/**
* Tests if there are any referrals remaining to be processed.
* If name resolution has already completed then any remaining
* referrals (in the current referral exception) will be ignored.
*/
boolean hasMoreReferrals() {
if (debug)
System.out.println("LdapReferralException.hasMoreReferrals");
return (! foundEntry) && (referralIndex < referralCount);
}
/**
* Tests if there are any referral exceptions remaining to be processed.
*/
boolean hasMoreReferralExceptions() {
if (debug)
System.out.println(
"LdapReferralException.hasMoreReferralExceptions");
return (nextReferralEx != null);
}
/**
* Sets the counter which records the number of hops that result
* from following a sequence of referrals.
*/
void setHopCount(int hopCount) {
if (debug)
System.out.println("LdapReferralException.setHopCount");
this.hopCount = hopCount;
}
/**
* Sets the flag to indicate that the target name has been resolved.
*/
void setNameResolved(boolean resolved) {
if (debug)
System.out.println("LdapReferralException.setNameResolved");
foundEntry = resolved;
}
/**
* Sets the exception generated while processing a referral.
* Only the first exception is recorded.
*/
void setNamingException(NamingException e) {
if (debug)
System.out.println("LdapReferralException.setNamingException");
if (errorEx == null) {
e.setRootCause(this); //record the referral exception that caused it
errorEx = e;
}
}
/**
* Gets the new RDN name.
*/
String getNewRdn() {
if (debug)
System.out.println("LdapReferralException.getNewRdn");
return newRdn;
}
/**
* Sets the new RDN name so that the rename operation can be completed
* (when a referral is being followed).
*/
void setNewRdn(String newRdn) {
if (debug)
System.out.println("LdapReferralException.setNewRdn");
this.newRdn = newRdn;
}
/**
* Gets the exception generated while processing a referral.
*/
NamingException getNamingException() {
if (debug)
System.out.println("LdapReferralException.getNamingException");
return errorEx;
}
/**
* Display the state of each element in a chain of LdapReferralException
* objects.
*/
void dump() {
System.out.println();
System.out.println("LdapReferralException.dump");
LdapReferralException ptr = this;
while (ptr != null) {
ptr.dumpState();
ptr = ptr.nextReferralEx;
}
}
/**
* Display the state of this LdapReferralException object.
*/
private void dumpState() {
System.out.println("LdapReferralException.dumpState");
System.out.println(" hashCode=" + hashCode());
System.out.println(" foundEntry=" + foundEntry);
System.out.println(" skipThisReferral=" + skipThisReferral);
System.out.println(" referralIndex=" + referralIndex);
if (referrals != null) {
System.out.println(" referrals:");
for (int i = 0; i < referralCount; i++) {
System.out.println(" [" + i + "] " + referrals.elementAt(i));
}
} else {
System.out.println(" referrals=null");
}
System.out.println(" errorEx=" + errorEx);
if (nextReferralEx == null) {
System.out.println(" nextRefEx=null");
} else {
System.out.println(" nextRefEx=" + nextReferralEx.hashCode());
}
System.out.println();
}
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import java.io.IOException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import javax.naming.CommunicationException;
import javax.naming.NamingException;
import java.util.concurrent.TimeUnit;
final class LdapRequest {
private final static BerDecoder EOF = new BerDecoder(new byte[]{}, -1, 0);
private final static String CLOSE_MSG = "LDAP connection has been closed";
private final static String TIMEOUT_MSG_FMT = "LDAP response read timed out, timeout used: %d ms.";
LdapRequest next; // Set/read in synchronized Connection methods
final int msgId; // read-only
private final BlockingQueue<BerDecoder> replies;
private volatile boolean cancelled;
private volatile boolean closed;
private volatile boolean completed;
private final boolean pauseAfterReceipt;
LdapRequest(int msgId, boolean pause, int replyQueueCapacity) {
this.msgId = msgId;
this.pauseAfterReceipt = pause;
if (replyQueueCapacity == -1) {
this.replies = new LinkedBlockingQueue<>();
} else {
this.replies = new LinkedBlockingQueue<>(8 * replyQueueCapacity / 10);
}
}
void cancel() {
cancelled = true;
replies.offer(EOF);
}
synchronized void close() {
closed = true;
replies.offer(EOF);
}
private boolean isClosed() {
return closed && (replies.size() == 0 || replies.peek() == EOF);
}
synchronized boolean addReplyBer(BerDecoder ber) {
// check the closed boolean value here as we don't want anything
// to be added to the queue after close() has been called.
if (cancelled || closed) {
return false;
}
// peek at the BER buffer to check if it is a SearchResultDone PDU
try {
ber.parseSeq(null);
ber.parseInt();
completed = (ber.peekByte() == LdapClient.LDAP_REP_RESULT);
} catch (IOException e) {
// ignore
}
ber.reset();
// Add a new reply to the queue of unprocessed replies.
try {
replies.put(ber);
} catch (InterruptedException e) {
// ignore
}
return pauseAfterReceipt;
}
/**
* Read reply BER
* @param millis timeout, infinite if the value is negative
* @return BerDecoder if reply was read successfully
* @throws CommunicationException request has been canceled and request does not need to be abandoned
* @throws NamingException request has been closed or timed out. Request does need to be abandoned
* @throws InterruptedException LDAP operation has been interrupted
*/
BerDecoder getReplyBer(long millis) throws NamingException,
InterruptedException {
if (cancelled) {
throw new CommunicationException("Request: " + msgId +
" cancelled");
}
if (isClosed()) {
throw new NamingException(CLOSE_MSG);
}
BerDecoder result = millis > 0 ?
replies.poll(millis, TimeUnit.MILLISECONDS) : replies.take();
if (cancelled) {
throw new CommunicationException("Request: " + msgId +
" cancelled");
}
// poll from 'replies' blocking queue ended-up with timeout
if (result == null) {
throw new NamingException(String.format(TIMEOUT_MSG_FMT, millis));
}
// Unexpected EOF can be caused by connection closure or cancellation
if (result == EOF) {
throw new NamingException(CLOSE_MSG);
}
return result;
}
boolean hasSearchCompleted() {
return completed;
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import java.util.Vector;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttributes;
import javax.naming.ldap.Control;
/**
* %%% public for use by LdapSasl %%%
*/
public final class LdapResult {
int msgId;
public int status; // %%% public for use by LdapSasl
String matchedDN;
String errorMessage;
// Vector<String | Vector<String>>
Vector<Vector<String>> referrals = null;
LdapReferralException refEx = null;
Vector<LdapEntry> entries = null;
Vector<Control> resControls = null;
public byte[] serverCreds = null; // %%% public for use by LdapSasl
String extensionId = null; // string OID
byte[] extensionValue = null; // BER OCTET STRING
// This function turns an LdapResult that came from a compare operation
// into one that looks like it came from a search operation. This is
// useful when the caller asked the context to do a search, but it was
// carried out as a compare. In this case, the client still expects a
// result that looks like it came from a search.
boolean compareToSearchResult(String name) {
boolean successful = false;
switch (status) {
case LdapClient.LDAP_COMPARE_TRUE:
status = LdapClient.LDAP_SUCCESS;
entries = new Vector<>(1,1);
Attributes attrs = new BasicAttributes(LdapClient.caseIgnore);
LdapEntry entry = new LdapEntry( name, attrs );
entries.addElement(entry);
successful = true;
break;
case LdapClient.LDAP_COMPARE_FALSE:
status = LdapClient.LDAP_SUCCESS;
entries = new Vector<>(0);
successful = true;
break;
default:
successful = false;
break;
}
return successful;
}
}

View File

@@ -0,0 +1,437 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import javax.naming.*;
import javax.naming.directory.*;
import java.util.Hashtable;
import com.sun.jndi.toolkit.dir.HierMemDirCtx;
/**
* This is the class used to implement LDAP's GetSchema call.
*
* It subclasses HierMemDirContext for most of the functionality. It
* overrides functions that cause the schema definitions to change.
* In such a case, it write the schema to the LdapServer and (assuming
* there are no errors), calls it's superclass's equivalent function.
* Thus, the schema tree and the LDAP server's schema attributes are
* always in sync.
*/
final class LdapSchemaCtx extends HierMemDirCtx {
static private final boolean debug = false;
private static final int LEAF = 0; // schema object (e.g. attribute type defn)
private static final int SCHEMA_ROOT = 1; // schema tree root
static final int OBJECTCLASS_ROOT = 2; // root of object class subtree
static final int ATTRIBUTE_ROOT = 3; // root of attribute type subtree
static final int SYNTAX_ROOT = 4; // root of syntax subtree
static final int MATCHRULE_ROOT = 5; // root of matching rule subtree
static final int OBJECTCLASS = 6; // an object class definition
static final int ATTRIBUTE = 7; // an attribute type definition
static final int SYNTAX = 8; // a syntax definition
static final int MATCHRULE = 9; // a matching rule definition
private SchemaInfo info= null;
private boolean setupMode = true;
private int objectType;
static DirContext createSchemaTree(Hashtable<String,Object> env,
String subschemasubentry, LdapCtx schemaEntry,
Attributes schemaAttrs, boolean netscapeBug)
throws NamingException {
try {
LdapSchemaParser parser = new LdapSchemaParser(netscapeBug);
SchemaInfo allinfo = new SchemaInfo(subschemasubentry,
schemaEntry, parser);
LdapSchemaCtx root = new LdapSchemaCtx(SCHEMA_ROOT, env, allinfo);
LdapSchemaParser.LDAP2JNDISchema(schemaAttrs, root);
return root;
} catch (NamingException e) {
schemaEntry.close(); // cleanup
throw e;
}
}
// Called by createNewCtx
private LdapSchemaCtx(int objectType, Hashtable<String,Object> environment,
SchemaInfo info) {
super(environment, LdapClient.caseIgnore);
this.objectType = objectType;
this.info = info;
}
// override HierMemDirCtx.close to prevent premature GC of shared data
public void close() throws NamingException {
info.close();
}
// override to ignore obj and use attrs
// treat same as createSubcontext
final public void bind(Name name, Object obj, Attributes attrs)
throws NamingException {
if (!setupMode) {
if (obj != null) {
throw new IllegalArgumentException("obj must be null");
}
// Update server
addServerSchema(attrs);
}
// Update in-memory copy
LdapSchemaCtx newEntry =
(LdapSchemaCtx)super.doCreateSubcontext(name, attrs);
}
final protected void doBind(Name name, Object obj, Attributes attrs,
boolean useFactory) throws NamingException {
if (!setupMode) {
throw new SchemaViolationException(
"Cannot bind arbitrary object; use createSubcontext()");
} else {
super.doBind(name, obj, attrs, false); // always ignore factories
}
}
// override to use bind() instead
final public void rebind(Name name, Object obj, Attributes attrs)
throws NamingException {
try {
doLookup(name, false);
throw new SchemaViolationException(
"Cannot replace existing schema object");
} catch (NameNotFoundException e) {
bind(name, obj, attrs);
}
}
final protected void doRebind(Name name, Object obj, Attributes attrs,
boolean useFactory) throws NamingException {
if (!setupMode) {
throw new SchemaViolationException(
"Cannot bind arbitrary object; use createSubcontext()");
} else {
super.doRebind(name, obj, attrs, false); // always ignore factories
}
}
final protected void doUnbind(Name name) throws NamingException {
if (!setupMode) {
// Update server
try {
// Lookup entry from memory
LdapSchemaCtx target = (LdapSchemaCtx)doLookup(name, false);
deleteServerSchema(target.attrs);
} catch (NameNotFoundException e) {
return;
}
}
// Update in-memory copy
super.doUnbind(name);
}
final protected void doRename(Name oldname, Name newname)
throws NamingException {
if (!setupMode) {
throw new SchemaViolationException("Cannot rename a schema object");
} else {
super.doRename(oldname, newname);
}
}
final protected void doDestroySubcontext(Name name) throws NamingException {
if (!setupMode) {
// Update server
try {
// Lookup entry from memory
LdapSchemaCtx target = (LdapSchemaCtx)doLookup(name, false);
deleteServerSchema(target.attrs);
} catch (NameNotFoundException e) {
return;
}
}
// Update in-memory copy
super.doDestroySubcontext(name);
}
// Called to create oc, attr, syntax or matching rule roots and leaf entries
final LdapSchemaCtx setup(int objectType, String name, Attributes attrs)
throws NamingException{
try {
setupMode = true;
LdapSchemaCtx answer =
(LdapSchemaCtx) super.doCreateSubcontext(
new CompositeName(name), attrs);
answer.objectType = objectType;
answer.setupMode = false;
return answer;
} finally {
setupMode = false;
}
}
final protected DirContext doCreateSubcontext(Name name, Attributes attrs)
throws NamingException {
if (attrs == null || attrs.size() == 0) {
throw new SchemaViolationException(
"Must supply attributes describing schema");
}
if (!setupMode) {
// Update server
addServerSchema(attrs);
}
// Update in-memory copy
LdapSchemaCtx newEntry =
(LdapSchemaCtx) super.doCreateSubcontext(name, attrs);
return newEntry;
}
final private static Attributes deepClone(Attributes orig)
throws NamingException {
BasicAttributes copy = new BasicAttributes(true);
NamingEnumeration<? extends Attribute> attrs = orig.getAll();
while (attrs.hasMore()) {
copy.put((Attribute)attrs.next().clone());
}
return copy;
}
final protected void doModifyAttributes(ModificationItem[] mods)
throws NamingException {
if (setupMode) {
super.doModifyAttributes(mods);
} else {
Attributes copy = deepClone(attrs);
// Apply modifications to copy
applyMods(mods, copy);
// Update server copy
modifyServerSchema(attrs, copy);
// Update in-memory copy
attrs = copy;
}
}
// we override this so the superclass creates the right kind of contexts
// Default is to create LEAF objects; caller will change after creation
// if necessary
final protected HierMemDirCtx createNewCtx() {
LdapSchemaCtx ctx = new LdapSchemaCtx(LEAF, myEnv, info);
return ctx;
}
final private void addServerSchema(Attributes attrs)
throws NamingException {
Attribute schemaAttr;
switch (objectType) {
case OBJECTCLASS_ROOT:
schemaAttr = info.parser.stringifyObjDesc(attrs);
break;
case ATTRIBUTE_ROOT:
schemaAttr = info.parser.stringifyAttrDesc(attrs);
break;
case SYNTAX_ROOT:
schemaAttr = info.parser.stringifySyntaxDesc(attrs);
break;
case MATCHRULE_ROOT:
schemaAttr = info.parser.stringifyMatchRuleDesc(attrs);
break;
case SCHEMA_ROOT:
throw new SchemaViolationException(
"Cannot create new entry under schema root");
default:
throw new SchemaViolationException(
"Cannot create child of schema object");
}
Attributes holder = new BasicAttributes(true);
holder.put(schemaAttr);
//System.err.println((String)schemaAttr.get());
info.modifyAttributes(myEnv, DirContext.ADD_ATTRIBUTE, holder);
}
/**
* When we delete an entry, we use the original to make sure that
* any formatting inconsistencies are eliminated.
* This is because we're just deleting a value from an attribute
* on the server and there might not be any checks for extra spaces
* or parens.
*/
final private void deleteServerSchema(Attributes origAttrs)
throws NamingException {
Attribute origAttrVal;
switch (objectType) {
case OBJECTCLASS_ROOT:
origAttrVal = info.parser.stringifyObjDesc(origAttrs);
break;
case ATTRIBUTE_ROOT:
origAttrVal = info.parser.stringifyAttrDesc(origAttrs);
break;
case SYNTAX_ROOT:
origAttrVal = info.parser.stringifySyntaxDesc(origAttrs);
break;
case MATCHRULE_ROOT:
origAttrVal = info.parser.stringifyMatchRuleDesc(origAttrs);
break;
case SCHEMA_ROOT:
throw new SchemaViolationException(
"Cannot delete schema root");
default:
throw new SchemaViolationException(
"Cannot delete child of schema object");
}
ModificationItem[] mods = new ModificationItem[1];
mods[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, origAttrVal);
info.modifyAttributes(myEnv, mods);
}
/**
* When we modify an entry, we use the original attribute value
* in the schema to make sure that any formatting inconsistencies
* are eliminated. A modification is done by deleting the original
* value and adding a new value with the modification.
*/
final private void modifyServerSchema(Attributes origAttrs,
Attributes newAttrs) throws NamingException {
Attribute newAttrVal;
Attribute origAttrVal;
switch (objectType) {
case OBJECTCLASS:
origAttrVal = info.parser.stringifyObjDesc(origAttrs);
newAttrVal = info.parser.stringifyObjDesc(newAttrs);
break;
case ATTRIBUTE:
origAttrVal = info.parser.stringifyAttrDesc(origAttrs);
newAttrVal = info.parser.stringifyAttrDesc(newAttrs);
break;
case SYNTAX:
origAttrVal = info.parser.stringifySyntaxDesc(origAttrs);
newAttrVal = info.parser.stringifySyntaxDesc(newAttrs);
break;
case MATCHRULE:
origAttrVal = info.parser.stringifyMatchRuleDesc(origAttrs);
newAttrVal = info.parser.stringifyMatchRuleDesc(newAttrs);
break;
default:
throw new SchemaViolationException(
"Cannot modify schema root");
}
ModificationItem[] mods = new ModificationItem[2];
mods[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, origAttrVal);
mods[1] = new ModificationItem(DirContext.ADD_ATTRIBUTE, newAttrVal);
info.modifyAttributes(myEnv, mods);
}
final static private class SchemaInfo {
private LdapCtx schemaEntry;
private String schemaEntryName;
LdapSchemaParser parser;
private String host;
private int port;
private boolean hasLdapsScheme;
SchemaInfo(String schemaEntryName, LdapCtx schemaEntry,
LdapSchemaParser parser) {
this.schemaEntryName = schemaEntryName;
this.schemaEntry = schemaEntry;
this.parser = parser;
this.port = schemaEntry.port_number;
this.host = schemaEntry.hostname;
this.hasLdapsScheme = schemaEntry.hasLdapsScheme;
}
synchronized void close() throws NamingException {
if (schemaEntry != null) {
schemaEntry.close();
schemaEntry = null;
}
}
private LdapCtx reopenEntry(Hashtable<?,?> env) throws NamingException {
// Use subschemasubentry name as DN
return new LdapCtx(schemaEntryName, host, port,
env, hasLdapsScheme);
}
synchronized void modifyAttributes(Hashtable<?,?> env,
ModificationItem[] mods)
throws NamingException {
if (schemaEntry == null) {
schemaEntry = reopenEntry(env);
}
schemaEntry.modifyAttributes("", mods);
}
synchronized void modifyAttributes(Hashtable<?,?> env, int mod,
Attributes attrs) throws NamingException {
if (schemaEntry == null) {
schemaEntry = reopenEntry(env);
}
schemaEntry.modifyAttributes("", mod, attrs);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,223 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Vector;
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.spi.*;
import javax.naming.ldap.*;
import javax.naming.ldap.LdapName;
import com.sun.jndi.toolkit.ctx.Continuation;
final class LdapSearchEnumeration
extends AbstractLdapNamingEnumeration<SearchResult> {
private Name startName; // prefix of names of search results
private LdapCtx.SearchArgs searchArgs = null;
private final AccessControlContext acc = AccessController.getContext();
LdapSearchEnumeration(LdapCtx homeCtx, LdapResult search_results,
String starter, LdapCtx.SearchArgs args, Continuation cont)
throws NamingException {
super(homeCtx, search_results,
args.name, /* listArg */
cont);
// fully qualified name of starting context of search
startName = new LdapName(starter);
searchArgs = args;
}
@Override
protected SearchResult createItem(String dn, Attributes attrs,
Vector<Control> respCtls)
throws NamingException {
Object obj = null;
String relStart; // name relative to starting search context
String relHome; // name relative to homeCtx.currentDN
boolean relative = true; // whether relative to currentDN
// need to strip off all but lowest component of dn
// so that is relative to current context (currentDN)
try {
Name parsed = new LdapName(dn);
// System.err.println("dn string: " + dn);
// System.err.println("dn name: " + parsed);
if (startName != null && parsed.startsWith(startName)) {
relStart = parsed.getSuffix(startName.size()).toString();
relHome = parsed.getSuffix(homeCtx.currentParsedDN.size()).toString();
} else {
relative = false;
relHome = relStart =
LdapURL.toUrlString(homeCtx.hostname, homeCtx.port_number,
dn, homeCtx.hasLdapsScheme);
}
} catch (NamingException e) {
// could not parse name
relative = false;
relHome = relStart =
LdapURL.toUrlString(homeCtx.hostname, homeCtx.port_number,
dn, homeCtx.hasLdapsScheme);
}
// Name relative to search context
CompositeName cn = new CompositeName();
if (!relStart.equals("")) {
cn.add(relStart);
}
// Name relative to homeCtx
CompositeName rcn = new CompositeName();
if (!relHome.equals("")) {
rcn.add(relHome);
}
//System.err.println("relStart: " + cn);
//System.err.println("relHome: " + rcn);
// Fix attributes to be able to get schema
homeCtx.setParents(attrs, rcn);
// only generate object when requested
if (searchArgs.cons.getReturningObjFlag()) {
if (attrs.get(Obj.JAVA_ATTRIBUTES[Obj.CLASSNAME]) != null) {
// Entry contains Java-object attributes (ser/ref object)
// serialized object or object reference
try {
obj = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws NamingException {
return Obj.decodeObject(attrs);
}
}, acc);
} catch (PrivilegedActionException e) {
throw (NamingException)e.getException();
}
}
if (obj == null) {
obj = new LdapCtx(homeCtx, dn);
}
// Call getObjectInstance before removing unrequested attributes
try {
// rcn is either relative to homeCtx or a fully qualified DN
obj = DirectoryManager.getObjectInstance(
obj, rcn, (relative ? homeCtx : null),
homeCtx.envprops, attrs);
} catch (NamingException e) {
throw e;
} catch (Exception e) {
NamingException ne =
new NamingException(
"problem generating object using object factory");
ne.setRootCause(e);
throw ne;
}
// remove Java attributes from result, if necessary
// Even if CLASSNAME attr not there, there might be some
// residual attributes
String[] reqAttrs;
if ((reqAttrs = searchArgs.reqAttrs) != null) {
// create an attribute set for those requested
Attributes rattrs = new BasicAttributes(true); // caseignore
for (int i = 0; i < reqAttrs.length; i++) {
rattrs.put(reqAttrs[i], null);
}
for (int i = 0; i < Obj.JAVA_ATTRIBUTES.length; i++) {
// Remove Java-object attributes if not requested
if (rattrs.get(Obj.JAVA_ATTRIBUTES[i]) == null) {
attrs.remove(Obj.JAVA_ATTRIBUTES[i]);
}
}
}
}
/*
* name in search result is either the stringified composite name
* relative to the search context that can be passed directly to
* methods of the search context, or the fully qualified DN
* which can be used with the initial context.
*/
SearchResult sr;
if (respCtls != null) {
sr = new SearchResultWithControls(
(relative ? cn.toString() : relStart), obj, attrs,
relative, homeCtx.convertControls(respCtls));
} else {
sr = new SearchResult(
(relative ? cn.toString() : relStart),
obj, attrs, relative);
}
sr.setNameInNamespace(dn);
return sr;
}
@Override
public void appendUnprocessedReferrals(LdapReferralException ex) {
// a referral has been followed so do not create relative names
startName = null;
super.appendUnprocessedReferrals(ex);
}
@Override
protected AbstractLdapNamingEnumeration<? extends NameClassPair> getReferredResults(
LdapReferralContext refCtx) throws NamingException {
// repeat the original operation at the new context
return (AbstractLdapNamingEnumeration<? extends NameClassPair>)refCtx.search(
searchArgs.name, searchArgs.filter, searchArgs.cons);
}
@Override
protected void update(AbstractLdapNamingEnumeration<? extends NameClassPair> ne) {
super.update(ne);
// Update search-specific variables
LdapSearchEnumeration se = (LdapSearchEnumeration)ne;
startName = se.startName;
//VR - keep original args, don't overwite with current args
// searchArgs = se.searchArgs;
}
void setStartName(Name nm) {
startName = nm;
}
}

View File

@@ -0,0 +1,336 @@
/*
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import javax.naming.*;
import java.net.MalformedURLException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.security.AccessController;
import java.security.PrivilegedAction;
import sun.security.action.GetPropertyAction;
import java.util.Locale;
import java.util.StringTokenizer;
import com.sun.jndi.toolkit.url.Uri;
import com.sun.jndi.toolkit.url.UrlUtil;
/*
* Extract components of an LDAP URL.
*
* The format of an LDAP URL is defined in RFC 2255 as follows:
*
* ldapurl = scheme "://" [hostport] ["/"
* [dn ["?" [attributes] ["?" [scope]
* ["?" [filter] ["?" extensions]]]]]]
* scheme = "ldap"
* attributes = attrdesc *("," attrdesc)
* scope = "base" / "one" / "sub"
* dn = distinguishedName from Section 3 of [1]
* hostport = hostport from Section 5 of RFC 1738 [5]
* attrdesc = AttributeDescription from Section 4.1.5 of [2]
* filter = filter from Section 4 of [4]
* extensions = extension *("," extension)
* extension = ["!"] extype ["=" exvalue]
* extype = token / xtoken
* exvalue = LDAPString from section 4.1.2 of [2]
* token = oid from section 4.1 of [3]
* xtoken = ("X-" / "x-") token
*
* For example,
*
* ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US
* ldap://host.com:6666/o=IMC,c=US??sub?(cn=Babs%20Jensen)
*
* This class also supports ldaps URLs.
*/
final public class LdapURL extends Uri {
private static final String PARSE_MODE_PROP = "com.sun.jndi.ldapURLParsing";
private static final ParseMode DEFAULT_PARSE_MODE = ParseMode.COMPAT;
public static final ParseMode PARSE_MODE;
static {
PrivilegedAction<String> action =
new GetPropertyAction(PARSE_MODE_PROP, DEFAULT_PARSE_MODE.toString());
ParseMode parseMode = DEFAULT_PARSE_MODE;
try {
String mode = AccessController.doPrivileged(action);
parseMode = ParseMode.valueOf(mode.toUpperCase(Locale.ROOT));
} catch (Throwable t) {
parseMode = DEFAULT_PARSE_MODE;
} finally {
PARSE_MODE = parseMode;
}
}
private boolean useSsl = false;
private String DN = null;
private String attributes = null;
private String scope = null;
private String filter = null;
private String extensions = null;
/**
* Creates an LdapURL object from an LDAP URL string.
*/
public LdapURL(String url) throws NamingException {
super();
try {
init(url); // scheme, host, port, path, query
useSsl = scheme.equalsIgnoreCase("ldaps");
if (! (scheme.equalsIgnoreCase("ldap") || useSsl)) {
throw newInvalidURISchemeException(url);
}
parsePathAndQuery(); // DN, attributes, scope, filter, extensions
} catch (MalformedURLException e) {
NamingException ne = new NamingException("Cannot parse url: " + url);
ne.setRootCause(e);
throw ne;
} catch (UnsupportedEncodingException e) {
NamingException ne = new NamingException("Cannot parse url: " + url);
ne.setRootCause(e);
throw ne;
}
}
@Override
protected MalformedURLException newInvalidURISchemeException(String uri) {
return new MalformedURLException("Not an LDAP URL: " + uri);
}
@Override
protected boolean isSchemeOnly(String uri) {
return isLdapSchemeOnly(uri);
}
@Override
protected ParseMode parseMode() {
return PARSE_MODE;
}
/**
* Returns true if the URL is an LDAPS URL.
*/
public boolean useSsl() {
return useSsl;
}
/**
* Returns the LDAP URL's distinguished name.
*/
public String getDN() {
return DN;
}
/**
* Returns the LDAP URL's attributes.
*/
public String getAttributes() {
return attributes;
}
/**
* Returns the LDAP URL's scope.
*/
public String getScope() {
return scope;
}
/**
* Returns the LDAP URL's filter.
*/
public String getFilter() {
return filter;
}
/**
* Returns the LDAP URL's extensions.
*/
public String getExtensions() {
return extensions;
}
/**
* Given a space-separated list of LDAP URLs, returns an array of strings.
*/
public static String[] fromList(String urlList) throws NamingException {
String[] urls = new String[(urlList.length() + 1) / 2];
int i = 0; // next available index in urls
StringTokenizer st = new StringTokenizer(urlList, " ");
while (st.hasMoreTokens()) {
// we don't accept scheme-only URLs here
urls[i++] = validateURI(st.nextToken());
}
String[] trimmed = new String[i];
System.arraycopy(urls, 0, trimmed, 0, i);
return trimmed;
}
public static boolean isLdapSchemeOnly(String uri) {
return "ldap:".equals(uri) || "ldaps:".equals(uri);
}
public static String validateURI(String uri) {
// no validation in legacy mode parsing
if (PARSE_MODE == ParseMode.LEGACY) {
return uri;
}
// special case of scheme-only URIs
if (isLdapSchemeOnly(uri)) {
return uri;
}
// use java.net.URI to validate the uri syntax
return URI.create(uri).toString();
}
/**
* Derermines whether an LDAP URL has query components.
*/
public static boolean hasQueryComponents(String url) {
return (url.lastIndexOf('?') != -1);
}
/*
* Assembles an LDAP or LDAPS URL string from its components.
* If "host" is an IPv6 literal, it may optionally include delimiting
* brackets.
*/
static String toUrlString(String host, int port, String dn, boolean useSsl)
{
try {
String h = (host != null) ? host : "";
if ((h.indexOf(':') != -1) && (h.charAt(0) != '[')) {
h = "[" + h + "]"; // IPv6 literal
}
String p = (port != -1) ? (":" + port) : "";
String d = (dn != null) ? ("/" + UrlUtil.encode(dn, "UTF8")) : "";
String uri = useSsl ? "ldaps://" + h + p + d : "ldap://" + h + p + d;
return validateURI(uri);
} catch (UnsupportedEncodingException e) {
// UTF8 should always be supported
throw new IllegalStateException("UTF-8 encoding unavailable");
}
}
/*
* Parses the path and query components of an URL and sets this
* object's fields accordingly.
*/
private void parsePathAndQuery() throws MalformedURLException,
UnsupportedEncodingException {
// path begins with a '/' or is empty
if (path.equals("")) {
return;
}
DN = path.startsWith("/") ? path.substring(1) : path;
if (DN.length() > 0) {
DN = UrlUtil.decode(DN, "UTF8");
}
// query begins with a '?' or is null
if (query == null || query.length() < 2) {
return;
}
int currentIndex = 1;
int nextQmark;
int endIndex;
// attributes:
nextQmark = query.indexOf('?', currentIndex);
endIndex = nextQmark == -1 ? query.length() : nextQmark;
if (endIndex - currentIndex > 0) {
attributes = query.substring(currentIndex, endIndex);
}
currentIndex = endIndex + 1;
if (currentIndex >= query.length()) {
return;
}
// scope:
nextQmark = query.indexOf('?', currentIndex);
endIndex = nextQmark == -1 ? query.length() : nextQmark;
if (endIndex - currentIndex > 0) {
scope = query.substring(currentIndex, endIndex);
}
currentIndex = endIndex + 1;
if (currentIndex >= query.length()) {
return;
}
// filter:
nextQmark = query.indexOf('?', currentIndex);
endIndex = nextQmark == -1 ? query.length() : nextQmark;
if (endIndex - currentIndex > 0) {
filter = query.substring(currentIndex, endIndex);
filter = UrlUtil.decode(filter, "UTF8");
}
currentIndex = endIndex + 1;
if (currentIndex >= query.length()) {
return;
}
// extensions:
if (query.length() - currentIndex > 0) {
extensions = query.substring(currentIndex);
extensions = UrlUtil.decode(extensions, "UTF8");
}
}
/*
public static void main(String[] args) throws Exception {
LdapURL url = new LdapURL(args[0]);
System.out.println("Example LDAP URL: " + url.toString());
System.out.println(" scheme: " + url.getScheme());
System.out.println(" host: " + url.getHost());
System.out.println(" port: " + url.getPort());
System.out.println(" DN: " + url.getDN());
System.out.println(" attrs: " + url.getAttributes());
System.out.println(" scope: " + url.getScope());
System.out.println(" filter: " + url.getFilter());
System.out.println(" extens: " + url.getExtensions());
System.out.println("");
}
*/
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright (c) 1999, 2002, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import java.io.IOException;
/**
* This class implements the LDAPv3 Request Control for manageDsaIT as
* defined in
* <a href="http://www.ietf.org/internet-drafts/draft-ietf-ldapext-namedref-00.txt">draft-ietf-ldapext-namedref-00.txt</a>.
*
* The control has no control value.
*
* @author Vincent Ryan
*/
final public class ManageReferralControl extends BasicControl {
/**
* The manage referral control's assigned object identifier
* is 2.16.840.1.113730.3.4.2.
*
* @serial
*/
public static final String OID = "2.16.840.1.113730.3.4.2";
private static final long serialVersionUID = 909382692585717224L;
/**
* Constructs a manage referral critical control.
*/
public ManageReferralControl() {
super(OID, true, null);
}
/**
* Constructs a manage referral control.
*
* @param criticality The control's criticality setting.
*/
public ManageReferralControl(boolean criticality) {
super(OID, criticality, null);
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 1999, 2002, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import javax.naming.*;
import javax.naming.ldap.*;
class NameClassPairWithControls extends NameClassPair implements HasControls {
private Control[] controls;
public NameClassPairWithControls(String name, String className,
Control[] controls) {
super(name, className);
this.controls = controls;
}
public Control[] getControls() throws NamingException {
return controls;
}
private static final long serialVersionUID = 2010738921219112944L;
}

View File

@@ -0,0 +1,287 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.event.*;
import javax.naming.ldap.*;
import javax.naming.ldap.LdapName;
import java.util.Vector;
import com.sun.jndi.toolkit.ctx.Continuation;
/**
* Gathers information to generate events by using the Persistent Search
* control.
*<p>
* This class maintains a list of listeners all interested in the same
* "search" request. It creates a thread that does the persistent search
* and blocks, collecting the results of the search.
* For each result that it receives from the search, it fires the
* corresponding event to its listeners. If an exception is encountered,
* it fires a NamingExceptionEvent.
*
* @author Rosanna Lee
*/
final class NamingEventNotifier implements Runnable {
private final static boolean debug = false;
private Vector<NamingListener> namingListeners;
private Thread worker;
private LdapCtx context;
private EventContext eventSrc;
private EventSupport support;
private NamingEnumeration<SearchResult> results;
// package private; used by EventSupport to remove it
NotifierArgs info;
NamingEventNotifier(EventSupport support, LdapCtx ctx, NotifierArgs info,
NamingListener firstListener) throws NamingException {
this.info = info;
this.support = support;
Control psearch;
try {
psearch = new PersistentSearchControl(
info.mask,
true /* no info about original entry(s) */,
true /* additional info about changes */,
Control.CRITICAL);
} catch (java.io.IOException e) {
NamingException ne = new NamingException(
"Problem creating persistent search control");
ne.setRootCause(e);
throw ne;
}
// Add psearch control to existing list
context = (LdapCtx)ctx.newInstance(new Control[]{psearch});
eventSrc = ctx;
namingListeners = new Vector<>();
namingListeners.addElement(firstListener);
worker = Obj.helper.createThread(this);
worker.setDaemon(true); // not a user thread
worker.start();
}
// package private; used by EventSupport; namingListener already synchronized
void addNamingListener(NamingListener l) {
namingListeners.addElement(l);
}
// package private; used by EventSupport; namingListener already synchronized
void removeNamingListener(NamingListener l) {
namingListeners.removeElement(l);
}
// package private; used by EventSupport; namingListener already synchronized
boolean hasNamingListeners() {
return namingListeners.size() > 0;
}
/**
* Execute "persistent search".
* For each result, create the appropriate NamingEvent and
* queue to be dispatched to listeners.
*/
public void run() {
try {
Continuation cont = new Continuation();
cont.setError(this, info.name);
Name nm = (info.name == null || info.name.equals("")) ?
new CompositeName() : new CompositeName().add(info.name);
results = context.searchAux(nm, info.filter, info.controls,
true, false, cont);
// Change root of search results so that it will generate
// names relative to the event context instead of that
// named by nm
((LdapSearchEnumeration)(NamingEnumeration)results)
.setStartName(context.currentParsedDN);
SearchResult si;
Control[] respctls;
EntryChangeResponseControl ec;
long changeNum;
while (results.hasMore()) {
si = results.next();
respctls = (si instanceof HasControls) ?
((HasControls) si).getControls() : null;
if (debug) {
System.err.println("notifier: " + si);
System.err.println("respCtls: " + respctls);
}
// Just process ECs; ignore all the rest
if (respctls != null) {
for (int i = 0; i < respctls.length; i++) {
// %%% Should be checking OID instead of class
// %%% in case using someone else's EC ctl
if (respctls[i] instanceof EntryChangeResponseControl) {
ec = (EntryChangeResponseControl)respctls[i];
changeNum = ec.getChangeNumber();
switch (ec.getChangeType()) {
case EntryChangeResponseControl.ADD:
fireObjectAdded(si, changeNum);
break;
case EntryChangeResponseControl.DELETE:
fireObjectRemoved(si, changeNum);
break;
case EntryChangeResponseControl.MODIFY:
fireObjectChanged(si, changeNum);
break;
case EntryChangeResponseControl.RENAME:
fireObjectRenamed(si, ec.getPreviousDN(),
changeNum);
break;
}
}
break;
}
}
}
} catch (InterruptedNamingException e) {
if (debug) System.err.println("NamingEventNotifier Interrupted");
} catch (NamingException e) {
// Fire event to notify NamingExceptionEvent listeners
fireNamingException(e);
// This notifier is no longer valid
support.removeDeadNotifier(info);
} finally {
cleanup();
}
if (debug) System.err.println("NamingEventNotifier finished");
}
private void cleanup() {
if (debug) System.err.println("NamingEventNotifier cleanup");
try {
if (results != null) {
if (debug) System.err.println("NamingEventNotifier enum closing");
results.close(); // this will abandon the search
results = null;
}
if (context != null) {
if (debug) System.err.println("NamingEventNotifier ctx closing");
context.close();
context = null;
}
} catch (NamingException e) {}
}
/**
* Stop the dispatcher so we can be destroyed.
* package private; used by EventSupport
*/
void stop() {
if (debug) System.err.println("NamingEventNotifier being stopping");
if (worker != null) {
worker.interrupt(); // kill our thread
worker = null;
}
}
/**
* Fire an "object added" event to registered NamingListeners.
*/
private void fireObjectAdded(Binding newBd, long changeID) {
if (namingListeners == null || namingListeners.size() == 0)
return;
NamingEvent e = new NamingEvent(eventSrc, NamingEvent.OBJECT_ADDED,
newBd, null, new Long(changeID));
support.queueEvent(e, namingListeners);
}
/**
* Fire an "object removed" event to registered NamingListeners.
*/
private void fireObjectRemoved(Binding oldBd, long changeID) {
if (namingListeners == null || namingListeners.size() == 0)
return;
NamingEvent e = new NamingEvent(eventSrc, NamingEvent.OBJECT_REMOVED,
null, oldBd, new Long(changeID));
support.queueEvent(e, namingListeners);
}
/**
* Fires an "object changed" event to registered NamingListeners.
*/
private void fireObjectChanged(Binding newBd, long changeID) {
if (namingListeners == null || namingListeners.size() == 0)
return;
// Name hasn't changed; construct old binding using name from new binding
Binding oldBd = new Binding(newBd.getName(), null, newBd.isRelative());
NamingEvent e = new NamingEvent(
eventSrc, NamingEvent.OBJECT_CHANGED, newBd, oldBd, new Long(changeID));
support.queueEvent(e, namingListeners);
}
/**
* Fires an "object renamed" to registered NamingListeners.
*/
private void fireObjectRenamed(Binding newBd, String oldDN, long changeID) {
if (namingListeners == null || namingListeners.size() == 0)
return;
Binding oldBd = null;
try {
LdapName dn = new LdapName(oldDN);
if (dn.startsWith(context.currentParsedDN)) {
String relDN = dn.getSuffix(context.currentParsedDN.size()).toString();
oldBd = new Binding(relDN, null);
}
} catch (NamingException e) {}
if (oldBd == null) {
oldBd = new Binding(oldDN, null, false /* not relative name */);
}
NamingEvent e = new NamingEvent(
eventSrc, NamingEvent.OBJECT_RENAMED, newBd, oldBd, new Long(changeID));
support.queueEvent(e, namingListeners);
}
private void fireNamingException(NamingException e) {
if (namingListeners == null || namingListeners.size() == 0)
return;
NamingExceptionEvent evt = new NamingExceptionEvent(eventSrc, e);
support.queueEvent(evt, namingListeners);
}
}

View File

@@ -0,0 +1,149 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import javax.naming.directory.SearchControls;
import javax.naming.event.*;
/**
* This class holds the information in an event registration/deregistration
* request. This includes the name, filter, search controls and
* the different interfaces that the listener implements. This last piece
* of information determines which event(s) the listener is interested in.
*<p>
* It overrides equals() and hashCode() to use all these pieces of
* information so that it can be used correctly in a hashtable.
*
* @author Rosanna Lee
*/
final class NotifierArgs {
static final int ADDED_MASK = 0x1;
static final int REMOVED_MASK = 0x2;
static final int CHANGED_MASK = 0x4;
static final int RENAMED_MASK = 0x8;
// these fields are package private; used by NamingEventNotifier
String name;
String filter;
SearchControls controls;
int mask;
// package private
NotifierArgs(String name, int scope, NamingListener l) {
this(name, "(objectclass=*)", null, l);
// if scope is not default, create search ctl and set it
if (scope != EventContext.ONELEVEL_SCOPE) {
controls = new SearchControls();
controls.setSearchScope(scope);
}
}
// package private
NotifierArgs(String name, String filter, SearchControls ctls,
NamingListener l) {
this.name = name;
this.filter = filter;
this.controls = ctls;
if (l instanceof NamespaceChangeListener) {
mask |= ADDED_MASK|REMOVED_MASK|RENAMED_MASK;
}
if (l instanceof ObjectChangeListener) {
mask |= CHANGED_MASK;
}
}
// checks name, filter, controls
public boolean equals(Object obj) {
if (obj instanceof NotifierArgs) {
NotifierArgs target = (NotifierArgs)obj;
return mask == target.mask &&
name.equals(target.name) && filter.equals(target.filter) &&
checkControls(target.controls);
}
return false;
}
private boolean checkControls(SearchControls ctls) {
if ((controls == null || ctls == null)) {
return ctls == controls;
}
// ctls are nonempty
return (controls.getSearchScope() == ctls.getSearchScope()) &&
(controls.getTimeLimit() == ctls.getTimeLimit()) &&
(controls.getDerefLinkFlag() == ctls.getDerefLinkFlag()) &&
(controls.getReturningObjFlag() == ctls.getReturningObjFlag()) &&
(controls.getCountLimit() == ctls.getCountLimit()) &&
checkStringArrays(controls.getReturningAttributes(),
ctls.getReturningAttributes());
}
private static boolean checkStringArrays(String[] s1, String[] s2) {
if ((s1 == null) || (s2 == null)) {
return s1 == s2;
}
// both are nonnull
if (s1.length != s2.length) {
return false;
}
for (int i = 0; i < s1.length; i++) {
if (!s1[i].equals(s2[i])) {
return false;
}
}
return true;
}
// save from having to recalculate each time
private int sum = -1;
public int hashCode() {
if (sum == -1)
sum = mask + name.hashCode() + filter.hashCode() + controlsCode();
return sum;
}
// used in calculating hash code
private int controlsCode() {
if (controls == null) return 0;
int total = controls.getTimeLimit() + (int)controls.getCountLimit() +
(controls.getDerefLinkFlag() ? 1 : 0) +
(controls.getReturningObjFlag() ? 1 : 0);
String[] attrs = controls.getReturningAttributes();
if (attrs != null) {
for (int i = 0; i < attrs.length; i++) {
total += attrs[i].hashCode();
}
}
return total;
}
}

View File

@@ -0,0 +1,669 @@
/*
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.spi.DirectoryManager;
import javax.naming.spi.DirStateFactory;
import java.io.IOException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.InputStream;
import java.util.Hashtable;
import java.util.Vector;
import java.util.StringTokenizer;
import sun.misc.BASE64Encoder;
import sun.misc.BASE64Decoder;
import java.lang.reflect.Proxy;
import java.lang.reflect.Modifier;
/**
* Class containing static methods and constants for dealing with
* encoding/decoding JNDI References and Serialized Objects
* in LDAP.
* @author Vincent Ryan
* @author Rosanna Lee
*/
final class Obj {
private Obj () {}; // Make sure no one can create one
// package private; used by Connection
static VersionHelper helper = VersionHelper.getVersionHelper();
// LDAP attributes used to support Java objects.
static final String[] JAVA_ATTRIBUTES = {
"objectClass",
"javaSerializedData",
"javaClassName",
"javaFactory",
"javaCodeBase",
"javaReferenceAddress",
"javaClassNames",
"javaRemoteLocation" // Deprecated
};
static final int OBJECT_CLASS = 0;
static final int SERIALIZED_DATA = 1;
static final int CLASSNAME = 2;
static final int FACTORY = 3;
static final int CODEBASE = 4;
static final int REF_ADDR = 5;
static final int TYPENAME = 6;
/**
* @deprecated
*/
@Deprecated
private static final int REMOTE_LOC = 7;
// LDAP object classes to support Java objects
static final String[] JAVA_OBJECT_CLASSES = {
"javaContainer",
"javaObject",
"javaNamingReference",
"javaSerializedObject",
"javaMarshalledObject",
};
static final String[] JAVA_OBJECT_CLASSES_LOWER = {
"javacontainer",
"javaobject",
"javanamingreference",
"javaserializedobject",
"javamarshalledobject",
};
static final int STRUCTURAL = 0; // structural object class
static final int BASE_OBJECT = 1; // auxiliary java object class
static final int REF_OBJECT = 2; // auxiliary reference object class
static final int SER_OBJECT = 3; // auxiliary serialized object class
static final int MAR_OBJECT = 4; // auxiliary marshalled object class
/**
* Encode an object in LDAP attributes.
* Supports binding Referenceable or Reference, Serializable,
* and DirContext.
*
* If the object supports the Referenceable interface then encode
* the reference to the object. See encodeReference() for details.
*<p>
* If the object is serializable, it is stored as follows:
* javaClassName
* value: Object.getClass();
* javaSerializedData
* value: serialized form of Object (in binary form).
* javaTypeName
* value: getTypeNames(Object.getClass());
*/
private static Attributes encodeObject(char separator,
Object obj, Attributes attrs,
Attribute objectClass, boolean cloned)
throws NamingException {
boolean structural =
(objectClass.size() == 0 ||
(objectClass.size() == 1 && objectClass.contains("top")));
if (structural) {
objectClass.add(JAVA_OBJECT_CLASSES[STRUCTURAL]);
}
// References
if (obj instanceof Referenceable) {
objectClass.add(JAVA_OBJECT_CLASSES[BASE_OBJECT]);
objectClass.add(JAVA_OBJECT_CLASSES[REF_OBJECT]);
if (!cloned) {
attrs = (Attributes)attrs.clone();
}
attrs.put(objectClass);
return (encodeReference(separator,
((Referenceable)obj).getReference(),
attrs, obj));
} else if (obj instanceof Reference) {
objectClass.add(JAVA_OBJECT_CLASSES[BASE_OBJECT]);
objectClass.add(JAVA_OBJECT_CLASSES[REF_OBJECT]);
if (!cloned) {
attrs = (Attributes)attrs.clone();
}
attrs.put(objectClass);
return (encodeReference(separator, (Reference)obj, attrs, null));
// Serializable Object
} else if (obj instanceof java.io.Serializable) {
objectClass.add(JAVA_OBJECT_CLASSES[BASE_OBJECT]);
if (!(objectClass.contains(JAVA_OBJECT_CLASSES[MAR_OBJECT]) ||
objectClass.contains(JAVA_OBJECT_CLASSES_LOWER[MAR_OBJECT]))) {
objectClass.add(JAVA_OBJECT_CLASSES[SER_OBJECT]);
}
if (!cloned) {
attrs = (Attributes)attrs.clone();
}
attrs.put(objectClass);
attrs.put(new BasicAttribute(JAVA_ATTRIBUTES[SERIALIZED_DATA],
serializeObject(obj)));
if (attrs.get(JAVA_ATTRIBUTES[CLASSNAME]) == null) {
attrs.put(JAVA_ATTRIBUTES[CLASSNAME],
obj.getClass().getName());
}
if (attrs.get(JAVA_ATTRIBUTES[TYPENAME]) == null) {
Attribute tAttr =
LdapCtxFactory.createTypeNameAttr(obj.getClass());
if (tAttr != null) {
attrs.put(tAttr);
}
}
// DirContext Object
} else if (obj instanceof DirContext) {
// do nothing
} else {
throw new IllegalArgumentException(
"can only bind Referenceable, Serializable, DirContext");
}
// System.err.println(attrs);
return attrs;
}
/**
* Each value in javaCodebase contains a list of space-separated
* URLs. Each value is independent; we can pick any of the values
* so we just use the first one.
* @return an array of URL strings for the codebase
*/
private static String[] getCodebases(Attribute codebaseAttr) throws
NamingException {
if (codebaseAttr == null) {
return null;
} else {
StringTokenizer parser =
new StringTokenizer((String)codebaseAttr.get());
Vector<String> vec = new Vector<>(10);
while (parser.hasMoreTokens()) {
vec.addElement(parser.nextToken());
}
String[] answer = new String[vec.size()];
for (int i = 0; i < answer.length; i++) {
answer[i] = vec.elementAt(i);
}
return answer;
}
}
/*
* Decode an object from LDAP attribute(s).
* The object may be a Reference, or a Serialized object.
*
* See encodeObject() and encodeReference() for details on formats
* expected.
*/
static Object decodeObject(Attributes attrs)
throws NamingException {
Attribute attr;
// Get codebase, which is used in all 3 cases.
String[] codebases = getCodebases(attrs.get(JAVA_ATTRIBUTES[CODEBASE]));
try {
if ((attr = attrs.get(JAVA_ATTRIBUTES[SERIALIZED_DATA])) != null) {
if (!VersionHelper12.isSerialDataAllowed()) {
throw new NamingException("Object deserialization is not allowed");
}
ClassLoader cl = helper.getURLClassLoader(codebases);
return deserializeObject((byte[])attr.get(), cl);
} else if ((attr = attrs.get(JAVA_ATTRIBUTES[REMOTE_LOC])) != null) {
// javaRemoteLocation attribute (RMI stub will be created)
if (!VersionHelper12.isSerialDataAllowed()) {
throw new NamingException("Object deserialization is not allowed");
}
// For backward compatibility only
return decodeRmiObject(
(String)attrs.get(JAVA_ATTRIBUTES[CLASSNAME]).get(),
(String)attr.get(), codebases);
}
attr = attrs.get(JAVA_ATTRIBUTES[OBJECT_CLASS]);
if (attr != null &&
(attr.contains(JAVA_OBJECT_CLASSES[REF_OBJECT]) ||
attr.contains(JAVA_OBJECT_CLASSES_LOWER[REF_OBJECT]))) {
return decodeReference(attrs, codebases);
}
return null;
} catch (IOException e) {
NamingException ne = new NamingException();
ne.setRootCause(e);
throw ne;
}
}
/**
* Convert a Reference object into several LDAP attributes.
*
* A Reference is stored as into the following attributes:
* javaClassName
* value: Reference.getClassName();
* javaFactory
* value: Reference.getFactoryClassName();
* javaCodeBase
* value: Reference.getFactoryClassLocation();
* javaReferenceAddress
* value: #0#typeA#valA
* value: #1#typeB#valB
* value: #2#typeC##[serialized RefAddr C]
* value: #3#typeD#valD
*
* where
* - the first character denotes the separator
* - the number following the first separator denotes the position
* of the RefAddr within the Reference
* - "typeA" is RefAddr.getType()
* - ## denotes that the Base64-encoded form of the non-StringRefAddr
* is to follow; otherwise the value that follows is
* StringRefAddr.getContents()
*
* The default separator is the hash character (#).
* May provide property for this in future.
*/
private static Attributes encodeReference(char separator,
Reference ref, Attributes attrs, Object orig)
throws NamingException {
if (ref == null)
return attrs;
String s;
if ((s = ref.getClassName()) != null) {
attrs.put(new BasicAttribute(JAVA_ATTRIBUTES[CLASSNAME], s));
}
if ((s = ref.getFactoryClassName()) != null) {
attrs.put(new BasicAttribute(JAVA_ATTRIBUTES[FACTORY], s));
}
if ((s = ref.getFactoryClassLocation()) != null) {
attrs.put(new BasicAttribute(JAVA_ATTRIBUTES[CODEBASE], s));
}
// Get original object's types if caller has not explicitly
// specified other type names
if (orig != null && attrs.get(JAVA_ATTRIBUTES[TYPENAME]) != null) {
Attribute tAttr =
LdapCtxFactory.createTypeNameAttr(orig.getClass());
if (tAttr != null) {
attrs.put(tAttr);
}
}
int count = ref.size();
if (count > 0) {
Attribute refAttr = new BasicAttribute(JAVA_ATTRIBUTES[REF_ADDR]);
RefAddr refAddr;
BASE64Encoder encoder = null;
for (int i = 0; i < count; i++) {
refAddr = ref.get(i);
if (refAddr instanceof StringRefAddr) {
refAttr.add(""+ separator + i +
separator + refAddr.getType() +
separator + refAddr.getContent());
} else {
if (encoder == null)
encoder = new BASE64Encoder();
refAttr.add(""+ separator + i +
separator + refAddr.getType() +
separator + separator +
encoder.encodeBuffer(serializeObject(refAddr)));
}
}
attrs.put(refAttr);
}
return attrs;
}
/*
* A RMI object is stored in the directory as
* javaClassName
* value: Object.getClass();
* javaRemoteLocation
* value: URL of RMI object (accessed through the RMI Registry)
* javaCodebase:
* value: URL of codebase of where to find classes for object
*
* Return the RMI Location URL itself. This will be turned into
* an RMI object when getObjectInstance() is called on it.
* %%% Ignore codebase for now. Depend on RMI registry to send code.-RL
* @deprecated For backward compatibility only
*/
private static Object decodeRmiObject(String className,
String rmiName, String[] codebases) throws NamingException {
return new Reference(className, new StringRefAddr("URL", rmiName));
}
/*
* Restore a Reference object from several LDAP attributes
*/
private static Reference decodeReference(Attributes attrs,
String[] codebases) throws NamingException, IOException {
Attribute attr;
String className;
String factory = null;
if ((attr = attrs.get(JAVA_ATTRIBUTES[CLASSNAME])) != null) {
className = (String)attr.get();
} else {
throw new InvalidAttributesException(JAVA_ATTRIBUTES[CLASSNAME] +
" attribute is required");
}
if ((attr = attrs.get(JAVA_ATTRIBUTES[FACTORY])) != null) {
factory = (String)attr.get();
}
Reference ref = new Reference(className, factory,
(codebases != null? codebases[0] : null));
/*
* string encoding of a RefAddr is either:
*
* #posn#<type>#<address>
* or
* #posn#<type>##<base64-encoded address>
*/
if ((attr = attrs.get(JAVA_ATTRIBUTES[REF_ADDR])) != null) {
String val, posnStr, type;
char separator;
int start, sep, posn;
BASE64Decoder decoder = null;
ClassLoader cl = helper.getURLClassLoader(codebases);
/*
* Temporary Vector for decoded RefAddr addresses - used to ensure
* unordered addresses are correctly re-ordered.
*/
Vector<RefAddr> refAddrList = new Vector<>();
refAddrList.setSize(attr.size());
for (NamingEnumeration<?> vals = attr.getAll(); vals.hasMore(); ) {
val = (String)vals.next();
if (val.length() == 0) {
throw new InvalidAttributeValueException(
"malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - "+
"empty attribute value");
}
// first character denotes encoding separator
separator = val.charAt(0);
start = 1; // skip over separator
// extract position within Reference
if ((sep = val.indexOf(separator, start)) < 0) {
throw new InvalidAttributeValueException(
"malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - " +
"separator '" + separator + "'" + "not found");
}
if ((posnStr = val.substring(start, sep)) == null) {
throw new InvalidAttributeValueException(
"malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - " +
"empty RefAddr position");
}
try {
posn = Integer.parseInt(posnStr);
} catch (NumberFormatException nfe) {
throw new InvalidAttributeValueException(
"malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - " +
"RefAddr position not an integer");
}
start = sep + 1; // skip over position and trailing separator
// extract type
if ((sep = val.indexOf(separator, start)) < 0) {
throw new InvalidAttributeValueException(
"malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - " +
"RefAddr type not found");
}
if ((type = val.substring(start, sep)) == null) {
throw new InvalidAttributeValueException(
"malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - " +
"empty RefAddr type");
}
start = sep + 1; // skip over type and trailing separator
// extract content
if (start == val.length()) {
// Empty content
refAddrList.setElementAt(new StringRefAddr(type, null), posn);
} else if (val.charAt(start) == separator) {
// Check if deserialization of binary RefAddr is allowed from
// 'javaReferenceAddress' LDAP attribute.
if (!VersionHelper12.isSerialDataAllowed()) {
throw new NamingException("Object deserialization is not allowed");
}
// Double separators indicate a non-StringRefAddr
// Content is a Base64-encoded serialized RefAddr
++start; // skip over consecutive separator
// %%% RL: exception if empty after double separator
if (decoder == null)
decoder = new BASE64Decoder();
RefAddr ra = (RefAddr)
deserializeObject(
decoder.decodeBuffer(val.substring(start)),
cl);
refAddrList.setElementAt(ra, posn);
} else {
// Single separator indicates a StringRefAddr
refAddrList.setElementAt(new StringRefAddr(type,
val.substring(start)), posn);
}
}
// Copy to real reference
for (int i = 0; i < refAddrList.size(); i++) {
ref.add(refAddrList.elementAt(i));
}
}
return (ref);
}
/*
* Serialize an object into a byte array
*/
private static byte[] serializeObject(Object obj) throws NamingException {
try {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
try (ObjectOutputStream serial = new ObjectOutputStream(bytes)) {
serial.writeObject(obj);
}
return (bytes.toByteArray());
} catch (IOException e) {
NamingException ne = new NamingException();
ne.setRootCause(e);
throw ne;
}
}
/*
* Deserializes a byte array into an object.
*/
private static Object deserializeObject(byte[] obj, ClassLoader cl)
throws NamingException {
try {
// Create ObjectInputStream for deserialization
ByteArrayInputStream bytes = new ByteArrayInputStream(obj);
try (ObjectInputStream deserial = cl == null ?
new ObjectInputStream(bytes) :
new LoaderInputStream(bytes, cl)) {
return deserial.readObject();
} catch (ClassNotFoundException e) {
NamingException ne = new NamingException();
ne.setRootCause(e);
throw ne;
}
} catch (IOException e) {
NamingException ne = new NamingException();
ne.setRootCause(e);
throw ne;
}
}
/**
* Returns the attributes to bind given an object and its attributes.
*/
static Attributes determineBindAttrs(
char separator, Object obj, Attributes attrs, boolean cloned,
Name name, Context ctx, Hashtable<?,?> env)
throws NamingException {
// Call state factories to convert object and attrs
DirStateFactory.Result res =
DirectoryManager.getStateToBind(obj, name, ctx, env, attrs);
obj = res.getObject();
attrs = res.getAttributes();
// We're only storing attributes; no further processing required
if (obj == null) {
return attrs;
}
//if object to be bound is a DirContext extract its attributes
if ((attrs == null) && (obj instanceof DirContext)) {
cloned = true;
attrs = ((DirContext)obj).getAttributes("");
}
boolean ocNeedsCloning = false;
// Create "objectClass" attribute
Attribute objectClass;
if (attrs == null || attrs.size() == 0) {
attrs = new BasicAttributes(LdapClient.caseIgnore);
cloned = true;
// No objectclasses supplied, use "top" to start
objectClass = new BasicAttribute("objectClass", "top");
} else {
// Get existing objectclass attribute
objectClass = attrs.get("objectClass");
if (objectClass == null && !attrs.isCaseIgnored()) {
// %%% workaround
objectClass = attrs.get("objectclass");
}
// No objectclasses supplied, use "top" to start
if (objectClass == null) {
objectClass = new BasicAttribute("objectClass", "top");
} else if (ocNeedsCloning || !cloned) {
objectClass = (Attribute)objectClass.clone();
}
}
// convert the supplied object into LDAP attributes
attrs = encodeObject(separator, obj, attrs, objectClass, cloned);
// System.err.println("Determined: " + attrs);
return attrs;
}
/**
* An ObjectInputStream that uses a class loader to find classes.
*/
private static final class LoaderInputStream extends ObjectInputStream {
private ClassLoader classLoader;
LoaderInputStream(InputStream in, ClassLoader cl) throws IOException {
super(in);
classLoader = cl;
}
protected Class<?> resolveClass(ObjectStreamClass desc) throws
IOException, ClassNotFoundException {
try {
// %%% Should use Class.forName(desc.getName(), false, classLoader);
// except we can't because that is only available on JDK1.2
return classLoader.loadClass(desc.getName());
} catch (ClassNotFoundException e) {
return super.resolveClass(desc);
}
}
protected Class<?> resolveProxyClass(String[] interfaces) throws
IOException, ClassNotFoundException {
ClassLoader nonPublicLoader = null;
boolean hasNonPublicInterface = false;
// define proxy in class loader of non-public interface(s), if any
Class<?>[] classObjs = new Class<?>[interfaces.length];
for (int i = 0; i < interfaces.length; i++) {
Class<?> cl = Class.forName(interfaces[i], false, classLoader);
if ((cl.getModifiers() & Modifier.PUBLIC) == 0) {
if (hasNonPublicInterface) {
if (nonPublicLoader != cl.getClassLoader()) {
throw new IllegalAccessError(
"conflicting non-public interface class loaders");
}
} else {
nonPublicLoader = cl.getClassLoader();
hasNonPublicInterface = true;
}
}
classObjs[i] = cl;
}
try {
return Proxy.getProxyClass(hasNonPublicInterface ?
nonPublicLoader : classLoader, classObjs);
} catch (IllegalArgumentException e) {
throw new ClassNotFoundException(null, e);
}
}
}
}

View File

@@ -0,0 +1,161 @@
/*
* Copyright (c) 1999, 2002, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import java.io.IOException;
/**
* This class implements the LDAPv3 Request Control for the persistent search
* mechanism as defined in
* <a href="http://www.ietf.org/internet-drafts/draft-ietf-ldapext-psearch-02.txt">draft-ietf-ldapext-psearch-02.txt</a>.
*
* The control's value has the following ASN.1 definition:
* <pre>
*
* PersistentSearch ::= SEQUENCE {
* changeTypes INTEGER,
* changesOnly BOOLEAN,
* returnECs BOOLEAN
* }
*
* </pre>
*
* @see EntryChangeResponseControl
* @author Vincent Ryan
*/
final public class PersistentSearchControl extends BasicControl {
/**
* The persistent search control's assigned object identifier
* is 2.16.840.1.113730.3.4.3.
*/
public static final String OID = "2.16.840.1.113730.3.4.3";
/**
* Indicates interest in entries which have been added.
*/
public static final int ADD = 1;
/**
* Indicates interest in entries which have been deleted.
*/
public static final int DELETE = 2;
/**
* Indicates interest in entries which have been modified.
*/
public static final int MODIFY = 4;
/**
* Indicates interest in entries which have been renamed.
*/
public static final int RENAME = 8;
/**
* Indicates interest in entries which have been added, deleted,
* modified or renamed.
*/
public static final int ANY = ADD | DELETE | MODIFY | RENAME;
/**
* The change types of interest. All changes, by default.
*
* @serial
*/
private int changeTypes = ANY;
/**
* Return original entries and changed entries or only changed entries.
*
* @serial
*/
private boolean changesOnly = false;
/**
* Return entry change controls.
*
* @serial
*/
private boolean returnControls = true;
private static final long serialVersionUID = 6335140491154854116L;
/**
* Constructs a persistent search non-critical control.
* The original entries, any changed entries (additions,
* deletions, modifications or renames) and entry change
* controls are requested.
*
* @exception IOException If a BER encoding error occurs.
*/
public PersistentSearchControl() throws IOException {
super(OID);
super.value = setEncodedValue();
}
/**
* Constructs a persistent search control.
*
* @param changeTypes The change types of interest.
* @param changesOnly Return original entries and changed entries
* or only the changed entries.
* @param returnControls Return entry change controls.
* @param criticality The control's criticality.
* @exception IOException If a BER encoding error occurs.
*/
public PersistentSearchControl(int changeTypes, boolean changesOnly,
boolean returnControls, boolean criticality) throws IOException {
super(OID, criticality, null);
this.changeTypes = changeTypes;
this.changesOnly = changesOnly;
this.returnControls = returnControls;
super.value = setEncodedValue();
}
/*
* Sets the ASN.1 BER encoded value of the persistent search control.
* The result is the raw BER bytes including the tag and length of
* the control's value. It does not include the controls OID or criticality.
*
* @return A possibly null byte array representing the ASN.1 BER encoded
* value of the LDAP persistent search control.
* @exception IOException If a BER encoding error occurs.
*/
private byte[] setEncodedValue() throws IOException {
// build the ASN.1 encoding
BerEncoder ber = new BerEncoder(32);
ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
ber.encodeInt(changeTypes);
ber.encodeBoolean(changesOnly);
ber.encodeBoolean(returnControls);
ber.endSeq();
return ber.getTrimmedBuf();
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import javax.naming.NamingEnumeration;
interface ReferralEnumeration<T> extends NamingEnumeration<T> {
void appendUnprocessedReferrals(LdapReferralException ex);
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 1999, 2002, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.ldap.*;
class SearchResultWithControls extends SearchResult implements HasControls {
private Control[] controls;
public SearchResultWithControls(String name, Object obj, Attributes attrs,
boolean isRelative, Control[] controls) {
super(name, obj, attrs, isRelative);
this.controls = controls;
}
public Control[] getControls() throws NamingException {
return controls;
}
private static final long serialVersionUID = 8476983938747908202L;
}

View File

@@ -0,0 +1,310 @@
/*
* Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import java.util.*;
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.spi.NamingManager;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
/**
* This class discovers the location of LDAP services by querying DNS.
* See http://www.ietf.org/internet-drafts/draft-ietf-ldapext-locate-07.txt
*/
class ServiceLocator {
private static final String SRV_RR = "SRV";
private static final String[] SRV_RR_ATTR = new String[]{SRV_RR};
private static final Random random = new Random();
private ServiceLocator() {
}
/**
* Maps a distinguished name (RFC 2253) to a fully qualified domain name.
* Processes a sequence of RDNs having a DC attribute.
* The special RDN "DC=." denotes the root of the domain tree.
* Multi-valued RDNs, non-DC attributes, binary-valued attributes and the
* RDN "DC=." all reset the domain name and processing continues.
*
* @param dn A string distinguished name (RFC 2253).
* @return A domain name or null if none can be derived.
* @throw InvalidNameException If the distinugished name is invalid.
*/
static String mapDnToDomainName(String dn) throws InvalidNameException {
if (dn == null) {
return null;
}
StringBuffer domain = new StringBuffer();
LdapName ldapName = new LdapName(dn);
// process RDNs left-to-right
//List<Rdn> rdnList = ldapName.getRdns();
List<Rdn> rdnList = ldapName.getRdns();
for (int i = rdnList.size() - 1; i >= 0; i--) {
//Rdn rdn = rdnList.get(i);
Rdn rdn = rdnList.get(i);
// single-valued RDN with a DC attribute
if ((rdn.size() == 1) &&
("dc".equalsIgnoreCase(rdn.getType()) )) {
Object attrval = rdn.getValue();
if (attrval instanceof String) {
if (attrval.equals(".") ||
(domain.length() == 1 && domain.charAt(0) == '.')) {
domain.setLength(0); // reset (when current or previous
// RDN value is "DC=.")
}
if (domain.length() > 0) {
domain.append('.');
}
domain.append(attrval);
} else {
domain.setLength(0); // reset (when binary-valued attribute)
}
} else {
domain.setLength(0); // reset (when multi-valued RDN or non-DC)
}
}
return (domain.length() != 0) ? domain.toString() : null;
}
/**
* Locates the LDAP service for a given domain.
* Queries DNS for a list of LDAP Service Location Records (SRV) for a
* given domain name.
*
* @param domainName A string domain name.
* @param environment The possibly null environment of the context.
* @return An ordered list of hostports for the LDAP service or null if
* the service has not been located.
*/
static String[] getLdapService(String domainName, Map<?,?> environment) {
if (environment instanceof Hashtable) {
return getLdapService(domainName, (Hashtable)environment);
}
return getLdapService(domainName, new Hashtable<>(environment));
}
/**
* Locates the LDAP service for a given domain.
* Queries DNS for a list of LDAP Service Location Records (SRV) for a
* given domain name.
*
* @param domainName A string domain name.
* @param environment The possibly null environment of the context.
* @return An ordered list of hostports for the LDAP service or null if
* the service has not been located.
*/
static String[] getLdapService(String domainName, Hashtable<?,?> environment) {
if (domainName == null || domainName.length() == 0) {
return null;
}
String dnsUrl = "dns:///_ldap._tcp." + domainName;
String[] hostports = null;
try {
// Create the DNS context using NamingManager rather than using
// the initial context constructor. This avoids having the initial
// context constructor call itself (when processing the URL
// argument in the getAttributes call).
Context ctx = NamingManager.getURLContext("dns", environment);
if (!(ctx instanceof DirContext)) {
return null; // cannot create a DNS context
}
Attributes attrs =
((DirContext)ctx).getAttributes(dnsUrl, SRV_RR_ATTR);
Attribute attr;
if (attrs != null && ((attr = attrs.get(SRV_RR)) != null)) {
int numValues = attr.size();
int numRecords = 0;
SrvRecord[] srvRecords = new SrvRecord[numValues];
// create the service records
int i = 0;
int j = 0;
while (i < numValues) {
try {
srvRecords[j] = new SrvRecord((String) attr.get(i));
j++;
} catch (Exception e) {
// ignore bad value
}
i++;
}
numRecords = j;
// trim
if (numRecords < numValues) {
SrvRecord[] trimmed = new SrvRecord[numRecords];
System.arraycopy(srvRecords, 0, trimmed, 0, numRecords);
srvRecords = trimmed;
}
// Sort the service records in ascending order of their
// priority value. For records with equal priority, move
// those with weight 0 to the top of the list.
if (numRecords > 1) {
Arrays.sort(srvRecords);
}
// extract the host and port number from each service record
hostports = extractHostports(srvRecords);
}
} catch (NamingException e) {
// ignore
}
return hostports;
}
/**
* Extract hosts and port numbers from a list of SRV records.
* An array of hostports is returned or null if none were found.
*/
private static String[] extractHostports(SrvRecord[] srvRecords) {
String[] hostports = null;
int head = 0;
int tail = 0;
int sublistLength = 0;
int k = 0;
for (int i = 0; i < srvRecords.length; i++) {
if (hostports == null) {
hostports = new String[srvRecords.length];
}
// find the head and tail of the list of records having the same
// priority value.
head = i;
while (i < srvRecords.length - 1 &&
srvRecords[i].priority == srvRecords[i + 1].priority) {
i++;
}
tail = i;
// select hostports from the sublist
sublistLength = (tail - head) + 1;
for (int j = 0; j < sublistLength; j++) {
hostports[k++] = selectHostport(srvRecords, head, tail);
}
}
return hostports;
}
/*
* Randomly select a service record in the range [head, tail] and return
* its hostport value. Follows the algorithm in RFC 2782.
*/
private static String selectHostport(SrvRecord[] srvRecords, int head,
int tail) {
if (head == tail) {
return srvRecords[head].hostport;
}
// compute the running sum for records between head and tail
int sum = 0;
for (int i = head; i <= tail; i++) {
if (srvRecords[i] != null) {
sum += srvRecords[i].weight;
srvRecords[i].sum = sum;
}
}
String hostport = null;
// If all records have zero weight, select first available one;
// otherwise, randomly select a record according to its weight
int target = (sum == 0 ? 0 : random.nextInt(sum + 1));
for (int i = head; i <= tail; i++) {
if (srvRecords[i] != null && srvRecords[i].sum >= target) {
hostport = srvRecords[i].hostport;
srvRecords[i] = null; // make this record unavailable
break;
}
}
return hostport;
}
/**
* This class holds a DNS service (SRV) record.
* See http://www.ietf.org/rfc/rfc2782.txt
*/
static class SrvRecord implements Comparable<SrvRecord> {
int priority;
int weight;
int sum;
String hostport;
/**
* Creates a service record object from a string record.
* DNS supplies the string record in the following format:
* <pre>
* <Priority> " " <Weight> " " <Port> " " <Host>
* </pre>
*/
SrvRecord(String srvRecord) throws Exception {
StringTokenizer tokenizer = new StringTokenizer(srvRecord, " ");
String port;
if (tokenizer.countTokens() == 4) {
priority = Integer.parseInt(tokenizer.nextToken());
weight = Integer.parseInt(tokenizer.nextToken());
port = tokenizer.nextToken();
hostport = tokenizer.nextToken() + ":" + port;
} else {
throw new IllegalArgumentException();
}
}
/*
* Sort records in ascending order of priority value. For records with
* equal priority move those with weight 0 to the top of the list.
*/
public int compareTo(SrvRecord that) {
if (priority > that.priority) {
return 1; // this > that
} else if (priority < that.priority) {
return -1; // this < that
} else if (weight == 0 && that.weight != 0) {
return -1; // this < that
} else if (weight != 0 && that.weight == 0) {
return 1; // this > that
} else {
return 0; // this == that
}
}
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import java.util.Arrays; // JDK1.2
import java.io.OutputStream;
import javax.naming.ldap.Control;
/**
* Represents the identity of a 'simple' authenticated LDAP connection.
* In addition to ClientId information, this class contains also the
* username and password.
*
* @author Rosanna Lee
*/
class SimpleClientId extends ClientId {
final private String username;
final private Object passwd;
final private int myHash;
SimpleClientId(int version, String hostname, int port,
String protocol, Control[] bindCtls, OutputStream trace,
String socketFactory, String username, Object passwd) {
super(version, hostname, port, protocol, bindCtls, trace,
socketFactory);
this.username = username;
int pwdHashCode = 0;
if (passwd == null) {
this.passwd = null;
} else if (passwd instanceof byte[]) {
this.passwd = ((byte[])passwd).clone();
pwdHashCode = Arrays.hashCode((byte[])passwd);
} else if (passwd instanceof char[]) {
this.passwd = ((char[])passwd).clone();
pwdHashCode = Arrays.hashCode((char[])passwd);
} else {
this.passwd = passwd;
pwdHashCode = passwd.hashCode();
}
myHash = super.hashCode()
^ (username != null ? username.hashCode() : 0)
^ pwdHashCode;
}
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof SimpleClientId)) {
return false;
}
SimpleClientId other = (SimpleClientId)obj;
return super.equals(obj)
&& (username == other.username // null OK
|| (username != null && username.equals(other.username)))
&& ((passwd == other.passwd) // null OK
|| (passwd != null && other.passwd != null
&& (((passwd instanceof String) && passwd.equals(other.passwd))
|| ((passwd instanceof byte[])
&& (other.passwd instanceof byte[])
&& Arrays.equals((byte[])passwd, (byte[])other.passwd))
|| ((passwd instanceof char[])
&& (other.passwd instanceof char[])
&& Arrays.equals((char[])passwd, (char[])other.passwd)))));
}
public int hashCode() {
return myHash;
}
public String toString() {
return super.toString() + ":" + username; // omit password for security
}
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import javax.naming.ldap.UnsolicitedNotification;
import javax.naming.NamingException;
import javax.naming.ldap.Control;
import java.util.Vector;
/**
* A concrete implementation of an UnsolicitedNotification.
* @author Rosanna Lee
*/
final class UnsolicitedResponseImpl implements UnsolicitedNotification {
private String oid;
private String[] referrals;
private byte[] extensionValue;
private NamingException exception;
private Control[] controls;
UnsolicitedResponseImpl(String oid, byte[] berVal, Vector<Vector<String>> ref,
int status, String msg, String matchedDN, Control[] controls) {
this.oid = oid;
this.extensionValue = berVal;
if (ref != null && ref.size() > 0) {
int len = ref.size();
referrals = new String[len];
for (int i = 0; i < len; i++) {
// ref is a list of single-String Vectors
referrals[i] = ref.elementAt(i).elementAt(0);
}
}
exception = LdapCtx.mapErrorCode(status, msg);
// matchedDN ignored for now; could be used to set resolvedName
// exception.setResolvedName(new CompositeName().add(matchedDN));
this.controls = controls;
}
/**
* Retrieves the object identifier of the response.
*
* @return A possibly null object identifier string representing the LDAP
* <tt>ExtendedResponse.responseName</tt> component.
*/
public String getID() {
return oid;
}
/**
* Retrieves the ASN.1 BER encoded value of the LDAP extended operation
* response. Null is returned if the value is absent from the response
* sent by the LDAP server.
* The result is the raw BER bytes including the tag and length of
* the response value. It does not include the response OID.
*
* @return A possibly null byte array representing the ASN.1 BER encoded
* contents of the LDAP <tt>ExtendedResponse.response</tt>
* component.
*/
public byte[] getEncodedValue() {
return extensionValue;
}
/**
* Retrieves the referral(s) sent by the server.
*
* @return A possibly null array of referrals, each of which is represented
* by a URL string. If null, no referral was sent by the server.
*/
public String[] getReferrals() {
return referrals;
}
/**
* Retrieves the exception as constructed using information
* sent by the server.
* @return A possibly null exception as constructed using information
* sent by the server. If null, a "success" status was indicated by
* the server.
*/
public NamingException getException() {
return exception;
}
public Control[] getControls() throws NamingException {
return controls;
}
private static final long serialVersionUID = 5913778898401784775L;
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import java.net.MalformedURLException;
import java.net.URL;
abstract class VersionHelper {
private static VersionHelper helper = null;
VersionHelper() {} // Disallow anyone from creating one of these.
static {
try {
Class.forName("java.net.URLClassLoader"); // 1.2 test
Class.forName("java.security.PrivilegedAction"); // 1.2 test
helper = (VersionHelper)
Class.forName(
"com.sun.jndi.ldap.VersionHelper12").newInstance();
} catch (Exception e) {
}
// Use 1.1 helper if 1.2 test fails, or if we cannot create 1.2 helper
if (helper == null) {
try {
helper = (VersionHelper)
Class.forName(
"com.sun.jndi.ldap.VersionHelper11").newInstance();
} catch (Exception e) {
// should never happen
}
}
}
static VersionHelper getVersionHelper() {
return helper;
}
abstract ClassLoader getURLClassLoader(String[] url)
throws MalformedURLException;
static protected URL[] getUrlArray(String[] url) throws MalformedURLException {
URL[] urlArray = new URL[url.length];
for (int i = 0; i < urlArray.length; i++) {
urlArray[i] = new URL(url[i]);
}
return urlArray;
}
abstract Class<?> loadClass(String className) throws ClassNotFoundException;
abstract Thread createThread(Runnable r);
}

View File

@@ -0,0 +1,128 @@
/*
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap;
import java.net.URLClassLoader;
import java.net.MalformedURLException;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import sun.misc.SharedSecrets;
final class VersionHelper12 extends VersionHelper {
// System property to control whether classes may be loaded from an
// arbitrary URL code base.
private static final String TRUST_URL_CODEBASE_PROPERTY =
"com.sun.jndi.ldap.object.trustURLCodebase";
// System property to control whether classes are allowed to be loaded from
// 'javaSerializedData', 'javaRemoteLocation' or 'javaReferenceAddress' attributes.
private static final String TRUST_SERIAL_DATA_PROPERTY =
"com.sun.jndi.ldap.object.trustSerialData";
/**
* Determines whether objects may be deserialized or reconstructed from a content of
* 'javaSerializedData', 'javaRemoteLocation' or 'javaReferenceAddress' LDAP attributes.
*/
private static final boolean trustSerialData;
// Determine whether classes may be loaded from an arbitrary URL code base.
private static final boolean trustURLCodebase;
static {
String trust = getPrivilegedProperty(TRUST_URL_CODEBASE_PROPERTY, "false");
trustURLCodebase = "true".equalsIgnoreCase(trust);
String trustSDString = getPrivilegedProperty(TRUST_SERIAL_DATA_PROPERTY, "false");
trustSerialData = "true".equalsIgnoreCase(trustSDString);
}
private static String getPrivilegedProperty(String propertyName, String defaultVal) {
PrivilegedAction<String> action = () -> System.getProperty(propertyName, defaultVal);
if (System.getSecurityManager() == null) {
return action.run();
} else {
return AccessController.doPrivileged(action);
}
}
VersionHelper12() {} // Disallow external from creating one of these.
/**
* Returns true if deserialization or reconstruction of objects from
* 'javaSerializedData', 'javaRemoteLocation' and 'javaReferenceAddress'
* LDAP attributes is allowed.
*
* @return true if deserialization is allowed; false - otherwise
*/
public static boolean isSerialDataAllowed() {
return trustSerialData;
}
ClassLoader getURLClassLoader(String[] url)
throws MalformedURLException {
ClassLoader parent = getContextClassLoader();
/*
* Classes may only be loaded from an arbitrary URL code base when
* the system property com.sun.jndi.ldap.object.trustURLCodebase
* has been set to "true".
*/
if (url != null && trustURLCodebase) {
return URLClassLoader.newInstance(getUrlArray(url), parent);
} else {
return parent;
}
}
Class<?> loadClass(String className) throws ClassNotFoundException {
ClassLoader cl = getContextClassLoader();
return Class.forName(className, true, cl);
}
private ClassLoader getContextClassLoader() {
return AccessController.doPrivileged(
new PrivilegedAction<ClassLoader>() {
public ClassLoader run() {
return Thread.currentThread().getContextClassLoader();
}
}
);
}
Thread createThread(final Runnable r) {
final AccessControlContext acc = AccessController.getContext();
// 4290486: doPrivileged is needed to create a thread in
// an environment that restricts "modifyThreadGroup".
return AccessController.doPrivileged(
new PrivilegedAction<Thread>() {
public Thread run() {
return SharedSecrets.getJavaLangAccess()
.newThreadWithAcc(r, acc);
}
}
);
}
}

View File

@@ -0,0 +1,471 @@
/*
* Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap.ext;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.security.cert.CertificateException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.HostnameVerifier;
import sun.security.util.HostnameChecker;
import javax.naming.ldap.*;
import com.sun.jndi.ldap.Connection;
/**
* This class implements the LDAPv3 Extended Response for StartTLS as
* defined in
* <a href="http://www.ietf.org/rfc/rfc2830.txt">Lightweight Directory
* Access Protocol (v3): Extension for Transport Layer Security</a>
*
* The object identifier for StartTLS is 1.3.6.1.4.1.1466.20037
* and no extended response value is defined.
*
*<p>
* The Start TLS extended request and response are used to establish
* a TLS connection over the existing LDAP connection associated with
* the JNDI context on which <tt>extendedOperation()</tt> is invoked.
*
* @see StartTlsRequest
* @author Vincent Ryan
*/
final public class StartTlsResponseImpl extends StartTlsResponse {
private static final boolean debug = false;
/*
* The dNSName type in a subjectAltName extension of an X.509 certificate
*/
private static final int DNSNAME_TYPE = 2;
/*
* The server's hostname.
*/
private transient String hostname = null;
/*
* The LDAP socket.
*/
private transient Connection ldapConnection = null;
/*
* The original input stream.
*/
private transient InputStream originalInputStream = null;
/*
* The original output stream.
*/
private transient OutputStream originalOutputStream = null;
/*
* The SSL socket.
*/
private transient SSLSocket sslSocket = null;
/*
* The SSL socket factories.
*/
private transient SSLSocketFactory defaultFactory = null;
private transient SSLSocketFactory currentFactory = null;
/*
* The list of cipher suites to be enabled.
*/
private transient String[] suites = null;
/*
* The hostname verifier callback.
*/
private transient HostnameVerifier verifier = null;
/*
* The flag to indicate that the TLS connection is closed.
*/
private transient boolean isClosed = true;
private static final long serialVersionUID = -1126624615143411328L;
// public no-arg constructor required by JDK's Service Provider API.
public StartTlsResponseImpl() {}
/**
* Overrides the default list of cipher suites enabled for use on the
* TLS connection. The cipher suites must have already been listed by
* <tt>SSLSocketFactory.getSupportedCipherSuites()</tt> as being supported.
* Even if a suite has been enabled, it still might not be used because
* the peer does not support it, or because the requisite certificates
* (and private keys) are not available.
*
* @param suites The non-null list of names of all the cipher suites to
* enable.
* @see #negotiate
*/
public void setEnabledCipherSuites(String[] suites) {
// The impl does accept null suites, although the spec requires
// a non-null list.
this.suites = suites == null ? null : suites.clone();
}
/**
* Overrides the default hostname verifier used by <tt>negotiate()</tt>
* after the TLS handshake has completed. If
* <tt>setHostnameVerifier()</tt> has not been called before
* <tt>negotiate()</tt> is invoked, <tt>negotiate()</tt>
* will perform a simple case ignore match. If called after
* <tt>negotiate()</tt>, this method does not do anything.
*
* @param verifier The non-null hostname verifier callback.
* @see #negotiate
*/
public void setHostnameVerifier(HostnameVerifier verifier) {
this.verifier = verifier;
}
/**
* Negotiates a TLS session using the default SSL socket factory.
* <p>
* This method is equivalent to <tt>negotiate(null)</tt>.
*
* @return The negotiated SSL session
* @throw IOException If an IO error was encountered while establishing
* the TLS session.
* @see #setEnabledCipherSuites
* @see #setHostnameVerifier
*/
public SSLSession negotiate() throws IOException {
return negotiate(null);
}
/**
* Negotiates a TLS session using an SSL socket factory.
* <p>
* Creates an SSL socket using the supplied SSL socket factory and
* attaches it to the existing connection. Performs the TLS handshake
* and returns the negotiated session information.
* <p>
* If cipher suites have been set via <tt>setEnabledCipherSuites</tt>
* then they are enabled before the TLS handshake begins.
* <p>
* Hostname verification is performed after the TLS handshake completes.
* The default check performs a case insensitive match of the server's
* hostname against that in the server's certificate. The server's
* hostname is extracted from the subjectAltName in the server's
* certificate (if present). Otherwise the value of the common name
* attribute of the subject name is used. If a callback has
* been set via <tt>setHostnameVerifier</tt> then that verifier is used if
* the default check fails.
* <p>
* If an error occurs then the SSL socket is closed and an IOException
* is thrown. The underlying connection remains intact.
*
* @param factory The possibly null SSL socket factory to use.
* If null, the default SSL socket factory is used.
* @return The negotiated SSL session
* @throw IOException If an IO error was encountered while establishing
* the TLS session.
* @see #setEnabledCipherSuites
* @see #setHostnameVerifier
*/
public SSLSession negotiate(SSLSocketFactory factory) throws IOException {
if (isClosed && sslSocket != null) {
throw new IOException("TLS connection is closed.");
}
if (factory == null) {
factory = getDefaultFactory();
}
if (debug) {
System.out.println("StartTLS: About to start handshake");
}
SSLSession sslSession = startHandshake(factory).getSession();
if (debug) {
System.out.println("StartTLS: Completed handshake");
}
SSLPeerUnverifiedException verifExcep = null;
try {
if (verify(hostname, sslSession)) {
isClosed = false;
return sslSession;
}
} catch (SSLPeerUnverifiedException e) {
// Save to return the cause
verifExcep = e;
}
if ((verifier != null) &&
verifier.verify(hostname, sslSession)) {
isClosed = false;
return sslSession;
}
// Verification failed
close();
sslSession.invalidate();
if (verifExcep == null) {
verifExcep = new SSLPeerUnverifiedException(
"hostname of the server '" + hostname +
"' does not match the hostname in the " +
"server's certificate.");
}
throw verifExcep;
}
/**
* Closes the TLS connection gracefully and reverts back to the underlying
* connection.
*
* @throw IOException If an IO error was encountered while closing the
* TLS connection
*/
public void close() throws IOException {
if (isClosed) {
return;
}
if (debug) {
System.out.println("StartTLS: replacing SSL " +
"streams with originals");
}
// Replace SSL streams with the original streams
ldapConnection.replaceStreams(
originalInputStream, originalOutputStream, false);
if (debug) {
System.out.println("StartTLS: closing SSL Socket");
}
sslSocket.close();
isClosed = true;
}
/**
* Sets the connection for TLS to use. The TLS connection will be attached
* to this connection.
*
* @param ldapConnection The non-null connection to use.
* @param hostname The server's hostname. If null, the hostname used to
* open the connection will be used instead.
*/
public void setConnection(Connection ldapConnection, String hostname) {
this.ldapConnection = ldapConnection;
this.hostname = (hostname == null || hostname.isEmpty())
? ldapConnection.host : hostname;
originalInputStream = ldapConnection.inStream;
originalOutputStream = ldapConnection.outStream;
}
/*
* Returns the default SSL socket factory.
*
* @return The default SSL socket factory.
* @throw IOException If TLS is not supported.
*/
private SSLSocketFactory getDefaultFactory() throws IOException {
if (defaultFactory != null) {
return defaultFactory;
}
return (defaultFactory =
(SSLSocketFactory) SSLSocketFactory.getDefault());
}
/*
* Start the TLS handshake and manipulate the input and output streams.
*
* @param factory The SSL socket factory to use.
* @return The SSL socket.
* @throw IOException If an exception occurred while performing the
* TLS handshake.
*/
private SSLSocket startHandshake(SSLSocketFactory factory)
throws IOException {
if (ldapConnection == null) {
throw new IllegalStateException("LDAP connection has not been set."
+ " TLS requires an existing LDAP connection.");
}
if (factory != currentFactory) {
// Create SSL socket layered over the existing connection
sslSocket = (SSLSocket) factory.createSocket(ldapConnection.sock,
ldapConnection.host, ldapConnection.port, false);
currentFactory = factory;
if (debug) {
System.out.println("StartTLS: Created socket : " + sslSocket);
}
}
if (suites != null) {
sslSocket.setEnabledCipherSuites(suites);
if (debug) {
System.out.println("StartTLS: Enabled cipher suites");
}
}
// Connection must be quite for handshake to proceed
try {
if (debug) {
System.out.println(
"StartTLS: Calling sslSocket.startHandshake");
}
sslSocket.startHandshake();
if (debug) {
System.out.println(
"StartTLS: + Finished sslSocket.startHandshake");
}
// Replace original streams with the new SSL streams
ldapConnection.replaceStreams(sslSocket.getInputStream(),
sslSocket.getOutputStream(), true);
if (debug) {
System.out.println("StartTLS: Replaced IO Streams");
}
} catch (IOException e) {
if (debug) {
System.out.println("StartTLS: Got IO error during handshake");
e.printStackTrace();
}
sslSocket.close();
isClosed = true;
throw e; // pass up exception
}
return sslSocket;
}
/*
* Verifies that the hostname in the server's certificate matches the
* hostname of the server.
* The server's first certificate is examined. If it has a subjectAltName
* that contains a dNSName then that is used as the server's hostname.
* The server's hostname may contain a wildcard for its left-most name part.
* Otherwise, if the certificate has no subjectAltName then the value of
* the common name attribute of the subject name is used.
*
* @param hostname The hostname of the server.
* @param session the SSLSession used on the connection to host.
* @return true if the hostname is verified, false otherwise.
*/
private boolean verify(String hostname, SSLSession session)
throws SSLPeerUnverifiedException {
java.security.cert.Certificate[] certs = null;
// if IPv6 strip off the "[]"
if (hostname != null && hostname.startsWith("[") &&
hostname.endsWith("]")) {
hostname = hostname.substring(1, hostname.length() - 1);
}
try {
HostnameChecker checker = HostnameChecker.getInstance(
HostnameChecker.TYPE_LDAP);
// Use ciphersuite to determine whether Kerberos is active.
if (session.getCipherSuite().startsWith("TLS_KRB5")) {
Principal principal = getPeerPrincipal(session);
if (!HostnameChecker.match(hostname, principal)) {
throw new SSLPeerUnverifiedException(
"hostname of the kerberos principal:" + principal +
" does not match the hostname:" + hostname);
}
} else { // X.509
// get the subject's certificate
certs = session.getPeerCertificates();
X509Certificate peerCert;
if (certs[0] instanceof java.security.cert.X509Certificate) {
peerCert = (java.security.cert.X509Certificate) certs[0];
} else {
throw new SSLPeerUnverifiedException(
"Received a non X509Certificate from the server");
}
checker.match(hostname, peerCert);
}
// no exception means verification passed
return true;
} catch (SSLPeerUnverifiedException e) {
/*
* The application may enable an anonymous SSL cipher suite, and
* hostname verification is not done for anonymous ciphers
*/
String cipher = session.getCipherSuite();
if (cipher != null && (cipher.indexOf("_anon_") != -1)) {
return true;
}
throw e;
} catch (CertificateException e) {
/*
* Pass up the cause of the failure
*/
throw(SSLPeerUnverifiedException)
new SSLPeerUnverifiedException("hostname of the server '" +
hostname +
"' does not match the hostname in the " +
"server's certificate.").initCause(e);
}
}
/*
* Get the peer principal from the session
*/
private static Principal getPeerPrincipal(SSLSession session)
throws SSLPeerUnverifiedException {
Principal principal;
try {
principal = session.getPeerPrincipal();
} catch (AbstractMethodError e) {
// if the JSSE provider does not support it, return null, since
// we need it only for Kerberos.
principal = null;
}
return principal;
}
}

View File

@@ -0,0 +1,160 @@
/*
* Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap.pool;
/**
* Represents a description of PooledConnection in Connections.
* Contains a PooledConnection, its state (busy, idle, expired), and idle time.
*
* Any access or update to a descriptor's state is synchronized.
*
* @author Rosanna Lee
*/
final class ConnectionDesc {
private final static boolean debug = Pool.debug;
// Package private because used by Pool.showStats()
static final byte BUSY = (byte)0;
static final byte IDLE = (byte)1;
static final byte EXPIRED = (byte)2;
final private PooledConnection conn;
private byte state = IDLE; // initial state
private long idleSince;
private long useCount = 0; // for stats & debugging only
ConnectionDesc(PooledConnection conn) {
this.conn = conn;
}
ConnectionDesc(PooledConnection conn, boolean use) {
this.conn = conn;
if (use) {
state = BUSY;
++useCount;
}
}
/**
* Two desc are equal if their PooledConnections are the same.
* This is useful when searching for a ConnectionDesc using only its
* PooledConnection.
*/
public boolean equals(Object obj) {
return obj != null
&& obj instanceof ConnectionDesc
&& ((ConnectionDesc)obj).conn == conn;
}
/**
* Hashcode is that of PooledConnection to facilitate
* searching for a ConnectionDesc using only its PooledConnection.
*/
public int hashCode() {
return conn.hashCode();
}
/**
* Changes the state of a ConnectionDesc from BUSY to IDLE and
* records the current time so that we will know how long it has been idle.
* @return true if state change occurred.
*/
synchronized boolean release() {
d("release()");
if (state == BUSY) {
state = IDLE;
idleSince = System.currentTimeMillis();
return true; // Connection released, ready for reuse
} else {
return false; // Connection wasn't busy to begin with
}
}
/**
* If ConnectionDesc is IDLE, change its state to BUSY and return
* its connection.
*
* @return ConnectionDesc's PooledConnection if it was idle; null otherwise.
*/
synchronized PooledConnection tryUse() {
d("tryUse()");
if (state == IDLE) {
state = BUSY;
++useCount;
return conn;
}
return null;
}
/**
* If ConnectionDesc is IDLE and has expired, close the corresponding
* PooledConnection.
*
* @param threshold a connection that has been idle before this time
* have expired.
*
* @return true if entry is idle and has expired; false otherwise.
*/
synchronized boolean expire(long threshold) {
if (state == IDLE && idleSince < threshold) {
d("expire(): expired");
state = EXPIRED;
conn.closeConnection(); // Close real connection
return true; // Expiration successful
} else {
d("expire(): not expired");
return false; // Expiration did not occur
}
}
public String toString() {
return conn.toString() + " " +
(state == BUSY ? "busy" : (state == IDLE ? "idle" : "expired"));
}
// Used by Pool.showStats()
int getState() {
return state;
}
// Used by Pool.showStats()
long getUseCount() {
return useCount;
}
private void d(String msg) {
if (debug) {
System.err.println("ConnectionDesc." + msg + " " + toString());
}
}
}

View File

@@ -0,0 +1,391 @@
/*
* Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap.pool;
import java.util.ArrayList; // JDK 1.2
import java.util.List;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import javax.naming.NamingException;
import javax.naming.InterruptedNamingException;
import javax.naming.CommunicationException;
/**
* Represents a list of PooledConnections (actually, ConnectionDescs) with the
* same pool id.
* The list starts out with an initial number of connections.
* Additional PooledConnections are created lazily upon demand.
* The list has a maximum size. When the number of connections
* reaches the maximum size, a request for a PooledConnection blocks until
* a connection is returned to the list. A maximum size of zero means that
* there is no maximum: connection creation will be attempted when
* no idle connection is available.
*
* The list may also have a preferred size. If the current list size
* is less than the preferred size, a request for a connection will result in
* a PooledConnection being created (even if an idle connection is available).
* If the current list size is greater than the preferred size,
* a connection being returned to the list will be closed and removed from
* the list. A preferred size of zero means that there is no preferred size:
* connections are created only when no idle connection is available and
* a connection being returned to the list is not closed. Regardless of the
* preferred size, connection creation always observes the maximum size:
* a connection won't be created if the list size is at or exceeds the
* maximum size.
*
* @author Rosanna Lee
*/
// Package private: accessed only by Pool
final class Connections implements PoolCallback {
private static final boolean debug = Pool.debug;
private static final boolean trace =
com.sun.jndi.ldap.LdapPoolManager.trace;
private static final int DEFAULT_SIZE = 10;
final private int maxSize;
final private int prefSize;
final private List<ConnectionDesc> conns;
private boolean closed = false; // Closed for business
private Reference<Object> ref; // maintains reference to id to prevent premature GC
/**
* @param id the identity (connection request) of the connections in the list
* @param initSize the number of connections to create initially
* @param prefSize the preferred size of the pool. The pool will try
* to maintain a pool of this size by creating and closing connections
* as needed.
* @param maxSize the maximum size of the pool. The pool will not exceed
* this size. If the pool is at this size, a request for a connection
* will block until an idle connection is released to the pool or
* when one is removed.
* @param factory The factory responsible for creating a connection
*/
Connections(Object id, int initSize, int prefSize, int maxSize,
PooledConnectionFactory factory) throws NamingException {
this.maxSize = maxSize;
if (maxSize > 0) {
// prefSize and initSize cannot exceed specified maxSize
this.prefSize = Math.min(prefSize, maxSize);
initSize = Math.min(initSize, maxSize);
} else {
this.prefSize = prefSize;
}
conns = new ArrayList<>(maxSize > 0 ? maxSize : DEFAULT_SIZE);
// Maintain soft ref to id so that this Connections' entry in
// Pool doesn't get GC'ed prematurely
ref = new SoftReference<>(id);
d("init size=", initSize);
d("max size=", maxSize);
d("preferred size=", prefSize);
// Create initial connections
PooledConnection conn;
for (int i = 0; i < initSize; i++) {
conn = factory.createPooledConnection(this);
td("Create ", conn ,factory);
conns.add(new ConnectionDesc(conn)); // Add new idle conn to pool
}
}
/**
* Retrieves a PooledConnection from this list of connections.
* Use an existing one if one is idle, or create one if the list's
* max size hasn't been reached. If max size has been reached, wait
* for a PooledConnection to be returned, or one to be removed (thus
* not reaching the max size any longer).
*
* @param timeout if > 0, msec to wait until connection is available
* @param factory creates the PooledConnection if one needs to be created
*
* @return A non-null PooledConnection
* @throws NamingException PooledConnection cannot be created, because this
* thread was interrupted while it waited for an available connection,
* or if it timed out while waiting, or the creation of a connection
* resulted in an error.
*/
synchronized PooledConnection get(long timeout,
PooledConnectionFactory factory) throws NamingException {
PooledConnection conn;
long start = (timeout > 0 ? System.currentTimeMillis() : 0);
long waittime = timeout;
d("get(): before");
while ((conn = getOrCreateConnection(factory)) == null) {
if (timeout > 0 && waittime <= 0) {
throw new CommunicationException(
"Timeout exceeded while waiting for a connection: " +
timeout + "ms");
}
try {
d("get(): waiting");
if (waittime > 0) {
wait(waittime); // Wait until one is released or removed
} else {
wait();
}
} catch (InterruptedException e) {
throw new InterruptedNamingException(
"Interrupted while waiting for a connection");
}
// Check whether we timed out
if (timeout > 0) {
long now = System.currentTimeMillis();
waittime = timeout - (now - start);
}
}
d("get(): after");
return conn;
}
/**
* Retrieves an idle connection from this list if one is available.
* If none is available, create a new one if maxSize hasn't been reached.
* If maxSize has been reached, return null.
* Always called from a synchronized method.
*/
private PooledConnection getOrCreateConnection(
PooledConnectionFactory factory) throws NamingException {
int size = conns.size(); // Current number of idle/nonidle conns
PooledConnection conn = null;
if (prefSize <= 0 || size >= prefSize) {
// If no prefSize specified, or list size already meets or
// exceeds prefSize, then first look for an idle connection
ConnectionDesc entry;
for (int i = 0; i < size; i++) {
entry = conns.get(i);
if ((conn = entry.tryUse()) != null) {
d("get(): use ", conn);
td("Use ", conn);
return conn;
}
}
}
// Check if list size already at maxSize specified
if (maxSize > 0 && size >= maxSize) {
return null; // List size is at limit; cannot create any more
}
conn = factory.createPooledConnection(this);
td("Create and use ", conn, factory);
conns.add(new ConnectionDesc(conn, true)); // Add new conn to pool
return conn;
}
/**
* Releases connection back into list.
* If the list size is below prefSize, the connection may be reused.
* If the list size exceeds prefSize, then the connection is closed
* and removed from the list.
*
* public because implemented as part of PoolCallback.
*/
public synchronized boolean releasePooledConnection(PooledConnection conn) {
ConnectionDesc entry;
int loc = conns.indexOf(entry=new ConnectionDesc(conn));
d("release(): ", conn);
if (loc >= 0) {
// Found entry
if (closed || (prefSize > 0 && conns.size() > prefSize)) {
// If list size exceeds prefSize, close connection
d("release(): closing ", conn);
td("Close ", conn);
// size must be >= 2 so don't worry about empty list
conns.remove(entry);
conn.closeConnection();
} else {
d("release(): release ", conn);
td("Release ", conn);
// Get ConnectionDesc from list to get correct state info
entry = conns.get(loc);
// Return connection to list, ready for reuse
entry.release();
}
notifyAll();
d("release(): notify");
return true;
} else {
return false;
}
}
/**
* Removes PooledConnection from list of connections.
* The closing of the connection is separate from this method.
* This method is called usually when the caller encouters an error
* when using the connection and wants it removed from the pool.
*
* @return true if conn removed; false if it was not in pool
*
* public because implemented as part of PoolCallback.
*/
public synchronized boolean removePooledConnection(PooledConnection conn) {
if (conns.remove(new ConnectionDesc(conn))) {
d("remove(): ", conn);
notifyAll();
d("remove(): notify");
td("Remove ", conn);
if (conns.isEmpty()) {
// Remove softref to make pool entry eligible for GC.
// Once ref has been removed, it cannot be reinstated.
ref = null;
}
return true;
} else {
d("remove(): not found ", conn);
return false;
}
}
/**
* Goes through all entries in list, removes and closes ones that have been
* idle before threshold.
*
* @param threshold an entry idle since this time has expired.
* @return true if no more connections in list
*/
boolean expire(long threshold) {
List<ConnectionDesc> clonedConns;
synchronized(this) {
clonedConns = new ArrayList<>(conns);
}
List<ConnectionDesc> expired = new ArrayList<>();
for (ConnectionDesc entry : clonedConns) {
d("expire(): ", entry);
if (entry.expire(threshold)) {
expired.add(entry);
td("expire(): Expired ", entry);
}
}
synchronized (this) {
conns.removeAll(expired);
// Don't need to call notify() because we're
// removing only idle connections. If there were
// idle connections, then there should be no waiters.
return conns.isEmpty(); // whether whole list has 'expired'
}
}
/**
* Called when this instance of Connections has been removed from Pool.
* This means that no one can get any pooled connections from this
* Connections any longer. Expire all idle connections as of 'now'
* and leave indicator so that any in-use connections will be closed upon
* their return.
*/
synchronized void close() {
expire(System.currentTimeMillis()); // Expire idle connections
closed = true; // Close in-use connections when they are returned
}
String getStats() {
int idle = 0;
int busy = 0;
int expired = 0;
long use = 0;
int len;
synchronized (this) {
len = conns.size();
ConnectionDesc entry;
for (int i = 0; i < len; i++) {
entry = conns.get(i);
use += entry.getUseCount();
switch (entry.getState()) {
case ConnectionDesc.BUSY:
++busy;
break;
case ConnectionDesc.IDLE:
++idle;
break;
case ConnectionDesc.EXPIRED:
++expired;
}
}
}
return "size=" + len + "; use=" + use + "; busy=" + busy
+ "; idle=" + idle + "; expired=" + expired;
}
private void d(String msg, Object o1) {
if (debug) {
d(msg + o1);
}
}
private void d(String msg, int i) {
if (debug) {
d(msg + i);
}
}
private void d(String msg) {
if (debug) {
System.err.println(this + "." + msg + "; size: " + conns.size());
}
}
private void td(String msg, Object o1, Object o2) {
if (trace) { // redo test to avoid object creation
td(msg + o1 + "[" + o2 + "]");
}
}
private void td(String msg, Object o1) {
if (trace) { // redo test to avoid object creation
td(msg + o1);
}
}
private void td(String msg) {
if (trace) {
System.err.println(msg);
}
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) 2002, 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap.pool;
/**
* Is a reference to Connections that is stored in Pool.
* This is an intermediate object that is outside of the circular
* reference loop of
* com.sun.jndi.ldap.Connection <-> com.sun.jndi.ldap.LdapClient
* <-> com.sun.jndi.ldap.pool.Connections
*
* Because Connection is a daemon thread, it will keep LdapClient
* alive until LdapClient closes Connection. This will in turn
* keep Connections alive. So even when Connections is removed
* from (the WeakHashMap of) Pool, it won't be finalized.
* ConnectionsRef acts as Connections's finalizer.
*
* Without connection pooling, com.sun.jndi.ldap.LdapCtx's finalize()
* closes LdapClient, which in turn closes Connection.
* With connection pooling, ConnectionsRef's finalize() calls
* Connections.close(), which in turn will close all idle connections
* and mark Connections such that in-use connections will be closed
* when they are returned to the pool.
*/
final class ConnectionsRef {
final private Connections conns;
ConnectionsRef(Connections conns) {
this.conns = conns;
}
Connections getConnections() {
return conns;
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap.pool;
import java.lang.ref.WeakReference;
import java.lang.ref.ReferenceQueue;
/*
* This class defines a WeakReference to the ConnectionRef (the referent).
*
* The ConnectionRef enables to break the reference
* cycle between Connection, LdapClient, Connections and ConnectionDesc,
* shown in the figure below.
*
* -------> Connections -----> ConnectionDesc
* | ^ |
* | | |
* | | |
* ConnectionsRef LdapClient <------------
* ^ | ^
* : | |
* : v |
* ConnectionsWeakRef Connection
*
* The ConnectionsRef is for cleaning up the resources held by the
* Connection thread by making them available to the GC. The pool
* uses ConnectionRef to hold the pooled resources.
*
* This class in turn holds a WeakReference with a ReferenceQueue to the
* ConnectionRef to track when the ConnectionRef becomes ready
* for getting GC'ed. It extends from WeakReference in order to hold a
* reference to Connections used for closing (which in turn terminates
* the Connection thread) it by monitoring the ReferenceQueue.
*/
class ConnectionsWeakRef extends WeakReference<ConnectionsRef> {
private final Connections conns;
ConnectionsWeakRef (ConnectionsRef connsRef,
ReferenceQueue<? super ConnectionsRef> queue) {
super(connsRef, queue);
this.conns = connsRef.getConnections();
}
Connections getConnections() {
return conns;
}
}

View File

@@ -0,0 +1,254 @@
/*
* Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap.pool;
import java.util.ArrayList;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.io.PrintStream;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import javax.naming.NamingException;
/**
* A map of pool ids to Connections.
* Key is an object that uniquely identifies a PooledConnection request
* (typically information needed to create the connection).
* The definitions of the key's equals() and hashCode() methods are
* vital to its unique identification in a Pool.
*
* Value is a ConnectionsRef, which is a reference to Connections,
* a list of equivalent connections.
*
* Supports methods that
* - retrieves (or creates as necessary) a connection from the pool
* - removes expired connections from the pool
*
* Connections cleanup:
* A WeakHashMap is used for mapping the pool ids and Connections.
* A SoftReference from the value to the key is kept to hold the map
* entry as long as possible. This allows the GC to remove Connections
* from the Pool under situations of VM running out of resources.
* To take an appropriate action of 'closing the connections' before the GC
* reclaims the ConnectionsRef objects, the ConnectionsRef objects are made
* weakly reachable through a list of weak references registered with
* a reference queue.
* Upon an entry gets removed from the WeakHashMap, the ConnectionsRef (value
* in the map) object is weakly reachable. When another sweep of
* clearing the weak references is made by the GC it puts the corresponding
* ConnectionsWeakRef object into the reference queue.
* The reference queue is monitored lazily for reclaimable Connections
* whenever a pooled connection is requested or a call to remove the expired
* connections is made. The monitoring is done regularly when idle connection
* timeout is set as the PoolCleaner removes expired connections periodically.
* As determined by the experiements, cleanup of resources using the
* ReferenceQueue mechanism is reliable and has immidiate effect than the
* finalizer approach.
*
* @author Rosanna Lee
*/
final public class Pool {
static final boolean debug = com.sun.jndi.ldap.LdapPoolManager.debug;
/*
* Used for connections cleanup
*/
private static final ReferenceQueue<ConnectionsRef> queue =
new ReferenceQueue<>();
private static final Collection<Reference<ConnectionsRef>> weakRefs =
Collections.synchronizedList(new LinkedList<Reference<ConnectionsRef>>());
final private int maxSize; // max num of identical conn per pool
final private int prefSize; // preferred num of identical conn per pool
final private int initSize; // initial number of identical conn to create
final private Map<Object, ConnectionsRef> map;
public Pool(int initSize, int prefSize, int maxSize) {
map = new WeakHashMap<>();
this.prefSize = prefSize;
this.maxSize = maxSize;
this.initSize = initSize;
}
/**
* Gets a pooled connection for id. The pooled connection might be
* newly created, as governed by the maxSize and prefSize settings.
* If a pooled connection is unavailable and cannot be created due
* to the maxSize constraint, this call blocks until the constraint
* is removed or until 'timeout' ms has elapsed.
*
* @param id identity of the connection to get
* @param timeout the number of milliseconds to wait before giving up
* @param factory the factory to use for creating the connection if
* creation is necessary
* @return a pooled connection
* @throws NamingException the connection could not be created due to
* an error.
*/
public PooledConnection getPooledConnection(Object id, long timeout,
PooledConnectionFactory factory) throws NamingException {
d("get(): ", id);
if (debug) {
synchronized (map) {
d("size: ", map.size());
}
}
expungeStaleConnections();
Connections conns;
synchronized (map) {
conns = getConnections(id);
if (conns == null) {
d("get(): creating new connections list for ", id);
// No connections for this id so create a new list
conns = new Connections(id, initSize, prefSize, maxSize,
factory);
ConnectionsRef connsRef = new ConnectionsRef(conns);
map.put(id, connsRef);
// Create a weak reference to ConnectionsRef
Reference<ConnectionsRef> weakRef =
new ConnectionsWeakRef(connsRef, queue);
// Keep the weak reference through the element of a linked list
weakRefs.add(weakRef);
}
d("get(): size after: ", map.size());
}
return conns.get(timeout, factory); // get one connection from list
}
private Connections getConnections(Object id) {
ConnectionsRef ref = map.get(id);
return (ref != null) ? ref.getConnections() : null;
}
/**
* Goes through the connections in this Pool and expires ones that
* have been idle before 'threshold'. An expired connection is closed
* and then removed from the pool (removePooledConnection() will eventually
* be called, and the list of pools itself removed if it becomes empty).
*
* @param threshold connections idle before 'threshold' should be closed
* and removed.
*/
public void expire(long threshold) {
Collection<ConnectionsRef> copy;
synchronized (map) {
copy = new ArrayList<>(map.values());
}
ArrayList<ConnectionsRef> removed = new ArrayList<>();
Connections conns;
for (ConnectionsRef ref : copy) {
conns = ref.getConnections();
if (conns.expire(threshold)) {
d("expire(): removing ", conns);
removed.add(ref);
}
}
synchronized (map) {
map.values().removeAll(removed);
}
expungeStaleConnections();
}
/*
* Closes the connections contained in the ConnectionsRef object that
* is going to be reclaimed by the GC. Called by getPooledConnection()
* and expire() methods of this class.
*/
private static void expungeStaleConnections() {
ConnectionsWeakRef releaseRef = null;
while ((releaseRef = (ConnectionsWeakRef) queue.poll())
!= null) {
Connections conns = releaseRef.getConnections();
if (debug) {
System.err.println(
"weak reference cleanup: Closing Connections:" + conns);
}
// cleanup
conns.close();
weakRefs.remove(releaseRef);
releaseRef.clear();
}
}
public void showStats(PrintStream out) {
Object id;
Connections conns;
out.println("===== Pool start ======================");
out.println("maximum pool size: " + maxSize);
out.println("preferred pool size: " + prefSize);
out.println("initial pool size: " + initSize);
synchronized (map) {
out.println("current pool size: " + map.size());
for (Map.Entry<Object, ConnectionsRef> entry : map.entrySet()) {
id = entry.getKey();
conns = entry.getValue().getConnections();
out.println(" " + id + ":" + conns.getStats());
}
}
out.println("====== Pool end =====================");
}
public String toString() {
synchronized (map) {
return super.toString() + " " + map.toString();
}
}
private void d(String msg, int i) {
if (debug) {
System.err.println(this + "." + msg + i);
}
}
private void d(String msg, Object obj) {
if (debug) {
System.err.println(this + "." + msg + obj);
}
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap.pool;
/**
* Represents a callback used to release or remove a PooledConnection back
* into the pool.
*
* A pooled connection typically has a close method that its clients
* use to indicate that they no longer need the connection. This close
* method should use the methods defined in this interface to
* interact with the connection pool to return the connection
* to the pool.
*
* The methods in this interface are typically invoked by a PooledConnection.
* The methods in this interface are typically implemented by the connection
* pool manager.
*
* @author Rosanna Lee
*/
public interface PoolCallback {
/**
* Releases a useable connection back to the pool.
*
* @param conn The connection to release.
* @return true if the connection released; false if the connection
* is no longer in the pool.
*/
public abstract boolean releasePooledConnection(PooledConnection conn);
/**
* Removes a connection from the pool. The connection should not be reused.
* The physical connection should have already been closed.
*
* @param conn The connection to return.
* @return true if the connection was removed; false if the connection
* is no longer in the pool prior to removal.
*/
public abstract boolean removePooledConnection(PooledConnection conn);
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap.pool;
/**
* Thread that wakes up periodically and closes expired, unused connections.
*
* @author Rosanna Lee
*/
final public class PoolCleaner implements Runnable {
final private Pool[] pools;
final private long period;
/**
* @param period ms to wait between cleaning
* @param pools non-null array of Pools to clean
*/
public PoolCleaner(long period, Pool[] pools) {
super();
this.period = period;
this.pools = pools.clone();
}
@Override
public void run() {
long threshold;
while (true) {
synchronized (this) {
// Wait for duration of period ms
try {
wait(period);
} catch (InterruptedException ignore) {
}
// Connections idle since threshold have expired
threshold = System.currentTimeMillis() - period;
for (int i = 0; i < pools.length; i++) {
if (pools[i] != null) {
pools[i].expire(threshold);
}
}
}
}
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap.pool;
/**
* Represents a connection that is managed in a pool. The connection
* may be reused by multiple clients.
*
* A pooled connection typically has a close method that its clients
* use to indicate that they no longer need the connection. This close
* method would interact with the connection pool to return the connection
* to the pool (see PoolCallback).
*<p>
* The pooled connection also needs to provide a close method that the
* connection pool can use to physically close the connection.
* The pool might need to physically close the connection as determined
* by the pool's policy (for example, to manage the pool size or idle
* connections). This second close method should *not* use PoolCallback
* methods. It should only do what is required to close the physical
* connection.
*
* @author Rosanna Lee
*/
public interface PooledConnection {
/**
* Closes the physical connection.
*/
public abstract void closeConnection();
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap.pool;
import javax.naming.NamingException;
/**
* Represents a factory that creates PooledConnection.
*
* The user of the connection pool should provide an implementation of this
* interface and pass it to the Pool.getPooledConnection() method.
* The implementation of the factory should contain all the information
* necessary to create a PooledConnection.
*
* @author Rosanna Lee
*/
public interface PooledConnectionFactory {
/**
* Creates a pooled connection.
* @param pcb callback responsible for removing and releasing the pooled
* connection from the pool.
*/
public abstract PooledConnection createPooledConnection(PoolCallback pcb)
throws NamingException;
};

View File

@@ -0,0 +1,135 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap.sasl;
import javax.security.auth.callback.*;
import javax.security.sasl.RealmCallback;
import javax.security.sasl.RealmChoiceCallback;
import java.io.IOException;
/**
* DefaultCallbackHandler for satisfying NameCallback and
* PasswordCallback for an LDAP client.
* NameCallback is used for getting the authentication ID and is
* gotten from the java.naming.security.principal property.
* PasswordCallback is gotten from the java.naming.security.credentials
* property and must be of type String, char[] or byte[].
* If byte[], it is assumed to have UTF-8 encoding.
*
* If the caller of getPassword() will be using the password as
* a byte array, then it should encode the char[] array returned by
* getPassword() into a byte[] using UTF-8.
*
* @author Rosanna Lee
*/
final class DefaultCallbackHandler implements CallbackHandler {
private char[] passwd;
private String authenticationID;
private String authRealm;
DefaultCallbackHandler(String principal, Object cred, String realm)
throws IOException {
authenticationID = principal;
authRealm = realm;
if (cred instanceof String) {
passwd = ((String)cred).toCharArray();
} else if (cred instanceof char[]) {
passwd = ((char[])cred).clone();
} else if (cred != null) {
// assume UTF-8 encoding
String orig = new String((byte[])cred, "UTF8");
passwd = orig.toCharArray();
}
}
public void handle(Callback[] callbacks)
throws IOException, UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof NameCallback) {
((NameCallback)callbacks[i]).setName(authenticationID);
} else if (callbacks[i] instanceof PasswordCallback) {
((PasswordCallback)callbacks[i]).setPassword(passwd);
} else if (callbacks[i] instanceof RealmChoiceCallback) {
/* Deals with a choice of realms */
String[] choices =
((RealmChoiceCallback)callbacks[i]).getChoices();
int selected = 0;
if (authRealm != null && authRealm.length() > 0) {
selected = -1; // no realm chosen
for (int j = 0; j < choices.length; j++) {
if (choices[j].equals(authRealm)) {
selected = j;
}
}
if (selected == -1) {
StringBuffer allChoices = new StringBuffer();
for (int j = 0; j < choices.length; j++) {
allChoices.append(choices[j] + ",");
}
throw new IOException("Cannot match " +
"'java.naming.security.sasl.realm' property value, '" +
authRealm + "' with choices " + allChoices +
"in RealmChoiceCallback");
}
}
((RealmChoiceCallback)callbacks[i]).setSelectedIndex(selected);
} else if (callbacks[i] instanceof RealmCallback) {
/* 1 or 0 realms specified in challenge */
RealmCallback rcb = (RealmCallback) callbacks[i];
if (authRealm != null) {
rcb.setText(authRealm); // Use what user supplied
} else {
String defaultRealm = rcb.getDefaultText();
if (defaultRealm != null) {
rcb.setText(defaultRealm); // Use what server supplied
} else {
rcb.setText(""); // Specify no realm
}
}
} else {
throw new UnsupportedCallbackException(callbacks[i]);
}
}
}
void clearPassword() {
if (passwd != null) {
for (int i = 0; i < passwd.length; i++) {
passwd[i] = '\0';
}
passwd = null;
}
}
protected void finalize() throws Throwable {
clearPassword();
}
}

View File

@@ -0,0 +1,201 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap.sasl;
import java.io.*;
import java.util.Vector;
import java.util.Hashtable;
import java.util.StringTokenizer;
import javax.naming.AuthenticationException;
import javax.naming.AuthenticationNotSupportedException;
import javax.naming.NamingException;
import javax.naming.ldap.Control;
import javax.security.auth.callback.CallbackHandler;
import javax.security.sasl.*;
import com.sun.jndi.ldap.Connection;
import com.sun.jndi.ldap.LdapClient;
import com.sun.jndi.ldap.LdapResult;
/**
* Handles SASL support.
*
* @author Vincent Ryan
* @author Rosanna Lee
*/
final public class LdapSasl {
// SASL stuff
private static final String SASL_CALLBACK = "java.naming.security.sasl.callback";
private static final String SASL_AUTHZ_ID =
"java.naming.security.sasl.authorizationId";
private static final String SASL_REALM =
"java.naming.security.sasl.realm";
private static final int LDAP_SUCCESS = 0;
private static final int LDAP_SASL_BIND_IN_PROGRESS = 14; // LDAPv3
private LdapSasl() {
}
/**
* Performs SASL bind.
* Creates a SaslClient by using a default CallbackHandler
* that uses the Context.SECURITY_PRINCIPAL and Context.SECURITY_CREDENTIALS
* properties to satisfy the callbacks, and by using the
* SASL_AUTHZ_ID property as the authorization id. If the SASL_AUTHZ_ID
* property has not been set, Context.SECURITY_PRINCIPAL is used.
* If SASL_CALLBACK has been set, use that instead of the default
* CallbackHandler.
*<p>
* If bind is successful and the selected SASL mechanism has a security
* layer, set inStream and outStream to be filter streams that use
* the security layer. These will be used for subsequent communication
* with the server.
*<p>
* @param conn The non-null connection to use for sending an LDAP BIND
* @param server Non-null string name of host to connect to
* @param dn Non-null DN to bind as; also used as authentication ID
* @param pw Possibly null password; can be byte[], char[] or String
* @param authMech A non-null space-separated list of SASL authentication
* mechanisms.
* @param env The possibly null environment of the context, possibly containing
* properties for used by SASL mechanisms
* @param bindCtls The possibly null controls to accompany the bind
* @return LdapResult containing status of the bind
*/
@SuppressWarnings("unchecked")
public static LdapResult saslBind(LdapClient clnt, Connection conn,
String server, String dn, Object pw,
String authMech, Hashtable<?,?> env, Control[] bindCtls)
throws IOException, NamingException {
SaslClient saslClnt = null;
boolean cleanupHandler = false;
// Use supplied callback handler or create default
CallbackHandler cbh =
(env != null) ? (CallbackHandler)env.get(SASL_CALLBACK) : null;
if (cbh == null) {
cbh = new DefaultCallbackHandler(dn, pw, (String)env.get(SASL_REALM));
cleanupHandler = true;
}
// Prepare parameters for creating SASL client
String authzId = (env != null) ? (String)env.get(SASL_AUTHZ_ID) : null;
String[] mechs = getSaslMechanismNames(authMech);
try {
// Create SASL client to use using SASL package
saslClnt = Sasl.createSaslClient(
mechs, authzId, "ldap", server, (Hashtable<String, ?>)env, cbh);
if (saslClnt == null) {
throw new AuthenticationNotSupportedException(authMech);
}
LdapResult res;
String mechName = saslClnt.getMechanismName();
byte[] response = saslClnt.hasInitialResponse() ?
saslClnt.evaluateChallenge(NO_BYTES) : null;
res = clnt.ldapBind(null, response, bindCtls, mechName, true);
while (!saslClnt.isComplete() &&
(res.status == LDAP_SASL_BIND_IN_PROGRESS ||
res.status == LDAP_SUCCESS)) {
response = saslClnt.evaluateChallenge(
res.serverCreds != null? res.serverCreds : NO_BYTES);
if (res.status == LDAP_SUCCESS) {
if (response != null) {
throw new AuthenticationException(
"SASL client generated response after success");
}
break;
}
res = clnt.ldapBind(null, response, bindCtls, mechName, true);
}
if (res.status == LDAP_SUCCESS) {
if (!saslClnt.isComplete()) {
throw new AuthenticationException(
"SASL authentication not complete despite server claims");
}
String qop = (String) saslClnt.getNegotiatedProperty(Sasl.QOP);
// If negotiated integrity or privacy,
if (qop != null && (qop.equalsIgnoreCase("auth-int")
|| qop.equalsIgnoreCase("auth-conf"))) {
InputStream newIn = new SaslInputStream(saslClnt,
conn.inStream);
OutputStream newOut = new SaslOutputStream(saslClnt,
conn.outStream);
conn.replaceStreams(newIn, newOut);
} else {
saslClnt.dispose();
}
}
return res;
} catch (SaslException e) {
NamingException ne = new AuthenticationException(
authMech);
ne.setRootCause(e);
throw ne;
} finally {
if (cleanupHandler) {
((DefaultCallbackHandler)cbh).clearPassword();
}
}
}
/**
* Returns an array of SASL mechanisms given a string of space
* separated SASL mechanism names.
* @param The non-null string containing the mechanism names
* @return A non-null array of String; each element of the array
* contains a single mechanism name.
*/
private static String[] getSaslMechanismNames(String str) {
StringTokenizer parser = new StringTokenizer(str);
Vector<String> mechs = new Vector<>(10);
while (parser.hasMoreTokens()) {
mechs.addElement(parser.nextToken());
}
String[] mechNames = new String[mechs.size()];
for (int i = 0; i < mechs.size(); i++) {
mechNames[i] = mechs.elementAt(i);
}
return mechNames;
}
private static final byte[] NO_BYTES = new byte[0];
}

View File

@@ -0,0 +1,218 @@
/*
* Copyright (c) 2001, 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap.sasl;
import javax.security.sasl.Sasl;
import javax.security.sasl.SaslClient;
import javax.security.sasl.SaslException;
import java.io.IOException;
import java.io.EOFException;
import java.io.InputStream;
/**
* This class is used by clients of Java SASL that need to create an input stream
* that uses SaslClient's unwrap() method to decode the SASL buffers
* sent by the SASL server.
*
* Extend from InputStream instead of FilterInputStream because
* we need to override less methods in InputStream. That is, the
* behavior of the default implementations in InputStream matches
* more closely with the behavior we want in SaslInputStream.
*
* @author Rosanna Lee
*/
public class SaslInputStream extends InputStream {
private static final boolean debug = false;
private byte[] saslBuffer; // buffer for storing raw bytes
private byte[] lenBuf = new byte[4]; // buffer for storing length
private byte[] buf = new byte[0]; // buffer for storing processed bytes
// Initialized to empty buffer
private int bufPos = 0; // read position in buf
private InputStream in; // underlying input stream
private SaslClient sc;
private int recvMaxBufSize = 65536;
SaslInputStream(SaslClient sc, InputStream in) throws SaslException {
super();
this.in = in;
this.sc = sc;
String str = (String) sc.getNegotiatedProperty(Sasl.MAX_BUFFER);
if (str != null) {
try {
recvMaxBufSize = Integer.parseInt(str);
} catch (NumberFormatException e) {
throw new SaslException(Sasl.MAX_BUFFER +
" property must be numeric string: " + str);
}
}
saslBuffer = new byte[recvMaxBufSize];
}
public int read() throws IOException {
byte[] inBuf = new byte[1];
int count = read(inBuf, 0, 1);
if (count > 0) {
return inBuf[0];
} else {
return -1;
}
}
public int read(byte[] inBuf, int start, int count) throws IOException {
if (bufPos >= buf.length) {
int actual = fill(); // read and unwrap next SASL buffer
while (actual == 0) { // ignore zero length content
actual = fill();
}
if (actual == -1) {
return -1; // EOF
}
}
int avail = buf.length - bufPos;
if (count > avail) {
// Requesting more that we have stored
// Return all that we have; next invocation of read() will
// trigger fill()
System.arraycopy(buf, bufPos, inBuf, start, avail);
bufPos = buf.length;
return avail;
} else {
// Requesting less than we have stored
// Return all that was requested
System.arraycopy(buf, bufPos, inBuf, start, count);
bufPos += count;
return count;
}
}
/**
* Fills the buf with more data by reading a SASL buffer, unwrapping it,
* and leaving the bytes in buf for read() to return.
* @return The number of unwrapped bytes available
*/
private int fill() throws IOException {
// Read in length of buffer
int actual = readFully(lenBuf, 4);
if (actual != 4) {
return -1;
}
int len = networkByteOrderToInt(lenBuf, 0, 4);
if (len > recvMaxBufSize) {
throw new IOException(
len + "exceeds the negotiated receive buffer size limit:" +
recvMaxBufSize);
}
if (debug) {
System.err.println("reading " + len + " bytes from network");
}
// Read SASL buffer
actual = readFully(saslBuffer, len);
if (actual != len) {
throw new EOFException("Expecting to read " + len +
" bytes but got " + actual + " bytes before EOF");
}
// Unwrap
buf = sc.unwrap(saslBuffer, 0, len);
bufPos = 0;
return buf.length;
}
/**
* Read requested number of bytes before returning.
* @return The number of bytes actually read; -1 if none read
*/
private int readFully(byte[] inBuf, int total) throws IOException {
int count, pos = 0;
if (debug) {
System.err.println("readFully " + total + " from " + in);
}
while (total > 0) {
count = in.read(inBuf, pos, total);
if (debug) {
System.err.println("readFully read " + count);
}
if (count == -1 ) {
return (pos == 0? -1 : pos);
}
pos += count;
total -= count;
}
return pos;
}
public int available() throws IOException {
return buf.length - bufPos;
}
public void close() throws IOException {
SaslException save = null;
try {
sc.dispose(); // Dispose of SaslClient's state
} catch (SaslException e) {
// Save exception for throwing after closing 'in'
save = e;
}
in.close(); // Close underlying input stream
if (save != null) {
throw save;
}
}
/**
* Returns the integer represented by 4 bytes in network byte order.
*/
// Copied from com.sun.security.sasl.util.SaslImpl.
private static int networkByteOrderToInt(byte[] buf, int start, int count) {
if (count > 4) {
throw new IllegalArgumentException("Cannot handle more than 4 bytes");
}
int answer = 0;
for (int i = 0; i < count; i++) {
answer <<= 8;
answer |= ((int)buf[start+i] & 0xff);
}
return answer;
}
}

View File

@@ -0,0 +1,135 @@
/*
* Copyright (c) 2001, 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap.sasl;
import javax.security.sasl.Sasl;
import javax.security.sasl.SaslClient;
import javax.security.sasl.SaslException;
import java.io.IOException;
import java.io.FilterOutputStream;
import java.io.OutputStream;
class SaslOutputStream extends FilterOutputStream {
private static final boolean debug = false;
private byte[] lenBuf = new byte[4]; // buffer for storing length
private int rawSendSize = 65536;
private SaslClient sc;
SaslOutputStream(SaslClient sc, OutputStream out) throws SaslException {
super(out);
this.sc = sc;
if (debug) {
System.err.println("SaslOutputStream: " + out);
}
String str = (String) sc.getNegotiatedProperty(Sasl.RAW_SEND_SIZE);
if (str != null) {
try {
rawSendSize = Integer.parseInt(str);
} catch (NumberFormatException e) {
throw new SaslException(Sasl.RAW_SEND_SIZE +
" property must be numeric string: " + str);
}
}
}
// Override this method to call write(byte[], int, int) counterpart
// super.write(int) simply calls out.write(int)
public void write(int b) throws IOException {
byte[] buffer = new byte[1];
buffer[0] = (byte)b;
write(buffer, 0, 1);
}
/**
* Override this method to "wrap" the outgoing buffer before
* writing it to the underlying output stream.
*/
public void write(byte[] buffer, int offset, int total) throws IOException {
int count;
byte[] wrappedToken, saslBuffer;
// "Packetize" buffer to be within rawSendSize
if (debug) {
System.err.println("Total size: " + total);
}
for (int i = 0; i < total; i += rawSendSize) {
// Calculate length of current "packet"
count = (total - i) < rawSendSize ? (total - i) : rawSendSize;
// Generate wrapped token
wrappedToken = sc.wrap(buffer, offset+i, count);
// Write out length
intToNetworkByteOrder(wrappedToken.length, lenBuf, 0, 4);
if (debug) {
System.err.println("sending size: " + wrappedToken.length);
}
out.write(lenBuf, 0, 4);
// Write out wrapped token
out.write(wrappedToken, 0, wrappedToken.length);
}
}
public void close() throws IOException {
SaslException save = null;
try {
sc.dispose(); // Dispose of SaslClient's state
} catch (SaslException e) {
// Save exception for throwing after closing 'in'
save = e;
}
super.close(); // Close underlying output stream
if (save != null) {
throw save;
}
}
// Copied from com.sun.security.sasl.util.SaslImpl
/**
* Encodes an integer into 4 bytes in network byte order in the buffer
* supplied.
*/
private static void intToNetworkByteOrder(int num, byte[] buf, int start,
int count) {
if (count > 4) {
throw new IllegalArgumentException("Cannot handle more than 4 bytes");
}
for (int i = count-1; i >= 0; i--) {
buf[start+i] = (byte)(num & 0xff);
num >>>= 8;
}
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap.spi;
import javax.naming.Context;
import javax.naming.NamingException;
import java.util.Map;
import java.util.Optional;
/**
* Service-provider class for DNS lookups when performing LDAP operations.
*
* <p> An LDAP DNS provider is a concrete subclass of this class that
* has a zero-argument constructor. LDAP DNS providers are located using the
* ServiceLoader facility, as specified by
* {@linkplain javax.naming.directory.InitialDirContext InitialDirectContext}.
*
* The
* {@link java.util.ServiceLoader ServiceLoader} is used to create and register
* implementations of {@code LdapDnsProvider}.
*
* <p> An LDAP DNS provider can be used in environments where the default
* DNS resolution mechanism is not sufficient to accurately pinpoint the
* correct LDAP servers needed to perform LDAP operations. For example, in an
* environment containing a mix of {@code ldap} and {@code ldaps} servers
* you may want the {@linkplain javax.naming.ldap.LdapContext LdapContext}
* to query {@code ldaps} servers only.
*
* @since 12
*/
public abstract class LdapDnsProvider {
// The {@code RuntimePermission("ldapDnsProvider")} is
// necessary to subclass and instantiate the {@code LdapDnsProvider} class.
private static final RuntimePermission DNSPROVIDER_PERMISSION =
new RuntimePermission("ldapDnsProvider");
/**
* Creates a new instance of {@code LdapDnsProvider}.
*
* @throws SecurityException if a security manager is present and its
* {@code checkPermission} method doesn't allow
* the {@code RuntimePermission("ldapDnsProvider")}.
*/
protected LdapDnsProvider() {
this(checkPermission());
}
private LdapDnsProvider(Void unused) {
// nothing to do.
}
private static Void checkPermission() {
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(DNSPROVIDER_PERMISSION);
}
return null;
}
/**
* Lookup the endpoints and domain name for the given {@link Context}
* {@link Context#PROVIDER_URL provider URL} and environment. The resolved
* endpoints and domain name are returned as an
* {@link LdapDnsProviderResult}.
*
* <p> An endpoint is a {@code String} representation of an LDAP URL which
* points to an LDAP server to be used for LDAP operations. The syntax of
* an LDAP URL is defined by <a href="http://www.ietf.org/rfc/rfc2255.txt">
* <i>RFC&nbsp;2255: The LDAP URL Format</i></a>.
*
* @param url The {@link Context} {@link Context#PROVIDER_URL provider URL}
* @param env The {@link Context} environment.
*
* @return an {@link LdapDnsProviderResult} or empty {@code Optional}
* if the lookup fails.
*
* @throws NamingException if the {@code url} is not valid or an error
* occurred while performing the lookup.
* @throws NullPointerException if either {@code url} or {@code env} are
* {@code null}.
*/
public abstract Optional<LdapDnsProviderResult> lookupEndpoints(
String url, Map<?,?> env) throws NamingException;
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap.spi;
import java.util.List;
import java.util.ArrayList;
/**
* The result of a DNS lookup for an LDAP URL.
*
* <p> This class is used by an {@link LdapDnsProvider} to return the result
* of a DNS lookup for a given LDAP URL. The result consists of a domain name
* and its associated ldap server endpoints.
*
* <p> A {@code null} {@code domainName} is equivalent to and represented
* by an empty string.
*
* @since 12
*/
public final class LdapDnsProviderResult {
private final String domainName;
private final List<String> endpoints;
/**
* Construct an LdapDnsProviderResult consisting of a resolved domain name
* and the ldap server endpoints that serve the domain.
*
* @param domainName the resolved domain name; can be null.
* @param endpoints the possibly empty list of resolved ldap server
* endpoints
*
* @throws NullPointerException if {@code endpoints} contains {@code null}
* elements.
* @throws ClassCastException if {@code endpoints} contains non-
* {@code String} elements.
*/
public LdapDnsProviderResult(String domainName, List<String> endpoints) {
this.domainName = (domainName == null) ? "" : domainName;
this.endpoints = new ArrayList<>(endpoints);
}
/**
* Returns the domain name resolved from the ldap URL. This method returns
* the empty string if the {@code LdapDnsProviderResult} is created with a
* null domain name.
*
* @return the resolved domain name
*/
public String getDomainName() {
return domainName;
}
/**
* Returns the possibly empty list of individual server endpoints resolved
* from the ldap URL.
*
* @return a possibly empty unmodifiable {@link List} containing the
* resolved ldap server endpoints
*/
public List<String> getEndpoints() {
return endpoints;
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 1999, 2002, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.rmi.registry;
import java.rmi.*;
import java.rmi.server.UnicastRemoteObject;
import javax.naming.*;
/**
* The ReferenceWrapper class is a Remote wrapper for Reference
* objects. It wraps around a Reference on the server, and makes the
* Reference accessible to clients.
*
* @author Scott Seligman
*/
public class ReferenceWrapper
extends UnicastRemoteObject
implements RemoteReference
{
protected Reference wrappee; // reference being wrapped
public ReferenceWrapper(Reference wrappee)
throws NamingException, RemoteException
{
this.wrappee = wrappee;
}
public Reference getReference() throws RemoteException {
return wrappee;
}
private static final long serialVersionUID = 6078186197417641456L;
}

View File

@@ -0,0 +1,640 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.rmi.registry;
import java.util.Hashtable;
import java.util.Properties;
import java.rmi.*;
import java.rmi.server.*;
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.security.AccessController;
import java.security.PrivilegedAction;
import javax.naming.*;
import javax.naming.spi.NamingManager;
/**
* A RegistryContext is a context representing a remote RMI registry.
*
* @author Scott Seligman
*/
public class RegistryContext implements Context, Referenceable {
private Hashtable<String, Object> environment;
private Registry registry;
private String host;
private int port;
private static final NameParser nameParser = new AtomicNameParser();
private static final String SOCKET_FACTORY = "com.sun.jndi.rmi.factory.socket";
/**
* Determines whether classes may be loaded from an arbitrary URL code base.
*/
static final boolean trustURLCodebase;
static {
// System property to control whether classes may be loaded from an
// arbitrary URL codebase
PrivilegedAction<String> act = () -> System.getProperty(
"com.sun.jndi.rmi.object.trustURLCodebase", "false");
String trust = AccessController.doPrivileged(act);
trustURLCodebase = "true".equalsIgnoreCase(trust);
}
Reference reference = null; // ref used to create this context, if any
// Environment property that, if set, indicates that a security
// manager should be installed (if none is already in place).
public static final String SECURITY_MGR =
"java.naming.rmi.security.manager";
/**
* Returns a context for the registry at a given host and port.
* If "host" is null, uses default host.
* If "port" is non-positive, uses default port.
* Cloning of "env" is handled by caller; see comments within
* RegistryContextFactory.getObjectInstance(), for example.
*/
@SuppressWarnings("unchecked")
public RegistryContext(String host, int port, Hashtable<?, ?> env)
throws NamingException
{
environment = (env == null)
? new Hashtable<String, Object>(5)
: (Hashtable<String, Object>) env;
if (environment.get(SECURITY_MGR) != null) {
installSecurityMgr();
}
// chop off '[' and ']' in an IPv6 literal address
if ((host != null) && (host.charAt(0) == '[')) {
host = host.substring(1, host.length() - 1);
}
RMIClientSocketFactory socketFactory =
(RMIClientSocketFactory) environment.get(SOCKET_FACTORY);
registry = getRegistry(host, port, socketFactory);
this.host = host;
this.port = port;
}
/**
* Returns a clone of a registry context. The context's private state
* is independent of the original's (so closing one context, for example,
* won't close the other).
*/
// %%% Alternatively, this could be done with a clone() method.
@SuppressWarnings("unchecked") // clone()
RegistryContext(RegistryContext ctx) {
environment = (Hashtable<String, Object>)ctx.environment.clone();
registry = ctx.registry;
host = ctx.host;
port = ctx.port;
reference = ctx.reference;
}
protected void finalize() {
close();
}
public Object lookup(Name name) throws NamingException {
if (name.isEmpty()) {
return (new RegistryContext(this));
}
Remote obj;
try {
obj = registry.lookup(name.get(0));
} catch (NotBoundException e) {
throw (new NameNotFoundException(name.get(0)));
} catch (RemoteException e) {
throw (NamingException)wrapRemoteException(e).fillInStackTrace();
}
return (decodeObject(obj, name.getPrefix(1)));
}
public Object lookup(String name) throws NamingException {
return lookup(new CompositeName(name));
}
/**
* If the object to be bound is both Remote and Referenceable, binds the
* object itself, not its Reference.
*/
public void bind(Name name, Object obj) throws NamingException {
if (name.isEmpty()) {
throw (new InvalidNameException(
"RegistryContext: Cannot bind empty name"));
}
try {
registry.bind(name.get(0), encodeObject(obj, name.getPrefix(1)));
} catch (AlreadyBoundException e) {
NamingException ne = new NameAlreadyBoundException(name.get(0));
ne.setRootCause(e);
throw ne;
} catch (RemoteException e) {
throw (NamingException)wrapRemoteException(e).fillInStackTrace();
}
}
public void bind(String name, Object obj) throws NamingException {
bind(new CompositeName(name), obj);
}
public void rebind(Name name, Object obj) throws NamingException {
if (name.isEmpty()) {
throw (new InvalidNameException(
"RegistryContext: Cannot rebind empty name"));
}
try {
registry.rebind(name.get(0), encodeObject(obj, name.getPrefix(1)));
} catch (RemoteException e) {
throw (NamingException)wrapRemoteException(e).fillInStackTrace();
}
}
public void rebind(String name, Object obj) throws NamingException {
rebind(new CompositeName(name), obj);
}
public void unbind(Name name) throws NamingException {
if (name.isEmpty()) {
throw (new InvalidNameException(
"RegistryContext: Cannot unbind empty name"));
}
try {
registry.unbind(name.get(0));
} catch (NotBoundException e) {
// method is idempotent
} catch (RemoteException e) {
throw (NamingException)wrapRemoteException(e).fillInStackTrace();
}
}
public void unbind(String name) throws NamingException {
unbind(new CompositeName(name));
}
/**
* Rename is implemented by this sequence of operations:
* lookup, bind, unbind. The sequence is not performed atomically.
*/
public void rename(Name oldName, Name newName) throws NamingException {
bind(newName, lookup(oldName));
unbind(oldName);
}
public void rename(String name, String newName) throws NamingException {
rename(new CompositeName(name), new CompositeName(newName));
}
public NamingEnumeration<NameClassPair> list(Name name) throws
NamingException {
if (!name.isEmpty()) {
throw (new InvalidNameException(
"RegistryContext: can only list \"\""));
}
try {
String[] names = registry.list();
return (new NameClassPairEnumeration(names));
} catch (RemoteException e) {
throw (NamingException)wrapRemoteException(e).fillInStackTrace();
}
}
public NamingEnumeration<NameClassPair> list(String name) throws
NamingException {
return list(new CompositeName(name));
}
public NamingEnumeration<Binding> listBindings(Name name)
throws NamingException
{
if (!name.isEmpty()) {
throw (new InvalidNameException(
"RegistryContext: can only list \"\""));
}
try {
String[] names = registry.list();
return (new BindingEnumeration(this, names));
} catch (RemoteException e) {
throw (NamingException)wrapRemoteException(e).fillInStackTrace();
}
}
public NamingEnumeration<Binding> listBindings(String name) throws
NamingException {
return listBindings(new CompositeName(name));
}
public void destroySubcontext(Name name) throws NamingException {
throw (new OperationNotSupportedException());
}
public void destroySubcontext(String name) throws NamingException {
throw (new OperationNotSupportedException());
}
public Context createSubcontext(Name name) throws NamingException {
throw (new OperationNotSupportedException());
}
public Context createSubcontext(String name) throws NamingException {
throw (new OperationNotSupportedException());
}
public Object lookupLink(Name name) throws NamingException {
return lookup(name);
}
public Object lookupLink(String name) throws NamingException {
return lookup(name);
}
public NameParser getNameParser(Name name) throws NamingException {
return nameParser;
}
public NameParser getNameParser(String name) throws NamingException {
return nameParser;
}
public Name composeName(Name name, Name prefix) throws NamingException {
Name result = (Name)prefix.clone();
return result.addAll(name);
}
public String composeName(String name, String prefix)
throws NamingException
{
return composeName(new CompositeName(name),
new CompositeName(prefix)).toString();
}
public Object removeFromEnvironment(String propName)
throws NamingException
{
return environment.remove(propName);
}
public Object addToEnvironment(String propName, Object propVal)
throws NamingException
{
if (propName.equals(SECURITY_MGR)) {
installSecurityMgr();
}
return environment.put(propName, propVal);
}
@SuppressWarnings("unchecked") // clone()
public Hashtable<String, Object> getEnvironment() throws NamingException {
return (Hashtable<String, Object>)environment.clone();
}
public void close() {
environment = null;
registry = null;
// &&& If we were caching registry connections, we would probably
// uncache this one now.
}
public String getNameInNamespace() {
return ""; // Registry has an empty name
}
/**
* Returns an RMI registry reference for this context.
*<p>
* If this context was created from a reference, that reference is
* returned. Otherwise, an exception is thrown if the registry's
* host is "localhost" or the default (null). Although this could
* possibly make for a valid reference, it's far more likely to be
* an easily made error.
*
* @see RegistryContextFactory
*/
public Reference getReference() throws NamingException {
if (reference != null) {
return (Reference)reference.clone(); // %%% clone the addrs too?
}
if (host == null || host.equals("localhost")) {
throw (new ConfigurationException(
"Cannot create a reference for an RMI registry whose " +
"host was unspecified or specified as \"localhost\""));
}
String url = "rmi://";
// Enclose IPv6 literal address in '[' and ']'
url = (host.indexOf(":") > -1) ? url + "[" + host + "]" :
url + host;
if (port > 0) {
url += ":" + Integer.toString(port);
}
RefAddr addr = new StringRefAddr(RegistryContextFactory.ADDRESS_TYPE,
url);
return (new Reference(RegistryContext.class.getName(),
addr,
RegistryContextFactory.class.getName(),
null));
}
/**
* Wrap a RemoteException inside a NamingException.
*/
public static NamingException wrapRemoteException(RemoteException re) {
NamingException ne;
if (re instanceof ConnectException) {
ne = new ServiceUnavailableException();
} else if (re instanceof AccessException) {
ne = new NoPermissionException();
} else if (re instanceof StubNotFoundException ||
re instanceof UnknownHostException ||
re instanceof SocketSecurityException) {
ne = new ConfigurationException();
} else if (re instanceof ExportException ||
re instanceof ConnectIOException ||
re instanceof MarshalException ||
re instanceof UnmarshalException ||
re instanceof NoSuchObjectException) {
ne = new CommunicationException();
} else if (re instanceof ServerException &&
re.detail instanceof RemoteException) {
ne = wrapRemoteException((RemoteException)re.detail);
} else {
ne = new NamingException();
}
ne.setRootCause(re);
return ne;
}
/**
* Returns the registry at a given host, port and socket factory.
* If "host" is null, uses default host.
* If "port" is non-positive, uses default port.
* If "socketFactory" is null, uses the default socket.
*/
private static Registry getRegistry(String host, int port,
RMIClientSocketFactory socketFactory)
throws NamingException
{
// %%% We could cache registry connections here. The transport layer
// may already reuse connections.
try {
if (socketFactory == null) {
return LocateRegistry.getRegistry(host, port);
} else {
return LocateRegistry.getRegistry(host, port, socketFactory);
}
} catch (RemoteException e) {
throw (NamingException)wrapRemoteException(e).fillInStackTrace();
}
}
/**
* Attempts to install a security manager if none is currently in
* place.
*/
private static void installSecurityMgr() {
try {
System.setSecurityManager(new RMISecurityManager());
} catch (Exception e) {
}
}
/**
* Encodes an object prior to binding it in the registry. First,
* NamingManager.getStateToBind() is invoked. If the resulting
* object is Remote, it is returned. If it is a Reference or
* Referenceable, the reference is wrapped in a Remote object.
* Otherwise, an exception is thrown.
*
* @param name The object's name relative to this context.
*/
private Remote encodeObject(Object obj, Name name)
throws NamingException, RemoteException
{
obj = NamingManager.getStateToBind(obj, name, this, environment);
if (obj instanceof Remote) {
return (Remote)obj;
}
if (obj instanceof Reference) {
return (new ReferenceWrapper((Reference)obj));
}
if (obj instanceof Referenceable) {
return (new ReferenceWrapper(((Referenceable)obj).getReference()));
}
throw (new IllegalArgumentException(
"RegistryContext: " +
"object to bind must be Remote, Reference, or Referenceable"));
}
/**
* Decodes an object that has been retrieved from the registry.
* First, if the object is a RemoteReference, the Reference is
* unwrapped. Then, NamingManager.getObjectInstance() is invoked.
*
* @param name The object's name relative to this context.
*/
private Object decodeObject(Remote r, Name name) throws NamingException {
try {
Object obj = (r instanceof RemoteReference)
? ((RemoteReference)r).getReference()
: (Object)r;
/*
* Classes may only be loaded from an arbitrary URL codebase when
* the system property com.sun.jndi.rmi.object.trustURLCodebase
* has been set to "true".
*/
// Use reference if possible
Reference ref = null;
if (obj instanceof Reference) {
ref = (Reference) obj;
} else if (obj instanceof Referenceable) {
ref = ((Referenceable)(obj)).getReference();
}
if (ref != null && ref.getFactoryClassLocation() != null &&
!trustURLCodebase) {
throw new ConfigurationException(
"The object factory is untrusted. Set the system property" +
" 'com.sun.jndi.rmi.object.trustURLCodebase' to 'true'.");
}
return NamingManager.getObjectInstance(obj, name, this,
environment);
} catch (NamingException e) {
throw e;
} catch (RemoteException e) {
throw (NamingException)
wrapRemoteException(e).fillInStackTrace();
} catch (Exception e) {
NamingException ne = new NamingException();
ne.setRootCause(e);
throw ne;
}
}
}
/**
* A name parser for case-sensitive atomic names.
*/
class AtomicNameParser implements NameParser {
private static final Properties syntax = new Properties();
public Name parse(String name) throws NamingException {
return (new CompoundName(name, syntax));
}
}
/**
* An enumeration of name / class-name pairs.
*/
class NameClassPairEnumeration implements NamingEnumeration<NameClassPair> {
private final String[] names;
private int nextName; // index into "names"
NameClassPairEnumeration(String[] names) {
this.names = names;
nextName = 0;
}
public boolean hasMore() {
return (nextName < names.length);
}
public NameClassPair next() throws NamingException {
if (!hasMore()) {
throw (new java.util.NoSuchElementException());
}
// Convert name to a one-element composite name, so embedded
// meta-characters are properly escaped.
String name = names[nextName++];
Name cname = (new CompositeName()).add(name);
NameClassPair ncp = new NameClassPair(cname.toString(),
"java.lang.Object");
ncp.setNameInNamespace(name);
return ncp;
}
public boolean hasMoreElements() {
return hasMore();
}
public NameClassPair nextElement() {
try {
return next();
} catch (NamingException e) { // should never happen
throw (new java.util.NoSuchElementException(
"javax.naming.NamingException was thrown"));
}
}
public void close() {
nextName = names.length;
}
}
/**
* An enumeration of Bindings.
*
* The actual registry lookups are performed when next() is called. It would
* be nicer to defer this until the object (or its class name) is actually
* requested. The problem with that approach is that Binding.getObject()
* cannot throw NamingException.
*/
class BindingEnumeration implements NamingEnumeration<Binding> {
private RegistryContext ctx;
private final String[] names;
private int nextName; // index into "names"
BindingEnumeration(RegistryContext ctx, String[] names) {
// Clone ctx in case someone closes it before we're through.
this.ctx = new RegistryContext(ctx);
this.names = names;
nextName = 0;
}
protected void finalize() {
ctx.close();
}
public boolean hasMore() {
if (nextName >= names.length) {
ctx.close();
}
return (nextName < names.length);
}
public Binding next() throws NamingException {
if (!hasMore()) {
throw (new java.util.NoSuchElementException());
}
// Convert name to a one-element composite name, so embedded
// meta-characters are properly escaped.
String name = names[nextName++];
Name cname = (new CompositeName()).add(name);
Object obj = ctx.lookup(cname);
String cnameStr = cname.toString();
Binding binding = new Binding(cnameStr, obj);
binding.setNameInNamespace(cnameStr);
return binding;
}
public boolean hasMoreElements() {
return hasMore();
}
public Binding nextElement() {
try {
return next();
} catch (NamingException e) {
throw (new java.util.NoSuchElementException(
"javax.naming.NamingException was thrown"));
}
}
public void close () {
finalize();
}
}

View File

@@ -0,0 +1,178 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.rmi.registry;
import java.util.Enumeration;
import java.util.Hashtable;
import javax.naming.*;
import javax.naming.spi.*;
import com.sun.jndi.url.rmi.rmiURLContextFactory;
/**
* A RegistryContextFactory takes an RMI registry reference, and
* creates the corresponding RMI object or registry context. In
* addition, it serves as the initial context factory when using an
* RMI registry as an initial context.
*<p>
* When an initial context is being created, the environment
* property "java.naming.provider.url" should contain the RMI URL of
* the appropriate registry. Otherwise, the default URL "rmi:" is used.
*<p>
* An RMI registry reference contains one or more StringRefAddrs of
* type "URL", each containing a single RMI URL. Other addresses
* are ignored. Multiple URLs represent alternative addresses for the
* same logical resource. The order of the addresses is not significant.
*
* @author Scott Seligman
*/
public class RegistryContextFactory
implements ObjectFactory, InitialContextFactory
{
/**
* The type of each address in an RMI registry reference.
*/
public final static String ADDRESS_TYPE = "URL";
public Context getInitialContext(Hashtable<?,?> env) throws NamingException {
if (env != null) {
env = (Hashtable) env.clone();
}
return URLToContext(getInitCtxURL(env), env);
}
public Object getObjectInstance(Object ref, Name name, Context nameCtx,
Hashtable<?,?> env)
throws NamingException
{
if (!isRegistryRef(ref)) {
return null;
}
/*
* No need to clone env here. If getObjectInstance()
* returns something other than a RegistryContext (which
* happens if you're looking up an object bound in the
* registry, as opposed to looking up the registry itself),
* then the context is GCed right away and there's no need to
* clone the environment. If getObjectInstance() returns a
* RegistryContext, then it still goes through
* GenericURLContext, which calls RegistryContext.lookup()
* with an empty name, which clones the environment.
*/
Object obj = URLsToObject(getURLs((Reference)ref), env);
if (obj instanceof RegistryContext) {
RegistryContext ctx = (RegistryContext)obj;
ctx.reference = (Reference)ref;
}
return obj;
}
private static Context URLToContext(String url, Hashtable<?,?> env)
throws NamingException
{
rmiURLContextFactory factory = new rmiURLContextFactory();
Object obj = factory.getObjectInstance(url, null, null, env);
if (obj instanceof Context) {
return (Context)obj;
} else {
throw (new NotContextException(url));
}
}
private static Object URLsToObject(String[] urls, Hashtable<?,?> env)
throws NamingException
{
rmiURLContextFactory factory = new rmiURLContextFactory();
return factory.getObjectInstance(urls, null, null, env);
}
/**
* Reads environment to find URL of initial context.
* The default URL is "rmi:".
*/
private static String getInitCtxURL(Hashtable<?,?> env) {
final String defaultURL = "rmi:";
String url = null;
if (env != null) {
url = (String)env.get(Context.PROVIDER_URL);
}
return ((url != null) ? url : defaultURL);
}
/**
* Returns true if argument is an RMI registry reference.
*/
private static boolean isRegistryRef(Object obj) {
if (!(obj instanceof Reference)) {
return false;
}
String thisClassName = RegistryContextFactory.class.getName();
Reference ref = (Reference)obj;
return thisClassName.equals(ref.getFactoryClassName());
}
/**
* Returns the URLs contained within an RMI registry reference.
*/
private static String[] getURLs(Reference ref) throws NamingException {
int size = 0; // number of URLs
String[] urls = new String[ref.size()];
Enumeration<RefAddr> addrs = ref.getAll();
while (addrs.hasMoreElements()) {
RefAddr addr = addrs.nextElement();
if ((addr instanceof StringRefAddr) &&
addr.getType().equals(ADDRESS_TYPE)) {
urls[size++] = (String)addr.getContent();
}
}
if (size == 0) {
throw (new ConfigurationException(
"Reference contains no valid addresses"));
}
// Trim URL array down to size.
if (size == ref.size()) {
return urls;
}
String[] urls2 = new String[size];
System.arraycopy(urls, 0, urls2, 0, size);
return urls2;
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.rmi.registry;
import java.rmi.*;
import javax.naming.*;
/**
* The RemoteReference interface wraps a Reference in a Remote wrapper.
*
* @author Scott Seligman
*/
public interface RemoteReference extends Remote {
Reference getReference() throws NamingException, RemoteException;
}

View File

@@ -0,0 +1,301 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.toolkit.corba;
// Needed for RMI/IIOP
import java.rmi.Remote;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.util.Hashtable;
import java.util.Properties;
import java.util.Enumeration;
import org.omg.CORBA.ORB;
import javax.naming.*;
import com.sun.jndi.cosnaming.CNCtx;
/**
* Contains utilities for performing CORBA-related tasks:
* 1. Get the org.omg.CORBA.Object for a java.rmi.Remote object.
* 2. Create an ORB to use for a given host/port, and environment properties.
*
* @author Simon Nash
* @author Bryan Atsatt
*/
public class CorbaUtils {
/**
* Returns the CORBA object reference associated with a Remote
* object by using the javax.rmi.CORBA package.
*<p>
* Use reflection to avoid hard dependencies on javax.rmi.CORBA package.
* This method effective does the following:
*<blockquote><pre>
* java.lang.Object stub;
* try {
* stub = PortableRemoteObject.toStub(remoteObj);
* } catch (Exception e) {
* throw new ConfigurationException("Object not exported or not found");
* }
* if (!(stub instanceof javax.rmi.CORBA.Stub)) {
* return null; // JRMP impl or JRMP stub
* }
* try {
* ((javax.rmi.CORBA.Stub)stub).connect(orb); // try to connect IIOP stub
* } catch (RemoteException e) {
* // ignore 'already connected' error
* }
* return (javax.rmi.CORBA.Stub)stub;
*
* @param remoteObj The non-null remote object for
* @param orb The non-null ORB to connect the remote object to
* @return The CORBA Object for remoteObj; null if <tt>remoteObj</tt>
* is a JRMP implementation or JRMP stub.
* @exception ClassNotFoundException The RMI-IIOP package is not available
* @exception ConfigurationException The CORBA Object cannot be obtained
* because of configuration problems.
*/
public static org.omg.CORBA.Object remoteToCorba(Remote remoteObj, ORB orb)
throws ClassNotFoundException, ConfigurationException {
synchronized (CorbaUtils.class) {
if (toStubMethod == null) {
initMethodHandles();
}
}
// First, get remoteObj's stub
// javax.rmi.CORBA.Stub stub = PortableRemoteObject.toStub(remoteObj);
java.lang.Object stub;
try {
stub = toStubMethod.invoke(null, new java.lang.Object[]{remoteObj});
} catch (InvocationTargetException e) {
Throwable realException = e.getTargetException();
// realException.printStackTrace();
ConfigurationException ce = new ConfigurationException(
"Problem with PortableRemoteObject.toStub(); object not exported or stub not found");
ce.setRootCause(realException);
throw ce;
} catch (IllegalAccessException e) {
ConfigurationException ce = new ConfigurationException(
"Cannot invoke javax.rmi.PortableRemoteObject.toStub(java.rmi.Remote)");
ce.setRootCause(e);
throw ce;
}
// Next, make sure that the stub is javax.rmi.CORBA.Stub
if (!corbaStubClass.isInstance(stub)) {
return null; // JRMP implementation or JRMP stub
}
// Next, make sure that the stub is connected
// Invoke stub.connect(orb)
try {
connectMethod.invoke(stub, new java.lang.Object[]{orb});
} catch (InvocationTargetException e) {
Throwable realException = e.getTargetException();
// realException.printStackTrace();
if (!(realException instanceof java.rmi.RemoteException)) {
ConfigurationException ce = new ConfigurationException(
"Problem invoking javax.rmi.CORBA.Stub.connect()");
ce.setRootCause(realException);
throw ce;
}
// ignore RemoteException because stub might have already
// been connected
} catch (IllegalAccessException e) {
ConfigurationException ce = new ConfigurationException(
"Cannot invoke javax.rmi.CORBA.Stub.connect()");
ce.setRootCause(e);
throw ce;
}
// Finally, return stub
return (org.omg.CORBA.Object)stub;
}
/**
* Get ORB using given server and port number, and properties from environment.
*
* @param server Possibly null server; if null means use default;
* For applet, it is the applet host; for app, it is localhost.
* @param port Port number, -1 means default port
* @param env Possibly null environment. Contains environment properties.
* Could contain ORB itself; or applet used for initializing ORB.
* Use all String properties from env for initializing ORB
* @return A non-null ORB.
*/
public static ORB getOrb(String server, int port, Hashtable<?,?> env) {
// See if we can get info from environment
Properties orbProp;
// Extract any org.omg.CORBA properties from environment
if (env != null) {
if (env instanceof Properties) {
// Already a Properties, just clone
orbProp = (Properties) env.clone();
} else {
// Get all String properties
Enumeration<?> envProp;
orbProp = new Properties();
for (envProp = env.keys(); envProp.hasMoreElements();) {
String key = (String)envProp.nextElement();
Object val = env.get(key);
if (val instanceof String) {
orbProp.put(key, val);
}
}
}
} else {
orbProp = new Properties();
}
if (server != null) {
orbProp.put("org.omg.CORBA.ORBInitialHost", server);
}
if (port >= 0) {
orbProp.put("org.omg.CORBA.ORBInitialPort", ""+port);
}
// Get Applet from environment
if (env != null) {
Object applet = env.get(Context.APPLET);
if (applet != null) {
// Create ORBs for an applet
return initAppletORB(applet, orbProp);
}
}
// Create ORBs using orbProp for a standalone application
return ORB.init(new String[0], orbProp);
}
/**
* Check whether object factory code base is trusted.
* Classes may only be loaded from an arbitrary URL code base when
* the system property com.sun.jndi.rmi.object.trustURLCodebase
* has been set to "true".
*/
public static boolean isObjectFactoryTrusted(Object obj)
throws NamingException {
// Extract Reference, if possible
Reference ref = null;
if (obj instanceof Reference) {
ref = (Reference) obj;
} else if (obj instanceof Referenceable) {
ref = ((Referenceable)(obj)).getReference();
}
if (ref != null && ref.getFactoryClassLocation() != null &&
!CNCtx.trustURLCodebase) {
throw new ConfigurationException(
"The object factory is untrusted. Set the system property" +
" 'com.sun.jndi.cosnaming.object.trustURLCodebase' to 'true'.");
}
return true;
}
/**
* This method returns a new ORB instance for the given applet
* without creating a static dependency on java.applet.
*/
private static ORB initAppletORB(Object applet, Properties orbProp) {
try {
Class<?> appletClass = Class.forName("java.applet.Applet", true, null);
if (!appletClass.isInstance(applet)) {
throw new ClassCastException(applet.getClass().getName());
}
// invoke the static method ORB.init(applet, orbProp);
Method method = ORB.class.getMethod("init", appletClass, Properties.class);
return (ORB) method.invoke(null, applet, orbProp);
} catch (ClassNotFoundException e) {
// java.applet.Applet doesn't exist and the applet parameter is
// non-null; so throw CCE
throw new ClassCastException(applet.getClass().getName());
} catch (NoSuchMethodException e) {
throw new AssertionError(e);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
}
throw new AssertionError(e);
} catch (IllegalAccessException iae) {
throw new AssertionError(iae);
}
}
// Fields used for reflection of RMI-IIOP
private static Method toStubMethod = null;
private static Method connectMethod = null;
private static Class<?> corbaStubClass = null;
/**
* Initializes reflection method handles for RMI-IIOP.
* @exception ClassNotFoundException javax.rmi.CORBA.* not available
*/
private static void initMethodHandles() throws ClassNotFoundException {
// Get javax.rmi.CORBA.Stub class
corbaStubClass = Class.forName("javax.rmi.CORBA.Stub");
// Get javax.rmi.CORBA.Stub.connect(org.omg.CORBA.ORB) method
try {
connectMethod = corbaStubClass.getMethod("connect",
new Class<?>[] {org.omg.CORBA.ORB.class});
} catch (NoSuchMethodException e) {
throw new IllegalStateException(
"No method definition for javax.rmi.CORBA.Stub.connect(org.omg.CORBA.ORB)");
}
// Get javax.rmi.PortableRemoteObject class
Class<?> proClass = Class.forName("javax.rmi.PortableRemoteObject");
// Get javax.rmi.PortableRemoteObject.toStub(java.rmi.Remote) method
try {
toStubMethod = proClass.getMethod("toStub",
new Class<?>[] {java.rmi.Remote.class});
} catch (NoSuchMethodException e) {
throw new IllegalStateException(
"No method definition for javax.rmi.PortableRemoteObject.toStub(java.rmi.Remote)");
}
}
}

View File

@@ -0,0 +1,692 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.toolkit.ctx;
import javax.naming.*;
/**
* Clients: deal only with names for its own naming service
* and deals with single contexts that can be built up into
* hierarchical naming systems.
* Direct subclasses of AtomicContext must provide implementations for
* the abstract a_ Context methods, and c_parseComponent().
*
* If the subclass implements the notion of implicit nns,
* it must override the a_*_nns Context methods as well.
*
* @author Rosanna Lee
*
*/
public abstract class AtomicContext extends ComponentContext {
private static int debug = 0;
protected AtomicContext () {
_contextType = _ATOMIC;
}
// ------ Abstract methods whose implementation are provided by subclasses
/* Equivalent to Context methods */
protected abstract Object a_lookup(String name, Continuation cont)
throws NamingException;
protected abstract Object a_lookupLink(String name, Continuation cont)
throws NamingException;
protected abstract NamingEnumeration<NameClassPair> a_list(
Continuation cont) throws NamingException;
protected abstract NamingEnumeration<Binding> a_listBindings(
Continuation cont) throws NamingException;
protected abstract void a_bind(String name, Object obj, Continuation cont)
throws NamingException;
protected abstract void a_rebind(String name, Object obj, Continuation cont)
throws NamingException;
protected abstract void a_unbind(String name, Continuation cont)
throws NamingException;
protected abstract void a_destroySubcontext(String name, Continuation cont)
throws NamingException;
protected abstract Context a_createSubcontext(String name,
Continuation cont) throws NamingException;
protected abstract void a_rename(String oldname, Name newname,
Continuation cont) throws NamingException;
protected abstract NameParser a_getNameParser(Continuation cont)
throws NamingException;
/* Parsing */
/**
* Parse 'inputName' into two parts:
* head: the first component in this name
* tail: the rest of the unused name.
*
* Subclasses should provide an implementation for this method
* which parses inputName using its own name syntax.
*/
protected abstract StringHeadTail c_parseComponent(String inputName,
Continuation cont) throws NamingException;
// ------ Methods that need to be overridden by subclass
/* Resolution method for supporting federation */
/**
* Resolves the nns for 'name' when the named context is acting
* as an intermediate context.
*
* For a system that supports junctions, this would be equilvalent to
* a_lookup(name, cont);
* because for junctions, an intermediate slash simply signifies
* a syntactic separator.
*
* For a system that supports implicit nns, this would be equivalent to
* a_lookup_nns(name, cont);
* because for implicit nns, a slash always signifies the implicit nns,
* regardless of whether it is intermediate or trailing.
*
* By default this method supports junctions, and also allows for an
* implicit nns to be dynamically determined through the use of the
* "nns" reference (see a_processJunction_nns()).
* Contexts that implement implicit nns directly should provide an
* appropriate override.
*/
protected Object a_resolveIntermediate_nns(String name, Continuation cont)
throws NamingException {
try {
final Object obj = a_lookup(name, cont);
// Do not append "" to Continuation 'cont' even if set
// because the intention is to ignore the nns
//
if (obj != null && getClass().isInstance(obj)) {
// If "obj" is in the same type as this object, it must
// not be a junction. Continue the lookup with "/".
cont.setContinueNNS(obj, name, this);
return null;
} else if (obj != null && !(obj instanceof Context)) {
// obj is not even a context, so try to find its nns
// dynamically by constructing a Reference containing obj.
RefAddr addr = new RefAddr("nns") {
public Object getContent() {
return obj;
}
private static final long serialVersionUID =
-3399518522645918499L;
};
Reference ref = new Reference("java.lang.Object", addr);
// Resolved name has trailing slash to indicate nns
CompositeName resName = new CompositeName();
resName.add(name);
resName.add(""); // add trailing slash
// Set continuation leave it to
// PartialCompositeContext.getPCContext() to throw CPE.
// Do not use setContinueNNS() because we've already
// consumed "/" (i.e., moved it to resName).
cont.setContinue(ref, resName, this);
return null;
} else {
return obj;
}
} catch (NamingException e) {
e.appendRemainingComponent(""); // add nns back
throw e;
}
}
/* Equivalent of Context Methods for supporting nns */
// The following methods are called when the DirContext methods
// are invoked with a name that has a trailing slash.
// For naming systems that support implicit nns,
// the trailing slash signifies the implicit nns.
// For such naming systems, override these a_*_nns methods.
//
// For naming systems that support junctions (explicit nns),
// the trailing slash is meaningless because a junction does not
// have an implicit nns. The default implementation here
// throws a NameNotFoundException for such names.
// If a context wants to accept a trailing slash as having
// the same meaning as the same name without a trailing slash,
// then it should override these a_*_nns methods.
protected Object a_lookup_nns(String name, Continuation cont)
throws NamingException {
a_processJunction_nns(name, cont);
return null;
}
protected Object a_lookupLink_nns(String name, Continuation cont)
throws NamingException {
a_processJunction_nns(name, cont);
return null;
}
protected NamingEnumeration<NameClassPair> a_list_nns(Continuation cont)
throws NamingException {
a_processJunction_nns(cont);
return null;
}
protected NamingEnumeration<Binding> a_listBindings_nns(Continuation cont)
throws NamingException {
a_processJunction_nns(cont);
return null;
}
protected void a_bind_nns(String name, Object obj, Continuation cont)
throws NamingException {
a_processJunction_nns(name, cont);
}
protected void a_rebind_nns(String name, Object obj, Continuation cont)
throws NamingException {
a_processJunction_nns(name, cont);
}
protected void a_unbind_nns(String name, Continuation cont)
throws NamingException {
a_processJunction_nns(name, cont);
}
protected Context a_createSubcontext_nns(String name, Continuation cont)
throws NamingException {
a_processJunction_nns(name, cont);
return null;
}
protected void a_destroySubcontext_nns(String name, Continuation cont)
throws NamingException {
a_processJunction_nns(name, cont);
}
protected void a_rename_nns(String oldname, Name newname, Continuation cont)
throws NamingException {
a_processJunction_nns(oldname, cont);
}
protected NameParser a_getNameParser_nns(Continuation cont)
throws NamingException {
a_processJunction_nns(cont);
return null;
}
protected boolean isEmpty(String name) {
return name == null || name.equals("");
}
// ------ implementations of c_ and c_*_nns methods using
// ------ the corresponding a_ and a_*_nns methods
/* Equivalent to methods in Context interface */
protected Object c_lookup(Name name, Continuation cont)
throws NamingException {
Object ret = null;
if (resolve_to_penultimate_context(name, cont)) {
ret = a_lookup(name.toString(), cont);
if (ret != null && ret instanceof LinkRef) {
cont.setContinue(ret, name, this);
ret = null;
}
}
return ret;
}
protected Object c_lookupLink(Name name, Continuation cont)
throws NamingException {
if (resolve_to_penultimate_context(name, cont)) {
return a_lookupLink(name.toString(), cont);
}
return null;
}
protected NamingEnumeration<NameClassPair> c_list(Name name,
Continuation cont) throws NamingException {
if (resolve_to_context(name, cont)) {
return a_list(cont);
}
return null;
}
protected NamingEnumeration<Binding> c_listBindings(Name name,
Continuation cont) throws NamingException {
if (resolve_to_context(name, cont)) {
return a_listBindings(cont);
}
return null;
}
protected void c_bind(Name name, Object obj, Continuation cont)
throws NamingException {
if (resolve_to_penultimate_context(name, cont))
a_bind(name.toString(), obj, cont);
}
protected void c_rebind(Name name, Object obj, Continuation cont)
throws NamingException {
if (resolve_to_penultimate_context(name, cont))
a_rebind(name.toString(), obj, cont);
}
protected void c_unbind(Name name, Continuation cont)
throws NamingException {
if (resolve_to_penultimate_context(name, cont))
a_unbind(name.toString(), cont);
}
protected void c_destroySubcontext(Name name, Continuation cont)
throws NamingException {
if (resolve_to_penultimate_context(name, cont))
a_destroySubcontext(name.toString(), cont);
}
protected Context c_createSubcontext(Name name,
Continuation cont) throws NamingException {
if (resolve_to_penultimate_context(name, cont))
return a_createSubcontext(name.toString(), cont);
else
return null;
}
protected void c_rename(Name oldname, Name newname,
Continuation cont) throws NamingException {
if (resolve_to_penultimate_context(oldname, cont))
a_rename(oldname.toString(), newname, cont);
}
protected NameParser c_getNameParser(Name name,
Continuation cont) throws NamingException {
if (resolve_to_context(name, cont))
return a_getNameParser(cont);
return null;
}
/* The following are overridden only for AtomicContexts.
* AtomicContext is used by PartialCompositeDirContext and ComponentDirContext
* in the inheritance tree to make use of methods in
* PartialCompositeContext and ComponentContext. We only want to use the
* atomic forms when we're actually an atomic context.
*/
/* From ComponentContext */
protected Object c_resolveIntermediate_nns(Name name, Continuation cont)
throws NamingException {
if (_contextType == _ATOMIC) {
Object ret = null;
if (resolve_to_penultimate_context_nns(name, cont)) {
ret = a_resolveIntermediate_nns(name.toString(), cont);
if (ret != null && ret instanceof LinkRef) {
cont.setContinue(ret, name, this);
ret = null;
}
}
return ret;
} else {
// use ComponentContext
return super.c_resolveIntermediate_nns(name, cont);
}
}
/* Equivalent to methods in Context interface for nns */
protected Object c_lookup_nns(Name name, Continuation cont)
throws NamingException {
if (_contextType == _ATOMIC) {
Object ret = null;
if (resolve_to_penultimate_context_nns(name, cont)) {
ret = a_lookup_nns(name.toString(), cont);
if (ret != null && ret instanceof LinkRef) {
cont.setContinue(ret, name, this);
ret = null;
}
}
return ret;
} else {
return super.c_lookup_nns(name, cont);
}
}
protected Object c_lookupLink_nns(Name name, Continuation cont)
throws NamingException {
if (_contextType == _ATOMIC) {
// %%% check logic
resolve_to_nns_and_continue(name, cont);
return null;
} else {
// use ComponentContext
return super.c_lookupLink_nns(name, cont);
}
}
protected NamingEnumeration<NameClassPair> c_list_nns(Name name,
Continuation cont) throws NamingException {
if (_contextType == _ATOMIC) {
resolve_to_nns_and_continue(name, cont);
return null;
} else {
// use ComponentContext
return super.c_list_nns(name, cont);
}
}
protected NamingEnumeration<Binding> c_listBindings_nns(Name name,
Continuation cont) throws NamingException {
if (_contextType == _ATOMIC) {
resolve_to_nns_and_continue(name, cont);
return null;
} else {
// use ComponentContext
return super.c_listBindings_nns(name, cont);
}
}
protected void c_bind_nns(Name name, Object obj, Continuation cont)
throws NamingException {
if (_contextType == _ATOMIC) {
if (resolve_to_penultimate_context_nns(name, cont))
a_bind_nns(name.toString(), obj, cont);
} else {
// use ComponentContext
super.c_bind_nns(name, obj, cont);
}
}
protected void c_rebind_nns(Name name, Object obj, Continuation cont)
throws NamingException {
if (_contextType == _ATOMIC) {
if (resolve_to_penultimate_context_nns(name, cont))
a_rebind_nns(name.toString(), obj, cont);
} else {
// use ComponentContext
super.c_rebind_nns(name, obj, cont);
}
}
protected void c_unbind_nns(Name name, Continuation cont)
throws NamingException {
if (_contextType == _ATOMIC) {
if (resolve_to_penultimate_context_nns(name, cont))
a_unbind_nns(name.toString(), cont);
} else {
// use ComponentContext
super.c_unbind_nns(name, cont);
}
}
protected Context c_createSubcontext_nns(Name name,
Continuation cont) throws NamingException {
if (_contextType == _ATOMIC) {
if (resolve_to_penultimate_context_nns(name, cont))
return a_createSubcontext_nns(name.toString(), cont);
else
return null;
} else {
// use ComponentContext
return super.c_createSubcontext_nns(name, cont);
}
}
protected void c_destroySubcontext_nns(Name name, Continuation cont)
throws NamingException {
if (_contextType == _ATOMIC) {
if (resolve_to_penultimate_context_nns(name, cont))
a_destroySubcontext_nns(name.toString(), cont);
} else {
// use ComponentContext
super.c_destroySubcontext_nns(name, cont);
}
}
protected void c_rename_nns(Name oldname, Name newname, Continuation cont)
throws NamingException {
if (_contextType == _ATOMIC) {
if (resolve_to_penultimate_context_nns(oldname, cont))
a_rename_nns(oldname.toString(), newname, cont);
} else {
// use ComponentContext
super.c_rename_nns(oldname, newname, cont);
}
}
protected NameParser c_getNameParser_nns(Name name, Continuation cont)
throws NamingException {
if (_contextType == _ATOMIC) {
resolve_to_nns_and_continue(name, cont);
return null;
} else {
// use COmponentContext
return super.c_getNameParser_nns(name, cont);
}
}
// -------------- internal methods used by this class
/* Handles nns for junctions */
/**
* This function is used when implementing a naming system that
* supports junctions. For example, when the a_bind_nns(name, newobj)
* method is invoked, that means the caller is attempting to bind the
* object 'newobj' to the nns of 'name'. For context that supports
* junctions, 'name' names a junction and is pointing to the root
* of another naming system, which in turn might have an nns.
* This means that a_bind_nns() should first resolve 'name' and attempt to
* continue the operation in the context named by 'name'. (i.e. bind
* to the nns of the context named by 'name').
* If name is already empty, then throw NameNotFoundException because
* this context by default does not have any nns.
*/
protected void a_processJunction_nns(String name, Continuation cont)
throws NamingException {
if (name.equals("")) {
NameNotFoundException e = new NameNotFoundException();
cont.setErrorNNS(this, name);
throw cont.fillInException(e);
}
try {
// lookup name to continue operation in nns
Object target = a_lookup(name, cont);
if (cont.isContinue())
cont.appendRemainingComponent(""); // add nns back
else {
cont.setContinueNNS(target, name, this);
}
} catch (NamingException e) {
e.appendRemainingComponent(""); // add nns back
throw e;
}
}
/**
* This function is used when implementing a naming system that
* supports junctions. For example, when the a_list_nns(newobj)
* method is invoked, that means the caller is attempting to list the
* the nns context of of this context. For a context that supports
* junctions, it by default does not have any nns. Consequently,
* a NameNotFoundException is thrown.
*/
protected void a_processJunction_nns(Continuation cont) throws NamingException {
// Construct a new Reference that contains this context.
RefAddr addr = new RefAddr("nns") {
public Object getContent() {
return AtomicContext.this;
}
private static final long serialVersionUID = 3449785852664978312L;
};
Reference ref = new Reference("java.lang.Object", addr);
// Set continuation leave it to PartialCompositeContext.getPCContext()
// to throw the exception.
// Do not use setContinueNNS() because we've are
// setting relativeResolvedName to "/".
cont.setContinue(ref, _NNS_NAME, this);
}
/* *********** core resolution routines ******************* */
/** Resolve to context named by 'name'.
* Returns true if at named context (i.e. 'name' is empty name).
* Returns false otherwise, and sets Continuation on parts of 'name'
* not yet resolved.
*/
protected boolean resolve_to_context(Name name, Continuation cont)
throws NamingException {
String target = name.toString();
StringHeadTail ht = c_parseComponent(target, cont);
String tail = ht.getTail();
String head = ht.getHead();
if (debug > 0)
System.out.println("RESOLVE TO CONTEXT(" + target + ") = {" +
head + ", " + tail + "}");
if (head == null) {
// something is wrong; no name at all
InvalidNameException e = new InvalidNameException();
throw cont.fillInException(e);
}
if (!isEmpty(head)) {
// if there is head is a non-empty name
// this means more resolution to be done
try {
Object headCtx = a_lookup(head, cont);
// System.out.println("answer " + headCtx);
if (headCtx != null)
cont.setContinue(headCtx, head, this, (tail == null ? "" : tail));
else if (cont.isContinue())
cont.appendRemainingComponent(tail);
} catch (NamingException e) {
e.appendRemainingComponent(tail);
throw e;
}
} else {
cont.setSuccess(); // clear
return true;
}
return false;
}
/**
* Resolves to penultimate context named by 'name'.
* Returns true if penultimate context has been reached (i.e. name
* only has one atomic component left).
* Returns false otherwise, and sets Continuation to parts of name
* not yet resolved.
*/
protected boolean resolve_to_penultimate_context(Name name, Continuation cont)
throws NamingException {
String target = name.toString();
if (debug > 0)
System.out.println("RESOLVE TO PENULTIMATE" + target);
StringHeadTail ht = c_parseComponent(target, cont);
String tail = ht.getTail();
String head = ht.getHead();
if (head == null) {
// something is wrong; no name at all
InvalidNameException e = new InvalidNameException();
throw cont.fillInException(e);
}
if (!isEmpty(tail)) {
// more components; hence not at penultimate context yet
try {
Object headCtx = a_lookup(head, cont);
if (headCtx != null)
cont.setContinue(headCtx, head, this, tail);
else if (cont.isContinue())
cont.appendRemainingComponent(tail);
} catch (NamingException e) {
e.appendRemainingComponent(tail);
throw e;
}
} else {
// already at penultimate context
cont.setSuccess(); // clear
return true;
}
return false;
}
/**
* This function is similar to resolve_to_penultimate_context()
* except it should only be called by the nns() functions.
* This function fixes any exception or continuations so that
* it will have the proper nns name.
*/
protected boolean resolve_to_penultimate_context_nns(Name name,
Continuation cont)
throws NamingException {
try {
if (debug > 0)
System.out.println("RESOLVE TO PENULTIMATE NNS" + name.toString());
boolean answer = resolve_to_penultimate_context(name, cont);
// resolve_to_penultimate_context() only calls a_lookup().
// Any continuation it sets is lacking the nns, so
// we need to add it back
if (cont.isContinue())
cont.appendRemainingComponent("");
return answer;
} catch (NamingException e) {
// resolve_to_penultimate_context() only calls a_lookup().
// Any exceptions it throws is lacking the nns, so
// we need to add it back.
e.appendRemainingComponent("");
throw e;
}
}
/**
* Resolves to nns associated with 'name' and set Continuation
* to the result.
*/
protected void resolve_to_nns_and_continue(Name name, Continuation cont)
throws NamingException {
if (debug > 0)
System.out.println("RESOLVE TO NNS AND CONTINUE" + name.toString());
if (resolve_to_penultimate_context_nns(name, cont)) {
Object nns = a_lookup_nns(name.toString(), cont);
if (nns != null)
cont.setContinue(nns, name, this);
}
}
}

View File

@@ -0,0 +1,393 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.toolkit.ctx;
import javax.naming.*;
import javax.naming.directory.*;
/**
* Direct subclasses of AtomicDirContext must provide implementations for
* the abstract a_ DirContext methods, and override the a_ Context methods
* (which are no longer abstract because they have been overriden by
* PartialCompositeDirContext with dummy implementations).
*
* If the subclass implements the notion of implicit nns,
* it must override the a_*_nns DirContext and Context methods as well.
*
* @author Rosanna Lee
*
*/
public abstract class AtomicDirContext extends ComponentDirContext {
protected AtomicDirContext() {
_contextType = _ATOMIC;
}
// ------ Abstract methods whose implementations come from subclass
protected abstract Attributes a_getAttributes(String name, String[] attrIds,
Continuation cont)
throws NamingException;
protected abstract void a_modifyAttributes(String name, int mod_op,
Attributes attrs,
Continuation cont)
throws NamingException;
protected abstract void a_modifyAttributes(String name,
ModificationItem[] mods,
Continuation cont)
throws NamingException;
protected abstract void a_bind(String name, Object obj,
Attributes attrs,
Continuation cont)
throws NamingException;
protected abstract void a_rebind(String name, Object obj,
Attributes attrs,
Continuation cont)
throws NamingException;
protected abstract DirContext a_createSubcontext(String name,
Attributes attrs,
Continuation cont)
throws NamingException;
protected abstract NamingEnumeration<SearchResult> a_search(
Attributes matchingAttributes,
String[] attributesToReturn,
Continuation cont)
throws NamingException;
protected abstract NamingEnumeration<SearchResult> a_search(
String name,
String filterExpr,
Object[] filterArgs,
SearchControls cons,
Continuation cont)
throws NamingException;
protected abstract NamingEnumeration<SearchResult> a_search(
String name,
String filter,
SearchControls cons,
Continuation cont)
throws NamingException;
protected abstract DirContext a_getSchema(Continuation cont)
throws NamingException;
protected abstract DirContext a_getSchemaClassDefinition(Continuation cont)
throws NamingException;
// ------ Methods that need to be overridden by subclass
// default implementations of a_*_nns methods
// The following methods are called when the DirContext methods
// are invoked with a name that has a trailing slash.
// For naming systems that support implicit nns,
// the trailing slash signifies the implicit nns.
// For such naming systems, override these a_*_nns methods.
//
// For naming systems that support junctions (explicit nns),
// the trailing slash is meaningless because a junction does not
// have an implicit nns. The default implementation here
// throws a NameNotFoundException for such names.
// If a context wants to accept a trailing slash as having
// the same meaning as the same name without a trailing slash,
// then it should override these a_*_nns methods.
protected Attributes a_getAttributes_nns(String name,
String[] attrIds,
Continuation cont)
throws NamingException {
a_processJunction_nns(name, cont);
return null;
}
protected void a_modifyAttributes_nns(String name, int mod_op,
Attributes attrs,
Continuation cont)
throws NamingException {
a_processJunction_nns(name, cont);
}
protected void a_modifyAttributes_nns(String name,
ModificationItem[] mods,
Continuation cont)
throws NamingException {
a_processJunction_nns(name, cont);
}
protected void a_bind_nns(String name, Object obj,
Attributes attrs,
Continuation cont)
throws NamingException {
a_processJunction_nns(name, cont);
}
protected void a_rebind_nns(String name, Object obj,
Attributes attrs,
Continuation cont)
throws NamingException {
a_processJunction_nns(name, cont);
}
protected DirContext a_createSubcontext_nns(String name,
Attributes attrs,
Continuation cont)
throws NamingException {
a_processJunction_nns(name, cont);
return null;
}
protected NamingEnumeration<SearchResult> a_search_nns(
Attributes matchingAttributes,
String[] attributesToReturn,
Continuation cont)
throws NamingException {
a_processJunction_nns(cont);
return null;
}
protected NamingEnumeration<SearchResult> a_search_nns(String name,
String filterExpr,
Object[] filterArgs,
SearchControls cons,
Continuation cont)
throws NamingException {
a_processJunction_nns(name, cont);
return null;
}
protected NamingEnumeration<SearchResult> a_search_nns(String name,
String filter,
SearchControls cons,
Continuation cont)
throws NamingException {
a_processJunction_nns(name, cont);
return null;
}
protected DirContext a_getSchema_nns(Continuation cont) throws NamingException {
a_processJunction_nns(cont);
return null;
}
protected DirContext a_getSchemaDefinition_nns(Continuation cont)
throws NamingException {
a_processJunction_nns(cont);
return null;
}
// ------- implementations of c_ DirContext methods using corresponding
// ------- a_ and a_*_nns methods
protected Attributes c_getAttributes(Name name, String[] attrIds,
Continuation cont)
throws NamingException {
if (resolve_to_penultimate_context(name, cont))
return a_getAttributes(name.toString(), attrIds, cont);
return null;
}
protected void c_modifyAttributes(Name name, int mod_op,
Attributes attrs, Continuation cont)
throws NamingException {
if (resolve_to_penultimate_context(name, cont))
a_modifyAttributes(name.toString(), mod_op, attrs, cont);
}
protected void c_modifyAttributes(Name name, ModificationItem[] mods,
Continuation cont)
throws NamingException {
if (resolve_to_penultimate_context(name, cont))
a_modifyAttributes(name.toString(), mods, cont);
}
protected void c_bind(Name name, Object obj,
Attributes attrs, Continuation cont)
throws NamingException {
if (resolve_to_penultimate_context(name, cont))
a_bind(name.toString(), obj, attrs, cont);
}
protected void c_rebind(Name name, Object obj,
Attributes attrs, Continuation cont)
throws NamingException {
if (resolve_to_penultimate_context(name, cont))
a_rebind(name.toString(), obj, attrs, cont);
}
protected DirContext c_createSubcontext(Name name,
Attributes attrs,
Continuation cont)
throws NamingException {
if (resolve_to_penultimate_context(name, cont))
return a_createSubcontext(name.toString(),
attrs, cont);
return null;
}
protected NamingEnumeration<SearchResult> c_search(Name name,
Attributes matchingAttributes,
String[] attributesToReturn,
Continuation cont)
throws NamingException {
if (resolve_to_context(name, cont))
return a_search(matchingAttributes, attributesToReturn, cont);
return null;
}
protected NamingEnumeration<SearchResult> c_search(Name name,
String filter,
SearchControls cons,
Continuation cont)
throws NamingException {
if (resolve_to_penultimate_context(name, cont))
return a_search(name.toString(), filter, cons, cont);
return null;
}
protected NamingEnumeration<SearchResult> c_search(Name name,
String filterExpr,
Object[] filterArgs,
SearchControls cons,
Continuation cont)
throws NamingException {
if (resolve_to_penultimate_context(name, cont))
return a_search(name.toString(), filterExpr, filterArgs, cons, cont);
return null;
}
protected DirContext c_getSchema(Name name, Continuation cont)
throws NamingException {
if (resolve_to_context(name, cont))
return a_getSchema(cont);
return null;
}
protected DirContext c_getSchemaClassDefinition(Name name, Continuation cont)
throws NamingException {
if (resolve_to_context(name, cont))
return a_getSchemaClassDefinition(cont);
return null;
}
/* equivalent to methods in DirContext interface for nns */
protected Attributes c_getAttributes_nns(Name name, String[] attrIds,
Continuation cont)
throws NamingException {
if (resolve_to_penultimate_context_nns(name, cont))
return a_getAttributes_nns(name.toString(), attrIds, cont);
return null;
}
protected void c_modifyAttributes_nns(Name name, int mod_op,
Attributes attrs, Continuation cont)
throws NamingException {
if (resolve_to_penultimate_context_nns(name, cont))
a_modifyAttributes_nns(name.toString(), mod_op, attrs, cont);
}
protected void c_modifyAttributes_nns(Name name, ModificationItem[] mods,
Continuation cont)
throws NamingException {
if (resolve_to_penultimate_context_nns(name, cont))
a_modifyAttributes_nns(name.toString(), mods, cont);
}
protected void c_bind_nns(Name name, Object obj,
Attributes attrs, Continuation cont)
throws NamingException {
if (resolve_to_penultimate_context_nns(name, cont))
a_bind_nns(name.toString(), obj, attrs, cont);
}
protected void c_rebind_nns(Name name, Object obj,
Attributes attrs, Continuation cont)
throws NamingException {
if (resolve_to_penultimate_context_nns(name, cont))
a_rebind_nns(name.toString(), obj, attrs, cont);
}
protected DirContext c_createSubcontext_nns(Name name,
Attributes attrs,
Continuation cont)
throws NamingException {
if (resolve_to_penultimate_context_nns(name, cont))
return a_createSubcontext_nns(name.toString(), attrs, cont);
return null;
}
protected NamingEnumeration<SearchResult> c_search_nns(
Name name,
Attributes matchingAttributes,
String[] attributesToReturn,
Continuation cont)
throws NamingException {
resolve_to_nns_and_continue(name, cont);
return null;
}
protected NamingEnumeration<SearchResult> c_search_nns(Name name,
String filter,
SearchControls cons,
Continuation cont)
throws NamingException {
if (resolve_to_penultimate_context_nns(name, cont))
return a_search_nns(name.toString(), filter, cons, cont);
return null;
}
protected NamingEnumeration<SearchResult> c_search_nns(Name name,
String filterExpr,
Object[] filterArgs,
SearchControls cons,
Continuation cont)
throws NamingException {
if (resolve_to_penultimate_context_nns(name, cont))
return a_search_nns(name.toString(), filterExpr, filterArgs,
cons, cont);
return null;
}
protected DirContext c_getSchema_nns(Name name, Continuation cont)
throws NamingException {
resolve_to_nns_and_continue(name, cont);
return null;
}
protected DirContext c_getSchemaClassDefinition_nns(Name name, Continuation cont)
throws NamingException {
resolve_to_nns_and_continue(name, cont);
return null;
}
}

View File

@@ -0,0 +1,805 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.toolkit.ctx;
import javax.naming.*;
import javax.naming.spi.ResolveResult;
/**
* Provides implementation of p_* operations using
* c_* operations provided by subclasses.
*
* Clients: deal only with names for its own naming service. Must
* provide implementations for c_* methods, and for p_parseComponent()
* and the c_*_nns methods if the defaults are not appropriate.
*
* @author Rosanna Lee
* @author Scott Seligman
*/
public abstract class ComponentContext extends PartialCompositeContext {
private static int debug = 0;
protected ComponentContext() {
_contextType = _COMPONENT;
}
// ------ Abstract methods whose implementation are provided by subclass
/* Equivalent methods in Context interface */
protected abstract Object c_lookup(Name name, Continuation cont)
throws NamingException;
protected abstract Object c_lookupLink(Name name, Continuation cont)
throws NamingException;
protected abstract NamingEnumeration<NameClassPair> c_list(Name name,
Continuation cont) throws NamingException;
protected abstract NamingEnumeration<Binding> c_listBindings(Name name,
Continuation cont) throws NamingException;
protected abstract void c_bind(Name name, Object obj, Continuation cont)
throws NamingException;
protected abstract void c_rebind(Name name, Object obj, Continuation cont)
throws NamingException;
protected abstract void c_unbind(Name name, Continuation cont)
throws NamingException;
protected abstract void c_destroySubcontext(Name name, Continuation cont)
throws NamingException;
protected abstract Context c_createSubcontext(Name name,
Continuation cont) throws NamingException;
protected abstract void c_rename(Name oldname, Name newname,
Continuation cont) throws NamingException;
protected abstract NameParser c_getNameParser(Name name, Continuation cont)
throws NamingException;
// ------ Methods that may need to be overridden by subclass
/* Parsing method */
/**
* Determines which of the first components of 'name' belong
* to this naming system.
* If no components belong to this naming system, return
* the empty name (new CompositeName()) as the head,
* and the entire name as the tail.
*
* The default implementation supports strong separation.
* If the name is empty or if the first component is empty,
* head is the empty name and tail is the entire name.
* (This means that this context does not have any name to work with).
* Otherwise, it returns the first component as head, and the rest of
* the components as tail.
*
* Subclass should override this method according its own policies.
*
* For example, a weakly separated system with dynamic boundary
* determination would simply return as head 'name'.
* A weakly separated with static boundary
* determination would select the components in the front of 'name'
* that conform to some syntax rules. (e.g. in X.500 syntax, perhaps
* select front components that have a equal sign).
* If none conforms, return an empty name.
*/
protected HeadTail p_parseComponent(Name name, Continuation cont)
throws NamingException {
int separator;
// if no name to parse, or if we're already at boundary
if (name.isEmpty() || name.get(0).equals("")) {
separator = 0;
} else {
separator = 1;
}
Name head, tail;
if (name instanceof CompositeName) {
head = name.getPrefix(separator);
tail = name.getSuffix(separator);
} else {
// treat like compound name
head = new CompositeName().add(name.toString());
tail = null;
}
if (debug > 2) {
System.err.println("ORIG: " + name);
System.err.println("PREFIX: " + name);
System.err.println("SUFFIX: " + null);
}
return new HeadTail(head, tail);
}
/* Resolution method for supporting federation */
/**
* Resolves the nns for 'name' when the named context is acting
* as an intermediate context.
*
* For a system that supports only junctions, this would be
* equilvalent to
* c_lookup(name, cont);
* because for junctions, an intermediate slash simply signifies
* a syntactic separator.
*
* For a system that supports only implicit nns, this would be
* equivalent to
* c_lookup_nns(name, cont);
* because for implicit nns, a slash always signifies the implicit nns,
* regardless of whether it is intermediate or trailing.
*
* By default this method supports junctions, and also allows for an
* implicit nns to be dynamically determined through the use of the
* "nns" reference (see c_processJunction_nns()).
* Contexts that implement implicit nns directly should provide an
* appropriate override.
*
* A junction, by definition, is a binding of a name in one
* namespace to an object in another. The default implementation
* of this method detects the crossover into another namespace
* using the following heuristic: there is a junction when "name"
* resolves to a context that is not an instance of
* this.getClass(). Contexts supporting junctions for which this
* heuristic is inappropriate should override this method.
*/
protected Object c_resolveIntermediate_nns(Name name, Continuation cont)
throws NamingException {
try {
final Object obj = c_lookup(name, cont);
// Do not append "" to Continuation 'cont' even if set
// because the intention is to ignore the nns
if (obj != null && getClass().isInstance(obj)) {
// If "obj" is in the same type as this object, it must
// not be a junction. Continue the lookup with "/".
cont.setContinueNNS(obj, name, this);
return null;
} else if (obj != null && !(obj instanceof Context)) {
// obj is not even a context, so try to find its nns
// dynamically by constructing a Reference containing obj.
RefAddr addr = new RefAddr("nns") {
public Object getContent() {
return obj;
}
private static final long serialVersionUID =
-8831204798861786362L;
};
Reference ref = new Reference("java.lang.Object", addr);
// Resolved name has trailing slash to indicate nns
CompositeName resName = (CompositeName)name.clone();
resName.add(""); // add trailing slash
// Set continuation leave it to
// PartialCompositeContext.getPCContext() to throw CPE.
// Do not use setContinueNNS() because we've already
// consumed "/" (i.e., moved it to resName).
cont.setContinue(ref, resName, this);
return null;
} else {
// Consume "/" and continue
return obj;
}
} catch (NamingException e) {
e.appendRemainingComponent(""); // add nns back
throw e;
}
}
/* Equivalent of Context Methods for supporting nns */
// The following methods are called when the Context methods
// are invoked with a name that has a trailing slash.
// For naming systems that support implicit nns,
// the trailing slash signifies the implicit nns.
// For such naming systems, override these c_*_nns methods.
//
// For naming systems that do not support implicit nns, the
// default implementations here throw an exception. See
// c_processJunction_nns() for details.
protected Object c_lookup_nns(Name name, Continuation cont)
throws NamingException {
c_processJunction_nns(name, cont);
return null;
}
protected Object c_lookupLink_nns(Name name, Continuation cont)
throws NamingException {
c_processJunction_nns(name, cont);
return null;
}
protected NamingEnumeration<NameClassPair> c_list_nns(Name name,
Continuation cont) throws NamingException {
c_processJunction_nns(name, cont);
return null;
}
protected NamingEnumeration<Binding> c_listBindings_nns(Name name,
Continuation cont) throws NamingException {
c_processJunction_nns(name, cont);
return null;
}
protected void c_bind_nns(Name name, Object obj, Continuation cont)
throws NamingException {
c_processJunction_nns(name, cont);
}
protected void c_rebind_nns(Name name, Object obj, Continuation cont)
throws NamingException {
c_processJunction_nns(name, cont);
}
protected void c_unbind_nns(Name name, Continuation cont)
throws NamingException {
c_processJunction_nns(name, cont);
}
protected Context c_createSubcontext_nns(Name name,
Continuation cont) throws NamingException {
c_processJunction_nns(name, cont);
return null;
}
protected void c_destroySubcontext_nns(Name name, Continuation cont)
throws NamingException {
c_processJunction_nns(name, cont);
}
protected void c_rename_nns(Name oldname, Name newname, Continuation cont)
throws NamingException {
c_processJunction_nns(oldname, cont);
}
protected NameParser c_getNameParser_nns(Name name, Continuation cont)
throws NamingException {
c_processJunction_nns(name, cont);
return null;
}
// ------ internal method used by ComponentContext
/**
* Locates the nns using the default policy. This policy fully
* handles junctions, but otherwise throws an exception when an
* attempt is made to resolve an implicit nns.
*
* The default policy is as follows: If there is a junction in
* the namespace, then resolve to the junction and continue the
* operation there (thus deferring to that context to find its own
* nns). Otherwise, resolve as far as possible and then throw
* CannotProceedException with the resolved object being a reference:
* the address type is "nns", and the address contents is this
* context.
*
* For example, when c_bind_nns(name, obj, ...) is invoked, the
* caller is attempting to bind the object "obj" to the nns of
* "name". If "name" is a junction, it names an object in another
* naming system that (presumably) has an nns. c_bind_nns() will
* first resolve "name" to a context and then attempt to continue
* the bind operation there, (thus binding to the nns of the
* context named by "name"). If "name" is empty then throw an
* exception, since this context does not by default support an
* implicit nns.
*
* To implement a context that does support an implicit nns, it is
* necessary to override this default policy. This is done by
* overriding the c_*_nns() methods (which each call this method
* by default).
*/
protected void c_processJunction_nns(Name name, Continuation cont)
throws NamingException
{
if (name.isEmpty()) {
// Construct a new Reference that contains this context.
RefAddr addr = new RefAddr("nns") {
public Object getContent() {
return ComponentContext.this;
}
private static final long serialVersionUID =
-1389472957988053402L;
};
Reference ref = new Reference("java.lang.Object", addr);
// Set continuation leave it to PartialCompositeContext.getPCContext()
// to throw the exception.
// Do not use setContinueNNS() because we've are
// setting relativeResolvedName to "/".
cont.setContinue(ref, _NNS_NAME, this);
return;
}
try {
// lookup name to continue operation in nns
Object target = c_lookup(name, cont);
if (cont.isContinue())
cont.appendRemainingComponent("");
else {
cont.setContinueNNS(target, name, this);
}
} catch (NamingException e) {
e.appendRemainingComponent(""); // add nns back
throw e;
}
}
protected static final byte USE_CONTINUATION = 1;
protected static final byte TERMINAL_COMPONENT = 2;
protected static final byte TERMINAL_NNS_COMPONENT = 3;
/**
* Determine whether 'name' is a terminal component in
* this naming system.
* If so, return status indicating so, so that caller
* can perform context operation on this name.
*
* If not, then the first component(s) of 'name' names
* an intermediate context. In that case, resolve these components
* and set Continuation to be the object named.
*
* see test cases at bottom of file.
*/
protected HeadTail p_resolveIntermediate(Name name, Continuation cont)
throws NamingException {
int ret = USE_CONTINUATION;
cont.setSuccess(); // initialize
HeadTail p = p_parseComponent(name, cont);
Name tail = p.getTail();
Name head = p.getHead();
if (tail == null || tail.isEmpty()) {
//System.out.println("terminal : " + head);
ret = TERMINAL_COMPONENT;
} else if (!tail.get(0).equals("")) {
// tail does not begin with "/"
/*
if (head.isEmpty()) {
// Context could not find name that it can use
// illegal syntax error or name not found
//System.out.println("nnf exception : " + head);
NamingException e = new NameNotFoundException();
cont.setError(this, name);
throw cont.fillInException(e);
} else {
*/
// head is being used as intermediate context,
// resolve head and set Continuation with tail
try {
Object obj = c_resolveIntermediate_nns(head, cont);
//System.out.println("resInter : " + head + "=" + obj);
if (obj != null)
cont.setContinue(obj, head, this, tail);
else if (cont.isContinue()) {
checkAndAdjustRemainingName(cont.getRemainingName());
cont.appendRemainingName(tail);
}
} catch (NamingException e) {
checkAndAdjustRemainingName(e.getRemainingName());
e.appendRemainingName(tail);
throw e;
}
/*
}
*/
} else {
// tail begins with "/"
if (tail.size() == 1) {
ret = TERMINAL_NNS_COMPONENT;
//System.out.println("terminal_nns : " + head);
} else if (head.isEmpty() || isAllEmpty(tail)) {
// resolve nns of head and continue with tail.getSuffix(1)
Name newTail = tail.getSuffix(1);
try {
Object obj = c_lookup_nns(head, cont);
//System.out.println("lookup_nns : " + head + "=" + obj);
if (obj != null)
cont.setContinue(obj, head, this, newTail);
else if (cont.isContinue()) {
cont.appendRemainingName(newTail);
// Name rname = cont.getRemainingName();
//System.out.println("cont.rname" + rname);
}
} catch (NamingException e) {
e.appendRemainingName(newTail);
throw e;
}
} else {
// head is being used as intermediate context
// resolve and set continuation to tail
try {
Object obj = c_resolveIntermediate_nns(head, cont);
//System.out.println("resInter2 : " + head + "=" + obj);
if (obj != null)
cont.setContinue(obj, head, this, tail);
else if (cont.isContinue()) {
checkAndAdjustRemainingName(cont.getRemainingName());
cont.appendRemainingName(tail);
}
} catch (NamingException e) {
checkAndAdjustRemainingName(e.getRemainingName());
e.appendRemainingName(tail);
throw e;
}
}
}
p.setStatus(ret);
return p;
}
// When c_resolveIntermediate_nns() or c_lookup_nns() sets up
// its continuation, to indicate "nns", it appends an empty
// component to the remaining name (e.g. "eng/"). If last
// component of remaining name is empty; delete empty component
// before appending tail so that composition of the names work
// correctly. For example, when merging "eng/" and "c.b.a", we want
// the result to be "eng/c.b.a" because the trailing slash in eng
// is extraneous. When merging "" and "c.b.a", we want the result
// to be "/c.b.a" and so must keep the trailing slash (empty name).
void checkAndAdjustRemainingName(Name rname) throws InvalidNameException {
int count;
if (rname != null && (count=rname.size()) > 1 &&
rname.get(count-1).equals("")) {
rname.remove(count-1);
}
}
// Returns true if n contains only empty components
protected boolean isAllEmpty(Name n) {
int count = n.size();
for (int i =0; i < count; i++ ) {
if (!n.get(i).equals("")) {
return false;
}
}
return true;
}
// ------ implementations of p_ Resolver and Context methods using
// ------ corresponding c_ and c_*_nns methods
/* implementation for Resolver method */
protected ResolveResult p_resolveToClass(Name name,
Class<?> contextType,
Continuation cont)
throws NamingException {
if (contextType.isInstance(this)) {
cont.setSuccess();
return (new ResolveResult(this, name));
}
ResolveResult ret = null;
HeadTail res = p_resolveIntermediate(name, cont);
switch (res.getStatus()) {
case TERMINAL_NNS_COMPONENT:
Object obj = p_lookup(name, cont);
if (!cont.isContinue() && contextType.isInstance(obj)) {
ret = new ResolveResult(obj, _EMPTY_NAME);
}
break;
case TERMINAL_COMPONENT:
cont.setSuccess(); // no contextType found; return null
break;
default:
/* USE_CONTINUATION */
/* pcont already set or exception thrown */
break;
}
return ret;
}
/* implementations of p_ Context methods */
protected Object p_lookup(Name name, Continuation cont) throws NamingException {
Object ret = null;
HeadTail res = p_resolveIntermediate(name, cont);
switch (res.getStatus()) {
case TERMINAL_NNS_COMPONENT:
ret = c_lookup_nns(res.getHead(), cont);
if (ret instanceof LinkRef) {
cont.setContinue(ret, res.getHead(), this);
ret = null;
}
break;
case TERMINAL_COMPONENT:
ret = c_lookup(res.getHead(), cont);
if (ret instanceof LinkRef) {
cont.setContinue(ret, res.getHead(), this);
ret = null;
}
break;
default:
/* USE_CONTINUATION */
/* pcont already set or exception thrown */
break;
}
return ret;
}
protected NamingEnumeration<NameClassPair> p_list(Name name, Continuation cont)
throws NamingException {
NamingEnumeration<NameClassPair> ret = null;
HeadTail res = p_resolveIntermediate(name, cont);
switch (res.getStatus()) {
case TERMINAL_NNS_COMPONENT:
if (debug > 0)
System.out.println("c_list_nns(" + res.getHead() + ")");
ret = c_list_nns(res.getHead(), cont);
break;
case TERMINAL_COMPONENT:
if (debug > 0)
System.out.println("c_list(" + res.getHead() + ")");
ret = c_list(res.getHead(), cont);
break;
default:
/* USE_CONTINUATION */
/* cont already set or exception thrown */
break;
}
return ret;
}
protected NamingEnumeration<Binding> p_listBindings(Name name, Continuation cont) throws
NamingException {
NamingEnumeration<Binding> ret = null;
HeadTail res = p_resolveIntermediate(name, cont);
switch (res.getStatus()) {
case TERMINAL_NNS_COMPONENT:
ret = c_listBindings_nns(res.getHead(), cont);
break;
case TERMINAL_COMPONENT:
ret = c_listBindings(res.getHead(), cont);
break;
default:
/* USE_CONTINUATION */
/* cont already set or exception thrown */
break;
}
return ret;
}
protected void p_bind(Name name, Object obj, Continuation cont) throws
NamingException {
HeadTail res = p_resolveIntermediate(name, cont);
switch (res.getStatus()) {
case TERMINAL_NNS_COMPONENT:
c_bind_nns(res.getHead(), obj, cont);
break;
case TERMINAL_COMPONENT:
c_bind(res.getHead(), obj, cont);
break;
default:
/* USE_CONTINUATION */
/* cont already set or exception thrown */
break;
}
}
protected void p_rebind(Name name, Object obj, Continuation cont) throws
NamingException {
HeadTail res = p_resolveIntermediate(name, cont);
switch (res.getStatus()) {
case TERMINAL_NNS_COMPONENT:
c_rebind_nns(res.getHead(), obj, cont);
break;
case TERMINAL_COMPONENT:
c_rebind(res.getHead(), obj, cont);
break;
default:
/* USE_CONTINUATION */
/* cont already set or exception thrown */
break;
}
}
protected void p_unbind(Name name, Continuation cont) throws
NamingException {
HeadTail res = p_resolveIntermediate(name, cont);
switch (res.getStatus()) {
case TERMINAL_NNS_COMPONENT:
c_unbind_nns(res.getHead(), cont);
break;
case TERMINAL_COMPONENT:
c_unbind(res.getHead(), cont);
break;
default:
/* USE_CONTINUATION */
/* cont already set or exception thrown */
break;
}
}
protected void p_destroySubcontext(Name name, Continuation cont) throws
NamingException {
HeadTail res = p_resolveIntermediate(name, cont);
switch (res.getStatus()) {
case TERMINAL_NNS_COMPONENT:
c_destroySubcontext_nns(res.getHead(), cont);
break;
case TERMINAL_COMPONENT:
c_destroySubcontext(res.getHead(), cont);
break;
default:
/* USE_CONTINUATION */
/* cont already set or exception thrown */
break;
}
}
protected Context p_createSubcontext(Name name, Continuation cont) throws
NamingException {
Context ret = null;
HeadTail res = p_resolveIntermediate(name, cont);
switch (res.getStatus()) {
case TERMINAL_NNS_COMPONENT:
ret = c_createSubcontext_nns(res.getHead(), cont);
break;
case TERMINAL_COMPONENT:
ret = c_createSubcontext(res.getHead(), cont);
break;
default:
/* USE_CONTINUATION */
/* cont already set or exception thrown */
break;
}
return ret;
}
protected void p_rename(Name oldName, Name newName, Continuation cont) throws
NamingException {
HeadTail res = p_resolveIntermediate(oldName, cont);
switch (res.getStatus()) {
case TERMINAL_NNS_COMPONENT:
c_rename_nns(res.getHead(), newName, cont);
break;
case TERMINAL_COMPONENT:
c_rename(res.getHead(), newName, cont);
break;
default:
/* USE_CONTINUATION */
/* cont already set or exception thrown */
break;
}
}
protected NameParser p_getNameParser(Name name, Continuation cont) throws
NamingException {
NameParser ret = null;
HeadTail res = p_resolveIntermediate(name, cont);
switch (res.getStatus()) {
case TERMINAL_NNS_COMPONENT:
ret = c_getNameParser_nns(res.getHead(), cont);
break;
case TERMINAL_COMPONENT:
ret = c_getNameParser(res.getHead(), cont);
break;
default:
/* USE_CONTINUATION */
/* cont already set or exception thrown */
break;
}
return ret;
}
protected Object p_lookupLink(Name name, Continuation cont)
throws NamingException {
Object ret = null;
HeadTail res = p_resolveIntermediate(name, cont);
switch (res.getStatus()) {
case TERMINAL_NNS_COMPONENT:
ret = c_lookupLink_nns(res.getHead(), cont);
break;
case TERMINAL_COMPONENT:
ret = c_lookupLink(res.getHead(), cont);
break;
default:
/* USE_CONTINUATION */
/* cont already set or exception thrown */
break;
}
return ret;
}
}
/*
* How p_resolveIntermediate() should behave for various test cases
a.b/x {a.b, x}
c_resolveIntermediate_nns(a.b)
continue(x)
{x,}
terminal(x)
a.b/ {a.b, ""}
terminal_nns(a.b);
a.b//
{a.b, ("", "")}
c_lookup_nns(a.b)
continue({""})
{,""}
terminal_nns({})
/x {{}, {"", x}}
c_lookup_nns({})
continue(x)
{x,}
terminal(x)
//y {{}, {"", "", y}}
c_lookup_nns({})
continue({"", y})
{{}, {"", y}}
c_lookup_nns({})
continue(y)
{y,}
terminal(y)
a.b//y {a.b, {"", y}}
c_resolveIntermediate_nns(a.b)
continue({"", y})
{{}, {"",y}}
c_lookup_nns({});
continue(y)
{y,}
terminal(y);
*
*/

View File

@@ -0,0 +1,470 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.toolkit.ctx;
import javax.naming.*;
import javax.naming.directory.*;
/* Direct subclasses of ComponentDirContext must provide implementations for
* the abstract c_ DirContext methods, and override the c_ Context methods
* (which are no longer abstract because they have been overriden by
* AtomicContext, the direct superclass of PartialDSCompositeContext).
*
* If the subclass is supports implicit nns, it must override all the
* c_*_nns methods corresponding to those in DirContext and Context.
* See ComponentContext for details.
*
* @author Rosanna Lee
*/
public abstract class ComponentDirContext extends PartialCompositeDirContext {
protected ComponentDirContext () {
_contextType = _COMPONENT;
}
// ------ Abstract methods whose implementations are provided by subclass
/* Equivalent to methods in DirContext */
protected abstract Attributes c_getAttributes(Name name,
String[] attrIds,
Continuation cont)
throws NamingException;
protected abstract void c_modifyAttributes(Name name, int mod_op,
Attributes attrs,
Continuation cont)
throws NamingException;
protected abstract void c_modifyAttributes(Name name,
ModificationItem[] mods,
Continuation cont)
throws NamingException;
protected abstract void c_bind(Name name, Object obj,
Attributes attrs,
Continuation cont)
throws NamingException;
protected abstract void c_rebind(Name name, Object obj,
Attributes attrs,
Continuation cont)
throws NamingException;
protected abstract DirContext c_createSubcontext(Name name,
Attributes attrs,
Continuation cont)
throws NamingException;
protected abstract NamingEnumeration<SearchResult> c_search(
Name name,
Attributes matchingAttributes,
String[] attributesToReturn,
Continuation cont)
throws NamingException;
protected abstract NamingEnumeration<SearchResult> c_search(
Name name,
String filter,
SearchControls cons,
Continuation cont)
throws NamingException;
protected abstract NamingEnumeration<SearchResult> c_search(
Name name,
String filterExpr,
Object[] filterArgs,
SearchControls cons,
Continuation cont)
throws NamingException;
protected abstract DirContext c_getSchema(Name name, Continuation cont)
throws NamingException;
protected abstract DirContext c_getSchemaClassDefinition(Name name,
Continuation cont)
throws NamingException;
// ------- default implementations of c_*_nns methods from DirContext
// The following methods are called when the DirContext methods
// are invoked with a name that has a trailing slash.
// For naming systems that support implicit nns,
// the trailing slash signifies the implicit nns.
// For such naming systems, override these c_*_nns methods.
//
// For naming systems that support junctions (explicit nns),
// the trailing slash is meaningless because a junction does not
// have an implicit nns. The default implementation here
// throws a NameNotFoundException for such names.
// If a context wants to accept a trailing slash as having
// the same meaning as the same name without a trailing slash,
// then it should override these c_*_nns methods.
// See ComponentContext for details.
protected Attributes c_getAttributes_nns(Name name,
String[] attrIds,
Continuation cont)
throws NamingException {
c_processJunction_nns(name, cont);
return null;
}
protected void c_modifyAttributes_nns(Name name,
int mod_op,
Attributes attrs,
Continuation cont)
throws NamingException {
c_processJunction_nns(name, cont);
}
protected void c_modifyAttributes_nns(Name name,
ModificationItem[] mods,
Continuation cont)
throws NamingException {
c_processJunction_nns(name, cont);
}
protected void c_bind_nns(Name name,
Object obj,
Attributes attrs,
Continuation cont)
throws NamingException {
c_processJunction_nns(name, cont);
}
protected void c_rebind_nns(Name name,
Object obj,
Attributes attrs,
Continuation cont)
throws NamingException {
c_processJunction_nns(name, cont);
}
protected DirContext c_createSubcontext_nns(Name name,
Attributes attrs,
Continuation cont)
throws NamingException {
c_processJunction_nns(name, cont);
return null;
}
protected NamingEnumeration<SearchResult> c_search_nns(
Name name,
Attributes matchingAttributes,
String[] attributesToReturn,
Continuation cont)
throws NamingException {
c_processJunction_nns(name, cont);
return null;
}
protected NamingEnumeration<SearchResult> c_search_nns(
Name name,
String filter,
SearchControls cons,
Continuation cont)
throws NamingException {
c_processJunction_nns(name, cont);
return null;
}
protected NamingEnumeration<SearchResult> c_search_nns(
Name name,
String filterExpr,
Object[] filterArgs,
SearchControls cons,
Continuation cont)
throws NamingException {
c_processJunction_nns(name, cont);
return null;
}
protected DirContext c_getSchema_nns(Name name, Continuation cont)
throws NamingException {
c_processJunction_nns(name, cont);
return null;
}
protected DirContext c_getSchemaClassDefinition_nns(Name name, Continuation cont)
throws NamingException {
c_processJunction_nns(name, cont);
return null;
}
// ------- implementations of p_ DirContext methods using corresponding
// ------- DirContext c_ and c_*_nns methods
/* Implements for abstract methods declared in PartialCompositeDirContext */
protected Attributes p_getAttributes(Name name,
String[] attrIds,
Continuation cont)
throws NamingException {
HeadTail res = p_resolveIntermediate(name, cont);
Attributes answer = null;
switch (res.getStatus()) {
case TERMINAL_NNS_COMPONENT:
answer = c_getAttributes_nns(res.getHead(), attrIds, cont);
break;
case TERMINAL_COMPONENT:
answer = c_getAttributes(res.getHead(), attrIds, cont);
break;
default:
/* USE_CONTINUATION */
/* cont already set or exception thrown */
break;
}
return answer;
}
protected void p_modifyAttributes(Name name, int mod_op,
Attributes attrs,
Continuation cont)
throws NamingException {
HeadTail res = p_resolveIntermediate(name, cont);
switch (res.getStatus()) {
case TERMINAL_NNS_COMPONENT:
c_modifyAttributes_nns(res.getHead(), mod_op, attrs, cont);
break;
case TERMINAL_COMPONENT:
c_modifyAttributes(res.getHead(), mod_op, attrs, cont);
break;
default:
/* USE_CONTINUATION */
/* cont already set or exception thrown */
break;
}
}
protected void p_modifyAttributes(Name name,
ModificationItem[] mods,
Continuation cont)
throws NamingException {
HeadTail res = p_resolveIntermediate(name, cont);
switch (res.getStatus()) {
case TERMINAL_NNS_COMPONENT:
c_modifyAttributes_nns(res.getHead(), mods, cont);
break;
case TERMINAL_COMPONENT:
c_modifyAttributes(res.getHead(), mods, cont);
break;
default:
/* USE_CONTINUATION */
/* cont already set or exception thrown */
break;
}
}
protected void p_bind(Name name,
Object obj,
Attributes attrs,
Continuation cont)
throws NamingException {
HeadTail res = p_resolveIntermediate(name, cont);
switch (res.getStatus()) {
case TERMINAL_NNS_COMPONENT:
c_bind_nns(res.getHead(), obj, attrs, cont);
break;
case TERMINAL_COMPONENT:
c_bind(res.getHead(), obj, attrs, cont);
break;
default:
/* USE_CONTINUATION */
/* cont already set or exception thrown */
break;
}
}
protected void p_rebind(Name name, Object obj,
Attributes attrs, Continuation cont)
throws NamingException {
HeadTail res = p_resolveIntermediate(name, cont);
switch (res.getStatus()) {
case TERMINAL_NNS_COMPONENT:
c_rebind_nns(res.getHead(), obj, attrs, cont);
break;
case TERMINAL_COMPONENT:
c_rebind(res.getHead(), obj, attrs, cont);
break;
default:
/* USE_CONTINUATION */
/* cont already set or exception thrown */
break;
}
}
protected DirContext p_createSubcontext(Name name,
Attributes attrs,
Continuation cont)
throws NamingException {
HeadTail res = p_resolveIntermediate(name, cont);
DirContext answer = null;
switch (res.getStatus()) {
case TERMINAL_NNS_COMPONENT:
answer = c_createSubcontext_nns(res.getHead(), attrs, cont);
break;
case TERMINAL_COMPONENT:
answer = c_createSubcontext(res.getHead(), attrs, cont);
break;
default:
/* USE_CONTINUATION */
/* cont already set or exception thrown */
break;
}
return answer;
}
protected NamingEnumeration<SearchResult> p_search(
Name name,
Attributes matchingAttributes,
String[] attributesToReturn,
Continuation cont)
throws NamingException {
HeadTail res = p_resolveIntermediate(name, cont);
NamingEnumeration<SearchResult> answer = null;
switch (res.getStatus()) {
case TERMINAL_NNS_COMPONENT:
answer = c_search_nns(res.getHead(), matchingAttributes,
attributesToReturn, cont);
break;
case TERMINAL_COMPONENT:
answer = c_search(res.getHead(), matchingAttributes,
attributesToReturn, cont);
break;
default:
/* USE_CONTINUATION */
/* cont already set or exception thrown */
break;
}
return answer;
}
protected NamingEnumeration<SearchResult> p_search(Name name,
String filter,
SearchControls cons,
Continuation cont)
throws NamingException {
HeadTail res = p_resolveIntermediate(name, cont);
NamingEnumeration<SearchResult> answer = null;
switch (res.getStatus()) {
case TERMINAL_NNS_COMPONENT:
answer = c_search_nns(res.getHead(), filter, cons, cont);
break;
case TERMINAL_COMPONENT:
answer = c_search(res.getHead(), filter, cons, cont);
break;
default:
/* USE_CONTINUATION */
/* cont already set or exception thrown */
break;
}
return answer;
}
protected NamingEnumeration<SearchResult> p_search(Name name,
String filterExpr,
Object[] filterArgs,
SearchControls cons,
Continuation cont)
throws NamingException {
HeadTail res = p_resolveIntermediate(name, cont);
NamingEnumeration<SearchResult> answer = null;
switch (res.getStatus()) {
case TERMINAL_NNS_COMPONENT:
answer = c_search_nns(res.getHead(),
filterExpr, filterArgs, cons, cont);
break;
case TERMINAL_COMPONENT:
answer = c_search(res.getHead(), filterExpr, filterArgs, cons, cont);
break;
default:
/* USE_CONTINUATION */
/* cont already set or exception thrown */
break;
}
return answer;
}
protected DirContext p_getSchema(Name name, Continuation cont)
throws NamingException {
DirContext answer = null;
HeadTail res = p_resolveIntermediate(name, cont);
switch (res.getStatus()) {
case TERMINAL_NNS_COMPONENT:
answer = c_getSchema_nns(res.getHead(), cont);
break;
case TERMINAL_COMPONENT:
answer = c_getSchema(res.getHead(), cont);
break;
default:
/* USE_CONTINUATION */
/* cont already set or exception thrown */
break;
}
return answer;
}
protected DirContext p_getSchemaClassDefinition(Name name, Continuation cont)
throws NamingException {
DirContext answer = null;
HeadTail res = p_resolveIntermediate(name, cont);
switch (res.getStatus()) {
case TERMINAL_NNS_COMPONENT:
answer = c_getSchemaClassDefinition_nns(res.getHead(), cont);
break;
case TERMINAL_COMPONENT:
answer = c_getSchemaClassDefinition(res.getHead(), cont);
break;
default:
/* USE_CONTINUATION */
/* cont already set or exception thrown */
break;
}
return answer;
}
}

View File

@@ -0,0 +1,442 @@
/*
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.toolkit.ctx;
import javax.naming.*;
import javax.naming.spi.ResolveResult;
import java.util.Hashtable;
/**
* This class contains information required to continue
* the method (place where it left off, and remaining name to
* continue).
*
* @author Rosanna Lee
*/
public class Continuation extends ResolveResult {
/**
* The name that we started out with. It is initialized by the constructor
* and used to calculate to "resolved name" in NamingException in
* fillInException().
* %%% Note that this approach does not always do the calculation
* correctly with respect to absence or presence of the trailing slash
* for resolved name.
*/
protected Name starter;
/**
* Whether links were encountered.
*/
protected Object followingLink = null;
/**
* The environment used by the caller. Initialized by constructor and
* used when filling out a CannotProceedException.
*/
protected Hashtable<?,?> environment = null;
/**
* Indicates whether the Continuation instance indicates that the operation
* should be continued using the data in the Continuation.
* Typically, this is only false if an error has been encountered or if
* the operation has succeeded.
*/
protected boolean continuing = false;
/**
* The last resolved context. Used to set the "AltNameCtx" in a
* CannotProceedException.
*/
protected Context resolvedContext = null;
/**
* The resolved name relative to resolvedContext. Used to set the
* "AltName" in a CannotProceedException.
*/
protected Name relativeResolvedName = null;
/**
* Constructs a new instance of Continuation.
* Used as dummy for contexts that do not do federation (e.g. for schema ops)
*/
public Continuation() {
}
/**
* Constructs a new instance of Continuation.
* @param top The name of the object that is to be resolved/operated upon.
* This becomes the Continuation's 'starter' and is used to
* calculate the "resolved name" when filling in a NamingException.
* @param environment The environment used by the caller. It is used
* when setting the "environment" of a CannotProceedException.
*/
@SuppressWarnings("unchecked") // For Hashtable clone: environment.clone()
public Continuation(Name top, Hashtable<?,?> environment) {
super();
starter = top;
this.environment = (Hashtable<?,?>)
((environment == null) ? null : environment.clone());
}
/**
* Determines whether this Continuation contains data that should be
* used to continue the operation.
*
* @return true if operation should continue; false if operation has
* completed (successfully or unsuccessfully).
*/
public boolean isContinue() {
return continuing;
}
/**
* Sets this Continuation to indicate successful completion.
* Subsequent calls to isContinue() will return false.
* This method is different from the setError() methods only from
* the standpoint that this method does not set any of the other
* fields such as resolved object or resolved context. This is because
* this method is typically called when the context recognizes that
* the operation has successfully completed and that the continuation
* already contains the appropriately set fields.
* @see setError
* @see setErrorNNS
*/
public void setSuccess() {
continuing = false;
}
/**
* Fills in an exception's fields using data from this Continuation.
* The resolved name is set by subtracting remainingName from starter.
* %%% This might not not always produce the correct answer wrt trailing "/".
* If the exception is a CannotProceedException, its environment,
* altName, and altNameCtx fields are set using this continuation's
* environment, relativeResolvedName, and resolvedContext.
*
* @param e The non-null naming exception to fill.
* @return The non-null naming exception with its fields set using
* data from this Continuation.
*/
public NamingException fillInException(NamingException e) {
e.setRemainingName(remainingName);
e.setResolvedObj(resolvedObj);
if (starter == null || starter.isEmpty())
e.setResolvedName(null);
else if (remainingName == null)
e.setResolvedName(starter);
else
e.setResolvedName(
starter.getPrefix(starter.size() -
remainingName.size()));
if ((e instanceof CannotProceedException)) {
CannotProceedException cpe = (CannotProceedException)e;
Hashtable<?,?> env = (environment == null ?
new Hashtable<>(11) : (Hashtable<?,?>)environment.clone());
cpe.setEnvironment(env);
cpe.setAltNameCtx(resolvedContext);
cpe.setAltName(relativeResolvedName);
}
return e;
}
/**
* Sets this Continuation to indicated that an error has occurred,
* and that the remaining name is rename + "/".
*
* This method is typically called by _nns methods that have been
* given a name to process. It might process part of that name but
* encountered some error. Consequenetly, it would call setErrorNNS()
* with the remaining name. Since the _nns method was expected to
* operate upon the "nns" of the original name, the remaining name
* must include the "nns". That's why this method adds a trailing "/".
*<p>
* After this method is called, isContinuing() returns false.
*
* @param resObj The possibly null object that was resolved to.
* @param remain The non-null remaining name.
*/
public void setErrorNNS(Object resObj, Name remain) {
Name nm = (Name)(remain.clone());
try {
nm.add("");
} catch (InvalidNameException e) {
// ignore; can't happen for composite name
}
setErrorAux(resObj, nm);
}
/**
* Form that accepts a String name instead of a Name name.
* @param resObj The possibly null object that was resolved to.
* @param remain The possibly String remaining name.
*
* @see #setErrorNNS(java.lang.Object, javax.naming.Name)
*/
public void setErrorNNS(Object resObj, String remain) {
CompositeName rname = new CompositeName();
try {
if (remain != null && !remain.equals(""))
rname.add(remain);
rname.add("");
} catch (InvalidNameException e) {
// ignore, can't happen for composite name
}
setErrorAux(resObj, rname);
}
/**
* Sets this Continuation to indicated that an error has occurred
* and supply resolved information.
*
* This method is typically called by methods that have been
* given a name to process. It might process part of that name but
* encountered some error. Consequenetly, it would call setError()
* with the resolved object and the remaining name.
*<p>
* After this method is called, isContinuing() returns false.
*
* @param resObj The possibly null object that was resolved to.
* @param remain The possibly null remaining name.
*/
public void setError(Object resObj, Name remain) {
if (remain != null)
remainingName = (Name)(remain.clone());
else
remainingName = null;
setErrorAux(resObj, remainingName);
}
/**
* Form that accepts a String name instead of a Name name.
* @param resObj The possibly null object that was resolved to.
* @param remain The possibly String remaining name.
*
* @see #setError(java.lang.Object, javax.naming.Name)
*/
public void setError(Object resObj, String remain) {
CompositeName rname = new CompositeName();
if (remain != null && !remain.equals("")) {
try {
rname.add(remain);
} catch (InvalidNameException e) {
// ignore; can't happen for composite name
}
}
setErrorAux(resObj, rname);
}
private void setErrorAux(Object resObj, Name rname) {
remainingName = rname;
resolvedObj = resObj;
continuing = false;
}
private void setContinueAux(Object resObj,
Name relResName, Context currCtx, Name remain) {
if (resObj instanceof LinkRef) {
setContinueLink(resObj, relResName, currCtx, remain);
} else {
remainingName = remain;
resolvedObj = resObj;
relativeResolvedName = relResName;
resolvedContext = currCtx;
continuing = true;
}
}
/**
* Sets this Continuation with the supplied data, and set remaining name
* to be "/".
* This method is typically called by _nns methods that have been
* given a name to process. It might the name (without the nns) and
* continue process of the nns elsewhere.
* Consequently, it would call this form of the setContinueNNS().
* This method supplies "/" as the remaining name.
*<p>
* After this method is called, isContinuing() returns true.
*
* @param resObj The possibly null resolved object.
* @param relResName The non-null resolved name relative to currCtx.
* @param currCtx The non-null context from which relResName is to be resolved.
*/
public void setContinueNNS(Object resObj, Name relResName, Context currCtx) {
CompositeName rname = new CompositeName();
setContinue(resObj, relResName, currCtx, PartialCompositeContext._NNS_NAME);
}
/**
* Overloaded form that accesses String names.
*
* @param resObj The possibly null resolved object.
* @param relResName The non-null resolved name relative to currCtx.
* @param currCtx The non-null context from which relResName is to be resolved.
* @see #setContinueNNS(java.lang.Object, javax.naming.Name, javax.naming.Context)
*/
public void setContinueNNS(Object resObj, String relResName, Context currCtx) {
CompositeName relname = new CompositeName();
try {
relname.add(relResName);
} catch (NamingException e) {}
setContinue(resObj, relname, currCtx, PartialCompositeContext._NNS_NAME);
}
/**
* Sets this Continuation with the supplied data, and set remaining name
* to be the empty name.
* This method is typically called by list-style methods
* in which the target context implementing list() expects an
* empty name. For example when c_list() is given a non-empty name to
* process, it would resolve that name, and then call setContinue()
* with the resolved object so that the target context to be listed
* would be called with the empty name (i.e. list the target context itself).
*<p>
* After this method is called, isContinuing() returns true.
*
* @param resObj The possibly null resolved object.
* @param relResName The non-null resolved name relative to currCtx.
* @param currCtx The non-null context from which relResName is to be resolved.
*/
public void setContinue(Object obj, Name relResName, Context currCtx) {
setContinueAux(obj, relResName, currCtx,
(Name)PartialCompositeContext._EMPTY_NAME.clone());
}
/**
* Sets this Continuation with the supplied data.
* This method is typically called by a method that has been asked
* to operate on a name. The method resolves part of the name
* (relResName) to obj and sets the unprocessed part to rename.
* It calls setContinue() so that the operation can be continued
* using this data.
*<p>
* After this method is called, isContinuing() returns true.
*
* @param resObj The possibly null resolved object.
* @param relResName The non-null resolved name relative to currCtx.
* @param currCtx The non-null context from which relResName is to be resolved.
* @param remain The non-null remaining name.
*/
public void setContinue(Object obj, Name relResName, Context currCtx, Name remain) {
if (remain != null)
this.remainingName = (Name)(remain.clone());
else
this.remainingName = new CompositeName();
setContinueAux(obj, relResName, currCtx, remainingName);
}
/**
* String overload.
*
* @param resObj The possibly null resolved object.
* @param relResName The non-null resolved name relative to currCtx.
* @param currCtx The non-null context from which relResName is to be resolved.
* @param remain The non-null remaining name.
* @see #setContinue(java.lang.Object, java.lang.String, javax.naming.Context, java.lang.String)
*/
public void setContinue(Object obj, String relResName,
Context currCtx, String remain) {
CompositeName relname = new CompositeName();
if (!relResName.equals("")) {
try {
relname.add(relResName);
} catch (NamingException e){}
}
CompositeName rname = new CompositeName();
if (!remain.equals("")) {
try {
rname.add(remain);
} catch (NamingException e) {
}
}
setContinueAux(obj, relname, currCtx, rname);
}
/**
* %%% This method is kept only for backward compatibility. Delete when
* old implementations updated.
*
* Replaced by setContinue(obj, relResName, (Context)currCtx);
*
* @deprecated
*/
@Deprecated
public void setContinue(Object obj, Object currCtx) {
setContinue(obj, null, (Context)currCtx);
}
/**
* Sets this Continuation to process a linkRef.
* %%% Not working yet.
*/
private void setContinueLink(Object linkRef, Name relResName,
Context resolvedCtx, Name rname) {
this.followingLink = linkRef;
this.remainingName = rname;
this.resolvedObj = resolvedCtx;
this.relativeResolvedName = PartialCompositeContext._EMPTY_NAME;
this.resolvedContext = resolvedCtx;
this.continuing = true;
}
public String toString() {
if (remainingName != null)
return starter.toString() + "; remainingName: '" + remainingName + "'";
else
return starter.toString();
}
public String toString(boolean detail) {
if (!detail || this.resolvedObj == null)
return this.toString();
return this.toString() + "; resolvedObj: " + this.resolvedObj +
"; relativeResolvedName: " + relativeResolvedName +
"; resolvedContext: " + resolvedContext;
}
private static final long serialVersionUID = 8162530656132624308L;
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (c) 1999, 2004, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.toolkit.ctx;
import javax.naming.Name;
/**
* A class for returning the result of p_parseComponent();
*
* @author Rosanna Lee
*/
public class HeadTail {
private int status;
private Name head;
private Name tail;
public HeadTail(Name head, Name tail) {
this(head, tail, 0);
}
public HeadTail(Name head, Name tail, int status) {
this.status = status;
this.head = head;
this.tail = tail;
}
public void setStatus(int status) {
this.status = status;
}
public Name getHead() {
return this.head;
}
public Name getTail() {
return this.tail;
}
public int getStatus() {
return this.status;
}
}

View File

@@ -0,0 +1,523 @@
/*
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.toolkit.ctx;
import java.util.Hashtable;
import java.util.Enumeration;
import javax.naming.*;
import javax.naming.spi.Resolver;
import javax.naming.spi.ResolveResult;
import javax.naming.spi.NamingManager;
/**
* PartialCompositeContext implements Context operations on
* composite names using implementations of the p_ interfaces
* defined by its subclasses.
*
* The main purpose provided by this class is that it deals with
* partial resolutions and continuations, so that callers of the
* Context operation don't have to.
*
* Types of clients that will be direct subclasses of
* PartialCompositeContext may be service providers that implement
* one of the JNDI protocols, but which do not deal with
* continuations. Usually, service providers will be using
* one of the subclasses of PartialCompositeContext.
*
* @author Rosanna Lee
*/
public abstract class PartialCompositeContext implements Context, Resolver {
protected static final int _PARTIAL = 1;
protected static final int _COMPONENT = 2;
protected static final int _ATOMIC = 3;
protected int _contextType = _PARTIAL;
static final CompositeName _EMPTY_NAME = new CompositeName();
static CompositeName _NNS_NAME;
static {
try {
_NNS_NAME = new CompositeName("/");
} catch (InvalidNameException e) {
// Should never happen
}
}
protected PartialCompositeContext() {
}
// ------ Abstract methods whose implementations come from subclasses
/* Equivalent to method in Resolver interface */
protected abstract ResolveResult p_resolveToClass(Name name,
Class<?> contextType, Continuation cont) throws NamingException;
/* Equivalent to methods in Context interface */
protected abstract Object p_lookup(Name name, Continuation cont)
throws NamingException;
protected abstract Object p_lookupLink(Name name, Continuation cont)
throws NamingException;
protected abstract NamingEnumeration<NameClassPair> p_list(Name name,
Continuation cont) throws NamingException;
protected abstract NamingEnumeration<Binding> p_listBindings(Name name,
Continuation cont) throws NamingException;
protected abstract void p_bind(Name name, Object obj, Continuation cont)
throws NamingException;
protected abstract void p_rebind(Name name, Object obj, Continuation cont)
throws NamingException;
protected abstract void p_unbind(Name name, Continuation cont)
throws NamingException;
protected abstract void p_destroySubcontext(Name name, Continuation cont)
throws NamingException;
protected abstract Context p_createSubcontext(Name name, Continuation cont)
throws NamingException;
protected abstract void p_rename(Name oldname, Name newname,
Continuation cont)
throws NamingException;
protected abstract NameParser p_getNameParser(Name name, Continuation cont)
throws NamingException;
// ------ should be overridden by subclass;
// ------ not abstract only for backward compatibility
/**
* A cheap way of getting the environment.
* Default implementation is NOT cheap because it simply calls
* getEnvironment(), which most implementations clone before returning.
* Subclass should ALWAYS override this with the cheapest possible way.
* The toolkit knows to clone when necessary.
* @return The possibly null environment of the context.
*/
protected Hashtable<?,?> p_getEnvironment() throws NamingException {
return getEnvironment();
}
// ------ implementations of methods in Resolver and Context
// ------ using corresponding p_ methods provided by subclass
/* implementations for method in Resolver interface using p_ method */
public ResolveResult resolveToClass(String name,
Class<? extends Context> contextType)
throws NamingException
{
return resolveToClass(new CompositeName(name), contextType);
}
public ResolveResult resolveToClass(Name name,
Class<? extends Context> contextType)
throws NamingException
{
PartialCompositeContext ctx = this;
Hashtable<?,?> env = p_getEnvironment();
Continuation cont = new Continuation(name, env);
ResolveResult answer;
Name nm = name;
try {
answer = ctx.p_resolveToClass(nm, contextType, cont);
while (cont.isContinue()) {
nm = cont.getRemainingName();
ctx = getPCContext(cont);
answer = ctx.p_resolveToClass(nm, contextType, cont);
}
} catch (CannotProceedException e) {
Context cctx = NamingManager.getContinuationContext(e);
if (!(cctx instanceof Resolver)) {
throw e;
}
answer = ((Resolver)cctx).resolveToClass(e.getRemainingName(),
contextType);
}
return answer;
}
/* implementations for methods in Context interface using p_ methods */
public Object lookup(String name) throws NamingException {
return lookup(new CompositeName(name));
}
public Object lookup(Name name) throws NamingException {
PartialCompositeContext ctx = this;
Hashtable<?,?> env = p_getEnvironment();
Continuation cont = new Continuation(name, env);
Object answer;
Name nm = name;
try {
answer = ctx.p_lookup(nm, cont);
while (cont.isContinue()) {
nm = cont.getRemainingName();
ctx = getPCContext(cont);
answer = ctx.p_lookup(nm, cont);
}
} catch (CannotProceedException e) {
Context cctx = NamingManager.getContinuationContext(e);
answer = cctx.lookup(e.getRemainingName());
}
return answer;
}
public void bind(String name, Object newObj) throws NamingException {
bind(new CompositeName(name), newObj);
}
public void bind(Name name, Object newObj) throws NamingException {
PartialCompositeContext ctx = this;
Name nm = name;
Hashtable<?,?> env = p_getEnvironment();
Continuation cont = new Continuation(name, env);
try {
ctx.p_bind(nm, newObj, cont);
while (cont.isContinue()) {
nm = cont.getRemainingName();
ctx = getPCContext(cont);
ctx.p_bind(nm, newObj, cont);
}
} catch (CannotProceedException e) {
Context cctx = NamingManager.getContinuationContext(e);
cctx.bind(e.getRemainingName(), newObj);
}
}
public void rebind(String name, Object newObj) throws NamingException {
rebind(new CompositeName(name), newObj);
}
public void rebind(Name name, Object newObj) throws NamingException {
PartialCompositeContext ctx = this;
Name nm = name;
Hashtable<?,?> env = p_getEnvironment();
Continuation cont = new Continuation(name, env);
try {
ctx.p_rebind(nm, newObj, cont);
while (cont.isContinue()) {
nm = cont.getRemainingName();
ctx = getPCContext(cont);
ctx.p_rebind(nm, newObj, cont);
}
} catch (CannotProceedException e) {
Context cctx = NamingManager.getContinuationContext(e);
cctx.rebind(e.getRemainingName(), newObj);
}
}
public void unbind(String name) throws NamingException {
unbind(new CompositeName(name));
}
public void unbind(Name name) throws NamingException {
PartialCompositeContext ctx = this;
Name nm = name;
Hashtable<?,?> env = p_getEnvironment();
Continuation cont = new Continuation(name, env);
try {
ctx.p_unbind(nm, cont);
while (cont.isContinue()) {
nm = cont.getRemainingName();
ctx = getPCContext(cont);
ctx.p_unbind(nm, cont);
}
} catch (CannotProceedException e) {
Context cctx = NamingManager.getContinuationContext(e);
cctx.unbind(e.getRemainingName());
}
}
public void rename(String oldName, String newName) throws NamingException {
rename(new CompositeName(oldName), new CompositeName(newName));
}
public void rename(Name oldName, Name newName)
throws NamingException
{
PartialCompositeContext ctx = this;
Name nm = oldName;
Hashtable<?,?> env = p_getEnvironment();
Continuation cont = new Continuation(oldName, env);
try {
ctx.p_rename(nm, newName, cont);
while (cont.isContinue()) {
nm = cont.getRemainingName();
ctx = getPCContext(cont);
ctx.p_rename(nm, newName, cont);
}
} catch (CannotProceedException e) {
Context cctx = NamingManager.getContinuationContext(e);
if (e.getRemainingNewName() != null) {
// %%% e.getRemainingNewName() should never be null
newName = e.getRemainingNewName();
}
cctx.rename(e.getRemainingName(), newName);
}
}
public NamingEnumeration<NameClassPair> list(String name)
throws NamingException
{
return list(new CompositeName(name));
}
public NamingEnumeration<NameClassPair> list(Name name)
throws NamingException
{
PartialCompositeContext ctx = this;
Name nm = name;
NamingEnumeration<NameClassPair> answer;
Hashtable<?,?> env = p_getEnvironment();
Continuation cont = new Continuation(name, env);
try {
answer = ctx.p_list(nm, cont);
while (cont.isContinue()) {
nm = cont.getRemainingName();
ctx = getPCContext(cont);
answer = ctx.p_list(nm, cont);
}
} catch (CannotProceedException e) {
Context cctx = NamingManager.getContinuationContext(e);
answer = cctx.list(e.getRemainingName());
}
return answer;
}
public NamingEnumeration<Binding> listBindings(String name)
throws NamingException
{
return listBindings(new CompositeName(name));
}
public NamingEnumeration<Binding> listBindings(Name name)
throws NamingException
{
PartialCompositeContext ctx = this;
Name nm = name;
NamingEnumeration<Binding> answer;
Hashtable<?,?> env = p_getEnvironment();
Continuation cont = new Continuation(name, env);
try {
answer = ctx.p_listBindings(nm, cont);
while (cont.isContinue()) {
nm = cont.getRemainingName();
ctx = getPCContext(cont);
answer = ctx.p_listBindings(nm, cont);
}
} catch (CannotProceedException e) {
Context cctx = NamingManager.getContinuationContext(e);
answer = cctx.listBindings(e.getRemainingName());
}
return answer;
}
public void destroySubcontext(String name) throws NamingException {
destroySubcontext(new CompositeName(name));
}
public void destroySubcontext(Name name) throws NamingException {
PartialCompositeContext ctx = this;
Name nm = name;
Hashtable<?,?> env = p_getEnvironment();
Continuation cont = new Continuation(name, env);
try {
ctx.p_destroySubcontext(nm, cont);
while (cont.isContinue()) {
nm = cont.getRemainingName();
ctx = getPCContext(cont);
ctx.p_destroySubcontext(nm, cont);
}
} catch (CannotProceedException e) {
Context cctx = NamingManager.getContinuationContext(e);
cctx.destroySubcontext(e.getRemainingName());
}
}
public Context createSubcontext(String name) throws NamingException {
return createSubcontext(new CompositeName(name));
}
public Context createSubcontext(Name name) throws NamingException {
PartialCompositeContext ctx = this;
Name nm = name;
Context answer;
Hashtable<?,?> env = p_getEnvironment();
Continuation cont = new Continuation(name, env);
try {
answer = ctx.p_createSubcontext(nm, cont);
while (cont.isContinue()) {
nm = cont.getRemainingName();
ctx = getPCContext(cont);
answer = ctx.p_createSubcontext(nm, cont);
}
} catch (CannotProceedException e) {
Context cctx = NamingManager.getContinuationContext(e);
answer = cctx.createSubcontext(e.getRemainingName());
}
return answer;
}
public Object lookupLink(String name) throws NamingException {
return lookupLink(new CompositeName(name));
}
public Object lookupLink(Name name) throws NamingException {
PartialCompositeContext ctx = this;
Hashtable<?,?> env = p_getEnvironment();
Continuation cont = new Continuation(name, env);
Object answer;
Name nm = name;
try {
answer = ctx.p_lookupLink(nm, cont);
while (cont.isContinue()) {
nm = cont.getRemainingName();
ctx = getPCContext(cont);
answer = ctx.p_lookupLink(nm, cont);
}
} catch (CannotProceedException e) {
Context cctx = NamingManager.getContinuationContext(e);
answer = cctx.lookupLink(e.getRemainingName());
}
return answer;
}
public NameParser getNameParser(String name) throws NamingException {
return getNameParser(new CompositeName(name));
}
public NameParser getNameParser(Name name) throws NamingException {
PartialCompositeContext ctx = this;
Name nm = name;
NameParser answer;
Hashtable<?,?> env = p_getEnvironment();
Continuation cont = new Continuation(name, env);
try {
answer = ctx.p_getNameParser(nm, cont);
while (cont.isContinue()) {
nm = cont.getRemainingName();
ctx = getPCContext(cont);
answer = ctx.p_getNameParser(nm, cont);
}
} catch (CannotProceedException e) {
Context cctx = NamingManager.getContinuationContext(e);
answer = cctx.getNameParser(e.getRemainingName());
}
return answer;
}
public String composeName(String name, String prefix)
throws NamingException {
Name fullName = composeName(new CompositeName(name),
new CompositeName(prefix));
return fullName.toString();
}
/**
* This default implementation simply concatenates the two names.
* There's one twist when the "java.naming.provider.compose.elideEmpty"
* environment setting is set to "true": if each name contains a
* nonempty component, and if 'prefix' ends with an empty component or
* 'name' starts with one, then one empty component is dropped.
* For example:
* <pre>
* elideEmpty=false elideEmpty=true
* {"a"} + {"b"} => {"a", "b"} {"a", "b"}
* {"a"} + {""} => {"a", ""} {"a", ""}
* {"a"} + {"", "b"} => {"a", "", "b"} {"a", "b"}
* {"a", ""} + {"b", ""} => {"a", "", "b", ""} {"a", "b", ""}
* {"a", ""} + {"", "b"} => {"a", "", "", "b"} {"a", "", "b"}
* </pre>
*/
public Name composeName(Name name, Name prefix) throws NamingException {
Name res = (Name)prefix.clone();
if (name == null) {
return res;
}
res.addAll(name);
String elide = (String)
p_getEnvironment().get("java.naming.provider.compose.elideEmpty");
if (elide == null || !elide.equalsIgnoreCase("true")) {
return res;
}
int len = prefix.size();
if (!allEmpty(prefix) && !allEmpty(name)) {
if (res.get(len - 1).equals("")) {
res.remove(len - 1);
} else if (res.get(len).equals("")) {
res.remove(len);
}
}
return res;
}
// ------ internal methods used by PartialCompositeContext
/**
* Tests whether a name contains a nonempty component.
*/
protected static boolean allEmpty(Name name) {
Enumeration<String> enum_ = name.getAll();
while (enum_.hasMoreElements()) {
if (!enum_.nextElement().isEmpty()) {
return false;
}
}
return true;
}
/**
* Retrieves a PartialCompositeContext for the resolved object in
* cont. Throws CannotProceedException if not successful.
*/
protected static PartialCompositeContext getPCContext(Continuation cont)
throws NamingException {
Object obj = cont.getResolvedObj();
PartialCompositeContext pctx = null;
if (obj instanceof PartialCompositeContext) {
// Just cast if octx already is PartialCompositeContext
// %%% ignoring environment for now
return (PartialCompositeContext)obj;
} else {
throw cont.fillInException(new CannotProceedException());
}
}
};

View File

@@ -0,0 +1,576 @@
/*
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.toolkit.ctx;
import java.util.Hashtable;
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.spi.DirectoryManager;
/*
* Inherit from AtomicContext so that subclasses of PartialCompositeDirContext
* can get the ns methods defined in subclasses of PartialCompositeContext.
*
* Direct subclasses of DirContext should provide implementations for
* the p_ abstract DirContext methods and override the p_ Context methods
* (not abstract anymore because they are overridden by ComponentContext
* (the superclass of AtomicContext)).
*
* @author Rosanna Lee
*/
public abstract class PartialCompositeDirContext
extends AtomicContext implements DirContext {
protected PartialCompositeDirContext() {
_contextType = _PARTIAL;
}
// ------ Abstract methods whose implementation come from subclasses
/* Equivalent to DirContext methods */
protected abstract Attributes p_getAttributes(Name name, String[] attrIds,
Continuation cont)
throws NamingException;
protected abstract void p_modifyAttributes(Name name, int mod_op,
Attributes attrs,
Continuation cont)
throws NamingException;
protected abstract void p_modifyAttributes(Name name,
ModificationItem[] mods,
Continuation cont)
throws NamingException;
protected abstract void p_bind(Name name, Object obj,
Attributes attrs,
Continuation cont)
throws NamingException;
protected abstract void p_rebind(Name name, Object obj,
Attributes attrs,
Continuation cont)
throws NamingException;
protected abstract DirContext p_createSubcontext(Name name,
Attributes attrs,
Continuation cont)
throws NamingException;
protected abstract NamingEnumeration<SearchResult> p_search(
Name name,
Attributes matchingAttributes,
String[] attributesToReturn,
Continuation cont)
throws NamingException;
protected abstract NamingEnumeration<SearchResult> p_search(
Name name,
String filter,
SearchControls cons,
Continuation cont)
throws NamingException;
protected abstract NamingEnumeration<SearchResult> p_search(
Name name,
String filterExpr,
Object[] filterArgs,
SearchControls cons,
Continuation cont)
throws NamingException;
protected abstract DirContext p_getSchema(Name name, Continuation cont)
throws NamingException;
protected abstract DirContext p_getSchemaClassDefinition(Name name,
Continuation cont)
throws NamingException;
// ------ implementation for DirContext methods using
// ------ corresponding p_ methods
public Attributes getAttributes(String name)
throws NamingException {
return getAttributes(name, null);
}
public Attributes getAttributes(Name name)
throws NamingException {
return getAttributes(name, null);
}
public Attributes getAttributes(String name, String[] attrIds)
throws NamingException {
return getAttributes(new CompositeName(name), attrIds);
}
public Attributes getAttributes(Name name, String[] attrIds)
throws NamingException {
PartialCompositeDirContext ctx = this;
Hashtable<?,?> env = p_getEnvironment();
Continuation cont = new Continuation(name, env);
Attributes answer;
Name nm = name;
try {
answer = ctx.p_getAttributes(nm, attrIds, cont);
while (cont.isContinue()) {
nm = cont.getRemainingName();
ctx = getPCDirContext(cont);
answer = ctx.p_getAttributes(nm, attrIds, cont);
}
} catch (CannotProceedException e) {
DirContext cctx = DirectoryManager.getContinuationDirContext(e);
answer = cctx.getAttributes(e.getRemainingName(), attrIds);
}
return answer;
}
public void modifyAttributes(String name, int mod_op, Attributes attrs)
throws NamingException {
modifyAttributes(new CompositeName(name), mod_op, attrs);
}
public void modifyAttributes(Name name, int mod_op, Attributes attrs)
throws NamingException {
PartialCompositeDirContext ctx = this;
Hashtable<?,?> env = p_getEnvironment();
Continuation cont = new Continuation(name, env);
Name nm = name;
try {
ctx.p_modifyAttributes(nm, mod_op, attrs, cont);
while (cont.isContinue()) {
nm = cont.getRemainingName();
ctx = getPCDirContext(cont);
ctx.p_modifyAttributes(nm, mod_op, attrs, cont);
}
} catch (CannotProceedException e) {
DirContext cctx = DirectoryManager.getContinuationDirContext(e);
cctx.modifyAttributes(e.getRemainingName(), mod_op, attrs);
}
}
public void modifyAttributes(String name, ModificationItem[] mods)
throws NamingException {
modifyAttributes(new CompositeName(name), mods);
}
public void modifyAttributes(Name name, ModificationItem[] mods)
throws NamingException {
PartialCompositeDirContext ctx = this;
Hashtable<?,?> env = p_getEnvironment();
Continuation cont = new Continuation(name, env);
Name nm = name;
try {
ctx.p_modifyAttributes(nm, mods, cont);
while (cont.isContinue()) {
nm = cont.getRemainingName();
ctx = getPCDirContext(cont);
ctx.p_modifyAttributes(nm, mods, cont);
}
} catch (CannotProceedException e) {
DirContext cctx = DirectoryManager.getContinuationDirContext(e);
cctx.modifyAttributes(e.getRemainingName(), mods);
}
}
public void bind(String name, Object obj, Attributes attrs)
throws NamingException {
bind(new CompositeName(name), obj, attrs);
}
public void bind(Name name, Object obj, Attributes attrs)
throws NamingException {
PartialCompositeDirContext ctx = this;
Hashtable<?,?> env = p_getEnvironment();
Continuation cont = new Continuation(name, env);
Name nm = name;
try {
ctx.p_bind(nm, obj, attrs, cont);
while (cont.isContinue()) {
nm = cont.getRemainingName();
ctx = getPCDirContext(cont);
ctx.p_bind(nm, obj, attrs, cont);
}
} catch (CannotProceedException e) {
DirContext cctx = DirectoryManager.getContinuationDirContext(e);
cctx.bind(e.getRemainingName(), obj, attrs);
}
}
public void rebind(String name, Object obj, Attributes attrs)
throws NamingException {
rebind(new CompositeName(name), obj, attrs);
}
public void rebind(Name name, Object obj, Attributes attrs)
throws NamingException {
PartialCompositeDirContext ctx = this;
Hashtable<?,?> env = p_getEnvironment();
Continuation cont = new Continuation(name, env);
Name nm = name;
try {
ctx.p_rebind(nm, obj, attrs, cont);
while (cont.isContinue()) {
nm = cont.getRemainingName();
ctx = getPCDirContext(cont);
ctx.p_rebind(nm, obj, attrs, cont);
}
} catch (CannotProceedException e) {
DirContext cctx = DirectoryManager.getContinuationDirContext(e);
cctx.rebind(e.getRemainingName(), obj, attrs);
}
}
public DirContext createSubcontext(String name, Attributes attrs)
throws NamingException {
return createSubcontext(new CompositeName(name), attrs);
}
public DirContext createSubcontext(Name name, Attributes attrs)
throws NamingException {
PartialCompositeDirContext ctx = this;
Hashtable<?,?> env = p_getEnvironment();
Continuation cont = new Continuation(name, env);
DirContext answer;
Name nm = name;
try {
answer = ctx.p_createSubcontext(nm, attrs, cont);
while (cont.isContinue()) {
nm = cont.getRemainingName();
ctx = getPCDirContext(cont);
answer = ctx.p_createSubcontext(nm, attrs, cont);
}
} catch (CannotProceedException e) {
DirContext cctx = DirectoryManager.getContinuationDirContext(e);
answer = cctx.createSubcontext(e.getRemainingName(), attrs);
}
return answer;
}
public NamingEnumeration<SearchResult>
search(String name, Attributes matchingAttributes)
throws NamingException
{
return search(name, matchingAttributes, null);
}
public NamingEnumeration<SearchResult>
search(Name name, Attributes matchingAttributes)
throws NamingException
{
return search(name, matchingAttributes, null);
}
public NamingEnumeration<SearchResult>
search(String name,
Attributes matchingAttributes,
String[] attributesToReturn)
throws NamingException
{
return search(new CompositeName(name),
matchingAttributes, attributesToReturn);
}
public NamingEnumeration<SearchResult>
search(Name name,
Attributes matchingAttributes,
String[] attributesToReturn)
throws NamingException
{
PartialCompositeDirContext ctx = this;
Hashtable<?,?> env = p_getEnvironment();
Continuation cont = new Continuation(name, env);
NamingEnumeration<SearchResult> answer;
Name nm = name;
try {
answer = ctx.p_search(nm, matchingAttributes,
attributesToReturn, cont);
while (cont.isContinue()) {
nm = cont.getRemainingName();
ctx = getPCDirContext(cont);
answer = ctx.p_search(nm, matchingAttributes,
attributesToReturn, cont);
}
} catch (CannotProceedException e) {
DirContext cctx = DirectoryManager.getContinuationDirContext(e);
answer = cctx.search(e.getRemainingName(), matchingAttributes,
attributesToReturn);
}
return answer;
}
public NamingEnumeration<SearchResult>
search(String name,
String filter,
SearchControls cons)
throws NamingException
{
return search(new CompositeName(name), filter, cons);
}
public NamingEnumeration<SearchResult>
search(Name name,
String filter,
SearchControls cons)
throws NamingException
{
PartialCompositeDirContext ctx = this;
Hashtable<?,?> env = p_getEnvironment();
Continuation cont = new Continuation(name, env);
NamingEnumeration<SearchResult> answer;
Name nm = name;
try {
answer = ctx.p_search(nm, filter, cons, cont);
while (cont.isContinue()) {
nm = cont.getRemainingName();
ctx = getPCDirContext(cont);
answer = ctx.p_search(nm, filter, cons, cont);
}
} catch (CannotProceedException e) {
DirContext cctx = DirectoryManager.getContinuationDirContext(e);
answer = cctx.search(e.getRemainingName(), filter, cons);
}
return answer;
}
public NamingEnumeration<SearchResult>
search(String name,
String filterExpr,
Object[] filterArgs,
SearchControls cons)
throws NamingException
{
return search(new CompositeName(name), filterExpr, filterArgs, cons);
}
public NamingEnumeration<SearchResult>
search(Name name,
String filterExpr,
Object[] filterArgs,
SearchControls cons)
throws NamingException
{
PartialCompositeDirContext ctx = this;
Hashtable<?,?> env = p_getEnvironment();
Continuation cont = new Continuation(name, env);
NamingEnumeration<SearchResult> answer;
Name nm = name;
try {
answer = ctx.p_search(nm, filterExpr, filterArgs, cons, cont);
while (cont.isContinue()) {
nm = cont.getRemainingName();
ctx = getPCDirContext(cont);
answer = ctx.p_search(nm, filterExpr, filterArgs, cons, cont);
}
} catch (CannotProceedException e) {
DirContext cctx = DirectoryManager.getContinuationDirContext(e);
answer = cctx.search(e.getRemainingName(), filterExpr, filterArgs,
cons);
}
return answer;
}
public DirContext getSchema(String name) throws NamingException {
return getSchema(new CompositeName(name));
}
public DirContext getSchema(Name name) throws NamingException {
PartialCompositeDirContext ctx = this;
Hashtable<?,?> env = p_getEnvironment();
Continuation cont = new Continuation(name, env);
DirContext answer;
Name nm = name;
try {
answer = ctx.p_getSchema(nm, cont);
while (cont.isContinue()) {
nm = cont.getRemainingName();
ctx = getPCDirContext(cont);
answer = ctx.p_getSchema(nm, cont);
}
} catch (CannotProceedException e) {
DirContext cctx = DirectoryManager.getContinuationDirContext(e);
answer = cctx.getSchema(e.getRemainingName());
}
return answer;
}
public DirContext getSchemaClassDefinition(String name)
throws NamingException {
return getSchemaClassDefinition(new CompositeName(name));
}
public DirContext getSchemaClassDefinition(Name name)
throws NamingException {
PartialCompositeDirContext ctx = this;
Hashtable<?,?> env = p_getEnvironment();
Continuation cont = new Continuation(name, env);
DirContext answer;
Name nm = name;
try {
answer = ctx.p_getSchemaClassDefinition(nm, cont);
while (cont.isContinue()) {
nm = cont.getRemainingName();
ctx = getPCDirContext(cont);
answer = ctx.p_getSchemaClassDefinition(nm, cont);
}
} catch (CannotProceedException e) {
DirContext cctx = DirectoryManager.getContinuationDirContext(e);
answer = cctx.getSchemaClassDefinition(e.getRemainingName());
}
return answer;
}
// ------ internal method used by PartialCompositeDirContext
/**
* Retrieves a PartialCompositeDirContext for the resolved object in
* cont. Throws CannotProceedException if not successful.
*/
protected static PartialCompositeDirContext getPCDirContext(Continuation cont)
throws NamingException {
PartialCompositeContext pctx =
PartialCompositeContext.getPCContext(cont);
if (!(pctx instanceof PartialCompositeDirContext)) {
throw cont.fillInException(
new NotContextException(
"Resolved object is not a DirContext."));
}
return (PartialCompositeDirContext)pctx;
}
//------ Compensation for inheriting from AtomicContext
/*
* Dummy implementations defined here so that direct subclasses
* of PartialCompositeDirContext or ComponentDirContext do not
* have to provide dummy implementations for these.
* Override these for subclasses of AtomicDirContext.
*/
protected StringHeadTail c_parseComponent(String inputName,
Continuation cont) throws NamingException {
OperationNotSupportedException e = new
OperationNotSupportedException();
throw cont.fillInException(e);
}
protected Object a_lookup(String name, Continuation cont)
throws NamingException {
OperationNotSupportedException e = new
OperationNotSupportedException();
throw cont.fillInException(e);
}
protected Object a_lookupLink(String name, Continuation cont)
throws NamingException {
OperationNotSupportedException e = new
OperationNotSupportedException();
throw cont.fillInException(e);
}
protected NamingEnumeration<NameClassPair> a_list(
Continuation cont) throws NamingException {
OperationNotSupportedException e = new
OperationNotSupportedException();
throw cont.fillInException(e);
}
protected NamingEnumeration<Binding> a_listBindings(
Continuation cont) throws NamingException {
OperationNotSupportedException e = new
OperationNotSupportedException();
throw cont.fillInException(e);
}
protected void a_bind(String name, Object obj, Continuation cont)
throws NamingException {
OperationNotSupportedException e = new
OperationNotSupportedException();
throw cont.fillInException(e);
}
protected void a_rebind(String name, Object obj, Continuation cont)
throws NamingException {
OperationNotSupportedException e = new
OperationNotSupportedException();
throw cont.fillInException(e);
}
protected void a_unbind(String name, Continuation cont)
throws NamingException {
OperationNotSupportedException e = new
OperationNotSupportedException();
throw cont.fillInException(e);
}
protected void a_destroySubcontext(String name, Continuation cont)
throws NamingException {
OperationNotSupportedException e = new
OperationNotSupportedException();
throw cont.fillInException(e);
}
protected Context a_createSubcontext(String name, Continuation cont)
throws NamingException {
OperationNotSupportedException e = new
OperationNotSupportedException();
throw cont.fillInException(e);
}
protected void a_rename(String oldname, Name newname,
Continuation cont) throws NamingException {
OperationNotSupportedException e = new
OperationNotSupportedException();
throw cont.fillInException(e);
}
protected NameParser a_getNameParser(Continuation cont)
throws NamingException {
OperationNotSupportedException e = new
OperationNotSupportedException();
throw cont.fillInException(e);
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.toolkit.ctx;
/**
* A class for returning the result of c_parseComponent().
*
* @author Rosanna Lee
*/
public class StringHeadTail {
private int status;
private String head;
private String tail;
public StringHeadTail(String head, String tail) {
this(head, tail, 0);
}
public StringHeadTail(String head, String tail, int status) {
this.status = status;
this.head = head;
this.tail = tail;
}
public void setStatus(int status) {
this.status = status;
}
public String getHead() {
return this.head;
}
public String getTail() {
return this.tail;
}
public int getStatus() {
return this.status;
}
}

Some files were not shown because too many files have changed in this diff Show More