Skip to content

Don't use IP literals for TLS SNI - #186

Open
dmonagle wants to merge 4 commits into
grpc:mainfrom
dmonagle:fix/sni-ip-literal
Open

Don't use IP literals for TLS SNI#186
dmonagle wants to merge 4 commits into
grpc:mainfrom
dmonagle:fix/sni-ip-literal

Conversation

@dmonagle

@dmonagle dmonagle commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Motivation

When a connection's authority contains an IP literal — e.g. a .dns target created with an IP address, which is common when callers resolve addresses themselves (multi-homed servers, NAT64 synthesis on mobile clients, health checks by address) — the literal is passed to the TLS handshake as the SNI server hostname. RFC 6066 § 3 only permits DNS hostnames in SNI, and NIOSSL enforces this: the connection fails with NIOSSLExtraError.cannotUseIPAddressInSNI before the handshake starts, surfacing to callers as unavailable: Could not establish a connection….

SocketAddress.sniHostname already applies exactly this rule ("Literal IP addresses aren't allowed in the SNI hostname") — but the authority-derived hostname takes precedence over it and doesn't.

Modifications

Connection.sanitizeAuthorityForSNI now returns nil for IP literals — bare IPv4/IPv6, with a trailing port, or bracketed IPv6 — after its existing port-stripping. The literal check runs before port-stripping too, since that logic would otherwise mangle bare IPv6 literals like ::1. sniServerHostname then falls back through the existing ?? self.address.sniHostname path, so such connections are made without SNI instead of failing.

Result

Dialling an IP literal over TLS connects (subject to the configured certificate verification) instead of failing with cannotUseIPAddressInSNI. Hostname authorities are unaffected.

Extended the existing testAuthorityIsSanitized harness with IP-literal cases (127.0.0.1, 127.0.0.1:9443, ::1, [2001:db8::1]:443), all expecting no SNI.

@linux-foundation-easycla

linux-foundation-easycla Bot commented Jul 20, 2026

Copy link
Copy Markdown

CLA Signed
The committers listed above are authorized under a signed CLA.

  • ✅ login: dmonagle / name: David Monagle (0f1f2c5)

An authority containing an IP literal (e.g. a .dns target created with
an IP address, common when callers resolve addresses themselves) was
passed to the TLS handshake as the SNI server hostname. RFC 6066 § 3
only permits DNS hostnames in SNI and NIOSSL enforces this, so such
connections failed with cannotUseIPAddressInSNI before the handshake
started. SocketAddress.sniHostname already applies this rule; the
authority-derived hostname now does too: IP literals (bare v4/v6, with
a port, or bracketed v6) yield no SNI instead of a handshake failure.
@dmonagle
dmonagle force-pushed the fix/sni-ip-literal branch from 90302d3 to 0f1f2c5 Compare July 20, 2026 08:36

@glbrntt glbrntt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

One tiny change but looks good otherwise

// Undo the bracketing applied to IPv6 addresses in authorities
// (e.g. "[2001:db8::1]:443").
if host.utf8.first == UInt8(ascii: "["), host.utf8.last == UInt8(ascii: "]") {
host = String(host.dropFirst().dropLast())

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Square brackets imply an IPv6 address so we can just return nil here, I think.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done — brackets now return nil directly (and host became a let).

@glbrntt glbrntt added the 🔨 semver/patch No public API change. label Jul 20, 2026

@glbrntt glbrntt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Great, thank you!

@glbrntt
glbrntt enabled auto-merge (squash) July 20, 2026 10:59
@glbrntt

glbrntt commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Apologies for missing this: it looks like the CI is failing because of a Swift issue: swiftlang/swift#85427

We might be able to workaround it with a patch to swift-nio: apple/swift-nio#3696

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

Labels

🔨 semver/patch No public API change.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants