mirror of
https://github.com/emanuele-f/PCAPdroid.git
synced 2026-06-16 21:10:57 +08:00
Add ability to unblock connections from context menu
This commit is contained in:
parent
3be18fb2e5
commit
af2df846fd
@ -166,4 +166,8 @@ public class Billing {
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
public boolean canUseFirewall() {
|
||||
return isRedeemed(Billing.FIREWALL_SKU) && !CaptureService.isCapturingAsRoot();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1139,7 +1139,7 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
}
|
||||
|
||||
public void reloadBlocklist() {
|
||||
if(!mBilling.isRedeemed(Billing.FIREWALL_SKU) || mSettings.root_capture)
|
||||
if(!mBilling.canUseFirewall())
|
||||
return;
|
||||
|
||||
Log.d(TAG, "reloading firewall blocklist");
|
||||
|
||||
@ -116,7 +116,7 @@ public class EditFilterActivity extends BaseActivity {
|
||||
if(!Prefs.isMalwareDetectionEnabled(this, prefs))
|
||||
mOnlyBlacklisted.setVisibility(View.GONE);
|
||||
|
||||
if(!billing.isRedeemed(Billing.FIREWALL_SKU) || Prefs.isRootCaptureEnabled(prefs))
|
||||
if(!billing.canUseFirewall())
|
||||
mOnlyBlocked.setVisibility(View.GONE);
|
||||
|
||||
ConnectionsRegister reg = CaptureService.getConnsRegister();
|
||||
|
||||
@ -211,7 +211,7 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig
|
||||
Menu navMenu = mNavView.getMenu();
|
||||
navMenu.findItem(R.id.open_root_log).setVisible(Prefs.isRootCaptureEnabled(mPrefs));
|
||||
navMenu.findItem(R.id.malware_detection).setVisible(Prefs.isMalwareDetectionEnabled(this, mPrefs));
|
||||
navMenu.findItem(R.id.firewall).setVisible(mIab.isRedeemed(Billing.FIREWALL_SKU) && !Prefs.isRootCaptureEnabled(mPrefs));
|
||||
navMenu.findItem(R.id.firewall).setVisible(mIab.canUseFirewall());
|
||||
}
|
||||
|
||||
private void setupNavigationDrawer() {
|
||||
|
||||
@ -20,7 +20,6 @@
|
||||
package com.emanuelef.remote_capture.adapters;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@ -30,10 +29,9 @@ import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.emanuelef.remote_capture.CaptureService;
|
||||
import com.emanuelef.remote_capture.Billing;
|
||||
import com.emanuelef.remote_capture.PCAPdroid;
|
||||
import com.emanuelef.remote_capture.R;
|
||||
import com.emanuelef.remote_capture.Utils;
|
||||
@ -52,10 +50,10 @@ public class AppsStatsAdapter extends RecyclerView.Adapter<AppsStatsAdapter.View
|
||||
private final LayoutInflater mLayoutInflater;
|
||||
private final Drawable mUnknownIcon;
|
||||
private final MatchList mBlocklist;
|
||||
private final boolean mFirewallAvailable;
|
||||
private View.OnClickListener mListener;
|
||||
private List<AppStats> mStats;
|
||||
private final AppsResolver mApps;
|
||||
private final SharedPreferences mPrefs;
|
||||
private int mClickedPosition;
|
||||
|
||||
public class ViewHolder extends RecyclerView.ViewHolder {
|
||||
@ -102,7 +100,7 @@ public class AppsStatsAdapter extends RecyclerView.Adapter<AppsStatsAdapter.View
|
||||
mBlocklist = PCAPdroid.getInstance().getBlocklist();
|
||||
mListener = null;
|
||||
mStats = new ArrayList<>();
|
||||
mPrefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
mFirewallAvailable = Billing.newInstance(context).canUseFirewall();
|
||||
setHasStableIds(true);
|
||||
}
|
||||
|
||||
@ -121,7 +119,7 @@ public class AppsStatsAdapter extends RecyclerView.Adapter<AppsStatsAdapter.View
|
||||
|
||||
ViewHolder holder = new ViewHolder(view);
|
||||
|
||||
if(CaptureService.isFirewallEnabled(mContext, mPrefs)) {
|
||||
if(mFirewallAvailable) {
|
||||
// Enable the ability to show the context menu
|
||||
view.setLongClickable(true);
|
||||
|
||||
@ -130,8 +128,7 @@ public class AppsStatsAdapter extends RecyclerView.Adapter<AppsStatsAdapter.View
|
||||
mClickedPosition = holder.getAbsoluteAdapterPosition();
|
||||
return false;
|
||||
});
|
||||
} else
|
||||
view.setLongClickable(false);
|
||||
}
|
||||
|
||||
return holder;
|
||||
}
|
||||
|
||||
@ -151,9 +151,9 @@ public class AppsFragment extends Fragment implements ConnectionsListener {
|
||||
if(stats == null)
|
||||
return;
|
||||
|
||||
// TODO unblock
|
||||
menu.findItem(R.id.block_app).setVisible(true);
|
||||
menu.findItem(R.id.unblock_app).setVisible(false);
|
||||
boolean isBlocked = PCAPdroid.getInstance().getBlocklist().matchesApp(stats.getUid());
|
||||
menu.findItem(R.id.block_app).setVisible(!isBlocked);
|
||||
menu.findItem(R.id.unblock_app).setVisible(isBlocked);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -165,6 +165,8 @@ public class AppsFragment extends Fragment implements ConnectionsListener {
|
||||
|
||||
if(id == R.id.block_app)
|
||||
blocklist.addApp(app.getUid());
|
||||
else if(id == R.id.unblock_app)
|
||||
blocklist.removeApp(app.getUid());
|
||||
else
|
||||
return super.onContextItemSelected(item);
|
||||
|
||||
|
||||
@ -305,14 +305,19 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener
|
||||
|
||||
AppDescriptor app = mApps.get(conn.uid, 0);
|
||||
Context ctx = requireContext();
|
||||
Billing billing = Billing.newInstance(ctx);
|
||||
MenuItem item;
|
||||
|
||||
menu.findItem(R.id.block_menu).setVisible(billing.isRedeemed(Billing.FIREWALL_SKU) && !CaptureService.isCapturingAsRoot());
|
||||
boolean firewallAvailable = Billing.newInstance(ctx).canUseFirewall();
|
||||
boolean blockVisible = false;
|
||||
boolean unblockVisible = false;
|
||||
MatchList blocklist = PCAPdroid.getInstance().getBlocklist();
|
||||
|
||||
if(app != null) {
|
||||
item = menu.findItem(R.id.hide_app);
|
||||
boolean appBlocked = blocklist.matchesApp(app.getUid());
|
||||
blockVisible = !appBlocked;
|
||||
unblockVisible = appBlocked;
|
||||
|
||||
item = menu.findItem(R.id.hide_app);
|
||||
String label = Utils.shorten(MatchList.getRuleLabel(ctx, RuleType.APP, app.getPackageName()), max_length);
|
||||
item.setTitle(label);
|
||||
item.setVisible(true);
|
||||
@ -323,7 +328,11 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener
|
||||
|
||||
item = menu.findItem(R.id.block_app);
|
||||
item.setTitle(label);
|
||||
item.setVisible(true);
|
||||
item.setVisible(!appBlocked);
|
||||
|
||||
item = menu.findItem(R.id.unblock_app);
|
||||
item.setTitle(label);
|
||||
item.setVisible(appBlocked);
|
||||
|
||||
if(conn.isBlacklisted()) {
|
||||
item = menu.findItem(R.id.whitelist_app);
|
||||
@ -333,14 +342,22 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener
|
||||
}
|
||||
|
||||
if((conn.info != null) && (!conn.info.isEmpty())) {
|
||||
item = menu.findItem(R.id.hide_host);
|
||||
boolean hostBlocked = blocklist.matchesExactHost(conn.info);
|
||||
String label = Utils.shorten(MatchList.getRuleLabel(ctx, RuleType.HOST, conn.info), max_length);
|
||||
blockVisible |= !hostBlocked;
|
||||
unblockVisible |= hostBlocked;
|
||||
|
||||
item = menu.findItem(R.id.hide_host);
|
||||
item.setTitle(label);
|
||||
item.setVisible(true);
|
||||
|
||||
item = menu.findItem(R.id.block_host);
|
||||
item.setTitle(label);
|
||||
item.setVisible(true);
|
||||
item.setVisible(!hostBlocked);
|
||||
|
||||
item = menu.findItem(R.id.unblock_host);
|
||||
item.setTitle(label);
|
||||
item.setVisible(hostBlocked);
|
||||
|
||||
item = menu.findItem(R.id.search_host);
|
||||
item.setTitle(label);
|
||||
@ -351,10 +368,13 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener
|
||||
item.setVisible(true);
|
||||
|
||||
String dm_clean = Utils.cleanDomain(conn.info);
|
||||
String rootDomain = Utils.getSecondLevelDomain(dm_clean);
|
||||
String domain = Utils.getSecondLevelDomain(dm_clean);
|
||||
|
||||
if(!rootDomain.equals(dm_clean)) {
|
||||
label = Utils.shorten(MatchList.getRuleLabel(ctx, RuleType.HOST, rootDomain), max_length);
|
||||
if(!domain.equals(dm_clean)) {
|
||||
boolean domainBlocked = blocklist.matchesExactHost(domain);
|
||||
label = Utils.shorten(MatchList.getRuleLabel(ctx, RuleType.HOST, domain), max_length);
|
||||
blockVisible |= !domainBlocked;
|
||||
unblockVisible |= domainBlocked;
|
||||
|
||||
item = menu.findItem(R.id.hide_domain);
|
||||
item.setTitle(label);
|
||||
@ -362,7 +382,11 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener
|
||||
|
||||
item = menu.findItem(R.id.block_domain);
|
||||
item.setTitle(label);
|
||||
item.setVisible(true);
|
||||
item.setVisible(!domainBlocked);
|
||||
|
||||
item = menu.findItem(R.id.unblock_domain);
|
||||
item.setTitle(label);
|
||||
item.setVisible(domainBlocked);
|
||||
}
|
||||
|
||||
if(conn.isBlacklistedHost()) {
|
||||
@ -388,7 +412,18 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener
|
||||
menu.findItem(R.id.hide_ip).setTitle(label);
|
||||
menu.findItem(R.id.copy_ip).setTitle(label);
|
||||
menu.findItem(R.id.search_ip).setTitle(label);
|
||||
menu.findItem(R.id.block_ip).setTitle(label);
|
||||
|
||||
boolean ipBlocked = blocklist.matchesIP(conn.dst_ip);
|
||||
blockVisible |= !ipBlocked;
|
||||
unblockVisible |= ipBlocked;
|
||||
|
||||
menu.findItem(R.id.block_ip)
|
||||
.setTitle(label)
|
||||
.setVisible(!ipBlocked);
|
||||
menu.findItem(R.id.unblock_ip)
|
||||
.setTitle(label)
|
||||
.setVisible(ipBlocked);
|
||||
|
||||
if(conn.isBlacklistedIp())
|
||||
menu.findItem(R.id.whitelist_ip).setTitle(label).setVisible(true);
|
||||
|
||||
@ -401,6 +436,9 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener
|
||||
menu.findItem(R.id.hide_proto).setTitle(label);
|
||||
menu.findItem(R.id.search_proto).setTitle(label);
|
||||
|
||||
menu.findItem(R.id.block_menu).setVisible(firewallAvailable && blockVisible);
|
||||
menu.findItem(R.id.unblock_menu).setVisible(firewallAvailable && unblockVisible);
|
||||
|
||||
if(!conn.isBlacklisted())
|
||||
menu.findItem(R.id.whitelist_menu).setVisible(false);
|
||||
}
|
||||
@ -468,6 +506,18 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener
|
||||
} else if(id == R.id.block_domain) {
|
||||
blocklist.addHost(Utils.getSecondLevelDomain(conn.info));
|
||||
blocklist_changed = true;
|
||||
} else if(id == R.id.unblock_app) {
|
||||
blocklist.removeApp(conn.uid);
|
||||
blocklist_changed = true;
|
||||
} else if(id == R.id.unblock_ip) {
|
||||
blocklist.removeIp(conn.dst_ip);
|
||||
blocklist_changed = true;
|
||||
} else if(id == R.id.unblock_host) {
|
||||
blocklist.removeHost(conn.info);
|
||||
blocklist_changed = true;
|
||||
} else if(id == R.id.unblock_domain) {
|
||||
blocklist.removeHost(Utils.getSecondLevelDomain(conn.info));
|
||||
blocklist_changed = true;
|
||||
} else if(id == R.id.open_app_details) {
|
||||
Intent intent = new Intent(requireContext(), AppDetailsActivity.class);
|
||||
intent.putExtra(AppDetailsActivity.APP_UID_EXTRA, conn.uid);
|
||||
|
||||
@ -255,7 +255,8 @@ public class EditListFragment extends Fragment {
|
||||
}
|
||||
|
||||
if(toRemove.size() > 0) {
|
||||
mList.removeRules(toRemove);
|
||||
for(MatchList.Rule rule: toRemove)
|
||||
mList.removeRule(rule);
|
||||
mList.save();
|
||||
}
|
||||
}
|
||||
|
||||
@ -241,12 +241,11 @@ public class MatchList {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void addApp(String pkg) { addRule(new Rule(RuleType.APP, pkg)); }
|
||||
public void addIp(String ip) { addRule(new Rule(RuleType.IP, ip)); }
|
||||
public void addHost(String info) { addRule(new Rule(RuleType.HOST, Utils.cleanDomain(info))); }
|
||||
public void addProto(String proto) { addRule(new Rule(RuleType.PROTOCOL, proto)); }
|
||||
public void addCountry(String country_code) { addRule(new Rule(RuleType.COUNTRY, country_code)); }
|
||||
|
||||
public void addApp(String pkg) { addRule(new Rule(RuleType.APP, pkg)); }
|
||||
public void addApp(int uid) {
|
||||
AppDescriptor app = mResolver.get(uid, 0);
|
||||
if(app == null) {
|
||||
@ -258,6 +257,21 @@ public class MatchList {
|
||||
addApp(app.getPackageName());
|
||||
}
|
||||
|
||||
public void removeIp(String ip) { removeRule(new Rule(RuleType.IP, ip)); }
|
||||
public void removeHost(String info) { removeRule(new Rule(RuleType.HOST, Utils.cleanDomain(info))); }
|
||||
public void removeProto(String proto) { removeRule(new Rule(RuleType.PROTOCOL, proto)); }
|
||||
public void removeCountry(String country_code) { removeRule(new Rule(RuleType.COUNTRY, country_code)); }
|
||||
public void removeApp(String pkg) { removeRule(new Rule(RuleType.APP, pkg)); }
|
||||
public void removeApp(int uid) {
|
||||
AppDescriptor app = mResolver.get(uid, 0);
|
||||
if(app == null) {
|
||||
Log.e(TAG, "could not resolve UID " + uid);
|
||||
return;
|
||||
}
|
||||
|
||||
removeApp(app.getPackageName());
|
||||
}
|
||||
|
||||
static private String matchKey(RuleType tp, Object val) {
|
||||
return tp + "@" + val;
|
||||
}
|
||||
@ -283,24 +297,6 @@ public class MatchList {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void removeRules(List<Rule> rules) {
|
||||
mRules.removeAll(rules);
|
||||
|
||||
for(Rule rule: rules) {
|
||||
String val = rule.getValue().toString();
|
||||
String key = matchKey(rule.getType(), val);
|
||||
mMatches.remove(key);
|
||||
|
||||
if(rule.getType() == RuleType.APP) {
|
||||
int uid = mResolver.getUid(val);
|
||||
if(uid != Utils.UID_NO_FILTER)
|
||||
mUids.remove(uid);
|
||||
else
|
||||
Log.w(TAG, "removeRules: no uid found for package " + val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int addRules(MatchList to_add) {
|
||||
int num_added = 0;
|
||||
|
||||
@ -314,6 +310,21 @@ public class MatchList {
|
||||
return num_added;
|
||||
}
|
||||
|
||||
public void removeRule(Rule rule) {
|
||||
String val = rule.getValue().toString();
|
||||
String key = matchKey(rule.getType(), val);
|
||||
mRules.remove(rule);
|
||||
mMatches.remove(key);
|
||||
|
||||
if(rule.getType() == RuleType.APP) {
|
||||
int uid = mResolver.getUid(val);
|
||||
if(uid != Utils.UID_NO_FILTER)
|
||||
mUids.remove(uid);
|
||||
else
|
||||
Log.w(TAG, "removeRule: no uid found for package " + val);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean matchesApp(int uid) {
|
||||
// match apps based on their uid (faster) rather than their package name
|
||||
return mUids.contains(uid);
|
||||
@ -327,12 +338,17 @@ public class MatchList {
|
||||
return mMatches.containsKey(matchKey(RuleType.PROTOCOL, l7proto));
|
||||
}
|
||||
|
||||
public boolean matchesExactHost(String host) {
|
||||
host = Utils.cleanDomain(host);
|
||||
return mMatches.containsKey(matchKey(RuleType.HOST, host));
|
||||
}
|
||||
|
||||
public boolean matchesHost(String host) {
|
||||
// Keep in sync with the native blacklist_match_domain
|
||||
host = Utils.cleanDomain(host);
|
||||
|
||||
// exact domain match
|
||||
if(mMatches.containsKey(matchKey(RuleType.HOST, host)))
|
||||
if(matchesExactHost(host))
|
||||
return true;
|
||||
|
||||
// 2nd-level domain match
|
||||
|
||||
@ -115,8 +115,8 @@ public class Prefs {
|
||||
&& p.getBoolean(PREF_MALWARE_DETECTION, true));
|
||||
}
|
||||
public static boolean isFirewallEnabled(Context ctx, SharedPreferences p) {
|
||||
return(Billing.newInstance(ctx).isRedeemed(Billing.FIREWALL_SKU)
|
||||
&& !isRootCaptureEnabled(p)
|
||||
// NOTE: firewall can be disabled at runtime
|
||||
return(Billing.newInstance(ctx).canUseFirewall()
|
||||
&& p.getBoolean(PREF_FIREWALL, true));
|
||||
}
|
||||
public static boolean startAtBoot(SharedPreferences p) { return(p.getBoolean(PREF_START_AT_BOOT, false)); }
|
||||
|
||||
@ -58,7 +58,8 @@
|
||||
<item
|
||||
android:id="@+id/block_host"
|
||||
android:title=""
|
||||
tools:title="@string/host_val" />
|
||||
tools:title="@string/host_val"
|
||||
android:visible="false" />
|
||||
|
||||
<item
|
||||
android:id="@+id/block_domain"
|
||||
@ -68,6 +69,33 @@
|
||||
</menu>
|
||||
</item>
|
||||
|
||||
<item android:id="@+id/unblock_menu" android:title="@string/unblock">
|
||||
<menu>
|
||||
<item
|
||||
android:id="@+id/unblock_app"
|
||||
android:title=""
|
||||
tools:title="@string/app_val"
|
||||
android:visible="false" />
|
||||
|
||||
<item
|
||||
android:id="@+id/unblock_ip"
|
||||
android:title=""
|
||||
tools:title="@string/ip_address_val" />
|
||||
|
||||
<item
|
||||
android:id="@+id/unblock_host"
|
||||
android:title=""
|
||||
tools:title="@string/host_val"
|
||||
android:visible="false" />
|
||||
|
||||
<item
|
||||
android:id="@+id/unblock_domain"
|
||||
android:title=""
|
||||
tools:title="@string/host_val"
|
||||
android:visible="false" />
|
||||
</menu>
|
||||
</item>
|
||||
|
||||
<item android:title="@string/search">
|
||||
<menu>
|
||||
<item
|
||||
|
||||
@ -235,6 +235,7 @@
|
||||
<string name="private_dns_hinders_detection">Private DNS hinders detection</string>
|
||||
<string name="decryption_no_filter_warn">Select a target app when decrypting TLS to avoid losing your connection to the Internet</string>
|
||||
<string name="block">Block…</string>
|
||||
<string name="unblock">Unblock…</string>
|
||||
<string name="firewall">Firewall</string>
|
||||
<string name="firewall_rules">Firewall rules</string>
|
||||
<string name="blocked_pkts">Blocked</string>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user