1 /* ownCloud webDAV Library for Android is available under MIT license
2 * Copyright (C) 2014 ownCloud (http://www.owncloud.org/)
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
18 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
19 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 package com
.owncloud
.android
.oc_framework
.network
;
27 import java
.lang
.ref
.WeakReference
;
28 import java
.lang
.reflect
.InvocationTargetException
;
29 import java
.lang
.reflect
.Method
;
30 import java
.util
.concurrent
.atomic
.AtomicReference
;
32 import javax
.net
.ssl
.SSLSocket
;
34 import android
.util
.Log
;
38 * Enables the support of Server Name Indication if existing
39 * in the underlying network implementation.
41 * Build as a singleton.
43 * @author David A. Velasco
45 public class ServerNameIndicator
{
47 private static final String TAG
= ServerNameIndicator
.class.getSimpleName();
49 private static final AtomicReference
<ServerNameIndicator
> mSingleInstance
= new AtomicReference
<ServerNameIndicator
>();
51 private static final String METHOD_NAME
= "setHostname";
53 private final WeakReference
<Class
<?
>> mSSLSocketClassRef
;
54 private final WeakReference
<Method
> mSetHostnameMethodRef
;
58 * Private constructor, class is a singleton.
60 * @param sslSocketClass Underlying implementation class of {@link SSLSocket} used to connect with the server.
61 * @param setHostnameMethod Name of the method to call to enable the SNI support.
63 private ServerNameIndicator(Class
<?
> sslSocketClass
, Method setHostnameMethod
) {
64 mSSLSocketClassRef
= new WeakReference
<Class
<?
>>(sslSocketClass
);
65 mSetHostnameMethodRef
= (setHostnameMethod
== null
) ? null
: new WeakReference
<Method
>(setHostnameMethod
);
70 * Calls the {@code #setHostname(String)} method of the underlying implementation
71 * of {@link SSLSocket} if exists.
73 * Creates and initializes the single instance of the class when needed
75 * @param hostname The name of the server host of interest.
76 * @param sslSocket Client socket to connect with the server.
78 public static void setServerNameIndication(String hostname
, SSLSocket sslSocket
) {
79 final Method setHostnameMethod
= getMethod(sslSocket
);
80 if (setHostnameMethod
!= null
) {
82 setHostnameMethod
.invoke(sslSocket
, hostname
);
83 Log
.i(TAG
, "SNI done, hostname: " + hostname
);
85 } catch (IllegalArgumentException e
) {
86 Log
.e(TAG
, "Call to SSLSocket#setHost(String) failed ", e
);
88 } catch (IllegalAccessException e
) {
89 Log
.e(TAG
, "Call to SSLSocket#setHost(String) failed ", e
);
91 } catch (InvocationTargetException e
) {
92 Log
.e(TAG
, "Call to SSLSocket#setHost(String) failed ", e
);
95 Log
.i(TAG
, "SNI not supported");
101 * Gets the method to invoke trying to minimize the effective
102 * application of reflection.
104 * @param sslSocket Instance of the SSL socket to use in connection with server.
105 * @return Method to call to indicate the server name of interest to the server.
107 private static Method
getMethod(SSLSocket sslSocket
) {
108 final Class
<?
> sslSocketClass
= sslSocket
.getClass();
109 final ServerNameIndicator instance
= mSingleInstance
.get();
110 if (instance
== null
) {
111 return initFrom(sslSocketClass
);
113 } else if (instance
.mSSLSocketClassRef
.get() != sslSocketClass
) {
114 // the underlying class changed
115 return initFrom(sslSocketClass
);
117 } else if (instance
.mSetHostnameMethodRef
== null
) {
122 final Method cachedSetHostnameMethod
= instance
.mSetHostnameMethodRef
.get();
123 return (cachedSetHostnameMethod
== null
) ?
initFrom(sslSocketClass
) : cachedSetHostnameMethod
;
129 * Singleton initializer.
131 * Uses reflection to extract and 'cache' the method to invoke to indicate the desited host name to the server side.
133 * @param sslSocketClass Underlying class providing the implementation of {@link SSLSocket}.
134 * @return Method to call to indicate the server name of interest to the server.
136 private static Method
initFrom(Class
<?
> sslSocketClass
) {
137 Log
.i(TAG
, "SSLSocket implementation: " + sslSocketClass
.getCanonicalName());
138 Method setHostnameMethod
= null
;
140 setHostnameMethod
= sslSocketClass
.getMethod(METHOD_NAME
, String
.class);
141 } catch (SecurityException e
) {
142 Log
.e(TAG
, "Could not access to SSLSocket#setHostname(String) method ", e
);
144 } catch (NoSuchMethodException e
) {
145 Log
.i(TAG
, "Could not find SSLSocket#setHostname(String) method - SNI not supported");
147 mSingleInstance
.set(new ServerNameIndicator(sslSocketClass
, setHostnameMethod
));
148 return setHostnameMethod
;