DNS resolving

This commit is contained in:
M66B 2016-01-30 10:59:19 +01:00
parent 55042bedd3
commit b2e7c69af7
8 changed files with 139 additions and 86 deletions

View File

@ -16,6 +16,7 @@ public class AccessAdapter extends CursorAdapter {
private int colDaddr;
private int colDPort;
private int colDName;
private int colTime;
private int colAllowed;
@ -23,6 +24,7 @@ public class AccessAdapter extends CursorAdapter {
super(context, cursor, 0);
colDaddr = cursor.getColumnIndex("daddr");
colDPort = cursor.getColumnIndex("dport");
colDName = cursor.getColumnIndex("dname");
colTime = cursor.getColumnIndex("time");
colAllowed = cursor.getColumnIndex("allowed");
}
@ -35,8 +37,9 @@ public class AccessAdapter extends CursorAdapter {
@Override
public void bindView(final View view, final Context context, final Cursor cursor) {
// Get values
final String dest = cursor.getString(colDaddr);
final int dport = (cursor.isNull(colDPort) ? -1 : cursor.getInt(colDPort));
String daddr = cursor.getString(colDaddr);
int dport = (cursor.isNull(colDPort) ? -1 : cursor.getInt(colDPort));
String dname = (cursor.isNull(colDName) ? null : cursor.getString(colDName));
long time = cursor.getLong(colTime);
int allowed = cursor.getInt(colAllowed);
@ -48,12 +51,6 @@ public class AccessAdapter extends CursorAdapter {
// Set values
tvTime.setText(new SimpleDateFormat("HH:mm:ss").format(time));
cbIp.setChecked(allowed != 0);
Util.resolveName(dest, new Util.resolveListener() {
@Override
public void onResolved(String name, boolean resolved) {
tvDest.setText(name + (dport > 0 ? ":" + dport : ""));
}
});
tvDest.setText((dname == null ? daddr : dname) + (dport > 0 ? ":" + dport : ""));
}
}

View File

@ -16,7 +16,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 = 9;
private static final int DB_VERSION = 10;
private static boolean once = true;
private static List<LogChangedListener> logChangedListeners = new ArrayList<LogChangedListener>();
@ -69,6 +69,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
", sport INTEGER NULL" +
", daddr TEXT" +
", dport INTEGER NULL" +
", dname TEXT NULL" +
", uid INTEGER NULL" +
", data TEXT" +
", allowed INTEGER NULL" +
@ -76,7 +77,6 @@ public class DatabaseHelper extends SQLiteOpenHelper {
", interactive INTEGER NULL" +
");");
db.execSQL("CREATE INDEX idx_log_time ON log(time)");
db.execSQL("CREATE INDEX idx_log_source ON log(saddr)");
db.execSQL("CREATE INDEX idx_log_dest ON log(daddr)");
db.execSQL("CREATE INDEX idx_log_uid ON log(uid)");
}
@ -88,6 +88,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
", uid INTEGER NOT NULL" +
", daddr TEXT NOT NULL" +
", dport INTEGER NULL" +
", dname TEXT NULL" +
", time INTEGER NOT NULL" +
", allowed INTEGER NOT NULL" +
");");
@ -141,6 +142,13 @@ public class DatabaseHelper extends SQLiteOpenHelper {
createTableAccess(db);
oldVersion = 9;
}
if (oldVersion < 10) {
db.execSQL("DROP TABLE log");
db.execSQL("DROP TABLE access");
createTableLog(db);
createTableAccess(db);
oldVersion = 10;
}
if (oldVersion == DB_VERSION) {
db.setVersion(oldVersion);
@ -158,7 +166,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
// Log
public DatabaseHelper insertLog(Packet packet, int connection, boolean interactive) {
public DatabaseHelper insertLog(Packet packet, String dname, int connection, boolean interactive) {
synchronized (mContext.getApplicationContext()) {
SQLiteDatabase db = this.getWritableDatabase();
@ -185,6 +193,11 @@ public class DatabaseHelper extends SQLiteOpenHelper {
else
cv.put("dport", packet.dport);
if (dname == null)
cv.putNull("dname");
else
cv.put("dname", dname);
cv.put("data", packet.data);
if (packet.uid < 0)
@ -238,20 +251,24 @@ public class DatabaseHelper extends SQLiteOpenHelper {
public Cursor searchLog(String find) {
SQLiteDatabase db = this.getReadableDatabase();
String query = "SELECT ID AS _id, * FROM log";
query += " WHERE saddr LIKE ? OR daddr LIKE ? OR uid LIKE ?";
query += " WHERE daddr LIKE ? OR uid LIKE ?";
query += " ORDER BY time DESC";
return db.rawQuery(query, new String[]{"%" + find + "%", "%" + find + "%", "%" + find + "%"});
return db.rawQuery(query, new String[]{"%" + find + "%", "%" + find + "%"});
}
// Access
public DatabaseHelper updateAccess(Packet packet) {
public DatabaseHelper updateAccess(Packet packet, String dname) {
synchronized (mContext.getApplicationContext()) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put("time", packet.time);
cv.put("allowed", packet.allowed ? 1 : 0);
if (dname == null)
cv.putNull("dname");
else
cv.put("dname", dname);
int rows = db.update("access", cv, "uid = ? AND daddr = ? AND dport = ?", new String[]{
Integer.toString(packet.uid), packet.daddr, Integer.toString(packet.dport)});
@ -260,6 +277,12 @@ public class DatabaseHelper extends SQLiteOpenHelper {
cv.put("uid", packet.uid);
cv.put("daddr", packet.daddr);
cv.put("dport", packet.dport);
if (dname == null)
cv.putNull("dname");
else
cv.put("dname", dname);
if (db.insert("access", null, cv) == -1)
Log.e(TAG, "Insert access failed");
}

View File

@ -6,6 +6,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.util.Log;
@ -34,11 +35,13 @@ public class LogAdapter extends CursorAdapter {
private int colSPort;
private int colDaddr;
private int colDPort;
private int colDName;
private int colUid;
private int colData;
private int colAllowed;
private int colConnection;
private int colInteractive;
private InetAddress dns = null;
private InetAddress vpn4 = null;
private InetAddress vpn6 = null;
@ -53,6 +56,7 @@ public class LogAdapter extends CursorAdapter {
colSPort = cursor.getColumnIndex("sport");
colDaddr = cursor.getColumnIndex("daddr");
colDPort = cursor.getColumnIndex("dport");
colDName = cursor.getColumnIndex("dname");
colUid = cursor.getColumnIndex("uid");
colData = cursor.getColumnIndex("data");
colAllowed = cursor.getColumnIndex("allowed");
@ -61,6 +65,7 @@ public class LogAdapter extends CursorAdapter {
try {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
dns = SinkholeService.getDns(context);
vpn4 = InetAddress.getByName(prefs.getString("vpn4", "10.1.10.1"));
vpn6 = InetAddress.getByName(prefs.getString("vpn6", "fd00:1:fd00:1:fd00:1:fd00:1"));
} catch (UnknownHostException ex) {
@ -84,6 +89,7 @@ public class LogAdapter extends CursorAdapter {
int sport = (cursor.isNull(colSPort) ? -1 : cursor.getInt(colSPort));
String daddr = cursor.getString(colDaddr);
int dport = (cursor.isNull(colDPort) ? -1 : cursor.getInt(colDPort));
String dname = (cursor.isNull(colDName) ? null : cursor.getString(colDName));
int uid = (cursor.isNull(colUid) ? -1 : cursor.getInt(colUid));
String data = cursor.getString(colData);
int allowed = (cursor.isNull(colAllowed) ? -1 : cursor.getInt(colAllowed));
@ -94,7 +100,7 @@ public class LogAdapter extends CursorAdapter {
TextView tvTime = (TextView) view.findViewById(R.id.tvTime);
TextView tvProtocol = (TextView) view.findViewById(R.id.tvProtocol);
TextView tvFlags = (TextView) view.findViewById(R.id.tvFlags);
final TextView tvSAddr = (TextView) view.findViewById(R.id.tvSAddr);
TextView tvSAddr = (TextView) view.findViewById(R.id.tvSAddr);
TextView tvSPort = (TextView) view.findViewById(R.id.tvSPort);
final TextView tvDaddr = (TextView) view.findViewById(R.id.tvDAddr);
TextView tvDPort = (TextView) view.findViewById(R.id.tvDPort);
@ -171,12 +177,26 @@ public class LogAdapter extends CursorAdapter {
tvSAddr.setText(getKnownAddress(saddr));
if (resolve && !isKnownAddress(daddr))
Util.resolveName(daddr, new Util.resolveListener() {
@Override
public void onResolved(String name, boolean resolved) {
tvDaddr.setText(name);
}
});
if (dname == null) {
tvDaddr.setText(daddr);
new AsyncTask<String, Object, String>() {
@Override
protected String doInBackground(String... args) {
try {
return InetAddress.getByName(args[0]).toString();
} catch (UnknownHostException ignored) {
return args[0];
}
}
@Override
protected void onPostExecute(String name) {
tvDaddr.setText(name);
}
}.execute(daddr);
} else
tvDaddr.setText(dname);
else
tvDaddr.setText(getKnownAddress(daddr));
@ -192,7 +212,7 @@ public class LogAdapter extends CursorAdapter {
public boolean isKnownAddress(String addr) {
try {
InetAddress a = InetAddress.getByName(addr);
if (a.equals(vpn4) || a.equals(vpn6))
if (a.equals(dns) || a.equals(vpn4) || a.equals(vpn6))
return true;
} catch (UnknownHostException ignored) {
}
@ -202,6 +222,8 @@ public class LogAdapter extends CursorAdapter {
private String getKnownAddress(String addr) {
try {
InetAddress a = InetAddress.getByName(addr);
if (a.equals(dns))
return "dns";
if (a.equals(vpn4) || a.equals(vpn6))
return "vpn";
} catch (UnknownHostException ignored) {

View File

@ -19,17 +19,29 @@ package eu.faircode.netguard;
Copyright 2015-2016 by Marcel Bokhorst (M66B)
*/
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ResourceRecord {
public long Time;
public String QName;
public String AName;
public String Resource;
public int TTL;
private static DateFormat formater = SimpleDateFormat.getDateTimeInstance();
public ResourceRecord() {
}
@Override
public String toString() {
return "Q " + QName + " A " + AName + " R " + Resource + " TTL " + TTL;
return formater.format(
new Date(Time).getTime()) +
" Q " + QName +
" A " + AName +
" R " + Resource +
" TTL " + TTL +
" " + formater.format(new Date(Time + TTL * 1000L).getTime());
}
}

View File

@ -67,6 +67,7 @@ import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
@ -88,7 +89,7 @@ public class SinkholeService extends VpnService implements SharedPreferences.OnS
private boolean last_filter = false;
private String last_vpn4 = null;
private String last_vpn6 = null;
private String last_dns = null;
private InetAddress last_dns = null;
private boolean phone_state = false;
private Object subscriptionsChangedListener = null;
private ParcelFileDescriptor vpn = null;
@ -273,6 +274,7 @@ public class SinkholeService extends VpnService implements SharedPreferences.OnS
case reload:
reload();
cleanupDNS();
break;
case stop:
@ -350,7 +352,7 @@ public class SinkholeService extends VpnService implements SharedPreferences.OnS
boolean filter = prefs.getBoolean("filter", false);
String vpn4 = prefs.getString("vpn4", "10.1.10.1");
String vpn6 = prefs.getString("vpn6", "fd00:1:fd00:1:fd00:1:fd00:1");
String dns = getDns();
InetAddress dns = getDns(SinkholeService.this);
if (state != State.enforcing) {
if (state != State.none) {
@ -637,10 +639,11 @@ public class SinkholeService extends VpnService implements SharedPreferences.OnS
}
private void log(Packet packet) {
ResourceRecord rr = resolveDNS(packet.daddr);
DatabaseHelper dh = new DatabaseHelper(SinkholeService.this);
dh.insertLog(packet, (last_connected ? last_metered ? 2 : 1 : 0), last_interactive);
dh.insertLog(packet, rr == null ? null : rr.QName, (last_connected ? last_metered ? 2 : 1 : 0), last_interactive);
if (packet.uid > 0)
dh.updateAccess(packet);
dh.updateAccess(packet, rr == null ? null : rr.QName);
dh.close();
}
@ -676,25 +679,30 @@ public class SinkholeService extends VpnService implements SharedPreferences.OnS
}
}
private String getDns() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
String sysDns = Util.getDefaultDNS(SinkholeService.this);
public static InetAddress getDns(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
String sysDns = Util.getDefaultDNS(context);
String vpnDns = prefs.getString("dns", sysDns);
Log.i(TAG, "DNS system=" + sysDns + " VPN=" + vpnDns);
try {
if (TextUtils.isEmpty(vpnDns.trim()))
throw new IllegalArgumentException("dns");
InetAddress.getByName(vpnDns);
Log.i(TAG, "DNS using=" + vpnDns);
return vpnDns;
} catch (Throwable ignored1) {
InetAddress vpn = InetAddress.getByName(vpnDns);
Log.i(TAG, "DNS using=" + vpn);
return vpn;
} catch (UnknownHostException ignored1) {
try {
InetAddress.getByName(sysDns);
Log.i(TAG, "DNS using=" + sysDns);
return sysDns;
} catch (Throwable ignored2) {
Log.i(TAG, "DNS using=8.8.8.8");
return "8.8.8.8";
InetAddress sys = InetAddress.getByName(sysDns);
Log.i(TAG, "DNS using=" + sys);
return sys;
} catch (UnknownHostException ignored2) {
try {
InetAddress def = InetAddress.getByName("8.8.8.8");
Log.i(TAG, "DNS using=" + def);
return def;
} catch (UnknownHostException ignored) {
return null;
}
}
}
}
@ -708,7 +716,7 @@ public class SinkholeService extends VpnService implements SharedPreferences.OnS
last_tethering = tethering;
last_vpn4 = prefs.getString("vpn4", "10.1.10.1");
last_vpn6 = prefs.getString("vpn6", "fd00:1:fd00:1:fd00:1:fd00:1");
last_dns = getDns();
last_dns = getDns(SinkholeService.this);
// Build VPN service
final Builder builder = new Builder();
@ -930,9 +938,37 @@ public class SinkholeService extends VpnService implements SharedPreferences.OnS
}
}
private static HashMap<String, ResourceRecord> mapRR = new HashMap<>();
// Called from native code
private void dnsResolved(ResourceRecord rr) {
Log.i(TAG, rr.toString());
synchronized (mapRR) {
mapRR.put(rr.Resource, rr);
}
}
public static ResourceRecord resolveDNS(String ip) {
synchronized (mapRR) {
if (mapRR.containsKey(ip))
return mapRR.get(ip);
else
return null;
}
}
private void cleanupDNS() {
long now = new Date().getTime();
synchronized (mapRR) {
List<String> ips = new ArrayList<>(mapRR.keySet());
for (String ip : ips) {
ResourceRecord rr = mapRR.get(ip);
if (rr.Time + rr.TTL * 1000L < now &&
rr.Time + 10 * 60 * 1000L < now) {
mapRR.remove(ip);
Log.i(TAG, "Purged " + rr);
}
}
}
}
// Called from native code

View File

@ -74,8 +74,6 @@ public class Util {
private static native String jni_getprop(String name);
private static native String jni_resolve(String ip);
static {
System.loadLibrary("netguard");
}
@ -357,45 +355,6 @@ public class Util {
private static Map<String, String> mapIPHost = new HashMap<String, String>();
public interface resolveListener {
void onResolved(String name, boolean resolved);
}
public static void resolveName(String name, final resolveListener listener) {
// TODO DNS cache timeout
synchronized (mapIPHost) {
if (mapIPHost.containsKey(name))
listener.onResolved(mapIPHost.get(name), true);
else {
listener.onResolved(name, false);
new AsyncTask<String, Object, String>() {
@Override
protected String doInBackground(String... args) {
return jni_resolve(args[0]);
/*
try {
// This requires internet permission
return InetAddress.getByName(args[0]).getHostName();
} catch (UnknownHostException ignored) {
return args[0];
}
*/
}
@Override
protected void onPostExecute(String host) {
synchronized (mapIPHost) {
if (!mapIPHost.containsKey(host))
mapIPHost.put(host, host);
}
listener.onResolved(host, true);
}
}.execute(name);
}
}
}
public static void setTheme(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean dark = prefs.getBoolean("dark_theme", false);

View File

@ -2616,6 +2616,7 @@ void log_packet(const struct arguments *args, jobject jpacket) {
static jmethodID midDnsResolved = NULL;
static jmethodID midInitRR = NULL;
jfieldID fidQTime = NULL;
jfieldID fidQName = NULL;
jfieldID fidAName = NULL;
jfieldID fidResource = NULL;
@ -2641,18 +2642,21 @@ void dns_resolved(const struct arguments *args,
jobject jrr = jniNewObject(args->env, clsRR, midInitRR, rr);
if (fidQName == NULL) {
if (fidQTime == NULL) {
const char *string = "Ljava/lang/String;";
fidQTime = jniGetFieldID(args->env, clsRR, "Time", "J");
fidQName = jniGetFieldID(args->env, clsRR, "QName", string);
fidAName = jniGetFieldID(args->env, clsRR, "AName", string);
fidResource = jniGetFieldID(args->env, clsRR, "Resource", string);
fidTTL = jniGetFieldID(args->env, clsRR, "TTL", "I");
}
jlong jtime = time(NULL) * 1000LL;
jstring jqname = (*args->env)->NewStringUTF(args->env, qname);
jstring janame = (*args->env)->NewStringUTF(args->env, aname);
jstring jresource = (*args->env)->NewStringUTF(args->env, resource);
(*args->env)->SetLongField(args->env, jrr, fidQTime, jtime);
(*args->env)->SetObjectField(args->env, jrr, fidQName, jqname);
(*args->env)->SetObjectField(args->env, jrr, fidAName, janame);
(*args->env)->SetObjectField(args->env, jrr, fidResource, jresource);

View File

@ -25,7 +25,7 @@ These issues are caused by bugs in Android, or in the software provided by the m
<string name="menu_about">About</string>
<string name="menu_live">Live updates</string>
<string name="menu_resolve">Resolve host names</string>
<string name="menu_resolve">Show names</string>
<string name="menu_pcap_enabled">PCAP enabled</string>
<string name="menu_pcap_export">PCAP export</string>
<string name="menu_clear">Clear</string>