Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -251,12 +251,19 @@ public byte[] resolve(@NonNull byte[] dnsQuery, @Nullable Runnable onFailedAttem
long contentLength = responseBody.contentLength();
if (contentLength > MAX_DOH_RESPONSE_BYTES) {
Log.w(TAG, "DoH response too large: " + contentLength + " bytes");
// An oversized body is a wasted attempt like any other unusable
// response. Leaving it uncounted made the query worth a single
// failure however many attempts it burned, so an endpoint
// spraying junk took ten whole queries to trip the breaker
// while every other failure mode took three.
reportFailedAttempt(onFailedAttempt);
continue;
}

byte[] dnsResponse = response.peekBody(MAX_DOH_RESPONSE_BYTES + 1L).bytes();
if (dnsResponse.length > MAX_DOH_RESPONSE_BYTES) {
Log.w(TAG, "DoH response too large: " + dnsResponse.length + " bytes");
reportFailedAttempt(onFailedAttempt);
continue;
}
if (dnsResponse.length < 12) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,37 @@ public void resolveDoesNotReportCanceledCallAsFailedAttempt() throws Exception {
assertEquals(0, failures.get());
}

/**
* An oversized body is a wasted attempt and must be reported, so that an
* endpoint spraying junk trips the circuit breaker at the same rate as any
* other failing one — see issue #760.
*/
@Test
public void resolveReportsOversizedDnsResponseAsFailedAttempt() {
server.enqueue(dnsResponse(200, responseOfLength(65536)));
server.enqueue(dnsResponse(200, responseOfLength(65536)));
server.enqueue(dnsResponse(200, responseOfLength(65536)));

AtomicInteger failures = new AtomicInteger(0);
assertNull(client().resolve(QUERY, failures::incrementAndGet));

assertEquals(3, failures.get());
assertEquals(3, server.getRequestCount());
}

/** The chunked path (no declared Content-Length) reports the same way. */
@Test
public void resolveReportsOversizedChunkedResponseThenRecovers() {
server.enqueue(chunkedDnsResponse(200, responseOfLength(65536)));
server.enqueue(dnsResponse(200, RESPONSE));

AtomicInteger failures = new AtomicInteger(0);
assertArrayEquals(RESPONSE, client().resolve(QUERY, failures::incrementAndGet));

assertEquals(1, failures.get());
assertEquals(2, server.getRequestCount());
}

@Test
public void resolveRejectsOversizedDnsResponse() {
DnsOverHttpsClient.setScreenOff(true);
Expand Down