From 569240df85cbd15b2310b56c2b1dc9d3d5edca9f Mon Sep 17 00:00:00 2001 From: jacquarg Date: Tue, 24 Aug 2021 15:11:55 +0200 Subject: [PATCH] Fork orbotservice module of https://gitlab.com/guardianproject/tormobile/orbot tag 17.2.1-RC-2-tor-0.4.8.7 commit 8c03d58377ada063ce6adc57a6a82cf984645af8 --- .gitignore | 4 + build.gradle | 58 + proguard-rules.pro | 0 src/main/AndroidManifest.xml | 5 + src/main/assets/fronts | 14 + .../android/service/OrbotConstants.java | 184 ++ .../service/OrbotRawEventListener.java | 259 +++ .../android/service/OrbotService.java | 1559 +++++++++++++++++ .../android/service/StartTorReceiver.java | 43 + .../util/CustomTorResourceInstaller.java | 80 + .../service/util/PowerConnectionReceiver.java | 32 + .../android/service/util/Prefs.java | 254 +++ .../service/util/PrefsWeeklyWorker.java | 23 + .../android/service/util/TCPSourceApp.java | 291 +++ .../android/service/util/Utils.java | 56 + .../android/service/vpn/DNSResolver.java | 35 + .../android/service/vpn/OrbotVpnManager.java | 326 ++++ .../service/vpn/RequestPacketHandler.java | 81 + .../android/service/vpn/TorifiedApp.java | 207 +++ .../drawable-hdpi/ic_refresh_white_24dp.png | Bin 0 -> 675 bytes .../res/drawable-hdpi/ic_stat_notifyerr.png | Bin 0 -> 359 bytes src/main/res/drawable-hdpi/ic_stat_tor.png | Bin 0 -> 715 bytes .../res/drawable-hdpi/ic_stat_tor_xfer.png | Bin 0 -> 969 bytes .../res/drawable-ldpi/ic_stat_notifyerr.png | Bin 0 -> 262 bytes src/main/res/drawable-ldpi/ic_stat_tor.png | Bin 0 -> 420 bytes .../drawable-mdpi/ic_refresh_white_24dp.png | Bin 0 -> 426 bytes .../res/drawable-mdpi/ic_stat_notifyerr.png | Bin 0 -> 317 bytes src/main/res/drawable-mdpi/ic_stat_tor.png | Bin 0 -> 441 bytes .../res/drawable-mdpi/ic_stat_tor_xfer.png | Bin 0 -> 741 bytes .../drawable-xhdpi/ic_refresh_white_24dp.png | Bin 0 -> 895 bytes .../res/drawable-xhdpi/ic_stat_notifyerr.png | Bin 0 -> 406 bytes src/main/res/drawable-xhdpi/ic_stat_tor.png | Bin 0 -> 887 bytes .../res/drawable-xhdpi/ic_stat_tor_xfer.png | Bin 0 -> 1348 bytes .../drawable-xxhdpi/ic_refresh_white_24dp.png | Bin 0 -> 1231 bytes src/main/res/drawable-xxhdpi/ic_stat_tor.png | Bin 0 -> 1307 bytes .../res/drawable-xxhdpi/ic_stat_tor_xfer.png | Bin 0 -> 1734 bytes .../ic_refresh_white_24dp.png | Bin 0 -> 1553 bytes src/main/res/drawable-xxxhdpi/ic_stat_tor.png | Bin 0 -> 1734 bytes .../res/drawable-xxxhdpi/ic_stat_tor_xfer.png | Bin 0 -> 2424 bytes src/main/res/drawable/ic_stat_tor_xfer.png | Bin 0 -> 990 bytes src/main/res/values-ach/strings.xml | 11 + src/main/res/values-ar/strings.xml | 24 + src/main/res/values-az/strings.xml | 24 + src/main/res/values-bg/strings.xml | 24 + src/main/res/values-bn-rBD/strings.xml | 16 + src/main/res/values-bn-rIN/strings.xml | 11 + src/main/res/values-bn/strings.xml | 11 + src/main/res/values-brx/strings.xml | 11 + src/main/res/values-bs/strings.xml | 11 + src/main/res/values-ca/strings.xml | 21 + src/main/res/values-cs-rCZ/strings.xml | 21 + src/main/res/values-cs/strings.xml | 16 + src/main/res/values-cy/strings.xml | 14 + src/main/res/values-da/strings.xml | 13 + src/main/res/values-de-rAT/strings.xml | 11 + src/main/res/values-de/strings.xml | 26 + src/main/res/values-el/strings.xml | 21 + src/main/res/values-en-rGB/strings.xml | 11 + src/main/res/values-eo/strings.xml | 19 + src/main/res/values-es-rAR/strings.xml | 16 + src/main/res/values-es/strings.xml | 24 + src/main/res/values-et/strings.xml | 21 + src/main/res/values-eu/strings.xml | 21 + src/main/res/values-fa/strings.xml | 24 + src/main/res/values-fi/strings.xml | 23 + src/main/res/values-fr-rFR/strings.xml | 18 + src/main/res/values-fr/strings.xml | 11 + src/main/res/values-gl/strings.xml | 22 + src/main/res/values-gu-rIN/strings.xml | 11 + src/main/res/values-gu/strings.xml | 11 + src/main/res/values-guc/strings.xml | 44 + src/main/res/values-gum/strings.xml | 46 + src/main/res/values-hi/strings.xml | 13 + src/main/res/values-hr-rHR/strings.xml | 11 + src/main/res/values-hr/strings.xml | 24 + src/main/res/values-hu/strings.xml | 24 + src/main/res/values-hy-rAM/strings.xml | 11 + src/main/res/values-ia/strings.xml | 11 + src/main/res/values-in/strings.xml | 24 + src/main/res/values-is/strings.xml | 26 + src/main/res/values-it/strings.xml | 24 + src/main/res/values-iw/strings.xml | 21 + src/main/res/values-ja/strings.xml | 24 + src/main/res/values-kn-rIN/strings.xml | 11 + src/main/res/values-kn/strings.xml | 11 + src/main/res/values-ko/strings.xml | 21 + src/main/res/values-ky/strings.xml | 11 + src/main/res/values-lt-rLT/strings.xml | 11 + src/main/res/values-lt/strings.xml | 11 + src/main/res/values-lv/strings.xml | 23 + src/main/res/values-mk/strings.xml | 23 + src/main/res/values-ml/strings.xml | 11 + src/main/res/values-mn/strings.xml | 11 + src/main/res/values-mr-rIN/strings.xml | 11 + src/main/res/values-ms-rMY/strings.xml | 20 + src/main/res/values-ms/strings.xml | 21 + src/main/res/values-my/strings.xml | 11 + src/main/res/values-nah/strings.xml | 44 + src/main/res/values-nb/strings.xml | 25 + src/main/res/values-nl/strings.xml | 24 + src/main/res/values-pa/strings.xml | 11 + src/main/res/values-pbb/strings.xml | 44 + src/main/res/values-pl/strings.xml | 24 + src/main/res/values-ps/strings.xml | 11 + src/main/res/values-pt-rBR/strings.xml | 26 + src/main/res/values-pt-rPT/strings.xml | 15 + src/main/res/values-pt/strings.xml | 22 + src/main/res/values-ro-rRO/strings.xml | 11 + src/main/res/values-ro/strings.xml | 22 + src/main/res/values-ru/strings.xml | 24 + src/main/res/values-si-rLK/strings.xml | 20 + src/main/res/values-sk-rSK/strings.xml | 11 + src/main/res/values-sk/strings.xml | 15 + src/main/res/values-sl/strings.xml | 15 + src/main/res/values-sn/strings.xml | 12 + src/main/res/values-sq/strings.xml | 11 + src/main/res/values-sr/strings.xml | 21 + src/main/res/values-sv/strings.xml | 24 + src/main/res/values-ta/strings.xml | 16 + src/main/res/values-th/strings.xml | 17 + src/main/res/values-tl/strings.xml | 21 + src/main/res/values-tr/strings.xml | 26 + src/main/res/values-uk/strings.xml | 25 + src/main/res/values-ur/strings.xml | 11 + src/main/res/values-uz/strings.xml | 15 + src/main/res/values-vi/strings.xml | 26 + src/main/res/values-zh-rCN/strings.xml | 26 + src/main/res/values-zh-rTW/strings.xml | 18 + src/main/res/values/dimens.xml | 27 + src/main/res/values/strings.xml | 43 + 130 files changed, 5242 insertions(+) create mode 100644 .gitignore create mode 100644 build.gradle create mode 100644 proguard-rules.pro create mode 100644 src/main/AndroidManifest.xml create mode 100644 src/main/assets/fronts create mode 100644 src/main/java/org/torproject/android/service/OrbotConstants.java create mode 100644 src/main/java/org/torproject/android/service/OrbotRawEventListener.java create mode 100644 src/main/java/org/torproject/android/service/OrbotService.java create mode 100644 src/main/java/org/torproject/android/service/StartTorReceiver.java create mode 100644 src/main/java/org/torproject/android/service/util/CustomTorResourceInstaller.java create mode 100644 src/main/java/org/torproject/android/service/util/PowerConnectionReceiver.java create mode 100644 src/main/java/org/torproject/android/service/util/Prefs.java create mode 100644 src/main/java/org/torproject/android/service/util/PrefsWeeklyWorker.java create mode 100644 src/main/java/org/torproject/android/service/util/TCPSourceApp.java create mode 100644 src/main/java/org/torproject/android/service/util/Utils.java create mode 100644 src/main/java/org/torproject/android/service/vpn/DNSResolver.java create mode 100644 src/main/java/org/torproject/android/service/vpn/OrbotVpnManager.java create mode 100644 src/main/java/org/torproject/android/service/vpn/RequestPacketHandler.java create mode 100644 src/main/java/org/torproject/android/service/vpn/TorifiedApp.java create mode 100644 src/main/res/drawable-hdpi/ic_refresh_white_24dp.png create mode 100644 src/main/res/drawable-hdpi/ic_stat_notifyerr.png create mode 100644 src/main/res/drawable-hdpi/ic_stat_tor.png create mode 100644 src/main/res/drawable-hdpi/ic_stat_tor_xfer.png create mode 100644 src/main/res/drawable-ldpi/ic_stat_notifyerr.png create mode 100644 src/main/res/drawable-ldpi/ic_stat_tor.png create mode 100644 src/main/res/drawable-mdpi/ic_refresh_white_24dp.png create mode 100644 src/main/res/drawable-mdpi/ic_stat_notifyerr.png create mode 100644 src/main/res/drawable-mdpi/ic_stat_tor.png create mode 100644 src/main/res/drawable-mdpi/ic_stat_tor_xfer.png create mode 100644 src/main/res/drawable-xhdpi/ic_refresh_white_24dp.png create mode 100644 src/main/res/drawable-xhdpi/ic_stat_notifyerr.png create mode 100644 src/main/res/drawable-xhdpi/ic_stat_tor.png create mode 100644 src/main/res/drawable-xhdpi/ic_stat_tor_xfer.png create mode 100644 src/main/res/drawable-xxhdpi/ic_refresh_white_24dp.png create mode 100644 src/main/res/drawable-xxhdpi/ic_stat_tor.png create mode 100644 src/main/res/drawable-xxhdpi/ic_stat_tor_xfer.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_refresh_white_24dp.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_stat_tor.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_stat_tor_xfer.png create mode 100644 src/main/res/drawable/ic_stat_tor_xfer.png create mode 100644 src/main/res/values-ach/strings.xml create mode 100644 src/main/res/values-ar/strings.xml create mode 100644 src/main/res/values-az/strings.xml create mode 100644 src/main/res/values-bg/strings.xml create mode 100644 src/main/res/values-bn-rBD/strings.xml create mode 100644 src/main/res/values-bn-rIN/strings.xml create mode 100644 src/main/res/values-bn/strings.xml create mode 100644 src/main/res/values-brx/strings.xml create mode 100644 src/main/res/values-bs/strings.xml create mode 100644 src/main/res/values-ca/strings.xml create mode 100644 src/main/res/values-cs-rCZ/strings.xml create mode 100644 src/main/res/values-cs/strings.xml create mode 100644 src/main/res/values-cy/strings.xml create mode 100644 src/main/res/values-da/strings.xml create mode 100644 src/main/res/values-de-rAT/strings.xml create mode 100644 src/main/res/values-de/strings.xml create mode 100644 src/main/res/values-el/strings.xml create mode 100644 src/main/res/values-en-rGB/strings.xml create mode 100644 src/main/res/values-eo/strings.xml create mode 100644 src/main/res/values-es-rAR/strings.xml create mode 100644 src/main/res/values-es/strings.xml create mode 100644 src/main/res/values-et/strings.xml create mode 100644 src/main/res/values-eu/strings.xml create mode 100644 src/main/res/values-fa/strings.xml create mode 100644 src/main/res/values-fi/strings.xml create mode 100644 src/main/res/values-fr-rFR/strings.xml create mode 100644 src/main/res/values-fr/strings.xml create mode 100644 src/main/res/values-gl/strings.xml create mode 100644 src/main/res/values-gu-rIN/strings.xml create mode 100644 src/main/res/values-gu/strings.xml create mode 100644 src/main/res/values-guc/strings.xml create mode 100644 src/main/res/values-gum/strings.xml create mode 100644 src/main/res/values-hi/strings.xml create mode 100644 src/main/res/values-hr-rHR/strings.xml create mode 100644 src/main/res/values-hr/strings.xml create mode 100644 src/main/res/values-hu/strings.xml create mode 100644 src/main/res/values-hy-rAM/strings.xml create mode 100644 src/main/res/values-ia/strings.xml create mode 100644 src/main/res/values-in/strings.xml create mode 100644 src/main/res/values-is/strings.xml create mode 100644 src/main/res/values-it/strings.xml create mode 100644 src/main/res/values-iw/strings.xml create mode 100644 src/main/res/values-ja/strings.xml create mode 100644 src/main/res/values-kn-rIN/strings.xml create mode 100644 src/main/res/values-kn/strings.xml create mode 100644 src/main/res/values-ko/strings.xml create mode 100644 src/main/res/values-ky/strings.xml create mode 100644 src/main/res/values-lt-rLT/strings.xml create mode 100644 src/main/res/values-lt/strings.xml create mode 100644 src/main/res/values-lv/strings.xml create mode 100644 src/main/res/values-mk/strings.xml create mode 100644 src/main/res/values-ml/strings.xml create mode 100644 src/main/res/values-mn/strings.xml create mode 100644 src/main/res/values-mr-rIN/strings.xml create mode 100644 src/main/res/values-ms-rMY/strings.xml create mode 100644 src/main/res/values-ms/strings.xml create mode 100644 src/main/res/values-my/strings.xml create mode 100644 src/main/res/values-nah/strings.xml create mode 100644 src/main/res/values-nb/strings.xml create mode 100644 src/main/res/values-nl/strings.xml create mode 100644 src/main/res/values-pa/strings.xml create mode 100644 src/main/res/values-pbb/strings.xml create mode 100644 src/main/res/values-pl/strings.xml create mode 100644 src/main/res/values-ps/strings.xml create mode 100644 src/main/res/values-pt-rBR/strings.xml create mode 100644 src/main/res/values-pt-rPT/strings.xml create mode 100644 src/main/res/values-pt/strings.xml create mode 100644 src/main/res/values-ro-rRO/strings.xml create mode 100644 src/main/res/values-ro/strings.xml create mode 100644 src/main/res/values-ru/strings.xml create mode 100644 src/main/res/values-si-rLK/strings.xml create mode 100644 src/main/res/values-sk-rSK/strings.xml create mode 100644 src/main/res/values-sk/strings.xml create mode 100644 src/main/res/values-sl/strings.xml create mode 100644 src/main/res/values-sn/strings.xml create mode 100644 src/main/res/values-sq/strings.xml create mode 100644 src/main/res/values-sr/strings.xml create mode 100644 src/main/res/values-sv/strings.xml create mode 100644 src/main/res/values-ta/strings.xml create mode 100644 src/main/res/values-th/strings.xml create mode 100644 src/main/res/values-tl/strings.xml create mode 100644 src/main/res/values-tr/strings.xml create mode 100644 src/main/res/values-uk/strings.xml create mode 100644 src/main/res/values-ur/strings.xml create mode 100644 src/main/res/values-uz/strings.xml create mode 100644 src/main/res/values-vi/strings.xml create mode 100644 src/main/res/values-zh-rCN/strings.xml create mode 100644 src/main/res/values-zh-rTW/strings.xml create mode 100644 src/main/res/values/dimens.xml create mode 100644 src/main/res/values/strings.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..744767b --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/build +.idea/ +.gradle + diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..d7b2878 --- /dev/null +++ b/build.gradle @@ -0,0 +1,58 @@ +apply plugin: 'com.android.library' +apply from: "../commons.gradle" +apply from : '../dependencies.gradle' + +android { + ndkVersion '21.3.6528147' + + defaultConfig { + minSdkVersion 23 + targetSdkVersion 33 + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + packagingOptions { + resources { + excludes += ['META-INF/androidx.localbroadcastmanager_localbroadcastmanager.version'] + } + } + + + lint { + abortOnError true + checkReleaseBuilds false + disable 'InvalidPackage' + htmlReport true + lintConfig file('../lint.xml') + textReport false + xmlReport false + } + namespace 'org.torproject.android.service' +} + +dependencies { + + api libs.guardian_jtorctl + + //use locally built ipt_proxy+go_tun2socks + api project(':OrbotLib') + + api libs.tor_android + + implementation( + libs.android_shell, + libs.androidx_core, + libs.androidx_localbroadcast, + libs.pcap_core, + libs.pcap_factory, + libs.androidx_work, + libs.androidx_work_kotlin + ) + + implementation files('../libs/geoip.jar') +} diff --git a/proguard-rules.pro b/proguard-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml new file mode 100644 index 0000000..335ae6e --- /dev/null +++ b/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/assets/fronts b/src/main/assets/fronts new file mode 100644 index 0000000..606349c --- /dev/null +++ b/src/main/assets/fronts @@ -0,0 +1,14 @@ +snowflake-target https://snowflake-broker.torproject.net.global.prod.fastly.net/ +snowflake-front github.githubassets.com +snowflake-stun stun:stun.l.google.com:19302,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.sonetel.net:3478,stun:stun.stunprotocol.org:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 +snowflake-target-direct https://snowflake-broker.torproject.net/ +snowflake-amp-front www.google.com +snowflake-amp-cache https://cdn.ampproject.org/ +moat-cdn https://d50gd378qj74g.cloudfront.net/ +moat-url https://moat.torproject.org.global.prod.fastly.net/ +moat-front github.githubassets.com +snowflake-relay-url wss://snowflake.bamsoftware.com +snowflake-nat-probe https://snowflake-broker.torproject.net:8443/probe +snowflake-broker-1 snowflake 192.0.2.4:80 8838024498816A039FCBBAB14E6F40A0843051FA fingerprint=8838024498816A039FCBBAB14E6F40A0843051FA url=https://snowflake-broker.torproject.net.global.prod.fastly.net/ front=github.githubassets.com ice=stun:stun.l.google.com:19302,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.net:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 utls-imitate=hellorandomizedalpn +snowflake-broker-2 snowflake 192.0.2.3:80 2B280B23E1107BB62ABFC40DDCC8824814F80A72 fingerprint=2B280B23E1107BB62ABFC40DDCC8824814F80A72 url=https://snowflake-broker.torproject.net.global.prod.fastly.net/ front=github.githubassets.com ice=stun:stun.l.google.com:19302,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 utls-imitate=hellorandomizedalpn +snowflake-proxy-stun stun:stun.l.google.com:19302 diff --git a/src/main/java/org/torproject/android/service/OrbotConstants.java b/src/main/java/org/torproject/android/service/OrbotConstants.java new file mode 100644 index 0000000..b543056 --- /dev/null +++ b/src/main/java/org/torproject/android/service/OrbotConstants.java @@ -0,0 +1,184 @@ +/* Copyright (c) 2009, Nathan Freitas, Orbot/The Guardian Project - http://openideals.com/guardian */ +/* See LICENSE for licensing information */ + +package org.torproject.android.service; + +import android.content.Intent; + +import org.torproject.jni.TorService; + +import java.util.Arrays; +import java.util.List; + +public interface OrbotConstants { + + String TAG = "Orbot"; + + String PREF_OR = "pref_or"; + String PREF_OR_PORT = "pref_or_port"; + String PREF_OR_NICKNAME = "pref_or_nickname"; + String PREF_REACHABLE_ADDRESSES = "pref_reachable_addresses"; + String PREF_REACHABLE_ADDRESSES_PORTS = "pref_reachable_addresses_ports"; + + String PREF_TOR_SHARED_PREFS = "org.torproject.android_preferences"; + + String PREF_SOCKS = "pref_socks"; + + String PREF_HTTP = "pref_http"; + + String PREF_ISOLATE_DEST = "pref_isolate_dest"; + String PREF_ISOLATE_PORT = "pref_isolate_port"; + String PREF_ISOLATE_PROTOCOL = "pref_isolate_protocol"; + + String PREF_CONNECTION_PADDING = "pref_connection_padding"; + String PREF_REDUCED_CONNECTION_PADDING = "pref_reduced_connection_padding"; + String PREF_CIRCUIT_PADDING = "pref_circuit_padding"; + String PREF_REDUCED_CIRCUIT_PADDING = "pref_reduced_circuit_padding"; + + String PREF_PREFER_IPV6 = "pref_prefer_ipv6"; + String PREF_DISABLE_IPV4 = "pref_disable_ipv4"; + + + String APP_TOR_KEY = "_app_tor"; + String APP_DATA_KEY = "_app_data"; + String APP_WIFI_KEY = "_app_wifi"; + + + String DIRECTORY_TOR_DATA = "tordata"; + + //geoip data file asset key + String GEOIP_ASSET_KEY = "geoip"; + String GEOIP6_ASSET_KEY = "geoip6"; + + int TOR_TRANSPROXY_PORT_DEFAULT = 9040; + + int TOR_DNS_PORT_DEFAULT = 5400; + + String HTTP_PROXY_PORT_DEFAULT = "8118"; // like Privoxy! + String SOCKS_PROXY_PORT_DEFAULT = "9050"; + + //control port + String LOG_NOTICE_HEADER = "NOTICE: "; + String LOG_NOTICE_BOOTSTRAPPED = "Bootstrapped"; + + /** + * A request to Orbot to transparently start Tor services + */ + String ACTION_START = TorService.ACTION_START; + String ACTION_STOP = "org.torproject.android.intent.action.STOP"; + + // needed when Orbot exits and tor is not running, but the notification is still active + String ACTION_STOP_FOREGROUND_TASK = "org.torproject.android.intent.action.STOP_FOREGROUND_TASK"; + + String ACTION_START_VPN = "org.torproject.android.intent.action.START_VPN"; + String ACTION_STOP_VPN = "org.torproject.android.intent.action.STOP_VPN"; + String ACTION_RESTART_VPN = "org.torproject.android.intent.action.RESTART_VPN"; + + String ACTION_LOCAL_LOCALE_SET = "org.torproject.android.intent.LOCAL_LOCALE_SET"; + + String ACTION_UPDATE_ONION_NAMES = "org.torproject.android.intent.action.UPDATE_ONION_NAMES"; + + /** + * {@link Intent} send by Orbot with {@code ON/OFF/STARTING/STOPPING} status + */ + String ACTION_STATUS = TorService.ACTION_STATUS; + /** + * {@code String} that contains a status constant: {@link #STATUS_ON}, + * {@link #STATUS_OFF}, {@link #STATUS_STARTING}, or + * {@link #STATUS_STOPPING} + */ + String EXTRA_STATUS = TorService.EXTRA_STATUS; + /** + * A {@link String} {@code packageName} for Orbot to direct its status reply + * to, used in {@link #ACTION_START} {@link Intent}s sent to Orbot + */ + String EXTRA_PACKAGE_NAME = TorService.EXTRA_PACKAGE_NAME; + /** + * The SOCKS proxy settings in URL form. + */ + String EXTRA_SOCKS_PROXY = "org.torproject.android.intent.extra.SOCKS_PROXY"; + String EXTRA_SOCKS_PROXY_HOST = "org.torproject.android.intent.extra.SOCKS_PROXY_HOST"; + String EXTRA_SOCKS_PROXY_PORT = "org.torproject.android.intent.extra.SOCKS_PROXY_PORT"; + /** + * The HTTP proxy settings in URL form. + */ + String EXTRA_HTTP_PROXY = "org.torproject.android.intent.extra.HTTP_PROXY"; + String EXTRA_HTTP_PROXY_HOST = "org.torproject.android.intent.extra.HTTP_PROXY_HOST"; + String EXTRA_HTTP_PROXY_PORT = "org.torproject.android.intent.extra.HTTP_PROXY_PORT"; + + String EXTRA_DNS_PORT = "org.torproject.android.intent.extra.DNS_PORT"; + String EXTRA_TRANS_PORT = "org.torproject.android.intent.extra.TRANS_PORT"; + /** + * When present, indicates with certainty that the system itself did *not* send the Intent. + * Effectively, the lack of this extra indicates that the VPN is being started by the system + * as a result of the user's always-on preference for the VPN. + * See: + * Detect always-on | VPN | Android Developers + */ + String EXTRA_NOT_SYSTEM = "org.torproject.android.intent.extra.NOT_SYSTEM"; + + String LOCAL_ACTION_LOG = "log"; + String LOCAL_ACTION_STATUS = "status"; + String LOCAL_ACTION_BANDWIDTH = "bandwidth"; + String LOCAL_EXTRA_TOTAL_READ = "totalRead"; + String LOCAL_EXTRA_TOTAL_WRITTEN = "totalWritten"; + String LOCAL_EXTRA_LAST_WRITTEN = "lastWritten"; + String LOCAL_EXTRA_LAST_READ = "lastRead"; + String LOCAL_EXTRA_LOG = "log"; + String LOCAL_EXTRA_BOOTSTRAP_PERCENT = "percent"; + String LOCAL_ACTION_PORTS = "ports"; + String LOCAL_ACTION_V3_NAMES_UPDATED = "V3_NAMES_UPDATED"; + String LOCAL_ACTION_NOTIFICATION_START = "notification_start"; + String LOCAL_ACTION_SMART_CONNECT_EVENT = "smart"; + String LOCAL_EXTRA_SMART_STATUS = "status"; + String SMART_STATUS_NO_DIRECT = "no_direct"; + String SMART_STATUS_CIRCUMVENTION_ATTEMPT_FAILED = "bad_attempt_suggestion"; + + + /** + * All tor-related services and daemons are stopped + */ + String STATUS_OFF = TorService.STATUS_OFF; + + /** + * All tor-related services and daemons have completed starting + */ + String STATUS_ON = TorService.STATUS_ON; + String STATUS_STARTING = TorService.STATUS_STARTING; + String STATUS_STOPPING = TorService.STATUS_STOPPING; + + /** + * The user has disabled the ability for background starts triggered by + * apps. Fallback to the old {@link Intent} action that brings up Orbot: + * {@link #ACTION_START} + */ + String STATUS_STARTS_DISABLED = "STARTS_DISABLED"; + + // actions for internal command Intents + String CMD_SET_EXIT = "setexit"; + String CMD_ACTIVE = "ACTIVE"; + String CMD_SNOWFLAKE_PROXY = "sf_proxy"; + + String ONION_SERVICES_DIR = "v3_onion_services"; + String V3_CLIENT_AUTH_DIR = "v3_client_auth"; + + String PREFS_DNS_PORT = "PREFS_DNS_PORT"; + + String PREFS_KEY_TORIFIED = "PrefTord"; + + /** + * Include packages here to make the VPNService ignore these apps. This is to + * prevent tor over tor scenarios... + */ + List BYPASS_VPN_PACKAGES = Arrays.asList( + "org.torproject.torbrowser_alpha", + "org.torproject.torbrowser", + "org.onionshare.android", // issue #618 + "org.briarproject.briar.android" // https://github.com/guardianproject/orbot/issues/474 + ); + + List VPN_SUGGESTED_APPS = Arrays.asList("org.thoughtcrime.securesms", // Signal + "com.whatsapp", "com.instagram.android", "im.vector.app", "org.telegram.messenger", "com.twitter.android", "com.facebook.orca", "com.facebook.mlite", "com.brave.browser", "org.mozilla.focus"); + + String ONION_EMOJI = "\uD83E\uDDC5"; +} diff --git a/src/main/java/org/torproject/android/service/OrbotRawEventListener.java b/src/main/java/org/torproject/android/service/OrbotRawEventListener.java new file mode 100644 index 0000000..85726a6 --- /dev/null +++ b/src/main/java/org/torproject/android/service/OrbotRawEventListener.java @@ -0,0 +1,259 @@ +package org.torproject.android.service; + +import net.freehaven.tor.control.RawEventListener; +import net.freehaven.tor.control.TorControlCommands; + +import org.torproject.android.service.util.Prefs; +import org.torproject.android.service.util.Utils; +import org.torproject.jni.TorService; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; + +public class OrbotRawEventListener implements RawEventListener { + private final OrbotService mService; + private long mTotalBandwidthWritten, mTotalBandwidthRead; + private final Map hmBuiltNodes; + private final Map exitNodeMap; + private final Set ignoredInternalCircuits; + + private static final String CIRCUIT_BUILD_FLAG_IS_INTERNAL = "IS_INTERNAL"; + private static final String CIRCUIT_BUILD_FLAG_ONE_HOP_TUNNEL = "ONEHOP_TUNNEL"; + + OrbotRawEventListener(OrbotService orbotService) { + mService = orbotService; + mTotalBandwidthRead = 0; + mTotalBandwidthWritten = 0; + hmBuiltNodes = new HashMap<>(); + + exitNodeMap = new HashMap<>(); + ignoredInternalCircuits = new HashSet<>(); + + } + + @Override + public void onEvent(String keyword, String data) { + String[] payload = data.split(" "); + if (TorControlCommands.EVENT_BANDWIDTH_USED.equals(keyword)) { + handleBandwidth(Long.parseLong(payload[0]), Long.parseLong(payload[1])); + } else if (TorControlCommands.EVENT_NEW_DESC.equals(keyword)) { + handleNewDescriptors(payload); + } else if (TorControlCommands.EVENT_STREAM_STATUS.equals(keyword)) { + + handleStreamEventExpandedNotifications(payload[1], payload[3], payload[2], payload[4]); + + if (Prefs.useDebugLogging()) handleStreamEventsDebugLogging(payload[1], payload[0]); + } else if (TorControlCommands.EVENT_CIRCUIT_STATUS.equals(keyword)) { + String status = payload[1]; + String circuitId = payload[0]; + String path; + if (payload.length < 3 || status.equals(TorControlCommands.CIRC_EVENT_LAUNCHED)) + path = ""; + else path = payload[2]; + handleCircuitStatus(status, circuitId, path); + + // don't bother looking up internal circuits that Orbot clients won't directly use + if (data.contains(CIRCUIT_BUILD_FLAG_ONE_HOP_TUNNEL) || data.contains(CIRCUIT_BUILD_FLAG_IS_INTERNAL)) { + ignoredInternalCircuits.add(Integer.parseInt(circuitId)); + } + handleCircuitStatusExpandedNotifications(status, circuitId, path); + + } else if (TorControlCommands.EVENT_OR_CONN_STATUS.equals(keyword)) { + handleConnectionStatus(payload[1], payload[0]); + } else if (TorControlCommands.EVENT_DEBUG_MSG.equals(keyword) || TorControlCommands.EVENT_INFO_MSG.equals(keyword) || TorControlCommands.EVENT_NOTICE_MSG.equals(keyword) || TorControlCommands.EVENT_WARN_MSG.equals(keyword) || TorControlCommands.EVENT_ERR_MSG.equals(keyword)) { + handleDebugMessage(keyword, data); + } else { + String unrecognized = "Message (" + keyword + "): " + data; + mService.logNotice(unrecognized); + } + } + + private void handleBandwidth(long read, long written) { + String message = OrbotService.formatBandwidthCount(mService, read) + " ↓ / " + OrbotService.formatBandwidthCount(mService, written) + " ↑"; + + if (mService.getCurrentStatus().equals(TorService.STATUS_ON)) + mService.showBandwidthNotification(message, read != 0 || written != 0); + + mTotalBandwidthWritten += written; + mTotalBandwidthRead += read; + + mService.sendCallbackBandwidth(written, read, mTotalBandwidthWritten, mTotalBandwidthRead); + + } + + private void handleNewDescriptors(String[] descriptors) { + for (String descriptor : descriptors) + mService.debug("descriptors: " + descriptor); + } + + private void handleStreamEventExpandedNotifications(String status, String target, String circuitId, String clientProtocol) { + if (!status.equals(TorControlCommands.STREAM_EVENT_SUCCEEDED)) return; + if (!clientProtocol.contains("SOCKS5")) return; + int id = Integer.parseInt(circuitId); + if (target.contains(".onion")) + return; // don't display to users exit node info for onion addresses! + ExitNode node = exitNodeMap.get(id); + if (node != null) { + if (node.country == null && !node.querying) { + node.querying = true; + mService.exec(() -> { + try { + String[] networkStatus = mService.conn.getInfo("ns/id/" + node.fingerPrint).split(" "); + node.ipAddress = networkStatus[6]; + String countryCode = mService.conn.getInfo("ip-to-country/" + node.ipAddress).toUpperCase(Locale.getDefault()); + if (!countryCode.equals(TOR_CONTROLLER_COUNTRY_CODE_UNKNOWN)) { + String emoji = Utils.convertCountryCodeToFlagEmoji(countryCode); + String countryName = new Locale("", countryCode).getDisplayName(); + node.country = emoji + " " + countryName; + } else node.country = ""; + mService.setNotificationSubtext(node.toString()); + } catch (Exception ignored) { + } + }); + } else { + if (node.country != null) mService.setNotificationSubtext(node.toString()); + else mService.setNotificationSubtext(null); + } + } + } + + private static final String TOR_CONTROLLER_COUNTRY_CODE_UNKNOWN = "??"; + + private void handleStreamEventsDebugLogging(String streamId, String status) { + mService.debug("StreamStatus (" + streamId + "): " + status); + } + + private void handleCircuitStatusExpandedNotifications(String circuitStatus, String circuitId, String path) { + int id = Integer.parseInt(circuitId); + switch (circuitStatus) { + case TorControlCommands.CIRC_EVENT_BUILT -> { + if (ignoredInternalCircuits.contains(id)) + return; // this circuit won't be used by user clients + String[] nodes = path.split(","); + String exit = nodes[nodes.length - 1]; + String fingerprint = exit.split("~")[0]; + exitNodeMap.put(id, new ExitNode(fingerprint)); + } + case TorControlCommands.CIRC_EVENT_CLOSED -> { + exitNodeMap.remove(id); + ignoredInternalCircuits.remove(id); + } + case TorControlCommands.CIRC_EVENT_FAILED -> ignoredInternalCircuits.remove(id); + } + } + + private void handleCircuitStatus(String circuitStatus, String circuitId, String path) { + if (!Prefs.useDebugLogging()) return; + + StringBuilder sb = new StringBuilder(); + sb.append("Circuit ("); + sb.append((circuitId)); + sb.append(") "); + sb.append(circuitStatus); + sb.append(": "); + + StringTokenizer st = new StringTokenizer(path, ","); + DebugLoggingNode node; + + boolean isFirstNode = true; + int nodeCount = st.countTokens(); + + while (st.hasMoreTokens()) { + String nodePath = st.nextToken(); + String nodeId = null, nodeName = null; + + String[] nodeParts; + + if (nodePath.contains("=")) nodeParts = nodePath.split("="); + else nodeParts = nodePath.split("~"); + + if (nodeParts.length == 1) { + nodeId = nodeParts[0].substring(1); + nodeName = nodeId; + } else if (nodeParts.length == 2) { + nodeId = nodeParts[0].substring(1); + nodeName = nodeParts[1]; + } + + if (nodeId == null) continue; + + node = hmBuiltNodes.get(nodeId); + + if (node == null) { + node = new DebugLoggingNode(); + node.id = nodeId; + node.name = nodeName; + } + + node.status = circuitStatus; + + sb.append(node.name); + + if (st.hasMoreTokens()) sb.append(" > "); + + if (circuitStatus.equals(TorControlCommands.CIRC_EVENT_EXTENDED) && isFirstNode) { + hmBuiltNodes.put(node.id, node); + isFirstNode = false; + } else if (circuitStatus.equals(TorControlCommands.CIRC_EVENT_LAUNCHED)) { + if (Prefs.useDebugLogging() && nodeCount > 3) mService.debug(sb.toString()); + } else if (circuitStatus.equals(TorControlCommands.CIRC_EVENT_CLOSED)) { + hmBuiltNodes.remove(node.id); + } + + } + } + + private void handleConnectionStatus(String status, String unparsedNodeName) { + String message = "orConnStatus (" + parseNodeName(unparsedNodeName) + "): " + status; + mService.debug(message); + } + + private void handleDebugMessage(String severity, String message) { + if (severity.equalsIgnoreCase("debug")) mService.debug(severity + ": " + message); + else mService.logNotice(severity + ": " + message); + } + + public Map getNodes() { + return hmBuiltNodes; + } + + /** + * Used to store metadata about an exit node if expanded notifications are turned on + */ + public static class ExitNode { + ExitNode(String fingerPrint) { + this.fingerPrint = fingerPrint; + } + + public final String fingerPrint; + public String country; + public String ipAddress; + boolean querying = false; + + @Override + public String toString() { + return ipAddress + " " + country; + } + } + + + public static class DebugLoggingNode { + public String status; + public String id; + public String name; + } + + + private static String parseNodeName(String node) { + if (node.indexOf('=') != -1) { + return node.substring(node.indexOf("=") + 1); + } else if (node.indexOf('~') != -1) { + return node.substring(node.indexOf("~") + 1); + } + return node; + } +} diff --git a/src/main/java/org/torproject/android/service/OrbotService.java b/src/main/java/org/torproject/android/service/OrbotService.java new file mode 100644 index 0000000..cfbd8e6 --- /dev/null +++ b/src/main/java/org/torproject/android/service/OrbotService.java @@ -0,0 +1,1559 @@ +/* Copyright (c) 2009-2011, Nathan Freitas, Orbot / The Guardian Project - https://guardianproject.info/apps/orbot */ +/* See LICENSE for licensing information */ + +package org.torproject.android.service; + +import static org.torproject.jni.TorService.ACTION_ERROR; + +import android.annotation.SuppressLint; +import android.app.Application; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.content.res.Configuration; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkInfo; +import android.net.Uri; +import android.net.VpnService; +import android.os.Build; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.provider.BaseColumns; +import android.text.TextUtils; +import android.util.Log; +import android.widget.Toast; + +import net.freehaven.tor.control.TorControlCommands; +import net.freehaven.tor.control.TorControlConnection; + +import org.torproject.android.service.util.CustomTorResourceInstaller; +import org.torproject.android.service.util.PowerConnectionReceiver; +import org.torproject.android.service.util.Prefs; +import org.torproject.android.service.util.Utils; +import org.torproject.android.service.vpn.OrbotVpnManager; +import org.torproject.jni.TorService; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.security.SecureRandom; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Locale; +import java.util.Objects; +import java.util.StringTokenizer; +import java.util.UUID; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import IPtProxy.IPtProxy; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; +import androidx.core.app.NotificationCompat; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +@SuppressWarnings("StringConcatenationInsideStringBufferAppend") +public class OrbotService extends VpnService implements OrbotConstants { + + public final static String BINARY_TOR_VERSION = TorService.VERSION_NAME; + + static final int NOTIFY_ID = 1; + private static final int ERROR_NOTIFY_ID = 3; + + //these will be set dynamically due to build flavors + private static Uri V3_ONION_SERVICES_CONTENT_URI = null;//Uri.parse("content://org.torproject.android.ui.v3onionservice/v3"); + private static Uri V3_CLIENT_AUTH_URI = null;//Uri.parse("content://org.torproject.android.ui.v3onionservice.clientauth/v3auth"); + private final static String NOTIFICATION_CHANNEL_ID = "orbot_channel_1"; + private static final String[] V3_ONION_SERVICE_PROJECTION = new String[]{OnionService._ID, OnionService.NAME, OnionService.DOMAIN, OnionService.PORT, OnionService.ONION_PORT, OnionService.ENABLED, OnionService.PATH}; + private static final String[] V3_CLIENT_AUTH_PROJECTION = new String[]{V3ClientAuth._ID, V3ClientAuth.DOMAIN, V3ClientAuth.HASH, V3ClientAuth.ENABLED}; + + public static int mPortSOCKS = -1; + public static int mPortHTTP = -1; + public static int mPortDns = -1; + public static int mPortTrans = -1; + public static File appBinHome; + public static File appCacheHome; + private final ExecutorService mExecutor = Executors.newCachedThreadPool(); + OrbotRawEventListener mOrbotRawEventListener; + OrbotVpnManager mVpnManager; + Handler mHandler; + ActionBroadcastReceiver mActionBroadcastReceiver; + private String mCurrentStatus = STATUS_OFF; + TorControlConnection conn = null; + private ServiceConnection torServiceConnection; + private boolean shouldUnbindTorService; + private NotificationManager mNotificationManager = null; + private NotificationCompat.Builder mNotifyBuilder; + private File mV3OnionBasePath, mV3AuthBasePath; + + private PowerConnectionReceiver mPowerReceiver; + + private boolean mHasPower = false; + private boolean mHasWifi = false; + + /** + * @param bridgeList bridges that were manually entered into Orbot settings + * @return Array with each bridge as an element, no whitespace entries see issue #289... + */ + private static String[] parseBridgesFromSettings(String bridgeList) { + // this regex replaces lines that only contain whitespace with an empty String + bridgeList = bridgeList.trim().replaceAll("(?m)^[ \t]*\r?\n", ""); + return bridgeList.split("\\n"); + } + + public void debug(String msg) { + Log.d(TAG, msg); + + if (Prefs.useDebugLogging()) { + sendCallbackLogMessage(msg); + } + } + + public void logException(String msg, Exception e) { + if (Prefs.useDebugLogging()) { + Log.e(TAG, msg, e); + var baos = new ByteArrayOutputStream(); + e.printStackTrace(new PrintStream(baos)); + + sendCallbackLogMessage(msg + '\n' + baos); + } else sendCallbackLogMessage(msg); + + } + + private void showConnectedToTorNetworkNotification() { + mNotifyBuilder.setProgress(0, 0, false); + showToolbarNotification(getString(R.string.status_activated), NOTIFY_ID, R.drawable.ic_stat_tor); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + //this doesn't need to be shown to the user unless there is something to do + debug(getString(R.string.log_notice_low_memory_warning)); + } + + private void clearNotifications() { + if (mNotificationManager != null) mNotificationManager.cancelAll(); + + if (mOrbotRawEventListener != null) mOrbotRawEventListener.getNodes().clear(); + } + + @RequiresApi(api = Build.VERSION_CODES.O) + private void createNotificationChannel() { + var mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + var mChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, getString(R.string.app_name), NotificationManager.IMPORTANCE_LOW); + mChannel.setDescription(getString(R.string.app_description)); + mChannel.enableLights(false); + mChannel.enableVibration(false); + mChannel.setShowBadge(false); + mChannel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); + mNotificationManager.createNotificationChannel(mChannel); + } + + @SuppressLint({"NewApi", "RestrictedApi"}) + protected void showToolbarNotification(String notifyMsg, int notifyType, int icon) { + var intent = getPackageManager().getLaunchIntentForPackage(getPackageName()); + var pendIntent = PendingIntent.getActivity(OrbotService.this, 0, intent, PendingIntent.FLAG_IMMUTABLE); + + if (mNotifyBuilder == null) { + mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + mNotifyBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID).setSmallIcon(R.drawable.ic_stat_tor).setContentIntent(pendIntent).setCategory(Notification.CATEGORY_SERVICE); + } + + mNotifyBuilder.setOngoing(true); + + var title = getString(R.string.status_disabled); + if (mCurrentStatus.equals(STATUS_STARTING) || notifyMsg.equals(getString(R.string.status_starting_up))) + title = getString(R.string.status_starting_up); + else if (mCurrentStatus.equals(STATUS_ON)) { + title = getString(R.string.status_activated); + } + + mNotifyBuilder.setContentTitle(title); + + mNotifyBuilder.mActions.clear(); // clear out any notification actions, if any + if (conn != null && mCurrentStatus.equals(STATUS_ON)) { // only add new identity action when there is a connection + var pendingIntentNewNym = PendingIntent.getBroadcast(this, 0, new Intent(TorControlCommands.SIGNAL_NEWNYM), PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); + mNotifyBuilder.addAction(R.drawable.ic_refresh_white_24dp, getString(R.string.menu_new_identity), pendingIntentNewNym); + } else if (mCurrentStatus.equals(STATUS_OFF)) { + var pendingIntentConnect = PendingIntent.getBroadcast(this, 0, new Intent(LOCAL_ACTION_NOTIFICATION_START), PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); + mNotifyBuilder.addAction(R.drawable.ic_stat_tor, getString(R.string.connect_to_tor), pendingIntentConnect); + } + + mNotifyBuilder.setContentText(notifyMsg).setSmallIcon(icon).setTicker(notifyType != NOTIFY_ID ? notifyMsg : null); + + if (!mCurrentStatus.equals(STATUS_ON)) { + mNotifyBuilder.setSubText(null); + } + + if (!mCurrentStatus.equals(STATUS_STARTING)) { + mNotifyBuilder.setProgress(0, 0, false); // removes progress bar + } + + startForeground(NOTIFY_ID, mNotifyBuilder.build()); + } + + public int onStartCommand(Intent intent, int flags, int startId) { + try { + if (intent == null) { + Log.d(TAG, "Got null onStartCommand() intent"); + return Service.START_REDELIVER_INTENT; + } + + final boolean shouldStartVpnFromSystemIntent = !intent.getBooleanExtra(OrbotConstants.EXTRA_NOT_SYSTEM, false); + + if (mCurrentStatus.equals(STATUS_OFF)) + showToolbarNotification(getString(R.string.open_orbot_to_connect_to_tor), NOTIFY_ID, R.drawable.ic_stat_tor); + + if (shouldStartVpnFromSystemIntent) { + Log.d(TAG, "Starting VPN from system intent: " + intent); + showToolbarNotification(getString(R.string.status_starting_up), NOTIFY_ID, R.drawable.ic_stat_tor); + if (VpnService.prepare(this) == null) { + // Power-user mode doesn't matter here. If the system is starting the VPN, i.e. + // via always-on VPN, we need to start it regardless. + Prefs.putUseVpn(true); + mExecutor.execute(new IncomingIntentRouter(new Intent(ACTION_START))); + mExecutor.execute(new IncomingIntentRouter(new Intent(ACTION_START_VPN))); + } else { + Log.wtf(TAG, "Could not start VPN from system because it is not prepared, which should be impossible!"); + } + } else { + mExecutor.execute(new IncomingIntentRouter(intent)); + } + } catch (RuntimeException re) { + //catch this to avoid malicious launches as document Cure53 Audit: ORB-01-009 WP1/2: Orbot DoS via exported activity (High) + } + + return Service.START_REDELIVER_INTENT; + } + + private void showDeactivatedNotification() { + showToolbarNotification(getString(R.string.open_orbot_to_connect_to_tor), NOTIFY_ID, R.drawable.ic_stat_tor); + } + + @Override + public void onDestroy() { + try { + unregisterReceiver(mActionBroadcastReceiver); + + unregisterReceiver(mPowerReceiver); + + ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + connMgr.unregisterNetworkCallback(netCall); + } + + + } catch (IllegalArgumentException iae) { + //not registered yet + } + super.onDestroy(); + } + + private void stopTorAsync(boolean showNotification) { + debug("stopTor"); + + if (showNotification) sendCallbackLogMessage(getString(R.string.status_shutting_down)); + + var connectionPathway = Prefs.getConnectionPathway(); + // todo this needs to handle a lot of different cases that haven't been defined yet + // todo particularly this is true for the smart connection case... + if (connectionPathway.startsWith(Prefs.PATHWAY_SNOWFLAKE) || Prefs.getPrefSmartTrySnowflake()) { + IPtProxy.stopSnowflake(); + } else if (connectionPathway.equals(Prefs.PATHWAY_CUSTOM) || Prefs.getPrefSmartTryObfs4() != null) { + IPtProxy.stopLyrebird(); + } + + stopTor(); + + //stop the foreground priority and make sure to remove the persistent notification + stopForeground(!showNotification); + + if (showNotification) sendCallbackLogMessage(getString(R.string.status_disabled)); + + mPortDns = -1; + mPortSOCKS = -1; + mPortHTTP = -1; + mPortTrans = -1; + + if (!showNotification) { + clearNotifications(); + stopSelf(); + } + } + + private void stopTorOnError(String message) { + stopTorAsync(false); + showToolbarNotification(getString(R.string.unable_to_start_tor) + ": " + message, ERROR_NOTIFY_ID, R.drawable.ic_stat_notifyerr); + } + + private static HashMap mFronts; + + public static void loadCdnFronts(Context context) { + if (mFronts == null) { + mFronts = new HashMap<>(); + + try { + var reader = new BufferedReader(new InputStreamReader(context.getAssets().open("fronts"))); + String line; + while ((line = reader.readLine()) != null) { + int spaceIdx = line.indexOf(' '); + String key = line.substring(0, spaceIdx); + String val = line.substring(spaceIdx + 1); + mFronts.put(key, val); + } + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + public static String getCdnFront(String service) { + return mFronts.get(service); + } + + + private void startSnowflakeClientDomainFronting() { + //this is using the current, default Tor snowflake infrastructure + var target = getCdnFront("snowflake-target"); + var front = getCdnFront("snowflake-front"); + var stunServer = getCdnFront("snowflake-stun"); + + /* + // @param ice Comma-separated list of ICE servers. + // @param url URL of signaling broker. + // @param fronts Comma-separated list of front domains. + // @param ampCache OPTIONAL. URL of AMP cache to use as a proxy for signaling. + // Only needed when you want to do the rendezvous over AMP instead of a domain fronted server. + // @param sqsQueueURL OPTIONAL. URL of SQS Queue to use as a proxy for signaling. + // @param sqsCredsStr OPTIONAL. Credentials to access SQS Queue. + // @param logFile Name of log file. OPTIONAL. Defaults to no log. + // @param logToStateDir Resolve the log file relative to Tor's PT state dir. + // @param keepLocalAddresses Keep local LAN address ICE candidates. + // @param unsafeLogging Prevent logs from being scrubbed. + // @param maxPeers Capacity for number of multiplexed WebRTC peers. DEFAULTs to 1 if less than that. + // @return Port number where Snowflake will listen on, if no error happens during start up. + */ + IPtProxy.startSnowflake(stunServer, target, front, null, null, null, null,true, false, false, 1); + + } + + private void startSnowflakeClientAmpRendezvous() { + var stunServers = getCdnFront("snowflake-stun"); + var target = getCdnFront("snowflake-target-direct"); + var front = getCdnFront("snowflake-amp-front"); + var ampCache = getCdnFront("snowflake-amp-cache"); + IPtProxy.startSnowflake(stunServers, target, front, ampCache, null, null, null, true, false, false, 1); + } + + private final SecureRandom mSecureRandGen = new SecureRandom(); //used to randomly select STUN servers for snowflake + + public synchronized void enableSnowflakeProxy() { // This is to host a snowflake entrance node / bridge + if (!IPtProxy.isSnowflakeProxyRunning()) { + + if (Prefs.limitSnowflakeProxyingWifi() && (!mHasWifi)) + return; + + if (Prefs.limitSnowflakeProxyingCharging() && (!mHasPower)) + return; + + var capacity = 1; + var keepLocalAddresses = false; + var unsafeLogging = false; + var stunServers = getCdnFront("snowflake-stun").split(","); + + int randomIndex = mSecureRandGen.nextInt(stunServers.length); + var stunUrl = stunServers[randomIndex]; + var relayUrl = getCdnFront("snowflake-relay-url"); + var natProbeUrl = getCdnFront("snowflake-nat-probe"); + var brokerUrl = getCdnFront("snowflake-target-direct"); + IPtProxy.startSnowflakeProxy(capacity, brokerUrl, relayUrl, stunUrl, natProbeUrl, null, keepLocalAddresses, unsafeLogging, () -> { + Prefs.addSnowflakeServed(); + if (!Prefs.showSnowflakeProxyMessage()) return; + var message = String.format(getString(R.string.snowflake_proxy_client_connected_msg), ONION_EMOJI, ONION_EMOJI); + new Handler(getMainLooper()).post(() -> Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show()); + }); + logNotice(getString(R.string.log_notice_snowflake_proxy_enabled)); + + if (Prefs.showSnowflakeProxyMessage()) { + var message = getString(R.string.log_notice_snowflake_proxy_enabled); + new Handler(getMainLooper()).post(() -> Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show()); + } + + } + + } + + private final ConnectivityManager.NetworkCallback netCall = new ConnectivityManager.NetworkCallback() { + @Override + public void onAvailable(@NonNull Network network) { + super.onAvailable(network); + checkNetworkForSnowflakeProxy (); + } + + @Override + public void onLost(@NonNull Network network) { + super.onLost(network); + checkNetworkForSnowflakeProxy (); + } + }; + + private void enableSnowflakeProxyNetworkListener () { + if (Prefs.limitSnowflakeProxyingWifi()) { + //check if on wifi + ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + connMgr.registerDefaultNetworkCallback(netCall); + } + + } + } + + public void setHasPower (boolean hasPower) { + mHasPower = hasPower; + if (Prefs.beSnowflakeProxy()) { + if (Prefs.limitSnowflakeProxyingCharging()) { + if (mHasPower) enableSnowflakeProxy(); + else disableSnowflakeProxy(); + } + } + } + private void checkNetworkForSnowflakeProxy () { + ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + NetworkCapabilities netCap = connMgr.getNetworkCapabilities(connMgr.getActiveNetwork()); + if (netCap != null) + mHasWifi = netCap.hasTransport(NetworkCapabilities.TRANSPORT_WIFI); + else + mHasWifi = false; + } + else { + NetworkInfo netInfo = connMgr.getActiveNetworkInfo(); + if (netInfo != null) + mHasWifi = netInfo.getType() == ConnectivityManager.TYPE_WIFI; + } + + if (Prefs.beSnowflakeProxy()) { + if (Prefs.limitSnowflakeProxyingWifi()) { + if (mHasWifi) enableSnowflakeProxy(); + else disableSnowflakeProxy(); + } + } + } + + public synchronized void disableSnowflakeProxy() { + if (IPtProxy.isSnowflakeProxyRunning()) { + IPtProxy.stopSnowflakeProxy(); + logNotice(getString(R.string.log_notice_snowflake_proxy_disabled)); + + if (Prefs.showSnowflakeProxyMessage()) { + var message = getString(R.string.log_notice_snowflake_proxy_disabled); + new Handler(getMainLooper()).post(() -> Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show()); + } + + } + } + + // if someone stops during startup, we may have to wait for the conn port to be setup, so we can properly shutdown tor + private void stopTor() { + if (shouldUnbindTorService) { + unbindService(torServiceConnection); //unbinding from the tor service will stop tor + shouldUnbindTorService = false; + conn = null; + } else { + sendLocalStatusOffBroadcast(); + } + } + + private void requestTorRereadConfig() { + try { + if (conn != null) { + conn.signal(TorControlCommands.SIGNAL_RELOAD); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + protected void logNotice(String msg) { + if (msg != null && msg.trim().length() > 0) { + if (Prefs.useDebugLogging()) Log.d(TAG, msg); + sendCallbackLogMessage(msg); + } + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + @Override + public void onCreate() { + super.onCreate(); + configLanguage(); + try { + //set proper content URIs for current build flavor + V3_ONION_SERVICES_CONTENT_URI = Uri.parse("content://" + getApplicationContext().getPackageName() + ".ui.v3onionservice/v3"); + V3_CLIENT_AUTH_URI = Uri.parse("content://" + getApplicationContext().getPackageName() + ".ui.v3onionservice.clientauth/v3auth"); + + try { + mHandler = new Handler(); + + appBinHome = getFilesDir(); + if (!appBinHome.exists()) appBinHome.mkdirs(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + appCacheHome = new File(getDataDir(), DIRECTORY_TOR_DATA); + } else { + appCacheHome = getDir(DIRECTORY_TOR_DATA, Application.MODE_PRIVATE); + } + + if (!appCacheHome.exists()) appCacheHome.mkdirs(); + + mV3OnionBasePath = new File(getFilesDir().getAbsolutePath(), ONION_SERVICES_DIR); + if (!mV3OnionBasePath.isDirectory()) mV3OnionBasePath.mkdirs(); + + mV3AuthBasePath = new File(getFilesDir().getAbsolutePath(), V3_CLIENT_AUTH_DIR); + if (!mV3AuthBasePath.isDirectory()) mV3AuthBasePath.mkdirs(); + + if (mNotificationManager == null) { + mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + } + + IntentFilter filter = new IntentFilter(TorControlCommands.SIGNAL_NEWNYM); + filter.addAction(CMD_ACTIVE); + filter.addAction(ACTION_STATUS); + filter.addAction(ACTION_ERROR); + filter.addAction(LOCAL_ACTION_NOTIFICATION_START); + + mActionBroadcastReceiver = new ActionBroadcastReceiver(); + registerReceiver(mActionBroadcastReceiver, filter); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) createNotificationChannel(); + + var hasGeoip = new File(appBinHome, GEOIP_ASSET_KEY).exists(); + var hasGeoip6 = new File(appBinHome, GEOIP6_ASSET_KEY).exists(); + + // only write out geoip files if there's an app update or they don't exist + if (!hasGeoip || !hasGeoip6 || Prefs.isGeoIpReinstallNeeded()) { + try { + Log.d(TAG, "Installing geoip files..."); + new CustomTorResourceInstaller(this, appBinHome).installGeoIP(); + Prefs.setIsGeoIpReinstallNeeded(false); + } catch (IOException io) { // user has < 10MB free space on disk... + Log.e(TAG, "Error installing geoip files", io); + } + } + + + pluggableTransportInstall(); + + mVpnManager = new OrbotVpnManager(this); + + loadCdnFronts(this); + } catch (Exception e) { + Log.e(TAG, "Error setting up Orbot", e); + logNotice(getString(R.string.couldn_t_start_tor_process_) + " " + e.getClass().getSimpleName()); + } + + mPowerReceiver = new PowerConnectionReceiver(this); + + IntentFilter ifilter = new IntentFilter(); + ifilter.addAction(Intent.ACTION_POWER_CONNECTED); + ifilter.addAction(Intent.ACTION_POWER_DISCONNECTED); + registerReceiver(mPowerReceiver, ifilter); + + enableSnowflakeProxyNetworkListener(); + + if (Prefs.beSnowflakeProxy() + && !(Prefs.limitSnowflakeProxyingCharging() || Prefs.limitSnowflakeProxyingWifi())) + enableSnowflakeProxy(); + + } catch (RuntimeException re) { + //catch this to avoid malicious launches as document Cure53 Audit: ORB-01-009 WP1/2: Orbot DoS via exported activity (High) + } + } + + private void configLanguage() { + Configuration config = getBaseContext().getResources().getConfiguration(); + Locale locale = new Locale(Prefs.getDefaultLocale()); + Locale.setDefault(locale); + config.locale = locale; + getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics()); + } + + protected String getCurrentStatus() { + return mCurrentStatus; + } + + private void pluggableTransportInstall() { + var fileCacheDir = new File(getCacheDir(), "pt"); + if (!fileCacheDir.exists()) + //noinspection ResultOfMethodCallIgnored + fileCacheDir.mkdir(); + + try { + IPtProxy.setStateLocation(fileCacheDir.getAbsolutePath()); + debug("IPtProxy state: " + IPtProxy.getStateLocation()); + } catch (Error e) { + debug("IPtProxy state: not installed; " + e.getLocalizedMessage()); + + } + } + + private File updateTorrcCustomFile() throws IOException { + var prefs = Prefs.getSharedPrefs(getApplicationContext()); + var extraLines = new StringBuffer(); + + extraLines.append("\n"); + extraLines.append("RunAsDaemon 0").append('\n'); + extraLines.append("AvoidDiskWrites 1").append('\n'); + + var socksPortPref = prefs.getString(PREF_SOCKS, SOCKS_PROXY_PORT_DEFAULT); + if (socksPortPref.indexOf(':') != -1) socksPortPref = socksPortPref.split(":")[1]; + + socksPortPref = checkPortOrAuto(socksPortPref); + + var httpPortPref = prefs.getString(PREF_HTTP, HTTP_PROXY_PORT_DEFAULT); + if (httpPortPref.indexOf(':') != -1) httpPortPref = httpPortPref.split(":")[1]; + + httpPortPref = checkPortOrAuto(httpPortPref); + + var isolate = ""; + if (prefs.getBoolean(PREF_ISOLATE_DEST, false)) { + isolate += " IsolateDestAddr "; + } + if (prefs.getBoolean(PREF_ISOLATE_PORT, false)) { + isolate += " IsolateDestPort "; + } + if (prefs.getBoolean(PREF_ISOLATE_PROTOCOL, false)) { + isolate += " IsolateClientProtocol "; + } + + var ipv6Pref = ""; + if (prefs.getBoolean(PREF_PREFER_IPV6, true)) { + ipv6Pref += " IPv6Traffic PreferIPv6 "; + } + + if (prefs.getBoolean(PREF_DISABLE_IPV4, false)) { + ipv6Pref += " IPv6Traffic NoIPv4Traffic "; + } + + if (!Prefs.openProxyOnAllInterfaces()) { + extraLines.append("SOCKSPort ").append(socksPortPref).append(isolate).append(ipv6Pref).append('\n'); + } else { + extraLines.append("SOCKSPort 0.0.0.0:").append(socksPortPref).append(ipv6Pref).append(isolate).append("\n"); + extraLines.append("SocksPolicy accept *:*").append('\n'); + } + + extraLines.append("SafeSocks 0").append('\n'); + extraLines.append("TestSocks 0").append('\n'); + extraLines.append("HTTPTunnelPort ").append(httpPortPref).append(isolate).append('\n'); + + + if (prefs.getBoolean(PREF_CONNECTION_PADDING, false)) { + extraLines.append("ConnectionPadding 1").append('\n'); + } + + if (prefs.getBoolean(PREF_REDUCED_CONNECTION_PADDING, true)) { + extraLines.append("ReducedConnectionPadding 1").append('\n'); + } + + if (prefs.getBoolean(PREF_CIRCUIT_PADDING, true)) { + extraLines.append("CircuitPadding 1").append('\n'); + } else { + extraLines.append("CircuitPadding 0").append('\n'); + } + + if (prefs.getBoolean(PREF_REDUCED_CIRCUIT_PADDING, true)) { + extraLines.append("ReducedCircuitPadding 1").append('\n'); + } + + var transPort = prefs.getString("pref_transport", TOR_TRANSPROXY_PORT_DEFAULT + ""); + var dnsPort = prefs.getString("pref_dnsport", TOR_DNS_PORT_DEFAULT + ""); + + extraLines.append("TransPort ").append(checkPortOrAuto(transPort)).append(isolate).append('\n'); + extraLines.append("DNSPort ").append(checkPortOrAuto(dnsPort)).append(isolate).append('\n'); + + extraLines.append("VirtualAddrNetwork 10.192.0.0/10").append('\n'); + extraLines.append("AutomapHostsOnResolve 1").append('\n'); + + extraLines.append("DormantClientTimeout 10 minutes").append('\n'); + // extraLines.append("DormantOnFirstStartup 0").append('\n'); + extraLines.append("DormantCanceledByStartup 1").append('\n'); + + extraLines.append("DisableNetwork 0").append('\n'); + + if (Prefs.useDebugLogging()) { + extraLines.append("Log debug syslog").append('\n'); + extraLines.append("SafeLogging 0").append('\n'); + } + + extraLines = processSettingsImpl(extraLines); + + if (extraLines == null) return null; + + extraLines.append('\n'); + extraLines.append(prefs.getString("pref_custom_torrc", "")).append('\n'); + + logNotice(getString(R.string.log_notice_updating_torrc)); + + debug("torrc.custom=" + extraLines); + + var fileTorRcCustom = TorService.getTorrc(this); + updateTorConfigCustom(fileTorRcCustom, extraLines.toString(), false); + return fileTorRcCustom; + } + + private String checkPortOrAuto(String portString) { + if (!portString.equalsIgnoreCase("auto")) { + var isPortUsed = true; + var port = Integer.parseInt(portString); + + while (isPortUsed) { + isPortUsed = Utils.isPortOpen("127.0.0.1", port, 500); + + if (isPortUsed) //the specified port is not available, so let Tor find one instead + port++; + } + return port + ""; + } + + return portString; + } + + public void updateTorConfigCustom(File fileTorRcCustom, String extraLines, boolean append) throws IOException { + var ps = new PrintWriter(new FileWriter(fileTorRcCustom, append)); + ps.print(extraLines); + ps.flush(); + ps.close(); + } + + /** + * Send Orbot's status in reply to an + * {@link #ACTION_START} {@link Intent}, targeted only to + * the app that sent the initial request. If the user has disabled auto- + * starts, the reply {@code ACTION_START Intent} will include the extra + * {@link #STATUS_STARTS_DISABLED} + */ + private void replyWithStatus(Intent startRequest) { + String packageName = startRequest.getStringExtra(EXTRA_PACKAGE_NAME); + + Intent reply = new Intent(ACTION_STATUS); + reply.putExtra(EXTRA_STATUS, mCurrentStatus); + reply.putExtra(EXTRA_SOCKS_PROXY, "socks://127.0.0.1:" + mPortSOCKS); + reply.putExtra(EXTRA_SOCKS_PROXY_HOST, "127.0.0.1"); + reply.putExtra(EXTRA_SOCKS_PROXY_PORT, mPortSOCKS); + reply.putExtra(EXTRA_HTTP_PROXY, "http://127.0.0.1:" + mPortHTTP); + reply.putExtra(EXTRA_HTTP_PROXY_HOST, "127.0.0.1"); + reply.putExtra(EXTRA_HTTP_PROXY_PORT, mPortHTTP); + reply.putExtra(EXTRA_DNS_PORT, mPortDns); + + if (packageName != null) { + reply.setPackage(packageName); + sendBroadcast(reply); + } + + LocalBroadcastManager.getInstance(this).sendBroadcast(reply.setAction(LOCAL_ACTION_STATUS)); + + if (mPortSOCKS != -1 && mPortHTTP != -1) + sendCallbackPorts(mPortSOCKS, mPortHTTP, mPortDns, mPortTrans); + + } + + private boolean showTorServiceErrorMsg = false; + + private static final int TIMEOUT_MS = 15000; + + /** + * The entire process for starting tor and related services is run from this method. + */ + private void startTor() { + try { + if (torServiceConnection != null && conn != null) { + sendCallbackLogMessage(getString(R.string.log_notice_ignoring_start_request)); + showConnectedToTorNetworkNotification(); + return; + } + + mNotifyBuilder.setProgress(100, 0, false); + showToolbarNotification("", NOTIFY_ID, R.drawable.ic_stat_tor); + + if (Prefs.getConnectionPathway().equals(Prefs.PATHWAY_SMART)) { + smartConnectionPathwayStartTor(); + } + startTorService(); + showTorServiceErrorMsg = true; + + if (Prefs.hostOnionServicesEnabled()) { + try { + updateV3OnionNames(); + } catch (SecurityException se) { + logNotice(getString(R.string.log_notice_unable_to_update_onions)); + } + } + } catch (Exception e) { + logException(getString(R.string.unable_to_start_tor) + " " + e.getLocalizedMessage(), e); + stopTorOnError(e.getLocalizedMessage()); + } + } + + static int TRIES_DELETE = 0; + + private void smartConnectionPathwayStartTor() { + Log.d(TAG, "timing out in " + TIMEOUT_MS + "ms"); + new Handler(Looper.getMainLooper()).postDelayed(() -> { + Log.d(TAG, "timed out mCurrentStatus=" + mCurrentStatus); + if (!mCurrentStatus.equals(STATUS_ON)) { + Log.d(TAG, "stopping tor..."); + if (Prefs.getPrefSmartTrySnowflake()) { + Log.d(TAG, "trying snowflake didnt work"); + clearEphemeralSmartConnectionSettings(); + sendSmartStatusToActivity(SMART_STATUS_CIRCUMVENTION_ATTEMPT_FAILED); + } else if (Prefs.getPrefSmartTryObfs4() != null) { + Log.d(TAG, "trying obfs4 didnt work"); + clearEphemeralSmartConnectionSettings(); + sendSmartStatusToActivity(SMART_STATUS_CIRCUMVENTION_ATTEMPT_FAILED); + } else { + sendSmartStatusToActivity(SMART_STATUS_NO_DIRECT); + } + stopTorAsync(true); + } else { + // tor was connected in the allotted time + var obfs4 = Prefs.getPrefSmartTryObfs4(); + if (obfs4 != null) { + // set these obfs4 bridges + Prefs.setBridgesList(obfs4); + Prefs.putConnectionPathway(Prefs.PATHWAY_CUSTOM); + } else if (Prefs.getPrefSmartTrySnowflake()) { + // set snowflake + Prefs.putConnectionPathway(Prefs.PATHWAY_SNOWFLAKE); + } + clearEphemeralSmartConnectionSettings(); + } + }, ((TRIES_DELETE++) != 2) ? TIMEOUT_MS : 10000); + } + + private void clearEphemeralSmartConnectionSettings() { + Prefs.putPrefSmartTryObfs4(null); + Prefs.putPrefSmartTrySnowflake(false); + } + + private void sendSmartStatusToActivity(String status) { + var intent = new Intent(LOCAL_ACTION_SMART_CONNECT_EVENT).putExtra(LOCAL_EXTRA_SMART_STATUS, status); + LocalBroadcastManager.getInstance(this).sendBroadcast(intent); + } + + + private void updateV3OnionNames() throws SecurityException { + var contentResolver = getApplicationContext().getContentResolver(); + var onionServices = contentResolver.query(V3_ONION_SERVICES_CONTENT_URI, null, null, null, null); + if (onionServices != null) { + try { + while (onionServices.moveToNext()) { + var domain_index = onionServices.getColumnIndex(OnionService.DOMAIN); + var path_index = onionServices.getColumnIndex(OnionService.PATH); + var id_index = onionServices.getColumnIndex(OnionService._ID); + if (domain_index < 0 || path_index < 0 || id_index < 0) continue; + var domain = onionServices.getString(domain_index); + if (domain == null || TextUtils.isEmpty(domain)) { + var path = onionServices.getString(path_index); + var v3OnionDirPath = new File(mV3OnionBasePath.getAbsolutePath(), path).getCanonicalPath(); + var hostname = new File(v3OnionDirPath, "hostname"); + if (hostname.exists()) { + int id = onionServices.getInt(id_index); + domain = Utils.readInputStreamAsString(new FileInputStream(hostname)).trim(); + var fields = new ContentValues(); + fields.put(OnionService.DOMAIN, domain); + contentResolver.update(V3_ONION_SERVICES_CONTENT_URI, fields, OnionService._ID + "=" + id, null); + } + } + } + /* + This old status hack is temporary and fixes the issue reported by syphyr at + https://github.com/guardianproject/orbot/pull/556 + Down the line a better approach needs to happen for sending back the onion names updated + status, perhaps just adding it as an extra to the normal Intent callback... + */ + var oldStatus = mCurrentStatus; + var intent = new Intent(LOCAL_ACTION_V3_NAMES_UPDATED); + LocalBroadcastManager.getInstance(this).sendBroadcast(intent); + + mCurrentStatus = oldStatus; + } catch (Exception e) { + e.printStackTrace(); + } + onionServices.close(); + } + } + + private synchronized void startTorService() throws Exception { + updateTorConfigCustom(TorService.getDefaultsTorrc(this), """ + DNSPort 0 + TransPort 0 + DisableNetwork 1 + """, false); + + var fileTorrcCustom = updateTorrcCustomFile(); + if ((!fileTorrcCustom.exists()) || (!fileTorrcCustom.canRead())) return; + + sendCallbackLogMessage(getString(R.string.status_starting_up)); + + torServiceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName componentName, IBinder iBinder) { + + //moved torService to a local variable, since we only need it once + TorService torService = ((TorService.LocalBinder) iBinder).getService(); + + while ((conn = torService.getTorControlConnection()) == null) { + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + //wait another second before we set our own event listener + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + mOrbotRawEventListener = new OrbotRawEventListener(OrbotService.this); + + if (conn != null) { + try { + initControlConnection(); + if (conn == null) + return; // maybe there was an error setting up the control connection + + //override the TorService event listener + conn.addRawEventListener(mOrbotRawEventListener); + + logNotice(getString(R.string.log_notice_connected_to_tor_control_port)); + + //now set our own events + ArrayList events = new ArrayList<>(Arrays.asList(TorControlCommands.EVENT_OR_CONN_STATUS, TorControlCommands.EVENT_CIRCUIT_STATUS, TorControlCommands.EVENT_NOTICE_MSG, TorControlCommands.EVENT_WARN_MSG, TorControlCommands.EVENT_ERR_MSG, TorControlCommands.EVENT_BANDWIDTH_USED, TorControlCommands.EVENT_NEW_DESC, TorControlCommands.EVENT_ADDRMAP)); + if (Prefs.useDebugLogging()) { + events.add(TorControlCommands.EVENT_DEBUG_MSG); + events.add(TorControlCommands.EVENT_INFO_MSG); + } + + if (Prefs.useDebugLogging()) + events.add(TorControlCommands.EVENT_STREAM_STATUS); + + conn.setEvents(events); + logNotice(getString(R.string.log_notice_added_event_handler)); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + @Override + public void onServiceDisconnected(ComponentName componentName) { + if (Prefs.useDebugLogging()) Log.d(TAG, "TorService: onServiceDisconnected"); + sendLocalStatusOffBroadcast(); + } + + @Override + public void onNullBinding(ComponentName componentName) { + Log.w(TAG, "TorService: was unable to bind: onNullBinding"); + } + + @Override + public void onBindingDied(ComponentName componentName) { + Log.w(TAG, "TorService: onBindingDied"); + sendLocalStatusOffBroadcast(); + } + }; + + Intent serviceIntent = new Intent(this, TorService.class); + if (Build.VERSION.SDK_INT < 29) { + shouldUnbindTorService = bindService(serviceIntent, torServiceConnection, BIND_AUTO_CREATE); + } else { + shouldUnbindTorService = bindService(serviceIntent, BIND_AUTO_CREATE, mExecutor, torServiceConnection); + } + } + + private void sendLocalStatusOffBroadcast() { + var localOffStatus = new Intent(LOCAL_ACTION_STATUS).putExtra(EXTRA_STATUS, STATUS_OFF); + LocalBroadcastManager.getInstance(this).sendBroadcast(localOffStatus); + } + + protected void exec(Runnable run) { + mExecutor.execute(run); + } + + private void initControlConnection() { + if (conn != null) { + try { + String confSocks = conn.getInfo("net/listeners/socks"); + StringTokenizer st = new StringTokenizer(confSocks, " "); + if (confSocks.trim().isEmpty()) { + mPortSOCKS = 0; + } else { + confSocks = st.nextToken().split(":")[1]; + confSocks = confSocks.substring(0, confSocks.length() - 1); + mPortSOCKS = Integer.parseInt(confSocks); + } + String confHttp = conn.getInfo("net/listeners/httptunnel"); + if (confHttp.trim().isEmpty()) { + mPortHTTP = 0; + } else { + st = new StringTokenizer(confHttp, " "); + confHttp = st.nextToken().split(":")[1]; + confHttp = confHttp.substring(0, confHttp.length() - 1); + mPortHTTP = Integer.parseInt(confHttp); + } + String confDns = conn.getInfo("net/listeners/dns"); + st = new StringTokenizer(confDns, " "); + if (st.hasMoreTokens()) { + confDns = st.nextToken().split(":")[1]; + confDns = confDns.substring(0, confDns.length() - 1); + mPortDns = Integer.parseInt(confDns); + Prefs.getSharedPrefs(getApplicationContext()).edit().putInt(PREFS_DNS_PORT, mPortDns).apply(); + } + + String confTrans = conn.getInfo("net/listeners/trans"); + st = new StringTokenizer(confTrans, " "); + if (st.hasMoreTokens()) { + confTrans = st.nextToken().split(":")[1]; + confTrans = confTrans.substring(0, confTrans.length() - 1); + mPortTrans = Integer.parseInt(confTrans); + } + + sendCallbackPorts(mPortSOCKS, mPortHTTP, mPortDns, mPortTrans); + + } catch (IOException e) { + e.printStackTrace(); + stopTorOnError(e.getLocalizedMessage()); + conn = null; + } catch (NullPointerException npe) { + Log.e(TAG, "NPE reached... how???"); + npe.printStackTrace(); + stopTorOnError("stopping from NPE"); + conn = null; + } + } + } + + public void sendSignalActive() { + if (conn != null && mCurrentStatus.equals(STATUS_ON)) { + try { + conn.signal("ACTIVE"); + } catch (IOException e) { + debug("error send active: " + e.getLocalizedMessage()); + } + } + } + + public void newIdentity() { + if (conn == null) return; + new Thread() { + public void run() { + try { + if (conn != null && mCurrentStatus.equals(STATUS_ON)) { + mNotifyBuilder.setSubText(null); // clear previous exit node info if present + showToolbarNotification(getString(R.string.newnym), NOTIFY_ID, R.drawable.ic_stat_tor); + conn.signal(TorControlCommands.SIGNAL_NEWNYM); + } + } catch (Exception ioe) { + debug("error requesting newnym: " + ioe.getLocalizedMessage()); + } + } + }.start(); + } + + protected void sendCallbackBandwidth(long lastWritten, long lastRead, long totalWritten, long totalRead) { + LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(LOCAL_ACTION_BANDWIDTH).putExtra(LOCAL_EXTRA_TOTAL_WRITTEN, totalWritten).putExtra(LOCAL_EXTRA_TOTAL_READ, totalRead).putExtra(LOCAL_EXTRA_LAST_WRITTEN, lastWritten).putExtra(LOCAL_EXTRA_LAST_READ, lastRead)); + } + + private void sendCallbackLogMessage(final String logMessage) { + var notificationMessage = logMessage; + var localIntent = new Intent(LOCAL_ACTION_LOG).putExtra(LOCAL_EXTRA_LOG, logMessage); + if (logMessage.contains(LOG_NOTICE_HEADER)) { + notificationMessage = notificationMessage.substring(LOG_NOTICE_HEADER.length()); + if (notificationMessage.contains(LOG_NOTICE_BOOTSTRAPPED)) { + var percent = notificationMessage.substring(LOG_NOTICE_BOOTSTRAPPED.length()); + percent = percent.substring(0, percent.indexOf('%')).trim(); + localIntent.putExtra(LOCAL_EXTRA_BOOTSTRAP_PERCENT, percent); + mNotifyBuilder.setProgress(100, Integer.parseInt(percent), false); + notificationMessage = notificationMessage.substring(notificationMessage.indexOf(':') + 1).trim(); + } + } + showToolbarNotification(notificationMessage, NOTIFY_ID, R.drawable.ic_stat_tor); + mHandler.post(() -> LocalBroadcastManager.getInstance(OrbotService.this).sendBroadcast(localIntent)); + } + + private void sendCallbackPorts(int socksPort, int httpPort, int dnsPort, int transPort) { + var intent = new Intent(LOCAL_ACTION_PORTS).putExtra(EXTRA_SOCKS_PROXY_PORT, socksPort).putExtra(EXTRA_HTTP_PROXY_PORT, httpPort).putExtra(EXTRA_DNS_PORT, dnsPort).putExtra(EXTRA_TRANS_PORT, transPort); + + LocalBroadcastManager.getInstance(this).sendBroadcast(intent); + if (Prefs.useVpn() && mVpnManager != null) mVpnManager.handleIntent(new Builder(), intent); + } + + private StringBuffer processSettingsImpl(StringBuffer extraLines) throws IOException { + logNotice(getString(R.string.updating_settings_in_tor_service)); + var prefs = Prefs.getSharedPrefs(getApplicationContext()); + var becomeRelay = prefs.getBoolean(OrbotConstants.PREF_OR, false); + var ReachableAddresses = prefs.getBoolean(OrbotConstants.PREF_REACHABLE_ADDRESSES, false); + var enableStrictNodes = prefs.getBoolean("pref_strict_nodes", false); + var entranceNodes = prefs.getString("pref_entrance_nodes", ""); + var exitNodes = prefs.getString("pref_exit_nodes", ""); + var excludeNodes = prefs.getString("pref_exclude_nodes", ""); + + String pathway = Prefs.getConnectionPathway(); + if (pathway.equals(Prefs.PATHWAY_SMART)) { + // todo for now ... + } else if (pathway.equals(Prefs.PATHWAY_DIRECT)) { + extraLines = processSettingsImplDirectPathway(extraLines); + } else { + // snowflake or obfs4 + extraLines.append("UseBridges 1").append('\n'); + if (pathway.startsWith(Prefs.PATHWAY_SNOWFLAKE) || Prefs.getPrefSmartTrySnowflake()) { + extraLines = processSettingsImplSnowflake(extraLines); + } else if (pathway.equals(Prefs.PATHWAY_CUSTOM) || Prefs.getPrefSmartTryObfs4() != null) { + extraLines = processSettingsImplObfs4(extraLines); + } + } + var fileGeoIP = new File(appBinHome, GEOIP_ASSET_KEY); + var fileGeoIP6 = new File(appBinHome, GEOIP6_ASSET_KEY); + + if (fileGeoIP.exists()) { // only apply geoip if it exists + extraLines.append("GeoIPFile" + ' ').append(fileGeoIP.getCanonicalPath()).append('\n'); + extraLines.append("GeoIPv6File" + ' ').append(fileGeoIP6.getCanonicalPath()).append('\n'); + } + + if (!TextUtils.isEmpty(entranceNodes)) + extraLines.append("EntryNodes ").append(entranceNodes).append('\n'); + + if (!TextUtils.isEmpty(exitNodes)) + extraLines.append("ExitNodes ").append(exitNodes).append('\n'); + + if (!TextUtils.isEmpty(excludeNodes)) + extraLines.append("ExcludeNodes ").append(excludeNodes).append('\n'); + + extraLines.append("StrictNodes ").append(enableStrictNodes ? "1" : "0").append('\n'); + + extraLines.append("\n"); + + try { + if (ReachableAddresses) { + var ReachableAddressesPorts = prefs.getString(PREF_REACHABLE_ADDRESSES_PORTS, "*:80,*:443"); + extraLines.append("ReachableAddresses" + ' ').append(ReachableAddressesPorts).append('\n'); + } + + } catch (Exception e) { + showToolbarNotification(getString(R.string.your_reachableaddresses_settings_caused_an_exception_), ERROR_NOTIFY_ID, R.drawable.ic_stat_notifyerr); + return null; + } + + try { + if (becomeRelay && (!Prefs.bridgesEnabled()) && (!ReachableAddresses)) { + var ORPort = Integer.parseInt(Objects.requireNonNull(prefs.getString(PREF_OR_PORT, "9001"))); + var nickname = prefs.getString(PREF_OR_NICKNAME, "Orbot"); + var dnsFile = writeDNSFile(); + + extraLines.append("ServerDNSResolvConfFile").append(' ').append(dnsFile).append('\n'); // DNSResolv is not a typo + extraLines.append("ORPort").append(' ').append(ORPort).append('\n'); + extraLines.append("Nickname").append(' ').append(nickname).append('\n'); + extraLines.append("ExitPolicy").append(' ').append("reject *:*").append('\n'); + + } + } catch (Exception e) { + showToolbarNotification(getString(R.string.your_relay_settings_caused_an_exception_), ERROR_NOTIFY_ID, R.drawable.ic_stat_notifyerr); + return null; + } + + if (Prefs.hostOnionServicesEnabled()) { + var contentResolver = getApplicationContext().getContentResolver(); + addV3OnionServicesToTorrc(extraLines, contentResolver); + addV3ClientAuthToTorrc(extraLines, contentResolver); + } + + return extraLines; + } + + private StringBuffer processSettingsImplSnowflake(StringBuffer extraLines) { + Log.d(TAG, "in snowflake torrc config"); + extraLines.append("ClientTransportPlugin snowflake socks5 127.0.0.1:" + IPtProxy.snowflakePort()).append('\n'); + extraLines.append("Bridge ").append(getCdnFront("snowflake-broker-1")).append("\n"); + extraLines.append("Bridge ").append(getCdnFront("snowflake-broker-2")).append("\n"); + return extraLines; + } + + private StringBuffer processSettingsImplObfs4(StringBuffer extraLines) { + Log.d(TAG, "in obfs4 torrc config"); + extraLines.append("ClientTransportPlugin obfs3 socks5 127.0.0.1:" + IPtProxy.obfs3Port()).append('\n'); + extraLines.append("ClientTransportPlugin obfs4 socks5 127.0.0.1:" + IPtProxy.obfs4Port()).append('\n'); + var bridgeList = ""; + if (Prefs.getConnectionPathway().equals(Prefs.PATHWAY_CUSTOM)) { + bridgeList = Prefs.getBridgesList(); + } else bridgeList = Prefs.getPrefSmartTryObfs4(); + var customBridges = parseBridgesFromSettings(bridgeList); + for (var b : customBridges) + extraLines.append("Bridge ").append(b).append("\n"); + return extraLines; + } + + private StringBuffer processSettingsImplDirectPathway(StringBuffer extraLines) { + var prefs = Prefs.getSharedPrefs(getApplicationContext()); + extraLines.append("UseBridges 0").append('\n'); + if (!Prefs.useVpn()) { //set the proxy here if we aren't using a bridge + var proxyType = prefs.getString("pref_proxy_type", null); + if (proxyType != null && proxyType.length() > 0) { + var proxyHost = prefs.getString("pref_proxy_host", null); + var proxyPort = prefs.getString("pref_proxy_port", null); + var proxyUser = prefs.getString("pref_proxy_username", null); + var proxyPass = prefs.getString("pref_proxy_password", null); + + if ((proxyHost != null && proxyHost.length() > 0) && (proxyPort != null && proxyPort.length() > 0)) { + extraLines.append(proxyType).append("Proxy").append(' ').append(proxyHost).append(':').append(proxyPort).append('\n'); + + if (proxyUser != null && proxyPass != null) { + if (proxyType.equalsIgnoreCase("socks5")) { + extraLines.append("Socks5ProxyUsername").append(' ').append(proxyUser).append('\n'); + extraLines.append("Socks5ProxyPassword").append(' ').append(proxyPass).append('\n'); + } else + extraLines.append(proxyType).append("ProxyAuthenticator").append(' ').append(proxyUser).append(':').append(proxyPort).append('\n'); + + } else if (proxyPass != null) + extraLines.append(proxyType).append("ProxyAuthenticator").append(' ').append(proxyUser).append(':').append(proxyPort).append('\n'); + } + } + } + return extraLines; + } + + void showBandwidthNotification(String message, boolean isActiveTransfer) { + if (!mCurrentStatus.equals(STATUS_ON)) return; + var icon = !isActiveTransfer ? R.drawable.ic_stat_tor : R.drawable.ic_stat_tor_xfer; + showToolbarNotification(message, NOTIFY_ID, icon); + } + + public static String formatBandwidthCount(Context context, long bitsPerSecond) { + var nf = NumberFormat.getInstance(Locale.getDefault()); + if (bitsPerSecond < 1e6) + return nf.format(Math.round(((float) ((int) (bitsPerSecond * 10 / 1024)) / 10))) + context.getString(R.string.kibibyte_per_second); + else + return nf.format(Math.round(((float) ((int) (bitsPerSecond * 100 / 1024 / 1024)) / 100))) + context.getString(R.string.mebibyte_per_second); + } + + private void addV3OnionServicesToTorrc(StringBuffer torrc, ContentResolver contentResolver) { + try { + var onionServices = contentResolver.query(V3_ONION_SERVICES_CONTENT_URI, V3_ONION_SERVICE_PROJECTION, OnionService.ENABLED + "=1", null, null); + if (onionServices != null) { + while (onionServices.moveToNext()) { + var id_index = onionServices.getColumnIndex(OnionService._ID); + var port_index = onionServices.getColumnIndex(OnionService.PORT); + var onion_port_index = onionServices.getColumnIndex(OnionService.ONION_PORT); + var path_index = onionServices.getColumnIndex(OnionService.PATH); + var domain_index = onionServices.getColumnIndex(OnionService.DOMAIN); + // Ensure that are have all the indexes before trying to use them + if (id_index < 0 || port_index < 0 || onion_port_index < 0 || path_index < 0 || domain_index < 0) + continue; + + var id = onionServices.getInt(id_index); + var localPort = onionServices.getInt(port_index); + var onionPort = onionServices.getInt(onion_port_index); + var path = onionServices.getString(path_index); + var domain = onionServices.getString(domain_index); + if (path == null) { + path = "v3"; + if (domain == null) path += UUID.randomUUID().toString(); + else path += localPort; + var cv = new ContentValues(); + cv.put(OnionService.PATH, path); + contentResolver.update(V3_ONION_SERVICES_CONTENT_URI, cv, OnionService._ID + "=" + id, null); + } + var v3DirPath = new File(mV3OnionBasePath.getAbsolutePath(), path).getCanonicalPath(); + torrc.append("HiddenServiceDir ").append(v3DirPath).append("\n"); + torrc.append("HiddenServiceVersion 3").append("\n"); + torrc.append("HiddenServicePort ").append(onionPort).append(" 127.0.0.1:").append(localPort).append("\n"); + } + onionServices.close(); + } + } catch (Exception e) { + Log.e(TAG, e.getMessage()); + } + } + + public static String buildV3ClientAuthFile(String domain, String keyHash) { + return domain + ":descriptor:x25519:" + keyHash; + } + + private void addV3ClientAuthToTorrc(StringBuffer torrc, ContentResolver contentResolver) { + var v3auths = contentResolver.query(V3_CLIENT_AUTH_URI, V3_CLIENT_AUTH_PROJECTION, V3ClientAuth.ENABLED + "=1", null, null); + if (v3auths != null) { + for (File file : mV3AuthBasePath.listFiles()) { + if (!file.isDirectory()) + file.delete(); // todo the adapter should maybe just write these files and not do this in service... + } + torrc.append("ClientOnionAuthDir " + mV3AuthBasePath.getAbsolutePath()).append('\n'); + try { + int i = 0; + while (v3auths.moveToNext()) { + var domain_index = v3auths.getColumnIndex(V3ClientAuth.DOMAIN); + var hash_index = v3auths.getColumnIndex(V3ClientAuth.HASH); + // Ensure that are have all the indexes before trying to use them + if (domain_index < 0 || hash_index < 0) continue; + var domain = v3auths.getString(domain_index); + var hash = v3auths.getString(hash_index); + var authFile = new File(mV3AuthBasePath, (i++) + ".auth_private"); + authFile.createNewFile(); + var fos = new FileOutputStream(authFile); + fos.write(buildV3ClientAuthFile(domain, hash).getBytes()); + fos.close(); + } + } catch (Exception e) { + Log.e(TAG, "error adding v3 client auth..."); + } finally { + v3auths.close(); + } + } + } + + //using Google DNS for now as the public DNS server + private String writeDNSFile() throws IOException { + var file = new File(appBinHome, "resolv.conf"); + var bw = new PrintWriter(new FileWriter(file)); + bw.println("nameserver 8.8.8.8"); + bw.println("nameserver 8.8.4.4"); + bw.close(); + return file.getCanonicalPath(); + } + + @SuppressLint("NewApi") + @Override + public void onTrimMemory(int level) { + super.onTrimMemory(level); + switch (level) { + case TRIM_MEMORY_BACKGROUND -> debug("trim memory requested: app in the background"); + case TRIM_MEMORY_COMPLETE -> debug("trim memory requested: cleanup all memory"); + case TRIM_MEMORY_MODERATE -> debug("trim memory requested: clean up some memory"); + case TRIM_MEMORY_RUNNING_CRITICAL -> debug("trim memory requested: memory on device is very low and critical"); + case TRIM_MEMORY_RUNNING_LOW -> debug("trim memory requested: memory on device is running low"); + case TRIM_MEMORY_RUNNING_MODERATE -> debug("trim memory requested: memory on device is moderate"); + case TRIM_MEMORY_UI_HIDDEN -> debug("trim memory requested: app is not showing UI anymore"); + } + } + + public void setNotificationSubtext(String message) { + if (mNotifyBuilder != null) { + // stop showing expanded notifications if the user changed the after starting Orbot + // if (!Prefs.showExpandedNotifications()) message = null; + mNotifyBuilder.setSubText(message); + } + } + + @Override + public IBinder onBind(Intent intent) { + Log.d(TAG, "OrbotService: onBind"); + return super.onBind(intent); // invoking super class will call onRevoke() when appropriate + } + + // system calls this method when VPN disconnects (either by the user or another VPN app) + @Override + public void onRevoke() { + Prefs.putUseVpn(false); + mVpnManager.handleIntent(new Builder(), new Intent(ACTION_STOP_VPN)); + // tell UI, if it's open, to update immediately (don't wait for onResume() in Activity...) + LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(ACTION_STOP_VPN)); + } + + private void setExitNode(String newExits) { + + if (TextUtils.isEmpty(newExits)) { + Prefs.setExitNodes(""); + + if (conn != null) { + try { + var resetBuffer = new ArrayList(); + resetBuffer.add("ExitNodes"); + resetBuffer.add("StrictNodes"); + conn.resetConf(resetBuffer); + conn.setConf("DisableNetwork", "1"); + conn.setConf("DisableNetwork", "0"); + + } catch (Exception ioe) { + Log.e(TAG, "Connection exception occurred resetting exits", ioe); + } + } + } else { + + Prefs.setExitNodes("{" + newExits + "}"); + + if (conn != null) { + try { + var fileGeoIP = new File(appBinHome, GEOIP_ASSET_KEY); + var fileGeoIP6 = new File(appBinHome, GEOIP6_ASSET_KEY); + + conn.setConf("GeoIPFile", fileGeoIP.getCanonicalPath()); + conn.setConf("GeoIPv6File", fileGeoIP6.getCanonicalPath()); + conn.setConf("ExitNodes", newExits); + conn.setConf("StrictNodes", "1"); + conn.setConf("DisableNetwork", "1"); + conn.setConf("DisableNetwork", "0"); + + } catch (Exception ioe) { + Log.e(TAG, "Connection exception occurred resetting exits", ioe); + } + } + } + } + + public static final class OnionService implements BaseColumns { + public static final String NAME = "name"; + public static final String PORT = "port"; + public static final String ONION_PORT = "onion_port"; + public static final String DOMAIN = "domain"; + public static final String ENABLED = "enabled"; + public static final String PATH = "filepath"; + } + + public static final class V3ClientAuth implements BaseColumns { + public static final String DOMAIN = "domain"; + public static final String HASH = "hash"; + public static final String ENABLED = "enabled"; + } + + + private class IncomingIntentRouter implements Runnable { + final Intent mIntent; + + public IncomingIntentRouter(Intent intent) { + mIntent = intent; + } + + public void run() { + var action = mIntent.getAction(); + if (TextUtils.isEmpty(action)) return; + switch (action) { + case ACTION_START -> { + var connectionPathway = Prefs.getConnectionPathway(); + if (connectionPathway.equals(Prefs.PATHWAY_SNOWFLAKE) || Prefs.getPrefSmartTrySnowflake()) { + startSnowflakeClientDomainFronting(); + } else if (connectionPathway.equals(Prefs.PATHWAY_SNOWFLAKE_AMP)) { + startSnowflakeClientAmpRendezvous(); + } else if (connectionPathway.equals(Prefs.PATHWAY_CUSTOM) || Prefs.getPrefSmartTryObfs4() != null) { + IPtProxy.startLyrebird("DEBUG", false, false, null); + } + startTor(); + replyWithStatus(mIntent); + if (Prefs.useVpn()) { + if (mVpnManager != null && (!mVpnManager.isStarted())) { // start VPN here + Intent vpnIntent = VpnService.prepare(OrbotService.this); + if (vpnIntent == null) { //then we can run the VPN + mVpnManager.handleIntent(new Builder(), mIntent); + } + } + + if (mPortSOCKS != -1 && mPortHTTP != -1) + sendCallbackPorts(mPortSOCKS, mPortHTTP, mPortDns, mPortTrans); + } + } + case ACTION_STOP -> { + var userIsQuittingOrbot = mIntent.getBooleanExtra(ACTION_STOP_FOREGROUND_TASK, false); + stopTorAsync(!userIsQuittingOrbot); + } + case ACTION_UPDATE_ONION_NAMES -> updateV3OnionNames(); + case ACTION_STOP_FOREGROUND_TASK -> stopForeground(true); + case ACTION_START_VPN -> { + if (mVpnManager != null && (!mVpnManager.isStarted())) { + //start VPN here + Intent vpnIntent = VpnService.prepare(OrbotService.this); + if (vpnIntent == null) { //then we can run the VPN + mVpnManager.handleIntent(new Builder(), mIntent); + } + } + if (mPortSOCKS != -1 && mPortHTTP != -1) + sendCallbackPorts(mPortSOCKS, mPortHTTP, mPortDns, mPortTrans); + } + case ACTION_STOP_VPN -> { + if (mVpnManager != null) mVpnManager.handleIntent(new Builder(), mIntent); + } + case ACTION_RESTART_VPN -> { + if (mVpnManager != null) mVpnManager.restartVPN(new Builder()); + } + case ACTION_STATUS -> { + if (mCurrentStatus.equals(STATUS_OFF)) + showToolbarNotification(getString(R.string.open_orbot_to_connect_to_tor), NOTIFY_ID, R.drawable.ic_stat_tor); + replyWithStatus(mIntent); + } + case TorControlCommands.SIGNAL_RELOAD -> requestTorRereadConfig(); + case TorControlCommands.SIGNAL_NEWNYM -> newIdentity(); + case CMD_ACTIVE -> { + sendSignalActive(); + replyWithStatus(mIntent); + } + case CMD_SET_EXIT -> setExitNode(mIntent.getStringExtra("exit")); + case ACTION_LOCAL_LOCALE_SET -> configLanguage(); + case CMD_SNOWFLAKE_PROXY -> { + if (Prefs.beSnowflakeProxy()) { + enableSnowflakeProxy(); + } else disableSnowflakeProxy(); + } + default -> Log.w(TAG, "unhandled OrbotService Intent: " + action); + } + } + } + + private class ActionBroadcastReceiver extends BroadcastReceiver { + public void onReceive(Context context, Intent intent) { + switch (intent.getAction()) { + case TorControlCommands.SIGNAL_NEWNYM -> newIdentity(); + case CMD_ACTIVE -> sendSignalActive(); + case LOCAL_ACTION_NOTIFICATION_START -> startTor(); + case ACTION_ERROR -> { + if (showTorServiceErrorMsg) { + Toast.makeText(context, getString(R.string.orbot_config_invalid), Toast.LENGTH_LONG).show(); + showTorServiceErrorMsg = false; + } + stopTor(); + } + case ACTION_STATUS -> { + // hack for https://github.com/guardianproject/tor-android/issues/73 remove when fixed + var newStatus = intent.getStringExtra(EXTRA_STATUS); + if (mCurrentStatus.equals(STATUS_OFF) && newStatus.equals(STATUS_STOPPING)) + break; + mCurrentStatus = newStatus; + if (mCurrentStatus.equals(STATUS_OFF)) { + showDeactivatedNotification(); + } + sendStatusToOrbotActivity(); + } + } + } + } + + private void sendStatusToOrbotActivity() { + var localStatus = new Intent(LOCAL_ACTION_STATUS).putExtra(EXTRA_STATUS, mCurrentStatus); + LocalBroadcastManager.getInstance(OrbotService.this).sendBroadcast(localStatus); // update the activity with what's new + } +} diff --git a/src/main/java/org/torproject/android/service/StartTorReceiver.java b/src/main/java/org/torproject/android/service/StartTorReceiver.java new file mode 100644 index 0000000..fa5229b --- /dev/null +++ b/src/main/java/org/torproject/android/service/StartTorReceiver.java @@ -0,0 +1,43 @@ +package org.torproject.android.service; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.text.TextUtils; + +import androidx.core.content.ContextCompat; + +import org.torproject.android.service.util.Prefs; + + +public class StartTorReceiver extends BroadcastReceiver implements OrbotConstants { + + @Override + public void onReceive(Context context, Intent intent) { + + try { + /* sanitize the Intent before forwarding it to OrbotService */ + Prefs.setContext(context); + String action = intent.getAction(); + if (TextUtils.equals(action, ACTION_START)) { + String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME); + if (Prefs.allowBackgroundStarts()) { + Intent startTorIntent = new Intent(context, OrbotService.class).setAction(action).putExtra(OrbotConstants.EXTRA_NOT_SYSTEM, true); + if (packageName != null) { + startTorIntent.putExtra(OrbotService.EXTRA_PACKAGE_NAME, packageName); + } + ContextCompat.startForegroundService(context, startTorIntent); + } else if (!TextUtils.isEmpty(packageName)) { + // let the requesting app know that the user has disabled + // starting via Intent + Intent startsDisabledIntent = new Intent(ACTION_STATUS); + startsDisabledIntent.putExtra(EXTRA_STATUS, STATUS_STARTS_DISABLED); + startsDisabledIntent.setPackage(packageName); + context.sendBroadcast(startsDisabledIntent); + } + } + } catch (RuntimeException re) { + //catch this to avoid malicious launches as document Cure53 Audit: ORB-01-009 WP1/2: Orbot DoS via exported activity (High) + } + } +} diff --git a/src/main/java/org/torproject/android/service/util/CustomTorResourceInstaller.java b/src/main/java/org/torproject/android/service/util/CustomTorResourceInstaller.java new file mode 100644 index 0000000..4bd19fc --- /dev/null +++ b/src/main/java/org/torproject/android/service/util/CustomTorResourceInstaller.java @@ -0,0 +1,80 @@ +package org.torproject.android.service.util; + +import android.content.Context; + +import org.torproject.android.service.OrbotConstants; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.zip.ZipInputStream; + +public class CustomTorResourceInstaller { + + private final File installFolder; + private final Context context; + + public CustomTorResourceInstaller(Context context, File installFolder) { + this.installFolder = installFolder; + this.context = context; + } + + /* + * Write the inputstream contents to the file + */ + private static void streamToFile(InputStream stm, File outFile, boolean append, boolean zip) throws IOException { + byte[] buffer = new byte[1024]; + + int bytecount; + + OutputStream stmOut = new FileOutputStream(outFile.getAbsolutePath(), append); + ZipInputStream zis = null; + + if (zip) { + zis = new ZipInputStream(stm); + zis.getNextEntry(); + stm = zis; + } + + while ((bytecount = stm.read(buffer)) > 0) { + stmOut.write(buffer, 0, bytecount); + } + + stmOut.close(); + stm.close(); + + if (zis != null) zis.close(); + } + + /* + * Extract the Tor resources from the APK file using ZIP + */ + public void installGeoIP() throws IOException { + if (!installFolder.exists()) installFolder.mkdirs(); + assetToFile(OrbotConstants.GEOIP_ASSET_KEY, OrbotConstants.GEOIP_ASSET_KEY, false, false); + assetToFile(OrbotConstants.GEOIP6_ASSET_KEY, OrbotConstants.GEOIP6_ASSET_KEY, false, false); + } + + /* + * Reads file from assetPath/assetKey writes it to the install folder + */ + private File assetToFile(String assetPath, String assetKey, boolean isZipped, boolean isExecutable) throws IOException { + InputStream is = context.getAssets().open(assetPath); + File outFile = new File(installFolder, assetKey); + streamToFile(is, outFile, false, isZipped); + if (isExecutable) { + setExecutable(outFile); + } + return outFile; + } + + private void setExecutable(File fileBin) { + fileBin.setReadable(true); + fileBin.setExecutable(true); + fileBin.setWritable(false); + fileBin.setWritable(true, true); + } +} + diff --git a/src/main/java/org/torproject/android/service/util/PowerConnectionReceiver.java b/src/main/java/org/torproject/android/service/util/PowerConnectionReceiver.java new file mode 100644 index 0000000..b0ac28e --- /dev/null +++ b/src/main/java/org/torproject/android/service/util/PowerConnectionReceiver.java @@ -0,0 +1,32 @@ +package org.torproject.android.service.util; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import org.torproject.android.service.OrbotService; + +import java.util.Objects; + +public class PowerConnectionReceiver extends BroadcastReceiver { + + private final OrbotService mService; + + public PowerConnectionReceiver(OrbotService service) { + mService = service; + } + + @Override + public void onReceive(Context context, Intent intent) { + + if (Prefs.limitSnowflakeProxyingCharging()) { + if (Objects.equals(intent.getAction(), Intent.ACTION_POWER_CONNECTED)) { + mService.setHasPower(true); + + } else if (Objects.equals(intent.getAction(), Intent.ACTION_POWER_DISCONNECTED)) { + mService.setHasPower(false); + } + } + } + +} \ No newline at end of file diff --git a/src/main/java/org/torproject/android/service/util/Prefs.java b/src/main/java/org/torproject/android/service/util/Prefs.java new file mode 100644 index 0000000..36fed01 --- /dev/null +++ b/src/main/java/org/torproject/android/service/util/Prefs.java @@ -0,0 +1,254 @@ +package org.torproject.android.service.util; + +import android.content.Context; +import android.content.SharedPreferences; + +import androidx.work.ExistingPeriodicWorkPolicy; +import androidx.work.PeriodicWorkRequest; +import androidx.work.WorkManager; + +import org.torproject.android.service.OrbotConstants; + +import java.util.Locale; +import java.util.concurrent.TimeUnit; + +public class Prefs { + + private final static String PREF_BRIDGES_ENABLED = "pref_bridges_enabled"; + private final static String PREF_BRIDGES_LIST = "pref_bridges_list"; + private final static String PREF_DEFAULT_LOCALE = "pref_default_locale"; + private final static String PREF_DETECT_ROOT = "pref_detect_root"; + private final static String PREF_ENABLE_LOGGING = "pref_enable_logging"; + private final static String PREF_START_ON_BOOT = "pref_start_boot"; + private final static String PREF_ALLOW_BACKGROUND_STARTS = "pref_allow_background_starts"; + private final static String PREF_OPEN_PROXY_ON_ALL_INTERFACES = "pref_open_proxy_on_all_interfaces"; + private final static String PREF_USE_VPN = "pref_vpn"; + private final static String PREF_EXIT_NODES = "pref_exit_nodes"; + private final static String PREF_BE_A_SNOWFLAKE = "pref_be_a_snowflake"; + private final static String PREF_SHOW_SNOWFLAKE_MSG = "pref_show_snowflake_proxy_msg"; + private final static String PREF_BE_A_SNOWFLAKE_LIMIT_WIFI = "pref_be_a_snowflake_limit_wifi"; + private final static String PREF_BE_A_SNOWFLAKE_LIMIT_CHARGING = "pref_be_a_snowflake_limit_charing"; + + private final static String PREF_SMART_TRY_SNOWFLAKE = "pref_smart_try_snowflake"; + private final static String PREF_SMART_TRY_OBFS4 = "pref_smart_try_obfs"; + private static final String PREF_POWER_USER_MODE = "pref_power_user"; + + + private final static String PREF_HOST_ONION_SERVICES = "pref_host_onionservices"; + + private final static String PREF_SNOWFLAKES_SERVED_COUNT = "pref_snowflakes_served"; + private final static String PREF_SNOWFLAKES_SERVED_COUNT_WEEKLY = "pref_snowflakes_served_weekly"; + + private static final String PREF_CURRENT_VERSION = "pref_current_version"; + + private static final String PREF_CONNECTION_PATHWAY = "pref_connection_pathway"; + public static final String PATHWAY_SMART = "smart", PATHWAY_DIRECT = "direct", + PATHWAY_SNOWFLAKE = "snowflake", PATHWAY_SNOWFLAKE_AMP = "snowflake_amp", PATHWAY_CUSTOM = "custom"; + + public static final String PREF_SECURE_WINDOW_FLAG = "pref_flag_secure"; + + private static SharedPreferences prefs; + + public static int getCurrentVersionForUpdate() { + return prefs.getInt(PREF_CURRENT_VERSION, 0); + } + + public static void setCurrentVersionForUpdate(int version) { + putInt(PREF_CURRENT_VERSION, version); + } + + private static final String PREF_REINSTALL_GEOIP = "pref_geoip"; + public static boolean isGeoIpReinstallNeeded() { + return prefs.getBoolean(PREF_REINSTALL_GEOIP, true); + } + public static void setIsGeoIpReinstallNeeded(boolean reinstallNeeded) { + putBoolean(PREF_REINSTALL_GEOIP, reinstallNeeded); + } + + public static void setContext(Context context) { + if (prefs == null) { + prefs = getSharedPrefs(context); + } + + } + + public static void initWeeklyWorker () { + PeriodicWorkRequest.Builder myWorkBuilder = + new PeriodicWorkRequest.Builder(PrefsWeeklyWorker.class, 7, TimeUnit.DAYS); + + PeriodicWorkRequest myWork = myWorkBuilder.build(); + WorkManager.getInstance() + .enqueueUniquePeriodicWork("prefsWeeklyWorker", ExistingPeriodicWorkPolicy.KEEP, myWork); + } + + private static void putBoolean(String key, boolean value) { + prefs.edit().putBoolean(key, value).apply(); + } + + private static void putInt(String key, int value) { + prefs.edit().putInt(key, value).apply(); + } + + private static void putString(String key, String value) { + prefs.edit().putString(key, value).apply(); + } + + public static boolean hostOnionServicesEnabled () { + return prefs.getBoolean(PREF_HOST_ONION_SERVICES, true); + } + + public static void putHostOnionServicesEnabled(boolean value) { + putBoolean(PREF_HOST_ONION_SERVICES, value); + } + + public static boolean bridgesEnabled() { + //if phone is in Farsi, enable bridges by default + boolean bridgesEnabledDefault = Locale.getDefault().getLanguage().equals("fa"); + return prefs.getBoolean(PREF_BRIDGES_ENABLED, bridgesEnabledDefault); + } + + public static void putBridgesEnabled(boolean value) { + putBoolean(PREF_BRIDGES_ENABLED, value); + } + + public static String getBridgesList() { + String defaultBridgeType = "obfs4"; + if (Locale.getDefault().getLanguage().equals("fa")) + defaultBridgeType = "meek"; //if Farsi, use meek as the default bridge type + return prefs.getString(PREF_BRIDGES_LIST, defaultBridgeType); + } + + public static void setBridgesList(String value) { + putString(PREF_BRIDGES_LIST, value); + } + + public static String getDefaultLocale() { + return prefs.getString(PREF_DEFAULT_LOCALE, Locale.getDefault().getLanguage()); + } + + public static void setDefaultLocale(String value) { + putString(PREF_DEFAULT_LOCALE, value); + } + + public static boolean detectRoot () { + return prefs.getBoolean(PREF_DETECT_ROOT,true); + } + + public static boolean beSnowflakeProxy () { + return prefs.getBoolean(PREF_BE_A_SNOWFLAKE,false); + } + + public static boolean showSnowflakeProxyMessage() { + return prefs.getBoolean(PREF_SHOW_SNOWFLAKE_MSG, false); + } + + public static void setBeSnowflakeProxy (boolean beSnowflakeProxy) { + putBoolean(PREF_BE_A_SNOWFLAKE,beSnowflakeProxy); + } + + public static void setBeSnowflakeProxyLimitWifi (boolean beSnowflakeProxy) { + putBoolean(PREF_BE_A_SNOWFLAKE_LIMIT_WIFI,beSnowflakeProxy); + } + + public static void setBeSnowflakeProxyLimitCharging (boolean beSnowflakeProxy) { + putBoolean(PREF_BE_A_SNOWFLAKE_LIMIT_CHARGING,beSnowflakeProxy); + } + + public static boolean limitSnowflakeProxyingWifi () { + return prefs.getBoolean(PREF_BE_A_SNOWFLAKE_LIMIT_WIFI,false); + } + + public static boolean limitSnowflakeProxyingCharging () { + return prefs.getBoolean(PREF_BE_A_SNOWFLAKE_LIMIT_CHARGING,false); + } + + public static boolean useDebugLogging() { + return prefs.getBoolean(PREF_ENABLE_LOGGING, false); + } + + public static boolean allowBackgroundStarts() { + return prefs.getBoolean(PREF_ALLOW_BACKGROUND_STARTS, true); + } + + public static boolean openProxyOnAllInterfaces() { + return prefs.getBoolean(PREF_OPEN_PROXY_ON_ALL_INTERFACES, false); + } + + public static boolean useVpn() { + return prefs.getBoolean(PREF_USE_VPN, false); + } + + public static void putUseVpn(boolean value) { + putBoolean(PREF_USE_VPN, value); + } + + public static boolean startOnBoot() { + return prefs.getBoolean(PREF_START_ON_BOOT, true); + } + + public static void putStartOnBoot(boolean value) { + putBoolean(PREF_START_ON_BOOT, value); + } + + public static String getExitNodes() { + return prefs.getString(PREF_EXIT_NODES, ""); + } + + public static void setExitNodes(String country) { + putString(PREF_EXIT_NODES, country); + } + + public static SharedPreferences getSharedPrefs(Context context) { + return context.getSharedPreferences(OrbotConstants.PREF_TOR_SHARED_PREFS, Context.MODE_MULTI_PROCESS); + } + + public static int getSnowflakesServed () { return prefs.getInt(PREF_SNOWFLAKES_SERVED_COUNT,0);} + public static int getSnowflakesServedWeekly () { return prefs.getInt(PREF_SNOWFLAKES_SERVED_COUNT_WEEKLY,0);} + + public static void addSnowflakeServed () { + putInt(PREF_SNOWFLAKES_SERVED_COUNT,getSnowflakesServed()+1); + putInt(PREF_SNOWFLAKES_SERVED_COUNT_WEEKLY,getSnowflakesServedWeekly()+1); + } + + public static void resetSnowflakesServedWeekly () { + putInt(PREF_SNOWFLAKES_SERVED_COUNT_WEEKLY,0); + + } + + public static String getConnectionPathway() { + // TODO lots of migration work need to be done here when users upgrade to orbot 17 !!! + return prefs.getString(PREF_CONNECTION_PATHWAY, PATHWAY_SMART); + } + + public static void putConnectionPathway(String pathway) { + putString(PREF_CONNECTION_PATHWAY, pathway); + } + + public static void putPrefSmartTrySnowflake(boolean trySnowflake) { + putBoolean(PREF_SMART_TRY_SNOWFLAKE, trySnowflake); + } + + public static boolean getPrefSmartTrySnowflake() { + return prefs.getBoolean(PREF_SMART_TRY_SNOWFLAKE, false); + } + + public static void putPrefSmartTryObfs4(String bridges) { + putString(PREF_SMART_TRY_OBFS4, bridges); + } + + public static String getPrefSmartTryObfs4() { + return prefs.getString(PREF_SMART_TRY_OBFS4, null); + } + + public static boolean isPowerUserMode() { + return prefs.getBoolean(PREF_POWER_USER_MODE, false); + } + + public static void setSecureWindow (boolean isFlagSecure) { + putBoolean(PREF_SECURE_WINDOW_FLAG, isFlagSecure); + } + + public static boolean isSecureWindow () { + return prefs.getBoolean(PREF_SECURE_WINDOW_FLAG, true); + } +} diff --git a/src/main/java/org/torproject/android/service/util/PrefsWeeklyWorker.java b/src/main/java/org/torproject/android/service/util/PrefsWeeklyWorker.java new file mode 100644 index 0000000..ab14287 --- /dev/null +++ b/src/main/java/org/torproject/android/service/util/PrefsWeeklyWorker.java @@ -0,0 +1,23 @@ +package org.torproject.android.service.util; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.work.Worker; +import androidx.work.WorkerParameters; + +public class PrefsWeeklyWorker extends Worker { + + public PrefsWeeklyWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { + super(context, workerParams); + } + + @NonNull + @Override + public Result doWork() { + + Prefs.resetSnowflakesServedWeekly(); + + return Result.success(); + } +} diff --git a/src/main/java/org/torproject/android/service/util/TCPSourceApp.java b/src/main/java/org/torproject/android/service/util/TCPSourceApp.java new file mode 100644 index 0000000..14d5039 --- /dev/null +++ b/src/main/java/org/torproject/android/service/util/TCPSourceApp.java @@ -0,0 +1,291 @@ +package org.torproject.android.service.util; + +/*********************************************************************** + * + * Copyright (c) 2013, Sebastiano Gottardo + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the MegaDevs nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL SEBASTIANO GOTTARDO BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.Collections; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Main class for the TCPSourceApp library. + * + * @author Sebastiano Gottardo + */ +public class TCPSourceApp { + + /* + * In a Linux-based OS, each active TCP socket is mapped in the following + * two files. A socket may be mapped in the '/proc/net/tcp' file in case + * of a simple IPv4 address, or in the '/proc/net/tcp6' if an IPv6 address + * is available. + */ + private static final String TCP_4_FILE_PATH = "/proc/net/tcp"; + private static final String TCP_6_FILE_PATH = "/proc/net/tcp6"; + /* + * Two regular expressions that are able to extract valuable informations + * from the two /proc/net/tcp* files. More specifically, there are three + * fields that are extracted: + * - address + * - port + * - PID + */ + private static final String TCP_6_PATTERN = "\\d+:\\s([0-9A-F]{32}):([0-9A-F]{4})\\s[0-9A-F]{32}:[0-9A-F]{4}\\s[0-9A-F]{2}\\s[0-9]{8}:[0-9]{8}\\s[0-9]{2}:[0-9]{8}\\s[0-9]{8}\\s+([0-9]+)"; + private static final String TCP_4_PATTERN = "\\d+:\\s([0-9A-F]{8}):([0-9A-F]{4})\\s[0-9A-F]{8}:[0-9A-F]{4}\\s[0-9A-F]{2}\\s[0-9A-F]{8}:[0-9A-F]{8}\\s[0-9]{2}:[0-9]{8}\\s[0-9A-F]{8}\\s+([0-9]+)"; +//sargo:/ $ cat /proc/net/tcp6 +// sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode +// 0: 00000000000000000000000000000000:C36A 00000000000000000000000000000000:0000 8A 00000000:00000000 00:00000000 00000000 1001 0 35059 1 0000000000000000 99 0 0 10 0 +// 1: 00000000000000000000000000000000:A64B 00000000000000000000000000000000:0000 8A 00000000:00000000 00:00000000 00000000 1001 0 910009 1 0000000000000000 99 0 0 10 0 + /* + * Optimises the socket lookup by checking if the connected network + * interface has a 'valid' IPv6 address (a global address, not a link-local + * one). + */ + private static boolean checkConnectedIfaces = true; +// sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode +// 0: 00000000:C368 00000000:0000 8A 00000000:00000000 00:00000000 00000000 1001 0 34999 1 0000000000000000 99 0 0 10 0 + + /** + * The main method of the TCPSourceApp library. This method receives an + * Android Context instance, which is used to access the PackageManager. + * It parses the /proc/net/tcp* files, looking for a socket entry that + * matches the given port. If it finds an entry, this method extracts the + * PID value and it uses the PackageManager.getPackagesFromPid() method to + * find the originating application. + * + * @param context a valid Android Context instance + * @param daddr the (logical) address of the destination + * @param dport the (logical) port of the destination + * @return an AppDescriptor object, representing the found application; null + * if no application could be found + */ + public static AppDescriptor getApplicationInfo(Context context, String saddr, int sport, String daddr, int dport) { + + File tcp; + BufferedReader reader; + String line; + StringBuilder builder; + String content; + + try { + boolean hasIPv6 = true; + + // if true, checks for a connected network interface with a valid + // IPv4 / IPv6 address + if (checkConnectedIfaces) { + String ipv4Address = getIPAddress(true); + String ipv6Address = getIPAddress(false); + + hasIPv6 = (ipv6Address.length() > 0); + } + + tcp = new File(TCP_6_FILE_PATH); + reader = new BufferedReader(new FileReader(tcp)); + builder = new StringBuilder(); + + while ((line = reader.readLine()) != null) { + builder.append(line); + } + + content = builder.toString(); + + Matcher m6 = Pattern.compile(TCP_6_PATTERN, Pattern.CASE_INSENSITIVE | Pattern.UNIX_LINES | Pattern.DOTALL).matcher(content); + + if (hasIPv6) + while (m6.find()) { +// String addressEntry = m6.group(1); + String portEntry = m6.group(2); + int pidEntry = Integer.parseInt(m6.group(3)); + + if (Integer.parseInt(portEntry, 16) == dport) { + PackageManager manager = context.getPackageManager(); + String[] packagesForUid = manager.getPackagesForUid(pidEntry); + + if (packagesForUid != null) { + String packageName = packagesForUid[0]; + PackageInfo pInfo = manager.getPackageInfo(packageName, 0); + String version = pInfo.versionName; + + return new AppDescriptor(pidEntry, packageName, version); + } + } + } + + } catch (Exception e) { + e.printStackTrace(); + } + + // From here, no connection with the given port could be found in the tcp6 file + // So let's try the tcp (IPv4) one + + try { + tcp = new File(TCP_4_FILE_PATH); + reader = new BufferedReader(new FileReader(tcp)); + builder = new StringBuilder(); + + while ((line = reader.readLine()) != null) { + builder.append(line); + } + + content = builder.toString(); + + Matcher m4 = Pattern.compile(TCP_4_PATTERN, Pattern.CASE_INSENSITIVE | Pattern.UNIX_LINES | Pattern.DOTALL).matcher(content); + + while (m4.find()) { + String addressEntry = m4.group(1); + String portEntry = m4.group(2); + int pidEntry = Integer.valueOf(m4.group(3)); + + if (Integer.parseInt(portEntry, 16) == dport) { + PackageManager manager = context.getPackageManager(); + String[] packagesForUid = manager.getPackagesForUid(pidEntry); + + if (packagesForUid != null) { + String packageName = packagesForUid[0]; + PackageInfo pInfo = manager.getPackageInfo(packageName, 0); + String version = pInfo.versionName; + + return new AppDescriptor(pidEntry, packageName, version); + } + } + } + + } catch (Exception e) { + e.printStackTrace(); + } + + return null; + } + + @SuppressLint("DefaultLocale") + public static String getIPAddress(boolean useIPv4) throws SocketException { + + List interfaces = Collections.list(NetworkInterface.getNetworkInterfaces()); + + for (NetworkInterface intf : interfaces) { + List addrs = Collections.list(intf.getInetAddresses()); + + for (InetAddress addr : addrs) { + if (!addr.isLoopbackAddress()) { + String sAddr = addr.getHostAddress().toUpperCase(); + + boolean isIPv4 = addr instanceof Inet4Address; + + if (useIPv4) { + if (isIPv4) + return sAddr; + } else { + if (!isIPv4) { + if (sAddr.startsWith("FE80")) // skipping link-local addresses + continue; + + int delim = sAddr.indexOf('%'); // drop ip6 port suffix + return delim < 0 ? sAddr : sAddr.substring(0, delim); + } + } + } + } + } + + return ""; + } + + /* + * Sets the connected interfaces optimisation. + */ + public static void setCheckConnectedIfaces(boolean value) { + checkConnectedIfaces = value; + } + + /* + * This class represents an Android application. Each application is + * uniquely identified by its package name (e.g. com.megadevs.tcpsourceapp) + * and its version (e.g. 1.0). + */ + public static class AppDescriptor { + + private final String packageName; + private final String version; + private final int uid; + + public AppDescriptor(int uid, String pName, String ver) { + this.uid = uid; + packageName = pName; + version = ver; + } + + public int getUid() { + return uid; + } + + public String getPackageName() { + return packageName; + } + + public String getVersion() { + return version; + } + + /* + * Override of the 'equals' method, in order to have a proper + * comparison between two AppDescriptor objects. + * + * (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object o) { + + if (o instanceof AppDescriptor) { + boolean c1 = ((AppDescriptor) o).packageName.compareTo(this.packageName) == 0; + boolean c2 = ((AppDescriptor) o).version.compareTo(this.version) == 0; + + return c1 && c2; + } + + return false; + } + + } + +} \ No newline at end of file diff --git a/src/main/java/org/torproject/android/service/util/Utils.java b/src/main/java/org/torproject/android/service/util/Utils.java new file mode 100644 index 0000000..73f6755 --- /dev/null +++ b/src/main/java/org/torproject/android/service/util/Utils.java @@ -0,0 +1,56 @@ +/* Copyright (c) 2009, Nathan Freitas, Orbot / The Guardian Project - http://openideals.com/guardian */ +/* See LICENSE for licensing information */ + + +package org.torproject.android.service.util; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.util.Locale; + +public class Utils { + public static boolean isPortOpen(final String ip, final int port, final int timeout) { + try { + Socket socket = new Socket(); + socket.connect(new InetSocketAddress(ip, port), timeout); + socket.close(); + return true; + } catch (Exception ex) { + //ex.printStackTrace(); + return false; + } + } + + public static String readInputStreamAsString(InputStream stream) { + String line; + + StringBuilder out = new StringBuilder(); + + try { + BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); + + while ((line = reader.readLine()) != null) { + out.append(line); + out.append('\n'); + + } + } catch (IOException e) { + e.printStackTrace(); + } + + return out.toString(); + } + + public static String convertCountryCodeToFlagEmoji(String countryCode) { + countryCode = countryCode.toUpperCase(Locale.getDefault()); + int flagOffset = 0x1F1E6; + int asciiOffset = 0x41; + int firstChar = Character.codePointAt(countryCode, 0) - asciiOffset + flagOffset; + int secondChar = Character.codePointAt(countryCode, 1) - asciiOffset + flagOffset; + return new String(Character.toChars(firstChar)) + new String(Character.toChars(secondChar)); + } +} diff --git a/src/main/java/org/torproject/android/service/vpn/DNSResolver.java b/src/main/java/org/torproject/android/service/vpn/DNSResolver.java new file mode 100644 index 0000000..0432a54 --- /dev/null +++ b/src/main/java/org/torproject/android/service/vpn/DNSResolver.java @@ -0,0 +1,35 @@ +package org.torproject.android.service.vpn; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; + +public class DNSResolver { + + private final int mPort; + private InetAddress mLocalhost = null; + + public DNSResolver(int localPort) { + mPort = localPort; + } + + public byte[] processDNS(byte[] payload) throws IOException { + + if (mLocalhost == null) mLocalhost = InetAddress.getLocalHost(); + + DatagramPacket packet = new DatagramPacket(payload, payload.length, mLocalhost, mPort); + DatagramSocket datagramSocket = new DatagramSocket(); + datagramSocket.send(packet); + + // Await response from DNS server + byte[] buf = new byte[1024]; + packet = new DatagramPacket(buf, buf.length); + datagramSocket.receive(packet); + + datagramSocket.close(); + + return packet.getData(); + } + +} diff --git a/src/main/java/org/torproject/android/service/vpn/OrbotVpnManager.java b/src/main/java/org/torproject/android/service/vpn/OrbotVpnManager.java new file mode 100644 index 0000000..d14217f --- /dev/null +++ b/src/main/java/org/torproject/android/service/vpn/OrbotVpnManager.java @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.torproject.android.service.vpn; + +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageManager.NameNotFoundException; +import android.net.VpnService; +import android.os.Build; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.ParcelFileDescriptor; +import android.system.OsConstants; +import android.util.Log; +import android.widget.Toast; + +import org.pcap4j.packet.IllegalRawDataException; +import org.pcap4j.packet.IpPacket; +import org.pcap4j.packet.IpSelector; +import org.pcap4j.packet.UdpPacket; +import org.pcap4j.packet.namednumber.IpNumber; +import org.pcap4j.packet.namednumber.UdpPort; +import org.torproject.android.service.OrbotConstants; +import org.torproject.android.service.OrbotService; +import org.torproject.android.service.R; +import org.torproject.android.service.util.Prefs; + +import java.io.DataOutputStream; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import IPtProxy.IPtProxy; +import IPtProxy.PacketFlow; + +public class OrbotVpnManager implements Handler.Callback, OrbotConstants { + private static final String TAG = "OrbotVpnManager"; + boolean isStarted = false; + private final static String mSessionName = "OrbotVPN"; + private ParcelFileDescriptor mInterface; + private int mTorSocks = -1; + private int mTorDns = -1; + private final VpnService mService; + private final SharedPreferences prefs; + private DNSResolver mDnsResolver; + + private final ExecutorService mExec = Executors.newFixedThreadPool(10); + private Thread mThreadPacket; + private boolean keepRunningPacket = false; + + private FileInputStream fis; + private DataOutputStream fos; + + private static final int DELAY_FD_LISTEN_MS = 5000; + + public OrbotVpnManager(OrbotService service) { + mService = service; + prefs = Prefs.getSharedPrefs(mService.getApplicationContext()); + } + + public void handleIntent(VpnService.Builder builder, Intent intent) { + if (intent != null) { + var action = intent.getAction(); + if (action != null) { + switch (action) { + case ACTION_START_VPN, ACTION_START -> { + Log.d(TAG, "starting VPN"); + isStarted = true; + } + case ACTION_STOP_VPN, ACTION_STOP -> { + isStarted = false; + Log.d(TAG, "stopping VPN"); + stopVPN(); + + //reset ports + mTorSocks = -1; + mTorDns = -1; + } + case OrbotConstants.LOCAL_ACTION_PORTS -> { + Log.d(TAG, "setting VPN ports"); + int torSocks = intent.getIntExtra(OrbotService.EXTRA_SOCKS_PROXY_PORT, -1); +// int torHttp = intent.getIntExtra(OrbotService.EXTRA_HTTP_PROXY_PORT,-1); + int torDns = intent.getIntExtra(OrbotService.EXTRA_DNS_PORT, -1); + + //if running, we need to restart + if ((torSocks != -1 && torSocks != mTorSocks && torDns != -1 && torDns != mTorDns)) { + + mTorSocks = torSocks; + mTorDns = torDns; + + setupTun2Socks(builder); + } + } + } + } + } + } + + public void restartVPN(VpnService.Builder builder) { + stopVPN(); + setupTun2Socks(builder); + } + + private void stopVPN() { + keepRunningPacket = false; + + if (mInterface != null) { + try { + Log.d(TAG, "closing interface, destroying VPN interface"); + IPtProxy.stopSocks(); + if (fis != null) { + fis.close(); + fis = null; + } + + if (fos != null) { + fos.close(); + fos = null; + } + + mInterface.close(); + mInterface = null; + } catch (Exception | Error e) { + Log.d(TAG, "error stopping tun2socks", e); + } + } + + if (mThreadPacket != null && mThreadPacket.isAlive()) { + mThreadPacket.interrupt(); + } + } + + @Override + public boolean handleMessage(Message message) { + if (message != null) { + Toast.makeText(mService, message.what, Toast.LENGTH_SHORT).show(); + } + return true; + } + + public final static String FAKE_DNS = "10.0.0.1"; + + private synchronized void setupTun2Socks(final VpnService.Builder builder) { + try { + final String defaultRoute = "0.0.0.0"; + final String virtualGateway = "192.168.50.1"; + + // builder.setMtu(VPN_MTU); + // builder.addAddress(virtualGateway, 32); + builder.addAddress(virtualGateway, 24) + .addRoute(defaultRoute, 0) + .addRoute(FAKE_DNS, 32) + .addDnsServer(FAKE_DNS) //just setting a value here so DNS is captured by TUN interface + .setSession(mService.getString(R.string.orbot_vpn)); + + //handle ipv6 + builder.addAddress("fdfe:dcba:9876::1", 126); + builder.addRoute("::", 0); + + /* + * Can't use this since our HTTP proxy is only CONNECT and not a full proxy + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + builder.setHttpProxy(ProxyInfo.buildDirectProxy("localhost",mTorHttp)); + }**/ + + doAppBasedRouting(builder); + + // https://developer.android.com/reference/android/net/VpnService.Builder#setMetered(boolean) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + builder.setMetered(false); + + // Explicitly allow both families, so we do not block + // traffic for ones without DNS servers (issue 129). + builder.allowFamily(OsConstants.AF_INET); + builder.allowFamily(OsConstants.AF_INET6); + + } + + builder.setSession(mSessionName) + .setConfigureIntent(null) // previously this was set to a null member variable + .setBlocking(true); + + mInterface = builder.establish(); + mDnsResolver = new DNSResolver(mTorDns); + + final Handler handler = new Handler(Looper.getMainLooper()); + handler.postDelayed(() -> { + try { + startListeningToFD(); + } catch (IOException e) { + Log.d(TAG, "VPN tun listening has stopped", e); + } + }, DELAY_FD_LISTEN_MS); + + } catch (Exception e) { + Log.d(TAG, "VPN tun setup has stopped", e); + } + } + + private void startListeningToFD() throws IOException { + if (mInterface == null) return; // Prepare hasn't been called yet + + fis = new FileInputStream(mInterface.getFileDescriptor()); + fos = new DataOutputStream(new FileOutputStream(mInterface.getFileDescriptor())); + + //write packets back out to TUN + PacketFlow pFlow = packet -> { + try { + fos.write(packet); + } catch (IOException e) { + Log.e(TAG, "error writing to VPN fd", e); + } + }; + + IPtProxy.startSocks(pFlow, "127.0.0.1", mTorSocks); + + //read packets from TUN and send to go-tun2socks + mThreadPacket = new Thread() { + public void run() { + + var buffer = new byte[32767 * 2]; //64k + keepRunningPacket = true; + while (keepRunningPacket) { + try { + int pLen = fis.read(buffer); // will block on API 21+ + + if (pLen > 0) { + var pdata = Arrays.copyOf(buffer, pLen); + try { + var packet = IpSelector.newPacket(pdata, 0, pdata.length); + + if (packet instanceof IpPacket ipPacket) { + if (isPacketDNS(ipPacket)) + mExec.execute(new RequestPacketHandler(ipPacket, pFlow, mDnsResolver)); + else if (isPacketICMP(ipPacket)) { + //do nothing, drop! + } else IPtProxy.inputPacket(pdata); + } + } catch (IllegalRawDataException e) { + Log.e(TAG, e.getLocalizedMessage()); + } + } + } catch (Exception e) { + Log.d(TAG, "error reading from VPN fd: " + e.getLocalizedMessage()); + } + } + } + }; + mThreadPacket.start(); + } + + private static boolean isPacketDNS(IpPacket p) { + if (p.getHeader().getProtocol() == IpNumber.UDP) { + var up = (UdpPacket) p.getPayload(); + return up.getHeader().getDstPort() == UdpPort.DOMAIN; + } + return false; + } + + private static boolean isPacketICMP(IpPacket p) { + return (p.getHeader().getProtocol() == IpNumber.ICMPV4 || p.getHeader().getProtocol() == IpNumber.ICMPV6); + } + + private void doAppBasedRouting(VpnService.Builder builder) throws NameNotFoundException { + var apps = TorifiedApp.getApps(mService, prefs); + var individualAppsWereSelected = false; + var isLockdownMode = isVpnLockdown(mService); + + for (TorifiedApp app : apps) { + if (app.isTorified() && (!app.getPackageName().equals(mService.getPackageName()))) { + if (prefs.getBoolean(app.getPackageName() + OrbotConstants.APP_TOR_KEY, true)) { + builder.addAllowedApplication(app.getPackageName()); + } + individualAppsWereSelected = true; + } + } + Log.i(TAG, "App based routing is enabled?=" + individualAppsWereSelected + ", isLockdownMode=" + isLockdownMode); + + if (isLockdownMode) { + /* TODO https://github.com/guardianproject/orbot/issues/774 + Need to allow briar, onionshare, etc to enter orbot's vpn gateway, but not enter the tor + network, that way these apps can use their own tor connection + // TODO "add" these packages here... + */ + } + + if (!individualAppsWereSelected && !isLockdownMode) { + // disallow orobt itself... + builder.addDisallowedApplication(mService.getPackageName()); + + // disallow tor apps to avoid tor over tor, Orbot doesnt need to concern itself with them + for (String packageName : OrbotConstants.BYPASS_VPN_PACKAGES) + builder.addDisallowedApplication(packageName); + } + } + + public boolean isStarted() { + return isStarted; + } + + private boolean isVpnLockdown(final VpnService vpn) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + return vpn.isLockdownEnabled(); + } else { + return false; + } + } +} diff --git a/src/main/java/org/torproject/android/service/vpn/RequestPacketHandler.java b/src/main/java/org/torproject/android/service/vpn/RequestPacketHandler.java new file mode 100644 index 0000000..9747c5e --- /dev/null +++ b/src/main/java/org/torproject/android/service/vpn/RequestPacketHandler.java @@ -0,0 +1,81 @@ +package org.torproject.android.service.vpn; + +import android.util.Log; + +import org.pcap4j.packet.DnsPacket; +import org.pcap4j.packet.IpPacket; +import org.pcap4j.packet.IpV4Packet; +import org.pcap4j.packet.IpV6Packet; +import org.pcap4j.packet.UdpPacket; + +import java.net.Inet6Address; + +import IPtProxy.PacketFlow; + + +public class RequestPacketHandler implements Runnable { + + final IpPacket packet; + final PacketFlow pFlow; + final DNSResolver mDnsResolver; + + private static final String TAG = "RequestPacketHandler"; + + public RequestPacketHandler(IpPacket packet, PacketFlow pFLow, DNSResolver dnsResolver) { + this.packet = packet; + this.pFlow = pFLow; + this.mDnsResolver = dnsResolver; + } + + public void run() { + try { + UdpPacket udpPacket = (UdpPacket) packet.getPayload(); + + byte[] dnsResp = mDnsResolver.processDNS(udpPacket.getPayload().getRawData()); + + if (dnsResp != null) { + + DnsPacket dnsRequest = (DnsPacket) udpPacket.getPayload(); + DnsPacket dnsResponse = DnsPacket.newPacket(dnsResp, 0, dnsResp.length); + + DnsPacket.Builder dnsBuilder = new DnsPacket.Builder(); + dnsBuilder.questions(dnsRequest.getHeader().getQuestions()); + dnsBuilder.id(dnsRequest.getHeader().getId()); + + dnsBuilder.answers(dnsResponse.getHeader().getAnswers()); + dnsBuilder.response(dnsResponse.getHeader().isResponse()); + dnsBuilder.additionalInfo(dnsResponse.getHeader().getAdditionalInfo()); + dnsBuilder.qdCount(dnsResponse.getHeader().getQdCount()); + dnsBuilder.anCount(dnsResponse.getHeader().getAnCount()); + dnsBuilder.arCount(dnsResponse.getHeader().getArCount()); + dnsBuilder.opCode(dnsResponse.getHeader().getOpCode()); + dnsBuilder.rCode(dnsResponse.getHeader().getrCode()); + dnsBuilder.authenticData(dnsResponse.getHeader().isAuthenticData()); + dnsBuilder.authoritativeAnswer(dnsResponse.getHeader().isAuthoritativeAnswer()); + dnsBuilder.authorities(dnsResponse.getHeader().getAuthorities()); + + UdpPacket.Builder udpBuilder = new UdpPacket.Builder(udpPacket).srcPort(udpPacket.getHeader().getDstPort()).dstPort(udpPacket.getHeader().getSrcPort()).srcAddr(packet.getHeader().getDstAddr()).dstAddr(packet.getHeader().getSrcAddr()).correctChecksumAtBuild(true).correctLengthAtBuild(true).payloadBuilder(dnsBuilder); + + IpPacket respPacket = null; + + if (packet instanceof IpV4Packet ipPacket) { + + IpV4Packet.Builder ipv4Builder = new IpV4Packet.Builder(); + ipv4Builder.version(ipPacket.getHeader().getVersion()).protocol(ipPacket.getHeader().getProtocol()).tos(ipPacket.getHeader().getTos()).srcAddr(ipPacket.getHeader().getDstAddr()).dstAddr(ipPacket.getHeader().getSrcAddr()).correctChecksumAtBuild(true).correctLengthAtBuild(true).dontFragmentFlag(ipPacket.getHeader().getDontFragmentFlag()).reservedFlag(ipPacket.getHeader().getReservedFlag()).moreFragmentFlag(ipPacket.getHeader().getMoreFragmentFlag()).ttl(Integer.valueOf(64).byteValue()).payloadBuilder(udpBuilder); + + respPacket = ipv4Builder.build(); + + } else if (packet instanceof IpV6Packet) { + respPacket = new IpV6Packet.Builder((IpV6Packet) packet).srcAddr((Inet6Address) packet.getHeader().getDstAddr()).dstAddr((Inet6Address) packet.getHeader().getSrcAddr()).payloadBuilder(udpBuilder).build(); + } + + + byte[] rawResponse = respPacket.getRawData(); + pFlow.writePacket(rawResponse); + } + + } catch (Exception ioe) { + Log.e(TAG, "could not parse DNS packet: " + ioe); + } + } +} diff --git a/src/main/java/org/torproject/android/service/vpn/TorifiedApp.java b/src/main/java/org/torproject/android/service/vpn/TorifiedApp.java new file mode 100644 index 0000000..7296e6a --- /dev/null +++ b/src/main/java/org/torproject/android/service/vpn/TorifiedApp.java @@ -0,0 +1,207 @@ +package org.torproject.android.service.vpn; + +import android.Manifest; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; + +import org.torproject.android.service.OrbotConstants; + +import java.text.Normalizer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.StringTokenizer; + +public class TorifiedApp implements Comparable { + + private boolean enabled; + private int uid; + private String username; + private String procname; + private String name; + private Drawable icon; + private String packageName; + + private boolean torified = false; + private boolean usesInternet = false; + + public static ArrayList getApps(Context context, SharedPreferences prefs) { + + String tordAppString = prefs.getString(OrbotConstants.PREFS_KEY_TORIFIED, ""); + String[] tordApps; + + StringTokenizer st = new StringTokenizer(tordAppString, "|"); + tordApps = new String[st.countTokens()]; + int tordIdx = 0; + while (st.hasMoreTokens()) { + tordApps[tordIdx++] = st.nextToken(); + } + + Arrays.sort(tordApps); + + //else load the apps up + PackageManager pMgr = context.getPackageManager(); + + List lAppInfo = pMgr.getInstalledApplications(0); + + Iterator itAppInfo = lAppInfo.iterator(); + + ArrayList apps = new ArrayList<>(); + + while (itAppInfo.hasNext()) { + ApplicationInfo aInfo = itAppInfo.next(); + TorifiedApp app = new TorifiedApp(); + try { + PackageInfo pInfo = pMgr.getPackageInfo(aInfo.packageName, PackageManager.GET_PERMISSIONS); + if (OrbotConstants.BYPASS_VPN_PACKAGES.contains(aInfo.packageName)) { + app.setUsesInternet(false); + } else if (pInfo != null && pInfo.requestedPermissions != null) { + for (String permInfo : pInfo.requestedPermissions) { + if (permInfo.equals(Manifest.permission.INTERNET)) { + app.setUsesInternet(true); + } + } + } + + + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + if ((aInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 1) { + //System app + app.setUsesInternet(true); + } + + + if (!app.usesInternet()) continue; + else { + apps.add(app); + } + + + app.setEnabled(aInfo.enabled); + app.setUid(aInfo.uid); + app.setUsername(pMgr.getNameForUid(app.getUid())); + app.setProcname(aInfo.processName); + app.setPackageName(aInfo.packageName); + + try { + app.setName(pMgr.getApplicationLabel(aInfo).toString()); + } catch (Exception e) { + app.setName(aInfo.packageName); + } + + //app.setIcon(pMgr.getApplicationIcon(aInfo)); + + // check if this application is allowed + app.setTorified(Arrays.binarySearch(tordApps, app.getPackageName()) >= 0); + } + + Collections.sort(apps); + + return apps; + } + + public static void sortAppsForTorifiedAndAbc(List apps) { + Collections.sort(apps, (o1, o2) -> { + /* Some apps start with lowercase letters and without the sorting being case + insensitive they'd appear at the end of the grid of apps, a position where users + would likely not expect to find them. + */ + if (o1.isTorified() == o2.isTorified()) + return Normalizer.normalize(o1.getName(), Normalizer.Form.NFD).compareToIgnoreCase(Normalizer.normalize(o2.getName(), Normalizer.Form.NFD)); + if (o1.isTorified()) return -1; + return 1; + }); + } + + public boolean usesInternet() { + return usesInternet; + } + + public void setUsesInternet(boolean usesInternet) { + this.usesInternet = usesInternet; + } + + public boolean isTorified() { + return torified; + } + + public void setTorified(boolean torified) { + this.torified = torified; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public int getUid() { + return uid; + } + + public void setUid(int uid) { + this.uid = uid; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getProcname() { + return procname; + } + + public void setProcname(String procname) { + this.procname = procname; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Drawable getIcon() { + return icon; + } + + public void setIcon(Drawable icon) { + this.icon = icon; + } + + @Override + public int compareTo(Object another) { + return this.toString().compareToIgnoreCase(another.toString()); + } + + @Override + public String toString() { + return getName(); + } + + public String getPackageName() { + return packageName; + } + + public void setPackageName(String packageName) { + this.packageName = packageName; + } +} diff --git a/src/main/res/drawable-hdpi/ic_refresh_white_24dp.png b/src/main/res/drawable-hdpi/ic_refresh_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..19bea38ef3c7a3826c8be7084d7eb323a1aab03e GIT binary patch literal 675 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k3?#4J%UA`ZJ_PuLxB}__BMCgcdgvk0&;BJr ze!&b%RlA@4`ty8`V&m$+%jKJ5zkZHvF8{YRnw3Ri=dLTJHg~uMpR8(Ndm59lzH)nx zxwZJpYdm~6=X}|B@STLEf60zGW%i7y55aPaq#yq*m37FoILpAmDD3It7-Dhy>6DvM zhYSQ-Z#zwN((Dd8rPRUIu}Q;PFK5w$1<(K9Z+pq^bgO0kJ>&A{PtKrqwDQg0{t0@vb!%Rvq=UP_~`Jd2|d5WYN6q&op$QdrI~A{ZrKMKslx_kwlXTO4Y+&yoES76x+W@iD99(shDbV{;Ln)%ej(G# z{|=)5FZd?dDr)U{_3d3FdwU@BtEOJw_K8asGp%O2JJc3M=`OgN*qg`yVt0`=bHa&$ zyXg;}UX5*Z{hPc0?fFGEzdH6mxKY=3vgBLbOy9Q)jh`{||8 r|9)|v)X6(pd*iOw*ZIG&-^94@svVETikxe}kZ168^>bP0l+XkKf|k3?#4J%UA`Z!UKFlT!Hj|8h}1Gz865FWlDnl zf*BZl9y3pvzO|)!V`AL#6)R7yt64MS;|rEqQ<(ZM26fv2)h+gPaSX9I{dUS-z9s`6 zx83osAqA%x-u|{D)d2m{LnsTMxrb`x22|cV{n~svyNy)gmyD zeQw#`rr7_SZ?tEY8XoJ*_OZ{g)6EREKb6Z^(O>l5%xuy;<0;FO40wPXs!b!PC{xWt~$(696CF!Y%*+ literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-hdpi/ic_stat_tor.png b/src/main/res/drawable-hdpi/ic_stat_tor.png new file mode 100644 index 0000000000000000000000000000000000000000..0f8f87d1a60a40890ef5376322c6fa4fdfa31073 GIT binary patch literal 715 zcmeAS@N?(olHy`uVBq!ia0vp^5gs}UfefHjCm8hg^#R#H^+1z5zyQbwnhX>Nn$XtP)(!>`DIgaJ zfDQnXt*xyP63Bo62)CuB1;_vbpfVsE2!OJ12E@TY#{dlnS^;!Pe}Dgk2@`;B0a^jH z3aAmNe)r_i1Yn5xmIV0)GpNsDc5Tpl!;+x5wBrtkgno9UJY(ATC$BSi{oN@LefByUXGwR0=t1H%Qk?S7)ezdOm-M3x9 zAeZ-aaSYKofA`YOphE^CYzcS1%a${0o4Wdj8cbjk3lNMk%;d?P+B0(&D4a+;_W4exgT_XVxAUL6eg4yMYg%r!$tp*?bLW4bVVe@DeyfM= zu)ao&L3dyRb9d%W9f=)}7>vXVo*AwCed_>kGY``{hv(|gx3hK0xVFMS!pMT~}k4Z-U~6<$j@CU%i*V_Fi?`q3=>o zZg`eFWSRf~ literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-hdpi/ic_stat_tor_xfer.png b/src/main/res/drawable-hdpi/ic_stat_tor_xfer.png new file mode 100644 index 0000000000000000000000000000000000000000..44a2f772553d12026c85b546004e386b13b90cbc GIT binary patch literal 969 zcmY*YdrZ<{6vecarbKFG&t(NzIwP}Xxe^OCN*N-R&$6<|TGOolsAk0il^-Ao2NobTTAUHAbd6eI=s&Gw_wXaW3< zJQ3bQ@M2B&Mjdq4&ZN=2T7=2s1W1FyP*qi>R;xj@S}nNAWCAHKFE<*Ekd>8{N~Kbz zQdLw`z(WrScBISPeB zqtTE~@Y2#!n2=~g2mlFy;1r7yA35MKApsIgIAXCXnL>q$PLhCd01}nlg#;qUPyQC*pn@f2yOp46y>v5F(JDoAgi^nz(41?$C^9!#*fiD z6aBcyb0WOw?e`ChlxFQ0Me|p+3LizjN!Q#vW^;7B>+R7r4tR!Yetxoc)_JUF2lqXE z-#ak;<;Sm@WcR(;Qz|(giTxYDwozKYqTlm*aCKb&TK0vnm&0Q8ce@@Nhua=6#t&>6 zpBFCZ^_vMCQ+r2)tjEGdAH&`eK>P(!^_+#a!yJGt~*% z>qRC@R%YUiuDPp^7oT^zT-y_vp?QUSrrXV~b&TQEv~7t|iC0>t(nnWDcL!#61dTF; zNi$jI>l(&NPR*-*dHx1Yb&MB1<++vZ&fqd5*y$N(o$c)%&Q51+%$k&{0e{1x>VZ#v zuI}!^i9YYvH3zYqxXdw@pyqwt&POte%V2~=7w+PS7v|ZnzG~nL8T8g^Lh30$_4V?` z!jX{?Uy9;C)3dH|nW8tg6;Zct9+50qwQSYt!||aJ7Zqd5&5taXa&0}LV}}feZDK*S zzjTXT{=l>(N#5oNom6mg!^dFG{(}b+%NTB>
;elgn}f9{Ewb6%0IWJlzmKP`=) LAmEwT=brfu{jcNS literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-ldpi/ic_stat_notifyerr.png b/src/main/res/drawable-ldpi/ic_stat_notifyerr.png new file mode 100644 index 0000000000000000000000000000000000000000..43d71e2fee77bdd067563b87c6d417b3ce283e5e GIT binary patch literal 262 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+3?vf;>QaG}eSlAhE0F$A0dU^^D+_3ZNJ)@i zFhlBxk7<7w#LZ35;mR%)DyWbx%g8xB|2I%R$|JE`{1NxnFhp@M)jl%t}74q&Xp4K-(BRUHx3vIVCg!0Deb? ArvLx| literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-ldpi/ic_stat_tor.png b/src/main/res/drawable-ldpi/ic_stat_tor.png new file mode 100644 index 0000000000000000000000000000000000000000..e3d85b5b61e528107a670d74c39d79fef609c084 GIT binary patch literal 420 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+3?vf;>QaH!`~aU2S0Md=5Cg9A${j!_nv?|j z1v4=5#V87x&gEak7ak=)wO}?f89+nH;4GIbxJ06sZAB|;s{(t{jj!fsu0y%%R z+~B4Ej=q>VO?Tyci+2tk&)E+LOjGJnX?bk%vLvL*(|wuonWt|`7B)UzrTUt;?&iCQ zZ|o)~`&`rmiWc>F3eTPCP?ES)=&s4dx#4#6Oj*`1`r7s)Hf;X07JZW{;Zng~%Od@&% zPhQv5lWX~Jsg%XM__X7T<0sQ5gRaRY!Z#W?#Ba}4%jQ?&VR1gkx1dQ`=EQNA8674! zW^grD{Nt@ZD)@4b#lt_gPj>XgRIHMT3<-C8w)g6+SJqOOyzLgux;8O)*@v2o(JJ5D o{Lf6D&)Bste9`+;s^NdxzBKdi%(h?(1iF;L)78&qol`;+073-;_W%F@ literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-mdpi/ic_stat_notifyerr.png b/src/main/res/drawable-mdpi/ic_stat_notifyerr.png new file mode 100644 index 0000000000000000000000000000000000000000..e55893c3acb7c35060cbff2c32785412720d1370 GIT binary patch literal 317 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbMaAg$MYAxB}__Gyr{Wd@q1T%ajE9 z1v7{oeItJM#IC5w9aAPQR9vtJ)`8M&gaC6nT!vjy=DhTUEJAgqqXUY%}n;D@{l((laAI-GC93c z`j-fUYU$;FoO}2VcpUh{yJ}aSi^EZe*D>w~UpGm3+Vd!LSIl~^A2U^FwM@0b<`|Lk h#aEefcK?xNkxf-p&I$T`ZyV4N44$rjF6*2Ung9yEtf>G1 literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-mdpi/ic_stat_tor.png b/src/main/res/drawable-mdpi/ic_stat_tor.png new file mode 100644 index 0000000000000000000000000000000000000000..be7dccf7efa45fada4f512275bb8d13ebacb9463 GIT binary patch literal 441 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbMaAwFmfwxB}__g8*br{4WS}icU$8 zUogWG-oG=ie3zTz{9$Q1_w0uJ6iy3U(UYg%FwXc_rS_};+K=B47uzp8vCwA0-6-ww zGeCV?JzX3_G|snPx+&D8Ai91+`)E_Vk*!(X({p`c0V>@3qFv|pY#_}(iV#Hum&QSNg z^gC1Ce0IgtF3gDvicG0HGZZEX7%M33XG`Mbd@6d=LuHB95+S2)9~g`{E4R1J;M&>Z zGYCZ`UbE8U&HXR5TB*00gTe~DWM4f?q3D> literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-mdpi/ic_stat_tor_xfer.png b/src/main/res/drawable-mdpi/ic_stat_tor_xfer.png new file mode 100644 index 0000000000000000000000000000000000000000..04168ec8cca210c516b1cb4253530d8aea87893f GIT binary patch literal 741 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbQ|PfzdR;C&U%V{XYsw41s*c+zMd8 zrIiHv1v5;nU_SIKZsp;wJhyj$lGa{iXVf_T!CigYi&-g;pC)s5a~1z9dH7bb?Mm~@ z);-VqC-ii*$JGA3|LDn^*VDKDIJM*Nyq)zM{U=YEJ!k3in{_f*FU-Am=HOoM@1KGy z%a|f>-Hk5VeE#<1U(XqVHvaTDHgK~FBxSDC}_98$*5$#W+IUg8sOrg zp}AbKVG~zOOaz4-)O=$JBSYl~asIc?2q}szt&t5%yr}*^c z%cpM-zn?wr8T;f9{2U=%TzrCp8=rIV8vAp9Yp+(ZQ8JRqn-D1y6yU1N>gl6Xq;Tx0 t^HE`;i%IwFH7#9rO=q5*pRkCLL0H|BQ9CU3C@>HhJYD@<);T3K0RS8f^M(Kb literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xhdpi/ic_refresh_white_24dp.png b/src/main/res/drawable-xhdpi/ic_refresh_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..91d695d2b8af90210aa008611208eb25c42c81a6 GIT binary patch literal 895 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBgK_U~~!a32_B-|BnL7LZF7D(AS>bEEhgRKLwpY2@tr-Nn&#`I1tz!OOd?E|P4_60V((XWa{7 zVo7+uh)Zx&#|54*p<&aHy^w0V|HOy;{mcjaPbd6b!no$UOndj_`lTybA4bGH(sN^% zc7LXEPOe4yv%-wyuTF9)e&7K{W4)(~V~EG`w^MJ1H#vwLTibKW=&+d2nLAw}TXc>r zy=%0&HSG5Hz0dyF=j+YYm?3$l{oKcczmL7IlRtdtCqw(2?>{LlwqgDw{#B{1rXWLNW|y>}!8gUWMtN74pRVoL_`vI5EXE{V1qRhJC<6K}ZOIdN@6L*kN&0nToV|7SH^(J@O> zFppiHu*SPrV$zKbI+v|hir$G*E#O=fxIpsKj*V&)YGgkbbv}_6y>ny99gj7Sj)^sH zFE_Y%HN*S8Bh$CFznB-aU&-bEb-HxPEW51Z2j-amTHta**vis0P1M?&J?LV@;=LDS zSFDN23g7x#+H?Bi+t%L_J0d?hm;FyDuW33o^Xb*&Rh>0m&+DF?KRVsM#YK0|KGxND je!Cm4-&R+5Hfnytn)yQFEit0N6u{u=>gTe~DWM4f+Pq5p literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xhdpi/ic_stat_notifyerr.png b/src/main/res/drawable-xhdpi/ic_stat_notifyerr.png new file mode 100644 index 0000000000000000000000000000000000000000..36f750a5b5016c4fb0a1241d6b7a541e5b1e53be GIT binary patch literal 406 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sQ2{<7u0Z-fZ9t^a_Ps#UWlMtm zf*E{n-1_(})a}69HMV$i7NDn8q!PIB zpG%05y{pEh{_xP-Cr(^b{rj+L>Q(#M)$hf9`fT2Rs@kz=a{s5K1HqpP>jNG*DpVFe zm=|+ANa2-q!YRhj>~*pc`A!_sj1HMr3ETa%)VOu)93H7_$uV(Ul$q3brXkn=ee^G@ zU$^|8vA1v*=h=>do~I`hToyoW#q6XSeNH SG3g)BXAGXMelF{r5}E)!W7!V? literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xhdpi/ic_stat_tor.png b/src/main/res/drawable-xhdpi/ic_stat_tor.png new file mode 100644 index 0000000000000000000000000000000000000000..2344022f4f12b37742c52d73ec62ffe048b7f53b GIT binary patch literal 887 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBgK_VB`z%32_B-|BnJ#2}*6ns$BJ#lGqwY0LuS!s zVDRtvba4#vIDT~6^{_(@0&Rf?T}oj(t0Oif#82JzMXYN5x!?OcR!&GUPu!Vw?quk{ z{N-=XsOVjf%1JJEJ@EEs6t6%+b-L;S?hU(zHn2Y^+gbDHf*r#Te!l)4jgN&JE;G#e zc)X-eaED9yolmb9ynpuX-Ms&Q1P&a2e7X3?)2YH0lZ)6KtL|Ag^2`2sa=D(lrylc3L@v;{7Vsk{fG0>t=JxmI{+Dqn4Ubc87+VmF8M zoCrpJRuAz883rfDMbkF(Dg-%{DyA`pa;;eDl`^@cmnrF7YVXC<9V?PtBE;RDN{p}e zhUb0_NH57wz9-(oCbgz9j(Bp0+zreG|>Tu)DFZn`I d$NNv)?~&x)b$Z8331E6*@O1TaS?83{1ONaw99RGV literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xhdpi/ic_stat_tor_xfer.png b/src/main/res/drawable-xhdpi/ic_stat_tor_xfer.png new file mode 100644 index 0000000000000000000000000000000000000000..270a41398463a76eb4599f36c4a9dbee9e4cb4af GIT binary patch literal 1348 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBgK_VEi566XFWw{vQP+G6XJ6>IB9< z!-|q1zhH){^{OHV{;o54yi2}9;ru?S(77@5zP?)$`M#F3T+#HH1F{ohhe!!zqJ4v^{e|R-1b#|8H%Sxv!Z{GfREq!u_q;)`G zVo3Amjgz}ooR2@-)ac96v2~kh>;f^}oc7Dc8y*J#U|?Vh@N{tu@i?BGAi;6q#1WrK zRbpXgWs^z@3f3h&ex&Amn9EE~$mGMPwGkVe;vE7!W<mJVkguOPA zdC?VVDaqLl&Y7+AR!Sy5eI_<7M?2xbkx3W1_7`PKN4FFe#N4>4{W$Ra8--m=vK>}N zK;||k;Rk-N{m!XrZ(tNo7hm%#_HNh%CcbqMS9VudJi7AW<-@*}2TxbC<~?}az49Q( z?DoGq{uD0#!r+~w_L-e+WrO6jJujr1TsN`>bad-QD49saI!hRM_#RSLQeM1h@#5s9 z#|soRCR`VJbFHF;m(zi_)AMATrp!Kv3A5N(UrNr5Ovq1JoP8paaigO7`Ae5BncbIW zQwo`R;9_R?x`hnY6~9Eq9BsmVmau&k+ADg))WLac$YLH>=So(S+Q>!OD_A^dObH8} zp16ILP;9e^#)mIiDJ~mVr+YhH4~S;h>3k*q=!cH3bhjeolKWTg#Kc`=DhtkL-qp74 zo2a&x)!E>Ni3={Xb~d-YnNDdWjfnL%WD1p3NKo$-sPMWnGuwldhyAk zf(hL^2EnhVa0pNCTCg%m>&B!jCOJE|BwAfqB5AC>AtPgMt8VwXdCWX3ju`3uoRvGt z5wknC!YCKf&uc`H+ zZopOD%Qu_F1q1Kt$89pHjw~!ZAv62wCFYAoj0~%;KEJ<7$YVVyLwLISxvXmS1;MtrtHPV1d zKxfMRKM9#jENs{O+op0jk+J0W3qz-mb8kWi@z-^*0i3$ILGANZaYxLZRzeYy3Dh#Aoj0zSE&Kc-OH*PV0nh*)_4yIUdRKbCw^ zWME+O_jGX#i8%ar`fF=xN0DRBPt-V?Hs~pasIG|i3h}zLwKq&~yIAgBeO0HsRgTfC z=GN~zZ=Aka>F!L+i8lVVH$E5ME3e;doc`=hyyn%#on)kBM`BP=r zKU2`$cE_ej`_rel8><){6S=;+KAB%2GW|$_fx^lwjv9#|U+mu=Yf`a{w`DBdyPcm?YpSK*w1x8QS}nydI5zNXJ+buV z!Hcbr3LMOK#M@1aYdjmJH)p<|OgnSau8YS4xH-h8u@3rOg7pX96}HMPcnv zH#ka$l&QzR<`sG3DVMTDq56}8FstWxjwzd#F3s5-Jndf+mz0Qf`-+Kw-Vw8=m4d7Aa@KTM{gx|?+0H(g1pIUipy>GS>I$vX=&3v@VzHtk{Bch$7? z?ry$cZShlX74Gc3aVIEuuG!PdO-GN{Ti?1?bEk89+1gDaZ;syCvEfq2Ti2xH%D*pa zo?+jyTY6vSUn6^2y<2HZ0v;56@Q{3CcOb0sv2dJmmd(-Y!q?Nyo}a$%qILPxcGgm# z_`2Ff|BchN_WnOv`GW7OQOU=@VfJ@EK2G|wZtM5wPp9T5*k=4HwJrPhyZ_tmm91jm c75*`;F+Q}v=kEd@V2Wq(boFyt=akR{0Iks-eEDF`Iq+Xhcn}4t}n?K4)KRfV&|xRdfh--VE5!+?XK^-s}vry{`x5Y zr-4~3Tc}n2&HhYxJqbP@VO0~`*^=)RXDuxGTPi+%YUT4672Iyum>PN43GV!TaAlq4 zr8v$T8$JhjZaY`w@Wf`j^>SJ7wiJ)Mrn&QuJbHUgXcq$m(|u1D$B>A_Pp{vMzU&~t z@S*GK)PR*;T#p!|Huma8bh}^Git6{c$Q9BTpz*!-zCr!o%u8moQ_?4^)h5gT`l|S| zcC&G{kJG|#D?L+Fj$JbQE=j!5nvl)gIE!(~G&T!CCWhW_v{{-592i!tWO}*Z zvFyFL*sG84YtC*8_%b{Hh4lu3KObKISrX>)^J%x_8Nm%s4sC3&?@R6f&u~Mr;m`et zzvfH~WRPPqunJ-i{-Msy)_5oS!>gWN<_7`?e#|gWPuld2D=>8Gsw->TYadn=!GIQ~ag4Si){XwE<11o)YTN-cda(3i6ns&r5e&r(#pF^7L zO_P4SnY;Dq;-}HZl>%qF=IpF|T$**(TzuC2Vtc`-Sr!$2TNGa%6zk~}^p26((qKK) z^)KgDDSkHHw1AAxwKKAoML%-d(BgEuXMxb>x<4HWlC5D89S;RESX-^W&U$Sb#$oKu za>#nw8fAsRO^V%4n|5}Dlr2`6(BpKhWWhJ>xCssc9{QV;^zyXYR76hA4*o0@DAyX6 zn;CY*GRk$~qR2Y^Q0+w*X8qiDu=DywDXz8F(pzHPpT1UCJ!QJTHz{bt>16FNC=b#?gt&o#3T*qdD`a8oQk$9h_}uc3zA zmd8bx-yQ3X5c!-GEb6!E@$S6xZ*e#3u6|PDdcSkKvgh-s?^`CtNi7K}Wz>{#3A(rM z;`i*iyg^(4b{tpxizlKTECopd9!lLu|?0*m#e>YdXd!kf012?@}0b(SYU=@@O1TaS?83{ F1OP{FBlG|O literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xxhdpi/ic_stat_tor_xfer.png b/src/main/res/drawable-xxhdpi/ic_stat_tor_xfer.png new file mode 100644 index 0000000000000000000000000000000000000000..0d09348f25f17ef6d55e5f87fa0ff7cbe9d9bd76 GIT binary patch literal 1734 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`ol6-Qn1TX)LR^8||D#|u1gIAR;&V=V z15?$}k|4ie2Ii`nB0}%>u*UuTdys41znx-jpPciwOFeA9o^E*dR$So4ucNkb=Eh7Vt*-zYO3I31^yFK^v>z>Vkg*U!(D zZ>cPDnJvr2R9Gm$ANWDcONDbHlLGfs;UEbULcFa+d=vSF^zrek9hHKv*lZ!>J zJa0Sip4!gzC?nA3{U^T#uD6?hdI)V)NmbDma{nltc#?4ohkxg@sW-lbOsu?iLe^`x z?y9+(jV((RmhQe3VwNdl_fqn-l-7#7)j22hjlH@4HJ#bEi+#yT&JAoX=i;;vD>8Cd zlSp?4N z5s+2d+4;e!Wra+E%07u@fmus8*iU}+bpFZ11{Vb@l6G}17CPO$Lr7_r(trO$u4FphFa3GSYu8;Fp_a5j z{?#WE^aQv1tqt29v-E}NTBqW*@2!jr|7r;4+~X;=V?HCn`$=HwJqfPuGBba4oNCI~ zvo8>u_+!?hX^AsSl^z!8?wY@$`_l}AmhDz~-V+yDuV^xP7pu&gRpOnbe?o89l@Gg} z7k|y@-_lfjb=yVl&-eav$mpJG+cJ^;%GJk-L8VLL`zJEIU3{F!|CkSto$PhP%^!G_ zs`9u}6mBdoR0>ITkyF{eV3xthMSqvpoY84YY4bfMb$Wkh){@^>A7nQld|>zX#Ddz# z->-RGJ+u4!<}J?;yGLA}XUJ!6`1|E&aev8;Qga%YOrNzyV4v*Q;JxdY>^#pMIN>SR z4vlbk-3gX|!v(KAw(Y;Qd4J$U0q*V(ys1*PSI;l{ZFcAK>4!=aB3srSy%(s*zGR*2 zl=PY@w=d?`$-Ilqcv{u*CyqNhtN61-x=5ncwaV*J-#e2}DMWiK_hfAN$Lw8oZa$}1 zR9eo?t6zMNZCx9oUh%Sj>HKYRTc+&)QuW`g(IH^dlJ`%_KmTt3$;;Qr#@Qcge;QbH OFnGH9xvX{{{GjxB|KVN5M!90q*^JyMQTR zWl4}k51j3G3`yE zd0zKbasT?|_dY#6;y$lpneTIxv$GHK7whI<;O>a|rJwU+=lwe|?#Gv$?FC z@8J#$k=$zOR?KsQ?0h?EGziE>djRJPs{9U zSx=eUe%#qp>JV(nmp;i<{sJ@CwS0zMS|yf~KV>p5+RY)cs$y}h(QXFQgka7CtG4*e z^k=`ZhgD!j!D^-XY!5WqTW)ctydynoi1FJSFB+x673{Ph36fY)+s-zu@bnL2&T%|o~5)&9Oclac+2 z=tRES+m71G4<^LyZC~tmOxb-V!z06!^)V*fwv}vR@bmaKIbwY;qd=GEZ{%!lp7pIucG}L9RNjQC+nQg(vv(x(77ku7+X^mRB0Mn{7 zObh(h9nfKTwe?RL&rQLM1p2L9+kqv?zJ2J!+rf_mD zP7~B~bg(?T=ybtFQKo#UFUJl&`I)?8`d+VueQkXAUO#CFa|pIIUr_l%?c;l~#Wxfk zU%1Jq&k<4h8{H#(^YGUTc7@$K`vvz_INCq$d*c<@b=SmcC+iz^84q*q)|bq#%Jll*c;|&p z&60fSTAiD>mcQ^@QaIDVaBXm5<4g5T+Wlod-$N|f3p3_;U(|c*@^tHwBKR zrM=bq_ogRHButy@ON;ocWt*R+e!a5V^8V|M=J~RRIlpf9A!WejbQQ4nG)}kN(e|h`6 z|H;cp|64g(yJznFIe+VZw%51Sx|&p(?@hc9OTYgZj23?U*pRYlF)(K{c)I$ztaD0e F0stI+@ooSB literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xxxhdpi/ic_stat_tor.png b/src/main/res/drawable-xxxhdpi/ic_stat_tor.png new file mode 100644 index 0000000000000000000000000000000000000000..5b6154a82b7a558c9299da75eb62e808f24fd082 GIT binary patch literal 1734 zcmeHH{WsHl9RKWr&En4FS?VNdQQAo)?y_mCCYI-$gBnpNDY-0<-v~<{qg+p6sypr# z$t8JOg&1mT+#*6A)8?Uh+KlV|8~5DTIj`62yw3Z#_v@V3ZF~9&C5$!(0H8!WNpakC z#=k@(HnsP7;^qM+u(x%lZpMxO@!$Huh~B}D&0h0D9O+KrSQ;wk*-sTseU)-^5KJa9 z5t+9YAH6fF6835Eh!%B-(|+jWl7}S`wZxY(O)e0{jHBu zLN5DUipsQIK65p;v=Ota9uT1?#$$P!^Z`55s`YBsm95A7Paq3rYarLB-mFv}Bu&;Ld`cW(&*q&$s6cJ><*=1jYP zASoxtVc6(;J;SpuEoCe?hIuE6&06FR$0wW#Ll`}-C(Diy1)N6PI(wJ+YQgag-!RrDD(IZ{)ML{4A6 z=#MCQ(vuit)wc*nN)V)nsO($Gs-;^n8ew>2R@0ZtYacF5L;K|cJgMqe9n$SJe9&R= zh?mL`KXr;$4(++Y6;+LXv=Pql>>B?8HA=lJ)S9xaqyJPlEXh-G7B25`!8#vHaoi#v z-r1ZqGMC}Y>pA~MmVVSBY(d~}m6mV%l;0;PJTh=q6iNLW|JdTwz)&l*fQ(;n(AT<} zgYw8we9lAUj(YmqIgK#BhSGEHAzka<3hqu2m-+0#P;Zee-}~C}_W@nh+7l zz(O@rxtvZbzdgf9AoTK2SkSfe0v7@4`Lu(9q#zK80QBbc`}?!uJdz>^FEJv@cvpZT zD6%XM319kL#_r+7)A*^9Q8#S~u>+%TNZ&>TQ;W9DRi5GO3yMQT?}%N@^voFEk*w&w z)IV@TVlZ!45C8$L&H+i3f*r5L%}1YHn20Q=P0c_^lHG3Y^uTCZFHnLmi{-s4-r;@A zL~w{crd!?TvEy#O&5L#fr&Ty4yRc5ka}Gw3tT6m5#_66MPnb|sb~C=pW~|u;po$P^ zZc$y`r-Xhr5QioiHhXlWYv7`JTO~aPu^6u+RV=iNQYz9KIfannJED zm=@f4>!Eh|N--TOs}mbIN;}IDmd9NNAHx)~f8R*#q@*!NdrgXc7#Tr3!$?!^l#Z^Z zY{Ergv})|NApJVYA~MD%5wzDc_mjq(ncUg=BpWJwTzq)VUnJAfd&&RuV8Tktt(?b} b9azHdx>gg=dM9{rbGQJFN~b)rX2kvjN0%a* literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xxxhdpi/ic_stat_tor_xfer.png b/src/main/res/drawable-xxxhdpi/ic_stat_tor_xfer.png new file mode 100644 index 0000000000000000000000000000000000000000..8e6eb04a1968802fd050f06f7935073a684d62d3 GIT binary patch literal 2424 zcmeHI`8OMg8jVOK8Zy>UNr^RDq?M_i))Ez}k%n5M4OOC1qLv`WNJNpg)G}jl)li{h ziKWz1l;Wv1mfDKxqNt)M)fw&c{*8C$o^!u@?zz9+^PO{3NJKlyBT7dA0Dz=D0Z%?u z>i-B8J-id=k`4plAj#3i_R#+L7ymZ!f6>4i_j&T+L$8OEiOv9!(|tFvPT#?6s_Fb? zv8=Ca-lF19zb``O3?6SK%h_Gj&hAI3J$k?1pkcPd2z+a)_wvI#t6%;Z#F|WnKmWGh zXgDgVA}iwmWjjEz`0pNxWlHLiP2k;H)R`A`(ne2KeuufFkFYtrT=-8-QkARqq*o`= z9T?_-UuJ%8n*7ncV;NV%wAxQy`q-^W4?g~B`q>@wRf{2I_i|mDPsE;YSG=n6xtg&* z#{Uu&6(-&c!rhwOkWu)&aO3CG=6GAe?KiW{agM{StcM9p{0kE{)lur6#PcVty<(C| zJg&FsKd^WG&rAU{Bvh%)xy>OGNqjiI3IKqE?D1AEwE3%4MX(L{6i&acMkoXtM#70$ z&zX+F?bL+K?8v3<&#+e39+5=IaYZC2^F;KbFhiPFUNB>gQX0L}Og~V`Ym;~@y9d#l z3Eq15PF}9Oke3B2LTEoe#jhx*)2?5f7Q3O$2b(s>a8MibD@f8b&_aPlG}y??k&oS zCzLubY(kZ|_ZbaQ#)_+HZV4(Y>GQ`8kaarXivy~x*Gqe4rw41M&ciYOHM{XzM) zI7VKr0080bZ*&igMdNVg8F95yT^0*IVAJU>lPnA-Va`02X$(lB#)%~M=)@eKO=;^A z?~a8z-`3V|H=g}|OqE1-i0eP8g9-X>){=E3WNTor%29!32a-Bpt)_EzTk~V9x=e{% zX1XSYo^1HzwOJb1lRQ}ccqhsZNT?1$3mCMWSUZ_bKBn?3T^Mbrwn@_}xAWIHR{`E2 z6Zm{DRII(>kHQc-a`e3ytJe${U#2WDz}wWDm>?M8kFWXcOEm>XDo;sQItU}2#7Yf6U-p`9?(J;!)8s;ig8}8lt;sWj z1a|mQ38|Vt30#(mH6%dyGa{!tN5xx4-r(2L;S*5zHvdYjse5+)Woy4K3Rzcs2HHR! zE2tX8>XqtMj8Th*kfK4;2?yAP3-x5bSj40U|=XOSWb8~a3xx~(m7XaJ}r2bM@djEVvx0j`h>nVk^B{5&2 zWPdXUZ`eR~u$xD$l!-cNn$yZ#%+-wSQ*(JjeVbZv6}Lg7u|K1bL^w}(ag)#+pxXPW zk_LwPz*5z-+jkKdC8rEw$D*~%Cju`QsTb#4Q*0gPaDGrrCZEYQbCLP*@JrG!yDKn5b5x1PJyq+b zp;l;MV_4S4-KMDVyfCEVpU^%Vv17OuDQd`&PwJfMO-!+{9TbR&p!iF;w)rmeC4ntf zwG++3C73zRXUq|2F^ z=~XO`+RU`4>=*2P6E$S}o~D&&EVwIEiKk+&@$4}Jmof#% zO~E$0*Wd%8*IG5~gNTL-5vt7)0~OPS(X%Z>TiSv`@Q_9K-a~(@*|NyA)U&P_A!LP4 zSO)n|Mp?Ef?kX?gJx3hKXD1`hq+H{=x2gw<%D>hs@nDcQ#8bQv?n{ve7CfaLw`hb$ z^r*qzoLf>R32ja(@Y48gr@or_&MP6ni|SbA?FZ==&!&yDlP?qHRrV7&H?85IRlkf^p^}uPq}C><-$vhOCtZ%{D@yZbMe>K6Y#aV!R=yh6 z($>)_E3uIDtsW(-bmDFG#(W~Of{R$(}hCe%E+i44vBiuXf$4tQo6Qne=O;co}T`36etnE_x;-u@lyaeoJysnQtFxz z;^BNg|LvXtJkK-I>GTu;yw(or&&|!fFe2&(IQffK~g8SQmi>7H-0bp%rX6Bz^pj|7_vSc!uCjsF3D2nclq6p(Sz8=f6 zEM3WYL|on6+&trXo*V=k2&hylH;s*ry`hwv48zc3X3#%G?79mfz%Y!+G|dmUwzf`} zN~OPhA$@?hBx^wsoRo54QhkXUh5@#1pH)gd>p0Fvey7dwVWn za3G@hlu{RnXo{KBLM=B6GhW)?&gwX=ztS?M79H0it6 zkciGjQS=BRKBo_YWm#Vo3WWy;R^a>o?@Fm#0YI6i`7;0*h-heMX4ZGRr^76SI4h;R zLrQsE+k_B*%+Jps8VIOXtCyHL(;9JX6~~>^t~2avAmU}iFg}Rm_!*^CD-lHet5_`F zJP=T+RK8*6BW>fa0Pr0EoESE#dc#U7JEcCqu&{7+AfQ&Oy{^wP0Q`xFFAE_~NhxpB z>ql4L0qT^?(ozBdX*&_E6^lh3qjhyPxmK%9N-5U?;GAI?ivaMDQtCzkkld?JE7pGv zN5n~Hz6%j|5b=ppsiaqXKS1vv%d*}QLY%N|`y(mk_ssm_sI99fzRiC>48xP1d#P`d zmzI`}F!RSmlrc^7>dwy2c#^r$vI4;4uIqk1bX90$xm-&7Y`Fr2>@~caKkPqsmaXm5>eT8-CqXz41J@mudj=Zjg9*N;3)ve0>GU_ zl#b(g0}+2f#CNjU>{r9SG7`Nqht0t#+W}ydLdiZ`Mn)Opui_@% literal 0 HcmV?d00001 diff --git a/src/main/res/values-ach/strings.xml b/src/main/res/values-ach/strings.xml new file mode 100644 index 0000000..49c8dbc --- /dev/null +++ b/src/main/res/values-ach/strings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/res/values-ar/strings.xml b/src/main/res/values-ar/strings.xml new file mode 100644 index 0000000..4e3d31c --- /dev/null +++ b/src/main/res/values-ar/strings.xml @@ -0,0 +1,24 @@ + + + اوربوت + اوربوت هو تطبيق الوكيل-البروكسي الحر الذي يمكّن تطبيقات أخرى لاستخدام الإنترنت بأمان أكثر . يُستخدم اوربوت تور لتشفير تحركات مرورك على الإنترنت ، ثم يخفيك ويجعلك وهمي من خلال سلسلة من أجهزة الكمبيوتر في جميع أنحاء العالم . تور هو تطبيق حر وشبكة مفتوحة والتي تساعدك على حماية نفسك من مراقبة الشبكات التي تهدد الحرية الشخصية والخصوصية ، والأنشطة التجارية السرية والعلاقات ، وأمن الدولة والحكومات القمعيّة والتي تستخدم مايعرف باسم تحليل حركة مرور البيانات . + جاري تشغيل اوربوت... + متصل بشبكة تور + تم إيقاف اوربوت + جاري ايقاف خدمة تور + + + + + + + + + لقد تعذر بدء تشغيل تور: + إعدادات العنوان القابل للاتصال لديك تسببت بخطأ! + إعدادات المرحلات لديك تسببت بخطأ! + عُثر علي عملية تور سابقة تعمل... + غير قادر على بدء تور: + لقد تحولت الى هوية تور جديدة + تحديث الاعدادات في خدمات تور + diff --git a/src/main/res/values-az/strings.xml b/src/main/res/values-az/strings.xml new file mode 100644 index 0000000..0f1ce5e --- /dev/null +++ b/src/main/res/values-az/strings.xml @@ -0,0 +1,24 @@ + + + Orbot + Pulsuz proksi tətbiqetməsi olan Orbot başqa tətbiqetmələrə internetdən daha təhlükəsiz istifadə etmək imkanı verir. Orbot sizin internet trafikinizi şifrələmək üçün Tordan istifadə edir və dünyanın hər yerində kompüterlərin birindən o birinə sıçramaqla bunu gizlədir. Tor pulsuz proqram təminatıdır, eyni zamanda sizin şəxsi azadlığınız və təhlükəsizliyinizə, gizli biznes fəaliyyəti və əlaqələrə, o cümlədən trafik analiz adlanan dövlət təhlükəsizliyinə xələl gətirə biləcək şəbəkə nəzarəti formalarından müdafiə olunmağa yardım edən açıq şəbəkədir. + Orbot başlayır... + Tor şəbəkəsinə bağlandı + Orbot deaktivasiya edildi + Tor xidməti işini başa vurur + + + + + + + + + Tor prosesi başlana bilmədi: + Sənin ƏlçatanÜnvanlar seçimin istisnaya səbəb oldu! + Sənin keçid seçimlərin istisnaya səbəb oldu! + mövcud Tor prosesi tapıldı... + Tor-un başlanmasını dayandır: + Yeni Tor oxşarına qoşuldun! + Tor xidmətində quraşdırmalar yüklənir + diff --git a/src/main/res/values-bg/strings.xml b/src/main/res/values-bg/strings.xml new file mode 100644 index 0000000..4d4803e --- /dev/null +++ b/src/main/res/values-bg/strings.xml @@ -0,0 +1,24 @@ + + + Орбот + Orbot е безплатна прокси програма, която дава възможноста на други програми да използват интерент по-сигурно. Orbot използва Tor, за да криптира Интернет трафика и след това го скрива като препраща през няколко компютъра по целия свят. Tor е безплатен софтуер и отворена мрежа, която ти помага да се предпазиш от шпиониране по мрежата, което заплашва твоята свобода и лично пространство, конфиденциални бизнес отношение и връзки, и от вид правителствено следене наречено трафик анализ. + Орбот стартира... + Свързан към Тор мрежата + Орбот е деактивиран + TorService спира + + + + + + + + + Неуспешно стартиране на Тор процес: + Твоята ReachableAddresses настройка предизвика грешка! + Твоите настройки за препращане предизвикаха изключение! + намерил си съвместим Tor проект... + Не може да стартира Тор: + Сменихте своята Тор идентичност! + обновяване на настройките в Tor услугата + diff --git a/src/main/res/values-bn-rBD/strings.xml b/src/main/res/values-bn-rBD/strings.xml new file mode 100644 index 0000000..3f5d7d2 --- /dev/null +++ b/src/main/res/values-bn-rBD/strings.xml @@ -0,0 +1,16 @@ + + + অরবট + Orbot একটি ফ্রি প্রক্সি অ্যাপ্লিকেশন যা অন্যান্য Apps কে আরও নিরাপদভাবে ইন্টারনেট ব্যবহার করার ক্ষমতাপ্রদান করে। Orbot আপনার ইন্টারনেট ট্রাফিক এনক্রিপ্ট করতে টর ব্যবহার এবং তারপর সারা বিশ্বের কম্পিউটারের সিরিজের moddho diye porichalito kore gopon kore. টর ফ্রি সফটওয়্যার এবং আপনি ট্রাফিক বিশ্লেষণ হিসেবে পরিচিত ব্যক্তিগত স্বাধীনতা ও গোপনীয়তা, গোপনীয় বাণিজ্যিক কার্যক্রম এবং সম্পর্ক, এবং রাষ্ট্রীয় নিরাপত্তা হুমকির মুখে পড়ে নেটওয়ার্ক নজরদারি একটি ফর্ম বিরুদ্ধে রক্ষা করতে সাহায্য করে যে একটি খোলা নেটওয়ার্ক. + অরবট চালু হচ্ছে . . . + টর নেটওয়ার্কের সাথে সংযুক্ত হয়েছে + অরবট নিষ্ক্রিয় করা হয়েছে + + + + + + + + + diff --git a/src/main/res/values-bn-rIN/strings.xml b/src/main/res/values-bn-rIN/strings.xml new file mode 100644 index 0000000..49c8dbc --- /dev/null +++ b/src/main/res/values-bn-rIN/strings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/res/values-bn/strings.xml b/src/main/res/values-bn/strings.xml new file mode 100644 index 0000000..49c8dbc --- /dev/null +++ b/src/main/res/values-bn/strings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/res/values-brx/strings.xml b/src/main/res/values-brx/strings.xml new file mode 100644 index 0000000..49c8dbc --- /dev/null +++ b/src/main/res/values-brx/strings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/res/values-bs/strings.xml b/src/main/res/values-bs/strings.xml new file mode 100644 index 0000000..0fe1e9e --- /dev/null +++ b/src/main/res/values-bs/strings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/res/values-ca/strings.xml b/src/main/res/values-ca/strings.xml new file mode 100644 index 0000000..c19e70d --- /dev/null +++ b/src/main/res/values-ca/strings.xml @@ -0,0 +1,21 @@ + + + Orbot + L\'Orbot s\'està iniciant... + Esteu connectat a la xarxa Tor + L\'Orbot està desactivat + + + + + + + + + No s\'ha pogut iniciar el procés Tor: + Els paràmetres d\'adreces accessibles han provocat una excepció. + Els paràmetres de reemissió han causat una excepció. + s\'ha trobat un procés Tor existent... + Ha estat impossible iniciar Tor: + Heu canviat a una nova identitat Tor. + diff --git a/src/main/res/values-cs-rCZ/strings.xml b/src/main/res/values-cs-rCZ/strings.xml new file mode 100644 index 0000000..b0839e7 --- /dev/null +++ b/src/main/res/values-cs-rCZ/strings.xml @@ -0,0 +1,21 @@ + + + Orbot + Orbot se spouští... + Připojen k síti Tor + Orbot je deaktivován + + + + + + + + + Nelze spustit Tor proces: + Vaše nastavení dostupných adres (ReachableAddresses) způsobilo chybu! + Vaše nastavení relace způsobilo výjimku! + nalezev existující Tor proces… + Nelze spustit Tor: + Vaše identita na Toru byla změněna! + diff --git a/src/main/res/values-cs/strings.xml b/src/main/res/values-cs/strings.xml new file mode 100644 index 0000000..3cb2792 --- /dev/null +++ b/src/main/res/values-cs/strings.xml @@ -0,0 +1,16 @@ + + + Orbot + Orbot se spouští… + Připojen k síti Tor + \"Orbot je deaktivován + Orbot se vypíná + + + + + + + + + diff --git a/src/main/res/values-cy/strings.xml b/src/main/res/values-cy/strings.xml new file mode 100644 index 0000000..0b48c55 --- /dev/null +++ b/src/main/res/values-cy/strings.xml @@ -0,0 +1,14 @@ + + + Orbot + Mae Orbot yn cychwyn... + Wedi cysylltu â rhwydwaith Tor + + + + + + + + + diff --git a/src/main/res/values-da/strings.xml b/src/main/res/values-da/strings.xml new file mode 100644 index 0000000..9dea6c2 --- /dev/null +++ b/src/main/res/values-da/strings.xml @@ -0,0 +1,13 @@ + + + Orbot + Orbot er en gratis og åben proxy-applikation, der gør det muligt at anvende internettet mere sikkert fra andre programmer. Orbot bruger Tor til at kryptere internettrafikken, og skjuler den ved at sende den gennem serverere, lokaliseret i hele verden. Tor er gratis og åben software, der kan hjælpe dig mod netværksovervågning kaldet trafikanalyse, der kan true din personlige frihed, dit privatliv, handelsaktivitet og forhold. + Orbot starter op ... + Der er forbindelse til Tor-netværket + Orbot er slået fra + Kunne ikke starte Tor processen: + Din ReachableAdresses indstilling forudsagede en fejl! + Dine relæindstillinger forårsagede en fejl! + fandt eksisterende Tor process... + Kunne ikke starte Tor: + diff --git a/src/main/res/values-de-rAT/strings.xml b/src/main/res/values-de-rAT/strings.xml new file mode 100644 index 0000000..49c8dbc --- /dev/null +++ b/src/main/res/values-de-rAT/strings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml new file mode 100644 index 0000000..f3faf41 --- /dev/null +++ b/src/main/res/values-de/strings.xml @@ -0,0 +1,26 @@ + + + Orbot + Orbot ist eine kostenlose Proxy-Anwendung, mit deren Hilfe andere Anwendungen das Internet sicherer nutzen können. Orbot verwendet Tor, um Ihren Internetverkehr zu verschlüsseln und ihn dann zu verbergen, indem er über eine Reihe weltweit verteilter Computer geleitet wird. Tor ist ein freies Programm und ein offenes Netzwerk, das Ihnen hilft, sich gegen Angriffe auf die persönliche Freiheit und die Privatsphäre oder auf vertrauliche Geschäftsbeziehungen sowie gegen die Datenüberwachung aus Staatssicherheitsgründen zu wehren. + Orbot startet … + Verbunden mit dem Tor-Netzwerk + Orbot ist deaktiviert + TorService wird heruntergefahren + + + + + + + + + Tor-Prozess konnte nicht gestartet werden: + Ihre eingestellten erreichbaren Adressen haben einen Ausnahmefehler verursacht! + Ihre Relaiseinstellungen haben einen Ausnahmefehler verursacht! + bestehender Tor-Prozess gefunden … + Tor kann nicht gestartet werden: + Sie haben zu einer neuen Tor-Identität gewechselt! + Einstellungen im Tor-Dienst werden aktualisiert + MiB/s + KiB/s + diff --git a/src/main/res/values-el/strings.xml b/src/main/res/values-el/strings.xml new file mode 100644 index 0000000..2ca098b --- /dev/null +++ b/src/main/res/values-el/strings.xml @@ -0,0 +1,21 @@ + + + Orbot + Το Orbot ξεκινά + Συνδέθηκε στο δίκτυο Tor + \"Το Orbot είναι Απενεργοποιημένο + + + + + + + + + Αδυναμία εκκίνησης της διεργασίας Tor: + Οι ρυθμίσεις ReachableAddresses προκάλεσαν μια εξαίρεση! + Οι ρυθμίσεις αναμεταδότη σας προκάλεσαν μια εξαίρεση! + αναζήτηση διεργασιών Tor... + Αδυναμία εκκίνησης του Tor: + Έχετε αλλάξει επιτυχώς την ταυτότητα σας στο Tor! + diff --git a/src/main/res/values-en-rGB/strings.xml b/src/main/res/values-en-rGB/strings.xml new file mode 100644 index 0000000..05e2b96 --- /dev/null +++ b/src/main/res/values-en-rGB/strings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/res/values-eo/strings.xml b/src/main/res/values-eo/strings.xml new file mode 100644 index 0000000..5f492fc --- /dev/null +++ b/src/main/res/values-eo/strings.xml @@ -0,0 +1,19 @@ + + + Orbot + Orbot ŝaltiĝas... + Konektita al Tor-reto + Orbot estas malaktivigita + + + + + + + + + Tor-procedo ne sukcesis stariĝi: + Via agordo de relajso kaŭzis escepton! + trovi ekzistantan Tor-procedon... + Ne eblas startigi Tor: + diff --git a/src/main/res/values-es-rAR/strings.xml b/src/main/res/values-es-rAR/strings.xml new file mode 100644 index 0000000..1763d99 --- /dev/null +++ b/src/main/res/values-es-rAR/strings.xml @@ -0,0 +1,16 @@ + + + Orbot + Orbot está iniciándose... + Conectado a la red Tor + \"Orbot está Desactivado + + + + + + + + + No se puede iniciar Tor: + diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml new file mode 100644 index 0000000..38b1f4b --- /dev/null +++ b/src/main/res/values-es/strings.xml @@ -0,0 +1,24 @@ + + + Orbot + Orbot es una aplicación libre de proxy (interpuesto) que faculta a otras aplicaciones para usar Internet de forma más segura. Orbot utiliza Tor para cifrar su tráfico de Internet, y luego lo oculta rebotándolo a través de una serie de computadoras por todo el mundo. Tor es software libre y una red abierta que le ayuda a defenderse contra una forma de vigilancia de red conocida como análisis de tráfico que amenaza la libertad y la privacidad personales, las actividades y relaciones comerciales confidenciales, y la seguridad de estado. + Orbot está iniciandose... + Conectado a la red Tor + Orbot está desactivado + TorService se está cerrando + + + + + + + + + No se pudo iniciar el proceso de Tor: + ¡Sus \'Reglas de direcciones accesibles\' han producido una excepción! + ¡La configuración de su repetidor ha producido una excepción! + Se encontró un proceso de Tor ya existente... + No fue posible iniciar Tor: + ¡Ha cambiado a una nueva identidad de Tor! + actualizando la configuración en el servicio Tor + diff --git a/src/main/res/values-et/strings.xml b/src/main/res/values-et/strings.xml new file mode 100644 index 0000000..2fda107 --- /dev/null +++ b/src/main/res/values-et/strings.xml @@ -0,0 +1,21 @@ + + + Orbot + Orbot käivitub... + Ühendatud Tor võrgustikku + Orbot deaktiveeritud + + + + + + + + + Tor protsessi käivitamine ebaõnnestus: + Teie ReachableAddresses seadistus põhjustas ekse! + Teie releeseadistus põhjustas ekse! + leidus töötav Tor protsess... + Tor käivitamine ebaõnnestus: + Lülitusite uuele Tor identiteedile! + diff --git a/src/main/res/values-eu/strings.xml b/src/main/res/values-eu/strings.xml new file mode 100644 index 0000000..028af70 --- /dev/null +++ b/src/main/res/values-eu/strings.xml @@ -0,0 +1,21 @@ + + + Orbot + Orbot abiarazten ari da... + Tor sarera konektatuta + \"Orbot desaktibatuta dago + + + + + + + + + Ezin izan da Tor prozesua hasi: + Zure helbide atzigarriak salbuespena sortu du! + Zure errele ezarpenak salbuespena sortu dute! + Aurreko Tor prozesua topatuta... + Ezin izan da Tor hasi: + ChatSecure - Andriodentzako berehalako mezulari bezero ziurra + diff --git a/src/main/res/values-fa/strings.xml b/src/main/res/values-fa/strings.xml new file mode 100644 index 0000000..225bbca --- /dev/null +++ b/src/main/res/values-fa/strings.xml @@ -0,0 +1,24 @@ + + + ربات پیازی + اربت یک برنامه پروکسی مجانی است که دیگر برنامه ها را به استفاده امن از اینترنت توانمند می کند . اربوت از تور برای رمزگذاری کردن ترافیک اینترنت شما استفاده می کند و بعد آن ها را از طریق کامپیوترهای متفاوت در نقاط مختلف جهان مخفی می کند. تور یک برنامه مجانی و شبکه باز است که شما از شما در مقابل تحت نظر بودن در شبکه٬‌ تهدید آزادی های شخصی٬ خصوصی٬ فعالیت های کاری و رابطه های شخصی بطور امن محافظت می کند. + اربوت درحال آغاز است... + متصل به شبکه تور + اربوت غیرفعال شده است. + TorService خاموش است + + + + + + + + + تور نتواست اجرا شود: + تنظیمات شما برای آدرس قابل دسترسی باعث ایجاد خطا شده اند ! + تنظیمات رله شما موجب خطا شده اند + یک تور درحال اجرا پیدا شد... + ناتوان در راه اندازی تور: + شما به شناسه جدید تور منتقل شدید + به روز رسانی تنظیمات در سرویس Tor + diff --git a/src/main/res/values-fi/strings.xml b/src/main/res/values-fi/strings.xml new file mode 100644 index 0000000..cf21f11 --- /dev/null +++ b/src/main/res/values-fi/strings.xml @@ -0,0 +1,23 @@ + + + Orbot + Orbot on ilmainen välityspalvelinsovellus, joka tarjoaa muille sovelluksille mahdollisuuden käyttää internetiä turvallisemmin. Orbot käyttää Toria kryptaamaan verkkoliikenteesi ja sitten piilottaa sen kierrättämällä sitä usean tietokoneen kautta ympäri maailman. Tor on vapaa ohjelmisto ja avoin verkosto, jotka auttavat puolustautumaan vapautta ja yksityisyyttä uhkaavalta verkkovalvonnalta ja valtioiden verkonseurannalta sekä suojaamaan salaisia liiketoimintoja ja -yhteyksiä. + Orbot käynnistyy... + Yhdistetty Tor-verkkoon + Orbot on poistettu käytöstä + + + + + + + + + Tor-prosessin käynnistäminen epäonnistui: + ReachableAccess-asetuksesi aiheuttivat virheen! + Releasetuksesi aiheuttivat virheen! + löytyi olemassaoleva Tor-prosessi... + Torin käynnistys epäonnistui: + Vaihdoit Tor-identiteettisi uuteen! + päivitetään Tor-palvelun asetuksia + diff --git a/src/main/res/values-fr-rFR/strings.xml b/src/main/res/values-fr-rFR/strings.xml new file mode 100644 index 0000000..4e01819 --- /dev/null +++ b/src/main/res/values-fr-rFR/strings.xml @@ -0,0 +1,18 @@ + + + Orbot + Orbot est une appli libre de serveur mandataire permettant aux applis d\'utiliser Internet avec une sécurité accrue. Orbot utilise Tor pour chiffrer votre trafic Internet et le cache ensuite en le relayant au travers d\'ordinateurs de par le monde. Tor est un logiciel libre et un réseau ouvert qui vous aide à vous défendre contre une forme de surveillance réseau qui menace la liberté personnelle et la protection des données personnelles, les activités professionnelles confidentielles et les relations, et l\'analyse du trafic des gouvernements. + Orbot démarre… + Connecté au réseau Tor + Orbot est désactivé + TorService est en cours d\'extinction + Impossible de démarrer le processus Tor : + Vos paramètres ReachableAddresses (adresses accessibles) ont causé une exception ! + Vos paramètres de relais ont causé une exception ! + un processus existant de Tor a été trouvé… + Impossible de démarrer Tor : + Vous avez basculé vers une nouvelle identité Tor ! + mise à jour des paramètres dans le service Tor + Mio/s + Kio/s + diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml new file mode 100644 index 0000000..4821a35 --- /dev/null +++ b/src/main/res/values-fr/strings.xml @@ -0,0 +1,11 @@ + + + Orbot + Orbot est une application proxy gratuite qui améliore l\'utilisation plus sécurisée des applications. Orbot utilise Tor pour crypter votre trafic internet et le cacher en passant par une série d\'ordinateur partout dans le monde. Tor est un logiciel gratuit et un réseau ouvert qui vous aide à vous défendre contre les surveillances de réseau qui font peur à la liberté personnelle et la vie privée, les activités confidentielles des entreprises et des relations, et l\'état de la sécurité connu sous le nom d\'analyse de trafic. + Démarrage de Orbot… + Connecté au réseau Tor + Orbot est désactivé + Ouvrez Orbot pour vous connecter à Tor + Le service Tor est en cours de fermeture + Se Connecter à Tor + diff --git a/src/main/res/values-gl/strings.xml b/src/main/res/values-gl/strings.xml new file mode 100644 index 0000000..06debfd --- /dev/null +++ b/src/main/res/values-gl/strings.xml @@ -0,0 +1,22 @@ + + + Orbot + Orbot é unha aplicación de proxy libre que permite a outras aplicacións usar a internet dun xeito máis seguro. Orbot usa Tor para encriptar o teu tráfico de internet ocultando e rebotándoo a través dunha serie de ordenadores ao redor do mundo. Tor é software libre e unha rede aberta que axuda a defenderte contra unha forma de vixiancia na rede que ameaza a liberdade e privacidade persoal, actividades confidenciáis de negocios e relacións, e estado de seguridade coñecido como análise de tráfico. + Orbot está a se iniciar... + Conectado á rede Tor + Orbot está desactivado + + + + + + + + + No se pudio inciar o proceso Tor: + O seu axuste de ReachableAddresses causou unha excepción! + Os seus axustes de relay causou unha excepción! + atopado proceso Tor existente... + Incapaz de arrancar Tor + Cambiou a unha nova identidade Tor! + diff --git a/src/main/res/values-gu-rIN/strings.xml b/src/main/res/values-gu-rIN/strings.xml new file mode 100644 index 0000000..0fe1e9e --- /dev/null +++ b/src/main/res/values-gu-rIN/strings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/res/values-gu/strings.xml b/src/main/res/values-gu/strings.xml new file mode 100644 index 0000000..0fe1e9e --- /dev/null +++ b/src/main/res/values-gu/strings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/res/values-guc/strings.xml b/src/main/res/values-guc/strings.xml new file mode 100644 index 0000000..be3befc --- /dev/null +++ b/src/main/res/values-guc/strings.xml @@ -0,0 +1,44 @@ + + + orbot + 0rbot VNP + Orbot shia wane a\'yataaya maliasat, akaalinjia tü wane a\'yataayaakalüirua süpüla anainjatüin shia\'yataaya. Orbot a\'yataasü sulu\'u \"internet\" süka wane kanüliasü Tor je süchikijee sunujuluin sulu\'u wane kojuyasü \"computador\" sainküsü mmakat. Tor maliasat je taashisü süpüla sa\'inmajüin pia suulia eekai mojulaweein pümüin wanaa sümaa pi\'yataain sulu\'u \"internet\". + Eepeja\'asu Orbot… + Antirrawaas jumaa atalaya Tor + Yokutusu\'u Orbot + Pe\'erulaa Orbot jupula puyata ín juma Tor. + Yokuuter Tor + Antirrawaa sunain Tor + sükumajia Orbot maliaasat + + Nojotsü o\'ottuin sukuaipa shiatain Tor + Tü anatiaaka erree juntunuín casaa, nojots jainrre\'in + Tü anatiaka eerjaya maimatuaa, nojots jainrrei´n + Antusü sünain sajia wanee Tor etüjulia … + no jot süpüla achuajaje\'in Tor + + Tü piranaajain sülü wanee jeketü shiyawase Tor + + Anoutuüin tüü akümajiak süpüla Tor + + Anuli\'aa jekets + Atapajus juntia + süntiraya tü süchecheria sütsin + + KiB/s + MiB/s + + %sPuuproxinka copo de nieve munaka amuliajús wayuu jupula nainjuin tu nainrreka.%s + + Nojoluinjat aitanüin junulia cewoolla. + Motée ainrüü + Proxy copo de nieve amuliajús jupula painruuín tu paiinrreka + proxinka copo de nieve Yokúuts + Jeketerr tu anatiakal tu ipünamiukal C + Kaa´muinshi atumaa chi ayawuajuikai kazachiki + Antirrashi Tor juma ipunamuika eki. + Nojots apunuin juchuntia eepeja\'asú + Mojus jukuaipaa junain j\'iitaayaa erre aatunu\'in geoip + + + diff --git a/src/main/res/values-gum/strings.xml b/src/main/res/values-gum/strings.xml new file mode 100644 index 0000000..3325caf --- /dev/null +++ b/src/main/res/values-gum/strings.xml @@ -0,0 +1,46 @@ + + + Orbot + Orbot VPN + Orbotpe chi anwei kɵmɵn ikpape internetyu mentra kenamarmɵ chine maramtik kɵn. +Orbotpe, internet yu wammeran mentra lasrpitsɵsrkɵmɵntrappe Tor teik tɵka wetɵ ashmɵntrai isua marik kɵn, trek chippe chi wam merane esekawa ampumai imai purep ipik kɵn ik pirau mupa kuin pɵrap. +Torpe kan software kɵn mayaelai chi anwei kɵmɵ, incha chu puisrɵ, kape chu pasriksrɵpe, wam kerilan mu kɵpen wamipikelɵ wetɵ ashmɵntrai purukupik kɵn, trek tɵwai chutɵ kucha maya misakwan aship pasrapik Estado teik wan purukupik kɵn, kan \"analisis de tráfico\" teik tɵka. + Orbotpe pɵlpasrɵp pasran… + Toryu kelicha pasrik + Orbotpe kuetsik pasran + Orbotwan kur Toryu kelichap + Orbotpe kasrɵp pasran + Tor yu kelitsik pasran + Orborwan tamartrentikpe kalɵ kɵn + + Torwan p­‎​ɵlpasrtrap kaimeik pasran + ReachableAddresses wan tamartrentikpe nɵrɵ pek tamarik kenamisran + Relay wan tamartrentikpe nɵrɵ pek tamarik kenamisran + Wetɵtraik kenamisran Tor… + Torpe pɵlpasrtrap kaiman + + Tor maik kenamisrɵpik kuipe yunɵmaran + + Toryu srɵitɵ tamaramelɵ + + Srɵitɵ malɵ kena maramik + Puerto de controlwan munirap + Puerto de controlyu kelichip + + KiB/s + MiB/s + + 1%sÑui proxy snowflake purukun munkɵsrɵn asam keilan ashchap inchen 1%s + + Ishik munchi meran asam kaik k​­ɵn + Memoria patsap pasran + Snowflake proxype kurik pasran + Snowflake proxype kasreik pasran + Torrcwan na isuikmai tamaramik + Control portwan keerik pasramisran + Control portwan kelitsik pasran + Katɵle pɵlpasramik cha mieikwan ka tan, yantɵ pɵlmarik pasran + Geoipyu mariklan tamara pasramikyu, kalɵ wepian + + + diff --git a/src/main/res/values-hi/strings.xml b/src/main/res/values-hi/strings.xml new file mode 100644 index 0000000..a343744 --- /dev/null +++ b/src/main/res/values-hi/strings.xml @@ -0,0 +1,13 @@ + + + Orbot + औरबौट एक मुफ्त अैप + + + + + + + + + diff --git a/src/main/res/values-hr-rHR/strings.xml b/src/main/res/values-hr-rHR/strings.xml new file mode 100644 index 0000000..b80fb51 --- /dev/null +++ b/src/main/res/values-hr-rHR/strings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/res/values-hr/strings.xml b/src/main/res/values-hr/strings.xml new file mode 100644 index 0000000..9d94cbb --- /dev/null +++ b/src/main/res/values-hr/strings.xml @@ -0,0 +1,24 @@ + + + Orbot + Orbot je besplatna proxy aplikacija koja omogućuje ostalim aplikacijama da koriste internet sigurnije. Orbot koristi Tor za enkripciju Vašeg Internet prometa, a zatim ga skriva šaljući ga kroz seriju računala diljem svijeta. Tor je besplatan software i otvorena mreža koja pomaže u borbi protiv nadzora mreže koji ugrožava osobne slobode i privatnost, povjerljive poslovne aktivnosti i odnose, te pomaže u borbi protiv analize prometa. + Orbot se pokreće... + Spojen na Tor mrežu + Orbot je deaktiviran + TorService se gasi + + + + + + + + + Nije moguće započeti Tor proces: + Vaše ReachableAddresses postavke su uzrokovale iznimku! + Vaše postavke releja su uzrokovale iznimku! + pronađeni postojeći Tor procesi... + Nije moguće pokrenuti Tor: + Prebacili ste se na nov Tor identitet! + ažuriram postavke u Tor usluzi + diff --git a/src/main/res/values-hu/strings.xml b/src/main/res/values-hu/strings.xml new file mode 100644 index 0000000..01e90d3 --- /dev/null +++ b/src/main/res/values-hu/strings.xml @@ -0,0 +1,24 @@ + + + Orbot + Az Orbot egy ingyenes proxy alkalmazás, ami képessé tesz alkalmazásokat, hogy biztonságosabban használhassák az internetet. Az Orbot Tor-t használ, hogy titkosítsa az internetforgalmadat és elrejtse azáltal, hogy pattogtatja számítógépek sorozatain keresztül a világ körül. A Tor ingyenes szoftver és nyitott hálózat, ami segít megvédeni a hálózati felügyelettől, ami fenyegeti a személyi szabadságot és magánéletet, a bizalmas céges tevékenységeket és kapcsolatokat, és állambiztonság címén a forgalomelemzéstől. + Az Orbot indul... + Csatlakozva a Tor hálózathoz + Az Orbot deaktiválva + A TorService leáll + + + + + + + + + Nem indítható el a Tor folyamat: + Az elérhető címeid beállításai kivételt okoztak! + Az átjátszód beállításai kivételt okoztak! + létező Tor folyamat találva... + A Tor indítása sikertelen: + Új Tor identitásra váltottál! + a Tor szolgáltatás beállításainak frissítése + diff --git a/src/main/res/values-hy-rAM/strings.xml b/src/main/res/values-hy-rAM/strings.xml new file mode 100644 index 0000000..49c8dbc --- /dev/null +++ b/src/main/res/values-hy-rAM/strings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/res/values-ia/strings.xml b/src/main/res/values-ia/strings.xml new file mode 100644 index 0000000..b80fb51 --- /dev/null +++ b/src/main/res/values-ia/strings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/res/values-in/strings.xml b/src/main/res/values-in/strings.xml new file mode 100644 index 0000000..da496ec --- /dev/null +++ b/src/main/res/values-in/strings.xml @@ -0,0 +1,24 @@ + + + Orbot + Orbot adalah aplikasi proxy gratis yang membuat aplikasi-aplikasi lainnya dapat terkoneksi dengan internet secara aman. Orbot menggunakan Tor untuk mengenkripsi hubungan internet anda dan menyalurkannya melewati berbagai komputer di seluruh dunia. Tor adalah software gratis dan suatu network terbuka, yang membantu anda menghindari pengawasan network yang mengancam kebebasan pribadi dan privasi, aktivitas bisnis rahasia dan relasi, serta keamanan negara yang dikenal dengan analisa traffic. + Orbot sedang dimulai... + Tersambung ke Jaringan Tor + Orbot telah dibatalkan + TorService dimatikan + + + + + + + + + Tidak dapat memulai proses Tor: + Pengaturan ReachableAddresses anda menyebabkan kesalahan! + Pengaturan relay anda menyebabkan kesalahan! + menemukan proses Tor yang ada... + Tidak dapat memulai Tor: + Anda telah beralih ke identitas Tor baru! + memperbarui setting pada layanan Tor + diff --git a/src/main/res/values-is/strings.xml b/src/main/res/values-is/strings.xml new file mode 100644 index 0000000..ae45307 --- /dev/null +++ b/src/main/res/values-is/strings.xml @@ -0,0 +1,26 @@ + + + Orbot + Orbot er ókeypis proxy smáforrit sem gerir öðrum smáforritum kleift að nota veraldarvefinn á öruggari hátt. Orbot notar Tor til að dulkóða umferð þína á netinu og felur hana svo með að hoppa í gegnum fjölda tölva um allan heim. Tor er ókeypis hugbúnaður og opið net sem aðstoðar þig við að verjast gegn eftirliti á netinu sem vinnur gegn frelsi einkalífsins og friðhelgi, trúnaðar viðskiptamálum og samböndum, og ríkisöryggi þekkt sem umferðargreining. + Orbot er að ræsa... + Tengdur við Tor netið + Orbot er slökkt + + + + + + + + + Gat ekki kveikt á Tor þráð: + Þitt ReachableAddressur stillingar ollu undanþágu! + Endurvarp stillingar þínar ollu undanþágu! + fann annan Tor þráð... + Get ekki kveikt á Tor: + Þú ert komin með nýtt Tor auðkenni! + uppfæri stillingar í Tor þjónustu + + MiB/sek + KiB/sek + diff --git a/src/main/res/values-it/strings.xml b/src/main/res/values-it/strings.xml new file mode 100644 index 0000000..1949cd4 --- /dev/null +++ b/src/main/res/values-it/strings.xml @@ -0,0 +1,24 @@ + + + Orbot + Orbot è un\'applicazione proxy che permette alle altre applicazioni di accedere a internet in maniera più sicura. Orbot usa Tor per cifrare il traffico internet e lo nasconde poi facendolo rimbalzare attraverso una serie di computer attorno al mondo. Tor è un software libero e una rete aperta che aiuta a difendersi da una forma di sorveglianza della rete conosciuta come analisi del traffico. Quest\'ultima minaccia libertà e privacy personale, attività commerciali riservate, rapporti interpersonali, e persino la sicurezza di stato. + Orbot è in esecuzione... + Connesso alla rete Tor + \"Orbot è disattivato + TorService si sta spegnendo + + + + + + + + + Impossibile avviare il processo Tor: + La tua configurazione dell\'indirizzo raggiungibile ha causato un\'eccezione! + La configurazione del tuo relay ha causato un\'eccezione! + trovato un processo Tor esistente... + Impossibile avviare Tor: + Sei passato a una nuova identità Tor! + aggiornamento impostazioni nel servizio Tor + diff --git a/src/main/res/values-iw/strings.xml b/src/main/res/values-iw/strings.xml new file mode 100644 index 0000000..88c6df0 --- /dev/null +++ b/src/main/res/values-iw/strings.xml @@ -0,0 +1,21 @@ + + + Orbot + Orbot הינה אפליקציית פרוקסי חינמית המאפשרת לאפליקציות אחרות להשתמש באינטרנט בבטחה. Orbot נעזרת ב-Tor כדי להצפין את תעבורת האינטרנט שלך ולהסוותה באמצעותה ניתובה דרך מספר מחשבים ברחבי העולם. Tor היא תוכנה חופשית ורשת פתוחה המסייעת לך להתגונן מפני סוגים מסוימים של אמצעי ניטור ומעקב אחר רשת האינטרנט המאיימים על הפרטיות, החירות האישית, פעילויות עסקיות ומערכות יחסים חשאיות. + Orbot מתחיל… + מחובר לרשת Tor + Orbot כבוי + + + + + + + + + אין אפשרות להתחיל תהליך Tor: + נמצא תהליך Tor קיים… + לא מסוגל להתחיל את Tor: + החלפת אל זהות Tor חדשה! + מעדכן הגדרות Tor במכשיר + diff --git a/src/main/res/values-ja/strings.xml b/src/main/res/values-ja/strings.xml new file mode 100644 index 0000000..c076afb --- /dev/null +++ b/src/main/res/values-ja/strings.xml @@ -0,0 +1,24 @@ + + + Orbot + Orbotは他のアプリがインターネットをより安全に使うことを可能にするフリーのプロキシアプリです。Orbotでは、Torを用いてあなたの端末のトラフィックを暗号化し、世界中のコンピューターを中継することで、そのトラフィックを隠します。Torはフリーのソフトウェアとオープンなネットワークであり、ユーザーの自由とプライバシーを脅かす監視活動や、機密のビジネス活動、国家によるトラフィック分析から身を守ることを助けてくれます。 + Orbotが開始されています... + Torネットワークに接続しています + \"Orbotが解除されました + TorServiceが終了しています + + + + + + + + + Torプロセスの起動に失敗: + あなたのReachableAddresses設定により例外が発生しました! + あなたのリレー設定により例外が発生しました! + Torプロセスを発見 + Torを実行できませんでした + 新たな Tor の身元に切り替えました。 + Torサービスの設定を更新中 + diff --git a/src/main/res/values-kn-rIN/strings.xml b/src/main/res/values-kn-rIN/strings.xml new file mode 100644 index 0000000..49c8dbc --- /dev/null +++ b/src/main/res/values-kn-rIN/strings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/res/values-kn/strings.xml b/src/main/res/values-kn/strings.xml new file mode 100644 index 0000000..49c8dbc --- /dev/null +++ b/src/main/res/values-kn/strings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/res/values-ko/strings.xml b/src/main/res/values-ko/strings.xml new file mode 100644 index 0000000..c130ba4 --- /dev/null +++ b/src/main/res/values-ko/strings.xml @@ -0,0 +1,21 @@ + + + Orbot + Orbot 시작 중… + Tor 네트워크에 연결됨 + Orbot이 비활성화되어 있습니다 + + + + + + + + + Tor 프로세스 시작 불가능: + 귀하의 ReachableAddress 설정은 예외를 발생시켰습니다! + 귀하의 중계서버 설정은 예외를 발생시켰습니다! + 존재하고 있는 Tor 프로세스 발견 + Tor 시작 불가능 + 새로운 Tor 신원으로 전환되었습니다! + diff --git a/src/main/res/values-ky/strings.xml b/src/main/res/values-ky/strings.xml new file mode 100644 index 0000000..05e2b96 --- /dev/null +++ b/src/main/res/values-ky/strings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/res/values-lt-rLT/strings.xml b/src/main/res/values-lt-rLT/strings.xml new file mode 100644 index 0000000..49c8dbc --- /dev/null +++ b/src/main/res/values-lt-rLT/strings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/res/values-lt/strings.xml b/src/main/res/values-lt/strings.xml new file mode 100644 index 0000000..938fc43 --- /dev/null +++ b/src/main/res/values-lt/strings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/res/values-lv/strings.xml b/src/main/res/values-lv/strings.xml new file mode 100644 index 0000000..27bd19c --- /dev/null +++ b/src/main/res/values-lv/strings.xml @@ -0,0 +1,23 @@ + + + Orbot + Orbot ir starpniekserveru bezmaksas lietotne, kas sniedz iespēju citām lietotnēm drošāk lietot internetu. Orbot izmanto Tor, lai šifrētu Jūsu interneta datplūsmu, tad to paslēpj, pārsūtot to caur daudziem datoriem visā pasaulē. Tor ir bezmaksas programmatūra un atvērts tīkls, kas palīdz Jums aizsargāties pret tīkla uzraudzības veidu - datplūsmas analīzi -, ar kuras palīdzību tiek apdraudēta personiskā brīvība un privātums, konfidenciālas lietišķas darbības un attiecības, kā arī valsts drošība. + Orbot startē... + Izveidots savienojums ar tīklu Tor + Orbot ir deaktivēts + + + + + + + + + Neizdevās palaist Tor\'a procesu: + Jūsu ReachableAddresses iestatījumi izraisīja izņēmuma stāvokli! + Jūsu retranslatora iestatījumi izraisīja izņēmuma situāciju! + Atrada esošu Tor procesu... + Nevar startēt Tor: + Jūs pārslēdzāties uz jaunu Tor\'a identitāti! + atjaunina Tor pakalpojuma iestatījumus + diff --git a/src/main/res/values-mk/strings.xml b/src/main/res/values-mk/strings.xml new file mode 100644 index 0000000..5cf0c16 --- /dev/null +++ b/src/main/res/values-mk/strings.xml @@ -0,0 +1,23 @@ + + + Orbot + Orbot е слободна прокси апликација која им овозможува на другите апликации да го користат интернетот побезбедно. Orbot користи Tor за шифрирање на интернет-сообраќајот, а потоа го сокрива и го доставува преку неколку компјутери во целиот свет. Tor е слободен софтвер и отворена мрежа која се справува со вид надзор на мрежата која штети на личната слобода и приватноста, доверливи деловни активности и односи, и државната безбедност позната како анализа на сообраќајот. + Орбот се стартува... + Поврзан на мрежата на Tor + Орбот е деактивиран + + + + + + + + + Не може да се започне Tor-процесот: + Вашите поставки за достапните адреси предизвикаа грешка! + Вашите поставки за реле предизвикаа грешка! + Пронајден постоечки Tor-процес ... + Tor не може да се стартува: + Се префрливте на нов идентитет на Tor! + ажурирање на поставките во сервисот Tor + diff --git a/src/main/res/values-ml/strings.xml b/src/main/res/values-ml/strings.xml new file mode 100644 index 0000000..49c8dbc --- /dev/null +++ b/src/main/res/values-ml/strings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/res/values-mn/strings.xml b/src/main/res/values-mn/strings.xml new file mode 100644 index 0000000..49c8dbc --- /dev/null +++ b/src/main/res/values-mn/strings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/res/values-mr-rIN/strings.xml b/src/main/res/values-mr-rIN/strings.xml new file mode 100644 index 0000000..49c8dbc --- /dev/null +++ b/src/main/res/values-mr-rIN/strings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/res/values-ms-rMY/strings.xml b/src/main/res/values-ms-rMY/strings.xml new file mode 100644 index 0000000..ae6035b --- /dev/null +++ b/src/main/res/values-ms-rMY/strings.xml @@ -0,0 +1,20 @@ + + + Orbot + Orbot telah dimulakan.. + Bersambung ke rangkaian Tor + \"Orbot telah dinyah-aktifkan + + + + + + + + + Tidak boleh memulakan proses Tor: + Tetapan ReachableAddresses anda menyebabkan pengecualian! + Tetapan relay anda menyebabkan pengecualian! + Proses Tor sedia ada ditemui.. + Tidak dapat memulakan Tor: + diff --git a/src/main/res/values-ms/strings.xml b/src/main/res/values-ms/strings.xml new file mode 100644 index 0000000..b3d8f16 --- /dev/null +++ b/src/main/res/values-ms/strings.xml @@ -0,0 +1,21 @@ + + + Orbot + Orbot telah dimulakan.. + Bersambung ke rangkaian Tor + \"Orbot telah dinyah-aktifkan + Orbot sedang ditutup + + + + + + + + + Tidak boleh memulakan proses Tor: + Tetapan ReachableAddresses anda menyebabkan pengecualian! + Tetapan relay anda menyebabkan pengecualian! + Proses Tor sedia ada ditemui.. + Tidak dapat memulakan Tor: + diff --git a/src/main/res/values-my/strings.xml b/src/main/res/values-my/strings.xml new file mode 100644 index 0000000..b80fb51 --- /dev/null +++ b/src/main/res/values-my/strings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/res/values-nah/strings.xml b/src/main/res/values-nah/strings.xml new file mode 100644 index 0000000..eacd0e4 --- /dev/null +++ b/src/main/res/values-nah/strings.xml @@ -0,0 +1,44 @@ + + + Orbot + Orbot VPN + Tepoztlatlatquitl proxy ahmotlazotli Orbot, mitzmaca netlacaneconiliztli. Orbot tlamanitiliz Tor pampa nahuati ichtaca amatlacuiloli nochi tepoztlapohualoni in tlalticpac. Tor ca tlanemacuatextli ihuan metlatl tlapouhqui inin quipalehui huicpa pihpiyaliztli, ihuan quicuitlauh tlacaxoxouhcayotl ihuan tlacaconemiliztli ica hueyi altepetl. + Orbot pehualtica … + Tencuitica matlatl Tor Network + Orbot ahmo tequitica + Xictlapoz Orbot pampa xictencuiz Tor + Orbot tlaltzacuatica + Xictencuiz Tor + Ahmo melahuac mocentilis Orbot + + Ahmo huel opehua Tor + ¡Mocentiliz ReachableAddresses ye omochihua, yehce za iyopa! + ¡Mocentilis tepozteknolotilistli ye omochihua, yehce za iyopa! + Otinamiqueh ce ayiliztli cenyuhqui quenime Tor … + Ahmo opehua Tor + + ¡Axan tiyancuitlacauh ica Tor! + + Mochihua yancuic in mocentilis ipan Tor + + Yancuic in ixtli in yollotl + Ticchiatocqueh in huelititzalantli + Nican xictencuiz huelititzalantli + + KiB/s + MiB/s + + %sMocepayahuiproxy otepalehui oquitzicuini ahmo huelmati%s + + Ahmo huel xictlecahuiz xonacatl itocalhuan + ¡Ihuaniamatlacuiloli, ocachi tlamantli ahmo actica motepoznonotza! + Yuhcayotl proxy Snowflake neconiliztli + Yuhcayotl proxy Snowflake ahmo neconiliztli + Mocentiliz torrc quenime mitzpactia, papatica. + Mocahuaz in ihuelititzalantli + Ye octencui huelititzalantli + Ahmo onicma pehuaicihuitiliztli, ye opehua. + Onca ce neixpololiztli ihcuac otictlali tlanechicolli geoip + + + diff --git a/src/main/res/values-nb/strings.xml b/src/main/res/values-nb/strings.xml new file mode 100644 index 0000000..bf3a560 --- /dev/null +++ b/src/main/res/values-nb/strings.xml @@ -0,0 +1,25 @@ + + + Orbot + Orbot er en gratis proxy app som gjør det mulig for andre apps å bruke Internett mer sikkert. Orbot bruker Tor for å kryptere din Internettrafikk, og skjuler da din trafikk ved å sende trafikken gjennom en lang rekke datamaskiner over hele verden. Tor er et gratis dataprogram, og et åpent nettverk som hjelper deg å forsvare deg mot en form for nettverksovervåking som truer din personlige frihet og privatliv, konfidensiell bedriftsvirksomhet og relasjoner, og statlig sikkerhet kjent som trafikkanalyse. + Orbot starter... + Koblet til Tor-nettverket + Orbot er deaktivert + + + + + + + + + Kunne ikke starte Tor prosessen: + Dine \"adresser som kan nås\"-innstillinger forårsaket et unntak! + Dine relé-innstillinger forårsaket et unntak! + fant eksisterende Tor-prosess... + Klarte ikke å starte Tor: + Du har byttet til en ny Tor-identitet! + Oppdaterer innstillinger i Tor service + MiB/s + KiB/s + diff --git a/src/main/res/values-nl/strings.xml b/src/main/res/values-nl/strings.xml new file mode 100644 index 0000000..e79f1ed --- /dev/null +++ b/src/main/res/values-nl/strings.xml @@ -0,0 +1,24 @@ + + + Orbot + Orbot is een gratis en vrije proxy-app die het andere apps mogelijk maakt het internet veiliger te gebruiken. Orbot gebruikt Tor om je internetverkeer te coderen en het vervolgens te verhullen het door het door een serie computers over de hele wereld te routeren. Tor is vrije software en een open netwerk dat je helpt te verdedigen tegen een vorm van netwerktoezicht die persoonlijke vrijheid en privacy, vertrouwelijke bedrijfsactiviteiten en relaties en staatsveiligheid genaamd \'traffic analyse\' bedreigt. + Orbot is bezig met starten… + Verbonden met het Tor-netwerk + Orbot is uitgeschakeld + TorService wordt afgesloten + + + + + + + + + Kon Tor-proces niet starten: + Je ReachableAddresses-instellingen veroorzaakten een fout! + Je relay-instellingen veroorzaakten een fout! + bestaand Tor-proces gevonden… + Kan Tor niet starten: + Je bent naar een nieuwe Tor identiteit gewisseld! + instellingen in Tor-dienst worden bijgewerkt + diff --git a/src/main/res/values-pa/strings.xml b/src/main/res/values-pa/strings.xml new file mode 100644 index 0000000..0fe1e9e --- /dev/null +++ b/src/main/res/values-pa/strings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/res/values-pbb/strings.xml b/src/main/res/values-pbb/strings.xml new file mode 100644 index 0000000..0feff34 --- /dev/null +++ b/src/main/res/values-pbb/strings.xml @@ -0,0 +1,44 @@ + + + Orbot + Orbot VPN + ORBOTa’ teeçx PROXY’, çaamte spahtxçxa’ dewemee vxiswa’ja’; naayu’ vxite çaam wejxatxku ũukhmee kvxisu’ju’. ORBOTa’ TORa’sku kseelpi’ji’ mjĩitx jxpa’yahtx jxaawkaan. TORa’ vxite çaam wejxa’, txa’wẽyçxa dewewa’jme’, naa wejxa’ wala pu’çxhin kĩhsupa jiyuwẽte, txãa pa’ka kwe’sx mjĩitx nwe’wek peethegkameen; sa’ ũukhmee vxiswa’ja’. + Orbota’ takhna ũsa’… + Tor Networkte txitxanxi ũsa’ + Orbota’ fxĩçxhanxi ũsa’ + Orbota’s phade Torte txitxaya’pacxa + Orbota’ aphnxi ũsa’ + Torte mtxiitxaa + Orbot phewu’jnxi’ ewmee’ + + Tora’ takhya’ ewuume’ + !Idx ReachableAddresses phewu’jnxi´ teeçx yuwek nvxiht! + !Idx relay phewu’jnxi’ teeçx yuwek nvxiht! + Teeçx Tor vxitnxiya’sak uy… + Tor takhya’ ewuume’ + + ¡Yu’ptheg vxite use Tor yase! + + Tor Kpuuse’jnxi mjĩi’s phewu’j naa tudte + + U’se yase + Vxitxkwe phadewa’ ũythasna + U’kawa’ ũythasna + + KiB/s + MiB/s + + Idx snowflake proxi pu’çxhku maasume kaajxkhẽwu’jxa + + Spulxa yase kaateka’jya ewuumeen + ¡çaam pe’pe utaya’pa’! + Naa proxy Snowflake vxiswa’ ewa’ + Naa proxy Snowflake vxiswa’ ewme’ + Torrc Khpuusee’jnxi’s phewu’jna… + Khẽkh vxite u’kacxa thegwa’ + Txitxanxi ũsa’ çaam pe’pete u’kaya’ + Pki’taya jxkaahnxi’s wẽse’jme’. Takhya’k + Ewuume’ geoiptxi ki’pwẽte’ + + + diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml new file mode 100644 index 0000000..23b462f --- /dev/null +++ b/src/main/res/values-pl/strings.xml @@ -0,0 +1,24 @@ + + + Orbot + Orbot jest darmową aplikacją proxy która wspomaga inne aplikacje do używania internetu bezpiecznie. Orbot używa Tora do szyfrowania Twojego ruchu internetowego i następnie przepuszczania go przez wiele innych komputereów pororzucanych na całym świecie. Tor jest darmowym oprogramowaniem i otwartą siecią która pomaga Tobie w obronie przed monitoringiem sieci która zagrarza osobistej wolności i prywatności, poufnym biznesowym aktywnościom. + Orbot startuje... + Podłączony do sieci Tor + Orbot wyłączony + TorService wyłącza się + + + + + + + + + Nie można było zainicjować procesu Tora: + Twoje ustawienia dostępnych adresów spowodowały wyjątek! + Ustawienia Twojego przekaźnika spowodowały wyjątek! + znaleziono istniejący proces Tora... + Nie można wystartować aplikacji Tor: + Nowa tożsamość Tor\'a została zmieniona! + aktualizowanie ustawień w serwisie Tor + diff --git a/src/main/res/values-ps/strings.xml b/src/main/res/values-ps/strings.xml new file mode 100644 index 0000000..49c8dbc --- /dev/null +++ b/src/main/res/values-ps/strings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/res/values-pt-rBR/strings.xml b/src/main/res/values-pt-rBR/strings.xml new file mode 100644 index 0000000..5682116 --- /dev/null +++ b/src/main/res/values-pt-rBR/strings.xml @@ -0,0 +1,26 @@ + + + Orbot + Orbot é um aplicativo de proxy livre que capacita outros aplicativos a usar a internet com mais segurança. Orbot usa o Tor para criptografar seu tráfego na internet e então o esconde \"saltando\" entre uma série de computadores ao redor do mundo. Tor é um software livre e de rede aberta que ajuda você a se defender de certas formas de vigilância que ameaçam privacidade e liberdade pessoais, atividades e relações comerciais confidenciais e segurança estatal conhecida como análise de tráfego. + Orbot está iniciando... + Conectado à rede Tor + Orbot está desativado + TorService está desligando + + + + + + + + + Não foi possível iniciar o processo Tor: + Suas configurações de Endereços Acessíveis causou uma exceção! + Suas configurações de retransmissão causaram uma exceção! + procurando processos Tor existentes... + Habilitar iniciar o Tor: + Você trocou para uma nova identidade Tor! + atualizando configurações no serviço Tor + MiB/s + KiB/s + diff --git a/src/main/res/values-pt-rPT/strings.xml b/src/main/res/values-pt-rPT/strings.xml new file mode 100644 index 0000000..d34f1b2 --- /dev/null +++ b/src/main/res/values-pt-rPT/strings.xml @@ -0,0 +1,15 @@ + + + Orbot + + + + + + + + + + MiB/s + KiB/s + diff --git a/src/main/res/values-pt/strings.xml b/src/main/res/values-pt/strings.xml new file mode 100644 index 0000000..bb37a86 --- /dev/null +++ b/src/main/res/values-pt/strings.xml @@ -0,0 +1,22 @@ + + + Orbot + O Orbot está a iniciar... + Conetado à rede Tor + O Orbot está desativado + + + + + + + + + Não foi possível iniciar o processo Tor: + encontrado o processo Tor existente... + Não é possível iniciar o Tor: + Mudou para uma nova identidade do Tor! + a atualizar as definições no serviço Tor + MiB/s + KiB/s + diff --git a/src/main/res/values-ro-rRO/strings.xml b/src/main/res/values-ro-rRO/strings.xml new file mode 100644 index 0000000..b80fb51 --- /dev/null +++ b/src/main/res/values-ro-rRO/strings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/res/values-ro/strings.xml b/src/main/res/values-ro/strings.xml new file mode 100644 index 0000000..c3cfeee --- /dev/null +++ b/src/main/res/values-ro/strings.xml @@ -0,0 +1,22 @@ + + + Orbot + Orbot porneste... + Conectat la reteaua Tor + Orbot este dezactivat + + + + + + + + + Nu am putut porni procesul Tor: + Setarile tale pentru adrese accesibile au cauzat o exceptie! + Setarile tale de relay au cauzat o exceptie! + gasit proces Tor existent... + Nu am reusit sa pornesc Tor: + Ați comutat la o nouă identitate Tor! + se actualizează configurările în serviciul Tor + diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml new file mode 100644 index 0000000..f0e149a --- /dev/null +++ b/src/main/res/values-ru/strings.xml @@ -0,0 +1,24 @@ + + + Orbot + Orbot - это свободная программа для прокси-соединений, она позволяет другим приложениям более безопасно использовать интернет-соединение. Orbot использует Tor для шифрования интернет-трафика, который затем скрывается в ходе пересылки через несколько компьютеров в разных частях планеты. Tor является свободным программным приложением, а также открытой сетью, помогающей защититься от слежки в сетях, угрожающей личной свободе и частной жизни, конфиденциальным бизнес-деятельности и контактам, а также государственной программе безопасности, известной как анализ трафика. + Запуск Orbot... + Подключён к сети Tor + Orbot отключён + Служба Tor выключается + + + + + + + + + Невозможно запустить Tor: + Ваши настройки доступных адресов вызвали исключение! + Настройки вашего ретранслятора вызвали исключение! + найден существующий процесс Tor... + Невозможно запустить Tor: + Вы переключились на новый идентификатор Tor! + обновление настроек в сервисе Tor + diff --git a/src/main/res/values-si-rLK/strings.xml b/src/main/res/values-si-rLK/strings.xml new file mode 100644 index 0000000..6182291 --- /dev/null +++ b/src/main/res/values-si-rLK/strings.xml @@ -0,0 +1,20 @@ + + + Orbot + Orbot ආරම්භ කරමින්… + Tor ජාලයට සබැදියි + Orbot ක්‍රියාවිරහිත කර ඇත + + + + + + + + + Tor ක්‍රියාවලින් ආරම්භ කිරීමට නොහැකි විය: + ඔබේ සේන්දුවන ලිපිනයෙහි සිටුවම්වල ව්‍යතිරේකයක් හටගෙන ඇත! + ඔබේ ප්‍රතියෝජක සිටුවම්වල ව්‍යතිරේකයක් හටගෙන ඇත! + පවතින Tor ක්‍රියාවලි හමුවිය... + Tor ආරම්භ කිරීමට නොහැකිය: + diff --git a/src/main/res/values-sk-rSK/strings.xml b/src/main/res/values-sk-rSK/strings.xml new file mode 100644 index 0000000..0fe1e9e --- /dev/null +++ b/src/main/res/values-sk-rSK/strings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/res/values-sk/strings.xml b/src/main/res/values-sk/strings.xml new file mode 100644 index 0000000..996d73e --- /dev/null +++ b/src/main/res/values-sk/strings.xml @@ -0,0 +1,15 @@ + + + Orbot + Orbot štartuje… + Pripojený do Tor siete + Orbot je deaktivovaný + + + + + + + + + diff --git a/src/main/res/values-sl/strings.xml b/src/main/res/values-sl/strings.xml new file mode 100644 index 0000000..7f1d22c --- /dev/null +++ b/src/main/res/values-sl/strings.xml @@ -0,0 +1,15 @@ + + + Orbot + Orbot se zaganja... + Povezan v omrežje Tor + Orbot ni aktiviran + + + + + + + + + diff --git a/src/main/res/values-sn/strings.xml b/src/main/res/values-sn/strings.xml new file mode 100644 index 0000000..9b98a0c --- /dev/null +++ b/src/main/res/values-sn/strings.xml @@ -0,0 +1,12 @@ + + + Orbot + + + + + + + + + diff --git a/src/main/res/values-sq/strings.xml b/src/main/res/values-sq/strings.xml new file mode 100644 index 0000000..b80fb51 --- /dev/null +++ b/src/main/res/values-sq/strings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/res/values-sr/strings.xml b/src/main/res/values-sr/strings.xml new file mode 100644 index 0000000..e6b7756 --- /dev/null +++ b/src/main/res/values-sr/strings.xml @@ -0,0 +1,21 @@ + + + Орбот + Орбот је бесплатна прокси апликација која даје моћ другим апликацијама да безбедније користе интернет. Орбот користи Тор за шифровање вашег интернет саобраћаја и онда га скрива слањем кроз низ рачунара широм света. Тор је слободан софтвер и отворена мрежа која помаже да се одбраните од разних облика надзора мрежа који угрожавају личну слободу и приватност, поверљиве пословне активности и личне односе и државне безбедности познате као анализа саобраћаја. + Орбот се покреће... + Повезан са Тор мрежом + Орбот је деактивиран + + + + + + + + + Није могуће покренути Тор процес: + Ваше ReachableAddresses поставке су изазвале изузетак! + Ваше поставке релеја су изазвале изузетак! + нађен покренути Тор процес... + Тор није у могућности да се покрене: + diff --git a/src/main/res/values-sv/strings.xml b/src/main/res/values-sv/strings.xml new file mode 100644 index 0000000..c49c009 --- /dev/null +++ b/src/main/res/values-sv/strings.xml @@ -0,0 +1,24 @@ + + + Orbot + Orbot är en gratis proxyapp som möjliggör andra appar att använda internet mer säkert. Orbot använder Tor för att kryptera din internettrafik och döljer den genom att den studsar genom ett antal datorer världen över. Tor är fri programvara och ett öppet nätverk som hjälper dig att skydda dig mot en form av nätverksövervakning som hotar personlig integritet och frihet, hemliga affärsaktiviteter och relationer, och skyddar mot statlig övervakning även kallad trafikanalys. + Orbot startar... + Uppkopplad till Tor nätverket + Orbot är inaktiverad + TorService avslutas + + + + + + + + + Kunde inte starta Tor-process: + Din NåbaraAdresser inställning orsakade ett undantag! + Din relä inställning orsakade ett undantag! + hittade existerande Tor process... + Kan inte starta Tor: + Du har bytt till en ny Tor identitet! + uppdaterar inställningar i Tor-tjänsten + diff --git a/src/main/res/values-ta/strings.xml b/src/main/res/values-ta/strings.xml new file mode 100644 index 0000000..0351d9d --- /dev/null +++ b/src/main/res/values-ta/strings.xml @@ -0,0 +1,16 @@ + + + ஆர்பாட் + ஆர்பாட், இன்னும பாதுகாப்பான முறையில் இணைய பயன்படுத்த மற்ற பயன்பாடுகள் பலப்படுத்துகிறார் என்று ஒரு இலவச ப்ராக்ஸி பயன்பாடு ஆகும். ஆர்பாட் உங்கள் இணைய போக்குவரத்து குறியாக்க தோர் பயன்படுத்துகிறது மற்றும் அதன் பின்னர் உலகம் முழுவதும் கணினிகள் ஒரு தொடர் மூலம் எதிர்க்கிறது அதை மறுத்தவர். தோர் இலவச மென்பொருள் மற்றும் நீங்கள் போக்குவரத்து பகுப்பாய்வு என்ற தனிப்பட்ட சுதந்திரம் மற்றும் தனியுரிமை, ரகசிய வணிக நடவடிக்கைகள் மற்றும் உறவுகள், மற்றும் மாநில பாதுகாப்பை அச்சுறுத்தும் நெட்வொர்க் கண்காணிப்பு வடிவ எதிராக பாதுகாக்க உதவுகிறது என்று ஒரு திறந்த நெட்வொர்க் ஆகும். + ஆர்பாட் துவங்குகிறது... + தோர் நெட்வொர்க் இணைக்கப்பட்ட + ஆர்பாட் நிறுத்தப்பட்டது + + + + + + + + + diff --git a/src/main/res/values-th/strings.xml b/src/main/res/values-th/strings.xml new file mode 100644 index 0000000..ad20cb8 --- /dev/null +++ b/src/main/res/values-th/strings.xml @@ -0,0 +1,17 @@ + + + Orbot + เชื่อมต่อกับเครือข่าย Tor + + + + + + + + + ไม่สามารถเริ่มโพรเซสของ Tor + การตั้งค่า ReachableAddresses ของคุณทำให้เกิดข้อผิดพลาด + การตั้งค่ารีเลย์ของคุณทำให้เกิดข้อผิดพลาด + ไม่สามารถเริ่ม Tor + diff --git a/src/main/res/values-tl/strings.xml b/src/main/res/values-tl/strings.xml new file mode 100644 index 0000000..9ba1625 --- /dev/null +++ b/src/main/res/values-tl/strings.xml @@ -0,0 +1,21 @@ + + + Orbot + Nagsisimula na ang Orbot… + Konektado sa Tor network + Naka-deactivate ang Orbot + + + + + + + + + Hindi ma umpisahan ang proseso ng Tor: + Ang iyong ReachableAddresses settings ay nag sanhi ng exception! + Ang iyong relay settings ay nag sanhi ng exception! + nakakita ng umiiral na Tor process... + Hindi masimulan ang Tor: + Ikaw ay nagpalit ng bagong pagkakakilanlan sa Tor! + diff --git a/src/main/res/values-tr/strings.xml b/src/main/res/values-tr/strings.xml new file mode 100644 index 0000000..ec6e6ec --- /dev/null +++ b/src/main/res/values-tr/strings.xml @@ -0,0 +1,26 @@ + + + Orbot + Orbot başka uygulamaların interneti daha güvenli olarak kullanmasını sağlayan ücretsiz bir proxy uygulamasıdır. Orbot Tor\'u kullanarak internet trafiğinizi şifreler ve dünya üzerindeki pek çok farklı bilgisayardan geçirerek gizler. Tor sizin kişisel özgürlüğünüzü ve mahremiyetinizi, gizli ticari aktivitelerinizi ve bağlantılarınızı koruma altına alan bir yazılım ve açık ağdır. + Orbot başlatılıyor... + Tor ağına bağlandı + Orbot devre dışı bırakıldı + Tor hizmeti kapatılıyor + + + + + + + + + Tor işlemi başlatılamadı + ErişilebilirAdresleriniz ayarlarınız bir istisnaya yol açtı! + Tor aktarıcı ayarlarınız bir istisnaya yol açtı! + varolan bir Tor işlemi bulundu... + Tor başlatılamadı: + Yeni bir Tor kimliğine geçiş yaptınız! + Tor hizmet ayarları güncellemesi + MiB/s + KiB/s + diff --git a/src/main/res/values-uk/strings.xml b/src/main/res/values-uk/strings.xml new file mode 100644 index 0000000..d752922 --- /dev/null +++ b/src/main/res/values-uk/strings.xml @@ -0,0 +1,25 @@ + + + Orbot + Orbot — це вільна програма для проксі-з\'єднань, яка дозволяє іншим додаткам безпечніше використовувати інтернет-з\'єднання. Orbot використовує Tor для шифрування інтернет-трафіку, який далі приховується під час пересилання через кілька комп\'ютерів у різних частинах планети. Tor є вільним програмним забезпеченням, а також відкритою мережею, що допомагає захиститися від мережевого стеження, яке загрожує особистій свободі та приватному життю, конфіденційній бізнес-діяльності і контактам, а також державної програми безпеки, що відома як аналіз трафіку. + Запуск Orbot... + Під\'єднаний до мережі Tor + «Orbot» від\'єднаний + + + + + + + + + Неможливо запустити Tor: + Ваші налаштування Доступних Адрес викликали виключення! + Налаштування вашого ретранслятора викликали виключення! + знайдено існуючий процес Tor... + Неможливо запустити Tor: + Ви перемкнулись на новий ідентифікатор Tor! + оновлення налаштувань у сервісі Tor + МіБ/с + КіБ/с + diff --git a/src/main/res/values-ur/strings.xml b/src/main/res/values-ur/strings.xml new file mode 100644 index 0000000..05e2b96 --- /dev/null +++ b/src/main/res/values-ur/strings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/res/values-uz/strings.xml b/src/main/res/values-uz/strings.xml new file mode 100644 index 0000000..639c0fd --- /dev/null +++ b/src/main/res/values-uz/strings.xml @@ -0,0 +1,15 @@ + + + Orbot + Orbot ishga tushirilmoqda… + Tor tarmog\'iga ulangan + + + + + + + + + Mavjud bo\'lgan Tor jarayoni topildi... + diff --git a/src/main/res/values-vi/strings.xml b/src/main/res/values-vi/strings.xml new file mode 100644 index 0000000..f6ce42f --- /dev/null +++ b/src/main/res/values-vi/strings.xml @@ -0,0 +1,26 @@ + + + Orbot + Orbot là một ứng dụng proxy miễn phí, được thiết kế để làm cho các ứng dụng khác kết nối với Internet một cách an toàn. Orbot sử dụng Tor để mã hóa các kết nối Internet rồi ẩn danh nó thông qua một loạt các nút trong mạng Tor. Tor là phần mềm miễn phí và là một mạng lưới mở giúp bạn chống lại sự giám sát mạng, vốn đe dọa riêng tư trực tuyến, hay các hoạt động bí mật... + Ortbot đang khởi động... + Đã kết nối với mạng Tor + Orbot đã được vô hiệu hóa + Đang tắt dịch vụ Tor + + + + + + + + + Không thể khởi động tiến trình Tor: + Thiết lập ReachableAddresses đã gây ra một vấn đề! + Thiết lập relay của bạn đã gây ra một vấn đề! + tìm ra tiến trình hiện hành của Tor... + Không thể khởi động Tor được: + Bạn đã chuyển sang một mạch Tor mới! + đang cập nhật cài đặt dịch vụ Tor + MiB/s + KiB/s + diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml new file mode 100644 index 0000000..9ae4b8b --- /dev/null +++ b/src/main/res/values-zh-rCN/strings.xml @@ -0,0 +1,26 @@ + + + Orbot + Orbot 是一款免费的代理应用,能够让其他应用更安全地使用互联网。通过在位于世界各地的一系列计算机之间进行跳转,Orbot 可利用 Tor 对网络通信进行加密并隐藏。Tor 是一款免费的软件,并且是一个开放的网络。它可以保护用户免受流量分析的危害,这种网络监控可对个人自由与隐私、商业机密活动和关系以及国家安全造成威胁。 + Orbot 正在启动... + 已连接到 Tor 网络 + Orbot 已停用 + Tor服务 正在关闭 + + + + + + + + + 无法启动 Tor 进程: + 可访问地址设置导致异常! + 中继设置导致异常! + 发现当前已存在 Tor 进程... + 无法启动 Tor: + 已切换为新的 Tor 标识! + 正在更新 Tor 服务中的设置 + MiB/秒 + KiB/秒 + diff --git a/src/main/res/values-zh-rTW/strings.xml b/src/main/res/values-zh-rTW/strings.xml new file mode 100644 index 0000000..41fc882 --- /dev/null +++ b/src/main/res/values-zh-rTW/strings.xml @@ -0,0 +1,18 @@ + + + Orbot + Orbot是一款免費的網絡代理應用程式,用來保護其他應用程式的上網安全。 +Orbot使用Tor在全球一系列的電腦間跳躍,以便隱藏網路流量並加密。Tor是個免費軟體也是個開放網路,能幫您抵禦流量分析。它是某一種網路監控,牽涉到個人的自由與隱私、商業部分的機密關係和活動、甚至國家安全。 + Orbot 正在啟動中... + 已連線至 Tor 網路 + Orbot 已停用 + Tor服務 正在關閉 + + + + + + + + + diff --git a/src/main/res/values/dimens.xml b/src/main/res/values/dimens.xml new file mode 100644 index 0000000..f417206 --- /dev/null +++ b/src/main/res/values/dimens.xml @@ -0,0 +1,27 @@ + + + + + 64dp + + 64dp + 12dp + + diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml new file mode 100644 index 0000000..7d23258 --- /dev/null +++ b/src/main/res/values/strings.xml @@ -0,0 +1,43 @@ + + + Orbot + Orbot VPN + Orbot is a free proxy app that empowers other apps to use the internet more securely. Orbot uses Tor to encrypt your Internet traffic and then hides it by bouncing through a series of computers around the world. Tor is free software and an open network that helps you defend against a form of network surveillance that threatens personal freedom and privacy, confidential business activities and relationships, and state security known as traffic analysis. + Orbot is Starting… + Connected to the Tor Network + Orbot is Deactivated + Open Orbot to Connect to Tor + Orbot is Shutting Down + Connect to Tor + Orbot Configuration Invalid + + Couldn\'t start Tor process: + Your ReachableAddresses settings caused an exception! + Your relay settings caused an exception! + found existing Tor process… + Unable to start Tor: + + You\'ve switched to a new Tor identity! + + updating settings in Tor service + + New Identity + Waiting for control port… + Connecting to control port: + + KiB/s + MiB/s + + %s Kindness Mode helped someone circumvent censorship %s + + unable to upload onion names + Low memory warning! + Snowflake proxy mode enabled + Snowflake proxy mode disabled + Updating torrc custom configuration… + Added control port event handler + Connected to tor control port + Ignoring start request, already started. + + + -- GitLab