This document describes how to obtain DNS resolution time when using the okhttp library for network requests on Android.
1. Overview
When using the okhttp library for network requests on Android, you can obtain DNS resolution time through EventListener. EventListener provides multiple callback methods to track network requests at various stages, including DNS resolution, connection establishment, data transmission, and more.
On Android, you can use the InetAddress.getAllByName(String host) method to obtain the resolution results from the local DNS. You can compare these results with expected resolution results to determine if there is any hijacking.
2. Methods for obtaining DNS resolution time
The following are steps for using okhttp and EventListener to obtain DNS resolution time:
Step 1: Import the okhttp library
Ensure that you add the build.gradle dependency for okhttp in your file:
implementation 'com.squareup.okhttp3:okhttp:4.9.3'Step 2: Create a custom EventListener
You need to create a custom EventListener class to monitor and record DNS resolution time:
import okhttp3.EventListener;
import okhttp3.Call;
import okhttp3.HttpUrl;
public class DnsTimingEventListener extends EventListener {
private long dnsStartTime;
private long dnsEndTime;
@Override
public void dnsStart(Call call, String domainName) {
super.dnsStart(call, domainName);
dnsStartTime = System.currentTimeMillis();
}
@Override
public void dnsEnd(Call call, String domainName, List<InetAddress> inetAddressList) {
super.dnsEnd(call, domainName, inetAddressList);
dnsEndTime = System.currentTimeMillis();
long dnsDuration = dnsEndTime - dnsStartTime;
System.out.println("DNS resolution time: " + dnsDuration + " milliseconds");
}
}Step 3: Create an OkHttpClient instance and set EventListener.Factory
You can set the custom EventListener factory to the OkHttpClient instance:
import okhttp3.OkHttpClient;
// Create OkHttpClient instance
OkHttpClient client = new OkHttpClient.Builder()
.eventListenerFactory(new EventListener.Factory() {
@Override
public EventListener create(Call call) {
return new DnsTimingEventListener();
}
})
.build();Step 4: Execute network requests
You can use the configured OkHttpClient instance to execute network requests:
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
public void executeNetworkRequest() {
// Create Request object
Request request = new Request.Builder()
// Configure the actual URL address in .url()
.url("https://www.example.com")
.build();
try {
// Execute the request
Response response = client.newCall(request).execute();
// Process the response
if (response.isSuccessful()) {
System.out.println("Request successful");
// You can process response data here
} else {
System.out.println("Request failed, status code: " + response.code());
}
} catch (IOException e) {
e.printStackTrace();
}
}You can use the above method to obtain DNS resolution time and process it accordingly. This helps you understand the time consumption of network requests and can be used for performance optimization and monitoring.
You can implement the OkHttp DNS interface to use the HTTPDNS service. If you implement the OkHttp DNS interface and use HTTPDNS, the resolution time that you obtain from the preceding method is the HTTPDNS resolution time. If you do not implement the OkHttp DNS interface, OkHttp uses the system's default DNS service, InetAddress. In this case, the resolution time that you obtain is the resolution time of the system's DNS service.
You can refer to the following code to implement the DNS interface of OkHttp:
public class OkHttpDns implements Dns {
private static OkHttpDns instance;
private static Object lock = new Object();
private DNSResolver mDNSResolver = DNSResolver.getInstance();
private OkHttpDns() {
}
public static OkHttpDns getInstance() {
if (null == instance) {
synchronized (lock) {
if (instance == null) {
instance = new OkHttpDns();
}
}
}
return instance;
}
@Override
public List<InetAddress> lookup(@NonNull String hostname) throws UnknownHostException {
// Call the API provided by the HTTPDNS Android SDK for domain name resolution
String ip = null;
String[] ipv4Array = mDNSResolver.getIpv4ByHostFromCache(hostname,true);
if (ipv4Array != null && ipv4Array.length > 0) {
ip = ipv4Array[0];
}else {
ip = mDNSResolver.getIPV4ByHost(hostname);
}
if (ip != null) {
// If the IP is not null, use it directly for the network request
Log.d(TAG, "mDnsCache IP: " + ip);
return Arrays.asList(InetAddress.getAllByName(ip));
}
// If null is returned, use the system DNS service to resolve the domain name
return Dns.SYSTEM.lookup(hostname);
}
}3. Obtaining LocalDNS resolution results and performing hijacking detection comparison
You can obtain LocalDNS resolution results using the following code:
public List<String> getIPAddresses(String domain) {
List<String> ipAddresses = new ArrayList<>();
try {
InetAddress[] addresses = InetAddress.getAllByName(domain);
for (InetAddress address : addresses) {
ipAddresses.add(address.getHostAddress());
}
} catch (UnknownHostException e) {
e.printStackTrace();
}
return ipAddresses;
}
You can refer to the following code to compare the obtained LocalDNS resolution results with your expected resolution results to determine if there is any hijacking:
public boolean isDnsHijacked(String domain, List<String> expectedIps) {
List<String> localIps = getIPAddresses(domain);
if (localIps.isEmpty()) {
return false; // Unable to resolve domain name
}
for (String localIp : localIps) {
if (!expectedIps.contains(localIp)) {
return true; // Found mismatched IP, DNS hijacking may exist
}
}
return false; // All resolution results match
}