github.com/teknogeek/dnscontrol/v2@v2.10.1-0.20200227202244-ae299b55ba42/pkg/transform/arpa.go (about) 1 package transform 2 3 import ( 4 "fmt" 5 "net" 6 "strings" 7 ) 8 9 // ReverseDomainName turns a CIDR block into a reversed (in-addr) name. 10 func ReverseDomainName(cidr string) (string, error) { 11 a, c, err := net.ParseCIDR(cidr) 12 if err != nil { 13 return "", err 14 } 15 base, err := reverseaddr(a.String()) 16 if err != nil { 17 return "", err 18 } 19 base = strings.TrimRight(base, ".") 20 if !a.Equal(c.IP) { 21 return "", fmt.Errorf("CIDR %v has 1 bits beyond the mask", cidr) 22 } 23 24 bits, total := c.Mask.Size() 25 var toTrim int 26 if bits == 0 { 27 return "", fmt.Errorf("Cannot use /0 in reverse cidr") 28 } 29 30 // Handle IPv4 "Classless in-addr.arpa delegation" RFC2317: 31 if total == 32 && bits >= 25 && bits < 32 { 32 // first address / netmask . Class-b-arpa. 33 fparts := strings.Split(c.IP.String(), ".") 34 first := fparts[3] 35 bparts := strings.SplitN(base, ".", 2) 36 return fmt.Sprintf("%s/%d.%s", first, bits, bparts[1]), nil 37 } 38 39 // Handle IPv4 Class-full and IPv6: 40 if total == 32 { 41 if bits%8 != 0 { 42 return "", fmt.Errorf("IPv4 mask must be multiple of 8 bits") 43 } 44 toTrim = (total - bits) / 8 45 } else if total == 128 { 46 if bits%4 != 0 { 47 return "", fmt.Errorf("IPv6 mask must be multiple of 4 bits") 48 } 49 toTrim = (total - bits) / 4 50 } else { 51 return "", fmt.Errorf("Address is not IPv4 or IPv6: %v", cidr) 52 } 53 54 parts := strings.SplitN(base, ".", toTrim+1) 55 return parts[len(parts)-1], nil 56 } 57 58 // copied from go source. 59 // https://github.com/golang/go/blob/bfc164c64d33edfaf774b5c29b9bf5648a6447fb/src/net/dnsclient.go#L15 60 61 // reverseaddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP 62 // address addr suitable for rDNS (PTR) record lookup or an error if it fails 63 // to parse the IP address. 64 func reverseaddr(addr string) (arpa string, err error) { 65 ip := net.ParseIP(addr) 66 if ip == nil { 67 return "", &net.DNSError{Err: "unrecognized address", Name: addr} 68 } 69 if ip.To4() != nil { 70 return uitoa(uint(ip[15])) + "." + uitoa(uint(ip[14])) + "." + uitoa(uint(ip[13])) + "." + uitoa(uint(ip[12])) + ".in-addr.arpa.", nil 71 } 72 // Must be IPv6 73 buf := make([]byte, 0, len(ip)*4+len("ip6.arpa.")) 74 // Add it, in reverse, to the buffer 75 for i := len(ip) - 1; i >= 0; i-- { 76 v := ip[i] 77 buf = append(buf, hexDigit[v&0xF]) 78 buf = append(buf, '.') 79 buf = append(buf, hexDigit[v>>4]) 80 buf = append(buf, '.') 81 } 82 // Append "ip6.arpa." and return (buf already has the final .) 83 buf = append(buf, "ip6.arpa."...) 84 return string(buf), nil 85 } 86 87 // Convert unsigned integer to decimal string. 88 func uitoa(val uint) string { 89 if val == 0 { // avoid string allocation 90 return "0" 91 } 92 var buf [20]byte // big enough for 64bit value base 10 93 i := len(buf) - 1 94 for val >= 10 { 95 q := val / 10 96 buf[i] = byte('0' + val - q*10) 97 i-- 98 val = q 99 } 100 // val < 10 101 buf[i] = byte('0' + val) 102 return string(buf[i:]) 103 } 104 105 const hexDigit = "0123456789abcdef"