NOEXPORT void client_run(CLI *c) {
    jmp_buf exception_buffer, *exception_backup;
    int err, rst;
#ifndef USE_FORK
    int num;
#endif

#ifndef USE_FORK
#ifdef USE_OS_THREADS
    CRYPTO_atomic_add(&num_clients, 1, &num, stunnel_locks[LOCK_CLIENTS]);
#else
    num=++num_clients;
#endif
    ui_clients(num);
#endif

        /* initialize the client context */
    c->remote_fd.fd=INVALID_SOCKET;
    c->fd=INVALID_SOCKET;
    c->ssl=NULL;
    c->sock_bytes=c->ssl_bytes=0;
    if(c->opt->option.client) {
        c->sock_rfd=&(c->local_rfd);
        c->sock_wfd=&(c->local_wfd);
        c->ssl_rfd=c->ssl_wfd=&(c->remote_fd);
    } else {
        c->sock_rfd=c->sock_wfd=&(c->remote_fd);
        c->ssl_rfd=&(c->local_rfd);
        c->ssl_wfd=&(c->local_wfd);
    }
    c->fds=s_poll_alloc();
    addrlist_clear(&c->connect_addr, 0);

        /* try to process the request */
    exception_backup=c->exception_pointer;
    c->exception_pointer=&exception_buffer;
    err=setjmp(exception_buffer);
    if(!err) {
        client_try(c);
    }
    c->exception_pointer=exception_backup;

    rst=err==1 && c->opt->option.reset;
    s_log(LOG_NOTICE,
        "Connection %s: %llu byte(s) sent to TLS, %llu byte(s) sent to socket",
        rst ? "reset" : "closed",
        (unsigned long long)c->ssl_bytes, (unsigned long long)c->sock_bytes);

        /* cleanup temporary (e.g. IDENT) socket */
    if(c->fd!=INVALID_SOCKET)
        closesocket(c->fd);
    c->fd=INVALID_SOCKET;

        /* cleanup the TLS context */
    if(c->ssl) { /* TLS initialized */
        SSL_set_shutdown(c->ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
        SSL_free(c->ssl);
        c->ssl=NULL;
#if OPENSSL_VERSION_NUMBER >= 0x10100006L
        /* OpenSSL version >= 1.1.0-pre6 */
        /* the function is no longer needed */
#elif OPENSSL_VERSION_NUMBER >= 0x10100004L
        /* OpenSSL version 1.1.0-pre4 or 1.1.0-pre5 */
        ERR_remove_thread_state();
#elif OPENSSL_VERSION_NUMBER >= 0x10000000L
        /* OpenSSL version >= 1.0.0 */
        ERR_remove_thread_state(NULL);
#else
        /* OpenSSL version < 1.0.0 */
        ERR_remove_state(0);
#endif
    }

        /* cleanup the remote socket */
    if(c->remote_fd.fd!=INVALID_SOCKET) { /* remote socket initialized */
        if(rst && c->remote_fd.is_socket) /* reset */
            reset(c->remote_fd.fd, "linger (remote)");
        closesocket(c->remote_fd.fd);
        s_log(LOG_DEBUG, "Remote descriptor (FD=%ld) closed",
            (long)c->remote_fd.fd);
        c->remote_fd.fd=INVALID_SOCKET;
    }

        /* cleanup the local socket */
    if(c->local_rfd.fd!=INVALID_SOCKET) { /* local socket initialized */
        if(c->local_rfd.fd==c->local_wfd.fd) {
            if(rst && c->local_rfd.is_socket)
                reset(c->local_rfd.fd, "linger (local)");
            closesocket(c->local_rfd.fd);
            s_log(LOG_DEBUG, "Local descriptor (FD=%ld) closed",
                (long)c->local_rfd.fd);
        } else { /* stdin/stdout */
            if(rst && c->local_rfd.is_socket)
                reset(c->local_rfd.fd, "linger (local_rfd)");
            if(rst && c->local_wfd.is_socket)
                reset(c->local_wfd.fd, "linger (local_wfd)");
        }
        c->local_rfd.fd=c->local_wfd.fd=INVALID_SOCKET;
    }

#ifdef USE_FORK
    /* display child return code if it managed to arrive on time */
    /* otherwise it will be retrieved by the init process and ignored */
    if(c->opt->exec_name) /* 'exec' specified */
        pid_status_hang("Child process"); /* null SIGCHLD handler was used */
    s_log(LOG_DEBUG, "Service [%s] finished", c->opt->servname);
#else
#ifdef USE_OS_THREADS
    CRYPTO_atomic_add(&num_clients, -1, &num, stunnel_locks[LOCK_CLIENTS]);
#else
    num=--num_clients;
#endif
    ui_clients(num);
    s_log(LOG_DEBUG, "Service [%s] finished (%ld left)", c->opt->servname, num);
#endif

        /* free the client context */
    str_free(c->connect_addr.addr);
    /* a client does not have its own local copy of
       c->connect_addr.session and c->connect_addr.fd */
    s_poll_free(c->fds);
    str_free(c->accepted_address);
}