001 /*
002 * Copyright 2007-2014 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2014 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021 package com.unboundid.ldap.sdk;
022
023
024
025 import java.util.concurrent.LinkedBlockingQueue;
026 import java.util.concurrent.TimeUnit;
027
028 import com.unboundid.asn1.ASN1OctetString;
029 import com.unboundid.ldap.protocol.BindRequestProtocolOp;
030 import com.unboundid.ldap.protocol.LDAPMessage;
031 import com.unboundid.ldap.protocol.LDAPResponse;
032 import com.unboundid.util.Extensible;
033 import com.unboundid.util.InternalUseOnly;
034 import com.unboundid.util.ThreadSafety;
035 import com.unboundid.util.ThreadSafetyLevel;
036
037 import static com.unboundid.ldap.sdk.LDAPMessages.*;
038 import static com.unboundid.util.Debug.*;
039 import static com.unboundid.util.StaticUtils.*;
040
041
042
043 /**
044 * This class provides an API that should be used to represent an LDAPv3 SASL
045 * bind request. A SASL bind includes a SASL mechanism name and an optional set
046 * of credentials.
047 * <BR><BR>
048 * See <A HREF="http://www.ietf.org/rfc/rfc4422.txt">RFC 4422</A> for more
049 * information about the Simple Authentication and Security Layer.
050 */
051 @Extensible()
052 @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
053 public abstract class SASLBindRequest
054 extends BindRequest
055 implements ResponseAcceptor
056 {
057 /**
058 * The BER type to use for the credentials element in a simple bind request
059 * protocol op.
060 */
061 protected static final byte CRED_TYPE_SASL = (byte) 0xA3;
062
063
064
065 /**
066 * The serial version UID for this serializable class.
067 */
068 private static final long serialVersionUID = -5842126553864908312L;
069
070
071
072 // The message ID to use for LDAP messages used in bind processing.
073 private int messageID;
074
075 // The queue used to receive responses from the server.
076 private final LinkedBlockingQueue<LDAPResponse> responseQueue;
077
078
079
080 /**
081 * Creates a new SASL bind request with the provided controls.
082 *
083 * @param controls The set of controls to include in this SASL bind request.
084 */
085 protected SASLBindRequest(final Control[] controls)
086 {
087 super(controls);
088
089 messageID = -1;
090 responseQueue = new LinkedBlockingQueue<LDAPResponse>();
091 }
092
093
094
095 /**
096 * {@inheritDoc}
097 */
098 @Override()
099 public String getBindType()
100 {
101 return getSASLMechanismName();
102 }
103
104
105
106 /**
107 * Retrieves the name of the SASL mechanism used in this SASL bind request.
108 *
109 * @return The name of the SASL mechanism used in this SASL bind request.
110 */
111 public abstract String getSASLMechanismName();
112
113
114
115 /**
116 * {@inheritDoc}
117 */
118 @Override()
119 public int getLastMessageID()
120 {
121 return messageID;
122 }
123
124
125
126 /**
127 * Sends an LDAP message to the directory server and waits for the response.
128 *
129 * @param connection The connection to the directory server.
130 * @param bindDN The bind DN to use for the request. It should be
131 * {@code null} for most types of SASL bind requests.
132 * @param saslCredentials The SASL credentials to use for the bind request.
133 * It may be {@code null} if no credentials are
134 * required.
135 * @param controls The set of controls to include in the request. It
136 * may be {@code null} if no controls are required.
137 * @param timeoutMillis The maximum length of time in milliseconds to wait
138 * for a response, or zero if it should wait forever.
139 *
140 * @return The bind response message returned by the directory server.
141 *
142 * @throws LDAPException If a problem occurs while sending the request or
143 * reading the response, or if a timeout occurred
144 * while waiting for the response.
145 */
146 protected final BindResult sendBindRequest(final LDAPConnection connection,
147 final String bindDN,
148 final ASN1OctetString saslCredentials,
149 final Control[] controls,
150 final long timeoutMillis)
151 throws LDAPException
152 {
153 if (messageID == -1)
154 {
155 messageID = connection.nextMessageID();
156 }
157
158 final BindRequestProtocolOp protocolOp =
159 new BindRequestProtocolOp(bindDN, getSASLMechanismName(),
160 saslCredentials);
161
162 final LDAPMessage requestMessage =
163 new LDAPMessage(messageID, protocolOp, controls);
164 return sendMessage(connection, requestMessage, timeoutMillis);
165 }
166
167
168
169 /**
170 * Sends an LDAP message to the directory server and waits for the response.
171 *
172 * @param connection The connection to the directory server.
173 * @param requestMessage The LDAP message to send to the directory server.
174 * @param timeoutMillis The maximum length of time in milliseconds to wait
175 * for a response, or zero if it should wait forever.
176 *
177 * @return The response message received from the server.
178 *
179 * @throws LDAPException If a problem occurs while sending the request or
180 * reading the response, or if a timeout occurred
181 * while waiting for the response.
182 */
183 protected final BindResult sendMessage(final LDAPConnection connection,
184 final LDAPMessage requestMessage,
185 final long timeoutMillis)
186 throws LDAPException
187 {
188 if (connection.synchronousMode())
189 {
190 return sendMessageSync(connection, requestMessage, timeoutMillis);
191 }
192
193 final int msgID = requestMessage.getMessageID();
194 connection.registerResponseAcceptor(msgID, this);
195 try
196 {
197 final long requestTime = System.nanoTime();
198 connection.getConnectionStatistics().incrementNumBindRequests();
199 connection.sendMessage(requestMessage);
200
201 // Wait for and process the response.
202 final LDAPResponse response;
203 try
204 {
205 if (timeoutMillis > 0)
206 {
207 response = responseQueue.poll(timeoutMillis, TimeUnit.MILLISECONDS);
208 }
209 else
210 {
211 response = responseQueue.take();
212 }
213 }
214 catch (InterruptedException ie)
215 {
216 debugException(ie);
217 throw new LDAPException(ResultCode.LOCAL_ERROR,
218 ERR_BIND_INTERRUPTED.get(connection.getHostPort()), ie);
219 }
220
221 return handleResponse(connection, response, requestTime);
222 }
223 finally
224 {
225 connection.deregisterResponseAcceptor(msgID);
226 }
227 }
228
229
230
231 /**
232 * Sends an LDAP message to the directory server and waits for the response.
233 * This should only be used when the connection is operating in synchronous
234 * mode.
235 *
236 * @param connection The connection to the directory server.
237 * @param requestMessage The LDAP message to send to the directory server.
238 * @param timeoutMillis The maximum length of time in milliseconds to wait
239 * for a response, or zero if it should wait forever.
240 *
241 * @return The response message received from the server.
242 *
243 * @throws LDAPException If a problem occurs while sending the request or
244 * reading the response, or if a timeout occurred
245 * while waiting for the response.
246 */
247 private BindResult sendMessageSync(final LDAPConnection connection,
248 final LDAPMessage requestMessage,
249 final long timeoutMillis)
250 throws LDAPException
251 {
252 // Set the appropriate timeout on the socket.
253 try
254 {
255 connection.getConnectionInternals(true).getSocket().setSoTimeout(
256 (int) timeoutMillis);
257 }
258 catch (Exception e)
259 {
260 debugException(e);
261 }
262
263
264 final int msgID = requestMessage.getMessageID();
265 final long requestTime = System.nanoTime();
266 connection.getConnectionStatistics().incrementNumBindRequests();
267 connection.sendMessage(requestMessage);
268
269 while (true)
270 {
271 final LDAPResponse response = connection.readResponse(messageID);
272 if (response instanceof IntermediateResponse)
273 {
274 final IntermediateResponseListener listener =
275 getIntermediateResponseListener();
276 if (listener != null)
277 {
278 listener.intermediateResponseReturned(
279 (IntermediateResponse) response);
280 }
281 }
282 else
283 {
284 return handleResponse(connection, response, requestTime);
285 }
286 }
287 }
288
289
290
291 /**
292 * Performs the necessary processing for handling a response.
293 *
294 * @param connection The connection used to read the response.
295 * @param response The response to be processed.
296 * @param requestTime The time the request was sent to the server.
297 *
298 * @return The bind result.
299 *
300 * @throws LDAPException If a problem occurs.
301 */
302 private BindResult handleResponse(final LDAPConnection connection,
303 final LDAPResponse response,
304 final long requestTime)
305 throws LDAPException
306 {
307 if (response == null)
308 {
309 final long waitTime = nanosToMillis(System.nanoTime() - requestTime);
310 throw new LDAPException(ResultCode.TIMEOUT,
311 ERR_SASL_BIND_CLIENT_TIMEOUT.get(waitTime, getSASLMechanismName(),
312 messageID, connection.getHostPort()));
313 }
314
315 if (response instanceof ConnectionClosedResponse)
316 {
317 final ConnectionClosedResponse ccr = (ConnectionClosedResponse) response;
318 final String message = ccr.getMessage();
319 if (message == null)
320 {
321 // The connection was closed while waiting for the response.
322 throw new LDAPException(ccr.getResultCode(),
323 ERR_CONN_CLOSED_WAITING_FOR_BIND_RESPONSE.get(
324 connection.getHostPort(), toString()));
325 }
326 else
327 {
328 // The connection was closed while waiting for the response.
329 throw new LDAPException(ccr.getResultCode(),
330 ERR_CONN_CLOSED_WAITING_FOR_BIND_RESPONSE_WITH_MESSAGE.get(
331 connection.getHostPort(), toString(), message));
332 }
333 }
334
335 connection.getConnectionStatistics().incrementNumBindResponses(
336 System.nanoTime() - requestTime);
337 return (BindResult) response;
338 }
339
340
341
342 /**
343 * {@inheritDoc}
344 */
345 @InternalUseOnly()
346 public final void responseReceived(final LDAPResponse response)
347 throws LDAPException
348 {
349 try
350 {
351 responseQueue.put(response);
352 }
353 catch (Exception e)
354 {
355 debugException(e);
356 throw new LDAPException(ResultCode.LOCAL_ERROR,
357 ERR_EXCEPTION_HANDLING_RESPONSE.get(getExceptionMessage(e)), e);
358 }
359 }
360 }