BIO_ACCEPT(3)

πŸ“¦
version 4.4.0 Β· 2 versions
, in arch rolling

πŸ’… BIO_ACCEPT(3) β€” wrappers for socket operations πŸ’…

okay so bestie, this is a page from the Library Functions Manual for libcrypto (yeah, the OpenSSL-ish crypto library), and it's literally just a little pile of socket-helper functions. nothing scary. come sit with me 🫢


✨ NAME ✨

BIO_get_host_ip, BIO_get_port, BIO_get_accept_socket, BIO_accept, BIO_sock_error, BIO_sock_non_fatal_error, BIO_sock_should_retry, BIO_socket_nbio, BIO_set_tcp_ndelay β€” these are the wrappers for socket operations. yes, all nine of them, they're a package deal πŸ’–


πŸŽ€ SYNOPSIS πŸŽ€

this is the library β€œlibcrypto” situation, and you get it via #include <openssl/bio.h>:

int
BIO_get_host_ip(const char *hostname, unsigned char *in_addr_buffer);

int
BIO_get_port(const char *servname, unsigned short *port);

int
BIO_get_accept_socket(char *host_port, int bind_mode);

int
BIO_accept(int socket, char **addr);

int
BIO_sock_error(int socket);

int
BIO_sock_non_fatal_error(int errnum);

int
BIO_sock_should_retry(int retval);

int
BIO_socket_nbio(int socket, int mode);

int
BIO_set_tcp_ndelay(int socket, int on);

like, copy-paste this verbatim, don't be cute with it πŸ™ˆ


🌸 DESCRIPTION 🌸

πŸ¦‹ the lookup girlies

BIO_get_host_ip() looks up one IPv4 address for the given hostname using getaddrinfo(3), then writes the first returned IPv4 address into *in_addr_buffer. and like β€” the caller is responsible for handing over a buffer that's at least sizeof(in_addr_t) bytes long. no, she won't check for you, she trusts you. after a successful call you gotta cast in_addr_buffer to (in_addr_t *). yes, really. periodt.

BIO_get_port() looks up servname in the services(5) database using getaddrinfo(3) and stores the associated port number at the location specified by the port argument. slay.

🧚 the socket-making girlies

BIO_get_accept_socket() creates an IPv4 TCP socket and listen(2)s for incoming connections. iconic behaviour. the string host_port gets parsed:

  • if it contains a colon, the substring before the colon is interpreted as a local hostname of the interface to bind(2) to.
  • if the hostname is empty, consists of a single asterisk ("*:..."), or if there is no colon, INADDR_ANY is used instead of a local hostname.
  • the rest of the string host_port, or the whole string if it contains no colon, is treated as a service name.
  • the hostname and the service name are converted to a local IP address and port number using getaddrinfo(3).

and if bind_mode is the constant BIO_BIND_REUSEADDR, allowing local address reuse is attempted using setsockopt(2) with an argument of SO_REUSEADDR before calling bind(2). considerate queen πŸ’…

BIO_accept() calls accept(2) to receive one connection on the socket. when it receives a connection, it free(3)s *addr, and if it's an IPv4 connection, it allocates a new string, writes the peer IP address in dotted decimal form, a colon, and the decimal port number into the string, and stores a pointer to the string in *addr. and for other address families, or if getnameinfo(3) or memory allocation fails, *addr is set to NULL but BIO_accept() succeeds anyway. she's flexible like that.

⭐ the error-inspection girlies

BIO_sock_error() retrieves, clears, and returns the error status code of the socket by calling getsockopt(2) with arguments SOL_SOCKET and SO_ERROR. three jobs, one function.

BIO_sock_non_fatal_error() determines whether the error status code errnum represents a recoverable error.

BIO_sock_should_retry() determines whether a recoverable error occurred by inspecting both errno(2) and retval, which is supposed to usually be the return value of a previously called function like BIO_accept(), BIO_read(3), or BIO_write(3). iykyk.

πŸ”₯ the mode-setting girlies

BIO_socket_nbio() β€” if mode is non-zero, it switches the socket to non-blocking mode using fcntl(2). if mode is 0, it switches to blocking mode. binary queen.

BIO_set_tcp_ndelay() sets the TCP_NODELAY option on the socket if on is 1 or clears it if on is 0; see tcp(4) for details. no in-between, bestie. πŸ’€


πŸ‘‘ RETURN VALUES πŸ‘‘

BIO_get_host_ip(), BIO_get_port(), and BIO_socket_nbio() return 1 on success or 0 on failure. that's it. simple.

BIO_get_accept_socket() returns the file descriptor of the newly created listening socket, or -1 if:

BIO_accept() returns the file descriptor of the received connection, or:

  • -1 on fatal errors β€” that is, when addr is NULL or accept(2) fails fatally
  • -2 when accept(2) fails in a non-fatal way and might succeed when retried later
lowkey the -1 vs -2 distinction matters, don't sleep on it 🫢

BIO_sock_error() returns an error status code like EAGAIN, ECONNABORTED, ECONNREFUSED, ECONNRESET, ELOOP, EMSGSIZE, ENOBUFS, ENOTCONN, EPIPE, ETIMEDOUT, or others, β€” or 0 if the socket is not in an error state, or 1 if getsockopt(2) fails. the way there are like three different kinds of return value here 😭

BIO_sock_non_fatal_error() returns 1 if errnum is EAGAIN, EALREADY, EINPROGRESS, EINTR, or ENOTCONN and 0 otherwise β€” even if errnum is 0. like, yes, even then. don't @ me.

BIO_sock_should_retry() returns 1 if BIO_sock_non_fatal_error(errno) is 1 and retval is either 0 or -1, or 0 otherwise.

BIO_set_tcp_ndelay() returns 0 on success or -1 on failure.


πŸ’€ ERRORS πŸ’€

if BIO_get_host_ip(), BIO_get_port(), or BIO_get_accept_socket() fail, or BIO_accept() fails fatally, the following diagnostics can be retrieved with ERR_get_error(3), ERR_GET_REASON(3), and ERR_reason_error_string(3):

  • BIO_R_ACCEPT_ERROR "accept error" β€” accept(2) failed fatally in BIO_accept().
  • BIO_R_BAD_HOSTNAME_LOOKUP "bad hostname lookup" β€” getaddrinfo(3) failed or hostname was NULL in BIO_get_host_ip(), or getaddrinfo(3) failed in BIO_get_accept_socket().
  • BIO_R_INVALID_ARGUMENT "invalid argument" β€” getaddrinfo(3) failed in BIO_get_port().
  • ERR_R_MALLOC_FAILURE "malloc failure" β€” memory allocation failed in BIO_get_accept_socket(), or BIO_accept() succeeded but was unable to allocate memory for *addr. for BIO_accept(), the returned file descriptor is valid, and communication with the peer can be attempted using it. so like, it's fine? kind of? 🫢
  • BIO_R_NO_PORT_SPECIFIED "no port specified" β€” the servname argument was NULL in BIO_get_port(), or host_port was NULL or ended after the first colon in BIO_get_accept_socket().
  • BIO_R_NULL_PARAMETER "null parameter" β€” the addr argument was NULL in BIO_accept().
  • BIO_R_UNABLE_TO_BIND_SOCKET "unable to bind socket" β€” bind(2) failed in BIO_get_accept_socket().
  • BIO_R_UNABLE_TO_CREATE_SOCKET "unable to create socket" β€” socket(2) failed in BIO_get_accept_socket().
  • BIO_R_UNABLE_TO_LISTEN_SOCKET "unable to listen socket" β€” listen(2) failed in BIO_get_accept_socket().

🌈 SEE ALSO 🌈

bind(2), connect(2), errno(2), fcntl(2), getsockopt(2), listen(2), sigaction(2), socket(2), BIO_new(3), BIO_read(3), getaddrinfo(3), ip(4), tcp(4)

go read them, they're all icons in their own right πŸ‘‘


πŸ“ HISTORY πŸ“

BIO_sock_should_retry() first appeared in SSLeay 0.6.5, and the other functions except BIO_socket_nbio() in SSLeay 0.8.0. they've all been available since OpenBSD 2.4. legacy behaviour, honestly.

BIO_socket_nbio() first appeared in SSLeay 0.9.1 and has been available since OpenBSD 2.6. she's the baby of the group πŸ’œ


and that's the whole page bestie, no cap. this one came out of Debian and it's dated September 10, 2026 β€” so like, future-dated and fabulous. go forth and accept(2) responsibly πŸŽ€βœ¨πŸ¦‹

πŸ“¦ where this page comes from
  • arch rolling: libressl 4.4.0-1 Β· ISC AND custom:OpenSSL
  • πŸ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms