Skip to content

This PR adds Server Name Indication (SNI) support to the TLS server handshake on the zig-0.15.x branch.#29

Closed
pomelio wants to merge 1 commit into
ianic:zig-0.15.xfrom
pomelio:zig-0.15.x
Closed

This PR adds Server Name Indication (SNI) support to the TLS server handshake on the zig-0.15.x branch.#29
pomelio wants to merge 1 commit into
ianic:zig-0.15.xfrom
pomelio:zig-0.15.x

Conversation

@pomelio
Copy link
Copy Markdown

@pomelio pomelio commented Apr 1, 2026

Add SNI-based certificate selection to server handshake on zig-0.15.x

PR Description

This PR adds Server Name Indication (SNI) support to the TLS server handshake on the zig-0.15.x branch.

The server now parses the server_name extension from ClientHello, stores the requested hostname in handshake state, and allows callers to select a certificate dynamically through a new Options.sni_auth callback. If the callback does not return a certificate, the existing auth field remains the default fallback.

  • Added Options.sni_auth for dynamic certificate selection.
  • Added server-side handshake state for the parsed SNI hostname.
  • Implemented parsing for the server_name TLS extension in ClientHello.
  • Switched certificate emission in the server flight from fixed opt.auth to the resolved handshake auth.
  • Deferred signature scheme validation until after certificate selection, so validation is performed against the actual certificate chosen for the handshake.
  • Added tests for:
    • existing ClientHello parsing behavior
    • SNI parsing and SNI-driven certificate selection

Before this change, the server always used a single certificate from auth, regardless of the hostname requested by the client. That prevented serving multiple TLS hostnames from the same listener.

This PR enables virtual hosting scenarios while keeping the existing API behavior intact for callers that only provide auth.

New server option:

sni_auth: ?SniAuth = null

With:

pub const SniAuth = struct {
ctx: *anyopaque,
selectFn: *const fn (ctx: *anyopaque, server_name: ?[]const u8)
?*CertKeyPair,
};

Behavior:

  • selectFn is called after parsing SNI from ClientHello
  • if it returns a certificate, that certificate is used
  • if it returns null, the handshake falls back to auth

This change is backward compatible:

  • existing users of auth continue to work unchanged
  • sni_auth is optional

Validated locally with:

zig fmt --check src/handshake_server.zig
zig test src/handshake_server.zig

Relevant SNI tests pass, including the new SNI-specific handshake parsing test.

Note: the zig test src/handshake_server.zig run later crashes in an existing unrelated client-side test (handshake_client.test.handshake verify server finished message), after the new handshake server tests have already passed.

Add SNI-based certificate selection to server handshake on `zig-0.15.x`

**PR Description**

This PR adds Server Name Indication (SNI) support to the TLS server
handshake on the `zig-0.15.x` branch.

The server now parses the `server_name` extension from `ClientHello`,
stores the requested hostname in handshake state, and allows callers to
select a certificate dynamically through a new `Options.sni_auth`
callback. If the callback does not return a certificate, the existing
`auth` field remains the default fallback.

- Added `Options.sni_auth` for dynamic certificate selection.
- Added server-side handshake state for the parsed SNI hostname.
- Implemented parsing for the `server_name` TLS extension in
  `ClientHello`.
- Switched certificate emission in the server flight from fixed
  `opt.auth` to the resolved handshake auth.
- Deferred signature scheme validation until after certificate
  selection, so validation is performed against the actual certificate
  chosen for the handshake.
- Added tests for:
  - existing `ClientHello` parsing behavior
  - SNI parsing and SNI-driven certificate selection

Before this change, the server always used a single certificate from
`auth`, regardless of the hostname requested by the client. That
prevented serving multiple TLS hostnames from the same listener.

This PR enables virtual hosting scenarios while keeping the existing API
behavior intact for callers that only provide `auth`.

New server option:

```zig
sni_auth: ?SniAuth = null
```

With:

```zig
pub const SniAuth = struct {
ctx: *anyopaque,
selectFn: *const fn (ctx: *anyopaque, server_name: ?[]const u8)
?*CertKeyPair,
};
```

Behavior:
- `selectFn` is called after parsing SNI from `ClientHello`
- if it returns a certificate, that certificate is used
- if it returns `null`, the handshake falls back to `auth`

This change is backward compatible:
- existing users of `auth` continue to work unchanged
- `sni_auth` is optional

Validated locally with:

```sh
zig fmt --check src/handshake_server.zig
zig test src/handshake_server.zig
```

Relevant SNI tests pass, including the new SNI-specific handshake
parsing test.

Note: the `zig test src/handshake_server.zig` run later crashes in an
existing unrelated client-side test (`handshake_client.test.handshake
verify server finished message`), after the new handshake server tests
have already passed.
@ianic ianic closed this Apr 21, 2026
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.

2 participants