-
Notifications
You must be signed in to change notification settings - Fork 250
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
f8f22ab
commit 2973223
Showing
8 changed files
with
360 additions
and
210 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package bootstrap | ||
|
||
import ( | ||
"context" | ||
"net" | ||
"net/netip" | ||
"net/url" | ||
"time" | ||
|
||
"github.com/AdguardTeam/golibs/errors" | ||
"github.com/AdguardTeam/golibs/log" | ||
"github.com/AdguardTeam/golibs/netutil" | ||
) | ||
|
||
// DialHandler is a dial function for creating unencrypted network connections | ||
// to the upstream server. It establishes the connection to the server | ||
// specified at initialization and ignores the addr. | ||
type DialHandler func(ctx context.Context, network, addr string) (conn net.Conn, err error) | ||
|
||
func ResolveDialContext( | ||
u *url.URL, | ||
timeout time.Duration, | ||
resolvers []Resolver, | ||
) (h DialHandler, err error) { | ||
host, port, err := netutil.SplitHostPort(u.Host) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var ctx context.Context | ||
if timeout > 0 { | ||
var cancel func() | ||
ctx, cancel = context.WithTimeout(context.Background(), timeout) | ||
defer cancel() | ||
} else { | ||
ctx = context.Background() | ||
} | ||
|
||
addrs, err := LookupParallel(ctx, resolvers, host) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var resolverAddresses []string | ||
for _, addr := range addrs { | ||
addrPort := netip.AddrPortFrom(addr, uint16(port)) | ||
resolverAddresses = append(resolverAddresses, addrPort.String()) | ||
} | ||
|
||
return NewDialContext(timeout, resolverAddresses...), nil | ||
} | ||
|
||
func NewDialContext(timeout time.Duration, addrs ...string) (h DialHandler) { | ||
dialer := &net.Dialer{ | ||
Timeout: timeout, | ||
} | ||
|
||
if len(addrs) == 0 { | ||
return func(ctx context.Context, network, addr string) (net.Conn, error) { | ||
return nil, errors.Error("no addresses") | ||
} | ||
} | ||
|
||
return func(ctx context.Context, network, _ string) (net.Conn, error) { | ||
var errs []error | ||
|
||
// Return first connection without error. | ||
// | ||
// Note that we're using addrs instead of what's passed to the function. | ||
for _, addr := range addrs { | ||
log.Tracef("Dialing to %s", addr) | ||
start := time.Now() | ||
conn, err := dialer.DialContext(ctx, network, addr) | ||
elapsed := time.Since(start) | ||
if err == nil { | ||
log.Tracef( | ||
"dialer has successfully initialized connection to %s in %s", | ||
addr, | ||
elapsed, | ||
) | ||
|
||
return conn, nil | ||
} | ||
|
||
errs = append(errs, err) | ||
|
||
log.Tracef( | ||
"dialer failed to initialize connection to %s, in %s, cause: %s", | ||
addr, | ||
elapsed, | ||
err, | ||
) | ||
} | ||
|
||
return nil, errors.List("all dialers failed", errs...) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
package bootstrap | ||
|
||
import ( | ||
"context" | ||
"net/netip" | ||
"time" | ||
|
||
"github.com/AdguardTeam/golibs/errors" | ||
"github.com/AdguardTeam/golibs/log" | ||
) | ||
|
||
// Resolver resolves the hostnames to IP addresses. | ||
type Resolver interface { | ||
// LookupIPAddr looks up the IP addresses for the given host. network must | ||
// be one of "ip", "ip4" or "ip6". | ||
LookupNetIP(ctx context.Context, network string, host string) (addrs []netip.Addr, err error) | ||
} | ||
|
||
// LookupParallel tries to lookup for ip of host with all resolvers | ||
// concurrently. | ||
func LookupParallel( | ||
ctx context.Context, | ||
resolvers []Resolver, | ||
host string, | ||
) (addrs []netip.Addr, err error) { | ||
resolversNum := len(resolvers) | ||
switch resolversNum { | ||
case 0: | ||
return nil, errors.Error("no resolvers specified") | ||
case 1: | ||
addrs, err = lookup(ctx, resolvers[0], host) | ||
|
||
return addrs, err | ||
default: | ||
// Go on. | ||
} | ||
|
||
// Size of channel must accommodate results of lookups from all resolvers, | ||
// sending into channel will be block otherwise. | ||
ch := make(chan *lookupResult, resolversNum) | ||
for _, res := range resolvers { | ||
go lookupAsync(ctx, res, host, ch) | ||
} | ||
|
||
var errs []error | ||
for n := 0; n < resolversNum; n++ { | ||
result := <-ch | ||
if result.err != nil { | ||
errs = append(errs, result.err) | ||
|
||
continue | ||
} | ||
|
||
return result.addrs, nil | ||
} | ||
|
||
return nil, errors.List("all resolvers failed", errs...) | ||
} | ||
|
||
// lookupResult is a structure that represents the result of a lookup. | ||
type lookupResult struct { | ||
err error | ||
addrs []netip.Addr | ||
} | ||
|
||
// lookupAsync tries to lookup for ip of host with r and sends the result into | ||
// resCh. | ||
func lookupAsync(ctx context.Context, r Resolver, host string, resCh chan *lookupResult) { | ||
addrs, err := lookup(ctx, r, host) | ||
resCh <- &lookupResult{ | ||
err: err, | ||
addrs: addrs, | ||
} | ||
} | ||
|
||
// lookup tries to lookup ip of host with r. | ||
func lookup(ctx context.Context, r Resolver, host string) (addrs []netip.Addr, err error) { | ||
start := time.Now() | ||
addrs, err = r.LookupNetIP(ctx, "ip", host) | ||
elapsed := time.Since(start) | ||
if err != nil { | ||
log.Debug("lookup for %s failed in %s: %s", host, elapsed, err) | ||
} else { | ||
log.Debug("lookup for %s succeeded in %s, result: %s", host, elapsed, addrs) | ||
} | ||
|
||
return addrs, err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.