Skip to content

Commit

Permalink
Ability to block QUIC always or only on decryption
Browse files Browse the repository at this point in the history
Commit ddec1a8 limited the ability to block QUIC to only
connections matching the decryption rules. Some users may still want
to always block QUIC for different reasons, so this commit makes it
possible to choose the block policy to apply.

See #369
  • Loading branch information
emanuele-f committed Jan 28, 2024
1 parent 2628764 commit 4a7a55d
Show file tree
Hide file tree
Showing 10 changed files with 70 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1309,7 +1309,7 @@ public int getMitmAddonUid() {

public int getVpnMTU() { return VPN_MTU; }

public int blockQuick() { return(mSettings.block_quic ? 1 : 0); }
public int getBlockQuickMode() { return mSettings.block_quic_mode.ordinal(); }

// returns 1 if dumpPcapData should be called
public int pcapDumpEnabled() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,13 @@ public void onBackPressed() {

public static class SettingsFragment extends PreferenceFragmentCompat {
private SwitchPreference mTlsDecryption;
private SwitchPreference mBlockQuic;
private SwitchPreference mFullPayloadEnabled;
private SwitchPreference mRootCaptureEnabled;
private SwitchPreference mAutoBlockPrivateDNS;
private EditTextPreference mMitmproxyOpts;
private DropDownPreference mIpMode;
private DropDownPreference mCapInterface;
private DropDownPreference mBlockQuic;
private Preference mVpnExceptions;
private Preference mSocks5Settings;
private Preference mDnsSettings;
Expand Down Expand Up @@ -167,7 +167,8 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setupSecurityPrefs();
setupOtherPrefs();

socks5ProxyAndQuicHideShow(mTlsDecryption.isChecked(), rootCaptureEnabled());
socks5ProxyHideShow(mTlsDecryption.isChecked(), rootCaptureEnabled());
mBlockQuic.setVisible(!rootCaptureEnabled());
rootCaptureHideShow(rootCaptureEnabled());

Intent intent = requireActivity().getIntent();
Expand Down Expand Up @@ -309,7 +310,7 @@ private void setupTrafficInspectionPrefs() {

mMitmWizard.setVisible((boolean) newValue);
mMitmproxyOpts.setVisible((boolean) newValue);
socks5ProxyAndQuicHideShow((boolean) newValue, rootCaptureEnabled());
socks5ProxyHideShow((boolean) newValue, rootCaptureEnabled());
return true;
});

Expand Down Expand Up @@ -343,9 +344,8 @@ private void setupTrafficInspectionPrefs() {
mSocks5Settings = requirePreference("socks5_settings");
}

private void socks5ProxyAndQuicHideShow(boolean tlsDecryption, boolean rootEnabled) {
private void socks5ProxyHideShow(boolean tlsDecryption, boolean rootEnabled) {
mSocks5Settings.setVisible(!tlsDecryption && !rootEnabled);
mBlockQuic.setVisible(tlsDecryption && !rootEnabled);
}

private void setupOtherPrefs() {
Expand Down Expand Up @@ -406,7 +406,7 @@ private void rootCaptureHideShow(boolean enabled) {
} else {
mAutoBlockPrivateDNS.setVisible(true);
mBlockQuic.setVisible(true);
socks5ProxyAndQuicHideShow(mTlsDecryption.isChecked(), false);
socks5ProxyHideShow(mTlsDecryption.isChecked(), false);
}

mIpMode.setVisible(!enabled);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class CaptureSettings implements Serializable {
public boolean root_capture;
public boolean pcapdroid_trailer;
public boolean full_payload;
public boolean block_quic;
public Prefs.BlockQuicMode block_quic_mode;
public boolean auto_block_private_dns;
public boolean pcapng_format;
public String capture_interface;
Expand Down Expand Up @@ -54,7 +54,7 @@ public CaptureSettings(Context ctx, SharedPreferences prefs) {
capture_interface = Prefs.getCaptureInterface(prefs);
tls_decryption = Prefs.getTlsDecryptionEnabled(prefs);
full_payload = Prefs.getFullPayloadMode(prefs);
block_quic = Prefs.blockQuic(prefs);
block_quic_mode = Prefs.getBlockQuicMode(prefs);
auto_block_private_dns = Prefs.isPrivateDnsBlockingEnabled(prefs);
mitmproxy_opts = Prefs.getMitmproxyOpts(prefs);
pcapng_format = Prefs.isPcapngEnabled(ctx, prefs);
Expand Down Expand Up @@ -82,7 +82,7 @@ public CaptureSettings(Context ctx, Intent intent) {
max_dump_size = getInt(intent, Prefs.PREF_MAX_DUMP_SIZE, 0);
tls_decryption = getBool(intent, Prefs.PREF_TLS_DECRYPTION_KEY, false);
full_payload = false;
block_quic = getBool(intent, Prefs.PREF_BLOCK_QUIC, false);
block_quic_mode = Prefs.getBlockQuicMode(getString(intent, Prefs.PREF_BLOCK_QUIC, Prefs.BLOCK_QUIC_MODE_DEFAULT));
auto_block_private_dns = getBool(intent, Prefs.PREF_AUTO_BLOCK_PRIVATE_DNS, true);
mitmproxy_opts = getString(intent, Prefs.PREF_MITMPROXY_OPTS, "");
pcapng_format = getBool(intent, Prefs.PREF_PCAPNG_ENABLED, false) && Billing.newInstance(ctx).isPurchased(Billing.PCAPNG_SKU);
Expand Down
25 changes: 22 additions & 3 deletions app/src/main/java/com/emanuelef/remote_capture/model/Prefs.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ public class Prefs {
public static final String IP_MODE_BOTH = "both";
public static final String IP_MODE_DEFAULT = IP_MODE_IPV4_ONLY;

public static final String BLOCK_QUIC_MODE_NEVER = "never";
public static final String BLOCK_QUIC_MODE_ALWAYS = "always";
public static final String BLOCK_QUIC_MODE_TO_DECRYPT = "to_decrypt";
public static final String BLOCK_QUIC_MODE_DEFAULT = BLOCK_QUIC_MODE_NEVER;

public static final String PAYLOAD_MODE_NONE = "none";
public static final String PAYLOAD_MODE_MINIMAL = "minimal";
public static final String PAYLOAD_MODE_FULL = "full";
Expand Down Expand Up @@ -83,7 +88,7 @@ public class Prefs {
public static final String PREF_TLS_DECRYPTION_SETUP_DONE = "tls_decryption_setup_ok";
public static final String PREF_CA_INSTALLATION_SKIPPED = "ca_install_skipped";
public static final String PREF_FULL_PAYLOAD = "full_payload";
public static final String PREF_BLOCK_QUIC = "block_quic";
public static final String PREF_BLOCK_QUIC = "block_quic_mode";
public static final String PREF_AUTO_BLOCK_PRIVATE_DNS = "auto_block_private_dns";
public static final String PREF_APP_VERSION = "appver";
public static final String PREF_LOCKDOWN_VPN_NOTICE_SHOWN = "vpn_lockdown_notice";
Expand Down Expand Up @@ -113,6 +118,12 @@ public enum IpMode {
BOTH,
}

public enum BlockQuicMode {
NEVER,
ALWAYS,
TO_DECRYPT
}

public enum PayloadMode {
NONE,
MINIMAL,
Expand All @@ -136,6 +147,14 @@ public static IpMode getIPMode(String pref) {
}
}

public static BlockQuicMode getBlockQuicMode(String pref) {
switch (pref) {
case BLOCK_QUIC_MODE_ALWAYS: return BlockQuicMode.ALWAYS;
case BLOCK_QUIC_MODE_TO_DECRYPT: return BlockQuicMode.TO_DECRYPT;
default: return BlockQuicMode.NEVER;
}
}

public static PayloadMode getPayloadMode(String pref) {
switch (pref) {
case PAYLOAD_MODE_MINIMAL: return PayloadMode.MINIMAL;
Expand Down Expand Up @@ -182,6 +201,7 @@ public static void setPortMappingEnabled(SharedPreferences p, boolean enabled) {
public static String getSocks5Password(SharedPreferences p) { return(p.getString(PREF_SOCKS5_PASSWORD_KEY, "")); }
public static String getAppFilter(SharedPreferences p) { return(p.getString(PREF_APP_FILTER, "")); }
public static IpMode getIPMode(SharedPreferences p) { return(getIPMode(p.getString(PREF_IP_MODE, IP_MODE_DEFAULT))); }
public static BlockQuicMode getBlockQuicMode(SharedPreferences p) { return(getBlockQuicMode(p.getString(PREF_BLOCK_QUIC, BLOCK_QUIC_MODE_DEFAULT))); }
public static boolean useEnglishLanguage(SharedPreferences p){ return("english".equals(p.getString(PREF_APP_LANGUAGE, "system")));}
public static boolean isRootCaptureEnabled(SharedPreferences p) { return(Utils.isRootAvailable() && p.getBoolean(PREF_ROOT_CAPTURE, false)); }
public static boolean isPcapdroidTrailerEnabled(SharedPreferences p) { return(p.getBoolean(PREF_PCAPDROID_TRAILER, false)); }
Expand All @@ -202,7 +222,6 @@ public static boolean isPcapngEnabled(Context ctx, SharedPreferences p) {
public static boolean startAtBoot(SharedPreferences p) { return(p.getBoolean(PREF_START_AT_BOOT, false)); }
public static boolean isTLSDecryptionSetupDone(SharedPreferences p) { return(p.getBoolean(PREF_TLS_DECRYPTION_SETUP_DONE, false)); }
public static boolean getFullPayloadMode(SharedPreferences p) { return(p.getBoolean(PREF_FULL_PAYLOAD, false)); }
public static boolean blockQuic(SharedPreferences p) { return(p.getBoolean(PREF_BLOCK_QUIC, false)); }
public static boolean isPrivateDnsBlockingEnabled(SharedPreferences p) { return(p.getBoolean(PREF_AUTO_BLOCK_PRIVATE_DNS, true)); }
public static boolean lockdownVpnNoticeShown(SharedPreferences p) { return(p.getBoolean(PREF_LOCKDOWN_VPN_NOTICE_SHOWN, false)); }
public static boolean trailerNoticeShown(SharedPreferences p) { return(p.getBoolean(PREF_PCAPDROID_TRAILER_NOTICE_SHOWN, false)); }
Expand All @@ -224,7 +243,7 @@ public static String asString(Context ctx) {
"\nTLSDecryption: " + getTlsDecryptionEnabled(p) +
"\nTLSSetupOk: " + isTLSDecryptionSetupDone(p) +
"\nCAInstallSkipped: " + MitmAddon.isCAInstallationSkipped(ctx) +
"\nBlockQuic: " + blockQuic(p) +
"\nBlockQuic: " + getBlockQuicMode(p) +
"\nRootCapture: " + isRootCaptureEnabled(p) +
"\nSocks5: " + getSocks5Enabled(p) +
"\nBlockPrivateDns: " + isPrivateDnsBlockingEnabled(p) +
Expand Down
14 changes: 9 additions & 5 deletions app/src/main/jni/core/capture_vpn.c
Original file line number Diff line number Diff line change
Expand Up @@ -385,10 +385,14 @@ static bool should_proxify(pcapdroid_t *pd, const zdtun_5tuple_t *tuple, pd_conn
/* ******************************************************* */

void vpn_process_ndpi(pcapdroid_t *pd, const zdtun_5tuple_t *tuple, pd_conn_t *data) {
if(pd->vpn.block_quic && (data->l7proto == NDPI_PROTOCOL_QUIC) &&
pd->tls_decryption.enabled && matches_decryption_whitelist(pd, tuple, data)) {
data->blacklisted_internal = true;
data->to_block = true;
if(data->l7proto == NDPI_PROTOCOL_QUIC) {
block_quic_mode_t block_mode = pd->vpn.block_quic_mode;

if ((block_mode == BLOCK_QUIC_MODE_ALWAYS) ||
((block_mode == BLOCK_QUIC_MODE_TO_DECRYPT) && matches_decryption_whitelist(pd, tuple, data))) {
data->blacklisted_internal = true;
data->to_block = true;
}
}

if(block_private_dns && !data->to_block &&
Expand Down Expand Up @@ -449,7 +453,7 @@ int run_vpn(pcapdroid_t *pd) {
#if ANDROID
pd->vpn.resolver = init_uid_resolver(pd->sdk_ver, pd->env, pd->capture_service);
pd->vpn.known_dns_servers = blacklist_init();
pd->vpn.block_quic = getIntPref(pd->env, pd->capture_service, "blockQuick");
pd->vpn.block_quic_mode = getIntPref(pd->env, pd->capture_service, "getBlockQuickMode");

pd->vpn.ipv4.enabled = (bool) getIntPref(pd->env, pd->capture_service, "getIPv4Enabled");
pd->vpn.ipv4.dns_server = getIPv4Pref(pd->env, pd->capture_service, "getDnsServer");
Expand Down
9 changes: 8 additions & 1 deletion app/src/main/jni/core/pcapdroid.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ typedef enum {
PAYLOAD_MODE_FULL
} payload_mode_t;

// NOTE: sync with Prefs.BlockQuicMode
typedef enum {
BLOCK_QUIC_MODE_NEVER = 0,
BLOCK_QUIC_MODE_ALWAYS,
BLOCK_QUIC_MODE_TO_DECRYPT
} block_quic_mode_t;

typedef struct {
jint incr_id; // an incremental number which identifies a specific connection

Expand Down Expand Up @@ -205,7 +212,7 @@ typedef struct pcapdroid {
union {
struct {
int tunfd;
bool block_quic;
block_quic_mode_t block_quic_mode;
blacklist_t *known_dns_servers;
uid_resolver_t *resolver;

Expand Down
11 changes: 11 additions & 0 deletions app/src/main/res/values/arrays.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,15 @@
<item>@string/theme_light</item>
<item>@string/theme_dark</item>
</string-array>

<string-array name="block_quic">
<item>never</item>
<item>always</item>
<item>to_decrypt</item>
</string-array>
<string-array name="block_quic_labels">
<item>@string/never</item>
<item>@string/always</item>
<item>@string/for_connections_to_decrypt</item>
</string-array>
</resources>
3 changes: 3 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -497,4 +497,7 @@
<string name="redirected">redirected</string>
<string name="connection_redirected_port_map">This connection has been redirected due to a port mapping rule</string>
<string name="dnscrypt_how_to">How to use DoH / DNSCrypt with PCAPdroid</string>
<string name="never">Never</string>
<string name="always">Always</string>
<string name="for_connections_to_decrypt">Only for connections to decrypt</string>
</resources>
10 changes: 6 additions & 4 deletions app/src/main/res/xml/root_preferences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,14 @@
app:summary="@string/pcapng_format_summary"
app:defaultValue="true" />

<SwitchPreference
app:key="block_quic"
<DropDownPreference
app:key="block_quic_mode"
app:title="@string/block_quick"
android:entries="@array/block_quic_labels"
android:entryValues="@array/block_quic"
app:iconSpaceReserved="false"
app:summary="@string/block_quick_summary"
app:defaultValue="false" />
app:defaultValue="never"
app:useSimpleSummaryProvider="true" />

<SwitchPreference
app:key="full_payload"
Expand Down
2 changes: 1 addition & 1 deletion docs/app_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,14 @@ As shown above, the capture settings can be specified by using intent extras. Th
| max_pkts_per_flow | int | 43 | | only dump the first max_pkts_per_flow packets per flow |
| max_dump_size | int | 43 | | max size in bytes for the PCAP dump |
| tls_decryption | bool | 49 | vpn | true to enable the built-in TLS decryption |
| block_quic | bool | 51 | vpn | true to block QUIC traffic (73+: matching the decryption whitelist)|
| auto_block_private_dns | bool | 51 | vpn | true to detect and possibly block private DNS to inspect traffic |
| ip_mode | string | 56 | vpn | which IP addresses to use for the VPN: ipv4 \| ipv6 \| both |
| mitmproxy_opts | string | 62 | | additional options to provide to mitmproxy in decryption mode |
| pcap_name | string | 62 | | write the PCAP to Download/PCAPdroid/*pcap_name* in pcap_file mode |
| pcapng_format | bool | 62 | | true to use the PCAPNG dump format (overrides pcapdroid_trailer)* |
| socks5_username | string | 64 | vpn | username for the optional SOCKS5 proxy authentication |
| socks5_password | string | 64 | vpn | password for the optional SOCKS5 proxy authentication |
| block_quic_mode | string | 73 | vpn | never | always | to_decrypt (matching the decryption whitelist) |

\*: paid feature

Expand Down

0 comments on commit 4a7a55d

Please sign in to comment.