github.com/hexonet/dnscontrol@v0.2.8/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"