summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@gnutls.org>2001-10-21 14:32:03 +0000
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2001-10-21 14:32:03 +0000
commit77e5bcb42d91f612ae049469184e6b66291d2982 (patch)
treee435d47f2e526ad1f9ffe2aef9dc4a907712bab3
parentd49946d7e5c7361a10353f70e53dbcdf6fb83963 (diff)
downloadgnutls-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.c67
-rw-r--r--lib/gnutls_buffers.h2
-rw-r--r--lib/gnutls_errors.c2
-rw-r--r--lib/gnutls_errors_int.h2
-rw-r--r--lib/gnutls_int.h5
-rw-r--r--lib/gnutls_record.c9
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.