diff --git a/src/xip/xip.go b/src/xip/xip.go index 5fae5b7..21d357d 100644 --- a/src/xip/xip.go +++ b/src/xip/xip.go @@ -174,9 +174,9 @@ func processQuestion(q dnsmessage.Question, b *dnsmessage.Builder) error { } case dnsmessage.TypeALL: { - // We don't implement type ANY, so return "NotImplemented" like CloudFlare + // We don't implement type ANY, so return "NotImplemented" like CloudFlare (1.1.1.1) // https://blog.cloudflare.com/rfc8482-saying-goodbye-to-any/ - // Google (8.8.8.8) returns A & AAAA records. + // Google (8.8.8.8) returns every record they can find (A, AAAA, SOA, NS, MX, ...). return &DNSError{RCode: dnsmessage.RCodeNotImplemented} } case dnsmessage.TypeMX: @@ -230,6 +230,25 @@ func processQuestion(q dnsmessage.Question, b *dnsmessage.Builder) error { return err } } + default: + { + // default is the same case as an A/AAAA record which is not found, + // i.e. we return no answers, but we return an authority section + err := b.StartAuthorities() + if err != nil { + return err + } + err = b.SOAResource(dnsmessage.ResourceHeader{ + Name: q.Name, + Type: dnsmessage.TypeSOA, + Class: dnsmessage.ClassINET, + TTL: 604800, // 60 * 60 * 24 * 7 == 1 week; it's not gonna change + Length: 0, + }, SOAResource(q.Name.String())) + if err != nil { + return err + } + } } return nil } diff --git a/src/xip/xip_test.go b/src/xip/xip_test.go index b7fbf4b..a6e4e66 100644 --- a/src/xip/xip_test.go +++ b/src/xip/xip_test.go @@ -214,6 +214,45 @@ var _ = Describe("Xip", func() { Expect(len(response.Additionals)).To(Equal(0)) }) }) + When("a record is requested but there's no record to return (e.g. SRV, HINFO)", func() { + BeforeEach(func() { + name = "no-srv-record.sslip.io." + nameArray = [255]byte{} // zero-out the array otherwise tests will fail with leftovers from longer "name"s + copy(nameArray[:], name) + queryType = dnsmessage.TypeSRV + + expectedSOA := xip.SOAResource(name) + expectedAuthority := dnsmessage.Resource{ + Header: dnsmessage.ResourceHeader{ + Name: dnsmessage.Name{ + Data: nameArray, + Length: uint8(len(name)), + }, + Type: dnsmessage.TypeSOA, + Class: dnsmessage.ClassINET, + TTL: 604800, + Length: 36, + }, + Body: &expectedSOA, + } + expectedResponse.Authorities = append(expectedResponse.Authorities, expectedAuthority) + }) + It("responds with no answers but with an authority", func() { + Expect(err).ToNot(HaveOccurred()) + Expect(len(response.Questions)).To(Equal(1)) + Expect(response.Questions[0]).To(Equal(question)) + // break test down for easier debugging + Expect(len(response.Answers)).To(Equal(0)) + Expect(len(response.Authorities)).To(Equal(1)) + Expect(response.Authorities[0].Header.Name).To(Equal(expectedResponse.Authorities[0].Header.Name)) + Expect(response.Authorities[0].Header).To(Equal(expectedResponse.Authorities[0].Header)) + Expect(response.Authorities[0].Body).To(Equal(expectedResponse.Authorities[0].Body)) + Expect(response.Authorities[0]).To(Equal(expectedResponse.Authorities[0])) + // I've made a decision to not populate the Additionals section because it's too much work + // (And I don't think it's necessary) + Expect(len(response.Additionals)).To(Equal(0)) + }) + }) }) Describe("ResponseHeader()", func() {