mirror of
https://github.com/M66B/NetGuard.git
synced 2025-03-15 08:29:02 +00:00
Dynamically update IP filters
This commit is contained in:
parent
b9227d4060
commit
565df22ae4
5 changed files with 46 additions and 97 deletions
|
@ -41,7 +41,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
|||
private static final String TAG = "NetGuard.Database";
|
||||
|
||||
private static final String DB_NAME = "Netguard";
|
||||
private static final int DB_VERSION = 19;
|
||||
private static final int DB_VERSION = 20;
|
||||
|
||||
private static boolean once = true;
|
||||
private static List<LogChangedListener> logChangedListeners = new ArrayList<>();
|
||||
|
@ -159,6 +159,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
|||
", connections INTEGER NULL" +
|
||||
");");
|
||||
db.execSQL("CREATE UNIQUE INDEX idx_access ON access(uid, version, protocol, daddr, dport)");
|
||||
db.execSQL("CREATE INDEX idx_access_daddr ON access(daddr)");
|
||||
db.execSQL("CREATE INDEX idx_access_block ON access(block)");
|
||||
}
|
||||
|
||||
|
@ -308,6 +309,10 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
|||
db.execSQL("ALTER TABLE access ADD COLUMN connections INTEGER NULL");
|
||||
oldVersion = 19;
|
||||
}
|
||||
if (oldVersion < 20) {
|
||||
db.execSQL("CREATE INDEX IF NOT EXISTS idx_access_daddr ON access(daddr)");
|
||||
oldVersion = 20;
|
||||
}
|
||||
|
||||
if (oldVersion == DB_VERSION) {
|
||||
db.setVersion(oldVersion);
|
||||
|
@ -621,7 +626,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
|||
db.beginTransactionNonExclusive();
|
||||
try {
|
||||
// There is a segmented index on uid
|
||||
// There is no index on block for write performance
|
||||
// There is an index on block
|
||||
db.delete("access", "uid = ? AND block < 0", new String[]{Integer.toString(uid)});
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
|
@ -694,7 +699,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
|||
mLock.readLock().lock();
|
||||
try {
|
||||
SQLiteDatabase db = this.getReadableDatabase();
|
||||
// There is a segmented index on uid and daddr
|
||||
// There is a segmented index on uid, block and daddr
|
||||
// There is no index on allowed and time for write performance
|
||||
String query = "SELECT MAX(time) AS time, daddr, allowed";
|
||||
query += " FROM access";
|
||||
|
@ -722,21 +727,9 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
|||
}
|
||||
}
|
||||
|
||||
public long getBlockedRuleCount(int uid) {
|
||||
mLock.readLock().lock();
|
||||
try {
|
||||
SQLiteDatabase db = this.getReadableDatabase();
|
||||
// There is a segmented index on uid
|
||||
// There is an index on block
|
||||
return db.compileStatement("SELECT COUNT(*) FROM access WHERE block > 0 AND uid =" + uid).simpleQueryForLong();
|
||||
} finally {
|
||||
mLock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// DNS
|
||||
|
||||
public void insertDns(ResourceRecord rr) {
|
||||
public boolean insertDns(ResourceRecord rr) {
|
||||
mLock.writeLock().lock();
|
||||
try {
|
||||
SQLiteDatabase db = this.getWritableDatabase();
|
||||
|
@ -760,6 +753,8 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
|||
Log.e(TAG, "Update dns failed rows=" + rows);
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
|
||||
return (rows == 0);
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
|
@ -822,21 +817,22 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
|||
}
|
||||
}
|
||||
|
||||
public Cursor getAccessDns() {
|
||||
public Cursor getAccessDns(String dname) {
|
||||
mLock.readLock().lock();
|
||||
try {
|
||||
SQLiteDatabase db = this.getReadableDatabase();
|
||||
|
||||
// There is a segmented index on dns.qname
|
||||
// There is no index on access.daddr for write performance
|
||||
// There is an index on access.block
|
||||
// There is an index on access.daddr and access.block
|
||||
String query = "SELECT a.uid, a.version, a.protocol, a.daddr, d.resource, a.dport, a.block";
|
||||
query += " FROM access AS a";
|
||||
query += " LEFT JOIN dns AS d";
|
||||
query += " ON d.qname = a.daddr";
|
||||
query += " WHERE a.block >= 0";
|
||||
if (dname != null)
|
||||
query += " AND a.daddr = ?";
|
||||
|
||||
return db.rawQuery(query, new String[]{});
|
||||
return db.rawQuery(query, dname == null ? new String[]{} : new String[]{dname});
|
||||
} finally {
|
||||
mLock.readLock().unlock();
|
||||
}
|
||||
|
|
|
@ -148,8 +148,7 @@ public class SinkholeService extends VpnService implements SharedPreferences.OnS
|
|||
private static final int MSG_STATS_STOP = 2;
|
||||
private static final int MSG_STATS_UPDATE = 3;
|
||||
private static final int MSG_PACKET = 4;
|
||||
private static final int MSG_RR = 5;
|
||||
private static final int MSG_USAGE = 6;
|
||||
private static final int MSG_USAGE = 5;
|
||||
|
||||
private enum State {none, waiting, enforcing, stats}
|
||||
|
||||
|
@ -599,10 +598,6 @@ public class SinkholeService extends VpnService implements SharedPreferences.OnS
|
|||
log((Packet) msg.obj, msg.arg1, msg.arg2 > 0);
|
||||
break;
|
||||
|
||||
case MSG_RR:
|
||||
resolved((ResourceRecord) msg.obj);
|
||||
break;
|
||||
|
||||
case MSG_USAGE:
|
||||
usage((Usage) msg.obj);
|
||||
break;
|
||||
|
@ -641,12 +636,6 @@ public class SinkholeService extends VpnService implements SharedPreferences.OnS
|
|||
}
|
||||
}
|
||||
|
||||
private void resolved(ResourceRecord rr) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(SinkholeService.this);
|
||||
if (prefs.getBoolean("filter", false) && prefs.getBoolean("resolved", true))
|
||||
DatabaseHelper.getInstance(SinkholeService.this).insertDns(rr);
|
||||
}
|
||||
|
||||
private void usage(Usage usage) {
|
||||
if (usage.Uid >= 0 && !(usage.Uid == 0 && usage.Protocol == 17 && usage.DPort == 53)) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(SinkholeService.this);
|
||||
|
@ -1100,7 +1089,7 @@ public class SinkholeService extends VpnService implements SharedPreferences.OnS
|
|||
if (filter) {
|
||||
prepareUidAllowed(listAllowed, listRule);
|
||||
prepareHostsBlocked();
|
||||
prepareUidIPFilters();
|
||||
prepareUidIPFilters(null);
|
||||
prepareForwarding();
|
||||
}
|
||||
|
||||
|
@ -1111,15 +1100,6 @@ public class SinkholeService extends VpnService implements SharedPreferences.OnS
|
|||
int prio = Integer.parseInt(prefs.getString("loglevel", Integer.toString(Log.WARN)));
|
||||
jni_start(vpn.getFd(), mapForward.containsKey(53), prio);
|
||||
}
|
||||
|
||||
// Native needs to be started for name resolving
|
||||
if (filter)
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateUidIPFilters();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void stopNative(ParcelFileDescriptor vpn, boolean clear) {
|
||||
|
@ -1197,10 +1177,11 @@ public class SinkholeService extends VpnService implements SharedPreferences.OnS
|
|||
}
|
||||
}
|
||||
|
||||
private void prepareUidIPFilters() {
|
||||
mapUidIPFilters.clear();
|
||||
private void prepareUidIPFilters(String dname) {
|
||||
if (dname == null)
|
||||
mapUidIPFilters.clear();
|
||||
|
||||
Cursor cursor = DatabaseHelper.getInstance(SinkholeService.this).getAccessDns();
|
||||
Cursor cursor = DatabaseHelper.getInstance(SinkholeService.this).getAccessDns(dname);
|
||||
int colUid = cursor.getColumnIndex("uid");
|
||||
int colVersion = cursor.getColumnIndex("version");
|
||||
int colProtocol = cursor.getColumnIndex("protocol");
|
||||
|
@ -1226,44 +1207,30 @@ public class SinkholeService extends VpnService implements SharedPreferences.OnS
|
|||
dport = 0;
|
||||
long key = (version << 40) | (protocol << 32) | (dport << 16) | uid;
|
||||
|
||||
if (!mapUidIPFilters.containsKey(key))
|
||||
mapUidIPFilters.put(key, new HashMap());
|
||||
synchronized (mapUidIPFilters) {
|
||||
if (!mapUidIPFilters.containsKey(key))
|
||||
mapUidIPFilters.put(key, new HashMap());
|
||||
|
||||
try {
|
||||
// Log.i(TAG, "Set filter uid=" + uid + " " + daddr + " " + dresource + "/" + dport + "=" + block);
|
||||
if (dresource == null) {
|
||||
if (Util.isNumericAddress(daddr))
|
||||
mapUidIPFilters.get(key).put(InetAddress.getByName(daddr), block);
|
||||
} else {
|
||||
if (Util.isNumericAddress(dresource))
|
||||
mapUidIPFilters.get(key).put(InetAddress.getByName(dresource), block);
|
||||
else
|
||||
Log.w(TAG, "Address not numeric " + daddr);
|
||||
try {
|
||||
if (dname != null)
|
||||
Log.i(TAG, "Set filter uid=" + uid + " " + daddr + " " + dresource + "/" + dport + "=" + block);
|
||||
if (dresource == null) {
|
||||
if (Util.isNumericAddress(daddr))
|
||||
mapUidIPFilters.get(key).put(InetAddress.getByName(daddr), block);
|
||||
} else {
|
||||
if (Util.isNumericAddress(dresource))
|
||||
mapUidIPFilters.get(key).put(InetAddress.getByName(dresource), block);
|
||||
else
|
||||
Log.w(TAG, "Address not numeric " + daddr);
|
||||
}
|
||||
} catch (UnknownHostException ex) {
|
||||
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
|
||||
}
|
||||
} catch (UnknownHostException ex) {
|
||||
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
|
||||
}
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
private void updateUidIPFilters() {
|
||||
// Android caches DNS for 10 minutes
|
||||
Cursor cursor = DatabaseHelper.getInstance(SinkholeService.this).getAccess();
|
||||
int colDAddr = cursor.getColumnIndex("daddr");
|
||||
while (cursor.moveToNext()) {
|
||||
String daddr = cursor.getString(colDAddr);
|
||||
try {
|
||||
// This will result in native callbacks
|
||||
InetAddress.getAllByName(daddr);
|
||||
} catch (UnknownHostException ex) {
|
||||
Log.w(TAG, "Update " + ex.toString() + "\n" + Log.getStackTraceString(ex));
|
||||
}
|
||||
}
|
||||
cursor.close();
|
||||
Log.i(TAG, "UID/IP filters updated");
|
||||
}
|
||||
|
||||
private void prepareForwarding() {
|
||||
mapForward.clear();
|
||||
|
||||
|
@ -1409,10 +1376,10 @@ public class SinkholeService extends VpnService implements SharedPreferences.OnS
|
|||
|
||||
// Called from native code
|
||||
private void dnsResolved(ResourceRecord rr) {
|
||||
Message msg = logHandler.obtainMessage();
|
||||
msg.obj = rr;
|
||||
msg.what = MSG_RR;
|
||||
logHandler.sendMessage(msg);
|
||||
if (DatabaseHelper.getInstance(SinkholeService.this).insertDns(rr)) {
|
||||
Log.i(TAG, "New IP " + rr);
|
||||
prepareUidIPFilters(rr.QName);
|
||||
}
|
||||
}
|
||||
|
||||
// Called from native code
|
||||
|
@ -1465,10 +1432,10 @@ public class SinkholeService extends VpnService implements SharedPreferences.OnS
|
|||
} catch (UnknownHostException ex) {
|
||||
Log.w(TAG, "Allowed " + ex.toString() + "\n" + Log.getStackTraceString(ex));
|
||||
}
|
||||
}
|
||||
|
||||
if (!filtered)
|
||||
packet.allowed = (mapUidAllowed.containsKey(packet.uid) && mapUidAllowed.get(packet.uid));
|
||||
if (!filtered)
|
||||
packet.allowed = (mapUidAllowed.containsKey(packet.uid) && mapUidAllowed.get(packet.uid));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -75,7 +75,6 @@ however it is impossible to guarantee NetGuard will work correctly on every devi
|
|||
<string name="setting_log_app">Log internet access</string>
|
||||
<string name="setting_access">Notify on internet access</string>
|
||||
<string name="setting_filter">Filter traffic</string>
|
||||
<string name="setting_resolved">Store resolved domain names</string>
|
||||
<string name="setting_track_usage">Track network usage</string>
|
||||
<string name="setting_reset_usage">Reset network usage</string>
|
||||
<string name="setting_show_resolved" translatable="false">Show resolved domain names</string>
|
||||
|
@ -121,7 +120,6 @@ however it is impossible to guarantee NetGuard will work correctly on every devi
|
|||
<string name="summary_log_app">Log attempts to access the internet for applications. This might result in extra battery usage.</string>
|
||||
<string name="summary_access">Show a status bar notification when an application attempts to access a new internet address</string>
|
||||
<string name="summary_filter">Filter IP packets going out of the VPN tunnel. This might result in extra battery usage.</string>
|
||||
<string name="summary_resolved">Store domain names/IP addresses into a database. This makes the traffic log more useful, but might result in extra battery usage.</string>
|
||||
<string name="summary_track_usage">Track the number of bytes sent and received for each application and address. This might result in extra battery usage.</string>
|
||||
<string name="summary_block_domains">Respond with \'name error\' (NXDOMAIN) for blocked domain names. This switch is disabled when no hosts file is available.</string>
|
||||
|
||||
|
|
|
@ -132,12 +132,6 @@
|
|||
android:key="use_hosts"
|
||||
android:summary="@string/summary_block_domains"
|
||||
android:title="@string/setting_block_domains" />
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="true"
|
||||
android:dependency="filter"
|
||||
android:key="resolved"
|
||||
android:summary="@string/summary_resolved"
|
||||
android:title="@string/setting_resolved" />
|
||||
<Preference
|
||||
android:dependency="filter"
|
||||
android:key="forwarding"
|
||||
|
|
|
@ -132,12 +132,6 @@
|
|||
android:key="use_hosts"
|
||||
android:summary="@string/summary_block_domains"
|
||||
android:title="@string/setting_block_domains" />
|
||||
<eu.faircode.netguard.SwitchPreference
|
||||
android:defaultValue="true"
|
||||
android:dependency="filter"
|
||||
android:key="resolved"
|
||||
android:summary="@string/summary_resolved"
|
||||
android:title="@string/setting_resolved" />
|
||||
<Preference
|
||||
android:dependency="filter"
|
||||
android:key="forwarding"
|
||||
|
|
Loading…
Add table
Reference in a new issue