org.apache.geode.internal.net.SocketCreator.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.geode.internal.net.SocketCreator.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for additional information regarding
 * copyright ownership. The ASF licenses this file to You 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.
 */
package org.apache.geode.internal.net;

import java.io.FileInputStream;
import java.io.IOException;
import java.net.BindException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.nio.channels.ServerSocketChannel;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.PrivateKey;
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.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import java.util.Set;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedKeyManager;

import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.Logger;

import org.apache.geode.GemFireConfigException;
import org.apache.geode.SystemConnectException;
import org.apache.geode.SystemFailure;
import org.apache.geode.admin.internal.InetAddressUtil;
import org.apache.geode.cache.wan.GatewaySender;
import org.apache.geode.cache.wan.GatewayTransportFilter;
import org.apache.geode.distributed.ClientSocketFactory;
import org.apache.geode.distributed.internal.DistributionConfig;
import org.apache.geode.distributed.internal.DistributionConfigImpl;
import org.apache.geode.distributed.internal.InternalDistributedSystem;
import org.apache.geode.internal.ClassPathLoader;
import org.apache.geode.internal.ConnectionWatcher;
import org.apache.geode.internal.GfeConsoleReaderFactory;
import org.apache.geode.internal.GfeConsoleReaderFactory.GfeConsoleReader;
import org.apache.geode.internal.admin.SSLConfig;
import org.apache.geode.internal.cache.wan.TransportFilterServerSocket;
import org.apache.geode.internal.cache.wan.TransportFilterSocketFactory;
import org.apache.geode.internal.i18n.LocalizedStrings;
import org.apache.geode.internal.logging.LogService;
import org.apache.geode.internal.logging.log4j.LocalizedMessage;
import org.apache.geode.internal.security.SecurableCommunicationChannel;
import org.apache.geode.internal.util.PasswordUtil;

/**
 * Analyze configuration data (gemfire.properties) and configure sockets accordingly for SSL.
 * <p>
 * gemfire.useSSL = (true|false) default false.<br/>
 * gemfire.ssl.debug = (true|false) default false.<br/>
 * gemfire.ssl.needClientAuth = (true|false) default true.<br/>
 * gemfire.ssl.protocols = <i>list of protocols</i><br/>
 * gemfire.ssl.ciphers = <i>list of cipher suites</i><br/>
 * <p>
 * The following may be included to configure the certificates used by the Sun Provider.
 * <p>
 * javax.net.ssl.trustStore = <i>pathname</i><br/>
 * javax.net.ssl.trustStorePassword = <i>password</i><br/>
 * javax.net.ssl.keyStore = <i>pathname</i><br/>
 * javax.net.ssl.keyStorePassword = <i>password</i><br/>
 * <p>
 * Additional properties will be set as System properties to be available as needed by other
 * provider implementations.
 */
public class SocketCreator {

    private static final Logger logger = LogService.getLogger();

    /**
     * Optional system property to enable GemFire usage of link-local addresses
     */
    public static final String USE_LINK_LOCAL_ADDRESSES_PROPERTY = DistributionConfig.GEMFIRE_PREFIX
            + "net.useLinkLocalAddresses";

    /**
     * True if GemFire should use link-local addresses
     */
    private static final boolean useLinkLocalAddresses = Boolean.getBoolean(USE_LINK_LOCAL_ADDRESSES_PROPERTY);

    /**
     * we cache localHost to avoid bug #40619, access-violation in native code
     */
    private static final InetAddress localHost;

    /**
     * all classes should use this variable to determine whether to use IPv4 or IPv6 addresses
     */
    private static boolean useIPv6Addresses = !Boolean.getBoolean("java.net.preferIPv4Stack")
            && Boolean.getBoolean("java.net.preferIPv6Addresses");

    private static final Map<InetAddress, String> hostNames = new HashMap<>();

    /**
     * flag to force always using DNS (regardless of the fact that these lookups can hang)
     */
    public static final boolean FORCE_DNS_USE = Boolean
            .getBoolean(DistributionConfig.GEMFIRE_PREFIX + "forceDnsUse");

    /**
     * set this to false to inhibit host name lookup
     */
    public static volatile boolean resolve_dns = true;

    /**
     * True if this SocketCreator has been initialized and is ready to use
     */
    private boolean ready = false;

    /**
     * Only print this SocketCreator's config once
     */
    private boolean configShown = false;

    /**
     * context for SSL socket factories
     */
    private SSLContext sslContext;

    private SSLConfig sslConfig;

    static {
        InetAddress inetAddress = null;
        try {
            inetAddress = InetAddress.getByAddress(InetAddress.getLocalHost().getAddress());
            if (inetAddress.isLoopbackAddress()) {
                InetAddress ipv4Fallback = null;
                InetAddress ipv6Fallback = null;
                // try to find a non-loopback address
                Set myInterfaces = getMyAddresses();
                boolean preferIPv6 = SocketCreator.useIPv6Addresses;
                String lhName = null;
                for (Iterator<InetAddress> it = myInterfaces.iterator(); lhName == null && it.hasNext();) {
                    InetAddress addr = it.next();
                    if (addr.isLoopbackAddress() || addr.isAnyLocalAddress()) {
                        break;
                    }
                    boolean ipv6 = addr instanceof Inet6Address;
                    boolean ipv4 = addr instanceof Inet4Address;
                    if ((preferIPv6 && ipv6) || (!preferIPv6 && ipv4)) {
                        String addrName = reverseDNS(addr);
                        if (inetAddress.isLoopbackAddress()) {
                            inetAddress = addr;
                            lhName = addrName;
                        } else if (addrName != null) {
                            inetAddress = addr;
                            lhName = addrName;
                        }
                    } else {
                        if (preferIPv6 && ipv4 && ipv4Fallback == null) {
                            ipv4Fallback = addr;
                        } else if (!preferIPv6 && ipv6 && ipv6Fallback == null) {
                            ipv6Fallback = addr;
                        }
                    }
                }
                // vanilla Ubuntu installations will have a usable IPv6 address when
                // running as a guest OS on an IPv6-enabled machine. We also look for
                // the alternative IPv4 configuration.
                if (inetAddress.isLoopbackAddress()) {
                    if (ipv4Fallback != null) {
                        inetAddress = ipv4Fallback;
                        SocketCreator.useIPv6Addresses = false;
                    } else if (ipv6Fallback != null) {
                        inetAddress = ipv6Fallback;
                        SocketCreator.useIPv6Addresses = true;
                    }
                }
            }
        } catch (UnknownHostException e) {
        }
        localHost = inetAddress;
    }

    /**
     * A factory used to create client <code>Sockets</code>.
     */
    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(DistributionConfig.GEMFIRE_PREFIX + "setTcpKeepAlive");
        if (str != null) {
            ENABLE_TCP_KEEP_ALIVE = Boolean.valueOf(str);
        } else {
            ENABLE_TCP_KEEP_ALIVE = true;
        }
    }

    // -------------------------------------------------------------------------
    // Constructor
    // -------------------------------------------------------------------------

    /**
     * Constructs new SocketCreator instance.
     */
    SocketCreator(final SSLConfig sslConfig) {
        this.sslConfig = sslConfig;
        initialize();
    }

    // -------------------------------------------------------------------------
    // Static instance accessors
    // -------------------------------------------------------------------------

    /**
     * All GemFire code should use this method instead of InetAddress.getLocalHost(). See bug #40619
     */
    public static InetAddress getLocalHost() throws UnknownHostException {
        if (localHost == null) {
            throw new UnknownHostException();
        }
        return localHost;
    }

    /**
     * All classes should use this instead of relying on the JRE system property
     */
    public static boolean preferIPv6Addresses() {
        return SocketCreator.useIPv6Addresses;
    }

    /**
     * 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 = (String) 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 = (String) hostNames.get(addr);
        if (result == null) {
            hostNames.put(addr, hostName);
            return hostName;
        }
        return result;
    }

    /**
     * Reset the hostNames caches
     */
    public static synchronized void resetHostNameCache() {
        hostNames.clear();
    }

    // -------------------------------------------------------------------------
    // Initializers (change SocketCreator state)
    // -------------------------------------------------------------------------

    /**
     * Initialize this SocketCreator.
     * <p>
     * Caller must synchronize on the SocketCreator instance.
     */
    @SuppressWarnings("hiding")
    private void initialize() {
        try {
            // set p2p values...
            if (SecurableCommunicationChannel.CLUSTER.equals(sslConfig.getSecuredCommunicationChannel())) {
                if (this.sslConfig.isEnabled()) {
                    System.setProperty("p2p.useSSL", "true");
                    System.setProperty("p2p.oldIO", "true");
                    System.setProperty("p2p.nodirectBuffers", "true");
                } else {
                    System.setProperty("p2p.useSSL", "false");
                }
            }

            try {
                if (this.sslConfig.isEnabled() && sslContext == null) {
                    sslContext = createAndConfigureSSLContext();
                    SSLContext.setDefault(sslContext);
                }
            } catch (Exception e) {
                throw new GemFireConfigException("Error configuring GemFire ssl ", e);
            }

            // make sure TCPConduit picks up p2p properties...
            org.apache.geode.internal.tcp.TCPConduit.init();

            initializeClientSocketFactory();
            this.ready = true;
        } catch (VirtualMachineError err) {
            SystemFailure.initiateFailure(err);
            // If this ever returns, rethrow the error. We're poisoned
            // now, so don't let this thread continue.
            throw err;
        } catch (Error t) {
            // Whenever you catch Error or Throwable, you must also
            // catch VirtualMachineError (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.
     * 
     * @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() throws GeneralSecurityException, IOException {

        SSLContext newSSLContext = getSSLContextInstance();
        KeyManager[] keyManagers = getKeyManagers();
        TrustManager[] trustManagers = getTrustManagers();

        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<String, String> 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 <code>false</code> existing gemfire.properties file is read,
     *        if <code>true</code>, properties from gemfire.properties file are ignored.
     */
    public static void readSSLProperties(Map<String, String> env, boolean ignoreGemFirePropsFile) {
        Properties props = new Properties();
        DistributionConfigImpl.loadGemFireProperties(props, ignoreGemFirePropsFile);
        for (Object entry : props.entrySet()) {
            Map.Entry<String, String> ent = (Map.Entry<String, String>) 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 SSLContext getSSLContextInstance() {
        String[] protocols = sslConfig.getProtocolsAsStringArray();
        SSLContext sslContext = null;
        if (protocols != null && protocols.length > 0) {
            for (String protocol : protocols) {
                if (!protocol.equals("any")) {
                    try {
                        sslContext = SSLContext.getInstance(protocol);
                        break;
                    } catch (NoSuchAlgorithmException e) {
                        // continue
                    }
                }
            }
        }
        if (sslContext != null) {
            return sslContext;
        }
        // lookup known algorithms
        String[] knownAlgorithms = { "SSL", "SSLv2", "SSLv3", "TLS", "TLSv1", "TLSv1.1", "TLSv1.2" };
        for (String algo : knownAlgorithms) {
            try {
                sslContext = SSLContext.getInstance(algo);
                break;
            } catch (NoSuchAlgorithmException e) {
                // continue
            }
        }
        return sslContext;
    }

    private TrustManager[] getTrustManagers()
            throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
        TrustManager[] trustManagers = null;
        GfeConsoleReader consoleReader = GfeConsoleReaderFactory.getDefaultConsoleReader();

        String trustStoreType = sslConfig.getTruststoreType();
        if (StringUtils.isEmpty(trustStoreType)) {
            // read from console, default on empty
            if (consoleReader.isSupported()) {
                trustStoreType = consoleReader
                        .readLine("Please enter the trustStoreType (javax.net.ssl.trustStoreType) : ");
            } else {
                trustStoreType = KeyStore.getDefaultType();
            }
        }

        KeyStore ts = KeyStore.getInstance(trustStoreType);
        String trustStorePath = sslConfig.getTruststore();
        if (StringUtils.isEmpty(trustStorePath)) {
            if (consoleReader.isSupported()) {
                trustStorePath = consoleReader
                        .readLine("Please enter the trustStore location (javax.net.ssl.trustStore) : ");
            }
        }
        FileInputStream fis = new FileInputStream(trustStorePath);
        String passwordString = sslConfig.getTruststorePassword();
        char[] password = null;
        if (passwordString != null) {
            if (passwordString.trim().equals("")) {
                if (!StringUtils.isEmpty(passwordString)) {
                    String toDecrypt = "encrypted(" + passwordString + ")";
                    passwordString = PasswordUtil.decrypt(toDecrypt);
                    password = passwordString.toCharArray();
                }
                // read from the console
                if (StringUtils.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() throws KeyStoreException, IOException, NoSuchAlgorithmException,
            CertificateException, UnrecoverableKeyException {
        GfeConsoleReader consoleReader = GfeConsoleReaderFactory.getDefaultConsoleReader();

        KeyManager[] keyManagers = null;
        String keyStoreType = sslConfig.getKeystoreType();
        if (StringUtils.isEmpty(keyStoreType)) {
            // read from console, default on empty
            if (consoleReader.isSupported()) {
                keyStoreType = consoleReader
                        .readLine("Please enter the keyStoreType (javax.net.ssl.keyStoreType) : ");
            } else {
                keyStoreType = KeyStore.getDefaultType();
            }
        }
        KeyStore keyStore = KeyStore.getInstance(keyStoreType);
        String keyStoreFilePath = sslConfig.getKeystore();
        if (StringUtils.isEmpty(keyStoreFilePath)) {
            if (consoleReader.isSupported()) {
                keyStoreFilePath = consoleReader
                        .readLine("Please enter the keyStore location (javax.net.ssl.keyStore) : ");
            } else {
                keyStoreFilePath = System.getProperty("user.home") + System.getProperty("file.separator")
                        + ".keystore";
            }
        }

        FileInputStream fileInputStream = new FileInputStream(keyStoreFilePath);
        String passwordString = sslConfig.getKeystorePassword();
        char[] password = null;
        if (passwordString != null) {
            if (passwordString.trim().equals("")) {
                String encryptedPass = System.getenv("javax.net.ssl.keyStorePassword");
                if (!StringUtils.isEmpty(encryptedPass)) {
                    String toDecrypt = "encrypted(" + encryptedPass + ")";
                    passwordString = PasswordUtil.decrypt(toDecrypt);
                    password = passwordString.toCharArray();
                }
                // read from the console
                if (StringUtils.isEmpty(passwordString) && consoleReader != null) {
                    password = consoleReader
                            .readPassword("Please enter password for keyStore (javax.net.ssl.keyStorePassword) : ");
                }
            } else {
                password = passwordString.toCharArray();
            }
        }
        keyStore.load(fileInputStream, password);
        // default algorithm can be changed by setting property "ssl.KeyManagerFactory.algorithm" in
        // security properties
        KeyManagerFactory keyManagerFactory = KeyManagerFactory
                .getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(keyStore, password);
        keyManagers = keyManagerFactory.getKeyManagers();
        // follow the security tip in java doc
        if (password != null) {
            java.util.Arrays.fill(password, ' ');
        }

        KeyManager[] extendedKeyManagers = new KeyManager[keyManagers.length];

        for (int i = 0; i < keyManagers.length; i++)

        {
            extendedKeyManagers[i] = new ExtendedAliasKeyManager(keyManagers[i], sslConfig.getAlias());
        }

        return extendedKeyManagers;
    }

    private static final class ExtendedAliasKeyManager extends X509ExtendedKeyManager {

        private final X509ExtendedKeyManager delegate;

        private final String keyAlias;

        /**
         * Constructor.
         * 
         * @param mgr The X509KeyManager used as a delegate
         * @param keyAlias The alias name of the server's keypair and supporting certificate chain
         */
        ExtendedAliasKeyManager(KeyManager mgr, String keyAlias) {
            this.delegate = (X509ExtendedKeyManager) mgr;
            this.keyAlias = keyAlias;
        }

        @Override
        public String[] getClientAliases(final String s, final Principal[] principals) {
            return delegate.getClientAliases(s, principals);
        }

        @Override
        public String chooseClientAlias(final String[] strings, final Principal[] principals, final Socket socket) {
            if (!StringUtils.isEmpty(this.keyAlias)) {
                return keyAlias;
            }
            return delegate.chooseClientAlias(strings, principals, socket);
        }

        @Override
        public String[] getServerAliases(final String s, final Principal[] principals) {
            return delegate.getServerAliases(s, principals);
        }

        @Override
        public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
            if (!StringUtils.isEmpty(this.keyAlias)) {
                PrivateKey key = this.delegate.getPrivateKey(this.keyAlias);
                return getKeyAlias(keyType, key);
            }
            return this.delegate.chooseServerAlias(keyType, issuers, socket);

        }

        @Override
        public X509Certificate[] getCertificateChain(final String s) {
            if (!StringUtils.isEmpty(this.keyAlias)) {
                return delegate.getCertificateChain(keyAlias);
            }
            return delegate.getCertificateChain(s);
        }

        @Override
        public PrivateKey getPrivateKey(final String alias) {
            return delegate.getPrivateKey(alias);
        }

        @Override
        public String chooseEngineServerAlias(final String keyType, final Principal[] principals,
                final SSLEngine sslEngine) {
            if (!StringUtils.isEmpty(this.keyAlias)) {
                PrivateKey key = this.delegate.getPrivateKey(this.keyAlias);
                return getKeyAlias(keyType, key);
            }
            return this.delegate.chooseEngineServerAlias(keyType, principals, sslEngine);

        }

        private String getKeyAlias(final String keyType, final PrivateKey key) {
            if (key != null) {
                if (key.getAlgorithm().equals(keyType)) {
                    return this.keyAlias;
                } else {
                    return null;
                }
            } else {
                return null;
            }
        }
    }

    // -------------------------------------------------------------------------
    // Public methods
    // -------------------------------------------------------------------------

    /**
     * Returns true if this SocketCreator is configured to use SSL.
     */
    public boolean useSSL() {
        return this.sslConfig.isEnabled();
    }

    /**
     * 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) throws IOException {
        return createServerSocket(nport, backlog, null);
    }

    public ServerSocket createServerSocket(int nport, int backlog, InetAddress bindAddr,
            List<GatewayTransportFilter> transportFilters, int socketBufferSize) throws IOException {
        if (transportFilters.isEmpty()) {
            return createServerSocket(nport, backlog, bindAddr, socketBufferSize);
        } else {
            printConfig();
            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) throws IOException {
        return createServerSocket(nport, backlog, bindAddr, -1, sslConfig.isEnabled());
    }

    public ServerSocket createServerSocket(int nport, int backlog, InetAddress bindAddr, int socketBufferSize)
            throws IOException {
        return createServerSocket(nport, backlog, bindAddr, socketBufferSize, sslConfig.isEnabled());
    }

    private ServerSocket createServerSocket(int nport, int backlog, InetAddress bindAddr, int socketBufferSize,
            boolean sslConnection) throws IOException {
        printConfig();
        if (sslConnection) {
            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;
        }
    }

    /**
     * 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 tcpBufferSize
     *
     * @return Returns the new server socket.
     *
     * @throws IOException
     */
    public ServerSocket createServerSocketUsingPortRange(InetAddress ba, int backlog, boolean isBindAddress,
            boolean useNIO, int tcpBufferSize, int[] tcpPortRange) throws IOException {
        return createServerSocketUsingPortRange(ba, backlog, isBindAddress, useNIO, tcpBufferSize, tcpPortRange,
                sslConfig.isEnabled());
    }

    /**
     * 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 tcpBufferSize
     * @param sslConnection whether to connect using SSL
     *
     * @return Returns the new server socket.
     *
     * @throws IOException
     */
    public ServerSocket createServerSocketUsingPortRange(InetAddress ba, int backlog, boolean isBindAddress,
            boolean useNIO, int tcpBufferSize, int[] tcpPortRange, boolean sslConnection) 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());
                }
            }
            try {
                if (useNIO) {
                    ServerSocketChannel channl = ServerSocketChannel.open();
                    socket = channl.socket();

                    InetSocketAddress addr = new InetSocketAddress(isBindAddress ? ba : null, localPort);
                    socket.bind(addr, backlog);
                } else {
                    socket = this.createServerSocket(localPort, backlog, isBindAddress ? ba : null, tcpBufferSize,
                            sslConnection);
                }
                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, int timeout) throws IOException {
        return connect(InetAddress.getByName(host), port, timeout, null, true, -1);
    }

    /**
     * Return a client socket. This method is used by client/server clients.
     */
    public Socket connectForClient(String host, int port, int timeout, int socketBufferSize) throws IOException {
        return connect(InetAddress.getByName(host), port, timeout, null, true, socketBufferSize);
    }

    /**
     * Return a client socket. This method is used by peers.
     */
    public Socket connectForServer(InetAddress inetadd, int port) throws IOException {
        return connect(inetadd, port, 0, null, false, -1);
    }

    /**
     * Return a client socket. This method is used by peers.
     */
    public Socket connectForServer(InetAddress inetadd, int port, int socketBufferSize) throws IOException {
        return connect(inetadd, port, 0, null, false, socketBufferSize);
    }

    /**
     * Return a client socket, timing out if unable to connect and timeout > 0 (millis). The parameter
     * <i>timeout</i> 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, int timeout, ConnectionWatcher optionalWatcher,
            boolean clientSide) throws IOException {
        return connect(inetadd, port, timeout, optionalWatcher, clientSide, -1);
    }

    /**
     * Return a client socket, timing out if unable to connect and timeout > 0 (millis). The parameter
     * <i>timeout</i> 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, int timeout, ConnectionWatcher optionalWatcher,
            boolean clientSide, int socketBufferSize) throws IOException {
        return connect(inetadd, port, timeout, optionalWatcher, clientSide, socketBufferSize,
                sslConfig.isEnabled());
    }

    /**
     * Return a client socket, timing out if unable to connect and timeout > 0 (millis). The parameter
     * <i>timeout</i> 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, int timeout, ConnectionWatcher optionalWatcher,
            boolean clientSide, int socketBufferSize, boolean sslConnection) throws IOException {
        Socket socket = null;
        SocketAddress sockaddr = new InetSocketAddress(inetadd, port);
        printConfig();
        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();

                // Optionally enable SO_KEEPALIVE in the OS network protocol.
                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);
                }
                socket.connect(sockaddr, Math.max(timeout, 0));
                configureClientSSLSocket(socket, timeout);
                return socket;
            } else {
                if (clientSide && this.clientSocketFactory != null) {
                    socket = this.clientSocketFactory.createSocket(inetadd, port);
                } else {
                    socket = new Socket();

                    // Optionally enable SO_KEEPALIVE in the OS network protocol.
                    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);
                    }
                    socket.connect(sockaddr, Math.max(timeout, 0));
                }
                return socket;
            }
        } finally {
            if (optionalWatcher != null) {
                optionalWatcher.afterConnect(socket);
            }
        }
    }

    /**
     * Will be a server socket... this one simply registers the listeners.
     */
    public void configureServerSSLSocket(Socket socket) throws IOException {
        if (socket instanceof SSLSocket) {
            SSLSocket sslSocket = (SSLSocket) socket;
            try {
                sslSocket.startHandshake();
                SSLSession session = sslSocket.getSession();
                Certificate[] peer = session.getPeerCertificates();
                if (logger.isDebugEnabled()) {
                    logger.debug(LocalizedMessage.create(LocalizedStrings.SocketCreator_SSL_CONNECTION_FROM_PEER_0,
                            ((X509Certificate) peer[0]).getSubjectDN()));
                }
            } catch (SSLPeerUnverifiedException ex) {
                if (this.sslConfig.isRequireAuth()) {
                    logger.fatal(
                            LocalizedMessage.create(
                                    LocalizedStrings.SocketCreator_SSL_ERROR_IN_AUTHENTICATING_PEER_0_1,
                                    new Object[] { socket.getInetAddress(), Integer.valueOf(socket.getPort()) }),
                            ex);
                    throw ex;
                }
            } catch (SSLException ex) {
                logger.fatal(
                        LocalizedMessage.create(LocalizedStrings.SocketCreator_SSL_ERROR_IN_CONNECTING_TO_PEER_0_1,
                                new Object[] { socket.getInetAddress(), Integer.valueOf(socket.getPort()) }),
                        ex);
                throw ex;
            }
        }
    }

    // -------------------------------------------------------------------------
    // Private implementation methods
    // -------------------------------------------------------------------------

    /**
     * Configure the SSLServerSocket based on this SocketCreator's settings.
     */
    private void finishServerSocket(SSLServerSocket serverSocket) throws IOException {
        serverSocket.setUseClientMode(false);
        if (this.sslConfig.isRequireAuth()) {
            // serverSocket.setWantClientAuth( true );
            serverSocket.setNeedClientAuth(true);
        }
        serverSocket.setEnableSessionCreation(true);

        // restrict protocols
        String[] protocols = this.sslConfig.getProtocolsAsStringArray();
        if (!"any".equalsIgnoreCase(protocols[0])) {
            serverSocket.setEnabledProtocols(protocols);
        }
        // restrict ciphers
        String[] ciphers = this.sslConfig.getCiphersAsStringArray();
        if (!"any".equalsIgnoreCase(ciphers[0])) {
            serverSocket.setEnabledCipherSuites(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, int timeout) throws IOException {
        if (socket instanceof SSLSocket) {
            SSLSocket sslSocket = (SSLSocket) socket;

            sslSocket.setUseClientMode(true);
            sslSocket.setEnableSessionCreation(true);

            String[] protocols = this.sslConfig.getProtocolsAsStringArray();

            // restrict cyphers
            if (protocols != null && !"any".equalsIgnoreCase(protocols[0])) {
                sslSocket.setEnabledProtocols(protocols);
            }
            String[] ciphers = this.sslConfig.getCiphersAsStringArray();
            if (ciphers != null && !"any".equalsIgnoreCase(ciphers[0])) {
                sslSocket.setEnabledCipherSuites(ciphers);
            }

            try {
                if (timeout > 0) {
                    sslSocket.setSoTimeout(timeout);
                }
                sslSocket.startHandshake();
                SSLSession session = sslSocket.getSession();
                Certificate[] peer = session.getPeerCertificates();
                if (logger.isDebugEnabled()) {
                    logger.debug(LocalizedMessage.create(LocalizedStrings.SocketCreator_SSL_CONNECTION_FROM_PEER_0,
                            ((X509Certificate) peer[0]).getSubjectDN()));
                }
            } catch (SSLHandshakeException ex) {
                logger.fatal(
                        LocalizedMessage.create(LocalizedStrings.SocketCreator_SSL_ERROR_IN_CONNECTING_TO_PEER_0_1,
                                new Object[] { socket.getInetAddress(), Integer.valueOf(socket.getPort()) }),
                        ex);
                throw ex;
            } catch (SSLPeerUnverifiedException ex) {
                if (this.sslConfig.isRequireAuth()) {
                    logger.fatal(LocalizedMessage
                            .create(LocalizedStrings.SocketCreator_SSL_ERROR_IN_AUTHENTICATING_PEER), ex);
                    throw ex;
                }
            } catch (SSLException ex) {
                logger.fatal(
                        LocalizedMessage.create(LocalizedStrings.SocketCreator_SSL_ERROR_IN_CONNECTING_TO_PEER_0_1,
                                new Object[] { socket.getInetAddress(), Integer.valueOf(socket.getPort()) }),
                        ex);
                throw ex;
            }

        }
    }

    /**
     * Print current configured state to log.
     */
    private void printConfig() {
        if (!configShown && logger.isDebugEnabled()) {
            configShown = true;
            StringBuffer sb = new StringBuffer();
            sb.append("SSL Configuration: \n");
            sb.append("  ssl-enabled = " + this.sslConfig.isEnabled()).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");
                }
            }
            logger.debug(sb.toString());
        }
    }

    protected void initializeClientSocketFactory() {
        this.clientSocketFactory = null;
        String className = System.getProperty(DistributionConfig.GEMFIRE_PREFIX + "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());
    }

    /**
     * returns a set of the non-loopback InetAddresses for this machine
     */
    public static Set<InetAddress> getMyAddresses() {
        Set<InetAddress> result = new HashSet<InetAddress>();
        Set<InetAddress> locals = new HashSet<InetAddress>();
        Enumeration<NetworkInterface> interfaces;
        try {
            interfaces = NetworkInterface.getNetworkInterfaces();
        } catch (SocketException e) {
            throw new IllegalArgumentException(
                    LocalizedStrings.StartupMessage_UNABLE_TO_EXAMINE_NETWORK_INTERFACES.toLocalizedString(), e);
        }
        while (interfaces.hasMoreElements()) {
            NetworkInterface face = interfaces.nextElement();
            boolean faceIsUp = false;
            try {
                faceIsUp = face.isUp();
            } catch (SocketException e) {
                InternalDistributedSystem ids = InternalDistributedSystem.getAnyInstance();
                if (ids != null) {
                    logger.info("Failed to check if network interface is up. Skipping {}", face, e);
                }
            }
            if (faceIsUp) {
                Enumeration<InetAddress> addrs = face.getInetAddresses();
                while (addrs.hasMoreElements()) {
                    InetAddress addr = addrs.nextElement();
                    if (addr.isLoopbackAddress() || addr.isAnyLocalAddress()
                            || (!useLinkLocalAddresses && addr.isLinkLocalAddress())) {
                        locals.add(addr);
                    } else {
                        result.add(addr);
                    }
                } // while
            }
        } // while
          // fix for bug #42427 - allow product to run on a standalone box by using
          // local addresses if there are no non-local addresses available
        if (result.size() == 0) {
            return locals;
        } else {
            return result;
        }
    }

    /**
     * This method uses JNDI to look up an address in DNS and return its name
     * 
     * @param addr
     *
     * @return the host name associated with the address or null if lookup isn't possible or there is
     *         no host name for this address
     */
    public static String reverseDNS(InetAddress addr) {
        byte[] addrBytes = addr.getAddress();
        // reverse the address suitable for reverse lookup
        String lookup = "";
        for (int index = addrBytes.length - 1; index >= 0; index--) {
            lookup = lookup + (addrBytes[index] & 0xff) + '.';
        }
        lookup += "in-addr.arpa";
        // System.out.println("Looking up: " + lookup);

        try {
            Hashtable env = new Hashtable();
            env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory");
            DirContext ctx = new InitialDirContext(env);
            Attributes attrs = ctx.getAttributes(lookup, new String[] { "PTR" });
            for (NamingEnumeration ae = attrs.getAll(); ae.hasMoreElements();) {
                Attribute attr = (Attribute) ae.next();
                for (Enumeration vals = attr.getAll(); vals.hasMoreElements();) {
                    Object elem = vals.nextElement();
                    if ("PTR".equals(attr.getID()) && elem != null) {
                        return elem.toString();
                    }
                }
            }
            ctx.close();
        } catch (Exception e) {
            // ignored
        }
        return null;
    }

    /**
     * Returns true if host matches the LOCALHOST.
     */
    public static boolean isLocalHost(Object host) {
        if (host instanceof InetAddress) {
            if (InetAddressUtil.LOCALHOST.equals(host)) {
                return true;
            } else if (((InetAddress) host).isLoopbackAddress()) {
                return true;
            } else {
                try {
                    Enumeration en = NetworkInterface.getNetworkInterfaces();
                    while (en.hasMoreElements()) {
                        NetworkInterface i = (NetworkInterface) en.nextElement();
                        for (Enumeration en2 = i.getInetAddresses(); en2.hasMoreElements();) {
                            InetAddress addr = (InetAddress) en2.nextElement();
                            if (host.equals(addr)) {
                                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 <code>java.lang.UnknownHostException</code>.
     * <p>
     * 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());
        }
    }
}