Beispiel #1
0
/* Checks whether there are received data within
 * a timeframe.
 *
 * Returns 0 if data were received, GNUTLS_E_TIMEDOUT
 * on timeout and a negative error code on error.
 */
int _gnutls_io_check_recv(gnutls_session_t session, unsigned int ms)
{
	gnutls_transport_ptr_t fd = session->internals.transport_recv_ptr;
	int ret = 0, err;

	if (unlikely
	    (session->internals.pull_timeout_func == gnutls_system_recv_timeout
	     && session->internals.pull_func != system_read)) {
		_gnutls_debug_log("The pull function has been replaced but not the pull timeout.");
		return gnutls_assert_val(GNUTLS_E_PULL_ERROR);
	}

	reset_errno(session);

	ret = session->internals.pull_timeout_func(fd, ms);
	if (ret == -1) {
		err = get_errno(session);
		_gnutls_read_log
		    ("READ_TIMEOUT: %d returned from %p, errno=%d (timeout: %u)\n",
		     (int) ret, fd, err, ms);
		return errno_to_gerr(err, IS_DTLS(session));
	}

	if (ret > 0)
		return 0;
	else
		return GNUTLS_E_TIMEDOUT;
}
Beispiel #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);
}
Beispiel #3
0
static ssize_t
_gnutls_dgram_read(gnutls_session_t session, mbuffer_st ** bufel,
		   gnutls_pull_func pull_func, unsigned int *ms)
{
	ssize_t i, ret;
	uint8_t *ptr;
	struct timespec t1, t2;
	size_t max_size, recv_size;
	gnutls_transport_ptr_t fd = session->internals.transport_recv_ptr;
	unsigned int diff;

	max_size = max_record_recv_size(session);
	recv_size = max_size;

	session->internals.direction = 0;

	if (ms && *ms > 0) {
		ret = _gnutls_io_check_recv(session, *ms);
		if (ret < 0)
			return gnutls_assert_val(ret);
		gnutls_gettime(&t1);
	}

	*bufel = _mbuffer_alloc_align16(max_size, get_total_headers(session));
	if (*bufel == NULL)
		return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);

	ptr = (*bufel)->msg.data;

	reset_errno(session);
	i = pull_func(fd, ptr, recv_size);

	if (i < 0) {
		int err = get_errno(session);

		_gnutls_read_log("READ: %d returned from %p, errno=%d\n",
				 (int) i, fd, err);

		ret = errno_to_gerr(err, 1);
		goto cleanup;
	} else {
		_gnutls_read_log("READ: Got %d bytes from %p\n", (int) i,
				 fd);
		if (i == 0) {
			/* If we get here, we likely have a stream socket.
			 * That assumption may not work on DCCP. */
			gnutls_assert();
			ret = 0;
			goto cleanup;
		}

		_mbuffer_set_udata_size(*bufel, i);
	}

	if (ms && *ms > 0) {
		gnutls_gettime(&t2);
		diff = timespec_sub_ms(&t2, &t1);
		if (diff < *ms)
			*ms -= diff;
		else {
			ret = gnutls_assert_val(GNUTLS_E_TIMEDOUT);
			goto cleanup;
		}
	}

	_gnutls_read_log("READ: read %d bytes from %p\n", (int) i, fd);

	return i;

      cleanup:
	_mbuffer_xfree(bufel);
	return ret;
}
Beispiel #4
0
static ssize_t
_gnutls_stream_read(gnutls_session_t session, mbuffer_st ** bufel,
		    size_t size, gnutls_pull_func pull_func,
		    unsigned int *ms)
{
	size_t left;
	ssize_t i = 0;
	size_t max_size = max_record_recv_size(session);
	uint8_t *ptr;
	gnutls_transport_ptr_t fd = session->internals.transport_recv_ptr;
	int ret;
	struct timespec t1, t2;
	unsigned int diff;

	session->internals.direction = 0;

	*bufel = _mbuffer_alloc_align16(MAX(max_size, size), get_total_headers(session));
	if (!*bufel) {
		gnutls_assert();
		return GNUTLS_E_MEMORY_ERROR;
	}
	ptr = (*bufel)->msg.data;

	left = size;
	while (left > 0) {
		if (ms && *ms > 0) {
			ret = _gnutls_io_check_recv(session, *ms);
			if (ret < 0) {
				gnutls_assert();
				goto cleanup;
			}

			gnutls_gettime(&t1);
		}

		reset_errno(session);

		i = pull_func(fd, &ptr[size - left], left);

		if (i < 0) {
			int err = get_errno(session);

			_gnutls_read_log
			    ("READ: %d returned from %p, errno=%d gerrno=%d\n",
			     (int) i, fd, errno,
			     session->internals.errnum);

			if (err == EAGAIN || err == EINTR) {
				if (size - left > 0) {

					_gnutls_read_log
					    ("READ: returning %d bytes from %p\n",
					     (int) (size - left), fd);

					goto finish;
				}

				ret = errno_to_gerr(err, 0);
				goto cleanup;
			} else {
				gnutls_assert();
				ret = GNUTLS_E_PULL_ERROR;
				goto cleanup;
			}
		} else {

			_gnutls_read_log("READ: Got %d bytes from %p\n",
					 (int) i, fd);

			if (i == 0)
				break;	/* EOF */
		}

		left -= i;
		(*bufel)->msg.size += i;

		if (ms && *ms > 0 && *ms != GNUTLS_INDEFINITE_TIMEOUT) {
			gnutls_gettime(&t2);
			diff = timespec_sub_ms(&t2, &t1);
			if (diff < *ms)
				*ms -= diff;
			else {
				ret = gnutls_assert_val(GNUTLS_E_TIMEDOUT);
				goto cleanup;
			}
		}
	}

      finish:

	_gnutls_read_log("READ: read %d bytes from %p\n",
			 (int) (size - left), fd);

	if (size - left == 0)
		_mbuffer_xfree(bufel);

	return (size - left);

      cleanup:
	_mbuffer_xfree(bufel);
	return ret;
}
Beispiel #5
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;
    }
}
Beispiel #6
0
/* 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_t session, void *iptr,
              size_t sizeOfPtr, int flags)
{
    size_t left;
    ssize_t i = 0;
    char *ptr = iptr;
    unsigned j, x, sum = 0;
    gnutls_transport_ptr_t fd = session->internals.transport_recv_ptr;

    session->internals.direction = 0;

    left = sizeOfPtr;
    while (left > 0)
    {

        if (session->internals._gnutls_pull_func == NULL)
            i = recv (GNUTLS_POINTER_TO_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);
}
Beispiel #7
0
static ssize_t
_gnutls_stream_read (gnutls_session_t session, mbuffer_st **bufel,
		     size_t size, gnutls_pull_func pull_func)
{
  size_t left;
  ssize_t i = 0;
  size_t max_size = _gnutls_get_max_decrypted_data(session);
  char *ptr;
  gnutls_transport_ptr_t fd = session->internals.transport_recv_ptr;

  *bufel = _mbuffer_alloc (0, MAX(max_size, size));
  if (!*bufel)
    {
      gnutls_assert ();
      return GNUTLS_E_MEMORY_ERROR;
    }
  ptr = (*bufel)->msg.data;

  session->internals.direction = 0;

  left = size;
  while (left > 0)
    {
      reset_errno (session);

      i = pull_func (fd, &ptr[size - left], left);

      if (i < 0)
        {
          int err = get_errno (session);

          _gnutls_read_log ("READ: %d returned from %p, errno=%d gerrno=%d\n",
                            (int) i, fd, errno, session->internals.errnum);

          if (err == EAGAIN || err == EINTR)
            {
              if (size - left > 0)
                {

                  _gnutls_read_log ("READ: returning %d bytes from %p\n",
                                    (int) (size - left), fd);

                  goto finish;
                }

              if (err == EAGAIN)
                return GNUTLS_E_AGAIN;
              return GNUTLS_E_INTERRUPTED;
            }
          else
            {
              gnutls_assert ();
              return GNUTLS_E_PULL_ERROR;
            }
        }
      else
        {

          _gnutls_read_log ("READ: Got %d bytes from %p\n", (int) i, fd);

          if (i == 0)
            break;              /* EOF */
        }

      left -= i;
      (*bufel)->msg.size += i;
    }

finish:

  _gnutls_read_log ("READ: read %d bytes from %p\n",
                        (int) (size - left), fd);

  return (size - left);
}
Beispiel #8
0
static ssize_t
_gnutls_dgram_read (gnutls_session_t session, mbuffer_st **bufel,
		    gnutls_pull_func pull_func)
{
  ssize_t i, ret;
  char *ptr;
  size_t max_size = _gnutls_get_max_decrypted_data(session);
  size_t recv_size = MAX_RECV_SIZE(session);
  gnutls_transport_ptr_t fd = session->internals.transport_recv_ptr;

  if (recv_size > max_size)
    recv_size = max_size;

  *bufel = _mbuffer_alloc (0, max_size);
  if (*bufel == NULL)
    return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);

  ptr = (*bufel)->msg.data;

  session->internals.direction = 0;

  reset_errno (session);
  i = pull_func (fd, ptr, recv_size);

  if (i < 0)
    {
      int err = get_errno (session);

      _gnutls_read_log ("READ: %d returned from %p, errno=%d gerrno=%d\n",
			(int) i, fd, errno, session->internals.errnum);

      if (err == EAGAIN)
        {
          ret = GNUTLS_E_AGAIN;
          goto cleanup;
        }
      else if (err == EINTR)
        {
          ret = GNUTLS_E_INTERRUPTED;
          goto cleanup;
        }
      else
        {
          gnutls_assert ();
          ret = GNUTLS_E_PULL_ERROR;
          goto cleanup;
        }
    }
  else
    {
      _gnutls_read_log ("READ: Got %d bytes from %p\n", (int) i, fd);
      if (i == 0) 
        {
          /* If we get here, we likely have a stream socket.
           * FIXME: this probably breaks DCCP. */
          gnutls_assert ();
          ret = 0;
          goto cleanup;
        }

      _mbuffer_set_udata_size (*bufel, i);
    }

  _gnutls_read_log ("READ: read %d bytes from %p\n", (int) i, fd);
  
  return i;
  
cleanup:
  _mbuffer_xfree(bufel);
  return ret;
}
Beispiel #9
0
/* 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_t session, void *iptr,
	      size_t sizeOfPtr, int flags)
{
  size_t left;
  ssize_t i = 0;
  char *ptr = iptr;
  unsigned j, x, sum = 0;
  gnutls_transport_ptr_t fd = session->internals.transport_recv_ptr;

  session->internals.direction = 0;

  left = sizeOfPtr;
  while (left > 0)
    {

      session->internals.errnum = 0;

      if (session->internals._gnutls_pull_func == NULL)
	{
	  i = recv (GNUTLS_POINTER_TO_INT (fd), &ptr[sizeOfPtr - left],
		    left, flags);
#if HAVE_WINSOCK2_H
	  if (i < 0)
	    {
	      int tmperr = WSAGetLastError ();
	      switch (tmperr)
		{
		case WSAEWOULDBLOCK:
		  session->internals.errnum = EAGAIN;
		  break;

		case WSAEINTR:
		  session->internals.errnum = EINTR;
		  break;

		default:
		  session->internals.errnum = EIO;
		  break;
		}
	      WSASetLastError (tmperr);
	    }
#endif
	}
      else
	i = session->internals._gnutls_pull_func (fd,
						  &ptr[sizeOfPtr -
						       left], left);

      if (i < 0)
	{
	  int err = session->internals.errnum ? session->internals.errnum
	    : errno;

	  _gnutls_read_log ("READ: %d returned from %p, errno=%d gerrno=%d\n",
			    i, fd, errno, session->internals.errnum);

	  if (err == EAGAIN || err == EINTR)
	    {
	      if (sizeOfPtr - left > 0)
		{

		  _gnutls_read_log ("READ: returning %d bytes from %p\n",
				    sizeOfPtr - left, fd);

		  goto finish;
		}
	      gnutls_assert ();

	      if (err == EAGAIN)
		return GNUTLS_E_AGAIN;
	      return GNUTLS_E_INTERRUPTED;
	    }
	  else
	    {
	      gnutls_assert ();
	      return GNUTLS_E_PULL_ERROR;
	    }
	}
      else
	{

	  _gnutls_read_log ("READ: Got %d bytes from %p\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 %p\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);
}