diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPAsynchronousConnection.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPAsynchronousConnection.java index a28bb923ba6..abad1d77dc4 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPAsynchronousConnection.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPAsynchronousConnection.java @@ -168,9 +168,9 @@ public interface LDAPAsynchronousConnection { /** - * Makes a single change to an existing entry in the directory (for - * example, changes the value of an attribute, adds a new attribute - * value, or removes an existing attribute value).
+ * Makes a single change to an existing entry in the directory. + * For example, changes the value of an attribute, adds a new attribute + * value, or removes an existing attribute value.
* The LDAPModification object specifies both the change to be made and * the LDAPAttribute value to be changed. * @@ -190,9 +190,9 @@ public interface LDAPAsynchronousConnection { throws LDAPException; /** - * Makes a single change to an existing entry in the directory (for - * example, changes the value of an attribute, adds a new attribute - * value, or removes an existing attribute value).
+ * Makes a single change to an existing entry in the directory. + * For example, changes the value of an attribute, adds a new attribute + * value, or removes an existing attribute value.
* The LDAPModification object specifies both the change to be made and * the LDAPAttribute value to be changed. * @@ -215,9 +215,9 @@ public interface LDAPAsynchronousConnection { throws LDAPException; /** - * Makes a set of changes to an existing entry in the directory (for - * example, changes attribute values, adds new attribute values, or - * removes existing attribute values). + * Makes a set of changes to an existing entry in the directory. + * For example, changes attribute values, adds new attribute values, or + * removes existing attribute values. *

* @param dn distinguished name of the entry to modify * @param mods a set of modifications to make to the entry @@ -235,9 +235,9 @@ public interface LDAPAsynchronousConnection { throws LDAPException; /** - * Makes a set of changes to an existing entry in the directory (for - * example, changes attribute values, adds new attribute values, or - * removes existing attribute values). + * Makes a set of changes to an existing entry in the directory. + * For example, changes attribute values, adds new attribute values, or + * removes existing attribute values. * * @param dn distinguished name of the entry to modify * @param mods a set of modifications to make to the entry diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPConnSetupMgr.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPConnSetupMgr.java index 3cad0bb677b..fbebb588d39 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPConnSetupMgr.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPConnSetupMgr.java @@ -38,12 +38,12 @@ import java.net.*; * When a connection is successfully created, a socket is opened. The socket * is passed to the LDAPConnThread. The LDAPConnThread must call * invalidateConnection() if the connection is lost due to a network or - * server error, or disconnect() if the connection is deliberately terminated + * server error, or closeConnection() if the connection is deliberately terminated * by the user. */ -class LDAPConnSetupMgr implements Cloneable, java.io.Serializable { +class LDAPConnSetupMgr implements java.io.Serializable { - static final long serialVersionUID = 1519402748245755306L; + static final long serialVersionUID = 1519402748245755307L; /** * Policy for opening a connection when multiple servers are used */ @@ -65,28 +65,32 @@ class LDAPConnSetupMgr implements Cloneable, java.io.Serializable { * Representation for a server in the server list. */ class ServerEntry { - String host; - int port; + LDAPUrl url; int connSetupStatus; Thread connSetupThread; - ServerEntry(String host, int port, int status) { - this.host = host; - this.port = port; + ServerEntry(LDAPUrl url, int status) { + this.url = url; connSetupStatus = status; connSetupThread = null; - } - + } public String toString() { - return "{" +host+":"+port + " status="+connSetupStatus+"}"; + return "{" + url + " status="+connSetupStatus+"}"; } } + /** * Socket to the connected server */ private Socket m_socket = null; + /** + * Original, underlying socket to the server, see layerSocket() + */ + private Socket m_origSocket = null; + + /** * Last exception occured during connection setup */ @@ -128,25 +132,50 @@ class LDAPConnSetupMgr implements Cloneable, java.io.Serializable { */ private transient int m_attemptCnt = 0; - /** - * Connection IDs for ldap trace messages - */ - private static int m_nextId; - private int m_id; - /** * Constructor * @param host list of host names to which to connect * @param port list of port numbers corresponding to the host list * @param factory socket factory for SSL connections */ - LDAPConnSetupMgr(String[] hosts, int[] ports, LDAPSocketFactory factory) { + LDAPConnSetupMgr(String[] hosts, int[] ports, LDAPSocketFactory factory) throws LDAPException{ m_dsList = new ServerEntry[hosts.length]; + boolean secure = (factory != null); for (int i=0; i < hosts.length; i++) { - m_dsList[i] = new ServerEntry(hosts[i], ports[i], NEVER_USED); + String url = secure ? "ldaps://" : "ldap://"; + url += hosts[i] + ":" + ports[i]; + try { + m_dsList[i] = new ServerEntry(new LDAPUrl(url), NEVER_USED); + } + catch (MalformedURLException ex) { + throw new LDAPException("Invalid host:port " + hosts[i]+":"+ports[i], + LDAPException.PARAM_ERROR); + } + } + m_factory = factory; + } + + LDAPConnSetupMgr(String[] urls, LDAPSocketFactory factory) throws LDAPException{ + m_dsList = new ServerEntry[urls.length]; + for (int i=0; i < urls.length; i++) { + try { + LDAPUrl url = new LDAPUrl(urls[i]); + m_dsList[i] = new ServerEntry(url, NEVER_USED); + } + catch (MalformedURLException ex) { + throw new LDAPException("Malformed LDAP URL " + urls[i], + LDAPException.PARAM_ERROR); + } + } + m_factory = factory; + } + + LDAPConnSetupMgr(LDAPUrl[] urls, LDAPSocketFactory factory) throws LDAPException{ + m_dsList = new ServerEntry[urls.length]; + for (int i=0; i < urls.length; i++) { + m_dsList[i] = new ServerEntry(urls[i], NEVER_USED); } m_factory = factory; - m_id = m_nextId++; } /** @@ -204,7 +233,7 @@ class LDAPConnSetupMgr implements Cloneable, java.io.Serializable { th.interrupt(); cleanup(); throw new LDAPException( - "connect timeout, " + getServerList() + " might be unreachable", + "Connect timeout, " + getServerList() + " might be unreachable", LDAPException.CONNECT_ERROR); } @@ -213,12 +242,13 @@ class LDAPConnSetupMgr implements Cloneable, java.io.Serializable { } throw new LDAPException( - "failed to connect to server " + getServerList(), + "Failed to connect to server " + getServerList(), LDAPException.CONNECT_ERROR); } private void reset() { m_socket = null; + m_origSocket = null; m_connException = null; m_attemptCnt = 0; @@ -231,9 +261,9 @@ class LDAPConnSetupMgr implements Cloneable, java.io.Serializable { StringBuffer sb = new StringBuffer(); for (int i=0; i < m_dsList.length; i++) { sb.append(i==0 ? "" : " "); - sb.append(m_dsList[i].host); + sb.append(m_dsList[i].url.getHost()); sb.append(":"); - sb.append(m_dsList[i].port); + sb.append(m_dsList[i].url.getPort()); } return sb.toString(); } @@ -268,9 +298,25 @@ class LDAPConnSetupMgr implements Cloneable, java.io.Serializable { newDsList[j] = m_dsList[m_dsIdx]; m_dsList = newDsList; m_dsIdx = j; + + try { + m_socket.close(); + } catch (Exception e) { + } finally { + m_socket = null; + } + } - - m_socket = null; + + if (m_origSocket != null) { + + try { + m_origSocket.close(); + } catch (Exception e) { + } finally { + m_origSocket = null; + } + } } /** @@ -278,30 +324,69 @@ class LDAPConnSetupMgr implements Cloneable, java.io.Serializable { * Mark the connected server status as DISCONNECTED. This will * put it at top of the server list for the next connect attempt. */ - void disconnect() { + void closeConnection() { if (m_socket != null) { + m_dsList[m_dsIdx].connSetupStatus = DISCONNECTED; + + try { + m_socket.close(); + } catch (Exception e) { + } finally { + m_socket = null; + } + } + + if (m_origSocket != null) { + + try { + m_origSocket.close(); + } catch (Exception e) { + } finally { + m_origSocket = null; + } } - - m_socket = null; } Socket getSocket() { return m_socket; } + + /** + * Layer a new socket over the existing one (used by startTLS) + */ + void layerSocket(LDAPTLSSocketFactory factory) throws LDAPException{ + Socket s = factory.makeSocket(m_socket); + m_origSocket = m_socket; + m_socket = s; + } String getHost() { if (m_dsIdx >= 0) { - return m_dsList[m_dsIdx].host; + return m_dsList[m_dsIdx].url.getHost(); } - return m_dsList[0].host; + return m_dsList[0].url.getHost(); } int getPort() { if (m_dsIdx >= 0) { - return m_dsList[m_dsIdx].port; + return m_dsList[m_dsIdx].url.getPort(); } - return m_dsList[0].port; + return m_dsList[0].url.getPort(); + } + + boolean isSecure() { + if (m_dsIdx >= 0) { + return m_dsList[m_dsIdx].url.isSecure(); + } + return m_dsList[0].url.isSecure(); + } + + LDAPUrl getLDAPUrl() { + if (m_dsIdx >= 0) { + return m_dsList[m_dsIdx].url; + } + return m_dsList[0].url; } int getConnSetupDelay() { @@ -363,8 +448,7 @@ class LDAPConnSetupMgr implements Cloneable, java.io.Serializable { //Create a Thread to execute connectSetver() final int dsIdx = i; - String threadName = "ConnSetupMgr " + - m_dsList[dsIdx].host + ":" + m_dsList[dsIdx].port; + String threadName = "ConnSetupMgr " + m_dsList[dsIdx].url; Thread t = new Thread(new Runnable() { public void run() { connectServer(dsIdx); @@ -407,20 +491,29 @@ class LDAPConnSetupMgr implements Cloneable, java.io.Serializable { Thread currThread = Thread.currentThread(); Socket sock = null; LDAPException conex = null; - + try { /* If we are to create a socket ourselves, make sure it has sufficient privileges to connect to the desired host */ - if (m_factory == null) { - sock = new Socket (entry.host, entry.port); - //s.setSoLinger( false, -1 ); + if (!entry.url.isSecure()) { + sock = new Socket (entry.url.getHost(), entry.url.getPort()); } else { - sock = m_factory.makeSocket(entry.host, entry.port); + LDAPSocketFactory factory = m_factory; + if (factory == null) { + factory = entry.url.getSocketFactory(); + } + if (factory == null) { + throw new LDAPException("Can not connect, no socket factory " + entry.url, + LDAPException.OTHER); + } + sock = factory.makeSocket(entry.url.getHost(), entry.url.getPort()); } + + sock.setTcpNoDelay( true ); } catch (IOException e) { conex = new LDAPException("failed to connect to server " - + entry.host+":"+entry.port, LDAPException.CONNECT_ERROR); + + entry.url, LDAPException.CONNECT_ERROR); } catch (LDAPException e) { conex = e; @@ -514,29 +607,5 @@ class LDAPConnSetupMgr implements Cloneable, java.io.Serializable { str += m_dsList[i]+ " "; } return str; - } - - int getID() { - return m_id; } - - String getLDAPUrl() { - return ((m_factory == null) ? "ldap" : "ldaps") + - "://" + getHost() + ":" + getPort(); - } - - public Object clone() { - try { - LDAPConnSetupMgr cloneMgr = (LDAPConnSetupMgr) super.clone(); - cloneMgr.m_dsList = new ServerEntry[m_dsList.length]; - for (int i=0; i"; + } + } + + /** + * Layer a SSL socket over the current non-SSL one + */ + void layerSocket(LDAPTLSSocketFactory factory) throws Exception { + synchronized (m_sendRequestLock) { + try { + m_connMgr.layerSocket(factory); + setInputStream(m_connMgr.getSocket().getInputStream()); + setOutputStream(m_connMgr.getSocket().getOutputStream()); + } + catch (Exception e) { + m_serverInput = m_origServerInput; + m_serverOutput = m_origServerOutput; + throw e; + } + } + } + + void setBound(boolean bound) { + m_bound = bound; + } + + boolean isBound() { + return (m_thread != null) && m_bound; } InputStream getInputStream() { @@ -142,6 +181,10 @@ class LDAPConnThread extends Thread { m_serverOutput = os; } + int getRequestCount() { + return m_requests.size(); + } + void setTraceOutput(Object traceOutput) { synchronized (m_sendRequestLock) { if (traceOutput == null) { @@ -161,7 +204,7 @@ class LDAPConnThread extends Thread { String timeStamp = m_timeFormat.format(new Date()); StringBuffer sb = new StringBuffer(timeStamp); sb.append(" ldc="); - sb.append(m_connMgr.getID()); + sb.append(m_id); synchronized( m_sendRequestLock ) { if (m_traceOutput instanceof PrintWriter) { @@ -207,38 +250,71 @@ class LDAPConnThread extends Thread { void sendRequest (LDAPConnection conn, JDAPProtocolOp request, LDAPMessageQueue toNotify, LDAPConstraints cons) throws LDAPException { - if (!m_doRun) { - throw new LDAPException ( "not connected to a server", - LDAPException.SERVER_DOWN ); + + if (m_thread == null) { + throw new LDAPException ( "Not connected to a server", + LDAPException.SERVER_DOWN ); } + LDAPMessage msg = new LDAPMessage(allocateId(), request, cons.getServerControls()); if ( toNotify != null ) { - if (!(request instanceof JDAPAbandonRequest || - request instanceof JDAPUnbindRequest)) { - /* Only worry about toNotify if we expect a response... */ - this.m_requests.put (new Integer (msg.getMessageID()), toNotify); - /* Notify the backlog checker that there may be another outstanding - request */ - resultRetrieved(); - } + /* Only worry about toNotify if we expect a response... */ + m_requests.put (new Integer (msg.getMessageID()), toNotify); + + /* Notify the backlog checker that there may be another outstanding + request */ + resultRetrieved(); + toNotify.addRequest(msg.getMessageID(), conn, this, cons.getTimeLimit()); } + if (!sendRequest(msg, /*ignoreErrors=*/false)) { + throw new LDAPException("Server or network error", + LDAPException.SERVER_DOWN); + } + } + + private boolean sendRequest (LDAPMessage msg, boolean ignoreErrors) { synchronized( m_sendRequestLock ) { try { if (m_traceOutput != null) { logTraceMessage(msg.toTraceString()); } msg.write (m_serverOutput); - m_serverOutput.flush (); - } catch (IOException e) { - networkError(e); + m_serverOutput.flush(); + return true; } + catch (IOException e) { + if (!ignoreErrors) { + networkError(e); + } + } + catch (NullPointerException e) { + // m_serverOutput is null bacause of network error + if (!ignoreErrors) { + if (m_thread != null) { + throw e; + } + } + } + return false; } } + private void sendUnbindRequest(LDAPControl[] ctrls) { + LDAPMessage msg = + new LDAPMessage(allocateId(), new JDAPUnbindRequest(), ctrls); + sendRequest(msg, /*ignoreErrors=*/true); + } + + private void sendAbandonRequest(int id, LDAPControl[] ctrls) { + LDAPMessage msg = + new LDAPMessage(allocateId(), new JDAPAbandonRequest(id), ctrls); + sendRequest(msg, /*ignoreErrors=*/true); + } + /** * Register with this connection thread. * @param conn LDAP connection @@ -252,8 +328,8 @@ class LDAPConnThread extends Thread { return m_registered.size(); } - boolean isRunning() { - return m_doRun; + boolean isConnected() { + return m_thread != null; } /** @@ -261,111 +337,118 @@ class LDAPConnThread extends Thread { * is deregistered. Then, this thread should be killed. * @param conn LDAP connection */ - public synchronized void deregister(LDAPConnection conn) { + /** + * De-Register with this connection thread. If all the connection + * is deregistered. Then, this thread should be killed. + * @param conn LDAP connection + */ + synchronized void deregister(LDAPConnection conn) { + + if (m_thread == null) { + return; + } + m_registered.removeElement(conn); if (m_registered.size() == 0) { - try { + + // No more request processing + Thread t = m_thread; + m_thread = null; - if (!m_disconnected) { - LDAPSearchConstraints cons = conn.getSearchConstraints(); - sendRequest (null, new JDAPUnbindRequest (), null, cons); - } + try { + // Notify the server + sendUnbindRequest(conn.getConstraints().getServerControls()); - // must be set after the call to sendRequest - m_doRun =false; - - if ( m_thread != null && m_thread != Thread.currentThread()) { - - m_thread.interrupt(); - - // Wait up to 1 sec for thread to accept disconnect - // notification. When the interrupt is accepted, - // m_thread is set to null. See run() method. - try { - wait(1000); - } - catch (InterruptedException e) { - } + // interrupt the thread + try { + t.interrupt(); + wait(500); } - + catch (InterruptedException ignore) {} + } catch (Exception e) { LDAPConnection.printDebug(e.toString()); } finally { - cleanUp(); + cleanUp(null); } } } + /** - * Clean ups before shutdown the thread. + * Clean up after the thread shutdown. + * The list of registered clients m_registered is left in the current state + * to enable the clients to recover the connection. */ - private void cleanUp() { - if (!m_disconnected) { + private void cleanUp(LDAPException ex) { + + resultRetrieved(); + + try { + m_serverOutput.close (); + } catch (Exception e) { + } finally { + m_serverOutput = null; + } + + try { + m_serverInput.close (); + } catch (Exception e) { + } finally { + m_serverInput = null; + } + + if (m_origServerInput != null) { try { - m_serverOutput.close (); + m_origServerInput.close (); } catch (Exception e) { } finally { - m_serverOutput = null; + m_origServerInput = null; } + } + if (m_origServerOutput != null) { try { - m_serverInput.close (); + m_origServerOutput.close (); } catch (Exception e) { } finally { - m_serverInput = null; + m_origServerOutput = null; } + } - try { - m_socket.close (); - } catch (Exception e) { - } finally { - m_socket = null; - } - - m_disconnected = true; + // Notify the Connection Manager + if (ex != null) { + // the connection is lost + m_connMgr.invalidateConnection(); + } + else { + // the connection is closed + m_connMgr.closeConnection(); + } - /** - * Notify the Connection Setup Manager that the connection was - * terminated by the user - */ - m_connMgr.disconnect(); - - /** - * Cancel all outstanding requests - */ - if (m_requests != null) { - Enumeration requests = m_requests.elements(); - while (requests.hasMoreElements()) { - LDAPMessageQueue listener = - (LDAPMessageQueue)requests.nextElement(); + // Set the status for all outstanding requests + Enumeration requests = m_requests.elements(); + while (requests.hasMoreElements()) { + try { + LDAPMessageQueue listener = + (LDAPMessageQueue)requests.nextElement(); + if (ex != null) { + listener.setException(this, ex); + } + else { listener.removeAllRequests(this); } - } - - /** - * Notify all the registered about this bad moment. - * IMPORTANT: This needs to be done at last. Otherwise, the socket - * input stream and output stream might never get closed and the whole - * task will get stuck in the stop method when we tried to stop the - * LDAPConnThread. - */ - - if (m_registered != null) { - Vector registerCopy = (Vector)m_registered.clone(); - - Enumeration cancelled = registerCopy.elements(); - - while (cancelled.hasMoreElements ()) { - LDAPConnection c = (LDAPConnection)cancelled.nextElement(); - c.deregisterConnection(); - } } - m_registered.removeAllElements(); - m_requests.clear(); - m_messages = null; - m_cache = null; + catch (Exception ignore) {} + } + m_requests.clear(); + + if (m_messages != null) { + m_messages.clear(); } + + m_bound = false; } /** @@ -383,8 +466,8 @@ class LDAPConnThread extends Thread { while( listeners.hasMoreElements() ) { LDAPMessageQueue l = (LDAPMessageQueue)listeners.nextElement(); - // If there are any threads waiting for a regular response - // message, we have to go read the next incoming message + // If there are clients waiting for a regular response + // message, skip backlog checking if ( !(l instanceof LDAPSearchListener) ) { return; } @@ -437,29 +520,21 @@ class LDAPConnThread extends Thread { * Reads from the LDAP server input stream for incoming LDAP messages. */ public void run() { - - // if there is a problem of establishing connection to the server, - // stop the thread right away... - if (!m_doRun) { - return; - } - m_thread = Thread.currentThread(); LDAPMessage msg = null; JDAPBERTagDecoder decoder = new JDAPBERTagDecoder(); + int[] nread = new int[1]; - while (m_doRun) { - yield(); - int[] nread = new int[1]; - nread[0] = 0; + while (Thread.currentThread() == m_thread) { try { - // Check after every BACKLOG_CHKCNT messages if the backlog is not too high + // Check every BACKLOG_CHKCNT messages if the backlog is not too high if (--m_backlogCheckCounter <= 0) { m_backlogCheckCounter = BACKLOG_CHKCNT; checkBacklog(); } + nread[0] = 0; BERElement element = BERElement.getElement(decoder, m_serverInput, nread); @@ -473,17 +548,14 @@ class LDAPConnThread extends Thread { // entry, thereby avoiding serialization of the entry stored in the // cache processResponse (msg, nread[0]); + Thread.yield(); } catch (Exception e) { - if (m_doRun) { + if (Thread.currentThread() == m_thread) { networkError(e); } else { - // interrupted from deregister() - synchronized (this) { - m_thread = null; - notifyAll(); - } + resultRetrieved(); } } } @@ -502,24 +574,6 @@ class LDAPConnThread extends Thread { return; /* nobody is waiting for this response (!) */ } - // For asynch operations controls are to be read from the LDAPMessage - // For synch operations controls are copied into the LDAPConnection - // For synch search operations, controls are also copied into - // LDAPSearchResults (see LDAPConnection.checkSearchMsg()) - if ( ! l.isAsynchOp()) { - - /* Were there any controls for this client? */ - LDAPControl[] con = msg.getControls(); - if (con != null) { - int msgid = msg.getMessageID(); - LDAPConnection ldc = l.getConnection(msgid); - if (ldc != null) { - ldc.setResponseControls( this, - new LDAPResponseControl(ldc, msgid, con)); - } - } - } - if (m_cache != null && (l instanceof LDAPSearchListener)) { cacheSearchResult((LDAPSearchListener)l, msg, size); } @@ -530,10 +584,42 @@ class LDAPConnThread extends Thread { m_requests.remove (messageID); if (m_requests.size() == 0) { m_backlogCheckCounter = BACKLOG_CHKCNT; - } + } + + // Change IO streams if startTLS extended op completed + if (msg instanceof LDAPExtendedResponse) { + LDAPExtendedResponse extrsp = (LDAPExtendedResponse) msg; + String extid = extrsp.getID(); + if (extrsp.getResultCode() == 0 && extid != null && + extid.equals(LDAPConnection.OID_startTLS)) { + + changeIOStreams(); + } + } } } + private void changeIOStreams() { + + // Note: For the startTLS, the new streams are layered over the + // existing ones so current IO streams as well as the socket MUST + // not be closed. + m_origServerInput = m_serverInput; + m_origServerOutput = m_serverOutput; + m_serverInput = null; + m_serverOutput = null; + + while (m_serverInput == null || m_serverOutput == null) { + try { + if (Thread.currentThread() != m_thread) { + return; // user disconnected + } + Thread.sleep(200); + } catch (InterruptedException ignore) {} + } + } + + /** * Collect search results to be added to the LDAPCache. Search results are * packaged in a vector and temporary stored into a hashtable m_messages @@ -629,12 +715,13 @@ class LDAPConnThread extends Thread { } /** - * Stop dispatching responses for a particular message ID. + * Stop dispatching responses for a particular message ID and send + * the abandon request. * @param id Message ID for which to discard responses. */ - void abandon (int id ) { + void abandon (int id, LDAPControl[] ctrls) { - if (!m_doRun) { + if (m_thread == null) { return; } @@ -647,6 +734,8 @@ class LDAPConnThread extends Thread { l.removeRequest(id); } resultRetrieved(); // If LDAPConnThread is blocked in checkBacklog() + + sendAbandonRequest(id, ctrls); } /** @@ -657,7 +746,7 @@ class LDAPConnThread extends Thread { */ LDAPMessageQueue changeListener (int id, LDAPMessageQueue toNotify) { - if (!m_doRun) { + if (m_thread == null) { toNotify.setException(this, new LDAPException("Server or network error", LDAPException.SERVER_DOWN)); return null; @@ -672,26 +761,11 @@ class LDAPConnThread extends Thread { */ private synchronized void networkError (Exception e) { - m_doRun =false; - - // notify the Connection Setup Manager that the connection is lost - m_connMgr.invalidateConnection(); - - try { - - // notify each listener that the server is down. - Enumeration requests = m_requests.elements(); - while (requests.hasMoreElements()) { - LDAPMessageQueue listener = - (LDAPMessageQueue)requests.nextElement(); - listener.setException(this, new LDAPException("Server or network error", - LDAPException.SERVER_DOWN)); - } - - } catch (NullPointerException ee) { - System.err.println("Exception: "+ee.toString()); + if (m_thread == null) { + return; } - - cleanUp(); + m_thread = null; // No more request processing + cleanUp(new LDAPException("Server or network error", + LDAPException.SERVER_DOWN)); } -} +} \ No newline at end of file diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPConnection.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPConnection.java index 01bd79065c4..a4d9e88d333 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPConnection.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPConnection.java @@ -94,7 +94,7 @@ import java.net.*; public class LDAPConnection implements LDAPv3, LDAPAsynchronousConnection, Cloneable, Serializable { - static final long serialVersionUID = -8698420087475771144L; + static final long serialVersionUID = -8698420087475771145L; /** * Version of the LDAP protocol used by default. @@ -208,9 +208,6 @@ public class LDAPConnection * Constants */ private final static String defaultFilter = "(objectClass=*)"; - private final static LDAPConstraints readConstraints = new - LDAPSearchConstraints(); - /** * Internal variables */ @@ -223,48 +220,46 @@ public class LDAPConnection private Vector m_responseListeners; private Vector m_searchListeners; - private boolean m_bound; - private String m_prevBoundDN; - private String m_prevBoundPasswd; + private String m_boundDN; private String m_boundPasswd; private int m_protocolVersion = LDAP_VERSION; + private LDAPConnSetupMgr m_connMgr; private int m_connSetupDelay = -1; private int m_connectTimeout = 0; private LDAPSocketFactory m_factory; + + // A flag if m_factory is used to start TLS + private boolean m_isTLSFactory; + /* m_thread does all socket i/o for the object and any clones */ private transient LDAPConnThread m_thread = null; - /* To manage received server controls on a per-thread basis, - we keep a table of active threads and a table of controls, - indexed by thread */ - private Vector m_attachedList = new Vector(); + + /* To manage received server controls on a per-thread basis */ private Hashtable m_responseControlTable = new Hashtable(); private LDAPCache m_cache = null; - static Hashtable m_threadConnTable = new Hashtable(); - // This handles the case when the client lost the connection to the - // server. After the client reconnects with the server, the bound resets - // to false. If the client used to have anonymous bind, then this boolean - // will take care of the case whether the client should send anonymous bind - // request to the server. - private boolean m_anonymousBound = false; + // A flag set if startTLS() called successfully + private boolean m_useTLS; + + // OID for the extended operation startTLS + final static String OID_startTLS = "1.3.6.1.4.1.1466.20037"; private Object m_security = null; private LDAPSaslBind m_saslBinder = null; private Properties m_securityProperties; private Hashtable m_properties = new Hashtable(); private LDAPConnection m_referralConnection; - private String m_authMethod = "none"; /** * Properties */ - private final static Float SdkVersion = new Float(4.16f); + private final static Float SdkVersion = new Float(4.17f); private final static Float ProtocolVersion = new Float(3.0f); private final static String SecurityVersion = new String("none,simple,sasl"); private final static Float MajorVersion = new Float(4.0f); - private final static Float MinorVersion = new Float(0.16f); + private final static Float MinorVersion = new Float(0.17f); private final static String DELIM = "#"; private final static String PersistSearchPackageName = "netscape.ldap.controls.LDAPPersistSearchControl"; @@ -426,6 +421,8 @@ public class LDAPConnection } /** + * Change a property of a connection.

+ * * The following properties are defined:
* com.netscape.ldap.schema.quoting - "standard" or "NetscapeBug"
* Note: if this property is not set, the SDK will query the server @@ -647,14 +644,15 @@ public class LDAPConnection /** * Gets the object representing the socket factory used to establish - * a connection to the LDAP server. + * a connection to the LDAP server or for the start TLS operation. *

* * @return the object representing the socket factory used to - * establish a connection to a server. + * establish a connection to a server or for the start TLS operation. * @see netscape.ldap.LDAPSocketFactory * @see netscape.ldap.LDAPSSLSocketFactory * @see netscape.ldap.LDAPConnection#setSocketFactory(netscape.ldap.LDAPSocketFactory) + * @see netscape.ldap.LDAPConnection#startTLS */ public LDAPSocketFactory getSocketFactory () { return m_factory; @@ -662,17 +660,24 @@ public class LDAPConnection /** * Specifies the object representing the socket factory that you - * want to use to establish a connection to a server. + * want to use to establish a connection to a server or for the + * start TLS operation. *

- * + * If the socket factory is to be used to establish a connection + * setSocketFactory() must be called before + * connect(). For the start TLS operation + * setSocketFactory() must be called after connect(). * @param factory the object representing the socket factory that - * you want to use to establish a connection to a server + * you want to use to establish a connection to a server or for + * the start TLS operation. * @see netscape.ldap.LDAPSocketFactory * @see netscape.ldap.LDAPSSLSocketFactory * @see netscape.ldap.LDAPConnection#getSocketFactory + * @see netscape.ldap.LDAPConnection#startTLS */ public void setSocketFactory (LDAPSocketFactory factory) { m_factory = factory; + m_isTLSFactory = false; } /** @@ -681,23 +686,8 @@ public class LDAPConnection * @return true if connected to an LDAP server over this connection. * If not connected to an LDAP server, returns false. */ - public boolean isConnected() { - synchronized (this) { - if (m_thread != null && m_thread.isRunning()) { - return true; - } - } - // Not connected; If m_thread != null then there was a network - // error and cleanup is currently in progress. Give it some - // time to complete. - if (m_thread != null) { - try { - Thread.sleep(500); - } - catch (InterruptedException ignore) {} - Thread.yield(); - } - return false; + public synchronized boolean isConnected() { + return m_thread != null && m_thread.isConnected(); } /** @@ -707,15 +697,38 @@ public class LDAPConnection * either a blank name or password), returns false. */ public boolean isAuthenticated () { - if (m_bound) { - if ((m_boundDN == null) || m_boundDN.equals("") || - (m_boundPasswd == null) || m_boundPasswd.equals("")) { - return false; - } + boolean bound = false; + synchronized (this) { + bound = (m_thread != null) && m_thread.isBound(); } - return m_bound; + return bound; } + /** + * Mark the connection bind state + */ + synchronized void setBound(boolean bound) { + if (m_thread != null) { + if (!bound) { + m_thread.setBound(false); + } + // sasl bind + else if (m_saslBinder != null) { + m_thread.setBound(true); + } + // simple bind + else { + m_thread.setBound(!isAnonymousUser()); + } + } + } + + + boolean isAnonymousUser() { + return (m_boundDN == null) || m_boundDN.equals("") || + (m_boundPasswd == null) || m_boundPasswd.equals(""); + } + /** * Connects to the specified host and port. If this LDAPConnection object * represents an open connection, the connection is closed first @@ -917,7 +930,8 @@ public class LDAPConnection } /* Create the Connection Setup Manager */ - m_connMgr = new LDAPConnSetupMgr(hostList, portList, m_factory); + m_connMgr = new LDAPConnSetupMgr(hostList, portList, + m_isTLSFactory ? null : m_factory); m_connMgr.setConnSetupDelay(m_connSetupDelay); m_connMgr.setConnectTimeout(m_connectTimeout); @@ -928,6 +942,13 @@ public class LDAPConnection } } + void connect(LDAPUrl[] urls) throws LDAPException { + m_connMgr = new LDAPConnSetupMgr(urls, m_factory); + m_connMgr.setConnSetupDelay(m_connSetupDelay); + m_connMgr.setConnectTimeout(m_connectTimeout); + connect(); + } + /** * Connects to the specified host and port and uses the specified DN and * password to authenticate to the server, with the specified LDAP @@ -1039,9 +1060,13 @@ public class LDAPConnection LDAPException.PARAM_ERROR ); } - m_connMgr.openConnection(); - m_thread = getNewThread(m_connMgr, m_cache); - authenticateSSLConnection(); + if (m_thread == null) { + m_thread = new LDAPConnThread(m_connMgr, m_cache, getTraceOutput()); + } + + m_thread.connect(this); + + checkClientAuth(); } /** @@ -1071,88 +1096,12 @@ public class LDAPConnection } - private synchronized LDAPConnThread getNewThread(LDAPConnSetupMgr connMgr, - LDAPCache cache) - throws LDAPException { - - LDAPConnThread newThread = null; - Vector v = null; - - synchronized(m_threadConnTable) { - - Enumeration keys = m_threadConnTable.keys(); - boolean connExists = false; - - // transverse each thread - while (keys.hasMoreElements()) { - LDAPConnThread connThread = (LDAPConnThread)keys.nextElement(); - Vector connVector = (Vector)m_threadConnTable.get(connThread); - Enumeration enumv = connVector.elements(); - - // traverse each LDAPConnection under the same thread - while (enumv.hasMoreElements()) { - LDAPConnection conn = (LDAPConnection)enumv.nextElement(); - - // this is not a brand new connection - if (conn.equals(this)) { - connExists = true; - - if (!connThread.isRunning()) { - // need to move all the LDAPConnections from the dead thread - // to the new thread - try { - newThread = new LDAPConnThread(connMgr, cache, - getTraceOutput()); - v = (Vector)m_threadConnTable.remove(connThread); - break; - } catch (Exception e) { - throw new LDAPException ("unable to establish connection", - LDAPException.UNAVAILABLE); - } - } - break; - } - } - - if (connExists) { - break; - } - } - - // if this connection is new or the corresponding thread for the current - // connection is dead - if (!connExists) { - try { - newThread = new LDAPConnThread(connMgr, cache, - getTraceOutput()); - v = new Vector(); - v.addElement(this); - } catch (Exception e) { - throw new LDAPException ("unable to establish connection", - LDAPException.UNAVAILABLE); - } - } - - // add new thread to the table - if (newThread != null) { - m_threadConnTable.put(newThread, v); - for (int i=0, n=v.size(); i 1) { - disconnect(); - connect(); - } - } + forceNonSharedConnection(); + m_boundDN = null; m_protocolVersion = 3; // Must be 3 for SASL if ( props == null ) { @@ -1448,7 +1381,6 @@ public class LDAPConnection m_saslBinder = new LDAPSaslBind( dn, mechanisms, packageName, props, cbh ); m_saslBinder.bind( this ); - m_authMethod = "sasl"; m_boundDN = dn; } @@ -1487,19 +1419,11 @@ public class LDAPConnection cons = m_defaultConstraints; } - m_prevBoundDN = m_boundDN; - m_prevBoundPasswd = m_boundPasswd; m_boundDN = dn; m_boundPasswd = passwd; m_protocolVersion = version; - - if (m_thread == null) { - connect(); - } - else if (m_thread.getClientCount() > 1) { - disconnect(); - connect(); - } + + forceNonSharedConnection(); if (listener == null) { listener = new LDAPResponseListener(/*asynchOp=*/true); @@ -1691,133 +1615,199 @@ public class LDAPConnection } /** - * Internal routine. Binds to the LDAP server. - * @param version protocol version to request from server - * @param rebind true if the bind/authenticate operation is requested, - * false if the method is invoked by the "smart failover" feature - * @exception LDAPException failed to bind - */ - private void internalBind (int version, boolean rebind, - LDAPConstraints cons) throws LDAPException { - m_saslBinder = null; + * Begin using the Transport Layer Security (TLS) protocol for session + * privacy. + *

+ * Before startTLS() is called, a socket factory of type + * LDAPTLSSocketFactory must be set for the connection. + * The factory must be set after the connect() call. + *

+ *

+     * LDAPConnection ldc = new LDAPConnection();
+     * try {
+     *     ldc.connect(host, port);
+     *     ldc.setSocketFactory(new JSSSocketFactory());
+     *     ldc.startTLS();
+     *     ldc.autheticate(3, userdn, userpw);
+     * }
+     * catch (LDAPException e) {
+     * ...
+     * }
+     * 
+ *

+ * If the socket factory of the connection is not capable of initiating a + * TLS session , an LDAPException is thrown with the error code + * TLS_NOT_SUPPORTED. + *

+ * If the server does not support the transition to a TLS session, an + * LDAPException is thrown with the error code returned by the server. + * If there are outstanding LDAP operations on the connection or the + * connection is already in the secure mode, an LDAPException is thrown. + * + * @exception LDAPException Failed to convert to a TLS session. + * @see netscape.ldap.LDAPTLSSocketFactory + * @see netscape.ldap.LDAPConnection#setSocketFactory(netscape.ldap.LDAPSocketFactory) + * @see netscape.ldap.LDAPConnection#isTLS + * @since LDAPJDK 4.17 + */ + public void startTLS() throws LDAPException { - if (!isConnected()) { - m_bound = false; - m_authMethod = "none"; - connect (); - // special case: the connection currently is not bound, and now there is - // a bind request. The connection needs to reconnect if the thread has - // more than one LDAPConnection. - } else if (!m_bound && rebind && m_thread.getClientCount() > 1) { - disconnect(); - connect(); + if (m_useTLS) { + throw new LDAPException("Already using TLS", + LDAPException.OTHER); } - // if the connection is still intact and no rebind request - if (m_bound && !rebind) { - return; + if (m_factory == null || !( m_factory instanceof LDAPTLSSocketFactory)) { + throw new LDAPException("No socket factory for the startTLS operation", + LDAPException.OTHER); } - // if the connection was lost and did not have any kind of bind - // operation and the current one does not request any bind operation (ie, - // no authenticate has been called) - if (!m_anonymousBound && - ((m_boundDN == null) || (m_boundPasswd == null)) && - !rebind) { - return; - } + // Denote that m_factory is to be used from now on used for TLS and not for + // SSL connections. + m_isTLSFactory = true; - if (m_bound && rebind) { - if (m_protocolVersion == version) { - if (m_anonymousBound && ((m_boundDN == null) || (m_boundPasswd == null))) - return; + checkConnection(/*rebind=*/true); - if (!m_anonymousBound && (m_boundDN != null) && (m_boundPasswd != null) && - m_boundDN.equals(m_prevBoundDN) && - m_boundPasswd.equals(m_prevBoundPasswd)) { - return; - } - } - - // if we got this far, we need to rebind since previous and - // current credentials are not the same. - // if the current connection is the only connection of the thread, - // then reuse this current connection. otherwise, disconnect the current - // one (ie, detach from the current thread) and reconnect again (ie, get - // a new thread). - if (m_thread.getClientCount() > 1) { - disconnect(); - connect(); + // If some requests still in progress, throw exception + synchronized (this) { + if (isConnected() && m_thread.getRequestCount() != 0) { + throw new LDAPException( + "Connection has outstanding LDAP operations", + LDAPException.OTHER); } } - m_protocolVersion = version; - - LDAPResponseListener myListener = getResponseListener (); + // Send startTLS extended op try { - if (m_referralConnection != null && m_referralConnection.isConnected()) { - m_referralConnection.disconnect(); - } - m_referralConnection = null; - m_bound = false; - sendRequest(new JDAPBindRequest(m_protocolVersion, m_boundDN, - m_boundPasswd), - myListener, cons); - checkMsg( myListener.getResponse() ); - markConnAsBound(); - m_rebindConstraints = (LDAPConstraints)cons.clone(); - m_authMethod = "simple"; - } catch (LDAPReferralException e) { - m_referralConnection = createReferralConnection(e, cons); - } finally { - releaseResponseListener(myListener); + LDAPExtendedOperation response = + extendedOperation(new LDAPExtendedOperation(OID_startTLS, null), + m_defaultConstraints); + } + catch (LDAPException ex) { + ex.setExtraMessage("Cannot start TLS"); + throw ex; + } + + /** + * Server accepted startTLS request, try to initiate TLS over the + * current socket. + */ + try { + m_thread.layerSocket((LDAPTLSSocketFactory)m_factory); + m_useTLS = true; + } + catch (LDAPException ex) { + ex.setExtraMessage("Failed to start TLS"); + throw ex; + } + catch (Exception ex) { + throw new LDAPException("Failed to start TLS", + LDAPException.OTHER); } } /** - * Mark this connection as bound in the thread connection table + * Indicates if the session is currently protected by TLS. + * @return true if TLS was activated. + * @since LDAPJDK 4.17 + * @see netscape.ldap.LDAPConnection#startTLS */ - void markConnAsBound() { - synchronized (m_threadConnTable) { - if (m_threadConnTable.containsKey(m_thread)) { - Vector v = (Vector)m_threadConnTable.get(m_thread); - for (int i=0, n=v.size(); i 1) { + reconnect(/*rebind=*/false); + } + } + + /** + * Internal routine. Binds to the LDAP server. + * @exception LDAPException failed to bind + */ + private void simpleBind (LDAPConstraints cons) throws LDAPException { + + m_saslBinder = null; + + LDAPResponseListener myListener = new LDAPResponseListener ( /*asynchOp=*/false ); + try { + + if (m_referralConnection != null && m_referralConnection.isConnected()) { + m_referralConnection.disconnect(); } + m_referralConnection = null; + + setBound(false); + sendRequest(new JDAPBindRequest(m_protocolVersion, m_boundDN, + m_boundPasswd), + myListener, cons); + checkMsg( myListener.getResponse() ); + + setBound(true); + m_rebindConstraints = (LDAPConstraints)cons.clone(); + + } catch (LDAPReferralException e) { + m_referralConnection = createReferralConnection(e, cons); } } /** * Send a request to the server */ - void sendRequest(JDAPProtocolOp oper, LDAPMessageQueue myListener, + synchronized void sendRequest(JDAPProtocolOp oper, LDAPMessageQueue myListener, LDAPConstraints cons) throws LDAPException { - // retry three times if we get a nullPointer exception. - // Don't remove this. The following situation might happen: - // Before sendRequest gets invoked, it is possible that the LDAPConnThread - // encountered a network error and called deregisterConnection which - // set the thread reference to null. boolean requestSent=false; - for (int i=0; !requestSent && i<3; i++) { + boolean restoreTried=false; + + while (!requestSent) { + try { m_thread.sendRequest(this, oper, myListener, cons); + + /** + * In Java, a lost socket connected is often not detected until + * a read is attempted following a write. Wait for the response + * from the server to be sure the connection is really there. + */ + if (!myListener.isAsynchOp()) { + myListener.waitFirstMessage(); + } + requestSent=true; - } catch(NullPointerException ne) { - // do nothing - }catch (IllegalArgumentException e) { + } + + catch (IllegalArgumentException e) { throw new LDAPException(e.getMessage(), LDAPException.PARAM_ERROR); } - - } - if (!isConnected()) { - throw new LDAPException("The connection is not available", - LDAPException.OTHER); + catch(NullPointerException e) { + if (isConnected() || restoreTried) { + break; // give up + } + // else try to restore the connection + } + catch (LDAPException e) { + if (e.getLDAPResultCode() != e.SERVER_DOWN || restoreTried) { + throw e; // give up + } + // else try to restore the connection + } + + // Try to restore the connection if needed, but no more then once + if (!requestSent && !restoreTried) { + restoreTried = true; + myListener.reset(); + boolean rebind = !(oper instanceof JDAPBindRequest); + restoreConnection(rebind); + } } + if (!requestSent) { throw new LDAPException("Failed to send request", LDAPException.OTHER); @@ -1825,32 +1815,46 @@ public class LDAPConnection } /** - * Internal routine. Binds to the LDAP server. + * Check and restore the connection if needed. * This method is used by the "smart failover" feature. If a server * or network error has occurred, an attempt is made to automatically * restore the connection on the next ldap operation request - * @exception LDAPException failed to bind or the user has disconncted + * @param rebind true if needs rebind after reconnect */ - private void internalBind (LDAPConstraints cons) throws LDAPException { - + private void checkConnection(boolean rebind) throws LDAPException { + + if (isConnected()) { + return; + } // If the user has invoked disconnect() no attempt is made // to restore the connection - if (m_connMgr != null && m_connMgr.isUserDisconnected()) { + if (m_connMgr == null) { throw new LDAPException("not connected", LDAPException.OTHER); } + restoreConnection(rebind); + } + + /** + * Reconnect and reauthenticate + */ + private void restoreConnection(boolean rebind) throws LDAPException { + connect(); + if (m_useTLS) { + m_useTLS = false; + startTLS(); + } + + if (!rebind) { + return; + } + if (m_saslBinder != null) { - if ( !isConnected() ) { - connect(); - } m_saslBinder.bind(this, false); - m_authMethod = "sasl"; - } else { - //Rebind using m_rebindConstraints - if (m_rebindConstraints == null) { - m_rebindConstraints = m_defaultConstraints; - } - internalBind (m_protocolVersion, false, m_rebindConstraints); + } + else if (m_rebindConstraints != null) { + //Needs bind + simpleBind(m_rebindConstraints); } } @@ -1861,26 +1865,38 @@ public class LDAPConnection * @return the authentication method, or "none" */ public String getAuthenticationMethod() { - return isConnected() ? m_authMethod : "none"; + if (!isAuthenticated()) { + return "none"; + } + else { + return (m_saslBinder == null) ? "simple" : "sasl"; + } + } /** * Disconnect from the server and then reconnect using the current - * credentials and authentication method + * credentials and authentication method.

+ * If TLS was enabled with the startTLS() call, reenable TLS after + * reconnect. * @exception LDAPException if not previously connected, or if * there is a failure on disconnecting or on connecting */ - public void reconnect() throws LDAPException { - + public void reconnect() throws LDAPException { + reconnect(/*rebind=*/true); + } + + void reconnect(boolean rebind) throws LDAPException { + // Save connect parameters that get cleared by disconnect() + boolean useTLS = m_useTLS; + LDAPConnSetupMgr connMgr = m_connMgr; + LDAPConstraints rebindCons = m_rebindConstraints; + disconnect(); - connect(); - - if (m_saslBinder != null) { - m_saslBinder.bind(this, true); - m_authMethod = "sasl"; - } else { - internalBind (m_protocolVersion, true, m_defaultConstraints); - } + m_useTLS = useTLS; + m_connMgr = connMgr; + m_rebindConstraints = rebindCons; + restoreConnection(rebind); } /** @@ -1895,11 +1911,8 @@ public class LDAPConnection if (!isConnected()) { return; } - // Clone the Connection Setup Manager if the connection is shared - if (m_thread.isRunning() && m_thread.getClientCount() > 1) { - m_connMgr = (LDAPConnSetupMgr) m_connMgr.clone(); - m_connMgr.disconnect(); - } + + m_thread.deregister(this); if (m_referralConnection != null && m_referralConnection.isConnected()) { m_referralConnection.disconnect(); @@ -1911,46 +1924,11 @@ public class LDAPConnection m_cache = null; } - //deleteThreadConnEntry(); - deregisterConnection(); - } - - /** - * Remove this connection from the thread connection table - */ - private void deleteThreadConnEntry() { - synchronized (m_threadConnTable) { - Vector connVector = (Vector)m_threadConnTable.get(m_thread); - if (connVector == null) { - printDebug("Thread table does not contain the thread of this object"); - return; - } - Enumeration enumv = connVector.elements(); - while (enumv.hasMoreElements()) { - LDAPConnection c = (LDAPConnection)enumv.nextElement(); - if (c.equals(this)) { - connVector.removeElement(c); - if (connVector.size() == 0) { - m_threadConnTable.remove(m_thread); - } - return; - } - } - } - } - - /** - * Remove the association between this object and the connection thread - */ - synchronized void deregisterConnection() { - if (m_thread != null) { - deleteThreadConnEntry(); - if (m_thread.isRunning()) { - m_thread.deregister(this); - } - } + m_responseControlTable.clear(); + m_rebindConstraints = null; m_thread = null; - m_bound = false; + m_connMgr = null; + m_useTLS = false; } /** @@ -2533,9 +2511,24 @@ public class LDAPConnection printDebug("Exception: "+e); } - internalBind(cons); + checkConnection(/*rebind=*/true); + + /* Is this a persistent search? */ + boolean isPersistentSearch = false; + LDAPControl[] controls = + (LDAPControl[])getOption(LDAPv3.SERVERCONTROLS, cons); + for (int i = 0; (controls != null) && (i < controls.length); i++) { + if ( controls[i] instanceof + netscape.ldap.controls.LDAPPersistSearchControl ) { + isPersistentSearch = true; + break; + } + } + + // Persistent search is an asynchronous operation + LDAPSearchListener myListener = isPersistentSearch ? new LDAPSearchListener(/*asynchOp=*/true, cons) : + getSearchListener ( cons ); - LDAPSearchListener myListener = getSearchListener ( cons ); int deref = cons.getDereference(); JDAPSearchRequest request = null; @@ -2561,18 +2554,6 @@ public class LDAPConnection throw e; } - /* Is this a persistent search? */ - boolean isPersistentSearch = false; - LDAPControl[] controls = - (LDAPControl[])getOption(LDAPv3.SERVERCONTROLS, cons); - for (int i = 0; (controls != null) && (i < controls.length); i++) { - if ( controls[i] instanceof - netscape.ldap.controls.LDAPPersistSearchControl ) { - isPersistentSearch = true; - break; - } - } - /* For a persistent search, don't wait for a first result, because there may be none at this time if changesOnly was specified in the control. @@ -2649,15 +2630,13 @@ public class LDAPConnection } catch (LDAPException ex) { if (msg.getProtocolOp() instanceof JDAPSearchResultReference) { - /* - Don't want to miss all remaining results, so continue. - This is very ugly (using println). We should have a - configurable parameter (probably in LDAPSearchConstraints) - whether to ignore failed search references or throw exception. - */ - System.err.println( "LDAPConnection.checkSearchMsg: " + - "ignoring bad referral" ); - return; + // Ignore Search Result Referral Errors ? + if (cons.getReferralErrors() == cons.REFERRAL_ERROR_CONTINUE) { + return; // Don't want to miss all remaining results + } + else { + throw ex; + } } throw ex; } @@ -2725,7 +2704,7 @@ public class LDAPConnection public boolean compare( String DN, LDAPAttribute attr, LDAPConstraints cons) throws LDAPException { - internalBind(cons); + checkConnection(/*rebind=*/true); LDAPResponseListener myListener = getResponseListener (); Enumeration en = attr.getStringValues(); @@ -2847,7 +2826,7 @@ public class LDAPConnection */ public void add( LDAPEntry entry, LDAPConstraints cons ) throws LDAPException { - internalBind (cons); + checkConnection(/*rebind=*/true); LDAPResponseListener myListener = getResponseListener (); LDAPAttributeSet attrs = entry.getAttributeSet (); @@ -2920,7 +2899,7 @@ public class LDAPConnection public LDAPExtendedOperation extendedOperation( LDAPExtendedOperation op, LDAPConstraints cons) throws LDAPException { - internalBind (cons); + checkConnection(/*rebind=*/true); LDAPResponseListener myListener = getResponseListener (); LDAPMessage response = null; @@ -2956,9 +2935,9 @@ public class LDAPConnection } /** - * Makes a single change to an existing entry in the directory - * (for example, changes the value of an attribute, adds a new - * attribute value, or removes an existing attribute value).

+ * Makes a single change to an existing entry in the directory. + * For example, changes the value of an attribute, adds a new + * attribute value, or removes an existing attribute value.

* * Use the LDAPModification object to specify the change * to make and the LDAPAttribute object @@ -2993,8 +2972,8 @@ public class LDAPConnection /** * Makes a single change to an existing entry in the directory and * allows you to specify preferences for this LDAP modify operation - * by using an LDAPConstraints object. For - * example, you can specify whether or not to follow referrals. + * by using an LDAPConstraints object. + * For example, you can specify whether or not to follow referrals. * You can also apply LDAP v3 controls to the operation. *

* @@ -3022,9 +3001,9 @@ public class LDAPConnection } /** - * Makes a set of changes to an existing entry in the directory - * (for example, changes attribute values, adds new attribute values, - * or removes existing attribute values).

+ * Makes a set of changes to an existing entry in the directory. + * For example, changes attribute values, adds new attribute values, + * or removes existing attribute values.

* * Use the LDAPModificationSet object to specify the set * of changes to make. Changes are specified in terms @@ -3067,8 +3046,8 @@ public class LDAPConnection /** * Makes a set of changes to an existing entry in the directory and * allows you to specify preferences for this LDAP modify operation - * by using an LDAPConstraints object. For - * example, you can specify whether or not to follow referrals. + * by using an LDAPConstraints object. + * For example, you can specify whether or not to follow referrals. * You can also apply LDAP v3 controls to the operation. *

* @@ -3099,9 +3078,9 @@ public class LDAPConnection } /** - * Makes a set of changes to an existing entry in the directory - * (for example, changes attribute values, adds new attribute values, - * or removes existing attribute values).

+ * Makes a set of changes to an existing entry in the directory. + * For example, changes attribute values, adds new attribute values, + * or removes existing attribute values.

* * Use an array of LDAPModification objects to specify the * changes to make. Each change must be specified by @@ -3124,8 +3103,8 @@ public class LDAPConnection /** * Makes a set of changes to an existing entry in the directory and * allows you to specify preferences for this LDAP modify operation - * by using an LDAPConstraints object. For - * example, you can specify whether or not to follow referrals. + * by using an LDAPConstraints object. + * For example, you can specify whether or not to follow referrals. * You can also apply LDAP v3 controls to the operation. *

* @@ -3140,7 +3119,7 @@ public class LDAPConnection */ public void modify (String DN, LDAPModification[] mods, LDAPConstraints cons) throws LDAPException { - internalBind (cons); + checkConnection(/*rebind=*/true); LDAPResponseListener myListener = getResponseListener (); LDAPMessage response = null; @@ -3203,7 +3182,7 @@ public class LDAPConnection */ public void delete( String DN, LDAPConstraints cons ) throws LDAPException { - internalBind (cons); + checkConnection(/*rebind=*/true); LDAPResponseListener myListener = getResponseListener (); LDAPMessage response; @@ -3382,7 +3361,7 @@ public class LDAPConnection boolean deleteOldRDN, LDAPConstraints cons) throws LDAPException { - internalBind (cons); + checkConnection(/*rebind=*/true); LDAPResponseListener myListener = getResponseListener (); try { @@ -3470,7 +3449,7 @@ public class LDAPConnection cons = m_defaultConstraints; } - internalBind (cons); + checkConnection(/*rebind=*/true); if (listener == null) { listener = new LDAPResponseListener(/*asynchOp=*/true); @@ -3651,7 +3630,7 @@ public class LDAPConnection cons = m_defaultConstraints; } - internalBind (cons); + checkConnection(/*rebind=*/true); if (listener == null) { listener = new LDAPResponseListener(/*asynchOp=*/true); @@ -3664,9 +3643,9 @@ public class LDAPConnection } /** - * Makes a single change to an existing entry in the directory (for - * example, changes the value of an attribute, adds a new attribute - * value, or removes an existing attribute value).
+ * Makes a single change to an existing entry in the directory. + * For example, changes the value of an attribute, adds a new attribute + * value, or removes an existing attribute value.
* The LDAPModification object specifies both the change to make and * the LDAPAttribute value to be changed. * @@ -3689,9 +3668,9 @@ public class LDAPConnection } /** - * Makes a single change to an existing entry in the directory (for - * example, changes the value of an attribute, adds a new attribute - * value, or removes an existing attribute value).
+ * Makes a single change to an existing entry in the directory. + * For example, changes the value of an attribute, adds a new attribute + * value, or removes an existing attribute value.
* The LDAPModification object specifies both the change to make and * the LDAPAttribute value to be changed. * @@ -3716,7 +3695,7 @@ public class LDAPConnection cons = m_defaultConstraints; } - internalBind (cons); + checkConnection(/*rebind=*/true); if (listener == null) { listener = new LDAPResponseListener(/*asynchOp=*/true); @@ -3729,9 +3708,9 @@ public class LDAPConnection } /** - * Makes a set of changes to an existing entry in the directory (for - * example, changes attribute values, adds new attribute values, or - * removes existing attribute values). + * Makes a set of changes to an existing entry in the directory. + * For example, changes attribute values, adds new attribute values, or + * removes existing attribute values. *

* @param dn distinguished name of the entry to modify * @param mods a set of changes to make to the entry @@ -3751,8 +3730,8 @@ public class LDAPConnection } /** - * Makes a set of changes to an existing entry in the directory (for - * example, changes attribute values, adds new attribute values, or + * Makes a set of changes to an existing entry in the directory. + * For example, changes attribute values, adds new attribute values, or * removes existing attribute values). * * @param dn distinguished name of the entry to modify @@ -3776,7 +3755,7 @@ public class LDAPConnection cons = m_defaultConstraints; } - internalBind (cons); + checkConnection(/*rebind=*/true); if (listener == null) { listener = new LDAPResponseListener(/*asynchOp=*/true); @@ -3840,7 +3819,7 @@ public class LDAPConnection cons = m_defaultConstraints; } - internalBind (cons); + checkConnection(/*rebind=*/true); if (listener == null) { listener = new LDAPResponseListener(/*asynchOp=*/true); @@ -3939,7 +3918,7 @@ public class LDAPConnection cons = m_defaultConstraints; } - internalBind (cons); + checkConnection(/*rebind=*/true); if (listener == null) { listener = new LDAPSearchListener(/*asynchOp=*/true, cons); @@ -4008,7 +3987,7 @@ public class LDAPConnection cons = m_defaultConstraints; } - internalBind (cons); + checkConnection(/*rebind=*/true); if (listener == null) { listener = new LDAPResponseListener(/*asynchOp=*/true); @@ -4035,26 +4014,14 @@ public class LDAPConnection return; } - for (int i=0; i<3; i++) { - try { - /* Tell listener thread to discard results */ - m_thread.abandon( id ); - - /* Tell server to stop sending results */ - m_thread.sendRequest(null, new JDAPAbandonRequest(id), null, m_defaultConstraints); - - /* No response is forthcoming */ - break; - } catch (NullPointerException ne) { - // do nothing - } - } - if (m_thread == null) { - throw new LDAPException("Failed to send abandon request to the server.", - LDAPException.OTHER); - } + try { + /* Tell listener thread to discard results and send an abandon request */ + LDAPControl ctrls[] = m_defaultConstraints.getServerControls(); + m_thread.abandon( id, ctrls ); + + } catch (Exception ignore) {} } - + /** * Cancels all outstanding search requests associated with this * LDAPSearchListener object and discards any results already received. @@ -4520,26 +4487,22 @@ public class LDAPConnection * @see netscape.ldap.LDAPSearchResults#getResponseControls */ public LDAPControl[] getResponseControls() { - LDAPControl[] controls = null; + LDAPControl[] controls = null; + Thread caller = Thread.currentThread(); + + /* Get the latest controls for the caller thread */ + synchronized(m_responseControlTable) { + ResponseControls rspCtrls = + (ResponseControls) m_responseControlTable.get(caller); - /* Get the latest controls returned for our thread */ - synchronized(m_responseControlTable) { - Vector responses = (Vector)m_responseControlTable.get(m_thread); - - if (responses != null) { - // iterate through each response control - for (int i=0,size=responses.size(); i 0)) { + if (rspCtrls == null || rspCtrls.msgID != msgID) { + rspCtrls = new ResponseControls(msgID, ctrls); - // look at each response control - for (int i=v.size()-1; i>=0; i--) { - LDAPResponseControl response = - (LDAPResponseControl)v.elementAt(i); - - // if this response control belongs to this connection - if (response.getConnection().equals(this)) { - - // if the given control is null or - // the given control is not null and the current - // control does not correspond to the new LDAPMessage - if ((con == null) || - (con.getMsgID() != response.getMsgID())) { - v.removeElement(response); - } - - // For the same connection, if the message id from the - // given control is the same as the one in the queue, - // those controls in the queue will not get removed - // since they come from the persistent search control - // which allows more than one control response for - // one persistent search operation. - } - } - } else { - if (con != null) { - v = new Vector(); - } - } - - if (con != null) { - v.addElement(con); - m_responseControlTable.put(current, v); + m_responseControlTable.put(client, rspCtrls); } - - /* Do some garbage collection: check if any attached threads have - exited */ - /* Now check all threads in the list */ - Enumeration e = m_attachedList.elements(); - while( e.hasMoreElements() ) { - LDAPConnThread aThread = (LDAPConnThread)e.nextElement(); - if ( !aThread.isRunning() ) { - m_responseControlTable.remove( aThread ); - m_attachedList.removeElement( aThread ); - } + else { + rspCtrls.addControls(ctrls); } } - /* Make sure we're registered */ - if ( m_attachedList.indexOf( current ) < 0 ) { - m_attachedList.addElement( current ); - } } /** @@ -4970,10 +4899,10 @@ public class LDAPConnection * @param cons search constraints * @return new LDAPConnection, already connected and authenticated */ - private LDAPConnection prepareReferral( String connectList, + private LDAPConnection referralConnect( LDAPUrl[] refList, LDAPConstraints cons ) throws LDAPException { - LDAPConnection connection = new LDAPConnection (this.getSocketFactory()); + LDAPConnection connection = new LDAPConnection (getSocketFactory()); // Set the same connection setup failover policy as for this connection connection.setConnSetupDelay(getConnSetupDelay()); @@ -4982,6 +4911,11 @@ public class LDAPConnection connection.setOption(REFERRALS_REBIND_PROC, cons.getRebindProc()); connection.setOption(BIND, cons.getBindProc()); + Object traceOut = getProperty(TRACE_PROPERTY); + if (traceOut != null) { + connection.setProperty(TRACE_PROPERTY, traceOut); + } + // need to set the protocol version which gets passed to connection connection.setOption(PROTOCOL_VERSION, new Integer(m_protocolVersion)); @@ -4990,7 +4924,7 @@ public class LDAPConnection new Integer(cons.getHopLimit()-1)); try { - connection.connect (connectList, connection.DEFAULT_PORT); + connection.connect (refList); } catch (LDAPException e) { throw new LDAPException("Referral connect failed: " + e.getMessage(), @@ -5018,10 +4952,12 @@ public class LDAPConnection e.getLDAPResultCode()); } } - - - private String createReferralConnectList(LDAPUrl[] urls) { - String connectList = ""; + + /** + * Check for "ldap(s):///" referrals (no host:port) and replace them with + * "ldap(s)://currentHost:currentPort". + */ + private void adjustReferrals(LDAPUrl[] urls) { String host = null; int port =0; @@ -5029,33 +4965,19 @@ public class LDAPConnection host = urls[i].getHost(); port = urls[i].getPort(); if ( (host == null) || (host.length() < 1) ) { - // If no host:port was specified, use the latest (hop-wise) parameters + // If no host:port was specified, use the latest (hop-wise) parameters host = getHost(); port = getPort(); + urls[i] = new LDAPUrl (host, port, + urls[i].getDN(), + urls[i].getAttributeArray(), + urls[i].getScope(), + urls[i].getFilter(), + urls[i].isSecure()); } - connectList += (i==0 ? "" : " ") + host+":"+port; } - - return (connectList.length() == 0) ? null : connectList; } - private LDAPUrl findReferralURL(LDAPConnection ldc, LDAPUrl[] urls) { - String connHost = ldc.getHost(); - int connPort = ldc.getPort(); - for (int i = 0; i < urls.length; i++) { - if (urls[i].getHost() == null || urls[i].getHost().length() < 1) { - // No host:port specified, compare with the latest (hop-wise) parameters - if (connHost.equals(getHost()) && connPort == getPort()) { - return urls[i]; - } - } - else if (connHost.equals(urls[i].getHost()) && connPort == urls[i].getPort()) { - return urls[i]; - } - } - return null; - } - /** * Establish the LDAPConnection to the referred server. This one is used * for bind operation only since we need to keep this new connection for @@ -5074,19 +4996,28 @@ public class LDAPConnection throw e; } - String connectList = - createReferralConnectList(e.getURLs()); + LDAPUrl[] refList = e.getURLs(); + // If there are no referrals (because the server isn't set up for // them), give up here - if (connectList == null) { + if (refList == null) { throw new LDAPException("No target URL in referral", LDAPException.NO_RESULTS_RETURNED); } + adjustReferrals(refList); + + LDAPConnection connection = referralConnect(refList, cons); + + // which one did we connect to... + LDAPUrl refURL = connection.m_connMgr.getLDAPUrl(); + + String refDN = refURL.getDN(); + if ((refDN == null) || (refDN.equals(""))) { + refDN = m_boundDN; + } - LDAPConnection connection = null; - connection = prepareReferral(connectList, cons); try { - connection.authenticate(m_protocolVersion, m_boundDN, m_boundPasswd); + connection.authenticate(m_protocolVersion, refDN, m_boundPasswd); } catch (LDAPException authEx) { // Disconnect needed to terminate the LDAPConnThread @@ -5115,71 +5046,110 @@ public class LDAPConnection Vector results ) throws LDAPException { - if (cons.getHopLimit() <= 0) { - throw new LDAPException("exceed hop limit", - e.getLDAPResultCode(), - e.getLDAPErrorMessage()); - } - if (!cons.getReferrals()) { - if (ops == JDAPProtocolOp.SEARCH_REQUEST) { - LDAPSearchResults res = new LDAPSearchResults(); - res.add(e); - results.addElement(res); - return; - } else { - throw e; - } - } - - LDAPUrl urls[] = e.getURLs(); - // If there are no referrals (because the server isn't configured to - // return one), give up here - if ( urls == null || urls.length == 0) { - return; - } - - LDAPUrl referralURL = null; + LDAPUrl refURL = null; LDAPConnection connection = null; + + try { - if (m_referralConnection != null && m_referralConnection.isConnected()) { - referralURL = findReferralURL(m_referralConnection, urls); - } - if (referralURL != null) { - connection = m_referralConnection; - } - else { - String connectList = createReferralConnectList(urls); - connection = prepareReferral( connectList, cons ); + if (cons.getHopLimit() <= 0) { + throw new LDAPException("exceed hop limit", + e.getLDAPResultCode(), + e.getLDAPErrorMessage()); + } + if (!cons.getReferrals()) { + if (ops == JDAPProtocolOp.SEARCH_REQUEST) { + LDAPSearchResults res = new LDAPSearchResults(); + res.add(e); + results.addElement(res); + return; + } else { + throw e; + } + } + + LDAPUrl urls[] = e.getURLs(); + // If there are no referrals (because the server isn't configured to + // return one), give up here + if ( urls == null || urls.length == 0) { + return; + } + adjustReferrals(urls); - // which one did we connect to... - referralURL = findReferralURL(connection, urls); - - // Authenticate - referralRebind(connection, cons); - } + // Check if we can use m_referralConnection to follow this referral + if (m_referralConnection != null && m_referralConnection.isConnected()) { + String refHost = m_referralConnection.getHost(); + int refPort = m_referralConnection.getPort(); + try { + // Compare ipAddr:port for each referral with the m_referralConnection + String refAddr = InetAddress.getByName(refHost).getHostAddress(); + for (int i = 0; i < urls.length; i++) { + String urlHost = urls[i].getHost(); + int urlPort = urls[i].getPort(); + String urlAddr = InetAddress.getByName(urlHost).getHostAddress(); + if (refAddr == urlAddr && refPort == urlPort) { + refURL = urls[i]; + break; + } + } + } + catch (UnknownHostException ex) { + // Compare host names rather than ip addr + for (int i = 0; i < urls.length; i++) { + if (refHost == urls[i].getHost() && + refPort == urls[i].getPort()) { + refURL = urls[i]; + break; + } + } + } + } - String newDN = referralURL.getDN(); - String DN = null; - if ((newDN == null) || (newDN.equals(""))) { - DN = dn; - } else { - DN = newDN; - } - // If this was a one-level search, and a direct subordinate - // has a referral, there will be a "?base" in the URL, and - // we have to rewrite the scope from one to base - if ( referralURL.getUrl().indexOf("?base") > -1 ) { - scope = SCOPE_BASE; - } + if (refURL != null) { + connection = m_referralConnection; + } + else { + connection = referralConnect( urls, cons ); + + // which one did we connect to... + refURL = connection.m_connMgr.getLDAPUrl(); + + // Authenticate + referralRebind(connection, cons); + } - LDAPSearchConstraints newcons = (LDAPSearchConstraints)cons.clone(); - newcons.setHopLimit( cons.getHopLimit()-1 ); + String newDN = refURL.getDN(); + String DN = null; + if ((newDN == null) || (newDN.equals(""))) { + DN = dn; + } else { + DN = newDN; + } + // If this was a one-level search, and a direct subordinate + // has a referral, there will be a "?base" in the URL, and + // we have to rewrite the scope from one to base + if ( refURL.getUrl().indexOf("?base") > -1 ) { + scope = SCOPE_BASE; + } - performReferrals(connection, newcons, ops, DN, scope, filter, - types, attrsOnly, mods, entry, attr, results); + LDAPSearchConstraints newcons = (LDAPSearchConstraints)cons.clone(); + newcons.setHopLimit( cons.getHopLimit()-1 ); + + referralOperation(connection, newcons, ops, DN, scope, filter, + types, attrsOnly, mods, entry, attr, results); + + } + catch (LDAPException ex) { + if (refURL != null) { + ex.setExtraMessage("Failed to follow referral to " + refURL); + } + else { + ex.setExtraMessage("Failed to follow referral"); + } + throw ex; + } } - void performReferrals(LDAPConnection connection, + void referralOperation(LDAPConnection connection, LDAPConstraints cons, int ops, String dn, int scope, String filter, String types[], boolean attrsOnly, LDAPModification mods[], LDAPEntry entry, @@ -5263,9 +5233,9 @@ public class LDAPConnection if ( u == null || u.length == 0) { return null; } - - String connectList = createReferralConnectList(u); - LDAPConnection connection = prepareReferral( connectList, cons); + adjustReferrals(u); + + LDAPConnection connection = referralConnect( u, cons); referralRebind(connection, cons); LDAPExtendedOperation results = connection.extendedOperation( op ); @@ -5275,56 +5245,57 @@ public class LDAPConnection } /** - * Creates and returns a new LDAPConnection object that - * contains the same information as the current connection, including: + * Returns a new LDAPConnection object that shares + * the physical connection to the LDAP server but has its own state. + * + * The returned LDAPConnection object contains the same + * state as the current connection, including: *

*

* @return the LDAPconnection object representing the * new object. */ public synchronized Object clone() { + + LDAPConnection c = null; + try { - LDAPConnection c = (LDAPConnection)super.clone(); - - if (!isConnected()) { - this.internalBind(m_defaultConstraints); + if (m_thread != null) { + checkConnection(/*rebind=*/true); } - + } + catch (LDAPException ignore) {} + + try { + c = (LDAPConnection)super.clone(); c.m_defaultConstraints = (LDAPSearchConstraints)m_defaultConstraints.clone(); c.m_responseListeners = null; - c.m_searchListeners = null; - c.m_bound = this.m_bound; - c.m_connMgr = m_connMgr; - c.m_connSetupDelay = m_connSetupDelay; - c.m_boundDN = this.m_boundDN; - c.m_boundPasswd = this.m_boundPasswd; - c.m_prevBoundDN = this.m_prevBoundDN; - c.m_prevBoundPasswd = this.m_prevBoundPasswd; - c.m_anonymousBound = this.m_anonymousBound; - c.setCache(this.m_cache); // increments cache reference cnt - c.m_factory = this.m_factory; - c.m_thread = this.m_thread; /* share current connection thread */ + c.m_searchListeners = null; + c.m_properties = (Hashtable)m_properties.clone(); + c.m_responseControlTable = new Hashtable(); - synchronized (m_threadConnTable) { - Vector v = (Vector)m_threadConnTable.get(this.m_thread); - if (v != null) { - v.addElement(c); - } else { - printDebug("Failed to clone"); - return null; - } + if (c.m_cache != null) { + c.m_cache.addReference(); } - c.m_thread.register(c); - return c; - } catch (Exception e) { - } - return null; + if (isConnected()) { + /* share current connection thread */ + c.m_thread.register(c); + } + else { + c.m_thread = null; + c.m_connMgr = null; + } + + } catch (Exception ignore) {} + + return c; } /** @@ -5389,11 +5360,9 @@ public class LDAPConnection int cloneCnt = (m_thread == null) ? 0 : m_thread.getClientCount(); StringBuffer sb = new StringBuffer("LDAPConnection {"); //url - sb.append((m_factory == null ? "ldap" : "ldaps")); - sb.append("://"); - sb.append(getHost()); - sb.append(":"); - sb.append(getPort()); + if (m_connMgr != null) { + sb.append(m_connMgr.getLDAPUrl().getServerUrl()); + } // clone count if (cloneCnt > 1) { sb.append(" ("); @@ -5412,7 +5381,27 @@ public class LDAPConnection return sb.toString(); } - + + /** + * A helper class for collecting response controls. Used as a value + * in m_responseControlTable + */ + class ResponseControls { + int msgID; + Vector ctrls; + + public ResponseControls(int msgID, LDAPControl[] ctrls) { + this.msgID = msgID; + this.ctrls = new Vector(); + this.ctrls.addElement(ctrls); + } + + void addControls(LDAPControl[] ctrls) { + this.ctrls.addElement(ctrls); + } + } + + /** * Prints out the LDAP Java SDK version and the highest LDAP * protocol version supported by the SDK. To view this diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPConstraints.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPConstraints.java index 7e0a000205a..baaa495a512 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPConstraints.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPConstraints.java @@ -360,30 +360,31 @@ public class LDAPConstraints implements Cloneable, java.io.Serializable { /** * Makes a copy of an existing set of constraints. - * @returns a copy of an existing set of constraints + * @return a copy of an existing set of constraints */ public Object clone() { - LDAPConstraints o = new LDAPConstraints(); + try { + LDAPConstraints o = (LDAPConstraints) super.clone(); - o.m_time_limit = this.m_time_limit; - o.referrals = this.referrals; - o.m_bind_proc = this.m_bind_proc; - o.m_rebind_proc = this.m_rebind_proc; - o.m_hop_limit = this.m_hop_limit; - if ( (this.m_clientControls != null) && - (this.m_clientControls.length > 0) ) { - o.m_clientControls = new LDAPControl[this.m_clientControls.length]; - for( int i = 0; i < this.m_clientControls.length; i++ ) - o.m_clientControls[i] = - (LDAPControl)this.m_clientControls[i].clone(); + if ( (this.m_clientControls != null) && + (this.m_clientControls.length > 0) ) { + o.m_clientControls = new LDAPControl[this.m_clientControls.length]; + for( int i = 0; i < this.m_clientControls.length; i++ ) + o.m_clientControls[i] = + (LDAPControl)this.m_clientControls[i].clone(); + } + if ( (this.m_serverControls != null) && + (this.m_serverControls.length > 0) ) { + o.m_serverControls = new LDAPControl[this.m_serverControls.length]; + for( int i = 0; i < this.m_serverControls.length; i++ ) + o.m_serverControls[i] = + (LDAPControl)this.m_serverControls[i].clone(); + } + return o; } - if ( (this.m_serverControls != null) && - (this.m_serverControls.length > 0) ) { - o.m_serverControls = new LDAPControl[this.m_serverControls.length]; - for( int i = 0; i < this.m_serverControls.length; i++ ) - o.m_serverControls[i] = - (LDAPControl)this.m_serverControls[i].clone(); + catch (CloneNotSupportedException ex) { + // shold never happen, the class is Cloneable + return null; } - return o; } } diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPException.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPException.java index ade17e6f330..d28363be4c6 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPException.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPException.java @@ -123,6 +123,7 @@ import java.io.*; * 95 MORE_RESULTS_TO_RETURN * 96 CLIENT_LOOP * 97 REFERRAL_LIMIT_EXCEEDED + * 112 TLS_NOT_SUPPORTED (LDAP v3) * *

* @@ -132,7 +133,7 @@ import java.io.*; public class LDAPException extends java.lang.Exception implements java.io.Serializable { - static final long serialVersionUID = -9215007872184847924L; + static final long serialVersionUID = -9215007872184847925L; /** * (0) The operation completed successfully. @@ -556,11 +557,21 @@ public class LDAPException extends java.lang.Exception */ public final static int REFERRAL_LIMIT_EXCEEDED = 0x61; + /** + * (112) The socket factory of the connection is not capable + * of initiating a TLS session. + *

+ * + * @see netscape.ldap.LDAPConnection#startTLS + */ + public final static int TLS_NOT_SUPPORTED = 0x70; + /** * Internal variables */ private int resultCode = -1; private String errorMessage = null; + private String extraMessage = null; private String matchedDN = null; private Locale m_locale = Locale.getDefault(); private static Hashtable cacheResource = new Hashtable(); @@ -733,17 +744,32 @@ public class LDAPException extends java.lang.Exception public String getLDAPErrorMessage () { return errorMessage; } + + + /** + * Adds additional explanation to the error message + */ + void setExtraMessage (String msg) { + if (extraMessage == null) { + extraMessage = msg; + } + else { + extraMessage += "; " + msg; + } + } /** * Returns the maximal subset of a DN which could be matched by the - * server, if the server returned one of the following errors: + * server. + * + * The method should be used if the server returned one of the + * following errors: *

- * * For example, if the DN cn=Babs Jensen, o=People, c=Airius.com * could not be found by the DN o=People, c=Airius.com * could be found, the matched DN is @@ -791,14 +817,19 @@ public class LDAPException extends java.lang.Exception */ public String toString() { String str = super.toString() + " (" + resultCode + ")" ; - if ( (errorMessage != null) && (errorMessage.length() > 0) ) + if ( (errorMessage != null) && (errorMessage.length() > 0) ) { str += "; " + errorMessage; - if ( (matchedDN != null) && (matchedDN.length() > 0) ) + } + if ( (matchedDN != null) && (matchedDN.length() > 0) ) { str += "; matchedDN = " + matchedDN; - String errorStr = null; - if (((errorStr = errorCodeToString(m_locale)) != null) && - (errorStr.length() > 0)) - str += "; " + errorStr; + } + String errorStr = errorCodeToString(m_locale); + if ((errorStr != null) && (errorStr.length() > 0)) { + str += "; " + errorStr; + } + if (extraMessage != null) { + str += "; " + extraMessage; + } return str; } diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPMessageQueue.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPMessageQueue.java index ae49fa86db4..d7e3bd53ddb 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPMessageQueue.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPMessageQueue.java @@ -35,7 +35,7 @@ import java.util.Vector; */ class LDAPMessageQueue implements java.io.Serializable { - static final long serialVersionUID = -7163312406176592277L; + static final long serialVersionUID = -7163312406176592278L; /** * Request entry encapsulates request parameters @@ -88,6 +88,27 @@ class LDAPMessageQueue implements java.io.Serializable { return m_asynchOp; } + /** + * Blocks until a response is available. + * Used by LDAPConnection.sendRequest (synch ops) to test if the server + * is really available after a request had been sent. + * @exception LDAPException Network error exception + * @exception LDAPInterruptedException The invoking thread was interrupted + */ + synchronized void waitFirstMessage () throws LDAPException { + + while(m_requestList.size() != 0 && m_exception == null && m_messageQueue.size() == 0) { + waitForMessage(); + } + + // Network exception occurred ? + if (m_exception != null) { + LDAPException ex = m_exception; + m_exception = null; + throw ex; + } + } + /** * Blocks until a response is available or until all operations * associated with the object have completed or been canceled. @@ -287,7 +308,7 @@ class LDAPMessageQueue implements java.io.Serializable { // Mark conn as bound for asych bind operations if (isAsynchOp() && msg.getType() == msg.BIND_RESPONSE) { if (((LDAPResponse) msg).getResultCode() == 0) { - getConnection(msg.getMessageID()).markConnAsBound(); + getConnection(msg.getMessageID()).setBound(true); } } diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSSLSocketFactory.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSSLSocketFactory.java index f5c95a6f5e1..8bfd22d2aba 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSSLSocketFactory.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSSLSocketFactory.java @@ -178,7 +178,7 @@ public class LDAPSSLSocketFactory /** - * This method is currently not implemented. + * This method is currently not implemented. * Enables client authentication for an application that uses * an external (file-based) certificate database. * Call this method before you call makeSocket. diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSaslBind.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSaslBind.java index 22963f9c372..74a27eb56f4 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSaslBind.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSaslBind.java @@ -225,7 +225,7 @@ public class LDAPSaslBind implements LDAPBind, java.io.Serializable { _saslClient, className, "getOutputStream", args, argNames); ldc.setOutputStream(os); - ldc.markConnAsBound(); + ldc.setBound(true); } catch (LDAPException e) { throw e; } catch (Exception e) { diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSearchConstraints.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSearchConstraints.java index 34d0822d482..e280cde58c7 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSearchConstraints.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSearchConstraints.java @@ -34,11 +34,25 @@ package netscape.ldap; public class LDAPSearchConstraints extends LDAPConstraints implements Cloneable { + + // Constants for behavior when a search continuation reference cannot + // be followed + /** + * Continue processing if there is an error following a search continuation + * reference + */ + public static final int REFERRAL_ERROR_CONTINUE = 0; + /** + * Throw exception if there is an error following a search continuation + * reference + */ + public static final int REFERRAL_ERROR_EXCEPTION = 1; private int deref; private int maxRes; private int batch; private int serverTimeLimit; private int maxBacklog = 100; + private int referralErrors = REFERRAL_ERROR_CONTINUE; /** * Constructs an LDAPSearchConstraints object that specifies @@ -81,8 +95,10 @@ public class LDAPSearchConstraints extends LDAPConstraints * @param hop_limit maximum number of referrals to follow in a * sequence when attempting to resolve a request * @see netscape.ldap.LDAPConnection#setOption(int, java.lang.Object) - * @see netscape.ldap.LDAPConnection#search(netscape.ldap.LDAPUrl, netscape.ldap.LDAPSearchConstraints) - * @see netscape.ldap.LDAPConnection#search(java.lang.String, int, java.lang.String, java.lang.String[], boolean, netscape.ldap.LDAPSearchConstraints) + * @see netscape.ldap.LDAPConnection#search(netscape.ldap.LDAPUrl, +netscape.ldap.LDAPSearchConstraints) + * @see netscape.ldap.LDAPConnection#search(java.lang.String, int, java.lang.String, +java.lang.String[], boolean, netscape.ldap.LDAPSearchConstraints) */ public LDAPSearchConstraints( int msLimit, int dereference, int maxResults, boolean doReferrals, int batchSize, @@ -125,8 +141,10 @@ public class LDAPSearchConstraints extends LDAPConstraints * @param hop_limit maximum number of referrals to follow in a * sequence when attempting to resolve a request * @see netscape.ldap.LDAPConnection#setOption(int, java.lang.Object) - * @see netscape.ldap.LDAPConnection#search(netscape.ldap.LDAPUrl, netscape.ldap.LDAPSearchConstraints) - * @see netscape.ldap.LDAPConnection#search(java.lang.String, int, java.lang.String, java.lang.String[], boolean, netscape.ldap.LDAPSearchConstraints) + * @see netscape.ldap.LDAPConnection#search(netscape.ldap.LDAPUrl, +netscape.ldap.LDAPSearchConstraints) + * @see netscape.ldap.LDAPConnection#search(java.lang.String, int, java.lang.String, +java.lang.String[], boolean, netscape.ldap.LDAPSearchConstraints) */ public LDAPSearchConstraints( int msLimit, int timeLimit, int dereference, @@ -171,8 +189,10 @@ public class LDAPSearchConstraints extends LDAPConstraints * @param hop_limit maximum number of referrals to follow in a * sequence when attempting to resolve a request * @see netscape.ldap.LDAPConnection#setOption(int, java.lang.Object) - * @see netscape.ldap.LDAPConnection#search(netscape.ldap.LDAPUrl, netscape.ldap.LDAPSearchConstraints) - * @see netscape.ldap.LDAPConnection#search(java.lang.String, int, java.lang.String, java.lang.String[], boolean, netscape.ldap.LDAPSearchConstraints) + * @see netscape.ldap.LDAPConnection#search(netscape.ldap.LDAPUrl, +netscape.ldap.LDAPSearchConstraints) + * @see netscape.ldap.LDAPConnection#search(java.lang.String, int, java.lang.String, +java.lang.String[], boolean, netscape.ldap.LDAPSearchConstraints) */ public LDAPSearchConstraints( int msLimit, int timeLimit, int dereference, @@ -296,51 +316,101 @@ public class LDAPSearchConstraints extends LDAPConstraints return maxBacklog; } + /** + * Reports if errors when following search continuation references are + * to cause processing of the remaining results to be aborted. + *

+ * If an LDAP server does not contain an entry at the base DN for a search, + * it may be configured to return a referral. If it contains an entry at + * the base DN of a subtree search, one or more of the child entries may + * contain search continuation references. The search continuation + * references are returned to the client, which may follow them by issuing + * a search request to the host indicated in the search reference. + *

+ * If the LDAPConnection object has been configured to follow + * referrals automatically, it may fail when issuing a search request to + * the host indicated in a search reference, e.g. because there is no + * entry there, because it does not have credentials, because it does not + * have sufficient permissions, etc. If the client aborts evaluation of the + * search results (obtained through LDAPSearchResults) when a + * search reference cannot be followed, any remaining results are discarded. + *

+ * Up to version 4.17 of the Java LDAP SDK, the SDK printed an error + * message but continued to process the remaining search results and search + * continuation references. + *

+ * As of SDK version 4.17, the default behavior is still to continue + * processing any remaining search results and search continuation + * references if there is an error following a referral, but the behavior + * may be changed with setReferralErrors to throw an exception + * instead. + * + * @return REFERRAL_ERROR_CONTINUE if remaining results are + * to be processed when there is an error following a search continuation + * reference, REFERRAL_ERROR_EXCEPTION if such an error is to + * cause an LDAPException. + * + * @see netscape.ldap.LDAPConstraints#setReferrals + * @since LDAPJDK 4.17 + */ + public int getReferralErrors() { + return referralErrors; + } + + /** + * Specifies if errors when following search continuation references are + * to cause processing of the remaining results to be aborted. + *

+ * If an LDAP server does not contain an entry at the base DN for a search, + * it may be configured to return a referral. If it contains an entry at + * the base DN of a subtree search, one or more of the child entries may + * contain search continuation references. The search continuation + * references are returned to the client, which may follow them by issuing + * a search request to the host indicated in the search reference. + *

+ * If the LDAPConnection object has been configured to follow + * referrals automatically, it may fail when issuing a search request to + * the host indicated in a search reference, e.g. because there is no + * entry there, because it does not have credentials, because it does not + * have sufficient permissions, etc. If the client aborts evaluation of the + * search results (obtained through LDAPSearchResults) when a + * search reference cannot be followed, any remaining results are discarded. + *

+ * Up to version 4.17 of the Java LDAP SDK, the SDK printed an error + * message but continued to process the remaining search results and search + * continuation references. + *

+ * As of SDK version 4.17, the default behavior is still to continue + * processing any remaining search results and search continuation + * references if there is an error following a referral, but the behavior + * may be changed with setReferralErrors to throw an exception + * instead. + * + * @param errorBehavior Either REFERRAL_ERROR_CONTINUE if + * remaining results are to be processed when there is an error following a + * search continuation reference or REFERRAL_ERROR_EXCEPTION + * if such an error is to cause an LDAPException. + * + * @see netscape.ldap.LDAPSearchConstraints#getReferralErrors + * @see netscape.ldap.LDAPSearchResults#next + * @see netscape.ldap.LDAPSearchResults#nextElement + * @since LDAPJDK 4.17 + */ + public void setReferralErrors(int errorBehavior) { + if ( (errorBehavior != REFERRAL_ERROR_CONTINUE) && + (errorBehavior != REFERRAL_ERROR_EXCEPTION) ) { + throw new IllegalArgumentException( "Invalid error behavior: " + + errorBehavior ); + } + referralErrors = errorBehavior; + } + /** * Makes a copy of an existing set of search constraints. * @return a copy of an existing set of search constraints. */ public Object clone() { - LDAPSearchConstraints o = new LDAPSearchConstraints(); - - o.serverTimeLimit = this.serverTimeLimit; - o.deref = this.deref; - o.maxRes = this.maxRes; - o.batch = this.batch; - o.maxBacklog = this.maxBacklog; - - o.setHopLimit(this.getHopLimit()); - o.setReferrals(this.getReferrals()); - o.setTimeLimit(this.getTimeLimit()); - - if (this.getBindProc() != null) { - o.setBindProc(this.getBindProc()); - } else { - o.setRebindProc(this.getRebindProc()); - } - - LDAPControl[] tClientControls = this.getClientControls(); - LDAPControl[] oClientControls; - - if ( (tClientControls != null) && - (tClientControls.length > 0) ) { - oClientControls = new LDAPControl[tClientControls.length]; - for( int i = 0; i < tClientControls.length; i++ ) - oClientControls[i] = (LDAPControl)tClientControls[i].clone(); - o.setClientControls(oClientControls); - } - - LDAPControl[] tServerControls = this.getServerControls(); - LDAPControl[] oServerControls; - - if ( (tServerControls != null) && - (tServerControls.length > 0) ) { - oServerControls = new LDAPControl[tServerControls.length]; - for( int i = 0; i < tServerControls.length; i++ ) - oServerControls[i] = (LDAPControl)tServerControls[i].clone(); - o.setServerControls(oServerControls); - } - + LDAPSearchConstraints o = (LDAPSearchConstraints) super.clone(); return o; } @@ -356,7 +426,8 @@ public class LDAPSearchConstraints extends LDAPConstraints sb.append("server time limit " + serverTimeLimit + ", "); sb.append("aliases " + deref + ", "); sb.append("batch size " + batch + ", "); - sb.append("max backlog " + maxBacklog); + sb.append("max backlog " + maxBacklog + ", "); + sb.append("referralErrors " + referralErrors); sb.append('}'); return sb.toString(); diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSearchResults.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSearchResults.java index 9ff3e352108..9a09c7b0480 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSearchResults.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSearchResults.java @@ -549,7 +549,7 @@ public class LDAPSearchResults implements Enumeration, java.io.Serializable { currBase, currScope, currFilter, currAttrs, currAttrsOnly); } catch (LDAPException e) { - System.err.println("LDAPSearchResults.fetchResult: "+e); + add(e); } finally { currConn.releaseSearchListener(resultSource); } @@ -567,7 +567,7 @@ public class LDAPSearchResults implements Enumeration, java.io.Serializable { currConn.checkSearchMsg(this, msg, currCons, currBase, currScope, currFilter, currAttrs, currAttrsOnly); } catch (LDAPException e) { - System.err.println("Exception: "+e); + add(e); } } } diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPTLSSocketFactory.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPTLSSocketFactory.java new file mode 100644 index 00000000000..553a9598796 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPTLSSocketFactory.java @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 2002 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ +package netscape.ldap; + +import java.util.*; +import java.io.*; +import java.net.*; + +/** + * A socket factory interface for supporting the start TLS LDAPv3 + * extension (RFC 2830). + *

+ * + * @version 1.0 + * @since LDAPJDK 4.17 + */ +public interface LDAPTLSSocketFactory extends LDAPSocketFactory { + /** + * Creates an SSL socket layered over an existing socket. + * + * Used for the start TLS operations (RFC2830). + * + * @param s An existing non-SSL socket + * @return A SSL socket layered over the input socket + * @exception LDAPException on error creating socket + * @see LDAPConnection#startTLS + */ + public Socket makeSocket(Socket s) throws LDAPException; +} diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPUrl.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPUrl.java index d0620e4046f..d0a8ba29e46 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPUrl.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPUrl.java @@ -24,7 +24,6 @@ package netscape.ldap; import java.util.*; import java.io.*; import java.net.MalformedURLException; -import netscape.ldap.factory.*; /** * Represents an LDAP URL. The complete specification for LDAP URLs is in @@ -81,7 +80,7 @@ import netscape.ldap.factory.*; */ public class LDAPUrl implements java.io.Serializable { - static final long serialVersionUID = -3245440798565713640L; + static final long serialVersionUID = -3245440798565713641L; public static String defaultFilter = "(objectClass=*)"; private String m_hostName; @@ -371,7 +370,7 @@ public class LDAPUrl implements java.io.Serializable { } else m_attributes = null; - StringBuffer url = new StringBuffer (secure ? "LDAPS://" :"LDAP://"); + StringBuffer url = new StringBuffer (secure ? "ldaps://" :"ldap://"); if (host != null) { url.append (host); @@ -444,6 +443,15 @@ public class LDAPUrl implements java.io.Serializable { return m_DN; } + /** + * Return the server part of the ldap url, ldap://host:port + * @return server url. + */ + String getServerUrl() { + return (m_secure ? "ldaps://" : "ldap://") + + m_hostName + ":" + m_portNumber; + } + /** * Return the collection of attributes specified in the URL, or null * for "every attribute" @@ -545,10 +553,10 @@ public class LDAPUrl implements java.io.Serializable { if (m_factory == null) { - // No factory explicity set, try to determine + // No factory explicitly set, try to determine // the default one. try { - // First try iPlanet JSSSocketFactory + // First try Mozilla JSSSocketFactory Class c = Class.forName("netscape.ldap.factory.JSSSocketFactory"); m_factory = (LDAPSocketFactory) c.newInstance(); } @@ -561,7 +569,8 @@ public class LDAPUrl implements java.io.Serializable { try { // then try Sun JSSESocketFactory - m_factory = new JSSESocketFactory(null); + Class c = Class.forName("netscape.ldap.factory.JSSESocketFactory"); + m_factory = (LDAPSocketFactory) c.newInstance(); } catch (Throwable e) { } diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/errors/ErrorCodes.props b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/errors/ErrorCodes.props index ad480130a47..4f13931a46e 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/errors/ErrorCodes.props +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/errors/ErrorCodes.props @@ -49,3 +49,4 @@ 95=More results to return 96=Client detected loop 97=Referral hop limit exceeded +112=Cannot start TLS diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/errors/ErrorCodes_de.props b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/errors/ErrorCodes_de.props index fed73a874d4..6786f595d45 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/errors/ErrorCodes_de.props +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/errors/ErrorCodes_de.props @@ -49,3 +49,4 @@ 95=Es stehen noch weitere Ergebnisse zur Verfügung 96=Client stellte Schleife fest 97=Beschränkung für Zwischenschritte bei Referenz überschritten +112=TLS kann nicht activiert werden diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/errors/ErrorCodes_fr.props b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/errors/ErrorCodes_fr.props index 24573d6b7f4..de9524ab855 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/errors/ErrorCodes_fr.props +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/errors/ErrorCodes_fr.props @@ -49,3 +49,4 @@ 95=Autres résultats à venir 96=Client a détecté la boucle 97=Nombre de rebonds sur référence trop grand +112=Impossible d'activer TLS diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/errors/ErrorCodes_ja.props b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/errors/ErrorCodes_ja.props index f87bea7013e..d8a0c84f10d 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/errors/ErrorCodes_ja.props +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/errors/ErrorCodes_ja.props @@ -49,3 +49,4 @@ 95=\u3055\u3089\u306B\u7D50\u679C\u304C\u8FD4\u3055\u308C\u307E\u3059\u3002 96=\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8\u304C\u30EB\u30FC\u30D7\u3092\u691C\u51FA\u3057\u307E\u3057\u305F\u3002 97=\u30EC\u30D5\u30A7\u30E9\u30EB \u30DB\u30C3\u30D7 \u30EA\u30DF\u30C3\u30C8\u3092\u8D85\u904E\u3057\u307E\u3057\u305F\u3002 +112=TLS\u3092\u-6a75\u59cb\u3067\u304d\u307e\u305b\u3093\u3002 diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/factory/JSSESocketFactory.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/factory/JSSESocketFactory.java index 78a2c651645..4e1125c6fa3 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/factory/JSSESocketFactory.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/factory/JSSESocketFactory.java @@ -37,16 +37,23 @@ import netscape.ldap.*; * @see LDAPSocketFactory * @see LDAPConnection#LDAPConnection(netscape.ldap.LDAPSocketFactory) */ -public class JSSESocketFactory - implements LDAPSocketFactory, java.io.Serializable { +public class JSSESocketFactory implements LDAPTLSSocketFactory, + java.io.Serializable { - static final long serialVersionUID = 6834205777733266609L; + static final long serialVersionUID = 6834205777733266610L; // Optional explicit cipher suites to use - private String[] suites; + protected String[] suites; // The socket factory - private SSLSocketFactory factory; + protected SSLSocketFactory factory; + /** + * Default factory constructor + */ + public JSSESocketFactory() { + this(null, null); + } + /** * Factory constructor that uses the default JSSE SSLSocketFactory * @@ -73,7 +80,7 @@ public class JSSESocketFactory } /** - * Creates an SSL socket + * Creates an SSL socket. * * @param host Host name or IP address of SSL server * @param port Port numbers of SSL server @@ -98,16 +105,49 @@ public class JSSESocketFactory sock.startHandshake(); } catch (UnknownHostException e) { - throw new LDAPException("SSL connection to " + host + - ":" + port + ", " + e.getMessage(), + throw new LDAPException("JSSESocketFactory.makeSocket - Unknown host: " + host, LDAPException.CONNECT_ERROR); } catch (IOException f) { - throw new LDAPException("SSL connection to " + host + - ":" + port + ", " + f.getMessage(), + throw new LDAPException("JSSESocketFactory.makeSocket " + + host + ":" + port + ", " + f.getMessage(), LDAPException.CONNECT_ERROR); } return sock; } + + /** + * Creates an SSL socket layered over an existing socket. + * + * Used for the startTLS implementation (RFC2830). + * + * @param s An existing non-SSL socket + * @return A SSL socket layered over the input socket + * @exception LDAPException on error creating socket + * @since LDAPJDK 4.17 + */ + public Socket makeSocket(Socket s) + throws LDAPException { + + SSLSocket sock = null; + String host = s.getInetAddress().getHostName(); + int port = s.getPort(); + + try { + sock = (SSLSocket)factory.createSocket(s, host, port, /*autoClose=*/ true); + + if (suites != null) { + sock.setEnabledCipherSuites(suites); + } + + sock.startHandshake(); + + } catch (IOException f) { + throw new LDAPException("JSSESocketFactory - start TLS, " + f.getMessage(), + LDAPException.TLS_NOT_SUPPORTED); + } + + return sock; + } } diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/factory/JSSSocketFactory.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/factory/JSSSocketFactory.java new file mode 100644 index 00000000000..ab7c9e7cd3a --- /dev/null +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/factory/JSSSocketFactory.java @@ -0,0 +1,291 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ +package netscape.ldap.factory; + +import java.net.*; +import java.io.*; + +import netscape.ldap.*; + +import org.mozilla.jss.ssl.SSLSocket; +import org.mozilla.jss.ssl.SSLCertificateApprovalCallback.ValidityStatus; +import org.mozilla.jss.ssl.SSLCertificateApprovalCallback; +import org.mozilla.jss.crypto.X509Certificate; +import org.mozilla.jss.crypto.AlreadyInitializedException; +import org.mozilla.jss.CryptoManager; + +/** + * Creates an SSL socket connection to a server, using the Netscape/Mozilla + * JSS package. + * This class implements the LDAPSocketFactory + * interface. + *

+ * NOTE: This class is internal to Netscape and is distributed only with + * Netscape products. + *

+ * By default, the factory uses "secmod.db", "key3.db" and "cert7.db" + * databases in the current directory. If you need to override this default + * setting, then you should call the static initialize method + * before creating the first instance of JSSSocketFactory. + *

+ *

+ *       ...
+ *       JSSSocketFactory.initialize("../certDir");
+ *       LDAPConnection ld = new LDAPConnection(new JSSSocketFactory());
+ *       ...
+ * 
+ * @version 1.1 + * @see LDAPSocketFactory + * @see LDAPConnection#LDAPConnection(netscape.ldap.LDAPSocketFactory) + */ + +public class JSSSocketFactory implements Serializable, + LDAPTLSSocketFactory, + SSLCertificateApprovalCallback +{ + + static final long serialVersionUID = -6926469178017736903L; + + /** + * Default security module database path "secmod.db". + */ + public static final String defaultModDB = "secmod.db"; + + /** + * Default key database path "key3.db". + */ + public static final String defaultKeyDB = "key3.db"; + + /** + * Default certificate database path "cert7.db". + */ + public static final String defaultCertDB = "cert7.db"; + + private static String moddb; + private static String keydb; + private static String certdb; + + + /** + * Constructs a new JSSSocketFactory. + * CryptoManager.initialize(certdbDir) or + * JSSSocketFactory.initialize(certdbDir) must be called once in the + * application before using the object. + * + * @exception LDAPException on initialization error + * @see netscape.ldap.factory.JSSSocketFactory#JSSSocketFactory(java.lang.String) + */ + public JSSSocketFactory() throws LDAPException{ + initialize("."); + } + + /** + * Constructs a new JSSSocketFactory, initializing the + * JSS security system if it has not already been initialized + * + * @param certdbDir The full path, relative or absolute, of the certificate + * database directory + * @exception LDAPException on initialization error + */ + public JSSSocketFactory( String certdbDir ) throws LDAPException{ + initialize( certdbDir ); + } + + /** + * Initialize the JSS security subsystem. + *

+ * This method allows you to override the current directory as the + * default certificate database directory. The directory is expected + * to contain secmod.db, key3.db and + * cert7.db files as the security module database, key database + * and certificate database respectively. + *

+ * The method may be called only once, before the first instance of + * JSSSocketFactory is created. When creating the first + * instance, the constructor will automatically initialize the JSS + * security subsystem using the defaults, unless it is already initialized. + *

+ * @param certdbDir The full path, relative or absolute, of the certificate + * database directory. + * @exception LDAPException on initialization error + * @see netscape.ldap.factory.JSSSocketFactory#JSSSocketFactory(String) + */ + public static void initialize( String certdbDir ) throws LDAPException { + moddb = certdbDir + File.separator + JSSSocketFactory.defaultModDB; + keydb = certdbDir + File.separator + JSSSocketFactory.defaultKeyDB; + certdb = certdbDir + File.separator + JSSSocketFactory.defaultCertDB; + try { + CryptoManager.initialize( certdbDir ); + } catch (AlreadyInitializedException e) { + // This is ok + } catch (Exception e) { + throw new LDAPException("Failed to initialize JSSSocketFactory: " + + e.getMessage(), LDAPException.OTHER); + } + } + + /** + * Initialize the JSS security subsystem. + *

+ * This method allows you to override the default name and location of + * the security module database, key database and certificate databases. + *

+ * The method may be called only once, before the first instance of + * JSSSocketFactory is created. When creating the first + * instance, the constructor will automatically initialize the JSS + * security subsystem using the defaults, unless it is already initialized. + *

+ * @param moddb The full path, relative or absolute, of the security + * module database. + * @param keydb The full path, relative or absolute, of the key database. + * @param keydb The full path, relative or absolute, of the certificate + * database. + * @see netscape.ldap.factory.JSSSocketFactory#JSSSocketFactory + * @see netscape.ldap.factory.JSSSocketFactory#initialize(java.lang.String) + * @exception LDAPException on initialization error + * @deprecated Please call JSSSocketFactory(String certDir) instead + */ + public static void initialize( String moddb, String keydb, String certdb ) + throws LDAPException { + JSSSocketFactory.moddb = moddb; + JSSSocketFactory.keydb = keydb; + JSSSocketFactory.certdb = certdb; + int ind = certdb.lastIndexOf( File.separator ); + String certdbDir = ( ind == 0 ) ? File.separator : + ( ind > 0 ) ? certdb.substring( 0, ind ) : "."; + initialize( certdbDir ); + } + + /** + * Returns the full path of the security module + * database + * + * @return The full path, relative or absolute, of the security module database + */ + public static String getModDB() { + return moddb; + } + + /** + * Returns the full path of the key database + * + * @return The full path, relative or absolute, of the key database + */ + public static String getKeyDB() { + return keydb; + } + + /** + * Returns the full path of the certificate database + * + * @return The full path, relative or absolute, of the certificate database + */ + public static String getCertDB() { + return certdb; + } + + /** + * Creates an SSL socket + * + * @param host Host name or IP address of SSL server + * @param port Port numbers of SSL server + * @return A socket for an encrypted session + * @exception LDAPException on error creating socket + */ + public Socket makeSocket( String host, int port ) throws LDAPException { + SSLSocket socket = null; + try { + + socket = new SSLSocket( host, // address + port, // port + null, // localAddress + 0, // localPort + this, // certApprovalCallback + null // clientCertSelectionCallback + ); + + socket.forceHandshake(); + + } + catch (UnknownHostException e) { + throw new LDAPException("JSSSocketFactory.makeSocket - Unknown host: " + host, + LDAPException.CONNECT_ERROR); + + } + catch (Exception e) { + throw new LDAPException("JSSSocketFactory.makeSocket " + + host + ":" + port + ", " + e.getMessage(), + LDAPException.CONNECT_ERROR); + } + + return socket; + } + + /** + * The default implementation of the SSLCertificateApprovalCallback + * interface. + *

+ * This default implementation always returns true. If you need to + * verify the server certificate validity, then you should override + * this method. + *

+ * @param serverCert X509 Certificate + * @param status The validity of the server certificate + * @return true, by default we trust the certificate + */ + public boolean approve(X509Certificate serverCert, + ValidityStatus status) { + + return true; + } + + /** + * Creates an SSL socket layered over an existing socket. + * + * Used for the startTLS implementation (RFC2830). + * + * @param s An existing non-SSL socket + * @return A SSL socket layered over the input socket + * @exception LDAPException on error creating socket + * @since LDAPJDK 4.17 + */ + public Socket makeSocket(Socket s) throws LDAPException { + SSLSocket socket = null; + String host = s.getInetAddress().getHostName(); + int port = s.getPort(); + try { + socket = new SSLSocket( s, + host, + this, // certApprovalCallback + null // clientCertSelectionCallback + ); + + socket.forceHandshake(); + + } catch (Exception e) { + throw new LDAPException("JSSSocketFactory - start TLS, " + e.getMessage(), + LDAPException.TLS_NOT_SUPPORTED); + } + + return socket; + } +}