blocky/util/arpa.go

92 lines
1.8 KiB
Go

package util
import (
"errors"
"fmt"
"net"
"strconv"
"strings"
)
const (
IPv4PtrSuffix = ".in-addr.arpa."
IPv6PtrSuffix = ".ip6.arpa."
byteBits = 8
)
var ErrInvalidArpaAddrLen = errors.New("arpa hostname is not of expected length")
func ParseIPFromArpaAddr(arpa string) (net.IP, error) {
if strings.HasSuffix(arpa, IPv4PtrSuffix) {
return parseIPv4FromArpaAddr(arpa)
}
if strings.HasSuffix(arpa, IPv6PtrSuffix) {
return parseIPv6FromArpaAddr(arpa)
}
return nil, fmt.Errorf("invalid arpa hostname: %s", arpa)
}
func parseIPv4FromArpaAddr(arpa string) (net.IP, error) {
const base10 = 10
revAddr := strings.TrimSuffix(arpa, IPv4PtrSuffix)
parts := strings.Split(revAddr, ".")
if len(parts) != net.IPv4len {
return nil, ErrInvalidArpaAddrLen
}
buf := make([]byte, 0, net.IPv4len)
// Parse and add each byte, in reverse, to the buffer
for i := len(parts) - 1; i >= 0; i-- {
part, err := strconv.ParseUint(parts[i], base10, byteBits)
if err != nil {
return nil, err
}
buf = append(buf, byte(part))
}
return net.IPv4(buf[0], buf[1], buf[2], buf[3]), nil
}
func parseIPv6FromArpaAddr(arpa string) (net.IP, error) {
const (
base16 = 16
ipv6Bytes = 2 * net.IPv6len
nibbleBits = byteBits / 2
)
revAddr := strings.TrimSuffix(arpa, IPv6PtrSuffix)
parts := strings.Split(revAddr, ".")
if len(parts) != ipv6Bytes {
return nil, ErrInvalidArpaAddrLen
}
buf := make([]byte, 0, net.IPv6len)
// Parse and add each byte, in reverse, to the buffer
for i := len(parts) - 1; i >= 0; i -= 2 {
msNibble, err := strconv.ParseUint(parts[i], base16, byteBits)
if err != nil {
return nil, err
}
lsNibble, err := strconv.ParseUint(parts[i-1], base16, byteBits)
if err != nil {
return nil, err
}
part := msNibble<<nibbleBits | lsNibble
buf = append(buf, byte(part))
}
return net.IP(buf), nil
}