diff --git a/app/build.gradle b/app/build.gradle index edecf64d..6009b6bb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,13 +1,13 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 29 - buildToolsVersion "29.0.3" + compileSdkVersion 30 + buildToolsVersion '30.0.2' defaultConfig { applicationId "dnsfilter.android" minSdkVersion 14 - targetSdkVersion 29 + targetSdkVersion 30 } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 518d9e60..077a3686 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="1505100" + android:versionName="1.50.51.0"> @@ -10,8 +10,8 @@ - - + + + android:label="personalDNSfilter"> diff --git a/app/src/main/assets/dnsfilter.conf b/app/src/main/assets/dnsfilter.conf index 5bec836b..7068f797 100644 --- a/app/src/main/assets/dnsfilter.conf +++ b/app/src/main/assets/dnsfilter.conf @@ -66,7 +66,8 @@ routeUnderlyingDNS = false # Uncomment setting below for using this option. # As an example below the list of Google DNS servers for IPV4 and IPV6. # This should avoid hardcoded usage of Google DNS Servers, bypassing system DNS settings. -# routeIPs = 8.8.8.8; 8.8.4.4; 2001:4860:4860::8888; 2001:4860:4860::8844 +# e.g. routeIPs = 8.8.8.8; 8.8.4.4; 2001:4860:4860::8888; 2001:4860:4860::8844 +routeIPs = # AUTOSTART = true|false - used only by Android version. # if true android app is started automatically on device boot completed. @@ -195,10 +196,14 @@ logTextSize = 14 # repeatingLogSuppressTime - time in milliseconds in which repeating logs are suppressed repeatingLogSuppressTime = 2000 +# live log timestamp settings +addLiveLogTimestamp = true +liveLogTimeStampFormat = ''HH:mm:ss'' + # Text and Link for the footer bar footerLink = Want to support us? Feel free to DONATE! # Initial info PopUp showInitialInfoPopUp = true initialInfoPopUpTitle = Consider your rating! -initialInfoPopUpText = Thanks for using our free app personalDNSfilter!\nSometimes we get bad ratings due to misunderstanding.\nTherefore, before rating, please check our FAQ, or ask our Telegram group.\nA bad rating is not motivating to provide this free app further. +initialInfoPopUpText = Thanks for using our free app personalDNSfilter! Sometimes we get bad ratings due to misunderstanding. Therefore, before rating, please check our FAQ, or ask our Telegram group. A bad rating is not motivating to provide this free app further.

In case you do not want to invest time to understand a possible issue, then just uninstall the app and do not provide an uneducated rating! diff --git a/app/src/main/java/dnsfilter/DNSFilterManager.java b/app/src/main/java/dnsfilter/DNSFilterManager.java index a55e8577..5a2a8318 100644 --- a/app/src/main/java/dnsfilter/DNSFilterManager.java +++ b/app/src/main/java/dnsfilter/DNSFilterManager.java @@ -64,7 +64,7 @@ of the License, or (at your option) any later version. public class DNSFilterManager extends ConfigurationAccess { - public static final String VERSION = "1505001"; + public static final String VERSION = "1505100"; private static DNSFilterManager INSTANCE = new DNSFilterManager(); @@ -330,7 +330,7 @@ private byte[] mergeAndPersistConfig(byte[] currentConfigBytes) throws IOExcepti if (ln.startsWith(currentKeys[i] + " =")) { if (currentKeys[i].equals("filterActive") && filterDisabledV15045) ln = "filterActive = false"; - else + else if (!useDefaultConfig(currentKeys[i])) ln = currentKeys[i] + " = " + currentConfig.getProperty(currentKeys[i], "").replace("\n","\\n"); } } @@ -363,6 +363,10 @@ private byte[] mergeAndPersistConfig(byte[] currentConfigBytes) throws IOExcepti return configBytes; } + private boolean useDefaultConfig(String currentKey) { + return ( currentKey.equals("initialInfoPopUpText") || currentKey.equals("initialInfoPopUpTitle")); + } + private void createDefaultConfiguration() { try { diff --git a/app/src/main/java/dnsfilter/DNSFilterProxy.java b/app/src/main/java/dnsfilter/DNSFilterProxy.java index 327151e3..2d2a4d4d 100644 --- a/app/src/main/java/dnsfilter/DNSFilterProxy.java +++ b/app/src/main/java/dnsfilter/DNSFilterProxy.java @@ -32,6 +32,8 @@ of the License, or (at your option) any later version. import java.net.NetworkInterface; import java.net.SocketException; import java.net.UnknownHostException; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.StringTokenizer; import java.util.Vector; @@ -156,6 +158,13 @@ public void closeLogger() { long repeatingLogSuppressTime = Long.parseLong(DNSFilterManager.getInstance().getConfig().getProperty("repeatingLogSuppressTime", "1000")); myLogger.setSuppressTime(repeatingLogSuppressTime); + boolean liveLogTimestampEnabled = Boolean.parseBoolean(DNSFilterManager.getInstance().getConfig().getProperty("addLiveLogTimestamp", "false")); + myLogger.setTimestampFormat(null); + if (liveLogTimestampEnabled) { + String timeStampPattern = DNSFilterManager.getInstance().getConfig().getProperty("liveLogTimeStampFormat", "hh:mm:ss"); + myLogger.setTimestampFormat(timeStampPattern); + } + initDNS(filtermgr); int port = Integer.parseInt(DNSFilterManager.getInstance().getConfig().getProperty("dnsProxyPortNonAndroid","53")); diff --git a/app/src/main/java/dnsfilter/DNSServer.java b/app/src/main/java/dnsfilter/DNSServer.java index ea774c4c..7d97cf05 100644 --- a/app/src/main/java/dnsfilter/DNSServer.java +++ b/app/src/main/java/dnsfilter/DNSServer.java @@ -422,8 +422,9 @@ public String getProtocolName(){ @Override public void resolve(DatagramPacket request, DatagramPacket response) throws IOException { - for (int i = 0; i<2; i++) { //retry once in case of EOFException (pooled connection was already closed) - Connection con = Connection.connect(address, timeout, ssl, null, proxy); + + Connection con = Connection.connect(address, timeout, ssl, null, proxy); + for (int i = 0; i < 2; i++) { //retry once in case of EOFException (pooled connection was already closed) con.setSoTimeout(timeout); try { DataInputStream in = new DataInputStream(con.getInputStream()); @@ -437,9 +438,13 @@ public void resolve(DatagramPacket request, DatagramPacket response) throws IOEx con.release(true); return; } catch (EOFException eof) { - con.release(false); - if (i == 1) - throw new IOException ("EOF when reading from "+this.toString(),eof); // retried already once, now throw exception + if (i == 0) + // pooled connection was closed in between - refresh connection and retry! + con.refreshConnection(); + else { + con.release(false); + throw new IOException("EOF when reading from " + this.toString(), eof); // retried already once, now throw exception + } } catch (IOException eio) { con.release(false); throw eio; @@ -492,8 +497,10 @@ public void resolve(DatagramPacket request, DatagramPacket response) throws IOEx byte[] reqHeader = buildRequestHeader(request.getLength()); + Connection con = Connection.connect(urlHostAddress, timeout, true, null, proxy); + con.setSoTimeout(timeout); + for (int i = 0; i<2; i++) { //retry once in case of EOFException (pooled connection was already closed) - Connection con = Connection.connect(urlHostAddress, timeout, true, null, proxy); try { OutputStream out = con.getOutputStream(); @@ -530,12 +537,16 @@ public void resolve(DatagramPacket request, DatagramPacket response) throws IOEx } readResponseFromStream(new DataInputStream(in), size, response); response.setSocketAddress(address); - con.release(reuse); + con.release(reuse && !responseHeader.getConnectionClose()); return; } catch (EOFException eof) { - con.release(false); - if (i == 1) - throw new IOException ("EOF when reading from "+this.toString(),eof); // retried already once, now throw exception + if (i == 0) + // pooled connection was closed in between - refresh connection and retry! + con.refreshConnection(); + else { + con.release(false); + throw new IOException("EOF when reading from " + this.toString(), eof); // retried already once, now throw exception + } } catch (IOException eio) { con.release(false); throw eio; diff --git a/app/src/main/java/dnsfilter/android/AppSelectorView.java b/app/src/main/java/dnsfilter/android/AppSelectorView.java index 5b32d3bb..f2205191 100644 --- a/app/src/main/java/dnsfilter/android/AppSelectorView.java +++ b/app/src/main/java/dnsfilter/android/AppSelectorView.java @@ -11,7 +11,9 @@ import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.LayoutInflater; +import android.view.View; import android.widget.CheckBox; +import android.widget.EditText; import android.widget.LinearLayout; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -20,7 +22,7 @@ import util.Logger; -public class AppSelectorView extends LinearLayout { +public class AppSelectorView extends LinearLayout implements View.OnClickListener { private PackageManager pm = this.getContext().getPackageManager(); private boolean loaded = false; @@ -30,6 +32,36 @@ public class AppSelectorView extends LinearLayout { private AsyncLoader runningUpdate = null; private ComparableAppInfoWrapper[] wrappers = null; + private View searchView; + private View emptyResult; + + @Override + public void onClick(View v) { + //search clicked + if (!loaded || runningUpdate != null) + return ; + + String searchStr = ((EditText)searchView.findViewById(R.id.searchString)).getText().toString().toLowerCase(); + + ComparableAppInfoWrapper[] allwrappers = wrappers; + int count = 0; + emptyResult.setVisibility(View.GONE); + + for (int i = 0; i < allwrappers.length; i++) { + boolean visible = allwrappers[i].checkBox.getText().toString().toLowerCase().indexOf(searchStr) != -1; + if (visible) + allwrappers[i].checkBox.setVisibility(View.VISIBLE); + else + allwrappers[i].checkBox.setVisibility(View.GONE); + + if (visible) + count++; + } + if (count == 0) + emptyResult.setVisibility(View.VISIBLE); + Logger.getLogger().logLine("Found: "+count+" apps!"); + + } private class ComparableAppInfoWrapper implements Comparable { @@ -86,7 +118,6 @@ public synchronized void run() { return; try { - //set 'Loading apps...' info final TextView infoText = new TextView(getContext()); infoText.setTextColor(Color.BLACK); @@ -111,11 +142,13 @@ public void run() { sortedWrappers.add(new ComparableAppInfoWrapper(packages[i], entry)); } - //remove 'Loading apps...' info + //remove 'Loading apps...' info, add searchView post(new Runnable() { @Override public void run() { removeView(infoText); + addView(searchView); + addView(emptyResult); } }); @@ -166,6 +199,11 @@ public AppSelectorView(Context context, AttributeSet attrs, int defStyleAttr, in public void loadAppList() { + searchView = LayoutInflater.from(getContext()).inflate(R.layout.appselectorsearch, null); + searchView.findViewById(R.id.searchBtn).setOnClickListener(this); + emptyResult = LayoutInflater.from(getContext()).inflate(R.layout.emptyresult, null); + emptyResult.setVisibility(View.GONE); + if (loaded || runningUpdate != null) return; @@ -195,6 +233,8 @@ public void clear() { //clear wrappers = null; + if (searchView!= null) + searchView.setOnClickListener(null); this.removeAllViews(); loaded = false; } diff --git a/app/src/main/java/dnsfilter/android/DNSProxyActivity.java b/app/src/main/java/dnsfilter/android/DNSProxyActivity.java index d9b540ba..d566a94f 100644 --- a/app/src/main/java/dnsfilter/android/DNSProxyActivity.java +++ b/app/src/main/java/dnsfilter/android/DNSProxyActivity.java @@ -1799,6 +1799,12 @@ protected void startup() { try { long repeatingLogSuppressTime = Long.parseLong(getConfig().getProperty("repeatingLogSuppressTime", "1000")); + boolean liveLogTimestampEnabled = Boolean.parseBoolean(getConfig().getProperty("addLiveLogTimestamp", "false")); + myLogger.setTimestampFormat(null); + if (liveLogTimestampEnabled) { + String timeStampPattern = getConfig().getProperty("liveLogTimeStampFormat", "hh:mm:ss"); + myLogger.setTimestampFormat(timeStampPattern); + } myLogger.setSuppressTime(repeatingLogSuppressTime); boolean vpnInAdditionToProxyMode = Boolean.parseBoolean(getConfig().getProperty("vpnInAdditionToProxyMode", "false")); boolean vpnDisabled = !vpnInAdditionToProxyMode && Boolean.parseBoolean(getConfig().getProperty("dnsProxyOnAndroid", "false")); diff --git a/app/src/main/java/util/SuppressRepeatingsLogger.java b/app/src/main/java/util/SuppressRepeatingsLogger.java index 892228ec..01cf9cb0 100644 --- a/app/src/main/java/util/SuppressRepeatingsLogger.java +++ b/app/src/main/java/util/SuppressRepeatingsLogger.java @@ -1,20 +1,35 @@ package util; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.HashMap; import java.util.Map; public class SuppressRepeatingsLogger implements LoggerInterface { LoggerInterface nestedLogger; - private String lastLog = ""; private HashMap lastLogs = new HashMap(); private long timeRepeat = 0; private long lastCleanup = 0; + DateFormat dateFormatter = null; //new SimpleDateFormat("H:mm:ss"); + String lastTS=""; public SuppressRepeatingsLogger(LoggerInterface nestedLogger) { this.nestedLogger = nestedLogger; } + private void addTimeStamp() { + if (dateFormatter == null) + return; + String ts = dateFormatter.format(new Date()); + if ((!ts.equals(lastTS))) { + nestedLogger.logLine(dateFormatter.format(new Date())); + lastTS=ts; + } + } + + public void setNestedLogger(LoggerInterface nestedLogger) { this.nestedLogger = nestedLogger; } @@ -27,6 +42,13 @@ public void setSuppressTime(long suppressTime) { timeRepeat = suppressTime; } + public void setTimestampFormat(String timeStampPattern) { + if (timeStampPattern != null) + dateFormatter = new SimpleDateFormat(timeStampPattern); + else + dateFormatter = null; + } + private boolean repeatingLog(String logStr){ long lastLogged = 0; @@ -58,13 +80,15 @@ private boolean repeatingLog(String logStr){ @Override public void logLine(String txt) { - if (!repeatingLog(txt)) + if (!repeatingLog(txt)) { + addTimeStamp(); nestedLogger.logLine(txt); - lastLog = txt; + } } @Override public void logException(Exception e) { + addTimeStamp(); nestedLogger.logException(e); } @@ -72,7 +96,6 @@ public void logException(Exception e) { public void log(String txt) { if (!repeatingLog(txt)) nestedLogger.log(txt); - lastLog = txt; } @Override diff --git a/app/src/main/java/util/conpool/Connection.java b/app/src/main/java/util/conpool/Connection.java index c5de204a..044a1e6a 100644 --- a/app/src/main/java/util/conpool/Connection.java +++ b/app/src/main/java/util/conpool/Connection.java @@ -23,11 +23,9 @@ of the License, or (at your option) any later version. package util.conpool; - import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -38,6 +36,7 @@ of the License, or (at your option) any later version. import java.net.Socket; import java.net.SocketException; import java.net.SocketTimeoutException; +import java.nio.channels.SocketChannel; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; @@ -47,7 +46,6 @@ of the License, or (at your option) any later version. import javax.net.ssl.SSLSocketFactory; import util.ExecutionEnvironment; -import util.Logger; import util.TimeoutListener; import util.TimeoutTime; import util.TimoutNotificator; @@ -65,6 +63,10 @@ public class Connection implements TimeoutListener { boolean acquired = true; boolean valid = true; boolean ssl = false; + private InetSocketAddress sadr; + private int conTimeout; + private SSLSocketFactory sslSocketFactory; + private Proxy proxy; private static byte[] NO_IP = new byte[]{0,0,0,0}; private static HashMap connPooled = new HashMap(); @@ -194,14 +196,24 @@ private static String[] parseHosts(String line) { } } - + private void initConnection(InetSocketAddress sadr, int conTimeout, boolean ssl, SSLSocketFactory sslSocketFactory, Proxy proxy) throws IOException { + + this.sadr = sadr; + this.conTimeout = conTimeout; + this.ssl = ssl; + this.sslSocketFactory = sslSocketFactory; + this.proxy = proxy; + establishConnection(); + } + + private void establishConnection() throws IOException { if (conTimeout <0) conTimeout= 0; if (proxy == Proxy.NO_PROXY) { - socket = new Socket(); + socket = SocketChannel.open().socket(); ExecutionEnvironment.getEnvironment().protectSocket(socket,0); socket.connect(sadr,conTimeout); } else { @@ -224,7 +236,31 @@ private void initConnection(InetSocketAddress sadr, int conTimeout, boolean ssl, socket.setSoTimeout(0); //reset the read timeout for the SSL handshake } - + public void refreshConnection() throws IOException { + + int sock_timeout = 0; + if (socket != null) + sock_timeout = socket.getSoTimeout(); + // close existing connection + try { + in.invalidate(); + out.invalidate(); + + if (!ssl) { //SSLSocket doesn't support this + socket.shutdownOutput(); + socket.shutdownInput(); + } + socket.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + } + + //establish new connection + establishConnection(); + socket.setSoTimeout(sock_timeout); + + initStreams(); + } public static void setPoolTimeoutSeconds(int secs) { POOLTIMEOUT_SECONDS=secs; diff --git a/app/src/main/java/util/conpool/HttpProxy.java b/app/src/main/java/util/conpool/HttpProxy.java index d970fe5a..d2af5426 100644 --- a/app/src/main/java/util/conpool/HttpProxy.java +++ b/app/src/main/java/util/conpool/HttpProxy.java @@ -27,6 +27,7 @@ of the License, or (at your option) any later version. import java.net.InetSocketAddress; import java.net.Proxy; import java.net.Socket; +import java.nio.channels.SocketChannel; import util.ExecutionEnvironment; import util.http.HttpHeader; @@ -68,7 +69,7 @@ public Socket openTunnel(InetSocketAddress adr, int conTimeout, boolean protect //get address with name of final host but IP of the proxy for SSL hostname check consistency InetSocketAddress conAdr = new InetSocketAddress(InetAddress.getByAddress(adr.getHostName(), proxyAdr.getAddress().getAddress()), proxyAdr.getPort()); - Socket proxyCon = new Socket(); + Socket proxyCon = SocketChannel.open().socket(); if (protect) ExecutionEnvironment.getEnvironment().protectSocket(proxyCon,0); diff --git a/app/src/main/java/util/http/HttpHeader.java b/app/src/main/java/util/http/HttpHeader.java index cc2a2c42..5df4a502 100644 --- a/app/src/main/java/util/http/HttpHeader.java +++ b/app/src/main/java/util/http/HttpHeader.java @@ -23,9 +23,9 @@ of the License, or (at your option) any later version. package util.http; import java.io.ByteArrayInputStream; +import java.io.EOFException; import java.io.IOException; import java.io.InputStream; - import java.util.HashMap; import java.util.Iterator; import java.util.Vector; @@ -40,7 +40,7 @@ public class HttpHeader { public static final int REQUEST_HEADER = 1; public static final int RESPONSE_HEADER = 2; - + public final static int HTTP=1; public final static int HTTPS=2; public final static int OTHER=3; @@ -75,6 +75,10 @@ public HttpHeader(InputStream in, int type) throws IOException { this.type = type; _first = Utils.readLineFromStream(in,true); + + if (_first.equals("")) + throw new EOFException("HttpHeader failed to read! No Data!"); + if (type == REQUEST_HEADER) { parseURI(); if (hostEntry != null) @@ -181,13 +185,13 @@ public void setRequest(String request) throws IOException { public String getResponseMessage() { if (type != RESPONSE_HEADER) - throw new IllegalStateException(this + " is not a ResponseHeader!"); + throw new IllegalStateException(this + " is not a response header!"); return _first; } public int getResponseCode() { if (type != RESPONSE_HEADER) - throw new IllegalStateException(this + " is not a ResponseHeader!"); + throw new IllegalStateException(this + " is not a response header!"); return responsecode; } @@ -283,7 +287,7 @@ public long getContentLength() { public boolean getConnectionClose() { String value = getValue("Connection"); if (value == null) - return false; + return true; else if (value.equalsIgnoreCase("close")) return true; else diff --git a/app/src/main/res/drawable-night/noresult.xml b/app/src/main/res/drawable-night/noresult.xml new file mode 100644 index 00000000..7264471e --- /dev/null +++ b/app/src/main/res/drawable-night/noresult.xml @@ -0,0 +1,18 @@ + + + + diff --git a/app/src/main/res/drawable/noresult.xml b/app/src/main/res/drawable/noresult.xml new file mode 100644 index 00000000..3cd46945 --- /dev/null +++ b/app/src/main/res/drawable/noresult.xml @@ -0,0 +1,18 @@ + + + + diff --git a/app/src/main/res/drawable/search_icon.xml b/app/src/main/res/drawable/search_icon.xml new file mode 100644 index 00000000..1dda34f1 --- /dev/null +++ b/app/src/main/res/drawable/search_icon.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/layout-night/appselectorcheckbox.xml b/app/src/main/res/layout-night/appselectorcheckbox.xml index 3555ea54..b1cd406f 100644 --- a/app/src/main/res/layout-night/appselectorcheckbox.xml +++ b/app/src/main/res/layout-night/appselectorcheckbox.xml @@ -8,6 +8,7 @@ android:layout_marginTop="0dp" android:button="@drawable/custom_checkbox" android:checked="false" - android:textColor="#FFFFFF" > + android:textColor="#FFFFFF" + android:textSize="14dp"> diff --git a/app/src/main/res/layout-night/appselectorsearch.xml b/app/src/main/res/layout-night/appselectorsearch.xml new file mode 100644 index 00000000..0e2a550c --- /dev/null +++ b/app/src/main/res/layout-night/appselectorsearch.xml @@ -0,0 +1,29 @@ + + + + + +