-
Notifications
You must be signed in to change notification settings - Fork 1
/
parse.go
119 lines (94 loc) · 2.71 KB
/
parse.go
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
116
117
118
119
// Package mergeips provides a way to convert list of IP randes definitions,
// like individual IPs, CIDR subners and begin-end ranges to the minimal list of net IPNet
package mergeips
import (
"bytes"
"errors"
"fmt"
"net"
"strings"
"github.com/Djarvur/go-mergeips/ipnet"
"github.com/Djarvur/go-mergeips/iprange"
)
// Errors
var (
ErrInputInvalid = errors.New("invalid input")
)
// Scanner is a simple interface to support Scan() function.
// Intentionnaly compatible with bufio.Scanner
type Scanner interface {
Scan() bool
Text() string
Err() error
}
// Scan is used to parse source to the list of net.IPNet
func Scan(s Scanner) (res []*net.IPNet, err error) {
for s.Scan() {
subnets, err := Parse(s.Text(), false) // nolint: govet
if err != nil {
return nil, err
}
res = append(res, subnets...)
}
if err = s.Err(); err != nil {
return nil, err
}
return res, nil
}
// Parse parses a string to net.IPNet
// String might be in 3 forms:
// ip address itself, in v4 or v6 notation
// CIDR subnet address, v4 or v6
// IP adresses range, v4 or v6, in form begin-end
// If strict is false CIDR form subnet could be defined with not-a-first addrsss in the subnet.
// Otherwise the error will be returned
func Parse(s string, strict bool) ([]*net.IPNet, error) {
fields := strings.Split(s, "/")
if len(fields) > 2 {
return nil, fmt.Errorf("%q: %w", s, ErrInputInvalid)
}
if len(fields) == 2 {
return parseCIDR(s, strict)
}
fields = strings.Split(s, "-")
if len(fields) > 2 {
return nil, fmt.Errorf("%q: %w", s, ErrInputInvalid)
}
if len(fields) == 2 {
return parseRange(fields[0], fields[1])
}
return parseIP(s)
}
// Merge merges list of net.IPNet to the smallest possible set
func Merge(nets []*net.IPNet) []*net.IPNet {
return ipnet.MergeSorted(ipnet.DedupSorted(ipnet.Sort(nets)))
}
func parseCIDR(s string, strict bool) ([]*net.IPNet, error) {
ip, n, err := net.ParseCIDR(s)
if err != nil || (strict && !ip.Equal(n.IP)) {
return nil, fmt.Errorf("%q: %w", s, ErrInputInvalid)
}
return []*net.IPNet{n}, nil
}
func parseRange(beginString string, endString string) ([]*net.IPNet, error) {
var (
begin = net.ParseIP(beginString)
end = net.ParseIP(endString)
)
if begin == nil || end == nil || (begin.To4() == nil) != (end.To4() == nil) || bytes.Compare(begin, end) > 0 {
return nil, fmt.Errorf("%q-%q: %w", beginString, endString, ErrInputInvalid)
}
return iprange.Merge(begin, end), nil
}
func parseIP(s string) ([]*net.IPNet, error) {
begin := net.ParseIP(s)
if begin == nil {
return nil, fmt.Errorf("%q: %w", s, ErrInputInvalid)
}
bits := 128
if ipV4 := begin.To4(); ipV4 != nil {
bits = 32
begin = ipV4
}
return []*net.IPNet{{IP: begin, Mask: net.CIDRMask(bits, bits)}}, nil
}