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.LDAPConnection; 050import com.unboundid.ldap.sdk.LDAPConnectionOptions; 051import com.unboundid.ldap.sdk.LDAPConnectionPool; 052import com.unboundid.ldap.sdk.LDAPException; 053import com.unboundid.ldap.sdk.PostConnectProcessor; 054import com.unboundid.ldap.sdk.ResultCode; 055import com.unboundid.ldap.sdk.ServerSet; 056import com.unboundid.ldap.sdk.SimpleBindRequest; 057import com.unboundid.ldap.sdk.SingleServerSet; 058import com.unboundid.ldap.sdk.StartTLSPostConnectProcessor; 059import com.unboundid.ldap.sdk.extensions.StartTLSExtendedRequest; 060import com.unboundid.util.args.ArgumentException; 061import com.unboundid.util.args.ArgumentParser; 062import com.unboundid.util.args.BooleanArgument; 063import com.unboundid.util.args.DNArgument; 064import com.unboundid.util.args.FileArgument; 065import com.unboundid.util.args.IntegerArgument; 066import com.unboundid.util.args.StringArgument; 067import com.unboundid.util.ssl.AggregateTrustManager; 068import com.unboundid.util.ssl.JVMDefaultTrustManager; 069import com.unboundid.util.ssl.KeyStoreKeyManager; 070import com.unboundid.util.ssl.PromptTrustManager; 071import com.unboundid.util.ssl.SSLUtil; 072import com.unboundid.util.ssl.TrustAllTrustManager; 073import com.unboundid.util.ssl.TrustStoreTrustManager; 074 075import static com.unboundid.util.UtilityMessages.*; 076 077 078 079/** 080 * This class provides a basis for developing command-line tools that have the 081 * ability to communicate with multiple directory servers, potentially with 082 * very different settings for each. For example, it may be used to help create 083 * tools that move or compare data from one server to another. 084 * <BR><BR> 085 * Each server will be identified by a prefix and/or suffix that will be added 086 * to the argument name (e.g., if the first server has a prefix of "source", 087 * then the "hostname" argument will actually be "sourceHostname"). The 088 * base names for the arguments this class supports include: 089 * <UL> 090 * <LI>hostname -- Specifies the address of the directory server. If this 091 * isn't specified, then a default of "localhost" will be used.</LI> 092 * <LI>port -- specifies the port number of the directory server. If this 093 * isn't specified, then a default port of 389 will be used.</LI> 094 * <LI>bindDN -- Specifies the DN to use to bind to the directory server using 095 * simple authentication. If this isn't specified, then simple 096 * authentication will not be performed.</LI> 097 * <LI>bindPassword -- Specifies the password to use when binding with simple 098 * authentication or a password-based SASL mechanism.</LI> 099 * <LI>bindPasswordFile -- Specifies the path to a file containing the 100 * password to use when binding with simple authentication or a 101 * password-based SASL mechanism.</LI> 102 * <LI>useSSL -- Indicates that communication with the server should be 103 * secured using SSL.</LI> 104 * <LI>useStartTLS -- Indicates that communication with the server should be 105 * secured using StartTLS.</LI> 106 * <LI>trustAll -- Indicates that the client should trust any certificate 107 * that the server presents to it.</LI> 108 * <LI>keyStorePath -- Specifies the path to the key store to use to obtain 109 * client certificates.</LI> 110 * <LI>keyStorePassword -- Specifies the password to use to access the 111 * contents of the key store.</LI> 112 * <LI>keyStorePasswordFile -- Specifies the path ot a file containing the 113 * password to use to access the contents of the key store.</LI> 114 * <LI>keyStoreFormat -- Specifies the format to use for the key store 115 * file.</LI> 116 * <LI>trustStorePath -- Specifies the path to the trust store to use to 117 * obtain client certificates.</LI> 118 * <LI>trustStorePassword -- Specifies the password to use to access the 119 * contents of the trust store.</LI> 120 * <LI>trustStorePasswordFile -- Specifies the path ot a file containing the 121 * password to use to access the contents of the trust store.</LI> 122 * <LI>trustStoreFormat -- Specifies the format to use for the trust store 123 * file.</LI> 124 * <LI>certNickname -- Specifies the nickname of the client certificate to 125 * use when performing SSL client authentication.</LI> 126 * <LI>saslOption -- Specifies a SASL option to use when performing SASL 127 * authentication.</LI> 128 * </UL> 129 * If SASL authentication is to be used, then a "mech" SASL option must be 130 * provided to specify the name of the SASL mechanism to use. Depending on the 131 * SASL mechanism, additional SASL options may be required or optional. 132 */ 133@Extensible() 134@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_NOT_THREADSAFE) 135public abstract class MultiServerLDAPCommandLineTool 136 extends CommandLineTool 137{ 138 // The set of prefixes and suffixes that will be used for server names. 139 private final int numServers; 140 private final String[] serverNamePrefixes; 141 private final String[] serverNameSuffixes; 142 143 // The set of arguments used to hold information about connection properties. 144 private final BooleanArgument[] trustAll; 145 private final BooleanArgument[] useSSL; 146 private final BooleanArgument[] useStartTLS; 147 private final DNArgument[] bindDN; 148 private final FileArgument[] bindPasswordFile; 149 private final FileArgument[] keyStorePasswordFile; 150 private final FileArgument[] trustStorePasswordFile; 151 private final IntegerArgument[] port; 152 private final StringArgument[] bindPassword; 153 private final StringArgument[] certificateNickname; 154 private final StringArgument[] host; 155 private final StringArgument[] keyStoreFormat; 156 private final StringArgument[] keyStorePath; 157 private final StringArgument[] keyStorePassword; 158 private final StringArgument[] saslOption; 159 private final StringArgument[] trustStoreFormat; 160 private final StringArgument[] trustStorePath; 161 private final StringArgument[] trustStorePassword; 162 163 // Variables used when creating and authenticating connections. 164 private final BindRequest[] bindRequest; 165 private final ServerSet[] serverSet; 166 private final SSLSocketFactory[] startTLSSocketFactory; 167 168 // An atomic reference to an aggregate trust manager that will check a 169 // JVM-default set of trusted issuers, and then its own cache, before 170 // prompting the user about whether to trust the presented certificate chain. 171 // Re-using this trust manager will allow the tool to benefit from a common 172 // cache if multiple connections are needed. 173 private final AtomicReference<AggregateTrustManager> 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(final OutputStream outStream, 210 final OutputStream errStream, 211 final String[] serverNamePrefixes, 212 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(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 private String genArgName(final int index, final String base) 457 { 458 final StringBuilder buffer = new StringBuilder(); 459 460 if (serverNamePrefixes != null) 461 { 462 buffer.append(serverNamePrefixes[index]); 463 464 if (base.equals("saslOption")) 465 { 466 buffer.append("SASLOption"); 467 } 468 else 469 { 470 buffer.append(StaticUtils.capitalize(base)); 471 } 472 } 473 else 474 { 475 buffer.append(base); 476 } 477 478 if (serverNameSuffixes != null) 479 { 480 buffer.append(serverNameSuffixes[index]); 481 } 482 483 return buffer.toString(); 484 } 485 486 487 488 /** 489 * Adds the arguments needed by this command-line tool to the provided 490 * argument parser which are not related to connecting or authenticating to 491 * the directory server. 492 * 493 * @param parser The argument parser to which the arguments should be added. 494 * 495 * @throws ArgumentException If a problem occurs while adding the arguments. 496 */ 497 public abstract void addNonLDAPArguments(ArgumentParser parser) 498 throws ArgumentException; 499 500 501 502 /** 503 * {@inheritDoc} 504 */ 505 @Override() 506 public final void doExtendedArgumentValidation() 507 throws ArgumentException 508 { 509 doExtendedNonLDAPArgumentValidation(); 510 } 511 512 513 514 /** 515 * Performs any necessary processing that should be done to ensure that the 516 * provided set of command-line arguments were valid. This method will be 517 * called after the basic argument parsing has been performed and after all 518 * LDAP-specific argument validation has been processed, and immediately 519 * before the {@link CommandLineTool#doToolProcessing} method is invoked. 520 * 521 * @throws ArgumentException If there was a problem with the command-line 522 * arguments provided to this program. 523 */ 524 public void doExtendedNonLDAPArgumentValidation() 525 throws ArgumentException 526 { 527 // No processing will be performed by default. 528 } 529 530 531 532 /** 533 * Retrieves the connection options that should be used for connections that 534 * are created with this command line tool. Subclasses may override this 535 * method to use a custom set of connection options. 536 * 537 * @return The connection options that should be used for connections that 538 * are created with this command line tool. 539 */ 540 public LDAPConnectionOptions getConnectionOptions() 541 { 542 return new LDAPConnectionOptions(); 543 } 544 545 546 547 /** 548 * Retrieves a connection that may be used to communicate with the indicated 549 * directory server. 550 * <BR><BR> 551 * Note that this method is threadsafe and may be invoked by multiple threads 552 * accessing the same instance only while that instance is in the process of 553 * invoking the {@link #doToolProcessing} method. 554 * 555 * @param serverIndex The zero-based index of the server to which the 556 * connection should be established. 557 * 558 * @return A connection that may be used to communicate with the indicated 559 * directory server. 560 * 561 * @throws LDAPException If a problem occurs while creating the connection. 562 */ 563 @ThreadSafety(level=ThreadSafetyLevel.METHOD_THREADSAFE) 564 public final LDAPConnection getConnection(final int serverIndex) 565 throws LDAPException 566 { 567 final LDAPConnection connection = getUnauthenticatedConnection(serverIndex); 568 569 try 570 { 571 if (bindRequest[serverIndex] != null) 572 { 573 connection.bind(bindRequest[serverIndex]); 574 } 575 } 576 catch (final LDAPException le) 577 { 578 Debug.debugException(le); 579 connection.close(); 580 throw le; 581 } 582 583 return connection; 584 } 585 586 587 588 /** 589 * Retrieves an unauthenticated connection that may be used to communicate 590 * with the indicated directory server. 591 * <BR><BR> 592 * Note that this method is threadsafe and may be invoked by multiple threads 593 * accessing the same instance only while that instance is in the process of 594 * invoking the {@link #doToolProcessing} method. 595 * 596 * @param serverIndex The zero-based index of the server to which the 597 * connection should be established. 598 * 599 * @return An unauthenticated connection that may be used to communicate with 600 * the indicated directory server. 601 * 602 * @throws LDAPException If a problem occurs while creating the connection. 603 */ 604 @ThreadSafety(level=ThreadSafetyLevel.METHOD_THREADSAFE) 605 public final LDAPConnection getUnauthenticatedConnection( 606 final int serverIndex) 607 throws LDAPException 608 { 609 if (serverSet[serverIndex] == null) 610 { 611 serverSet[serverIndex] = createServerSet(serverIndex); 612 bindRequest[serverIndex] = createBindRequest(serverIndex); 613 } 614 615 final LDAPConnection connection = serverSet[serverIndex].getConnection(); 616 617 if (useStartTLS[serverIndex].isPresent()) 618 { 619 try 620 { 621 final ExtendedResult extendedResult = 622 connection.processExtendedOperation(new StartTLSExtendedRequest( 623 startTLSSocketFactory[serverIndex])); 624 if (! extendedResult.getResultCode().equals(ResultCode.SUCCESS)) 625 { 626 throw new LDAPException(extendedResult.getResultCode(), 627 ERR_LDAP_TOOL_START_TLS_FAILED.get( 628 extendedResult.getDiagnosticMessage())); 629 } 630 } 631 catch (final LDAPException le) 632 { 633 Debug.debugException(le); 634 connection.close(); 635 throw le; 636 } 637 } 638 639 return connection; 640 } 641 642 643 644 /** 645 * Retrieves a connection pool that may be used to communicate with the 646 * indicated directory server. 647 * <BR><BR> 648 * Note that this method is threadsafe and may be invoked by multiple threads 649 * accessing the same instance only while that instance is in the process of 650 * invoking the {@link #doToolProcessing} method. 651 * 652 * @param serverIndex The zero-based index of the server to which the 653 * connection should be established. 654 * @param initialConnections The number of connections that should be 655 * initially established in the pool. 656 * @param maxConnections The maximum number of connections to maintain 657 * in the pool. 658 * 659 * @return A connection that may be used to communicate with the indicated 660 * directory server. 661 * 662 * @throws LDAPException If a problem occurs while creating the connection 663 * pool. 664 */ 665 @ThreadSafety(level=ThreadSafetyLevel.METHOD_THREADSAFE) 666 public final LDAPConnectionPool getConnectionPool( 667 final int serverIndex, 668 final int initialConnections, 669 final int maxConnections) 670 throws LDAPException 671 { 672 if (serverSet[serverIndex] == null) 673 { 674 serverSet[serverIndex] = createServerSet(serverIndex); 675 bindRequest[serverIndex] = createBindRequest(serverIndex); 676 } 677 678 PostConnectProcessor postConnectProcessor = null; 679 if (useStartTLS[serverIndex].isPresent()) 680 { 681 postConnectProcessor = new StartTLSPostConnectProcessor( 682 startTLSSocketFactory[serverIndex]); 683 } 684 685 return new LDAPConnectionPool(serverSet[serverIndex], 686 bindRequest[serverIndex], initialConnections, maxConnections, 687 postConnectProcessor); 688 } 689 690 691 692 /** 693 * Creates the server set to use when creating connections or connection 694 * pools. 695 * 696 * @param serverIndex The zero-based index of the server to which the 697 * connection should be established. 698 * 699 * @return The server set to use when creating connections or connection 700 * pools. 701 * 702 * @throws LDAPException If a problem occurs while creating the server set. 703 */ 704 public final ServerSet createServerSet(final int serverIndex) 705 throws LDAPException 706 { 707 final SSLUtil sslUtil = createSSLUtil(serverIndex); 708 709 SocketFactory socketFactory = null; 710 if (useSSL[serverIndex].isPresent()) 711 { 712 try 713 { 714 socketFactory = sslUtil.createSSLSocketFactory(); 715 } 716 catch (final Exception e) 717 { 718 Debug.debugException(e); 719 throw new LDAPException(ResultCode.LOCAL_ERROR, 720 ERR_LDAP_TOOL_CANNOT_CREATE_SSL_SOCKET_FACTORY.get( 721 StaticUtils.getExceptionMessage(e)), e); 722 } 723 } 724 else if (useStartTLS[serverIndex].isPresent()) 725 { 726 try 727 { 728 startTLSSocketFactory[serverIndex] = sslUtil.createSSLSocketFactory(); 729 } 730 catch (final Exception e) 731 { 732 Debug.debugException(e); 733 throw new LDAPException(ResultCode.LOCAL_ERROR, 734 ERR_LDAP_TOOL_CANNOT_CREATE_SSL_SOCKET_FACTORY.get( 735 StaticUtils.getExceptionMessage(e)), e); 736 } 737 } 738 739 return new SingleServerSet(host[serverIndex].getValue(), 740 port[serverIndex].getValue(), socketFactory, getConnectionOptions()); 741 } 742 743 744 745 /** 746 * Creates the SSLUtil instance to use for secure communication. 747 * 748 * @param serverIndex The zero-based index of the server to which the 749 * connection should be established. 750 * 751 * @return The SSLUtil instance to use for secure communication, or 752 * {@code null} if secure communication is not needed. 753 * 754 * @throws LDAPException If a problem occurs while creating the SSLUtil 755 * instance. 756 */ 757 public final SSLUtil createSSLUtil(final int serverIndex) 758 throws LDAPException 759 { 760 if (useSSL[serverIndex].isPresent() || useStartTLS[serverIndex].isPresent()) 761 { 762 KeyManager keyManager = null; 763 if (keyStorePath[serverIndex].isPresent()) 764 { 765 char[] pw = null; 766 if (keyStorePassword[serverIndex].isPresent()) 767 { 768 pw = keyStorePassword[serverIndex].getValue().toCharArray(); 769 } 770 else if (keyStorePasswordFile[serverIndex].isPresent()) 771 { 772 try 773 { 774 pw = getPasswordFileReader().readPassword( 775 keyStorePasswordFile[serverIndex].getValue()); 776 } 777 catch (final Exception e) 778 { 779 Debug.debugException(e); 780 throw new LDAPException(ResultCode.LOCAL_ERROR, 781 ERR_LDAP_TOOL_CANNOT_READ_KEY_STORE_PASSWORD.get( 782 StaticUtils.getExceptionMessage(e)), e); 783 } 784 } 785 786 try 787 { 788 keyManager = new KeyStoreKeyManager( 789 keyStorePath[serverIndex].getValue(), pw, 790 keyStoreFormat[serverIndex].getValue(), 791 certificateNickname[serverIndex].getValue(), true); 792 } 793 catch (final Exception e) 794 { 795 Debug.debugException(e); 796 throw new LDAPException(ResultCode.LOCAL_ERROR, 797 ERR_LDAP_TOOL_CANNOT_CREATE_KEY_MANAGER.get( 798 StaticUtils.getExceptionMessage(e)), e); 799 } 800 } 801 802 TrustManager tm; 803 if (trustAll[serverIndex].isPresent()) 804 { 805 tm = new TrustAllTrustManager(false); 806 } 807 else if (trustStorePath[serverIndex].isPresent()) 808 { 809 char[] pw = null; 810 if (trustStorePassword[serverIndex].isPresent()) 811 { 812 pw = trustStorePassword[serverIndex].getValue().toCharArray(); 813 } 814 else if (trustStorePasswordFile[serverIndex].isPresent()) 815 { 816 try 817 { 818 pw = getPasswordFileReader().readPassword( 819 trustStorePasswordFile[serverIndex].getValue()); 820 } 821 catch (final Exception e) 822 { 823 Debug.debugException(e); 824 throw new LDAPException(ResultCode.LOCAL_ERROR, 825 ERR_LDAP_TOOL_CANNOT_READ_TRUST_STORE_PASSWORD.get( 826 StaticUtils.getExceptionMessage(e)), e); 827 } 828 } 829 830 tm = new TrustStoreTrustManager( 831 trustStorePath[serverIndex].getValue(), pw, 832 trustStoreFormat[serverIndex].getValue(), true); 833 } 834 else 835 { 836 tm = promptTrustManager.get(); 837 if (tm == null) 838 { 839 final AggregateTrustManager atm = new AggregateTrustManager(false, 840 JVMDefaultTrustManager.getInstance(), 841 new PromptTrustManager()); 842 if (promptTrustManager.compareAndSet(null, atm)) 843 { 844 tm = atm; 845 } 846 else 847 { 848 tm = promptTrustManager.get(); 849 } 850 } 851 } 852 853 return new SSLUtil(keyManager, tm); 854 } 855 else 856 { 857 return null; 858 } 859 } 860 861 862 863 /** 864 * Creates the bind request to use to authenticate to the indicated server. 865 * 866 * @param serverIndex The zero-based index of the server to which the 867 * connection should be established. 868 * 869 * @return The bind request to use to authenticate to the indicated server, 870 * or {@code null} if no bind should be performed. 871 * 872 * @throws LDAPException If a problem occurs while creating the bind 873 * request. 874 */ 875 public final BindRequest createBindRequest(final int serverIndex) 876 throws LDAPException 877 { 878 final String pw; 879 if (bindPassword[serverIndex].isPresent()) 880 { 881 pw = bindPassword[serverIndex].getValue(); 882 } 883 else if (bindPasswordFile[serverIndex].isPresent()) 884 { 885 try 886 { 887 pw = new String(getPasswordFileReader().readPassword( 888 bindPasswordFile[serverIndex].getValue())); 889 } 890 catch (final Exception e) 891 { 892 Debug.debugException(e); 893 throw new LDAPException(ResultCode.LOCAL_ERROR, 894 ERR_LDAP_TOOL_CANNOT_READ_BIND_PASSWORD.get( 895 StaticUtils.getExceptionMessage(e)), e); 896 } 897 } 898 else 899 { 900 pw = null; 901 } 902 903 if (saslOption[serverIndex].isPresent()) 904 { 905 final String dnStr; 906 if (bindDN[serverIndex].isPresent()) 907 { 908 dnStr = bindDN[serverIndex].getValue().toString(); 909 } 910 else 911 { 912 dnStr = null; 913 } 914 915 return SASLUtils.createBindRequest(dnStr, pw, null, 916 saslOption[serverIndex].getValues()); 917 } 918 else if (bindDN[serverIndex].isPresent()) 919 { 920 return new SimpleBindRequest(bindDN[serverIndex].getValue(), pw); 921 } 922 else 923 { 924 return null; 925 } 926 } 927}