001/* 002 * Copyright 2011-2020 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2011-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) 2011-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.ldap.listener; 037 038 039 040import java.net.InetAddress; 041import javax.net.SocketFactory; 042import javax.net.ServerSocketFactory; 043import javax.net.ssl.SSLSocketFactory; 044import javax.net.ssl.SSLServerSocketFactory; 045 046import com.unboundid.ldap.sdk.LDAPException; 047import com.unboundid.ldap.sdk.ResultCode; 048import com.unboundid.util.Debug; 049import com.unboundid.util.NotMutable; 050import com.unboundid.util.NotNull; 051import com.unboundid.util.Nullable; 052import com.unboundid.util.StaticUtils; 053import com.unboundid.util.ThreadSafety; 054import com.unboundid.util.ThreadSafetyLevel; 055import com.unboundid.util.ssl.SSLUtil; 056import com.unboundid.util.ssl.TrustAllTrustManager; 057 058import static com.unboundid.ldap.listener.ListenerMessages.*; 059 060 061 062/** 063 * This class provides a data structure that can be used to configure a 064 * listener for use in the in-memory directory server. Each in-memory directory 065 * server instance has the ability to have multiple listeners, and those 066 * listeners may have different settings (e.g., listen on one port for 067 * unencrypted LDAP communication with optional support for StartTLS, and listen 068 * on a separate port for SSL-encrypted communication). If the server is to 069 * provide support for SSL and/or StartTLS, then the {@link SSLUtil} class can 070 * make it easy to create the necessary socket factories. 071 */ 072@NotMutable() 073@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 074public final class InMemoryListenerConfig 075{ 076 // The address on which this listener should accept client connections. 077 @Nullable private final InetAddress listenAddress; 078 079 // The port on which this listener should accept client connections. 080 private final int listenPort; 081 082 // The socket factory that should be used for accepting new connections. 083 @Nullable private final ServerSocketFactory serverSocketFactory; 084 085 // The socket factory that should be used for creating client connections. 086 @Nullable private final SocketFactory clientSocketFactory; 087 088 // The socket factory that will be used to add StartTLS encryption to an 089 // existing connection. 090 @Nullable private final SSLSocketFactory startTLSSocketFactory; 091 092 // The used to refer to this listener. 093 @NotNull private final String listenerName; 094 095 096 097 /** 098 * Creates a new in-memory directory server listener configuration with the 099 * provided settings. 100 * 101 * @param listenerName The name to assign to this listener. It 102 * must not be {@code null} and must not be the 103 * same as the name for any other listener 104 * configured in the server. 105 * @param listenAddress The address on which the listener should 106 * accept connections from clients. It may be 107 * {@code null} to indicate that it should 108 * accept connections on all addresses on all 109 * interfaces. 110 * @param listenPort The port on which the listener should accept 111 * connections from clients. It may be 0 to 112 * indicate that the server should 113 * automatically choose an available port. 114 * @param serverSocketFactory The socket factory that should be used to 115 * create sockets when accepting client 116 * connections. It may be {@code null} if the 117 * JVM-default server socket factory should be 118 * used. 119 * @param clientSocketFactory The socket factory that should be used to 120 * create client connections to the server. It 121 * may be {@code null} if the JVM-default 122 * socket factory should be used. 123 * @param startTLSSocketFactory The socket factory that should be used to 124 * add StartTLS encryption to existing 125 * connections. It may be {@code null} if 126 * StartTLS is not to be supported on this 127 * listener, and should be {@code null} if the 128 * server socket factory already provides some 129 * other form of communication security. 130 * 131 * @throws LDAPException If the provided listener name is {@code null} or 132 * the configured listen port is out of range. 133 */ 134 public InMemoryListenerConfig(@NotNull final String listenerName, 135 @Nullable final InetAddress listenAddress, final int listenPort, 136 @Nullable final ServerSocketFactory serverSocketFactory, 137 @Nullable final SocketFactory clientSocketFactory, 138 @Nullable final SSLSocketFactory startTLSSocketFactory) 139 throws LDAPException 140 { 141 if ((listenerName == null) || listenerName.isEmpty()) 142 { 143 throw new LDAPException(ResultCode.PARAM_ERROR, 144 ERR_LISTENER_CFG_NO_NAME.get()); 145 } 146 147 if ((listenPort < 0) || (listenPort > 65_535)) 148 { 149 throw new LDAPException(ResultCode.PARAM_ERROR, 150 ERR_LISTENER_CFG_INVALID_PORT.get(listenPort)); 151 } 152 153 this.listenerName = listenerName; 154 this.listenAddress = listenAddress; 155 this.listenPort = listenPort; 156 this.serverSocketFactory = serverSocketFactory; 157 this.clientSocketFactory = clientSocketFactory; 158 this.startTLSSocketFactory = startTLSSocketFactory; 159 } 160 161 162 163 /** 164 * Creates a new listener configuration that will listen for unencrypted LDAP 165 * communication on an automatically-selected port on all available addresses. 166 * It will not support StartTLS. 167 * 168 * @param listenerName The name to use for the listener. It must not be 169 * {@code null}. 170 * 171 * @return The newly-created listener configuration. 172 * 173 * @throws LDAPException If the provided name is {@code null}. 174 */ 175 @NotNull() 176 public static InMemoryListenerConfig createLDAPConfig( 177 @NotNull final String listenerName) 178 throws LDAPException 179 { 180 return new InMemoryListenerConfig(listenerName, null, 0, null, null, null); 181 } 182 183 184 185 /** 186 * Creates a new listener configuration that will listen for unencrypted LDAP 187 * communication on the specified port on all available addresses. It will 188 * not support StartTLS. 189 * 190 * @param listenerName The name to use for the listener. It must not be 191 * {@code null}. 192 * @param listenPort The port on which the listener should accept 193 * connections from clients. It may be 0 to indicate 194 * that the server should automatically choose an 195 * available port. 196 * 197 * @return The newly-created listener configuration. 198 * 199 * @throws LDAPException If the provided listener name is {@code null} or 200 * the configured listen port is out of range. 201 */ 202 @NotNull() 203 public static InMemoryListenerConfig createLDAPConfig( 204 @NotNull final String listenerName, 205 final int listenPort) 206 throws LDAPException 207 { 208 return new InMemoryListenerConfig(listenerName, null, listenPort, null, 209 null, null); 210 } 211 212 213 214 /** 215 * Creates a new listener configuration that will listen for unencrypted LDAP 216 * communication, and may optionally support StartTLS. 217 * 218 * @param listenerName The name to assign to this listener. It 219 * must not be {@code null} and must not be the 220 * same as the name for any other listener 221 * configured in the server. 222 * @param listenAddress The address on which the listener should 223 * accept connections from clients. It may be 224 * {@code null} to indicate that it should 225 * accept connections on all addresses on all 226 * interfaces. 227 * @param listenPort The port on which the listener should accept 228 * connections from clients. It may be 0 to 229 * indicate that the server should 230 * automatically choose an available port. 231 * @param startTLSSocketFactory The socket factory that should be used to 232 * add StartTLS encryption to an existing 233 * connection. It may be {@code null} if 234 * StartTLS is not to be supported on this 235 * listener, and should be {@code null} if the 236 * server socket factory already provides some 237 * other form of communication security. 238 * 239 * @return The newly-created listener configuration. 240 * 241 * @throws LDAPException If the provided listener name is {@code null} or 242 * the configured listen port is out of range. 243 */ 244 @NotNull() 245 public static InMemoryListenerConfig createLDAPConfig( 246 @NotNull final String listenerName, 247 @Nullable final InetAddress listenAddress, 248 final int listenPort, 249 @Nullable final SSLSocketFactory startTLSSocketFactory) 250 throws LDAPException 251 { 252 return new InMemoryListenerConfig(listenerName, listenAddress, listenPort, 253 null, null, startTLSSocketFactory); 254 } 255 256 257 258 /** 259 * Creates a new listener configuration that will listen for SSL-encrypted 260 * LDAP communication on an automatically-selected port on all available 261 * addresses. 262 * 263 * @param listenerName The name to use for the listener. It must not 264 * be {@code null}. 265 * @param serverSocketFactory The SSL server socket factory that will be 266 * used for accepting SSL-based connections from 267 * clients. It must not be {@code null}. 268 * 269 * @return The newly-created listener configuration. 270 * 271 * @throws LDAPException If the provided name is {@code null}. 272 */ 273 @NotNull() 274 public static InMemoryListenerConfig createLDAPSConfig( 275 @NotNull final String listenerName, 276 @NotNull final SSLServerSocketFactory serverSocketFactory) 277 throws LDAPException 278 { 279 return createLDAPSConfig(listenerName, null, 0, serverSocketFactory, null); 280 } 281 282 283 284 /** 285 * Creates a new listener configuration that will listen for SSL-encrypted 286 * LDAP communication on the specified port on all available addresses. 287 * 288 * @param listenerName The name to use for the listener. It must not 289 * be {@code null}. 290 * @param listenPort The port on which the listener should accept 291 * connections from clients. It may be 0 to 292 * indicate that the server should 293 * automatically choose an available port. 294 * @param serverSocketFactory The SSL server socket factory that will be 295 * used for accepting SSL-based connections from 296 * clients. It must not be {@code null}. 297 * 298 * @return The newly-created listener configuration. 299 * 300 * @throws LDAPException If the provided name is {@code null}. 301 */ 302 @NotNull() 303 public static InMemoryListenerConfig createLDAPSConfig( 304 @NotNull final String listenerName, final int listenPort, 305 @NotNull final SSLServerSocketFactory serverSocketFactory) 306 throws LDAPException 307 { 308 return createLDAPSConfig(listenerName, null, listenPort, 309 serverSocketFactory, null); 310 } 311 312 313 314 /** 315 * Creates a new listener configuration that will listen for SSL-encrypted 316 * LDAP communication on an automatically-selected port on all available 317 * addresses. 318 * 319 * @param listenerName The name to use for the listener. It must not 320 * be {@code null}. 321 * @param listenAddress The address on which the listener should 322 * accept connections from clients. It may be 323 * {@code null} to indicate that it should 324 * accept connections on all addresses on all 325 * interfaces. 326 * @param listenPort The port on which the listener should accept 327 * connections from clients. It may be 0 to 328 * indicate that the server should 329 * automatically choose an available port. 330 * @param serverSocketFactory The SSL server socket factory that will be 331 * used for accepting SSL-based connections from 332 * clients. It must not be {@code null}. 333 * @param clientSocketFactory The SSL socket factory that will be used to 334 * create secure connections to the server. It 335 * may be {@code null} if a default "trust all" 336 * socket factory should be used. 337 * 338 * @return The newly-created listener configuration. 339 * 340 * @throws LDAPException If the provided name or server socket factory is 341 * {@code null}, or an error occurs while attempting to create a 342 * client socket factory. 343 */ 344 @NotNull() 345 public static InMemoryListenerConfig createLDAPSConfig( 346 @NotNull final String listenerName, 347 @Nullable final InetAddress listenAddress, 348 final int listenPort, 349 @NotNull final SSLServerSocketFactory serverSocketFactory, 350 @Nullable final SSLSocketFactory clientSocketFactory) 351 throws LDAPException 352 { 353 if (serverSocketFactory == null) 354 { 355 throw new LDAPException(ResultCode.PARAM_ERROR, 356 ERR_LISTENER_CFG_NO_SSL_SERVER_SOCKET_FACTORY.get()); 357 } 358 359 final SSLSocketFactory clientFactory; 360 if (clientSocketFactory == null) 361 { 362 try 363 { 364 final SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager()); 365 clientFactory = sslUtil.createSSLSocketFactory(); 366 } 367 catch (final Exception e) 368 { 369 Debug.debugException(e); 370 throw new LDAPException(ResultCode.LOCAL_ERROR, 371 ERR_LISTENER_CFG_COULD_NOT_CREATE_SSL_SOCKET_FACTORY.get( 372 StaticUtils.getExceptionMessage(e)), 373 e); 374 } 375 } 376 else 377 { 378 clientFactory = clientSocketFactory; 379 } 380 381 return new InMemoryListenerConfig(listenerName, listenAddress, listenPort, 382 serverSocketFactory, clientFactory, null); 383 } 384 385 386 387 /** 388 * Retrieves the name for this listener configuration. 389 * 390 * @return The name for this listener configuration. 391 */ 392 @NotNull() 393 public String getListenerName() 394 { 395 return listenerName; 396 } 397 398 399 400 /** 401 * Retrieves the address on which the listener should accept connections from 402 * clients, if defined. 403 * 404 * @return The address on which the listener should accept connections from 405 * clients, or {@code null} if it should accept connections on all 406 * addresses on all interfaces. 407 */ 408 @Nullable() 409 public InetAddress getListenAddress() 410 { 411 return listenAddress; 412 } 413 414 415 416 /** 417 * Retrieves the port on which the listener should accept connections from 418 * clients, if defined. 419 * 420 * @return The port on which the listener should accept connections from 421 * clients, or 0 if the listener should automatically select an 422 * available port. 423 */ 424 public int getListenPort() 425 { 426 return listenPort; 427 } 428 429 430 431 /** 432 * Retrieves the socket factory that should be used to create sockets when 433 * accepting client connections, if defined. 434 * 435 * @return The socket factory that should be used to create sockets when 436 * accepting client connections, or {@code null} if the JVM-default 437 * server socket factory should be used. 438 */ 439 @Nullable() 440 public ServerSocketFactory getServerSocketFactory() 441 { 442 return serverSocketFactory; 443 } 444 445 446 447 /** 448 * Retrieves the socket factory that should be used to create client 449 * connections to the server, if defined. 450 * 451 * @return The socket factory that should be used to create client 452 * connections to the server, or {@code null} if the JVM-default 453 * socket factory should be used. 454 */ 455 @Nullable() 456 public SocketFactory getClientSocketFactory() 457 { 458 return clientSocketFactory; 459 } 460 461 462 463 /** 464 * Retrieves the socket factory that should be used to add StartTLS encryption 465 * to existing connections, if defined. 466 * 467 * @return The socket factory that should be used to add StartTLS encryption 468 * to existing connections, or {@code null} if StartTLS should not be 469 * supported. 470 */ 471 @Nullable() 472 public SSLSocketFactory getStartTLSSocketFactory() 473 { 474 return startTLSSocketFactory; 475 } 476 477 478 479 /** 480 * Retrieves a string representation of this listener configuration. 481 * 482 * @return A string representation of this listener configuration. 483 */ 484 @Override() 485 @NotNull() 486 public String toString() 487 { 488 final StringBuilder buffer = new StringBuilder(); 489 toString(buffer); 490 return buffer.toString(); 491 } 492 493 494 495 /** 496 * Appends a string representation of this listener configuration to the 497 * provided buffer. 498 * 499 * @param buffer The buffer to which the information should be appended. 500 */ 501 public void toString(@NotNull final StringBuilder buffer) 502 { 503 buffer.append("InMemoryListenerConfig(name='"); 504 buffer.append(listenerName); 505 buffer.append('\''); 506 507 if (listenAddress != null) 508 { 509 buffer.append(", listenAddress='"); 510 buffer.append(listenAddress.getHostAddress()); 511 buffer.append('\''); 512 } 513 514 buffer.append(", listenPort="); 515 buffer.append(listenPort); 516 517 if (serverSocketFactory != null) 518 { 519 buffer.append(", serverSocketFactoryClass='"); 520 buffer.append(serverSocketFactory.getClass().getName()); 521 buffer.append('\''); 522 } 523 524 if (clientSocketFactory != null) 525 { 526 buffer.append(", clientSocketFactoryClass='"); 527 buffer.append(clientSocketFactory.getClass().getName()); 528 buffer.append('\''); 529 } 530 531 if (startTLSSocketFactory != null) 532 { 533 buffer.append(", startTLSSocketFactoryClass='"); 534 buffer.append(startTLSSocketFactory.getClass().getName()); 535 buffer.append('\''); 536 } 537 538 buffer.append(')'); 539 } 540}