-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathresolver.go
More file actions
165 lines (155 loc) · 4.47 KB
/
Copy pathresolver.go
File metadata and controls
165 lines (155 loc) · 4.47 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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package gonnect
import (
"context"
"errors"
"net"
"os"
"strings"
)
// DnsServer specifies a custom DNS server to use for resolution.
type DnsServer struct {
// Net is the network type to use (e.g., "udp", "tcp").
// If empty, defaults to "udp".
Net string
// Addr is the address of the DNS server (e.g., "8.8.8.8:53" or "1.1.1.1").
// By default port is 53 so it can be omitted.
Addr string
}
// net returns the network type, defaulting to "udp" if not specified.
func (s *DnsServer) net() string {
if s.Net == "" {
return "udp"
}
return s.Net
}
func (s *DnsServer) addr() string {
host, port, err := net.SplitHostPort(s.Addr)
if err != nil {
return net.JoinHostPort(s.Addr, "53")
}
if strings.ToLower(port) == "dns" {
return net.JoinHostPort(host, "53")
}
return s.Addr
}
// ResolverCfg configures a DNS resolver.
type ResolverCfg struct {
// DontPreferGo disables the use of Go's pure Go DNS resolver.
// When false (default), PreferGo is enabled in the built resolver.
DontPreferGo bool
// StrictErrors controls whether errors from the DNS server are fatal.
// When true, errors stop resolution immediately.
StrictErrors bool
// Dial is an optional custom dial function for establishing connections.
// If nil, uses net.Dialer.DialContext.
Dial Dial
// Server is an optional custom DNS server to use for resolution.
// If nil, uses the system default DNS servers.
Server *DnsServer
}
// Build creates and returns a configured net.Resolver instance.
// If Server is set, all DNS queries are routed through that server.
// If Dial is set, it is used for establishing connections instead of the default dialer.
func (cfg ResolverCfg) Build() net.Resolver {
return net.Resolver{
PreferGo: !cfg.DontPreferGo,
StrictErrors: cfg.StrictErrors,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
if cfg.Server == nil {
return cfg.dial(ctx, network, address)
} else {
return cfg.dial(ctx, cfg.Server.net(), cfg.Server.addr())
}
},
}
}
// dial establishes a connection using the custom Dial function if provided,
// otherwise falls back to net.Dialer.DialContext.
func (cfg ResolverCfg) dial(
ctx context.Context, network, address string,
) (net.Conn, error) {
if cfg.Dial == nil {
return (&net.Dialer{}).DialContext(ctx, network, address)
} else {
return cfg.Dial(ctx, network, address)
}
}
// wellKnownPorts contains a fallback table of common service names to port numbers.
var wellKnownPorts = map[string]map[string]int{
"tcp": {
"http": 80,
"https": 443,
"tls": 443,
"ssl": 443,
"ssh": 22,
"ftp": 21,
"smtp": 25,
"dns": 53,
"pop3": 110,
"pop": 110,
"imap": 143,
"telnet": 23,
"socks": 1080,
"mysql": 3306,
"postgres": 5432,
"redis": 6379,
"mongodb": 27017,
},
"udp": {
"dns": 53,
"dhcp": 67,
"ntp": 123,
},
}
// LookupPortOffline resolve port name to port (e.g. http to 80) using only
// local data.
func LookupPortOffline(network, service string) (port int, err error) {
// Dirty hack
r := &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, _, _ string) (net.Conn, error) {
return nil, errors.New("BLOCKED")
},
}
port, err = r.LookupPort(context.Background(), network, service)
if err != nil {
// Fallback to hardcoded well-known ports
service = strings.ToLower(service)
network = strings.ToLower(network)
if ports, ok := wellKnownPorts[network]; ok {
if p, ok := ports[service]; ok {
return p, nil
}
}
err = &net.DNSError{
Err: "unknown port",
Name: network + "/" + service,
IsNotFound: true,
}
}
return
}
// UnfuckGoDns sets GODEBUG=netedns0=0 to disable EDNS0 extension unsupported
// by a lot of consumer routers but still used by go resolver by default for
// some fucking reason.
// And there is no ways to disable it with Resolver methods/fields or something.
//
// Some related links:
// - https://pkg.go.dev/net#hdr-Name_Resolution
// - https://github.com/golang/go/issues/67925
// - https://github.com/moby/moby/issues/47923
//
// It is safe to use UnfuckGoDns with already set GODEBUG var.
// UnfuckGoDns only appends netedns0=0 if netedns0 is not set.
// It doesn't alter other fields and do nothing if netedns0 is already set.
func UnfuckGoDns() {
godebug := os.Getenv("GODEBUG")
if strings.Contains(godebug, "netedns0=") {
return
}
if godebug == "" {
_ = os.Setenv("GODEBUG", "netedns0=0")
} else {
_ = os.Setenv("GODEBUG", godebug+",netedns0=0")
}
}