Skip to content

fix: try all resolved addresses when connecting a TLS link - #2689

Open
nikitatsym wants to merge 2 commits into
eclipse-zenoh:mainfrom
nikitatsym:fix/tls-resolve-all-addrs-main
Open

fix: try all resolved addresses when connecting a TLS link#2689
nikitatsym wants to merge 2 commits into
eclipse-zenoh:mainfrom
nikitatsym:fix/tls-resolve-all-addrs-main

Conversation

@nikitatsym

@nikitatsym nikitatsym commented Jul 18, 2026

Copy link
Copy Markdown

Problem

When connecting a tls/<hostname>:<port> endpoint, the TLS link resolves the hostname and dials only the first resolved address (get_tls_addr() returns lookup_host().next()). If that address is unreachable, the retry loop keeps redialing the same dead address until connect/timeout_ms expires — even when another resolved address is reachable.

The plain TCP link already handles this correctly: get_tcp_addrs() returns all resolved addresses and LinkManagerUnicastTcp::new_link iterates over them, collecting errors (io/zenoh-links/zenoh-link-tcp/src/unicast.rs).

Real-world impact

Client on an LTE hotspot whose resolver synthesizes DNS64 AAAA records (64:ff9b::/96) without a working NAT64 route — common on mobile networks. getaddrinfo returns the synthesized (dead) IPv6 first and the working IPv4 second. A zenoh 1.9.0 client fails with:

Can not create a new TLS link bound to DnsName("example.com"):
[64:ff9b::9835:bbdf]:7447: No route to host (os error 65)   (repeated until timeout)
Unable to connect to any of [tls/example.com:7447]. Timeout!

nc and openssl s_client to the same hostname succeed from the same machine because they iterate over resolved addresses.

Fix

Mirror the TCP link pattern in the TLS link: add get_tls_addrs() (all resolved addresses, multicast filtered, same shape as get_tcp_addrs()); LinkManagerUnicastTls::new_link iterates, uses the first successful TCP connection, collects per-address errors, and reports all attempts on total failure. Listener paths unchanged. QUIC/WS links appear to have the same single-address behavior — happy to extend the same pattern there if desired.

Testing

  • Unit tests for the multi-address connect fallback (connect_first_reachable in zenoh-link-tls): a dead address falls through to a reachable one; total failure reports every attempted address; an empty address list is an error. Deterministic (loopback only, no DNS).
  • cargo test / check / fmt / clippy clean on zenoh-link-tls.
  • Differential test on an affected network (DNS64 without NAT64): a client built from this branch connects (falls through the dead IPv6 to the working IPv4); stock 1.9.0 times out as above.

🏷️ Label-Based Checklist

Based on the labels applied to this PR, please complete these additional requirements:

Labels: bug

🐛 Bug Fix Requirements

Since this PR is labeled as a bug fix, please ensure:

  • Root cause documented - Explain what caused the bug in the PR description
  • Reproduction test added - Test that fails on main branch without the fix
  • Test passes with fix - The reproduction test passes with your changes
  • Regression prevention - Test will catch if this bug reoccurs in the future
  • Fix is minimal - Changes are focused only on fixing the bug
  • Related bugs checked - Verified no similar bugs exist in related code

Why this matters: Bugs without tests often reoccur.

Instructions:

  1. Check off items as you complete them (change - [ ] to - [x])
  2. The PR checklist CI will verify these are completed

This checklist updates automatically when labels change, but preserves your checked boxes.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves the TLS unicast connection path to behave like the existing TCP link logic when a hostname resolves to multiple IP addresses, by attempting multiple resolved socket addresses instead of repeatedly retrying only the first resolution result.

Changes:

  • Added get_tls_addrs() to resolve and return all non-multicast socket addresses for a TLS endpoint (mirroring TCP’s get_tcp_addrs() pattern).
  • Updated LinkManagerUnicastTls::new_link to iterate over resolved addresses, attempt connections, and aggregate connection errors when all attempts fail.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
io/zenoh-links/zenoh-link-tls/src/utils.rs Adds a multi-address resolver helper (get_tls_addrs) for TLS endpoints.
io/zenoh-links/zenoh-link-tls/src/unicast.rs Uses the new resolver helper to attempt TCP connection across all resolved addresses and aggregate failures.
Comments suppressed due to low confidence (1)

io/zenoh-links/zenoh-link-tls/src/unicast.rs:394

  • The address-iteration logic only covers the TCP connect step. If a TCP connect succeeds but the TLS handshake fails, new_link will return that handshake error without trying the remaining resolved addresses, even though another address might work. To fully implement "try all resolved addresses when connecting a TLS link", consider moving the TLS handshake into the per-address loop and continuing on handshake failure while accumulating errors.
        // Initialize the TlsStream
        let tls_stream = connector
            .connect(server_name.to_owned(), tcp_stream)
            .await
            .map_err(|e| {

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@codecov

codecov Bot commented Jul 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 74.50%. Comparing base (572f7b1) to head (d29f945).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2689      +/-   ##
==========================================
- Coverage   74.51%   74.50%   -0.02%     
==========================================
  Files         417      417              
  Lines       62942    62996      +54     
==========================================
+ Hits        46901    46933      +32     
- Misses      16041    16063      +22     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@nikitatsym

Copy link
Copy Markdown
Author

For the label check: this PR is a bug fix (the TLS link only ever dials the first resolved address). Could a maintainer add the bug label please?

On the Copilot comment about retrying the TLS handshake across addresses - that's a slightly different scope: this PR is about bringing the TLS link in line with the existing TCP link behaviour, where the iteration covers the connect phase only. Glad to extend it in a follow-up.

@diogomatsubara diogomatsubara added the bug Something isn't working label Jul 23, 2026
@nikitatsym

Copy link
Copy Markdown
Author

Thanks for adding the bug label. The generated checklist requires an automated reproduction/regression test and verification that no related bugs exist. This PR currently has a real-network differential test, while similar QUIC/WS behavior is intentionally left for follow-ups. Would you prefer an automated test in this PR, or can these checklist items be handled differently?

@nikitatsym
nikitatsym force-pushed the fix/tls-resolve-all-addrs-main branch from fbcc6f0 to 46893e4 Compare July 24, 2026 04:53
Signed-off-by: Nikita Tsymbal <nikita.tsym@gmail.com>
Signed-off-by: Nikita Tsymbal <nikita.tsym@gmail.com>
@nikitatsym
nikitatsym force-pushed the fix/tls-resolve-all-addrs-main branch from 46893e4 to d29f945 Compare July 28, 2026 03:16
@nikitatsym

nikitatsym commented Jul 28, 2026

Copy link
Copy Markdown
Author

@milyin @diogomatsubara Rebased on latest main and added unit tests covering the multi-address connect fallback.
The bug-fix checklist items are addressed now. The CI run is pending maintainer approval (fork workflows), could you approve it and take a look when you have a chance?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants