001/* 002 * Copyright 2012-2016 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2012-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.concurrent.atomic.AtomicReference; 027import javax.net.SocketFactory; 028import javax.net.ssl.KeyManager; 029import javax.net.ssl.SSLSocketFactory; 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 SSLSocketFactory[] startTLSSocketFactory; 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 startTLSSocketFactory = new SSLSocketFactory[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 final StringBuilder groupNameBuffer = new StringBuilder(); 269 if (serverNamePrefixes != null) 270 { 271 final String prefix = serverNamePrefixes[i].replace('-', ' ').trim(); 272 groupNameBuffer.append(StaticUtils.capitalize(prefix, true)); 273 } 274 275 if (serverNameSuffixes != null) 276 { 277 if (groupNameBuffer.length() > 0) 278 { 279 groupNameBuffer.append(' '); 280 } 281 282 final String suffix = serverNameSuffixes[i].replace('-', ' ').trim(); 283 groupNameBuffer.append(StaticUtils.capitalize(suffix, true)); 284 } 285 286 groupNameBuffer.append(' '); 287 groupNameBuffer.append(INFO_MULTI_LDAP_TOOL_GROUP_CONN_AND_AUTH.get()); 288 final String groupName = groupNameBuffer.toString(); 289 290 291 host[i] = new StringArgument(null, genArgName(i, "hostname"), true, 1, 292 INFO_LDAP_TOOL_PLACEHOLDER_HOST.get(), 293 INFO_LDAP_TOOL_DESCRIPTION_HOST.get(), "localhost"); 294 host[i].setArgumentGroupName(groupName); 295 parser.addArgument(host[i]); 296 297 port[i] = new IntegerArgument(null, genArgName(i, "port"), true, 1, 298 INFO_LDAP_TOOL_PLACEHOLDER_PORT.get(), 299 INFO_LDAP_TOOL_DESCRIPTION_PORT.get(), 1, 65535, 389); 300 port[i].setArgumentGroupName(groupName); 301 parser.addArgument(port[i]); 302 303 bindDN[i] = new DNArgument(null, genArgName(i, "bindDN"), false, 1, 304 INFO_LDAP_TOOL_PLACEHOLDER_DN.get(), 305 INFO_LDAP_TOOL_DESCRIPTION_BIND_DN.get()); 306 bindDN[i].setArgumentGroupName(groupName); 307 parser.addArgument(bindDN[i]); 308 309 bindPassword[i] = new StringArgument(null, genArgName(i, "bindPassword"), 310 false, 1, INFO_LDAP_TOOL_PLACEHOLDER_PASSWORD.get(), 311 INFO_LDAP_TOOL_DESCRIPTION_BIND_PW.get()); 312 bindPassword[i].setArgumentGroupName(groupName); 313 parser.addArgument(bindPassword[i]); 314 315 bindPasswordFile[i] = new FileArgument(null, 316 genArgName(i, "bindPasswordFile"), false, 1, 317 INFO_LDAP_TOOL_PLACEHOLDER_PATH.get(), 318 INFO_LDAP_TOOL_DESCRIPTION_BIND_PW_FILE.get(), true, true, true, 319 false); 320 bindPasswordFile[i].setArgumentGroupName(groupName); 321 parser.addArgument(bindPasswordFile[i]); 322 323 useSSL[i] = new BooleanArgument(null, genArgName(i, "useSSL"), 1, 324 INFO_LDAP_TOOL_DESCRIPTION_USE_SSL.get()); 325 useSSL[i].setArgumentGroupName(groupName); 326 parser.addArgument(useSSL[i]); 327 328 useStartTLS[i] = new BooleanArgument(null, genArgName(i, "useStartTLS"), 329 1, INFO_LDAP_TOOL_DESCRIPTION_USE_START_TLS.get()); 330 useStartTLS[i].setArgumentGroupName(groupName); 331 parser.addArgument(useStartTLS[i]); 332 333 trustAll[i] = new BooleanArgument(null, genArgName(i, "trustAll"), 1, 334 INFO_LDAP_TOOL_DESCRIPTION_TRUST_ALL.get()); 335 trustAll[i].setArgumentGroupName(groupName); 336 parser.addArgument(trustAll[i]); 337 338 keyStorePath[i] = new StringArgument(null, genArgName(i, "keyStorePath"), 339 false, 1, INFO_LDAP_TOOL_PLACEHOLDER_PATH.get(), 340 INFO_LDAP_TOOL_DESCRIPTION_KEY_STORE_PATH.get()); 341 keyStorePath[i].setArgumentGroupName(groupName); 342 parser.addArgument(keyStorePath[i]); 343 344 keyStorePassword[i] = new StringArgument(null, 345 genArgName(i, "keyStorePassword"), false, 1, 346 INFO_LDAP_TOOL_PLACEHOLDER_PASSWORD.get(), 347 INFO_LDAP_TOOL_DESCRIPTION_KEY_STORE_PASSWORD.get()); 348 keyStorePassword[i].setArgumentGroupName(groupName); 349 parser.addArgument(keyStorePassword[i]); 350 351 keyStorePasswordFile[i] = new FileArgument(null, 352 genArgName(i, "keyStorePasswordFile"), false, 1, 353 INFO_LDAP_TOOL_PLACEHOLDER_PATH.get(), 354 INFO_LDAP_TOOL_DESCRIPTION_KEY_STORE_PASSWORD_FILE.get(), true, 355 true, true, false); 356 keyStorePasswordFile[i].setArgumentGroupName(groupName); 357 parser.addArgument(keyStorePasswordFile[i]); 358 359 keyStoreFormat[i] = new StringArgument(null, 360 genArgName(i, "keyStoreFormat"), false, 1, 361 INFO_LDAP_TOOL_PLACEHOLDER_FORMAT.get(), 362 INFO_LDAP_TOOL_DESCRIPTION_KEY_STORE_FORMAT.get()); 363 keyStoreFormat[i].setArgumentGroupName(groupName); 364 parser.addArgument(keyStoreFormat[i]); 365 366 trustStorePath[i] = new StringArgument(null, 367 genArgName(i, "trustStorePath"), false, 1, 368 INFO_LDAP_TOOL_PLACEHOLDER_PATH.get(), 369 INFO_LDAP_TOOL_DESCRIPTION_TRUST_STORE_PATH.get()); 370 trustStorePath[i].setArgumentGroupName(groupName); 371 parser.addArgument(trustStorePath[i]); 372 373 trustStorePassword[i] = new StringArgument(null, 374 genArgName(i, "trustStorePassword"), false, 1, 375 INFO_LDAP_TOOL_PLACEHOLDER_PASSWORD.get(), 376 INFO_LDAP_TOOL_DESCRIPTION_TRUST_STORE_PASSWORD.get()); 377 trustStorePassword[i].setArgumentGroupName(groupName); 378 parser.addArgument(trustStorePassword[i]); 379 380 trustStorePasswordFile[i] = new FileArgument(null, 381 genArgName(i, "trustStorePasswordFile"), false, 1, 382 INFO_LDAP_TOOL_PLACEHOLDER_PATH.get(), 383 INFO_LDAP_TOOL_DESCRIPTION_TRUST_STORE_PASSWORD_FILE.get(), true, 384 true, true, false); 385 trustStorePasswordFile[i].setArgumentGroupName(groupName); 386 parser.addArgument(trustStorePasswordFile[i]); 387 388 trustStoreFormat[i] = new StringArgument(null, 389 genArgName(i, "trustStoreFormat"), false, 1, 390 INFO_LDAP_TOOL_PLACEHOLDER_FORMAT.get(), 391 INFO_LDAP_TOOL_DESCRIPTION_TRUST_STORE_FORMAT.get()); 392 trustStoreFormat[i].setArgumentGroupName(groupName); 393 parser.addArgument(trustStoreFormat[i]); 394 395 certificateNickname[i] = new StringArgument(null, 396 genArgName(i, "certNickname"), false, 1, 397 INFO_LDAP_TOOL_PLACEHOLDER_CERT_NICKNAME.get(), 398 INFO_LDAP_TOOL_DESCRIPTION_CERT_NICKNAME.get()); 399 certificateNickname[i].setArgumentGroupName(groupName); 400 parser.addArgument(certificateNickname[i]); 401 402 saslOption[i] = new StringArgument(null, genArgName(i, "saslOption"), 403 false, 0, INFO_LDAP_TOOL_PLACEHOLDER_SASL_OPTION.get(), 404 INFO_LDAP_TOOL_DESCRIPTION_SASL_OPTION.get()); 405 saslOption[i].setArgumentGroupName(groupName); 406 parser.addArgument(saslOption[i]); 407 408 parser.addDependentArgumentSet(bindDN[i], bindPassword[i], 409 bindPasswordFile[i]); 410 411 parser.addExclusiveArgumentSet(useSSL[i], useStartTLS[i]); 412 parser.addExclusiveArgumentSet(bindPassword[i], bindPasswordFile[i]); 413 parser.addExclusiveArgumentSet(keyStorePassword[i], 414 keyStorePasswordFile[i]); 415 parser.addExclusiveArgumentSet(trustStorePassword[i], 416 trustStorePasswordFile[i]); 417 parser.addExclusiveArgumentSet(trustAll[i], trustStorePath[i]); 418 } 419 420 addNonLDAPArguments(parser); 421 } 422 423 424 425 /** 426 * Constructs the name to use for an argument from the given base and the 427 * appropriate prefix and suffix. 428 * 429 * @param index The index into the set of prefixes and suffixes. 430 * @param base The base name for the argument. 431 * 432 * @return The constructed argument name. 433 */ 434 private String genArgName(final int index, final String base) 435 { 436 final StringBuilder buffer = new StringBuilder(); 437 438 if (serverNamePrefixes != null) 439 { 440 buffer.append(serverNamePrefixes[index]); 441 442 if (base.equals("saslOption")) 443 { 444 buffer.append("SASLOption"); 445 } 446 else 447 { 448 buffer.append(StaticUtils.capitalize(base)); 449 } 450 } 451 else 452 { 453 buffer.append(base); 454 } 455 456 if (serverNameSuffixes != null) 457 { 458 buffer.append(serverNameSuffixes[index]); 459 } 460 461 return buffer.toString(); 462 } 463 464 465 466 /** 467 * Adds the arguments needed by this command-line tool to the provided 468 * argument parser which are not related to connecting or authenticating to 469 * the directory server. 470 * 471 * @param parser The argument parser to which the arguments should be added. 472 * 473 * @throws ArgumentException If a problem occurs while adding the arguments. 474 */ 475 public abstract void addNonLDAPArguments(final ArgumentParser parser) 476 throws ArgumentException; 477 478 479 480 /** 481 * {@inheritDoc} 482 */ 483 @Override() 484 public final void doExtendedArgumentValidation() 485 throws ArgumentException 486 { 487 doExtendedNonLDAPArgumentValidation(); 488 } 489 490 491 492 /** 493 * Performs any necessary processing that should be done to ensure that the 494 * provided set of command-line arguments were valid. This method will be 495 * called after the basic argument parsing has been performed and after all 496 * LDAP-specific argument validation has been processed, and immediately 497 * before the {@link CommandLineTool#doToolProcessing} method is invoked. 498 * 499 * @throws ArgumentException If there was a problem with the command-line 500 * arguments provided to this program. 501 */ 502 public void doExtendedNonLDAPArgumentValidation() 503 throws ArgumentException 504 { 505 // No processing will be performed by default. 506 } 507 508 509 510 /** 511 * Retrieves the connection options that should be used for connections that 512 * are created with this command line tool. Subclasses may override this 513 * method to use a custom set of connection options. 514 * 515 * @return The connection options that should be used for connections that 516 * are created with this command line tool. 517 */ 518 public LDAPConnectionOptions getConnectionOptions() 519 { 520 return new LDAPConnectionOptions(); 521 } 522 523 524 525 /** 526 * Retrieves a connection that may be used to communicate with the indicated 527 * 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 A connection that may be used to communicate with the indicated 537 * 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 getConnection(final int serverIndex) 543 throws LDAPException 544 { 545 final LDAPConnection connection = getUnauthenticatedConnection(serverIndex); 546 547 try 548 { 549 if (bindRequest[serverIndex] != null) 550 { 551 connection.bind(bindRequest[serverIndex]); 552 } 553 } 554 catch (LDAPException le) 555 { 556 Debug.debugException(le); 557 connection.close(); 558 throw le; 559 } 560 561 return connection; 562 } 563 564 565 566 /** 567 * Retrieves an unauthenticated connection that may be used to communicate 568 * with the indicated directory server. 569 * <BR><BR> 570 * Note that this method is threadsafe and may be invoked by multiple threads 571 * accessing the same instance only while that instance is in the process of 572 * invoking the {@link #doToolProcessing} method. 573 * 574 * @param serverIndex The zero-based index of the server to which the 575 * connection should be established. 576 * 577 * @return An unauthenticated connection that may be used to communicate with 578 * the indicated directory server. 579 * 580 * @throws LDAPException If a problem occurs while creating the connection. 581 */ 582 @ThreadSafety(level=ThreadSafetyLevel.METHOD_THREADSAFE) 583 public final LDAPConnection getUnauthenticatedConnection( 584 final int serverIndex) 585 throws LDAPException 586 { 587 if (serverSet[serverIndex] == null) 588 { 589 serverSet[serverIndex] = createServerSet(serverIndex); 590 bindRequest[serverIndex] = createBindRequest(serverIndex); 591 } 592 593 final LDAPConnection connection = serverSet[serverIndex].getConnection(); 594 595 if (useStartTLS[serverIndex].isPresent()) 596 { 597 try 598 { 599 final ExtendedResult extendedResult = 600 connection.processExtendedOperation(new StartTLSExtendedRequest( 601 startTLSSocketFactory[serverIndex])); 602 if (! extendedResult.getResultCode().equals(ResultCode.SUCCESS)) 603 { 604 throw new LDAPException(extendedResult.getResultCode(), 605 ERR_LDAP_TOOL_START_TLS_FAILED.get( 606 extendedResult.getDiagnosticMessage())); 607 } 608 } 609 catch (LDAPException le) 610 { 611 Debug.debugException(le); 612 connection.close(); 613 throw le; 614 } 615 } 616 617 return connection; 618 } 619 620 621 622 /** 623 * Retrieves a connection pool that may be used to communicate with the 624 * indicated directory server. 625 * <BR><BR> 626 * Note that this method is threadsafe and may be invoked by multiple threads 627 * accessing the same instance only while that instance is in the process of 628 * invoking the {@link #doToolProcessing} method. 629 * 630 * @param serverIndex The zero-based index of the server to which the 631 * connection should be established. 632 * @param initialConnections The number of connections that should be 633 * initially established in the pool. 634 * @param maxConnections The maximum number of connections to maintain 635 * in the pool. 636 * 637 * @return A connection that may be used to communicate with the indicated 638 * directory server. 639 * 640 * @throws LDAPException If a problem occurs while creating the connection 641 * pool. 642 */ 643 @ThreadSafety(level=ThreadSafetyLevel.METHOD_THREADSAFE) 644 public final LDAPConnectionPool getConnectionPool( 645 final int serverIndex, 646 final int initialConnections, 647 final int maxConnections) 648 throws LDAPException 649 { 650 if (serverSet[serverIndex] == null) 651 { 652 serverSet[serverIndex] = createServerSet(serverIndex); 653 bindRequest[serverIndex] = createBindRequest(serverIndex); 654 } 655 656 PostConnectProcessor postConnectProcessor = null; 657 if (useStartTLS[serverIndex].isPresent()) 658 { 659 postConnectProcessor = new StartTLSPostConnectProcessor( 660 startTLSSocketFactory[serverIndex]); 661 } 662 663 return new LDAPConnectionPool(serverSet[serverIndex], 664 bindRequest[serverIndex], initialConnections, maxConnections, 665 postConnectProcessor); 666 } 667 668 669 670 /** 671 * Creates the server set to use when creating connections or connection 672 * pools. 673 * 674 * @param serverIndex The zero-based index of the server to which the 675 * connection should be established. 676 * 677 * @return The server set to use when creating connections or connection 678 * pools. 679 * 680 * @throws LDAPException If a problem occurs while creating the server set. 681 */ 682 public final ServerSet createServerSet(final int serverIndex) 683 throws LDAPException 684 { 685 final SSLUtil sslUtil = createSSLUtil(serverIndex); 686 687 SocketFactory socketFactory = null; 688 if (useSSL[serverIndex].isPresent()) 689 { 690 try 691 { 692 socketFactory = sslUtil.createSSLSocketFactory(); 693 } 694 catch (Exception e) 695 { 696 Debug.debugException(e); 697 throw new LDAPException(ResultCode.LOCAL_ERROR, 698 ERR_LDAP_TOOL_CANNOT_CREATE_SSL_SOCKET_FACTORY.get( 699 StaticUtils.getExceptionMessage(e)), e); 700 } 701 } 702 else if (useStartTLS[serverIndex].isPresent()) 703 { 704 try 705 { 706 startTLSSocketFactory[serverIndex] = sslUtil.createSSLSocketFactory(); 707 } 708 catch (Exception e) 709 { 710 Debug.debugException(e); 711 throw new LDAPException(ResultCode.LOCAL_ERROR, 712 ERR_LDAP_TOOL_CANNOT_CREATE_SSL_SOCKET_FACTORY.get( 713 StaticUtils.getExceptionMessage(e)), e); 714 } 715 } 716 717 return new SingleServerSet(host[serverIndex].getValue(), 718 port[serverIndex].getValue(), socketFactory, getConnectionOptions()); 719 } 720 721 722 723 /** 724 * Creates the SSLUtil instance to use for secure communication. 725 * 726 * @param serverIndex The zero-based index of the server to which the 727 * connection should be established. 728 * 729 * @return The SSLUtil instance to use for secure communication, or 730 * {@code null} if secure communication is not needed. 731 * 732 * @throws LDAPException If a problem occurs while creating the SSLUtil 733 * instance. 734 */ 735 public final SSLUtil createSSLUtil(final int serverIndex) 736 throws LDAPException 737 { 738 if (useSSL[serverIndex].isPresent() || useStartTLS[serverIndex].isPresent()) 739 { 740 KeyManager keyManager = null; 741 if (keyStorePath[serverIndex].isPresent()) 742 { 743 char[] pw = null; 744 if (keyStorePassword[serverIndex].isPresent()) 745 { 746 pw = keyStorePassword[serverIndex].getValue().toCharArray(); 747 } 748 else if (keyStorePasswordFile[serverIndex].isPresent()) 749 { 750 try 751 { 752 pw = keyStorePasswordFile[serverIndex].getNonBlankFileLines(). 753 get(0).toCharArray(); 754 } 755 catch (Exception e) 756 { 757 Debug.debugException(e); 758 throw new LDAPException(ResultCode.LOCAL_ERROR, 759 ERR_LDAP_TOOL_CANNOT_READ_KEY_STORE_PASSWORD.get( 760 StaticUtils.getExceptionMessage(e)), e); 761 } 762 } 763 764 try 765 { 766 keyManager = new KeyStoreKeyManager( 767 keyStorePath[serverIndex].getValue(), pw, 768 keyStoreFormat[serverIndex].getValue(), 769 certificateNickname[serverIndex].getValue()); 770 } 771 catch (Exception e) 772 { 773 Debug.debugException(e); 774 throw new LDAPException(ResultCode.LOCAL_ERROR, 775 ERR_LDAP_TOOL_CANNOT_CREATE_KEY_MANAGER.get( 776 StaticUtils.getExceptionMessage(e)), e); 777 } 778 } 779 780 TrustManager trustManager; 781 if (trustAll[serverIndex].isPresent()) 782 { 783 trustManager = new TrustAllTrustManager(false); 784 } 785 else if (trustStorePath[serverIndex].isPresent()) 786 { 787 char[] pw = null; 788 if (trustStorePassword[serverIndex].isPresent()) 789 { 790 pw = trustStorePassword[serverIndex].getValue().toCharArray(); 791 } 792 else if (trustStorePasswordFile[serverIndex].isPresent()) 793 { 794 try 795 { 796 pw = trustStorePasswordFile[serverIndex].getNonBlankFileLines(). 797 get(0).toCharArray(); 798 } 799 catch (Exception e) 800 { 801 Debug.debugException(e); 802 throw new LDAPException(ResultCode.LOCAL_ERROR, 803 ERR_LDAP_TOOL_CANNOT_READ_TRUST_STORE_PASSWORD.get( 804 StaticUtils.getExceptionMessage(e)), e); 805 } 806 } 807 808 trustManager = new TrustStoreTrustManager( 809 trustStorePath[serverIndex].getValue(), pw, 810 trustStoreFormat[serverIndex].getValue(), true); 811 } 812 else 813 { 814 trustManager = promptTrustManager.get(); 815 if (trustManager == null) 816 { 817 final PromptTrustManager m = new PromptTrustManager(); 818 promptTrustManager.compareAndSet(null, m); 819 trustManager = promptTrustManager.get(); 820 } 821 } 822 823 return new SSLUtil(keyManager, trustManager); 824 } 825 else 826 { 827 return null; 828 } 829 } 830 831 832 833 /** 834 * Creates the bind request to use to authenticate to the indicated server. 835 * 836 * @param serverIndex The zero-based index of the server to which the 837 * connection should be established. 838 * 839 * @return The bind request to use to authenticate to the indicated server, 840 * or {@code null} if no bind should be performed. 841 * 842 * @throws LDAPException If a problem occurs while creating the bind 843 * request. 844 */ 845 public final BindRequest createBindRequest(final int serverIndex) 846 throws LDAPException 847 { 848 final String pw; 849 if (bindPassword[serverIndex].isPresent()) 850 { 851 pw = bindPassword[serverIndex].getValue(); 852 } 853 else if (bindPasswordFile[serverIndex].isPresent()) 854 { 855 try 856 { 857 pw = bindPasswordFile[serverIndex].getNonBlankFileLines().get(0); 858 } 859 catch (Exception e) 860 { 861 Debug.debugException(e); 862 throw new LDAPException(ResultCode.LOCAL_ERROR, 863 ERR_LDAP_TOOL_CANNOT_READ_BIND_PASSWORD.get( 864 StaticUtils.getExceptionMessage(e)), e); 865 } 866 } 867 else 868 { 869 pw = null; 870 } 871 872 if (saslOption[serverIndex].isPresent()) 873 { 874 final String dnStr; 875 if (bindDN[serverIndex].isPresent()) 876 { 877 dnStr = bindDN[serverIndex].getValue().toString(); 878 } 879 else 880 { 881 dnStr = null; 882 } 883 884 return SASLUtils.createBindRequest(dnStr, pw, null, 885 saslOption[serverIndex].getValues()); 886 } 887 else if (bindDN[serverIndex].isPresent()) 888 { 889 return new SimpleBindRequest(bindDN[serverIndex].getValue(), pw); 890 } 891 else 892 { 893 return null; 894 } 895 } 896}