/*
* NOTE: This copyright does *not* cover user programs that use HQ
* program services by normal system calls through the application
* program interfaces provided as part of the Hyperic Plug-in Development
* Kit or the Hyperic Client Development Kit - this is merely considered
* normal use of the program, and does *not* fall under the heading of
* "derived work".
*
* Copyright (C) [2004, 2005, 2006], Hyperic, Inc.
* This file is part of HQ.
*
* HQ is free software; you can redistribute it and/or modify
* it under the terms version 2 of the GNU General Public License as
* published by the Free Software Foundation. This program is distributed
* in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*/
package org.hyperic.hq.bizapp.agent.client;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import javax.net.ssl.SSLSocket;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.hyperic.hq.agent.client.AgentConnection;
import org.hyperic.util.security.DefaultSSLProviderImpl;
import org.hyperic.util.security.HQSSLProviderImpl;
import org.hyperic.util.security.KeystoreConfig;
import org.hyperic.util.security.SSLProvider;
/**
* An object which wraps an AgentConnection object, so as to provide
* SSL capabilities (associated with the CAMServerHandler's handleConnection
* method)
*/
public class SecureAgentConnection
extends AgentConnection
{
private static final Log log = LogFactory.getLog(SecureAgentConnection.class);
private static final String PROP_READ_TIMEOUT = "agent.readTimeOut";
private static final String PROP_POST_HANDSHAKE_TIMEOUT = "agent.postHandshakeTimeOut";
private static final int READ_TIMEOUT = 60000;
private static final int POST_HANDSHAKE_TIMEOUT = 5 * 60 * 1000;
private String agentAddress;
private int agentPort;
private String authToken;
private boolean acceptUnverifiedCertificate = false;
private KeystoreConfig keystoreConfig;
private SecureAgentConnection(KeystoreConfig keystoreConfig, String agentAddress, int agentPort,
String authToken) {
super(agentAddress, agentPort);
this.agentAddress = agentAddress;
this.agentPort = agentPort;
this.authToken = authToken;
this.keystoreConfig = keystoreConfig;
}
public SecureAgentConnection(String agentAddress, int agentPort, String authToken,
KeystoreConfig keystoreConfig,
boolean acceptUnverifiedCertificate) {
this(keystoreConfig, agentAddress, agentPort, authToken);
this.acceptUnverifiedCertificate = acceptUnverifiedCertificate;
}
@Override
protected Socket getSocket()
throws IOException
{
SSLSocket socket;
log.debug("Creating secure socket");
try {
// Check for configured agent read timeout from System properties
int readTimeout;
try {
readTimeout = Integer.parseInt(System.getProperty(PROP_READ_TIMEOUT));
} catch (NumberFormatException e) {
readTimeout = READ_TIMEOUT;
}
// Check for configured agent post handshake timeout
// from System properties
int postHandshakeTimeout;
try {
postHandshakeTimeout =
Integer.parseInt(System.getProperty(PROP_POST_HANDSHAKE_TIMEOUT));
} catch (NumberFormatException e) {
postHandshakeTimeout = POST_HANDSHAKE_TIMEOUT;
}
SSLProvider sslProvider = new HQSSLProviderImpl(keystoreConfig, acceptUnverifiedCertificate);
SSLSocketFactory factory = sslProvider.getSSLSocketFactory();
// See the following links...
// http://www.apache.org/dist/httpcomponents/httpcore/RELEASE_NOTES-4.1.x.txt
// http://www-128.ibm.com/developerworks/forums/dw_thread.jsp?message=13695343&cat=10&thread=73546&treeDisplayType=threadmode1&forum=178#13695343
// In any case, it would seem as though the bug has since been fixed in IBM's JRE, no need to work around it anymore...
socket = (SSLSocket) factory.createSocket();
// Make sure the InetAddress used to initialize the socket has a non-null hostname (empty string).
// This prevents slow and unnecessary reverse DNS querying when the connection is opened.
InetAddress withoutHost = InetAddress.getByName(this.agentAddress);
InetAddress withHost = InetAddress.getByAddress("", withoutHost.getAddress());
InetSocketAddress address = new InetSocketAddress( withHost, this.agentPort);
socket.connect(address, readTimeout);
// Set the socket timeout during the initial handshake to detect
// connection issues with the agent.
socket.setSoTimeout(readTimeout);
log.debug("Secure socket is connected to " + address + " - starting handshake.");
socket.startHandshake();
log.debug("SSL handshake complete");
// [HHQ-3694] The timeout is set to a post handshake value.
socket.setSoTimeout(postHandshakeTimeout);
} catch(IOException exc){
IOException toThrow = new IOException("Unable to connect to " +
this.agentAddress + ":" +
this.agentPort + ": " +
exc.getMessage());
// call initCause instead of constructor to be java 1.5 compat
toThrow.initCause(exc);
throw toThrow;
}
// Write our security settings
try {
DataOutputStream dOs;
dOs = new DataOutputStream(socket.getOutputStream());
dOs.writeUTF(this.authToken);
} catch(IOException exc){
IOException toThrow = new IOException("Unable to write auth params to server");
// call initCause instead of constructor to be java 1.5 compat
toThrow.initCause(exc);
throw toThrow;
}
return socket;
}
public String toString(){
return this.agentAddress + ":" + this.agentPort;
}
}