/** * gnutls_alert_send: * @session: is a #gnutls_session_t type. * @level: is the level of the alert * @desc: is the alert description * * This function will send an alert to the peer in order to inform * him of something important (eg. his Certificate could not be verified). * If the alert level is Fatal then the peer is expected to close the * connection, otherwise he may ignore the alert and continue. * * The error code of the underlying record send function will be * returned, so you may also receive %GNUTLS_E_INTERRUPTED or * %GNUTLS_E_AGAIN as well. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise * an error code is returned. **/ int gnutls_alert_send(gnutls_session_t session, gnutls_alert_level_t level, gnutls_alert_description_t desc) { uint8_t data[2]; int ret; const char *name; data[0] = (uint8_t) level; data[1] = (uint8_t) desc; name = gnutls_alert_get_name((int) data[1]); if (name == NULL) name = "(unknown)"; _gnutls_record_log("REC: Sending Alert[%d|%d] - %s\n", data[0], data[1], name); if ((ret = _gnutls_send_int(session, GNUTLS_ALERT, -1, EPOCH_WRITE_CURRENT, data, 2, MBUFFER_FLUSH)) >= 0) return 0; else return ret; }
/* Send TLS/IA data. If data==NULL && sizeofdata==NULL, then the last send was interrupted for some reason, and then we try to send it again. Returns the number of bytes sent, or an error code. If this return E_AGAIN and E_INTERRUPTED, call this function again with data==NULL&&sizeofdata=0NULL until it returns successfully. */ static ssize_t _gnutls_send_inner_application (gnutls_session_t session, gnutls_ia_apptype_t msg_type, const char *data, size_t sizeofdata) { opaque *p = NULL; size_t plen = 0; ssize_t len; if (data != NULL) { plen = sizeofdata + 4; p = gnutls_malloc (plen); if (!p) { gnutls_assert (); return GNUTLS_E_MEMORY_ERROR; } *(unsigned char *) p = (unsigned char) (msg_type & 0xFF); _gnutls_write_uint24 (sizeofdata, p + 1); memcpy (p + 4, data, sizeofdata); } len = _gnutls_send_int (session, GNUTLS_INNER_APPLICATION, -1, p, plen); if (p) gnutls_free (p); return len; }
/** * gnutls_record_send: * @session: is a #gnutls_session_t structure. * @data: contains the data to send * @data_size: is the length of the data * * This function has the similar semantics with send(). The only * difference is that it accepts a GnuTLS session, and uses different * error codes. * Note that if the send buffer is full, send() will block this * function. See the send() documentation for more information. * * You can replace the default push function which is send(), by using * gnutls_transport_set_push_function(). * * If the EINTR is returned by the internal push function * 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 exact same parameters; alternatively * you could provide a %NULL pointer for data, and 0 for * size. cf. gnutls_record_get_direction(). * * Note that in DTLS this function will return the %GNUTLS_E_LARGE_PACKET * error code if the send data exceed the data MTU value - as returned * by gnutls_dtls_get_data_mtu(). The errno value EMSGSIZE * also maps to %GNUTLS_E_LARGE_PACKET. * Note that since 3.2.13 this function can be called under cork in DTLS * mode, and will refuse to send data over the MTU size by returning * %GNUTLS_E_LARGE_PACKET. * * Returns: The number of bytes sent, or a negative error code. The * number of bytes sent might be less than @data_size. The maximum * number of bytes this function can send in a single call depends * on the negotiated maximum record size. **/ ssize_t gnutls_record_send(gnutls_session_t session, const void *data, size_t data_size) { if (session->internals.record_flush_mode == RECORD_FLUSH) { return _gnutls_send_int(session, GNUTLS_APPLICATION_DATA, -1, EPOCH_WRITE_CURRENT, data, data_size, MBUFFER_FLUSH); } else { /* GNUTLS_CORKED */ int ret; if (IS_DTLS(session)) { if (data_size + session->internals.record_presend_buffer.length > gnutls_dtls_get_data_mtu(session)) { return gnutls_assert_val(GNUTLS_E_LARGE_PACKET); } } ret = _gnutls_buffer_append_data(&session->internals. record_presend_buffer, data, data_size); if (ret < 0) return gnutls_assert_val(ret); return data_size; } }
/** * gnutls_record_send - sends to the peer the specified data * @session: is a #gnutls_session_t structure. * @data: contains the data to send * @sizeofdata: is the length of the data * * This function has the similar semantics with send(). The only * difference is that is accepts a GNUTLS session, and uses different * error codes. * * Note that if the send buffer is full, send() will block this * function. See the send() documentation for full information. You * can replace the default push function by using * gnutls_transport_set_ptr2() with a call to send() with a * MSG_DONTWAIT flag if blocking is a problem. * * If the EINTR is returned by the internal push function (the * default is send()} 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 parameters; alternatively * you could provide a %NULL pointer for data, and 0 for * size. cf. gnutls_record_get_direction(). * * Returns the number of bytes sent, or a negative error code. The * number of bytes sent might be less than @sizeofdata. The maximum * number of bytes this function can send in a single call depends on * the negotiated maximum record size. **/ ssize_t gnutls_record_send (gnutls_session_t session, const void *data, size_t sizeofdata) { return _gnutls_send_int (session, GNUTLS_APPLICATION_DATA, -1, data, sizeofdata); }
/** * gnutls_record_send: * @session: is a #gnutls_session_t structure. * @data: contains the data to send * @data_size: is the length of the data * * This function has the similar semantics with send(). The only * difference is that it accepts a GnuTLS session, and uses different * error codes. * Note that if the send buffer is full, send() will block this * function. See the send() documentation for full information. You * can replace the default push function by using * gnutls_transport_set_ptr2() with a call to send() with a * MSG_DONTWAIT flag if blocking is a problem. * If the EINTR is returned by the internal push function (the * default is send()) 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 parameters; alternatively * you could provide a %NULL pointer for data, and 0 for * size. cf. gnutls_record_get_direction(). The errno value EMSGSIZE * maps to %GNUTLS_E_LARGE_PACKET. * * Returns: The number of bytes sent, or a negative error code. The * number of bytes sent might be less than @data_size. The maximum * number of bytes this function can send in a single call depends * on the negotiated maximum record size. **/ ssize_t gnutls_record_send (gnutls_session_t session, const void *data, size_t data_size) { return _gnutls_send_int (session, GNUTLS_APPLICATION_DATA, -1, EPOCH_WRITE_CURRENT, data, data_size, MBUFFER_FLUSH); }
/* This function writes the data that are left in the * Handshake write buffer (ie. because the previous write was * interrupted. * */ ssize_t _gnutls_handshake_io_write_flush (gnutls_session_t session) { mbuffer_head_st *const send_buffer = &session->internals.handshake_send_buffer; gnutls_datum_t msg; int ret; uint16_t epoch; ssize_t total = 0; mbuffer_st *cur; _gnutls_write_log ("HWRITE FLUSH: %d bytes in buffer.\n", (int) send_buffer->byte_length); if (IS_DTLS(session)) return _dtls_transmit(session); for (cur = _mbuffer_head_get_first (send_buffer, &msg); cur != NULL; cur = _mbuffer_head_get_first (send_buffer, &msg)) { epoch = cur->epoch; ret = _gnutls_send_int (session, cur->type, cur->htype, epoch, msg.data, msg.size, 0); if (ret >= 0) { total += ret; ret = _mbuffer_head_remove_bytes (send_buffer, ret); if (ret == 1) _gnutls_epoch_refcount_dec(session, epoch); _gnutls_write_log ("HWRITE: wrote %d bytes, %d bytes left.\n", ret, (int) send_buffer->byte_length); } else { _gnutls_write_log ("HWRITE error: code %d, %d bytes left.\n", ret, (int) send_buffer->byte_length); gnutls_assert (); return ret; } } return _gnutls_io_write_flush (session); }
/* This function is to be called if the handshake was successfully * completed. This sends a Change Cipher Spec packet to the peer. */ ssize_t _gnutls_send_change_cipher_spec (gnutls_session_t session, int again) { static const opaque data[1] = { GNUTLS_TYPE_CHANGE_CIPHER_SPEC }; _gnutls_handshake_log ("REC[%x]: Sent ChangeCipherSpec\n", session); if (again == 0) return _gnutls_send_int (session, GNUTLS_CHANGE_CIPHER_SPEC, -1, data, 1); else { return _gnutls_io_write_flush (session); } }
/** * gnutls_record_send: * @session: is a #gnutls_session_t structure. * @data: contains the data to send * @data_size: is the length of the data * * This function has the similar semantics with send(). The only * difference is that it accepts a GnuTLS session, and uses different * error codes. * Note that if the send buffer is full, send() will block this * function. See the send() documentation for full information. You * can replace the default push function by using * gnutls_transport_set_ptr2() with a call to send() with a * MSG_DONTWAIT flag if blocking is a problem. * If the EINTR is returned by the internal push function (the * default is send()) 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 parameters; alternatively * you could provide a %NULL pointer for data, and 0 for * size. cf. gnutls_record_get_direction(). * * Note that in DTLS this function will return the %GNUTLS_E_LARGE_PACKET * error code if the send data exceed the data MTU value - as returned * by gnutls_dtls_get_data_mtu(). The errno value EMSGSIZE * also maps to %GNUTLS_E_LARGE_PACKET. * * Returns: The number of bytes sent, or a negative error code. The * number of bytes sent might be less than @data_size. The maximum * number of bytes this function can send in a single call depends * on the negotiated maximum record size. **/ ssize_t gnutls_record_send (gnutls_session_t session, const void *data, size_t data_size) { if (session->internals.record_flush_mode == RECORD_FLUSH) { return _gnutls_send_int (session, GNUTLS_APPLICATION_DATA, -1, EPOCH_WRITE_CURRENT, data, data_size, MBUFFER_FLUSH); } else /* GNUTLS_CORKED */ { int ret; ret = _gnutls_buffer_append_data(&session->internals.record_presend_buffer, data, data_size); if (ret < 0) return gnutls_assert_val(ret); return data_size; } }
/* * Sends heartbeat data. */ static int heartbeat_send_data(gnutls_session_t session, const void *data, size_t data_size, uint8_t type) { int ret, pos; uint8_t *response; response = gnutls_malloc(1 + 2 + data_size + DEFAULT_PAYLOAD_SIZE); if (response == NULL) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); pos = 0; response[pos++] = type; _gnutls_write_uint16(data_size, &response[pos]); pos += 2; memcpy(&response[pos], data, data_size); pos += data_size; ret = gnutls_rnd(GNUTLS_RND_NONCE, &response[pos], DEFAULT_PAYLOAD_SIZE); if (ret < 0) { gnutls_assert(); goto cleanup; } pos += DEFAULT_PAYLOAD_SIZE; ret = _gnutls_send_int(session, GNUTLS_HEARTBEAT, -1, EPOCH_WRITE_CURRENT, response, pos, MBUFFER_FLUSH); cleanup: gnutls_free(response); return ret; }
/* This function fragments and transmits a previously buffered * outgoing message. It accepts mtu_data which is a buffer to * be reused (should be set to NULL initially). */ static inline int transmit_message (gnutls_session_t session, mbuffer_st *bufel, uint8_t **buf) { uint8_t *data, *mtu_data; int ret = 0; unsigned int offset, frag_len, data_size; const unsigned int mtu = gnutls_dtls_get_data_mtu(session) - DTLS_HANDSHAKE_HEADER_SIZE; if (bufel->type == GNUTLS_CHANGE_CIPHER_SPEC) { _gnutls_dtls_log ("DTLS[%p]: Sending Packet[%u] fragment %s(%d)\n", session, bufel->handshake_sequence, _gnutls_handshake2str (bufel->htype), bufel->htype); return _gnutls_send_int (session, bufel->type, -1, bufel->epoch, _mbuffer_get_uhead_ptr(bufel), _mbuffer_get_uhead_size(bufel), 0); } if (*buf == NULL) *buf = gnutls_malloc(mtu + DTLS_HANDSHAKE_HEADER_SIZE); if (*buf == NULL) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); mtu_data = *buf; data = _mbuffer_get_udata_ptr( bufel); data_size = _mbuffer_get_udata_size(bufel); /* Write fixed headers */ /* Handshake type */ mtu_data[0] = (uint8_t) bufel->htype; /* Total length */ _gnutls_write_uint24 (data_size, &mtu_data[1]); /* Handshake sequence */ _gnutls_write_uint16 (bufel->handshake_sequence, &mtu_data[4]); /* Chop up and send handshake message into mtu-size pieces. */ for (offset=0; offset <= data_size; offset += mtu) { /* Calculate fragment length */ if(offset + mtu > data_size) frag_len = data_size - offset; else frag_len = mtu; /* Fragment offset */ _gnutls_write_uint24 (offset, &mtu_data[6]); /* Fragment length */ _gnutls_write_uint24 (frag_len, &mtu_data[9]); memcpy (&mtu_data[DTLS_HANDSHAKE_HEADER_SIZE], data+offset, frag_len); _gnutls_dtls_log ("DTLS[%p]: Sending Packet[%u] fragment %s(%d) with " "length: %u, offset: %u, fragment length: %u\n", session, bufel->handshake_sequence, _gnutls_handshake2str (bufel->htype), bufel->htype, data_size, offset, frag_len); ret = _gnutls_send_int (session, bufel->type, bufel->htype, bufel->epoch, mtu_data, DTLS_HANDSHAKE_HEADER_SIZE + frag_len, 0); if (ret < 0) { gnutls_assert(); break; } } return ret; }
/* This is a send function for the gnutls handshake * protocol. Just makes sure that all data have been sent. */ ssize_t _gnutls_handshake_io_send_int (gnutls_session_t session, content_type_t type, gnutls_handshake_description_t htype, const void *iptr, size_t n) { size_t left; ssize_t ret = 0; const opaque *ptr; ssize_t retval = 0; ptr = iptr; if (session->internals.handshake_send_buffer.length > 0 && ptr == NULL && n == 0) { /* resuming previously interrupted write */ gnutls_assert (); ret = _gnutls_buffer_get (&session->internals.handshake_send_buffer, &ptr, &n); if (ret < 0) { gnutls_assert (); return retval; } type = session->internals.handshake_send_buffer_type; htype = session->internals.handshake_send_buffer_htype; } else if (session->internals.handshake_send_buffer.length > 0) { gnutls_assert (); return GNUTLS_E_INTERNAL_ERROR; } #ifdef WRITE_DEBUG else { size_t sum = 0, x, j; _gnutls_write_log ("HWRITE: will write %d bytes to %d.\n", n, gnutls_transport_get_ptr (session)); for (x = 0; x < ((n) / 16) + 1; x++) { if (sum > n) break; _gnutls_write_log ("%.4x - ", x); for (j = 0; j < 16; j++) { if (sum < n) { _gnutls_write_log ("%.2x ", ((unsigned char *) ptr)[sum++]); } else break; } _gnutls_write_log ("\n"); } _gnutls_write_log ("\n"); } #endif if (n == 0) { /* if we have no data to send */ gnutls_assert (); return 0; } else if (ptr == NULL) { gnutls_assert (); return GNUTLS_E_INTERNAL_ERROR; } left = n; while (left > 0) { ret = _gnutls_send_int (session, type, htype, &ptr[n - left], left); if (ret <= 0) { if (ret == 0) { gnutls_assert (); ret = GNUTLS_E_INTERNAL_ERROR; } if (left > 0 && (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN)) { gnutls_assert (); retval = _gnutls_buffer_insert (&session->internals. handshake_send_buffer, &ptr[n - left], left); if (retval < 0) { gnutls_assert (); return retval; } session->internals.handshake_send_buffer_prev_size += n - left; session->internals.handshake_send_buffer_type = type; session->internals.handshake_send_buffer_htype = htype; } else { session->internals.handshake_send_buffer_prev_size = 0; session->internals.handshake_send_buffer.length = 0; } gnutls_assert (); return ret; } left -= ret; } retval = n + session->internals.handshake_send_buffer_prev_size; session->internals.handshake_send_buffer.length = 0; session->internals.handshake_send_buffer_prev_size = 0; return retval; }