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