]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Added functions to assist buffering during transmission.
authorNikos Mavrogiannopoulos <nmav@gnutls.org>
Tue, 15 Jan 2013 20:23:03 +0000 (21:23 +0100)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Tue, 15 Jan 2013 20:23:03 +0000 (21:23 +0100)
Added the gnutls_sbuf_t structure and accompanying
functions to enable buffering in sending application data.

NEWS
doc/cha-gtls-app.texi
lib/Makefile.am
lib/includes/gnutls/gnutls.h.in
lib/libgnutls.map
lib/sbuf.c [new file with mode: 0644]
tests/Makefile.am
tests/mini-sbuf.c [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index f7b4d9a6976febde9c4bfb9d20dc568a3ddbf99a..54d5aa60bb42697d71d30e81bc839f266f188ab2 100644 (file)
--- 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)
index bf4ab617c7d332ba982480d18a4040b27dcc82e8..3f8fdd396971e82ba6794e9592b62ab0e447919d 100644 (file)
@@ -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
index 9a6a587bf94cc4d51f47f614fe4466fea9600074..acf053e23791af5daccdae8d925e8fa7661c0597 100644 (file)
@@ -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
index fd868b5173abd6a49ebb98ec1df7d4c055aea310..15eab2d6217af0f7b65e2de9aa3205e68d5f7fae 100644 (file)
@@ -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.
index 2e7630c87f9732ae8dd8292e1cc307aed796e708..99c97322dac1f00d22f9d52bbd146828df3ec11a 100644 (file)
@@ -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 (file)
index 0000000..e4cf5ed
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include <gnutls_int.h>
+#include <gnutls_errors.h>
+#include <gnutls_num.h>
+#include <gnutls_str.h>
+
+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;
+}
index c06ba0f3f72cf83f05bc23e24c6caaee0d4b9bd1..26f7ab2a60b5a5ee556743132858b787e3cf46f3 100644 (file)
@@ -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 (file)
index 0000000..c2478a8
--- /dev/null
@@ -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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#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 ();
+}