/* * Copyright (C) 2000,2001,2002,2003 Nikos Mavroyanopoulos * * This file is part of GNUTLS. * * The GNUTLS library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* This is the only file that uses the berkeley sockets API. * * Also holds all the buffering code used in gnutls. * The buffering code works as: * * RECORD LAYER: * 1. uses a buffer to hold data (application/handshake), * we got but they were not requested, yet. * (see gnutls_record_buffer_put(), gnutls_record_buffer_get_size() etc.) * * 2. uses a buffer to hold data that were incomplete (ie the read/write * was interrupted) * (see _gnutls_io_read_buffered(), _gnutls_io_write_buffered() etc.) * * HANDSHAKE LAYER: * 1. Uses a buffer to hold data that was not sent or received * complete. (E.g. sent 10 bytes of a handshake packet that is 20 bytes * long). * (see _gnutls_handshake_send_int(), _gnutls_handshake_recv_int()) * * 2. Uses buffer to hold the last received handshake message. * (see _gnutls_handshake_buffer_put() etc.) * */ #include #include #include #include #include #ifdef HAVE_ERRNO_H # include #endif #ifndef EAGAIN # define EAGAIN EWOULDBLOCK #endif inline static int RET( int err) { if (err==EAGAIN) return GNUTLS_E_AGAIN; return GNUTLS_E_INTERRUPTED; } #ifdef IO_DEBUG # include #endif /* Buffers received packets of type APPLICATION DATA and * HANDSHAKE DATA. */ int _gnutls_record_buffer_put(ContentType type, gnutls_session session, char *data, size_t length) { if (length==0) return 0; switch( type) { case GNUTLS_APPLICATION_DATA: if ( _gnutls_buffer_append( &session->internals.application_data_buffer, data, length) < 0) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } _gnutls_buffers_log( "BUF[REC]: Inserted %d bytes of Data(%d)\n", length, type); break; case GNUTLS_HANDSHAKE: if ( _gnutls_buffer_append( &session->internals.handshake_data_buffer, data, length) < 0) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } _gnutls_buffers_log( "BUF[HSK]: Inserted %d bytes of Data(%d)\n", length, type); break; default: gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } return 0; } int _gnutls_record_buffer_get_size(ContentType type, gnutls_session session) { switch( type) { case GNUTLS_APPLICATION_DATA: return session->internals.application_data_buffer.length; case GNUTLS_HANDSHAKE: return session->internals.handshake_data_buffer.length; default: return GNUTLS_E_INVALID_REQUEST; } } /** * gnutls_record_check_pending - checks if there are any data to receive in gnutls buffers. * @session: is a &gnutls_session structure. * * This function checks if there are any data to receive * in the gnutls buffers. Returns the size of that data or 0. * Notice that you may also use select() to check for data in * a TCP connection, instead of this function. * (gnutls leaves some data in the tcp buffer in order for select * to work). **/ size_t gnutls_record_check_pending(gnutls_session session) { return _gnutls_record_buffer_get_size(GNUTLS_APPLICATION_DATA, session); } int _gnutls_record_buffer_get(ContentType type, gnutls_session session, char *data, size_t length) { if (length==0 || data==NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } switch(type) { case GNUTLS_APPLICATION_DATA: if (length > session->internals.application_data_buffer.length) { length = session->internals.application_data_buffer.length; } _gnutls_buffers_log( "BUFFER[REC][AD]: Read %d bytes of Data(%d)\n", length, type); session->internals.application_data_buffer.length -= length; memcpy(data, session->internals.application_data_buffer.data, length); /* overwrite buffer */ memmove(session->internals.application_data_buffer.data, &session->internals.application_data_buffer.data[length], session->internals.application_data_buffer.length); /* we do no longer realloc the application_data_buffer.data, * since it serves no practical reason. It also decreases * performance. */ break; case GNUTLS_HANDSHAKE: if (length > session->internals.handshake_data_buffer.length) { length = session->internals.handshake_data_buffer.length; } _gnutls_buffers_log( "BUF[REC][HD]: Read %d bytes of Data(%d)\n", length, type); session->internals.handshake_data_buffer.length -= length; memcpy(data, session->internals.handshake_data_buffer.data, length); /* overwrite buffer */ memmove(session->internals.handshake_data_buffer.data, &session->internals.handshake_data_buffer.data[length], session->internals.handshake_data_buffer.length); break; default: gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } return length; } /* This function is like read. But it does not return -1 on error. * It does return gnutls_errno instead. * * Flags are only used if the default recv() function is being used. */ static ssize_t _gnutls_read( gnutls_session session, void *iptr, size_t sizeOfPtr, int flags) { size_t left; ssize_t i=0; char *ptr = iptr; uint j,x, sum=0; gnutls_transport_ptr fd = session->internals.transport_recv_ptr; session->internals.direction = 0; left = sizeOfPtr; while (left > 0) { if (session->internals._gnutls_pull_func==NULL) i = recv( (int)fd, &ptr[sizeOfPtr-left], left, flags); else i = session->internals._gnutls_pull_func(fd, &ptr[sizeOfPtr-left], left); if (i < 0) { _gnutls_read_log( "READ: %d returned from %d, errno=%d\n", i, fd, errno); if (errno == EAGAIN || errno == EINTR) { if (sizeOfPtr-left > 0) { _gnutls_read_log( "READ: returning %d bytes from %d\n", sizeOfPtr-left, fd); goto finish; } gnutls_assert(); return RET(errno); } else { gnutls_assert(); return GNUTLS_E_PULL_ERROR; } } else { _gnutls_read_log( "READ: Got %d bytes from %d\n", i, fd); if (i == 0) break; /* EOF */ } left -= i; } finish: if (_gnutls_log_level >= 7) { char line[128]; char tmp[16]; _gnutls_read_log( "READ: read %d bytes from %d\n", (sizeOfPtr-left), fd); for (x=0;x<((sizeOfPtr-left)/16)+1;x++) { line[0] = 0; sprintf( tmp, "%.4x - ",x); _gnutls_str_cat( line, sizeof(line), tmp); for (j=0;j<16;j++) { if (sum<(sizeOfPtr-left)) { sprintf( tmp, "%.2x ", ((unsigned char*)ptr)[sum++]); _gnutls_str_cat( line, sizeof(line), tmp); } } _gnutls_read_log( "%s\n", line); } } return (sizeOfPtr - left); } #define RCVLOWAT session->internals.lowat /* This function is only used with berkeley style sockets. * Clears the peeked data (read with MSG_PEEK). */ int _gnutls_io_clear_peeked_data( gnutls_session session) { char *peekdata; int ret, sum; if (session->internals.have_peeked_data==0 || RCVLOWAT==0) return 0; peekdata = gnutls_alloca( RCVLOWAT); if (peekdata==NULL) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } /* this was already read by using MSG_PEEK - so it shouldn't fail */ sum = 0; do { /* we need this to finish now */ ret = _gnutls_read( session, peekdata, RCVLOWAT-sum, 0); if (ret > 0) sum+=ret; } while( ret==GNUTLS_E_INTERRUPTED || ret==GNUTLS_E_AGAIN || sum < RCVLOWAT); gnutls_afree(peekdata); if (ret < 0) { gnutls_assert(); return ret; } session->internals.have_peeked_data=0; return 0; } void _gnutls_io_clear_read_buffer( gnutls_session session) { session->internals.record_recv_buffer.length = 0; } /* This function is like recv(with MSG_PEEK). But it does not return -1 on error. * It does return gnutls_errno instead. * This function reads data from the socket and keeps them in a buffer, of up to * MAX_RECV_SIZE. * * This is not a general purpose function. It returns EXACTLY the data requested, * which are stored in a local (in the session) buffer. A pointer (iptr) to this buffer is returned. * */ ssize_t _gnutls_io_read_buffered( gnutls_session session, opaque **iptr, size_t sizeOfPtr, ContentType recv_type) { ssize_t ret=0, ret2=0; size_t min; int buf_pos; char *buf; int recvlowat; int recvdata, alloc_size; *iptr = session->internals.record_recv_buffer.data; if ( sizeOfPtr > MAX_RECV_SIZE || sizeOfPtr == 0) { gnutls_assert(); /* internal error */ return GNUTLS_E_INVALID_REQUEST; } /* If an external pull function is used, then do not leave * any data into the kernel buffer. */ if (session->internals._gnutls_pull_func != NULL) { recvlowat = 0; } else { /* leave peeked data to the kernel space only if application data * is received and we don't have any peeked * data in gnutls session. */ if ( recv_type != GNUTLS_APPLICATION_DATA && session->internals.have_peeked_data==0) recvlowat = 0; else recvlowat = RCVLOWAT; } /* calculate the actual size, ie. get the minimum of the * buffered data and the requested data. */ min = GMIN( session->internals.record_recv_buffer.length, sizeOfPtr); if ( min > 0) { /* if we have enough buffered data * then just return them. */ if ( min == sizeOfPtr) { return min; } } /* min is over zero. recvdata is the data we must * receive in order to return the requested data. */ recvdata = sizeOfPtr - min; /* Check if the previously read data plus the new data to * receive are longer than the maximum receive buffer size. */ if ((session->internals.record_recv_buffer.length + recvdata) > MAX_RECV_SIZE) { gnutls_assert(); /* internal error */ return GNUTLS_E_INVALID_REQUEST; } /* Allocate the data required to store the new packet. */ alloc_size = recvdata+session->internals.record_recv_buffer.length; session->internals.record_recv_buffer.data = gnutls_realloc_fast( session->internals.record_recv_buffer.data, alloc_size); if ( session->internals.record_recv_buffer.data==NULL) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } buf_pos = session->internals.record_recv_buffer.length; buf = session->internals.record_recv_buffer.data; *iptr = buf; /* READ DATA - but leave RCVLOWAT bytes in the kernel buffer. */ if ( recvdata - recvlowat > 0) { ret = _gnutls_read( session, &buf[buf_pos], recvdata - recvlowat, 0); /* return immediately if we got an interrupt or eagain * error. */ if (ret < 0 && gnutls_error_is_fatal(ret)==0) { return ret; } } /* copy fresh data to our buffer. */ if (ret > 0) { _gnutls_read_log("RB: Have %d bytes into buffer. Adding %d bytes.\n", session->internals.record_recv_buffer.length, ret); _gnutls_read_log("RB: Requested %d bytes\n", sizeOfPtr); session->internals.record_recv_buffer.length += ret; } buf_pos = session->internals.record_recv_buffer.length; /* This is hack in order for select to work. Just leave recvlowat data, * into the kernel buffer (using a read with MSG_PEEK), thus making * select think, that the socket is ready for reading. * MSG_PEEK is only used with berkeley style sockets. */ if (ret == (recvdata - recvlowat) && recvlowat > 0) { ret2 = _gnutls_read( session, &buf[buf_pos], recvlowat, MSG_PEEK); if (ret2 < 0 && gnutls_error_is_fatal(ret2)==0) { return ret2; } if (ret2 > 0) { _gnutls_read_log("RB-PEEK: Read %d bytes in PEEK MODE.\n", ret2); _gnutls_read_log("RB-PEEK: Have %d bytes into buffer. Adding %d bytes.\nRB: Requested %d bytes\n", session->internals.record_recv_buffer.length, ret2, sizeOfPtr); session->internals.have_peeked_data = 1; session->internals.record_recv_buffer.length += ret2; } } if (ret < 0 || ret2 < 0) { gnutls_assert(); /* that's because they are initialized to 0 */ return GMIN(ret, ret2); } ret += ret2; if (ret > 0 && ret < recvlowat) { gnutls_assert(); return GNUTLS_E_AGAIN; } if (ret==0) { /* EOF */ gnutls_assert(); return 0; } ret = session->internals.record_recv_buffer.length; if ((ret > 0) && ((size_t)ret < sizeOfPtr)) { /* Short Read */ gnutls_assert(); return GNUTLS_E_AGAIN; } else { return ret; } } /* These two functions are used to insert data to the send buffer of the handshake or * record protocol. The send buffer is kept if a send is interrupted and we need to keep * the data left to sent, in order to send them later. */ #define MEMSUB(x,y) ((ssize_t)((ptrdiff_t)x-(ptrdiff_t)y)) inline static int _gnutls_buffer_insert( gnutls_buffer * buffer, const opaque* _data, size_t data_size) { if ( ( MEMSUB(_data, buffer->data) >= 0) && (MEMSUB(_data, buffer->data) < (ssize_t)buffer->length) ) { /* the given _data is part of the buffer. */ if (data_size > buffer->length) { gnutls_assert(); /* this shouldn't have happened */ return GNUTLS_E_INTERNAL_ERROR; } if (_data==buffer->data) { /* then don't even memmove */ buffer->length = data_size; return 0; } memmove( buffer->data, _data, data_size); buffer->length = data_size; return 0; } if (_gnutls_buffer_append( buffer, _data, data_size) < 0) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } return 0; } inline static int _gnutls_buffer_get( gnutls_buffer * buffer, const opaque ** ptr, size_t *ptr_size) { *ptr_size = buffer->length; *ptr = buffer->data; return 0; } /* This function is like write. But it does not return -1 on error. * It does return gnutls_errno instead. * * In case of E_AGAIN and E_INTERRUPTED errors, you must call gnutls_write_flush(), * until it returns ok (0). * * We need to push exactly the data in n, since we cannot send less * data. In TLS the peer must receive the whole packet in order * to decrypt and verify the integrity. * */ ssize_t _gnutls_io_write_buffered( gnutls_session session, const void *iptr, size_t n) { size_t left; uint j,x, sum=0; ssize_t retval, i; const opaque * ptr; int ret; gnutls_transport_ptr fd = session->internals.transport_send_ptr; /* to know where the procedure was interrupted. */ session->internals.direction = 1; ptr = iptr; /* In case the previous write was interrupted, check if the * iptr != NULL and we have data in the buffer. * If this is true then return an error. */ if (session->internals.record_send_buffer.length > 0 && iptr != NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } /* If data in the buffer exist */ if (iptr == NULL) { /* checking is handled above */ ret = _gnutls_buffer_get( &session->internals.record_send_buffer, &ptr, &n); if (ret < 0) { gnutls_assert(); return ret; } _gnutls_write_log( "WRITE: Restoring old write. (%d bytes to send)\n", n); } _gnutls_write_log( "WRITE: Will write %d bytes to %d.\n", n, fd); i = 0; left = n; while (left > 0) { if (session->internals._gnutls_push_func==NULL) i = send( (int)fd, &ptr[n-left], left, 0); else i = session->internals._gnutls_push_func(fd, &ptr[n-left], left); if (i == -1) { if (errno == EAGAIN || errno == EINTR) { session->internals.record_send_buffer_prev_size += n - left; retval = _gnutls_buffer_insert( &session->internals.record_send_buffer, &ptr[n-left], left); if (retval < 0) { gnutls_assert(); return retval; } _gnutls_write_log( "WRITE: Interrupted. Stored %d bytes to buffer. Already sent %d bytes.\n", left, n-left); retval = RET(errno); return retval; } else { gnutls_assert(); return GNUTLS_E_PUSH_ERROR; } } left -= i; if (_gnutls_log_level >= 7) { char line[128]; char tmp[16]; _gnutls_write_log( "WRITE: wrote %d bytes to %d. Left %d bytes. Total %d bytes.\n", i, fd, left, n); for (x=0;x<(uint)((i)/16)+1;x++) { line[0] = 0; if (sum>n-left) break; sprintf( tmp, "%.4x - ",x); _gnutls_str_cat( line, sizeof(line), tmp); for (j=0;j<16;j++) { if (suminternals.record_send_buffer_prev_size; session->internals.record_send_buffer.length = 0; session->internals.record_send_buffer_prev_size = 0; return retval; } /* This is exactly like write_buffered, but will use two buffers to read * from. */ ssize_t _gnutls_io_write_buffered2( gnutls_session 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 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 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 session) { ssize_t ret; ret = _gnutls_handshake_io_send_int( session, 0, 0, NULL, 0); if (ret < 0) { gnutls_assert(); return ret; } _gnutls_write_log("HANDSHAKE_FLUSH: written[1] %d bytes\n", ret); if (session->internals.handshake_send_buffer.length == 0) { ret = session->internals.handshake_send_buffer_prev_size; /* done */ session->internals.handshake_send_buffer_prev_size = 0; } 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 session, ContentType type, HandshakeType 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 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; } /* 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 session, ContentType type, HandshakeType htype, void *iptr, size_t sizeOfPtr) { size_t left; ssize_t i; char *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; } /* Buffer for handshake packets. Keeps the packets in order * for finished messages to use them. Used in HMAC calculation * and finished messages. */ int _gnutls_handshake_buffer_put( gnutls_session session, char *data, size_t length) { if (length==0) return 0; if ( (session->internals.max_handshake_data_buffer_size > 0) && ((length+session->internals.handshake_hash_buffer.length) > session->internals.max_handshake_data_buffer_size)) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } _gnutls_buffers_log( "BUF[HSK]: Inserted %d bytes of Data\n", length); if ( _gnutls_buffer_append( &session->internals.handshake_hash_buffer, data, length) < 0) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } return 0; } int _gnutls_handshake_buffer_get_size( gnutls_session session) { return session->internals.handshake_hash_buffer.length; } /* this function does not touch the buffer * and returns data from it (peek mode!) */ int _gnutls_handshake_buffer_peek( gnutls_session session, char *data, size_t length) { if (length > session->internals.handshake_hash_buffer.length) { length = session->internals.handshake_hash_buffer.length; } _gnutls_buffers_log( "BUF[HSK]: Peeked %d bytes of Data\n", length); memcpy(data, session->internals.handshake_hash_buffer.data, length); return length; } /* this function does not touch the buffer * and returns data from it (peek mode!) */ int _gnutls_handshake_buffer_get_ptr( gnutls_session session, char **data_ptr, size_t *length) { if (length!=NULL) *length = session->internals.handshake_hash_buffer.length; _gnutls_buffers_log( "BUF[HSK]: Peeked %d bytes of Data\n", *length); if (data_ptr!=NULL) *data_ptr = session->internals.handshake_hash_buffer.data; return 0; } /* Does not free the buffer */ int _gnutls_handshake_buffer_empty( gnutls_session session) { _gnutls_buffers_log( "BUF[HSK]: Emptied buffer\n"); session->internals.handshake_hash_buffer.length = 0; return 0; } int _gnutls_handshake_buffer_clear( gnutls_session session) { _gnutls_buffers_log( "BUF[HSK]: Cleared Data from buffer\n"); _gnutls_buffer_clear( &session->internals.handshake_hash_buffer); return 0; }