/****************************************************************************** * The MIT License * Copyright (c) 2003 Novell Inc. www.novell.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the Software), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. *******************************************************************************/ // // Novell.Directory.Ldap.LdapConnection.cs // // Author: // Sunil Kumar (Sunilk@novell.com) // // (C) 2003 Novell, Inc (http://www.novell.com) // using System; using Novell.Directory.Ldap; using Novell.Directory.Ldap.Asn1; using Novell.Directory.Ldap.Rfc2251; using Novell.Directory.Ldap.Utilclass; #if !TARGET_JVM using Mono.Security.Protocol.Tls; #endif using System.Security.Cryptography.X509Certificates; namespace Novell.Directory.Ldap { /// The central class that encapsulates the connection /// to a directory server through the Ldap protocol. /// LdapConnection objects are used to perform common Ldap /// operations such as search, modify and add. /// /// In addition, LdapConnection objects allow you to bind to an /// Ldap server, set connection and search constraints, and perform /// several other tasks. /// /// An LdapConnection object is not connected on /// construction and can only be connected to one server at one /// port. Multiple threads may share this single connection, typically /// by cloning the connection object, one for each thread. An /// application may have more than one LdapConnection object, connected /// to the same or different directory servers. /// /// /// public class LdapConnection : System.ICloneable { private void InitBlock() { defSearchCons = new LdapSearchConstraints(); responseCtlSemaphore = new System.Object(); } /// Returns the protocol version uses to authenticate. /// /// 0 is returned if no authentication has been performed. /// /// /// The protol version used for authentication or 0 /// not authenticated. /// /// virtual public int ProtocolVersion { get { BindProperties prop = conn.BindProperties; if (prop == null) { return Ldap_V3; } return prop.ProtocolVersion; } } /// Returns the distinguished name (DN) used for as the bind name during /// the last successful bind operation. null is returned /// if no authentication has been performed or if the bind resulted in /// an aonymous connection. /// /// /// The distinguished name if authenticated; otherwise, null. /// /// virtual public System.String AuthenticationDN { get { BindProperties prop = conn.BindProperties; if (prop == null) { return null; } if (prop.Anonymous) { return null; } return prop.AuthenticationDN; } } /// Returns the method used to authenticate the connection. The return /// value is one of the following: /// /// /// /// /// The method used to authenticate the connection. /// virtual public System.String AuthenticationMethod { get { BindProperties prop = conn.BindProperties; if (prop == null) { return "simple"; } return conn.BindProperties.AuthenticationMethod; } } /// Returns the properties if any specified on binding with a /// SASL mechanism. /// /// Null is returned if no authentication has been performed /// or no authentication Map is present. /// /// /// The bind properties Map Object used for SASL bind or null if /// the connection is not present or not authenticated. /// /// virtual public System.Collections.IDictionary SaslBindProperties { get { BindProperties prop = conn.BindProperties; if (prop == null) { return null; } return conn.BindProperties.SaslBindProperties; } } /// Returns the call back handler if any specified on binding with a /// SASL mechanism. /// /// Null is returned if no authentication has been performed /// or no authentication call back handler is present. /// /// /// The call back handler used for SASL bind or null if the /// object is not present or not authenticated. /// /// virtual public System.Object SaslBindCallbackHandler { get { BindProperties prop = conn.BindProperties; if (prop == null) { return null; } return conn.BindProperties.SaslCallbackHandler; } } /// Returns a copy of the set of constraints associated with this /// connection. These constraints apply to all operations performed /// through this connection (unless a different set of constraints is /// specified when calling an operation method). /// /// /// The set of default contraints that apply to this connection. /// /// /// Sets the constraints that apply to all operations performed through /// this connection (unless a different set of constraints is specified /// when calling an operation method). An LdapSearchConstraints object /// which is passed to this method sets all constraints, while an /// LdapConstraints object passed to this method sets only base constraints. /// /// /// An LdapConstraints or LdapSearchConstraints Object /// containing the contstraint values to set. /// /// /// /// /// /// virtual public LdapConstraints Constraints { get { return (LdapConstraints) (this.defSearchCons).Clone(); } set { // Set all constraints, replace the object with a new one if (value is LdapSearchConstraints) { defSearchCons = (LdapSearchConstraints) value.Clone(); return ; } // We set the constraints this way, so a thread doesn't get an // conconsistant view of the referrals. LdapSearchConstraints newCons = (LdapSearchConstraints) defSearchCons.Clone(); newCons.HopLimit = value.HopLimit; newCons.TimeLimit = value.TimeLimit; newCons.setReferralHandler(value.getReferralHandler()); newCons.ReferralFollowing = value.ReferralFollowing; LdapControl[] lsc = value.getControls(); if (lsc != null) { newCons.setControls(lsc); } System.Collections.Hashtable lp = newCons.Properties; if (lp != null) { newCons.Properties = lp; } defSearchCons = newCons; return ; } } /// Returns the host name of the Ldap server to which the object is or /// was last connected, in the format originally specified. /// /// /// The host name of the Ldap server to which the object last /// connected or null if the object has never connected. /// /// virtual public System.String Host { get { return conn.Host; } } /// Returns the port number of the Ldap server to which the object is or /// was last connected. /// /// /// The port number of the Ldap server to which the object last /// connected or -1 if the object has never connected. /// /// virtual public int Port { get { return conn.Port; } } /// Returns a copy of the set of search constraints associated with this /// connection. These constraints apply to search operations performed /// through this connection (unless a different set of /// constraints is specified when calling the search operation method). /// /// /// The set of default search contraints that apply to /// this connection. /// /// /// /// /// /// virtual public LdapSearchConstraints SearchConstraints { get { return (LdapSearchConstraints) this.defSearchCons.Clone(); } } /// Indicates whther the perform Secure Operation or not /// /// /// /// True if SSL is on /// False if its not on /// public bool SecureSocketLayer { get { return conn.Ssl; } set { conn.Ssl=value; } } /// Indicates whether the object has authenticated to the connected Ldap /// server. /// /// /// True if the object has authenticated; false if it has not /// authenticated. /// /// virtual public bool Bound { get { return conn.Bound; } } /// Indicates whether the connection represented by this object is open /// at this time. /// /// /// True if connection is open; false if the connection is closed. /// virtual public bool Connected { get { return conn.Connected; } } /// Indicatates if the connection is protected by TLS. /// /// /// If startTLS has completed this method returns true. /// If stopTLS has completed or start tls failed, this method returns false. /// /// True if the connection is protected by TLS. /// /// virtual public bool TLS { get { return conn.TLS; } } /// Returns the Server Controls associated with the most recent response /// to a synchronous request on this connection object, or null /// if the latest response contained no Server Controls. The method /// always returns null for asynchronous requests. For asynchronous /// requests, the response controls are available in LdapMessage. /// /// /// The server controls associated with the most recent response /// to a synchronous request or null if the response contains no server /// controls. /// /// /// /// virtual public LdapControl[] ResponseControls { get { if (responseCtls == null) { return null; } // We have to clone the control just in case // we have two client threads that end up retreiving the // same control. LdapControl[] clonedControl = new LdapControl[responseCtls.Length]; // Also note we synchronize access to the local response // control object just in case another message containing controls // comes in from the server while we are busy duplicating // this one. lock (responseCtlSemaphore) { for (int i = 0; i < responseCtls.Length; i++) { clonedControl[i] = (LdapControl) (responseCtls[i]).Clone(); } } // Return the cloned copy. Note we have still left the // control in the local responseCtls variable just in case // somebody requests it again. return clonedControl; } } /// Return the Connection object associated with this LdapConnection /// /// /// the Connection object /// virtual internal Connection Connection { /* package */ get { return conn; } } /// Return the Connection object name associated with this LdapConnection /// /// /// the Connection object name /// virtual internal System.String ConnectionName { /* package */ get { return name; } } private LdapSearchConstraints defSearchCons; private LdapControl[] responseCtls = null; // Synchronization Object used to synchronize access to responseCtls private System.Object responseCtlSemaphore; private Connection conn = null; private static System.Object nameLock; // protect agentNum private static int lConnNum = 0; // Debug, LdapConnection number private System.String name; // String name for debug /// Used with search to specify that the scope of entrys to search is to /// search only the base obect. /// /// SCOPE_BASE = 0 /// public const int SCOPE_BASE = 0; /// Used with search to specify that the scope of entrys to search is to /// search only the immediate subordinates of the base obect. /// /// SCOPE_ONE = 1 /// public const int SCOPE_ONE = 1; /// Used with search to specify that the scope of entrys to search is to /// search the base object and all entries within its subtree. /// /// SCOPE_ONE = 2 /// public const int SCOPE_SUB = 2; /// Used with search instead of an attribute list to indicate that no /// attributes are to be returned. /// /// NO_ATTRS = "1.1" /// public const System.String NO_ATTRS = "1.1"; /// Used with search instead of an attribute list to indicate that all /// attributes are to be returned. /// /// ALL_USER_ATTRS = "*" /// public const System.String ALL_USER_ATTRS = "*"; /// Specifies the Ldapv3 protocol version when performing a bind operation. /// /// Specifies Ldap version V3 of the protocol, and is specified /// when performing bind operations. /// You can use this identifier in the version parameter /// of the bind method to specify an Ldapv3 bind. /// Ldap_V3 is the default protocol version /// /// Ldap_V3 = 3 /// /// public const int Ldap_V3 = 3; /// The default port number for Ldap servers. /// /// You can use this identifier to specify the port when establishing /// a clear text connection to a server. This the default port. /// /// DEFAULT_PORT = 389 /// /// public const int DEFAULT_PORT = 389; /// The default SSL port number for Ldap servers. /// /// DEFAULT_SSL_PORT = 636 /// /// You can use this identifier to specify the port when establishing /// a an SSL connection to a server.. /// public const int DEFAULT_SSL_PORT = 636; /// A string that can be passed in to the getProperty method. /// /// Ldap_PROPERTY_SDK = "version.sdk" /// /// You can use this string to request the version of the SDK. /// public const System.String Ldap_PROPERTY_SDK = "version.sdk"; /// A string that can be passed in to the getProperty method. /// /// Ldap_PROPERTY_PROTOCOL = "version.protocol" /// /// You can use this string to request the version of the /// Ldap protocol. /// public const System.String Ldap_PROPERTY_PROTOCOL = "version.protocol"; /// A string that can be passed in to the getProperty method. /// /// Ldap_PROPERTY_SECURITY = "version.security" /// /// You can use this string to request the type of security /// being used. /// public const System.String Ldap_PROPERTY_SECURITY = "version.security"; /// A string that corresponds to the server shutdown notification OID. /// This notification may be used by the server to advise the client that /// the server is about to close the connection due to an error /// condition. /// /// SERVER_SHUTDOWN_OID = "1.3.6.1.4.1.1466.20036" /// public const System.String SERVER_SHUTDOWN_OID = "1.3.6.1.4.1.1466.20036"; /// The OID string that identifies a StartTLS request and response. private const System.String START_TLS_OID = "1.3.6.1.4.1.1466.20037"; /* * Constructors */ /// Constructs a new LdapConnection object, which will use the supplied /// class factory to construct a socket connection during /// LdapConnection.connect method. /// /// /// An object capable of producing a Socket. /// /// public LdapConnection() { InitBlock(); // Get a unique connection name for debug conn = new Connection(); return ; } /* public LdapConnection(X509Certificate cert) { InitBlock(); // Get a unique connection name for debug conn = new Connection(); conn.Cert = cert; return ; } */ /* * The following are methods that affect the operation of * LdapConnection, but are not Ldap requests. */ /// Returns a copy of the object with a private context, but sharing the /// network connection if there is one. /// /// The network connection remains open until all clones have /// disconnected or gone out of scope. Any connection opened after /// cloning is private to the object making the connection. /// /// The clone can issue requests and freely modify options and search /// constraints, and , without affecting the source object or other clones. /// If the clone disconnects or reconnects, it is completely dissociated /// from the source object and other clones. Reauthenticating in a clone, /// however, is a global operation which will affect the source object /// and all associated clones, because it applies to the single shared /// physical connection. Any request by an associated object after one /// has reauthenticated will carry the new identity. /// /// /// A of the object. /// public System.Object Clone() { LdapConnection newClone; System.Object newObj; try { newObj = base.MemberwiseClone(); newClone = (LdapConnection) newObj; } catch (System.Exception ce) { throw new System.SystemException("Internal error, cannot create clone"); } newClone.conn = conn; // same underlying connection //now just duplicate the defSearchCons and responseCtls if (defSearchCons != null) { newClone.defSearchCons = (LdapSearchConstraints) defSearchCons.Clone(); } else { newClone.defSearchCons = null; } if (responseCtls != null) { newClone.responseCtls = new LdapControl[responseCtls.Length]; for (int i = 0; i < responseCtls.Length; i++) { newClone.responseCtls[i] = (LdapControl) responseCtls[i].Clone(); } } else { newClone.responseCtls = null; } conn.incrCloneCount(); // Increment the count of clones return newObj; } /// Closes the connection, if open, and releases any other resources held /// by the object. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// /// /// /// ~LdapConnection() { // Disconnect did not come from user API call Disconnect(defSearchCons, false); return ; } /// Returns a property of a connection object. /// /// /// Name of the property to be returned. /// /// The following read-only properties are available /// for any given connection: /// /// /// A deep copy of the property is provided where applicable; a /// client does not need to clone the object received. /// /// /// The object associated with the requested property, /// or null if the property is not defined. /// /// /// /// /// /// public virtual System.Object getProperty(System.String name) { if (name.ToUpper().Equals(Ldap_PROPERTY_SDK.ToUpper())) return Connection.sdk; else if (name.ToUpper().Equals(Ldap_PROPERTY_PROTOCOL.ToUpper())) return Connection.protocol; else if (name.ToUpper().Equals(Ldap_PROPERTY_SECURITY.ToUpper())) return Connection.security; else { return null; } } /// Registers an object to be notified on arrival of an unsolicited /// message from a server. /// /// An unsolicited message has the ID 0. A new thread is created and /// the method "messageReceived" in each registered object is called in /// turn. /// /// /// An object to be notified on arrival of an /// unsolicited message from a server. This object must /// implement the LdapUnsolicitedNotificationListener interface. /// /// public virtual void AddUnsolicitedNotificationListener(LdapUnsolicitedNotificationListener listener) { if (listener != null) conn.AddUnsolicitedNotificationListener(listener); } /// Deregisters an object so that it will no longer be notified on /// arrival of an unsolicited message from a server. If the object is /// null or was not previously registered for unsolicited notifications, /// the method does nothing. /// /// /// /// An object to no longer be notified on arrival of /// an unsolicited message from a server. /// /// public virtual void RemoveUnsolicitedNotificationListener(LdapUnsolicitedNotificationListener listener) { if (listener != null) conn.RemoveUnsolicitedNotificationListener(listener); } /// Starts Transport Layer Security (TLS) protocol on this connection /// to enable session privacy. /// /// This affects the LdapConnection object and all cloned objects. A /// socket factory that implements LdapTLSSocketFactory must be set on the /// connection. /// /// /// LdapException Thrown if TLS cannot be started. If a /// SocketFactory has been specified that does not implement /// LdapTLSSocketFactory an LdapException is thrown. /// /// public virtual void startTLS() { LdapMessage startTLS = MakeExtendedOperation(new LdapExtendedOperation(LdapConnection.START_TLS_OID, null), null); int tlsID = startTLS.MessageID; conn.acquireWriteSemaphore(tlsID); try { if (!conn.areMessagesComplete()) { throw new LdapLocalException(ExceptionMessages.OUTSTANDING_OPERATIONS, LdapException.OPERATIONS_ERROR); } // Stop reader when response to startTLS request received conn.stopReaderOnReply(tlsID); // send tls message LdapResponseQueue queue = SendRequestToServer(startTLS, defSearchCons.TimeLimit, null, null); LdapExtendedResponse response = (LdapExtendedResponse) queue.getResponse(); response.chkResultCode(); conn.startTLS(); } finally { //Free this semaphore no matter what exceptions get thrown conn.startReader(); conn.freeWriteSemaphore(tlsID); } return ; } /// Stops Transport Layer Security(TLS) on the LDAPConnection and reverts /// back to an anonymous state. /// /// @throws LDAPException This can occur for the following reasons: /// /// ///

Note: The Sun and IBM implementions of JSSE do not currently allow /// stopping TLS on an open Socket. In order to produce the same results /// this method currently disconnects the socket and reconnects, giving /// the application an anonymous connection to the server, as required /// by StopTLS

///
public virtual void stopTLS() { if (!TLS) { throw new LdapLocalException(ExceptionMessages.NO_STARTTLS, LdapException.OPERATIONS_ERROR); } int semaphoreID = conn.acquireWriteSemaphore(); try { if (!conn.areMessagesComplete()) { throw new LdapLocalException(ExceptionMessages.OUTSTANDING_OPERATIONS, LdapException.OPERATIONS_ERROR); } //stopTLS stops and starts the reader thread for us. conn.stopTLS(); } finally { conn.freeWriteSemaphore(semaphoreID); /* Now that the TLS socket is closed, reset everything. This next line is temporary until JSSE is fixed to properly handle TLS stop */ this.Connect(this.Host, this.Port); } return ; } //************************************************************************* // Below are all of the Ldap protocol operation methods //************************************************************************* //************************************************************************* // abandon methods //************************************************************************* /// /// /// Notifies the server not to send additional results associated with /// this LdapSearchResults object, and discards any results already /// received. /// /// /// An object returned from a search. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual void Abandon(LdapSearchResults results) { Abandon(results, defSearchCons); return ; } /// /// /// Notifies the server not to send additional results associated with /// this LdapSearchResults object, and discards any results already /// received. /// /// /// An object returned from a search. /// /// /// The contraints specific to the operation. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual void Abandon(LdapSearchResults results, LdapConstraints cons) { results.Abandon(); return ; } /// /// Abandons an asynchronous operation. /// /// /// The ID of the asynchronous operation to abandon. The ID /// can be obtained from the response queue for the /// operation. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual void Abandon(int id) { Abandon(id, defSearchCons); return ; } /// Abandons an asynchronous operation, using the specified /// constraints. /// /// /// The ID of the asynchronous operation to abandon. /// The ID can be obtained from the search /// queue for the operation. /// /// /// The contraints specific to the operation. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual void Abandon(int id, LdapConstraints cons) { // We need to inform the Message Agent which owns this messageID to // remove it from the queue. try { MessageAgent agent = conn.getMessageAgent(id); agent.Abandon(id, cons); return ; } catch (System.FieldAccessException ex) { return ; // Ignore error } } /// Abandons all outstanding operations managed by the queue. /// /// All operations in progress, which are managed by the specified queue, /// are abandoned. /// /// /// The queue returned from an asynchronous request. /// All outstanding operations managed by the queue /// are abandoned, and the queue is emptied. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual void Abandon(LdapMessageQueue queue) { Abandon(queue, defSearchCons); return ; } /// Abandons all outstanding operations managed by the queue. /// /// All operations in progress, which are managed by the specified /// queue, are abandoned. /// /// /// The queue returned from an asynchronous request. /// All outstanding operations managed by the queue /// are abandoned, and the queue is emptied. /// /// /// The contraints specific to the operation. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual void Abandon(LdapMessageQueue queue, LdapConstraints cons) { if (queue != null) { MessageAgent agent; if (queue is LdapSearchQueue) { agent = queue.MessageAgent; } else { agent = queue.MessageAgent; } int[] msgIds = agent.MessageIDs; for (int i = 0; i < msgIds.Length; i++) { agent.Abandon(msgIds[i], cons); } } return ; } //************************************************************************* // add methods //************************************************************************* /// Synchronously adds an entry to the directory. /// /// /// LdapEntry object specifying the distinguished /// name and attributes of the new entry. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual void Add(LdapEntry entry) { Add(entry, defSearchCons); return ; } /// /// Synchronously adds an entry to the directory, using the specified /// constraints. /// /// /// LdapEntry object specifying the distinguished /// name and attributes of the new entry. /// /// /// Constraints specific to the operation. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual void Add(LdapEntry entry, LdapConstraints cons) { LdapResponseQueue queue = Add(entry, null, cons); // Get a handle to the add response LdapResponse addResponse = (LdapResponse) (queue.getResponse()); // Set local copy of responseControls synchronously if there were any lock (responseCtlSemaphore) { responseCtls = addResponse.Controls; } chkResultCode(queue, cons, addResponse); return ; } /// Asynchronously adds an entry to the directory. /// /// /// LdapEntry object specifying the distinguished /// name and attributes of the new entry. /// /// /// Handler for messages returned from a server in /// response to this request. If it is null, a /// queue object is created internally. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual LdapResponseQueue Add(LdapEntry entry, LdapResponseQueue queue) { return Add(entry, queue, defSearchCons); } /// Asynchronously adds an entry to the directory, using the specified /// constraints. /// /// /// LdapEntry object specifying the distinguished /// name and attributes of the new entry. /// /// /// Handler for messages returned from a server in /// response to this request. If it is null, a /// queue object is created internally. /// /// /// Constraints specific to the operation. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual LdapResponseQueue Add(LdapEntry entry, LdapResponseQueue queue, LdapConstraints cons) { if (cons == null) cons = defSearchCons; // error check the parameters if (entry == null) { throw new System.ArgumentException("The LdapEntry parameter" + " cannot be null"); } if ((System.Object) entry.DN == null) { throw new System.ArgumentException("The DN value must be present" + " in the LdapEntry object"); } LdapMessage msg = new LdapAddRequest(entry, cons.getControls()); return SendRequestToServer(msg, cons.TimeLimit, queue, null); } //************************************************************************* // bind methods //************************************************************************* /// Synchronously authenticates to the Ldap server (that the object is /// currently connected to) as an Ldapv3 bind, using the specified name and /// password. /// /// If the object has been disconnected from an Ldap server, /// this method attempts to reconnect to the server. If the object /// has already authenticated, the old authentication is discarded. /// /// /// If non-null and non-empty, specifies that the /// connection and all operations through it should /// be authenticated with dn as the distinguished /// name. /// /// /// If non-null and non-empty, specifies that the /// connection and all operations through it should /// be authenticated with dn as the distinguished /// name and passwd as password. /// /// Note: the application should use care in the use /// of String password objects. These are long lived /// objects, and may expose a security risk, especially /// in objects that are serialized. The LdapConnection /// keeps no long lived instances of these objects. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// /// public virtual void Bind(System.String dn, System.String passwd) { Bind(Ldap_V3, dn, passwd, defSearchCons); return ; } /// Synchronously authenticates to the Ldap server (that the object is /// currently connected to) using the specified name, password, /// and Ldap version. /// /// If the object has been disconnected from an Ldap server, /// this method attempts to reconnect to the server. If the object /// has already authenticated, the old authentication is discarded. /// /// /// The Ldap protocol version, use Ldap_V3. /// Ldap_V2 is not supported. /// /// /// If non-null and non-empty, specifies that the /// connection and all operations through it should /// be authenticated with dn as the distinguished /// name. /// /// /// If non-null and non-empty, specifies that the /// connection and all operations through it should /// be authenticated with dn as the distinguished /// name and passwd as password. /// /// Note: the application should use care in the use /// of String password objects. These are long lived /// objects, and may expose a security risk, especially /// in objects that are serialized. The LdapConnection /// keeps no long lived instances of these objects. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// /// public virtual void Bind(int version, System.String dn, System.String passwd) { Bind(version, dn, passwd, defSearchCons); return ; } /// Synchronously authenticates to the Ldap server (that the object is /// currently connected to) as an Ldapv3 bind, using the specified name, /// password, and constraints. /// /// If the object has been disconnected from an Ldap server, /// this method attempts to reconnect to the server. If the object /// has already authenticated, the old authentication is discarded. /// /// /// If non-null and non-empty, specifies that the /// connection and all operations through it should /// be authenticated with dn as the distinguished /// name. /// /// /// If non-null and non-empty, specifies that the /// connection and all operations through it should /// be authenticated with dn as the distinguished /// name and passwd as password. /// Note: the application should use care in the use /// of String password objects. These are long lived /// objects, and may expose a security risk, especially /// in objects that are serialized. The LdapConnection /// keeps no long lived instances of these objects. /// /// /// Constraints specific to the operation. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// /// public virtual void Bind(System.String dn, System.String passwd, LdapConstraints cons) { Bind(Ldap_V3, dn, passwd, cons); return ; } /// Synchronously authenticates to the Ldap server (that the object is /// currently connected to) using the specified name, password, Ldap version, /// and constraints. /// /// If the object has been disconnected from an Ldap server, /// this method attempts to reconnect to the server. If the object /// has already authenticated, the old authentication is discarded. /// /// /// The Ldap protocol version, use Ldap_V3. /// Ldap_V2 is not supported. /// /// /// If non-null and non-empty, specifies that the /// connection and all operations through it should /// be authenticated with dn as the distinguished /// name. /// /// /// If non-null and non-empty, specifies that the /// connection and all operations through it should /// be authenticated with dn as the distinguished /// name and passwd as password. /// /// Note: the application should use care in the use /// of String password objects. These are long lived /// objects, and may expose a security risk, especially /// in objects that are serialized. The LdapConnection /// keeps no long lived instances of these objects. /// /// /// The constraints specific to the operation. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// /// public virtual void Bind(int version, System.String dn, System.String passwd, LdapConstraints cons) { sbyte[] pw = null; if ((System.Object) passwd != null) { try { System.Text.Encoding encoder = System.Text.Encoding.GetEncoding("utf-8"); byte[] ibytes = encoder.GetBytes(passwd); pw=SupportClass.ToSByteArray(ibytes); // pw = passwd.getBytes("UTF8"); passwd = null; // Keep no reference to String object } catch (System.IO.IOException ex) { passwd = null; // Keep no reference to String object throw new System.SystemException(ex.ToString()); } } Bind(version, dn, pw, cons); return ; } /// Synchronously authenticates to the Ldap server (that the object is /// currently connected to) using the specified name, password, /// and Ldap version. /// /// If the object has been disconnected from an Ldap server, /// this method attempts to reconnect to the server. If the object /// has already authenticated, the old authentication is discarded. /// /// /// The version of the Ldap protocol to use /// in the bind, use Ldap_V3. Ldap_V2 is not supported. /// /// /// If non-null and non-empty, specifies that the /// connection and all operations through it should /// be authenticated with dn as the distinguished /// name. /// /// /// If non-null and non-empty, specifies that the /// connection and all operations through it should /// be authenticated with dn as the distinguished /// name and passwd as password. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// [CLSCompliantAttribute(false)] public virtual void Bind(int version, System.String dn, sbyte[] passwd) { Bind(version, dn, passwd, defSearchCons); return ; } /// /// Synchronously authenticates to the Ldap server (that the object is /// currently connected to) using the specified name, password, Ldap version, /// and constraints. /// /// If the object has been disconnected from an Ldap server, /// this method attempts to reconnect to the server. If the object /// has already authenticated, the old authentication is discarded. /// /// /// The Ldap protocol version, use Ldap_V3. /// Ldap_V2 is not supported. /// /// /// If non-null and non-empty, specifies that the /// connection and all operations through it should /// be authenticated with dn as the distinguished /// name. /// /// /// If non-null and non-empty, specifies that the /// connection and all operations through it should /// be authenticated with dn as the distinguished /// name and passwd as password. /// /// /// The constraints specific to the operation. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// [CLSCompliantAttribute(false)] public virtual void Bind(int version, System.String dn, sbyte[] passwd, LdapConstraints cons) { LdapResponseQueue queue = Bind(version, dn, passwd, null, cons); LdapResponse res = (LdapResponse) queue.getResponse(); if (res != null) { // Set local copy of responseControls synchronously if any lock (responseCtlSemaphore) { responseCtls = res.Controls; } chkResultCode(queue, cons, res); } return ; } /// Asynchronously authenticates to the Ldap server (that the object is /// currently connected to) using the specified name, password, Ldap /// version, and queue. /// /// If the object has been disconnected from an Ldap server, /// this method attempts to reconnect to the server. If the object /// has already authenticated, the old authentication is discarded. /// /// /// /// The Ldap protocol version, use Ldap_V3. /// Ldap_V2 is not supported. /// /// /// If non-null and non-empty, specifies that the /// connection and all operations through it should /// be authenticated with dn as the distinguished /// name. /// /// /// If non-null and non-empty, specifies that the /// connection and all operations through it should /// be authenticated with dn as the distinguished /// name and passwd as password. /// /// /// Handler for messages returned from a server in /// response to this request. If it is null, a /// queue object is created internally. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// [CLSCompliantAttribute(false)] public virtual LdapResponseQueue Bind(int version, System.String dn, sbyte[] passwd, LdapResponseQueue queue) { return Bind(version, dn, passwd, queue, defSearchCons); } /// Asynchronously authenticates to the Ldap server (that the object is /// currently connected to) using the specified name, password, Ldap /// version, queue, and constraints. /// /// If the object has been disconnected from an Ldap server, /// this method attempts to reconnect to the server. If the object /// had already authenticated, the old authentication is discarded. /// /// /// The Ldap protocol version, use Ldap_V3. /// Ldap_V2 is not supported. /// /// /// If non-null and non-empty, specifies that the /// connection and all operations through it should /// be authenticated with dn as the distinguished /// name. /// /// /// If non-null and non-empty, specifies that the /// connection and all operations through it should /// be authenticated with dn as the distinguished /// name and passwd as password. /// /// /// Handler for messages returned from a server in /// response to this request. If it is null, a /// queue object is created internally. /// /// /// Constraints specific to the operation. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// [CLSCompliantAttribute(false)] public virtual LdapResponseQueue Bind(int version, System.String dn, sbyte[] passwd, LdapResponseQueue queue, LdapConstraints cons) { int msgId; BindProperties bindProps; if (cons == null) cons = defSearchCons; if ((System.Object) dn == null) { dn = ""; } else { dn = dn.Trim(); } if (passwd == null) passwd = new sbyte[]{}; bool anonymous = false; if (passwd.Length == 0) { anonymous = true; // anonymous, passwd length zero with simple bind dn = ""; // set to null if anonymous } LdapMessage msg = new LdapBindRequest(version, dn, passwd, cons.getControls()); msgId = msg.MessageID; bindProps = new BindProperties(version, dn, "simple", anonymous, null, null); // For bind requests, if not connected, attempt to reconnect if (!conn.Connected) { if ((System.Object) conn.Host != null) { conn.connect(conn.Host, conn.Port); } else { throw new LdapException(ExceptionMessages.CONNECTION_IMPOSSIBLE, LdapException.CONNECT_ERROR, null); } } // The semaphore is released when the bind response is queued. conn.acquireWriteSemaphore(msgId); return SendRequestToServer(msg, cons.TimeLimit, queue, bindProps); } //************************************************************************* // compare methods //************************************************************************* /// /// Synchronously checks to see if an entry contains an attribute /// with a specified value. /// /// /// The distinguished name of the entry to use in the /// comparison. /// /// /// The attribute to compare against the entry. The /// method checks to see if the entry has an /// attribute with the same name and value as this /// attribute. /// /// /// True if the entry has the value, /// and false if the entry does not /// have the value or the attribute. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual bool Compare(System.String dn, LdapAttribute attr) { return Compare(dn, attr, defSearchCons); } /// /// Synchronously checks to see if an entry contains an attribute with a /// specified value, using the specified constraints. /// /// /// The distinguished name of the entry to use in the /// comparison. /// /// /// The attribute to compare against the entry. The /// method checks to see if the entry has an /// attribute with the same name and value as this /// attribute. /// /// /// Constraints specific to the operation. /// /// /// True if the entry has the value, /// and false if the entry does not /// have the value or the attribute. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual bool Compare(System.String dn, LdapAttribute attr, LdapConstraints cons) { bool ret = false; LdapResponseQueue queue = Compare(dn, attr, null, cons); LdapResponse res = (LdapResponse) queue.getResponse(); // Set local copy of responseControls synchronously - if there were any lock (responseCtlSemaphore) { responseCtls = res.Controls; } if (res.ResultCode == LdapException.COMPARE_TRUE) { ret = true; } else if (res.ResultCode == LdapException.COMPARE_FALSE) { ret = false; } else { chkResultCode(queue, cons, res); } return ret; } /// Asynchronously compares an attribute value with one in the directory, /// using the specified queue. /// /// Please note that a successful completion of this command results in /// one of two status codes: LdapException.COMPARE_TRUE if the entry /// has the value, and LdapException.COMPARE_FALSE if the entry /// does not have the value or the attribute. /// /// /// The distinguished name of the entry containing an /// attribute to compare. /// /// /// An attribute to compare. /// /// /// The queue for messages returned from a server in /// response to this request. If it is null, a /// queue object is created internally. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// /// /// /// /// /// public virtual LdapResponseQueue Compare(System.String dn, LdapAttribute attr, LdapResponseQueue queue) { return Compare(dn, attr, queue, defSearchCons); } /// Asynchronously compares an attribute value with one in the directory, /// using the specified queue and contraints. /// /// Please note that a successful completion of this command results in /// one of two status codes: LdapException.COMPARE_TRUE if the entry /// has the value, and LdapException.COMPARE_FALSE if the entry /// does not have the value or the attribute. /// /// /// The distinguished name of the entry containing an /// attribute to compare. /// /// /// An attribute to compare. /// /// /// Handler for messages returned from a server in /// response to this request. If it is null, a /// queue object is created internally. /// /// /// Constraints specific to the operation. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// /// /// /// /// /// public virtual LdapResponseQueue Compare(System.String dn, LdapAttribute attr, LdapResponseQueue queue, LdapConstraints cons) { if (attr.size() != 1) { throw new System.ArgumentException("compare: Exactly one value " + "must be present in the LdapAttribute"); } if ((System.Object) dn == null) { // Invalid parameter throw new System.ArgumentException("compare: DN cannot be null"); } if (cons == null) cons = defSearchCons; LdapMessage msg = new LdapCompareRequest(dn, attr.Name, attr.ByteValue, cons.getControls()); return SendRequestToServer(msg, cons.TimeLimit, queue, null); } //************************************************************************* // connect methods //************************************************************************* /// /// Connects to the specified host and port. /// /// If this LdapConnection object represents an open connection, the /// connection is closed first before the new connection is opened. /// At this point, there is no authentication, and any operations are /// conducted as an anonymous client. /// /// When more than one host name is specified, each host is contacted /// in turn until a connection can be established. /// /// /// A host name or a dotted string representing the IP address /// of a host running an Ldap server. It may also /// contain a list of host names, space-delimited. Each host /// name can include a trailing colon and port number. /// /// /// The TCP or UDP port number to connect to or contact. /// The default Ldap port is 389. The port parameter is /// ignored for any host hame which includes a colon and /// port number. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// /// public virtual void Connect(System.String host, int port) { // connect doesn't affect other clones // If not a clone, destroys old connection. // Step through the space-delimited list SupportClass.Tokenizer hostList = new SupportClass.Tokenizer(host, " "); System.String address = null; int specifiedPort; int colonIndex; //after the colon is the port while (hostList.HasMoreTokens()) { try { specifiedPort = port; address = hostList.NextToken(); colonIndex = address.IndexOf((System.Char) ':'); if (colonIndex != - 1 && colonIndex + 1 != address.Length) { //parse Port out of address try { specifiedPort = System.Int32.Parse(address.Substring(colonIndex + 1)); address = address.Substring(0, (colonIndex) - (0)); } catch (System.Exception e) { throw new System.ArgumentException(ExceptionMessages.INVALID_ADDRESS); } } // This may return a different conn object // Disassociate this clone with the underlying connection. conn = conn.destroyClone(true); conn.connect(address, specifiedPort); break; } catch (LdapException LE) { if (!hostList.HasMoreTokens()) throw LE; } } return ; } //************************************************************************* // delete methods //************************************************************************* /// /// Synchronously deletes the entry with the specified distinguished name /// from the directory. /// /// Note: A Delete operation will not remove an entry that contains /// subordinate entries, nor will it dereference alias entries. /// /// /// The distinguished name of the entry to delete. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual void Delete(System.String dn) { Delete(dn, defSearchCons); return ; } /// Synchronously deletes the entry with the specified distinguished name /// from the directory, using the specified constraints. /// /// Note: A Delete operation will not remove an entry that contains /// subordinate entries, nor will it dereference alias entries. /// /// /// The distinguished name of the entry to delete. /// /// /// Constraints specific to the operation. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual void Delete(System.String dn, LdapConstraints cons) { LdapResponseQueue queue = Delete(dn, null, cons); // Get a handle to the delete response LdapResponse deleteResponse = (LdapResponse) (queue.getResponse()); // Set local copy of responseControls synchronously - if there were any lock (responseCtlSemaphore) { responseCtls = deleteResponse.Controls; } chkResultCode(queue, cons, deleteResponse); return ; } /// Asynchronously deletes the entry with the specified distinguished name /// from the directory and returns the results to the specified queue. /// /// Note: A Delete operation will not remove an entry that contains /// subordinate entries, nor will it dereference alias entries. /// /// /// The distinguished name of the entry to modify. /// /// /// The queue for messages returned from a server in /// response to this request. If it is null, a /// queue object is created internally. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// /// public virtual LdapResponseQueue Delete(System.String dn, LdapResponseQueue queue) { return Delete(dn, queue, defSearchCons); } /// Asynchronously deletes the entry with the specified distinguished name /// from the directory, using the specified contraints and queue. /// /// Note: A Delete operation will not remove an entry that contains /// subordinate entries, nor will it dereference alias entries. /// /// /// The distinguished name of the entry to delete. /// /// /// The queue for messages returned from a server in /// response to this request. If it is null, a /// queue object is created internally. /// /// /// The constraints specific to the operation. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// /// public virtual LdapResponseQueue Delete(System.String dn, LdapResponseQueue queue, LdapConstraints cons) { if ((System.Object) dn == null) { // Invalid DN parameter throw new System.ArgumentException(ExceptionMessages.DN_PARAM_ERROR); } if (cons == null) cons = defSearchCons; LdapMessage msg = new LdapDeleteRequest(dn, cons.getControls()); return SendRequestToServer(msg, cons.TimeLimit, queue, null); } //************************************************************************* // disconnect method //************************************************************************* /// /// Synchronously disconnects from the Ldap server. /// /// Before the object can perform Ldap operations again, it must /// reconnect to the server by calling connect. /// /// The disconnect method abandons any outstanding requests, issues an /// unbind request to the server, and then closes the socket. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// /// public virtual void Disconnect() { // disconnect from API call Disconnect(defSearchCons, true); return ; } /// Synchronously disconnects from the Ldap server. /// /// Before the object can perform Ldap operations again, it must /// reconnect to the server by calling connect. /// /// The disconnect method abandons any outstanding requests, issues an /// unbind request to the server, and then closes the socket. /// /// /// LDPConstraints to be set with the unbind request /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual void Disconnect(LdapConstraints cons) { // disconnect from API call Disconnect(cons, true); return ; } /// Synchronously disconnect from the server /// /// /// true if application call disconnect API, false if finalize. /// private void Disconnect(LdapConstraints cons, bool how) { // disconnect doesn't affect other clones // If not a clone, distroys connection conn = conn.destroyClone(how); return ; } //************************************************************************* // extendedOperation methods //************************************************************************* /// Provides a synchronous means to access extended, non-mandatory /// operations offered by a particular Ldapv3 compliant server. /// /// /// The object which contains (1) an identifier of an extended /// operation which should be recognized by the particular Ldap /// server this client is connected to and (2) /// an operation-specific sequence of octet strings /// or BER-encoded values. /// /// /// An operation-specific object, containing an ID and either an octet /// string or BER-encoded values. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual LdapExtendedResponse ExtendedOperation(LdapExtendedOperation op) { return ExtendedOperation(op, defSearchCons); } /* * Synchronous Ldap extended request with SearchConstraints */ /// /// Provides a synchronous means to access extended, non-mandatory /// operations offered by a particular Ldapv3 compliant server. /// /// /// The object which contains (1) an identifier of an extended /// operation which should be recognized by the particular Ldap /// server this client is connected to and (2) an /// operation-specific sequence of octet strings /// or BER-encoded values. /// /// /// The constraints specific to the operation. /// /// /// An operation-specific object, containing an ID and either an /// octet string or BER-encoded values. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual LdapExtendedResponse ExtendedOperation(LdapExtendedOperation op, LdapConstraints cons) { // Call asynchronous API and get back handler to reponse queue LdapResponseQueue queue = ExtendedOperation(op, cons, null); LdapExtendedResponse response = (LdapExtendedResponse) queue.getResponse(); // Set local copy of responseControls synchronously - if there were any lock (responseCtlSemaphore) { responseCtls = response.Controls; } chkResultCode(queue, cons, response); return response; } /* * Asynchronous Ldap extended request */ /// Provides an asynchronous means to access extended, non-mandatory /// operations offered by a particular Ldapv3 compliant server. /// /// /// The object which contains (1) an identifier of an extended /// operation which should be recognized by the particular Ldap /// server this client is connected to and (2) an /// operation-specific sequence of octet strings /// or BER-encoded values. /// /// /// The queue for messages returned from a server in /// response to this request. If it is null, a queue /// object is created internally. /// /// /// An operation-specific object, containing an ID and either an octet /// string or BER-encoded values. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual LdapResponseQueue ExtendedOperation(LdapExtendedOperation op, LdapResponseQueue queue) { return ExtendedOperation(op, defSearchCons, queue); } /* * Asynchronous Ldap extended request with SearchConstraints */ /// Provides an asynchronous means to access extended, non-mandatory /// operations offered by a particular Ldapv3 compliant server. /// /// /// The object which contains (1) an identifier of an extended /// operation which should be recognized by the particular Ldap /// server this client is connected to and (2) an operation- /// specific sequence of octet strings or BER-encoded values. /// /// /// The queue for messages returned from a server in /// response to this request. If it is null, a queue /// object is created internally. /// /// /// The constraints specific to this operation. /// /// /// An operation-specific object, containing an ID and either an /// octet string or BER-encoded values. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual LdapResponseQueue ExtendedOperation(LdapExtendedOperation op, LdapConstraints cons, LdapResponseQueue queue) { // Use default constraints if none-specified if (cons == null) cons = defSearchCons; LdapMessage msg = MakeExtendedOperation(op, cons); return SendRequestToServer(msg, cons.TimeLimit, queue, null); } /// Formulates the extended operation, constraints into an /// LdapMessage and returns the LdapMessage. This is used by /// extendedOperation and startTLS which needs the LdapMessage to /// get the MessageID. /// protected internal virtual LdapMessage MakeExtendedOperation(LdapExtendedOperation op, LdapConstraints cons) { // Use default constraints if none-specified if (cons == null) cons = defSearchCons; // error check the parameters if ((System.Object) op.getID() == null) { // Invalid extended operation parameter, no OID specified throw new System.ArgumentException(ExceptionMessages.OP_PARAM_ERROR); } return new LdapExtendedRequest(op, cons.getControls()); } //************************************************************************* // getResponseControls method //************************************************************************* //************************************************************************* // modify methods //************************************************************************* /// Synchronously makes a single change to an existing entry in the /// directory. /// /// For example, this modify method 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. /// /// If the request fails with {@link LdapException.CONNECT_ERROR}, /// it is indeterminate whether or not the server made the modification. /// /// /// The distinguished name of the entry to modify. /// /// /// A single change to be made to the entry. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual void Modify(System.String dn, LdapModification mod) { Modify(dn, mod, defSearchCons); return ; } /// /// Synchronously makes a single change to an existing entry in the /// directory, using the specified constraints. /// /// For example, this modify method 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. /// /// If the request fails with {@link LdapException.CONNECT_ERROR}, /// it is indeterminate whether or not the server made the modification. /// /// /// The distinguished name of the entry to modify. /// /// /// A single change to be made to the entry. /// /// /// The constraints specific to the operation. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual void Modify(System.String dn, LdapModification mod, LdapConstraints cons) { LdapModification[] mods = new LdapModification[1]; mods[0] = mod; Modify(dn, mods, cons); return ; } /// /// Synchronously makes a set of changes to an existing entry in the /// directory. /// /// For example, this modify method changes attribute values, adds /// new attribute values, or removes existing attribute values. /// /// Because the server applies all changes in an LdapModification array /// atomically, the application can expect that no changes /// have been performed if an error is returned. /// If the request fails with {@link LdapException.CONNECT_ERROR}, /// it is indeterminate whether or not the server made the modifications. /// /// /// Distinguished name of the entry to modify. /// /// /// The changes to be made to the entry. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual void Modify(System.String dn, LdapModification[] mods) { Modify(dn, mods, defSearchCons); return ; } /// Synchronously makes a set of changes to an existing entry in the /// directory, using the specified constraints. /// /// For example, this modify method changes attribute values, adds new /// attribute values, or removes existing attribute values. /// /// Because the server applies all changes in an LdapModification array /// atomically, the application can expect that no changes /// have been performed if an error is returned. /// If the request fails with {@link LdapException.CONNECT_ERROR}, /// it is indeterminate whether or not the server made the modifications. /// /// /// The distinguished name of the entry to modify. /// /// /// The changes to be made to the entry. /// /// /// The constraints specific to the operation. /// /// /// LdapException A general exception which includes an /// error message and an Ldap error code. /// public virtual void Modify(System.String dn, LdapModification[] mods, LdapConstraints cons) { LdapResponseQueue queue = Modify(dn, mods, null, cons); // Get a handle to the modify response LdapResponse modifyResponse = (LdapResponse) (queue.getResponse()); // Set local copy of responseControls synchronously - if there were any lock (responseCtlSemaphore) { responseCtls = modifyResponse.Controls; } chkResultCode(queue, cons, modifyResponse); return ; } /// Asynchronously makes a single change to an existing entry in the /// directory. /// /// For example, this modify method can change the value of an attribute, /// add a new attribute value, or remove an existing attribute value. /// /// The LdapModification object specifies both the change to be made and /// the LdapAttribute value to be changed. /// /// If the request fails with {@link LdapException.CONNECT_ERROR}, /// it is indeterminate whether or not the server made the modification. /// /// /// Distinguished name of the entry to modify. /// /// /// A single change to be made to the entry. /// /// /// Handler for messages returned from a server in /// response to this request. If it is null, a /// queue object is created internally. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual LdapResponseQueue Modify(System.String dn, LdapModification mod, LdapResponseQueue queue) { return Modify(dn, mod, queue, defSearchCons); } /// Asynchronously makes a single change to an existing entry in the /// directory, using the specified constraints and queue. /// /// For example, this modify method can change the value of an attribute, /// add a new attribute value, or remove an existing attribute value. /// /// The LdapModification object specifies both the change to be made /// and the LdapAttribute value to be changed. /// /// If the request fails with {@link LdapException.CONNECT_ERROR}, /// it is indeterminate whether or not the server made the modification. /// /// /// Distinguished name of the entry to modify. /// /// /// A single change to be made to the entry. /// /// /// Handler for messages returned from a server in /// response to this request. If it is null, a /// queue object is created internally. /// /// /// Constraints specific to the operation. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual LdapResponseQueue Modify(System.String dn, LdapModification mod, LdapResponseQueue queue, LdapConstraints cons) { LdapModification[] mods = new LdapModification[1]; mods[0] = mod; return Modify(dn, mods, queue, cons); } /// Asynchronously makes a set of changes to an existing entry in the /// directory. /// /// For example, this modify method can change attribute values, add new /// attribute values, or remove existing attribute values. /// /// Because the server applies all changes in an LdapModification array /// atomically, the application can expect that no changes /// have been performed if an error is returned. /// If the request fails with {@link LdapException.CONNECT_ERROR}, /// it is indeterminate whether or not the server made the modifications. /// /// /// The distinguished name of the entry to modify. /// /// /// The changes to be made to the entry. /// /// /// The queue for messages returned from a server in /// response to this request. If it is null, a /// queue object is created internally. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual LdapResponseQueue Modify(System.String dn, LdapModification[] mods, LdapResponseQueue queue) { return Modify(dn, mods, queue, defSearchCons); } /// Asynchronously makes a set of changes to an existing entry in the /// directory, using the specified constraints and queue. /// /// For example, this modify method can change attribute values, add new /// attribute values, or remove existing attribute values. /// /// Because the server applies all changes in an LdapModification array /// atomically, the application can expect that no changes /// have been performed if an error is returned. /// If the request fails with {@link LdapException.CONNECT_ERROR}, /// it is indeterminate whether or not the server made the modifications. /// /// /// The distinguished name of the entry to modify. /// /// /// The changes to be made to the entry. /// /// /// The queue for messages returned from a server in /// response to this request. If it is null, a /// queue object is created internally. /// /// /// Constraints specific to the operation. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual LdapResponseQueue Modify(System.String dn, LdapModification[] mods, LdapResponseQueue queue, LdapConstraints cons) { if ((System.Object) dn == null) { // Invalid DN parameter throw new System.ArgumentException(ExceptionMessages.DN_PARAM_ERROR); } if (cons == null) cons = defSearchCons; LdapMessage msg = new LdapModifyRequest(dn, mods, cons.getControls()); return SendRequestToServer(msg, cons.TimeLimit, queue, null); } //************************************************************************* // read methods //************************************************************************* /// Synchronously reads the entry for the specified distiguished name (DN) /// and retrieves all attributes for the entry. /// /// /// The distinguished name of the entry to retrieve. /// /// /// the LdapEntry read from the server. /// /// /// LdapException if the object was not found /// public virtual LdapEntry Read(System.String dn) { return Read(dn, defSearchCons); } /// /// Synchronously reads the entry for the specified distiguished name (DN), /// using the specified constraints, and retrieves all attributes for the /// entry. /// /// /// The distinguished name of the entry to retrieve. /// /// /// The constraints specific to the operation. /// /// /// the LdapEntry read from the server /// /// /// LdapException if the object was not found /// public virtual LdapEntry Read(System.String dn, LdapSearchConstraints cons) { return Read(dn, null, cons); } /// /// Synchronously reads the entry for the specified distinguished name (DN) /// and retrieves only the specified attributes from the entry. /// /// /// The distinguished name of the entry to retrieve. /// /// /// The names of the attributes to retrieve. /// /// /// the LdapEntry read from the server /// /// /// LdapException if the object was not found /// public virtual LdapEntry Read(System.String dn, System.String[] attrs) { return Read(dn, attrs, defSearchCons); } /// Synchronously reads the entry for the specified distinguished name (DN), /// using the specified constraints, and retrieves only the specified /// attributes from the entry. /// /// /// The distinguished name of the entry to retrieve. /// /// /// The names of the attributes to retrieve. /// /// /// The constraints specific to the operation. /// /// /// the LdapEntry read from the server /// /// /// LdapException if the object was not found /// public virtual LdapEntry Read(System.String dn, System.String[] attrs, LdapSearchConstraints cons) { LdapSearchResults sr = Search(dn, SCOPE_BASE, null, attrs, false, cons); LdapEntry ret = null; if (sr.hasMore()) { ret = sr.next(); if (sr.hasMore()) { // "Read response is ambiguous, multiple entries returned" throw new LdapLocalException(ExceptionMessages.READ_MULTIPLE, LdapException.AMBIGUOUS_RESPONSE); } } return ret; } /// Synchronously reads the entry specified by the Ldap URL. /// /// When this read method is called, a new connection is created /// automatically, using the host and port specified in the URL. After /// finding the entry, the method closes the connection (in other words, /// it disconnects from the Ldap server). /// /// If the URL specifies a filter and scope, they are not used. Of the /// information specified in the URL, this method only uses the Ldap host /// name and port number, the base distinguished name (DN), and the list /// of attributes to return. /// /// /// Ldap URL specifying the entry to read. /// /// /// The entry specified by the base DN. /// /// /// LdapException if the object was not found /// public static LdapEntry Read(LdapUrl toGet) { LdapConnection lconn = new LdapConnection(); lconn.Connect(toGet.Host, toGet.Port); LdapEntry toReturn = lconn.Read(toGet.getDN(), toGet.AttributeArray); lconn.Disconnect(); return toReturn; } /// Synchronously reads the entry specified by the Ldap URL, using the /// specified constraints. /// /// When this method is called, a new connection is created /// automatically, using the host and port specified in the URL. After /// finding the entry, the method closes the connection (in other words, /// it disconnects from the Ldap server). /// /// If the URL specifies a filter and scope, they are not used. Of the /// information specified in the URL, this method only uses the Ldap host /// name and port number, the base distinguished name (DN), and the list /// of attributes to return. /// /// /// The entry specified by the base DN. /// /// /// Ldap URL specifying the entry to read. /// /// /// Constraints specific to the operation. /// /// /// LdapException if the object was not found /// public static LdapEntry Read(LdapUrl toGet, LdapSearchConstraints cons) { LdapConnection lconn = new LdapConnection(); lconn.Connect(toGet.Host, toGet.Port); LdapEntry toReturn = lconn.Read(toGet.getDN(), toGet.AttributeArray, cons); lconn.Disconnect(); return toReturn; } //************************************************************************* // rename methods //************************************************************************* /// /// Synchronously renames an existing entry in the directory. /// /// /// The current distinguished name of the entry. /// /// /// The new relative distinguished name for the entry. /// /// /// If true, the old name is not retained as an /// attribute value. If false, the old name is /// retained as an attribute value. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual void Rename(System.String dn, System.String newRdn, bool deleteOldRdn) { Rename(dn, newRdn, deleteOldRdn, defSearchCons); return ; } /// /// Synchronously renames an existing entry in the directory, using the /// specified constraints. /// /// /// The current distinguished name of the entry. /// /// /// The new relative distinguished name for the entry. /// /// /// If true, the old name is not retained as an /// attribute value. If false, the old name is /// retained as an attribute value. /// /// /// The constraints specific to the operation. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual void Rename(System.String dn, System.String newRdn, bool deleteOldRdn, LdapConstraints cons) { // null for newParentdn means that this is originating as an Ldapv2 call Rename(dn, newRdn, null, deleteOldRdn, cons); return ; } /// Synchronously renames an existing entry in the directory, possibly /// repositioning the entry in the directory tree. /// /// /// The current distinguished name of the entry. /// /// /// The new relative distinguished name for the entry. /// /// /// The distinguished name of an existing entry which /// is to be the new parent of the entry. /// /// /// If true, the old name is not retained as an /// attribute value. If false, the old name is /// retained as an attribute value. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual void Rename(System.String dn, System.String newRdn, System.String newParentdn, bool deleteOldRdn) { Rename(dn, newRdn, newParentdn, deleteOldRdn, defSearchCons); return ; } /// /// Synchronously renames an existing entry in the directory, using the /// specified constraints and possibly repositioning the entry in the /// directory tree. /// /// /// The current distinguished name of the entry. /// /// /// The new relative distinguished name for the entry. /// /// /// The distinguished name of an existing entry which /// is to be the new parent of the entry. /// /// /// If true, the old name is not retained as an /// attribute value. If false, the old name is /// retained as an attribute value. /// /// /// The constraints specific to the operation. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual void Rename(System.String dn, System.String newRdn, System.String newParentdn, bool deleteOldRdn, LdapConstraints cons) { LdapResponseQueue queue = Rename(dn, newRdn, newParentdn, deleteOldRdn, null, cons); // Get a handle to the rename response LdapResponse renameResponse = (LdapResponse) (queue.getResponse()); // Set local copy of responseControls synchronously - if there were any lock (responseCtlSemaphore) { responseCtls = renameResponse.Controls; } chkResultCode(queue, cons, renameResponse); return ; } /* * rename */ /// Asynchronously renames an existing entry in the directory. /// /// /// The current distinguished name of the entry. /// /// /// The new relative distinguished name for the entry. /// /// /// If true, the old name is not retained as an /// attribute value. If false, the old name is /// retained as an attribute value. /// /// /// The queue for messages returned from a server in /// response to this request. If it is null, a /// queue object is created internally. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual LdapResponseQueue Rename(System.String dn, System.String newRdn, bool deleteOldRdn, LdapResponseQueue queue) { return Rename(dn, newRdn, deleteOldRdn, queue, defSearchCons); } /// Asynchronously renames an existing entry in the directory, using the /// specified constraints. /// /// /// The current distinguished name of the entry. /// /// /// The new relative distinguished name for the entry. /// /// /// If true, the old name is not retained as an /// attribute value. If false, the old name is /// retained as an attribute value. /// /// /// The queue for messages returned from a server in /// response to this request. If it is null, a /// queue object is created internally. /// /// /// The constraints specific to the operation. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual LdapResponseQueue Rename(System.String dn, System.String newRdn, bool deleteOldRdn, LdapResponseQueue queue, LdapConstraints cons) { return Rename(dn, newRdn, null, deleteOldRdn, queue, cons); } /// Asynchronously renames an existing entry in the directory, possibly /// repositioning the entry in the directory. /// /// /// The current distinguished name of the entry. /// /// /// The new relative distinguished name for the entry. /// /// /// The distinguished name of an existing entry which /// is to be the new parent of the entry. /// /// /// If true, the old name is not retained as an /// attribute value. If false, the old name is /// retained as an attribute value. /// /// /// The queue for messages returned from a server in /// response to this request. If it is null, a /// queue object is created internally. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual LdapResponseQueue Rename(System.String dn, System.String newRdn, System.String newParentdn, bool deleteOldRdn, LdapResponseQueue queue) { return Rename(dn, newRdn, newParentdn, deleteOldRdn, queue, defSearchCons); } /// Asynchronously renames an existing entry in the directory, using the /// specified constraints and possibily repositioning the entry in the /// directory. /// /// /// The current distinguished name of the entry. /// /// /// The new relative distinguished name for the entry. /// /// /// The distinguished name of an existing entry which /// is to be the new parent of the entry. /// /// /// If true, the old name is not retained as an /// attribute value. If false, the old name is /// retained as an attribute value. /// /// /// The queue for messages returned from a server in /// response to this request. If it is null, a /// queue object is created internally. /// /// /// The constraints specific to the operation. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual LdapResponseQueue Rename(System.String dn, System.String newRdn, System.String newParentdn, bool deleteOldRdn, LdapResponseQueue queue, LdapConstraints cons) { if ((System.Object) dn == null || (System.Object) newRdn == null) { // Invalid DN or RDN parameter throw new System.ArgumentException(ExceptionMessages.RDN_PARAM_ERROR); } if (cons == null) cons = defSearchCons; LdapMessage msg = new LdapModifyDNRequest(dn, newRdn, newParentdn, deleteOldRdn, cons.getControls()); return SendRequestToServer(msg, cons.TimeLimit, queue, null); } //************************************************************************* // search methods //************************************************************************* /// /// Synchronously performs the search specified by the parameters. /// /// /// The base distinguished name to search from. /// /// /// The scope of the entries to search. The following /// are the valid options: /// /// /// Search filter specifying the search criteria. /// /// /// Names of attributes to retrieve. /// /// /// If true, returns the names but not the values of /// the attributes found. If false, returns the /// names and values for attributes found. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual LdapSearchResults Search(System.String base_Renamed, int scope, System.String filter, System.String[] attrs, bool typesOnly) { return Search(base_Renamed, scope, filter, attrs, typesOnly, defSearchCons); } /// /// Synchronously performs the search specified by the parameters, /// using the specified search constraints (such as the /// maximum number of entries to find or the maximum time to wait for /// search results). /// /// As part of the search constraints, the method allows specifying /// whether or not the results are to be delivered all at once or in /// smaller batches. If specified that the results are to be delivered in /// smaller batches, each iteration blocks only until the next batch of /// results is returned. /// /// /// The base distinguished name to search from. /// /// /// The scope of the entries to search. The following /// are the valid options: /// /// /// The search filter specifying the search criteria. /// /// /// The names of attributes to retrieve. /// /// /// If true, returns the names but not the values of /// the attributes found. If false, returns the /// names and values for attributes found. /// /// /// The constraints specific to the search. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual LdapSearchResults Search(System.String base_Renamed, int scope, System.String filter, System.String[] attrs, bool typesOnly, LdapSearchConstraints cons) { LdapSearchQueue queue = Search(base_Renamed, scope, filter, attrs, typesOnly, null, cons); if (cons == null) cons = defSearchCons; return new LdapSearchResults(this, queue, cons); } /// Asynchronously performs the search specified by the parameters. /// /// /// The base distinguished name to search from. /// /// /// The scope of the entries to search. The following /// are the valid options: /// /// /// Search filter specifying the search criteria. /// /// /// Names of attributes to retrieve. /// /// /// If true, returns the names but not the values of /// the attributes found. If false, returns the /// names and values for attributes found. /// /// /// Handler for messages returned from a server in /// response to this request. If it is null, a /// queue object is created internally. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual LdapSearchQueue Search(System.String base_Renamed, int scope, System.String filter, System.String[] attrs, bool typesOnly, LdapSearchQueue queue) { return Search(base_Renamed, scope, filter, attrs, typesOnly, queue, defSearchCons); } /// Asynchronously performs the search specified by the parameters, /// also allowing specification of constraints for the search (such /// as the maximum number of entries to find or the maximum time to /// wait for search results). /// /// /// The base distinguished name to search from. /// /// /// The scope of the entries to search. The following /// are the valid options: /// /// /// The search filter specifying the search criteria. /// /// /// The names of attributes to retrieve. /// /// /// If true, returns the names but not the values of /// the attributes found. If false, returns the /// names and values for attributes found. /// /// /// The queue for messages returned from a server in /// response to this request. If it is null, a /// queue object is created internally. /// /// /// The constraints specific to the search. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public virtual LdapSearchQueue Search(System.String base_Renamed, int scope, System.String filter, System.String[] attrs, bool typesOnly, LdapSearchQueue queue, LdapSearchConstraints cons) { if ((System.Object) filter == null) { filter = "objectclass=*"; } if (cons == null) cons = defSearchCons; LdapMessage msg = new LdapSearchRequest(base_Renamed, scope, filter, attrs, cons.Dereference, cons.MaxResults, cons.ServerTimeLimit, typesOnly, cons.getControls()); MessageAgent agent; LdapSearchQueue myqueue = queue; if (myqueue == null) { agent = new MessageAgent(); myqueue = new LdapSearchQueue(agent); } else { agent = queue.MessageAgent; } try { agent.sendMessage(conn, msg, cons.TimeLimit, myqueue, null); } catch (LdapException lex) { throw lex; } return myqueue; } /* * Ldap URL search */ /// Synchronously performs the search specified by the Ldap URL, returning /// an enumerable LdapSearchResults object. /// /// /// The Ldap URL specifying the entry to read. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public static LdapSearchResults Search(LdapUrl toGet) { // Get a clone of default search constraints, method alters batchSize return Search(toGet, null); } /* * Ldap URL search */ /// Synchronously perfoms the search specified by the Ldap URL, using /// the specified search constraints (such as the maximum number of /// entries to find or the maximum time to wait for search results). /// /// When this method is called, a new connection is created /// automatically, using the host and port specified in the URL. After /// all search results have been received from the server, the method /// closes the connection (in other words, it disconnects from the Ldap /// server). /// /// As part of the search constraints, a choice can be made as to whether /// to have the results delivered all at once or in smaller batches. If /// the results are to be delivered in smaller batches, each iteration /// blocks only until the next batch of results is returned. /// /// /// /// Ldap URL specifying the entry to read. /// /// /// The constraints specific to the search. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// public static LdapSearchResults Search(LdapUrl toGet, LdapSearchConstraints cons) { LdapConnection lconn = new LdapConnection(); lconn.Connect(toGet.Host, toGet.Port); if (cons == null) { // This is a clone, so we already have our own copy cons = lconn.SearchConstraints; } else { // get our own copy of user's constraints because we modify it cons = (LdapSearchConstraints) cons.Clone(); } cons.BatchSize = 0; // Must wait until all results arrive LdapSearchResults toReturn = lconn.Search(toGet.getDN(), toGet.Scope, toGet.Filter, toGet.AttributeArray, false, cons); lconn.Disconnect(); return toReturn; } /// Sends an Ldap request to a directory server. /// /// The specified the Ldap request is sent to the directory server /// associated with this connection using default constraints. An Ldap /// request object is a subclass {@link LdapMessage} with the operation /// type set to one of the request types. You can build a request by using /// the request classes found in this package /// /// You should note that, since Ldap requests sent to the server /// using sendRequest are asynchronous, automatic referral following /// does not apply to these requests. /// /// /// The Ldap request to send to the directory server. /// /// The queue for messages returned from a server in /// response to this request. If it is null, a /// queue object is created internally. /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// /// /// /// /// /// public virtual LdapMessageQueue SendRequest(LdapMessage request, LdapMessageQueue queue) { return SendRequest(request, queue, null); } /// Sends an Ldap request to a directory server. /// /// The specified the Ldap request is sent to the directory server /// associated with this connection. An Ldap request object is an /// {@link LdapMessage} with the operation type set to one of the request /// types. You can build a request by using the request classes found in this /// package /// /// You should note that, since Ldap requests sent to the server /// using sendRequest are asynchronous, automatic referral following /// does not apply to these requests. /// /// /// The Ldap request to send to the directory server. /// /// The queue for messages returned from a server in /// response to this request. If it is null, a /// queue object is created internally. /// /// The constraints that apply to this request /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// /// /// /// /// /// public virtual LdapMessageQueue SendRequest(LdapMessage request, LdapMessageQueue queue, LdapConstraints cons) { if (!request.Request) { throw new System.SystemException("Object is not a request message"); } if (cons == null) { cons = defSearchCons; } // Get the correct queue for a search request MessageAgent agent; LdapMessageQueue myqueue = queue; if (myqueue == null) { agent = new MessageAgent(); if (request.Type == LdapMessage.SEARCH_REQUEST) { myqueue = new LdapSearchQueue(agent); } else { myqueue = new LdapResponseQueue(agent); } } else { if (request.Type == LdapMessage.SEARCH_REQUEST) { agent = queue.MessageAgent; } else { agent = queue.MessageAgent; } } try { agent.sendMessage(conn, request, cons.TimeLimit, myqueue, null); } catch (LdapException lex) { throw lex; } return myqueue; } //************************************************************************* // helper methods //************************************************************************* /// Locates the appropriate message agent and sends /// the Ldap request to a directory server. /// /// /// the message to send /// /// /// the timeout value /// /// /// the response queue or null /// /// /// the LdapResponseQueue for this request /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// private LdapResponseQueue SendRequestToServer(LdapMessage msg, int timeout, LdapResponseQueue queue, BindProperties bindProps) { MessageAgent agent; if (queue == null) { agent = new MessageAgent(); queue = new LdapResponseQueue(agent); } else { agent = queue.MessageAgent; } agent.sendMessage(conn, msg, timeout, queue, bindProps); return queue; } /// get an LdapConnection object so that we can follow a referral. /// This function is never called if cons.getReferralFollowing() returns /// false. /// /// /// the array of referral strings /// /// /// /// The referralInfo object /// /// /// LdapReferralException A general exception which includes /// an error message and an Ldap error code. /// private ReferralInfo getReferralConnection(System.String[] referrals) { ReferralInfo refInfo = null; System.Exception ex = null; LdapConnection rconn = null; LdapReferralHandler rh = defSearchCons.getReferralHandler(); int i = 0; // Check if we use LdapRebind to get authentication credentials if ((rh == null) || (rh is LdapAuthHandler)) { for (i = 0; i < referrals.Length; i++) { // dn, pw are null in the default case (anonymous bind) System.String dn = null; sbyte[] pw = null; try { rconn = new LdapConnection(); rconn.Constraints = defSearchCons; LdapUrl url = new LdapUrl(referrals[i]); rconn.Connect(url.Host, url.Port); if (rh != null) { if (rh is LdapAuthHandler) { // Get application supplied dn and pw LdapAuthProvider ap = ((LdapAuthHandler) rh).getAuthProvider(url.Host, url.Port); dn = ap.DN; pw = ap.Password; } } rconn.Bind(Ldap_V3, dn, pw); ex = null; refInfo = new ReferralInfo(rconn, referrals, url); // Indicate this connection created to follow referral rconn.Connection.ActiveReferral = refInfo; break; } catch (System.Exception lex) { if (rconn != null) { try { rconn.Disconnect(); rconn = null; ex = lex; } catch (LdapException e) { ; // ignore } } } } } // Check if application gets connection and does bind else { // rh instanceof LdapBind try { rconn = ((LdapBindHandler) rh).Bind(referrals, this); if (rconn == null) { LdapReferralException rex = new LdapReferralException(ExceptionMessages.REFERRAL_ERROR); rex.setReferrals(referrals); throw rex; } // Figure out which Url belongs to the connection for (int idx = 0; idx < referrals.Length; idx++) { try { LdapUrl url = new LdapUrl(referrals[idx]); if (url.Host.ToUpper().Equals(rconn.Host.ToUpper()) && (url.Port == rconn.Port)) { refInfo = new ReferralInfo(rconn, referrals, url); break; } } catch (System.Exception e) { ; // ignore } } if (refInfo == null) { // Could not match LdapBind.bind() connecction with URL list ex = new LdapLocalException(ExceptionMessages.REFERRAL_BIND_MATCH, LdapException.CONNECT_ERROR); } } catch (System.Exception lex) { rconn = null; ex = lex; } } if (ex != null) { // Could not connect to any server, throw an exception LdapException ldapex; if (ex is LdapReferralException) { throw (LdapReferralException) ex; } else if (ex is LdapException) { ldapex = (LdapException) ex; } else { ldapex = new LdapLocalException(ExceptionMessages.SERVER_CONNECT_ERROR, new System.Object[]{conn.Host}, LdapException.CONNECT_ERROR, ex); } // Error attempting to follow a referral LdapReferralException rex = new LdapReferralException(ExceptionMessages.REFERRAL_ERROR, ldapex); rex.setReferrals(referrals); // Use last URL string for the failed referral rex.FailedReferral = referrals[referrals.Length - 1]; throw rex; } // We now have an authenticated connection // to be used to follow the referral. return refInfo; } /// Check the result code and throw an exception if needed. /// /// If referral following is enabled, checks if we need to /// follow a referral /// /// /// - the message queue of the current response /// /// /// - the constraints that apply to the request /// /// /// - the LdapResponse to check /// private void chkResultCode(LdapMessageQueue queue, LdapConstraints cons, LdapResponse response) { if ((response.ResultCode == LdapException.REFERRAL) && cons.ReferralFollowing) { // Perform referral following and return System.Collections.ArrayList refConn = null; try { chaseReferral(queue, cons, response, response.Referrals, 0, false, null); } finally { releaseReferralConnections(refConn); } } else { // Throws exception for non success result response.chkResultCode(); } return ; } /// Follow referrals if necessary referral following enabled. /// This function is called only by synchronous requests. /// Search responses come here only if referral following is /// enabled and if we are processing a SearchResultReference /// or a Response with a status of REFERRAL, i.e. we are /// going to follow a referral. /// /// This functions recursively follows a referral until a result /// is returned or until the hop limit is reached. /// /// /// The LdapResponseQueue for this request /// /// /// The constraints that apply to the request /// /// /// The referral or search reference response message /// /// /// The referral array returned from the /// initial request. /// /// /// the number of hops already used while /// following this referral /// /// /// true if the message is a search reference /// /// /// An optional array list used to store /// the LdapConnection objects used in following the referral. /// /// /// The array list used to store the all LdapConnection objects /// used in following the referral. The list will be empty /// if there were none. /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// /* package */ internal virtual System.Collections.ArrayList chaseReferral(LdapMessageQueue queue, LdapConstraints cons, LdapMessage msg, System.String[] initialReferrals, int hopCount, bool searchReference, System.Collections.ArrayList connectionList) { System.Collections.ArrayList connList = connectionList; LdapConnection rconn = null; // new conn for following referral ReferralInfo rinfo = null; // referral info LdapMessage origMsg; // Get a place to store new connections if (connList == null) { connList = new System.Collections.ArrayList(cons.HopLimit); } // Following referrals or search reference System.String[] refs; // referral list if (initialReferrals != null) { // Search continuation reference from a search request refs = initialReferrals; origMsg = msg.RequestingMessage; } else { // Not a search request LdapResponse resp = (LdapResponse) queue.getResponse(); if (resp.ResultCode != LdapException.REFERRAL) { // Not referral result,throw Exception if nonzero result resp.chkResultCode(); return connList; } // We have a referral response refs = resp.Referrals; origMsg = resp.RequestingMessage; } LdapUrl refUrl; // referral represented as URL try { // increment hop count, check max hops if (hopCount++ > cons.HopLimit) { throw new LdapLocalException("Max hops exceeded", LdapException.REFERRAL_LIMIT_EXCEEDED); } // Get a connection to follow the referral rinfo = getReferralConnection(refs); rconn = rinfo.ReferralConnection; refUrl = rinfo.ReferralUrl; connList.Add(rconn); // rebuild msg into new msg changing msgID,dn,scope,filter LdapMessage newMsg = rebuildRequest(origMsg, refUrl, searchReference); // Send new message on new connection try { MessageAgent agent; if (queue is LdapResponseQueue) { agent = queue.MessageAgent; } else { agent = queue.MessageAgent; } agent.sendMessage(rconn.Connection, newMsg, defSearchCons.TimeLimit, queue, null); } catch (InterThreadException ex) { // Error ending request to referred server LdapReferralException rex = new LdapReferralException(ExceptionMessages.REFERRAL_SEND, LdapException.CONNECT_ERROR, null, ex); rex.setReferrals(initialReferrals); ReferralInfo ref_Renamed = rconn.Connection.ActiveReferral; rex.FailedReferral = ref_Renamed.ReferralUrl.ToString(); throw rex; } if (initialReferrals == null) { // For operation results, when all responses are complete, // the stack unwinds back to the original and returns // to the application. // An exception is thrown for an error connList = chaseReferral(queue, cons, null, null, hopCount, false, connList); } else { // For search, just return to LdapSearchResults object return connList; } } catch (System.Exception ex) { if (ex is LdapReferralException) { throw (LdapReferralException) ex; } else { // Set referral list and failed referral LdapReferralException rex = new LdapReferralException(ExceptionMessages.REFERRAL_ERROR, ex); rex.setReferrals(refs); if (rinfo != null) { rex.FailedReferral = rinfo.ReferralUrl.ToString(); } else { rex.FailedReferral = refs[refs.Length - 1]; } throw rex; } } return connList; } /// Builds a new request replacing dn, scope, and filter where approprate /// /// /// the original LdapMessage to build the new request from /// /// /// the referral url /// /// /// a new LdapMessage with appropriate information replaced /// /// /// LdapException A general exception which includes an error /// message and an Ldap error code. /// private LdapMessage rebuildRequest(LdapMessage msg, LdapUrl url, bool reference) { System.String dn = url.getDN(); // new base System.String filter = null; switch (msg.Type) { case LdapMessage.SEARCH_REQUEST: if (reference) { filter = url.Filter; } break; // We are allowed to get a referral for the following case LdapMessage.ADD_REQUEST: case LdapMessage.BIND_REQUEST: case LdapMessage.COMPARE_REQUEST: case LdapMessage.DEL_REQUEST: case LdapMessage.EXTENDED_REQUEST: case LdapMessage.MODIFY_RDN_REQUEST: case LdapMessage.MODIFY_REQUEST: break; // The following return no response case LdapMessage.ABANDON_REQUEST: case LdapMessage.UNBIND_REQUEST: default: throw new LdapLocalException(ExceptionMessages.IMPROPER_REFERRAL, new System.Object[]{msg.Type}, LdapException.LOCAL_ERROR); } return msg.Clone(dn, filter, reference); } /* * Release connections acquired by following referrals * * @param list the list of the connections */ /* package */ internal virtual void releaseReferralConnections(System.Collections.ArrayList list) { if (list == null) { return ; } // Release referral connections for (int i = list.Count - 1; i >= 0; i--) { LdapConnection rconn = null; try { rconn=(LdapConnection)list[i]; list.RemoveAt(i); // rconn = (LdapConnection) list.RemoveAt(i); rconn.Disconnect(); } catch (System.IndexOutOfRangeException ex) { continue; } catch (LdapException lex) { continue; } } return ; } //************************************************************************* // Schema Related methods //************************************************************************* /// Retrieves the schema associated with a particular schema DN in the /// directory server. /// The schema DN for a particular entry is obtained by calling the /// getSchemaDN method of LDAPConnection /// /// /// The schema DN used to fetch the schema. /// /// /// An LDAPSchema entry containing schema attributes. If the /// entry contains no schema attributes then the returned LDAPSchema object /// will be empty. /// /// /// LDAPException This exception occurs if the schema entry /// cannot be retrieved with this connection. /// /// /// /// /// public virtual LdapSchema FetchSchema(System.String schemaDN) { LdapEntry ent = Read(schemaDN, LdapSchema.schemaTypeNames); return new LdapSchema(ent); } /// Retrieves the Distiguished Name (DN) for the schema advertised in the /// root DSE of the Directory Server. /// /// The DN can be used with the methods fetchSchema and modify to retreive /// and extend schema definitions. The schema entry is located by reading /// subschemaSubentry attribute of the root DSE. This is equivalent to /// calling {@link #getSchemaDN(String) } with the DN parameter as an empty /// string: getSchemaDN(""). /// /// /// /// Distinguished Name of a schema entry in effect for the /// Directory. /// /// LDAPException This exception occurs if the schema DN /// cannot be retrieved, or if the subschemaSubentry attribute associated /// with the root DSE contains multiple values. /// /// /// /// /// /// public virtual System.String GetSchemaDN() { return GetSchemaDN(""); } /// Retrieves the Distiguished Name (DN) of the schema associated with a /// entry in the Directory. /// /// The DN can be used with the methods fetchSchema and modify to retreive /// and extend schema definitions. Reads the subschemaSubentry of the entry /// specified. /// /// /// Distinguished Name of any entry. The subschemaSubentry /// attribute is queried from this entry. /// /// /// Distinguished Name of a schema entry in effect for the entry /// identified by dn. /// /// /// LDAPException This exception occurs if a null or empty /// value is passed as dn, if the subschemasubentry attribute cannot /// be retrieved, or the subschemasubentry contains multiple values. /// /// /// /// /// /// public virtual System.String GetSchemaDN(System.String dn) { System.String[] attrSubSchema = new System.String[]{"subschemaSubentry"}; /* Read the entries subschemaSubentry attribute. Throws an exception if * no entries are returned. */ LdapEntry ent = this.Read(dn, attrSubSchema); LdapAttribute attr = ent.getAttribute(attrSubSchema[0]); System.String[] values = attr.StringValueArray; if (values == null || values.Length < 1) { throw new LdapLocalException(ExceptionMessages.NO_SCHEMA, new System.Object[]{dn}, LdapException.NO_RESULTS_RETURNED); } else if (values.Length > 1) { throw new LdapLocalException(ExceptionMessages.MULTIPLE_SCHEMA, new System.Object[]{dn}, LdapException.CONSTRAINT_VIOLATION); } return values[0]; } } }