/* This is exactly like write_buffered, but will use two buffers to read * from. */ ssize_t _gnutls_io_write_buffered2 (gnutls_session_t session, const void *iptr, size_t n, const void *iptr2, size_t n2) { if (n == 0) { return _gnutls_io_write_buffered (session, iptr2, n2); } else { opaque *sptr; ssize_t ret; sptr = gnutls_alloca (n + n2); if (sptr == NULL) { gnutls_assert (); return GNUTLS_E_MEMORY_ERROR; } memcpy (sptr, iptr, n); memcpy (&sptr[n], iptr2, n2); ret = _gnutls_io_write_buffered (session, sptr, n + n2); gnutls_afree (sptr); return ret; } }
/* This function writes the data that are left in the * TLS write buffer (ie. because the previous write was * interrupted. */ ssize_t _gnutls_io_write_flush (gnutls_session_t session) { ssize_t ret; if (session->internals.record_send_buffer.length == 0) return 0; /* done */ ret = _gnutls_io_write_buffered (session, NULL, 0); _gnutls_write_log ("WRITE FLUSH: %d [buffer: %d]\n", ret, session->internals.record_send_buffer.length); return ret; }
/* This function behaves exactly like write(). The only difference is * that it accepts, the gnutls_session_t and the content_type_t of data to * send (if called by the user the Content is specific) * It is intended to transfer data, under the current session. * * @type: The content type to send * @htype: If this is a handshake message then the handshake type * @epoch_rel: %EPOCH_READ_* or %EPOCH_WRITE_* * @data: the data to be sent * @data_size: the size of the @data * @target_length: @data_size + minimum required padding * @mflags: zero or %MBUFFER_FLUSH * * Oct 30 2001: Removed capability to send data more than MAX_RECORD_SIZE. * This makes the function much easier to read, and more error resistant * (there were cases were the old function could mess everything up). * --nmav * * This function may accept a NULL pointer for data, and 0 for size, if * and only if the previous send was interrupted for some reason. * */ ssize_t _gnutls_send_tlen_int (gnutls_session_t session, content_type_t type, gnutls_handshake_description_t htype, unsigned int epoch_rel, const void *_data, size_t data_size, size_t target_length, unsigned int mflags) { mbuffer_st *bufel; ssize_t cipher_size; int retval, ret; int send_data_size; uint8_t *headers; int header_size; const uint8_t *data = _data; record_parameters_st *record_params; record_state_st *record_state; ret = _gnutls_epoch_get (session, epoch_rel, &record_params); if (ret < 0) return gnutls_assert_val(ret); /* Safeguard against processing data with an incomplete cipher state. */ if (!record_params->initialized) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); record_state = &record_params->write; /* Do not allow null pointer if the send buffer is empty. * If the previous send was interrupted then a null pointer is * ok, and means to resume. */ if (session->internals.record_send_buffer.byte_length == 0 && (data_size == 0 && _data == NULL)) { gnutls_assert (); return GNUTLS_E_INVALID_REQUEST; } if (type != GNUTLS_ALERT) /* alert messages are sent anyway */ if (session_is_valid (session) || session->internals.may_not_write != 0) { gnutls_assert (); return GNUTLS_E_INVALID_SESSION; } if (data_size > MAX_USER_SEND_SIZE(session)) { if (IS_DTLS(session)) return gnutls_assert_val(GNUTLS_E_LARGE_PACKET); send_data_size = MAX_USER_SEND_SIZE(session); } else send_data_size = data_size; /* Only encrypt if we don't have data to send * from the previous run. - probably interrupted. */ if (mflags != 0 && session->internals.record_send_buffer.byte_length > 0) { ret = _gnutls_io_write_flush (session); if (ret > 0) cipher_size = ret; else cipher_size = 0; retval = session->internals.record_send_buffer_user_size; } else { if (unlikely((send_data_size == 0 && target_length == 0))) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); /* now proceed to packet encryption */ cipher_size = MAX_RECORD_SEND_SIZE(session); bufel = _mbuffer_alloc (0, cipher_size+CIPHER_SLACK_SIZE); if (bufel == NULL) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); headers = _mbuffer_get_uhead_ptr(bufel); headers[0] = type; /* Use the default record version, if it is * set. */ copy_record_version (session, htype, &headers[1]); header_size = RECORD_HEADER_SIZE(session); /* Adjust header length and add sequence for DTLS */ if (IS_DTLS(session)) memcpy(&headers[3], &record_state->sequence_number.i, 8); _gnutls_record_log ("REC[%p]: Preparing Packet %s(%d) with length: %d and target length: %d\n", session, _gnutls_packet2str (type), type, (int) data_size, (int) target_length); _mbuffer_set_udata_size(bufel, cipher_size); _mbuffer_set_uhead_size(bufel, header_size); ret = _gnutls_encrypt (session, data, send_data_size, target_length, bufel, type, record_params); if (ret <= 0) { gnutls_assert (); if (ret == 0) ret = GNUTLS_E_ENCRYPTION_FAILED; gnutls_free (bufel); return ret; /* error */ } cipher_size = _mbuffer_get_udata_size(bufel); retval = send_data_size; session->internals.record_send_buffer_user_size = send_data_size; /* increase sequence number */ if (sequence_increment (session, &record_state->sequence_number) != 0) { session_invalidate (session); gnutls_free (bufel); return gnutls_assert_val(GNUTLS_E_RECORD_LIMIT_REACHED); } ret = _gnutls_io_write_buffered (session, bufel, mflags); } if (ret != cipher_size) { /* If we have sent any data then just return * the error value. Do not invalidate the session. */ if (ret < 0 && gnutls_error_is_fatal (ret) == 0) return gnutls_assert_val(ret); if (ret > 0) ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); session_unresumable (session); session->internals.may_not_write = 1; return gnutls_assert_val(ret); } session->internals.record_send_buffer_user_size = 0; _gnutls_record_log ("REC[%p]: Sent Packet[%d] %s(%d) in epoch %d and length: %d\n", session, (unsigned int) _gnutls_uint64touint32 (&record_state->sequence_number), _gnutls_packet2str (type), type, (int) record_params->epoch, (int) cipher_size); return retval; }
/* This function behaves exactly like write(). The only difference is * that it accepts, the gnutls_session_t and the content_type_t of data to * send (if called by the user the Content is specific) * It is intended to transfer data, under the current session. * * Oct 30 2001: Removed capability to send data more than MAX_RECORD_SIZE. * This makes the function much easier to read, and more error resistant * (there were cases were the old function could mess everything up). * --nmav * * This function may accept a NULL pointer for data, and 0 for size, if * and only if the previous send was interrupted for some reason. * */ ssize_t _gnutls_send_int (gnutls_session_t session, content_type_t type, gnutls_handshake_description_t htype, unsigned int epoch_rel, const void *_data, size_t sizeofdata, unsigned int mflags) { mbuffer_st *bufel; ssize_t cipher_size; int retval, ret; int data2send_size; uint8_t headers[5]; const uint8_t *data = _data; record_parameters_st *record_params; record_state_st *record_state; ret = _gnutls_epoch_get (session, epoch_rel, &record_params); if (ret < 0) { gnutls_assert (); return ret; } /* Safeguard against processing data with an incomplete cipher state. */ if (!record_params->initialized) { gnutls_assert (); return GNUTLS_E_INVALID_REQUEST; } record_state = &record_params->write; /* Do not allow null pointer if the send buffer is empty. * If the previous send was interrupted then a null pointer is * ok, and means to resume. */ if (session->internals.record_send_buffer.byte_length == 0 && (sizeofdata == 0 && _data == NULL)) { gnutls_assert (); return GNUTLS_E_INVALID_REQUEST; } if (type != GNUTLS_ALERT) /* alert messages are sent anyway */ if (session_is_valid (session) || session->internals.may_not_write != 0) { gnutls_assert (); return GNUTLS_E_INVALID_SESSION; } headers[0] = type; /* Use the default record version, if it is * set. */ copy_record_version (session, htype, &headers[1]); _gnutls_record_log ("REC[%p]: Sending Packet[%d] %s(%d) with length: %d\n", session, (int) _gnutls_uint64touint32 (&record_state->sequence_number), _gnutls_packet2str (type), type, (int) sizeofdata); if (sizeofdata > MAX_RECORD_SEND_SIZE) data2send_size = MAX_RECORD_SEND_SIZE; else data2send_size = sizeofdata; /* Only encrypt if we don't have data to send * from the previous run. - probably interrupted. */ if (mflags != 0 && session->internals.record_send_buffer.byte_length > 0) { ret = _gnutls_io_write_flush (session); if (ret > 0) cipher_size = ret; else cipher_size = 0; retval = session->internals.record_send_buffer_user_size; } else { /* now proceed to packet encryption */ cipher_size = data2send_size + MAX_RECORD_OVERHEAD; bufel = _mbuffer_alloc (cipher_size, cipher_size); if (bufel == NULL) { gnutls_assert (); return GNUTLS_E_MEMORY_ERROR; } cipher_size = _gnutls_encrypt (session, headers, RECORD_HEADER_SIZE, data, data2send_size, _mbuffer_get_udata_ptr (bufel), cipher_size, type, (session->internals.priorities.no_padding == 0) ? 1 : 0, record_params); if (cipher_size <= 0) { gnutls_assert (); if (cipher_size == 0) cipher_size = GNUTLS_E_ENCRYPTION_FAILED; gnutls_free (bufel); return cipher_size; /* error */ } retval = data2send_size; session->internals.record_send_buffer_user_size = data2send_size; /* increase sequence number */ if (_gnutls_uint64pp (&record_state->sequence_number) != 0) { session_invalidate (session); gnutls_assert (); gnutls_free (bufel); return GNUTLS_E_RECORD_LIMIT_REACHED; } _mbuffer_set_udata_size (bufel, cipher_size); ret = _gnutls_io_write_buffered (session, bufel, mflags); } if (ret != cipher_size) { if (ret < 0 && gnutls_error_is_fatal (ret) == 0) { /* If we have sent any data then just return * the error value. Do not invalidate the session. */ gnutls_assert (); return ret; } if (ret > 0) { gnutls_assert (); ret = GNUTLS_E_INTERNAL_ERROR; } session_unresumable (session); session->internals.may_not_write = 1; gnutls_assert (); return ret; } session->internals.record_send_buffer_user_size = 0; _gnutls_record_log ("REC[%p]: Sent Packet[%d] %s(%d) with length: %d\n", session, (int) _gnutls_uint64touint32 (&record_state->sequence_number), _gnutls_packet2str (type), type, (int) cipher_size); return retval; }