From: Nikos Mavrogiannopoulos Date: Tue, 15 Jan 2013 20:23:03 +0000 (+0100) Subject: Added functions to assist buffering during transmission. X-Git-Tag: gnutls_3_1_7~113 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=00059345424e9541eda9b6d6dfb303bdf29f55d5;p=thirdparty%2Fgnutls.git Added functions to assist buffering during transmission. Added the gnutls_sbuf_t structure and accompanying functions to enable buffering in sending application data. --- diff --git a/NEWS b/NEWS index f7b4d9a697..54d5aa60bb 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,9 @@ See the end for copying conditions. ** libgnutls: Simplified the DTLS sliding window implementation. +** libgnutls: Added the gnutls_sbuf_t structure and accompanying +functions to enable buffering in sending application data. + ** libgnutls: Added %RANDOM_PADDING priority string. It enables a new random padding mechanism in TLS allowing random padding in TLS records in all ciphersuites. Both the server and the client must support this @@ -13,6 +16,10 @@ extension. Its status can be checked using gnutls_record_get_random_padding_stat ** API and ABI modifications: gnutls_record_get_random_padding_status: Added +gnutls_sbuf_deinit: Added +gnutls_sbuf_init: Added +gnutls_sbuf_flush: Added +gnutls_sbuf_queue: Added * Version 3.1.6 (released 2013-01-02) diff --git a/doc/cha-gtls-app.texi b/doc/cha-gtls-app.texi index bf4ab617c7..3f8fdd3969 100644 --- a/doc/cha-gtls-app.texi +++ b/doc/cha-gtls-app.texi @@ -9,6 +9,7 @@ * Setting up the transport layer:: * TLS handshake:: * Data transfer and termination:: +* Buffered data transfer:: * Handling alerts:: * Priority Strings:: * Selecting cryptographic key sizes:: @@ -801,6 +802,23 @@ A session can be deinitialized with the @funcref{gnutls_deinit} function. @showfuncdesc{gnutls_bye} @showfuncdesc{gnutls_deinit} +@node Buffered data transfer +@section Buffered data transfer + +Although @funcref{gnutls_record_send} is sufficient to transmit data +to the peer, when many small chunks of data are to be transmitted +it is inefficient and wastes bandwidth due to the TLS record +overhead. In that case it is preferrable to combine the small chunks +before transmission. The @emph{gnutls_sbuf} interface is a helper interface +that provides that functionality. + +@showfuncdesc{gnutls_sbuf_init} + +@showfuncdesc{gnutls_sbuf_queue} + +@showfuncB{gnutls_sbuf_flush,gnutls_sbuf_deinit} + + @node Handling alerts @section Handling alerts During a TLS connection alert messages may be exchanged by the diff --git a/lib/Makefile.am b/lib/Makefile.am index 9a6a587bf9..acf053e237 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -77,7 +77,7 @@ COBJECTS = gnutls_record.c gnutls_compress.c debug.c gnutls_cipher.c \ gnutls_rsa_export.c gnutls_helper.c gnutls_supplemental.c \ random.c crypto-api.c gnutls_privkey.c gnutls_pcert.c \ gnutls_pubkey.c locks.c gnutls_dtls.c system_override.c \ - crypto-backend.c verify-tofu.c pin.c tpm.c + crypto-backend.c verify-tofu.c pin.c tpm.c sbuf.c if ENABLE_PKCS11 COBJECTS += pkcs11.c pkcs11_privkey.c pkcs11_write.c pkcs11_secret.c diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index fd868b5173..15eab2d621 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -1937,6 +1937,19 @@ typedef int (*gnutls_pin_callback_t) (void *userdata, int attempt, void gnutls_certificate_set_pin_function (gnutls_certificate_credentials_t, gnutls_pin_callback_t fn, void *userdata); +/* Buffered session I/O */ +typedef struct gnutls_sbuf_st *gnutls_sbuf_t; + +ssize_t gnutls_sbuf_queue (gnutls_sbuf_t sb, const void *data, + size_t data_size); + +ssize_t gnutls_sbuf_flush (gnutls_sbuf_t sb); + +#define GNUTLS_SBUF_QUEUE_FLUSHES 1 +int gnutls_sbuf_init (gnutls_sbuf_t * sb, gnutls_session_t session, + unsigned int flags); +void gnutls_sbuf_deinit(gnutls_sbuf_t sb); + /* Gnutls error codes. The mapping to a TLS alert is also shown in * comments. diff --git a/lib/libgnutls.map b/lib/libgnutls.map index 2e7630c87f..99c97322da 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -877,6 +877,10 @@ GNUTLS_3_1_0 { gnutls_pubkey_import_x509_crq; gnutls_pubkey_print; gnutls_record_get_random_padding_status; + gnutls_sbuf_deinit; + gnutls_sbuf_init; + gnutls_sbuf_flush; + gnutls_sbuf_queue; } GNUTLS_3_0_0; GNUTLS_PRIVATE { diff --git a/lib/sbuf.c b/lib/sbuf.c new file mode 100644 index 0000000000..e4cf5ed7c8 --- /dev/null +++ b/lib/sbuf.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2013 Nikos Mavrogiannopoulos + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The libdane library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + * + */ + +#include +#include +#include +#include + +struct gnutls_sbuf_st { + gnutls_session_t session; + gnutls_buffer_st buf; + unsigned int flags; +}; + +/** + * gnutls_init: + * @isb: is a pointer to a #gnutls_sbuf_t structure. + * @session: a GnuTLS session + * @flags: should be zero or %GNUTLS_SBUF_QUEUE_FLUSHES + * + * This function initializes a #gnutls_sbuf_t structure associated + * with the provided session. If the flag %GNUTLS_SBUF_QUEUE_FLUSHES + * is set then gnutls_sbuf_queue() will flush when the maximum + * data size for a record is reached. + * + * Returns: %GNUTLS_E_SUCCESS on success, or an error code. + * + * Since: 3.1.7 + **/ +int gnutls_sbuf_init (gnutls_sbuf_t * isb, gnutls_session_t session, + unsigned int flags) +{ +struct gnutls_sbuf_st* sb; + + sb = gnutls_malloc(sizeof(*sb)); + if (sb == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + _gnutls_buffer_init(&sb->buf); + sb->session = session; + sb->flags = flags; + + *isb = sb; + + return 0; +} + +/** + * gnutls_sbuf_deinit: + * @sb: is a #gnutls_sbuf_t structure. + * + * This function clears all buffers associated with the @sb + * structure. The GnuTLS session associated with the structure + * is left intact. + * + * Since: 3.1.7 + **/ +void gnutls_sbuf_deinit(gnutls_sbuf_t sb) +{ + _gnutls_buffer_clear(&sb->buf); + gnutls_free(sb); +} + +/** + * gnutls_sbuf_queue: + * @sb: is a #gnutls_sbuf_t structure. + * @data: contains the data to send + * @data_size: is the length of the data + * + * This function is the buffered equivalent of gnutls_record_send(). + * Instead of sending the data immediately the data are buffered + * until gnutls_sbuf_queue() is called, or if the flag %GNUTLS_SBUF_QUEUE_FLUSHES + * is set, until the number of bytes for a full record is reached. + * + * This function must only be used with blocking sockets. + * + * Returns: On success, if no data were sent then zero is returned, otherwise the + * number of bytes sent. If an error occurs a negative error code is returned. + * + * Since: 3.1.7 + **/ +ssize_t gnutls_sbuf_queue (gnutls_sbuf_t sb, const void *data, + size_t data_size) +{ +int ret; + + ret = _gnutls_buffer_append_data(&sb->buf, data, data_size); + if (ret < 0) + return gnutls_assert_val(ret); + + if ((sb->flags & GNUTLS_SBUF_QUEUE_FLUSHES) && + sb->buf.length >= MAX_RECORD_SEND_SIZE(sb->session)) + { + do + { + ret = gnutls_record_send(sb->session, sb->buf.data, sb->buf.length); + } + while (ret < 0 && gnutls_error_is_fatal(ret) == 0); + if (ret < 0) + return gnutls_assert_val(ret); + + sb->buf.data += ret; + sb->buf.length -= ret; + return ret; + } + + return 0; +} + +/** + * gnutls_sbuf_flush: + * @sb: is a #gnutls_sbuf_t structure. + * + * This function flushes the buffer @sb. All the data stored are transmitted. + * + * This function must only be used with blocking sockets. + * + * Returns: On success, the number of bytes sent, otherwise a negative error code. + * + * Since: 3.1.7 + **/ +ssize_t gnutls_sbuf_flush (gnutls_sbuf_t sb) +{ +int ret; +ssize_t total = 0; + + while(sb->buf.length > 0) + { + do + { + ret = gnutls_record_send(sb->session, sb->buf.data, sb->buf.length); + } + while (ret < 0 && gnutls_error_is_fatal(ret) == 0); + if (ret < 0) + return gnutls_assert_val(ret); + + sb->buf.data += ret; + sb->buf.length -= ret; + total += ret; + } + + return total; +} diff --git a/tests/Makefile.am b/tests/Makefile.am index c06ba0f3f7..26f7ab2a60 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -71,7 +71,7 @@ ctests = mini-deflate simple gc set_pkcs12_cred certder certuniqueid \ mini-termination mini-x509-cas mini-x509-2 pkcs12_simple \ mini-emsgsize-dtls mini-handshake-timeout chainverify-unsorted \ mini-dtls-heartbeat mini-x509-callbacks key-openssl \ - mini-dtls-srtp mini-dtls-record + mini-dtls-srtp mini-dtls-record mini-sbuf if ENABLE_OCSP ctests += ocsp diff --git a/tests/mini-sbuf.c b/tests/mini-sbuf.c new file mode 100644 index 0000000000..c2478a8e5c --- /dev/null +++ b/tests/mini-sbuf.c @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2008-2012 Free Software Foundation, Inc. + * + * Author: Simon Josefsson + * + * This file is part of GnuTLS. + * + * GnuTLS is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuTLS is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GnuTLS; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include "eagain-common.h" + +#include "utils.h" + +const char* side = ""; + +static void +tls_log_func (int level, const char *str) +{ + fprintf (stderr, "%s|<%d>| %s", side, level, str); +} + +#define MAX_BUF 60*1024 + +static unsigned char server_buf[MAX_BUF]; +static unsigned char client_buf[MAX_BUF]; + +void +doit (void) +{ + /* Server stuff. */ + gnutls_anon_server_credentials_t s_anoncred; + const gnutls_datum_t p3 = { (unsigned char *) pkcs3, strlen (pkcs3) }; + static gnutls_dh_params_t dh_params; + gnutls_session_t server; + gnutls_sbuf_t ssbuf; + int sret = GNUTLS_E_AGAIN; + /* Client stuff. */ + gnutls_anon_client_credentials_t c_anoncred; + gnutls_session_t client; + int cret = GNUTLS_E_AGAIN; + /* Need to enable anonymous KX specifically. */ + int ret; + ssize_t left, spos, cpos; + + /* General init. */ + gnutls_global_init (); + gnutls_global_set_log_function (tls_log_func); + if (debug) + gnutls_global_set_log_level (4711); + + /* Init server */ + gnutls_anon_allocate_server_credentials (&s_anoncred); + gnutls_dh_params_init (&dh_params); + gnutls_dh_params_import_pkcs3 (dh_params, &p3, GNUTLS_X509_FMT_PEM); + gnutls_anon_set_server_dh_params (s_anoncred, dh_params); + gnutls_init (&server, GNUTLS_SERVER); + gnutls_priority_set_direct (server, "NONE:+VERS-TLS-ALL:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+ANON-DH", NULL); + gnutls_credentials_set (server, GNUTLS_CRD_ANON, s_anoncred); + gnutls_dh_set_prime_bits (server, 1024); + gnutls_transport_set_push_function (server, server_push); + gnutls_transport_set_pull_function (server, server_pull); + gnutls_transport_set_ptr (server, (gnutls_transport_ptr_t)server); + + gnutls_sbuf_init(&ssbuf, server, GNUTLS_SBUF_QUEUE_FLUSHES); + + gnutls_rnd(GNUTLS_RND_NONCE, server_buf, sizeof(server_buf)); + + /* Init client */ + gnutls_anon_allocate_client_credentials (&c_anoncred); + gnutls_init (&client, GNUTLS_CLIENT); + gnutls_priority_set_direct (client, "NONE:+VERS-TLS-ALL:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+ANON-DH", NULL); + gnutls_credentials_set (client, GNUTLS_CRD_ANON, c_anoncred); + gnutls_transport_set_push_function (client, client_push); + gnutls_transport_set_pull_function (client, client_pull); + gnutls_transport_set_ptr (client, (gnutls_transport_ptr_t)client); + + memset(client_buf, 0, sizeof(client_buf)); + + HANDSHAKE(client, server); + + if (debug) + success ("Handshake established\n"); + +#define SEND_SIZE 100 + left = sizeof(server_buf); + spos = 0; + cpos = 0; + while (left > 0) + { + if (left > SEND_SIZE) + { + ret = gnutls_sbuf_queue(ssbuf, server_buf+spos, SEND_SIZE); + left -= SEND_SIZE; + spos += SEND_SIZE; + } + else + { + gnutls_sbuf_queue(ssbuf, server_buf+spos, left); + ret = gnutls_sbuf_flush(ssbuf); + left = 0; + } + + if (ret < 0) + { + fail("Error sending\n"); + abort(); + } + + if (ret > 0) + { /* received in client side */ + ret = gnutls_record_recv(client, client_buf+cpos, sizeof(client_buf)-cpos); + if (ret > 0) + cpos += ret; + + if (ret < 0) + { + fail("Error sending\n"); + abort(); + } + } + } + + if (memcmp(client_buf, server_buf, sizeof(server_buf)) != 0) + { + fail("Data do not match!\n"); + abort(); + } + + gnutls_bye (client, GNUTLS_SHUT_RDWR); + gnutls_bye (server, GNUTLS_SHUT_RDWR); + + gnutls_deinit (client); + gnutls_deinit (server); + + gnutls_anon_free_client_credentials (c_anoncred); + gnutls_anon_free_server_credentials (s_anoncred); + + gnutls_dh_params_deinit (dh_params); + + gnutls_global_deinit (); +}