How to override DNS in HTTP connections in Java

Solution 1:

If using Apache's HttpClient, you can create a custom DNS resolver to detect the host you'd like to redirect, and then provide a substitute IP address.

Note: Just changing the Host header for HTTPS requests doesn't work. It will throw "javax.net.ssl.SSLPeerUnverifiedException", forcing you to trust bad certificates, stop SNI from working, etc., so really not an option. A custom DnsResolver is the only clean way I've found to get these requests to work with HTTPS in Java.

Example:

/* Custom DNS resolver */
DnsResolver dnsResolver = new SystemDefaultDnsResolver() {
    @Override
    public InetAddress[] resolve(final String host) throws UnknownHostException {
        if (host.equalsIgnoreCase("my.host.com")) {
            /* If we match the host we're trying to talk to, 
               return the IP address we want, not what is in DNS */
            return new InetAddress[] { InetAddress.getByName("127.0.0.1") };
        } else {
            /* Else, resolve it as we would normally */
            return super.resolve(host);
        }
    }
};

/* HttpClientConnectionManager allows us to use custom DnsResolver */
BasicHttpClientConnectionManager connManager = new BasicHttpClientConnectionManager(
    /* We're forced to create a SocketFactory Registry.  Passing null
       doesn't force a default Registry, so we re-invent the wheel. */
    RegistryBuilder.<ConnectionSocketFactory>create()
        .register("http", PlainConnectionSocketFactory.getSocketFactory())
        .register("https", SSLConnectionSocketFactory.getSocketFactory())
        .build(), 
    null, /* Default ConnectionFactory */ 
    null, /* Default SchemePortResolver */ 
    dnsResolver  /* Our DnsResolver */
    );

/* build HttpClient that will use our DnsResolver */
HttpClient httpClient = HttpClientBuilder.create()
        .setConnectionManager(connManager)
        .build();

/* build our request */
HttpGet httpRequest = new HttpGet("https://my.host.com/page?and=stuff"); 

/* Executing our request should now hit 127.0.0.1, regardless of DNS */
HttpResponse httpResponse = httpClient.execute(httpRequest);