Пример #1
0
/* 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;
}
Пример #2
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);
}
Пример #3
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;
    }
}