diff options
author | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2001-10-21 14:32:03 +0000 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2001-10-21 14:32:03 +0000 |
commit | 77e5bcb42d91f612ae049469184e6b66291d2982 (patch) | |
tree | e435d47f2e526ad1f9ffe2aef9dc4a907712bab3 | |
parent | d49946d7e5c7361a10353f70e53dbcdf6fb83963 (diff) | |
download | gnutls-77e5bcb42d91f612ae049469184e6b66291d2982.tar.gz |
changed gnutls_write() semantics in order to cope with interrupted system
calls and non blocking IO
-rw-r--r-- | lib/gnutls_buffers.c | 67 | ||||
-rw-r--r-- | lib/gnutls_buffers.h | 2 | ||||
-rw-r--r-- | lib/gnutls_errors.c | 2 | ||||
-rw-r--r-- | lib/gnutls_errors_int.h | 2 | ||||
-rw-r--r-- | lib/gnutls_int.h | 5 | ||||
-rw-r--r-- | lib/gnutls_record.c | 9 |
6 files changed, 62 insertions, 25 deletions
diff --git a/lib/gnutls_buffers.c b/lib/gnutls_buffers.c index 696008497d..91b6913f4c 100644 --- a/lib/gnutls_buffers.c +++ b/lib/gnutls_buffers.c @@ -179,7 +179,7 @@ static ssize_t _gnutls_read(SOCKET fd, void *iptr, size_t sizeOfPtr, int flags) if (errno==EAGAIN) return GNUTLS_E_AGAIN; else return GNUTLS_E_INTERRUPTED; } else - return GNUTLS_E_UNKNOWN_ERROR; + return GNUTLS_E_PULL_ERROR; } else { #ifdef READ_DEBUG _gnutls_log( "READ: Got %d bytes from %d\n", i, fd); @@ -367,52 +367,77 @@ ssize_t _gnutls_read_buffered( int fd, GNUTLS_STATE state, opaque **iptr, size_t * This function may not cope right with interrupted system calls * and EAGAIN error. Ideas? * + * 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 + * to decrypt and verify the integrity. + * */ -ssize_t _gnutls_write(int fd, const void *iptr, size_t n, int flags) +ssize_t _gnutls_write(int fd, GNUTLS_STATE state, const void *iptr, size_t n, int flags) { 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; -#ifdef WRITE_DEBUG - _gnutls_log( "WRITE: wrote %d bytes to %d\n", n, fd); - for (x=0;x<(n/16)+1;x++) { - _gnutls_log( "%.4x - ",x); - for (j=0;j<16;j++) { - if (sum<n) { - _gnutls_log( "%.2x ", ((unsigned char*)ptr)[sum++]); - } - } - _gnutls_log( "\n"); - + /* 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. + */ + if (state->gnutls_internals.send_buffer_ind[1] > 0 && state->gnutls_internals.send_buffer_ind[1] != n) { + 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]; } -#endif + left = n; while (left > 0) { if (_gnutls_push_func==NULL) - i = send(fd, &ptr[i], left, flags); + i = send(fd, &ptr[start_pos+i], left, flags); else - i = _gnutls_push_func(fd, &ptr[i], left); + i = _gnutls_push_func(fd, &ptr[start_pos+i], left); if (i == -1) { if (errno == EAGAIN || errno == EINTR) { - i = 0; + state->gnutls_internals.send_buffer_ind[0] = n - left; + state->gnutls_internals.send_buffer_ind[1] = n; gnutls_assert(); -/* if (errno==EAGAIN) return GNUTLS_E_AGAIN; - * else return GNUTLS_E_INTERRUPTED; - */ +#ifdef WRITE_DEBUG + _gnutls_log( "WRITE: Interrupted. wrote %d bytes to %d. Left %d\n", n-left, fd, left); +#endif + if (errno==EAGAIN) return GNUTLS_E_AGAIN; + else return GNUTLS_E_INTERRUPTED; + } else { gnutls_assert(); - return GNUTLS_E_UNKNOWN_ERROR; + return GNUTLS_E_PUSH_ERROR; } } left -= i; } + state->gnutls_internals.send_buffer_ind[0] = state->gnutls_internals.send_buffer_ind[1] = 0; + +#ifdef WRITE_DEBUG + _gnutls_log( "WRITE: wrote %d bytes to %d\n", n, fd); + for (x=0;x<(n/16)+1;x++) { + _gnutls_log( "%.4x - ",x); + for (j=0;j<16;j++) { + if (sum<n) { + _gnutls_log( "%.2x ", ((unsigned char*)ptr)[sum++]); + } + } + _gnutls_log( "\n"); + + } +#endif return n; } diff --git a/lib/gnutls_buffers.h b/lib/gnutls_buffers.h index ca40d0e69f..9297b6b182 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, const void *iptr, size_t n, int ); +ssize_t _gnutls_write(int fd, GNUTLS_STATE, const void *iptr, size_t n, int ); /* used in SSL3 */ int gnutls_getHashDataFromBuffer( GNUTLS_STATE state, char *data, int length); diff --git a/lib/gnutls_errors.c b/lib/gnutls_errors.c index ea39c55dd1..ef0710825f 100644 --- a/lib/gnutls_errors.c +++ b/lib/gnutls_errors.c @@ -73,6 +73,8 @@ static gnutls_error_entry error_algorithms[] = { GNUTLS_ERROR_ENTRY( GNUTLS_E_EXPIRED, 1), GNUTLS_ERROR_ENTRY( GNUTLS_E_HASH_FAILED, 1), GNUTLS_ERROR_ENTRY( GNUTLS_E_PARSING_ERROR, 1), + GNUTLS_ERROR_ENTRY( GNUTLS_E_PULL_ERROR, 1), + GNUTLS_ERROR_ENTRY( GNUTLS_E_PUSH_ERROR, 1), GNUTLS_ERROR_ENTRY( GNUTLS_E_NO_CERTIFICATE_FOUND, 1), GNUTLS_ERROR_ENTRY( GNUTLS_E_RECORD_LIMIT_REACHED, 1), GNUTLS_ERROR_ENTRY( GNUTLS_E_ASN1_PARSING_ERROR, 1), diff --git a/lib/gnutls_errors_int.h b/lib/gnutls_errors_int.h index 79a8a547ec..77cebf04a5 100644 --- a/lib/gnutls_errors_int.h +++ b/lib/gnutls_errors_int.h @@ -53,5 +53,7 @@ #define GNUTLS_E_INVALID_PARAMETERS -50 #define GNUTLS_E_INVALID_REQUEST -51 #define GNUTLS_E_INTERRUPTED -52 +#define GNUTLS_E_PUSH_ERROR -53 +#define GNUTLS_E_PULL_ERROR -54 #define GNUTLS_E_UNIMPLEMENTED_FEATURE -250 diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index a6de5db713..c5460688f1 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -360,6 +360,11 @@ 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. + */ /* 0 if no peeked data was kept, 1 otherwise. */ diff --git a/lib/gnutls_record.c b/lib/gnutls_record.c index 36cad52101..472b78a854 100644 --- a/lib/gnutls_record.c +++ b/lib/gnutls_record.c @@ -462,7 +462,7 @@ ssize_t gnutls_send_int(SOCKET cd, GNUTLS_STATE state, ContentType type, Handsha return cipher_size; /* error */ } - if (_gnutls_write(cd, cipher, cipher_size, 0) != 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; @@ -499,7 +499,7 @@ ssize_t gnutls_send_int(SOCKET cd, GNUTLS_STATE state, ContentType type, Handsha return cipher_size; } - if (_gnutls_write(cd, cipher, cipher_size, 0) != 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; @@ -1013,7 +1013,10 @@ AlertDescription gnutls_get_last_alert( GNUTLS_STATE state) { * difference is that is accepts a GNUTLS state. * * If the EINTR is returned by the internal push function (write()) - * then GNUTLS_E_INTERRUPTED, will be returned. + * then GNUTLS_E_INTERRUPTED, will be returned. If GNUTLS_E_INTERRUPTED or + * GNUTLS_E_AGAIN is returned you must call this function again, with the + * same (exactly) parameters. Otherwise the write operation will be + * corrupted and the connection will be terminated. * * Returns the number of bytes received, zero on EOF, or * a negative error code. |