PageRenderTime 57ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/nogotofail/clients/android/src/net/nogotofail/RouterSocketClient.java

https://gitlab.com/gaurav1981/nogotofail
Java | 826 lines | 665 code | 70 blank | 91 comment | 82 complexity | 2d9203dce590858fbfde03d85ec57890 MD5 | raw file
Possible License(s): Apache-2.0
  1. /*
  2. * Copyright 2014 Google Inc. All Rights Reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package net.nogotofail;
  17. import android.content.Context;
  18. import android.content.SharedPreferences;
  19. import android.net.ConnectivityManager;
  20. import android.net.NetworkInfo;
  21. import android.os.Handler;
  22. import android.preference.PreferenceManager;
  23. import android.util.Log;
  24. import java.io.BufferedReader;
  25. import java.io.BufferedWriter;
  26. import java.io.EOFException;
  27. import java.io.IOException;
  28. import java.io.InputStreamReader;
  29. import java.io.OutputStreamWriter;
  30. import java.lang.reflect.InvocationTargetException;
  31. import java.net.InetSocketAddress;
  32. import java.net.Proxy;
  33. import java.net.ProxySelector;
  34. import java.net.Socket;
  35. import java.net.SocketAddress;
  36. import java.net.URI;
  37. import java.security.cert.X509Certificate;
  38. import java.util.Collections;
  39. import java.util.List;
  40. import java.util.Random;
  41. import java.util.concurrent.CopyOnWriteArrayList;
  42. import java.util.concurrent.TimeUnit;
  43. import javax.net.ssl.KeyManager;
  44. import javax.net.ssl.SSLContext;
  45. import javax.net.ssl.SSLException;
  46. import javax.net.ssl.SSLSession;
  47. import javax.net.ssl.SSLSocket;
  48. import javax.net.ssl.SSLSocketFactory;
  49. import javax.net.ssl.TrustManager;
  50. /**
  51. * Daemon that, when running, maintains a connection to the MiTM server. The traffic over the
  52. * connection is handled by a {@link ConnectionHandler} provided to the daemon.
  53. *
  54. * <p>The daemon reports its state changes to {@link StateChangeListener} instances registered using
  55. * {@link #addStateChangeListener(StateChangeListener) addStateChangeListener}.
  56. */
  57. public class RouterSocketClient {
  58. /**
  59. * Handler of the traffic over the connection to a MiTM server. Once the handler is done with a
  60. * connection, the connection is closed.
  61. */
  62. public interface ConnectionHandler {
  63. void handleConnection(Socket socket) throws IOException;
  64. }
  65. /**
  66. * Receiver of state change events.
  67. */
  68. public interface StateChangeListener {
  69. void onStarted();
  70. void onConnecting(InetSocketAddress address);
  71. void onConnected(InetSocketAddress address);
  72. void onWaitingToRetry(String cause, long delayMillis);
  73. void onStopped();
  74. void onUnknownCertificate(X509Certificate certificate);
  75. }
  76. private static final String TAG = RouterSocketClient.class.getSimpleName();
  77. private static final long MIN_DELAY_MILLIS = TimeUnit.SECONDS.toMillis(2);
  78. private static final long MAX_DELAY_MILLIS = TimeUnit.MINUTES.toMillis(30);
  79. private static final int SERVER_CONNECT_TIMEOUT_MILLIS = 20000;
  80. private static final int HTTP_PROXY_CONNECT_TIMEOUT_MILLIS = 5000;
  81. private static final int TLS_HANDSHAKE_TIMEOUT_MILLIS = 20000;
  82. private static enum ConnectionState {
  83. DISCONNECTED,
  84. STARTING,
  85. RUNNING,
  86. STOPPING,
  87. STOPPING_RESTART_REQUESTED,
  88. }
  89. private final Object mLock = new Object();
  90. private final ConnectionHandler mConnectionHandler;
  91. private final ConnectivityManager mConnectivityManager;
  92. private final Random mRng;
  93. private final SSLSocketFactory mSSLSocketFactory;
  94. private enum StateChangeEventType {
  95. STARTED,
  96. CONNECTING,
  97. CONNECTED,
  98. WAITING_TO_RETRY,
  99. STOPPED,
  100. UNKNOWN_CERTIFICATE,
  101. }
  102. private static class StateChangeEvent {
  103. private final StateChangeEventType mType;
  104. private final InetSocketAddress mAddress;
  105. private final String mCause;
  106. private final long mDelayMillis;
  107. private final X509Certificate mCertificate;
  108. private StateChangeEvent(StateChangeEventType type) {
  109. mType = type;
  110. mAddress = null;
  111. mCause = null;
  112. mDelayMillis = 0;
  113. mCertificate = null;
  114. }
  115. private StateChangeEvent(StateChangeEventType type, InetSocketAddress address) {
  116. mType = type;
  117. mAddress = address;
  118. mCause = null;
  119. mDelayMillis = 0;
  120. mCertificate = null;
  121. }
  122. private StateChangeEvent(StateChangeEventType type, String cause, long delayMillis) {
  123. mType = type;
  124. mAddress = null;
  125. mCause = cause;
  126. mDelayMillis = delayMillis;
  127. mCertificate = null;
  128. }
  129. private StateChangeEvent(StateChangeEventType type, X509Certificate certificate) {
  130. mType = type;
  131. mAddress = null;
  132. mCause = null;
  133. mDelayMillis = 0;
  134. mCertificate = certificate;
  135. }
  136. }
  137. /** @GuardedBy {@link #mLock} */
  138. private StateChangeEvent mLastStateChangeEvent =
  139. new StateChangeEvent(StateChangeEventType.STOPPED);
  140. private final CopyOnWriteArrayList<StateChangeListener> mStateChangeListeners =
  141. new CopyOnWriteArrayList<StateChangeListener>();
  142. private final Context mContext;
  143. private final Handler mDelayedRestartHandler;
  144. private final PreferencesBackedPinningX509TrustManager mTrustManager;
  145. /**
  146. * Listener invoked whenever preferences change.
  147. *
  148. * <p><em>NOTE:</em> {@code SharedPreferences} uses a {@code WeakHashMap} to store its listeners.
  149. * Thus, listeners may be garbage-collected unless they are hard-referenced from elsewhere.
  150. * Referencing the listener from a final field of this instance creates a hard-reference for the
  151. * whole lifetime of this instance. */
  152. private final SharedPreferences.OnSharedPreferenceChangeListener mPreferencesChangeListener;
  153. /** @GuardedBy {@link #mLock} */
  154. private ConnectionState mConnectionState = ConnectionState.DISCONNECTED;
  155. /** @GuardedBy {@link #mLock} */
  156. private Thread mThread;
  157. /** @GuardedBy {@link #mLock} */
  158. private Socket mSocket;
  159. /** @GuardedBy {@link #mLock} */
  160. private long mNextDelayMillis = 0;
  161. /** @GuardedBy {@link #mLock} */
  162. private Runnable mDelayedRestartRunnable;
  163. public RouterSocketClient(
  164. Context context,
  165. ConnectivityManager connectivityManager,
  166. ConnectionHandler connectionHandler,
  167. Random rng) {
  168. mContext = context;
  169. mDelayedRestartHandler = new Handler(mContext.getMainLooper());
  170. mConnectivityManager = connectivityManager;
  171. mConnectionHandler = connectionHandler;
  172. mRng = rng;
  173. mTrustManager = new PreferencesBackedPinningX509TrustManager(mContext);
  174. try {
  175. SSLContext sslContext = SSLContext.getInstance("TLS");
  176. sslContext.init(
  177. new KeyManager[0], // No need for client or server keys
  178. new TrustManager[] {
  179. mTrustManager
  180. },
  181. null // use default RNG source
  182. );
  183. mSSLSocketFactory = sslContext.getSocketFactory();
  184. } catch (Exception e) {
  185. throw new RuntimeException("Failed to initialize SSLSocketFactory", e);
  186. }
  187. // Listen for changes in SharedPreferences that may require reconnecting
  188. mPreferencesChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
  189. @Override
  190. public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
  191. Log.d(TAG, "onSharedPreferenceChanged(\"" + key + "\")");
  192. if ((AttacksPreferenceFragment.isReconnectRequiredToApplyPreference(mContext, key))
  193. || (AdvancedPreferenceFragment.isReconnectRequiredToApplyPreference(mContext, key))) {
  194. // Multiple settings may change roughly at the same time. To avoid restarting multiple
  195. // times in quick succession, we schedule delayed restarts. Because of the way the
  196. // scheduling method below works, this results in only one restart request albeit with
  197. // after a delay.
  198. scheduleDelayedRestart("Setting changed: " + key);
  199. }
  200. }
  201. };
  202. SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
  203. sharedPreferences.registerOnSharedPreferenceChangeListener(mPreferencesChangeListener);
  204. }
  205. /**
  206. * Registers the provided listener and immediately notifies it about the most recent event
  207. * (if any).
  208. */
  209. public void addStateChangeListener(StateChangeListener listener) {
  210. StateChangeEvent lastEvent;
  211. synchronized (mLock) {
  212. lastEvent = mLastStateChangeEvent;
  213. }
  214. if (lastEvent != null) {
  215. notifyEvent(listener, lastEvent);
  216. }
  217. mStateChangeListeners.add(listener);
  218. }
  219. public void removeStateChangeListener(StateChangeListener listener) {
  220. mStateChangeListeners.remove(listener);
  221. }
  222. /**
  223. * Invoked when the data connectivity state may have changed.
  224. *
  225. * @param activeNetworkInfo information about the active data network, or {@code null} if no
  226. * data network is available.
  227. */
  228. void onDataConnectivityMayHaveChanged(NetworkInfo activeNetworkInfo) {
  229. if (activeNetworkInfo == null) {
  230. Log.i(TAG, "No data connectivity");
  231. } else {
  232. Log.i(TAG, "Data connectivity may have changed."
  233. + " Active network type: " + activeNetworkInfo.getTypeName()
  234. + ", subtype: " + activeNetworkInfo.getSubtypeName()
  235. + ", state: " + activeNetworkInfo.getState());
  236. }
  237. if ((activeNetworkInfo != null) && (activeNetworkInfo.isConnected())) {
  238. resetRetryState();
  239. start();
  240. } else {
  241. stop();
  242. }
  243. }
  244. public void onServiceStarted() {
  245. Log.i(TAG, "onServiceStarted()");
  246. resetRetryState();
  247. start();
  248. }
  249. public void onServiceStopped() {
  250. Log.i(TAG, "onServiceStopped");
  251. stop();
  252. }
  253. private void setConnectionState(ConnectionState state) {
  254. synchronized (mLock) {
  255. if (mConnectionState != state) {
  256. Log.d(TAG, mConnectionState + " -> " + state);
  257. mConnectionState = state;
  258. }
  259. }
  260. }
  261. /**
  262. * Schedules a delayed restart request. If the method is invoked multiple times in succession,
  263. * only the most recent request is honored.
  264. */
  265. private void scheduleDelayedRestart(String reason) {
  266. Log.d(TAG, "Scheduling a delayed restart. Reason: " + reason);
  267. Runnable runnable = new Runnable() {
  268. @Override
  269. public void run() {
  270. synchronized (mLock) {
  271. if (mDelayedRestartRunnable != this) {
  272. // Another Runnable replaced this one
  273. return;
  274. }
  275. mDelayedRestartRunnable = null;
  276. }
  277. restart();
  278. }
  279. };
  280. synchronized (mLock) {
  281. if (mDelayedRestartRunnable != null) {
  282. mDelayedRestartHandler.removeCallbacks(mDelayedRestartRunnable);
  283. }
  284. mDelayedRestartRunnable = runnable;
  285. mDelayedRestartHandler.postDelayed(runnable, 1000);
  286. }
  287. }
  288. public void restart() {
  289. Log.d(TAG, "restart()");
  290. stop();
  291. resetRetryState();
  292. start();
  293. }
  294. public void start() {
  295. Log.d(TAG, "start()");
  296. synchronized (mLock) {
  297. switch (mConnectionState) {
  298. case DISCONNECTED:
  299. setConnectionState(ConnectionState.STARTING);
  300. Log.i(TAG, "Starting...");
  301. Thread thread = new Thread(new Runnable() {
  302. @Override
  303. public void run() {
  304. blockingRun();
  305. }
  306. });
  307. mThread = thread;
  308. thread.start();
  309. break;
  310. case STARTING:
  311. case RUNNING:
  312. break;
  313. case STOPPING:
  314. case STOPPING_RESTART_REQUESTED:
  315. setConnectionState(ConnectionState.STOPPING_RESTART_REQUESTED);
  316. break;
  317. default:
  318. throw new IllegalStateException(String.valueOf(mConnectionState));
  319. }
  320. }
  321. }
  322. public void stop() {
  323. Log.d(TAG, "stop()");
  324. final Socket socket;
  325. Thread thread;
  326. synchronized (mLock) {
  327. switch (mConnectionState) {
  328. case STARTING:
  329. case RUNNING:
  330. case STOPPING:
  331. case STOPPING_RESTART_REQUESTED:
  332. setConnectionState(ConnectionState.STOPPING);
  333. break;
  334. case DISCONNECTED:
  335. return;
  336. default:
  337. throw new IllegalStateException(String.valueOf(mConnectionState));
  338. }
  339. socket = mSocket;
  340. mSocket = null;
  341. thread = mThread;
  342. mThread = null;
  343. }
  344. if (socket != null) {
  345. new Thread(new Runnable() {
  346. @Override
  347. public void run() {
  348. Closeables.closeQuietly(socket);
  349. }
  350. }).start();
  351. }
  352. if (thread != null) {
  353. Log.d(TAG, "Interrupting background thread...");
  354. thread.interrupt();
  355. }
  356. }
  357. public void close() {
  358. Log.d(TAG, "close()");
  359. stop();
  360. }
  361. private void blockingRun() {
  362. Log.i(TAG, "Running");
  363. notifyStarted();
  364. try {
  365. while (true) {
  366. switch (mConnectionState) {
  367. case STARTING:
  368. case RUNNING:
  369. setConnectionState(ConnectionState.RUNNING);
  370. break;
  371. case STOPPING:
  372. case STOPPING_RESTART_REQUESTED:
  373. case DISCONNECTED:
  374. return;
  375. default:
  376. throw new IllegalStateException(String.valueOf(mConnectionState));
  377. }
  378. NetworkInfo networkInfo = mConnectivityManager.getActiveNetworkInfo();
  379. if ((networkInfo == null) || (!networkInfo.isConnected())) {
  380. Log.w(TAG, "No data connectivity. Stopping");
  381. return;
  382. }
  383. Socket socket = null;
  384. try {
  385. synchronized (mLock) {
  386. switch (mConnectionState) {
  387. case RUNNING:
  388. break;
  389. case STARTING:
  390. case STOPPING:
  391. case STOPPING_RESTART_REQUESTED:
  392. case DISCONNECTED:
  393. return;
  394. default:
  395. throw new IllegalStateException(String.valueOf(mConnectionState));
  396. }
  397. }
  398. String serverHost = AdvancedPreferenceFragment.getMitmServerHost(mContext);
  399. int serverPort = AdvancedPreferenceFragment.getMitmServerPort(mContext);
  400. InetSocketAddress serverAddress =
  401. InetSocketAddress.createUnresolved(serverHost, serverPort);
  402. Log.i(TAG, "Connecting to " + serverAddress);
  403. notifyConnecting(serverAddress);
  404. socket = connectSocket(serverHost, serverPort);
  405. Log.i(TAG, "Connected to " + serverAddress + ". Upgrading to TLS");
  406. SSLSocket sslSocket =
  407. (SSLSocket) mSSLSocketFactory.createSocket(socket, serverHost, serverPort, true);
  408. tryEnableSni(sslSocket, serverHost);
  409. tryEnableSessionTickets(sslSocket);
  410. socket = sslSocket;
  411. sslSocket.setSoTimeout(TLS_HANDSHAKE_TIMEOUT_MILLIS);
  412. sslSocket.setUseClientMode(true);
  413. sslSocket.startHandshake();
  414. SSLSession sslSession = sslSocket.getSession();
  415. if (!sslSession.isValid()) {
  416. throw new SSLException("TLS/SSL handshake failed");
  417. }
  418. // No need to verify that server certificate matches the hostname to which we're
  419. // connecting because of the current trust model where the user is prompted whether to
  420. // accept an unknown server certificate based on its public key.
  421. Log.i(TAG, "Upgraded to " + sslSession.getProtocol() + " " + sslSession.getCipherSuite());
  422. Socket oldSocket;
  423. synchronized (mLock) {
  424. switch (mConnectionState) {
  425. case RUNNING:
  426. oldSocket = mSocket;
  427. mSocket = socket;
  428. break;
  429. default:
  430. return;
  431. }
  432. }
  433. Closeables.closeQuietly(oldSocket);
  434. notifyConnected(serverAddress);
  435. resetRetryState();
  436. mConnectionHandler.handleConnection(socket);
  437. } catch (IOException e) {
  438. Closeables.closeQuietly(socket);
  439. socket = null;
  440. synchronized (mLock) {
  441. if (mConnectionState != ConnectionState.RUNNING) {
  442. Log.w(TAG, "Failed to communicate with server -- shutting down", e);
  443. return;
  444. } else {
  445. Log.w(TAG, "Failed to communicate with server", e);
  446. }
  447. }
  448. // Wait (with exponential backoff) and retry
  449. long delayMillis = getAndIncrementRetryDelay();
  450. Throwable cause = e.getCause();
  451. // Notify if the connection failed because of an unknown certificate
  452. if (cause != null
  453. && cause instanceof PreferencesBackedPinningX509TrustManager
  454. .UnknownCertificateException) {
  455. X509Certificate certificate = ((PreferencesBackedPinningX509TrustManager
  456. .UnknownCertificateException) cause).certificate;
  457. notifyUnknownCertificate(certificate);
  458. }
  459. Log.d(TAG, "Sleeping for " + (delayMillis / 1000) + " seconds before retrying");
  460. notifyWaitingToRetry(e.getMessage(), delayMillis);
  461. try {
  462. Thread.sleep(delayMillis);
  463. } catch (InterruptedException e2) {
  464. synchronized (mLock) {
  465. if (mConnectionState != ConnectionState.RUNNING) {
  466. return;
  467. }
  468. }
  469. continue;
  470. }
  471. } finally {
  472. Closeables.closeQuietly(socket);
  473. }
  474. }
  475. } finally {
  476. Log.i(TAG, "Stopped");
  477. synchronized (mLock) {
  478. switch (mConnectionState) {
  479. case STOPPING_RESTART_REQUESTED:
  480. setConnectionState(ConnectionState.DISCONNECTED);
  481. start();
  482. break;
  483. case RUNNING:
  484. case STARTING:
  485. case DISCONNECTED:
  486. case STOPPING:
  487. setConnectionState(ConnectionState.DISCONNECTED);
  488. break;
  489. default:
  490. throw new IllegalStateException(String.valueOf(mConnectionState));
  491. }
  492. }
  493. notifyStopped();
  494. }
  495. }
  496. private static Socket connectSocket(String host, int port) throws IOException {
  497. // If HTTPS proxy is set, connect through that proxy instead of connecting directly.
  498. URI serverUri = URI.create("https://" + host + ":" + port);
  499. ProxySelector proxySelector = ProxySelector.getDefault();
  500. List<Proxy> proxies = proxySelector.select(serverUri);
  501. if ((proxies == null) || (proxies.isEmpty())) {
  502. proxies = Collections.singletonList(Proxy.NO_PROXY);
  503. }
  504. IOException lastFailure = null;
  505. for (Proxy proxy : proxies) {
  506. SocketAddress proxyAddress = proxy.address();
  507. try {
  508. if (Proxy.NO_PROXY.equals(proxy)) {
  509. // Direct connection
  510. return connectSocketNoProxy(host, port);
  511. } else if (proxy.type() == Proxy.Type.HTTP) {
  512. // HTTP proxy CONNECT
  513. return connectSocketViaHttpProxyConnectMethod(host, port, proxyAddress);
  514. } else {
  515. // Unsupported proxy type -- ignore
  516. }
  517. } catch (IOException e) {
  518. lastFailure = e;
  519. if (proxyAddress != null) {
  520. proxySelector.connectFailed(serverUri, proxyAddress, e);
  521. }
  522. }
  523. }
  524. if (lastFailure != null) {
  525. throw lastFailure;
  526. }
  527. throw new IOException(
  528. "No suitable connection methods found for " + serverUri + ": " + proxies);
  529. }
  530. /**
  531. * Connects to the specified destination directly, without going through any non-transparent
  532. * proxies.
  533. */
  534. private static Socket connectSocketNoProxy(String host, int port) throws IOException {
  535. Log.d(TAG, "Connecting to " + host + ":" + port + " directly");
  536. Socket socket = new Socket();
  537. boolean success = false;
  538. try {
  539. SocketAddress address = new InetSocketAddress(host, port);
  540. socket = new Socket();
  541. socket.connect(address, SERVER_CONNECT_TIMEOUT_MILLIS);
  542. success = true;
  543. return socket;
  544. } finally {
  545. if (!success) {
  546. Closeables.closeQuietly(socket);
  547. }
  548. }
  549. }
  550. /**
  551. * Connects to the specified destination using the {@code HTTP CONNECT} method of the specified
  552. * HTTP proxy.
  553. */
  554. private static Socket connectSocketViaHttpProxyConnectMethod(
  555. String host, int port, SocketAddress proxyAddress) throws IOException {
  556. Log.d(TAG, "Connecting to " + host + ":" + port + " via HTTP proxy " + proxyAddress);
  557. Socket socket = new Socket();
  558. boolean success = false;
  559. try {
  560. // The proxyAddress might not yet been resolved -- resolve it if necessary.
  561. InetSocketAddress proxyInetAddress = (InetSocketAddress) proxyAddress;
  562. if (proxyInetAddress.isUnresolved()) {
  563. proxyInetAddress = new InetSocketAddress(
  564. proxyInetAddress.getHostName(),
  565. proxyInetAddress.getPort());
  566. }
  567. socket = new Socket();
  568. socket.connect(proxyInetAddress, HTTP_PROXY_CONNECT_TIMEOUT_MILLIS);
  569. BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
  570. socket.getOutputStream(), "US-ASCII"));
  571. out.write("CONNECT " + host + ":" + port + " HTTP/1.1\r\n");
  572. out.write("Host: " + host + ":" + port + "\r\n");
  573. out.write("\r\n");
  574. out.flush();
  575. // Buffered reading below assumes that the protocol which will run after HTTP CONNECT
  576. // is such that the server does not transmit anything until the client transmits something.
  577. // Otherwise we might over-read here.
  578. BufferedReader in = new BufferedReader(new InputStreamReader(
  579. socket.getInputStream(), "US-ASCII"));
  580. String line;
  581. boolean statusLineRead = false;
  582. boolean responseHeadersRead = false;
  583. socket.setSoTimeout(SERVER_CONNECT_TIMEOUT_MILLIS);
  584. while ((line = in.readLine()) != null) {
  585. if (!statusLineRead) {
  586. String[] tokens = line.split("\\s+", 3);
  587. if (tokens.length != 3) {
  588. throw new IOException("Unexpected reply from HTTP proxy: " + line);
  589. }
  590. String httpVersion = tokens[0];
  591. String statusCodeString = tokens[1];
  592. String reason = tokens[2];
  593. if (!httpVersion.startsWith("HTTP/1.")) {
  594. throw new IOException("Unsupported HTTP version in HTTP proxy response: " + line);
  595. }
  596. if (!"200".equals(statusCodeString)) {
  597. throw new IOException(
  598. "HTTP proxy CONNECT failed. Status: " + statusCodeString + ", reason: " + reason);
  599. }
  600. statusLineRead = true;
  601. continue;
  602. }
  603. if (line.length() == 0) {
  604. responseHeadersRead = true;
  605. break;
  606. }
  607. }
  608. if (!statusLineRead) {
  609. throw new EOFException("Empty response from HTTP proxy");
  610. }
  611. if (!responseHeadersRead) {
  612. throw new EOFException("Premature end of stream while reading HTTP proxy response");
  613. }
  614. success = true;
  615. return socket;
  616. } finally {
  617. if (!success) {
  618. Closeables.closeQuietly(socket);
  619. }
  620. }
  621. }
  622. private void resetRetryState() {
  623. synchronized (mLock) {
  624. mNextDelayMillis = 0;
  625. }
  626. }
  627. private long getAndIncrementRetryDelay() {
  628. // IMPLEMENTATION NOTE: Randomized exponential backoff works by doubling a non-randomized delay
  629. // (mean) around which randomization (half a mean in each direction) is applied.
  630. long mean;
  631. synchronized (mLock) {
  632. mean = mNextDelayMillis;
  633. if (mean == 0) {
  634. mean = MIN_DELAY_MILLIS;
  635. } else {
  636. mean *= 2;
  637. if (mNextDelayMillis > MAX_DELAY_MILLIS) {
  638. mean = MAX_DELAY_MILLIS;
  639. }
  640. }
  641. mNextDelayMillis = mean;
  642. }
  643. // Return a random value (uniform distribution) from
  644. // mean / 2 + [-mean / 2; +mean / 2).
  645. return (long) ((mean / 2) + (mRng.nextDouble() * mean));
  646. }
  647. private void notifyStarted() {
  648. synchronized (mLock) {
  649. mLastStateChangeEvent = new StateChangeEvent(StateChangeEventType.STARTED);
  650. }
  651. for (StateChangeListener listener : mStateChangeListeners) {
  652. listener.onStarted();
  653. }
  654. }
  655. private void notifyStopped() {
  656. synchronized (mLock) {
  657. mLastStateChangeEvent = new StateChangeEvent(StateChangeEventType.STOPPED);
  658. }
  659. for (StateChangeListener listener : mStateChangeListeners) {
  660. listener.onStopped();
  661. }
  662. }
  663. private void notifyWaitingToRetry(String cause, long delayMillis) {
  664. synchronized (mLock) {
  665. mLastStateChangeEvent =
  666. new StateChangeEvent(StateChangeEventType.WAITING_TO_RETRY, cause, delayMillis);
  667. }
  668. for (StateChangeListener listener : mStateChangeListeners) {
  669. listener.onWaitingToRetry(cause, delayMillis);
  670. }
  671. }
  672. private void notifyConnecting(InetSocketAddress address) {
  673. synchronized (mLock) {
  674. mLastStateChangeEvent = new StateChangeEvent(StateChangeEventType.CONNECTING, address);
  675. }
  676. for (StateChangeListener listener : mStateChangeListeners) {
  677. listener.onConnecting(address);
  678. }
  679. }
  680. private void notifyConnected(InetSocketAddress address) {
  681. synchronized (mLock) {
  682. mLastStateChangeEvent = new StateChangeEvent(StateChangeEventType.CONNECTED, address);
  683. }
  684. for (StateChangeListener listener : mStateChangeListeners) {
  685. listener.onConnected(address);
  686. }
  687. }
  688. private void notifyUnknownCertificate(X509Certificate certificate) {
  689. synchronized (mLock) {
  690. mLastStateChangeEvent = new StateChangeEvent(StateChangeEventType.UNKNOWN_CERTIFICATE,
  691. certificate);
  692. }
  693. for (StateChangeListener listener : mStateChangeListeners) {
  694. listener.onUnknownCertificate(certificate);
  695. }
  696. }
  697. private void notifyEvent(StateChangeListener listener, StateChangeEvent event) {
  698. if ((listener == null) || (event == null)) {
  699. return;
  700. }
  701. switch (event.mType) {
  702. case STARTED:
  703. listener.onStarted();
  704. break;
  705. case STOPPED:
  706. listener.onStopped();
  707. break;
  708. case CONNECTING:
  709. listener.onConnecting(event.mAddress);
  710. break;
  711. case CONNECTED:
  712. listener.onConnected(event.mAddress);
  713. break;
  714. case WAITING_TO_RETRY:
  715. listener.onWaitingToRetry(event.mCause, event.mDelayMillis);
  716. break;
  717. case UNKNOWN_CERTIFICATE:
  718. listener.onUnknownCertificate(event.mCertificate);
  719. break;
  720. default:
  721. throw new IllegalArgumentException("Unknown event type: " + event.mType);
  722. }
  723. }
  724. public void whitelistCertificate(X509Certificate certificate) {
  725. mTrustManager.whitelist(certificate.getPublicKey());
  726. }
  727. public void blacklistCertificate(X509Certificate certificate) {
  728. mTrustManager.blacklist(certificate.getPublicKey());
  729. }
  730. private static void tryEnableSni(SSLSocket socket, String hostname) throws IOException {
  731. try {
  732. socket.getClass().getMethod("setHostname", String.class).invoke(socket, hostname);
  733. } catch (NoSuchMethodException e) {
  734. // setHostname method does not exist
  735. e.printStackTrace();
  736. } catch (IllegalAccessException e) {
  737. // setHostname method is not public or not accessible via Reflection API
  738. e.printStackTrace();
  739. } catch (InvocationTargetException e) {
  740. throw new IOException("Failed to enable SNI via Reflection API", e);
  741. }
  742. }
  743. private static void tryEnableSessionTickets(SSLSocket socket) throws IOException {
  744. try {
  745. socket.getClass().getMethod("setUseSessionTickets", boolean.class).invoke(socket, true);
  746. } catch (NoSuchMethodException e) {
  747. // setUseSessionTickets method does not exist
  748. e.printStackTrace();
  749. } catch (IllegalAccessException e) {
  750. // setUseSessionTickets method is not public or not accessible via Reflection API
  751. e.printStackTrace();
  752. } catch (InvocationTargetException e) {
  753. throw new IOException("Failed to enable session tickets via Reflection API", e);
  754. }
  755. }
  756. }