/* 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_t 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; }
/* * @ms: a pointer to the number of milliseconds to wait for data. Use zero or NULL for indefinite. * * 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_record_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. * * If the @ms parameter is non zero then this function will return before * the given amount of milliseconds or return GNUTLS_E_TIMEDOUT. * */ ssize_t _gnutls_io_read_buffered(gnutls_session_t session, size_t total, content_type_t recv_type, unsigned int *ms) { ssize_t ret; size_t min; mbuffer_st *bufel = NULL; size_t recvdata, readsize; if (total > max_record_recv_size(session) || total == 0) { gnutls_assert(); return GNUTLS_E_RECORD_OVERFLOW; } /* calculate the actual size, ie. get the minimum of the * buffered data and the requested data. */ min = MIN(session->internals.record_recv_buffer.byte_length, total); if (min > 0) { /* if we have enough buffered data * then just return them. */ if (min == total) { return min; } } /* min is over zero. recvdata is the data we must * receive in order to return the requested data. */ recvdata = total - min; readsize = recvdata; /* 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.byte_length + recvdata) > max_record_recv_size(session)) { gnutls_assert(); /* internal error */ return GNUTLS_E_INVALID_REQUEST; } /* READ DATA */ if (readsize > 0) { ret = _gnutls_read(session, &bufel, readsize, session->internals.pull_func, ms); /* return immediately if we got an interrupt or eagain * error. */ if (ret < 0) { return gnutls_assert_val(ret); } if (ret == 0) /* EOF */ return gnutls_assert_val(0); /* copy fresh data to our buffer. */ _gnutls_read_log ("RB: Have %d bytes into buffer. Adding %d bytes.\n", (int) session->internals.record_recv_buffer. byte_length, (int) ret); _gnutls_read_log("RB: Requested %d bytes\n", (int) total); _mbuffer_enqueue(&session->internals.record_recv_buffer, bufel); if (IS_DTLS(session)) ret = MIN(total, session->internals.record_recv_buffer. byte_length); else ret = session->internals.record_recv_buffer. byte_length; if ((ret > 0) && ((size_t) ret < total)) /* Short Read */ return gnutls_assert_val(GNUTLS_E_AGAIN); else return ret; } else return gnutls_assert_val(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_t session, opaque ** iptr, size_t sizeOfPtr, content_type_t recv_type) { ssize_t ret = 0, ret2 = 0; size_t min; int buf_pos; opaque *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 = MIN (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 MIN (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; } }