diff --git a/bosh-release/src/xip/xip.go b/bosh-release/src/xip/xip.go index 3f7e13e..a87629f 100644 --- a/bosh-release/src/xip/xip.go +++ b/bosh-release/src/xip/xip.go @@ -204,13 +204,10 @@ func processQuestion(q dnsmessage.Question, b *dnsmessage.Builder) (logMessage s } case dnsmessage.TypeAAAA: { - var nameToAAAA *dnsmessage.AAAAResource - nameToAAAA, err = NameToAAAA(q.Name.String()) + var nameToAAAAs []dnsmessage.AAAAResource + nameToAAAAs, err = NameToAAAA(q.Name.String()) if err != nil { - // There's only one possible error this can be: ErrNotFound. note that - // this could be written more efficiently; however, I wrote it to - // accommodate 'if err != nil' convention. My first version was 'if - // err == nil', and it flummoxed me. + // There's only one possible error this can be: ErrNotFound err = noAnswersOnlyAuthorities(q, b, &logMessage) return } else { @@ -218,18 +215,22 @@ func processQuestion(q dnsmessage.Question, b *dnsmessage.Builder) (logMessage s if err != nil { return } - err = b.AAAAResource(dnsmessage.ResourceHeader{ - Name: q.Name, - Type: dnsmessage.TypeAAAA, - Class: dnsmessage.ClassINET, - TTL: 604800, // 60 * 60 * 24 * 7 == 1 week; long TTL, these IP addrs don't change - Length: 0, - }, *nameToAAAA) - if err != nil { - return + var logMessages []string + for _, nameToAAAA := range nameToAAAAs { + err = b.AAAAResource(dnsmessage.ResourceHeader{ + Name: q.Name, + Type: dnsmessage.TypeAAAA, + Class: dnsmessage.ClassINET, + TTL: 604800, // 60 * 60 * 24 * 7 == 1 week; long TTL, these IP addrs don't change + Length: 0, + }, nameToAAAA) + if err != nil { + return + } + ip := net.IP(nameToAAAA.AAAA[:]) + logMessages = append(logMessages, ip.String()) } - ip := net.IP(nameToAAAA.AAAA[:]) - logMessage += ip.String() + logMessage += strings.Join(logMessages, ", ") } } case dnsmessage.TypeALL: @@ -383,14 +384,14 @@ func NameToA(fqdnString string) (*dnsmessage.AResource, error) { // NameToAAAA NameToA returns either an AAAAResource that matched the hostname // or ErrNotFound -func NameToAAAA(fqdnString string) (*dnsmessage.AAAAResource, error) { +func NameToAAAA(fqdnString string) ([]dnsmessage.AAAAResource, error) { fqdn := []byte(fqdnString) // is it a customized AAAA record? If so, return early if domain, ok := Customizations[fqdnString]; ok && len(domain.AAAA) > 0 { - return &domain.AAAA[0], nil // TODO: handle multiple AAAA records + return domain.AAAA, nil } if !ipv6RE.Match(fqdn) { - return &dnsmessage.AAAAResource{}, ErrNotFound + return nil, ErrNotFound } ipv6RE.Longest() @@ -402,7 +403,7 @@ func NameToAAAA(fqdnString string) (*dnsmessage.AAAAResource, error) { for i := range ipv16address { AAAAR.AAAA[i] = ipv16address[i] } - return &AAAAR, nil + return []dnsmessage.AAAAResource{AAAAR}, nil } func NSResources() map[string]dnsmessage.NSResource { diff --git a/bosh-release/src/xip/xip_test.go b/bosh-release/src/xip/xip_test.go index 305a097..71d5562 100644 --- a/bosh-release/src/xip/xip_test.go +++ b/bosh-release/src/xip/xip_test.go @@ -406,9 +406,10 @@ var _ = Describe("Xip", func() { Describe("NameToAAAA()", func() { DescribeTable("when it succeeds", func(fqdn string, expectedAAAA dnsmessage.AAAAResource) { - ipv6Answer, err := xip.NameToAAAA(fqdn) + ipv6Answers, err := xip.NameToAAAA(fqdn) Expect(err).ToNot(HaveOccurred()) - Expect(*ipv6Answer).To(Equal(expectedAAAA)) + Expect(len(ipv6Answers)).To(Equal(1)) + Expect(ipv6Answers[0]).To(Equal(expectedAAAA)) }, // sslip.io website Entry("sslip.io", "sslip.io.", xip.Customizations["sslip.io."].AAAA[0]), @@ -439,12 +440,30 @@ var _ = Describe("Xip", func() { It("should succeed every time", func() { for i := 0; i < 1000; i++ { addr := randomIPv6Address() - ipv6Answer, err := xip.NameToAAAA(strings.ReplaceAll(addr.String(), ":", "-")) + ipv6Answers, err := xip.NameToAAAA(strings.ReplaceAll(addr.String(), ":", "-")) Expect(err).ToNot(HaveOccurred()) - Expect(ipv6Answer.AAAA[:]).To(Equal([]uint8(addr))) + Expect(ipv6Answers[0].AAAA[:]).To(Equal([]uint8(addr))) } }) }) + When("There is more than one AAAA record", func() { + It("returns them all", func() { + fqdn := random8ByteString() + xip.Customizations[fqdn] = xip.DomainCustomization{ + //copy(xip.Customizations[fqdn].AAAA, dnsmessage.AAAAResource) + AAAA: []dnsmessage.AAAAResource{ + {AAAA: [16]byte{1}}, + {AAAA: [16]byte{2}}, + }, + } + ipv6Addrs, err := xip.NameToAAAA(fqdn) + Expect(err).ToNot(HaveOccurred()) + Expect(len(ipv6Addrs)).To(Equal(2)) + Expect(ipv6Addrs[0].AAAA).To(Equal([16]byte{1})) + Expect(ipv6Addrs[1].AAAA).To(Equal([16]byte{2})) + delete(xip.Customizations, fqdn) + }) + }) }) }) @@ -465,3 +484,13 @@ func randomIPv6Address() net.IP { } return ipv6 } + +// random8ByteString() returns an 8-char string consisting solely of the letters a-z. +func random8ByteString() string { + var randomString []byte + for i := 0; i < 8; i++ { + // 97 == ascii 'a', and there are 26 letters in the alphabet + randomString = append(randomString, byte(97+rand.Intn(26))) + } + return string(randomString) +}