[gnutls-dev] Re: Gnutls4win: Problem with custom push/pull functions, errno and Visual Studio (Workaround included)

Simon Josefsson jas at extundo.com
Tue Nov 7 11:14:45 CET 2006


I've installed this patch.

Btw, it seems others have run into other errno problems in push/pull
functions, see:
http://darcs.0x539.de/trac/obby/cgi-bin/trac.cgi/browser/net6/src/encrypt.cpp
I think we should use WSAGetLastError to set errno ourselves, but I'll
post the patch for this separately.

/Simon

Index: NEWS
===================================================================
RCS file: /cvs/gnutls/gnutls/NEWS,v
retrieving revision 2.732
diff -u -p -r2.732 NEWS
--- NEWS	6 Nov 2006 11:53:13 -0000	2.732
+++ NEWS	7 Nov 2006 10:10:38 -0000
@@ -5,6 +5,22 @@ See the end for copying conditions.
 
 * Version 1.5.4 (unreleased)
 
+** New API functions to set errno in push/pull functions.
+Under Windows, setting the errno variable in a push/pull replacement
+may end up setting the wrong errno variable, and GnuTLS send/recv
+functions become confused about the real errno returned from a failed
+push/pull function.  Therefor, we have added two APIs to set the errno
+variable used by GnuTLS.  The APIs can also help to keep things
+thread-safe, by avoiding potentially global variables.  Typically,
+instead of setting errno in your push/pull function, you will call one
+of these functions.  It is recommended to use
+gnutls_transport_set_errno, but if you don't have the session variable
+easily accessible in the push/pull replacement function, you can use
+gnutls_transport_set_global_errno.
+
+void gnutls_transport_set_errno (gnutls_session_t session, int err);
+void gnutls_transport_set_global_errno (int err);
+
 ** Several OpenPGP API fixes.
 All suggested by ludovic.courtes at laas.fr (Ludovic Courtès).  The most
 important fix is to change the return value of
@@ -28,7 +44,8 @@ This fixes build errors similar to "No r
 `libgnutls-`expr', needed by `all-am'." when building for Windows.
 
 ** API and ABI modifications:
-No changes since last version.
+gnutls_transport_set_errno: ADD
+gnutls_transport_set_global_errno: ADD
 
 * Version 1.5.3 (released 2006-10-26)
 
Index: includes/gnutls/gnutls.h.in
===================================================================
RCS file: /cvs/gnutls/gnutls/includes/gnutls/gnutls.h.in,v
retrieving revision 1.23
diff -u -p -r1.23 gnutls.h.in
--- includes/gnutls/gnutls.h.in	5 May 2006 09:07:17 -0000	1.23
+++ includes/gnutls/gnutls.h.in	7 Nov 2006 10:10:38 -0000
@@ -736,6 +736,9 @@ extern "C"
   void gnutls_transport_set_pull_function (gnutls_session_t session,
 					   gnutls_pull_func pull_func);
 
+  void gnutls_transport_set_errno (gnutls_session_t session, int err);
+  void gnutls_transport_set_global_errno (int err);
+
 /* session specific 
  */
   void gnutls_session_set_ptr (gnutls_session_t session, void *ptr);
Index: lib/gnutls_buffers.c
===================================================================
RCS file: /cvs/gnutls/gnutls/lib/gnutls_buffers.c,v
retrieving revision 2.128
diff -u -p -r2.128 gnutls_buffers.c
--- lib/gnutls_buffers.c	13 Aug 2006 15:08:02 -0000	2.128
+++ lib/gnutls_buffers.c	7 Nov 2006 10:10:38 -0000
@@ -77,6 +77,58 @@ RET (int err)
 # include <io_debug.h>
 #endif
 
+/**
+ * gnutls_transport_set_errno:
+ * @session: is a #gnutls_session_t structure.
+ * @err: error value to store in session-specific errno variable.
+ *
+ * Store @err in the session-specific errno variable.  Useful values
+ * for @err is EAGAIN and EINTR, other values are treated will be
+ * treated as real errors in the push/pull function.
+ *
+ * This function is useful in replacement push/pull functions set by
+ * gnutls_transport_set_push_function and
+ * gnutls_transport_set_pullpush_function under Windows, where the
+ * replacement push/pull may not have access to the same @errno
+ * variable that is used by GnuTLS (e.g., the application is linked to
+ * msvcr71.dll and gnutls is linked to msvcrt.dll).
+ *
+ * If you don't have the @session variable easily accessible from the
+ * push/pull function, and don't worry about thread conflicts, you can
+ * also use gnutls_transport_set_global_errno().
+ **/
+void
+gnutls_transport_set_errno (gnutls_session_t session, int err)
+{
+  session->internals.errnum = err;
+}
+
+/**
+ * gnutls_transport_set_global_errno:
+ * @err: error value to store in global errno variable.
+ *
+ * Store @err in the global errno variable.  Useful values for @err is
+ * EAGAIN and EINTR, other values are treated will be treated as real
+ * errors in the push/pull function.
+ *
+ * This function is useful in replacement push/pull functions set by
+ * gnutls_transport_set_push_function and
+ * gnutls_transport_set_pullpush_function under Windows, where the
+ * replacement push/pull may not have access to the same @errno
+ * variable that is used by GnuTLS (e.g., the application is linked to
+ * msvcr71.dll and gnutls is linked to msvcrt.dll).
+ *
+ * Whether this function is thread safe or not depends on whether the
+ * global variable errno is thread safe, some system libraries make it
+ * a thread-local variable.  When feasible, using the guaranteed
+ * thread-safe gnutls_transport_set_errno() may be better.
+ **/
+void
+gnutls_transport_set_global_errno (int err)
+{
+  errno = err;
+}
+
 /* Buffers received packets of type APPLICATION DATA and
  * HANDSHAKE DATA.
  */
@@ -264,6 +316,8 @@ _gnutls_read (gnutls_session_t session, 
   while (left > 0)
     {
 
+      session->internals.errnum = 0;
+
       if (session->internals._gnutls_pull_func == NULL)
 	i = recv (GNUTLS_POINTER_TO_INT(fd), &ptr[sizeOfPtr - left],
 		  left, flags);
@@ -274,10 +328,13 @@ _gnutls_read (gnutls_session_t session, 
 
       if (i < 0)
 	{
-	  _gnutls_read_log ("READ: %d returned from %d, errno=%d\n", i,
-			    fd, errno);
+	  int err = session->internals.errnum ? session->internals.errnum
+	    : errno;
+
+	  _gnutls_read_log ("READ: %d returned from %d, errno=%d gerrno=%d\n",
+			    i, fd, errno, session->internals.errnum);
 
-	  if (errno == EAGAIN || errno == EINTR)
+	  if (err == EAGAIN || err == EINTR)
 	    {
 	      if (sizeOfPtr - left > 0)
 		{
@@ -289,7 +346,7 @@ _gnutls_read (gnutls_session_t session, 
 		}
 	      gnutls_assert ();
 
-	      return RET (errno);
+	      return RET (err);
 	    }
 	  else
 	    {
@@ -701,6 +758,8 @@ _gnutls_io_write_buffered (gnutls_sessio
   while (left > 0)
     {
 
+      session->internals.errnum = 0;
+
       if (session->internals._gnutls_push_func == NULL)
 	i = send (GNUTLS_POINTER_TO_INT(fd), &ptr[n - left], left, 0);
       else
@@ -708,7 +767,10 @@ _gnutls_io_write_buffered (gnutls_sessio
 
       if (i == -1)
 	{
-	  if (errno == EAGAIN || errno == EINTR)
+	  int err = session->internals.errnum ? session->internals.errnum
+	    : errno;
+
+	  if (err == EAGAIN || err == EINTR)
 	    {
 	      session->internals.record_send_buffer_prev_size += n - left;
 
@@ -726,7 +788,7 @@ _gnutls_io_write_buffered (gnutls_sessio
 		("WRITE: Interrupted. Stored %d bytes to buffer. Already sent %d bytes.\n",
 		 left, n - left);
 
-	      retval = RET (errno);
+	      retval = RET (err);
 
 	      return retval;
 	    }
Index: lib/gnutls_int.h
===================================================================
RCS file: /cvs/gnutls/gnutls/lib/gnutls_int.h,v
retrieving revision 2.369
diff -u -p -r2.369 gnutls_int.h
--- lib/gnutls_int.h	13 Aug 2006 15:07:45 -0000	2.369
+++ lib/gnutls_int.h	7 Nov 2006 10:10:38 -0000
@@ -598,6 +598,16 @@ typedef struct
    */
   gnutls_datum_t recv_buffer;
 
+  /* To avoid using global variables, and especially on Windows where
+   * the application may use a different errno variable than GnuTLS,
+   * it is possible to use gnutls_transport_set_errno to set a
+   * session-specific errno variable in the user-replaceable push/pull
+   * functions.  This value is used by the send/recv functions.  (The
+   * strange name of this variable is because 'errno' is typically
+   * #define'd.)
+   */
+  int errnum;
+
   /* If you add anything here, check _gnutls_handshake_internal_state_clear().
    */
 } internals_st;




More information about the Gnutls-devel mailing list