PTR: 1.0.0.127.in-addr.arpa → 127.0.0.1.sslip.io

We implement PTR records for IPv4.

When a PTR record is not found (e.g. "127.in-addr.arpa"), it returns the
SOA record, but, unlike other record lookups (e.g. "MX"), the SOA's
mname is locked to "sslip.io" because setting the mname to
"127.in-addr.arpa" doesn't make sense.

To be done:
- Implement IPv6
- Implement Metrics
- Update README
- Deploy new version
This commit is contained in:
Brian Cunnie
2022-07-10 08:08:58 -07:00
parent 110e214cd6
commit db763e071c
2 changed files with 79 additions and 0 deletions

View File

@@ -173,6 +173,22 @@ var _ = Describe("sslip.io-dns-server", func() {
"@127.0.0.1 get._acme-challenge.a.b.c.subdomain-key.k-v.io txt +short",
`baffled-boy._acme-challenge`,
`TypeTXT get._acme-challenge.a.b.c.subdomain-key.k-v.io. \? \["baffled-boy._acme-challenge"\]`),
Entry(`get a PTR for 1.0.168.192.in-addr.arpa returns 192.168.0.1.sslip.io`,
"@127.0.0.1 1.0.168.192.in-addr.arpa ptr +short",
`\A192.168.0.1.sslip.io.\n\z`,
`TypePTR 1.0.168.192.in-addr.arpa. \? 192.168.0.1.sslip.io.`),
Entry(`get a PTR for 1.0.0.127.blah.in-addr.arpa returns no records`,
"@127.0.0.1 1.0.0.127.blah.in-addr.arpa ptr +short",
`\A\z`,
`TypePTR 1.0.0.127.blah.in-addr.arpa. \? nil, SOA sslip.io. briancunnie.gmail.com. 2022042500 900 900 1800 180\n$`),
Entry(`get a PTR for blah.1.0.0.127.in-addr.arpa returns no records`,
"@127.0.0.1 blah.1.0.0.127.in-addr.arpa ptr +short",
`\A\z`,
`TypePTR blah.1.0.0.127.in-addr.arpa. \? nil, SOA sslip.io. briancunnie.gmail.com. 2022042500 900 900 1800 180\n$`),
Entry(`get a PTR for 0.0.127.in-addr.arpa returns no records`,
"@127.0.0.1 0.0.127.in-addr.arpa ptr +short",
`\A\z`,
`TypePTR 0.0.127.in-addr.arpa. \? nil, SOA sslip.io. briancunnie.gmail.com. 2022042500 900 900 1800 180\n$`),
)
})
Describe("for more complex assertions", func() {

View File

@@ -12,6 +12,7 @@ import (
"log"
"net"
"net/http"
"net/netip"
"reflect"
"regexp"
"strconv"
@@ -93,6 +94,7 @@ var (
ipv4REDashes = regexp.MustCompile(`(^|[.-])(((25[0-5]|(2[0-4]|1?[0-9])?[0-9])-){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9]))($|[.-])`)
// https://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses
ipv6RE = regexp.MustCompile(`(^|[.-])(([0-9a-fA-F]{1,4}-){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}-){1,7}-|([0-9a-fA-F]{1,4}-){1,6}-[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}-){1,5}(-[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}-){1,4}(-[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}-){1,3}(-[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}-){1,2}(-[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}-((-[0-9a-fA-F]{1,4}){1,6})|-((-[0-9a-fA-F]{1,4}){1,7}|-)|fe80-(-[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]+|--(ffff(-0{1,4})?-)?((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9])|([0-9a-fA-F]{1,4}-){1,4}-((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9]))($|[.-])`)
ipv4ReverseRE = regexp.MustCompile(`^(.*)\.in-addr\.arpa\.$`)
dns01ChallengeRE = regexp.MustCompile(`(?i)_acme-challenge\.`) // (?i) → non-capturing case insensitive
kvRE = regexp.MustCompile(`\.k-v\.io\.$`)
nsAwsSslip, _ = dnsmessage.NewName("ns-aws.sslip.io.")
@@ -557,6 +559,40 @@ func (x *Xip) processQuestion(q dnsmessage.Question, srcAddr net.IP) (response R
}
return response, logMessage + strings.Join(logMessageTXTss, ", "), nil
}
case dnsmessage.TypePTR:
{
var ptr *dnsmessage.PTRResource
ptr = PTRResource([]byte(q.Name.String()))
if ptr == nil {
// No Answers, only 1 Authorities
soaHeader, soaResource := SOAAuthority(dnsmessage.MustNewName("sslip.io."))
response.Authorities = append(response.Authorities,
func(b *dnsmessage.Builder) error {
if err = b.SOAResource(soaHeader, soaResource); err != nil {
return err
}
return nil
})
return response, logMessage + "nil, SOA " + soaLogMessage(soaResource), nil
}
//x.Metrics.AnsweredQueries++
response.Answers = append(response.Answers,
// 1 CNAME record, via Customizations
func(b *dnsmessage.Builder) error {
err = b.PTRResource(dnsmessage.ResourceHeader{
Name: q.Name,
Type: dnsmessage.TypePTR,
Class: dnsmessage.ClassINET,
TTL: 604800, // 60 * 60 * 24 * 7 == 1 week; long TTL, these IP addrs don't change
Length: 0,
}, *ptr)
if err != nil {
return err
}
return nil
})
return response, logMessage + ptr.PTR.String(), nil
}
default:
{
// default is the same case as an A/AAAA record which is not found,
@@ -801,6 +837,33 @@ func SOAResource(name dnsmessage.Name) dnsmessage.SOAResource {
}
}
// PTRResource returns the PTR record, otherwise nil
func PTRResource(fqdn []byte) *dnsmessage.PTRResource {
// "reverse", for example, means "1.0.0.127", as in "1.0.0.127.in-addr.arpa"
// the regular IP would be "127.0.0.1"
if ipv4ReverseRE.Match(fqdn) {
reversedIPv4 := ipv4ReverseRE.FindSubmatch(fqdn)[1]
reversedIPv4address := net.ParseIP(string(reversedIPv4)).To4()
if reversedIPv4address == nil {
return nil
}
ip := netip.AddrFrom4([4]byte{
reversedIPv4address[3],
reversedIPv4address[2],
reversedIPv4address[1],
reversedIPv4address[0],
})
ptrName, err := dnsmessage.NewName(ip.String() + ".sslip.io.")
if err != nil {
return nil
}
return &dnsmessage.PTRResource{
PTR: ptrName,
}
}
return nil
}
// when TXT for "ip.sslip.io" is queried, return the IP address of the querier
func ipSslipIo(x *Xip, srcAddr net.IP) ([]dnsmessage.TXTResource, error) {
x.Metrics.AnsweredTXTSrcIPQueries++