Implemented local hosts resolver

This commit is contained in:
PeratX 2017-04-26 21:51:28 +08:00
parent e6d6d5ee92
commit 1422bcc737
6 changed files with 193 additions and 9 deletions

View File

@ -5,6 +5,8 @@
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
android:name=".Daedalus"

View File

@ -14,6 +14,7 @@ import android.util.Log;
import org.itxtech.daedalus.activity.MainActivity;
import org.itxtech.daedalus.service.DaedalusVpnService;
import org.itxtech.daedalus.util.DnsServer;
import org.itxtech.daedalus.util.HostsResolver;
import java.util.ArrayList;
import java.util.Arrays;
@ -56,12 +57,15 @@ public class Daedalus extends Application {
private static Daedalus instance = null;
private static SharedPreferences prefs;
private static Thread mHostsResolver;
@Override
public void onCreate() {
super.onCreate();
initConfig();
mHostsResolver = new Thread(new HostsResolver());
mHostsResolver.start();
instance = this;
}
@ -81,6 +85,10 @@ public class Daedalus extends Application {
instance = null;
prefs = null;
HostsResolver.shutdown();
mHostsResolver.interrupt();
HostsResolver.clean();
mHostsResolver = null;
}
public Intent getServiceIntent() {

View File

@ -1,11 +1,16 @@
package org.itxtech.daedalus.activity;
import android.Manifest;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.NavigationView;
import android.support.v4.app.ActivityCompat;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
@ -22,6 +27,7 @@ import org.itxtech.daedalus.fragment.AboutFragment;
import org.itxtech.daedalus.fragment.DnsTestFragment;
import org.itxtech.daedalus.fragment.MainFragment;
import org.itxtech.daedalus.fragment.SettingsFragment;
import org.itxtech.daedalus.util.HostsResolver;
/**
* Daedalus Project
@ -49,6 +55,12 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
public static final int FRAGMENT_SETTINGS = 2;
public static final int FRAGMENT_ABOUT = 3;
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
private static MainActivity instance = null;
private MainFragment mMain;
@ -72,6 +84,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
setTheme(R.style.AppTheme_NoActionBar_TransparentStatusBar);
super.onCreate(savedInstanceState);
initHostsResolver();
instance = this;
setContentView(R.layout.activity_main);
@ -104,6 +118,23 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
Log.d(TAG, "onCreate");
}
public void initHostsResolver() {
if (Daedalus.getPrefs().getBoolean("settings_local_host_resolve", false)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
int permission = ActivityCompat.checkSelfPermission(this.getApplicationContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permission != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
}
}
HostsResolver.startLoad(getExternalFilesDir(null).getPath() + "/hosts");
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
@Override
public void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);

View File

@ -11,6 +11,7 @@ import android.view.View;
import android.view.ViewGroup;
import org.itxtech.daedalus.Daedalus;
import org.itxtech.daedalus.R;
import org.itxtech.daedalus.activity.MainActivity;
import org.itxtech.daedalus.util.DnsServer;
/**
@ -69,12 +70,9 @@ public class SettingsFragment extends PreferenceFragment {
});
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
SwitchPreference countQueryTimes = (SwitchPreference) findPreference("settings_count_query_times");
countQueryTimes.setChecked(false);
countQueryTimes.setEnabled(false);
SwitchPreference autoBoot = (SwitchPreference) findPreference("settings_boot");
autoBoot.setChecked(false);
autoBoot.setEnabled(false);
SwitchPreference advanced = (SwitchPreference) findPreference("settings_advanced_switch");
advanced.setEnabled(false);
advanced.setChecked(false);
}
SwitchPreference advanced = (SwitchPreference) findPreference("settings_advanced_switch");
@ -85,6 +83,16 @@ public class SettingsFragment extends PreferenceFragment {
return true;
}
});
SwitchPreference localHosts = (SwitchPreference) findPreference("settings_local_host_resolve");
localHosts.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
MainActivity.getInstance().initHostsResolver();
return true;
}
});
updateAdvancedOptions(advanced.isChecked());
}

View File

@ -17,11 +17,14 @@ import android.system.OsConstants;
import android.system.StructPollfd;
import android.util.Log;
import de.measite.minidns.DNSMessage;
import de.measite.minidns.Record;
import de.measite.minidns.record.A;
import de.measite.minidns.util.InetAddressUtil;
import org.itxtech.daedalus.Daedalus;
import org.itxtech.daedalus.R;
import org.itxtech.daedalus.activity.MainActivity;
import org.itxtech.daedalus.receiver.StatusBarBroadcastReceiver;
import org.itxtech.daedalus.util.HostsResolver;
import org.pcap4j.packet.*;
import org.pcap4j.packet.factory.PacketFactoryPropertiesLoader;
import org.pcap4j.util.PropertiesLoader;
@ -61,6 +64,7 @@ public class DaedalusVpnService extends VpnService implements Runnable {
private static NotificationCompat.Builder notification = null;
private static long dnsQueryTimes = 0;
private static boolean localHostsResolve = false;
private Thread mThread = null;
private ParcelFileDescriptor descriptor;
@ -205,6 +209,7 @@ public class DaedalusVpnService extends VpnService implements Runnable {
boolean advanced = Daedalus.getPrefs().getBoolean("settings_advanced_switch", false);
boolean statisticQuery = Daedalus.getPrefs().getBoolean("settings_count_query_times", false);
localHostsResolve = Daedalus.getPrefs().getBoolean("settings_local_host_resolve", false);
Log.d(TAG, "tun0 add " + format + " pServ " + primaryServer + " sServ " + secondaryServer);
Inet4Address primaryDNSServer = InetAddressUtil.ipv4From(primaryServer);
Inet4Address secondaryDNSServer = InetAddressUtil.ipv4From(secondaryServer);
@ -503,9 +508,30 @@ public class DaedalusVpnService extends VpnService implements Runnable {
return;
}
String dnsQueryName = dnsMsg.getQuestion().name.toString();
Log.i(TAG, "handleDnsRequest: DNS Name " + dnsQueryName + " , sending to " + destAddr);
DatagramPacket outPacket = new DatagramPacket(dnsRawData, 0, dnsRawData.length, destAddr, parsedUdp.getHeader().getDstPort().valueAsInt());
forwardPacket(outPacket, parsedPacket);
try {
if (localHostsResolve && HostsResolver.canResolve(dnsQueryName)) {
String response = HostsResolver.resolve(dnsQueryName);
Log.i(TAG, "handleDnsRequest: DNS Name " + dnsQueryName + " Address " + response + ", using local hosts to resolve.");
DNSMessage.Builder builder = dnsMsg.asBuilder();
int[] ip = new int[4];
byte i = 0;
for (String block : response.split("\\.")) {
ip[i] = Integer.parseInt(block);
i++;
}
Record<A> answer = new Record<>(dnsQueryName, Record.TYPE.getType(A.class), 1, 255, new A(ip[0], ip[1], ip[2], ip[3]));
builder.setQuestions(null);
builder.addAnswer(answer);
handleDnsResponse(parsedPacket, builder.build().toArray());
} else {
Log.i(TAG, "handleDnsRequest: DNS Name " + dnsQueryName + " , sending to " + destAddr);
DatagramPacket outPacket = new DatagramPacket(dnsRawData, 0, dnsRawData.length, destAddr, parsedUdp.getHeader().getDstPort().valueAsInt());
forwardPacket(outPacket, parsedPacket);
}
} catch (Exception e) {
Log.e(TAG, e.toString());
}
}
static class VpnNetworkException extends Exception {

View File

@ -0,0 +1,109 @@
package org.itxtech.daedalus.util;
import android.util.Log;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
/**
* Daedalus Project
*
* @author iTXTech
* @link https://itxtech.org
* <p>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*/
public class HostsResolver implements Runnable {
private static final String TAG = "DHostsResolver";
public static final int STATUS_LOADED = 0;
public static final int STATUS_LOADING = 1;
public static final int STATUS_NOT_LOADED = 2;
public static final int STATUS_PENDING_LOAD = 3;
private static int status = STATUS_NOT_LOADED;
private static String fileName;
private static HashMap<String, String> hosts;
private static boolean shutdown = false;
public HostsResolver() {
status = STATUS_NOT_LOADED;
fileName = "";
shutdown = false;
}
public static void shutdown() {
shutdown = true;
}
public static boolean isLoaded() {
return status == STATUS_LOADED;
}
public static void startLoad(String loadFile) {
Log.d(TAG, "Loading file " + loadFile);
fileName = loadFile;
status = STATUS_PENDING_LOAD;
}
public static void clean() {
hosts = null;
}
public static boolean canResolve(String hostname) {
return hosts.containsKey(hostname);
}
public static String resolve(String hostname) {
return hosts.get(hostname);
}
private void load() {
try {
status = STATUS_LOADING;
File file = new File(fileName);
if (file.exists() && file.canRead()) {
FileInputStream stream = new FileInputStream(file);
BufferedReader dataIO = new BufferedReader(new InputStreamReader(stream));
hosts = new HashMap<>();
String strLine;
String[] data;
while ((strLine = dataIO.readLine()) != null) {
//Log.d(TAG, strLine);
if (!strLine.equals("") && !strLine.startsWith("#")) {
data = strLine.split("\\s+");
hosts.put(data[1], data[0]);
Log.d(TAG, "Putting " + data[0] + " " + data[1]);
}
}
dataIO.close();
stream.close();
status = STATUS_LOADED;
} else {
status = STATUS_NOT_LOADED;
}
} catch (Exception e) {
Log.e(TAG, e.toString());
}
}
@Override
public void run() {
try {
while (!shutdown) {
if (status == STATUS_PENDING_LOAD) {
load();
}
Thread.sleep(100);
}
} catch (Exception e) {
Log.e(TAG, e.toString());
}
}
}