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