Dynamically update IP filters

This commit is contained in:
M66B 2016-03-09 17:51:49 +01:00
parent b9227d4060
commit 565df22ae4
5 changed files with 46 additions and 97 deletions

View File

@ -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();
}

View File

@ -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));
}
}
}

View File

@ -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>

View File

@ -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"

View File

@ -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"