/*
* Copyright 2013, The Sporting Exchange Limited
*
* 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.
*/
package com.betfair.cougar.core.impl.jmx;
import com.betfair.cougar.logging.CougarLogger;
import com.betfair.cougar.logging.CougarLoggingUtils;
import com.sun.jdmk.comm.CommunicationException;
import com.sun.jdmk.comm.HtmlAdaptorServer;
import com.sun.jdmk.comm.JdmkHtmlRequestHandler;
import org.springframework.core.io.Resource;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.logging.Level;
/**
* Extends HtmlAdaptorServer to add TLS support
*/
public class TlsHtmlAdaptorServer extends HtmlAdaptorServer {
private static final int SOCKET_TIMEOUT = 10 * 1000;
private static final CougarLogger logger = CougarLoggingUtils.getLogger(TlsHtmlAdaptorServer.class);
private final Resource keystoreResource;
private final String keystorePasswd;
private final String keystoreType;
private final String certPasswd;
private ServerSocket serverSocket;
private Socket socket;
private final boolean tlsEnabled;
private final boolean reuseAddress;
private InetAddress lastClientAddress;
public TlsHtmlAdaptorServer(final Resource keystoreResource, final String keystorePasswd,
final String certPasswd, final int port, final String keystoreType, final boolean enabled, boolean reuseAddress) {
this.keystoreResource = keystoreResource;
this.keystorePasswd = keystorePasswd;
this.certPasswd = certPasswd;
this.keystoreType = keystoreType;
this.tlsEnabled = enabled;
this.reuseAddress = reuseAddress;
setPort(port);
}
public boolean isTlsEnabled() {
return tlsEnabled;
}
@Override
protected void doBind() throws CommunicationException, InterruptedException {
if (tlsEnabled) {
final KeyStore keyStore;
try {
keyStore = loadKeyStore(keystoreResource, keystorePasswd, keystoreType);
} catch (Exception e) {
logger.log(e);
throw new RuntimeException("Can't load keystore from " + keystoreResource, e);
}
try {
serverSocket = createSecureServerSocket(keyStore, certPasswd);
} catch (Exception e) {
logger.log(e);
throw new RuntimeException("Error while creating server socket", e);
}
}
else {
try {
serverSocket = createServerSocket();
} catch (Exception e) {
logger.log(e);
throw new RuntimeException("Error while creating server socket", e);
}
}
logger.log(Level.INFO, "Created ServerSocket " + serverSocket);
}
@Override
protected void doReceive() throws CommunicationException,
InterruptedException {
try {
this.socket = this.serverSocket.accept();
this.socket.setSoTimeout(SOCKET_TIMEOUT);
} catch (IOException e) {
logger.log(e);
throw new CommunicationException(e, "Error while accepting connection on server socket");
}
}
@Override
protected void doProcess() throws CommunicationException ,InterruptedException {
this.lastClientAddress = this.socket.getInetAddress();
new JdmkHtmlRequestHandler(this.socket, this ,getMBeanServer(), getServedClientCount());
this.socket = null;
}
@Override
protected void doUnbind() throws CommunicationException,
InterruptedException {
try {
this.serverSocket.close();
} catch (IOException e) {
logger.log(e);
throw new CommunicationException(e,"Error while closing socket");
}
}
protected ServerSocket createSecureServerSocket(final KeyStore keyStore, final String passwd)
throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException, IOException {
final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(keyStore, passwd.toCharArray());
final SSLContext sslcontext = SSLContext.getInstance("SSLv3");
sslcontext.init(keyManagerFactory.getKeyManagers(), null, null);
ServerSocket ss = sslcontext.getServerSocketFactory().createServerSocket();
return bindSocket(ss);
}
protected KeyStore loadKeyStore(final Resource keystoreResource, final String passwd, final String type)
throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException {
final KeyStore keyStore = KeyStore.getInstance(type);
InputStream keyStoreStream = null;
try {
keyStoreStream = keystoreResource.getInputStream();
keyStore.load(keyStoreStream, passwd.toCharArray());
return keyStore;
} finally {
if(keyStoreStream != null)
keyStoreStream.close();
}
}
private ServerSocket createServerSocket() throws IOException {
ServerSocket ss = new ServerSocket();
return bindSocket(ss);
}
private ServerSocket bindSocket(ServerSocket socket) throws IOException {
if (reuseAddress) {
socket.setReuseAddress(true);
}
socket.bind(new InetSocketAddress(getPort()), 2 * getMaxActiveClientCount());
return socket;
}
public String getLastConnectedClient() {
if (this.lastClientAddress == null) {
return "unknown";
}
return this.lastClientAddress.getHostAddress();
}
}