[Help-gnutls] Behavior of gnutls_record_recv()

Laurent Birtz laurent.birtz at kryptiva.com
Fri Jan 4 21:17:51 CET 2008


Hello,

I am considering using GnuTLS as a replacement of openssl for securing
communications between two processes, A and B. However, I am not using a
typical client-server model, where the server waits for a query from the
client and the client waits for the reply from the server. Instead, A
and B are peer processes, and each one can independently decide at any
time to send a message to the other side.

It is possible that one process fill up its input or output queue
because the two processes are not operating at the same speed.
Therefore, read and write quenching must be supported. For instance, if
the input queue of a process is full, then this process must stop
reading data from the other side but continue writing data to the other
side.

Can I use GnuTLS to fullfill these requirements? I am especially
concerned with the gnutls_record_recv() and gnutls_record_send()
functions that can return GNUTLS_E_REHANDSHAKE, GNUTLS_E_INTERRUPTED or
GNUTLS_E_AGAIN (I am using non-blocking sockets). The documentation says
that handshake requests can be ignored. For my quenching scheme to be
implementable, it is important that after a call to gnutls_record_send()
that returns GNUTLS_E_AGAIN, gnutls_record_get_direction() only returns
1, i.e. wait for socket to be writable. Otherwise, if
gnutls_record_get_direction() returns 0, then this means that more data
will need to be received from the remote side, and this will bust my
buffer capacity (in that case, the GnuTLS internal buffer might keep
growing indefinitely).

Pseudo-code showing my application logic:

loop:
int must_read = 0, must_write = 0;

if (! input_queue.is_full()) {
    int error = gnutls_record_recv(...);
   
    if (! error) {
        input_queue.push(...);
        /* Read again later. */
        must_read = 1;
    }
    
    else if (error == GNUTLS_E_INTERRUPTED || error == GNUTLS_E_AGAIN) {
        /* This must hold!! */
        assert(gnutls_record_get_direction(...) == 0);
        must_read = 1;
    }
    
    ... handle other errors ...
}

if (! output_queue.is_empty()) {
    int error = gnutls_record_send(...);
    
    if (! error) {
        /* Write again later. */
        must_write = 1;
    }
    
    else if (error == GNUTLS_E_INTERRUPTED || error == GNUTLS_E_AGAIN) {
        /* This must hold!! */
        assert(gnutls_record_get_direction(...) == 1);
        must_write = 1;
    }
    
    ... handle other errors ...
}

select() according to must_read and must_write;
goto loop;


Thanks a lot for your time,
Laurent Birtz






More information about the Gnutls-help mailing list