/** * $RCSfile$ * $Revision$ * $Date$ * * Copyright (C) 2004 Jive Software. All rights reserved. * * This software is published under the terms of the GNU Public License (GPL), * a copy of which is included in this distribution. */ package org.jivesoftware.messenger.net; import org.jivesoftware.messenger.ConnectionManager; import org.jivesoftware.messenger.ServerPort; import org.jivesoftware.util.LocaleUtils; import org.jivesoftware.util.Log; import javax.net.ssl.SSLException; import java.io.IOException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; /** * Implements a network front end with a dedicated thread reading * each incoming socket. */ public class SSLSocketAcceptThread extends Thread implements SocketAcceptMBean { /** * The default Jabber socket */ public static final int DEFAULT_PORT = 5223; /** * Interface to bind to */ private InetAddress bindInterface; /** * Holds information about the port on which the server will listen for connections. */ private ServerPort serverPort; /** * True while this thread should continue running. */ private boolean notTerminated = true; /** * The accept socket we're running */ private ServerSocket serverSocket; /** * Connection manager handling connections created by this thread. * */ private ConnectionManager connManager; /** * The number of SSL related exceptions occuring rapidly that should signal a need * to shutdown the SSL port. */ private static final int MAX_SSL_EXCEPTIONS = 10; private long acceptCount = 0; /** * Creates an instance using the default port, TLS transport security, and * JVM defaults for all security settings. * * @param connManager the connection manager that will manage connections * generated by this thread * @throws IOException if there was trouble initializing the SSL configuration. */ public SSLSocketAcceptThread(ConnectionManager connManager, ServerPort serverPort) throws IOException { super("Secure Socket Listener"); this.connManager = connManager; this.serverPort = serverPort; int port = serverPort.getPort(); String interfaceName = serverPort.getInterfaceName(); bindInterface = null; if (interfaceName != null) { try { if (interfaceName.trim().length() > 0) { bindInterface = InetAddress.getByName(interfaceName); } } catch (UnknownHostException e) { Log.error(LocaleUtils.getLocalizedString("admin.error"), e); } } serverSocket = SSLConfig.createServerSocket(port, bindInterface); } /** * Retrieve the port this server socket is bound to. * * @return the port the socket is bound to. */ public int getPort() { return serverSocket.getLocalPort(); } /** * Returns information about the port on which the server is listening for connections. * * @return information about the port on which the server is listening for connections. */ public ServerPort getServerPort() { return serverPort; } /** * Unblock the thread and force it to terminate. */ public void shutdown() { notTerminated = false; try { ServerSocket sSock = serverSocket; serverSocket = null; if (sSock != null) { sSock.close(); } } catch (IOException e) { // we don't care, no matter what, the socket should be dead } } /** * About as simple as it gets. The thread spins around an accept * call getting sockets and handing them to the SocketManager. * We need to detect run away failures since an SSL configuration * problem can cause the loop to spin, constantly rethrowing SSLExceptions * (e.g. if a certificate is in the keystore that can't be verified). */ public void run() { long lastExceptionTime = 0; int exceptionCounter = 0; while (notTerminated) { try { Socket sock = serverSocket.accept(); Log.debug("SSL Connect " + sock.toString()); connManager.addSocket(sock, true, serverPort); this.acceptCount++; } catch (SSLException se) { long exceptionTime = System.currentTimeMillis(); if (exceptionTime - lastExceptionTime > 1000) { // if the time between SSL exceptions is too long // reset the counter exceptionCounter = 1; } else { // If this exception occured within a second of the last one // we need to count it exceptionCounter++; } lastExceptionTime = exceptionTime; Log.error(LocaleUtils.getLocalizedString("admin.error.ssl"), se); // and if the number of consecutive exceptions exceeds the limit // we should assume there's an SSL problem or DOS attack and shutdown if (exceptionCounter > MAX_SSL_EXCEPTIONS) { String msg = "Shutting down SSL port - " + "suspected configuration problem"; Log.error(msg); Log.info(msg); shutdown(); } } catch (Exception e) { if (notTerminated) { Log.error(LocaleUtils.getLocalizedString("admin.error.ssl"), e); } } } try { ServerSocket sSock = serverSocket; serverSocket = null; if (sSock != null) { sSock.close(); } } catch (IOException e) { // we don't care, no matter what, the socket should be dead } } /** * @see org.jivesoftware.messenger.net.SocketAcceptMBean#getAcceptCount() */ public long getAcceptCount() { return this.acceptCount; } }