mirror of
https://github.com/m13253/dns-over-https.git
synced 2026-03-31 11:55:38 +00:00
Compare commits
15 Commits
m13253/res
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
82c5f0d327 | ||
|
|
602b3d6322 | ||
|
|
db0bd43256 | ||
|
|
06e3d67f79 | ||
|
|
d27aef852d | ||
|
|
6c561eb412 | ||
|
|
381bf28a69 | ||
|
|
0b0651a015 | ||
|
|
3130a747f8 | ||
|
|
fe9f9f9ad2 | ||
|
|
00c6af00ed | ||
|
|
04f3e029ac | ||
|
|
87b3eedded | ||
|
|
c57a45deaa | ||
|
|
dfba0c36c5 |
2
.github/workflows/go.yml
vendored
2
.github/workflows/go.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.24.3
|
go-version: 1.25.6
|
||||||
id: go
|
id: go
|
||||||
|
|
||||||
- name: Check out repository
|
- name: Check out repository
|
||||||
|
|||||||
@@ -90,6 +90,29 @@ func NewClient(conf *config.Config) (c *Client, err error) {
|
|||||||
Net: "tcp",
|
Net: "tcp",
|
||||||
Timeout: time.Duration(conf.Other.Timeout) * time.Second,
|
Timeout: time.Duration(conf.Other.Timeout) * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.conf.Other.Interface != "" {
|
||||||
|
localV4, localV6, err := c.getInterfaceIPs()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get interface IPs for %s: %v", c.conf.Other.Interface, err)
|
||||||
|
}
|
||||||
|
var localAddr net.IP
|
||||||
|
if localV4 != nil {
|
||||||
|
localAddr = localV4
|
||||||
|
} else {
|
||||||
|
localAddr = localV6
|
||||||
|
}
|
||||||
|
|
||||||
|
c.udpClient.Dialer = &net.Dialer{
|
||||||
|
Timeout: time.Duration(conf.Other.Timeout) * time.Second,
|
||||||
|
LocalAddr: &net.UDPAddr{IP: localAddr},
|
||||||
|
}
|
||||||
|
c.tcpClient.Dialer = &net.Dialer{
|
||||||
|
Timeout: time.Duration(conf.Other.Timeout) * time.Second,
|
||||||
|
LocalAddr: &net.TCPAddr{IP: localAddr},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, addr := range conf.Listen {
|
for _, addr := range conf.Listen {
|
||||||
c.udpServers = append(c.udpServers, &dns.Server{
|
c.udpServers = append(c.udpServers, &dns.Server{
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
@@ -120,6 +143,38 @@ func NewClient(conf *config.Config) (c *Client, err error) {
|
|||||||
PreferGo: true,
|
PreferGo: true,
|
||||||
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
var d net.Dialer
|
var d net.Dialer
|
||||||
|
if c.conf.Other.Interface != "" {
|
||||||
|
localV4, localV6, err := c.getInterfaceIPs()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Bootstrap dial warning: %v", err)
|
||||||
|
} else {
|
||||||
|
numServers := len(c.bootstrap)
|
||||||
|
bootstrap := c.bootstrap[rand.Intn(numServers)]
|
||||||
|
host, _, _ := net.SplitHostPort(bootstrap)
|
||||||
|
ip := net.ParseIP(host)
|
||||||
|
if ip != nil {
|
||||||
|
if ip.To4() != nil {
|
||||||
|
if localV4 != nil {
|
||||||
|
if strings.HasPrefix(network, "udp") {
|
||||||
|
d.LocalAddr = &net.UDPAddr{IP: localV4}
|
||||||
|
} else {
|
||||||
|
d.LocalAddr = &net.TCPAddr{IP: localV4}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if localV6 != nil {
|
||||||
|
if strings.HasPrefix(network, "udp") {
|
||||||
|
d.LocalAddr = &net.UDPAddr{IP: localV6}
|
||||||
|
} else {
|
||||||
|
d.LocalAddr = &net.TCPAddr{IP: localV6}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn, err := d.DialContext(ctx, network, bootstrap)
|
||||||
|
return conn, err
|
||||||
|
}
|
||||||
|
}
|
||||||
numServers := len(c.bootstrap)
|
numServers := len(c.bootstrap)
|
||||||
bootstrap := c.bootstrap[rand.Intn(numServers)]
|
bootstrap := c.bootstrap[rand.Intn(numServers)]
|
||||||
conn, err := d.DialContext(ctx, network, bootstrap)
|
conn, err := d.DialContext(ctx, network, bootstrap)
|
||||||
@@ -235,14 +290,72 @@ func (c *Client) newHTTPClient() error {
|
|||||||
if c.httpTransport != nil {
|
if c.httpTransport != nil {
|
||||||
c.httpTransport.CloseIdleConnections()
|
c.httpTransport.CloseIdleConnections()
|
||||||
}
|
}
|
||||||
dialer := &net.Dialer{
|
|
||||||
|
localV4, localV6, err := c.getInterfaceIPs()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Interface binding error: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
baseDialer := &net.Dialer{
|
||||||
Timeout: time.Duration(c.conf.Other.Timeout) * time.Second,
|
Timeout: time.Duration(c.conf.Other.Timeout) * time.Second,
|
||||||
KeepAlive: 30 * time.Second,
|
KeepAlive: 30 * time.Second,
|
||||||
// DualStack: true,
|
Resolver: c.bootstrapResolver,
|
||||||
Resolver: c.bootstrapResolver,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.httpTransport = &http.Transport{
|
c.httpTransport = &http.Transport{
|
||||||
DialContext: dialer.DialContext,
|
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
|
if c.conf.Other.Interface == "" {
|
||||||
|
return baseDialer.DialContext(ctx, network, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if network == "tcp4" && localV4 != nil {
|
||||||
|
d := *baseDialer
|
||||||
|
d.LocalAddr = &net.TCPAddr{IP: localV4}
|
||||||
|
return d.DialContext(ctx, network, addr)
|
||||||
|
}
|
||||||
|
if network == "tcp6" && localV6 != nil {
|
||||||
|
d := *baseDialer
|
||||||
|
d.LocalAddr = &net.TCPAddr{IP: localV6}
|
||||||
|
return d.DialContext(ctx, network, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manual Dual-Stack: Resolve host and try compatible families sequentially
|
||||||
|
host, port, _ := net.SplitHostPort(addr)
|
||||||
|
ips, err := c.bootstrapResolver.LookupIPAddr(ctx, host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastErr error
|
||||||
|
for _, ip := range ips {
|
||||||
|
d := *baseDialer
|
||||||
|
targetAddr := net.JoinHostPort(ip.String(), port)
|
||||||
|
|
||||||
|
if ip.IP.To4() != nil {
|
||||||
|
if localV4 == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
d.LocalAddr = &net.TCPAddr{IP: localV4}
|
||||||
|
} else {
|
||||||
|
if localV6 == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
d.LocalAddr = &net.TCPAddr{IP: localV6}
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := d.DialContext(ctx, "tcp", targetAddr)
|
||||||
|
if err == nil {
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
lastErr = err
|
||||||
|
}
|
||||||
|
|
||||||
|
if lastErr != nil {
|
||||||
|
return nil, lastErr
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("connection to %s failed: no matching local/remote IP families on interface %s", addr, c.conf.Other.Interface)
|
||||||
|
},
|
||||||
ExpectContinueTimeout: 1 * time.Second,
|
ExpectContinueTimeout: 1 * time.Second,
|
||||||
IdleConnTimeout: 90 * time.Second,
|
IdleConnTimeout: 90 * time.Second,
|
||||||
MaxIdleConns: 100,
|
MaxIdleConns: 100,
|
||||||
@@ -251,15 +364,18 @@ func (c *Client) newHTTPClient() error {
|
|||||||
TLSHandshakeTimeout: time.Duration(c.conf.Other.Timeout) * time.Second,
|
TLSHandshakeTimeout: time.Duration(c.conf.Other.Timeout) * time.Second,
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: c.conf.Other.TLSInsecureSkipVerify},
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: c.conf.Other.TLSInsecureSkipVerify},
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.conf.Other.NoIPv6 {
|
if c.conf.Other.NoIPv6 {
|
||||||
|
originalDial := c.httpTransport.DialContext
|
||||||
c.httpTransport.DialContext = func(ctx context.Context, network, address string) (net.Conn, error) {
|
c.httpTransport.DialContext = func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
if strings.HasPrefix(network, "tcp") {
|
if strings.HasPrefix(network, "tcp") {
|
||||||
network = "tcp4"
|
network = "tcp4"
|
||||||
}
|
}
|
||||||
return dialer.DialContext(ctx, network, address)
|
return originalDial(ctx, network, address)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err := http2.ConfigureTransport(c.httpTransport)
|
|
||||||
|
err = http2.ConfigureTransport(c.httpTransport)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -485,3 +601,38 @@ func (c *Client) findClientIP(w dns.ResponseWriter, r *dns.Msg) (ednsClientAddre
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getInterfaceIPs returns the first valid IPv4 and IPv6 addresses found on the interface
|
||||||
|
func (c *Client) getInterfaceIPs() (v4, v6 net.IP, err error) {
|
||||||
|
if c.conf.Other.Interface == "" {
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
|
ifi, err := net.InterfaceByName(c.conf.Other.Interface)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
addrs, err := ifi.Addrs()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addr := range addrs {
|
||||||
|
ip, _, err := net.ParseCIDR(addr.String())
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ip4 := ip.To4(); ip4 != nil {
|
||||||
|
if v4 == nil {
|
||||||
|
v4 = ip4
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if v6 == nil && !c.conf.Other.NoIPv6 {
|
||||||
|
v6 = ip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v4 == nil && v6 == nil {
|
||||||
|
return nil, nil, fmt.Errorf("no valid IP addresses found on interface %s", c.conf.Other.Interface)
|
||||||
|
}
|
||||||
|
return v4, v6, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ type others struct {
|
|||||||
Bootstrap []string `toml:"bootstrap"`
|
Bootstrap []string `toml:"bootstrap"`
|
||||||
Passthrough []string `toml:"passthrough"`
|
Passthrough []string `toml:"passthrough"`
|
||||||
Timeout uint `toml:"timeout"`
|
Timeout uint `toml:"timeout"`
|
||||||
|
Interface string `toml:"interface"`
|
||||||
NoCookies bool `toml:"no_cookies"`
|
NoCookies bool `toml:"no_cookies"`
|
||||||
NoECS bool `toml:"no_ecs"`
|
NoECS bool `toml:"no_ecs"`
|
||||||
NoIPv6 bool `toml:"no_ipv6"`
|
NoIPv6 bool `toml:"no_ipv6"`
|
||||||
|
|||||||
@@ -81,7 +81,9 @@ passthrough = [
|
|||||||
"captive.apple.com",
|
"captive.apple.com",
|
||||||
"connectivitycheck.gstatic.com",
|
"connectivitycheck.gstatic.com",
|
||||||
"detectportal.firefox.com",
|
"detectportal.firefox.com",
|
||||||
|
"globalreachtech.com",
|
||||||
"msftconnecttest.com",
|
"msftconnecttest.com",
|
||||||
|
"network-auth.com",
|
||||||
"nmcheck.gnome.org",
|
"nmcheck.gnome.org",
|
||||||
|
|
||||||
"pool.ntp.org",
|
"pool.ntp.org",
|
||||||
@@ -95,6 +97,11 @@ passthrough = [
|
|||||||
# Timeout for upstream request in seconds
|
# Timeout for upstream request in seconds
|
||||||
timeout = 30
|
timeout = 30
|
||||||
|
|
||||||
|
# Interface to bind to for outgoing connections.
|
||||||
|
# If empty, the system default route is used (usually eth0 or wlan0).
|
||||||
|
# Example: "eth1", "wlan0"
|
||||||
|
interface = ""
|
||||||
|
|
||||||
# Disable HTTP Cookies
|
# Disable HTTP Cookies
|
||||||
#
|
#
|
||||||
# Cookies may be useful if your upstream resolver is protected by some
|
# Cookies may be useful if your upstream resolver is protected by some
|
||||||
|
|||||||
18
go.mod
18
go.mod
@@ -1,20 +1,20 @@
|
|||||||
module github.com/m13253/dns-over-https/v2
|
module github.com/m13253/dns-over-https/v2
|
||||||
|
|
||||||
go 1.24
|
go 1.24.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/BurntSushi/toml v1.5.0
|
github.com/BurntSushi/toml v1.6.0
|
||||||
github.com/gorilla/handlers v1.5.2
|
github.com/gorilla/handlers v1.5.2
|
||||||
github.com/infobloxopen/go-trees v0.0.0-20221216143356-66ceba885ebc
|
github.com/infobloxopen/go-trees v0.0.0-20221216143356-66ceba885ebc
|
||||||
github.com/miekg/dns v1.1.66
|
github.com/miekg/dns v1.1.70
|
||||||
golang.org/x/net v0.40.0
|
golang.org/x/net v0.49.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
golang.org/x/mod v0.24.0 // indirect
|
golang.org/x/mod v0.32.0 // indirect
|
||||||
golang.org/x/sync v0.14.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.33.0 // indirect
|
golang.org/x/sys v0.40.0 // indirect
|
||||||
golang.org/x/text v0.25.0 // indirect
|
golang.org/x/text v0.33.0 // indirect
|
||||||
golang.org/x/tools v0.33.0 // indirect
|
golang.org/x/tools v0.41.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
34
go.sum
34
go.sum
@@ -1,5 +1,5 @@
|
|||||||
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
|
||||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
@@ -8,21 +8,23 @@ github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyE
|
|||||||
github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
|
github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
|
||||||
github.com/infobloxopen/go-trees v0.0.0-20221216143356-66ceba885ebc h1:RhT2pjLo3EVRmldbEcBdeRA7CGPWsNEJC+Y/N1aXQbg=
|
github.com/infobloxopen/go-trees v0.0.0-20221216143356-66ceba885ebc h1:RhT2pjLo3EVRmldbEcBdeRA7CGPWsNEJC+Y/N1aXQbg=
|
||||||
github.com/infobloxopen/go-trees v0.0.0-20221216143356-66ceba885ebc/go.mod h1:BaIJzjD2ZnHmx2acPF6XfGLPzNCMiBbMRqJr+8/8uRI=
|
github.com/infobloxopen/go-trees v0.0.0-20221216143356-66ceba885ebc/go.mod h1:BaIJzjD2ZnHmx2acPF6XfGLPzNCMiBbMRqJr+8/8uRI=
|
||||||
github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE=
|
github.com/miekg/dns v1.1.70 h1:DZ4u2AV35VJxdD9Fo9fIWm119BsQL5cZU1cQ9s0LkqA=
|
||||||
github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE=
|
github.com/miekg/dns v1.1.70/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
|
||||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
|
||||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
||||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
||||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
||||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
||||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
|
||||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
|||||||
Reference in New Issue
Block a user