Skip to content

Commit a9da758

Browse files
author
masensio
committed
Merge pull request owncloud#321 from owncloud/sni_support_based_on_network_implementation_built_in
Sni support based on network implementation built in
2 parents 34c85bf + 04e2434 commit a9da758

File tree

3 files changed

+195
-4
lines changed

3 files changed

+195
-4
lines changed

AndroidManifest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@
113113
<service
114114
android:name=".syncadapter.FileSyncService"
115115
android:exported="true"
116-
android:process=":sync">
116+
>
117117
<intent-filter>
118118
<action android:name="android.content.SyncAdapter" />
119119
</intent-filter>

oc_framework/src/com/owncloud/android/oc_framework/network/AdvancedSslSocketFactory.java

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,15 @@
2424
import java.net.Socket;
2525
import java.net.SocketAddress;
2626
import java.net.UnknownHostException;
27+
//import java.security.Provider;
2728
import java.security.cert.X509Certificate;
29+
//import java.util.Enumeration;
2830

2931
import javax.net.SocketFactory;
3032
import javax.net.ssl.SSLContext;
3133
import javax.net.ssl.SSLException;
3234
import javax.net.ssl.SSLHandshakeException;
35+
//import javax.net.ssl.SSLParameters;
3336
import javax.net.ssl.SSLPeerUnverifiedException;
3437
import javax.net.ssl.SSLSession;
3538
import javax.net.ssl.SSLSocket;
@@ -39,6 +42,7 @@
3942
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
4043
import org.apache.http.conn.ssl.X509HostnameVerifier;
4144

45+
//import android.os.Build;
4246
import android.util.Log;
4347

4448

@@ -84,8 +88,47 @@ public Socket createSocket(String host, int port, InetAddress clientHost, int cl
8488
return socket;
8589
}
8690

91+
/*
92+
private void logSslInfo() {
93+
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.FROYO) {
94+
Log.v(TAG, "SUPPORTED SSL PARAMETERS");
95+
logSslParameters(mSslContext.getSupportedSSLParameters());
96+
Log.v(TAG, "DEFAULT SSL PARAMETERS");
97+
logSslParameters(mSslContext.getDefaultSSLParameters());
98+
Log.i(TAG, "CURRENT PARAMETERS");
99+
Log.i(TAG, "Protocol: " + mSslContext.getProtocol());
100+
}
101+
Log.i(TAG, "PROVIDER");
102+
logSecurityProvider(mSslContext.getProvider());
103+
}
87104
88-
/**
105+
private void logSecurityProvider(Provider provider) {
106+
Log.i(TAG, "name: " + provider.getName());
107+
Log.i(TAG, "version: " + provider.getVersion());
108+
Log.i(TAG, "info: " + provider.getInfo());
109+
Enumeration<?> keys = provider.propertyNames();
110+
String key;
111+
while (keys.hasMoreElements()) {
112+
key = (String) keys.nextElement();
113+
Log.i(TAG, " property " + key + " : " + provider.getProperty(key));
114+
}
115+
}
116+
117+
private void logSslParameters(SSLParameters params) {
118+
Log.v(TAG, "Cipher suites: ");
119+
String [] elements = params.getCipherSuites();
120+
for (int i=0; i<elements.length ; i++) {
121+
Log.v(TAG, " " + elements[i]);
122+
}
123+
Log.v(TAG, "Protocols: ");
124+
elements = params.getProtocols();
125+
for (int i=0; i<elements.length ; i++) {
126+
Log.v(TAG, " " + elements[i]);
127+
}
128+
}
129+
*/
130+
131+
/**
89132
* Attempts to get a new socket connection to the given host within the
90133
* given time limit.
91134
*
@@ -110,19 +153,23 @@ public Socket createSocket(final String host, final int port,
110153
throw new IllegalArgumentException("Parameters may not be null");
111154
}
112155
int timeout = params.getConnectionTimeout();
156+
157+
//logSslInfo();
158+
113159
SocketFactory socketfactory = mSslContext.getSocketFactory();
114160
Log.d(TAG, " ... with connection timeout " + timeout + " and socket timeout " + params.getSoTimeout());
115161
Socket socket = socketfactory.createSocket();
116162
SocketAddress localaddr = new InetSocketAddress(localAddress, localPort);
117163
SocketAddress remoteaddr = new InetSocketAddress(host, port);
118164
socket.setSoTimeout(params.getSoTimeout());
119165
socket.bind(localaddr);
166+
ServerNameIndicator.setServerNameIndication(host, (SSLSocket)socket);
120167
socket.connect(remoteaddr, timeout);
121168
verifyPeerIdentity(host, port, socket);
122169
return socket;
123170
}
124171

125-
/**
172+
/**
126173
* @see ProtocolSocketFactory#createSocket(java.lang.String,int)
127174
*/
128175
public Socket createSocket(String host, int port) throws IOException,
@@ -238,5 +285,5 @@ private void verifyPeerIdentity(String host, int port, Socket socket) throws IOE
238285
throw io;
239286
}
240287
}
241-
288+
242289
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/* ownCloud Android client application
2+
* Copyright (C) 2012-2013 ownCloud Inc.
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License version 2,
6+
* as published by the Free Software Foundation.
7+
*
8+
* This program is distributed in the hope that it will be useful,
9+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
* GNU General Public License for more details.
12+
*
13+
* You should have received a copy of the GNU General Public License
14+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
15+
*
16+
*/
17+
18+
package com.owncloud.android.oc_framework.network;
19+
20+
import java.lang.ref.WeakReference;
21+
import java.lang.reflect.InvocationTargetException;
22+
import java.lang.reflect.Method;
23+
import java.util.concurrent.atomic.AtomicReference;
24+
25+
import javax.net.ssl.SSLSocket;
26+
27+
import android.util.Log;
28+
29+
30+
/**
31+
* Enables the support of Server Name Indication if existing
32+
* in the underlying network implementation.
33+
*
34+
* Build as a singleton.
35+
*
36+
* @author David A. Velasco
37+
*/
38+
public class ServerNameIndicator {
39+
40+
private static final String TAG = ServerNameIndicator.class.getSimpleName();
41+
42+
private static final AtomicReference<ServerNameIndicator> mSingleInstance = new AtomicReference<ServerNameIndicator>();
43+
44+
private static final String METHOD_NAME = "setHostname";
45+
46+
private final WeakReference<Class<?>> mSSLSocketClassRef;
47+
private final WeakReference<Method> mSetHostnameMethodRef;
48+
49+
50+
/**
51+
* Private constructor, class is a singleton.
52+
*
53+
* @param sslSocketClass Underlying implementation class of {@link SSLSocket} used to connect with the server.
54+
* @param setHostnameMethod Name of the method to call to enable the SNI support.
55+
*/
56+
private ServerNameIndicator(Class<?> sslSocketClass, Method setHostnameMethod) {
57+
mSSLSocketClassRef = new WeakReference<Class<?>>(sslSocketClass);
58+
mSetHostnameMethodRef = (setHostnameMethod == null) ? null : new WeakReference<Method>(setHostnameMethod);
59+
}
60+
61+
62+
/**
63+
* Calls the {@code #setHostname(String)} method of the underlying implementation
64+
* of {@link SSLSocket} if exists.
65+
*
66+
* Creates and initializes the single instance of the class when needed
67+
*
68+
* @param hostname The name of the server host of interest.
69+
* @param sslSocket Client socket to connect with the server.
70+
*/
71+
public static void setServerNameIndication(String hostname, SSLSocket sslSocket) {
72+
final Method setHostnameMethod = getMethod(sslSocket);
73+
if (setHostnameMethod != null) {
74+
try {
75+
setHostnameMethod.invoke(sslSocket, hostname);
76+
Log.i(TAG, "SNI done, hostname: " + hostname);
77+
78+
} catch (IllegalArgumentException e) {
79+
Log.e(TAG, "Call to SSLSocket#setHost(String) failed ", e);
80+
81+
} catch (IllegalAccessException e) {
82+
Log.e(TAG, "Call to SSLSocket#setHost(String) failed ", e);
83+
84+
} catch (InvocationTargetException e) {
85+
Log.e(TAG, "Call to SSLSocket#setHost(String) failed ", e);
86+
}
87+
} else {
88+
Log.i(TAG, "SNI not supported");
89+
}
90+
}
91+
92+
93+
/**
94+
* Gets the method to invoke trying to minimize the effective
95+
* application of reflection.
96+
*
97+
* @param sslSocket Instance of the SSL socket to use in connection with server.
98+
* @return Method to call to indicate the server name of interest to the server.
99+
*/
100+
private static Method getMethod(SSLSocket sslSocket) {
101+
final Class<?> sslSocketClass = sslSocket.getClass();
102+
final ServerNameIndicator instance = mSingleInstance.get();
103+
if (instance == null) {
104+
return initFrom(sslSocketClass);
105+
106+
} else if (instance.mSSLSocketClassRef.get() != sslSocketClass) {
107+
// the underlying class changed
108+
return initFrom(sslSocketClass);
109+
110+
} else if (instance.mSetHostnameMethodRef == null) {
111+
// SNI not supported
112+
return null;
113+
114+
} else {
115+
final Method cachedSetHostnameMethod = instance.mSetHostnameMethodRef.get();
116+
return (cachedSetHostnameMethod == null) ? initFrom(sslSocketClass) : cachedSetHostnameMethod;
117+
}
118+
}
119+
120+
121+
/**
122+
* Singleton initializer.
123+
*
124+
* Uses reflection to extract and 'cache' the method to invoke to indicate the desited host name to the server side.
125+
*
126+
* @param sslSocketClass Underlying class providing the implementation of {@link SSLSocket}.
127+
* @return Method to call to indicate the server name of interest to the server.
128+
*/
129+
private static Method initFrom(Class<?> sslSocketClass) {
130+
Log.i(TAG, "SSLSocket implementation: " + sslSocketClass.getCanonicalName());
131+
Method setHostnameMethod = null;
132+
try {
133+
setHostnameMethod = sslSocketClass.getMethod(METHOD_NAME, String.class);
134+
} catch (SecurityException e) {
135+
Log.e(TAG, "Could not access to SSLSocket#setHostname(String) method ", e);
136+
137+
} catch (NoSuchMethodException e) {
138+
Log.i(TAG, "Could not find SSLSocket#setHostname(String) method - SNI not supported");
139+
}
140+
mSingleInstance.set(new ServerNameIndicator(sslSocketClass, setHostnameMethod));
141+
return setHostnameMethod;
142+
}
143+
144+
}

0 commit comments

Comments
 (0)