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