/* Receive TLS/IA data. Store received TLS/IA message type in *MSG_TYPE, and the data in DATA of max SIZEOFDATA size. Return the number of bytes read, or an error code. */ static ssize_t _gnutls_recv_inner_application (gnutls_session_t session, gnutls_ia_apptype_t * msg_type, opaque * data, size_t sizeofdata) { ssize_t len; uint32_t len24; opaque pkt[4]; len = _gnutls_recv_int (session, GNUTLS_INNER_APPLICATION, -1, pkt, 4); if (len != 4) { gnutls_assert (); return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } *msg_type = pkt[0]; len24 = _gnutls_read_uint24 (&pkt[1]); if (*msg_type != GNUTLS_IA_APPLICATION_PAYLOAD && len24 != CHECKSUM_SIZE) { gnutls_assert (); return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } if (sizeofdata < len24) { /* XXX push back pkt to IA buffer? */ gnutls_assert (); return GNUTLS_E_SHORT_MEMORY_BUFFER; } if (len24 > 0) { uint32_t tmplen = len24; len24 = _gnutls_recv_int (session, GNUTLS_INNER_APPLICATION, -1, data, tmplen); if (len24 != tmplen) { gnutls_assert (); /* XXX Correct? */ return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } } return len24; }
/** * gnutls_bye: * @session: is a #gnutls_session_t structure. * @how: is an integer * * Terminates the current TLS/SSL connection. The connection should * have been initiated using gnutls_handshake(). @how should be one * of %GNUTLS_SHUT_RDWR, %GNUTLS_SHUT_WR. * * In case of %GNUTLS_SHUT_RDWR the TLS session gets * terminated and further receives and sends will be disallowed. If * the return value is zero you may continue using the underlying * transport layer. %GNUTLS_SHUT_RDWR sends an alert containing a close * request and waits for the peer to reply with the same message. * * In case of %GNUTLS_SHUT_WR the TLS session gets terminated * and further sends will be disallowed. In order to reuse the * connection you should wait for an EOF from the peer. * %GNUTLS_SHUT_WR sends an alert containing a close request. * * Note that not all implementations will properly terminate a TLS * connection. Some of them, usually for performance reasons, will * terminate only the underlying transport layer, and thus not * distinguishing between a malicious party prematurely terminating * the connection and normal termination. * * This function may also return %GNUTLS_E_AGAIN or * %GNUTLS_E_INTERRUPTED; cf. gnutls_record_get_direction(). * * Returns: %GNUTLS_E_SUCCESS on success, or an error code, see * function documentation for entire semantics. **/ int gnutls_bye (gnutls_session_t session, gnutls_close_request_t how) { int ret = 0; switch (STATE) { case STATE0: case STATE60: ret = _gnutls_io_write_flush (session); STATE = STATE60; if (ret < 0) { gnutls_assert (); return ret; } case STATE61: ret = gnutls_alert_send (session, GNUTLS_AL_WARNING, GNUTLS_A_CLOSE_NOTIFY); STATE = STATE61; if (ret < 0) { gnutls_assert (); return ret; } case STATE62: STATE = STATE62; if (how == GNUTLS_SHUT_RDWR) { do { ret = _gnutls_recv_int (session, GNUTLS_ALERT, -1, NULL, 0, NULL, session->internals.record_timeout_ms); } while (ret == GNUTLS_E_GOT_APPLICATION_DATA); if (ret >= 0) session->internals.may_not_read = 1; if (ret < 0) { gnutls_assert (); return ret; } } STATE = STATE62; break; default: gnutls_assert (); return GNUTLS_E_INTERNAL_ERROR; } STATE = STATE0; session->internals.may_not_write = 1; return 0; }
/** * gnutls_record_recv: * @session: is a #gnutls_session_t structure. * @data: the buffer that the data will be read into * @data_size: the number of requested bytes * * This function has the similar semantics with recv(). The only * difference is that it accepts a GnuTLS session, and uses different * error codes. * In the special case that a server requests a renegotiation, the * client may receive an error code of %GNUTLS_E_REHANDSHAKE. This * message may be simply ignored, replied with an alert * %GNUTLS_A_NO_RENEGOTIATION, or replied with a new handshake, * depending on the client's will. * If %EINTR is returned by the internal push function (the default * is recv()) then %GNUTLS_E_INTERRUPTED will be returned. If * %GNUTLS_E_INTERRUPTED or %GNUTLS_E_AGAIN is returned, you must * call this function again to get the data. See also * gnutls_record_get_direction(). * A server may also receive %GNUTLS_E_REHANDSHAKE when a client has * initiated a handshake. In that case the server can only initiate a * handshake or terminate the connection. * * Returns: The number of bytes received and zero on EOF (for stream * connections). A negative error code is returned in case of an error. * The number of bytes received might be less than the requested @data_size. **/ ssize_t gnutls_record_recv(gnutls_session_t session, void *data, size_t data_size) { return _gnutls_recv_int(session, GNUTLS_APPLICATION_DATA, -1, NULL, data, data_size, NULL, session->internals.record_timeout_ms); }
/** * gnutls_record_recv_seq: * @session: is a #gnutls_session_t structure. * @data: the buffer that the data will be read into * @data_size: the number of requested bytes * @seq: is the packet's 64-bit sequence number. Should have space for 8 bytes. * * This function is the same as gnutls_record_recv(), except that * it returns in addition to data, the sequence number of the data. * This is useful in DTLS where record packets might be received * out-of-order. The returned 8-byte sequence number is an * integer in big-endian format and should be * treated as a unique message identification. * * Returns: The number of bytes received and zero on EOF. A negative * error code is returned in case of an error. The number of bytes * received might be less than @data_size. * * Since: 3.0 **/ ssize_t gnutls_record_recv_seq (gnutls_session_t session, void *data, size_t data_size, unsigned char *seq) { return _gnutls_recv_int (session, GNUTLS_APPLICATION_DATA, -1, data, data_size, seq, session->internals.record_timeout_ms); }
/** * gnutls_record_recv_packet: * @session: is a #gnutls_session_t structure. * @packet: the structure that will hold the packet data * * This is a lower-level function than gnutls_record_recv() and allows * to directly receive the whole decrypted packet. That avoids a * memory copy, and is mostly applicable to applications seeking high * performance. * * The received packet is accessed using gnutls_packet_get() and * must be deinitialized using gnutls_packet_deinit(). The returned * packet will be %NULL if the return value is zero (EOF). * * Returns: The number of bytes received and zero on EOF (for stream * connections). A negative error code is returned in case of an error. * * Since: 3.3.5 **/ ssize_t gnutls_record_recv_packet(gnutls_session_t session, gnutls_packet_t *packet) { return _gnutls_recv_int(session, GNUTLS_APPLICATION_DATA, -1, packet, NULL, 0, NULL, session->internals.record_timeout_ms); }
/** * gnutls_record_recv: * @session: is a #gnutls_session_t structure. * @data: the buffer that the data will be read into * @data_size: the number of requested bytes * * This function has the similar semantics with recv(). The only * difference is that it accepts a GnuTLS session, and uses different * error codes. * In the special case that a server requests a renegotiation, the * client may receive an error code of %GNUTLS_E_REHANDSHAKE. This * message may be simply ignored, replied with an alert * %GNUTLS_A_NO_RENEGOTIATION, or replied with a new handshake, * depending on the client's will. * If %EINTR is returned by the internal push function (the default * is recv()) then %GNUTLS_E_INTERRUPTED will be returned. If * %GNUTLS_E_INTERRUPTED or %GNUTLS_E_AGAIN is returned, you must * call this function again to get the data. See also * gnutls_record_get_direction(). * A server may also receive %GNUTLS_E_REHANDSHAKE when a client has * initiated a handshake. In that case the server can only initiate a * handshake or terminate the connection. * * Returns: The number of bytes received and zero on EOF (for stream * connections). A negative error code is returned in case of an error. * The number of bytes received might be less than the requested @data_size. **/ ssize_t gnutls_record_recv (gnutls_session_t session, void *data, size_t data_size) { return _gnutls_recv_int (session, GNUTLS_APPLICATION_DATA, -1, data, data_size, NULL); }
/* This is a receive function for the gnutls handshake * protocol. Makes sure that we have received all data. */ ssize_t _gnutls_handshake_io_recv_int (gnutls_session_t session, content_type_t type, gnutls_handshake_description_t htype, void *iptr, size_t sizeOfPtr) { size_t left; ssize_t i; opaque *ptr; size_t dsize; ptr = iptr; left = sizeOfPtr; if (sizeOfPtr == 0 || iptr == NULL) { gnutls_assert (); return GNUTLS_E_INVALID_REQUEST; } if (session->internals.handshake_recv_buffer.length > 0) { /* if we have already received some data */ if (sizeOfPtr <= session->internals.handshake_recv_buffer.length) { /* if requested less data then return it. */ gnutls_assert (); memcpy (iptr, session->internals.handshake_recv_buffer.data, sizeOfPtr); session->internals.handshake_recv_buffer.length -= sizeOfPtr; memmove (session->internals.handshake_recv_buffer.data, &session->internals.handshake_recv_buffer. data[sizeOfPtr], session->internals.handshake_recv_buffer.length); return sizeOfPtr; } gnutls_assert (); memcpy (iptr, session->internals.handshake_recv_buffer.data, session->internals.handshake_recv_buffer.length); htype = session->internals.handshake_recv_buffer_htype; type = session->internals.handshake_recv_buffer_type; left -= session->internals.handshake_recv_buffer.length; session->internals.handshake_recv_buffer.length = 0; } while (left > 0) { dsize = sizeOfPtr - left; i = _gnutls_recv_int (session, type, htype, &ptr[dsize], left); if (i < 0) { if (dsize > 0 && (i == GNUTLS_E_INTERRUPTED || i == GNUTLS_E_AGAIN)) { gnutls_assert (); session->internals.handshake_recv_buffer.data = gnutls_realloc_fast (session->internals. handshake_recv_buffer.data, dsize); if (session->internals.handshake_recv_buffer.data == NULL) { gnutls_assert (); return GNUTLS_E_MEMORY_ERROR; } memcpy (session->internals.handshake_recv_buffer.data, iptr, dsize); session->internals.handshake_recv_buffer_htype = htype; session->internals.handshake_recv_buffer_type = type; session->internals.handshake_recv_buffer.length = dsize; } else session->internals.handshake_recv_buffer.length = 0; gnutls_assert (); return i; } else { if (i == 0) break; /* EOF */ } left -= i; } session->internals.handshake_recv_buffer.length = 0; return sizeOfPtr - left; }
/** * gnutls_heartbeat_ping: * @session: is a #gnutls_session_t structure. * @data_size: is the length of the ping payload. * @max_tries: if flags is %GNUTLS_HEARTBEAT_WAIT then this sets the number of retransmissions. Use zero for indefinite (until timeout). * @flags: if %GNUTLS_HEARTBEAT_WAIT then wait for pong or timeout instead of returning immediately. * * This function sends a ping to the peer. If the @flags is set * to %GNUTLS_HEARTBEAT_WAIT then it waits for a reply from the peer. * * Note that it is highly recommended to use this function with the * flag %GNUTLS_HEARTBEAT_WAIT, or you need to handle retransmissions * and timeouts manually. * * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. * * Since: 3.1.2 **/ int gnutls_heartbeat_ping(gnutls_session_t session, size_t data_size, unsigned int max_tries, unsigned int flags) { int ret; unsigned int retries = 1, diff; struct timespec now; if (data_size > MAX_HEARTBEAT_LENGTH) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); if (gnutls_heartbeat_allowed (session, GNUTLS_HB_LOCAL_ALLOWED_TO_SEND) == 0) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); /* resume previous call if interrupted */ if (session->internals.record_send_buffer.byte_length > 0 && session->internals.record_send_buffer.head != NULL && session->internals.record_send_buffer.head->type == GNUTLS_HEARTBEAT) return _gnutls_io_write_flush(session); switch (session->internals.hb_state) { case SHB_SEND1: if (data_size > DEFAULT_PAYLOAD_SIZE) data_size -= DEFAULT_PAYLOAD_SIZE; else data_size = 0; _gnutls_buffer_reset(&session->internals.hb_local_data); ret = _gnutls_buffer_resize(&session->internals. hb_local_data, data_size); if (ret < 0) return gnutls_assert_val(ret); ret = _gnutls_rnd(GNUTLS_RND_NONCE, session->internals.hb_local_data.data, data_size); if (ret < 0) return gnutls_assert_val(ret); gettime(&session->internals.hb_ping_start); session->internals.hb_local_data.length = data_size; session->internals.hb_state = SHB_SEND2; case SHB_SEND2: session->internals.hb_actual_retrans_timeout_ms = session->internals.hb_retrans_timeout_ms; retry: ret = heartbeat_send_data(session, session->internals.hb_local_data. data, session->internals.hb_local_data. length, HEARTBEAT_REQUEST); if (ret < 0) return gnutls_assert_val(ret); gettime(&session->internals.hb_ping_sent); if (!(flags & GNUTLS_HEARTBEAT_WAIT)) { session->internals.hb_state = SHB_SEND1; break; } session->internals.hb_state = SHB_RECV; case SHB_RECV: ret = _gnutls_recv_int(session, GNUTLS_HEARTBEAT, -1, NULL, 0, NULL, session->internals. hb_actual_retrans_timeout_ms); if (ret == GNUTLS_E_HEARTBEAT_PONG_RECEIVED) { session->internals.hb_state = SHB_SEND1; break; } else if (ret == GNUTLS_E_TIMEDOUT) { retries++; if (max_tries > 0 && retries > max_tries) { session->internals.hb_state = SHB_SEND1; return gnutls_assert_val(ret); } gettime(&now); diff = timespec_sub_ms(&now, &session->internals. hb_ping_start); if (diff > session->internals.hb_total_timeout_ms) { session->internals.hb_state = SHB_SEND1; return gnutls_assert_val(GNUTLS_E_TIMEDOUT); } session->internals.hb_actual_retrans_timeout_ms *= 2; session->internals.hb_actual_retrans_timeout_ms %= MAX_DTLS_TIMEOUT; session->internals.hb_state = SHB_SEND2; goto retry; } else if (ret < 0) { session->internals.hb_state = SHB_SEND1; return gnutls_assert_val(ret); } } return 0; }
/* This is a receive function for the gnutls handshake * protocol. Makes sure that we have received all data. */ ssize_t _gnutls_handshake_io_recv_int (gnutls_session_t session, content_type_t type, gnutls_handshake_description_t htype, void *iptr, size_t sizeOfPtr) { size_t left; ssize_t i; opaque *ptr; size_t dsize; ptr = iptr; left = sizeOfPtr; if (sizeOfPtr == 0 || iptr == NULL) { gnutls_assert (); return GNUTLS_E_INVALID_REQUEST; } if (session->internals.handshake_recv_buffer.length > 0) { size_t tmp; /* if we have already received some data */ if (sizeOfPtr <= session->internals.handshake_recv_buffer.length) { /* if requested less data then return it. */ gnutls_assert (); tmp = sizeOfPtr; _gnutls_string_get_data( &session->internals.handshake_recv_buffer, iptr, &tmp); return tmp; } gnutls_assert (); tmp = sizeOfPtr; _gnutls_string_get_data( &session->internals.handshake_recv_buffer, iptr, &tmp); left -= tmp; htype = session->internals.handshake_recv_buffer_htype; type = session->internals.handshake_recv_buffer_type; } while (left > 0) { dsize = sizeOfPtr - left; i = _gnutls_recv_int (session, type, htype, &ptr[dsize], left); if (i < 0) { if (dsize > 0 && (i == GNUTLS_E_INTERRUPTED || i == GNUTLS_E_AGAIN)) { gnutls_assert (); _gnutls_buffer_append (&session->internals.handshake_recv_buffer, iptr, dsize); session->internals.handshake_recv_buffer_htype = htype; session->internals.handshake_recv_buffer_type = type; } gnutls_assert (); return i; } else { if (i == 0) break; /* EOF */ } left -= i; } session->internals.handshake_recv_buffer.length = 0; return sizeOfPtr - left; }