Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions diagnose.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"cmp"
"context"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"log/slog"
Expand All @@ -28,6 +29,10 @@ type DiagnoseResult struct {
Region string
// Round-trip latency of the SrvInfo call.
Latency time.Duration
// PeerCertificates is the certificate chain presented by the server
// during the TLS handshake, with the leaf certificate first. Populated
// whenever the TLS handshake succeeded, even if a later probe step failed.
PeerCertificates []*x509.Certificate
}

// diagnoseError is returned by [Diagnoser.Diagnose] when a probe step fails.
Expand Down Expand Up @@ -165,6 +170,7 @@ func (a *agent) probeAddr(ctx context.Context, logger *slog.Logger, dialer Diale
if err := tlsConn.HandshakeContext(ctx); err != nil {
return result, &diagnoseError{Step: "tls", Err: err}
}
result.PeerCertificates = tlsConn.ConnectionState().PeerCertificates

// Muxado + SrvInfo
muxSess := muxado.Client(tlsConn, nil)
Expand Down
1 change: 1 addition & 0 deletions diagnose_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ func TestDiagnoseMuxadoSuccess(t *testing.T) {
assert.Equal(t, l.Addr().String(), result.Addr)
assert.Equal(t, testRegion, result.Region)
assert.Greater(t, result.Latency, time.Duration(0))
assert.NotEmpty(t, result.PeerCertificates, "peer certificate chain should be captured after TLS handshake")
}

// TestDiagnoseOnline connects to a live tunnel server and verifies the full
Expand Down
Loading