Passed
Push — main ( 226e09...0f61b2 )
by Rushan
02:24 queued 12s
created

validate.isPrivateIP   B

Complexity

Conditions 7

Size

Total Lines 15
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 6
nop 1
dl 0
loc 15
rs 8
c 0
b 0
f 0
1
package validate
2
3
import (
4
	"errors"
5
	"net"
6
	"net/url"
7
	"strings"
8
)
9
10
var (
11
	ErrUnexpectedSchema = errors.New("unexpected schema")
12
)
13
14
// URL is used to validate that value is a valid URL string. By default (if no schemas are passed),
15
// the function checks only for the http:// and https:// schemas. Use the schemas argument
16
// to configure the list of expected schemas. If an empty string is passed as a schema, then
17
// URL value may be treated as relative (without schema, e.g. "//example.com").
18
//
19
// If value is not a valid URL the function will return one of the errors:
20
//	• parsing error from url.Parse method if value cannot be parsed as an URL;
21
//	• ErrUnexpectedSchema if schema is not matching one of the listed schemas;
22
//	• ErrInvalid if value is not matching the regular expression.
23
func URL(value string, schemas ...string) error {
24
	if len(schemas) == 0 {
25
		schemas = []string{"http", "https"}
26
	}
27
	u, err := url.Parse(value)
28
	if err != nil {
29
		return err
30
	}
31
32
	err = validateSchema(u, schemas)
33
	if err != nil {
34
		return err
35
	}
36
37
	if !urlRegex.MatchString(value) {
38
		return ErrInvalid
39
	}
40
41
	return nil
42
}
43
44
func validateSchema(u *url.URL, schemas []string) error {
45
	for _, schema := range schemas {
46
		if schema == u.Scheme {
47
			return nil
48
		}
49
	}
50
51
	return ErrUnexpectedSchema
52
}
53
54
// IPRestriction can be used to limit valid IP address values.
55
type IPRestriction func(ip net.IP) bool
56
57
// DenyPrivateIP denies using of private IPs according to RFC 1918 (IPv4 addresses)
58
// and RFC 4193 (IPv6 addresses).
59
func DenyPrivateIP() IPRestriction {
60
	return isPrivateIP
61
}
62
63
// IP validates that a value is a valid IP address (IPv4 or IPv6). You can use a list
64
// of restrictions to additionally check for a restricted range of IPs. For example,
65
// you can deny using private IP addresses using DenyPrivateIP function.
66
//
67
// If value is not valid the function will return one of the errors:
68
//	• ErrInvalid on invalid IP address;
69
//	• ErrProhibited on restricted IP address.
70
func IP(value string, restrictions ...IPRestriction) error {
71
	return validateIP(value, restrictions...)
72
}
73
74
// IPv4 validates that a value is a valid IPv4 address. You can use a list
75
// of restrictions to additionally check for a restricted range of IPs. For example,
76
// you can deny using private IP addresses using DenyPrivateIP function.
77
//
78
// If value is not valid the function will return one of the errors:
79
//	• ErrInvalid on invalid IP address or when using IPv6;
80
//	• ErrProhibited on restricted IP address.
81
func IPv4(value string, restrictions ...IPRestriction) error {
82
	err := validateIP(value, restrictions...)
83
	if err != nil {
84
		return err
85
	}
86
	if !strings.Contains(value, ".") || strings.Contains(value, ":") {
87
		return ErrInvalid
88
	}
89
90
	return nil
91
}
92
93
// IPv6 validates that a value is a valid IPv6 address. You can use a list
94
// of restrictions to additionally check for a restricted range of IPs. For example,
95
// you can deny using private IP addresses using DenyPrivateIP function.
96
//
97
// If value is not valid the function will return one of the errors:
98
//	• ErrInvalid on invalid IP address or when using IPv4;
99
//	• ErrProhibited on restricted IP address.
100
func IPv6(value string, restrictions ...IPRestriction) error {
101
	err := validateIP(value, restrictions...)
102
	if err != nil {
103
		return err
104
	}
105
	if !strings.Contains(value, ":") {
106
		return ErrInvalid
107
	}
108
109
	return nil
110
}
111
112
func validateIP(value string, restrictions ...IPRestriction) error {
113
	ip := net.ParseIP(value)
114
	if ip == nil {
115
		return ErrInvalid
116
	}
117
	for _, isProhibited := range restrictions {
118
		if isProhibited(ip) {
119
			return ErrProhibited
120
		}
121
	}
122
123
	return nil
124
}
125
126
// isPrivateIP reports whether ip is a private address, according to
127
// RFC 1918 (IPv4 addresses) and RFC 4193 (IPv6 addresses).
128
//
129
// This function is ported from golang v1.17.
130
// See https://github.com/golang/go/blob/4c8f48ed4f3db0e3ba376e6b7a261d26b41d8dd0/src/net/ip.go#L133.
131
func isPrivateIP(ip net.IP) bool {
132
	if ip4 := ip.To4(); ip4 != nil {
133
		// Following RFC 1918, Section 3. Private Address Space which says:
134
		//   The Internet Assigned Numbers Authority (IANA) has reserved the
135
		//   following three blocks of the IP address space for private internets:
136
		//     10.0.0.0        -   10.255.255.255  (10/8 prefix)
137
		//     172.16.0.0      -   172.31.255.255  (172.16/12 prefix)
138
		//     192.168.0.0     -   192.168.255.255 (192.168/16 prefix)
139
		return ip4[0] == 10 ||
140
			(ip4[0] == 172 && ip4[1]&0xf0 == 16) ||
141
			(ip4[0] == 192 && ip4[1] == 168)
142
	}
143
	// Following RFC 4193, Section 8. IANA Considerations which says:
144
	//   The IANA has assigned the FC00::/7 prefix to "Unique Local Unicast".
145
	return len(ip) == net.IPv6len && ip[0]&0xfe == 0xfc
146
}
147