mirror of
https://github.com/aykhans/sarin.git
synced 2026-10-17 14:53:52 +00:00
fix: dial IPv6 hosts by enabling dual-stack and trying every resolved address
This commit is contained in:
2 files changed
+48
-22
No files matched your search
+47
-21
@@ -79,6 +79,7 @@ func NewHostClients(
|
|||||||
client := &fasthttp.HostClient{
|
client := &fasthttp.HostClient{
|
||||||
MaxConns: safeUintToInt(maxConns),
|
MaxConns: safeUintToInt(maxConns),
|
||||||
IsTLS: isTLS,
|
IsTLS: isTLS,
|
||||||
|
DialDualStack: true,
|
||||||
TLSConfig: &tls.Config{
|
TLSConfig: &tls.Config{
|
||||||
InsecureSkipVerify: skipVerify, //nolint:gosec
|
InsecureSkipVerify: skipVerify, //nolint:gosec
|
||||||
},
|
},
|
||||||
@@ -164,43 +165,68 @@ func fasthttpSocksDialer(ctx context.Context, proxyURL *url.URL, resolveLocally
|
|||||||
return func(addr string, timeout time.Duration) (net.Conn, error) {
|
return func(addr string, timeout time.Duration) (net.Conn, error) {
|
||||||
deadline := time.Now().Add(timeout)
|
deadline := time.Now().Add(timeout)
|
||||||
|
|
||||||
|
addrs := []string{addr}
|
||||||
if resolveLocally {
|
if resolveLocally {
|
||||||
host, port, err := net.SplitHostPort(addr)
|
var err error
|
||||||
|
addrs, err = resolveIPv4First(ctx, addr, timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, types.NewProxyDialError(proxyStr, err)
|
return nil, types.NewProxyDialError(proxyStr, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
dnsCtx, dnsCancel := context.WithTimeout(ctx, timeout)
|
|
||||||
ips, err := net.DefaultResolver.LookupIP(dnsCtx, "ip", host)
|
|
||||||
dnsCancel()
|
|
||||||
if err != nil {
|
|
||||||
return nil, types.NewProxyDialError(proxyStr, err)
|
|
||||||
}
|
|
||||||
if len(ips) == 0 {
|
|
||||||
return nil, types.NewProxyDialError(proxyStr, types.NewProxyResolveError(host))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the first resolved IP
|
// Try every resolved address, so one unreachable family does not fail the dial.
|
||||||
addr = net.JoinHostPort(ips[0].String(), port)
|
var lastErr error
|
||||||
}
|
for _, candidate := range addrs {
|
||||||
|
|
||||||
// Use remaining time for dial
|
|
||||||
remaining := time.Until(deadline)
|
remaining := time.Until(deadline)
|
||||||
if remaining <= 0 {
|
if remaining <= 0 {
|
||||||
return nil, types.NewProxyDialError(proxyStr, context.DeadlineExceeded)
|
return nil, types.NewProxyDialError(proxyStr, context.DeadlineExceeded)
|
||||||
}
|
}
|
||||||
|
|
||||||
dialCtx, dialCancel := context.WithTimeout(ctx, remaining)
|
dialCtx, dialCancel := context.WithTimeout(ctx, remaining)
|
||||||
defer dialCancel()
|
conn, err := contextDialer.DialContext(dialCtx, "tcp", candidate)
|
||||||
|
dialCancel()
|
||||||
conn, err := contextDialer.DialContext(dialCtx, "tcp", addr)
|
if err == nil {
|
||||||
if err != nil {
|
|
||||||
return nil, types.NewProxyDialError(proxyStr, err)
|
|
||||||
}
|
|
||||||
return conn, nil
|
return conn, nil
|
||||||
|
}
|
||||||
|
lastErr = err
|
||||||
|
}
|
||||||
|
return nil, types.NewProxyDialError(proxyStr, lastErr)
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resolveIPv4First resolves the host of addr into candidate addresses, IPv4 ones first.
|
||||||
|
// It can return the following errors:
|
||||||
|
// - types.ProxyResolveError
|
||||||
|
func resolveIPv4First(ctx context.Context, addr string, timeout time.Duration) ([]string, error) {
|
||||||
|
host, port, err := net.SplitHostPort(addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dnsCtx, dnsCancel := context.WithTimeout(ctx, timeout)
|
||||||
|
ips, err := net.DefaultResolver.LookupIP(dnsCtx, "ip", host)
|
||||||
|
dnsCancel()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(ips) == 0 {
|
||||||
|
return nil, types.NewProxyResolveError(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
addrs := make([]string, 0, len(ips))
|
||||||
|
for _, ip := range ips {
|
||||||
|
if ip.To4() != nil {
|
||||||
|
addrs = append(addrs, net.JoinHostPort(ip.String(), port))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, ip := range ips {
|
||||||
|
if ip.To4() == nil {
|
||||||
|
addrs = append(addrs, net.JoinHostPort(ip.String(), port))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return addrs, nil
|
||||||
|
}
|
||||||
|
|
||||||
// fasthttpConnectDialer creates a dial function that tunnels through an HTTP proxy with CONNECT,
|
// fasthttpConnectDialer creates a dial function that tunnels through an HTTP proxy with CONNECT,
|
||||||
// reaching the proxy over TLS when useTLS is set.
|
// reaching the proxy over TLS when useTLS is set.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ var _ script.HTTPDoer = (*scriptHTTPClient)(nil)
|
|||||||
// - types.ProxyDialError
|
// - types.ProxyDialError
|
||||||
func newScriptHTTPClients(ctx context.Context, proxies []url.URL, maxConns uint) ([]*scriptHTTPClient, error) {
|
func newScriptHTTPClients(ctx context.Context, proxies []url.URL, maxConns uint) ([]*scriptHTTPClient, error) {
|
||||||
if len(proxies) == 0 {
|
if len(proxies) == 0 {
|
||||||
return []*scriptHTTPClient{newScriptHTTPClient(fasthttp.DialTimeout, scriptHTTPDefaultTimeout, maxConns)}, nil
|
return []*scriptHTTPClient{newScriptHTTPClient(fasthttp.DialDualStackTimeout, scriptHTTPDefaultTimeout, maxConns)}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
clients := make([]*scriptHTTPClient, 0, len(proxies))
|
clients := make([]*scriptHTTPClient, 0, len(proxies))
|
||||||
|
|||||||
Reference in new issue
Block a user