sigs.k8s.io/external-dns@v0.14.1/pkg/rfc2317/arpa.go (about) 1 /* 2 Copyright 2023 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package rfc2317 18 19 import ( 20 "fmt" 21 "net" 22 "strconv" 23 "strings" 24 ) 25 26 // CidrToInAddr converts a CIDR block into its reverse lookup (in-addr) name. 27 // Given "2001::/16" returns "1.0.0.2.ip6.arpa" 28 // Given "10.20.30.0/24" returns "30.20.10.in-addr.arpa" 29 // Given "10.20.30.0/25" returns "0/25.30.20.10.in-addr.arpa" (RFC2317) 30 func CidrToInAddr(cidr string) (string, error) { 31 // If the user sent an IP instead of a CIDR (i.e. no "/"), turn it 32 // into a CIDR by adding /32 or /128 as appropriate. 33 ip := net.ParseIP(cidr) 34 if ip != nil { 35 if ip.To4() != nil { 36 cidr = ip.String() + "/32" 37 // Older code used `cidr + "/32"` but that didn't work with 38 // "IPv4 mapped IPv6 address". ip.String() returns the IPv4 39 // address for all IPv4 addresses no matter how they are 40 // expressed internally. 41 } else { 42 cidr = cidr + "/128" 43 } 44 } 45 46 a, c, err := net.ParseCIDR(cidr) 47 if err != nil { 48 return "", err 49 } 50 base, err := reverseaddr(a.String()) 51 if err != nil { 52 return "", err 53 } 54 base = strings.TrimRight(base, ".") 55 if !a.Equal(c.IP) { 56 return "", fmt.Errorf("CIDR %v has 1 bits beyond the mask", cidr) 57 } 58 59 bits, total := c.Mask.Size() 60 var toTrim int 61 if bits == 0 { 62 return "", fmt.Errorf("cannot use /0 in reverse CIDR") 63 } 64 65 // Handle IPv4 "Classless in-addr.arpa delegation" RFC2317: 66 if total == 32 && bits >= 25 && bits < 32 { 67 // first address / netmask . Class-b-arpa. 68 fparts := strings.Split(c.IP.String(), ".") 69 first := fparts[3] 70 bparts := strings.SplitN(base, ".", 2) 71 return fmt.Sprintf("%s/%d.%s", first, bits, bparts[1]), nil 72 } 73 74 // Handle IPv4 Class-full and IPv6: 75 if total == 32 { 76 if bits%8 != 0 { 77 return "", fmt.Errorf("IPv4 mask must be multiple of 8 bits") 78 } 79 toTrim = (total - bits) / 8 80 } else if total == 128 { 81 if bits%4 != 0 { 82 return "", fmt.Errorf("IPv6 mask must be multiple of 4 bits") 83 } 84 toTrim = (total - bits) / 4 85 } else { 86 return "", fmt.Errorf("invalid address (not IPv4 or IPv6): %v", cidr) 87 } 88 89 parts := strings.SplitN(base, ".", toTrim+1) 90 return parts[len(parts)-1], nil 91 } 92 93 // copied from go source. 94 // https://github.com/golang/go/blob/38b2c06e144c6ea7087c575c76c66e41265ae0b7/src/net/dnsclient.go#L26C1-L51C1 95 // The go source does not export this function so we copy it here. 96 97 // reverseaddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP 98 // address addr suitable for rDNS (PTR) record lookup or an error if it fails 99 // to parse the IP address. 100 func reverseaddr(addr string) (arpa string, err error) { 101 ip := net.ParseIP(addr) 102 if ip == nil { 103 return "", &net.DNSError{Err: "unrecognized address", Name: addr} 104 } 105 if ip.To4() != nil { 106 return Uitoa(uint(ip[15])) + "." + Uitoa(uint(ip[14])) + "." + Uitoa(uint(ip[13])) + "." + Uitoa(uint(ip[12])) + ".in-addr.arpa.", nil 107 } 108 // Must be IPv6 109 buf := make([]byte, 0, len(ip)*4+len("ip6.arpa.")) 110 // Add it, in reverse, to the buffer 111 for i := len(ip) - 1; i >= 0; i-- { 112 v := ip[i] 113 buf = append(buf, hexDigit[v&0xF], 114 '.', 115 hexDigit[v>>4], 116 '.') 117 } 118 // Append "ip6.arpa." and return (buf already has the final .) 119 buf = append(buf, "ip6.arpa."...) 120 return string(buf), nil 121 } 122 123 const hexDigit = "0123456789abcdef" 124 125 func Uitoa(val uint) string { 126 return strconv.FormatInt(int64(val), 10) 127 }