mirror of
https://github.com/cunnie/sslip.io.git
synced 2025-10-07 08:31:02 +08:00
DNS: sslip.io's MXes are protonmail's servers
...and everyone else's are themselves, e.g. `127.0.0.1.sslip.io`'s MX
record is `127.0.0.1.sslip.io` with a preference of 0. This allows me to
get email for sslip.io without worrying about email for every sslip.io
subdomain.
- Refactor: the global variable `NameServers` no longer holds the IP
addresses of the nameservers, merely their names. The addresses are now
held in the `Customizations` variable, the more appropriate place. We
only want one source of truth wherever possible.
- 🐞 The original Go Playground for creating `dnsmessage.Name`s was
wrong: it said to NOT put a dot at the end. You need the dot at the end.
The MX records for `sslip.io` now have dots at the end.
- The above bug caused `processQuestion()` to return an unexpected
error, but without the underlying error message. Now, when
`processQuestion()` errors in an unexpected manner, it logs the
underlying error message, which makes debugging much easier.
- Richer logging for MX queries: we now return the servers and
preferences rather than the terse `MX`.
- We use specific `fqdnString` rather than the generic `domain`
as a variable for consistency, which is the hobgoblin of small minds.
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/net/dns/dnsmessage"
|
"golang.org/x/net/dns/dnsmessage"
|
||||||
@@ -39,10 +40,10 @@ var (
|
|||||||
ipv4RE = regexp.MustCompile(`(^|[.-])(((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])[.-]){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))($|[.-])`)
|
ipv4RE = regexp.MustCompile(`(^|[.-])(((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])[.-]){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))($|[.-])`)
|
||||||
ipv6RE = regexp.MustCompile(`(^|[.-])(([0-9a-fA-F]{1,4}-){7,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]{1,}|--(ffff(-0{1,4}){0,1}-){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}-){1,4}-((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))($|[.-])`)
|
ipv6RE = regexp.MustCompile(`(^|[.-])(([0-9a-fA-F]{1,4}-){7,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]{1,}|--(ffff(-0{1,4}){0,1}-){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}-){1,4}-((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))($|[.-])`)
|
||||||
ErrNotFound = errors.New("record not found")
|
ErrNotFound = errors.New("record not found")
|
||||||
NameServers = map[string]dnsmessage.AResource{
|
NameServers = []string{
|
||||||
"ns-aws.nono.io.": {A: [4]byte{52, 0, 56, 137}},
|
"ns-aws.nono.io.",
|
||||||
"ns-azure.nono.io.": {A: [4]byte{52, 187, 42, 158}},
|
"ns-azure.nono.io.",
|
||||||
"ns-gce.nono.io.": {A: [4]byte{104, 155, 144, 4}},
|
"ns-gce.nono.io.",
|
||||||
}
|
}
|
||||||
|
|
||||||
Customizations = DomainCustomizations{
|
Customizations = DomainCustomizations{
|
||||||
@@ -57,22 +58,22 @@ var (
|
|||||||
// mail.protonmail.ch
|
// mail.protonmail.ch
|
||||||
{
|
{
|
||||||
Pref: 10,
|
Pref: 10,
|
||||||
// Use The Go Playground https://play.golang.org/p/knv3Jbkq0DP
|
// Use The Go Playground https://play.golang.org/p/tM4y1eLJ1dg
|
||||||
// to convert strings to dnsmessage.Name for easy cut-and-paste
|
// to convert strings to dnsmessage.Name for easy cut-and-paste
|
||||||
MX: dnsmessage.Name{
|
MX: dnsmessage.Name{
|
||||||
Length: 18,
|
Length: 19,
|
||||||
Data: [255]byte{
|
Data: [255]byte{
|
||||||
109, 97, 105, 108, 46, 112, 114, 111, 116, 111, 110, 109, 97, 105, 108, 46, 99, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
109, 97, 105, 108, 46, 112, 114, 111, 116, 111, 110, 109, 97, 105, 108, 46, 99, 104, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// mailsec.protonmail.ch
|
// mailsec.protonmail.ch
|
||||||
{
|
{
|
||||||
Pref: 10,
|
Pref: 20,
|
||||||
MX: dnsmessage.Name{
|
MX: dnsmessage.Name{
|
||||||
Length: 21,
|
Length: 22,
|
||||||
Data: [255]byte{
|
Data: [255]byte{
|
||||||
109, 97, 105, 108, 115, 101, 99, 46, 112, 114, 111, 116, 111, 110, 109, 97, 105, 108, 46, 99, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
109, 97, 105, 108, 115, 101, 99, 46, 112, 114, 111, 116, 111, 110, 109, 97, 105, 108, 46, 99, 104, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -151,7 +152,7 @@ func QueryResponse(queryBytes []byte) (responseBytes []byte, logMessage string,
|
|||||||
} else {
|
} else {
|
||||||
// processQuestion shouldn't return any error but DNSError,
|
// processQuestion shouldn't return any error but DNSError,
|
||||||
// but who knows? Someone might break contract. This is the guard.
|
// but who knows? Someone might break contract. This is the guard.
|
||||||
err = errors.New("processQuestion() returned unexpected error type")
|
err = fmt.Errorf("processQuestion() returned unexpected error type: %s", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -268,17 +269,22 @@ func processQuestion(q dnsmessage.Question, b *dnsmessage.Builder) (logMessage s
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = b.MXResource(dnsmessage.ResourceHeader{
|
mailExchangers := MXResource(q.Name.String())
|
||||||
Name: q.Name,
|
var logMessages []string
|
||||||
Type: dnsmessage.TypeMX,
|
for _, mailExchanger := range mailExchangers {
|
||||||
Class: dnsmessage.ClassINET,
|
err = b.MXResource(dnsmessage.ResourceHeader{
|
||||||
TTL: 604800, // 60 * 60 * 24 * 7 == 1 week; long TTL, these IP addrs don't change
|
Name: q.Name,
|
||||||
Length: 0,
|
Type: dnsmessage.TypeMX,
|
||||||
}, MXResource())
|
Class: dnsmessage.ClassINET,
|
||||||
if err != nil {
|
TTL: 604800, // 60 * 60 * 24 * 7 == 1 week; long TTL, these IP addrs don't change
|
||||||
return
|
Length: 0,
|
||||||
|
}, mailExchanger)
|
||||||
|
logMessages = append(logMessages, strconv.Itoa(int(mailExchanger.Pref))+" "+string(mailExchanger.MX.Data[:]))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
logMessage += "MX"
|
logMessage += strings.Join(logMessages, ", ")
|
||||||
}
|
}
|
||||||
case dnsmessage.TypeNS:
|
case dnsmessage.TypeNS:
|
||||||
{
|
{
|
||||||
@@ -406,7 +412,7 @@ func NameToAAAA(fqdnString string) (*dnsmessage.AAAAResource, error) {
|
|||||||
|
|
||||||
func NSResources() map[string]dnsmessage.NSResource {
|
func NSResources() map[string]dnsmessage.NSResource {
|
||||||
nsResources := make(map[string]dnsmessage.NSResource)
|
nsResources := make(map[string]dnsmessage.NSResource)
|
||||||
for nameServer, _ := range NameServers {
|
for _, nameServer := range NameServers {
|
||||||
var nameServerBytes [255]byte
|
var nameServerBytes [255]byte
|
||||||
copy(nameServerBytes[:], nameServer)
|
copy(nameServerBytes[:], nameServer)
|
||||||
nsResources[nameServer] = dnsmessage.NSResource{
|
nsResources[nameServer] = dnsmessage.NSResource{
|
||||||
@@ -419,28 +425,35 @@ func NSResources() map[string]dnsmessage.NSResource {
|
|||||||
return nsResources
|
return nsResources
|
||||||
}
|
}
|
||||||
|
|
||||||
func MXResource() dnsmessage.MXResource {
|
func MXResource(fqdnString string) []dnsmessage.MXResource {
|
||||||
|
if domain, ok := Customizations[fqdnString]; ok {
|
||||||
|
if len(domain.MX) > 0 {
|
||||||
|
return domain.MX
|
||||||
|
}
|
||||||
|
}
|
||||||
var mxHostBytes [255]byte
|
var mxHostBytes [255]byte
|
||||||
copy(mxHostBytes[:], MxHost)
|
copy(mxHostBytes[:], fqdnString)
|
||||||
return dnsmessage.MXResource{
|
return []dnsmessage.MXResource{
|
||||||
Pref: 0,
|
{
|
||||||
MX: dnsmessage.Name{
|
Pref: 0,
|
||||||
Data: mxHostBytes,
|
MX: dnsmessage.Name{
|
||||||
Length: uint8(len(MxHost)),
|
Data: mxHostBytes,
|
||||||
|
Length: uint8(len(fqdnString)),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SOAResource returns the hard-coded SOA
|
// SOAResource returns the hard-coded SOA
|
||||||
func SOAResource(domain string) dnsmessage.SOAResource {
|
func SOAResource(fqdnString string) dnsmessage.SOAResource {
|
||||||
var domainBytes [255]byte
|
var domainBytes [255]byte
|
||||||
copy(domainBytes[:], domain)
|
copy(domainBytes[:], fqdnString)
|
||||||
var mboxArray [255]byte
|
var mboxArray [255]byte
|
||||||
copy(mboxArray[:], Hostmaster)
|
copy(mboxArray[:], Hostmaster)
|
||||||
return dnsmessage.SOAResource{
|
return dnsmessage.SOAResource{
|
||||||
NS: dnsmessage.Name{
|
NS: dnsmessage.Name{
|
||||||
Data: domainBytes,
|
Data: domainBytes,
|
||||||
Length: uint8(len(domain)),
|
Length: uint8(len(fqdnString)),
|
||||||
},
|
},
|
||||||
MBox: dnsmessage.Name{
|
MBox: dnsmessage.Name{
|
||||||
Data: mboxArray,
|
Data: mboxArray,
|
||||||
|
@@ -290,18 +290,28 @@ var _ = Describe("Xip", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Describe("MXResource()", func() {
|
Describe("MXResource()", func() {
|
||||||
It("returns the MX resource (go ProtonMail!)", func() {
|
It("returns the MX resource", func() {
|
||||||
mx := xip.MXResource()
|
query := "xyz"
|
||||||
|
mx := xip.MXResource(query)
|
||||||
var mxHostBytes [255]byte
|
var mxHostBytes [255]byte
|
||||||
copy(mxHostBytes[:], xip.MxHost)
|
copy(mxHostBytes[:], query)
|
||||||
Expect(mx.MX.Data).To(Equal(mxHostBytes))
|
Expect(len(mx)).To(Equal(1))
|
||||||
|
Expect(mx[0].MX.Length).To(Equal(uint8(3))) // "xyz" has 3 letters
|
||||||
|
Expect(mx[0].MX.Data).To(Equal(mxHostBytes))
|
||||||
|
})
|
||||||
|
When("sslip.io is the domain being queried", func() {
|
||||||
|
It("returns sslip.io's custom MX records", func() {
|
||||||
|
mx := xip.MXResource("sslip.io.")
|
||||||
|
Expect(len(mx)).To(Equal(2))
|
||||||
|
Expect(mx[0].MX.Data).To(Equal(xip.Customizations["sslip.io."].MX[0].MX.Data))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("NSResources()", func() {
|
Describe("NSResources()", func() {
|
||||||
It("returns a map of the name servers", func() {
|
It("returns a map of the name servers", func() {
|
||||||
ns := xip.NSResources()
|
ns := xip.NSResources()
|
||||||
for nameServer, _ := range xip.NameServers {
|
for _, nameServer := range xip.NameServers {
|
||||||
var nameServerBytes [255]byte
|
var nameServerBytes [255]byte
|
||||||
copy(nameServerBytes[:], nameServer)
|
copy(nameServerBytes[:], nameServer)
|
||||||
Expect(ns[nameServer].NS.Data).To(Equal(nameServerBytes))
|
Expect(ns[nameServer].NS.Data).To(Equal(nameServerBytes))
|
||||||
|
Reference in New Issue
Block a user