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<