Skip to content

Instantly share code, notes, and snippets.

@philipwhiuk
Created January 8, 2018 00:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save philipwhiuk/bbf761b1090de72e83a3064f16a22b38 to your computer and use it in GitHub Desktop.
Save philipwhiuk/bbf761b1090de72e83a3064f16a22b38 to your computer and use it in GitHub Desktop.
Socket that supports DNS-SEC lookup
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.Set;
import android.util.Log;
import de.measite.minidns.dnssec.DNSSECValidationFailedException;
import de.measite.minidns.hla.DnssecResolverApi;
import de.measite.minidns.hla.ResolverResult;
import de.measite.minidns.record.A;
import de.measite.minidns.record.AAAA;
import de.measite.minidns.record.Data;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
public class Socket extends java.net.Socket {
private static final int READ_TIMEOUT = 10000;
private static final int CONNECT_TIMEOUT = 30000;
private final String hostname;
public Socket(String hostname, int port, boolean dnssecRequired, DNSCache dnsCache) throws IOException {
super();
this.hostname = hostname;
byte[] address = dnsCache.getAddress(hostname);
if (address != null) {
connect(new InetSocketAddress(InetAddress.getByAddress(address), port), CONNECT_TIMEOUT);
} else if (dnssecRequired) {
createDnsSecSocket(hostname, port);
} else {
//Just use the normal resolver which makes use of the Android cache
connect(new InetSocketAddress(InetAddress.getByName(hostname), port), CONNECT_TIMEOUT);
}
setSoTimeout(READ_TIMEOUT);
dnsCache.storeAddress(hostname, getInetAddress().getAddress());
}
/**
* Create a socket to a host using a specific IP address
*/
public Socket(String hostname, byte[] address, int port) throws IOException {
super();
this.hostname = hostname;
connect(new InetSocketAddress(InetAddress.getByAddress(address), port), CONNECT_TIMEOUT);
}
/**
* Use MiniDNS for a DNSSec Aware Resolver
*/
private void createDnsSecSocket(String hostname, int port) throws IOException {
IOException exception = new IOException("No records available");
try {
for (AAAA record : resolveInetAddresses(hostname, AAAA.class)) {
try {
connect(new InetSocketAddress(record.getInetAddress(), port), CONNECT_TIMEOUT);
return;
} catch (IOException lastException) {
exception = lastException;
}
}
} catch (DNSSECValidationFailedException e) {
Log.v("K9", "DNS-SEC IPv6 validation failure - will try IPv4", e);
e.printStackTrace();
}
for (A record : resolveInetAddresses(hostname, A.class)) {
try {
connect(new InetSocketAddress(record.getInetAddress(), port), CONNECT_TIMEOUT);
return;
} catch (IOException lastException) {
exception = lastException;
}
}
throw exception;
}
/**
* Fetch the A records
*/
private <D extends Data> Set<D> resolveInetAddresses(String hostname, Class<D> clazz) throws IOException {
ResolverResult<D> result = DnssecResolverApi.INSTANCE.resolve(hostname, clazz);
if (!result.wasSuccessful()) {
throw new IOException("DNS lookup failed:" + result.getResponseCode());
}
if (!result.isAuthenticData()) {
throw new IOException("DNS-SEC not available for host", result.getDnssecResultNotAuthenticException());
}
return result.getAnswers();
}
public SSLSocket upgradeToTLS(int tlsPort) throws KeyManagementException, NoSuchAlgorithmException, IOException {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, null, null);
SSLSocketFactory sf = sslContext.getSocketFactory();
return (SSLSocket) sf.createSocket(this, hostname, tlsPort, true);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment