mirror of
https://github.com/square/okhttp.git
synced 2025-11-05 12:17:34 +08:00
DNS over HTTPS (#3972)
DNS over HTTPS implementation experimental API in 3.11 https://tools.ietf.org/html/draft-ietf-doh-dns-over-https-11
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Square, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package okhttp3.dnsoverhttps;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import okhttp3.Cache;
|
||||
import okhttp3.Dns;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Protocol;
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
import okhttp3.mockwebserver.RecordedRequest;
|
||||
import okio.Buffer;
|
||||
import okio.ByteString;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class DnsOverHttpsTest {
|
||||
@Rule public final MockWebServer server = new MockWebServer();
|
||||
|
||||
private final OkHttpClient bootstrapClient =
|
||||
new OkHttpClient.Builder().protocols(Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1))
|
||||
.build();
|
||||
private final Dns dns = buildLocalhost(bootstrapClient);
|
||||
|
||||
@Before public void setUp() {
|
||||
server.setProtocols(bootstrapClient.protocols());
|
||||
}
|
||||
|
||||
@Test public void getOne() throws Exception {
|
||||
server.enqueue(dnsResponse(
|
||||
"0000818000010003000000000567726170680866616365626f6f6b03636f6d0000010001c00c00050001"
|
||||
+ "00000a6d000603617069c012c0300005000100000cde000c04737461720463313072c012c04200010"
|
||||
+ "0010000003b00049df00112"));
|
||||
|
||||
List<InetAddress> result = dns.lookup("google.com");
|
||||
|
||||
assertEquals(singletonList(address("157.240.1.18")), result);
|
||||
|
||||
RecordedRequest recordedRequest = server.takeRequest();
|
||||
assertEquals("GET", recordedRequest.getMethod());
|
||||
assertEquals("/lookup?ct&dns=AAABAAACAAAAAAAABmdvb2dsZQNjb20AAAEAAQZnb29nbGUDY29t"
|
||||
+ "AAAcAAE", recordedRequest.getPath());
|
||||
}
|
||||
|
||||
@Test public void getIpv6() throws Exception {
|
||||
server.enqueue(dnsResponse(
|
||||
"0000818000010003000000000567726170680866616365626f6f6b03636f6d00001c0001c00c00050001"
|
||||
+ "00000a1b000603617069c012c0300005000100000b1f000c04737461720463313072c012c042001c0"
|
||||
+ "0010000003b00102a032880f0290011faceb00c00000002"));
|
||||
|
||||
List<InetAddress> result = dns.lookup("google.com");
|
||||
|
||||
assertEquals(singletonList(address("2a03:2880:f029:11:face:b00c:0:2")), result);
|
||||
|
||||
RecordedRequest recordedRequest = server.takeRequest();
|
||||
assertEquals("GET", recordedRequest.getMethod());
|
||||
assertEquals("/lookup?ct&dns=AAABAAACAAAAAAAABmdvb2dsZQNjb20AAAEAAQZnb29nbGUDY29t"
|
||||
+ "AAAcAAE", recordedRequest.getPath());
|
||||
}
|
||||
|
||||
@Test public void failure() throws Exception {
|
||||
server.enqueue(dnsResponse(
|
||||
"0000818300010000000100000e7364666c6b686673646c6b6a64660265650000010001c01b00060001"
|
||||
+ "000007070038026e7303746c64c01b0a686f73746d61737465720d6565737469696e7465726e657"
|
||||
+ "4c01b5adb12c100000e10000003840012750000000e10"));
|
||||
|
||||
try {
|
||||
dns.lookup("google.com");
|
||||
fail();
|
||||
} catch (UnknownHostException uhe) {
|
||||
uhe.printStackTrace();
|
||||
assertEquals("google.com: NXDOMAIN", uhe.getMessage());
|
||||
}
|
||||
|
||||
RecordedRequest recordedRequest = server.takeRequest();
|
||||
assertEquals("GET", recordedRequest.getMethod());
|
||||
assertEquals("/lookup?ct&dns=AAABAAACAAAAAAAABmdvb2dsZQNjb20AAAEAAQZnb29nbGUDY29t"
|
||||
+ "AAAcAAE", recordedRequest.getPath());
|
||||
}
|
||||
|
||||
@Test public void failOnExcessiveResponse() {
|
||||
char[] array = new char[128 * 1024 + 2];
|
||||
Arrays.fill(array, '0');
|
||||
server.enqueue(dnsResponse(new String(array)));
|
||||
|
||||
try {
|
||||
dns.lookup("google.com");
|
||||
fail();
|
||||
} catch (IOException ioe) {
|
||||
assertEquals("google.com", ioe.getMessage());
|
||||
Throwable cause = ioe.getCause();
|
||||
assertTrue(cause instanceof IOException);
|
||||
assertEquals("response size exceeds limit (65536 bytes): 65537 bytes", cause.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test public void failOnBadResponse() {
|
||||
server.enqueue(dnsResponse("00"));
|
||||
|
||||
try {
|
||||
dns.lookup("google.com");
|
||||
fail();
|
||||
} catch (IOException ioe) {
|
||||
assertEquals("google.com", ioe.getMessage());
|
||||
Throwable cause = ioe.getCause();
|
||||
assertTrue(cause instanceof RuntimeException);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO GET preferred order - with tests to confirm this
|
||||
// 1. successful fresh cached GET response
|
||||
// 2. unsuccessful (404, 500) fresh cached GET response
|
||||
// 3. successful network response
|
||||
// 4. successful stale cached GET response
|
||||
// 5. unsuccessful response
|
||||
|
||||
// TODO how closely to follow POST rules on caching?
|
||||
|
||||
@Test public void usesCache() throws Exception {
|
||||
Cache cache = new Cache(new File("./target/DnsOverHttpsTest.cache"), 100 * 1024);
|
||||
OkHttpClient cachedClient = bootstrapClient.newBuilder().cache(cache).build();
|
||||
DnsOverHttps cachedDns = buildLocalhost(cachedClient);
|
||||
|
||||
server.enqueue(dnsResponse(
|
||||
"0000818000010003000000000567726170680866616365626f6f6b03636f6d0000010001c00c00050001"
|
||||
+ "00000a6d000603617069c012c0300005000100000cde000c04737461720463313072c012c04200010"
|
||||
+ "0010000003b00049df00112")
|
||||
.setHeader("cache-control", "private, max-age=298"));
|
||||
|
||||
List<InetAddress> result = cachedDns.lookup("google.com");
|
||||
|
||||
assertEquals(singletonList(address("157.240.1.18")), result);
|
||||
|
||||
RecordedRequest recordedRequest = server.takeRequest();
|
||||
assertEquals("GET", recordedRequest.getMethod());
|
||||
assertEquals("/lookup?ct&dns=AAABAAACAAAAAAAABmdvb2dsZQNjb20AAAEAAQZnb29nbGUDY29t"
|
||||
+ "AAAcAAE", recordedRequest.getPath());
|
||||
|
||||
result = cachedDns.lookup("google.com");
|
||||
assertEquals(singletonList(address("157.240.1.18")), result);
|
||||
}
|
||||
|
||||
private MockResponse dnsResponse(String s) {
|
||||
return new MockResponse()
|
||||
.setBody(new Buffer().write(ByteString.decodeHex(s)))
|
||||
.addHeader("content-type", "application/dns-message")
|
||||
.addHeader("content-length", s.length() / 2);
|
||||
}
|
||||
|
||||
private DnsOverHttps buildLocalhost(OkHttpClient bootstrapClient) {
|
||||
HttpUrl url = server.url("/lookup?ct");
|
||||
return new DnsOverHttps.Builder().client(bootstrapClient).resolvePrivateAddresses(true).url(url).build();
|
||||
}
|
||||
|
||||
private static InetAddress address(String host) {
|
||||
try {
|
||||
return InetAddress.getByName(host);
|
||||
} catch (UnknownHostException e) {
|
||||
// impossible for IP addresses
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user