<p dir="auto">I have found a bug in pure Go resolver that causes Dial to fail even though correct responses are returned from the server. This problem manifested itself as a failure when trying to search for docker images in docker 1.9.1 built on go1.4.3:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="DEBU[0003] Calling GET /v1.21/images/search
INFO[0003] GET /v1.21/images/search?term=phusion
DEBU[0003] hostDir: /etc/docker/certs.d/docker.io
DEBU[0003] pinging registry endpoint https://index.docker.io/v1/
DEBU[0003] attempting v1 ping for registry endpoint https://index.docker.io/v1/
DEBU[0003] Index server: https://index.docker.io/v1/
ERRO[0003] Handler for GET /v1.21/images/search returned error: Get https://index.docker.io/v1/search?q=phusion: dial tcp: lookup index.docker.io on x.x.x.x:53: no such host
ERRO[0003] HTTP Error                                    err=Get https://index.docker.io/v1/search?q=phusion: dial tcp: lookup index.docker.io on x.x.x.x:53: no such host statusCode=404"><pre class="notranslate"><code class="notranslate">DEBU[0003] Calling GET /v1.21/images/search
INFO[0003] GET /v1.21/images/search?term=phusion
DEBU[0003] hostDir: /etc/docker/certs.d/docker.io
DEBU[0003] pinging registry endpoint https://index.docker.io/v1/
DEBU[0003] attempting v1 ping for registry endpoint https://index.docker.io/v1/
DEBU[0003] Index server: https://index.docker.io/v1/
ERRO[0003] Handler for GET /v1.21/images/search returned error: Get https://index.docker.io/v1/search?q=phusion: dial tcp: lookup index.docker.io on x.x.x.x:53: no such host
ERRO[0003] HTTP Error                                    err=Get https://index.docker.io/v1/search?q=phusion: dial tcp: lookup index.docker.io on x.x.x.x:53: no such host statusCode=404
</code></pre></div>
<p dir="auto">Similar issues are <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="107693835" data-permission-text="Title is private" data-url="https://github.com/golang/go/issues/12712" data-hovercard-type="issue" data-hovercard-url="/golang/go/issues/12712/hovercard" href="https://github.com/golang/go/issues/12712">#12712</a> and <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="108766360" data-permission-text="Title is private" data-url="https://github.com/golang/go/issues/12778" data-hovercard-type="issue" data-hovercard-url="/golang/go/issues/12778/hovercard" href="https://github.com/golang/go/issues/12778">#12778</a></p>
<p dir="auto">I have tested go1.3.0 and it did not exhibit this problem, but at least 1.4.3, 1.5.1 and 1.5.2 are affected.</p>
<p dir="auto">Tested using the following code:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="package main

import (
    &quot;fmt&quot;
    &quot;io/ioutil&quot;
    &quot;log&quot;
    &quot;net/http&quot;
    &quot;os&quot;
)

func main() {
    resp, err := http.Get(os.Args[1])
    if err != nil {
        log.Fatal(err)
    }
    page, err := ioutil.ReadAll(resp.Body)
    resp.Body.Close()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf(&quot;%s&quot;, page)
}"><pre class="notranslate"><code class="notranslate">package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "os"
)

func main() {
    resp, err := http.Get(os.Args[1])
    if err != nil {
        log.Fatal(err)
    }
    page, err := ioutil.ReadAll(resp.Body)
    resp.Body.Close()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%s", page)
}
</code></pre></div>
<p dir="auto"><strong>Note - we only run an IPv4 stack. IPv6 is disabled on the client</strong></p>
<h2 dir="auto">Test fails with pure Go resolver</h2>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="# /tmp/fetch-1.5.2 https://index.docker.io/v1/search?q=phusion
2015/12/10 12:17:42 Get https://index.docker.io/v1/search?q=phusion: dial tcp: lookup index.docker.io on x.x.x.x:53: no such host
# docker search phusion
Error response from daemon: Get https://index.docker.io/v1/search?q=phusion: dial tcp: lookup index.docker.io on x.x.x.x:53: no such host"><pre class="notranslate"><code class="notranslate"># /tmp/fetch-1.5.2 https://index.docker.io/v1/search?q=phusion
2015/12/10 12:17:42 Get https://index.docker.io/v1/search?q=phusion: dial tcp: lookup index.docker.io on x.x.x.x:53: no such host
# docker search phusion
Error response from daemon: Get https://index.docker.io/v1/search?q=phusion: dial tcp: lookup index.docker.io on x.x.x.x:53: no such host
</code></pre></div>
<p dir="auto">DNS packet capture: <a href="https://github.com/golang/go/files/57660/PureGO_correct_but_fails.txt">PureGO_correct_but_fails.txt</a></p>
<p dir="auto">Here we see the AAAA response contains only CNAME RRs but no AAAA RRs. The resolver queries all 4 name servers and all 4 responses contain CNAME and A RRs which the resolver ignores. It then searches using the search domain (from <code class="notranslate">/etc/resolv.conf</code>) appended, finally determining that the host cannot be found.</p>
<h2 dir="auto">Test succeeds when forced to use cGo resolver</h2>
<p dir="auto">(By setting LOCALDOMAIN in the environment, as described <a href="https://golang.org/pkg/net/" rel="nofollow">in the docs</a>)</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="# LOCALDOMAIN= /tmp/fetch-1.5.2 https://index.docker.io/v1/search?q=phusion
{&quot;num_pages&quot;: 11, &quot;num_results&quot;: 257, &quot;results&quot;: [{&quot;is_automated&quot;: true, ..."><pre class="notranslate"><code class="notranslate"># LOCALDOMAIN= /tmp/fetch-1.5.2 https://index.docker.io/v1/search?q=phusion
{"num_pages": 11, "num_results": 257, "results": [{"is_automated": true, ...
</code></pre></div>
<p dir="auto">DNS packet capture: <a href="https://github.com/golang/go/files/57661/CGO_correct_but_succeeds.txt">CGO_correct_but_succeeds.txt</a></p>
<p dir="auto">Here we see the same AAAA and A RRs are returned but this time the resolver accepts the A records and the dial call succeeds.</p>
<h2 dir="auto">Summary</h2>
<p dir="auto">The name server returns correct responses for both A and AAAA queries but the pure Go resolver ignores the A response records when the AAAA response contains no RRs.</p>
<p dir="auto">I would have also expected that since IPv6 is disabled, querying for AAAA records is redundant, but the C library does it too. That may be because many hosts will have both stacks enabled but only have IPv4 routing configured so querying AAAA may be valid and successful but will require an A query anyway. Unfortunately that means the DNS server's load gets doubled for every address resolution.</p>
<h2 dir="auto">Recommendations</h2>
<ul dir="auto">
<li>Review section <strong>3. Expected Behaviour</strong> of <a href="https://www.ietf.org/rfc/rfc4074.txt" rel="nofollow">RFC4074</a></li>
<li>Enable AAAA queries only if the IPv6 stack is enabled</li>
</ul>
<pre class="notranslate"><a href="https://golang.org/issue/6336" rel="nofollow">issue #6336</a> unveiled that we need minimum DNS transport support in build-in DNS resolver
for when we cannot rely on underlying stuff such as libc. For now, looks like EDNS0 is
the only missing piece.</pre>