/** * $RCSfile$ * $Revision: 1583 $ * $Date: 2005-07-03 17:55:39 -0300 (Sun, 03 Jul 2005) $ * * Copyright (C) 2006 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.multiplexer.net; import org.jivesoftware.util.JiveGlobals; import org.jivesoftware.util.LocaleUtils; import org.jivesoftware.util.Log; import org.jivesoftware.multiplexer.ServerPort; 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. The old SSL method always uses a blocking model. * * @author Gaston Dombiak */ public class SSLSocketAcceptThread extends Thread { /** * The default Jabber socket */ public static final int DEFAULT_PORT = 5223; /** * 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; /** * 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; /** * Creates an instance using the default port, TLS transport security, and * JVM defaults for all security settings. * * @throws IOException if there was trouble initializing the SSL configuration. */ public SSLSocketAcceptThread(ServerPort serverPort) throws IOException { super("Secure Socket Listener"); this.serverPort = serverPort; int port = serverPort.getPort(); // Listen on a specific network interface if it has been set. String interfaceName = JiveGlobals.getXMLProperty("network.interface"); InetAddress 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()); SocketReader reader = SocketReaderFactory.createSocketReader(sock, true, serverPort, true); // Create a new reading thread for each new connected client Thread thread = new Thread(reader, reader.getName()); thread.setDaemon(true); thread.setPriority(Thread.NORM_PRIORITY); thread.start(); } 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 (Throwable 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 } } }