SocketFetcherpublic class SocketFetcher extends Object This class is used to get Sockets. Depending on the arguments passed
it will either return a plain java.net.Socket or dynamically load
the SocketFactory class specified in the classname param and return
a socket created by that SocketFactory. |
Fields Summary |
---|
private static boolean | debug |
Constructors Summary |
---|
private SocketFetcher()
// No one should instantiate this class.
|
Methods Summary |
---|
private static void | checkServerIdentity(java.lang.String server, javax.net.ssl.SSLSocket sslSocket)Check the server from the Socket connection against the server name(s)
as expressed in the server certificate (RFC 2595 check).
// Check against the server name(s) as expressed in server certificate
try {
java.security.cert.Certificate[] certChain =
sslSocket.getSession().getPeerCertificates();
if (certChain != null && certChain.length > 0 &&
certChain[0] instanceof X509Certificate &&
matchCert(server, (X509Certificate)certChain[0]))
return;
} catch (SSLPeerUnverifiedException e) {
sslSocket.close();
IOException ioex = new IOException(
"Can't verify identity of server: " + server);
ioex.initCause(e);
throw ioex;
}
// If we get here, there is nothing to consider the server as trusted.
sslSocket.close();
throw new IOException("Can't verify identity of server: " + server);
| private static void | configureSSLSocket(java.net.Socket socket, java.util.Properties props, java.lang.String prefix)Configure the SSL options for the socket (if it's an SSL socket),
based on the mail..ssl.protocols and
mail..ssl.ciphersuites properties.
if (!(socket instanceof SSLSocket))
return;
SSLSocket sslsocket = (SSLSocket)socket;
String protocols = props.getProperty(prefix + ".ssl.protocols", null);
if (protocols != null)
sslsocket.setEnabledProtocols(stringArray(protocols));
else {
/*
* At least the UW IMAP server insists on only the TLSv1
* protocol for STARTTLS, and won't accept the old SSLv2
* or SSLv3 protocols. Here we enable only the TLSv1
* protocol. XXX - this should probably be parameterized.
*/
sslsocket.setEnabledProtocols(new String[] {"TLSv1"});
}
String ciphers = props.getProperty(prefix + ".ssl.ciphersuites", null);
if (ciphers != null)
sslsocket.setEnabledCipherSuites(stringArray(ciphers));
if (debug) {
System.out.println("DEBUG SocketFetcher: SSL protocols after " +
Arrays.asList(sslsocket.getEnabledProtocols()));
System.out.println("DEBUG SocketFetcher: SSL ciphers after " +
Arrays.asList(sslsocket.getEnabledCipherSuites()));
}
/*
* Force the handshake to be done now so that we can report any
* errors (e.g., certificate errors) to the caller of the startTLS
* method.
*/
sslsocket.startHandshake();
| private static java.net.Socket | createSocket(java.net.InetAddress localaddr, int localport, java.lang.String host, int port, int cto, java.util.Properties props, java.lang.String prefix, javax.net.SocketFactory sf, boolean useSSL, boolean idCheck)Create a socket with the given local address and connected to
the given host and port. Use the specified connection timeout.
If a socket factory is specified, use it. Otherwise, use the
SSLSocketFactory if useSSL is true.
Socket socket;
if (sf != null)
socket = sf.createSocket();
else if (useSSL) {
String trusted;
if ((trusted = props.getProperty(prefix + ".ssl.trust")) != null) {
try {
MailSSLSocketFactory msf = new MailSSLSocketFactory();
if (trusted.equals("*"))
msf.setTrustAllHosts(true);
else
msf.setTrustedHosts(trusted.split("\\s+"));
sf = msf;
} catch (GeneralSecurityException gex) {
IOException ioex = new IOException(
"Can't create MailSSLSocketFactory");
ioex.initCause(gex);
throw ioex;
}
} else
sf = SSLSocketFactory.getDefault();
socket = sf.createSocket();
} else
socket = new Socket();
if (localaddr != null)
socket.bind(new InetSocketAddress(localaddr, localport));
if (cto >= 0)
socket.connect(new InetSocketAddress(host, port), cto);
else
socket.connect(new InetSocketAddress(host, port));
if (idCheck && socket instanceof SSLSocket)
checkServerIdentity(host, (SSLSocket)socket);
if (sf instanceof MailSSLSocketFactory) {
MailSSLSocketFactory msf = (MailSSLSocketFactory)sf;
if (!msf.isServerTrusted(host, (SSLSocket)socket)) {
try {
socket.close();
} finally {
throw new IOException("Server is not trusted: " + host);
}
}
}
return socket;
| private static java.lang.ClassLoader | getContextClassLoader()Convenience method to get our context class loader.
Assert any privileges we might have and then call the
Thread.getContextClassLoader method.
return (ClassLoader)
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
ClassLoader cl = null;
try {
cl = Thread.currentThread().getContextClassLoader();
} catch (SecurityException ex) { }
return cl;
}
});
| public static java.net.Socket | getSocket(java.lang.String host, int port, java.util.Properties props, java.lang.String prefix, boolean useSSL)This method returns a Socket. Properties control the use of
socket factories and other socket characteristics. The properties
used are:
- prefix.socketFactory
- prefix.socketFactory.class
- prefix.socketFactory.fallback
- prefix.socketFactory.port
- prefix.ssl.socketFactory
- prefix.ssl.socketFactory.class
- prefix.ssl.socketFactory.port
- prefix.timeout
- prefix.connectiontimeout
- prefix.localaddress
- prefix.localport
If we're making an SSL connection, the ssl.socketFactory
properties are used first, if set.
If the socketFactory property is set, the value is an
instance of a SocketFactory class, not a string. The
instance is used directly. If the socketFactory property
is not set, the socketFactory.class property is considered.
(Note that the SocketFactory property must be set using the
put method, not the setProperty
method.)
If the socketFactory.class property isn't set, the socket
returned is an instance of java.net.Socket connected to the
given host and port. If the socketFactory.class property is set,
it is expected to contain a fully qualified classname of a
javax.net.SocketFactory subclass. In this case, the class is
dynamically instantiated and a socket created by that
SocketFactory is returned.
If the socketFactory.fallback property is set to false, don't
fall back to using regular sockets if the socket factory fails.
The socketFactory.port specifies a port to use when connecting
through the socket factory. If unset, the port argument will be
used.
If the connectiontimeout property is set, the timeout is passed
to the socket connect method.
If the timeout property is set, it is used to set the socket timeout.
If the localaddress property is set, it's used as the local address
to bind to. If the localport property is also set, it's used as the
local port number to bind to.
if (debug)
System.out.println("DEBUG SocketFetcher: getSocket" +
", host " + host + ", port " + port +
", prefix " + prefix + ", useSSL " + useSSL);
if (prefix == null)
prefix = "socket";
if (props == null)
props = new Properties(); // empty
int cto = PropUtil.getIntProperty(props,
prefix + ".connectiontimeout", -1);
Socket socket = null;
String localaddrstr = props.getProperty(prefix + ".localaddress", null);
InetAddress localaddr = null;
if (localaddrstr != null)
localaddr = InetAddress.getByName(localaddrstr);
int localport = PropUtil.getIntProperty(props,
prefix + ".localport", 0);
boolean fb = PropUtil.getBooleanProperty(props,
prefix + ".socketFactory.fallback", true);
boolean idCheck = PropUtil.getBooleanProperty(props,
prefix + ".ssl.checkserveridentity", false);
int sfPort = -1;
String sfErr = "unknown socket factory";
try {
/*
* If using SSL, first look for SSL-specific class name or
* factory instance.
*/
SocketFactory sf = null;
String sfPortName = null;
if (useSSL) {
Object sfo = props.get(prefix + ".ssl.socketFactory");
if (sfo instanceof SocketFactory) {
sf = (SocketFactory)sfo;
sfErr = "SSL socket factory instance " + sf;
}
if (sf == null) {
String sfClass =
props.getProperty(prefix + ".ssl.socketFactory.class");
sf = getSocketFactory(sfClass);
sfErr = "SSL socket factory class " + sfClass;
}
sfPortName = ".ssl.socketFactory.port";
}
if (sf == null) {
Object sfo = props.get(prefix + ".socketFactory");
if (sfo instanceof SocketFactory) {
sf = (SocketFactory)sfo;
sfErr = "socket factory instance " + sf;
}
if (sf == null) {
String sfClass =
props.getProperty(prefix + ".socketFactory.class");
sf = getSocketFactory(sfClass);
sfErr = "socket factory class " + sfClass;
}
sfPortName = ".socketFactory.port";
}
// if we now have a socket factory, use it
if (sf != null) {
sfPort = PropUtil.getIntProperty(props,
prefix + sfPortName, -1);
// if port passed in via property isn't valid, use param
if (sfPort == -1)
sfPort = port;
socket = createSocket(localaddr, localport,
host, sfPort, cto, props, prefix, sf, useSSL, idCheck);
}
} catch (SocketTimeoutException sex) {
throw sex;
} catch (Exception ex) {
if (!fb) {
if (ex instanceof InvocationTargetException) {
Throwable t =
((InvocationTargetException)ex).getTargetException();
if (t instanceof Exception)
ex = (Exception)t;
}
if (ex instanceof IOException)
throw (IOException)ex;
IOException ioex = new IOException(
"Couldn't connect using " + sfErr +
" to host, port: " +
host + ", " + sfPort +
"; Exception: " + ex);
ioex.initCause(ex);
throw ioex;
}
}
if (socket == null)
socket = createSocket(localaddr, localport,
host, port, cto, props, prefix, null, useSSL, idCheck);
int to = PropUtil.getIntProperty(props, prefix + ".timeout", -1);
if (to >= 0)
socket.setSoTimeout(to);
configureSSLSocket(socket, props, prefix);
return socket;
| public static java.net.Socket | getSocket(java.lang.String host, int port, java.util.Properties props, java.lang.String prefix)
return getSocket(host, port, props, prefix, false);
| private static javax.net.SocketFactory | getSocketFactory(java.lang.String sfClass)Return a socket factory of the specified class.
if (sfClass == null || sfClass.length() == 0)
return null;
// dynamically load the class
ClassLoader cl = getContextClassLoader();
Class clsSockFact = null;
if (cl != null) {
try {
clsSockFact = Class.forName(sfClass, false, cl);
} catch (ClassNotFoundException cex) { }
}
if (clsSockFact == null)
clsSockFact = Class.forName(sfClass);
// get & invoke the getDefault() method
Method mthGetDefault = clsSockFact.getMethod("getDefault",
new Class[]{});
SocketFactory sf = (SocketFactory)
mthGetDefault.invoke(new Object(), new Object[]{});
return sf;
| private static boolean | matchCert(java.lang.String server, java.security.cert.X509Certificate cert)Do any of the names in the cert match the server name?
if (debug)
System.out.println("DEBUG SocketFetcher: matchCert server " +
server + ", cert " + cert);
/*
* First, try to use sun.security.util.HostnameChecker,
* which exists in Sun's JDK starting with 1.4.1.
* We use reflection to access it in case it's not available
* in the JDK we're running on.
*/
try {
Class hnc = Class.forName("sun.security.util.HostnameChecker");
// invoke HostnameChecker.getInstance(HostnameChecker.TYPE_LDAP)
// HostnameChecker.TYPE_LDAP == 2
// LDAP requires the same regex handling as we need
Method getInstance = hnc.getMethod("getInstance",
new Class[] { byte.class });
Object hostnameChecker = getInstance.invoke(new Object(),
new Object[] { new Byte((byte)2) });
// invoke hostnameChecker.match( server, cert)
if (debug)
System.out.println("DEBUG SocketFetcher: " +
"using sun.security.util.HostnameChecker");
Method match = hnc.getMethod("match",
new Class[] { String.class, X509Certificate.class });
try {
match.invoke(hostnameChecker, new Object[] { server, cert });
return true;
} catch (InvocationTargetException cex) {
if (debug)
System.out.println("DEBUG SocketFetcher: FAIL: " + cex);
return false;
}
} catch (Exception ex) {
if (debug)
System.out.println("DEBUG SocketFetcher: " +
"NO sun.security.util.HostnameChecker: " + ex);
// ignore failure and continue below
}
/*
* Lacking HostnameChecker, we implement a crude version of
* the same checks ourselves.
*/
try {
/*
* Check each of the subjectAltNames.
* XXX - only checks DNS names, should also handle
* case where server name is a literal IP address
*/
Collection names = cert.getSubjectAlternativeNames();
if (names != null) {
boolean foundName = false;
for (Iterator it = names.iterator(); it.hasNext(); ) {
List nameEnt = (List)it.next();
Integer type = (Integer)nameEnt.get(0);
if (type.intValue() == 2) { // 2 == dNSName
foundName = true;
String name = (String)nameEnt.get(1);
if (debug)
System.out.println("DEBUG SocketFetcher: " +
"found name: " + name);
if (matchServer(server, name))
return true;
}
}
if (foundName) // found a name, but no match
return false;
}
} catch (CertificateParsingException ex) {
// ignore it
}
// XXX - following is a *very* crude parse of the name and ignores
// all sorts of important issues such as quoting
Pattern p = Pattern.compile("CN=([^,]*)");
Matcher m = p.matcher(cert.getSubjectX500Principal().getName());
if (m.find() && matchServer(server, m.group(1).trim()))
return true;
return false;
| private static boolean | matchServer(java.lang.String server, java.lang.String name)Does the server we're expecting to connect to match the
given name from the server's certificate?
if (debug)
System.out.println("DEBUG SocketFetcher: match server " + server +
" with " + name);
if (name.startsWith("*.")) {
// match "foo.example.com" with "*.example.com"
String tail = name.substring(2);
if (tail.length() == 0)
return false;
int off = server.length() - tail.length();
if (off < 1)
return false;
// if tail matches and is preceeded by "."
return server.charAt(off - 1) == '." &&
server.regionMatches(true, off, tail, 0, tail.length());
} else
return server.equalsIgnoreCase(name);
| public static java.net.Socket | startTLS(java.net.Socket socket)Start TLS on an existing socket.
Supports the "STARTTLS" command in many protocols.
This version for compatibility with possible third party code
that might've used this API even though it shouldn't.
return startTLS(socket, new Properties(), "socket");
| public static java.net.Socket | startTLS(java.net.Socket socket, java.util.Properties props, java.lang.String prefix)Start TLS on an existing socket.
Supports the "STARTTLS" command in many protocols.
This version for compatibility with possible third party code
that might've used this API even though it shouldn't.
InetAddress a = socket.getInetAddress();
String host = a.getHostName();
return startTLS(socket, host, props, prefix);
| public static java.net.Socket | startTLS(java.net.Socket socket, java.lang.String host, java.util.Properties props, java.lang.String prefix)Start TLS on an existing socket.
Supports the "STARTTLS" command in many protocols.
int port = socket.getPort();
if (debug)
System.out.println("DEBUG SocketFetcher: startTLS host " +
host + ", port " + port);
String sfErr = "unknown socket factory";
try {
SSLSocketFactory ssf = null;
SocketFactory sf = null;
// first, look for an SSL socket factory
Object sfo = props.get(prefix + ".ssl.socketFactory");
if (sfo instanceof SocketFactory) {
sf = (SocketFactory)sfo;
sfErr = "SSL socket factory instance " + sf;
}
if (sf == null) {
String sfClass =
props.getProperty(prefix + ".ssl.socketFactory.class");
sf = getSocketFactory(sfClass);
sfErr = "SSL socket factory class " + sfClass;
}
if (sf != null && sf instanceof SSLSocketFactory)
ssf = (SSLSocketFactory)sf;
// next, look for a regular socket factory that happens to be
// an SSL socket factory
if (ssf == null) {
sfo = props.get(prefix + ".socketFactory");
if (sfo instanceof SocketFactory) {
sf = (SocketFactory)sfo;
sfErr = "socket factory instance " + sf;
}
if (sf == null) {
String sfClass =
props.getProperty(prefix + ".socketFactory.class");
sf = getSocketFactory(sfClass);
sfErr = "socket factory class " + sfClass;
}
if (sf != null && sf instanceof SSLSocketFactory)
ssf = (SSLSocketFactory)sf;
}
// finally, use the default SSL socket factory
if (ssf == null) {
String trusted;
if ((trusted = props.getProperty(prefix + ".ssl.trust")) !=
null) {
try {
MailSSLSocketFactory msf = new MailSSLSocketFactory();
if (trusted.equals("*"))
msf.setTrustAllHosts(true);
else
msf.setTrustedHosts(trusted.split("\\s+"));
ssf = msf;
sfErr = "mail SSL socket factory";
} catch (GeneralSecurityException gex) {
IOException ioex = new IOException(
"Can't create MailSSLSocketFactory");
ioex.initCause(gex);
throw ioex;
}
} else {
ssf = (SSLSocketFactory)SSLSocketFactory.getDefault();
sfErr = "default SSL socket factory";
}
}
socket = ssf.createSocket(socket, host, port, true);
boolean idCheck = PropUtil.getBooleanProperty(props,
prefix + ".ssl.checkserveridentity", false);
if (idCheck)
checkServerIdentity(host, (SSLSocket)socket);
if (ssf instanceof MailSSLSocketFactory) {
MailSSLSocketFactory msf = (MailSSLSocketFactory)ssf;
if (!msf.isServerTrusted(host, (SSLSocket)socket)) {
try {
socket.close();
} finally {
throw new IOException("Server is not trusted: " + host);
}
}
}
configureSSLSocket(socket, props, prefix);
} catch (Exception ex) {
if (ex instanceof InvocationTargetException) {
Throwable t =
((InvocationTargetException)ex).getTargetException();
if (t instanceof Exception)
ex = (Exception)t;
}
if (ex instanceof IOException)
throw (IOException)ex;
// wrap anything else before sending it on
IOException ioex = new IOException(
"Exception in startTLS using " + sfErr +
": host, port: " +
host + ", " + port +
"; Exception: " + ex);
ioex.initCause(ex);
throw ioex;
}
return socket;
| private static java.lang.String[] | stringArray(java.lang.String s)Parse a string into whitespace separated tokens
and return the tokens in an array.
StringTokenizer st = new StringTokenizer(s);
List tokens = new ArrayList();
while (st.hasMoreTokens())
tokens.add(st.nextToken());
return (String[])tokens.toArray(new String[tokens.size()]);
|
|