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,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;
}
}