// jTDS JDBC Driver for Microsoft SQL Server and Sybase
//Copyright (C) 2004 The jTDS Project
//
//This library is free software; you can redistribute it and/or
//modify it under the terms of the GNU Lesser General Public
//License as published by the Free Software Foundation; either
//version 2.1 of the License, or (at your option) any later version.
//
//This library 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
//Lesser General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public
//License along with this library; if not, write to the Free Software
//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
package net.sourceforge.jtds.ssl;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import com.sun.net.ssl.SSLContext;
import com.sun.net.ssl.TrustManager;
import com.sun.net.ssl.X509TrustManager;
import net.sourceforge.jtds.util.Logger;
/**
* Used for acquiring a socket factory when SSL is enabled.
*
* @author Rob Worsnop
* @author Mike Hutchinson
* @version $Id: SocketFactoriesSUN.java,v 1.1.2.1 2009-12-30 14:32:51 ickzon Exp $
*/
public class SocketFactoriesSUN {
/**
* Returns a socket factory, the behavior of which will depend on the SSL
* setting and whether or not the DB server supports SSL.
*
* @param ssl the SSL setting
* @param socket plain TCP/IP socket to wrap
*/
public static SocketFactory getSocketFactory(String ssl, Socket socket) {
return new TdsTlsSocketFactory(ssl, socket);
}
/**
* The socket factory for creating sockets based on the SSL setting.
*/
private static class TdsTlsSocketFactory extends SocketFactory {
private static SSLSocketFactory factorySingleton;
private final String ssl;
private final Socket socket;
/**
* Constructs a TdsTlsSocketFactory.
*
* @param ssl the SSL setting
* @param socket the TCP/IP socket to wrap
*/
public TdsTlsSocketFactory(String ssl, Socket socket) {
this.ssl = ssl;
this.socket = socket;
}
/**
* Create the SSL socket.
* <p/>
* NB. This method will actually create a connected socket over the
* TCP/IP network socket supplied via the constructor of this factory
* class.
*/
public Socket createSocket(String host, int port)
throws IOException, UnknownHostException {
SSLSocket sslSocket = (SSLSocket) getFactory()
.createSocket(new TdsTlsSocket(socket), host, port, true);
//
// See if connecting to local server.
// getLocalHost() will normally return the address of a real
// local network interface so we check that one and the loopback
// address localhost/127.0.0.1
//
// XXX: Disable TLS resume altogether, because the cause of local
// server failures is unknown and it also seems to sometiles occur
// with remote servers.
//
// if (socket.getInetAddress().equals(InetAddress.getLocalHost()) ||
// host.equalsIgnoreCase("localhost") || host.startsWith("127.")) {
// Resume session causes failures with a local server
// Invalidate the session to prevent resumes.
sslSocket.startHandshake(); // Any IOException thrown here
sslSocket.getSession().invalidate();
// Logger.println("TLS Resume disabled");
// }
return sslSocket;
}
/*
* (non-Javadoc)
*
* @see javax.net.SocketFactory#createSocket(java.net.InetAddress, int)
*/
public Socket createSocket(InetAddress host, int port)
throws IOException {
return null;
}
/*
* (non-Javadoc)
*
* @see javax.net.SocketFactory#createSocket(java.lang.String, int,
* java.net.InetAddress, int)
*/
public Socket createSocket(String host, int port,
InetAddress localHost, int localPort) throws IOException,
UnknownHostException {
return null;
}
/*
* (non-Javadoc)
*
* @see javax.net.SocketFactory#createSocket(java.net.InetAddress, int,
* java.net.InetAddress, int)
*/
public Socket createSocket(InetAddress host, int port,
InetAddress localHost, int localPort) throws IOException {
return null;
}
/**
* Returns an SSLSocketFactory whose behavior will depend on the SSL
* setting.
*
* @return an <code>SSLSocketFactory</code>
*/
private SSLSocketFactory getFactory() throws IOException {
try {
if (Ssl.SSL_AUTHENTICATE.equals(ssl)) {
// the default factory will produce a socket that authenticates
// the server using its certificate chain.
return (SSLSocketFactory) SSLSocketFactory.getDefault();
} else {
// Our custom factory will not authenticate the server.
return factory();
}
} catch (GeneralSecurityException e) {
Logger.logException(e);
throw new IOException(e.getMessage());
}
}
/**
* Returns an SSLSocketFactory whose sockets will not authenticate the
* server.
*
* @return an <code>SSLSocketFactory</code>
*/
private static SSLSocketFactory factory()
throws NoSuchAlgorithmException, KeyManagementException {
if (factorySingleton == null) {
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, trustManagers(), null);
factorySingleton = ctx.getSocketFactory();
}
return factorySingleton;
}
private static TrustManager[] trustManagers() {
X509TrustManager tm = new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public boolean isClientTrusted(X509Certificate[] chain) {
return true;
}
public boolean isServerTrusted(X509Certificate[] chain) {
return true;
}
};
return new X509TrustManager[]{tm};
}
}
}