Response queries include NS IP addrs in Additionals

When querying for NS (name server) records, the responses include an
"Additionals" section which lists the IP addresses of the name server.
This is a courtesy & an optimization: by sending the IP address, we
avoid the client sending a second query for the IP address of the
nameserver.

With this change, the following command...

```
dig @ns-aws.nono.io sslip.io ns
```

...will yield these additional records:

```diff
+;; ADDITIONAL SECTION:
+ns-aws.nono.io.                604800  IN      A       52.0.56.137
+ns-azure.nono.io.              604800  IN      A       52.187.42.158
+ns-gce.nono.io.                604800  IN      A       104.155.144.4
```
This commit is contained in:
Brian Cunnie
2021-01-20 06:39:02 -08:00
parent 9691e1326d
commit 4816854d3f
2 changed files with 57 additions and 12 deletions

View File

@@ -134,30 +134,38 @@ var _ = Describe("sslip.io-dns-server", func() {
Eventually(string(serverSession.Err.Contents())).Should(MatchRegexp(`TypeMX sslip.io. \? 10 mail.protonmail.ch., 20 mailsec.protonmail.ch.\n`)) Eventually(string(serverSession.Err.Contents())).Should(MatchRegexp(`TypeMX sslip.io. \? 10 mail.protonmail.ch., 20 mailsec.protonmail.ch.\n`))
}) })
}) })
When("there are multiple NS records returned (e.g. almost NS query)", func() { When("there are multiple NS records returned (e.g. almost any NS query)", func() {
It("returns all the records", func() { It("returns all the records", func() {
digArgs = "@localhost example.com ns" digArgs = "@localhost example.com ns"
digCmd = exec.Command("dig", strings.Split(digArgs, " ")...) digCmd = exec.Command("dig", strings.Split(digArgs, " ")...)
digSession, err = Start(digCmd, GinkgoWriter, GinkgoWriter) digSession, err = Start(digCmd, GinkgoWriter, GinkgoWriter)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Eventually(digSession).Should(Say(`flags: qr aa rd;`)) Eventually(digSession).Should(Say(`flags: qr aa rd; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 3`))
Eventually(digSession).Should(Say(`aws.nono.io.`)) Eventually(digSession).Should(Say(`;; ANSWER SECTION:`))
Eventually(digSession).Should(Say(`azure.nono.io.`)) Eventually(digSession).Should(Say(`ns-aws.nono.io.\n`))
Eventually(digSession).Should(Say(`gce.nono.io.`)) Eventually(digSession).Should(Say(`ns-azure.nono.io.\n`))
Eventually(digSession).Should(Say(`ns-gce.nono.io.\n`))
Eventually(digSession).Should(Say(`;; ADDITIONAL SECTION:`))
Eventually(digSession).Should(Say(`ns-aws.nono.io..*52.0.56.137\n`))
Eventually(digSession).Should(Say(`ns-azure.nono.io..*52.187.42.158\n`))
Eventually(digSession).Should(Say(`ns-gce.nono.io..*104.155.144.4\n`))
Eventually(digSession, 1).Should(Exit(0)) Eventually(digSession, 1).Should(Exit(0))
Eventually(string(serverSession.Err.Contents())).Should(MatchRegexp(`TypeNS example.com. \? ns-aws.nono.io., ns-azure.nono.io., ns-gce.nono.io.\n`)) Eventually(string(serverSession.Err.Contents())).Should(MatchRegexp(`TypeNS example.com. \? ns-aws.nono.io., ns-azure.nono.io., ns-gce.nono.io.\n`))
}) })
}) })
When(`the NS record for an "_acme-challenge" domain is queried`, func() { When(`the NS record for an "_acme-challenge" domain is queried`, func() {
It(`returns the NS record of the query with the "_acme-challenge." stripped`, func() { It(`returns the NS record of the query with the "_acme-challenge." stripped`, func() {
digArgs = "@localhost _acme-challenge.127-0-0-1.sslip.io ns" digArgs = "@localhost _acme-challenge.fe80--.sslip.io ns"
digCmd = exec.Command("dig", strings.Split(digArgs, " ")...) digCmd = exec.Command("dig", strings.Split(digArgs, " ")...)
digSession, err = Start(digCmd, GinkgoWriter, GinkgoWriter) digSession, err = Start(digCmd, GinkgoWriter, GinkgoWriter)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Eventually(digSession).Should(Say(`flags: qr aa rd;`)) Eventually(digSession).Should(Say(`flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1`))
Eventually(digSession).Should(Say(`127-0-0-1.sslip.io.`)) Eventually(digSession).Should(Say(`;; ANSWER SECTION:`))
Eventually(digSession).Should(Say(`fe80--.sslip.io.`))
Eventually(digSession).Should(Say(`;; ADDITIONAL SECTION:`))
Eventually(digSession).Should(Say(`fe80--.sslip.io..*fe80::\n`))
Eventually(digSession, 1).Should(Exit(0)) Eventually(digSession, 1).Should(Exit(0))
Eventually(string(serverSession.Err.Contents())).Should(MatchRegexp(`TypeNS _acme-challenge.127-0-0-1.sslip.io. \? 127-0-0-1.sslip.io.\n`)) Eventually(string(serverSession.Err.Contents())).Should(MatchRegexp(`TypeNS _acme-challenge.fe80--.sslip.io. \? fe80--.sslip.io.\n`))
}) })
}) })
When(`there are multiple TXT records returned (e.g. SPF for sslip.io)`, func() { When(`there are multiple TXT records returned (e.g. SPF for sslip.io)`, func() {

View File

@@ -112,6 +112,7 @@ type Response struct {
Header dnsmessage.Header Header dnsmessage.Header
Answers []func(*dnsmessage.Builder) error Answers []func(*dnsmessage.Builder) error
Authorities []func(*dnsmessage.Builder) error Authorities []func(*dnsmessage.Builder) error
Additionals []func(*dnsmessage.Builder) error
} }
// QueryResponse takes in a raw (packed) DNS query and returns a raw (packed) // QueryResponse takes in a raw (packed) DNS query and returns a raw (packed)
@@ -170,6 +171,14 @@ func QueryResponse(queryBytes []byte) (responseBytes []byte, logMessage string,
return nil, "", err return nil, "", err
} }
} }
if err = b.StartAdditionals(); err != nil {
return nil, "", err
}
for _, additionals := range response.Additionals {
if err = additionals(&b); err != nil {
return nil, "", err
}
}
if responseBytes, err = b.Finish(); err != nil { if responseBytes, err = b.Finish(); err != nil {
return nil, "", err return nil, "", err
} }
@@ -338,7 +347,6 @@ func processQuestion(q dnsmessage.Question, response *Response) (string, error)
nameServers := NSResources(q.Name.String()) nameServers := NSResources(q.Name.String())
var logMessages []string var logMessages []string
response.Answers = append(response.Answers, response.Answers = append(response.Answers,
// 1 or more A records; A records > 1 only available via Customizations
func(b *dnsmessage.Builder) error { func(b *dnsmessage.Builder) error {
for _, nameServer := range nameServers { for _, nameServer := range nameServers {
err = b.NSResource(dnsmessage.ResourceHeader{ err = b.NSResource(dnsmessage.ResourceHeader{
@@ -354,6 +362,36 @@ func processQuestion(q dnsmessage.Question, response *Response) (string, error)
} }
return nil return nil
}) })
response.Additionals = append(response.Additionals,
func(b *dnsmessage.Builder) error {
for _, nameServer := range nameServers {
for _, aResource := range NameToA(nameServer.NS.String()) {
err = b.AResource(dnsmessage.ResourceHeader{
Name: nameServer.NS,
Type: dnsmessage.TypeA,
Class: dnsmessage.ClassINET,
TTL: 604800, // 60 * 60 * 24 * 7 == 1 week; long TTL, these IP addrs don't change
Length: 0,
}, aResource)
if err != nil {
return err
}
}
for _, aaaaResource := range NameToAAAA(nameServer.NS.String()) {
err = b.AAAAResource(dnsmessage.ResourceHeader{
Name: nameServer.NS,
Type: dnsmessage.TypeAAAA,
Class: dnsmessage.ClassINET,
TTL: 604800, // 60 * 60 * 24 * 7 == 1 week; long TTL, these IP addrs don't change
Length: 0,
}, aaaaResource)
if err != nil {
return err
}
}
}
return nil
})
for _, nameServer := range nameServers { for _, nameServer := range nameServers {
logMessages = append(logMessages, nameServer.NS.String()) logMessages = append(logMessages, nameServer.NS.String())
} }
@@ -512,8 +550,7 @@ func NameToA(fqdnString string) []dnsmessage.AResource {
return []dnsmessage.AResource{} return []dnsmessage.AResource{}
} }
// NameToAAAA returns either []AAAAResource that matched the hostname // NameToAAAA returns an []AAAAResource that matched the hostname
// or ErrNotFound
func NameToAAAA(fqdnString string) []dnsmessage.AAAAResource { func NameToAAAA(fqdnString string) []dnsmessage.AAAAResource {
fqdn := []byte(fqdnString) fqdn := []byte(fqdnString)
// is it a customized AAAA record? If so, return early // is it a customized AAAA record? If so, return early