-
Notifications
You must be signed in to change notification settings - Fork 247
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
optimised finding server cert #4148
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,12 @@ | ||
package common | ||
|
||
import "runtime" | ||
|
||
const ( | ||
AndroidPlatform = "android" | ||
WindowsPlatform = "windows" | ||
) | ||
|
||
func OperatingSystemIs(targetOS string) bool { | ||
return runtime.GOOS == targetOS | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,10 +8,10 @@ import ( | |
"encoding/pem" | ||
"fmt" | ||
"io" | ||
"net" | ||
"net/http" | ||
"net/http/cookiejar" | ||
"net/url" | ||
"time" | ||
|
||
"go.uber.org/zap" | ||
|
||
|
@@ -38,31 +38,52 @@ type BaseClient struct { | |
challengeTaker *ChallengeTaker | ||
} | ||
|
||
func findServerCert(c *ConnectionParams) (*url.URL, *x509.Certificate, error) { | ||
netIps, err := server.FindReachableAddressesForPairingClient(c.netIPs) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
func findServerCert(c *ConnectionParams, reachableIPs []net.IP) (*url.URL, *x509.Certificate, error) { | ||
var baseAddress *url.URL | ||
var serverCert *x509.Certificate | ||
var certErrs error | ||
for _, ip := range netIps { | ||
u := c.BuildURL(ip) | ||
|
||
serverCert, err = getServerCert(u) | ||
if err != nil { | ||
var certErr string | ||
if certErrs != nil { | ||
certErr = certErrs.Error() | ||
|
||
type connectionError struct { | ||
ip net.IP | ||
err error | ||
} | ||
errCh := make(chan connectionError, len(reachableIPs)) | ||
|
||
type result struct { | ||
u *url.URL | ||
cert *x509.Certificate | ||
} | ||
successCh := make(chan result, 1) // as we close on the first success | ||
|
||
for _, ip := range reachableIPs { | ||
go func(ip net.IP) { | ||
u := c.BuildURL(ip) | ||
cert, err := getServerCert(u) | ||
if err != nil { | ||
errCh <- connectionError{ip: ip, err: fmt.Errorf("connecting to '%s' failed: %s", u, err.Error())} | ||
return | ||
} | ||
// If no error, send the results to the success channel | ||
successCh <- result{u: u, cert: cert} | ||
}(ip) | ||
} | ||
|
||
// Keep track of error counts | ||
errorCount := 0 | ||
var combinedErrors string | ||
for { | ||
select { | ||
case success := <-successCh: | ||
baseAddress = success.u | ||
serverCert = success.cert | ||
return baseAddress, serverCert, nil | ||
case ipErr := <-errCh: | ||
errorCount++ | ||
combinedErrors += fmt.Sprintf("IP %s: %s; ", ipErr.ip, ipErr.err) | ||
if errorCount == len(reachableIPs) { | ||
return nil, nil, fmt.Errorf(combinedErrors) | ||
} | ||
certErrs = fmt.Errorf("%sconnecting to '%s' failed: %s; ", certErr, u, err.Error()) | ||
continue | ||
} | ||
|
||
baseAddress = u | ||
break | ||
} | ||
return baseAddress, serverCert, certErrs | ||
} | ||
|
||
// NewBaseClient returns a fully qualified BaseClient from the given ConnectionParams | ||
|
@@ -71,13 +92,19 @@ func NewBaseClient(c *ConnectionParams, logger *zap.Logger) (*BaseClient, error) | |
var serverCert *x509.Certificate | ||
var certErrs error | ||
|
||
netIPs, err := server.FindReachableAddressesForPairingClient(c.netIPs) | ||
if err != nil { | ||
logger.Error("[local pair client] failed to find reachable addresses", zap.Error(err), zap.Any("netIPs", netIPs)) | ||
signal.SendLocalPairingEvent(Event{Type: EventConnectionError, Error: err.Error(), Action: ActionConnect}) | ||
return nil, err | ||
} | ||
|
||
maxRetries := 3 | ||
for i := 0; i < maxRetries; i++ { | ||
baseAddress, serverCert, certErrs = findServerCert(c) | ||
baseAddress, serverCert, certErrs = findServerCert(c, netIPs) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible for the search for a server certificate to last longer? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. each address may last up to 1 second |
||
if serverCert == nil { | ||
certErrs = fmt.Errorf("failed to connect to any of given addresses. %w", certErrs) | ||
time.Sleep(1 * time.Second) | ||
logger.Warn("failed to connect to any of given addresses. Retrying...", zap.Error(certErrs)) | ||
logger.Warn("failed to connect to any of given addresses. Retrying...", zap.Error(certErrs), zap.Any("netIPs", netIPs), zap.Int("retry", i+1)) | ||
} else { | ||
break | ||
} | ||
|
@@ -92,7 +119,7 @@ func NewBaseClient(c *ConnectionParams, logger *zap.Logger) (*BaseClient, error) | |
// No error on the dial out then the URL.Host is accessible | ||
signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess, Action: ActionConnect}) | ||
|
||
err := verifyCert(serverCert, c.publicKey) | ||
err = verifyCert(serverCert, c.publicKey) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
%w
can be used for error directlyThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should use
%w
here since they're notwrap
relation, WDYT? 🙂There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately wrapping only applies to errors on different call depths. In
go 1.20
you can useerrors.Join
https://pkg.go.dev/errors@master#Join for errors generated on the same call depth.At the moment
status-go
is usinggo 1.19
, so we don't have access toerrors.Join
which would be a nice solution.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The desktop app is using
go1.20.4
.