001/* 002 * Copyright 2008-2016 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2016 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.util; 022 023 024 025import java.io.OutputStream; 026import java.util.ArrayList; 027import java.util.Collections; 028import java.util.LinkedHashSet; 029import java.util.List; 030import java.util.Set; 031import java.util.concurrent.atomic.AtomicReference; 032import javax.net.SocketFactory; 033import javax.net.ssl.KeyManager; 034import javax.net.ssl.SSLSocketFactory; 035import javax.net.ssl.TrustManager; 036 037import com.unboundid.ldap.sdk.AggregatePostConnectProcessor; 038import com.unboundid.ldap.sdk.BindRequest; 039import com.unboundid.ldap.sdk.Control; 040import com.unboundid.ldap.sdk.ExtendedResult; 041import com.unboundid.ldap.sdk.LDAPConnection; 042import com.unboundid.ldap.sdk.LDAPConnectionOptions; 043import com.unboundid.ldap.sdk.LDAPConnectionPool; 044import com.unboundid.ldap.sdk.LDAPConnectionPoolHealthCheck; 045import com.unboundid.ldap.sdk.LDAPException; 046import com.unboundid.ldap.sdk.PostConnectProcessor; 047import com.unboundid.ldap.sdk.ResultCode; 048import com.unboundid.ldap.sdk.RoundRobinServerSet; 049import com.unboundid.ldap.sdk.ServerSet; 050import com.unboundid.ldap.sdk.SimpleBindRequest; 051import com.unboundid.ldap.sdk.SingleServerSet; 052import com.unboundid.ldap.sdk.StartTLSPostConnectProcessor; 053import com.unboundid.ldap.sdk.extensions.StartTLSExtendedRequest; 054import com.unboundid.util.args.ArgumentException; 055import com.unboundid.util.args.ArgumentParser; 056import com.unboundid.util.args.BooleanArgument; 057import com.unboundid.util.args.DNArgument; 058import com.unboundid.util.args.FileArgument; 059import com.unboundid.util.args.IntegerArgument; 060import com.unboundid.util.args.StringArgument; 061import com.unboundid.util.ssl.KeyStoreKeyManager; 062import com.unboundid.util.ssl.PromptTrustManager; 063import com.unboundid.util.ssl.SSLUtil; 064import com.unboundid.util.ssl.TrustAllTrustManager; 065import com.unboundid.util.ssl.TrustStoreTrustManager; 066 067import static com.unboundid.util.Debug.*; 068import static com.unboundid.util.StaticUtils.*; 069import static com.unboundid.util.UtilityMessages.*; 070 071 072 073/** 074 * This class provides a basis for developing command-line tools that 075 * communicate with an LDAP directory server. It provides a common set of 076 * options for connecting and authenticating to a directory server, and then 077 * provides a mechanism for obtaining connections and connection pools to use 078 * when communicating with that server. 079 * <BR><BR> 080 * The arguments that this class supports include: 081 * <UL> 082 * <LI>"-h {address}" or "--hostname {address}" -- Specifies the address of 083 * the directory server. If this isn't specified, then a default of 084 * "localhost" will be used.</LI> 085 * <LI>"-p {port}" or "--port {port}" -- Specifies the port number of the 086 * directory server. If this isn't specified, then a default port of 389 087 * will be used.</LI> 088 * <LI>"-D {bindDN}" or "--bindDN {bindDN}" -- Specifies the DN to use to bind 089 * to the directory server using simple authentication. If this isn't 090 * specified, then simple authentication will not be performed.</LI> 091 * <LI>"-w {password}" or "--bindPassword {password}" -- Specifies the 092 * password to use when binding with simple authentication or a 093 * password-based SASL mechanism.</LI> 094 * <LI>"-j {path}" or "--bindPasswordFile {path}" -- Specifies the path to the 095 * file containing the password to use when binding with simple 096 * authentication or a password-based SASL mechanism.</LI> 097 * <LI>"--promptForBindPassword" -- Indicates that the tool should 098 * interactively prompt the user for the bind password.</LI> 099 * <LI>"-Z" or "--useSSL" -- Indicates that the communication with the server 100 * should be secured using SSL.</LI> 101 * <LI>"-q" or "--useStartTLS" -- Indicates that the communication with the 102 * server should be secured using StartTLS.</LI> 103 * <LI>"-X" or "--trustAll" -- Indicates that the client should trust any 104 * certificate that the server presents to it.</LI> 105 * <LI>"-K {path}" or "--keyStorePath {path}" -- Specifies the path to the 106 * key store to use to obtain client certificates.</LI> 107 * <LI>"-W {password}" or "--keyStorePassword {password}" -- Specifies the 108 * password to use to access the contents of the key store.</LI> 109 * <LI>"-u {path}" or "--keyStorePasswordFile {path}" -- Specifies the path to 110 * the file containing the password to use to access the contents of the 111 * key store.</LI> 112 * <LI>"--promptForKeyStorePassword" -- Indicates that the tool should 113 * interactively prompt the user for the key store password.</LI> 114 * <LI>"--keyStoreFormat {format}" -- Specifies the format to use for the key 115 * store file.</LI> 116 * <LI>"-P {path}" or "--trustStorePath {path}" -- Specifies the path to the 117 * trust store to use when determining whether to trust server 118 * certificates.</LI> 119 * <LI>"-T {password}" or "--trustStorePassword {password}" -- Specifies the 120 * password to use to access the contents of the trust store.</LI> 121 * <LI>"-U {path}" or "--trustStorePasswordFile {path}" -- Specifies the path 122 * to the file containing the password to use to access the contents of 123 * the trust store.</LI> 124 * <LI>"--promptForTrustStorePassword" -- Indicates that the tool should 125 * interactively prompt the user for the trust store password.</LI> 126 * <LI>"--trustStoreFormat {format}" -- Specifies the format to use for the 127 * trust store file.</LI> 128 * <LI>"-N {nickname}" or "--certNickname {nickname}" -- Specifies the 129 * nickname of the client certificate to use when performing SSL client 130 * authentication.</LI> 131 * <LI>"-o {name=value}" or "--saslOption {name=value}" -- Specifies a SASL 132 * option to use when performing SASL authentication.</LI> 133 * </UL> 134 * If SASL authentication is to be used, then a "mech" SASL option must be 135 * provided to specify the name of the SASL mechanism to use (e.g., 136 * "--saslOption mech=EXTERNAL" indicates that the EXTERNAL mechanism should be 137 * used). Depending on the SASL mechanism, additional SASL options may be 138 * required or optional. They include: 139 * <UL> 140 * <LI> 141 * mech=ANONYMOUS 142 * <UL> 143 * <LI>Required SASL options: </LI> 144 * <LI>Optional SASL options: trace</LI> 145 * </UL> 146 * </LI> 147 * <LI> 148 * mech=CRAM-MD5 149 * <UL> 150 * <LI>Required SASL options: authID</LI> 151 * <LI>Optional SASL options: </LI> 152 * </UL> 153 * </LI> 154 * <LI> 155 * mech=DIGEST-MD5 156 * <UL> 157 * <LI>Required SASL options: authID</LI> 158 * <LI>Optional SASL options: authzID, realm</LI> 159 * </UL> 160 * </LI> 161 * <LI> 162 * mech=EXTERNAL 163 * <UL> 164 * <LI>Required SASL options: </LI> 165 * <LI>Optional SASL options: </LI> 166 * </UL> 167 * </LI> 168 * <LI> 169 * mech=GSSAPI 170 * <UL> 171 * <LI>Required SASL options: authID</LI> 172 * <LI>Optional SASL options: authzID, configFile, debug, protocol, 173 * realm, kdcAddress, useTicketCache, requireCache, 174 * renewTGT, ticketCachePath</LI> 175 * </UL> 176 * </LI> 177 * <LI> 178 * mech=PLAIN 179 * <UL> 180 * <LI>Required SASL options: authID</LI> 181 * <LI>Optional SASL options: authzID</LI> 182 * </UL> 183 * </LI> 184 * </UL> 185 * <BR><BR> 186 * Note that in general, methods in this class are not threadsafe. However, the 187 * {@link #getConnection()} and {@link #getConnectionPool(int,int)} methods may 188 * be invoked concurrently by multiple threads accessing the same instance only 189 * while that instance is in the process of invoking the 190 * {@link #doToolProcessing()} method. 191 */ 192@Extensible() 193@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_NOT_THREADSAFE) 194public abstract class LDAPCommandLineTool 195 extends CommandLineTool 196{ 197 // Arguments used to communicate with an LDAP directory server. 198 private BooleanArgument helpSASL = null; 199 private BooleanArgument promptForBindPassword = null; 200 private BooleanArgument promptForKeyStorePassword = null; 201 private BooleanArgument promptForTrustStorePassword = null; 202 private BooleanArgument trustAll = null; 203 private BooleanArgument useSSL = null; 204 private BooleanArgument useStartTLS = null; 205 private DNArgument bindDN = null; 206 private FileArgument bindPasswordFile = null; 207 private FileArgument keyStorePasswordFile = null; 208 private FileArgument trustStorePasswordFile = null; 209 private IntegerArgument port = null; 210 private StringArgument bindPassword = null; 211 private StringArgument certificateNickname = null; 212 private StringArgument host = null; 213 private StringArgument keyStoreFormat = null; 214 private StringArgument keyStorePath = null; 215 private StringArgument keyStorePassword = null; 216 private StringArgument saslOption = null; 217 private StringArgument trustStoreFormat = null; 218 private StringArgument trustStorePath = null; 219 private StringArgument trustStorePassword = null; 220 221 // Variables used when creating and authenticating connections. 222 private BindRequest bindRequest = null; 223 private ServerSet serverSet = null; 224 private SSLSocketFactory startTLSSocketFactory = null; 225 226 // The prompt trust manager that will be shared by all connections created 227 // for which it is appropriate. This will allow them to benefit from the 228 // common cache. 229 private final AtomicReference<PromptTrustManager> promptTrustManager; 230 231 232 233 /** 234 * Creates a new instance of this LDAP-enabled command-line tool with the 235 * provided information. 236 * 237 * @param outStream The output stream to use for standard output. It may be 238 * {@code System.out} for the JVM's default standard output 239 * stream, {@code null} if no output should be generated, 240 * or a custom output stream if the output should be sent 241 * to an alternate location. 242 * @param errStream The output stream to use for standard error. It may be 243 * {@code System.err} for the JVM's default standard error 244 * stream, {@code null} if no output should be generated, 245 * or a custom output stream if the output should be sent 246 * to an alternate location. 247 */ 248 public LDAPCommandLineTool(final OutputStream outStream, 249 final OutputStream errStream) 250 { 251 super(outStream, errStream); 252 253 promptTrustManager = new AtomicReference<PromptTrustManager>(); 254 } 255 256 257 258 /** 259 * Retrieves a set containing the long identifiers used for LDAP-related 260 * arguments injected by this class. 261 * 262 * @param tool The tool to use to help make the determination. 263 * 264 * @return A set containing the long identifiers used for LDAP-related 265 * arguments injected by this class. 266 */ 267 static Set<String> getLongLDAPArgumentIdentifiers( 268 final LDAPCommandLineTool tool) 269 { 270 final LinkedHashSet<String> ids = new LinkedHashSet<String>(21); 271 272 ids.add("hostname"); 273 ids.add("port"); 274 275 if (tool.supportsAuthentication()) 276 { 277 ids.add("bindDN"); 278 ids.add("bindPassword"); 279 ids.add("bindPasswordFile"); 280 ids.add("promptForBindPassword"); 281 } 282 283 ids.add("useSSL"); 284 ids.add("useStartTLS"); 285 ids.add("trustAll"); 286 ids.add("keyStorePath"); 287 ids.add("keyStorePassword"); 288 ids.add("keyStorePasswordFile"); 289 ids.add("promptForKeyStorePassword"); 290 ids.add("keyStoreFormat"); 291 ids.add("trustStorePath"); 292 ids.add("trustStorePassword"); 293 ids.add("trustStorePasswordFile"); 294 ids.add("promptForTrustStorePassword"); 295 ids.add("trustStoreFormat"); 296 ids.add("certNickname"); 297 298 if (tool.supportsAuthentication()) 299 { 300 ids.add("saslOption"); 301 ids.add("helpSASL"); 302 } 303 304 return Collections.unmodifiableSet(ids); 305 } 306 307 308 309 /** 310 * {@inheritDoc} 311 */ 312 @Override() 313 public final void addToolArguments(final ArgumentParser parser) 314 throws ArgumentException 315 { 316 final String argumentGroup; 317 final boolean supportsAuthentication = supportsAuthentication(); 318 if (supportsAuthentication) 319 { 320 argumentGroup = INFO_LDAP_TOOL_ARG_GROUP_CONNECT_AND_AUTH.get(); 321 } 322 else 323 { 324 argumentGroup = INFO_LDAP_TOOL_ARG_GROUP_CONNECT.get(); 325 } 326 327 328 host = new StringArgument('h', "hostname", true, 329 (supportsMultipleServers() ? 0 : 1), 330 INFO_LDAP_TOOL_PLACEHOLDER_HOST.get(), 331 INFO_LDAP_TOOL_DESCRIPTION_HOST.get(), "localhost"); 332 host.setArgumentGroupName(argumentGroup); 333 parser.addArgument(host); 334 335 port = new IntegerArgument('p', "port", true, 336 (supportsMultipleServers() ? 0 : 1), 337 INFO_LDAP_TOOL_PLACEHOLDER_PORT.get(), 338 INFO_LDAP_TOOL_DESCRIPTION_PORT.get(), 1, 65535, 389); 339 port.setArgumentGroupName(argumentGroup); 340 parser.addArgument(port); 341 342 if (supportsAuthentication) 343 { 344 bindDN = new DNArgument('D', "bindDN", false, 1, 345 INFO_LDAP_TOOL_PLACEHOLDER_DN.get(), 346 INFO_LDAP_TOOL_DESCRIPTION_BIND_DN.get()); 347 bindDN.setArgumentGroupName(argumentGroup); 348 if (includeAlternateLongIdentifiers()) 349 { 350 bindDN.addLongIdentifier("bind-dn"); 351 } 352 parser.addArgument(bindDN); 353 354 bindPassword = new StringArgument('w', "bindPassword", false, 1, 355 INFO_LDAP_TOOL_PLACEHOLDER_PASSWORD.get(), 356 INFO_LDAP_TOOL_DESCRIPTION_BIND_PW.get()); 357 bindPassword.setSensitive(true); 358 bindPassword.setArgumentGroupName(argumentGroup); 359 if (includeAlternateLongIdentifiers()) 360 { 361 bindPassword.addLongIdentifier("bind-password"); 362 } 363 parser.addArgument(bindPassword); 364 365 bindPasswordFile = new FileArgument('j', "bindPasswordFile", false, 1, 366 INFO_LDAP_TOOL_PLACEHOLDER_PATH.get(), 367 INFO_LDAP_TOOL_DESCRIPTION_BIND_PW_FILE.get(), true, true, true, 368 false); 369 bindPasswordFile.setArgumentGroupName(argumentGroup); 370 if (includeAlternateLongIdentifiers()) 371 { 372 bindPasswordFile.addLongIdentifier("bind-password-file"); 373 } 374 parser.addArgument(bindPasswordFile); 375 376 promptForBindPassword = new BooleanArgument(null, "promptForBindPassword", 377 1, INFO_LDAP_TOOL_DESCRIPTION_BIND_PW_PROMPT.get()); 378 promptForBindPassword.setArgumentGroupName(argumentGroup); 379 if (includeAlternateLongIdentifiers()) 380 { 381 promptForBindPassword.addLongIdentifier("prompt-for-bind-password"); 382 } 383 parser.addArgument(promptForBindPassword); 384 } 385 386 useSSL = new BooleanArgument('Z', "useSSL", 1, 387 INFO_LDAP_TOOL_DESCRIPTION_USE_SSL.get()); 388 useSSL.setArgumentGroupName(argumentGroup); 389 if (includeAlternateLongIdentifiers()) 390 { 391 useSSL.addLongIdentifier("use-ssl"); 392 } 393 parser.addArgument(useSSL); 394 395 useStartTLS = new BooleanArgument('q', "useStartTLS", 1, 396 INFO_LDAP_TOOL_DESCRIPTION_USE_START_TLS.get()); 397 useStartTLS.setArgumentGroupName(argumentGroup); 398 if (includeAlternateLongIdentifiers()) 399 { 400 useStartTLS.addLongIdentifier("use-starttls"); 401 useStartTLS.addLongIdentifier("use-start-tls"); 402 } 403 parser.addArgument(useStartTLS); 404 405 trustAll = new BooleanArgument('X', "trustAll", 1, 406 INFO_LDAP_TOOL_DESCRIPTION_TRUST_ALL.get()); 407 trustAll.setArgumentGroupName(argumentGroup); 408 if (includeAlternateLongIdentifiers()) 409 { 410 trustAll.addLongIdentifier("trustAllCertificates"); 411 trustAll.addLongIdentifier("trust-all"); 412 trustAll.addLongIdentifier("trust-all-certificates"); 413 } 414 parser.addArgument(trustAll); 415 416 keyStorePath = new StringArgument('K', "keyStorePath", false, 1, 417 INFO_LDAP_TOOL_PLACEHOLDER_PATH.get(), 418 INFO_LDAP_TOOL_DESCRIPTION_KEY_STORE_PATH.get()); 419 keyStorePath.setArgumentGroupName(argumentGroup); 420 if (includeAlternateLongIdentifiers()) 421 { 422 keyStorePath.addLongIdentifier("key-store-path"); 423 } 424 parser.addArgument(keyStorePath); 425 426 keyStorePassword = new StringArgument('W', "keyStorePassword", false, 1, 427 INFO_LDAP_TOOL_PLACEHOLDER_PASSWORD.get(), 428 INFO_LDAP_TOOL_DESCRIPTION_KEY_STORE_PASSWORD.get()); 429 keyStorePassword.setSensitive(true); 430 keyStorePassword.setArgumentGroupName(argumentGroup); 431 if (includeAlternateLongIdentifiers()) 432 { 433 keyStorePassword.addLongIdentifier("keyStorePIN"); 434 keyStorePassword.addLongIdentifier("key-store-password"); 435 keyStorePassword.addLongIdentifier("key-store-pin"); 436 } 437 parser.addArgument(keyStorePassword); 438 439 keyStorePasswordFile = new FileArgument('u', "keyStorePasswordFile", false, 440 1, INFO_LDAP_TOOL_PLACEHOLDER_PATH.get(), 441 INFO_LDAP_TOOL_DESCRIPTION_KEY_STORE_PASSWORD_FILE.get()); 442 keyStorePasswordFile.setArgumentGroupName(argumentGroup); 443 if (includeAlternateLongIdentifiers()) 444 { 445 keyStorePasswordFile.addLongIdentifier("keyStorePINFile"); 446 keyStorePasswordFile.addLongIdentifier("key-store-password-file"); 447 keyStorePasswordFile.addLongIdentifier("key-store-pin-file"); 448 } 449 parser.addArgument(keyStorePasswordFile); 450 451 promptForKeyStorePassword = new BooleanArgument(null, 452 "promptForKeyStorePassword", 1, 453 INFO_LDAP_TOOL_DESCRIPTION_KEY_STORE_PASSWORD_PROMPT.get()); 454 promptForKeyStorePassword.setArgumentGroupName(argumentGroup); 455 if (includeAlternateLongIdentifiers()) 456 { 457 promptForKeyStorePassword.addLongIdentifier("promptForKeyStorePIN"); 458 promptForKeyStorePassword.addLongIdentifier( 459 "prompt-for-key-store-password"); 460 promptForKeyStorePassword.addLongIdentifier("prompt-for-key-store-pin"); 461 } 462 parser.addArgument(promptForKeyStorePassword); 463 464 keyStoreFormat = new StringArgument(null, "keyStoreFormat", false, 1, 465 INFO_LDAP_TOOL_PLACEHOLDER_FORMAT.get(), 466 INFO_LDAP_TOOL_DESCRIPTION_KEY_STORE_FORMAT.get()); 467 keyStoreFormat.setArgumentGroupName(argumentGroup); 468 if (includeAlternateLongIdentifiers()) 469 { 470 keyStoreFormat.addLongIdentifier("keyStoreType"); 471 keyStoreFormat.addLongIdentifier("key-store-format"); 472 keyStoreFormat.addLongIdentifier("key-store-type"); 473 } 474 parser.addArgument(keyStoreFormat); 475 476 trustStorePath = new StringArgument('P', "trustStorePath", false, 1, 477 INFO_LDAP_TOOL_PLACEHOLDER_PATH.get(), 478 INFO_LDAP_TOOL_DESCRIPTION_TRUST_STORE_PATH.get()); 479 trustStorePath.setArgumentGroupName(argumentGroup); 480 if (includeAlternateLongIdentifiers()) 481 { 482 trustStorePath.addLongIdentifier("trust-store-path"); 483 } 484 parser.addArgument(trustStorePath); 485 486 trustStorePassword = new StringArgument('T', "trustStorePassword", false, 1, 487 INFO_LDAP_TOOL_PLACEHOLDER_PASSWORD.get(), 488 INFO_LDAP_TOOL_DESCRIPTION_TRUST_STORE_PASSWORD.get()); 489 trustStorePassword.setSensitive(true); 490 trustStorePassword.setArgumentGroupName(argumentGroup); 491 if (includeAlternateLongIdentifiers()) 492 { 493 trustStorePassword.addLongIdentifier("trustStorePIN"); 494 trustStorePassword.addLongIdentifier("trust-store-password"); 495 trustStorePassword.addLongIdentifier("trust-store-pin"); 496 } 497 parser.addArgument(trustStorePassword); 498 499 trustStorePasswordFile = new FileArgument('U', "trustStorePasswordFile", 500 false, 1, INFO_LDAP_TOOL_PLACEHOLDER_PATH.get(), 501 INFO_LDAP_TOOL_DESCRIPTION_TRUST_STORE_PASSWORD_FILE.get()); 502 trustStorePasswordFile.setArgumentGroupName(argumentGroup); 503 if (includeAlternateLongIdentifiers()) 504 { 505 trustStorePasswordFile.addLongIdentifier("trustStorePINFile"); 506 trustStorePasswordFile.addLongIdentifier("trust-store-password-file"); 507 trustStorePasswordFile.addLongIdentifier("trust-store-pin-file"); 508 } 509 parser.addArgument(trustStorePasswordFile); 510 511 promptForTrustStorePassword = new BooleanArgument(null, 512 "promptForTrustStorePassword", 1, 513 INFO_LDAP_TOOL_DESCRIPTION_TRUST_STORE_PASSWORD_PROMPT.get()); 514 promptForTrustStorePassword.setArgumentGroupName(argumentGroup); 515 if (includeAlternateLongIdentifiers()) 516 { 517 promptForTrustStorePassword.addLongIdentifier("promptForTrustStorePIN"); 518 promptForTrustStorePassword.addLongIdentifier( 519 "prompt-for-trust-store-password"); 520 promptForTrustStorePassword.addLongIdentifier( 521 "prompt-for-trust-store-pin"); 522 } 523 parser.addArgument(promptForTrustStorePassword); 524 525 trustStoreFormat = new StringArgument(null, "trustStoreFormat", false, 1, 526 INFO_LDAP_TOOL_PLACEHOLDER_FORMAT.get(), 527 INFO_LDAP_TOOL_DESCRIPTION_TRUST_STORE_FORMAT.get()); 528 trustStoreFormat.setArgumentGroupName(argumentGroup); 529 if (includeAlternateLongIdentifiers()) 530 { 531 trustStoreFormat.addLongIdentifier("trustStoreType"); 532 trustStoreFormat.addLongIdentifier("trust-store-format"); 533 trustStoreFormat.addLongIdentifier("trust-store-type"); 534 } 535 parser.addArgument(trustStoreFormat); 536 537 certificateNickname = new StringArgument('N', "certNickname", false, 1, 538 INFO_LDAP_TOOL_PLACEHOLDER_CERT_NICKNAME.get(), 539 INFO_LDAP_TOOL_DESCRIPTION_CERT_NICKNAME.get()); 540 certificateNickname.setArgumentGroupName(argumentGroup); 541 if (includeAlternateLongIdentifiers()) 542 { 543 certificateNickname.addLongIdentifier("certificate-nickname"); 544 } 545 parser.addArgument(certificateNickname); 546 547 if (supportsAuthentication) 548 { 549 saslOption = new StringArgument('o', "saslOption", false, 0, 550 INFO_LDAP_TOOL_PLACEHOLDER_SASL_OPTION.get(), 551 INFO_LDAP_TOOL_DESCRIPTION_SASL_OPTION.get()); 552 saslOption.setArgumentGroupName(argumentGroup); 553 if (includeAlternateLongIdentifiers()) 554 { 555 saslOption.addLongIdentifier("sasl-option"); 556 } 557 parser.addArgument(saslOption); 558 559 if (supportsSASLHelp()) 560 { 561 helpSASL = new BooleanArgument(null, "helpSASL", 562 INFO_LDAP_TOOL_DESCRIPTION_HELP_SASL.get()); 563 helpSASL.setArgumentGroupName(argumentGroup); 564 if (includeAlternateLongIdentifiers()) 565 { 566 helpSASL.addLongIdentifier("help-sasl"); 567 } 568 helpSASL.setUsageArgument(true); 569 parser.addArgument(helpSASL); 570 setHelpSASLArgument(helpSASL); 571 } 572 } 573 574 575 // Both useSSL and useStartTLS cannot be used together. 576 parser.addExclusiveArgumentSet(useSSL, useStartTLS); 577 578 // Only one option may be used for specifying the key store password. 579 parser.addExclusiveArgumentSet(keyStorePassword, keyStorePasswordFile, 580 promptForKeyStorePassword); 581 582 // Only one option may be used for specifying the trust store password. 583 parser.addExclusiveArgumentSet(trustStorePassword, trustStorePasswordFile, 584 promptForTrustStorePassword); 585 586 // It doesn't make sense to provide a trust store path if any server 587 // certificate should be trusted. 588 parser.addExclusiveArgumentSet(trustAll, trustStorePath); 589 590 // If a key store password is provided, then a key store path must have also 591 // been provided. 592 parser.addDependentArgumentSet(keyStorePassword, keyStorePath); 593 parser.addDependentArgumentSet(keyStorePasswordFile, keyStorePath); 594 parser.addDependentArgumentSet(promptForKeyStorePassword, keyStorePath); 595 596 // If a trust store password is provided, then a trust store path must have 597 // also been provided. 598 parser.addDependentArgumentSet(trustStorePassword, trustStorePath); 599 parser.addDependentArgumentSet(trustStorePasswordFile, trustStorePath); 600 parser.addDependentArgumentSet(promptForTrustStorePassword, trustStorePath); 601 602 // If a key or trust store path is provided, then the tool must either use 603 // SSL or StartTLS. 604 parser.addDependentArgumentSet(keyStorePath, useSSL, useStartTLS); 605 parser.addDependentArgumentSet(trustStorePath, useSSL, useStartTLS); 606 607 // If the tool should trust all server certificates, then the tool must 608 // either use SSL or StartTLS. 609 parser.addDependentArgumentSet(trustAll, useSSL, useStartTLS); 610 611 if (supportsAuthentication) 612 { 613 // If a bind DN was provided, then a bind password must have also been 614 // provided unless defaultToPromptForBindPassword returns true. 615 if (! defaultToPromptForBindPassword()) 616 { 617 parser.addDependentArgumentSet(bindDN, bindPassword, bindPasswordFile, 618 promptForBindPassword); 619 } 620 621 // If a bind DN was provided, then no SASL options must have been 622 // provided. 623 parser.addExclusiveArgumentSet(bindDN, saslOption); 624 625 // Only one option may be used for specifying the bind password. 626 parser.addExclusiveArgumentSet(bindPassword, bindPasswordFile, 627 promptForBindPassword); 628 629 // If a bind password was provided, then the a bind DN or SASL option 630 // must have also been provided. 631 parser.addDependentArgumentSet(bindPassword, bindDN, saslOption); 632 parser.addDependentArgumentSet(bindPasswordFile, bindDN, saslOption); 633 parser.addDependentArgumentSet(promptForBindPassword, bindDN, saslOption); 634 } 635 636 addNonLDAPArguments(parser); 637 } 638 639 640 641 /** 642 * Adds the arguments needed by this command-line tool to the provided 643 * argument parser which are not related to connecting or authenticating to 644 * the directory server. 645 * 646 * @param parser The argument parser to which the arguments should be added. 647 * 648 * @throws ArgumentException If a problem occurs while adding the arguments. 649 */ 650 public abstract void addNonLDAPArguments(final ArgumentParser parser) 651 throws ArgumentException; 652 653 654 655 /** 656 * {@inheritDoc} 657 */ 658 @Override() 659 public final void doExtendedArgumentValidation() 660 throws ArgumentException 661 { 662 // If more than one hostname or port number was provided, then make sure 663 // that the same number of values were provided for each. 664 if ((host.getValues().size() > 1) || (port.getValues().size() > 1)) 665 { 666 if (host.getValues().size() != port.getValues().size()) 667 { 668 throw new ArgumentException( 669 ERR_LDAP_TOOL_HOST_PORT_COUNT_MISMATCH.get( 670 host.getLongIdentifier(), port.getLongIdentifier())); 671 } 672 } 673 674 675 doExtendedNonLDAPArgumentValidation(); 676 } 677 678 679 680 /** 681 * Indicates whether this tool should provide the arguments that allow it to 682 * bind via simple or SASL authentication. 683 * 684 * @return {@code true} if this tool should provide the arguments that allow 685 * it to bind via simple or SASL authentication, or {@code false} if 686 * not. 687 */ 688 protected boolean supportsAuthentication() 689 { 690 return true; 691 } 692 693 694 695 /** 696 * Indicates whether this tool should default to interactively prompting for 697 * the bind password if a password is required but no argument was provided 698 * to indicate how to get the password. 699 * 700 * @return {@code true} if this tool should default to interactively 701 * prompting for the bind password, or {@code false} if not. 702 */ 703 protected boolean defaultToPromptForBindPassword() 704 { 705 return false; 706 } 707 708 709 710 /** 711 * Indicates whether this tool should provide a "--help-sasl" argument that 712 * provides information about the supported SASL mechanisms and their 713 * associated properties. 714 * 715 * @return {@code true} if this tool should provide a "--help-sasl" argument, 716 * or {@code false} if not. 717 */ 718 protected boolean supportsSASLHelp() 719 { 720 return true; 721 } 722 723 724 725 /** 726 * Indicates whether the LDAP-specific arguments should include alternate 727 * versions of all long identifiers that consist of multiple words so that 728 * they are available in both camelCase and dash-separated versions. 729 * 730 * @return {@code true} if this tool should provide multiple versions of 731 * long identifiers for LDAP-specific arguments, or {@code false} if 732 * not. 733 */ 734 protected boolean includeAlternateLongIdentifiers() 735 { 736 return false; 737 } 738 739 740 741 /** 742 * Retrieves a set of controls that should be included in any bind request 743 * generated by this tool. 744 * 745 * @return A set of controls that should be included in any bind request 746 * generated by this tool. It may be {@code null} or empty if no 747 * controls should be included in the bind request. 748 */ 749 protected List<Control> getBindControls() 750 { 751 return null; 752 } 753 754 755 756 /** 757 * Indicates whether this tool supports creating connections to multiple 758 * servers. If it is to support multiple servers, then the "--hostname" and 759 * "--port" arguments will be allowed to be provided multiple times, and 760 * will be required to be provided the same number of times. The same type of 761 * communication security and bind credentials will be used for all servers. 762 * 763 * @return {@code true} if this tool supports creating connections to 764 * multiple servers, or {@code false} if not. 765 */ 766 protected boolean supportsMultipleServers() 767 { 768 return false; 769 } 770 771 772 773 /** 774 * Performs any necessary processing that should be done to ensure that the 775 * provided set of command-line arguments were valid. This method will be 776 * called after the basic argument parsing has been performed and after all 777 * LDAP-specific argument validation has been processed, and immediately 778 * before the {@link CommandLineTool#doToolProcessing} method is invoked. 779 * 780 * @throws ArgumentException If there was a problem with the command-line 781 * arguments provided to this program. 782 */ 783 public void doExtendedNonLDAPArgumentValidation() 784 throws ArgumentException 785 { 786 // No processing will be performed by default. 787 } 788 789 790 791 /** 792 * Retrieves the connection options that should be used for connections that 793 * are created with this command line tool. Subclasses may override this 794 * method to use a custom set of connection options. 795 * 796 * @return The connection options that should be used for connections that 797 * are created with this command line tool. 798 */ 799 public LDAPConnectionOptions getConnectionOptions() 800 { 801 return new LDAPConnectionOptions(); 802 } 803 804 805 806 /** 807 * Retrieves a connection that may be used to communicate with the target 808 * directory server. 809 * <BR><BR> 810 * Note that this method is threadsafe and may be invoked by multiple threads 811 * accessing the same instance only while that instance is in the process of 812 * invoking the {@link #doToolProcessing} method. 813 * 814 * @return A connection that may be used to communicate with the target 815 * directory server. 816 * 817 * @throws LDAPException If a problem occurs while creating the connection. 818 */ 819 @ThreadSafety(level=ThreadSafetyLevel.METHOD_THREADSAFE) 820 public final LDAPConnection getConnection() 821 throws LDAPException 822 { 823 final LDAPConnection connection = getUnauthenticatedConnection(); 824 825 try 826 { 827 if (bindRequest != null) 828 { 829 connection.bind(bindRequest); 830 } 831 } 832 catch (LDAPException le) 833 { 834 debugException(le); 835 connection.close(); 836 throw le; 837 } 838 839 return connection; 840 } 841 842 843 844 /** 845 * Retrieves an unauthenticated connection that may be used to communicate 846 * with the target directory server. 847 * <BR><BR> 848 * Note that this method is threadsafe and may be invoked by multiple threads 849 * accessing the same instance only while that instance is in the process of 850 * invoking the {@link #doToolProcessing} method. 851 * 852 * @return An unauthenticated connection that may be used to communicate with 853 * the target directory server. 854 * 855 * @throws LDAPException If a problem occurs while creating the connection. 856 */ 857 @ThreadSafety(level=ThreadSafetyLevel.METHOD_THREADSAFE) 858 public final LDAPConnection getUnauthenticatedConnection() 859 throws LDAPException 860 { 861 if (serverSet == null) 862 { 863 serverSet = createServerSet(); 864 bindRequest = createBindRequest(); 865 } 866 867 final LDAPConnection connection = serverSet.getConnection(); 868 869 if (useStartTLS.isPresent()) 870 { 871 try 872 { 873 final ExtendedResult extendedResult = 874 connection.processExtendedOperation( 875 new StartTLSExtendedRequest(startTLSSocketFactory)); 876 if (! extendedResult.getResultCode().equals(ResultCode.SUCCESS)) 877 { 878 throw new LDAPException(extendedResult.getResultCode(), 879 ERR_LDAP_TOOL_START_TLS_FAILED.get( 880 extendedResult.getDiagnosticMessage())); 881 } 882 } 883 catch (LDAPException le) 884 { 885 debugException(le); 886 connection.close(); 887 throw le; 888 } 889 } 890 891 return connection; 892 } 893 894 895 896 /** 897 * Retrieves a connection pool that may be used to communicate with the target 898 * directory server. 899 * <BR><BR> 900 * Note that this method is threadsafe and may be invoked by multiple threads 901 * accessing the same instance only while that instance is in the process of 902 * invoking the {@link #doToolProcessing} method. 903 * 904 * @param initialConnections The number of connections that should be 905 * initially established in the pool. 906 * @param maxConnections The maximum number of connections to maintain 907 * in the pool. 908 * 909 * @return A connection that may be used to communicate with the target 910 * directory server. 911 * 912 * @throws LDAPException If a problem occurs while creating the connection 913 * pool. 914 */ 915 @ThreadSafety(level=ThreadSafetyLevel.METHOD_THREADSAFE) 916 public final LDAPConnectionPool getConnectionPool( 917 final int initialConnections, 918 final int maxConnections) 919 throws LDAPException 920 { 921 return getConnectionPool(initialConnections, maxConnections, 1, null, null, 922 true, null); 923 } 924 925 926 927 /** 928 * Retrieves a connection pool that may be used to communicate with the target 929 * directory server. 930 * <BR><BR> 931 * Note that this method is threadsafe and may be invoked by multiple threads 932 * accessing the same instance only while that instance is in the process of 933 * invoking the {@link #doToolProcessing} method. 934 * 935 * @param initialConnections The number of connections that should be 936 * initially established in the pool. 937 * @param maxConnections The maximum number of connections to 938 * maintain in the pool. 939 * @param initialConnectThreads The number of concurrent threads to use to 940 * establish the initial set of connections. 941 * A value greater than one indicates that 942 * the attempt to establish connections 943 * should be parallelized. 944 * @param beforeStartTLSProcessor An optional post-connect processor that 945 * should be used for the connection pool and 946 * should be invoked before any StartTLS 947 * post-connect processor that may be needed 948 * based on the selected arguments. It may 949 * be {@code null} if no such post-connect 950 * processor is needed. 951 * @param afterStartTLSProcessor An optional post-connect processor that 952 * should be used for the connection pool and 953 * should be invoked after any StartTLS 954 * post-connect processor that may be needed 955 * based on the selected arguments. It may 956 * be {@code null} if no such post-connect 957 * processor is needed. 958 * @param throwOnConnectFailure If an exception should be thrown if a 959 * problem is encountered while attempting to 960 * create the specified initial number of 961 * connections. If {@code true}, then the 962 * attempt to create the pool will fail if 963 * any connection cannot be established. If 964 * {@code false}, then the pool will be 965 * created but may have fewer than the 966 * initial number of connections (or possibly 967 * no connections). 968 * @param healthCheck An optional health check that should be 969 * configured for the connection pool. It 970 * may be {@code null} if the default health 971 * checking should be performed. 972 * 973 * @return A connection that may be used to communicate with the target 974 * directory server. 975 * 976 * @throws LDAPException If a problem occurs while creating the connection 977 * pool. 978 */ 979 @ThreadSafety(level=ThreadSafetyLevel.METHOD_THREADSAFE) 980 public final LDAPConnectionPool getConnectionPool( 981 final int initialConnections, final int maxConnections, 982 final int initialConnectThreads, 983 final PostConnectProcessor beforeStartTLSProcessor, 984 final PostConnectProcessor afterStartTLSProcessor, 985 final boolean throwOnConnectFailure, 986 final LDAPConnectionPoolHealthCheck healthCheck) 987 throws LDAPException 988 { 989 // Create the server set and bind request, if necessary. 990 if (serverSet == null) 991 { 992 serverSet = createServerSet(); 993 bindRequest = createBindRequest(); 994 } 995 996 997 // Prepare the post-connect processor for the pool. 998 final ArrayList<PostConnectProcessor> pcpList = 999 new ArrayList<PostConnectProcessor>(3); 1000 if (beforeStartTLSProcessor != null) 1001 { 1002 pcpList.add(beforeStartTLSProcessor); 1003 } 1004 1005 if (useStartTLS.isPresent()) 1006 { 1007 pcpList.add(new StartTLSPostConnectProcessor(startTLSSocketFactory)); 1008 } 1009 1010 if (afterStartTLSProcessor != null) 1011 { 1012 pcpList.add(afterStartTLSProcessor); 1013 } 1014 1015 final PostConnectProcessor postConnectProcessor; 1016 switch (pcpList.size()) 1017 { 1018 case 0: 1019 postConnectProcessor = null; 1020 break; 1021 case 1: 1022 postConnectProcessor = pcpList.get(0); 1023 break; 1024 default: 1025 postConnectProcessor = new AggregatePostConnectProcessor(pcpList); 1026 break; 1027 } 1028 1029 return new LDAPConnectionPool(serverSet, bindRequest, initialConnections, 1030 maxConnections, initialConnectThreads, postConnectProcessor, 1031 throwOnConnectFailure, healthCheck); 1032 } 1033 1034 1035 1036 /** 1037 * Creates the server set to use when creating connections or connection 1038 * pools. 1039 * 1040 * @return The server set to use when creating connections or connection 1041 * pools. 1042 * 1043 * @throws LDAPException If a problem occurs while creating the server set. 1044 */ 1045 public ServerSet createServerSet() 1046 throws LDAPException 1047 { 1048 final SSLUtil sslUtil = createSSLUtil(); 1049 1050 SocketFactory socketFactory = null; 1051 if (useSSL.isPresent()) 1052 { 1053 try 1054 { 1055 socketFactory = sslUtil.createSSLSocketFactory(); 1056 } 1057 catch (Exception e) 1058 { 1059 debugException(e); 1060 throw new LDAPException(ResultCode.LOCAL_ERROR, 1061 ERR_LDAP_TOOL_CANNOT_CREATE_SSL_SOCKET_FACTORY.get( 1062 getExceptionMessage(e)), e); 1063 } 1064 } 1065 else if (useStartTLS.isPresent()) 1066 { 1067 try 1068 { 1069 startTLSSocketFactory = sslUtil.createSSLSocketFactory(); 1070 } 1071 catch (Exception e) 1072 { 1073 debugException(e); 1074 throw new LDAPException(ResultCode.LOCAL_ERROR, 1075 ERR_LDAP_TOOL_CANNOT_CREATE_SSL_SOCKET_FACTORY.get( 1076 getExceptionMessage(e)), e); 1077 } 1078 } 1079 1080 if (host.getValues().size() == 1) 1081 { 1082 return new SingleServerSet(host.getValue(), port.getValue(), 1083 socketFactory, getConnectionOptions()); 1084 } 1085 else 1086 { 1087 final List<String> hostList = host.getValues(); 1088 final List<Integer> portList = port.getValues(); 1089 1090 final String[] hosts = new String[hostList.size()]; 1091 final int[] ports = new int[hosts.length]; 1092 1093 for (int i=0; i < hosts.length; i++) 1094 { 1095 hosts[i] = hostList.get(i); 1096 ports[i] = portList.get(i); 1097 } 1098 1099 return new RoundRobinServerSet(hosts, ports, socketFactory, 1100 getConnectionOptions()); 1101 } 1102 } 1103 1104 1105 1106 /** 1107 * Creates the SSLUtil instance to use for secure communication. 1108 * 1109 * @return The SSLUtil instance to use for secure communication, or 1110 * {@code null} if secure communication is not needed. 1111 * 1112 * @throws LDAPException If a problem occurs while creating the SSLUtil 1113 * instance. 1114 */ 1115 public SSLUtil createSSLUtil() 1116 throws LDAPException 1117 { 1118 return createSSLUtil(false); 1119 } 1120 1121 1122 1123 /** 1124 * Creates the SSLUtil instance to use for secure communication. 1125 * 1126 * @param force Indicates whether to create the SSLUtil object even if 1127 * neither the "--useSSL" nor the "--useStartTLS" argument was 1128 * provided. The key store and/or trust store paths must still 1129 * have been provided. This may be useful for tools that 1130 * accept SSL-based communication but do not themselves intend 1131 * to perform SSL-based communication as an LDAP client. 1132 * 1133 * @return The SSLUtil instance to use for secure communication, or 1134 * {@code null} if secure communication is not needed. 1135 * 1136 * @throws LDAPException If a problem occurs while creating the SSLUtil 1137 * instance. 1138 */ 1139 public SSLUtil createSSLUtil(final boolean force) 1140 throws LDAPException 1141 { 1142 if (force || useSSL.isPresent() || useStartTLS.isPresent()) 1143 { 1144 KeyManager keyManager = null; 1145 if (keyStorePath.isPresent()) 1146 { 1147 char[] pw = null; 1148 if (keyStorePassword.isPresent()) 1149 { 1150 pw = keyStorePassword.getValue().toCharArray(); 1151 } 1152 else if (keyStorePasswordFile.isPresent()) 1153 { 1154 try 1155 { 1156 pw = keyStorePasswordFile.getNonBlankFileLines().get(0). 1157 toCharArray(); 1158 } 1159 catch (Exception e) 1160 { 1161 debugException(e); 1162 throw new LDAPException(ResultCode.LOCAL_ERROR, 1163 ERR_LDAP_TOOL_CANNOT_READ_KEY_STORE_PASSWORD.get( 1164 getExceptionMessage(e)), e); 1165 } 1166 } 1167 else if (promptForKeyStorePassword.isPresent()) 1168 { 1169 getOut().print(INFO_LDAP_TOOL_ENTER_KEY_STORE_PASSWORD.get()); 1170 pw = StaticUtils.toUTF8String( 1171 PasswordReader.readPassword()).toCharArray(); 1172 getOut().println(); 1173 } 1174 1175 try 1176 { 1177 keyManager = new KeyStoreKeyManager(keyStorePath.getValue(), pw, 1178 keyStoreFormat.getValue(), certificateNickname.getValue()); 1179 } 1180 catch (Exception e) 1181 { 1182 debugException(e); 1183 throw new LDAPException(ResultCode.LOCAL_ERROR, 1184 ERR_LDAP_TOOL_CANNOT_CREATE_KEY_MANAGER.get( 1185 getExceptionMessage(e)), e); 1186 } 1187 } 1188 1189 TrustManager trustManager; 1190 if (trustAll.isPresent()) 1191 { 1192 trustManager = new TrustAllTrustManager(false); 1193 } 1194 else if (trustStorePath.isPresent()) 1195 { 1196 char[] pw = null; 1197 if (trustStorePassword.isPresent()) 1198 { 1199 pw = trustStorePassword.getValue().toCharArray(); 1200 } 1201 else if (trustStorePasswordFile.isPresent()) 1202 { 1203 try 1204 { 1205 pw = trustStorePasswordFile.getNonBlankFileLines().get(0). 1206 toCharArray(); 1207 } 1208 catch (Exception e) 1209 { 1210 debugException(e); 1211 throw new LDAPException(ResultCode.LOCAL_ERROR, 1212 ERR_LDAP_TOOL_CANNOT_READ_TRUST_STORE_PASSWORD.get( 1213 getExceptionMessage(e)), e); 1214 } 1215 } 1216 else if (promptForTrustStorePassword.isPresent()) 1217 { 1218 getOut().print(INFO_LDAP_TOOL_ENTER_TRUST_STORE_PASSWORD.get()); 1219 pw = StaticUtils.toUTF8String( 1220 PasswordReader.readPassword()).toCharArray(); 1221 getOut().println(); 1222 } 1223 1224 trustManager = new TrustStoreTrustManager(trustStorePath.getValue(), pw, 1225 trustStoreFormat.getValue(), true); 1226 } 1227 else 1228 { 1229 trustManager = promptTrustManager.get(); 1230 if (trustManager == null) 1231 { 1232 final PromptTrustManager m = new PromptTrustManager(); 1233 promptTrustManager.compareAndSet(null, m); 1234 trustManager = promptTrustManager.get(); 1235 } 1236 } 1237 1238 return new SSLUtil(keyManager, trustManager); 1239 } 1240 else 1241 { 1242 return null; 1243 } 1244 } 1245 1246 1247 1248 /** 1249 * Creates the bind request to use to authenticate to the server. 1250 * 1251 * @return The bind request to use to authenticate to the server, or 1252 * {@code null} if no bind should be performed. 1253 * 1254 * @throws LDAPException If a problem occurs while creating the bind 1255 * request. 1256 */ 1257 public BindRequest createBindRequest() 1258 throws LDAPException 1259 { 1260 if (! supportsAuthentication()) 1261 { 1262 return null; 1263 } 1264 1265 final Control[] bindControls; 1266 final List<Control> bindControlList = getBindControls(); 1267 if ((bindControlList == null) || bindControlList.isEmpty()) 1268 { 1269 bindControls = NO_CONTROLS; 1270 } 1271 else 1272 { 1273 bindControls = new Control[bindControlList.size()]; 1274 bindControlList.toArray(bindControls); 1275 } 1276 1277 byte[] pw; 1278 if (bindPassword.isPresent()) 1279 { 1280 pw = StaticUtils.getBytes(bindPassword.getValue()); 1281 } 1282 else if (bindPasswordFile.isPresent()) 1283 { 1284 try 1285 { 1286 pw = StaticUtils.getBytes( 1287 bindPasswordFile.getNonBlankFileLines().get(0)); 1288 } 1289 catch (Exception e) 1290 { 1291 debugException(e); 1292 throw new LDAPException(ResultCode.LOCAL_ERROR, 1293 ERR_LDAP_TOOL_CANNOT_READ_BIND_PASSWORD.get( 1294 getExceptionMessage(e)), e); 1295 } 1296 } 1297 else if (promptForBindPassword.isPresent()) 1298 { 1299 getOriginalOut().print(INFO_LDAP_TOOL_ENTER_BIND_PASSWORD.get()); 1300 pw = PasswordReader.readPassword(); 1301 getOriginalOut().println(); 1302 } 1303 else 1304 { 1305 pw = null; 1306 } 1307 1308 if (saslOption.isPresent()) 1309 { 1310 final String dnStr; 1311 if (bindDN.isPresent()) 1312 { 1313 dnStr = bindDN.getValue().toString(); 1314 } 1315 else 1316 { 1317 dnStr = null; 1318 } 1319 1320 return SASLUtils.createBindRequest(dnStr, pw, 1321 defaultToPromptForBindPassword(), this, null, 1322 saslOption.getValues(), bindControls); 1323 } 1324 else if (bindDN.isPresent()) 1325 { 1326 if ((pw == null) && (! bindDN.getValue().isNullDN()) && 1327 defaultToPromptForBindPassword()) 1328 { 1329 getOriginalOut().print(INFO_LDAP_TOOL_ENTER_BIND_PASSWORD.get()); 1330 pw = PasswordReader.readPassword(); 1331 getOriginalOut().println(); 1332 } 1333 1334 return new SimpleBindRequest(bindDN.getValue(), pw, bindControls); 1335 } 1336 else 1337 { 1338 return null; 1339 } 1340 } 1341}