-
Notifications
You must be signed in to change notification settings - Fork 9.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix DoH error when using ip address as hostname #7073
Conversation
@@ -67,7 +67,11 @@ class DnsOverHttps internal constructor( | |||
} | |||
} | |||
|
|||
return lookupHttps(hostname) | |||
return try { | |||
listOf(InetAddressUtil.forString(hostname)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a test?
We need to avoid an exception in most calls here. Maybe reverse this logic (try hostname first) and in the normal case call lookupHttps, and catch and exception, check if it's a ip address and then return forString.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be better if we try converting by forString first because we can avoid a potential https connection if the hostname is an numerical IP address.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, well avoid the exception then. 99+% of calls will be for hostnames, not IPs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If most of the calls will be hostname, maybe call the lookupHttps first would be better. I will update the code.
import java.net.UnknownHostException | ||
import java.util.* | ||
|
||
object InetAddressUtil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like a lot of supporting code. Are there existing methods that do this, or would a simple regex as a first parse be simpler (\d+.\d+...) or (\d+:...)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I implement the InetAddressUtil kotlin version refer to the com.google.common.net.InetAddresses class. I just concern about that a simple regex may cause some bugs due to the complex format of IPv4/IPv6. A common parse algorithm would be better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's sourced from there, you can just commit it here with a new copyright. In other places we've derived new code from other projects but keep that copyright. Let me look into this tonight, there may be simpler ways to do this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also updated the copyright just same as the raw implement file.
I'll backport this to 4.9.x when we have a fix, but until that happens you might want to have your own Dns wrapper that does this logic yourself. Thanks for the report and PR. |
Thank you for your review:) |
Looking at the logic of InetAddress.getAllByName, would the following be simpler?
getAllByName shortcircuits and avoids Dns if the hostname is an ip address. Using similar logic in isIpAddress. |
Please add some tests to DnsOverHttpsTest. |
…with an IP address hostname
It seems that the getAllByName function also needs some support functions such as textToNumericFormatV4/textToNumericFormatV6 from sun.net.util.IPAddressUtil class, I thought it might be btter to avoid import a class from sun.net package here. |
I just added some tests in DnsOverHttpsText. |
I didn't mean import it, I just meant the method I showed or similar. |
I just removed the complex support class. |
okhttp-dnsoverhttps/src/test/java/okhttp3/dnsoverhttps/DnsOverHttpsTest.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m unconvinced that this is the right layer for this fix. I think it’d be better if we never call Dns.lookup()
when we have an IP address.
} | ||
} | ||
|
||
private fun checkIsIpAddress(hostname: String): Boolean { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should use our existing function, canParseAsIpAddress()
from okhttp3.internal
. We definitely don’t want to functions to convert IP addresses to strings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I missed that there is a existing function.
return true | ||
} | ||
|
||
if (hostname[0].digitToIntOrNull(16) != null || hostname[0] == ':' || hostname[0] == '[') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the hostname is [
, that’s malformed. We do not pass the square braces to Dns
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought that the getAllByName will strip the square braces so I just pass the string to getAllByName directly.
Due to the canParseAsIpAddress will not handle [
, I will strip the square braces and use the existing one to check if the hostname is a IP address.
} else { | ||
hostname | ||
} | ||
if (address.canParseAsIpAddress()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice find, I forgot this was a thing. Can you invert this now?
Also if that function doesn't worry about [], we can probably skip that here also.
See
assertThat(parse("http://[::1]/").host()).isEqualTo("::1"); |
Which confirms the HttpUrl strips the [].
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean that the HttpUrl will handle the square braces before calling dns lookup, so we don't need to check and strips the []
here and just remove the test case for hostname "[::1]"?
At a risk of repeating myself, what happens if we never call any Dns implementation when the input is already an IP address? |
Yep - this makes sense. @TaoZang sorry to give you bad advice, and thanks for you patience. What do you think about make this change to avoid using Dns for IP Addresses. |
Yeah, maybe I just misunderstood your meaning before. Avoid to call DNS lookup in higher layer when the input is already an IP addres seems be make more sense. I will try to change the fix by this way. |
val addresses = if (socketHost.canParseAsIpAddress()) { | ||
listOf(InetAddress.getByName(socketHost)) | ||
} else { | ||
eventListener.dnsStart(call, socketHost) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like a nice benefit of this change. that DNS start event isn't emitted for an IP address.
@@ -53,6 +53,7 @@ class DnsOverHttps internal constructor( | |||
@get:JvmName("resolvePrivateAddresses") val resolvePrivateAddresses: Boolean, | |||
@get:JvmName("resolvePublicAddresses") val resolvePublicAddresses: Boolean | |||
) : Dns { | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Can you revert this line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a test that confirms this behaviour. Something in EventListenerTest like
@Test public void successfulCallEventSequenceForIpAddress() throws IOException {
server.enqueue(new MockResponse()
.setBody("abc"));
String ipAddress = InetAddress.getLocalHost().getHostAddress();
Call call = client.newCall(new Request.Builder()
.url(server.url("/").newBuilder().host(ipAddress).build())
.build());
Response response = call.execute();
assertThat(response.code()).isEqualTo(200);
assertThat(response.body().string()).isEqualTo("abc");
response.body().close();
assertThat(listener.recordedEventTypes()).containsExactly("CallStart",
"ProxySelectStart", "ProxySelectEnd",
"ConnectStart", "ConnectEnd", "ConnectionAcquired", "RequestHeadersStart",
"RequestHeadersEnd", "ResponseHeadersStart", "ResponseHeadersEnd", "ResponseBodyStart",
"ResponseBodyEnd", "ConnectionReleased", "CallEnd");
}
Sure, it seems a reasonable test case to cover this change. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great PR, Great Test. Thank you!
@yschimke I see this change was accepted into the |
) (cherry picked from commit 631a29e)
Trying on #7648 |
…7648) * Cherrypick: fix DoH error when using ip address as hostname (#7073) (cherry picked from commit 631a29e) Co-authored-by: Tao.Zang <[email protected]>
Fix the issue that when network trying to do a DNS lookup with an IP numeric hostname, the DnsOverHttps will throw an UnknownHostException instead of returning the InetAddress with this IP address.
For example, If user runs a http proxy, the DnsOverHttps module will try to lookup the local Proxy address such as 127.0.0.x.