Skip to content

deadline handshake - #553

Merged
dogancanbakir merged 60 commits into
mainfrom
tls-handshake-deadline
Oct 22, 2026
Merged

dogancanbakir merged 60 commits into
mainfrom
tls-handshake-deadline

Conversation

@Mzack9999

@Mzack9999 Mzack9999 commented Jun 25, 2026 •

Copy link
Copy Markdown
Member

Bound TLS handshakes with a connection deadline instead of spawning a watchdog goroutine plus two contexts per handshake, cutting per handshake timeout overhead from about 574ns and 8 allocs to 39ns and zero allocs while keeping the same timeout behavior.

Closes #554

Mzack9999 and others added 30 commits June 25, 2026 20:56
Bumps [github.com/projectdiscovery/networkpolicy](https://github.com/projectdiscovery/networkpolicy) from 0.1.40 to 0.1.41.
- [Release notes](https://github.com/projectdiscovery/networkpolicy/releases)
- [Commits](projectdiscovery/networkpolicy@v0.1.40...v0.1.41)

---
updated-dependencies:
- dependency-name: github.com/projectdiscovery/networkpolicy
  dependency-version: 0.1.41
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.48.0 to 0.55.0.
- [Commits](golang/net@v0.48.0...v0.55.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.55.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github.com/projectdiscovery/networkpolicy](https://github.com/projectdiscovery/networkpolicy) from 0.1.41 to 0.1.42.
- [Release notes](https://github.com/projectdiscovery/networkpolicy/releases)
- [Commits](projectdiscovery/networkpolicy@v0.1.41...v0.1.42)

---
updated-dependencies:
- dependency-name: github.com/projectdiscovery/networkpolicy
  dependency-version: 0.1.42
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.51.0 to 0.52.0.
- [Commits](golang/crypto@v0.51.0...v0.52.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.52.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github.com/projectdiscovery/networkpolicy](https://github.com/projectdiscovery/networkpolicy) from 0.1.42 to 0.1.43.
- [Release notes](https://github.com/projectdiscovery/networkpolicy/releases)
- [Commits](projectdiscovery/networkpolicy@v0.1.42...v0.1.43)

---
updated-dependencies:
- dependency-name: github.com/projectdiscovery/networkpolicy
  dependency-version: 0.1.43
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github.com/projectdiscovery/networkpolicy](https://github.com/projectdiscovery/networkpolicy) from 0.1.43 to 0.1.44.
- [Release notes](https://github.com/projectdiscovery/networkpolicy/releases)
- [Commits](projectdiscovery/networkpolicy@v0.1.43...v0.1.44)

---
updated-dependencies:
- dependency-name: github.com/projectdiscovery/networkpolicy
  dependency-version: 0.1.44
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github.com/projectdiscovery/blackrock](https://github.com/projectdiscovery/blackrock) from 0.0.1 to 0.0.2.
- [Release notes](https://github.com/projectdiscovery/blackrock/releases)
- [Commits](projectdiscovery/blackrock@v0.0.1...v0.0.2)

---
updated-dependencies:
- dependency-name: github.com/projectdiscovery/blackrock
  dependency-version: 0.0.2
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github.com/projectdiscovery/networkpolicy](https://github.com/projectdiscovery/networkpolicy) from 0.1.44 to 0.1.45.
- [Release notes](https://github.com/projectdiscovery/networkpolicy/releases)
- [Commits](projectdiscovery/networkpolicy@v0.1.44...v0.1.45)

---
updated-dependencies:
- dependency-name: github.com/projectdiscovery/networkpolicy
  dependency-version: 0.1.45
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github.com/projectdiscovery/networkpolicy](https://github.com/projectdiscovery/networkpolicy) from 0.1.45 to 0.1.46.
- [Release notes](https://github.com/projectdiscovery/networkpolicy/releases)
- [Commits](projectdiscovery/networkpolicy@v0.1.45...v0.1.46)

---
updated-dependencies:
- dependency-name: github.com/projectdiscovery/networkpolicy
  dependency-version: 0.1.46
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github.com/projectdiscovery/networkpolicy](https://github.com/projectdiscovery/networkpolicy) from 0.1.46 to 0.1.47.
- [Release notes](https://github.com/projectdiscovery/networkpolicy/releases)
- [Commits](projectdiscovery/networkpolicy@v0.1.46...v0.1.47)

---
updated-dependencies:
- dependency-name: github.com/projectdiscovery/networkpolicy
  dependency-version: 0.1.47
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github.com/projectdiscovery/hmap](https://github.com/projectdiscovery/hmap) from 0.0.101 to 0.0.102.
- [Release notes](https://github.com/projectdiscovery/hmap/releases)
- [Commits](projectdiscovery/hmap@v0.0.101...v0.0.102)

---
updated-dependencies:
- dependency-name: github.com/projectdiscovery/hmap
  dependency-version: 0.0.102
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github.com/projectdiscovery/utils](https://github.com/projectdiscovery/utils) from 0.11.1 to 0.11.2.
- [Release notes](https://github.com/projectdiscovery/utils/releases)
- [Changelog](https://github.com/projectdiscovery/utils/blob/main/CHANGELOG.md)
- [Commits](projectdiscovery/utils@v0.11.1...v0.11.2)

---
updated-dependencies:
- dependency-name: github.com/projectdiscovery/utils
  dependency-version: 0.11.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github.com/projectdiscovery/networkpolicy](https://github.com/projectdiscovery/networkpolicy) from 0.1.47 to 0.1.48.
- [Release notes](https://github.com/projectdiscovery/networkpolicy/releases)
- [Commits](projectdiscovery/networkpolicy@v0.1.47...v0.1.48)

---
updated-dependencies:
- dependency-name: github.com/projectdiscovery/networkpolicy
  dependency-version: 0.1.48
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github.com/projectdiscovery/retryabledns](https://github.com/projectdiscovery/retryabledns) from 1.0.115 to 1.0.116.
- [Release notes](https://github.com/projectdiscovery/retryabledns/releases)
- [Commits](projectdiscovery/retryabledns@v1.0.115...v1.0.116)

---
updated-dependencies:
- dependency-name: github.com/projectdiscovery/retryabledns
  dependency-version: 1.0.116
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github.com/projectdiscovery/networkpolicy](https://github.com/projectdiscovery/networkpolicy) from 0.1.48 to 0.1.49.
- [Release notes](https://github.com/projectdiscovery/networkpolicy/releases)
- [Commits](projectdiscovery/networkpolicy@v0.1.48...v0.1.49)

---
updated-dependencies:
- dependency-name: github.com/projectdiscovery/networkpolicy
  dependency-version: 0.1.49
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github.com/projectdiscovery/utils](https://github.com/projectdiscovery/utils) from 0.11.2 to 0.11.3.
- [Release notes](https://github.com/projectdiscovery/utils/releases)
- [Changelog](https://github.com/projectdiscovery/utils/blob/main/CHANGELOG.md)
- [Commits](projectdiscovery/utils@v0.11.2...v0.11.3)

---
updated-dependencies:
- dependency-name: github.com/projectdiscovery/utils
  dependency-version: 0.11.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github.com/projectdiscovery/networkpolicy](https://github.com/projectdiscovery/networkpolicy) from 0.1.49 to 0.1.50.
- [Release notes](https://github.com/projectdiscovery/networkpolicy/releases)
- [Commits](projectdiscovery/networkpolicy@v0.1.49...v0.1.50)

---
updated-dependencies:
- dependency-name: github.com/projectdiscovery/networkpolicy
  dependency-version: 0.1.50
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github.com/projectdiscovery/networkpolicy](https://github.com/projectdiscovery/networkpolicy) from 0.1.50 to 0.1.51.
- [Release notes](https://github.com/projectdiscovery/networkpolicy/releases)
- [Commits](projectdiscovery/networkpolicy@v0.1.50...v0.1.51)

---
updated-dependencies:
- dependency-name: github.com/projectdiscovery/networkpolicy
  dependency-version: 0.1.51
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github.com/projectdiscovery/utils](https://github.com/projectdiscovery/utils) from 0.11.3 to 0.11.5.
- [Release notes](https://github.com/projectdiscovery/utils/releases)
- [Changelog](https://github.com/projectdiscovery/utils/blob/main/CHANGELOG.md)
- [Commits](projectdiscovery/utils@v0.11.3...v0.11.5)

---
updated-dependencies:
- dependency-name: github.com/projectdiscovery/utils
  dependency-version: 0.11.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github.com/projectdiscovery/utils](https://github.com/projectdiscovery/utils) from 0.11.5 to 0.11.6.
- [Release notes](https://github.com/projectdiscovery/utils/releases)
- [Changelog](https://github.com/projectdiscovery/utils/blob/main/CHANGELOG.md)
- [Commits](projectdiscovery/utils@v0.11.5...v0.11.6)

---
updated-dependencies:
- dependency-name: github.com/projectdiscovery/utils
  dependency-version: 0.11.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github.com/projectdiscovery/networkpolicy](https://github.com/projectdiscovery/networkpolicy) from 0.1.51 to 0.1.52.
- [Release notes](https://github.com/projectdiscovery/networkpolicy/releases)
- [Commits](projectdiscovery/networkpolicy@v0.1.51...v0.1.52)

---
updated-dependencies:
- dependency-name: github.com/projectdiscovery/networkpolicy
  dependency-version: 0.1.52
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github.com/projectdiscovery/networkpolicy](https://github.com/projectdiscovery/networkpolicy) from 0.1.52 to 0.1.53.
- [Release notes](https://github.com/projectdiscovery/networkpolicy/releases)
- [Commits](projectdiscovery/networkpolicy@v0.1.52...v0.1.53)

---
updated-dependencies:
- dependency-name: github.com/projectdiscovery/networkpolicy
  dependency-version: 0.1.53
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github.com/projectdiscovery/utils](https://github.com/projectdiscovery/utils) from 0.11.6 to 0.11.9.
- [Release notes](https://github.com/projectdiscovery/utils/releases)
- [Changelog](https://github.com/projectdiscovery/utils/blob/main/CHANGELOG.md)
- [Commits](projectdiscovery/utils@v0.11.6...v0.11.9)

---
updated-dependencies:
- dependency-name: github.com/projectdiscovery/utils
  dependency-version: 0.11.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github.com/projectdiscovery/networkpolicy](https://github.com/projectdiscovery/networkpolicy) from 0.1.53 to 0.1.54.
- [Release notes](https://github.com/projectdiscovery/networkpolicy/releases)
- [Commits](projectdiscovery/networkpolicy@v0.1.53...v0.1.54)

---
updated-dependencies:
- dependency-name: github.com/projectdiscovery/networkpolicy
  dependency-version: 0.1.54
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
return the first connection instead of dialing again
dependabot Bot and others added 27 commits October 19, 2026 01:23
Bumps [github.com/projectdiscovery/networkpolicy](https://github.com/projectdiscovery/networkpolicy) from 0.1.54 to 0.1.55.
- [Release notes](https://github.com/projectdiscovery/networkpolicy/releases)
- [Commits](projectdiscovery/networkpolicy@v0.1.54...v0.1.55)

---
updated-dependencies:
- dependency-name: github.com/projectdiscovery/networkpolicy
  dependency-version: 0.1.55
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
AsZTLSConfig did not copy Certificates, so a ztls client using the
converted config sent no client certificate, and servers that require
one rejected the connection.

Copy each certificate, converting its signature schemes with a new
generic helper, convertSlice. Leave Leaf unset: zcrypto has its own
x509 type and parses the leaf from the chain when Leaf is nil.

GetClientCertificate is still not converted.

Fixes: #600

Signed-off-by: Dwi Siswanto <git@dw1.io>
DialTLSWithConfigImpersonate built its utls.Config from six fields of
the caller's config and dropped the rest. Impersonated dials sent no
client certificate and ignored RootCAs, VerifyPeerCertificate,
VerifyConnection, KeyLogWriter and ClientSessionCache.

Convert the config in one place, asUTLSConfig. It copies every client
field, adapting those whose types differ in utls, and keeps TLS 1.3
sessions in the caller's ClientSessionCache. crypto/tls checks that a
session's certificate chain is still trusted before it resumes the
session, but utls v1.8.2 doesn't. Add that check: unless
InsecureSkipVerify is set, a resumed dial fails when none of its
verified chains is still valid and leads to a root in the current
RootCAs, and the next dial makes a full handshake. A new test walks
the fields of tls.Config and fails on any field that is neither
converted nor listed as unset, so fields added by later Go releases
can't be dropped silently.

Custom identities also kept the state of their first connection. utls
writes the server name and key shares into the spec's extensions, so
later dials with the same identity sent the first server name and
reused key shares without their private keys, failing with "tls:
internal error". Give each connection its own SNI, key share and
session extensions. An identity that carries its own session, such as
a FakePreSharedKeyExtension from ja3.ParseWithRaw, dials without the
session cache, since utls panics when it loads a cached session into
an extension that already holds one.

DialZTLSWithConfig converted configs with MinVersion TLS 1.3 to
crypto/tls with AsTLSConfig, which dropped Certificates among other
fields. zcrypto supports TLS 1.3 since #589, so dial with zcrypto at
every version. Those dials now return a *ztls.Conn, and WithTLSData
no longer records them.

Only TLS 1.3 sessions are shared with crypto/tls, because utls v1.8.2
can't read the TLS 1.2 sessions that Go 1.25 and later write. utls
can't resume after a HelloRetryRequest either, so that dial fails and
drops the session. MinVersion and MaxVersion are still overwritten by
utls (#612).

Fixes: #593
Fixes: #594
Fixes: #595
Fixes: #596
Fixes: #597
Fixes: #598
Fixes: #599
Fixes: #611

Signed-off-by: Dwi Siswanto <git@dw1.io>
ParseWithJa3 set TLSVersMin and TLSVersMax to the JA3 version field.
That field is the ClientHello legacy_version, which TLS 1.3 clients
also send as TLS 1.2, so a spec parsed from a TLS 1.3 fingerprint
offered TLS 1.3 in supported_versions but accepted only TLS 1.2. Dials
to TLS 1.3 servers failed with "server selected unsupported protocol
version 304".

When the extensions include supported_versions, leave the versions
zero, so that utls takes them from that extension.

The key_share extension also had no key shares, which fails the TLS
1.3 handshake with "tls: internal error". Build it per call, like
supported_groups and ec_point_formats, with a key share for the first
JA3 curve that utls can generate a key for: X25519, P-256, P-384,
P-521 or X25519MLKEM768. utls doesn't start a TLS 1.3 handshake
without a classic ECDHE share, so X25519MLKEM768, which current
browsers list first, comes with an X25519 share, as in Chrome, and is
skipped when the JA3 curves lack X25519.

Dialing twice with the same spec also needs the fix for #611, because
utls writes the key data into the spec.

Fixes: #607

Signed-off-by: Dwi Siswanto <git@dw1.io>
The ztls fallback ran after the dial switch in dialIPS, on whatever
error was left in err. Since 6a73dd4 the TLS, impersonated and ztls
branches return their handshake errors early, so only plain dial
errors reached it. A failed crypto/tls handshake no longer fell back,
as the README says it does. A plain dial whose ProxyDialer failed was
redialed directly instead, bypassing the proxy: a TLS target got a
ztls connection, and other targets got a direct connection and an EOF
error in place of the proxy's.

Run the fallback from the crypto/tls branch, right after its handshake
fails, and drop it from the plain path. It still retries once with
AsZTLSConfig, ztls.ChromeCiphers and the default ztls curves, but only
when a retry can reach more servers and keeps the server checks,
client certificate and ECH settings of the config. It doesn't retry
when the context is done, the handshake timed out, the server doesn't
speak TLS, or MinVersion is TLS 1.3. Nor does it retry when the config
verifies the server, presents a client certificate or uses ECH:
AsZTLSConfig can't carry these into zcrypto, so the retry would verify
the server differently, connect without the certificate, or send the
hidden server name in the clear. A test classifies every
field of tls.Config, so a field added by a later Go release fails it
until someone decides whether it blocks the fallback.

When the retry fails too, return both errors, so that the crypto/tls
one can still be matched.

The ztls branch and the fallback now share one dial-and-handshake
helper. It closes the connection when the handshake fails, which the
ztls branch didn't.

DialTLS and DialTLSWithConfig can return a *ztls.Conn again, and
WithTLSData doesn't record it.

Fixes: #613
Fixes: #614

Signed-off-by: Dwi Siswanto <git@dw1.io>
…-TLS-1.3-fingerprints-in-ParseWithJa3

ja3: support TLS 1.3 fingerprints in ParseWithJa3
…ertificates-in-AsZTLSConfig

util: copy Certificates in AsZTLSConfig
…ls-Config-in-impersonated-ztls-dials

fix: honor tls.Config in impersonated & ztls dials
…-back-to-ztls-only-on-TLS-handshake-errors

dialer: fall back to ztls only on TLS handshake errors
…llback

report dial results to OnDialResult
…#624)

Bumps [github.com/klauspost/compress](https://github.com/klauspost/compress) from 1.18.2 to 1.18.7.
- [Release notes](https://github.com/klauspost/compress/releases)
- [Commits](klauspost/compress@v1.18.2...v1.18.7)

---
updated-dependencies:
- dependency-name: github.com/klauspost/compress
  dependency-version: 1.18.7
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
With impersonate.Custom, DialTLSWithConfigImpersonate returned the
error of ApplyPreset without closing the connection it had dialed, so
the socket stayed open until the garbage collector finalized it. The
setALPN and handshake failures closed it, each in its own branch.

Move the ClientHello setup and the handshake into handshakeImpersonated,
and close the connection where it is dialed when any of them fails.

Fixes: #608

Signed-off-by: Dwi Siswanto <git@dw1.io>
ParseWithJa3 cloned a package-level map of extensions but not the
extensions in it, so all the specs it returned shared them, except
supported_groups (10), ec_point_formats (11) and key_share (51). utls
writes the state of a connection, such as GREASE values and the padding
length, into the extensions of the spec it applies, so connections that
each used their own spec still raced on these extensions.

Build new extensions for each spec. golang.org/x/exp is no longer
imported, so it becomes an indirect dependency.

This is the ja3 part of #609.

Signed-off-by: Dwi Siswanto <git@dw1.io>
utls writes the state of a connection into the extensions of the spec
it applies: GREASE values into UtlsGREASEExtension and the curve and
version lists, the padding length, the ALPS code point and the GREASE
ECH payload, among others. newClientHelloSpec gave each dial its own
server name, key share and session extensions only, so concurrent dials
that shared an impersonate.Identity raced on the rest, and each dial
left its state in the identity.

Copy all the other extensions too, with their exported slices, since
utls writes to some slice elements in place. The copy uses reflection
rather than a list of types, so it also covers a type that a later utls
starts writing to, as long as utls assigns to its fields or to elements
of its exported slices, as v1.8.2 does. It adds about 5 microseconds
and 56 small allocations to a dial with the Chrome 120 spec, which
takes about 3 ms over loopback.

Fixes: #609

Signed-off-by: Dwi Siswanto <git@dw1.io>
utls replaces the MinVersion and MaxVersion of its config with the
versions of the ClientHello spec, so impersonated dials accepted any
version that the ClientHello offers: a dial with MinVersion TLS 1.3
negotiated TLS 1.2, and one with MaxVersion TLS 1.2 negotiated TLS 1.3.

Check the negotiated version in VerifyConnection, which utls calls in
every handshake that can succeed, resumed ones included. The ClientHello
still offers its own versions, to keep its fingerprint, so the dial
fails when the server selects a version outside the limits, where
DialTLSWithConfig offers fewer versions and can connect. A zero
MinVersion or MaxVersion still leaves that limit to the ClientHello.

Fixes: #612

Signed-off-by: Dwi Siswanto <git@dw1.io>
…-leak-the-connection-when-ApplyPreset-fails

dialer: don't leak the connection when ApplyPreset fails
…-every-extension-of-the-identity-for-each-dial

dialer: copy every extension of the identity for each dial
…r-MinVersion-and-MaxVersion-in-impersonated-dials

dialer: honor MinVersion and MaxVersion in impersonated dials
bump zcrypto to fix darwin/arm64 nocgo build
@dogancanbakir
dogancanbakir merged commit 2d35dbe into main Oct 22, 2026
5 checks passed
@dogancanbakir
dogancanbakir deleted the tls-handshake-deadline branch October 22, 2026 11:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tls handshake spawns a watchdog goroutine per dial

3 participants