Repository navigation
Expand file tree
/
Copy pathload.go
More file actions
142 lines (131 loc) · 3.71 KB
/
Copy pathload.go
File metadata and controls
142 lines (131 loc) · 3.71 KB
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
package filter
import (
"context"
"crypto/tls"
"fmt"
"io"
"net"
"net/http"
"net/netip"
"net/url"
"os"
"strings"
"time"
"github.com/coredns/coredns/plugin/pkg/transport"
)
// ListLoader contains the means to retrieve a list
type ListLoader interface {
Load(string) (io.ReadCloser, error)
}
// GetListLoader returns the ListLoader associated with the list's URL scheme
func (a ActionConfig) GetListLoader(uri string) (ListLoader, error) {
listUrl, err := url.Parse(uri)
if err != nil {
return nil, fmt.Errorf("invalid list URL %q; %w", uri, err)
}
// url.Parse does not require that the URL has a schema. Explicitly check
// for a scheme so that we know which loader to use.
if len(listUrl.Scheme) == 0 {
return nil, fmt.Errorf("invalid list URL %q; scheme empty", uri)
}
switch listUrl.Scheme {
case "file":
return a.FileLoader, nil
case "http", "https":
// url.Parse does not require that the URL has a host. Explicitly check
// for a host for network schemes only.
if len(listUrl.Host) == 0 {
return nil, fmt.Errorf("invalid list URL %q; host empty", uri)
}
return a.HTTPLoader, nil
default:
return nil, fmt.Errorf(
"unsupported list URL scheme %q; "+
"expected 'file', 'http', or 'https'",
listUrl.Scheme,
)
}
}
// FileListLoader retrieves lists from the local filesystem
type FileListLoader struct{}
// Load implements ListLoader
func (FileListLoader) Load(path string) (io.ReadCloser, error) {
trimmedPath := strings.TrimPrefix(path, "file://")
file, err := os.Open(trimmedPath)
if err != nil {
return nil, fmt.Errorf("error opening list %q; %w", path, err)
}
return file, nil
}
// HTTPListLoader retrieves lists from remote sources using HTTP/HTTPS
type HTTPListLoader struct {
Network string
ServerName string
ResolverIP netip.AddrPort
}
// Load implements ListLoader
func (h HTTPListLoader) Load(path string) (io.ReadCloser, error) {
client := &http.Client{}
if h.ResolverIP.IsValid() {
dialFunc := func(ctx context.Context, network, address string) (net.Conn, error) {
d := net.Dialer{
Timeout: time.Second * 5,
}
transportNet, err := h.transportToNetwork(h.Network)
if err != nil {
// This shouldn't be reached. See default return in
// HTTPListLoader.transportToNetwork
return nil, err
}
conn, err := d.DialContext(ctx, transportNet, h.ResolverIP.String())
if transportNet == "tcp" {
tlsConfig := &tls.Config{
ServerName: h.ServerName,
}
return tls.Client(conn, tlsConfig), err
}
return conn, err
}
dialerResolver := &net.Resolver{
PreferGo: true,
StrictErrors: true,
Dial: dialFunc,
}
dialer := &net.Dialer{
Resolver: dialerResolver,
}
dialCtx := func(ctx context.Context, network, addr string) (net.Conn, error) {
return dialer.DialContext(ctx, network, addr)
}
client.Transport = &http.Transport{
DialContext: dialCtx,
}
}
resp, err := client.Get(path)
if err != nil {
return nil, fmt.Errorf("error fetching list %q; %w", path, err)
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf(
"an error occurred fetching list %q; %s",
path,
resp.Status,
)
}
return resp.Body, nil
}
// convert the dns transport type to the corresponding network used by a dialer.
// loadTransport is expected to be a constant from
// github.com/coredns/coredns/plugin/pkg/transport
func (h HTTPListLoader) transportToNetwork(loadTransport string) (string, error) {
switch loadTransport {
case transport.DNS:
return "udp", nil
case transport.TLS:
return "tcp", nil
default:
// This error shouldn't be reached in normal circumstances since the
// Corefile parser only sets DNS or TLS transport
return "", fmt.Errorf("unknown listresolver transport %q", loadTransport)
}
}