/* 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; }
/* @ms: is the number of milliseconds to wait for data. Use zero for indefinite. * * This will receive record layer packets and add them to * application_data_buffer and handshake_data_buffer. * * If the htype is not -1 then handshake timeouts * will be enforced. */ ssize_t _gnutls_recv_in_buffers (gnutls_session_t session, content_type_t type, gnutls_handshake_description_t htype, unsigned int ms) { uint64 *packet_sequence; gnutls_datum_t ciphertext; mbuffer_st* bufel = NULL, *decrypted = NULL; gnutls_datum_t t; int ret; unsigned int empty_fragments = 0; record_parameters_st *record_params; record_state_st *record_state; struct tls_record_st record; begin: if (empty_fragments > session->internals.priorities.max_empty_records) { gnutls_assert (); return GNUTLS_E_TOO_MANY_EMPTY_PACKETS; } if (session->internals.read_eof != 0) { /* if we have already read an EOF */ return 0; } else if (session_is_valid (session) != 0 || session->internals.may_not_read != 0) return gnutls_assert_val(GNUTLS_E_INVALID_SESSION); /* get the record state parameters */ ret = _gnutls_epoch_get (session, EPOCH_READ_CURRENT, &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_INTERNAL_ERROR); record_state = &record_params->read; /* receive headers */ ret = recv_headers(session, type, htype, &record, &ms); if (ret < 0) { ret = gnutls_assert_val_fatal(ret); goto recv_error; } if (IS_DTLS(session)) packet_sequence = &record.sequence; else packet_sequence = &record_state->sequence_number; /* Read the packet data and insert it to record_recv_buffer. */ ret = _gnutls_io_read_buffered (session, record.packet_size, record.type, &ms); if (ret != record.packet_size) { gnutls_assert(); goto recv_error; } /* ok now we are sure that we have read all the data - so * move on ! */ ret = _mbuffer_linearize (&session->internals.record_recv_buffer); if (ret < 0) return gnutls_assert_val(ret); bufel = _mbuffer_head_get_first (&session->internals.record_recv_buffer, NULL); if (bufel == NULL) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); /* We allocate the maximum possible to allow few compressed bytes to expand to a * full record. */ decrypted = _mbuffer_alloc(record.length, record.length); if (decrypted == NULL) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); ciphertext.data = (uint8_t*)_mbuffer_get_udata_ptr(bufel) + record.header_size; ciphertext.size = record.length; /* decrypt the data we got. */ t.data = _mbuffer_get_udata_ptr(decrypted); t.size = _mbuffer_get_udata_size(decrypted); ret = _gnutls_decrypt (session, &ciphertext, &t, record.type, record_params, packet_sequence); if (ret >= 0) _mbuffer_set_udata_size(decrypted, ret); _mbuffer_head_remove_bytes (&session->internals.record_recv_buffer, record.header_size + record.length); if (ret < 0) { gnutls_assert(); _gnutls_audit_log(session, "Discarded message[%u] due to invalid decryption\n", (unsigned int)_gnutls_uint64touint32 (packet_sequence)); goto sanity_check_error; } /* check for duplicates. We check after the message * is processed and authenticated to avoid someone * messing with our windows. */ if (IS_DTLS(session) && session->internals.no_replay_protection == 0) { ret = _dtls_record_check(record_params, packet_sequence); if (ret < 0) { _gnutls_audit_log(session, "Discarded duplicate message[%u.%u]: %s\n", (unsigned int)record.sequence.i[0]*256 +(unsigned int)record.sequence.i[1], (unsigned int) _gnutls_uint64touint32 (packet_sequence), _gnutls_packet2str (record.type)); goto sanity_check_error; } _gnutls_record_log ("REC[%p]: Decrypted Packet[%u.%u] %s(%d) with length: %d\n", session, (unsigned int)record.sequence.i[0]*256 +(unsigned int)record.sequence.i[1], (unsigned int) _gnutls_uint64touint32 (packet_sequence), _gnutls_packet2str (record.type), record.type, (int)_mbuffer_get_udata_size(decrypted)); } else { _gnutls_record_log ("REC[%p]: Decrypted Packet[%u] %s(%d) with length: %d\n", session, (unsigned int) _gnutls_uint64touint32 (packet_sequence), _gnutls_packet2str (record.type), record.type, (int)_mbuffer_get_udata_size(decrypted)); } /* increase sequence number */ if (!IS_DTLS(session) && sequence_increment (session, &record_state->sequence_number) != 0) { session_invalidate (session); gnutls_assert (); ret = GNUTLS_E_RECORD_LIMIT_REACHED; goto sanity_check_error; } /* (originally for) TLS 1.0 CBC protection. * Actually this code is called if we just received * an empty packet. An empty TLS packet is usually * sent to protect some vulnerabilities in the CBC mode. * In that case we go to the beginning and start reading * the next packet. */ if (_mbuffer_get_udata_size(decrypted) == 0) { _mbuffer_xfree(&decrypted); empty_fragments++; goto begin; } if (record.v2) decrypted->htype = GNUTLS_HANDSHAKE_CLIENT_HELLO_V2; else { uint8_t * p = _mbuffer_get_udata_ptr(decrypted); decrypted->htype = p[0]; } ret = record_add_to_buffers (session, &record, type, htype, packet_sequence, decrypted); /* bufel is now either deinitialized or buffered somewhere else */ if (ret < 0) return gnutls_assert_val(ret); return ret; discard: session->internals.dtls.packets_dropped++; /* discard the whole received fragment. */ bufel = _mbuffer_head_pop_first(&session->internals.record_recv_buffer); _mbuffer_xfree(&bufel); return gnutls_assert_val(GNUTLS_E_AGAIN); sanity_check_error: if (IS_DTLS(session)) { session->internals.dtls.packets_dropped++; ret = gnutls_assert_val(GNUTLS_E_AGAIN); goto cleanup; } session_unresumable (session); session_invalidate (session); cleanup: _mbuffer_xfree(&decrypted); return ret; recv_error: if (ret < 0 && (gnutls_error_is_fatal (ret) == 0 || ret == GNUTLS_E_TIMEDOUT)) return ret; if (type == GNUTLS_ALERT) /* we were expecting close notify */ { session_invalidate (session); gnutls_assert (); return 0; } if (IS_DTLS(session)) { goto discard; } session_invalidate (session); session_unresumable (session); if (ret == 0) return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; else return ret; }
static int recv_headers( gnutls_session_t session, content_type_t type, gnutls_handshake_description_t htype, struct tls_record_st* record, unsigned int *ms) { int ret; gnutls_datum_t raw; /* raw headers */ /* Read the headers. */ record->header_size = record->packet_size = RECORD_HEADER_SIZE(session); ret = _gnutls_io_read_buffered (session, record->header_size, -1, ms); if (ret != record->header_size) { if (ret < 0 && gnutls_error_is_fatal (ret) == 0) return ret; if (ret > 0) ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; else if (ret == 0) ret = GNUTLS_E_PREMATURE_TERMINATION; return gnutls_assert_val(ret); } ret = _mbuffer_linearize (&session->internals.record_recv_buffer); if (ret < 0) return gnutls_assert_val(ret); _mbuffer_head_get_first (&session->internals.record_recv_buffer, &raw); if (raw.size < RECORD_HEADER_SIZE(session)) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); record_read_headers (session, raw.data, type, htype, record); /* Check if the DTLS epoch is valid */ if (IS_DTLS(session)) { if (_gnutls_epoch_is_valid(session, record->epoch) == 0) { _gnutls_audit_log(session, "Discarded message[%u] with invalid epoch %u.\n", (unsigned int)_gnutls_uint64touint32 (&record->sequence), (unsigned int)record->sequence.i[0]*256+(unsigned int)record->sequence.i[1]); gnutls_assert(); /* doesn't matter, just a fatal error */ return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } } /* Here we check if the Type of the received packet is * ok. */ if ((ret = check_recv_type (session, record->type)) < 0) return gnutls_assert_val(ret); /* Here we check if the advertized version is the one we * negotiated in the handshake. */ if ((ret = record_check_version (session, htype, record->version)) < 0) return gnutls_assert_val(ret); if (record->length > MAX_RECV_SIZE(session)) { _gnutls_audit_log (session, "Received packet with illegal length: %u\n", (unsigned int)record->length); return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); } _gnutls_record_log ("REC[%p]: Expected Packet %s(%d)\n", session, _gnutls_packet2str (type), type); _gnutls_record_log ("REC[%p]: Received Packet %s(%d) with length: %d\n", session, _gnutls_packet2str (record->type), record->type, record->length); return 0; }
/* This function behaves exactly like read(). The only difference is * that it accepts the gnutls_session_t and the content_type_t of data to * receive (if called by the user the Content is Userdata only) * It is intended to receive data, under the current session. * * The gnutls_handshake_description_t was introduced to support SSL V2.0 client hellos. */ ssize_t _gnutls_recv_int (gnutls_session_t session, content_type_t type, gnutls_handshake_description_t htype, opaque * data, size_t sizeofdata) { int decrypted_length; opaque version[2]; content_type_t recv_type; uint16_t length; uint8_t *ciphertext; int ret, ret2; uint16_t header_size; int empty_packet = 0; gnutls_datum_t data_enc, tmp; record_parameters_st *record_params; record_state_st *record_state; ret = _gnutls_epoch_get (session, EPOCH_READ_CURRENT, &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->read; if (type != GNUTLS_ALERT && (sizeofdata == 0 || data == NULL)) { return GNUTLS_E_INVALID_REQUEST; } begin: if (empty_packet > MAX_EMPTY_PACKETS_SEQUENCE) { gnutls_assert (); return GNUTLS_E_TOO_MANY_EMPTY_PACKETS; } if (session->internals.read_eof != 0) { /* if we have already read an EOF */ return 0; } else if (session_is_valid (session) != 0 || session->internals.may_not_read != 0) { gnutls_assert (); return GNUTLS_E_INVALID_SESSION; } /* If we have enough data in the cache do not bother receiving * a new packet. (in order to flush the cache) */ ret = check_buffers (session, type, data, sizeofdata); if (ret != 0) return ret; /* default headers for TLS 1.0 */ header_size = RECORD_HEADER_SIZE; if ((ret = _gnutls_io_read_buffered (session, header_size, -1)) != header_size) { if (ret < 0 && gnutls_error_is_fatal (ret) == 0) return ret; session_invalidate (session); if (type == GNUTLS_ALERT) { gnutls_assert (); return 0; /* we were expecting close notify */ } session_unresumable (session); gnutls_assert (); return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } ret = _mbuffer_linearize (&session->internals.record_recv_buffer); if (ret != 0) { gnutls_assert (); return ret; } _mbuffer_get_first (&session->internals.record_recv_buffer, &data_enc); if ((ret = record_check_headers (session, data_enc.data, type, htype, &recv_type, version, &length, &header_size)) < 0) { gnutls_assert (); return ret; } /* Here we check if the Type of the received packet is * ok. */ if ((ret = check_recv_type (recv_type)) < 0) { gnutls_assert (); return ret; } /* Here we check if the advertized version is the one we * negotiated in the handshake. */ if ((ret = record_check_version (session, htype, version)) < 0) { gnutls_assert (); session_invalidate (session); return ret; } _gnutls_record_log ("REC[%p]: Expected Packet[%d] %s(%d) with length: %d\n", session, (int) _gnutls_uint64touint32 (&record_state->sequence_number), _gnutls_packet2str (type), type, (int) sizeofdata); _gnutls_record_log ("REC[%p]: Received Packet[%d] %s(%d) with length: %d\n", session, (int) _gnutls_uint64touint32 (&record_state->sequence_number), _gnutls_packet2str (recv_type), recv_type, length); if (length > MAX_RECV_SIZE) { _gnutls_record_log ("REC[%p]: FATAL ERROR: Received packet with length: %d\n", session, length); session_unresumable (session); session_invalidate (session); gnutls_assert (); return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } /* check if we have that data into buffer. */ if ((ret = _gnutls_io_read_buffered (session, header_size + length, recv_type)) != header_size + length) { if (ret < 0 && gnutls_error_is_fatal (ret) == 0) return ret; session_unresumable (session); session_invalidate (session); gnutls_assert (); return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } /* ok now we are sure that we can read all the data - so * move on ! */ ret = _mbuffer_linearize (&session->internals.record_recv_buffer); if (ret != 0) { gnutls_assert (); return ret; } _mbuffer_get_first (&session->internals.record_recv_buffer, &data_enc); ciphertext = &data_enc.data[header_size]; ret = get_temp_recv_buffer (session, &tmp); if (ret < 0) { gnutls_assert (); return ret; } /* decrypt the data we got. */ ret = _gnutls_decrypt (session, ciphertext, length, tmp.data, tmp.size, recv_type, record_params); if (ret < 0) { session_unresumable (session); session_invalidate (session); gnutls_assert (); return ret; } _mbuffer_remove_bytes (&session->internals.record_recv_buffer, header_size + length); decrypted_length = ret; /* Check if this is a CHANGE_CIPHER_SPEC */ if (type == GNUTLS_CHANGE_CIPHER_SPEC && recv_type == GNUTLS_CHANGE_CIPHER_SPEC) { _gnutls_record_log ("REC[%p]: ChangeCipherSpec Packet was received\n", session); if ((size_t) ret != sizeofdata) { /* sizeofdata should be 1 */ gnutls_assert (); return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } memcpy (data, tmp.data, sizeofdata); return ret; } _gnutls_record_log ("REC[%p]: Decrypted Packet[%d] %s(%d) with length: %d\n", session, (int) _gnutls_uint64touint32 (&record_state->sequence_number), _gnutls_packet2str (recv_type), recv_type, decrypted_length); /* increase sequence number */ if (_gnutls_uint64pp (&record_state->sequence_number) != 0) { session_invalidate (session); gnutls_assert (); return GNUTLS_E_RECORD_LIMIT_REACHED; } ret = record_check_type (session, recv_type, type, htype, tmp.data, decrypted_length); if (ret < 0) { if (ret == GNUTLS_E_INT_RET_0) return 0; gnutls_assert (); return ret; } /* Get Application data from buffer */ if ((recv_type == type) && (type == GNUTLS_APPLICATION_DATA || type == GNUTLS_HANDSHAKE || type == GNUTLS_INNER_APPLICATION)) { ret = _gnutls_record_buffer_get (type, session, data, sizeofdata); if (ret < 0) { gnutls_assert (); return ret; } /* if the buffer just got empty */ if (_gnutls_record_buffer_get_size (type, session) == 0) { if ((ret2 = _gnutls_io_clear_peeked_data (session)) < 0) { gnutls_assert (); return ret2; } } } else { gnutls_assert (); return GNUTLS_E_UNEXPECTED_PACKET; /* we didn't get what we wanted to */ } /* (originally for) TLS 1.0 CBC protection. * Actually this code is called if we just received * an empty packet. An empty TLS packet is usually * sent to protect some vulnerabilities in the CBC mode. * In that case we go to the beginning and start reading * the next packet. */ if (ret == 0) { empty_packet++; goto begin; } 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. * * 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; }
/* 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, const void *_data, size_t sizeofdata) { uint8_t *cipher; int cipher_size; int retval, ret; int data2send_size; uint8_t headers[5]; const uint8_t *data = _data; int erecord_size = 0; opaque *erecord = NULL; /* 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.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[%x]: Sending Packet[%d] %s(%d) with length: %d\n", session, (int) _gnutls_uint64touint32 (&session->connection_state. write_sequence_number), _gnutls_packet2str (type), type, 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 (session->internals.record_send_buffer.length > 0) { ret = _gnutls_io_write_flush (session); if (ret > 0) cipher_size = ret; else cipher_size = 0; cipher = NULL; retval = session->internals.record_send_buffer_user_size; } else { /* now proceed to packet encryption */ cipher_size = data2send_size + MAX_RECORD_OVERHEAD; cipher = gnutls_malloc (cipher_size); if (cipher == NULL) { gnutls_assert (); return GNUTLS_E_MEMORY_ERROR; } cipher_size = _gnutls_encrypt (session, headers, RECORD_HEADER_SIZE, data, data2send_size, cipher, cipher_size, type, 1); if (cipher_size <= 0) { gnutls_assert (); if (cipher_size == 0) cipher_size = GNUTLS_E_ENCRYPTION_FAILED; gnutls_afree (erecord); gnutls_free (cipher); return cipher_size; /* error */ } retval = data2send_size; session->internals.record_send_buffer_user_size = data2send_size; /* increase sequence number */ if (_gnutls_uint64pp (&session->connection_state.write_sequence_number) != 0) { session_invalidate (session); gnutls_assert (); gnutls_afree (erecord); gnutls_free (cipher); return GNUTLS_E_RECORD_LIMIT_REACHED; } ret = _gnutls_io_write_buffered2 (session, erecord, erecord_size, cipher, cipher_size); gnutls_afree (erecord); gnutls_free (cipher); } if (ret != cipher_size + erecord_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[%x]: Sent Packet[%d] %s(%d) with length: %d\n", session, (int) _gnutls_uint64touint32 (&session-> connection_state. write_sequence_number), _gnutls_packet2str (type), type, cipher_size); return retval; }