summaryrefslogtreecommitdiff
path: root/lib/gnutls_buffers.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/gnutls_buffers.c')
-rw-r--r--lib/gnutls_buffers.c256
1 files changed, 212 insertions, 44 deletions
diff --git a/lib/gnutls_buffers.c b/lib/gnutls_buffers.c
index 364cbfb832..e1ae16bce2 100644
--- a/lib/gnutls_buffers.c
+++ b/lib/gnutls_buffers.c
@@ -1,3 +1,4 @@
+#define READ_DEBUG
/*
* Copyright (C) 2000,2001 Nikos Mavroyanopoulos
*
@@ -17,13 +18,16 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
-
+#define IO_DEBUG 5
#include <gnutls_int.h>
#include <gnutls_errors.h>
#include <gnutls_num.h>
#include <gnutls_record.h>
+#include <gnutls_buffers.h>
/* This is the only file that uses the berkeley sockets API.
+ *
+ * Also holds all the buffering code used in gnutls.
*/
#ifdef HAVE_ERRNO_H
@@ -113,6 +117,11 @@ int gnutls_check_pending(GNUTLS_STATE state) {
int gnutls_get_data_buffer(ContentType type, GNUTLS_STATE state, char *data, int length)
{
+ if (length < 0 || data==NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_PARAMETERS;
+ }
+
switch(type) {
case GNUTLS_APPLICATION_DATA:
@@ -304,7 +313,7 @@ ssize_t _gnutls_read_buffered( int fd, GNUTLS_STATE state, opaque **iptr, size_t
int recvlowat = RCVLOWAT;
int recvdata;
- *iptr = NULL;
+ *iptr = state->gnutls_internals.recv_buffer.data;
if ( sizeOfPtr > MAX_RECV_SIZE || sizeOfPtr == 0
|| (state->gnutls_internals.recv_buffer.size+sizeOfPtr) > MAX_RECV_SIZE) {
@@ -431,13 +440,11 @@ ssize_t _gnutls_read_buffered( int fd, GNUTLS_STATE state, opaque **iptr, size_t
}
}
-
-
/* This function is like write. But it does not return -1 on error.
* It does return gnutls_errno instead.
*
- * This function may not cope right with interrupted system calls
- * and EAGAIN error. Ideas?
+ * In case of E_AGAIN and E_INTERRUPTED errors, you must call gnutls_write_flush(),
+ * until it returns ok (0).
*
* We need to push exactly the data in n, since we cannot send less
* data. In TLS the peer must receive the whole packet in order
@@ -452,6 +459,7 @@ ssize_t _gnutls_write_buffered(SOCKET fd, GNUTLS_STATE state, const void *iptr,
#endif
ssize_t retval, i;
const opaque * ptr;
+ int ptrcopy; /* indicates whether to copy from the ptr */
ptr = iptr;
@@ -466,12 +474,14 @@ ssize_t _gnutls_write_buffered(SOCKET fd, GNUTLS_STATE state, const void *iptr,
/* If data in the buffer exist
*/
+ ptrcopy = 1;
if (iptr == NULL) {
/* checking is handled above */
ptr = state->gnutls_internals.send_buffer.data;
n = state->gnutls_internals.send_buffer.size;
+ ptrcopy = 0;
#ifdef WRITE_DEBUG
- _gnutls_log( "WRITE: Restoring old write. (%d data to send)\n", n);
+ _gnutls_log( "WRITE: Restoring old write. (%d bytes to send)\n", n);
#endif
}
@@ -492,19 +502,23 @@ ssize_t _gnutls_write_buffered(SOCKET fd, GNUTLS_STATE state, const void *iptr,
if (errno == EAGAIN || errno == EINTR) {
state->gnutls_internals.send_buffer_prev_size += n - left;
- state->gnutls_internals.send_buffer.data = gnutls_realloc_fast( state->gnutls_internals.send_buffer.data, left);
+ state->gnutls_internals.send_buffer.data = gnutls_realloc_fast(
+ state->gnutls_internals.send_buffer.data, left);
+
if (state->gnutls_internals.send_buffer.data == NULL) {
gnutls_assert();
return GNUTLS_E_MEMORY_ERROR;
}
state->gnutls_internals.send_buffer.size = left;
- /* use memmove since they may overlap
- */
- memmove( state->gnutls_internals.send_buffer.data, &ptr[n-left], left);
+
+ if (ptrcopy != 0)
+ memcpy( state->gnutls_internals.send_buffer.data, &ptr[n-left], left);
+ else
+ memmove( state->gnutls_internals.send_buffer.data, &state->gnutls_internals.send_buffer.data[n-left], left);
+
#ifdef WRITE_DEBUG
- _gnutls_log( "WRITE: Interrupted.\n");
+ _gnutls_log( "WRITE: Interrupted. Stored %d bytes to buffer. Already sent %d bytes.\n", left, n-left);
#endif
- gnutls_assert();
if (errno==EAGAIN) retval = GNUTLS_E_AGAIN;
else retval = GNUTLS_E_INTERRUPTED;
@@ -517,16 +531,20 @@ ssize_t _gnutls_write_buffered(SOCKET fd, GNUTLS_STATE state, const void *iptr,
left -= i;
#ifdef WRITE_DEBUG
- _gnutls_log( "WRITE: wrote %d bytes to %d. Left %d bytes\n", i, fd, left);
- for (x=0;x<((n-left)/16)+1;x++) {
+ _gnutls_log( "WRITE: wrote %d bytes to %d. Left %d bytes. Total %d bytes.\n", i, fd, left, n);
+ for (x=0;x<((i)/16)+1;x++) {
+ if (sum>n-left)
+ break;
+
_gnutls_log( "%.4x - ",x);
for (j=0;j<16;j++) {
if (sum<n-left) {
_gnutls_log( "%.2x ", ((unsigned char*)ptr)[sum++]);
- }
+ } else break;
}
_gnutls_log( "\n");
}
+ _gnutls_log( "\n");
#endif
}
@@ -553,8 +571,37 @@ ssize_t _gnutls_write_flush(SOCKET fd, GNUTLS_STATE state)
ret = _gnutls_write_buffered(fd, state, NULL, 0);
#ifdef WRITE_DEBUG
- _gnutls_log("WRITE FLUSH: %d\n", ret);
+ _gnutls_log("WRITE FLUSH: %d [buffer: %d]\n", ret, state->gnutls_internals.send_buffer.size);
+#endif
+
+ return ret;
+}
+
+/* This function writes the data that are left in the
+ * Handshake write buffer (ie. because the previous write was
+ * interrupted.
+ */
+ssize_t _gnutls_handshake_write_flush(SOCKET fd, GNUTLS_STATE state)
+{
+ ssize_t ret;
+
+ ret = _gnutls_handshake_send_int(fd, state, 0, 0, NULL, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+#ifdef WRITE_DEBUG
+ _gnutls_log("HANDSHAKE_FLUSH: written[1] %d bytes\n", ret);
#endif
+
+ if (state->gnutls_internals.handshake_send_buffer.size == 0) {
+ ret = state->gnutls_internals.handshake_send_buffer_prev_size; /* done */
+ state->gnutls_internals.handshake_send_buffer_prev_size = 0;
+ state->gnutls_internals.handshake_send_buffer.size = 0;
+
+ return ret;
+ }
+
return ret;
}
@@ -565,35 +612,90 @@ ssize_t _gnutls_write_flush(SOCKET fd, GNUTLS_STATE state)
ssize_t _gnutls_handshake_send_int( SOCKET fd, GNUTLS_STATE state, ContentType type, HandshakeType htype, void *iptr, size_t n)
{
size_t left;
- ssize_t i = 0;
- char *ptr = iptr;
-
- if (iptr==NULL && n == 0) {
- /* resuming interrupted write.
+ ssize_t i = 0, ret=0;
+ opaque *ptr;
+ int ptrcopy;
+ ssize_t retval = 0;
+
+ ptrcopy = 1;
+ if (state->gnutls_internals.handshake_send_buffer.size > 0 && iptr==NULL && n == 0) {
+ /* resuming previously interrupted write
*/
- return _gnutls_write_flush( fd, state);
+ gnutls_assert();
+ n = state->gnutls_internals.handshake_send_buffer.size;
+ iptr = state->gnutls_internals.handshake_send_buffer.data;
+
+ type = state->gnutls_internals.handshake_send_buffer_type;
+ htype = state->gnutls_internals.handshake_send_buffer_htype;
+ ptrcopy = 0;
+
+ } else if (state->gnutls_internals.handshake_send_buffer.size > 0) {
+ gnutls_assert();
+ return GNUTLS_E_UNKNOWN_ERROR;
}
- /* FIXME: Potential problem here. If ie one message has been
- * sent, and the next send_int() gets an interrupt (or e_again).
- * This might be more dangerous, since a non blocking server may
- * block here. Should a buffer be used here?
- */
+ if (n==0) { /* if we have no data to send */
+ gnutls_assert();
+ return 0;
+ } else if (iptr==NULL) {
+ gnutls_assert();
+ return GNUTLS_E_UNKNOWN_ERROR;
+ }
+
+
+ ptr = iptr;
left = n;
while (left > 0) {
- i = gnutls_send_int(fd, state, type, htype, &ptr[i], left);
- if (i <= 0) {
- gnutls_assert();
- if (n-left > 0) {
+ ret = gnutls_send_int(fd, state, type, htype, &ptr[n-left], left);
+
+ if (ret <= 0) {
+ if (ret==0) {
gnutls_assert();
- return n-left;
+ ret = GNUTLS_E_UNKNOWN_ERROR;
}
- return i;
+
+ if ( left > 0 && (ret==GNUTLS_E_INTERRUPTED || ret==GNUTLS_E_AGAIN)) {
+ gnutls_assert();
+
+ state->gnutls_internals.handshake_send_buffer.data = gnutls_realloc_fast(
+ state->gnutls_internals.handshake_send_buffer.data, left);
+
+ if (state->gnutls_internals.handshake_send_buffer.data==NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ if (ptrcopy!=0)
+ memcpy( state->gnutls_internals.handshake_send_buffer.data, &ptr[n-left], left);
+ else
+ if (n-left > 0)
+ memmove( state->gnutls_internals.handshake_send_buffer.data,
+ &state->gnutls_internals.handshake_send_buffer.data[n-left], left);
+
+ state->gnutls_internals.handshake_send_buffer.size = left;
+ state->gnutls_internals.handshake_send_buffer_prev_size += n-left;
+
+ state->gnutls_internals.handshake_send_buffer_type = type;
+ state->gnutls_internals.handshake_send_buffer_htype = htype;
+
+ } else {
+ state->gnutls_internals.handshake_send_buffer_prev_size = 0;
+ state->gnutls_internals.handshake_send_buffer.size = 0;
+ }
+
+ gnutls_assert();
+ return ret;
}
+ i = ret;
left -= i;
}
- return n;
+ retval = n + state->gnutls_internals.handshake_send_buffer_prev_size;
+
+ state->gnutls_internals.handshake_send_buffer.size = 0;
+ state->gnutls_internals.handshake_send_buffer_prev_size = 0;
+
+ return retval;
}
@@ -603,18 +705,69 @@ ssize_t _gnutls_handshake_send_int( SOCKET fd, GNUTLS_STATE state, ContentType t
ssize_t _gnutls_handshake_recv_int(int fd, GNUTLS_STATE state, ContentType type, HandshakeType htype, void *iptr, size_t sizeOfPtr)
{
size_t left;
- ssize_t i=0;
- char *ptr = iptr;
-
+ ssize_t i;
+ char *ptr;
+ size_t dsize;
+
+ ptr = iptr;
left = sizeOfPtr;
+
+ if (state->gnutls_internals.handshake_recv_buffer.size > 0) {
+ /* if we have already received some data */
+ fprintf(stderr, "C1: BUFFER_SIZE: %d\n", state->gnutls_internals.handshake_recv_buffer.size);
+ if (sizeOfPtr <= state->gnutls_internals.handshake_recv_buffer.size) {
+ /* if requested less data then return it.
+ */
+ gnutls_assert();
+ memcpy( iptr, state->gnutls_internals.handshake_recv_buffer.data, sizeOfPtr);
+
+ state->gnutls_internals.handshake_recv_buffer.size -= sizeOfPtr;
+
+ memmove( state->gnutls_internals.handshake_recv_buffer.data,
+ &state->gnutls_internals.handshake_recv_buffer.data[sizeOfPtr],
+ state->gnutls_internals.handshake_recv_buffer.size);
+ fprintf(stderr, "C2: BUFFER_SIZE: %d\n", state->gnutls_internals.handshake_recv_buffer.size);
+
+ return sizeOfPtr;
+ }
+ gnutls_assert();
+ memcpy( iptr, state->gnutls_internals.handshake_recv_buffer.data, state->gnutls_internals.handshake_recv_buffer.size);
+
+ htype = state->gnutls_internals.handshake_recv_buffer_htype;
+ type = state->gnutls_internals.handshake_recv_buffer_type;
+
+ left -= state->gnutls_internals.handshake_recv_buffer.size;
+
+ state->gnutls_internals.handshake_recv_buffer.size = 0;
+ }
+
while (left > 0) {
- i = gnutls_recv_int(fd, state, type, htype, &ptr[i], left);
+ dsize = sizeOfPtr - left;
+ i = gnutls_recv_int(fd, state, type, htype, &ptr[dsize], left);
if (i < 0) {
- if (sizeOfPtr - left > 0) {
+
+ if (dsize > 0 && (i==GNUTLS_E_INTERRUPTED || i==GNUTLS_E_AGAIN)) {
gnutls_assert();
- goto finish;
- }
+
+ state->gnutls_internals.handshake_recv_buffer.data = gnutls_realloc_fast(
+ state->gnutls_internals.handshake_recv_buffer.data, dsize);
+ if (state->gnutls_internals.handshake_recv_buffer.data==NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ memcpy( state->gnutls_internals.handshake_recv_buffer.data, iptr, dsize);
+
+ state->gnutls_internals.handshake_recv_buffer_htype = htype;
+ state->gnutls_internals.handshake_recv_buffer_type = type;
+
+ state->gnutls_internals.handshake_recv_buffer.size = dsize;
+ } else
+ state->gnutls_internals.handshake_recv_buffer.size = 0;
+
gnutls_assert();
+ fprintf(stderr, "C3: BUFFER_SIZE: %d\n", state->gnutls_internals.handshake_recv_buffer.size);
+
return i;
} else {
if (i == 0)
@@ -625,12 +778,27 @@ ssize_t _gnutls_handshake_recv_int(int fd, GNUTLS_STATE state, ContentType type,
}
- finish:
+ state->gnutls_internals.handshake_recv_buffer.size = 0;
+
+{int x,j,sum=0;
+ fprintf(stderr, "HREAD: read %d bytes from %d\n", (sizeOfPtr-left), fd);
+ for (x=0;x<((sizeOfPtr-left)/16)+1;x++) {
+ fprintf(stderr, "%.4x - ",x);
+ for (j=0;j<16;j++) {
+ if (sum<(sizeOfPtr-left)) {
+ fprintf(stderr, "%.2x ", ((unsigned char*)ptr)[sum++]);
+ }
+ }
+ fprintf(stderr, "\n");
+
+ }
+}
return (sizeOfPtr - left);
}
/* Buffer for handshake packets. Keeps the packets in order
- * for finished messages to use them.
+ * for finished messages to use them. Used in HMAC calculation
+ * and finished messages.
*/
int gnutls_insert_to_handshake_buffer( GNUTLS_STATE state, char *data, int length)
{