mirror of
https://github.com/emanuele-f/PCAPdroid.git
synced 2026-06-16 21:10:57 +08:00
Optimize connections matching
This commit is contained in:
parent
8701eb9145
commit
a642ddbd0d
@ -196,7 +196,7 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
last_bytes = 0;
|
||||
last_connections = 0;
|
||||
root_capture = Prefs.isRootCaptureEnabled(prefs);
|
||||
conn_reg = new ConnectionsRegister(CONNECTIONS_LOG_SIZE, prefs);
|
||||
conn_reg = new ConnectionsRegister(CONNECTIONS_LOG_SIZE, this, prefs);
|
||||
|
||||
if(dump_mode != Prefs.DumpMode.HTTP_SERVER)
|
||||
mHttpServer = null;
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
|
||||
package com.emanuelef.remote_capture;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Log;
|
||||
|
||||
@ -52,7 +53,7 @@ public class ConnectionsRegister {
|
||||
public final ConnectionsMatcher mExclusions;
|
||||
public boolean mExclusionsEnabled;
|
||||
|
||||
public ConnectionsRegister(int _size, SharedPreferences prefs) {
|
||||
public ConnectionsRegister(int _size, Context context, SharedPreferences prefs) {
|
||||
mTail = 0;
|
||||
mNumItems = 0;
|
||||
mUntrackedItems = 0;
|
||||
@ -62,7 +63,7 @@ public class ConnectionsRegister {
|
||||
mAppsStats = new HashMap<>(); // uid -> AppStats
|
||||
mExclusionsEnabled = true;
|
||||
mPrefs = prefs;
|
||||
mExclusions = new ConnectionsMatcher();
|
||||
mExclusions = new ConnectionsMatcher(context);
|
||||
|
||||
// Try to restore the exclusions
|
||||
String serialized = prefs.getString(Prefs.PREF_EXCLUSIONS, "");
|
||||
|
||||
@ -25,13 +25,11 @@ import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.util.Log;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.LayoutInflater;
|
||||
@ -70,6 +68,7 @@ import com.emanuelef.remote_capture.model.ConnectionDescriptor;
|
||||
import com.emanuelef.remote_capture.activities.ConnectionDetailsActivity;
|
||||
import com.emanuelef.remote_capture.adapters.ConnectionsAdapter;
|
||||
import com.emanuelef.remote_capture.model.ConnectionsMatcher;
|
||||
import com.emanuelef.remote_capture.model.ConnectionsMatcher.ItemType;
|
||||
import com.emanuelef.remote_capture.views.EmptyRecyclerView;
|
||||
import com.emanuelef.remote_capture.interfaces.ConnectionsListener;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
@ -302,18 +301,17 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener
|
||||
return;
|
||||
|
||||
AppDescriptor app = mApps.get(conn.uid);
|
||||
StyleSpan italic = new StyleSpan(Typeface.ITALIC);
|
||||
Context ctx = requireContext();
|
||||
|
||||
if(app != null) {
|
||||
MenuItem item = menu.findItem(R.id.exclude_app);
|
||||
item.setTitle(Utils.formatTextValue(ctx, null, italic, R.string.app_val, app.getName()));
|
||||
item.setTitle(ConnectionsMatcher.getLabel(ctx, ItemType.APP, app.getName()));
|
||||
item.setVisible(true);
|
||||
}
|
||||
|
||||
if((conn.info != null) && (!conn.info.isEmpty())) {
|
||||
MenuItem item = menu.findItem(R.id.exclude_host);
|
||||
item.setTitle(Utils.formatTextValue(ctx, null, italic, R.string.host_val, conn.info));
|
||||
item.setTitle(ConnectionsMatcher.getLabel(ctx, ItemType.HOST, conn.info));
|
||||
item.setVisible(true);
|
||||
|
||||
String rootDomain = Utils.getRootDomain(conn.info);
|
||||
@ -321,13 +319,13 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener
|
||||
if(!rootDomain.equals(conn.info)) {
|
||||
String val = "*" + rootDomain;
|
||||
item = menu.findItem(R.id.exclude_root_domain);
|
||||
item.setTitle(Utils.formatTextValue(ctx, null, italic, R.string.host_val, val));
|
||||
item.setTitle(ConnectionsMatcher.getLabel(ctx, ItemType.ROOT_DOMAIN, val));
|
||||
item.setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
menu.findItem(R.id.exclude_ip).setTitle(Utils.formatTextValue(ctx, null, italic, R.string.ip_address_val, conn.dst_ip));
|
||||
menu.findItem(R.id.exclude_proto).setTitle(Utils.formatTextValue(ctx, null, italic, R.string.protocol_val, conn.l7proto));
|
||||
menu.findItem(R.id.exclude_ip).setTitle(ConnectionsMatcher.getLabel(ctx, ItemType.IP, conn.dst_ip));
|
||||
menu.findItem(R.id.exclude_proto).setTitle(ConnectionsMatcher.getLabel(ctx, ItemType.PROTOCOL, conn.l7proto));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -685,8 +683,6 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener
|
||||
ListView exclusion = ((ListView)exclListView.findViewById(R.id.list));
|
||||
exclusion.setAdapter(adapter);
|
||||
exclusion.setOnItemClickListener((parent, view, position, id) -> {
|
||||
Log.d(TAG, "TODO:2 on click view " + position);
|
||||
|
||||
if(adapter.getCount() > 1)
|
||||
adapter.remove(adapter.getItem(position));
|
||||
});
|
||||
@ -704,6 +700,7 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener
|
||||
|
||||
private void updateExclusions(ExclusionsEditAdapter adapter) {
|
||||
ConnectionsRegister reg = CaptureService.getConnsRegister();
|
||||
ArrayList<ConnectionsMatcher.Item> toRemove = new ArrayList<>();
|
||||
|
||||
if(reg == null)
|
||||
return;
|
||||
@ -715,14 +712,14 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener
|
||||
while(iter.hasNext()) {
|
||||
ConnectionsMatcher.Item item = iter.next();
|
||||
|
||||
if(adapter.getPosition(item) < 0) {
|
||||
iter.remove();
|
||||
changed = true;
|
||||
}
|
||||
if(adapter.getPosition(item) < 0)
|
||||
toRemove.add(item);
|
||||
}
|
||||
|
||||
if(changed)
|
||||
if(toRemove.size() > 0) {
|
||||
reg.mExclusions.removeItems(toRemove);
|
||||
refreshFilteredConnections();
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteExclusions() {
|
||||
|
||||
@ -19,17 +19,20 @@
|
||||
|
||||
package com.emanuelef.remote_capture.model;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Typeface;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.emanuelef.remote_capture.R;
|
||||
import com.emanuelef.remote_capture.Utils;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
@ -37,60 +40,76 @@ import com.google.gson.JsonSerializer;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class ConnectionsMatcher {
|
||||
private static final String TAG = "ConnectionsMatcher";
|
||||
private static final StyleSpan italic = new StyleSpan(Typeface.ITALIC);
|
||||
private final Context mContext;
|
||||
private ArrayList<Item> mItems = new ArrayList<>();
|
||||
private final HashMap<String, Item> mMatches = new HashMap<>();
|
||||
|
||||
public static abstract class Item {
|
||||
protected String mLabel;
|
||||
public enum ItemType {
|
||||
APP,
|
||||
IP,
|
||||
HOST,
|
||||
ROOT_DOMAIN,
|
||||
PROTOCOL
|
||||
}
|
||||
|
||||
Item(String label) {
|
||||
public static class Item {
|
||||
private final String mLabel;
|
||||
private final ItemType mType;
|
||||
private final Object mValue;
|
||||
|
||||
Item(ItemType tp, Object value, String label) {
|
||||
mLabel = label;
|
||||
mType = tp;
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return mLabel;
|
||||
}
|
||||
|
||||
abstract public String getValue();
|
||||
abstract boolean matches(ConnectionDescriptor conn);
|
||||
public ItemType getType() {
|
||||
return mType;
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return mValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if(!(obj instanceof Item))
|
||||
return super.equals(obj);
|
||||
|
||||
Item other = (Item) obj;
|
||||
return((mType == other.mType) && (mValue.equals(other.mValue)));
|
||||
}
|
||||
}
|
||||
|
||||
private static class AppItem extends Item {
|
||||
int mUid;
|
||||
AppItem(int uid, String label) { super(label); mUid = uid; }
|
||||
public boolean matches(ConnectionDescriptor conn) { return conn.uid == mUid; }
|
||||
public String getValue() { return Integer.toString(mUid); }
|
||||
public ConnectionsMatcher(Context ctx) {
|
||||
mContext = ctx;
|
||||
}
|
||||
|
||||
private static class IpItem extends Item {
|
||||
String mIp;
|
||||
IpItem(String ip, String label) { super(label); mIp = ip; }
|
||||
public boolean matches(ConnectionDescriptor conn) { return conn.dst_ip.equals(mIp); }
|
||||
public String getValue() { return mIp; }
|
||||
}
|
||||
public static String getLabel(Context ctx, ItemType tp, String value) {
|
||||
int resid;
|
||||
|
||||
private static class HostItem extends Item {
|
||||
String mInfo;
|
||||
HostItem(String info, String label) { super(label); mInfo = info; }
|
||||
public boolean matches(ConnectionDescriptor conn) { return conn.info.equals(mInfo); }
|
||||
public String getValue() { return mInfo; }
|
||||
}
|
||||
switch(tp) {
|
||||
case APP: resid = R.string.app_val; break;
|
||||
case IP: resid = R.string.ip_address_val; break;
|
||||
case HOST:
|
||||
case ROOT_DOMAIN: resid = R.string.host_val; break;
|
||||
case PROTOCOL: resid = R.string.protocol_val; break;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
||||
private static class RootDomainItem extends Item {
|
||||
String mRootDomain;
|
||||
RootDomainItem(String domain, String label) { super(label); mRootDomain = domain; }
|
||||
public boolean matches(ConnectionDescriptor conn) { return Utils.getRootDomain(conn.info).equals(mRootDomain); }
|
||||
public String getValue() { return mRootDomain; }
|
||||
}
|
||||
|
||||
private static class ProtoItem extends Item {
|
||||
String mProto;
|
||||
ProtoItem(String info, String label) { super(label); mProto = info; }
|
||||
public boolean matches(ConnectionDescriptor conn) { return conn.l7proto.equals(mProto); }
|
||||
public String getValue() { return mProto; }
|
||||
return Utils.formatTextValue(ctx, null, italic, resid, value).toString();
|
||||
}
|
||||
|
||||
private static class Serializer implements JsonSerializer<ConnectionsMatcher> {
|
||||
@ -102,9 +121,9 @@ public class ConnectionsMatcher {
|
||||
for(Item item : src.mItems) {
|
||||
JsonObject itemObject = new JsonObject();
|
||||
|
||||
itemObject.add("type", new JsonPrimitive(item.getClass().getSimpleName()));
|
||||
itemObject.add("type", new JsonPrimitive(item.getType().name()));
|
||||
itemObject.add("label", new JsonPrimitive(item.getLabel()));
|
||||
itemObject.add("value", new JsonPrimitive(item.getValue()));
|
||||
itemObject.add("value", new JsonPrimitive(item.getValue().toString()));
|
||||
|
||||
itemsArr.add(itemObject);
|
||||
}
|
||||
@ -118,62 +137,61 @@ public class ConnectionsMatcher {
|
||||
mItems = new ArrayList<>();
|
||||
JsonArray itemArray = object.getAsJsonArray("items");
|
||||
|
||||
String appItemClass = AppItem.class.getSimpleName();
|
||||
String ipItemClass = IpItem.class.getSimpleName();
|
||||
String hostItemClass = HostItem.class.getSimpleName();
|
||||
String protoItemClass = ProtoItem.class.getSimpleName();
|
||||
String rootDomainItemClass = RootDomainItem.class.getSimpleName();
|
||||
|
||||
for(JsonElement el: itemArray) {
|
||||
JsonObject itemObj = el.getAsJsonObject();
|
||||
ItemType type;
|
||||
|
||||
String type = itemObj.get("type").getAsString();
|
||||
String label = itemObj.get("label").getAsString();
|
||||
JsonElement val = itemObj.get("value");
|
||||
try {
|
||||
type = ItemType.valueOf(itemObj.get("type").getAsString());
|
||||
} catch (IllegalArgumentException e) {
|
||||
e.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
|
||||
if(type.equals(appItemClass))
|
||||
addApp(val.getAsInt(), label);
|
||||
else if(type.equals(ipItemClass))
|
||||
addIp(val.getAsString(), label);
|
||||
else if(type.equals(hostItemClass))
|
||||
addHost(val.getAsString(), label);
|
||||
else if(type.equals(protoItemClass))
|
||||
addProto(val.getAsString(), label);
|
||||
else if(type.equals(rootDomainItemClass))
|
||||
addRootDomain(val.getAsString(), label);
|
||||
else
|
||||
Log.w(TAG, "unknown item type: " + type);
|
||||
String val = itemObj.get("value").getAsString();
|
||||
String label = getLabel(mContext, type, val);
|
||||
|
||||
addItem(new Item(type, val, label));
|
||||
}
|
||||
}
|
||||
|
||||
public void addApp(int uid, String label) { addItem(new AppItem(uid, label)); }
|
||||
public void addIp(String ip, String label) { addItem(new IpItem(ip, label)); }
|
||||
public void addHost(String info, String label) { addItem(new HostItem(info, label)); }
|
||||
public void addProto(String proto, String label) { addItem(new ProtoItem(proto, label)); }
|
||||
public void addRootDomain(String domain, String label) { addItem(new RootDomainItem(domain, label)); }
|
||||
public void addApp(int uid, String label) { addItem(new Item(ItemType.APP, uid, label)); }
|
||||
public void addIp(String ip, String label) { addItem(new Item(ItemType.IP, ip, label)); }
|
||||
public void addHost(String info, String label) { addItem(new Item(ItemType.HOST, info, label)); }
|
||||
public void addProto(String proto, String label) { addItem(new Item(ItemType.PROTOCOL, proto, label)); }
|
||||
public void addRootDomain(String domain, String label) { addItem(new Item(ItemType.ROOT_DOMAIN, domain, label)); }
|
||||
|
||||
static private String matchKey(ItemType tp, Object val) {
|
||||
return tp + "@" + val;
|
||||
}
|
||||
|
||||
private void addItem(Item item) {
|
||||
// Avoid duplicates
|
||||
if(!hasItem(item))
|
||||
String key = matchKey(item.getType(), item.getValue().toString());
|
||||
Log.d(TAG, key);
|
||||
|
||||
if(!mMatches.containsKey(key)) {
|
||||
mItems.add(item);
|
||||
mMatches.put(key, item);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasItem(Item search) {
|
||||
for(Item item : mItems) {
|
||||
if(item.mLabel.equals(search.mLabel))
|
||||
return true;
|
||||
}
|
||||
public void removeItems(List<Item> items) {
|
||||
mItems.removeAll(items);
|
||||
|
||||
return false;
|
||||
for(Item item: items) {
|
||||
String key = matchKey(item.getType(), item.getValue().toString());
|
||||
mMatches.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean matches(ConnectionDescriptor conn) {
|
||||
for(Item item : mItems) {
|
||||
if(item.matches(conn))
|
||||
return true;
|
||||
}
|
||||
boolean hasInfo = ((conn.info != null) && (!conn.info.isEmpty()));
|
||||
|
||||
return false;
|
||||
return(mMatches.containsKey(matchKey(ItemType.APP, conn.uid)) ||
|
||||
mMatches.containsKey(matchKey(ItemType.IP, conn.dst_ip)) ||
|
||||
mMatches.containsKey(matchKey(ItemType.PROTOCOL, conn.l7proto)) ||
|
||||
(hasInfo && mMatches.containsKey(matchKey(ItemType.HOST, conn.info))) ||
|
||||
(hasInfo && mMatches.containsKey(matchKey(ItemType.ROOT_DOMAIN, Utils.getRootDomain(conn.info)))));
|
||||
}
|
||||
|
||||
public Iterator<Item> iterItems() {
|
||||
@ -182,6 +200,7 @@ public class ConnectionsMatcher {
|
||||
|
||||
public void clear() {
|
||||
mItems.clear();
|
||||
mMatches.clear();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user