PsiphonTunnel.java 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969
  1. /*
  2. * Copyright (c) 2015, Psiphon Inc.
  3. * All rights reserved.
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. package ca.psiphon;
  20. import android.annotation.TargetApi;
  21. import android.content.Context;
  22. import android.net.ConnectivityManager;
  23. import android.net.wifi.WifiManager;
  24. import android.net.wifi.WifiInfo;
  25. import android.net.LinkProperties;
  26. import android.net.NetworkInfo;
  27. import android.net.VpnService;
  28. import android.os.Build;
  29. import android.os.ParcelFileDescriptor;
  30. import android.telephony.TelephonyManager;
  31. import android.util.Base64;
  32. import org.apache.http.conn.util.InetAddressUtils;
  33. import org.json.JSONArray;
  34. import org.json.JSONException;
  35. import org.json.JSONObject;
  36. import java.io.File;
  37. import java.io.FileInputStream;
  38. import java.io.FileOutputStream;
  39. import java.io.IOException;
  40. import java.io.PrintStream;
  41. import java.lang.reflect.InvocationTargetException;
  42. import java.lang.reflect.Method;
  43. import java.net.InetAddress;
  44. import java.net.NetworkInterface;
  45. import java.net.SocketException;
  46. import java.security.KeyStore;
  47. import java.security.KeyStoreException;
  48. import java.security.NoSuchAlgorithmException;
  49. import java.security.cert.CertificateException;
  50. import java.security.cert.X509Certificate;
  51. import java.util.ArrayList;
  52. import java.util.Collection;
  53. import java.util.Collections;
  54. import java.util.Enumeration;
  55. import java.util.HashMap;
  56. import java.util.List;
  57. import java.util.Locale;
  58. import java.util.Map;
  59. import java.util.concurrent.atomic.AtomicBoolean;
  60. import java.util.concurrent.atomic.AtomicInteger;
  61. import java.util.concurrent.atomic.AtomicReference;
  62. import go.psi.Psi;
  63. public class PsiphonTunnel extends Psi.PsiphonProvider.Stub {
  64. public interface HostService {
  65. public String getAppName();
  66. public Context getContext();
  67. public Object getVpnService(); // Object must be a VpnService (Android < 4 cannot reference this class name)
  68. public Object newVpnServiceBuilder(); // Object must be a VpnService.Builder (Android < 4 cannot reference this class name)
  69. public String getPsiphonConfig();
  70. public void onDiagnosticMessage(String message);
  71. public void onAvailableEgressRegions(List<String> regions);
  72. public void onSocksProxyPortInUse(int port);
  73. public void onHttpProxyPortInUse(int port);
  74. public void onListeningSocksProxyPort(int port);
  75. public void onListeningHttpProxyPort(int port);
  76. public void onUpstreamProxyError(String message);
  77. public void onConnecting();
  78. public void onConnected();
  79. public void onHomepage(String url);
  80. public void onClientRegion(String region);
  81. public void onClientUpgradeDownloaded(String filename);
  82. public void onClientIsLatestVersion();
  83. public void onSplitTunnelRegion(String region);
  84. public void onUntunneledAddress(String address);
  85. public void onBytesTransferred(long sent, long received);
  86. public void onStartedWaitingForNetworkConnectivity();
  87. public void onClientVerificationRequired(String serverNonce, int ttlSeconds, boolean resetCache);
  88. public void onActiveAuthorizationIDs(List<String> authorizations);
  89. public void onExiting();
  90. }
  91. private final HostService mHostService;
  92. private AtomicBoolean mVpnMode;
  93. private PrivateAddress mPrivateAddress;
  94. private AtomicReference<ParcelFileDescriptor> mTunFd;
  95. private AtomicInteger mLocalSocksProxyPort;
  96. private AtomicBoolean mRoutingThroughTunnel;
  97. private Thread mTun2SocksThread;
  98. private AtomicBoolean mIsWaitingForNetworkConnectivity;
  99. // mUsePacketTunnel specifies whether to use the packet
  100. // tunnel instead of tun2socks; currently this is for
  101. // testing only and is disabled.
  102. private boolean mUsePacketTunnel = false;
  103. // Only one PsiphonVpn instance may exist at a time, as the underlying
  104. // go.psi.Psi and tun2socks implementations each contain global state.
  105. private static PsiphonTunnel mPsiphonTunnel;
  106. public static synchronized PsiphonTunnel newPsiphonTunnel(HostService hostService) {
  107. if (mPsiphonTunnel != null) {
  108. mPsiphonTunnel.stop();
  109. }
  110. // Load the native go code embedded in psi.aar
  111. System.loadLibrary("gojni");
  112. mPsiphonTunnel = new PsiphonTunnel(hostService);
  113. return mPsiphonTunnel;
  114. }
  115. private PsiphonTunnel(HostService hostService) {
  116. mHostService = hostService;
  117. mVpnMode = new AtomicBoolean(false);
  118. mTunFd = new AtomicReference<ParcelFileDescriptor>();
  119. mLocalSocksProxyPort = new AtomicInteger(0);
  120. mRoutingThroughTunnel = new AtomicBoolean(false);
  121. mIsWaitingForNetworkConnectivity = new AtomicBoolean(false);
  122. }
  123. public Object clone() throws CloneNotSupportedException {
  124. throw new CloneNotSupportedException();
  125. }
  126. //----------------------------------------------------------------------------------------------
  127. // Public API
  128. //----------------------------------------------------------------------------------------------
  129. // To start, call in sequence: startRouting(), then startTunneling(). After startRouting()
  130. // succeeds, the caller must call stop() to clean up. These functions should not be called
  131. // concurrently. Do not call stop() while startRouting() or startTunneling() is in progress.
  132. // Returns true when the VPN routing is established; returns false if the VPN could not
  133. // be started due to lack of prepare or revoked permissions (called should re-prepare and
  134. // try again); throws exception for other error conditions.
  135. public synchronized boolean startRouting() throws Exception {
  136. // Note: tun2socks is loaded even in mUsePacketTunnel mode,
  137. // as disableUdpGwKeepalive will still be called.
  138. // Load tun2socks library embedded in the aar
  139. // If this method is called more than once with the same library name, the second and subsequent calls are ignored.
  140. // http://docs.oracle.com/javase/7/docs/api/java/lang/Runtime.html#loadLibrary%28java.lang.String%29
  141. System.loadLibrary("tun2socks");
  142. return startVpn();
  143. }
  144. // Throws an exception in error conditions. In the case of an exception, the routing
  145. // started by startRouting() is not immediately torn down (this allows the caller to control
  146. // exactly when VPN routing is stopped); caller should call stop() to clean up.
  147. public synchronized void startTunneling(String embeddedServerEntries) throws Exception {
  148. startPsiphon(embeddedServerEntries);
  149. }
  150. // Note: to avoid deadlock, do not call directly from a HostService callback;
  151. // instead post to a Handler if necessary to trigger from a HostService callback.
  152. // For example, deadlock can occur when a Notice callback invokes stop() since stop() calls
  153. // Psi.Stop() which will block waiting for tunnel-core Controller to shutdown which in turn
  154. // waits for Notice callback invoker to stop, meanwhile the callback thread has blocked waiting
  155. // for stop().
  156. public synchronized void stop() {
  157. stopVpn();
  158. stopPsiphon();
  159. mVpnMode.set(false);
  160. mLocalSocksProxyPort.set(0);
  161. }
  162. // Note: same deadlock note as stop().
  163. public synchronized void restartPsiphon() throws Exception {
  164. stopPsiphon();
  165. startPsiphon("");
  166. }
  167. // Call through to tunnel-core Controller.SetClientVerificationPayload. See description in
  168. // Controller.SetClientVerificationPayload.
  169. // Note: same deadlock note as stop().
  170. // Note: this function only has an effect after Psi.Start() and before Psi.Stop(),
  171. // so call it after startTunneling() and before stop().
  172. public synchronized void setClientVerificationPayload (String requestPayload) {
  173. Psi.SetClientVerificationPayload(requestPayload);
  174. }
  175. //----------------------------------------------------------------------------------------------
  176. // VPN Routing
  177. //----------------------------------------------------------------------------------------------
  178. private final static String VPN_INTERFACE_NETMASK = "255.255.255.0";
  179. private final static int VPN_INTERFACE_MTU = 1500;
  180. private final static int UDPGW_SERVER_PORT = 7300;
  181. private final static String DEFAULT_PRIMARY_DNS_SERVER = "8.8.4.4";
  182. private final static String DEFAULT_SECONDARY_DNS_SERVER = "8.8.8.8";
  183. // Note: Atomic variables used for getting/setting local proxy port, routing flag, and
  184. // tun fd, as these functions may be called via PsiphonProvider callbacks. Do not use
  185. // synchronized functions as stop() is synchronized and a deadlock is possible as callbacks
  186. // can be called while stop holds the lock.
  187. @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
  188. private boolean startVpn() throws Exception {
  189. mVpnMode.set(true);
  190. mPrivateAddress = selectPrivateAddress();
  191. Locale previousLocale = Locale.getDefault();
  192. final String errorMessage = "startVpn failed";
  193. try {
  194. // Workaround for https://code.google.com/p/android/issues/detail?id=61096
  195. Locale.setDefault(new Locale("en"));
  196. int mtu = VPN_INTERFACE_MTU;
  197. String dnsResolver = mPrivateAddress.mRouter;
  198. if (mUsePacketTunnel) {
  199. mtu = (int)Psi.GetPacketTunnelMTU();
  200. dnsResolver = Psi.GetPacketTunnelDNSResolverIPv4Address();
  201. }
  202. ParcelFileDescriptor tunFd =
  203. ((VpnService.Builder) mHostService.newVpnServiceBuilder())
  204. .setSession(mHostService.getAppName())
  205. .setMtu(mtu)
  206. .addAddress(mPrivateAddress.mIpAddress, mPrivateAddress.mPrefixLength)
  207. .addRoute("0.0.0.0", 0)
  208. .addRoute(mPrivateAddress.mSubnet, mPrivateAddress.mPrefixLength)
  209. .addDnsServer(dnsResolver)
  210. .establish();
  211. if (tunFd == null) {
  212. // As per http://developer.android.com/reference/android/net/VpnService.Builder.html#establish%28%29,
  213. // this application is no longer prepared or was revoked.
  214. return false;
  215. }
  216. mTunFd.set(tunFd);
  217. mRoutingThroughTunnel.set(false);
  218. mHostService.onDiagnosticMessage("VPN established");
  219. } catch(IllegalArgumentException e) {
  220. throw new Exception(errorMessage, e);
  221. } catch(IllegalStateException e) {
  222. throw new Exception(errorMessage, e);
  223. } catch(SecurityException e) {
  224. throw new Exception(errorMessage, e);
  225. } finally {
  226. // Restore the original locale.
  227. Locale.setDefault(previousLocale);
  228. }
  229. return true;
  230. }
  231. private boolean isVpnMode() {
  232. return mVpnMode.get();
  233. }
  234. private void setLocalSocksProxyPort(int port) {
  235. mLocalSocksProxyPort.set(port);
  236. }
  237. private void routeThroughTunnel() {
  238. if (!mRoutingThroughTunnel.compareAndSet(false, true)) {
  239. return;
  240. }
  241. if (!mUsePacketTunnel) {
  242. ParcelFileDescriptor tunFd = mTunFd.getAndSet(null);
  243. if (tunFd == null) {
  244. return;
  245. }
  246. String socksServerAddress = "127.0.0.1:" + Integer.toString(mLocalSocksProxyPort.get());
  247. String udpgwServerAddress = "127.0.0.1:" + Integer.toString(UDPGW_SERVER_PORT);
  248. startTun2Socks(
  249. tunFd,
  250. VPN_INTERFACE_MTU,
  251. mPrivateAddress.mRouter,
  252. VPN_INTERFACE_NETMASK,
  253. socksServerAddress,
  254. udpgwServerAddress,
  255. true);
  256. }
  257. mHostService.onDiagnosticMessage("routing through tunnel");
  258. // TODO: should double-check tunnel routing; see:
  259. // https://bitbucket.org/psiphon/psiphon-circumvention-system/src/1dc5e4257dca99790109f3bf374e8ab3a0ead4d7/Android/PsiphonAndroidLibrary/src/com/psiphon3/psiphonlibrary/TunnelCore.java?at=default#cl-779
  260. }
  261. private void stopVpn() {
  262. if (!mUsePacketTunnel) {
  263. stopTun2Socks();
  264. }
  265. ParcelFileDescriptor tunFd = mTunFd.getAndSet(null);
  266. if (tunFd != null) {
  267. try {
  268. tunFd.close();
  269. } catch (IOException e) {
  270. }
  271. }
  272. mRoutingThroughTunnel.set(false);
  273. }
  274. //----------------------------------------------------------------------------------------------
  275. // PsiphonProvider (Core support) interface implementation
  276. //----------------------------------------------------------------------------------------------
  277. @Override
  278. public void Notice(String noticeJSON) {
  279. handlePsiphonNotice(noticeJSON);
  280. }
  281. @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
  282. @Override
  283. public String BindToDevice(long fileDescriptor) throws Exception {
  284. if (!((VpnService)mHostService.getVpnService()).protect((int)fileDescriptor)) {
  285. throw new Exception("protect socket failed");
  286. }
  287. return "";
  288. }
  289. @Override
  290. public long HasNetworkConnectivity() {
  291. boolean hasConnectivity = hasNetworkConnectivity(mHostService.getContext());
  292. boolean wasWaitingForNetworkConnectivity = mIsWaitingForNetworkConnectivity.getAndSet(!hasConnectivity);
  293. if (!hasConnectivity && !wasWaitingForNetworkConnectivity) {
  294. // HasNetworkConnectivity may be called many times, but only call
  295. // onStartedWaitingForNetworkConnectivity once per loss of connectivity,
  296. // so the HostService may log a single message.
  297. mHostService.onStartedWaitingForNetworkConnectivity();
  298. }
  299. // TODO: change to bool return value once gobind supports that type
  300. return hasConnectivity ? 1 : 0;
  301. }
  302. @Override
  303. public String GetPrimaryDnsServer() {
  304. String dnsResolver = null;
  305. try {
  306. dnsResolver = getFirstActiveNetworkDnsResolver(mHostService.getContext());
  307. } catch (Exception e) {
  308. mHostService.onDiagnosticMessage("failed to get active network DNS resolver: " + e.getMessage());
  309. dnsResolver = DEFAULT_PRIMARY_DNS_SERVER;
  310. }
  311. return dnsResolver;
  312. }
  313. @Override
  314. public String GetSecondaryDnsServer() {
  315. return DEFAULT_SECONDARY_DNS_SERVER;
  316. }
  317. @Override
  318. public String IPv6Synthesize(String IPv4Addr) { return IPv4Addr; }
  319. @Override
  320. public String GetNetworkID() {
  321. // The network ID contains potential PII. In tunnel-core, the network ID
  322. // is used only locally in the client and not sent to the server.
  323. //
  324. // See network ID requirements here:
  325. // https://godoc.org/github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon#NetworkIDGetter
  326. String networkID = "UNKNOWN";
  327. Context context = mHostService.getContext();
  328. ConnectivityManager connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
  329. NetworkInfo activeNetworkInfo = null;
  330. try {
  331. activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
  332. } catch (java.lang.Exception e) {
  333. // May get exceptions due to missing permissions like android.permission.ACCESS_NETWORK_STATE.
  334. // Apps using the Psiphon Library and lacking android.permission.ACCESS_NETWORK_STATE will
  335. // proceed and use tactics, but with "UNKNOWN" as the sole network ID.
  336. }
  337. if (activeNetworkInfo != null && activeNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
  338. networkID = "WIFI";
  339. try {
  340. WifiManager wifiManager = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);
  341. WifiInfo wifiInfo = wifiManager.getConnectionInfo();
  342. if (wifiInfo != null) {
  343. networkID += "-" + wifiInfo.getBSSID();
  344. }
  345. } catch (java.lang.Exception e) {
  346. // May get exceptions due to missing permissions like android.permission.ACCESS_WIFI_STATE.
  347. // Fall through and use just "WIFI"
  348. }
  349. } else if (activeNetworkInfo != null && activeNetworkInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
  350. networkID = "MOBILE";
  351. try {
  352. TelephonyManager telephonyManager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
  353. if (telephonyManager != null) {
  354. networkID += "-" + telephonyManager.getNetworkOperator();
  355. }
  356. } catch (java.lang.Exception e) {
  357. // May get exceptions due to missing permissions.
  358. // Fall through and use just "MOBILE"
  359. }
  360. }
  361. return networkID;
  362. }
  363. //----------------------------------------------------------------------------------------------
  364. // Psiphon Tunnel Core
  365. //----------------------------------------------------------------------------------------------
  366. private void startPsiphon(String embeddedServerEntries) throws Exception {
  367. stopPsiphon();
  368. mHostService.onDiagnosticMessage("starting Psiphon library");
  369. // In packet tunnel mode, Psi.Start will dup the tun file descriptor
  370. // passed in via the config. So here we "check out" mTunFd, to ensure
  371. // it can't be closed before it's duplicated. (This would only happen
  372. // if stop() is called concurrently with startTunneling(), which should
  373. // not be done -- this could also cause file descriptor issues in
  374. // tun2socks mode. With the "check out", a closed and recycled file
  375. // descriptor will not be copied; but a different race condition takes
  376. // the place of that one: stop() may fail to close the tun fd. So the
  377. // prohibition on concurrent calls remains.)
  378. //
  379. // In tun2socks mode, the ownership of the fd is transferred to tun2socks.
  380. // In packet tunnel mode, tunnel code dups the fd and manages that copy
  381. // while PsiphonTunnel retains ownership of the original mTunFd copy. Both
  382. // file descriptors must be closed to halt VpnService, and stop() does
  383. // this.
  384. ParcelFileDescriptor tunFd = null;
  385. int fd = -1;
  386. if (mUsePacketTunnel) {
  387. tunFd = mTunFd.getAndSet(null);
  388. if (tunFd != null) {
  389. fd = tunFd.getFd();
  390. }
  391. }
  392. try {
  393. Psi.Start(
  394. loadPsiphonConfig(mHostService.getContext(), fd),
  395. embeddedServerEntries,
  396. "",
  397. this,
  398. isVpnMode(),
  399. false // Do not use IPv6 synthesizer for android
  400. );
  401. } catch (java.lang.Exception e) {
  402. throw new Exception("failed to start Psiphon library", e);
  403. } finally {
  404. if (mUsePacketTunnel) {
  405. mTunFd.getAndSet(tunFd);
  406. }
  407. }
  408. mHostService.onDiagnosticMessage("Psiphon library started");
  409. }
  410. private void stopPsiphon() {
  411. mHostService.onDiagnosticMessage("stopping Psiphon library");
  412. Psi.Stop();
  413. mHostService.onDiagnosticMessage("Psiphon library stopped");
  414. }
  415. private String loadPsiphonConfig(Context context, int tunFd)
  416. throws IOException, JSONException {
  417. // Load settings from the raw resource JSON config file and
  418. // update as necessary. Then write JSON to disk for the Go client.
  419. JSONObject json = new JSONObject(mHostService.getPsiphonConfig());
  420. // On Android, this directory must be set to the app private storage area.
  421. // The Psiphon library won't be able to use its current working directory
  422. // and the standard temporary directories do not exist.
  423. if (!json.has("DataStoreDirectory")) {
  424. json.put("DataStoreDirectory", context.getFilesDir());
  425. }
  426. if (!json.has("RemoteServerListDownloadFilename")) {
  427. File remoteServerListDownload = new File(context.getFilesDir(), "remote_server_list");
  428. json.put("RemoteServerListDownloadFilename", remoteServerListDownload.getAbsolutePath());
  429. }
  430. File oslDownloadDir = new File(context.getFilesDir(), "osl");
  431. if (!oslDownloadDir.exists()
  432. && !oslDownloadDir.mkdirs()) {
  433. // Failed to create osl directory
  434. // TODO: proceed anyway?
  435. throw new IOException("failed to create OSL download directory");
  436. }
  437. json.put("ObfuscatedServerListDownloadDirectory", oslDownloadDir.getAbsolutePath());
  438. // Note: onConnecting/onConnected logic assumes 1 tunnel connection
  439. json.put("TunnelPoolSize", 1);
  440. // Continue to run indefinitely until connected
  441. if (!json.has("EstablishTunnelTimeoutSeconds")) {
  442. json.put("EstablishTunnelTimeoutSeconds", 0);
  443. }
  444. // This parameter is for stats reporting
  445. if (!json.has("TunnelWholeDevice")) {
  446. json.put("TunnelWholeDevice", isVpnMode() ? 1 : 0);
  447. }
  448. json.put("EmitBytesTransferred", true);
  449. if (mLocalSocksProxyPort.get() != 0 && !json.has("LocalSocksProxyPort")) {
  450. // When mLocalSocksProxyPort is set, tun2socks is already configured
  451. // to use that port value. So we force use of the same port.
  452. // A side-effect of this is that changing the SOCKS port preference
  453. // has no effect with restartPsiphon(), a full stop() is necessary.
  454. json.put("LocalSocksProxyPort", mLocalSocksProxyPort);
  455. }
  456. json.put("UseIndistinguishableTLS", true);
  457. if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
  458. json.put("UseTrustedCACertificatesForStockTLS", true);
  459. }
  460. try {
  461. // Also enable indistinguishable TLS for HTTPS requests that
  462. // require system CAs.
  463. json.put(
  464. "TrustedCACertificatesFilename",
  465. setupTrustedCertificates(mHostService.getContext()));
  466. } catch (Exception e) {
  467. mHostService.onDiagnosticMessage(e.getMessage());
  468. }
  469. json.put("DeviceRegion", getDeviceRegion(mHostService.getContext()));
  470. if (mUsePacketTunnel) {
  471. json.put("PacketTunnelTunFileDescriptor", tunFd);
  472. json.put("DisableLocalSocksProxy", true);
  473. json.put("DisableLocalHTTPProxy", true);
  474. }
  475. return json.toString();
  476. }
  477. private void handlePsiphonNotice(String noticeJSON) {
  478. try {
  479. // All notices are sent on as diagnostic messages
  480. // except those that may contain private user data.
  481. boolean diagnostic = true;
  482. JSONObject notice = new JSONObject(noticeJSON);
  483. String noticeType = notice.getString("noticeType");
  484. if (noticeType.equals("Tunnels")) {
  485. int count = notice.getJSONObject("data").getInt("count");
  486. if (count > 0) {
  487. if (isVpnMode()) {
  488. routeThroughTunnel();
  489. }
  490. mHostService.onConnected();
  491. } else {
  492. mHostService.onConnecting();
  493. }
  494. } else if (noticeType.equals("AvailableEgressRegions")) {
  495. JSONArray egressRegions = notice.getJSONObject("data").getJSONArray("regions");
  496. ArrayList<String> regions = new ArrayList<String>();
  497. for (int i=0; i<egressRegions.length(); i++) {
  498. regions.add(egressRegions.getString(i));
  499. }
  500. mHostService.onAvailableEgressRegions(regions);
  501. } else if (noticeType.equals("SocksProxyPortInUse")) {
  502. mHostService.onSocksProxyPortInUse(notice.getJSONObject("data").getInt("port"));
  503. } else if (noticeType.equals("HttpProxyPortInUse")) {
  504. mHostService.onHttpProxyPortInUse(notice.getJSONObject("data").getInt("port"));
  505. } else if (noticeType.equals("ListeningSocksProxyPort")) {
  506. int port = notice.getJSONObject("data").getInt("port");
  507. setLocalSocksProxyPort(port);
  508. mHostService.onListeningSocksProxyPort(port);
  509. } else if (noticeType.equals("ListeningHttpProxyPort")) {
  510. int port = notice.getJSONObject("data").getInt("port");
  511. mHostService.onListeningHttpProxyPort(port);
  512. } else if (noticeType.equals("UpstreamProxyError")) {
  513. mHostService.onUpstreamProxyError(notice.getJSONObject("data").getString("message"));
  514. } else if (noticeType.equals("ClientUpgradeDownloaded")) {
  515. mHostService.onClientUpgradeDownloaded(notice.getJSONObject("data").getString("filename"));
  516. } else if (noticeType.equals("ClientIsLatestVersion")) {
  517. mHostService.onClientIsLatestVersion();
  518. } else if (noticeType.equals("Homepage")) {
  519. mHostService.onHomepage(notice.getJSONObject("data").getString("url"));
  520. } else if (noticeType.equals("ClientRegion")) {
  521. mHostService.onClientRegion(notice.getJSONObject("data").getString("region"));
  522. } else if (noticeType.equals("SplitTunnelRegion")) {
  523. mHostService.onSplitTunnelRegion(notice.getJSONObject("data").getString("region"));
  524. } else if (noticeType.equals("Untunneled")) {
  525. mHostService.onUntunneledAddress(notice.getJSONObject("data").getString("address"));
  526. } else if (noticeType.equals("BytesTransferred")) {
  527. diagnostic = false;
  528. JSONObject data = notice.getJSONObject("data");
  529. mHostService.onBytesTransferred(data.getLong("sent"), data.getLong("received"));
  530. } else if (noticeType.equals("ActiveAuthorizationIDs")) {
  531. JSONArray activeAuthorizationIDs = notice.getJSONObject("data").getJSONArray("IDs");
  532. ArrayList<String> authorizations = new ArrayList<String>();
  533. for (int i=0; i<activeAuthorizationIDs.length(); i++) {
  534. authorizations.add(activeAuthorizationIDs.getString(i));
  535. }
  536. mHostService.onActiveAuthorizationIDs(authorizations);
  537. } else if (noticeType.equals("Exiting")) {
  538. mHostService.onExiting();
  539. } else if(noticeType.equals("ClientVerificationRequired")) {
  540. JSONObject data = notice.getJSONObject("data");
  541. mHostService.onClientVerificationRequired(data.getString("nonce"), data.getInt("ttlSeconds"), data.getBoolean("resetCache"));
  542. } else if (noticeType.equals("ActiveTunnel")) {
  543. if (isVpnMode()) {
  544. if (notice.getJSONObject("data").getBoolean("isTCS")) {
  545. disableUdpGwKeepalive();
  546. } else {
  547. enableUdpGwKeepalive();
  548. }
  549. }
  550. }
  551. if (diagnostic) {
  552. String diagnosticMessage = noticeType + ": " + notice.getJSONObject("data").toString();
  553. mHostService.onDiagnosticMessage(diagnosticMessage);
  554. }
  555. } catch (JSONException e) {
  556. // Ignore notice
  557. }
  558. }
  559. private String setupTrustedCertificates(Context context) throws Exception {
  560. // Copy the Android system CA store to a local, private cert bundle file.
  561. //
  562. // This results in a file that can be passed to SSL_CTX_load_verify_locations
  563. // for use with OpenSSL modes in tunnel-core.
  564. // https://www.openssl.org/docs/manmaster/ssl/SSL_CTX_load_verify_locations.html
  565. //
  566. // TODO: to use the path mode of load_verify_locations would require emulating
  567. // the filename scheme used by c_rehash:
  568. // https://www.openssl.org/docs/manmaster/apps/c_rehash.html
  569. // http://stackoverflow.com/questions/19237167/the-new-subject-hash-openssl-algorithm-differs
  570. File directory = context.getDir("PsiphonCAStore", Context.MODE_PRIVATE);
  571. final String errorMessage = "copy AndroidCAStore failed";
  572. try {
  573. File file = new File(directory, "certs.dat");
  574. // Pave a fresh copy on every run, which ensures we're not using old certs.
  575. // Note: assumes KeyStore doesn't return revoked certs.
  576. //
  577. // TODO: this takes under 1 second, but should we avoid repaving every time?
  578. file.delete();
  579. PrintStream output = null;
  580. try {
  581. output = new PrintStream(new FileOutputStream(file));
  582. KeyStore keyStore;
  583. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
  584. keyStore = KeyStore.getInstance("AndroidCAStore");
  585. keyStore.load(null, null);
  586. } else {
  587. keyStore = KeyStore.getInstance("BKS");
  588. FileInputStream inputStream = new FileInputStream("/etc/security/cacerts.bks");
  589. try {
  590. keyStore.load(inputStream, "changeit".toCharArray());
  591. } finally {
  592. if (inputStream != null) {
  593. inputStream.close();
  594. }
  595. }
  596. }
  597. Enumeration<String> aliases = keyStore.aliases();
  598. while (aliases.hasMoreElements()) {
  599. String alias = aliases.nextElement();
  600. X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias);
  601. output.println("-----BEGIN CERTIFICATE-----");
  602. String pemCert = new String(Base64.encode(cert.getEncoded(), Base64.NO_WRAP), "UTF-8");
  603. // OpenSSL appears to reject the default linebreaking done by Base64.encode,
  604. // so we manually linebreak every 64 characters
  605. for (int i = 0; i < pemCert.length() ; i+= 64) {
  606. output.println(pemCert.substring(i, Math.min(i + 64, pemCert.length())));
  607. }
  608. output.println("-----END CERTIFICATE-----");
  609. }
  610. mHostService.onDiagnosticMessage("prepared PsiphonCAStore");
  611. return file.getAbsolutePath();
  612. } finally {
  613. if (output != null) {
  614. output.close();
  615. }
  616. }
  617. } catch (KeyStoreException e) {
  618. throw new Exception(errorMessage, e);
  619. } catch (NoSuchAlgorithmException e) {
  620. throw new Exception(errorMessage, e);
  621. } catch (CertificateException e) {
  622. throw new Exception(errorMessage, e);
  623. } catch (IOException e) {
  624. throw new Exception(errorMessage, e);
  625. }
  626. }
  627. private static String getDeviceRegion(Context context) {
  628. String region = "";
  629. TelephonyManager telephonyManager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
  630. if (telephonyManager != null) {
  631. region = telephonyManager.getSimCountryIso();
  632. if (region == null) {
  633. region = "";
  634. }
  635. if (region.length() == 0 && telephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_CDMA) {
  636. region = telephonyManager.getNetworkCountryIso();
  637. if (region == null) {
  638. region = "";
  639. }
  640. }
  641. }
  642. if (region.length() == 0) {
  643. Locale defaultLocale = Locale.getDefault();
  644. if (defaultLocale != null) {
  645. region = defaultLocale.getCountry();
  646. }
  647. }
  648. return region.toUpperCase(Locale.US);
  649. }
  650. //----------------------------------------------------------------------------------------------
  651. // Tun2Socks
  652. //----------------------------------------------------------------------------------------------
  653. @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
  654. private void startTun2Socks(
  655. final ParcelFileDescriptor vpnInterfaceFileDescriptor,
  656. final int vpnInterfaceMTU,
  657. final String vpnIpAddress,
  658. final String vpnNetMask,
  659. final String socksServerAddress,
  660. final String udpgwServerAddress,
  661. final boolean udpgwTransparentDNS) {
  662. if (mTun2SocksThread != null) {
  663. return;
  664. }
  665. mTun2SocksThread = new Thread(new Runnable() {
  666. @Override
  667. public void run() {
  668. runTun2Socks(
  669. vpnInterfaceFileDescriptor.detachFd(),
  670. vpnInterfaceMTU,
  671. vpnIpAddress,
  672. vpnNetMask,
  673. socksServerAddress,
  674. udpgwServerAddress,
  675. udpgwTransparentDNS ? 1 : 0);
  676. }
  677. });
  678. mTun2SocksThread.start();
  679. mHostService.onDiagnosticMessage("tun2socks started");
  680. }
  681. private void stopTun2Socks() {
  682. if (mTun2SocksThread != null) {
  683. try {
  684. terminateTun2Socks();
  685. mTun2SocksThread.join();
  686. } catch (InterruptedException e) {
  687. Thread.currentThread().interrupt();
  688. }
  689. mTun2SocksThread = null;
  690. mHostService.onDiagnosticMessage("tun2socks stopped");
  691. }
  692. }
  693. public static void logTun2Socks(String level, String channel, String msg) {
  694. String logMsg = "tun2socks: " + level + "(" + channel + "): " + msg;
  695. mPsiphonTunnel.mHostService.onDiagnosticMessage(logMsg);
  696. }
  697. private native static int runTun2Socks(
  698. int vpnInterfaceFileDescriptor,
  699. int vpnInterfaceMTU,
  700. String vpnIpAddress,
  701. String vpnNetMask,
  702. String socksServerAddress,
  703. String udpgwServerAddress,
  704. int udpgwTransparentDNS);
  705. private native static int terminateTun2Socks();
  706. private native static int enableUdpGwKeepalive();
  707. private native static int disableUdpGwKeepalive();
  708. //----------------------------------------------------------------------------------------------
  709. // Implementation: Network Utils
  710. //----------------------------------------------------------------------------------------------
  711. private static boolean hasNetworkConnectivity(Context context) {
  712. ConnectivityManager connectivityManager =
  713. (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
  714. if (connectivityManager == null) {
  715. return false;
  716. }
  717. NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
  718. return networkInfo != null && networkInfo.isConnected();
  719. }
  720. private static class PrivateAddress {
  721. final public String mIpAddress;
  722. final public String mSubnet;
  723. final public int mPrefixLength;
  724. final public String mRouter;
  725. public PrivateAddress(String ipAddress, String subnet, int prefixLength, String router) {
  726. mIpAddress = ipAddress;
  727. mSubnet = subnet;
  728. mPrefixLength = prefixLength;
  729. mRouter = router;
  730. }
  731. }
  732. private static PrivateAddress selectPrivateAddress() throws Exception {
  733. // Select one of 10.0.0.1, 172.16.0.1, or 192.168.0.1 depending on
  734. // which private address range isn't in use.
  735. Map<String, PrivateAddress> candidates = new HashMap<String, PrivateAddress>();
  736. candidates.put( "10", new PrivateAddress("10.0.0.1", "10.0.0.0", 8, "10.0.0.2"));
  737. candidates.put("172", new PrivateAddress("172.16.0.1", "172.16.0.0", 12, "172.16.0.2"));
  738. candidates.put("192", new PrivateAddress("192.168.0.1", "192.168.0.0", 16, "192.168.0.2"));
  739. candidates.put("169", new PrivateAddress("169.254.1.1", "169.254.1.0", 24, "169.254.1.2"));
  740. List<NetworkInterface> netInterfaces;
  741. try {
  742. netInterfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
  743. } catch (SocketException e) {
  744. throw new Exception("selectPrivateAddress failed", e);
  745. }
  746. for (NetworkInterface netInterface : netInterfaces) {
  747. for (InetAddress inetAddress : Collections.list(netInterface.getInetAddresses())) {
  748. String ipAddress = inetAddress.getHostAddress();
  749. if (InetAddressUtils.isIPv4Address(ipAddress)) {
  750. if (ipAddress.startsWith("10.")) {
  751. candidates.remove("10");
  752. }
  753. else if (
  754. ipAddress.length() >= 6 &&
  755. ipAddress.substring(0, 6).compareTo("172.16") >= 0 &&
  756. ipAddress.substring(0, 6).compareTo("172.31") <= 0) {
  757. candidates.remove("172");
  758. }
  759. else if (ipAddress.startsWith("192.168")) {
  760. candidates.remove("192");
  761. }
  762. }
  763. }
  764. }
  765. if (candidates.size() > 0) {
  766. return candidates.values().iterator().next();
  767. }
  768. throw new Exception("no private address available");
  769. }
  770. public static String getFirstActiveNetworkDnsResolver(Context context)
  771. throws Exception {
  772. Collection<InetAddress> dnsResolvers = getActiveNetworkDnsResolvers(context);
  773. if (!dnsResolvers.isEmpty()) {
  774. // strip the leading slash e.g., "/192.168.1.1"
  775. String dnsResolver = dnsResolvers.iterator().next().toString();
  776. if (dnsResolver.startsWith("/")) {
  777. dnsResolver = dnsResolver.substring(1);
  778. }
  779. return dnsResolver;
  780. }
  781. throw new Exception("no active network DNS resolver");
  782. }
  783. @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  784. private static Collection<InetAddress> getActiveNetworkDnsResolvers(Context context)
  785. throws Exception {
  786. final String errorMessage = "getActiveNetworkDnsResolvers failed";
  787. ArrayList<InetAddress> dnsAddresses = new ArrayList<InetAddress>();
  788. try {
  789. // Hidden API
  790. // - only available in Android 4.0+
  791. // - no guarantee will be available beyond 4.2, or on all vendor devices
  792. ConnectivityManager connectivityManager =
  793. (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
  794. Class<?> LinkPropertiesClass = Class.forName("android.net.LinkProperties");
  795. Method getActiveLinkPropertiesMethod = ConnectivityManager.class.getMethod("getActiveLinkProperties", new Class []{});
  796. Object linkProperties = getActiveLinkPropertiesMethod.invoke(connectivityManager);
  797. if (linkProperties != null) {
  798. if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
  799. Method getDnsesMethod = LinkPropertiesClass.getMethod("getDnses", new Class []{});
  800. Collection<?> dnses = (Collection<?>)getDnsesMethod.invoke(linkProperties);
  801. for (Object dns : dnses) {
  802. dnsAddresses.add((InetAddress)dns);
  803. }
  804. } else {
  805. // LinkProperties is public in API 21 (and the DNS function signature has changed)
  806. for (InetAddress dns : ((LinkProperties)linkProperties).getDnsServers()) {
  807. dnsAddresses.add(dns);
  808. }
  809. }
  810. }
  811. } catch (ClassNotFoundException e) {
  812. throw new Exception(errorMessage, e);
  813. } catch (NoSuchMethodException e) {
  814. throw new Exception(errorMessage, e);
  815. } catch (IllegalArgumentException e) {
  816. throw new Exception(errorMessage, e);
  817. } catch (IllegalAccessException e) {
  818. throw new Exception(errorMessage, e);
  819. } catch (InvocationTargetException e) {
  820. throw new Exception(errorMessage, e);
  821. } catch (NullPointerException e) {
  822. throw new Exception(errorMessage, e);
  823. }
  824. return dnsAddresses;
  825. }
  826. //----------------------------------------------------------------------------------------------
  827. // Exception
  828. //----------------------------------------------------------------------------------------------
  829. public static class Exception extends java.lang.Exception {
  830. private static final long serialVersionUID = 1L;
  831. public Exception(String message) {
  832. super(message);
  833. }
  834. public Exception(String message, Throwable cause) {
  835. super(message + ": " + cause.getMessage());
  836. }
  837. }
  838. }