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 */ 021package com.unboundid.ldap.sdk; 022 023 024 025import java.util.concurrent.LinkedBlockingQueue; 026import java.util.concurrent.TimeUnit; 027 028import com.unboundid.asn1.ASN1OctetString; 029import com.unboundid.ldap.protocol.BindRequestProtocolOp; 030import com.unboundid.ldap.protocol.LDAPMessage; 031import com.unboundid.ldap.protocol.LDAPResponse; 032import com.unboundid.util.Extensible; 033import com.unboundid.util.InternalUseOnly; 034import com.unboundid.util.ThreadSafety; 035import com.unboundid.util.ThreadSafetyLevel; 036 037import static com.unboundid.ldap.sdk.LDAPMessages.*; 038import static com.unboundid.util.Debug.*; 039import 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) 053public 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}