mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-25 13:29:27 +01:00
213 lines
5.2 KiB
Go
213 lines
5.2 KiB
Go
// Copyright 2020 Shivaram Lingamneni <slingamn@cs.stanford.edu>
|
|
// Copyright 2009 The Go Authors
|
|
// Released under the MIT license
|
|
|
|
package flatip
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"net"
|
|
)
|
|
|
|
var (
|
|
v4InV6Prefix = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}
|
|
|
|
IPv6loopback = IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
|
|
IPv6zero = IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
|
IPv4zero = IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0, 0, 0, 0}
|
|
|
|
ErrInvalidIPString = errors.New("String could not be interpreted as an IP address")
|
|
)
|
|
|
|
// packed versions of net.IP and net.IPNet; these are pure value types,
|
|
// so they can be compared with == and used as map keys.
|
|
|
|
// IP is a 128-bit representation of an IP address, using the 4-in-6 mapping
|
|
// to represent IPv4 addresses.
|
|
type IP [16]byte
|
|
|
|
// IPNet is a IP network. In a valid value, all bits after PrefixLen are zeroes.
|
|
type IPNet struct {
|
|
IP
|
|
PrefixLen uint8
|
|
}
|
|
|
|
// NetIP converts an IP into a net.IP.
|
|
func (ip IP) NetIP() (result net.IP) {
|
|
result = make(net.IP, 16)
|
|
copy(result[:], ip[:])
|
|
return
|
|
}
|
|
|
|
// FromNetIP converts a net.IP into an IP.
|
|
func FromNetIP(ip net.IP) (result IP) {
|
|
if len(ip) == 16 {
|
|
copy(result[:], ip[:])
|
|
} else {
|
|
result[10] = 0xff
|
|
result[11] = 0xff
|
|
copy(result[12:], ip[:])
|
|
}
|
|
return
|
|
}
|
|
|
|
// IPv4 returns the IP address representation of a.b.c.d
|
|
func IPv4(a, b, c, d byte) (result IP) {
|
|
copy(result[:12], v4InV6Prefix)
|
|
result[12] = a
|
|
result[13] = b
|
|
result[14] = c
|
|
result[15] = d
|
|
return
|
|
}
|
|
|
|
// ParseIP parses a string representation of an IP address into an IP.
|
|
// Unlike net.ParseIP, it returns an error instead of a zero value on failure,
|
|
// since the zero value of `IP` is a representation of a valid IP (::0, the
|
|
// IPv6 "unspecified address").
|
|
func ParseIP(ipstr string) (ip IP, err error) {
|
|
// TODO reimplement this without net.ParseIP
|
|
netip := net.ParseIP(ipstr)
|
|
if netip == nil {
|
|
err = ErrInvalidIPString
|
|
return
|
|
}
|
|
netip = netip.To16()
|
|
copy(ip[:], netip)
|
|
return
|
|
}
|
|
|
|
// String returns the string representation of an IP
|
|
func (ip IP) String() string {
|
|
// TODO reimplement this without using (net.IP).String()
|
|
return (net.IP)(ip[:]).String()
|
|
}
|
|
|
|
// IsIPv4 returns whether the IP is an IPv4 address.
|
|
func (ip IP) IsIPv4() bool {
|
|
return bytes.Equal(ip[:12], v4InV6Prefix)
|
|
}
|
|
|
|
// IsLoopback returns whether the IP is a loopback address.
|
|
func (ip IP) IsLoopback() bool {
|
|
if ip.IsIPv4() {
|
|
return ip[12] == 127
|
|
} else {
|
|
return ip == IPv6loopback
|
|
}
|
|
}
|
|
|
|
func (ip IP) IsUnspecified() bool {
|
|
return ip == IPv4zero || ip == IPv6zero
|
|
}
|
|
|
|
func rawCidrMask(length int) (m IP) {
|
|
n := uint(length)
|
|
for i := 0; i < 16; i++ {
|
|
if n >= 8 {
|
|
m[i] = 0xff
|
|
n -= 8
|
|
continue
|
|
}
|
|
m[i] = ^byte(0xff >> n)
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
func (ip IP) applyMask(mask IP) (result IP) {
|
|
for i := 0; i < 16; i += 1 {
|
|
result[i] = ip[i] & mask[i]
|
|
}
|
|
return
|
|
}
|
|
|
|
func cidrMask(ones, bits int) (result IP) {
|
|
switch bits {
|
|
case 32:
|
|
return rawCidrMask(96 + ones)
|
|
case 128:
|
|
return rawCidrMask(ones)
|
|
default:
|
|
return
|
|
}
|
|
}
|
|
|
|
// Mask returns the result of masking ip with the CIDR mask of
|
|
// length 'ones', out of a total of 'bits' (which must be either
|
|
// 32 for an IPv4 subnet or 128 for an IPv6 subnet).
|
|
func (ip IP) Mask(ones, bits int) (result IP) {
|
|
return ip.applyMask(cidrMask(ones, bits))
|
|
}
|
|
|
|
// ToNetIPNet converts an IPNet into a net.IPNet.
|
|
func (cidr IPNet) ToNetIPNet() (result net.IPNet) {
|
|
return net.IPNet{
|
|
IP: cidr.IP.NetIP(),
|
|
Mask: net.CIDRMask(int(cidr.PrefixLen), 128),
|
|
}
|
|
}
|
|
|
|
// Contains retuns whether the network contains `ip`.
|
|
func (cidr IPNet) Contains(ip IP) bool {
|
|
maskedIP := ip.Mask(int(cidr.PrefixLen), 128)
|
|
return cidr.IP == maskedIP
|
|
}
|
|
|
|
// FromNetIPnet converts a net.IPNet into an IPNet.
|
|
func FromNetIPNet(network net.IPNet) (result IPNet) {
|
|
ones, _ := network.Mask.Size()
|
|
if len(network.IP) == 16 {
|
|
copy(result.IP[:], network.IP[:])
|
|
} else {
|
|
result.IP[10] = 0xff
|
|
result.IP[11] = 0xff
|
|
copy(result.IP[12:], network.IP[:])
|
|
ones += 96
|
|
}
|
|
// perform masking so that equal CIDRs are ==
|
|
result.IP = result.IP.Mask(ones, 128)
|
|
result.PrefixLen = uint8(ones)
|
|
return
|
|
}
|
|
|
|
// String returns a string representation of an IPNet.
|
|
func (cidr IPNet) String() string {
|
|
ip := make(net.IP, 16)
|
|
copy(ip[:], cidr.IP[:])
|
|
ipnet := net.IPNet{
|
|
IP: ip,
|
|
Mask: net.CIDRMask(int(cidr.PrefixLen), 128),
|
|
}
|
|
return ipnet.String()
|
|
}
|
|
|
|
// HumanReadableString returns a string representation of an IPNet;
|
|
// if the network contains only a single IP address, it returns
|
|
// a representation of that address.
|
|
func (cidr IPNet) HumanReadableString() string {
|
|
if cidr.PrefixLen == 128 {
|
|
return cidr.IP.String()
|
|
}
|
|
return cidr.String()
|
|
}
|
|
|
|
// IsZero tests whether ipnet is the zero value of an IPNet, 0::0/0.
|
|
// Although this is a valid subnet, it can still be used as a sentinel
|
|
// value in some contexts.
|
|
func (ipnet IPNet) IsZero() bool {
|
|
return ipnet == IPNet{}
|
|
}
|
|
|
|
// ParseCIDR parses a string representation of an IP network in CIDR notation,
|
|
// then returns it as an IPNet (along with the original, unmasked address).
|
|
func ParseCIDR(netstr string) (ip IP, ipnet IPNet, err error) {
|
|
// TODO reimplement this without net.ParseCIDR
|
|
nip, nipnet, err := net.ParseCIDR(netstr)
|
|
if err != nil {
|
|
return
|
|
}
|
|
return FromNetIP(nip), FromNetIPNet(*nipnet), nil
|
|
}
|