How to override DNS in HTTP connections in Java

Curl has a function for manually specifying which IP address the host should decide. For instance:

curl https://google.com --resolve "google.com:443:173.194.72.113" 

This is especially useful when using HTTPS. If it were just an HTTP request, I could achieve this by specifying the IP address directly and adding the host header. But in HTTPS, which will break the connection, since the host of the SSL certificate will be compared with the IP address, and not with the host header.

My question is: how can I achieve the same thing in Java?

+7
source share
2 answers

If you are using Apache HttpClient , you can create your own DNS resolver to determine the host that you want to redirect, and then specify a replacement IP address.

Note. Simply changing the host header for HTTPS requests does not work. It will throw a "javax.net.ssl.SSLPeerUnverifiedException", forcing you to trust bad certificates, stop SNI, etc., so this is actually not an option. Custom DnsResolver is the only clean way 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); 
+11
source

I don’t have the code at hand, but you can also write your own SSL handler / verifier that can adapt or fail just by ignoring all the security. Using the core JDK network, we had to completely ignore SSL certificates for testing. It should be easy to find examples.

0
source

Source: https://habr.com/ru/post/971166/


All Articles