File: net.go

package info (click to toggle)
golang-github-c-robinson-iplib 1.0.3-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 316 kB
  • sloc: makefile: 2
file content (115 lines) | stat: -rw-r--r-- 3,519 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package iplib

import (
	"net"
	"strings"
)

// Net describes an iplib.Net object, the enumerated functions are those that
// are required for comparison, sorting, generic initialization and for
// ancillary functions such as those found in the iid and iana submodules
type Net interface {
	Contains(ip net.IP) bool
	ContainsNet(network Net) bool
	FirstAddress() net.IP
	IP() net.IP
	LastAddress() net.IP
	Mask() net.IPMask
	String() string
	Version() int
}

// NewNet returns a new Net object containing ip at the specified masklen. In
// the Net6 case the hostbits value will be set to 0. If the masklen is set
// to an insane value (greater than 32 for IPv4 or 128 for IPv6) an empty Net
// will be returned
func NewNet(ip net.IP, masklen int) Net {
	if EffectiveVersion(ip) == 6 {
		return NewNet6(ip, masklen, 0)
	}
	return NewNet4(ip, masklen)
}

// NewNetBetween takes two net.IP's as input and will return the largest
// netblock that can fit between them (exclusive of the IP's themselves).
// If there is an exact fit it will set a boolean to true, otherwise the bool
// will be false. If no fit can be found (probably because a >= b) an
// ErrNoValidRange will be returned.
func NewNetBetween(a, b net.IP) (Net, bool, error) {
	if CompareIPs(a, b) != -1 {
		return nil, false, ErrNoValidRange
	}

	if EffectiveVersion(a) != EffectiveVersion(b) {
		return nil, false, ErrNoValidRange
	}

	return fitNetworkBetween(NextIP(a), PreviousIP(b), 1)
}

// ByNet implements sort.Interface for iplib.Net based on the
// starting address of the netblock, with the netmask as a tie breaker. So if
// two Networks are submitted and one is a subset of the other, the enclosing
// network will be returned first.
type ByNet []Net

// Len implements sort.interface Len(), returning the length of the
// ByNetwork array
func (bn ByNet) Len() int {
	return len(bn)
}

// Swap implements sort.interface Swap(), swapping two elements in our array
func (bn ByNet) Swap(a, b int) {
	bn[a], bn[b] = bn[b], bn[a]
}

// Less implements sort.interface Less(), given two elements in the array it
// returns true if the LHS should sort before the RHS. For details on the
// implementation, see CompareNets()
func (bn ByNet) Less(a, b int) bool {
	val := CompareNets(bn[a], bn[b])
	if val == -1 {
		return true
	}
	return false
}

// ParseCIDR returns a new Net object. It is a passthrough to net.ParseCIDR
// and will return any error it generates to the caller. There is one major
// difference between how net.IPNet manages addresses and how ipnet.Net does,
// and this function exposes it: net.ParseCIDR *always* returns an IPv6
// address; if given a v4 address it returns the RFC4291 IPv4-mapped IPv6
// address internally, but treats it like v4 in practice. In contrast
// iplib.ParseCIDR will re-encode it as a v4
func ParseCIDR(s string) (net.IP, Net, error) {
	ip, ipnet, err := net.ParseCIDR(s)
	if err != nil {
		return ip, nil, err
	}
	masklen, _ := ipnet.Mask.Size()

	if strings.Contains(s, ".") {
		return ForceIP4(ip), NewNet4(ForceIP4(ip), masklen), err
	}

	if EffectiveVersion(ip) == 4 && masklen <= 32 {
		return ip, NewNet4(ip, masklen), err
	}

	return ip, NewNet6(ip, masklen, 0), err
}

func fitNetworkBetween(a, b net.IP, mask int) (Net, bool, error) {
	xnet := NewNet(a, mask)

	va := CompareIPs(xnet.FirstAddress(), a)
	vb := CompareIPs(xnet.LastAddress(), b)
	if va >= 0 && vb < 0 {
		return xnet, false, nil
	}
	if va == 0 && vb == 0 {
		return xnet, true, nil
	}
	return fitNetworkBetween(a, b, mask + 1)
}