Hide connections matching whitelist after delay

Some info of the connections, such as the host and the protocol, can
become available after the connection has been shown. Such info could
determine a match with the whitelist, in which case the connection must
be hidden.
This commit is contained in:
emanuele-f 2021-06-29 19:28:18 +02:00
parent 3e9680b96c
commit ca7c8127d9
5 changed files with 88 additions and 30 deletions

View File

@ -72,13 +72,15 @@ public class ConnectionsRegister {
conns = Arrays.copyOfRange(conns, conns.length - mSize, conns.length);
}
int in_items = Math.min((mSize - mNumItems), conns.length);
int out_items = conns.length - in_items;
int out_items = conns.length - Math.min((mSize - mNumItems), conns.length);
int insert_pos = mNumItems;
ConnectionDescriptor []removedItems = null;
//Log.d(TAG, "newConnections[" + mNumItems + "/" + mSize +"]: insert " + conns.length +
// " items at " + mTail + " (removed: " + out_items + " at " + firstPos() + ")");
if(out_items > 0) {
int pos = mTail;
int pos = firstPos();
removedItems = new ConnectionDescriptor[out_items];
// update the apps stats
@ -128,16 +130,16 @@ public class ConnectionsRegister {
}
}
public synchronized void connectionsUpdates(ConnectionUpdate[] conns) {
public synchronized void connectionsUpdates(ConnectionUpdate[] updates) {
int first_pos = firstPos();
int first_id = mItemsRing[first_pos].incr_id;
int last_id = mItemsRing[lastPos()].incr_id;
int []changed_pos = new int[conns.length];
int []changed_pos = new int[updates.length];
int k = 0;
Log.d(TAG, "connectionsUpdates: items=" + mNumItems + ", first_id=" + first_id + ", last_id=" + last_id);
for(ConnectionUpdate update: conns) {
for(ConnectionUpdate update: updates) {
int id = update.incr_id;
// ignore updates for untracked items
@ -159,10 +161,10 @@ public class ConnectionsRegister {
}
for(ConnectionsListener listener: mListeners) {
if(k != conns.length) {
if(k != updates.length) {
// some untracked items where skipped, shrink the array
changed_pos = Arrays.copyOf(changed_pos, k);
k = conns.length;
k = updates.length;
}
listener.connectionsUpdated(changed_pos);

View File

@ -43,6 +43,7 @@ import com.emanuelef.remote_capture.Utils;
import com.emanuelef.remote_capture.model.Whitelist;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Objects;
@ -58,7 +59,12 @@ public class ConnectionsAdapter extends RecyclerView.Adapter<ConnectionsAdapter.
private int mClickedPosition;
private int mNumRemovedItems;
public boolean mWhitelistEnabled;
// maps a connection ID to a position in mFilteredConn. Positions are shifted by mNumRemovedItems
// to provide an always increasing position even when items are removed. The correct unshifted
// position is returned by getFilteredItemPos.
private final HashMap<Integer, Integer> mIdToFilteredPos;
private ArrayList<ConnectionDescriptor> mFilteredConn;
private String mFilter;
public final Whitelist mWhitelist;
@ -175,8 +181,10 @@ public class ConnectionsAdapter extends RecyclerView.Adapter<ConnectionsAdapter.
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
ConnectionDescriptor conn = getItem(position);
if(conn == null)
if(conn == null) {
Log.w(TAG, "bad position: " + position);
return;
}
holder.bindConn(mContext, conn, mApps, mUnknownIcon);
}
@ -203,6 +211,20 @@ public class ConnectionsAdapter extends RecyclerView.Adapter<ConnectionsAdapter.
return(pos - mNumRemovedItems);
}
private void removeItemAt(int pos) {
int incr_id = mFilteredConn.get(pos).incr_id;
mFilteredConn.remove(pos);
mIdToFilteredPos.remove(incr_id);
notifyItemRemoved(pos);
}
/* Fixes the mappings in mIdToFilteredPos, starting from the provided position */
private void fixFilteredPositions(int startPos) {
for(int i=startPos; i<mFilteredConn.size(); i++)
mIdToFilteredPos.put(mFilteredConn.get(i).incr_id, i + mNumRemovedItems);
}
@Override
public void connectionsChanges(int num_connetions) {
mUnfilteredItemsCount = num_connetions;
@ -219,12 +241,12 @@ public class ConnectionsAdapter extends RecyclerView.Adapter<ConnectionsAdapter.
}
int numNew = 0;
int vpos = mNumRemovedItems + mFilteredConn.size();
int pos = mNumRemovedItems + mFilteredConn.size();
// Assume that connections are only added at the end of the dataset
for(ConnectionDescriptor conn : conns) {
if(matches(conn)) {
mIdToFilteredPos.put(conn.incr_id, vpos++);
mIdToFilteredPos.put(conn.incr_id, pos++);
mFilteredConn.add(conn);
numNew++;
}
@ -247,15 +269,14 @@ public class ConnectionsAdapter extends RecyclerView.Adapter<ConnectionsAdapter.
if(conn == null)
continue;
int vpos = getFilteredItemPos(conn.incr_id);
int pos = getFilteredItemPos(conn.incr_id);
if(vpos != -1) {
// Assume that connections are only remove from the start of the dataset
mFilteredConn.remove(0);
mIdToFilteredPos.remove(conn.incr_id);
if(pos != -1) {
// Assume that connections are only removed from the start of the dataset
removeItemAt(0);
// by incrementing mNumRemovedItems we can shift the position of subsequent items
mNumRemovedItems++;
notifyItemRemoved(vpos);
}
}
}
@ -269,19 +290,43 @@ public class ConnectionsAdapter extends RecyclerView.Adapter<ConnectionsAdapter.
}
ConnectionsRegister reg = CaptureService.requireConnsRegister();
int first_removed_pos = -1;
int num_just_removed = 0;
for(int pos : positions) {
ConnectionDescriptor conn = reg.getConn(pos);
// Sort order necessary to properly use num_just_removed
Arrays.sort(positions);
for(int reg_pos : positions) {
ConnectionDescriptor conn = reg.getConn(reg_pos);
if(conn != null) {
int vpos = getFilteredItemPos(conn.incr_id);
int pos = getFilteredItemPos(conn.incr_id);
if(vpos != -1) {
Log.d(TAG, "Changed item " + vpos + ", dataset size: " + getItemCount());
notifyItemChanged(pos);
if(pos != -1) {
pos -= num_just_removed;
if(matches(conn)) {
Log.d(TAG, "Changed item " + pos + ", dataset size: " + getItemCount());
notifyItemChanged(pos);
} else {
Log.d(TAG, "Unmatch item " + pos + ": " + conn.toString());
// A previously matching connection may not match anymore. This happens, for
// example, when its info or protocol is updated. In this case, the connection
// must be removed.
removeItemAt(pos);
num_just_removed++;
if(first_removed_pos == -1)
first_removed_pos = pos;
}
}
}
}
// Need to recalculate the mappings starting at the first removed item
if(first_removed_pos != -1)
fixFilteredPositions(first_removed_pos);
}
public void refreshFilteredConnections() {
@ -295,7 +340,7 @@ public class ConnectionsAdapter extends RecyclerView.Adapter<ConnectionsAdapter.
mNumRemovedItems = 0;
if(hasWhitelistFilter() || (mFilter != null)) {
int vpos = 0;
int pos = 0;
mFilteredConn = new ArrayList<>();
for(int i=0; i<mUnfilteredItemsCount; i++) {
@ -303,7 +348,7 @@ public class ConnectionsAdapter extends RecyclerView.Adapter<ConnectionsAdapter.
if(matches(conn)) {
mFilteredConn.add(conn);
mIdToFilteredPos.put(conn.incr_id, vpos++);
mIdToFilteredPos.put(conn.incr_id, pos++);
}
}
@ -320,8 +365,10 @@ public class ConnectionsAdapter extends RecyclerView.Adapter<ConnectionsAdapter.
ConnectionsRegister reg = CaptureService.getConnsRegister();
if((pos < 0) || (pos >= mUnfilteredItemsCount) || (reg == null))
if((pos < 0) || (pos >= mUnfilteredItemsCount) || (reg == null)) {
Log.w(TAG, "getItem: bad position: " + pos);
return null;
}
return reg.getConn(pos);
}

View File

@ -455,7 +455,7 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener
@Override
public void connectionsAdded(int start, ConnectionDescriptor []conns) {
mHandler.post(() -> {
Log.d(TAG, "Added " + conns.length + " connections at " + start);
Log.d(TAG, "Add " + conns.length + " connections at " + start);
mAdapter.connectionsAdded(start, conns);

View File

@ -21,6 +21,8 @@ package com.emanuelef.remote_capture.model;
import android.content.Context;
import androidx.annotation.NonNull;
import com.emanuelef.remote_capture.AppsResolver;
import com.emanuelef.remote_capture.R;
@ -128,4 +130,10 @@ public class ConnectionDescriptor implements Serializable {
app.getPackageName().equals(filter)))
);
}
@Override
public @NonNull String toString() {
return "[proto=" + ipproto + "/" + l7proto + "]: " + src_ip + ":" + src_port + " -> " +
dst_ip + ":" + dst_port + " [" + uid + "] " + info;
}
}

View File

@ -17,6 +17,7 @@
* Copyright 2020-21 - Emanuele Faranda
*/
#include <inttypes.h>
#include "vpnproxy.h"
#include "pcap_utils.h"
#include "common/utils.h"
@ -38,8 +39,8 @@ static ndpi_protocol_bitmask_struct_t masterProtos;
static int dumper_socket;
static bool send_header;
static int netd_resolve_waiting;
static time_t last_connections_dump;
static time_t next_connections_dump;
static u_int64_t last_connections_dump;
static u_int64_t next_connections_dump;
static vpnproxy_data_t *global_proxy = NULL;
/* ******************************************************* */
@ -728,7 +729,7 @@ static void sendConnectionsDump(vpnproxy_data_t *proxy) {
if((proxy->new_conns.cur_items == 0) && (proxy->conns_updates.cur_items == 0))
return;
log_d("sendConnectionsDump [after %d ms]: new=%d, updates=%d",
log_d("sendConnectionsDump [after %" PRIu64 " ms]: new=%d, updates=%d",
proxy->now_ms - last_connections_dump,
proxy->new_conns.cur_items, proxy->conns_updates.cur_items);