123 lines
No EOL
2.8 KiB
Go
123 lines
No EOL
2.8 KiB
Go
package reverse
|
|
|
|
import (
|
|
"net"
|
|
"regexp"
|
|
"bytes"
|
|
"strings"
|
|
)
|
|
|
|
type network struct {
|
|
IPnet *net.IPNet
|
|
Zone string // forward lookup zone
|
|
Template string
|
|
TTL uint32
|
|
RegexMatchIP *regexp.Regexp
|
|
Fallthrough bool
|
|
}
|
|
|
|
const hexDigit = "0123456789abcdef"
|
|
const templateNameIP = "{ip}"
|
|
const regexMatchV4 = "((?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\-){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))"
|
|
const regexMatchV6 = "([0-9a-fA-F]{32})"
|
|
|
|
// For forward lookup
|
|
// converts the hostname back to an ip, based on the template
|
|
// returns nil if there is no ip found
|
|
func (network *network) hostnameToIP(rname string) net.IP {
|
|
var matchedIP net.IP
|
|
|
|
// use precompiled regex by setup
|
|
match := network.RegexMatchIP.FindStringSubmatch(rname)
|
|
// regex did not matched
|
|
if (len(match) != 2) {
|
|
return nil
|
|
}
|
|
|
|
if network.IPnet.IP.To4() != nil {
|
|
matchedIP = net.ParseIP(strings.Replace(match[1], "-", ".", 4))
|
|
} else {
|
|
var buf bytes.Buffer
|
|
// convert back to an valid ipv6 string with colons
|
|
for i := 0; i < 8 * 4; i += 4 {
|
|
buf.WriteString(match[1][i:i + 4])
|
|
if (i < 28) {
|
|
buf.WriteString(":")
|
|
}
|
|
}
|
|
matchedIP = net.ParseIP(buf.String())
|
|
}
|
|
|
|
// No valid ip or it does not belong to this network
|
|
if matchedIP == nil || !network.IPnet.Contains(matchedIP) {
|
|
return nil
|
|
}
|
|
|
|
return matchedIP
|
|
}
|
|
|
|
// For reverse lookup
|
|
// Converts an Ip to an dns compatible hostname and injects it into the template.domain
|
|
func (network *network) ipToHostname(ip net.IP) string {
|
|
var name string
|
|
|
|
ipv4 := ip.To4()
|
|
if ipv4 != nil {
|
|
// replace . to -
|
|
name = uitoa(ipv4[0]) + "-" +
|
|
uitoa(ipv4[1]) + "-" +
|
|
uitoa(ipv4[2]) + "-" +
|
|
uitoa(ipv4[3])
|
|
} else {
|
|
// assume v6
|
|
// ensure zeros are present in string
|
|
buf := make([]byte, 0, len(ip) * 4)
|
|
for i := 0; i < len(ip); i++ {
|
|
v := ip[i]
|
|
buf = append(buf, hexDigit[v >> 4])
|
|
buf = append(buf, hexDigit[v & 0xF])
|
|
}
|
|
name = string(buf)
|
|
}
|
|
// inject the converted ip into the fqdn template
|
|
return strings.Replace(network.Template, templateNameIP, name, 1)
|
|
}
|
|
|
|
// just the same from net.ip package, but with uint8
|
|
func uitoa(val uint8) string {
|
|
if val == 0 {
|
|
// avoid string allocation
|
|
return "0"
|
|
}
|
|
var buf [20]byte // big enough for 64bit value base 10
|
|
i := len(buf) - 1
|
|
for val >= 10 {
|
|
q := val / 10
|
|
buf[i] = byte('0' + val - q * 10)
|
|
i--
|
|
val = q
|
|
}
|
|
// val < 10
|
|
buf[i] = byte('0' + val)
|
|
return string(buf[i:])
|
|
}
|
|
|
|
type networks []network
|
|
|
|
// implements the sort interface
|
|
func (slice networks) Len() int {
|
|
return len(slice)
|
|
}
|
|
|
|
// implements the sort interface
|
|
// cidr closer to the ip wins (by netmask)
|
|
func (slice networks) Less(i, j int) bool {
|
|
isize, _ := slice[i].IPnet.Mask.Size()
|
|
jsize, _ := slice[j].IPnet.Mask.Size()
|
|
return isize > jsize
|
|
}
|
|
|
|
// implements the sort interface
|
|
func (slice networks) Swap(i, j int) {
|
|
slice[i], slice[j] = slice[j], slice[i]
|
|
} |