summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@gnutls.org>2001-10-22 12:51:54 +0000
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2001-10-22 12:51:54 +0000
commitfd4bc2464998bf9118523c03158a2e3f658f89b5 (patch)
treedbb0efba16af8302b2c9c44b899897288f54c1ea
parent77e5bcb42d91f612ae049469184e6b66291d2982 (diff)
downloadgnutls-fd4bc2464998bf9118523c03158a2e3f658f89b5.tar.gz
updated/fixed the handling of interrupted writes
-rw-r--r--lib/gnutls_buffers.c57
-rw-r--r--lib/gnutls_buffers.h2
-rw-r--r--lib/gnutls_int.h12
-rw-r--r--lib/gnutls_record.c109
-rw-r--r--src/serv.c4
5 files changed, 102 insertions, 82 deletions
diff --git a/lib/gnutls_buffers.c b/lib/gnutls_buffers.c
index 91b6913f4c..2b0d05099a 100644
--- a/lib/gnutls_buffers.c
+++ b/lib/gnutls_buffers.c
@@ -360,7 +360,6 @@ 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.
*
@@ -372,43 +371,58 @@ ssize_t _gnutls_read_buffered( int fd, GNUTLS_STATE state, opaque **iptr, size_t
* to decrypt and verify the integrity.
*
*/
-ssize_t _gnutls_write(int fd, GNUTLS_STATE state, const void *iptr, size_t n, int flags)
+ssize_t _gnutls_write_buffered(int fd, GNUTLS_STATE state, const void *iptr, size_t n)
{
size_t left;
#ifdef WRITE_DEBUG
int j,x, sum=0;
#endif
- ssize_t i = 0;
- size_t start_pos = 0;
- const char *ptr = iptr;
+ ssize_t retval, i;
+ const opaque * ptr;
+ ptr = iptr;
+
/* In case the previous write was interrupted, check if the
- * current data have the same size with the previous.
- * If they haven't return an error.
+ * iptr != NULL and we have data in the buffer.
+ * If this is true then return an error.
*/
- if (state->gnutls_internals.send_buffer_ind[1] > 0 && state->gnutls_internals.send_buffer_ind[1] != n) {
+ if (state->gnutls_internals.send_buffer.size > 0 && iptr != NULL) {
gnutls_assert();
return GNUTLS_E_INVALID_PARAMETERS;
- } else if (state->gnutls_internals.send_buffer_ind[1] > 0) {
- /* send only the data we haven't send before.
- */
- n -= state->gnutls_internals.send_buffer_ind[0];
- start_pos = state->gnutls_internals.send_buffer_ind[0];
}
+ /* If data in the buffer exist
+ */
+ if (iptr == NULL) {
+ if ( state->gnutls_internals.send_buffer.size == 0) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_PARAMETERS;
+ } else {
+ ptr = state->gnutls_internals.send_buffer.data;
+ n = state->gnutls_internals.send_buffer.size;
+ }
+ }
+
+ i = 0;
left = n;
while (left > 0) {
if (_gnutls_push_func==NULL)
- i = send(fd, &ptr[start_pos+i], left, flags);
+ i = send(fd, &ptr[i], left, 0);
else
- i = _gnutls_push_func(fd, &ptr[start_pos+i], left);
+ i = _gnutls_push_func(fd, &ptr[i], left);
if (i == -1) {
if (errno == EAGAIN || errno == EINTR) {
- state->gnutls_internals.send_buffer_ind[0] = n - left;
- state->gnutls_internals.send_buffer_ind[1] = n;
- gnutls_assert();
+ 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);
+ if (state->gnutls_internals.send_buffer.data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+ state->gnutls_internals.send_buffer.size = left;
+ memcpy( state->gnutls_internals.send_buffer.data, &ptr[n-left], left);
#ifdef WRITE_DEBUG
_gnutls_log( "WRITE: Interrupted. wrote %d bytes to %d. Left %d\n", n-left, fd, left);
#endif
@@ -423,7 +437,10 @@ ssize_t _gnutls_write(int fd, GNUTLS_STATE state, const void *iptr, size_t n, in
left -= i;
}
- state->gnutls_internals.send_buffer_ind[0] = state->gnutls_internals.send_buffer_ind[1] = 0;
+ retval = n + state->gnutls_internals.send_buffer_prev_size;
+
+ state->gnutls_internals.send_buffer.size = 0;
+ state->gnutls_internals.send_buffer_prev_size = 0;
#ifdef WRITE_DEBUG
_gnutls_log( "WRITE: wrote %d bytes to %d\n", n, fd);
@@ -438,7 +455,7 @@ ssize_t _gnutls_write(int fd, GNUTLS_STATE state, const void *iptr, size_t n, in
}
#endif
- return n;
+ return retval;
}
diff --git a/lib/gnutls_buffers.h b/lib/gnutls_buffers.h
index 9297b6b182..e32ba97c87 100644
--- a/lib/gnutls_buffers.h
+++ b/lib/gnutls_buffers.h
@@ -25,7 +25,7 @@ ssize_t _gnutls_read_buffered(int fd, GNUTLS_STATE, opaque **iptr, size_t n, Con
void _gnutls_read_clear_buffer( GNUTLS_STATE);
int _gnutls_clear_peeked_data( SOCKET cd, GNUTLS_STATE state);
-ssize_t _gnutls_write(int fd, GNUTLS_STATE, const void *iptr, size_t n, int );
+ssize_t _gnutls_write_buffered(int fd, GNUTLS_STATE, const void *iptr, size_t n );
/* used in SSL3 */
int gnutls_getHashDataFromBuffer( GNUTLS_STATE state, char *data, int length);
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index c5460688f1..a8748d306e 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -25,8 +25,8 @@
#include <defines.h>
/*
-#define WRITE_DEBUG
#define BUFFERS_DEBUG
+#define WRITE_DEBUG
#define HARD_DEBUG
#define HANDSHAKE_DEBUG
#define RECORD_DEBUG
@@ -360,10 +360,12 @@ typedef struct {
*/
opaque recv_buffer_data[MAX_RECV_SIZE];
int recv_buffer_data_size;
- int send_buffer_ind[2]; /* 0 holds the
- * position last write was interrupted,
- * and 1 holds the size of the last
- * data requested to send.
+ gnutls_datum send_buffer; /* holds cached data
+ * for the gnutls_write_buffered()
+ * function.
+ */
+ int send_buffer_prev_size; /* holds the
+ * data written in the previous runs.
*/
/* 0 if no peeked data was kept, 1 otherwise.
diff --git a/lib/gnutls_record.c b/lib/gnutls_record.c
index 472b78a854..730253f1e8 100644
--- a/lib/gnutls_record.c
+++ b/lib/gnutls_record.c
@@ -147,6 +147,7 @@ int gnutls_deinit(GNUTLS_STATE state)
GNUTLS_FREE(state->gnutls_internals.buffer.data);
GNUTLS_FREE(state->gnutls_internals.buffer_handshake.data);
GNUTLS_FREE(state->gnutls_internals.hash_buffer.data);
+ GNUTLS_FREE(state->gnutls_internals.send_buffer.data);
gnutls_clear_creds( state);
@@ -407,10 +408,10 @@ int gnutls_bye(SOCKET cd, GNUTLS_STATE state, CloseRequest how)
ssize_t gnutls_send_int(SOCKET cd, GNUTLS_STATE state, ContentType type, HandshakeType htype, const void *_data, size_t sizeofdata)
{
uint8 *cipher;
- int i, cipher_size;
- int ret = 0;
- int iterations;
- int Size;
+ const uint8 *ptr;
+ int cipher_size;
+ int ret = 0, retval = 0;
+ int Size, data2send;
uint8 headers[5];
const uint8 *data=_data;
GNUTLS_Version lver;
@@ -423,14 +424,6 @@ ssize_t gnutls_send_int(SOCKET cd, GNUTLS_STATE state, ContentType type, Handsha
return GNUTLS_E_INVALID_SESSION;
}
- if (sizeofdata < MAX_ENC_LEN) {
- iterations = 1;
- Size = sizeofdata;
- } else {
- iterations = sizeofdata / MAX_ENC_LEN;
- Size = MAX_ENC_LEN;
- }
-
headers[0]=type;
if (htype==GNUTLS_CLIENT_HELLO) { /* then send the lowest
@@ -454,22 +447,61 @@ ssize_t gnutls_send_int(SOCKET cd, GNUTLS_STATE state, ContentType type, Handsha
(int) uint64touint32(&state->connection_state.write_sequence_number), _gnutls_packet2str(type), type, sizeofdata);
#endif
- for (i = 0; i < iterations; i++) {
- cipher_size = _gnutls_encrypt( state, headers, RECORD_HEADER_SIZE, &data[i*Size], Size, &cipher, type);
- if (cipher_size <= 0) {
- gnutls_assert();
- if (cipher_size==0) cipher_size = GNUTLS_E_ENCRYPTION_FAILED;
- return cipher_size; /* error */
+ /* in this loop we encrypt all data that are a multiple
+ * of the MAC_ENC_LEN;
+ */
+ data2send = sizeofdata;
+ ptr = data;
+ retval = 0;
+
+ while( data2send != 0) {
+
+ if (data2send - MAX_ENC_LEN >= 0) {
+ data2send -= MAX_ENC_LEN;
+ Size = MAX_ENC_LEN;
+ } else {
+ Size = data2send;
+ data2send = 0;
}
-
- if (_gnutls_write(cd, state, cipher, cipher_size, 0) != cipher_size) {
+
+ /* Only encrypt if we don't have data to send
+ * from the previous run. - probably interrupted.
+ */
+ if (state->gnutls_internals.send_buffer.size == 0) {
+ cipher_size = _gnutls_encrypt( state, headers, RECORD_HEADER_SIZE, ptr, Size, &cipher, type);
+ if (cipher_size <= 0) {
+ gnutls_assert();
+ if (cipher_size==0) cipher_size = GNUTLS_E_ENCRYPTION_FAILED;
+ return cipher_size; /* error */
+ }
+ } else {
+ /* order write buffered to write
+ * the buffered data.
+ */
+ cipher = NULL;
+ cipher_size = state->gnutls_internals.send_buffer.size +
+ state->gnutls_internals.send_buffer_prev_size;
+ }
+
+ ptr += Size;
+
+ if ( (ret=_gnutls_write_buffered(cd, state, cipher, cipher_size)) != cipher_size) {
gnutls_free( cipher);
+ if ( ret<0 && gnutls_is_fatal_error(ret)==0) {
+ /* If we have sent any data then return
+ * that value.
+ */
+ gnutls_assert();
+ if (retval > 0) return retval;
+ return ret;
+ }
state->gnutls_internals.valid_connection = VALID_FALSE;
state->gnutls_internals.resumable = RESUME_FALSE;
gnutls_assert();
- return GNUTLS_E_UNABLE_SEND_DATA;
+ return ret;
}
+ retval += Size;
gnutls_free(cipher);
#ifdef RECORD_DEBUG
@@ -487,40 +519,7 @@ ssize_t gnutls_send_int(SOCKET cd, GNUTLS_STATE state, ContentType type, Handsha
}
-
- /* rest of data
- */
- if (iterations > 1) {
- Size = sizeofdata % MAX_ENC_LEN;
- cipher_size = _gnutls_encrypt( state, headers, RECORD_HEADER_SIZE, &data[i*Size], Size, &cipher, type);
- if (cipher_size<=0) {
- if (cipher_size == 0) cipher_size = GNUTLS_E_ENCRYPTION_FAILED;
- gnutls_assert();
- return cipher_size;
- }
-
- if (_gnutls_write(cd, state, cipher, cipher_size, 0) != cipher_size) {
- gnutls_free(cipher);
- state->gnutls_internals.valid_connection = VALID_FALSE;
- state->gnutls_internals.resumable = RESUME_FALSE;
- gnutls_assert();
- return GNUTLS_E_UNABLE_SEND_DATA;
- }
-
- gnutls_free(cipher);
-
- /* increase sequence number
- */
- if (uint64pp( &state->connection_state.write_sequence_number)!=0) {
- state->gnutls_internals.valid_connection = VALID_FALSE;
- gnutls_assert();
- return GNUTLS_E_RECORD_LIMIT_REACHED;
- }
- }
-
- ret += sizeofdata;
-
- return ret;
+ return retval;
}
/* This function is to be called if the handshake was successfully
diff --git a/src/serv.c b/src/serv.c
index 0ccde3d8cf..b8011ac056 100644
--- a/src/serv.c
+++ b/src/serv.c
@@ -444,8 +444,10 @@ int main(int argc, char **argv)
if (ret > 0) {
if (http == 0) {
- gnutls_write(sd, state, buffer,
+ do {
+ ret = gnutls_write(sd, state, buffer,
strlen(buffer));
+ } while( ret==GNUTLS_E_INTERRUPTED || ret==GNUTLS_E_AGAIN);
} else {
strcpy( http_buffer, HTTP_BEGIN);
peer_print_info(sd, state);