From 4816854d3f01fe67db77a29e30734b2bb6aeaf31 Mon Sep 17 00:00:00 2001 From: Brian Cunnie Date: Wed, 20 Jan 2021 06:39:02 -0800 Subject: [PATCH] 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 ``` --- .../sslip.io-dns-server/integration_test.go | 26 +++++++---- .../src/sslip.io-dns-server/xip/xip.go | 43 +++++++++++++++++-- 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/bosh-release/src/sslip.io-dns-server/integration_test.go b/bosh-release/src/sslip.io-dns-server/integration_test.go index 20e3865..348b336 100644 --- a/bosh-release/src/sslip.io-dns-server/integration_test.go +++ b/bosh-release/src/sslip.io-dns-server/integration_test.go @@ -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`)) }) }) - 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() { digArgs = "@localhost example.com ns" digCmd = exec.Command("dig", strings.Split(digArgs, " ")...) digSession, err = Start(digCmd, GinkgoWriter, GinkgoWriter) Expect(err).ToNot(HaveOccurred()) - Eventually(digSession).Should(Say(`flags: qr aa rd;`)) - Eventually(digSession).Should(Say(`aws.nono.io.`)) - Eventually(digSession).Should(Say(`azure.nono.io.`)) - Eventually(digSession).Should(Say(`gce.nono.io.`)) + Eventually(digSession).Should(Say(`flags: qr aa rd; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 3`)) + Eventually(digSession).Should(Say(`;; ANSWER SECTION:`)) + Eventually(digSession).Should(Say(`ns-aws.nono.io.\n`)) + 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(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() { 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, " ")...) digSession, err = Start(digCmd, GinkgoWriter, GinkgoWriter) Expect(err).ToNot(HaveOccurred()) - Eventually(digSession).Should(Say(`flags: qr aa rd;`)) - Eventually(digSession).Should(Say(`127-0-0-1.sslip.io.`)) + Eventually(digSession).Should(Say(`flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1`)) + 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(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() { diff --git a/bosh-release/src/sslip.io-dns-server/xip/xip.go b/bosh-release/src/sslip.io-dns-server/xip/xip.go index 29e7848..1fdfdbc 100644 --- a/bosh-release/src/sslip.io-dns-server/xip/xip.go +++ b/bosh-release/src/sslip.io-dns-server/xip/xip.go @@ -112,6 +112,7 @@ type Response struct { Header dnsmessage.Header Answers []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) @@ -170,6 +171,14 @@ func QueryResponse(queryBytes []byte) (responseBytes []byte, logMessage string, 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 { return nil, "", err } @@ -338,7 +347,6 @@ func processQuestion(q dnsmessage.Question, response *Response) (string, error) nameServers := NSResources(q.Name.String()) var logMessages []string response.Answers = append(response.Answers, - // 1 or more A records; A records > 1 only available via Customizations func(b *dnsmessage.Builder) error { for _, nameServer := range nameServers { err = b.NSResource(dnsmessage.ResourceHeader{ @@ -354,6 +362,36 @@ func processQuestion(q dnsmessage.Question, response *Response) (string, error) } 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 { logMessages = append(logMessages, nameServer.NS.String()) } @@ -512,8 +550,7 @@ func NameToA(fqdnString string) []dnsmessage.AResource { return []dnsmessage.AResource{} } -// NameToAAAA returns either []AAAAResource that matched the hostname -// or ErrNotFound +// NameToAAAA returns an []AAAAResource that matched the hostname func NameToAAAA(fqdnString string) []dnsmessage.AAAAResource { fqdn := []byte(fqdnString) // is it a customized AAAA record? If so, return early