TLS_READ(3)
π TLS_READ(3) π
okay bestie, so this is the manual page for tls_read(3) β yes, the whole squad lives here: tls_read, tls_write, tls_handshake, tls_error, tls_close, and tls_reset. It's giving library functions manual energy and honestly? iconic. This is your whole guide to using a TLS connection, and we're gonna go through it together, no gatekeeping. π
β¨ NAME β¨
tls_read, tls_write, tls_handshake, tls_error, tls_close, tls_reset β literally just, use a TLS connection. that's the whole vibe. π¦
πΈ SYNOPSIS πΈ
#include <tls.h>
ssize_t
tls_read(struct tls *ctx, void *buf, size_t buflen);
ssize_t
tls_write(struct tls *ctx, const void *buf, size_t buflen);
int
tls_handshake(struct tls *ctx);
const char *
tls_error(struct tls *ctx);
int
tls_close(struct tls *ctx);
void
tls_reset(struct tls *ctx);
π DESCRIPTION π
so here's the tea, function by function:
tls_read() reads buflen bytes of data from the socket into buf, and it hands you back the amount of data it actually read. slay.
tls_write() writes buflen bytes of data from buf to the socket, and returns how much got written. periodt.
tls_handshake() explicitly performs the TLS handshake. bestie, you only need to call this if you genuinely need to guarantee the handshake has completed, because β plot twist β both tls_read() and tls_write() automatically do the TLS handshake whenever it's necessary. so it's like, the optional lil overachiever. π
The tls_error() function is your emotional support lookup: use it to retrieve a string with more info about the most recent error relating to a context.
tls_close() closes a connection after use. heads up though β only the TLS layer gets shut down, and the caller is responsible for closing the file descriptors, unless the connection was established using tls_connect(3) or tls_connect_servername(3). after closing the connection, ctx can be passed to tls_free(3). β¨
π₯ RETURN VALUES π₯
tls_read()andtls_write()return a size on success, or-1on error.tls_handshake()andtls_close()return0on success, or-1on error.
The tls_read(), tls_write(), tls_handshake(), and tls_close() functions also have two special return values, and these are lowkey the main character moments:
TLS_WANT_POLLINβ the underlying read file descriptor needs to be readable in order to continue.TLS_WANT_POLLOUTβ the underlying write file descriptor needs to be writeable in order to continue.
Here's how you play it, depending on your file descriptors:
- blocking file descriptors β the same function call should be repeated immediately. πβοΈ
- non-blocking file descriptors β the same function call should be repeated when the required condition has been met. β³
β οΈ Callers of these functions cannot rely on the value of the globalerrno. To prevent mishandling of error conditions,tls_read(),tls_write(),tls_handshake(), andtls_close()all explicitly clearerrno. so don't go peeking aterrnoand building theories, it's not that girl. π
tls_error() returns NULL if no error occurred with ctx during or since the last call to tls_handshake(), tls_read(), tls_write(), tls_close(), or tls_reset() involving ctx β or if memory allocation failed while trying to assemble the string describing the most recent error related to ctx.
π EXAMPLES π
first up: handling TLS writes on a blocking file descriptor. look at this lil loop, obsessed:
...
while (len > 0) {
ssize_t ret;
ret = tls_write(ctx, buf, len);
if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT)
continue;
if (ret == -1)
errx(1, "tls_write: %s", tls_error(ctx));
buf += ret;
len -= ret;
}
...
and next: handling TLS writes on a non-blocking file descriptor using poll(2). she's fancy, she's doing the most, we love her:
...
pfd[0].fd = fd;
pfd[0].events = POLLIN|POLLOUT;
while (len > 0) {
nready = poll(pfd, 1, 0);
if (nready == -1)
err(1, "poll");
if ((pfd[0].revents & (POLLERR|POLLNVAL)))
errx(1, "bad fd %d", pfd[0].fd);
if ((pfd[0].revents & (pfd[0].events|POLLHUP))) {
ssize_t ret;
ret = tls_write(ctx, buf, len);
if (ret == TLS_WANT_POLLIN)
pfd[0].events = POLLIN;
else if (ret == TLS_WANT_POLLOUT)
pfd[0].events = POLLOUT;
else if (ret == -1)
errx(1, "tls_write: %s", tls_error(ctx));
else {
buf += ret;
len -= ret;
}
}
}
...
π«Ά SEE ALSO π«Ά
tls_accept_socket(3), tls_configure(3), tls_conn_version(3), tls_connect(3), tls_init(3), tls_ocsp_process_response(3)
go read those next bestie, the lore runs deep. π
π HISTORY π
tls_read(),tls_write(),tls_error(),tls_close(), andtls_reset()appeared in OpenBSD 5.6 and got their final names in OpenBSD 5.7.tls_handshake()appeared in OpenBSD 5.9.
glow-up era fr fr. β
π AUTHORS π
Joel Sing <jsing@openbsd.org> with contributions from Bob Beck <beck@openbsd.org>
we stan. π
π¨ CAVEATS π¨
okay bestie this is the part where I grab your shoulders and look you dead in the eyes:
The function tls_error() returns an internal pointer. It must not be freed by the application, or a double free error will occur. π
and it gets worse β the pointer will become invalid when the next error occurs with ctx. so consequently, if the application may need the message at a later time, it has to copy the string before calling the next libtls function involving ctx β otherwise a segmentation fault or read access to unintended data is the likely result. πππ
not me warning you about a segfault. copy the string. copy it. i'm serious. π
that's the whole page, bestie β Debian's copy of it, dated July 9, 2019, and yes the title up top said TLS_READ(3) with "Library Functions Manual" riding shotgun. now go write some safe TLS code, you absolute icon. π
β¨π¦
π¦ where this page comes from
- alpine 3.22: libretls-doc 3.7.0-r2 Β· ISC AND ( BSD-3-Clause OR MIT )
- alpine 3.24: libretls-doc 3.8.1-r0 Β· ISC AND ( BSD-3-Clause OR MIT )
- arch rolling: libretls 3.8.1-3 Β· ISC AND BSD AND MIT
- debian bookworm: libtls-dev 3.7.0-4
- debian trixie: libtls-dev 3.8.1-2.1+b2
- epel 10: libretls-devel 3.8.1-4.el10_0 Β· ISC AND BSD-3-Clause AND MIT AND LicenseRef-Fedora-Public-Domain
- epel 9: libretls-devel 3.8.1-1.el9 Β· ISC AND BSD-3-Clause AND MIT AND LicenseRef-Fedora-Public-Domain
- fedora 43: libretls-devel 3.8.1-6.fc43 Β· ISC AND BSD-3-Clause AND MIT AND LicenseRef-Fedora-Public-Domain
- fedora 43: libretls-devel 3.8.1-6.fc43 Β· ISC AND BSD-3-Clause AND MIT AND LicenseRef-Fedora-Public-Domain
- fedora 44: libretls-devel 3.8.1-7.fc44 Β· ISC AND BSD-3-Clause AND MIT AND LicenseRef-Fedora-Public-Domain
- fedora 44: libretls-devel 3.8.1-7.fc44 Β· ISC AND BSD-3-Clause AND MIT AND LicenseRef-Fedora-Public-Domain
- nixos 26.05: libretls 3.8.1
- nixpkgs unstable: libretls 3.8.1
- ubuntu jammy: libtls-dev 3.4.1-2build1
- ubuntu noble: libtls-dev 3.8.1-2.1build2
- ubuntu questing: libtls-dev 3.8.1-2.1build2
- ubuntu resolute: libtls-dev 3.8.1-2.1build3
- ubuntu stonking: libtls-dev 3.8.1-2.1build4
- Copyright (c) 2014, 2015 Ted Unangst <tedu@openbsd.org>
- Copyright (c) 2015 Doug Hogan <doug@openbsd.org>
- Copyright (c) 2015 Joel Sing <jsing@openbsd.org>
- Copyright (c) 2015 Bob Beck <beck@openbsd.org>
- Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms