osbase/net/dns: add new DNS server
This adds a new DNS server service, which will replace CoreDNS. The
service has built-in handlers for certain names, but all other names
will be handled by runtime configurable handlers.
Change-Id: I4184d11422496e899794ef658ca1450e7bb01471
Reviewed-on: https://review.monogon.dev/c/monogon/+/3126
Tested-by: Jenkins CI
Reviewed-by: Lorenz Brun <lorenz@monogon.tech>
diff --git a/osbase/net/dns/name.go b/osbase/net/dns/name.go
new file mode 100644
index 0000000..9769d7e
--- /dev/null
+++ b/osbase/net/dns/name.go
@@ -0,0 +1,157 @@
+package dns
+
+import (
+ "net/netip"
+ "strings"
+)
+
+// IsSubDomain returns true if child is the same as or a subdomain of parent.
+// Both names should be in canonical form.
+func IsSubDomain(parent, child string) bool {
+ offset := len(child) - len(parent)
+ if offset < 0 || child[offset:] != parent {
+ return false
+ }
+ if offset == 0 || parent == "." {
+ return true
+ }
+ if child[offset-1] != '.' {
+ return false
+ }
+ j := offset - 2
+ for j >= 0 && child[j] == '\\' {
+ j--
+ }
+ return (offset-j)%2 == 0
+}
+
+// SplitLastLabel splits off the last label of a domain name. For example,
+// "www.example.com." is split into "www.example." and "com".
+func SplitLastLabel(name string) (rest string, label string) {
+ labelEnd := len(name)
+ if labelEnd != 0 && name[labelEnd-1] == '.' {
+ labelEnd--
+ }
+ labelStart := labelEnd
+ for ; labelStart > 0; labelStart-- {
+ if name[labelStart-1] != '.' {
+ continue
+ }
+ j := labelStart - 2
+ for j >= 0 && name[j] == '\\' {
+ j--
+ }
+ if (labelStart-j)%2 != 0 {
+ continue
+ }
+ break
+ }
+ return name[:labelStart], name[labelStart:labelEnd]
+}
+
+// ParseReverse parses name as a reverse lookup name. If name is not a reverse
+// name, the returned IP is invalid. The second return value indicates how many
+// bits of the address are present. The third return value is true if there are
+// extra labels before the reverse name.
+func ParseReverse(name string) (ip netip.Addr, bits int, extra bool) {
+ if strings.HasSuffix(name, "in-addr.arpa.") {
+ var ip [4]uint8
+ field := 0
+ pos := len(name) - len("in-addr.arpa.") - 1
+ for pos >= 0 && field < 4 {
+ if name[pos] != '.' {
+ break
+ }
+ nextPos := pos - 1
+ for nextPos >= 0 && name[nextPos] >= '0' && name[nextPos] <= '9' {
+ nextPos--
+ }
+ val := 0
+ for valPos := nextPos + 1; valPos < pos; valPos++ {
+ val = val*10 + int(name[valPos]) - '0'
+ }
+ valLen := pos - nextPos - 1
+ if valLen == 0 || valLen > 3 || (valLen != 1 && name[nextPos+1] == '0') || val > 255 {
+ // Number is empty, or too long, or has leading zero, or is too large.
+ break
+ }
+ ip[field] = uint8(val)
+ field++
+ pos = nextPos
+ }
+ if pos >= 0 {
+ // We did not parse the entire name.
+ j := pos - 1
+ for j >= 0 && name[j] == '\\' {
+ j--
+ }
+ if name[pos] != '.' || (pos-j)%2 == 0 {
+ // The last label we parsed was not terminated by a non-escaped dot.
+ field--
+ if field < 0 {
+ return netip.Addr{}, 0, false
+ }
+ ip[field] = 0
+ }
+ }
+ return netip.AddrFrom4(ip), field * 8, pos >= 0
+ }
+
+ if strings.HasSuffix(name, "ip6.arpa.") {
+ var ip [16]uint8
+ field := 0
+ half := false
+
+ pos := len(name) - len("ip6.arpa.") - 1
+ for pos > 0 && field < 16 {
+ if name[pos] != '.' {
+ break
+ }
+ var nibble uint8
+ if name[pos-1] >= '0' && name[pos-1] <= '9' {
+ nibble = name[pos-1] - '0'
+ } else if name[pos-1] >= 'a' && name[pos-1] <= 'f' {
+ nibble = name[pos-1] - 'a' + 10
+ } else {
+ break
+ }
+ if half {
+ ip[field] |= nibble
+ field++
+ half = false
+ } else {
+ ip[field] = nibble << 4
+ half = true
+ }
+ pos -= 2
+ }
+ if pos >= 0 {
+ // We did not parse the entire name.
+ j := pos - 1
+ for j >= 0 && name[j] == '\\' {
+ j--
+ }
+ if name[pos] != '.' || (pos-j)%2 == 0 {
+ // The last label we parsed was not terminated by a non-escaped dot.
+ if half {
+ half = false
+ ip[field] = 0
+ } else {
+ half = true
+ field--
+ if field < 0 {
+ return netip.Addr{}, 0, false
+ }
+ ip[field] &= 0xf0
+ }
+ }
+ }
+ bits := field * 8
+ if half {
+ bits += 4
+ }
+ return netip.AddrFrom16(ip), bits, pos >= 0
+ }
+
+ return netip.Addr{}, 0, false
+}