]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Added support for non-blocking DTLS.
authorNikos Mavrogiannopoulos <nmav@gnutls.org>
Sun, 3 Apr 2011 21:42:13 +0000 (23:42 +0200)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Sun, 3 Apr 2011 21:42:20 +0000 (23:42 +0200)
Added mini-eagain-dtls to test its operation.
Improved mini-eagain.

12 files changed:
.gitignore
lib/gnutls_buffers.c
lib/gnutls_dtls.c
lib/gnutls_int.h
lib/gnutls_state.c
lib/includes/gnutls/gnutls.h.in
lib/system.h
tests/Makefile.am
tests/eagain-common.h [new file with mode: 0644]
tests/mini-eagain-dtls.c [new file with mode: 0644]
tests/mini-eagain.c
tests/utils.c

index d286453d44c721aa9e5d0293e533391685fd6d73..4b94e31cb947241d81b9a036fbb40a5f993c948d 100644 (file)
@@ -412,6 +412,8 @@ src/psktool
 src/srptool
 stamp-h1
 tags
+tests/rng-fork
+tests/mini-eagain-dtls
 tests/*/out
 tests/Makefile
 tests/Makefile.in
@@ -446,6 +448,7 @@ tests/mpi
 tests/netconf-psk
 tests/nul-in-x509-names
 tests/openpgp-auth
+tests/openpgp-auth2
 tests/openpgp-certs/Makefile
 tests/openpgp-certs/Makefile.in
 tests/openpgp-keyring
index 778af8d0bc783ad7719b04c7d99c949592e29ee4..39657a0ed6bb7d0b8847f9ad6d37b4154f98fe03 100644 (file)
@@ -54,6 +54,7 @@
 #include <system.h>
 #include <gnutls_constate.h> /* gnutls_epoch_get */
 #include <errno.h>
+#include <system.h>
 
 #ifndef EAGAIN
 #define EAGAIN EWOULDBLOCK
@@ -61,7 +62,7 @@
 
 /* this is the maximum number of messages allowed to queue.
  */
-#define MAX_QUEUE 16
+#define MAX_QUEUE 32
 
 /* Buffers received packets of type APPLICATION DATA and
  * HANDSHAKE DATA.
@@ -553,7 +554,7 @@ _gnutls_io_write_flush (gnutls_session_t session)
       tosend += msg.size;
 
       /* we buffer up to MAX_QUEUE messages */
-      if (i >= sizeof (iovec) / sizeof (iovec[0]))
+      if (i >= MAX_QUEUE)
         {
           gnutls_assert ();
           return GNUTLS_E_INTERNAL_ERROR;
@@ -676,7 +677,6 @@ _gnutls_handshake_io_write_flush (gnutls_session_t session)
     }
 
   return _gnutls_io_write_flush (session);
-
 }
 
 
@@ -914,7 +914,7 @@ handshake_buffer_st* recv_buf = session->internals.handshake_recv_buffer;
     {
       if (session->internals.handshake_recv_buffer_size == 0 ||
         (session->internals.dtls.hsk_read_seq != recv_buf[LAST_ELEMENT].sequence))
-        return gnutls_assert_val(GNUTLS_E_AGAIN);
+        goto timeout;
 
       if (htype != recv_buf[LAST_ELEMENT].htype)
         {
@@ -932,7 +932,7 @@ handshake_buffer_st* recv_buf = session->internals.handshake_recv_buffer;
           return 0;
         }
       else
-        return gnutls_assert_val(GNUTLS_E_AGAIN);
+        goto timeout;
     }
   else /* TLS */
     {
@@ -949,7 +949,18 @@ handshake_buffer_st* recv_buf = session->internals.handshake_recv_buffer;
           return 0;
         }
       else
-        return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+        return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+    }
+
+timeout:
+  if (time(0)-session->internals.dtls.handshake_start_time > session->internals.dtls.total_timeout/1000) 
+    return gnutls_assert_val(GNUTLS_E_TIMEDOUT);
+  else
+    {
+      if (session->internals.dtls.blocking != 0)
+        millisleep(50);
+        
+      return gnutls_assert_val(GNUTLS_E_AGAIN);
     }
 }
 
@@ -1108,7 +1119,7 @@ next:
       while(session->internals.handshake_recv_buffer_size > 0 &&
         recv_buf[LAST_ELEMENT].sequence < session->internals.dtls.hsk_read_seq)
         {
-          _gnutls_audit_log("Discarded replayed handshake packet with sequence %d\n", tmp.sequence);
+          _gnutls_audit_log("Discarded replayed handshake packet with sequence %d\n", recv_buf[LAST_ELEMENT].sequence);
           _gnutls_handshake_buffer_clear(&recv_buf[LAST_ELEMENT]);
           session->internals.handshake_recv_buffer_size--;
         }
@@ -1128,8 +1139,10 @@ _gnutls_handshake_io_recv_int (gnutls_session_t session,
   int ret;
 
   ret = get_last_packet(session, htype, hsk);
-  if (ret >= 0)
-    return ret;
+  if (ret != GNUTLS_E_AGAIN && ret != GNUTLS_E_INTERRUPTED && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+    {
+      return gnutls_assert_val(ret);
+    }
 
   /* try using the already existing records before
    * trying to receive.
index 076be8c643173139d1458c3f78ae7a275f2380ba..89dc0661bb68d62be4d366e6cdedc2223e8d4a32 100644 (file)
@@ -1,7 +1,8 @@
 /*
- * Copyright (C) 2009 Free Software Foundation (copyright assignement pending)
+ * Copyright (C) 2009,2011 Free Software Foundation
  *
- * Author: Jonathan Bastien-Filiatrault
+ * Authors: Jonathan Bastien-Filiatrault
+ *          Nikos Mavrogiannopoulos
  *
  * This file is part of GNUTLS.
  *
@@ -155,6 +156,10 @@ time_t now = time(0);
 
 }
 
+#define UPDATE_TIMER \
+      session->internals.dtls.actual_retrans_timeout *= 2; \
+      session->internals.dtls.actual_retrans_timeout %= MAX_DTLS_TIMEOUT
+
 /* This function transmits the flight that has been previously
  * buffered.
  *
@@ -170,13 +175,52 @@ int ret;
   mbuffer_head_st *const send_buffer =
     &session->internals.handshake_send_buffer;
   mbuffer_st *cur;
-  unsigned int total_timeout = 0;
-  unsigned int timeout = session->internals.dtls.retrans_timeout;
   gnutls_handshake_description_t last_type = 0;
+  time_t now = time(0);
+
+  /* If we have already sent a flight and we are operating in a 
+   * non blocking way, check if it is time to retransmit or just
+   * return.
+   */
+  if (session->internals.dtls.flight_init != 0 && session->internals.dtls.blocking == 0)
+    {
+      /* just in case previous run was interrupted */
+      ret = _gnutls_io_write_flush (session);
+
+      if (session->internals.dtls.last_flight == 0)
+        {
+          /* check for ACK */
+          ret = _gnutls_io_check_recv(session, 0);
+          if (ret == GNUTLS_E_TIMEDOUT)
+            {
+              /* if no retransmission is required yet just return 
+               */
+              if (now-session->internals.dtls.handshake_start_time < session->internals.dtls.actual_retrans_timeout)
+                {
+                  session->internals.dtls.handshake_last_call = now;
+                  goto nb_timeout;
+                }
+            }
+          else /* received ack */
+            {
+              ret = 0;
+              goto cleanup;
+            }
+        }
+      else
+        return ret;
+    }
+
 
   do 
     {
-      _gnutls_dtls_log ("DTLS[%p]: Start of flight transmission.\n", session);
+      if (now-session->internals.dtls.handshake_start_time >= session->internals.dtls.total_timeout/1000) 
+        {
+          ret = gnutls_assert_val(GNUTLS_E_TIMEDOUT);
+          goto cleanup;
+        }
+
+      _gnutls_dtls_log ("DTLS[%p]: %sstart of flight transmission.\n", session,  (session->internals.dtls.flight_init == 0)?"":"re-");
 
       for (cur = send_buffer->head;
            cur != NULL; cur = cur->next)
@@ -185,12 +229,32 @@ int ret;
           last_type = cur->htype;
         }
 
+      if (session->internals.dtls.flight_init == 0)
+        {
+          session->internals.dtls.flight_init = 1;
+          session->internals.dtls.handshake_last_call = now;
+          session->internals.dtls.actual_retrans_timeout = session->internals.dtls.retrans_timeout;
+
+          if (last_type == GNUTLS_HANDSHAKE_FINISHED && _dtls_is_async(session))
+            {
+              /* we cannot do anything here. We just return 0 and
+               * if a retransmission occurs because peer didn't receive it
+               * we rely on the record layer calling this function again.
+               */
+              session->internals.dtls.last_flight = 1;
+            }
+          else
+            session->internals.dtls.last_flight = 0;
+
+
+        }
+
       ret = _gnutls_io_write_flush (session);
       if (ret < 0)
         return gnutls_assert_val(ret);
 
       /* last message in handshake -> no ack */
-      if (last_type == GNUTLS_HANDSHAKE_FINISHED && _dtls_is_async(session))
+      if (session->internals.dtls.last_flight != 0)
         {
           /* we cannot do anything here. We just return 0 and
            * if a retransmission occurs because peer didn't receive it
@@ -200,17 +264,21 @@ int ret;
         }
       else /* all other messages -> implicit ack (receive of next flight) */
         {
-          ret = _gnutls_io_check_recv(session, timeout);
+          if (session->internals.dtls.blocking != 0)
+            ret = _gnutls_io_check_recv(session, session->internals.dtls.actual_retrans_timeout);
+          else
+            {
+              ret = _gnutls_io_check_recv(session, 0);
+              if (ret == GNUTLS_E_TIMEDOUT)
+                {
+                  UPDATE_TIMER;
+                  goto nb_timeout;
+                }
+            }
         }
 
-      total_timeout += timeout;
-      timeout *= 2;
-      timeout %= MAX_DTLS_TIMEOUT;
-
-      if (total_timeout >= session->internals.dtls.total_timeout) {
-        ret = gnutls_assert_val(GNUTLS_E_TIMEDOUT);
-        goto cleanup;
-      }
+      UPDATE_TIMER;
+      now = time(0);
     } while(ret == GNUTLS_E_TIMEDOUT);
 
   if (ret < 0)
@@ -219,15 +287,20 @@ int ret;
       goto cleanup;
     }
 
-  _gnutls_dtls_log ("DTLS[%p]: End of flight transmission.\n", session);
   ret = 0;
 
 cleanup:
+  _gnutls_dtls_log ("DTLS[%p]: End of flight transmission.\n", session);
+
+  session->internals.dtls.flight_init = 0;
   drop_usage_count(session, send_buffer);
   _mbuffer_head_clear(send_buffer);
 
   /* SENDING -> WAITING state transition */
   return ret;
+
+nb_timeout:
+    return GNUTLS_E_AGAIN;
 }
 
 #define window_table session->internals.dtls.record_sw
@@ -352,7 +425,7 @@ int i, offset = 0;
 void gnutls_dtls_set_timeouts (gnutls_session_t session, unsigned int retrans_timeout,
   unsigned int total_timeout)
 {
-  session->internals.dtls.retrans_timeout  = retrans_timeout;
+  session->internals.dtls.retrans_timeout = retrans_timeout;
   session->internals.dtls.total_timeout  = total_timeout;
 }
 
index c44b9cab33bca32f49602824bac5f73a95a91879..b11df42fcfb607e3e50c229cccf7956428c06110 100644 (file)
@@ -581,17 +581,30 @@ typedef struct
 
   /* For DTLS handshake fragmentation and reassembly. */
   uint16_t hsk_write_seq;
-  int hsk_read_seq;
+  unsigned int hsk_read_seq;
   uint16_t mtu;
 
+  /* a flight transmission is in process */
+  unsigned int flight_init:1;
+  /* whether this is the last flight in the protocol  */
+  unsigned int last_flight:1;
   unsigned int retrans_timeout;
   unsigned int total_timeout;
 
   unsigned int hsk_hello_verify_requests;
   
   uint64_t record_sw[DTLS_RECORD_WINDOW_SIZE];
-  int record_sw_size;
-  
+  unsigned int record_sw_size;
+
+  /* non blocking stuff variables */
+  unsigned int blocking:1;
+  /* starting time of current handshake */
+  time_t handshake_start_time;
+  /* time in seconds of the last handshake call */
+  time_t handshake_last_call;
+  /* The actual retrans_timeout for the next message (e.g. doubled or so) */
+  unsigned int actual_retrans_timeout;
+
   /* timers to handle async handshake after gnutls_handshake()
    * has terminated. Required to handle retransmissions.
    */
@@ -850,7 +863,6 @@ struct gnutls_session_int
 };
 
 
-
 /* functions 
  */
 void _gnutls_set_current_version (gnutls_session_t session,
index b268971cb0515131a006c170f2a8488a4a66b714..f14927db181929316c5008c2e88603929a43e686 100644 (file)
@@ -258,6 +258,7 @@ _gnutls_handshake_internal_state_init (gnutls_session_t session)
   
   session->internals.dtls.hsk_read_seq = 0;
   session->internals.dtls.hsk_write_seq = 0;
+  session->internals.dtls.handshake_start_time = time(0);
 }
 
 void
@@ -380,6 +381,13 @@ gnutls_init (gnutls_session_t * session, unsigned int flags)
 
       (*session)->internals.dtls.record_sw_size = 0;
     }
+  else
+    (*session)->internals.transport = GNUTLS_STREAM;
+  
+  if (flags & GNUTLS_NONBLOCK)
+    (*session)->internals.dtls.blocking = 0;
+  else
+    (*session)->internals.dtls.blocking = 1;
 
   return 0;
 }
index 674ca6053220eccfd2b45f5e2ad8c2a8d51a8eea..821002135bbbba4edc05516221295435689ee35b 100644 (file)
@@ -284,12 +284,14 @@ extern "C"
    * @GNUTLS_SERVER: Connection end is a server.
    * @GNUTLS_CLIENT: Connection end is a client.
    * @GNUTLS_DATAGRAM: Connection is datagram oriented (DTLS).
+   * @GNUTLS_NONBLOCK: Connection should not block (DTLS).
    *
    * Enumeration of different TLS connection end types.
    */
 #define GNUTLS_SERVER 1
 #define GNUTLS_CLIENT (1<<1)
 #define GNUTLS_DATAGRAM (1<<2)
+#define GNUTLS_NONBLOCK (1<<3)
 
   /**
    * gnutls_alert_level_t:
index b1f556557a25ad945ea5d01e420dc790dfe70eba..849090ee3ac01659b54ebb27ecd3b09459615ecd 100644 (file)
@@ -4,7 +4,9 @@
 #include <gnutls_int.h>
 
 #ifndef _WIN32
-#include <sys/uio.h>            /* for writev */
+# include <sys/uio.h>            /* for writev */
+#else
+# include <windows.h>            /* for Sleep */
 #endif
 
 int system_errno (gnutls_transport_ptr_t);
@@ -35,4 +37,17 @@ ssize_t system_read_peek (gnutls_transport_ptr_t ptr, void *data,
 int _gnutls_atfork (void (*prepare) (void), void (*parent) (void),
                     void (*child) (void));
 
+static inline void millisleep(unsigned int ms)
+{
+#ifdef _WIN32
+  Sleep(ms);
+#else
+struct timespec ts;
+  ts.tv_sec = 0;
+  ts.tv_nsec = ms*1000*1000;
+  
+  nanosleep(&ts, NULL);
+#endif
+}
+
 #endif /* SYSTEM_H */
index 2d5e2d4d23eec3d93d829f825d1347cbb5072005..cef6bd66ec3582107982360612e8966774ee4b84 100644 (file)
@@ -27,7 +27,7 @@ if ENABLE_OPENPGP
 SUBDIRS += openpgp-certs
 endif
 
-EXTRA_DIST = libgcrypt.supp
+EXTRA_DIST = libgcrypt.supp eagain-common.h
 
 AM_CFLAGS = $(WARN_CFLAGS) $(WERROR_CFLAGS)
 AM_CPPFLAGS = \
@@ -60,7 +60,7 @@ ctests = simple gc set_pkcs12_cred certder certuniqueid mpi                   \
        crq_key_id x509sign-verify cve-2009-1415 cve-2009-1416          \
        crq_apis init_roundtrip pkcs12_s2k_pem dn2 mini-eagain          \
        nul-in-x509-names x509_altname pkcs12_encode mini-x509          \
-       mini-x509-rehandshake rng-fork #gendh
+       mini-x509-rehandshake rng-fork mini-eagain-dtls #gendh
 
 if ENABLE_OPENSSL
 ctests +=  openssl
diff --git a/tests/eagain-common.h b/tests/eagain-common.h
new file mode 100644 (file)
index 0000000..5fb436e
--- /dev/null
@@ -0,0 +1,144 @@
+#define min(x,y) ((x)<(y)?(x):(y))
+//#define EAGAIN_DEBUG
+#define RANDOMIZE
+
+static char to_server[64*1024];
+static size_t to_server_len = 0;
+
+static char to_client[64*1024];
+static size_t to_client_len = 0;
+
+#ifdef RANDOMIZE
+#define RETURN_RND_EAGAIN() \
+  static unsigned char rnd = 0; \
+  if (rnd++ % 2 == 0) \
+    { \
+      gnutls_transport_set_global_errno (EAGAIN); \
+      return -1; \
+    }
+#else
+#define RETURN_RND_EAGAIN()
+#endif
+
+static ssize_t
+client_push (gnutls_transport_ptr_t tr, const void *data, size_t len)
+{
+  size_t newlen;
+  RETURN_RND_EAGAIN();
+
+  len = min(len, sizeof(to_server)-to_server_len);
+
+  newlen = to_server_len + len;
+  memcpy (to_server + to_server_len, data, len);
+  to_server_len = newlen;
+#ifdef EAGAIN_DEBUG
+  fprintf(stderr, "eagain: pushed %d bytes to server (avail: %d)\n", (int)len, (int)to_server_len);
+#endif
+  return len;
+}
+
+static ssize_t
+client_pull (gnutls_transport_ptr_t tr, void *data, size_t len)
+{
+  RETURN_RND_EAGAIN();
+
+  if (to_client_len == 0)
+    {
+#ifdef EAGAIN_DEBUG
+      fprintf(stderr, "eagain: Not enough data by server (asked for: %d, have: %d)\n", (int)len, (int)to_client_len);
+#endif
+      gnutls_transport_set_global_errno (EAGAIN);
+      return -1;
+    }
+
+  len = min(len, to_client_len);
+
+  memcpy (data, to_client, len);
+
+  memmove (to_client, to_client + len, to_client_len - len);
+  to_client_len -= len;
+#ifdef EAGAIN_DEBUG
+  fprintf(stderr, "eagain: pulled %d bytes by client (avail: %d)\n", (int)len, (int)to_client_len);
+#endif
+  return len;
+}
+
+static ssize_t
+server_pull (gnutls_transport_ptr_t tr, void *data, size_t len)
+{
+  //success ("server_pull len %d has %d\n", len, to_server_len);
+  RETURN_RND_EAGAIN();
+
+  if (to_server_len == 0)
+    {
+#ifdef EAGAIN_DEBUG
+      fprintf(stderr, "eagain: Not enough data by client (asked for: %d, have: %d)\n", (int)len, (int)to_server_len);
+#endif
+      gnutls_transport_set_global_errno (EAGAIN);
+      return -1;
+    }
+
+  len = min(len, to_server_len);
+#ifdef EAGAIN_DEBUG
+  fprintf(stderr, "eagain: pulled %d bytes by server (avail: %d)\n", (int)len, (int)to_server_len);
+#endif
+  memcpy (data, to_server, len);
+
+  memmove (to_server, to_server + len, to_server_len - len);
+  to_server_len -= len;
+
+  return len;
+}
+
+static ssize_t
+server_push (gnutls_transport_ptr_t tr, const void *data, size_t len)
+{
+  size_t newlen;
+  RETURN_RND_EAGAIN();
+
+//  hexprint (data, len);
+
+  len = min(len, sizeof(to_client)-to_client_len);
+
+  newlen = to_client_len + len;
+  memcpy (to_client + to_client_len, data, len);
+  to_client_len = newlen;
+#ifdef EAGAIN_DEBUG
+  fprintf(stderr, "eagain: pushed %d bytes to client (avail: %d)\n", (int)len, (int)to_client_len);
+#endif
+
+  return len;
+}
+
+/* inline is used to avoid a gcc warning if used in mini-eagain */
+inline static int server_pull_timeout_func(gnutls_transport_ptr_t ptr, unsigned int ms)
+{
+int ret;
+
+  if (to_server_len > 0)
+    ret = 1; /* available data */
+  else
+    ret = 0; /* timeout */
+
+#ifdef EAGAIN_DEBUG
+  fprintf(stderr, "eagain: server_pull_timeout: %d\n", ret);
+#endif
+
+  return ret;
+}
+
+inline static int client_pull_timeout_func(gnutls_transport_ptr_t ptr, unsigned int ms)
+{
+int ret;
+
+  if (to_client_len > 0)
+    ret = 1;
+  else
+    ret = 0;
+
+#ifdef EAGAIN_DEBUG
+  fprintf(stderr, "eagain: client_pull_timeout: %d\n", ret);
+#endif
+
+  return ret;
+}
diff --git a/tests/mini-eagain-dtls.c b/tests/mini-eagain-dtls.c
new file mode 100644 (file)
index 0000000..0b3ed47
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2008, 2010 Free Software Foundation, Inc.
+ *
+ * Author: Simon Josefsson, Nikos Mavrogiannopoulos
+ *
+ * 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 "utils.h"
+#include "eagain-common.h"
+
+static void
+tls_log_func (int level, const char *str)
+{
+  fprintf (stderr, "|<%d>| %s", level, str);
+}
+
+static int handshake = 0;
+
+#define MAX_BUF 1024
+#define MSG "Hello TLS, and hi and how are you and more data here... and more... and even more and even more more data..."
+
+void
+doit (void)
+{
+  /* Server stuff. */
+  gnutls_anon_server_credentials_t s_anoncred;
+  const gnutls_datum_t p3 = { (char *) pkcs3, strlen (pkcs3) };
+  static gnutls_dh_params_t dh_params;
+  gnutls_session_t server;
+  int sret, cret;
+  /* Client stuff. */
+  gnutls_anon_client_credentials_t c_anoncred;
+  gnutls_session_t client;
+  /* Need to enable anonymous KX specifically. */
+  char buffer[MAX_BUF + 1];
+  ssize_t ns;
+  int ret, transferred = 0, msglen;
+
+  /* General init. */
+  gnutls_global_init ();
+  gnutls_global_set_log_function (tls_log_func);
+  if (debug)
+    gnutls_global_set_log_level (99);
+
+  /* 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_DATAGRAM|GNUTLS_NONBLOCK);
+  ret = gnutls_priority_set_direct (server, "NONE:+VERS-DTLS1.0:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+ANON-DH", NULL);
+  if (ret < 0)
+    exit(1);
+  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_pull_timeout_function (server, server_pull_timeout_func);
+
+  /* Init client */
+  gnutls_anon_allocate_client_credentials (&c_anoncred);
+  gnutls_init (&client, GNUTLS_CLIENT|GNUTLS_DATAGRAM|GNUTLS_NONBLOCK);
+  cret = gnutls_priority_set_direct (client, "NONE:+VERS-DTLS1.0:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+ANON-DH", NULL);
+  if (cret < 0)
+    exit(1);
+  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_pull_timeout_function (client, client_pull_timeout_func);
+
+  handshake = 1;
+  sret = cret = GNUTLS_E_AGAIN;
+  do
+    {
+      if (cret == GNUTLS_E_AGAIN)
+        {
+          //success ("loop invoking client:\n");
+          cret = gnutls_handshake (client);
+          //success ("client %d: %s\n", cret, gnutls_strerror (cret));
+        }
+
+      if (sret == GNUTLS_E_AGAIN)
+        {
+          //success ("loop invoking server:\n");
+          sret = gnutls_handshake (server);
+          //success ("server %d: %s\n", sret, gnutls_strerror (sret));
+        }
+    }
+  while ((cret == GNUTLS_E_AGAIN && gnutls_error_is_fatal(sret)==0) || (sret == GNUTLS_E_AGAIN && gnutls_error_is_fatal(cret)==0));
+
+  if (cret < 0 || sret < 0)
+    {
+      fprintf(stderr, "client: %s\n", gnutls_strerror(cret));
+      fprintf(stderr, "server: %s\n", gnutls_strerror(sret));
+      fail("Handshake failed\n");
+      exit(1);
+    }
+
+  handshake = 0;
+  if (debug)
+    success ("Handshake established\n");
+
+  do
+    {
+      ret = gnutls_record_send (client, MSG, strlen (MSG));
+    }
+  while(ret == GNUTLS_E_AGAIN);
+  //success ("client: sent %d\n", ns);
+
+  do
+    {
+      //success("transferred: %d\n", transferred);
+
+      do
+        {
+          ret = gnutls_record_recv (server, buffer, MAX_BUF);
+        }
+      while(ret == GNUTLS_E_AGAIN);
+
+      if (ret == 0)
+        fail ("server: didn't receive any data\n");
+      else if (ret < 0)
+        {
+          //      if (debug)
+          //          fputs ("#", stdout);
+          fail ("server: error: %s\n", gnutls_strerror (ret));
+        }
+      else
+        {
+          transferred += ret;
+            //        if (debug)
+              //          fputs ("*", stdout);
+        }
+
+      msglen = strlen (MSG);
+      do
+        {
+          ns = gnutls_record_send (server, MSG, msglen);
+        }
+      while (ns == GNUTLS_E_AGAIN);
+
+      do
+        {
+          ret = gnutls_record_recv (client, buffer, MAX_BUF);
+        }
+      while(ret == GNUTLS_E_AGAIN);
+
+
+      if (ret == 0)
+        {
+          fail ("client: Peer has closed the TLS connection\n");
+        }
+      else if (ret < 0)
+        {
+          if (debug)
+            fputs ("!", stdout);
+          fail ("client: Error: %s\n", gnutls_strerror (ret));
+        }
+      else
+        {
+          if (msglen != ret || memcmp (buffer, MSG, msglen) != 0)
+            {
+              fail ("client: Transmitted data do not match\n");
+            }
+
+          /* echo back */
+          do
+            {
+              ns = gnutls_record_send (client, buffer, msglen);
+            }
+          while (ns == GNUTLS_E_AGAIN);
+
+          transferred += ret;
+          if (debug)
+            fputs (".", stdout);
+        }
+    }
+  while (transferred < 70000);
+  if (debug)
+    fputs ("\n", stdout);
+
+  gnutls_bye (client, GNUTLS_SHUT_WR);
+  gnutls_bye (server, GNUTLS_SHUT_WR);
+
+  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 ();
+}
index 9bb7e2c321385054cd47e9faee7649ea772ac80b..3b2ea2951ca285b97ceff21feba74652641d9595 100644 (file)
@@ -32,6 +32,7 @@
 #include <gnutls/crypto.h>
 
 #include "utils.h"
+#include "eagain-common.h"
 
 static void
 tls_log_func (int level, const char *str)
@@ -41,109 +42,6 @@ tls_log_func (int level, const char *str)
 
 static int handshake = 0;
 
-char *to_server;
-size_t to_server_len;
-
-char *to_client;
-size_t to_client_len;
-
-
-static ssize_t
-client_pull (gnutls_transport_ptr_t tr, void *data, size_t len)
-{
-//  success ("client_pull len %d has %d\n", len, to_client_len);
-  static unsigned char rnd = 0;
-
-  if (rnd++ % 2 == 0 || to_client_len < len)
-    {
-      gnutls_transport_set_global_errno (EAGAIN);
-      return -1;
-    }
-
-  memcpy (data, to_client, len);
-
-  memmove (to_client, to_client + len, to_client_len - len);
-  to_client_len -= len;
-
-  return len;
-}
-
-static ssize_t
-client_push (gnutls_transport_ptr_t tr, const void *data, size_t len)
-{
-  char *tmp;
-  size_t newlen = to_server_len + len;
-  static unsigned char rnd = 0;
-
-  if (rnd++ % 2 == 0)
-    {
-      gnutls_transport_set_global_errno (EAGAIN);
-      return -1;
-    }
-
-  tmp = realloc (to_server, newlen);
-  if (!tmp)
-    {
-      fail ("Memory allocation failure...\n");
-      exit (1);
-    }
-  to_server = tmp;
-
-  memcpy (to_server + to_server_len, data, len);
-  to_server_len = newlen;
-
-  return len;
-}
-
-static ssize_t
-server_pull (gnutls_transport_ptr_t tr, void *data, size_t len)
-{
-  //success ("server_pull len %d has %d\n", len, to_server_len);
-  static unsigned char rnd = 0;
-
-  if (rnd++ % 2 == 0 || to_server_len < len)
-    {
-      gnutls_transport_set_global_errno (EAGAIN);
-      return -1;
-    }
-
-  memcpy (data, to_server, len);
-
-  memmove (to_server, to_server + len, to_server_len - len);
-  to_server_len -= len;
-
-  return len;
-}
-
-static ssize_t
-server_push (gnutls_transport_ptr_t tr, const void *data, size_t len)
-{
-  char *tmp;
-  size_t newlen = to_client_len + len;
-  static unsigned char rnd = 0;
-
-  if (rnd++ % 2 == 0)
-    {
-      gnutls_transport_set_global_errno (EAGAIN);
-      return -1;
-    }
-
-//  hexprint (data, len);
-
-  tmp = realloc (to_client, newlen);
-  if (!tmp)
-    {
-      fail ("Memory allocation failure...\n");
-      exit (1);
-    }
-  to_client = tmp;
-
-  memcpy (to_client + to_client_len, data, len);
-  to_client_len = newlen;
-
-  return len;
-}
-
 #define MAX_BUF 1024
 #define MSG "Hello TLS, and hi and how are you and more data here... and more... and even more and even more more data..."
 
@@ -155,11 +53,10 @@ doit (void)
   const gnutls_datum_t p3 = { (char *) pkcs3, strlen (pkcs3) };
   static gnutls_dh_params_t dh_params;
   gnutls_session_t server;
-  int sret = GNUTLS_E_AGAIN;
+  int sret, cret;
   /* Client stuff. */
   gnutls_anon_client_credentials_t c_anoncred;
   gnutls_session_t client;
-  int cret = GNUTLS_E_AGAIN;
   /* Need to enable anonymous KX specifically. */
   char buffer[MAX_BUF + 1];
   ssize_t ns;
@@ -169,7 +66,7 @@ doit (void)
   gnutls_global_init ();
   gnutls_global_set_log_function (tls_log_func);
   if (debug)
-    gnutls_global_set_log_level (99);
+    gnutls_global_set_log_level (2);
 
   /* Init server */
   gnutls_anon_allocate_server_credentials (&s_anoncred);
@@ -177,7 +74,9 @@ doit (void)
   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);
+  ret = gnutls_priority_set_direct (server, "NONE:+VERS-TLS-ALL:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+ANON-DH", NULL);
+  if (ret < 0)
+    exit(1);
   gnutls_credentials_set (server, GNUTLS_CRD_ANON, s_anoncred);
   gnutls_dh_set_prime_bits (server, 1024);
   gnutls_transport_set_push_function (server, server_push);
@@ -186,12 +85,15 @@ doit (void)
   /* 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);
+  ret = gnutls_priority_set_direct (client, "NONE:+VERS-TLS-ALL:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+ANON-DH", NULL);
+  if (ret < 0)
+    exit(1);
   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);
 
   handshake = 1;
+  sret = cret = GNUTLS_E_AGAIN;
   do
     {
       if (cret == GNUTLS_E_AGAIN)
@@ -210,35 +112,48 @@ doit (void)
     }
   while (cret == GNUTLS_E_AGAIN || sret == GNUTLS_E_AGAIN);
 
+  if (cret < 0 || sret < 0)
+    {
+      fprintf(stderr, "client: %s\n", gnutls_strerror(cret));
+      fprintf(stderr, "server: %s\n", gnutls_strerror(sret));
+      fail("Handshake failed\n");
+      exit(1);
+    }
+
   handshake = 0;
   if (debug)
     success ("Handshake established\n");
 
-  ns = gnutls_record_send (client, MSG, strlen (MSG));
+  do
+    {
+      ret = gnutls_record_send (client, MSG, strlen (MSG));
+    }
+  while(ret == GNUTLS_E_AGAIN);
   //success ("client: sent %d\n", ns);
 
   do
     {
       //success("transferred: %d\n", transferred);
 
-      ret = gnutls_record_recv (server, buffer, MAX_BUF);
+      do
+        {
+          ret = gnutls_record_recv (server, buffer, MAX_BUF);
+        }
+      while(ret == GNUTLS_E_AGAIN);
+
       if (ret == 0)
         fail ("server: didn't receive any data\n");
       else if (ret < 0)
         {
-//      if (debug)
-//          fputs ("#", stdout);
-          if (ret != GNUTLS_E_AGAIN)
-            {
-              fail ("server: error: %s\n", gnutls_strerror (ret));
-              break;
-            }
+          //      if (debug)
+          //          fputs ("#", stdout);
+          fail ("server: error: %s\n", gnutls_strerror (ret));
         }
       else
         {
           transferred += ret;
-//        if (debug)
-//          fputs ("*", stdout);
+            //        if (debug)
+              //          fputs ("*", stdout);
         }
 
       msglen = strlen (MSG);
@@ -248,7 +163,12 @@ doit (void)
         }
       while (ns == GNUTLS_E_AGAIN);
 
-      ret = gnutls_record_recv (client, buffer, MAX_BUF);
+      do
+        {
+          ret = gnutls_record_recv (client, buffer, MAX_BUF);
+        }
+      while(ret == GNUTLS_E_AGAIN);
+
       if (ret == 0)
         {
           fail ("client: Peer has closed the TLS connection\n");
@@ -257,11 +177,7 @@ doit (void)
         {
           if (debug)
             fputs ("!", stdout);
-          if (ret != GNUTLS_E_AGAIN)
-            {
-              fail ("client: Error: %s\n", gnutls_strerror (ret));
-              break;
-            }
+          fail ("client: Error: %s\n", gnutls_strerror (ret));
         }
       else
         {
@@ -292,9 +208,6 @@ doit (void)
   gnutls_deinit (client);
   gnutls_deinit (server);
 
-  free (to_server);
-  free (to_client);
-
   gnutls_anon_free_client_credentials (c_anoncred);
   gnutls_anon_free_server_credentials (s_anoncred);
 
index 105869c9030fc4024c702696458d7f7c218fccc0..c8ed85357f460cffe3866910f2c31ea35b073606 100644 (file)
@@ -60,7 +60,7 @@ success (const char *format, ...)
   va_list arg_ptr;
 
   va_start (arg_ptr, format);
-  vfprintf (stdout, format, arg_ptr);
+  vfprintf (stderr, format, arg_ptr);
   va_end (arg_ptr);
 }