| 
									
										
										
										
											2021-05-17 22:19:54 +02:00
										 |  |  | // Package cidr contains functions that deal with classless reverse zones in the DNS.
 | 
					
						
							|  |  |  | package cidr
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import (
 | 
					
						
							|  |  |  | 	"math"
 | 
					
						
							|  |  |  | 	"net"
 | 
					
						
							| 
									
										
										
										
											2021-06-04 04:17:17 -04:00
										 |  |  | 	"strings"
 | 
					
						
							| 
									
										
										
										
											2021-05-17 22:19:54 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/apparentlymart/go-cidr/cidr"
 | 
					
						
							|  |  |  | 	"github.com/miekg/dns"
 | 
					
						
							|  |  |  | )
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-04 04:17:17 -04:00
										 |  |  | // Split returns a slice of non-overlapping subnets that in union equal the subnet n,
 | 
					
						
							|  |  |  | // and where each subnet falls on a reverse name segment boundary.
 | 
					
						
							|  |  |  | // for ipv4 this is any multiple of 8 bits (/8, /16, /24 or /32)
 | 
					
						
							|  |  |  | // for ipv6 this is any multiple of 4 bits
 | 
					
						
							|  |  |  | func Split(n *net.IPNet) []string {
 | 
					
						
							|  |  |  | 	boundary := 8
 | 
					
						
							|  |  |  | 	nstr := n.String()
 | 
					
						
							| 
									
										
										
										
											2021-06-07 10:43:28 +00:00
										 |  |  | 	if strings.Contains(nstr, ":") {
 | 
					
						
							| 
									
										
										
										
											2021-06-04 04:17:17 -04:00
										 |  |  | 		boundary = 4
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2021-05-17 22:19:54 +02:00
										 |  |  | 	ones, _ := n.Mask.Size()
 | 
					
						
							| 
									
										
										
										
											2021-06-04 04:17:17 -04:00
										 |  |  | 	if ones%boundary == 0 {
 | 
					
						
							| 
									
										
										
										
											2021-05-17 22:19:54 +02:00
										 |  |  | 		return []string{n.String()}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-04 04:17:17 -04:00
										 |  |  | 	mask := int(math.Ceil(float64(ones)/float64(boundary))) * boundary
 | 
					
						
							| 
									
										
										
										
											2021-05-17 22:19:54 +02:00
										 |  |  | 	networks := nets(n, mask)
 | 
					
						
							|  |  |  | 	cidrs := make([]string, len(networks))
 | 
					
						
							|  |  |  | 	for i := range networks {
 | 
					
						
							|  |  |  | 		cidrs[i] = networks[i].String()
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	return cidrs
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // nets return a slice of prefixes with the desired mask subnetted from original network.
 | 
					
						
							|  |  |  | func nets(network *net.IPNet, newPrefixLen int) []*net.IPNet {
 | 
					
						
							|  |  |  | 	prefixLen, _ := network.Mask.Size()
 | 
					
						
							|  |  |  | 	maxSubnets := int(math.Exp2(float64(newPrefixLen)) / math.Exp2(float64(prefixLen)))
 | 
					
						
							| 
									
										
										
										
											2022-07-10 20:06:33 +02:00
										 |  |  | 	nets := []*net.IPNet{{IP: network.IP, Mask: net.CIDRMask(newPrefixLen, 8*len(network.IP))}}
 | 
					
						
							| 
									
										
										
										
											2021-05-17 22:19:54 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for i := 1; i < maxSubnets; i++ {
 | 
					
						
							|  |  |  | 		next, exceeds := cidr.NextSubnet(nets[len(nets)-1], newPrefixLen)
 | 
					
						
							|  |  |  | 		nets = append(nets, next)
 | 
					
						
							|  |  |  | 		if exceeds {
 | 
					
						
							|  |  |  | 			break
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nets
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Reverse return the reverse zones that are authoritative for each net in ns.
 | 
					
						
							|  |  |  | func Reverse(nets []string) []string {
 | 
					
						
							|  |  |  | 	rev := make([]string, len(nets))
 | 
					
						
							|  |  |  | 	for i := range nets {
 | 
					
						
							|  |  |  | 		ip, n, _ := net.ParseCIDR(nets[i])
 | 
					
						
							|  |  |  | 		r, err1 := dns.ReverseAddr(ip.String())
 | 
					
						
							|  |  |  | 		if err1 != nil {
 | 
					
						
							|  |  |  | 			continue
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		ones, bits := n.Mask.Size()
 | 
					
						
							|  |  |  | 		// get the size, in bits, of each portion of hostname defined in the reverse address. (8 for IPv4, 4 for IPv6)
 | 
					
						
							|  |  |  | 		sizeDigit := 8
 | 
					
						
							|  |  |  | 		if len(n.IP) == net.IPv6len {
 | 
					
						
							|  |  |  | 			sizeDigit = 4
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		// Get the first lower octet boundary to see what encompassing zone we should be authoritative for.
 | 
					
						
							|  |  |  | 		mod := (bits - ones) % sizeDigit
 | 
					
						
							|  |  |  | 		nearest := (bits - ones) + mod
 | 
					
						
							|  |  |  | 		offset := 0
 | 
					
						
							|  |  |  | 		var end bool
 | 
					
						
							|  |  |  | 		for i := 0; i < nearest/sizeDigit; i++ {
 | 
					
						
							|  |  |  | 			offset, end = dns.NextLabel(r, offset)
 | 
					
						
							|  |  |  | 			if end {
 | 
					
						
							|  |  |  | 				break
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		rev[i] = r[offset:]
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	return rev
 | 
					
						
							|  |  |  | }
 |