Implemented DNS over HTTPS (IETF draft)

This commit is contained in:
PeratX 2018-06-21 22:18:11 +08:00
parent 9932116dac
commit 1a8145971d
10 changed files with 472 additions and 162 deletions

View File

@ -0,0 +1,76 @@
package org.itxtech.daedalus.provider;
import android.os.ParcelFileDescriptor;
import android.util.Base64;
import okhttp3.*;
import org.itxtech.daedalus.service.DaedalusVpnService;
import org.minidns.dnsmessage.DnsMessage;
import org.pcap4j.packet.IpPacket;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
/**
* Daedalus Project
*
* @author iTX Technologies
* @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, either version 3 of the License, or
* (at your option) any later version.
*/
public class HttpsIetfProvider extends HttpsProvider {
private static final String HTTPS_SUFFIX = "https://";
private static final OkHttpClient HTTP_CLIENT = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.addInterceptor((chain) -> {
Request original = chain.request();
Request request = original.newBuilder()
.header("Accept", "application/dns-message")
.build();
return chain.proceed(request);
})
.build();
public HttpsIetfProvider(ParcelFileDescriptor descriptor, DaedalusVpnService service) {
super(descriptor, service);
}
@Override
protected void sendRequestToServer(IpPacket parsedPacket, DnsMessage message, String uri) {
whqList.add(new WaitingHttpsRequest(parsedPacket) {
@Override
public void doRequest() {
final byte[] rawRequest = message.toArray();
Request request = new Request.Builder()
.url(HttpUrl.parse(HTTPS_SUFFIX + uri).newBuilder()
.addQueryParameter("dns", Base64.encodeToString(rawRequest, Base64.DEFAULT))
.build())
.get()
.build();
HTTP_CLIENT.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
result = rawRequest;
completed = true;
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
result = response.body().bytes();
completed = true;
}
}
});
}
});
}
}

View File

@ -0,0 +1,190 @@
package org.itxtech.daedalus.provider;
import android.annotation.TargetApi;
import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.support.annotation.NonNull;
import android.system.Os;
import android.system.OsConstants;
import android.system.StructPollfd;
import android.util.Log;
import org.itxtech.daedalus.Daedalus;
import org.itxtech.daedalus.service.DaedalusVpnService;
import org.itxtech.daedalus.util.Logger;
import org.minidns.dnsmessage.DnsMessage;
import org.pcap4j.packet.IpPacket;
import org.pcap4j.packet.IpSelector;
import org.pcap4j.packet.UdpPacket;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.util.Iterator;
import java.util.LinkedList;
/**
* Daedalus Project
*
* @author iTX Technologies
* @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, either version 3 of the License, or
* (at your option) any later version.
*/
abstract public class HttpsProvider extends Provider {
private static final String TAG = "HttpsProvider";
protected final WhqList whqList = new WhqList();
public HttpsProvider(ParcelFileDescriptor descriptor, DaedalusVpnService service) {
super(descriptor, service);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void process() {
try {
FileDescriptor[] pipes = Os.pipe();
mInterruptFd = pipes[0];
mBlockFd = pipes[1];
FileInputStream inputStream = new FileInputStream(descriptor.getFileDescriptor());
FileOutputStream outputStream = new FileOutputStream(descriptor.getFileDescriptor());
byte[] packet = new byte[32767];
while (running) {
StructPollfd deviceFd = new StructPollfd();
deviceFd.fd = inputStream.getFD();
deviceFd.events = (short) OsConstants.POLLIN;
StructPollfd blockFd = new StructPollfd();
blockFd.fd = mBlockFd;
blockFd.events = (short) (OsConstants.POLLHUP | OsConstants.POLLERR);
if (!deviceWrites.isEmpty())
deviceFd.events |= (short) OsConstants.POLLOUT;
StructPollfd[] polls = new StructPollfd[2];
polls[0] = deviceFd;
polls[1] = blockFd;
Os.poll(polls, -1);
if (blockFd.revents != 0) {
Log.i(TAG, "Told to stop VPN");
running = false;
return;
}
boolean writeToDevice = false;
Iterator<WaitingHttpsRequest> iterator = whqList.iterator();
while (iterator.hasNext()) {
WaitingHttpsRequest request = iterator.next();
if (request.completed) {
writeToDevice = true;
handleDnsResponse(request.packet, request.result);
iterator.remove();
}
}
if (writeToDevice) {
Log.d(TAG, "Write to device");
writeToDevice(outputStream);
}
if ((deviceFd.revents & OsConstants.POLLIN) != 0) {
Log.d(TAG, "Read from device");
readPacketFromDevice(inputStream, packet);
}
service.providerLoopCallback();
}
} catch (Exception e) {
Logger.logException(e);
}
}
@Override
protected void handleDnsRequest(byte[] packetData) {
IpPacket parsedPacket;
try {
parsedPacket = (IpPacket) IpSelector.newPacket(packetData, 0, packetData.length);
} catch (Exception e) {
return;
}
if (!(parsedPacket.getPayload() instanceof UdpPacket)) {
return;
}
InetAddress destAddr = parsedPacket.getHeader().getDstAddr();
if (destAddr == null)
return;
String uri;
try {
uri = service.dnsServers.get(destAddr.getHostAddress());//https uri
} catch (Exception e) {
Logger.logException(e);
return;
}
UdpPacket parsedUdp = (UdpPacket) parsedPacket.getPayload();
if (parsedUdp.getPayload() == null) {
return;
}
byte[] dnsRawData = (parsedUdp).getPayload().getRawData();
DnsMessage dnsMsg;
try {
dnsMsg = new DnsMessage(dnsRawData);
if (Daedalus.getPrefs().getBoolean("settings_debug_output", false)) {
Logger.debug("DnsRequest: " + dnsMsg.toString());
}
} catch (IOException e) {
return;
}
if (dnsMsg.getQuestion() == null) {
Log.i(TAG, "handleDnsRequest: Discarding DNS packet with no query " + dnsMsg);
return;
}
if (!resolve(parsedPacket, dnsMsg) && uri != null) {
sendRequestToServer(parsedPacket, dnsMsg, uri);
}
}
protected abstract void sendRequestToServer(IpPacket parsedPacket, DnsMessage message, String uri);
//uri example: 1.1.1.1:1234/dnsQuery. The specified provider will add https:// and parameters
public abstract static class WaitingHttpsRequest {
public boolean completed = false;
public byte[] result;
public final IpPacket packet;
public WaitingHttpsRequest(IpPacket packet) {
this.packet = packet;
}
public abstract void doRequest();
}
/**
* Queue of WaitingOnSocketPacket, bound on time and space.
*/
public static class WhqList implements Iterable<WaitingHttpsRequest> {
private final LinkedList<WaitingHttpsRequest> list = new LinkedList<>();
public void add(WaitingHttpsRequest request) {
list.add(request);
request.doRequest();
}
@NonNull
public Iterator<WaitingHttpsRequest> iterator() {
return list.iterator();
}
int size() {
return list.size();
}
}
}

View File

@ -1,7 +1,28 @@
package org.itxtech.daedalus.provider;
import android.annotation.TargetApi;
import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.system.Os;
import org.itxtech.daedalus.Daedalus;
import org.itxtech.daedalus.service.DaedalusVpnService;
import org.itxtech.daedalus.util.Logger;
import org.itxtech.daedalus.util.RuleResolver;
import org.minidns.dnsmessage.DnsMessage;
import org.minidns.record.A;
import org.minidns.record.AAAA;
import org.minidns.record.Record;
import org.pcap4j.packet.*;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
/**
* Daedalus Project
@ -15,10 +36,14 @@ import org.itxtech.daedalus.service.DaedalusVpnService;
* (at your option) any later version.
*/
public abstract class Provider {
ParcelFileDescriptor descriptor;
DaedalusVpnService service;
boolean running = false;
static long dnsQueryTimes = 0;
protected ParcelFileDescriptor descriptor;
protected DaedalusVpnService service;
protected boolean running = false;
protected static long dnsQueryTimes = 0;
protected FileDescriptor mBlockFd = null;
protected FileDescriptor mInterruptFd = null;
protected final Queue<byte[]> deviceWrites = new LinkedList<>();
Provider(ParcelFileDescriptor descriptor, DaedalusVpnService service) {
this.descriptor = descriptor;
@ -40,5 +65,128 @@ public abstract class Provider {
running = false;
}
public abstract void stop();
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void stop() {
try {
if (mInterruptFd != null) {
Os.close(mInterruptFd);
}
if (mBlockFd != null) {
Os.close(mBlockFd);
}
if (this.descriptor != null) {
this.descriptor.close();
this.descriptor = null;
}
} catch (Exception ignored) {
}
}
protected void queueDeviceWrite(IpPacket ipOutPacket) {
dnsQueryTimes++;
deviceWrites.add(ipOutPacket.getRawData());
}
public boolean resolve(IpPacket parsedPacket, DnsMessage dnsMsg) {
String dnsQueryName = dnsMsg.getQuestion().name.toString();
try {
String response = RuleResolver.resolve(dnsQueryName, dnsMsg.getQuestion().type);
if (response != null && dnsMsg.getQuestion().type == Record.TYPE.A) {
Logger.info("Provider: Resolved " + dnsQueryName + " Local resolver response: " + response);
DnsMessage.Builder builder = dnsMsg.asBuilder()
.setQrFlag(true)
.addAnswer(new Record<>(dnsQueryName, Record.TYPE.A, 1, 64,
new A(Inet4Address.getByName(response).getAddress())));
handleDnsResponse(parsedPacket, builder.build().toArray());
return true;
} else if (response != null && dnsMsg.getQuestion().type == Record.TYPE.AAAA) {
Logger.info("Provider: Resolved " + dnsQueryName + " Local resolver response: " + response);
DnsMessage.Builder builder = dnsMsg.asBuilder()
.setQrFlag(true)
.addAnswer(new Record<>(dnsQueryName, Record.TYPE.AAAA, 1, 64,
new AAAA(Inet6Address.getByName(response).getAddress())));
handleDnsResponse(parsedPacket, builder.build().toArray());
return true;
}
} catch (Exception e) {
Logger.logException(e);
}
return false;
}
/**
* Handles a responsePayload from an upstream DNS server
*
* @param requestPacket The original request packet
* @param responsePayload The payload of the response
*/
void handleDnsResponse(IpPacket requestPacket, byte[] responsePayload) {
if (Daedalus.getPrefs().getBoolean("settings_debug_output", false)) {
try {
Logger.debug("DnsResponse: " + new DnsMessage(responsePayload).toString());
} catch (IOException e) {
Logger.logException(e);
}
}
UdpPacket udpOutPacket = (UdpPacket) requestPacket.getPayload();
UdpPacket.Builder payLoadBuilder = new UdpPacket.Builder(udpOutPacket)
.srcPort(udpOutPacket.getHeader().getDstPort())
.dstPort(udpOutPacket.getHeader().getSrcPort())
.srcAddr(requestPacket.getHeader().getDstAddr())
.dstAddr(requestPacket.getHeader().getSrcAddr())
.correctChecksumAtBuild(true)
.correctLengthAtBuild(true)
.payloadBuilder(
new UnknownPacket.Builder()
.rawData(responsePayload)
);
IpPacket ipOutPacket;
if (requestPacket instanceof IpV4Packet) {
ipOutPacket = new IpV4Packet.Builder((IpV4Packet) requestPacket)
.srcAddr((Inet4Address) requestPacket.getHeader().getDstAddr())
.dstAddr((Inet4Address) requestPacket.getHeader().getSrcAddr())
.correctChecksumAtBuild(true)
.correctLengthAtBuild(true)
.payloadBuilder(payLoadBuilder)
.build();
} else {
ipOutPacket = new IpV6Packet.Builder((IpV6Packet) requestPacket)
.srcAddr((Inet6Address) requestPacket.getHeader().getDstAddr())
.dstAddr((Inet6Address) requestPacket.getHeader().getSrcAddr())
.correctLengthAtBuild(true)
.payloadBuilder(payLoadBuilder)
.build();
}
queueDeviceWrite(ipOutPacket);
}
protected void writeToDevice(FileOutputStream outFd) throws DaedalusVpnService.VpnNetworkException {
try {
outFd.write(deviceWrites.poll());
} catch (IOException e) {
throw new DaedalusVpnService.VpnNetworkException("Outgoing VPN output stream closed");
}
}
protected void readPacketFromDevice(FileInputStream inputStream, byte[] packet) throws DaedalusVpnService.VpnNetworkException {
// Read the outgoing packet from the input stream.
int length;
try {
length = inputStream.read(packet);
} catch (IOException e) {
throw new DaedalusVpnService.VpnNetworkException("Cannot read from device", e);
}
if (length == 0) {
return;
}
final byte[] readPacket = Arrays.copyOfRange(packet, 0, length);
handleDnsRequest(readPacket);
}
protected abstract void handleDnsRequest(byte[] packetData) throws DaedalusVpnService.VpnNetworkException;
}

View File

@ -5,21 +5,34 @@ import org.itxtech.daedalus.Daedalus;
import org.itxtech.daedalus.service.DaedalusVpnService;
/**
* @author PeratX
* Daedalus Project
*
* @author iTX Technologies
* @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, either version 3 of the License, or
* (at your option) any later version.
*/
public class ProviderPicker {
public static final int DNS_QUERY_METHOD_UDP = 0;
public static final int DNS_QUERY_METHOD_TCP = 1;
public static final int DNS_QUERY_METHOD_HTTPS = 2;
public static final int DNS_QUERY_METHOD_TLS = 2;
public static final int DNS_QUERY_METHOD_HTTPS_IETF = 3;
public static final int DNS_QUERY_METHOD_HTTPS_JSON = 4;
public static Provider getProvider(ParcelFileDescriptor descriptor, DaedalusVpnService service) {
switch (Daedalus.getPrefs().getInt("settings_dns_query_method", DNS_QUERY_METHOD_UDP)) {
switch (Integer.valueOf(Daedalus.getPrefs().getString("settings_dns_query_method", "0"))) {
case DNS_QUERY_METHOD_UDP:
return new UdpProvider(descriptor, service);
case DNS_QUERY_METHOD_TCP:
return new TcpProvider(descriptor, service);
case DNS_QUERY_METHOD_HTTPS:
//TODO
case DNS_QUERY_METHOD_HTTPS_IETF:
return new HttpsIetfProvider(descriptor, service);
case DNS_QUERY_METHOD_HTTPS_JSON:
break;
case DNS_QUERY_METHOD_TLS:
break;
}
return new UdpProvider(descriptor, service);

View File

@ -47,7 +47,6 @@ public class TcpProvider extends UdpProvider {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void process() {
try {
Log.d(TAG, "Starting advanced DNS proxy.");
FileDescriptor[] pipes = Os.pipe();
mInterruptFd = pipes[0];
mBlockFd = pipes[1];

View File

@ -11,23 +11,21 @@ import android.util.Log;
import org.itxtech.daedalus.Daedalus;
import org.itxtech.daedalus.service.DaedalusVpnService;
import org.itxtech.daedalus.util.Logger;
import org.itxtech.daedalus.util.RuleResolver;
import org.itxtech.daedalus.util.server.DNSServerHelper;
import org.minidns.dnsmessage.DnsMessage;
import org.minidns.record.A;
import org.minidns.record.AAAA;
import org.minidns.record.Record;
import org.pcap4j.packet.*;
import org.pcap4j.packet.IpPacket;
import org.pcap4j.packet.IpSelector;
import org.pcap4j.packet.UdpPacket;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.*;
import java.util.Arrays;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
/**
* Daedalus Project
@ -44,40 +42,14 @@ public class UdpProvider extends Provider {
private static final String TAG = "UdpProvider";
private final WospList dnsIn = new WospList();
FileDescriptor mBlockFd = null;
FileDescriptor mInterruptFd = null;
final Queue<byte[]> deviceWrites = new LinkedList<>();
public UdpProvider(ParcelFileDescriptor descriptor, DaedalusVpnService service) {
super(descriptor, service);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void stop() {
try {
if (mInterruptFd != null) {
Os.close(mInterruptFd);
}
if (mBlockFd != null) {
Os.close(mBlockFd);
}
if (this.descriptor != null) {
this.descriptor.close();
this.descriptor = null;
}
} catch (Exception ignored) {
}
}
private void queueDeviceWrite(IpPacket ipOutPacket) {
dnsQueryTimes++;
deviceWrites.add(ipOutPacket.getRawData());
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void process() {
try {
Log.d(TAG, "Starting advanced DNS proxy.");
FileDescriptor[] pipes = Os.pipe();
mInterruptFd = pipes[0];
mBlockFd = pipes[1];
@ -149,35 +121,6 @@ public class UdpProvider extends Provider {
}
}
void writeToDevice(FileOutputStream outFd) throws DaedalusVpnService.VpnNetworkException {
try {
outFd.write(deviceWrites.poll());
} catch (IOException e) {
throw new DaedalusVpnService.VpnNetworkException("Outgoing VPN output stream closed");
}
}
void readPacketFromDevice(FileInputStream inputStream, byte[] packet) throws DaedalusVpnService.VpnNetworkException {
// Read the outgoing packet from the input stream.
int length;
try {
length = inputStream.read(packet);
} catch (IOException e) {
throw new DaedalusVpnService.VpnNetworkException("Cannot read from device", e);
}
if (length == 0) {
Log.w(TAG, "Got empty packet!");
return;
}
final byte[] readPacket = Arrays.copyOfRange(packet, 0, length);
handleDnsRequest(readPacket);
}
void forwardPacket(DatagramPacket outPacket, IpPacket parsedPacket) throws DaedalusVpnService.VpnNetworkException {
DatagramSocket dnsSocket;
try {
@ -210,64 +153,14 @@ public class UdpProvider extends Provider {
}
}
/**
* Handles a responsePayload from an upstream DNS server
*
* @param requestPacket The original request packet
* @param responsePayload The payload of the response
*/
void handleDnsResponse(IpPacket requestPacket, byte[] responsePayload) {
if (Daedalus.getPrefs().getBoolean("settings_debug_output", false)) {
try {
Logger.debug(new DnsMessage(responsePayload).toString());
} catch (IOException e) {
Logger.logException(e);
}
}
UdpPacket udpOutPacket = (UdpPacket) requestPacket.getPayload();
UdpPacket.Builder payLoadBuilder = new UdpPacket.Builder(udpOutPacket)
.srcPort(udpOutPacket.getHeader().getDstPort())
.dstPort(udpOutPacket.getHeader().getSrcPort())
.srcAddr(requestPacket.getHeader().getDstAddr())
.dstAddr(requestPacket.getHeader().getSrcAddr())
.correctChecksumAtBuild(true)
.correctLengthAtBuild(true)
.payloadBuilder(
new UnknownPacket.Builder()
.rawData(responsePayload)
);
IpPacket ipOutPacket;
if (requestPacket instanceof IpV4Packet) {
ipOutPacket = new IpV4Packet.Builder((IpV4Packet) requestPacket)
.srcAddr((Inet4Address) requestPacket.getHeader().getDstAddr())
.dstAddr((Inet4Address) requestPacket.getHeader().getSrcAddr())
.correctChecksumAtBuild(true)
.correctLengthAtBuild(true)
.payloadBuilder(payLoadBuilder)
.build();
} else {
ipOutPacket = new IpV6Packet.Builder((IpV6Packet) requestPacket)
.srcAddr((Inet6Address) requestPacket.getHeader().getDstAddr())
.dstAddr((Inet6Address) requestPacket.getHeader().getSrcAddr())
.correctLengthAtBuild(true)
.payloadBuilder(payLoadBuilder)
.build();
}
queueDeviceWrite(ipOutPacket);
}
/**
* Handles a DNS request, by either blocking it or forwarding it to the remote location.
*
* @param packetData The packet data to read
* @throws DaedalusVpnService.VpnNetworkException If some network error occurred
*/
private void handleDnsRequest(byte[] packetData) throws DaedalusVpnService.VpnNetworkException {
@Override
protected void handleDnsRequest(byte[] packetData) throws DaedalusVpnService.VpnNetworkException {
IpPacket parsedPacket;
try {
@ -312,7 +205,7 @@ public class UdpProvider extends Provider {
try {
dnsMsg = new DnsMessage(dnsRawData);
if (Daedalus.getPrefs().getBoolean("settings_debug_output", false)) {
Logger.debug(dnsMsg.toString());
Logger.debug("DnsRequest: " + dnsMsg.toString());
}
} catch (IOException e) {
Log.i(TAG, "handleDnsRequest: Discarding non-DNS or invalid packet", e);
@ -322,32 +215,11 @@ public class UdpProvider extends Provider {
Log.i(TAG, "handleDnsRequest: Discarding DNS packet with no query " + dnsMsg);
return;
}
String dnsQueryName = dnsMsg.getQuestion().name.toString();
try {
String response = RuleResolver.resolve(dnsQueryName, dnsMsg.getQuestion().type);
if (response != null && dnsMsg.getQuestion().type == Record.TYPE.A) {
Logger.info("Provider: Resolved " + dnsQueryName + " Local resolver response: " + response);
DnsMessage.Builder builder = dnsMsg.asBuilder()
.setQrFlag(true)
.addAnswer(new Record<>(dnsQueryName, Record.TYPE.A, 1, 64,
new A(Inet4Address.getByName(response).getAddress())));
handleDnsResponse(parsedPacket, builder.build().toArray());
} else if (response != null && dnsMsg.getQuestion().type == Record.TYPE.AAAA) {
Logger.info("Provider: Resolved " + dnsQueryName + " Local resolver response: " + response);
DnsMessage.Builder builder = dnsMsg.asBuilder()
.setQrFlag(true)
.addAnswer(new Record<>(dnsQueryName, Record.TYPE.AAAA, 1, 64,
new AAAA(Inet6Address.getByName(response).getAddress())));
handleDnsResponse(parsedPacket, builder.build().toArray());
} else {
Logger.info("Provider: Resolving " + dnsQueryName + " Type: " + dnsMsg.getQuestion().type.name() + " Sending to " + destAddr);
DatagramPacket outPacket = new DatagramPacket(dnsRawData, 0, dnsRawData.length, destAddr,
DNSServerHelper.getPortOrDefault(destAddr, parsedUdp.getHeader().getDstPort().valueAsInt()));
forwardPacket(outPacket, parsedPacket);
}
} catch (Exception e) {
Logger.logException(e);
if (!resolve(parsedPacket, dnsMsg)) {
DatagramPacket outPacket = new DatagramPacket(dnsRawData, 0, dnsRawData.length, destAddr,
DNSServerHelper.getPortOrDefault(destAddr, parsedUdp.getHeader().getDstPort().valueAsInt()));
forwardPacket(outPacket, parsedPacket);
}
}

View File

@ -17,8 +17,6 @@ import org.itxtech.daedalus.R;
import org.itxtech.daedalus.activity.MainActivity;
import org.itxtech.daedalus.provider.Provider;
import org.itxtech.daedalus.provider.ProviderPicker;
import org.itxtech.daedalus.provider.TcpProvider;
import org.itxtech.daedalus.provider.UdpProvider;
import org.itxtech.daedalus.receiver.StatusBarBroadcastReceiver;
import org.itxtech.daedalus.util.Logger;
import org.itxtech.daedalus.util.RuleResolver;
@ -191,9 +189,16 @@ public class DaedalusVpnService extends VpnService implements Runnable {
stopThread();
}
private InetAddress addDnsServer(Builder builder, String format, byte[] ipv6Template, InetAddress address) throws UnknownHostException {
private InetAddress addDnsServer(Builder builder, String format, byte[] ipv6Template, String addr) throws UnknownHostException {
int size = dnsServers.size();
size++;
if (addr.contains("/")) {//https uri
String alias = String.format(format, size + 1);
dnsServers.put(alias, addr);
builder.addRoute(alias, 32);
return InetAddress.getByName(alias);
}
InetAddress address = InetAddress.getByName(addr);
if (address instanceof Inet6Address && ipv6Template == null) {
Log.i(TAG, "addDnsServer: Ignoring DNS server " + address);
} else if (address instanceof Inet4Address) {
@ -253,8 +258,8 @@ public class DaedalusVpnService extends VpnService implements Runnable {
InetAddress aliasSecondary;
if (advanced) {
dnsServers = new HashMap<>();
aliasPrimary = addDnsServer(builder, format, ipv6Template, InetAddress.getByName(primaryServer));
aliasSecondary = addDnsServer(builder, format, ipv6Template, InetAddress.getByName(secondaryServer));
aliasPrimary = addDnsServer(builder, format, ipv6Template, primaryServer);
aliasSecondary = addDnsServer(builder, format, ipv6Template, secondaryServer);
} else {
aliasPrimary = InetAddress.getByName(primaryServer);
aliasSecondary = InetAddress.getByName(secondaryServer);

View File

@ -1,14 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer-array name="dns_query_methods">
<string-array name="dns_query_method_values">
<item>0</item>
<item>1</item>
<item>2</item>
</integer-array>
<string-array name="dns_query_method_values">
<item>3</item>
<item>4</item>
</string-array>
<string-array name="dns_query_methods">
<item>@string/settings_dns_udp</item>
<item>@string/settings_dns_tcp</item>
<item>@string/settings_dns_https</item>
<item>@string/settings_dns_tls</item>
<item>@string/settings_dns_https_ietf</item>
<item>@string/settings_dns_https_json</item>
</string-array>
</resources>

View File

@ -66,9 +66,11 @@
<string name="settings_dark_theme">Use Dark Theme</string>
<string name="settings_dns_query_method">DNS Query Method</string>
<string name="settings_dns_https">HTTPS</string>
<string name="settings_dns_tcp">TCP</string>
<string name="settings_dns_udp">UDP</string>
<string name="settings_dns_tls">TLS</string>
<string name="settings_dns_https_ietf">HTTPS (IETF draft)</string>
<string name="settings_dns_https_json">HTTPS (Google JSON)</string>
<string name="settings_rule_name">Rule Name</string>
<string name="settings_rule_type">Rule Type</string>

View File

@ -76,6 +76,7 @@
android:dialogTitle="@string/settings_dns_query_method"
android:entries="@array/dns_query_methods"
android:entryValues="@array/dns_query_method_values"
android:defaultValue="0"
android:enabled="false"/>
<SwitchPreference
android:key="settings_debug_output"