]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Added gnutls_record_recv_packet() and gnutls_packet_deinit()
authorNikos Mavrogiannopoulos <nmav@redhat.com>
Fri, 6 Jun 2014 13:48:55 +0000 (15:48 +0200)
committerNikos Mavrogiannopoulos <nmav@redhat.com>
Fri, 6 Jun 2014 14:14:59 +0000 (16:14 +0200)
These functions allow for a faster variant of gnutls_record_recv(),
i.e., a variant that eliminates the data memcpy().

lib/ext/heartbeat.c
lib/gnutls_buffers.c
lib/gnutls_buffers.h
lib/gnutls_handshake.c
lib/gnutls_record.c
lib/gnutls_record.h
lib/includes/gnutls/gnutls.h.in
lib/libgnutls.map

index 1b59d39b34b47d85ec77ce7957c560b970a84380..82cba3f31b2f817bdd595c48444d7af43ef6c83b 100644 (file)
@@ -227,7 +227,7 @@ gnutls_heartbeat_ping(gnutls_session_t session, size_t data_size,
        case SHB_RECV:
                ret =
                    _gnutls_recv_int(session, GNUTLS_HEARTBEAT, -1, NULL,
-                                    0, NULL,
+                                    NULL, 0, NULL,
                                     session->internals.
                                     hb_actual_retrans_timeout_ms);
                if (ret == GNUTLS_E_HEARTBEAT_PONG_RECEIVED) {
index 1f4f3d6021e3b5f4f7eef59076d22bfd3d52947d..de8cd7d28d93b9fcc5a2afb5dd918c690d5e229c 100644 (file)
@@ -160,6 +160,40 @@ _gnutls_record_buffer_get(content_type_t type,
        return length;
 }
 
+int
+_gnutls_record_buffer_get_packet(content_type_t type, gnutls_session_t session, gnutls_packet_st *packet)
+{
+       gnutls_datum_t msg;
+       mbuffer_st *bufel;
+
+       bufel =
+           _mbuffer_head_pop_first(&session->internals.record_buffer);
+       if (bufel == NULL)
+               return
+                   gnutls_assert_val
+                   (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+       if (type != bufel->type) {
+               if (IS_DTLS(session))
+                       _gnutls_audit_log(session,
+                                         "Discarded unexpected %s (%d) packet (expecting: %s)\n",
+                                         _gnutls_packet2str(bufel->type),
+                                         (int) bufel->type,
+                                         _gnutls_packet2str(type));
+               _mbuffer_head_remove_bytes(&session->internals.
+                                          record_buffer, msg.size);
+               return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
+       }
+
+       packet->ptr = bufel;
+       packet->data.size = bufel->msg.size - bufel->mark;
+       packet->data.data = bufel->msg.data + bufel->mark;
+
+       memcpy(packet->sequence, bufel->record_sequence.i, 8);
+
+       return packet->data.size;
+}
+
 inline static void reset_errno(gnutls_session_t session)
 {
        session->internals.errnum = 0;
index 9846b2668606c5e42010df08717d505526d10f23..4e98168e6849878c2f7d7d40159b7b91c52ce933 100644 (file)
@@ -52,6 +52,9 @@ inline static size_t record_check_unprocessed(gnutls_session_t session)
 int _gnutls_record_buffer_get(content_type_t type,
                              gnutls_session_t session, uint8_t * data,
                              size_t length, uint8_t seq[8]);
+int _gnutls_record_buffer_get_packet(content_type_t type,
+                                    gnutls_session_t session,
+                                    gnutls_packet_st *packet);
 ssize_t _gnutls_io_read_buffered(gnutls_session_t, size_t n,
                                 content_type_t, unsigned int *ms);
 int _gnutls_io_clear_peeked_data(gnutls_session_t session);
index 3765efd197d70f78e89af0cecf7b777069ce6b56..a43ea9f42c738a7c2ad0e19698da3c41c0992fdf 100644 (file)
@@ -2979,7 +2979,7 @@ static int recv_handshake_final(gnutls_session_t session, int init)
 
                ret =
                    _gnutls_recv_int(session, GNUTLS_CHANGE_CIPHER_SPEC,
-                                    -1, &ch, ccs_len, NULL, tleft);
+                                    -1, NULL, &ch, ccs_len, NULL, tleft);
                if (ret <= 0) {
                        ERR("recv ChangeCipherSpec", ret);
                        gnutls_assert();
index 9943756cb52321299aa505fea167a6faa706beaa..9158743775f091d78e793b0ce3b5c24e62d2175c 100644 (file)
@@ -321,7 +321,7 @@ int gnutls_bye(gnutls_session_t session, gnutls_close_request_t how)
                        do {
                                ret =
                                    _gnutls_recv_int(session, GNUTLS_ALERT,
-                                                    -1, NULL, 0, NULL,
+                                                    -1, NULL, NULL, 0, NULL,
                                                     session->internals.
                                                     record_timeout_ms);
                        }
@@ -635,6 +635,33 @@ check_buffers(gnutls_session_t session, content_type_t type,
        return 0;
 }
 
+/* Checks and retrieves any pending data in the application data record buffers.
+ */
+static int
+check_packet_buffers(gnutls_session_t session, content_type_t type,
+                    gnutls_packet_st *packet)
+{
+       if (_gnutls_record_buffer_get_size(session) > 0) {
+               int ret;
+               ret =
+                   _gnutls_record_buffer_get_packet(type, session,
+                                                    packet);
+               if (ret < 0) {
+                       if (IS_DTLS(session)) {
+                               if (ret == GNUTLS_E_UNEXPECTED_PACKET) {
+                                       ret = GNUTLS_E_AGAIN;
+                               }
+                       }
+                       gnutls_assert();
+                       return ret;
+               }
+
+               return ret;
+       }
+
+       return 0;
+}
+
 
 
 /* Here we check if the advertized version is the one we
@@ -1360,12 +1387,13 @@ _gnutls_recv_in_buffers(gnutls_session_t session, content_type_t type,
 ssize_t
 _gnutls_recv_int(gnutls_session_t session, content_type_t type,
                 gnutls_handshake_description_t htype,
+                gnutls_packet_st *packet,
                 uint8_t * data, size_t data_size, void *seq,
                 unsigned int ms)
 {
        int ret;
 
-       if ((type != GNUTLS_ALERT && type != GNUTLS_HEARTBEAT)
+       if (packet == NULL && (type != GNUTLS_ALERT && type != GNUTLS_HEARTBEAT)
            && (data_size == 0 || data == NULL))
                return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
 
@@ -1389,23 +1417,76 @@ _gnutls_recv_int(gnutls_session_t session, content_type_t type,
        case RECV_STATE_0:
 
                _dtls_async_timer_check(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, data_size, seq);
-               if (ret != 0)
-                       return ret;
 
-               ret = _gnutls_recv_in_buffers(session, type, htype, ms);
-               if (ret < 0 && ret != GNUTLS_E_SESSION_EOF)
-                       return gnutls_assert_val(ret);
+               if (packet == NULL) {
+                       /* 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, data_size, seq);
+                       if (ret != 0)
+                               return ret;
+
+                       ret = _gnutls_recv_in_buffers(session, type, htype, ms);
+                       if (ret < 0 && ret != GNUTLS_E_SESSION_EOF)
+                               return gnutls_assert_val(ret);
 
-               return check_buffers(session, type, data, data_size, seq);
+                       return check_buffers(session, type, data, data_size, seq);
+               } else {
+                       ret = check_packet_buffers(session, type, packet);
+                       if (ret != 0)
+                               return ret;
+
+                       ret = _gnutls_recv_in_buffers(session, type, -1, ms);
+                       if (ret < 0 && ret != GNUTLS_E_SESSION_EOF)
+                               return gnutls_assert_val(ret);
+
+                       return check_packet_buffers(session, type, packet);
+               }
        default:
                return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
        }
 }
 
+/**
+ * gnutls_packet_deinit:
+ * @packet: is a pointer to a #gnutls_packet_st structure.
+ *
+ * This function will deinitialize all data associated with
+ * the received packet.
+ *
+ * Since: 3.3.5
+ **/
+void gnutls_packet_deinit(gnutls_packet_st *packet)
+{
+       gnutls_free(packet->ptr);
+}
+
+/**
+ * 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 thatn 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 must be deinitialized using gnutls_packet_deinit().
+ *
+ * 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_st *packet)
+{
+       return _gnutls_recv_int(session, GNUTLS_APPLICATION_DATA, -1, packet,
+                               NULL, 0, NULL,
+                               session->internals.record_timeout_ms);
+}
+
 /**
  * gnutls_record_send:
  * @session: is a #gnutls_session_t structure.
@@ -1586,8 +1667,8 @@ int gnutls_record_uncork(gnutls_session_t session, unsigned int flags)
 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,
+       return _gnutls_recv_int(session, GNUTLS_APPLICATION_DATA, -1, NULL,
+                               data, data_size, NULL,
                                session->internals.record_timeout_ms);
 }
 
@@ -1615,8 +1696,8 @@ 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,
+       return _gnutls_recv_int(session, GNUTLS_APPLICATION_DATA, -1, NULL,
+                               data, data_size, seq,
                                session->internals.record_timeout_ms);
 }
 
index 4af18c291dc51d2975a313924da9eed696533a96..be90acfdb010fc324b707b06150c54197d75d147 100644 (file)
@@ -45,7 +45,9 @@ _gnutls_send_int(gnutls_session_t session, content_type_t type,
 }
 
 ssize_t _gnutls_recv_int(gnutls_session_t session, content_type_t type,
-                        gnutls_handshake_description_t, uint8_t * data,
+                        gnutls_handshake_description_t, 
+                        gnutls_packet_st *packet,
+                        uint8_t * data,
                         size_t sizeofdata, void *seq, unsigned int ms);
 
 inline static unsigned max_record_recv_size(gnutls_session_t session)
index 48c6d4783e743dc3ae0006df7c456494abab29f4..77a0bc2f51224c5253dda82ed95df23666b8df33 100644 (file)
@@ -933,6 +933,19 @@ ssize_t gnutls_record_send_range(gnutls_session_t session,
                                 const gnutls_range_st * range);
 ssize_t gnutls_record_recv(gnutls_session_t session, void *data,
                           size_t data_size);
+
+typedef struct gnutls_packet_st {
+       void *ptr; /* pointer used internally by gnutls */
+       gnutls_datum_t data; /* the packet data to access */
+       unsigned char sequence[8]; /* the packet's sequence number */
+} gnutls_packet_st;
+
+ssize_t
+gnutls_record_recv_packet(gnutls_session_t session, 
+                  gnutls_packet_st *packet);
+
+void gnutls_packet_deinit(gnutls_packet_st *packet);
+
 #define gnutls_read gnutls_record_recv
 #define gnutls_write gnutls_record_send
 ssize_t gnutls_record_recv_seq(gnutls_session_t session, void *data,
index 998ae844a742a17da80cfe8d5a94e7073d29ac59..6d05679d3b94d1bb8f0e6448aa903ad412f6bc2f 100644 (file)
@@ -1009,6 +1009,8 @@ GNUTLS_3_1_0 {
        gnutls_credentials_get;
        gnutls_x509_crl_iter_crt_serial;
        gnutls_x509_crl_iter_deinit;
+       gnutls_packet_deinit;
+       gnutls_record_recv_packet;
 } GNUTLS_3_0_0;
 
 GNUTLS_FIPS140 {