001/* 002 * Copyright 2012-2020 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2012-2020 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2012-2020 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.util; 037 038 039 040import java.io.OutputStream; 041import java.util.concurrent.atomic.AtomicReference; 042import javax.net.SocketFactory; 043import javax.net.ssl.KeyManager; 044import javax.net.ssl.SSLSocketFactory; 045import javax.net.ssl.TrustManager; 046 047import com.unboundid.ldap.sdk.BindRequest; 048import com.unboundid.ldap.sdk.ExtendedResult; 049import com.unboundid.ldap.sdk.InternalSDKHelper; 050import com.unboundid.ldap.sdk.LDAPConnection; 051import com.unboundid.ldap.sdk.LDAPConnectionOptions; 052import com.unboundid.ldap.sdk.LDAPConnectionPool; 053import com.unboundid.ldap.sdk.LDAPException; 054import com.unboundid.ldap.sdk.PostConnectProcessor; 055import com.unboundid.ldap.sdk.ResultCode; 056import com.unboundid.ldap.sdk.ServerSet; 057import com.unboundid.ldap.sdk.SimpleBindRequest; 058import com.unboundid.ldap.sdk.SingleServerSet; 059import com.unboundid.ldap.sdk.StartTLSPostConnectProcessor; 060import com.unboundid.ldap.sdk.extensions.StartTLSExtendedRequest; 061import com.unboundid.util.args.ArgumentException; 062import com.unboundid.util.args.ArgumentParser; 063import com.unboundid.util.args.BooleanArgument; 064import com.unboundid.util.args.DNArgument; 065import com.unboundid.util.args.FileArgument; 066import com.unboundid.util.args.IntegerArgument; 067import com.unboundid.util.args.StringArgument; 068import com.unboundid.util.ssl.AggregateTrustManager; 069import com.unboundid.util.ssl.KeyStoreKeyManager; 070import com.unboundid.util.ssl.SSLUtil; 071import com.unboundid.util.ssl.TrustAllTrustManager; 072import com.unboundid.util.ssl.TrustStoreTrustManager; 073 074import static com.unboundid.util.UtilityMessages.*; 075 076 077 078/** 079 * This class provides a basis for developing command-line tools that have the 080 * ability to communicate with multiple directory servers, potentially with 081 * very different settings for each. For example, it may be used to help create 082 * tools that move or compare data from one server to another. 083 * <BR><BR> 084 * Each server will be identified by a prefix and/or suffix that will be added 085 * to the argument name (e.g., if the first server has a prefix of "source", 086 * then the "hostname" argument will actually be "sourceHostname"). The 087 * base names for the arguments this class supports include: 088 * <UL> 089 * <LI>hostname -- Specifies the address of the directory server. If this 090 * isn't specified, then a default of "localhost" will be used.</LI> 091 * <LI>port -- specifies the port number of the directory server. If this 092 * isn't specified, then a default port of 389 will be used.</LI> 093 * <LI>bindDN -- Specifies the DN to use to bind to the directory server using 094 * simple authentication. If this isn't specified, then simple 095 * authentication will not be performed.</LI> 096 * <LI>bindPassword -- Specifies the password to use when binding with simple 097 * authentication or a password-based SASL mechanism.</LI> 098 * <LI>bindPasswordFile -- Specifies the path to a file containing the 099 * password to use when binding with simple authentication or a 100 * password-based SASL mechanism.</LI> 101 * <LI>useSSL -- Indicates that communication with the server should be 102 * secured using SSL.</LI> 103 * <LI>useStartTLS -- Indicates that communication with the server should be 104 * secured using StartTLS.</LI> 105 * <LI>trustAll -- Indicates that the client should trust any certificate 106 * that the server presents to it.</LI> 107 * <LI>keyStorePath -- Specifies the path to the key store to use to obtain 108 * client certificates.</LI> 109 * <LI>keyStorePassword -- Specifies the password to use to access the 110 * contents of the key store.</LI> 111 * <LI>keyStorePasswordFile -- Specifies the path ot a file containing the 112 * password to use to access the contents of the key store.</LI> 113 * <LI>keyStoreFormat -- Specifies the format to use for the key store 114 * file.</LI> 115 * <LI>trustStorePath -- Specifies the path to the trust store to use to 116 * obtain client certificates.</LI> 117 * <LI>trustStorePassword -- Specifies the password to use to access the 118 * contents of the trust store.</LI> 119 * <LI>trustStorePasswordFile -- Specifies the path ot a file containing the 120 * password to use to access the contents of the trust store.</LI> 121 * <LI>trustStoreFormat -- Specifies the format to use for the trust store 122 * file.</LI> 123 * <LI>certNickname -- Specifies the nickname of the client certificate to 124 * use when performing SSL client authentication.</LI> 125 * <LI>saslOption -- Specifies a SASL option to use when performing SASL 126 * authentication.</LI> 127 * </UL> 128 * If SASL authentication is to be used, then a "mech" SASL option must be 129 * provided to specify the name of the SASL mechanism to use. Depending on the 130 * SASL mechanism, additional SASL options may be required or optional. 131 */ 132@Extensible() 133@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_NOT_THREADSAFE) 134public abstract class MultiServerLDAPCommandLineTool 135 extends CommandLineTool 136{ 137 // The set of prefixes and suffixes that will be used for server names. 138 private final int numServers; 139 @Nullable private final String[] serverNamePrefixes; 140 @Nullable private final String[] serverNameSuffixes; 141 142 // The set of arguments used to hold information about connection properties. 143 @NotNull private final BooleanArgument[] trustAll; 144 @NotNull private final BooleanArgument[] useSSL; 145 @NotNull private final BooleanArgument[] useStartTLS; 146 @NotNull private final DNArgument[] bindDN; 147 @NotNull private final FileArgument[] bindPasswordFile; 148 @NotNull private final FileArgument[] keyStorePasswordFile; 149 @NotNull private final FileArgument[] trustStorePasswordFile; 150 @NotNull private final IntegerArgument[] port; 151 @NotNull private final StringArgument[] bindPassword; 152 @NotNull private final StringArgument[] certificateNickname; 153 @NotNull private final StringArgument[] host; 154 @NotNull private final StringArgument[] keyStoreFormat; 155 @NotNull private final StringArgument[] keyStorePath; 156 @NotNull private final StringArgument[] keyStorePassword; 157 @NotNull private final StringArgument[] saslOption; 158 @NotNull private final StringArgument[] trustStoreFormat; 159 @NotNull private final StringArgument[] trustStorePath; 160 @NotNull private final StringArgument[] trustStorePassword; 161 162 // Variables used when creating and authenticating connections. 163 @NotNull private final BindRequest[] bindRequest; 164 @NotNull private final ServerSet[] serverSet; 165 @NotNull private final SSLSocketFactory[] startTLSSocketFactory; 166 167 // An atomic reference to an aggregate trust manager that will check a 168 // JVM-default set of trusted issuers, and then its own cache, before 169 // prompting the user about whether to trust the presented certificate chain. 170 // Re-using this trust manager will allow the tool to benefit from a common 171 // cache if multiple connections are needed. 172 @NotNull private final AtomicReference<AggregateTrustManager> 173 promptTrustManager; 174 175 176 177 /** 178 * Creates a new instance of this multi-server LDAP command-line tool. At 179 * least one of the set of server name prefixes and suffixes must be 180 * non-{@code null}. If both are non-{@code null}, then they must have the 181 * same number of elements. 182 * 183 * @param outStream The output stream to use for standard output. 184 * It may be {@code System.out} for the JVM's 185 * default standard output stream, {@code null} if 186 * no output should be generated, or a custom 187 * output stream if the output should be sent to 188 * an alternate location. 189 * @param errStream The output stream to use for standard error. 190 * It may be {@code System.err} for the JVM's 191 * default standard error stream, {@code null} if 192 * no output should be generated, or a custom 193 * output stream if the output should be sent to 194 * an alternate location. 195 * @param serverNamePrefixes The prefixes to include before the names of 196 * each of the parameters to identify each server. 197 * It may be {@code null} if only suffixes should 198 * be used. 199 * @param serverNameSuffixes The suffixes to include after the names of each 200 * of the parameters to identify each server. It 201 * may be {@code null} if only prefixes should be 202 * used. 203 * 204 * @throws LDAPSDKUsageException If both the sets of server name prefixes 205 * and suffixes are {@code null} or empty, or 206 * if both sets are non-{@code null} but have 207 * different numbers of elements. 208 */ 209 public MultiServerLDAPCommandLineTool(@Nullable final OutputStream outStream, 210 @Nullable final OutputStream errStream, 211 @Nullable final String[] serverNamePrefixes, 212 @Nullable final String[] serverNameSuffixes) 213 throws LDAPSDKUsageException 214 { 215 super(outStream, errStream); 216 217 promptTrustManager = new AtomicReference<>(); 218 219 this.serverNamePrefixes = serverNamePrefixes; 220 this.serverNameSuffixes = serverNameSuffixes; 221 222 if (serverNamePrefixes == null) 223 { 224 if (serverNameSuffixes == null) 225 { 226 throw new LDAPSDKUsageException( 227 ERR_MULTI_LDAP_TOOL_PREFIXES_AND_SUFFIXES_NULL.get()); 228 } 229 else 230 { 231 numServers = serverNameSuffixes.length; 232 } 233 } 234 else 235 { 236 numServers = serverNamePrefixes.length; 237 238 if ((serverNameSuffixes != null) && 239 (serverNamePrefixes.length != serverNameSuffixes.length)) 240 { 241 throw new LDAPSDKUsageException( 242 ERR_MULTI_LDAP_TOOL_PREFIXES_AND_SUFFIXES_MISMATCH.get()); 243 } 244 } 245 246 if (numServers == 0) 247 { 248 throw new LDAPSDKUsageException( 249 ERR_MULTI_LDAP_TOOL_PREFIXES_AND_SUFFIXES_EMPTY.get()); 250 } 251 252 trustAll = new BooleanArgument[numServers]; 253 useSSL = new BooleanArgument[numServers]; 254 useStartTLS = new BooleanArgument[numServers]; 255 bindDN = new DNArgument[numServers]; 256 bindPasswordFile = new FileArgument[numServers]; 257 keyStorePasswordFile = new FileArgument[numServers]; 258 trustStorePasswordFile = new FileArgument[numServers]; 259 port = new IntegerArgument[numServers]; 260 bindPassword = new StringArgument[numServers]; 261 certificateNickname = new StringArgument[numServers]; 262 host = new StringArgument[numServers]; 263 keyStoreFormat = new StringArgument[numServers]; 264 keyStorePath = new StringArgument[numServers]; 265 keyStorePassword = new StringArgument[numServers]; 266 saslOption = new StringArgument[numServers]; 267 trustStoreFormat = new StringArgument[numServers]; 268 trustStorePath = new StringArgument[numServers]; 269 trustStorePassword = new StringArgument[numServers]; 270 271 bindRequest = new BindRequest[numServers]; 272 serverSet = new ServerSet[numServers]; 273 startTLSSocketFactory = new SSLSocketFactory[numServers]; 274 } 275 276 277 278 /** 279 * {@inheritDoc} 280 */ 281 @Override() 282 public final void addToolArguments(@NotNull final ArgumentParser parser) 283 throws ArgumentException 284 { 285 for (int i=0; i < numServers; i++) 286 { 287 final StringBuilder groupNameBuffer = new StringBuilder(); 288 if (serverNamePrefixes != null) 289 { 290 final String prefix = serverNamePrefixes[i].replace('-', ' ').trim(); 291 groupNameBuffer.append(StaticUtils.capitalize(prefix, true)); 292 } 293 294 if (serverNameSuffixes != null) 295 { 296 if (groupNameBuffer.length() > 0) 297 { 298 groupNameBuffer.append(' '); 299 } 300 301 final String suffix = serverNameSuffixes[i].replace('-', ' ').trim(); 302 groupNameBuffer.append(StaticUtils.capitalize(suffix, true)); 303 } 304 305 groupNameBuffer.append(' '); 306 groupNameBuffer.append(INFO_MULTI_LDAP_TOOL_GROUP_CONN_AND_AUTH.get()); 307 final String groupName = groupNameBuffer.toString(); 308 309 310 host[i] = new StringArgument(null, genArgName(i, "hostname"), true, 1, 311 INFO_LDAP_TOOL_PLACEHOLDER_HOST.get(), 312 INFO_LDAP_TOOL_DESCRIPTION_HOST.get(), "localhost"); 313 host[i].setArgumentGroupName(groupName); 314 parser.addArgument(host[i]); 315 316 port[i] = new IntegerArgument(null, genArgName(i, "port"), true, 1, 317 INFO_LDAP_TOOL_PLACEHOLDER_PORT.get(), 318 INFO_LDAP_TOOL_DESCRIPTION_PORT.get(), 1, 65_535, 389); 319 port[i].setArgumentGroupName(groupName); 320 parser.addArgument(port[i]); 321 322 bindDN[i] = new DNArgument(null, genArgName(i, "bindDN"), false, 1, 323 INFO_LDAP_TOOL_PLACEHOLDER_DN.get(), 324 INFO_LDAP_TOOL_DESCRIPTION_BIND_DN.get()); 325 bindDN[i].setArgumentGroupName(groupName); 326 parser.addArgument(bindDN[i]); 327 328 bindPassword[i] = new StringArgument(null, genArgName(i, "bindPassword"), 329 false, 1, INFO_LDAP_TOOL_PLACEHOLDER_PASSWORD.get(), 330 INFO_LDAP_TOOL_DESCRIPTION_BIND_PW.get()); 331 bindPassword[i].setSensitive(true); 332 bindPassword[i].setArgumentGroupName(groupName); 333 parser.addArgument(bindPassword[i]); 334 335 bindPasswordFile[i] = new FileArgument(null, 336 genArgName(i, "bindPasswordFile"), false, 1, 337 INFO_LDAP_TOOL_PLACEHOLDER_PATH.get(), 338 INFO_LDAP_TOOL_DESCRIPTION_BIND_PW_FILE.get(), true, true, true, 339 false); 340 bindPasswordFile[i].setArgumentGroupName(groupName); 341 parser.addArgument(bindPasswordFile[i]); 342 343 useSSL[i] = new BooleanArgument(null, genArgName(i, "useSSL"), 1, 344 INFO_LDAP_TOOL_DESCRIPTION_USE_SSL.get()); 345 useSSL[i].setArgumentGroupName(groupName); 346 parser.addArgument(useSSL[i]); 347 348 useStartTLS[i] = new BooleanArgument(null, genArgName(i, "useStartTLS"), 349 1, INFO_LDAP_TOOL_DESCRIPTION_USE_START_TLS.get()); 350 useStartTLS[i].setArgumentGroupName(groupName); 351 parser.addArgument(useStartTLS[i]); 352 353 trustAll[i] = new BooleanArgument(null, genArgName(i, "trustAll"), 1, 354 INFO_LDAP_TOOL_DESCRIPTION_TRUST_ALL.get()); 355 trustAll[i].setArgumentGroupName(groupName); 356 parser.addArgument(trustAll[i]); 357 358 keyStorePath[i] = new StringArgument(null, genArgName(i, "keyStorePath"), 359 false, 1, INFO_LDAP_TOOL_PLACEHOLDER_PATH.get(), 360 INFO_LDAP_TOOL_DESCRIPTION_KEY_STORE_PATH.get()); 361 keyStorePath[i].setArgumentGroupName(groupName); 362 parser.addArgument(keyStorePath[i]); 363 364 keyStorePassword[i] = new StringArgument(null, 365 genArgName(i, "keyStorePassword"), false, 1, 366 INFO_LDAP_TOOL_PLACEHOLDER_PASSWORD.get(), 367 INFO_LDAP_TOOL_DESCRIPTION_KEY_STORE_PASSWORD.get()); 368 keyStorePassword[i].setSensitive(true); 369 keyStorePassword[i].setArgumentGroupName(groupName); 370 parser.addArgument(keyStorePassword[i]); 371 372 keyStorePasswordFile[i] = new FileArgument(null, 373 genArgName(i, "keyStorePasswordFile"), false, 1, 374 INFO_LDAP_TOOL_PLACEHOLDER_PATH.get(), 375 INFO_LDAP_TOOL_DESCRIPTION_KEY_STORE_PASSWORD_FILE.get(), true, 376 true, true, false); 377 keyStorePasswordFile[i].setArgumentGroupName(groupName); 378 parser.addArgument(keyStorePasswordFile[i]); 379 380 keyStoreFormat[i] = new StringArgument(null, 381 genArgName(i, "keyStoreFormat"), false, 1, 382 INFO_LDAP_TOOL_PLACEHOLDER_FORMAT.get(), 383 INFO_LDAP_TOOL_DESCRIPTION_KEY_STORE_FORMAT.get()); 384 keyStoreFormat[i].setArgumentGroupName(groupName); 385 parser.addArgument(keyStoreFormat[i]); 386 387 trustStorePath[i] = new StringArgument(null, 388 genArgName(i, "trustStorePath"), false, 1, 389 INFO_LDAP_TOOL_PLACEHOLDER_PATH.get(), 390 INFO_LDAP_TOOL_DESCRIPTION_TRUST_STORE_PATH.get()); 391 trustStorePath[i].setArgumentGroupName(groupName); 392 parser.addArgument(trustStorePath[i]); 393 394 trustStorePassword[i] = new StringArgument(null, 395 genArgName(i, "trustStorePassword"), false, 1, 396 INFO_LDAP_TOOL_PLACEHOLDER_PASSWORD.get(), 397 INFO_LDAP_TOOL_DESCRIPTION_TRUST_STORE_PASSWORD.get()); 398 trustStorePassword[i].setSensitive(true); 399 trustStorePassword[i].setArgumentGroupName(groupName); 400 parser.addArgument(trustStorePassword[i]); 401 402 trustStorePasswordFile[i] = new FileArgument(null, 403 genArgName(i, "trustStorePasswordFile"), false, 1, 404 INFO_LDAP_TOOL_PLACEHOLDER_PATH.get(), 405 INFO_LDAP_TOOL_DESCRIPTION_TRUST_STORE_PASSWORD_FILE.get(), true, 406 true, true, false); 407 trustStorePasswordFile[i].setArgumentGroupName(groupName); 408 parser.addArgument(trustStorePasswordFile[i]); 409 410 trustStoreFormat[i] = new StringArgument(null, 411 genArgName(i, "trustStoreFormat"), false, 1, 412 INFO_LDAP_TOOL_PLACEHOLDER_FORMAT.get(), 413 INFO_LDAP_TOOL_DESCRIPTION_TRUST_STORE_FORMAT.get()); 414 trustStoreFormat[i].setArgumentGroupName(groupName); 415 parser.addArgument(trustStoreFormat[i]); 416 417 certificateNickname[i] = new StringArgument(null, 418 genArgName(i, "certNickname"), false, 1, 419 INFO_LDAP_TOOL_PLACEHOLDER_CERT_NICKNAME.get(), 420 INFO_LDAP_TOOL_DESCRIPTION_CERT_NICKNAME.get()); 421 certificateNickname[i].setArgumentGroupName(groupName); 422 parser.addArgument(certificateNickname[i]); 423 424 saslOption[i] = new StringArgument(null, genArgName(i, "saslOption"), 425 false, 0, INFO_LDAP_TOOL_PLACEHOLDER_SASL_OPTION.get(), 426 INFO_LDAP_TOOL_DESCRIPTION_SASL_OPTION.get()); 427 saslOption[i].setArgumentGroupName(groupName); 428 parser.addArgument(saslOption[i]); 429 430 parser.addDependentArgumentSet(bindDN[i], bindPassword[i], 431 bindPasswordFile[i]); 432 433 parser.addExclusiveArgumentSet(useSSL[i], useStartTLS[i]); 434 parser.addExclusiveArgumentSet(bindPassword[i], bindPasswordFile[i]); 435 parser.addExclusiveArgumentSet(keyStorePassword[i], 436 keyStorePasswordFile[i]); 437 parser.addExclusiveArgumentSet(trustStorePassword[i], 438 trustStorePasswordFile[i]); 439 parser.addExclusiveArgumentSet(trustAll[i], trustStorePath[i]); 440 } 441 442 addNonLDAPArguments(parser); 443 } 444 445 446 447 /** 448 * Constructs the name to use for an argument from the given base and the 449 * appropriate prefix and suffix. 450 * 451 * @param index The index into the set of prefixes and suffixes. 452 * @param base The base name for the argument. 453 * 454 * @return The constructed argument name. 455 */ 456 @NotNull() 457 private String genArgName(final int index, @NotNull final String base) 458 { 459 final StringBuilder buffer = new StringBuilder(); 460 461 if (serverNamePrefixes != null) 462 { 463 buffer.append(serverNamePrefixes[index]); 464 465 if (base.equals("saslOption")) 466 { 467 buffer.append("SASLOption"); 468 } 469 else 470 { 471 buffer.append(StaticUtils.capitalize(base)); 472 } 473 } 474 else 475 { 476 buffer.append(base); 477 } 478 479 if (serverNameSuffixes != null) 480 { 481 buffer.append(serverNameSuffixes[index]); 482 } 483 484 return buffer.toString(); 485 } 486 487 488 489 /** 490 * Adds the arguments needed by this command-line tool to the provided 491 * argument parser which are not related to connecting or authenticating to 492 * the directory server. 493 * 494 * @param parser The argument parser to which the arguments should be added. 495 * 496 * @throws ArgumentException If a problem occurs while adding the arguments. 497 */ 498 public abstract void addNonLDAPArguments(@NotNull ArgumentParser parser) 499 throws ArgumentException; 500 501 502 503 /** 504 * {@inheritDoc} 505 */ 506 @Override() 507 public final void doExtendedArgumentValidation() 508 throws ArgumentException 509 { 510 doExtendedNonLDAPArgumentValidation(); 511 } 512 513 514 515 /** 516 * Performs any necessary processing that should be done to ensure that the 517 * provided set of command-line arguments were valid. This method will be 518 * called after the basic argument parsing has been performed and after all 519 * LDAP-specific argument validation has been processed, and immediately 520 * before the {@link CommandLineTool#doToolProcessing} method is invoked. 521 * 522 * @throws ArgumentException If there was a problem with the command-line 523 * arguments provided to this program. 524 */ 525 public void doExtendedNonLDAPArgumentValidation() 526 throws ArgumentException 527 { 528 // No processing will be performed by default. 529 } 530 531 532 533 /** 534 * Retrieves the connection options that should be used for connections that 535 * are created with this command line tool. Subclasses may override this 536 * method to use a custom set of connection options. 537 * 538 * @return The connection options that should be used for connections that 539 * are created with this command line tool. 540 */ 541 @NotNull() 542 public LDAPConnectionOptions getConnectionOptions() 543 { 544 return new LDAPConnectionOptions(); 545 } 546 547 548 549 /** 550 * Retrieves a connection that may be used to communicate with the indicated 551 * directory server. 552 * <BR><BR> 553 * Note that this method is threadsafe and may be invoked by multiple threads 554 * accessing the same instance only while that instance is in the process of 555 * invoking the {@link #doToolProcessing} method. 556 * 557 * @param serverIndex The zero-based index of the server to which the 558 * connection should be established. 559 * 560 * @return A connection that may be used to communicate with the indicated 561 * directory server. 562 * 563 * @throws LDAPException If a problem occurs while creating the connection. 564 */ 565 @ThreadSafety(level=ThreadSafetyLevel.METHOD_THREADSAFE) 566 @NotNull() 567 public final LDAPConnection getConnection(final int serverIndex) 568 throws LDAPException 569 { 570 final LDAPConnection connection = getUnauthenticatedConnection(serverIndex); 571 572 try 573 { 574 if (bindRequest[serverIndex] != null) 575 { 576 connection.bind(bindRequest[serverIndex]); 577 } 578 } 579 catch (final LDAPException le) 580 { 581 Debug.debugException(le); 582 connection.close(); 583 throw le; 584 } 585 586 return connection; 587 } 588 589 590 591 /** 592 * Retrieves an unauthenticated connection that may be used to communicate 593 * with the indicated directory server. 594 * <BR><BR> 595 * Note that this method is threadsafe and may be invoked by multiple threads 596 * accessing the same instance only while that instance is in the process of 597 * invoking the {@link #doToolProcessing} method. 598 * 599 * @param serverIndex The zero-based index of the server to which the 600 * connection should be established. 601 * 602 * @return An unauthenticated connection that may be used to communicate with 603 * the indicated directory server. 604 * 605 * @throws LDAPException If a problem occurs while creating the connection. 606 */ 607 @ThreadSafety(level=ThreadSafetyLevel.METHOD_THREADSAFE) 608 @NotNull() 609 public final LDAPConnection getUnauthenticatedConnection( 610 final int serverIndex) 611 throws LDAPException 612 { 613 if (serverSet[serverIndex] == null) 614 { 615 serverSet[serverIndex] = createServerSet(serverIndex); 616 bindRequest[serverIndex] = createBindRequest(serverIndex); 617 } 618 619 final LDAPConnection connection = serverSet[serverIndex].getConnection(); 620 621 if (useStartTLS[serverIndex].isPresent()) 622 { 623 try 624 { 625 final ExtendedResult extendedResult = 626 connection.processExtendedOperation(new StartTLSExtendedRequest( 627 startTLSSocketFactory[serverIndex])); 628 if (! extendedResult.getResultCode().equals(ResultCode.SUCCESS)) 629 { 630 throw new LDAPException(extendedResult.getResultCode(), 631 ERR_LDAP_TOOL_START_TLS_FAILED.get( 632 extendedResult.getDiagnosticMessage())); 633 } 634 } 635 catch (final LDAPException le) 636 { 637 Debug.debugException(le); 638 connection.close(); 639 throw le; 640 } 641 } 642 643 return connection; 644 } 645 646 647 648 /** 649 * Retrieves a connection pool that may be used to communicate with the 650 * indicated directory server. 651 * <BR><BR> 652 * Note that this method is threadsafe and may be invoked by multiple threads 653 * accessing the same instance only while that instance is in the process of 654 * invoking the {@link #doToolProcessing} method. 655 * 656 * @param serverIndex The zero-based index of the server to which the 657 * connection should be established. 658 * @param initialConnections The number of connections that should be 659 * initially established in the pool. 660 * @param maxConnections The maximum number of connections to maintain 661 * in the pool. 662 * 663 * @return A connection that may be used to communicate with the indicated 664 * directory server. 665 * 666 * @throws LDAPException If a problem occurs while creating the connection 667 * pool. 668 */ 669 @ThreadSafety(level=ThreadSafetyLevel.METHOD_THREADSAFE) 670 @NotNull() 671 public final LDAPConnectionPool getConnectionPool( 672 final int serverIndex, 673 final int initialConnections, 674 final int maxConnections) 675 throws LDAPException 676 { 677 if (serverSet[serverIndex] == null) 678 { 679 serverSet[serverIndex] = createServerSet(serverIndex); 680 bindRequest[serverIndex] = createBindRequest(serverIndex); 681 } 682 683 PostConnectProcessor postConnectProcessor = null; 684 if (useStartTLS[serverIndex].isPresent()) 685 { 686 postConnectProcessor = new StartTLSPostConnectProcessor( 687 startTLSSocketFactory[serverIndex]); 688 } 689 690 return new LDAPConnectionPool(serverSet[serverIndex], 691 bindRequest[serverIndex], initialConnections, maxConnections, 692 postConnectProcessor); 693 } 694 695 696 697 /** 698 * Creates the server set to use when creating connections or connection 699 * pools. 700 * 701 * @param serverIndex The zero-based index of the server to which the 702 * connection should be established. 703 * 704 * @return The server set to use when creating connections or connection 705 * pools. 706 * 707 * @throws LDAPException If a problem occurs while creating the server set. 708 */ 709 @NotNull() 710 public final ServerSet createServerSet(final int serverIndex) 711 throws LDAPException 712 { 713 final SSLUtil sslUtil = createSSLUtil(serverIndex); 714 715 SocketFactory socketFactory = null; 716 if (useSSL[serverIndex].isPresent()) 717 { 718 try 719 { 720 socketFactory = sslUtil.createSSLSocketFactory(); 721 } 722 catch (final Exception e) 723 { 724 Debug.debugException(e); 725 throw new LDAPException(ResultCode.LOCAL_ERROR, 726 ERR_LDAP_TOOL_CANNOT_CREATE_SSL_SOCKET_FACTORY.get( 727 StaticUtils.getExceptionMessage(e)), e); 728 } 729 } 730 else if (useStartTLS[serverIndex].isPresent()) 731 { 732 try 733 { 734 startTLSSocketFactory[serverIndex] = sslUtil.createSSLSocketFactory(); 735 } 736 catch (final Exception e) 737 { 738 Debug.debugException(e); 739 throw new LDAPException(ResultCode.LOCAL_ERROR, 740 ERR_LDAP_TOOL_CANNOT_CREATE_SSL_SOCKET_FACTORY.get( 741 StaticUtils.getExceptionMessage(e)), e); 742 } 743 } 744 745 return new SingleServerSet(host[serverIndex].getValue(), 746 port[serverIndex].getValue(), socketFactory, getConnectionOptions()); 747 } 748 749 750 751 /** 752 * Creates the SSLUtil instance to use for secure communication. 753 * 754 * @param serverIndex The zero-based index of the server to which the 755 * connection should be established. 756 * 757 * @return The SSLUtil instance to use for secure communication, or 758 * {@code null} if secure communication is not needed. 759 * 760 * @throws LDAPException If a problem occurs while creating the SSLUtil 761 * instance. 762 */ 763 @Nullable() 764 public final SSLUtil createSSLUtil(final int serverIndex) 765 throws LDAPException 766 { 767 if (useSSL[serverIndex].isPresent() || useStartTLS[serverIndex].isPresent()) 768 { 769 KeyManager keyManager = null; 770 if (keyStorePath[serverIndex].isPresent()) 771 { 772 char[] pw = null; 773 if (keyStorePassword[serverIndex].isPresent()) 774 { 775 pw = keyStorePassword[serverIndex].getValue().toCharArray(); 776 } 777 else if (keyStorePasswordFile[serverIndex].isPresent()) 778 { 779 try 780 { 781 pw = getPasswordFileReader().readPassword( 782 keyStorePasswordFile[serverIndex].getValue()); 783 } 784 catch (final Exception e) 785 { 786 Debug.debugException(e); 787 throw new LDAPException(ResultCode.LOCAL_ERROR, 788 ERR_LDAP_TOOL_CANNOT_READ_KEY_STORE_PASSWORD.get( 789 StaticUtils.getExceptionMessage(e)), e); 790 } 791 } 792 793 try 794 { 795 keyManager = new KeyStoreKeyManager( 796 keyStorePath[serverIndex].getValue(), pw, 797 keyStoreFormat[serverIndex].getValue(), 798 certificateNickname[serverIndex].getValue(), true); 799 } 800 catch (final Exception e) 801 { 802 Debug.debugException(e); 803 throw new LDAPException(ResultCode.LOCAL_ERROR, 804 ERR_LDAP_TOOL_CANNOT_CREATE_KEY_MANAGER.get( 805 StaticUtils.getExceptionMessage(e)), e); 806 } 807 } 808 809 TrustManager tm; 810 if (trustAll[serverIndex].isPresent()) 811 { 812 tm = new TrustAllTrustManager(false); 813 } 814 else if (trustStorePath[serverIndex].isPresent()) 815 { 816 char[] pw = null; 817 if (trustStorePassword[serverIndex].isPresent()) 818 { 819 pw = trustStorePassword[serverIndex].getValue().toCharArray(); 820 } 821 else if (trustStorePasswordFile[serverIndex].isPresent()) 822 { 823 try 824 { 825 pw = getPasswordFileReader().readPassword( 826 trustStorePasswordFile[serverIndex].getValue()); 827 } 828 catch (final Exception e) 829 { 830 Debug.debugException(e); 831 throw new LDAPException(ResultCode.LOCAL_ERROR, 832 ERR_LDAP_TOOL_CANNOT_READ_TRUST_STORE_PASSWORD.get( 833 StaticUtils.getExceptionMessage(e)), e); 834 } 835 } 836 837 tm = new TrustStoreTrustManager( 838 trustStorePath[serverIndex].getValue(), pw, 839 trustStoreFormat[serverIndex].getValue(), true); 840 } 841 else 842 { 843 tm = promptTrustManager.get(); 844 if (tm == null) 845 { 846 final AggregateTrustManager atm = 847 InternalSDKHelper.getPreferredPromptTrustManagerChain(null); 848 if (promptTrustManager.compareAndSet(null, atm)) 849 { 850 tm = atm; 851 } 852 else 853 { 854 tm = promptTrustManager.get(); 855 } 856 } 857 } 858 859 return new SSLUtil(keyManager, tm); 860 } 861 else 862 { 863 return null; 864 } 865 } 866 867 868 869 /** 870 * Creates the bind request to use to authenticate to the indicated server. 871 * 872 * @param serverIndex The zero-based index of the server to which the 873 * connection should be established. 874 * 875 * @return The bind request to use to authenticate to the indicated server, 876 * or {@code null} if no bind should be performed. 877 * 878 * @throws LDAPException If a problem occurs while creating the bind 879 * request. 880 */ 881 @Nullable() 882 public final BindRequest createBindRequest(final int serverIndex) 883 throws LDAPException 884 { 885 final String pw; 886 if (bindPassword[serverIndex].isPresent()) 887 { 888 pw = bindPassword[serverIndex].getValue(); 889 } 890 else if (bindPasswordFile[serverIndex].isPresent()) 891 { 892 try 893 { 894 pw = new String(getPasswordFileReader().readPassword( 895 bindPasswordFile[serverIndex].getValue())); 896 } 897 catch (final Exception e) 898 { 899 Debug.debugException(e); 900 throw new LDAPException(ResultCode.LOCAL_ERROR, 901 ERR_LDAP_TOOL_CANNOT_READ_BIND_PASSWORD.get( 902 StaticUtils.getExceptionMessage(e)), e); 903 } 904 } 905 else 906 { 907 pw = null; 908 } 909 910 if (saslOption[serverIndex].isPresent()) 911 { 912 final String dnStr; 913 if (bindDN[serverIndex].isPresent()) 914 { 915 dnStr = bindDN[serverIndex].getValue().toString(); 916 } 917 else 918 { 919 dnStr = null; 920 } 921 922 return SASLUtils.createBindRequest(dnStr, pw, null, 923 saslOption[serverIndex].getValues()); 924 } 925 else if (bindDN[serverIndex].isPresent()) 926 { 927 return new SimpleBindRequest(bindDN[serverIndex].getValue(), pw); 928 } 929 else 930 { 931 return null; 932 } 933 } 934}