-
Notifications
You must be signed in to change notification settings - Fork 394
/
excluder.go
173 lines (147 loc) · 4.33 KB
/
excluder.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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package trust
import (
"net"
"strconv"
"strings"
"github.com/zeebo/errs"
"storj.io/common/storj"
)
var (
// ErrExclusion is an error class for exclusion related errors.
ErrExclusion = errs.Class("exclusion")
)
// NewExcluder takes a configuration string and returns an excluding Rule.
// Accepted forms are 1) a Satellite ID followed by '@', 2) a hostname or IP
// address, 3) a full Satellite URL.
func NewExcluder(config string) (Rule, error) {
url, err := parseExcluderConfig(config)
if err != nil {
return nil, err
}
switch {
case url.Host == "":
return NewIDExcluder(url.ID), nil
case url.ID.IsZero():
return NewHostExcluder(url.Host), nil
default:
return NewURLExcluder(url), nil
}
}
// URLExcluder excludes matching URLs.
type URLExcluder struct {
url SatelliteURL
}
// NewURLExcluder returns a new URLExcluder.
func NewURLExcluder(url SatelliteURL) *URLExcluder {
url.Host = normalizeHost(url.Host)
return &URLExcluder{
url: url,
}
}
// IsTrusted returns true if the given Satellite is trusted and false otherwise.
func (excluder *URLExcluder) IsTrusted(url SatelliteURL) bool {
url.Host = normalizeHost(url.Host)
return excluder.url != url
}
// String returns a string representation of the excluder.
func (excluder *URLExcluder) String() string {
return excluder.url.String()
}
// IDExcluder excludes URLs matching a given URL.
type IDExcluder struct {
id storj.NodeID
}
// NewIDExcluder returns a new IDExcluder.
func NewIDExcluder(id storj.NodeID) *IDExcluder {
return &IDExcluder{
id: id,
}
}
// IsTrusted returns true if the given Satellite is trusted and false otherwise.
func (excluder *IDExcluder) IsTrusted(url SatelliteURL) bool {
return excluder.id != url.ID
}
// String returns a string representation of the excluder.
func (excluder *IDExcluder) String() string {
return excluder.id.String() + "@"
}
// HostExcluder excludes URLs that match a given host. If the host is a domain
// name then URLs in a subdomain of that domain are excluded as well.
type HostExcluder struct {
host string
suffix string
}
// NewHostExcluder returns a new HostExcluder.
func NewHostExcluder(host string) *HostExcluder {
host = normalizeHost(host)
// If it appears to be a domain name (i.e. has a dot) then configure the
// suffix as well
var suffix string
if strings.ContainsRune(host, '.') {
suffix = "." + host
}
return &HostExcluder{
host: host,
suffix: suffix,
}
}
// IsTrusted returns true if the given Satellite is trusted and false otherwise.
func (excluder *HostExcluder) IsTrusted(url SatelliteURL) bool {
host := normalizeHost(url.Host)
if excluder.host == host {
return false
}
if excluder.suffix != "" && strings.HasSuffix(host, excluder.suffix) {
return false
}
return true
}
// String returns a string representation of the excluder.
func (excluder *HostExcluder) String() string {
return excluder.host
}
// parseExcluderConfig parses a excluder configuration. The following forms are accepted:
// - Satellite ID followed by @
// - Satellite host
// - Full Satellite URL (i.e. id@host:port).
func parseExcluderConfig(s string) (SatelliteURL, error) {
url, err := storj.ParseNodeURL(s)
if err != nil {
return SatelliteURL{}, ErrExclusion.Wrap(err)
}
switch {
case url.ID.IsZero() && url.Address != "":
// Just the address was specified. Ensure it does not have a port.
_, _, err := net.SplitHostPort(url.Address)
if err == nil {
return SatelliteURL{}, ErrExclusion.New("host exclusion must not include a port")
}
return SatelliteURL{
Host: url.Address,
}, nil
case !url.ID.IsZero() && url.Address == "":
// Just the ID was specified.
return SatelliteURL{
ID: url.ID,
}, nil
}
// storj.ParseNodeURL will have already verified that the address is
// well-formed, so if SplitHostPort fails it should be due to the address
// not having a port.
host, portStr, err := net.SplitHostPort(url.Address)
if err != nil {
return SatelliteURL{}, ErrExclusion.New("satellite URL exclusion must specify a port")
}
// Port should already be numeric so this shouldn't fail, but just in case.
port, err := strconv.Atoi(portStr)
if err != nil {
return SatelliteURL{}, ErrExclusion.New("satellite URL exclusion port is not numeric")
}
return SatelliteURL{
ID: url.ID,
Host: host,
Port: port,
}, nil
}