示例#1
0
/* Waits for the last flight or retransmits
 * the previous on timeout. Returns 0 on success.
 */
int _dtls_wait_and_retransmit(gnutls_session_t session)
{
int ret;

  if (session->internals.dtls.blocking != 0)
    ret = _gnutls_io_check_recv(session, TIMER_WINDOW);
  else
    ret = _gnutls_io_check_recv(session, 0);

  if (ret == GNUTLS_E_TIMEDOUT)
    {
      ret = _dtls_retransmit(session);
      if (ret == 0)
        {
          RETURN_DTLS_EAGAIN_OR_TIMEOUT(session, 0);
        }
      else
        return gnutls_assert_val(ret);
    }

  RESET_TIMER;
  return 0;
}
示例#2
0
文件: buffers.c 项目: gnutls/gnutls
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;
}
示例#3
0
文件: buffers.c 项目: gnutls/gnutls
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;
}
示例#4
0
/* This function transmits the flight that has been previously
 * buffered.
 *
 * This function is called from the handshake layer and calls the
 * record layer.
 */
int
_dtls_transmit (gnutls_session_t session)
{
int ret;
uint8_t* buf = NULL;
unsigned int timeout;

  /* PREPARING -> SENDING state transition */
  mbuffer_head_st *const send_buffer =
    &session->internals.handshake_send_buffer;
  mbuffer_st *cur;
  gnutls_handshake_description_t last_type = 0;
  unsigned int diff;
  struct timespec now;
  
  gettime(&now);

  /* If we have already sent a flight and we are operating in a 
   * non blocking way, check if it is time to retransmit or just
   * return.
   */
  if (session->internals.dtls.flight_init != 0 && session->internals.dtls.blocking == 0)
    {
      /* just in case previous run was interrupted */
      ret = _gnutls_io_write_flush (session);
      if (ret < 0)
        {
          gnutls_assert();
          goto cleanup;
        }

      if (session->internals.dtls.last_flight == 0 || !_dtls_is_async(session))
        {
          /* check for ACK */
          ret = _gnutls_io_check_recv(session, 0);
          if (ret == GNUTLS_E_TIMEDOUT)
            {
              /* if no retransmission is required yet just return 
               */
              if (_dtls_timespec_sub_ms(&now, &session->internals.dtls.last_retransmit) < TIMER_WINDOW)
                {
                  gnutls_assert();
                  goto nb_timeout;
                }
            }
          else /* received something */
            {
              if (ret == 0)
                {
                  ret = is_next_hpacket_expected(session);
                  if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
                    goto nb_timeout;
                  if (ret < 0 && ret != GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET)
                    {
                      gnutls_assert();
                      goto cleanup;
                    }
                  if (ret == 0) goto end_flight;
                  /* if ret == GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET retransmit */
                }
              else
                goto nb_timeout;
            }
        }
    }

  do 
    {
      timeout = TIMER_WINDOW;

      diff = _dtls_timespec_sub_ms(&now, &session->internals.dtls.handshake_start_time);
      if (diff >= session->internals.dtls.total_timeout_ms) 
        {
          _gnutls_dtls_log("Session timeout: %u ms\n", diff);
          ret = gnutls_assert_val(GNUTLS_E_TIMEDOUT);
          goto end_flight;
        }

      diff = _dtls_timespec_sub_ms(&now, &session->internals.dtls.last_retransmit);
      if (session->internals.dtls.flight_init == 0 || diff >= TIMER_WINDOW)
        {
          _gnutls_dtls_log ("DTLS[%p]: %sStart of flight transmission.\n", session,  (session->internals.dtls.flight_init == 0)?"":"re-");
          for (cur = send_buffer->head;
               cur != NULL; cur = cur->next)
            {
              ret = transmit_message (session, cur, &buf);
              if (ret < 0)
                {
                  gnutls_assert();
                  goto end_flight;
                }

              last_type = cur->htype;
            }
          gettime(&session->internals.dtls.last_retransmit);

          if (session->internals.dtls.flight_init == 0)
            {
              session->internals.dtls.flight_init = 1;
              RESET_TIMER;
              timeout = TIMER_WINDOW;

              if (last_type == GNUTLS_HANDSHAKE_FINISHED)
                {
              /* On the last flight we cannot ensure retransmission
               * from here. _dtls_wait_and_retransmit() is being called
               * by handshake.
               */
                  session->internals.dtls.last_flight = 1;
                }
              else
                session->internals.dtls.last_flight = 0;
            }
          else
            {
              UPDATE_TIMER;
            }
        }

      ret = _gnutls_io_write_flush (session);
      if (ret < 0)
        {
          ret = gnutls_assert_val(ret);
          goto cleanup;
        }

      /* last message in handshake -> no ack */
      if (session->internals.dtls.last_flight != 0)
        {
          /* we don't wait here. We just return 0 and
           * if a retransmission occurs because peer didn't receive it
           * we rely on the record or handshake
           * layer calling this function again.
           */
          ret = 0;
          goto cleanup;
        }
      else /* all other messages -> implicit ack (receive of next flight) */
        {
          if (session->internals.dtls.blocking != 0)
            ret = _gnutls_io_check_recv(session, timeout);
          else
            {
              ret = _gnutls_io_check_recv(session, 0);
              if (ret == GNUTLS_E_TIMEDOUT)
                {
                  goto nb_timeout;
                }
            }
          
          if (ret == 0)
            {
              ret = is_next_hpacket_expected(session);
              if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
                goto nb_timeout;

              if (ret == GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET)
                {
                  ret = GNUTLS_E_TIMEDOUT;
                  goto keep_up;
                }
              if (ret < 0)
                {
                  gnutls_assert();
                  goto cleanup;
                }
              goto end_flight;
            }
        }

keep_up:
      gettime(&now);
    } while(ret == GNUTLS_E_TIMEDOUT);

  if (ret < 0)
    {
      ret = gnutls_assert_val(ret);
      goto end_flight;
    }

  ret = 0;

end_flight:
  _gnutls_dtls_log ("DTLS[%p]: End of flight transmission.\n", session);
  _dtls_reset_hsk_state(session);

cleanup:
  if (buf != NULL)
    gnutls_free(buf);

  /* SENDING -> WAITING state transition */
  return ret;

nb_timeout:
  if (buf != NULL)
    gnutls_free(buf);

  RETURN_DTLS_EAGAIN_OR_TIMEOUT(session, ret);
}