122 lines
2.9 KiB
Go
122 lines
2.9 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"net"
|
|
"strings"
|
|
)
|
|
|
|
type IPAddr struct {
|
|
ip net.IP
|
|
ipNet *net.IPNet
|
|
broadcast net.IP
|
|
}
|
|
|
|
// Calculate broadcast address for an provided IP network.
|
|
func getBroadcast(n *net.IPNet) net.IP {
|
|
// Make new IP.
|
|
l := len(n.IP)
|
|
out := make(net.IP, l)
|
|
|
|
// For each octet, mask with inverse of mask to get the broadcast.
|
|
for i := 0; i < l; i++ {
|
|
out[i] = n.IP[i] | ^n.Mask[i]
|
|
}
|
|
return out
|
|
}
|
|
|
|
// Parse an IP address with or without CIDR.
|
|
func ParseIPAddr(ip string) (*IPAddr, error) {
|
|
var err error
|
|
out := new(IPAddr)
|
|
|
|
// If doesn't contain CIDR, do a normal IPv4 address parse.
|
|
if !strings.Contains(ip, "/") {
|
|
out.ip = net.ParseIP(ip)
|
|
// If IP is nil, there was an parse error.
|
|
if out.ip == nil {
|
|
return nil, fmt.Errorf("ip address parse error")
|
|
}
|
|
// Convert to IPv4 space as we do not care about keeping IPv4 in IPv6.
|
|
i4 := out.ip.To4()
|
|
if i4 != nil {
|
|
out.ip = i4
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// Parse CIDR, and return error if error parsing.
|
|
out.ip, out.ipNet, err = net.ParseCIDR(ip)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Convert to IPv4 space as we do not care about keeping IPv4 in IPv6.
|
|
// This also makes broadcast calculation work better.
|
|
i4 := out.ip.To4()
|
|
if i4 != nil {
|
|
out.ip = i4
|
|
}
|
|
i4 = out.ipNet.IP.To4()
|
|
if i4 != nil {
|
|
// Convert IPv6 mask to IPv4 mask as this is an IPv4 address.
|
|
if len(out.ipNet.Mask) == net.IPv6len {
|
|
out.ipNet.Mask = out.ipNet.Mask[12:]
|
|
}
|
|
out.ipNet.IP = i4
|
|
}
|
|
|
|
// Calculate broadcast.
|
|
out.broadcast = getBroadcast(out.ipNet)
|
|
return out, nil
|
|
}
|
|
|
|
// Return string represtation of IP address/CIDR.
|
|
func (n *IPAddr) String() string {
|
|
if n.ipNet == nil {
|
|
return n.ip.String()
|
|
}
|
|
return n.ipNet.String()
|
|
}
|
|
|
|
// Compare IP addreseses to see if the networks contain or is equal the IP address provided.
|
|
func (n *IPAddr) Contains(ip *IPAddr) bool {
|
|
// If both are just IP addresses, do an equal operation.
|
|
if n.ipNet == nil && ip.ipNet == nil {
|
|
return n.ip.Equal(ip.ip)
|
|
}
|
|
|
|
// If this is an network, but provided is not, use ip network contains.
|
|
if n.ipNet != nil && ip.ipNet == nil {
|
|
return n.ipNet.Contains(ip.ip)
|
|
}
|
|
|
|
// If this is not an IP net, but provided address is... Return false.
|
|
if n.ipNet == nil && ip.ipNet != nil {
|
|
return false
|
|
}
|
|
|
|
// If provided network addr is within IP range of this IP network, return true.
|
|
if bytes.Compare(ip.ipNet.IP, n.ipNet.IP) >= 0 && bytes.Compare(ip.ipNet.IP, n.broadcast) <= 0 {
|
|
return true
|
|
}
|
|
|
|
// If provided broadcast addr is within IP range of this IP network, return true.
|
|
if bytes.Compare(ip.broadcast, n.ipNet.IP) >= 0 && bytes.Compare(ip.broadcast, n.broadcast) <= 0 {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// If IP addresses intercept.
|
|
func (n *IPAddr) Intercepts(ip *IPAddr) bool {
|
|
// If this IP contains provided, it intercepts.
|
|
if n.Contains(ip) {
|
|
return true
|
|
}
|
|
// If provided IP contains this IP, it intercepts.
|
|
return ip.Contains(n)
|
|
}
|