Error Handling

s2n-tls functions that return 'int' return 0 to indicate success and -1 to indicate failure. s2n-tls functions that return pointer types return NULL in the case of failure. When an s2n-tls function returns a failure, s2n_errno will be set to a value corresponding to the error. This error value can be translated into a string explaining the error in English by calling s2n_strerror(s2n_errno, "EN"). A string containing human readable error name, can be generated with s2n_strerror_name(). A string containing internal debug information, including filename and line number, can be generated with s2n_strerror_debug(). This string is useful to include when reporting issues to the s2n-tls development team.

Example:

if (s2n_config_set_cipher_preferences(config, prefs) < 0) {
    printf("Setting cipher prefs failed! %s : %s", s2n_strerror(s2n_errno, "EN"), s2n_strerror_debug(s2n_errno, "EN"));
    return -1;
}

NOTE: To avoid possible confusion, s2n_errno should be cleared after processing an error: s2n_errno = S2N_ERR_T_OK

When using s2n-tls outside of C, the address of the thread-local s2n_errno may be obtained by calling the s2n_errno_location() function. This will ensure that the same TLS mechanisms are used with which s2n-tls was compiled.

Error Types

s2n-tls organizes errors into different "types" to allow applications to handle error values without catching all possibilities. Applications using non-blocking I/O should check the error type to determine if the I/O operation failed because it would block or for some other error. To retrieve the type for a given error use s2n_error_get_type(). Applications should perform any error handling logic using these high level types:

Here's an example that handles errors based on type:

#define SUCCESS 0
#define FAILURE 1
#define RETRY 2

s2n_errno = S2N_ERR_T_OK;
if (s2n_negotiate(conn, &blocked) < 0) {
    switch(s2n_error_get_type(s2n_errno)) {
        case S2N_ERR_T_BLOCKED:
            /* Blocked, come back later */
            return RETRY;
        case S2N_ERR_T_CLOSED:
            return SUCCESS;
        case S2N_ERR_T_IO:
            handle_io_err(errno);
            return FAILURE;
        case S2N_ERR_T_PROTO:
            handle_proto_err();
            return FAILURE;
        case S2N_ERR_T_ALERT:
            log_alert(s2n_connection_get_alert(conn));
            return FAILURE;
        /* Everything else */
        default:
            log_other_error();
            return FAILURE;
    }
}

Blinding

Blinding is a mitigation against timing side-channels which in some cases can leak information about encrypted data. By default s2n-tls will cause a thread to sleep between 10 and 30 seconds whenever tampering is detected.

Setting the S2N_SELF_SERVICE_BLINDING option with s2n_connection_set_blinding() turns off this behavior. This is useful for applications that are handling many connections in a single thread. In that case, if s2n_recv() or s2n_negotiate() return an error, self-service applications must call s2n_connection_get_delay() and pause activity on the connection for the specified number of nanoseconds before calling close() or shutdown(). s2n_shutdown() will fail if called before the blinding delay elapses.

Stacktraces

s2n-tls has an mechanism to capture stacktraces when errors occur. This mechanism is off by default, but can be enabled in code by calling s2n_stack_traces_enabled_set(). It can be enabled globally by setting the environment variable S2N_PRINT_STACKTRACE=1.

Call s2n_print_stacktrace() to print your stacktrace.

Note: Enabling stacktraces can significantly slow down unit tests, causing failures on tests (such as s2n_cbc_verify) that measure the timing of events.