com.gemstone.gemfire.internal.SocketCreator Maven / Gradle / Ivy
Show all versions of gemfire-core Show documentation
/*
* Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License. See accompanying
* LICENSE file.
*/
package com.gemstone.gemfire.internal;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.*;
import java.nio.channels.ServerSocketChannel;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.*;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.*;
import com.gemstone.gemfire.GemFireConfigException;
import com.gemstone.gemfire.SystemConnectException;
import com.gemstone.gemfire.SystemFailure;
import com.gemstone.gemfire.admin.internal.InetAddressUtil;
import com.gemstone.gemfire.cache.wan.GatewaySender;
import com.gemstone.gemfire.cache.wan.GatewayTransportFilter;
import com.gemstone.gemfire.distributed.ClientSocketFactory;
import com.gemstone.gemfire.distributed.internal.DistributionConfig;
import com.gemstone.gemfire.distributed.internal.DistributionConfigImpl;
import com.gemstone.gemfire.i18n.LogWriterI18n;
import com.gemstone.gemfire.internal.GfeConsoleReaderFactory.GfeConsoleReader;
import com.gemstone.gemfire.internal.cache.wan.TransportFilterServerSocket;
import com.gemstone.gemfire.internal.cache.wan.TransportFilterSocketFactory;
import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
import com.gemstone.gemfire.internal.shared.ClientSharedUtils;
import com.gemstone.gemfire.internal.util.PasswordUtil;
import com.gemstone.org.jgroups.util.ConnectionWatcher;
import com.gemstone.org.jgroups.util.GFLogWriter;
/**
* Analyze configuration data (gemfire.properties) and configure sockets
* accordingly for SSL.
*
* gemfire.useSSL = (true|false) default false.
* gemfire.ssl.debug = (true|false) default false.
* gemfire.ssl.needClientAuth = (true|false) default true.
* gemfire.ssl.protocols = list of protocols
* gemfire.ssl.ciphers = list of cipher suites
*
* The following may be included to configure the certificates used by the
* Sun Provider.
*
* javax.net.ssl.trustStore = pathname
* javax.net.ssl.trustStorePassword = password
* javax.net.ssl.keyStore = pathname
* javax.net.ssl.keyStorePassword = password
*
* Additional properties will be set as System properties to be available
* as needed by other provider implementations.
*/
public class SocketCreator implements com.gemstone.org.jgroups.util.SockCreator {
private static final Map hostNames;
/** flag to force always using DNS (regardless of the fact that these lookups can hang) */
public static final boolean FORCE_DNS_USE = Boolean.getBoolean("gemfire.forceDnsUse");
/** set this to false to inhibit host name lookup */
public static volatile boolean resolve_dns = true;
/** The default instance for use in GemFire socket creation */
private static SocketCreator DEFAULT_INSTANCE = new SocketCreator();
/**
* Allow unlimited concurrent reads (uses of SocketCreator). Re-initializing
* SocketCreator requires the write lock which will block out all reads until
* it's done. If this causes performance loss, removal of ReadWriteLock
* should only impact the Console or Admin APIs.
*/
//private final ReadWriteLock rw = new ReentrantReadWriteLock();
/** True if this SocketCreator has been initialized and is ready to use */
private boolean ready = false;
/** True if configured to use SSL */
private boolean useSSL;
/** True if configured to require client authentication */
private boolean needClientAuth;
/** Space-delimited list of SSL protocols to use, 'any' allows any */
private String[] protocols;
/** Space-delimited list of SSL ciphers to use, 'any' allows any */
private String[] ciphers;
/** Only print this SocketCreator's config once */
private boolean configShown = false;
/** context for SSL socket factories */
private SSLContext sslContext;
static {
// add localhost to hostnames
hostNames = new HashMap();
final InetAddress host = ClientSharedUtils.getCachedLocalHost();
if (host != null) {
hostNames.put(host, host.getHostName());
}
}
/** A factory used to create client Sockets
. */
private ClientSocketFactory clientSocketFactory;
/**
* Whether to enable TCP keep alive for sockets. This boolean is controlled by
* the gemfire.setTcpKeepAlive java system property. If not set then GemFire
* will enable keep-alive on server->client and p2p connections.
*/
public static final boolean ENABLE_TCP_KEEP_ALIVE;
static {
// bug #49484 - customers want tcp/ip keep-alive turned on by default
// to avoid dropped connections. It can be turned off by setting this
// property to false
String str = System.getProperty("gemfire.setTcpKeepAlive");
if (str != null) {
ENABLE_TCP_KEEP_ALIVE = Boolean.getBoolean("gemfire.setTcpKeepAlive");
} else {
ENABLE_TCP_KEEP_ALIVE = true;
}
}
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
/** Constructs new SocketCreator instance. */
private SocketCreator() {}
// -------------------------------------------------------------------------
// Static instance accessors
// -------------------------------------------------------------------------
/**
* Returns the default instance for use in GemFire socket creation.
*
* If not already initialized, the default instance of SocketCreator will be
* initialized using defaults in {@link
* com.gemstone.gemfire.distributed.internal.DistributionConfig}. If any
* values are specified in System properties, those values will be used to
* override the defaults.
*
* Synchronizes on the DEFAULT_INSTANCE.
*/
public static SocketCreator getDefaultInstance() {
synchronized (DEFAULT_INSTANCE) {
if (!DEFAULT_INSTANCE.ready) {
DEFAULT_INSTANCE.initialize();
}
}
return DEFAULT_INSTANCE;
}
/**
* Returns the default instance for use in GemFire socket creation after
* initializing it using the DistributionConfig.
*
* This will reinitialize the SocketCreator if it was previously initialized.
*
* Synchronizes on the DEFAULT_INSTANCE.
*/
public static SocketCreator getDefaultInstance(DistributionConfig config) {
synchronized (DEFAULT_INSTANCE) {
DEFAULT_INSTANCE.initialize(config);
}
return DEFAULT_INSTANCE;
}
/**
* Returns the default instance for use in GemFire socket creation after
* initializing it using defaults in {@link
* com.gemstone.gemfire.distributed.internal.DistributionConfig}. If any
* values are specified in the provided properties or in System properties,
* those values will be used to override the defaults.
*
* This will reinitialize the SocketCreator if it was previously initialized.
*
* Call will synchronize on the DEFAULT_INSTANCE.
*/
public static SocketCreator getDefaultInstance(Properties props) {
return getDefaultInstance(new DistributionConfigImpl(props));
}
/**
* Create and initialize a new non-default instance of SocketCreator.
*
* Synchronizes on the new instance.
*
* @param useSSL true if ssl is to be enabled
* @param needClientAuth true if client authentication is required
* @param protocols space-delimited list of ssl protocols to use
* @param ciphers space-delimited list of ssl ciphers to use
* @param sysProps vendor properties to be set as System properties
*/
public static SocketCreator createNonDefaultInstance(boolean useSSL,
boolean needClientAuth,
String protocols,
String ciphers,
Properties sysProps) {
SocketCreator sc = new SocketCreator();
synchronized (sc) {
sc.initialize(useSSL, needClientAuth, readArray(protocols), readArray(ciphers), sysProps);
}
return sc;
}
/**
* All GemFire code should use this method instead of
* InetAddress.getLocalHost(). See bug #40623
*/
public static InetAddress getLocalHost() throws UnknownHostException {
return ClientSharedUtils.getLocalHost();
}
/** All classes should use this instead of relying on the JRE system property */
public static boolean preferIPv6Addresses() {
return ClientSharedUtils.preferIPv6Addresses();
}
/**
* returns the host name for the given inet address, using a local cache
* of names to avoid dns hits and duplicate strings
*/
public static synchronized String getHostName(InetAddress addr) {
String result = hostNames.get(addr);
if (result == null) {
result = addr.getHostName();
hostNames.put(addr, result);
}
return result;
}
/**
* returns the host name for the given inet address, using a local cache of
* names to avoid dns hits and duplicate strings
*/
public static synchronized String getCanonicalHostName(InetAddress addr,
String hostName) {
String result = hostNames.get(addr);
if (result == null) {
hostNames.put(addr, hostName);
return hostName;
}
return result;
}
// -------------------------------------------------------------------------
// Initializers (change SocketCreator state)
// -------------------------------------------------------------------------
/**
* Initialize this SocketCreator.
*
* Caller must synchronize on the SocketCreator instance.
*
* @param useSSL true if ssl is to be enabled
* @param needClientAuth true if client authentication is required
* @param protocols array of ssl protocols to use
* @param ciphers array of ssl ciphers to use
* @param props vendor properties passed in through gfsecurity.properties
*/
private void initialize(boolean useSSL,
boolean needClientAuth,
String[] protocols,
String[] ciphers,
Properties props) {
Assert.assertHoldsLock(this, true);
try {
// rw.writeLock().lockInterruptibly();
// try {
this.useSSL = useSSL;
this.needClientAuth = needClientAuth;
this.protocols = protocols;
this.ciphers = ciphers;
if (this == DEFAULT_INSTANCE) {
// set p2p values...
if (this.useSSL) {
System.setProperty( "p2p.useSSL", "true" );
System.setProperty( "p2p.oldIO", "true" );
System.setProperty( "p2p.nodirectBuffers", "true" );
try {
if (sslContext == null) {
sslContext = createAndConfigureSSLContext(protocols, props);
SSLContext.setDefault(sslContext);
}
} catch (Exception e) {
throw new GemFireConfigException("Error configuring GemFire ssl ",e);
}
}
else {
System.setProperty( "p2p.useSSL", "false" );
}
// make sure TCPConduit picks up p2p properties...
com.gemstone.gemfire.internal.tcp.TCPConduit.init();
} else if (this.useSSL && sslContext == null) {
try {
sslContext = createAndConfigureSSLContext(protocols, props);
} catch (Exception e) {
throw new GemFireConfigException("Error configuring GemFire ssl ",e);
}
}
initializeClientSocketFactory();
this.ready = true;
// }
// finally {
// rw.writeLock().unlock();
// }
}
catch ( Error t ) {
if (SystemFailure.isJVMFailureError(t)) {
SystemFailure.initiateFailure(t);
// If this ever returns, rethrow the error. We're poisoned
// now, so don't let this thread continue.
throw t;
}
// Whenever you catch Error or Throwable, you must also
// check for fatal JVM error (see above). However, there is
// _still_ a possibility that you are dealing with a cascading
// error condition, so you also need to check to see if the JVM
// is still usable:
SystemFailure.checkFailure();
t.printStackTrace();
throw t;
}
catch ( RuntimeException re ) {
re.printStackTrace();
throw re;
}
}
/**
* Creates & configures the SSLContext when SSL is enabled.
*
* @param protocolNames
* valid SSL protocols for this connection
* @param props
* vendor properties passed in through gfsecurity.properties
* @return new SSLContext configured using the given protocols & properties
*
* @throws GeneralSecurityException
* if security information can not be found
* @throws IOException
* if information can not be loaded
*/
private SSLContext createAndConfigureSSLContext(String[] protocolNames, Properties props)
throws GeneralSecurityException, IOException {
SSLContext newSSLContext = getSSLContextInstance(protocolNames);
KeyManager[] keyManagers = getKeyManagers(props);
TrustManager[] trustManagers = getTrustManagers(props);
newSSLContext.init(keyManagers, trustManagers, null /* use the default secure random*/);
return newSSLContext;
}
/**
* Used by CacheServerLauncher and SystemAdmin to read the properties from
* console
*
* @param env
* Map in which the properties are to be read from console.
*/
public static void readSSLProperties(Map env) {
readSSLProperties(env, false);
}
/**
* Used to read the properties from console. AgentLauncher calls this method
* directly & ignores gemfire.properties. CacheServerLauncher and SystemAdmin
* call this through {@link #readSSLProperties(Map)} and do NOT ignore
* gemfire.properties.
*
* @param env
* Map in which the properties are to be read from console.
* @param ignoreGemFirePropsFile
* if false
existing gemfire.properties file is read, if
* true
, properties from gemfire.properties file are
* ignored.
*/
public static void readSSLProperties(Map env,
boolean ignoreGemFirePropsFile) {
Properties props = new Properties();
DistributionConfigImpl.loadGemFireProperties(props, ignoreGemFirePropsFile);
for (Object entry : props.entrySet()) {
@SuppressWarnings("unchecked")
Map.Entry ent = (Map.Entry)entry;
// if the value of ssl props is empty, read them from console
if (ent.getKey().startsWith(DistributionConfig.SSL_SYSTEM_PROPS_NAME)
|| ent.getKey().startsWith(DistributionConfig.SYS_PROP_NAME)) {
String key = ent.getKey();
if (key.startsWith(DistributionConfig.SYS_PROP_NAME)) {
key = key.substring(DistributionConfig.SYS_PROP_NAME.length());
}
if (ent.getValue() == null || ent.getValue().trim().equals("")) {
GfeConsoleReader consoleReader = GfeConsoleReaderFactory.getDefaultConsoleReader();
if (!consoleReader.isSupported()) {
throw new GemFireConfigException("SSL properties are empty, but a console is not available");
}
if (key.toLowerCase().contains("password")) {
char[] password = consoleReader.readPassword("Please enter "+key+": ");
env.put(key, PasswordUtil.encrypt(new String(password), false));
} else {
String val = consoleReader.readLine("Please enter "+key+": ");
env.put(key, val);
}
}
}
}
}
private static SSLContext getSSLContextInstance(String[] protocols) {
SSLContext c = null;
if (protocols != null && protocols.length > 0) {
for (String protocol : protocols) {
if (!protocol.equals("any")) {
try {
c = SSLContext.getInstance(protocol);
break;
} catch (NoSuchAlgorithmException e) {
// continue
}
}
}
}
if (c != null) {
return c;
}
// lookup known algorithms
String[] knownAlgorithms = {"SSL", "SSLv2", "SSLv3", "TLS", "TLSv1", "TLSv1.1"};
for (String algo : knownAlgorithms) {
try {
c = SSLContext.getInstance(algo);
break;
} catch (NoSuchAlgorithmException e) {
// continue
}
}
return c;
}
private TrustManager[] getTrustManagers(Properties sysProps)
throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
TrustManager[] trustManagers = null;
String trustStoreType = sysProps.getProperty("javax.net.ssl.trustStoreType");
GfeConsoleReader consoleReader = GfeConsoleReaderFactory.getDefaultConsoleReader();
if (trustStoreType == null) {
trustStoreType = System.getProperty("javax.net.ssl.trustStoreType", KeyStore.getDefaultType());
} else if (trustStoreType.trim().equals("")) {
//read from console, default on empty
if (consoleReader.isSupported()) {
trustStoreType = consoleReader.readLine("Please enter the trustStoreType (javax.net.ssl.trustStoreType) : ");
}
if (isEmpty(trustStoreType)) {
trustStoreType = KeyStore.getDefaultType();
}
}
KeyStore ts = KeyStore.getInstance(trustStoreType);
String trustStorePath = System.getProperty("javax.net.ssl.trustStore");
if (trustStorePath == null) {
trustStorePath = sysProps.getProperty("javax.net.ssl.trustStore");
}
if (trustStorePath != null) {
if (trustStorePath.trim().equals("")) {
trustStorePath = System.getenv("javax.net.ssl.trustStore");
//read from console
if (isEmpty(trustStorePath) && consoleReader.isSupported()) {
trustStorePath = consoleReader.readLine("Please enter the trustStore location (javax.net.ssl.trustStore) : ");
}
}
FileInputStream fis = new FileInputStream(trustStorePath);
String passwordString = System.getProperty("javax.net.ssl.trustStorePassword");
if (passwordString == null) {
passwordString = sysProps.getProperty("javax.net.ssl.trustStorePassword");
}
char [] password = null;
if (passwordString != null) {
if (passwordString.trim().equals("")) {
String encryptedPass = System.getenv("javax.net.ssl.trustStorePassword");
if (!isEmpty(encryptedPass)) {
String toDecrypt = "encrypted(" + encryptedPass + ")";
passwordString = PasswordUtil.decrypt(toDecrypt);
password = passwordString.toCharArray();
}
//read from the console
if (isEmpty(passwordString) && consoleReader.isSupported()) {
password = consoleReader.readPassword("Please enter password for trustStore (javax.net.ssl.trustStorePassword) : ");
}
} else {
password = passwordString.toCharArray();
}
}
ts.load(fis, password);
// default algorithm can be changed by setting property "ssl.TrustManagerFactory.algorithm" in security properties
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ts);
trustManagers = tmf.getTrustManagers();
// follow the security tip in java doc
if (password != null) {
java.util.Arrays.fill(password, ' ');
}
}
return trustManagers;
}
private KeyManager[] getKeyManagers(Properties sysProps)
throws KeyStoreException, FileNotFoundException, IOException,
NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException {
KeyManager[] keyManagers = null;
String keyStoreType = sysProps.getProperty("javax.net.ssl.keyStoreType");
GfeConsoleReader consoleReader = GfeConsoleReaderFactory.getDefaultConsoleReader();
if (keyStoreType == null) {
keyStoreType = System.getProperty("javax.net.ssl.keyStoreType", KeyStore.getDefaultType());
} else if (keyStoreType.trim().equals("")) {
// read from console, default on empty
if (consoleReader.isSupported()) {
keyStoreType = consoleReader.readLine("Please enter the keyStoreType (javax.net.ssl.keyStoreType) : ");
}
if (isEmpty(keyStoreType)) {
keyStoreType = KeyStore.getDefaultType();
}
}
KeyStore ks = KeyStore.getInstance(keyStoreType);
String keyStoreFilePath = sysProps.getProperty("javax.net.ssl.keyStore");
if (keyStoreFilePath == null) {
keyStoreFilePath = System.getProperty("javax.net.ssl.keyStore");
}
if (keyStoreFilePath != null) {
if (keyStoreFilePath.trim().equals("")) {
keyStoreFilePath = System.getenv("javax.net.ssl.keyStore");
//read from console
if (isEmpty(keyStoreFilePath) && consoleReader.isSupported()) {
keyStoreFilePath = consoleReader.readLine("Please enter the keyStore location (javax.net.ssl.keyStore) : ");
}
if (isEmpty(keyStoreFilePath)) {
keyStoreFilePath = System.getProperty("user.home") + System.getProperty("file.separator") + ".keystore";
}
}
FileInputStream fis = null;
fis = new FileInputStream(keyStoreFilePath);
String passwordString = sysProps.getProperty("javax.net.ssl.keyStorePassword");
if (passwordString == null) {
passwordString = System.getProperty("javax.net.ssl.keyStorePassword");
}
char [] password = null;
if (passwordString != null) {
if (passwordString.trim().equals("")) {
String encryptedPass = System.getenv("javax.net.ssl.keyStorePassword");
if (!isEmpty(encryptedPass)) {
String toDecrypt = "encrypted(" + encryptedPass + ")";
passwordString = PasswordUtil.decrypt(toDecrypt);
password = passwordString.toCharArray();
}
//read from the console
if (isEmpty(passwordString) && consoleReader != null) {
password = consoleReader.readPassword("Please enter password for keyStore (javax.net.ssl.keyStorePassword) : ");
}
} else {
password = passwordString.toCharArray();
}
}
ks.load(fis, password);
// default algorithm can be changed by setting property "ssl.KeyManagerFactory.algorithm" in security properties
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, password);
keyManagers = kmf.getKeyManagers();
// follow the security tip in java doc
if (password != null) {
java.util.Arrays.fill(password, ' ');
}
}
return keyManagers;
}
private boolean isEmpty(String string) {
return (string == null || string.trim().equals(""));
}
/**
* Perform initialization using defaults in {@link
* com.gemstone.gemfire.distributed.internal.DistributionConfig}. If any
* values are specified in System properties, those values will be used to
* override the defaults.
*
* Caller must synchronize on the SocketCreator instance.
*/
private void initialize() {
initialize(new DistributionConfigImpl(new Properties()));
}
/**
* Initialize this SocketCreator using the DistributionConfig.
*
* Caller must synchronize on the SocketCreator instance.
*/
private void initialize(DistributionConfig config) {
DistributionConfig conf = config;
if (conf == null) {
conf = new DistributionConfigImpl(new Properties());
}
initialize(conf.getSSLEnabled(),
conf.getSSLRequireAuthentication(),
readArray(conf.getSSLProtocols()),
readArray(conf.getSSLCiphers()),
conf.getSSLProperties());
}
// -------------------------------------------------------------------------
// Public methods
// -------------------------------------------------------------------------
/** Returns true if this SocketCreator is configured to use SSL. */
public boolean useSSL() {
return this.useSSL;
}
/**
* Return a ServerSocket possibly configured for SSL.
* SSL configuration is left up to JSSE properties in java.security file.
*/
public ServerSocket createServerSocket( int nport, int backlog,
GFLogWriter log ) throws IOException {
return createServerSocket( nport, backlog, null, log );
}
public ServerSocket createServerSocket(int nport, int backlog,
InetAddress bindAddr, GFLogWriter log,
List transportFilters, int socketBufferSize)
throws IOException {
if (transportFilters.isEmpty()) {
return createServerSocket(nport, backlog, bindAddr, log, socketBufferSize);
}
else {
printConfig(log);
ServerSocket result = new TransportFilterServerSocket(transportFilters);
result.setReuseAddress(true);
// Set the receive buffer size before binding the socket so
// that large buffers will be allocated on accepted sockets (see
// java.net.ServerSocket.setReceiverBufferSize javadocs)
result.setReceiveBufferSize(socketBufferSize);
try {
result.bind(new InetSocketAddress(bindAddr, nport), backlog);
}
catch (BindException e) {
BindException throwMe = new BindException(
LocalizedStrings.SocketCreator_FAILED_TO_CREATE_SERVER_SOCKET_ON_0_1
.toLocalizedString(new Object[] { bindAddr,
Integer.valueOf(nport) }));
throwMe.initCause(e);
throw throwMe;
}
return result;
}
}
/**
* Return a ServerSocket possibly configured for SSL.
* SSL configuration is left up to JSSE properties in java.security file.
*/
public ServerSocket createServerSocket( int nport, int backlog,
InetAddress bindAddr, GFLogWriter log ) throws IOException {
return createServerSocket( nport, backlog, null, log, -1 );
}
public ServerSocket createServerSocket(int nport, int backlog,
InetAddress bindAddr, GFLogWriter log, int socketBufferSize)
throws IOException {
// rw.readLock().lockInterruptibly();
// try {
printConfig( log );
if ( this.useSSL ) {
if (this.sslContext == null) {
throw new GemFireConfigException("SSL not configured correctly, Please look at previous error");
}
ServerSocketFactory ssf = this.sslContext.getServerSocketFactory();
SSLServerSocket serverSocket = (SSLServerSocket)ssf.createServerSocket();
serverSocket.setReuseAddress(true);
// If necessary, set the receive buffer size before binding the socket so
// that large buffers will be allocated on accepted sockets (see
// java.net.ServerSocket.setReceiverBufferSize javadocs)
if (socketBufferSize != -1) {
serverSocket.setReceiveBufferSize(socketBufferSize);
}
serverSocket.bind(new InetSocketAddress(bindAddr, nport), backlog);
finishServerSocket(serverSocket);
return serverSocket;
}
else {
//log.info("Opening server socket on " + nport, new Exception("SocketCreation"));
ServerSocket result = new ServerSocket();
result.setReuseAddress(true);
// If necessary, set the receive buffer size before binding the socket so
// that large buffers will be allocated on accepted sockets (see
// java.net.ServerSocket.setReceiverBufferSize javadocs)
if (socketBufferSize != -1) {
result.setReceiveBufferSize(socketBufferSize);
}
try {
result.bind(new InetSocketAddress(bindAddr, nport), backlog);
}
catch (BindException e) {
BindException throwMe = new BindException(LocalizedStrings.SocketCreator_FAILED_TO_CREATE_SERVER_SOCKET_ON_0_1.toLocalizedString(new Object[] {bindAddr, Integer.valueOf(nport)}));
throwMe.initCause(e);
throw throwMe;
}
return result;
}
// }
// finally {
// rw.readLock().unlock();
// }
}
/**
* Creates or bind server socket to a random port selected
* from tcp-port-range which is same as membership-port-range.
* @param ba
* @param backlog
* @param isBindAddress
* @param logger
* @param tcpBufferSize
* @return Returns the new server socket.
* @throws IOException
*/
public ServerSocket createServerSocketUsingPortRange(InetAddress ba, int backlog,
boolean isBindAddress, boolean useNIO, GFLogWriter logger,
int tcpBufferSize, int[] tcpPortRange) throws IOException {
ServerSocket socket = null;
int localPort = 0;
int startingPort = 0;
// Get a random port from range.
Random rand = new SecureRandom();
int portLimit = tcpPortRange[1];
int randPort = tcpPortRange[0] + rand.nextInt(tcpPortRange[1] - tcpPortRange[0] + 1);
startingPort = randPort;
localPort = startingPort;
while (true) {
if (localPort > portLimit) {
if (startingPort != 0) {
localPort = tcpPortRange[0];
portLimit = startingPort - 1;
startingPort = 0;
} else {
throw new SystemConnectException(
LocalizedStrings.TCPConduit_UNABLE_TO_FIND_FREE_PORT
.toLocalizedString(tcpPortRange[0], tcpPortRange[1]));
}
}
try {
if (useNIO) {
ServerSocketChannel channl = ServerSocketChannel.open();
socket = channl.socket();
InetSocketAddress addr = new InetSocketAddress(isBindAddress ? ba : null, localPort);
socket.bind(addr, backlog);
} else {
socket = SocketCreator.getDefaultInstance().createServerSocket(localPort, backlog, isBindAddress? ba : null, logger, tcpBufferSize);
}
break;
} catch (java.net.SocketException ex) {
if (useNIO || SocketCreator.treatAsBindException(ex)) {
localPort++;
} else {
throw ex;
}
}
}
return socket;
}
public static boolean treatAsBindException(SocketException se) {
if(se instanceof BindException) {
return true;
}
final String msg = se.getMessage();
return (msg != null && msg.contains("Invalid argument: listen failed"));
}
/**
* Return a client socket. This method is used by client/server clients.
*/
public Socket connectForClient(String host, int port, GFLogWriter log,
int timeout) throws IOException {
return connect(InetAddress.getByName(host), port, log, timeout,
null, true, -1, this.useSSL);
}
/**
* Return a client socket. This method is used by client/server clients.
*/
public Socket connectForClient(String host, int port, GFLogWriter log,
int timeout, int socketBufferSize) throws IOException {
return connect(InetAddress.getByName(host), port, log, timeout,
null, true, socketBufferSize, this.useSSL);
}
/**
* Return a client socket. This method is used by peers.
*/
public Socket connectForServer(InetAddress inetadd, int port,
GFLogWriter log) throws IOException {
return connect(inetadd, port, log, 0, null, false, -1, this.useSSL);
}
/**
* Return a client socket. This method is used by peers.
*/
public Socket connectForServer(InetAddress inetadd, int port,
GFLogWriter log, int socketBufferSize) throws IOException {
return connect(inetadd, port, log, 0, null, false, socketBufferSize,
this.useSSL);
}
/**
* Return a client socket, timing out if unable to connect and timeout > 0 (millis).
* The parameter timeout is ignored if SSL is being used, as there is no
* timeout argument in the ssl socket factory
*/
@Override
public Socket connect(InetAddress inetadd, int port, GFLogWriter log,
int timeout, ConnectionWatcher optionalWatcher, boolean clientSide)
throws IOException {
return connect(inetadd, port, log, timeout, optionalWatcher, clientSide,
-1, this.useSSL);
}
@Override
/**
* Return a client socket, timing out if unable to connect and timeout > 0 (millis).
* The parameter timeout is ignored if SSL is being used, as there is no
* timeout argument in the ssl socket factory
*/
public Socket connect(InetAddress inetadd, int port, GFLogWriter log,
int timeout, ConnectionWatcher optionalWatcher, boolean clientSide,
int socketBufferSize, boolean sslConnection) throws IOException {
Socket socket = null;
SocketAddress sockaddr = new InetSocketAddress(inetadd, port);
printConfig( log );
try {
if (sslConnection) {
if (this.sslContext == null) {
throw new GemFireConfigException("SSL not configured correctly, Please look at previous error");
}
SocketFactory sf = this.sslContext.getSocketFactory();
socket = sf.createSocket();
// If this is a client socket, optionally enable SO_KEEPALIVE in the OS
// network protocol.
if (clientSide) {
socket.setKeepAlive(ENABLE_TCP_KEEP_ALIVE);
}
// If necessary, set the receive buffer size before connecting the
// socket so that large buffers will be allocated on accepted sockets
// (see java.net.Socket.setReceiverBufferSize javadocs for details)
if (socketBufferSize != -1) {
socket.setReceiveBufferSize(socketBufferSize);
}
if (optionalWatcher != null) {
optionalWatcher.beforeConnect(socket);
}
if (timeout > 0) {
socket.connect( sockaddr, timeout );
}
else {
socket.connect( sockaddr );
}
configureClientSSLSocket( socket, log );
return socket;
}
else {
if (clientSide && this.clientSocketFactory != null) {
socket = this.clientSocketFactory.createSocket( inetadd, port );
} else {
socket = new Socket( );
// If this is a client socket, optionally enable SO_KEEPALIVE in the
// OS network protocol.
if (clientSide) {
socket.setKeepAlive(ENABLE_TCP_KEEP_ALIVE);
}
// If necessary, set the receive buffer size before connecting the
// socket so that large buffers will be allocated on accepted sockets
// (see java.net.Socket.setReceiverBufferSize javadocs for details)
if (socketBufferSize != -1) {
socket.setReceiveBufferSize(socketBufferSize);
}
if (optionalWatcher != null) {
optionalWatcher.beforeConnect(socket);
}
if (timeout > 0) {
socket.connect(sockaddr, timeout);
}
else {
socket.connect(sockaddr);
}
}
return socket;
}
}
finally {
if (optionalWatcher != null) {
optionalWatcher.afterConnect(socket);
}
}
// }
// finally {
// rw.readLock().unlock();
// }
}
/** has the isReachable method been looked up already? */
volatile boolean isReachableChecked;
/** InetAddress.isReachable() is in v1.5 and later */
volatile Method isReachableMethod;
public boolean isHostReachable(InetAddress host) {
boolean result = true;
try {
Method m = null;
if (isReachableChecked) {
m = isReachableMethod;
}
else {
// deadcoded - InetAddress.isReachable uses the ECHO port
// if we don't have root permission, and the ECHO port may
// be blocked
//m = InetAddress.class.getMethod("isReachable", new Class[] { int.class });
//isReachableMethod = m;
isReachableChecked = true;
}
if (m != null) {
result = ((Boolean)m.invoke(host, new Object[] {Integer.valueOf(250)})).booleanValue();
return result;
}
}
catch (InvocationTargetException e) {
}
// catch (NoSuchMethodException e) {
// }
catch (IllegalAccessException e) {
}
// any other bright ideas? attempts to connect a socket to a missing
// machine may hang, so don't try the echo port or anything requiring
// full Sockets
return result;
}
/** Will be a server socket... this one simply registers the listeners. */
public void configureServerSSLSocket(Socket socket,
GFLogWriter log) throws IOException {
// rw.readLock().lockInterruptibly();
// try {
if (socket instanceof SSLSocket) {
SSLSocket sslSocket = (SSLSocket)socket;
try {
sslSocket.startHandshake();
if (log != null) { // fix this to be logwriter level...
SSLSession session = sslSocket.getSession();
Certificate[] peer = session.getPeerCertificates();
log.info(LocalizedStrings.SocketCreator_SSL_CONNECTION_FROM_PEER_0,
((X509Certificate)peer[0]).getSubjectDN());
} // ...if
}
catch (SSLPeerUnverifiedException ex) {
if (this.needClientAuth) {
if (log != null) log.severe(LocalizedStrings.SocketCreator_SSL_ERROR_IN_AUTHENTICATING_PEER_0_1, new Object[] { socket.getInetAddress(), Integer.valueOf(socket.getPort())}, ex );
throw ex;
}
}
catch (SSLException ex) {
if (log != null) log.severe(LocalizedStrings.SocketCreator_SSL_ERROR_IN_CONNECTING_TO_PEER_0_1, new Object[] { socket.getInetAddress(), Integer.valueOf(socket.getPort())}, ex );
throw ex;
}
} // ...if
// }
// finally {
// rw.readLock().unlock();
// }
}
// -------------------------------------------------------------------------
// Private implementation methods
// -------------------------------------------------------------------------
/** Configure the SSLServerSocket based on this SocketCreator's settings. */
private void finishServerSocket(SSLServerSocket serverSocket) throws IOException {
serverSocket.setUseClientMode( false );
if ( this.needClientAuth ) {
//serverSocket.setWantClientAuth( true );
serverSocket.setNeedClientAuth( true );
}
serverSocket.setEnableSessionCreation( true );
// restrict cyphers
if ( ! "any".equalsIgnoreCase( this.protocols[0] ) ) {
serverSocket.setEnabledProtocols( this.protocols );
}
if ( ! "any".equalsIgnoreCase( this.ciphers[0] ) ) {
serverSocket.setEnabledCipherSuites( this.ciphers );
}
}
/**
* When a socket is accepted from a server socket, it should be passed to
* this method for SSL configuration.
*/
private void configureClientSSLSocket(Socket socket,
GFLogWriter log) throws IOException {
if ( socket instanceof SSLSocket ) {
SSLSocket sslSocket = (SSLSocket) socket;
sslSocket.setUseClientMode( true );
sslSocket.setEnableSessionCreation( true );
// restrict cyphers
if ( this.protocols != null && !"any".equalsIgnoreCase(this.protocols[0]) ) {
sslSocket.setEnabledProtocols( this.protocols );
}
if ( this.ciphers != null && !"any".equalsIgnoreCase(this.ciphers[0]) ) {
sslSocket.setEnabledCipherSuites( this.ciphers );
}
try {
sslSocket.startHandshake();
if (log != null && log.infoEnabled()) {
SSLSession session = sslSocket.getSession();
Certificate[] peer = session.getPeerCertificates();
log.info(LocalizedStrings.SocketCreator_SSL_CONNECTION_FROM_PEER_0,
((X509Certificate)peer[0]).getSubjectDN());
}
}
catch (SSLPeerUnverifiedException ex) {
if (this.needClientAuth) {
if(log !=null) log.severe(LocalizedStrings.SocketCreator_SSL_ERROR_IN_AUTHENTICATING_PEER, ex);
throw ex;
}
}
catch (SSLException ex) {
if(log != null) log.severe(LocalizedStrings.SocketCreator_SSL_ERROR_IN_CONNECTING_TO_PEER_0_1,
new Object[] {socket.getInetAddress(), Integer.valueOf(socket.getPort())}, ex);
throw ex;
}
// catch ( IOException e ) {
// if ( this.needClientAuth ) {
// logSevere( log, "SSL Error in authenticating peer.", e );
// throw e;
// }
// else {
// logWarning( log, "SSL Error in authenticating peer.", e );
// }
// }
}
}
/** Print current configured state to log. */
private void printConfig( GFLogWriter log ) {
if ( ! configShown && log.fineEnabled()) {
configShown = true;
StringBuilder sb = new StringBuilder();
sb.append( "SSL Configuration: \n" );
sb.append( " ssl-enabled = " + this.useSSL ).append( "\n" );
// add other options here....
for (String key: System.getProperties().stringPropertyNames()) { // fix for 46822
if ( key.startsWith( "javax.net.ssl" ) ) {
sb.append( " " ).append( key ).append( " = " ).append( System.getProperty( key ) ).append( "\n" );
}
}
log.fine( sb.toString() );
}
}
/** Read an array of values from a string, whitespace separated. */
private static String[] readArray(String text) {
if (text == null || text.trim().equals("")) {
return null;
}
StringTokenizer st = new StringTokenizer(text);
ArrayList v = new ArrayList();
while (st.hasMoreTokens()) {
v.add(st.nextToken());
}
return v.toArray(new String[v.size()]);
}
/**
* Closes the specified socket in a background thread and waits a limited
* amount of time for the close to complete. In some cases we see close
* hang (see bug 33665).
* Made public so it can be used from CacheClientProxy.
* @param sock the socket to close
* @param log the log writer to use in the thread (must not be null)
* @param who who the socket is connected to
* @param extra an optional Runnable with stuff to execute in the async thread
*/
public static void asyncClose(final Socket sock, final LogWriterI18n log, String who, final Runnable extra) {
if (sock == null || sock.isClosed()) {
return;
}
try {
ThreadGroup tg = LogWriterImpl.createThreadGroup("Socket asyncClose", log);
Thread t = new Thread(tg, new Runnable() {
public void run() {
if (extra != null) {
extra.run();
}
inlineClose(sock, log);
}
}, "AsyncSocketCloser for " + who);
t.setDaemon(true);
try {
t.start();
} catch (OutOfMemoryError ignore) {
// If we can't start a thread to close the socket just do it inline.
// See bug 50573.
inlineClose(sock, log);
return;
}
try {
// [bruce] if the network fails, this will wait the full amount of time
// on every close, so it must be kept very short. it was 750ms before,
// causing frequent hangs in net-down hydra tests
t.join(50/*ms*/);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
catch (Error e) {
if (SystemFailure.isJVMFailureError(e)) {
SystemFailure.initiateFailure(e);
// If this ever returns, rethrow the error. We're poisoned
// now, so don't let this thread continue.
}
throw e;
}
}
/**
* Closes the specified socket
* @param sock the socket to close
* @param log the log writer to use (must not be null)
*/
public static void inlineClose(final Socket sock, final LogWriterI18n log) {
// the next two statements are a mad attempt to fix bug
// 36041 - segv in jrockit in pthread signaling code. This
// seems to alleviate the problem.
try {
sock.shutdownInput();
sock.shutdownOutput();
}
catch (Exception e) {
}
try {
sock.close();
} catch (IOException ignore) {
}
catch (java.security.ProviderException pe) {
// some ssl implementations have trouble with termination and throw
// this exception. See bug #40783
}
catch (Error e) {
if (SystemFailure.isJVMFailureError(e)) {
SystemFailure.initiateFailure(e);
// If this ever returns, rethrow the error. We're poisoned
// now, so don't let this thread continue.
throw e;
}
// Whenever you catch Error or Throwable, you must also
// check for fatal JVM error (see above). However, there is
// _still_ a possibility that you are dealing with a cascading
// error condition, so you also need to check to see if the JVM
// is still usable:
SystemFailure.checkFailure();
// Sun's NIO implementation has been known to throw Errors
// that are caused by IOExceptions. If this is the case, it's
// okay.
if (e.getCause() instanceof IOException) {
// okay...
} else {
throw e;
}
}
}
protected void initializeClientSocketFactory() {
this.clientSocketFactory = null;
String className = System.getProperty("gemfire.clientSocketFactory");
if (className != null) {
Object o;
try {
Class> c = ClassPathLoader.getLatest().forName(className);
o = c.newInstance();
}
catch (Exception e) {
// No cache exists yet, so this can't be logged.
String s = "An unexpected exception occurred while instantiating a "
+ className + ": " + e;
throw new IllegalArgumentException(s);
}
if (o instanceof ClientSocketFactory) {
this.clientSocketFactory = (ClientSocketFactory) o;
} else {
String s = "Class \"" + className + "\" is not a ClientSocketFactory";
throw new IllegalArgumentException(s);
}
}
}
public void initializeTransportFilterClientSocketFactory(GatewaySender sender) {
this.clientSocketFactory = new TransportFilterSocketFactory()
.setGatewayTransportFilters(sender.getGatewayTransportFilters());
}
// // -------------------------------------------------------------------------
// // dummy ReadWriteLock impl's used to compare performance impact of
// // WriterPreferenceReadWriteLock usage
// // -------------------------------------------------------------------------
// private class RW implements ReadWriteLock {
// public Sync readLock() {
// return new S();
// }
// public Sync writeLock() {
// return new S();
// }
// }
// private class S implements Sync {
// public void acquire() throws InterruptedException {}
// public boolean attempt(long msecs) throws InterruptedException { return true; }
// public void release() {}
// }
/** returns a set of the non-loopback InetAddresses for this machine */
public static Set getMyAddresses(boolean includeLocal) {
try {
return ClientSharedUtils.getMyAddresses(includeLocal);
} catch (SocketException se) {
throw new IllegalArgumentException(
LocalizedStrings.StartupMessage_UNABLE_TO_EXAMINE_NETWORK_INTERFACES
.toLocalizedString(), se);
}
}
/**
* Returns true if host matches the LOCALHOST or any valid bind addresses of
* this machine.
*/
public static boolean isLocalHost(Object host) {
if (host instanceof InetAddress) {
final InetAddress addr = (InetAddress)host;
if (InetAddressUtil.LOCALHOST.equals(host)) {
return true;
}
else {
try {
Enumeration en = NetworkInterface
.getNetworkInterfaces();
while (en.hasMoreElements()) {
NetworkInterface i = en.nextElement();
for (Enumeration en2 = i.getInetAddresses(); en2
.hasMoreElements();) {
if (addr.equals(en2.nextElement())) {
return true;
}
}
}
return false;
} catch (SocketException e) {
throw new IllegalArgumentException(
LocalizedStrings.InetAddressUtil_UNABLE_TO_QUERY_NETWORK_INTERFACE
.toLocalizedString(), e);
}
}
}
else {
return isLocalHost(toInetAddress(host.toString()));
}
}
/**
* Converts the string host to an instance of InetAddress. Returns null if
* the string is empty. Fails Assertion if the conversion would result in
* java.lang.UnknownHostException
.
*
* Any leading slashes on host will be ignored.
*
* @param host string version the InetAddress
* @return the host converted to InetAddress instance
*/
public static InetAddress toInetAddress(String host) {
if (host == null || host.length() == 0) {
return null;
}
try {
if (host.indexOf("/") > -1) {
return InetAddress.getByName(host.substring(host.indexOf("/") + 1));
}
else {
return InetAddress.getByName(host);
}
} catch (java.net.UnknownHostException e) {
throw new IllegalArgumentException(e.getMessage());
}
}
}