From 34ef0a4ce96e4bbb60667c3cdf54cca366d17357 Mon Sep 17 00:00:00 2001 From: WendelHime <6754291+WendelHime@users.noreply.github.com> Date: Fri, 1 Nov 2024 15:10:40 -0300 Subject: [PATCH 1/2] chore: adding provider ID to op and verify error before failing the op --- fronted.go | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/fronted.go b/fronted.go index 9ec9ca9..af1781f 100644 --- a/fronted.go +++ b/fronted.go @@ -14,6 +14,7 @@ import ( "strings" "sync" "sync/atomic" + "syscall" "time" tls "github.com/refraction-networking/utls" @@ -371,10 +372,13 @@ func (f *fronted) doDial(m MasqueradeInterface) (conn net.Conn, retriable bool, defer op.End() op.Set("masquerade_domain", m.getDomain()) op.Set("masquerade_ip", m.getIpAddress()) + op.Set("masquerade_provider", m.getProviderID()) conn, err = m.dial(f.certPool, f.clientHelloID) if err != nil { - op.FailIf(err) + if !isNetworkUnreachable(err) { + op.FailIf(err) + } log.Debugf("Could not dial to %v, %v", m.getIpAddress(), err) // Don't re-add this candidate if it's any certificate error, as that // will just keep failing and will waste connections. We can't access the underlying @@ -386,12 +390,31 @@ func (f *fronted) doDial(m MasqueradeInterface) (conn net.Conn, retriable bool, log.Debugf("Unexpected error dialing, keeping masquerade: %v", err) retriable = true } - } else { - log.Debugf("Got successful connection to: %v", m) + return } + log.Debugf("Got successful connection to: %+v", m) return } +func isNetworkUnreachable(err error) bool { + var opErr *net.OpError + if errors.As(err, &opErr) { + if errors.Is(opErr.Err, syscall.ENETUNREACH) || errors.Is(opErr.Err, syscall.EHOSTUNREACH) { + return true + } + + // Fallback to message-based checking (works for Windows and Unix-like systems) + errMsg := opErr.Err.Error() + if strings.Contains(errMsg, "network is unreachable") || + strings.Contains(errMsg, "no route to host") || + strings.Contains(errMsg, "unreachable network") || + strings.Contains(errMsg, "unreachable host") { + return true + } + } + return false +} + func verifyPeerCertificate(rawCerts [][]byte, roots *x509.CertPool, domain string) error { if len(rawCerts) == 0 { return fmt.Errorf("no certificates presented") From 18aa1f1eff99794ff19a0d85680f3f2d496c677b Mon Sep 17 00:00:00 2001 From: WendelHime <6754291+WendelHime@users.noreply.github.com> Date: Mon, 4 Nov 2024 11:36:06 -0300 Subject: [PATCH 2/2] fix: add comment explaining about both err handling approaches --- fronted.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fronted.go b/fronted.go index af1781f..37299f7 100644 --- a/fronted.go +++ b/fronted.go @@ -399,11 +399,12 @@ func (f *fronted) doDial(m MasqueradeInterface) (conn net.Conn, retriable bool, func isNetworkUnreachable(err error) bool { var opErr *net.OpError if errors.As(err, &opErr) { + // The following error verifications look for errors that generally happen at Linux/Unix devices if errors.Is(opErr.Err, syscall.ENETUNREACH) || errors.Is(opErr.Err, syscall.EHOSTUNREACH) { return true } - // Fallback to message-based checking (works for Windows and Unix-like systems) + // The string verification errors use a broader approach with errors from windows and also linux/unix devices errMsg := opErr.Err.Error() if strings.Contains(errMsg, "network is unreachable") || strings.Contains(errMsg, "no route to host") ||