Modules

Tls

moduleextensionexperimentalSource

Available on macOS, Linux, Windows

#import "Extensions/Tls";

TLS 1.2 and 1.3 over Io readers and writers, on a bundled mbedTLS.

Fill in a Tls_Config, call tls_config_init once, then wrap an accepted or connected TCP stream with tls_connect (client) or tls_accept (server). A Tls_Stream is also an Io.Reader and Io.Writer (reader_of, writer_of), so code written for those, like Http, runs over TLS unchanged.

Calls block the calling fiber inside Io.io_run, so Io.with_deadline limits them and Io.cancel ends them. A stream belongs to one scheduler thread. Failures are returned, never fatal: handshake calls give a Tls_Error (error_text makes a line of it), while read and write give an Io.Io_Error and last_error the TLS reason.

A client verifies the server against the system's trusted roots unless roots_pem names others, and matches the host name against the certificate's subject alternative names. A stream is allocated by tls_connect or tls_accept and freed by close; tls_config_free releases the config. WebAssembly has no sockets or fibers, so the module is not there.

The example runs a client and a server on loopback with throwaway test certificates, valid for localhost. Never use them for anything else.

Example↗
Io :: #import "Extensions/Io";

ROOT :: #string END
-----BEGIN CERTIFICATE-----
MIIBXzCCAQWgAwIBAgIBATAKBggqhkjOPQQDAjAWMRQwEgYDVQQDDAtKYWkgVGVz
dCBjYTAgFw0yMDAxMDEwMDAwMDBaGA8yMTIwMDEwMTAwMDAwMFowFjEUMBIGA1UE
AwwLSmFpIFRlc3QgY2EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQD1LxitDVO
QKMsMsgSpdi5JldYih52cephbw9hiejFcmOH3zQJAkGrcEwyH4CzLFJKjhUKaT54
4UDSgerXHZBpo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd
BgNVHQ4EFgQUunX0563ngeQ6w8JsMUiqadFmd6YwCgYIKoZIzj0EAwIDSAAwRQIh
AI1Q4QwolbUZQxGjLzBltR4I6BPZLStohfpAiI20nwPRAiAjsRP061IzprBMR5JF
WnaWEW4Nf8JpkkFzPPaEp947tQ==
-----END CERTIFICATE-----
END

CERTIFICATE :: #string END
-----BEGIN CERTIFICATE-----
MIIBzDCCAXKgAwIBAgIBAzAKBggqhkjOPQQDAjAWMRQwEgYDVQQDDAtKYWkgVGVz
dCBjYTAgFw0yMDAxMDEwMDAwMDBaGA8yMTIwMDEwMTAwMDAwMFowFjEUMBIGA1UE
AwwLc2VydmVyLnRlc3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQC5kvwaJTy
O8qTdjNn9OgWvQ+QmLQDQDdzaUggjqJiooWHhj4peLTZFKWJHzwgZK3TRCnK9PXw
VYwzFQ5BLKq/o4GuMIGrMAkGA1UdEwQCMAAwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
JQQMMAoGCCsGAQUFBwMBMDkGA1UdEQQyMDCCCWxvY2FsaG9zdIILc2VydmVyLnRl
c3SHBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEwHQYDVR0OBBYEFIDVLb76eRrc3ond
VlB/e95+HlPnMB8GA1UdIwQYMBaAFLp19Oet54HkOsPCbDFIqmnRZnemMAoGCCqG
SM49BAMCA0gAMEUCIE3wJvrmLnn+L4DdTxS3SZyHLO5z/yT/dh7hyz1vGv5oAiEA
v9a4lUdydpygfLwCjF7DCb8AD9qcpTQOAj8cFauUUCs=
-----END CERTIFICATE-----
END

KEY :: #string END
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEILAaHg0fuEMf2UFJprp01LKXf54EQN3Xb7zkKzdoIxK1oAoGCCqGSM49
AwEHoUQDQgAEAuZL8GiU8jvKk3YzZ/ToFr0PkJi0A0A3c2lIII6iYqKFh4Y+KXi0
2RSliR88IGSt00QpyvT18FWMMxUOQSyqvw==
-----END EC PRIVATE KEY-----
END

Echo :: struct {
    listener: Io.Tcp_Listener;
    config: *Tls_Config;
}

// The server: accept one connection, answer a read with the same bytes, then close.
echo_once :: (job: *Echo) {
    tcp, io_error := Io.accept(job.listener);
    assert(io_error == .NONE);

    stream, error := tls_accept(job.config, Io.reader_of(tcp), Io.writer_of(tcp));

    if error.kind != .NONE {
        Io.close(tcp);
        return;
    }

    buffer: [64] u8;
    count, read_error := read(stream, buffer);

    if read_error == .NONE write(stream, string.{ count, buffer.data });

    close(stream);
}

main :: () {
    Io.io_run(() {
        server: Tls_Config;
        server.certificate_chain_pem = CERTIFICATE;
        server.private_key_pem = KEY;
        assert(tls_config_init(*server).kind == .NONE);
        defer tls_config_free(*server);

        // A client trusts the system's roots unless told otherwise; this one trusts only the test authority that signed the certificate.
        client: Tls_Config;
        client.roots_pem = ROOT;
        assert(tls_config_init(*client).kind == .NONE);
        defer tls_config_free(*client);

        listener, listen_error := Io.tcp_listen("127.0.0.1:0");
        assert(listen_error == .NONE);
        defer Io.close(listener);

        job := Echo.{ listener = listener, config = *server };
        Io.spawn(echo_once, *job);

        address, _ := Io.local_address(listener);
        tcp, connect_error := Io.connect(address);
        assert(connect_error == .NONE);

        // The name is sent as SNI and checked against the certificate.
        stream, error := tls_connect(*client, Io.reader_of(tcp), Io.writer_of(tcp), "localhost");

        // On an error nothing is closed: the TCP connection is still the caller's.
        assert(error.kind == .NONE, "%", error_text(error));
        defer close(stream);

        assert(version(stream) == "TLSv1.3");

        write(stream, "ping");

        buffer: [64] u8;
        count, read_error := read(stream, buffer);

        assert(read_error == .NONE && string.{ count, buffer.data } == "ping");
    });
}

Procedures#

Error#

detailexperimentalSource
(error: *Tls_Error) -> string

The sentence behind an error ("the certificate has expired"), or "". It is a copy in temporary storage, so it stays valid when the error is copied or goes out of scope, until the temporary storage is reset; copy it with copy_string to keep it longer.

alert_nameexperimentalsince 0.27.0Source
(alert: u8) -> string

The name of an alert description ("unknown_ca"), or "" for a number the module does not know.

error_textexperimentalSource
(error: Tls_Error) -> string

One line for logs and messages: "certificate verification failed: the certificate has expired". Like detail, the text is in temporary storage, never inside the error.

line := error_text(Tls_Error.{ kind = .CLOSED });
assert(line.count > 0);
↗

Config#

(config: *Tls_Config) -> Tls_Error

Parses the certificates and keys and builds the mbedTLS configurations. Call it once before the first connection; the config must stay where it is and outlive every stream made from it. Returns .CONFIG with a reason for a certificate or key that does not parse, a version range that is empty, or a server certificate without its key. A config with no certificate is a client; one with a certificate and key can also accept.

config: Tls_Config; // a client, trusting the system's roots
error := tls_config_init(*config);
assert(error.kind == .NONE, "%", error_text(error));
tls_config_free(*config);
↗

This one is only checked to compile, not run.

Stream#

tls_connectexperimentalSource
(config: *Tls_Config, reader: Io.Reader, writer: Io.Writer, server_name: string) -> (*Tls_Stream, Tls_Error)

Runs the client handshake over reader and writer and checks the server's certificate against server_name (which is also sent as the SNI name; "" sends none, and with verify_name off the name is not checked). On success the stream owns the connection: close closes it. On an error nothing is closed, the connection is the caller's to close.

Io :: #import "Extensions/Io";

Io.io_run(() {
    config: Tls_Config;
    tls_config_init(*config);
    defer tls_config_free(*config);

    tcp, io_error := Io.connect("example.com:443");
    assert(io_error == .NONE);

    stream, error := tls_connect(*config, Io.reader_of(tcp), Io.writer_of(tcp), "example.com");
    if error.kind != .NONE {
        Io.close(tcp);
        return;
    }
    defer close(stream);

    write(stream, "hello");
});
↗

This one is only checked to compile, not run.

tls_acceptexperimentalSource
(config: *Tls_Config, reader: Io.Reader, writer: Io.Writer) -> (*Tls_Stream, Tls_Error)

Runs the server handshake. The config needs a certificate and key. On success the stream owns the connection; on an error the caller closes it.

Io :: #import "Extensions/Io";

// `config` has a certificate chain and private key; `listener` is an Io.Tcp_Listener.
serve_one :: (config: *Tls_Config, listener: Io.Tcp_Listener) {
    tcp, io_error := Io.accept(listener);
    if io_error != .NONE return;

    stream, error := tls_accept(config, Io.reader_of(tcp), Io.writer_of(tcp));
    if error.kind != .NONE {
        Io.close(tcp);
        return;
    }
    defer close(stream);

    write(stream, "welcome");
}
↗

This one is only checked to compile, not run.

(stream: *Tls_Stream) -> string

The ALPN protocol both sides chose ("" when none was).

uses_http2 :: (stream: *Tls_Stream) -> bool {
    return alpn_protocol(stream) == "h2";
}
↗

This one is only checked to compile, not run.

cipher_suiteexperimentalSource
(stream: *Tls_Stream) -> string

The cipher suite in use, by mbedTLS's name ("TLS1-3-AES-128-GCM-SHA256", "TLS-ECDHE-ECDSA-WITH-CHACHA20-POLY1305-SHA256").

peer_certificate_derexperimentalsince 0.27.0Source
(stream: *Tls_Stream) -> [] u8

The DER of the certificate the peer proved itself with (the first of its chain): the server's on a client stream, the client's on a server stream that asked for one. The bytes belong to the stream and stay valid until close. Empty before the handshake or when the peer sent none.

(stream: *Tls_Stream) -> string

The hash the peer certificate's signature uses, by lower-case name: "md5", "sha1", "sha224", "sha256", "sha384" or "sha512". "" when there is no certificate or the algorithm names no hash (Ed25519, unknown OIDs). It reads the certificate's own DER, not the library's view of it.

server_nameexperimentalSource
(stream: *Tls_Stream) -> string

The name a client asked for in its SNI extension, on a stream from tls_accept ("" if it sent none). It points into the stream.

readexperimentalSource
(stream: *Tls_Stream, buffer: [] u8) -> (s64, Io.Io_Error)

Waits for data and reads what is there, up to buffer.count bytes. 0 is the end: the peer sent close_notify. A connection that ends without it is an error (.CONNECTION_RESET, and last_error says .TRUNCATED): the data may have been cut off.

read_greeting :: (stream: *Tls_Stream) -> string {
    buffer: [64] u8;
    count, error := read(stream, buffer);
    if error != .NONE || count == 0 return "";
    return copy_string(string.{ count, buffer.data });
}
↗

This one is only checked to compile, not run.

write_someexperimentalSource
(stream: *Tls_Stream, bytes: [] u8) -> (s64, Io.Io_Error)

Waits until the library has accepted at least one byte of bytes and sends it, together with the records of as many more bytes as fit in BATCH_BYTES; returns how many it took.

writeexperimental2 overloadsSource
(stream: *Tls_Stream, bytes: [] u8) -> (s64, Io.Io_Error)
(stream: *Tls_Stream, text: string) -> (s64, Io.Io_Error)

Sends all of bytes. On an error it returns how many went out. Sends all of bytes, waiting for the peer as needed, and returns how many went out with an Io.Io_Error. On .NONE that is bytes.count; on an error it is the bytes that were already sent. It is the same as write_some repeated, so a large write is split into records and may take several scheduler turns. Use write_some to send what fits without looping.

send_frame :: (stream: *Tls_Stream) {
    frame := u8.[1, 2, 3];
    sent, error := write(stream, frame);
    assert(error == .NONE && sent == frame.count);
}
↗

This one is only checked to compile, not run.

(stream: *Tls_Stream) -> Io.Io_Error

Waits until read would not have to wait for the network: decrypted bytes are waiting, or something has arrived (which may still be only part of a record).

closeexperimentalSource
(stream: *Tls_Stream, now := false) -> Io.Io_Error

Ends the stream: sends close_notify (unless now, which does not wait and sends nothing) and closes the connection under it, which wakes fibers still waiting on it with .CLOSED. The stream is freed once those fibers have left, so it must not be used after close; close it once.

finish :: (stream: *Tls_Stream) {
    close(stream); // sends close_notify, closes the TCP connection, frees the stream
}
↗

This one is only checked to compile, not run.

(reader: Io.Reader) -> bool

Whether reader is a TLS stream's reader (from reader_of) whose connection ended without the peer's close_notify (last_error(stream).kind == .TRUNCATED). For a protocol that frames its own messages, such as HTTP, where that end is no failure once the message is complete.

reader_ofexperimentalSource
(stream: *Tls_Stream) -> Io.Reader

The stream as an Io.Reader, for code written against readers (Io.read_all, the Http client). Reading it is read; closing it closes the whole stream, once. The reader points at the stream, so it is only valid until close.

Io :: #import "Extensions/Io";

read_everything :: (stream: *Tls_Stream) -> string {
    data, error := Io.read_all(reader_of(stream));
    return cast(string) data;
}
↗

This one is only checked to compile, not run.

writer_ofexperimentalSource
(stream: *Tls_Stream) -> Io.Writer

The stream as an Io.Writer, for code written against writers. Writing it is write_some; closing it closes the whole stream, once, so close either the reader or the writer of a stream and not both. It is only valid until close.

Names#

free_certificate_namesexperimentalsince 0.27.0Source

Releases the lists inside names (allocated with context.allocator by certificate_names) and resets it. The strings in it pointed into the DER, so they are not valid afterwards.

Trust macOS#

system_verifyexperimental2 overloadssince 0.31.0Source
(chain: [][] u8, server_name: string, data: *void) -> string
(chain: [][] u8, server_name: string, data: *void) -> string

Asks Security.framework whether chain (DER, the server's certificate first) is a valid chain for server_name. "" if it is, else why not.

Trust Windows#

CertOpenStoreforeignexperimentalSource
(provider: *u8, encoding: u32, crypto_provider: *void, flags: u32, parameter: *void) -> *void

Bindings#

jai_tls_conf_newforeignexperimentalSource
(is_client: s32, min_version: s32, max_version: s32, status: *s32) -> *void
jai_tls_pk_parseforeignexperimentalSource
(pk: *void, data: *u8, length: u64, password: *u8, password_length: u64) -> s32

Foreign#

jai_tls_conf_newforeignexperimentalSource
(is_client: s32, min_version: s32, max_version: s32, status: *s32) -> *void
jai_tls_pk_parseforeignexperimentalSource
(pk: *void, data: *u8, length: u64, password: *u8, password_length: u64) -> s32
CertOpenStoreforeignexperimentalSource
(provider: *u8, encoding: u32, crypto_provider: *void, flags: u32, parameter: *void) -> *void

Types#

Tls_Error_KindenumexperimentalSource
:: enum

What went wrong, in one value for everything the module does: a bad certificate in a config, a handshake that failed, a peer that is not who it says it is, a connection that broke.

NONE
no error: .{}
IO
the connection under TLS failed or timed out: io says how (.TIMED_OUT, .CANCELED, ...)
CONFIG
the config cannot be used: a certificate or key that does not parse, no system roots, ...
HANDSHAKE
the handshake failed: the peers share no protocol version, cipher or ALPN protocol, or one sent a fatal alert
VERIFY
the peer's certificate was not accepted (detail says why)
PROTOCOL
a record that does not decrypt or parse, or an alert from the peer, on an open connection
TRUNCATED
the connection ended without the peer's close_notify: the data may be incomplete
CLOSED
the stream was closed
config: Tls_Config;
config.certificate_chain_pem = "this is not a certificate";
config.private_key_pem = "this is not a key";

error := tls_config_init(*config);
assert(error.kind == .CONFIG);
assert(error_text(error).count > 0);
↗
Tls_ErrorstructexperimentalSource
:: struct

The sentence for people ("the certificate has expired") is kept inside the error in detail_bytes; read it with detail(*error) or print the whole error with error_text. code is mbedTLS's own negative error number (0 when the error did not come from it).

io: Io.Io_Error
For IO, and the closest Io_Error for the other kinds that end a read or write.
code: s32
verify_flags: u32
For VERIFY with mbedTLS's own verification: its result bits (X509_BADCERT_*), else 0.
alert: u8
For a fatal alert the peer sent: its description number (RFC 8446, section 6; alert_name spells it), else 0.
wrong_key_password: bool
For CONFIG: the private key was not read because the password does not decrypt it (or was needed and not given).
detail_count: u8
detail_bytes: [191] u8
Tls_VersionenumexperimentalSource
:: enum

A TLS protocol version, for Tls_Config.min_version and max_version: the range a client offers or a server accepts. The defaults are the whole range, TLS_1_2 to TLS_1_3, and the version that was settled on is reported by version(stream) as "TLSv1.2" or "TLSv1.3". Older versions are not supported. tls_config_init fails with .CONFIG when the range is empty (min_version above max_version).

TLS_1_2
TLS_1_3
config: Tls_Config;
config.min_version = .TLS_1_3;
config.max_version = .TLS_1_2;

error := tls_config_init(*config);
assert(error.kind == .CONFIG);
↗
Tls_Verify_ProctypeexperimentalSource
:: #type (chain: [][] u8, server_name: string, data: *void) -> string

Decides whether to trust a peer's certificate chain, instead of mbedTLS's own verification. chain is the peer's certificates in DER, its own first; server_name is what the client asked for (empty on the server). Returns "" to accept, or why the chain is rejected. It runs on the fiber that is doing the handshake, after the handshake itself succeeded, and may wait.

Tls_Key_Log_Proctypeexperimentalsince 0.27.0Source
:: #type (line: string, data: *void)

Gets one line of the NSS key log format (no newline), such as CLIENT_HANDSHAKE_TRAFFIC_SECRET <client random> <secret>, in hex.

Tls_ConfigstructexperimentalSource
:: struct

Zero-initialised, a config is a client that verifies servers against the system's roots. Fill in the fields you need, then call tls_config_init.

certificate_chain_pem: string
Server (and, for a client that must prove who it is, client): the certificate chain as PEM, the endpoint's own certificate first, and its private key: PEM or DER (PKCS#8, PKCS#1 or SEC1; the name _pem is kept from when only PEM was read). private_key_password decrypts an encrypted PKCS#8 key (PBES2 with PBKDF2 and AES or 3DES) or an OpenSSL-style encrypted PEM one; empty for a key that is not encrypted.
private_key_pem: string
private_key_password: string
roots_pem: string
The certificate authorities to trust, as PEM (any number of certificates). Empty: the system's (macOS and Windows use their own trust stores). They must be authorities: the server's own certificate listed here does not make a client trust it unless it is self-signed (the usual rule), so a certificate that a CA signed is trusted by giving that CA, or an intermediate (see root_must_be_self_signed).
crl_pem: string
Client with roots_pem: certificate revocation lists as PEM (any number, or one DER). The server's certificate must be covered: if no list in here was issued by its issuer, or one lists the certificate, the connection fails. Lists for the other certificates of a chain are used when present.
alpn: [] string
The protocols to offer (client) or accept (server) through ALPN, most preferred first: .["h2", "http/1.1"]. Empty: no ALPN extension. A server that has a list and a client that offers protocols but none in common fails the handshake.
verify: := true
Client: check the server's certificate chain and name. Turning it off makes the connection open to anyone in the middle; it exists for tests and for talking to devices on a private network, and is never the default.
verify_name: := true
Client: check that the server's certificate is for the name tls_connect is given. Off, the chain is still checked, and the name is still sent as SNI. For callers that match the name by their own rules (a database's, say); with verify_proc or the system's verifier on macOS and Windows the name is simply not passed on.
root_must_be_self_signed: := false
Client with roots_pem: the chain must end in a self-signed certificate from roots_pem. Otherwise any certificate listed there is a trust anchor, intermediate CAs included (the library's rule); OpenSSL-based clients need the root, and this option gives that behaviour.
client_auth: := false
Server: ask the client for a certificate and check it against roots_pem (or the system's roots on Linux). Clients without a valid certificate fail the handshake.
min_version: := Tls_Version.TLS_1_2
max_version: := Tls_Version.TLS_1_3
verify_proc: Tls_Verify_Proc
Replaces mbedTLS's check of the peer's chain (roots_pem and the system roots are then not used for verification). The system verifiers of macOS and Windows are this mechanism too.
verify_data: *void
key_log_proc: Tls_Key_Log_Proc
Client: called with each secret a handshake derives, as a key log line that tools such as Wireshark read to decrypt a capture. For debugging only: the lines decrypt the connection.
key_log_data: *void
native: *Tls_Native
Set by tls_config_init (its type is internal to the module): do not touch it, and do not copy a config after that.
client: Tls_Config;
client.alpn = .["h2", "http/1.1"];
assert(tls_config_init(*client).kind == .NONE);
defer tls_config_free(*client);
↗

This one is only checked to compile, not run.

Tls_StreamstructexperimentalSource
:: struct

A TLS connection over any Io.Reader and Io.Writer pair. Read and write it with read, write_some and write, or through reader_of and writer_of.

reader: Io.Reader
writer: Io.Writer
ssl: *void
fiber_context: #Context
The running fiber's context, saved before every call into the library, so the callbacks the library makes (which are #c_call) can run Jai code and park the fiber.
lock: Io.Fiber_Mutex
Calls into the library take turns. A fiber never waits for the network while it holds this: the receive callback asks the library to retry and the fiber waits outside, and the send callback only appends to out.
out: [..] u8
What the library has produced and the connection has not taken yet, in order. Appended to under lock by the send callback; flush writes it out under flush_lock, so the bytes of two flushes never interleave. The library is never parked in a send: that is what lets both ends of a connection write a lot at once and still read each other.
flush_lock: Io.Fiber_Mutex
flusher_running: bool
A fiber started by flush_later is sending out (it counts as a user of the stream).
io_error: Io.Io_Error
What the Io call under the running library call returned, when it failed.
broken: Io.Io_Error
Set once the connection is unusable (an Io call failed in the middle of a record, a record did not decrypt, the peer cut the connection short): what every later call returns.
failure: Tls_Error
The error behind broken, for last_error.
can_wait: bool
The underlying reader says when it is readable: the library is never made to wait for input inside a callback (it returns "want read" and the fiber waits outside), so a fiber waiting for data does not block a writer of the same stream.
ready: bool
ahead: [] u8
Bytes read from the connection and not yet handed to the library. The library asks for one record at a time (at most 16 KiB), and each ask used to cost a wait and a read on the connection; reading more at once and serving the library from here costs one pair for several records. The buffer lives from a read to the moment the library has taken all of it, so a quiet connection holds none; ahead_size is how much the next read takes (it grows while reads come back full, and starts again at the size of a record after a short one).
ahead_start: s64
ahead_end: s64
ahead_size: s64
established: bool
peer_closed: bool
closing: bool
users: s64
Calls in progress; the stream is freed after close when the last one has left.
key_log_proc: Tls_Key_Log_Proc
The client's key log, from its config.
key_log_data: *void
peer_name_count: u8
The server name a client asked for (SNI), on a server stream.
peer_name_bytes: [255] u8
Certificate_Namesstructexperimentalsince 0.27.0Source
:: struct

Slices into the DER the names were read from: they are valid as long as it is.

dns: [..] string
The dNSName entries, in the order the certificate lists them.
addresses: [..] string
The iPAddress entries: 4 raw bytes for IPv4, 16 for IPv6.
common_name: string
The first common name of the subject (UTF8String, PrintableString, IA5String and TeletexString values; a BMPString only if every character is ASCII).
has_common_name: bool
common_name_unreadable: bool
Set when the common name could not be read as text (a zero byte inside it, an unknown string type).
Bio_SendtypeforeignexperimentalSource
:: #type (context_data: *void, buffer: *u8, length: u64) -> s32 #c_call
Bio_ReceivetypeforeignexperimentalSource
:: #type (context_data: *void, buffer: *u8, length: u64) -> s32 #c_call
Export_Keys_CallbacktypeforeignexperimentalSource
:: #type (data: *void, type: s32, secret: *u8, secret_length: u64, client_random: *u8, server_random: *u8, prf: s32) #c_call
Sni_CallbacktypeforeignexperimentalSource
:: #type (data: *void, ssl: *void, name: *u8, length: u64) -> s32 #c_call
Cert_Chain_ParastructforeignexperimentalSource
:: struct

CERT_CHAIN_PARA up to pftCacheResync, the part every Windows version since Vista reads.

size: u32
requested_usage: Cert_Usage_Match
requested_issuance_policy: Cert_Usage_Match
url_retrieval_timeout: u32
check_revocation_freshness_time: s32
revocation_freshness_time: u32
cache_resync: *void
Https_Policy_DatastructforeignexperimentalSource
:: struct

HTTPSPolicyCallbackData

size: u32
auth_type: u32
checks: u32
server_name: *u16

Constants#

BATCH_BYTESexperimentalSource
:: 64 * 1024

The library makes one record per call (up to 8 KiB of data); a write of more goes through several records into out and reaches the connection in one send, up to this much, instead of one send (and one trip through the scheduler) per record.

ERR_NET_SEND_FAILEDforeignexperimentalSource
:: -0x004E

The library's own network codes (its sockets are not compiled in): what the BIO callbacks return when the Io call under them failed. The stream keeps the Io_Error.