Git Product home page Git Product logo

Comments (6)

eduardsui avatar eduardsui commented on August 16, 2024

Hello. TLSe wants to be transport layer agnostic. The SSL_* functions are just for a basic level of support/testing. You should really use the tls_* interface, which enables you to write your own write function. See tlsclienthello.c:

int send_pending(int client_sock, struct TLSContext *context) {
    unsigned int out_buffer_len = 0;
    const unsigned char *out_buffer = tls_get_write_buffer(context, &out_buffer_len);
    unsigned int out_buffer_index = 0;
    int send_res = 0;
    while ((out_buffer) && (out_buffer_len > 0)) {
        int res = send(client_sock, (char *)&out_buffer[out_buffer_index], out_buffer_len, 0);
        if (res <= 0) {
            send_res = res;
            break;
        }
        out_buffer_len -= res;
        out_buffer_index += res;
    }
    tls_buffer_clear(context);
    return send_res;
}

In my applications, I'm always using epoll/poll/select to check the write-status of the socket.

from tlse.

vbickov avatar vbickov commented on August 16, 2024

This issue is not related to a transport level only. You are clearly using system functions "send" and "recv" in _tls_ssl_private_send_pending (and in your example send_pending), therefore you should expect and process some error codes. Imagine the following situation:

_tls_ssl_private_send_pending (or send_pending) was able to send a part of the buffer, then received EINTR or EAGAIN. In reality - nothing happened. For some reason the system just wants you to retry your attempt. But the method will return error code instead. From user point of view, the only legit reaction is to close the connection, which is wrong.

See more here:

https://unix.stackexchange.com/questions/253349/eintr-is-there-a-rationale-behind-it

from tlse.

eduardsui avatar eduardsui commented on August 16, 2024

I understand the idea, but as I said, EAGAIN and EINTR are transport-layer specific. EAGAIN is set by send(transport layer). Assuming that a the TCP send buffer is full, you may get EAGAIN for a long time. TLSe deals only with the TLS layer. I wanted to be extremely portable and lightweight. The socket layer should be managed by you. As you posted, it is extremely simple to alter send_pending to suit your needs. send_pending isn't part of TLSe, is just an example. You may opt to manage EAGAIN by simply cheching errno or WSAGetLastError ("Error codes set by Windows Sockets are not made available through the errno variable"). I strongly advise you to use tls_* interface instead of SSL_*. When using tls_* you explicitly manage the I/O, see: https://github.com/eduardsui/tlse/blob/master/examples/tlshelloworld.c. In the send_pending function you may check for EAGAIN.

You may replace standard send/recv functions by using SSL_set_io. You may simply rewrite send_cb at your convenience.

I think the correct implementation should look something like this (not tested):

int _tls_ssl_private_send_pending(int client_sock, struct TLSContext *context) {
    unsigned int out_buffer_len = 0;
    const unsigned char *out_buffer = tls_get_write_buffer(context, &out_buffer_len);
    unsigned int out_buffer_index = 0;
    int send_res = 0;
    SOCKET_SEND_CALLBACK write_cb = NULL;
    SSLUserData *ssl_data = (SSLUserData *)context->user_data;
    if (ssl_data)
        write_cb = (SOCKET_SEND_CALLBACK)ssl_data->send;
    while ((out_buffer) && (out_buffer_len > 0)) {
        int res;
        if (write_cb)
            res = write_cb(client_sock, (char *)&out_buffer[out_buffer_index], out_buffer_len, 0);
        else
            res = send(client_sock, (char *)&out_buffer[out_buffer_index], out_buffer_len, 0);
        if (res <= 0) {
            if ((!write_cb) && (res < 0)) {
#ifdef _WIN32
                if (WSAGetLastError() == EAGAIN) {
                    context->tls_buffer_len = out_buffer_len;
                    memmove(context->tls_buffer, out_buffer + out_buffer_index, out_buffer_len);
                    return res;
                }
#else
                if ((errno == EAGAIN) || (errno == EINTR)) {
                    context->tls_buffer_len = out_buffer_len;
                    memmove(context->tls_buffer, out_buffer + out_buffer_index, out_buffer_len);
                    return res;
                }
#endif
            }
            send_res = res;
            break;
        }
        out_buffer_len -= res;
        out_buffer_index += res;
        send_res += res;
    }
    tls_buffer_clear(context);
    return send_res;
}

This will transparently pass errno to the application code to be managed there.

Also, as a side note, using poll to manage I/O provides huge advantages, especially in asynchronous implementations.

from tlse.

vbickov avatar vbickov commented on August 16, 2024

If you want to keep external interface portable, you can return your own error code. OpenSSL returns SSL_ERROR_SYSCALL from SSL_get_error in such case.

from tlse.

wqweto avatar wqweto commented on August 16, 2024

Trying to compile samples in latest master I get this (under WSL)

wqw@wqw-pc:~/tlse/examples$ gcc tlshelloworld.c -o tlshelloworld -DTLS_AMALGAMATION
In file included from tlshelloworld.c:1:0:
../tlse.c: In function ‘_tls_ssl_private_send_pending’:
../tlse.c:10100:22: error: ‘errno’ undeclared (first use in this function)
                 if ((errno == EAGAIN) || (errno == EINTR)) {
                      ^
../tlse.c:10100:22: note: each undeclared identifier is reported only once for each function it appears in
../tlse.c:10100:31: error: ‘EAGAIN’ undeclared (first use in this function)
                 if ((errno == EAGAIN) || (errno == EINTR)) {
                               ^
../tlse.c:10100:52: error: ‘EINTR’ undeclared (first use in this function)
                 if ((errno == EAGAIN) || (errno == EINTR)) {
                                                    ^
tlshelloworld.c: In function ‘main’:
tlshelloworld.c:192:45: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 4 has type ‘struct TLSContext *’ [-Wformat=]
                             fprintf(stderr, "Imported context (size: %i): %x\n", size, imported_context);

from tlse.

eduardsui avatar eduardsui commented on August 16, 2024

I forgot to include errno.h. Thanks.

E.

from tlse.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.