001 /*
002 * Copyright 2012-2013 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2012-2013 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 */
021 package com.unboundid.util;
022
023
024
025 import java.io.OutputStream;
026 import java.util.concurrent.atomic.AtomicReference;
027 import javax.net.SocketFactory;
028 import javax.net.ssl.KeyManager;
029 import javax.net.ssl.SSLContext;
030 import javax.net.ssl.TrustManager;
031
032 import com.unboundid.ldap.sdk.BindRequest;
033 import com.unboundid.ldap.sdk.ExtendedResult;
034 import com.unboundid.ldap.sdk.LDAPConnection;
035 import com.unboundid.ldap.sdk.LDAPConnectionOptions;
036 import com.unboundid.ldap.sdk.LDAPConnectionPool;
037 import com.unboundid.ldap.sdk.LDAPException;
038 import com.unboundid.ldap.sdk.PostConnectProcessor;
039 import com.unboundid.ldap.sdk.ResultCode;
040 import com.unboundid.ldap.sdk.ServerSet;
041 import com.unboundid.ldap.sdk.SimpleBindRequest;
042 import com.unboundid.ldap.sdk.SingleServerSet;
043 import com.unboundid.ldap.sdk.StartTLSPostConnectProcessor;
044 import com.unboundid.ldap.sdk.extensions.StartTLSExtendedRequest;
045 import com.unboundid.util.args.ArgumentException;
046 import com.unboundid.util.args.ArgumentParser;
047 import com.unboundid.util.args.BooleanArgument;
048 import com.unboundid.util.args.DNArgument;
049 import com.unboundid.util.args.FileArgument;
050 import com.unboundid.util.args.IntegerArgument;
051 import com.unboundid.util.args.StringArgument;
052 import com.unboundid.util.ssl.KeyStoreKeyManager;
053 import com.unboundid.util.ssl.PromptTrustManager;
054 import com.unboundid.util.ssl.SSLUtil;
055 import com.unboundid.util.ssl.TrustAllTrustManager;
056 import com.unboundid.util.ssl.TrustStoreTrustManager;
057
058 import 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)
118 public 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 }