Пример #1
0
/* This function behaves exactly like read(). The only difference is
 * that it accepts the gnutls_session_t and the content_type_t of data to
 * receive (if called by the user the Content is Userdata only)
 * It is intended to receive data, under the current session.
 *
 * The gnutls_handshake_description_t was introduced to support SSL V2.0 client hellos.
 */
ssize_t
_gnutls_recv_int (gnutls_session_t session, content_type_t type,
                  gnutls_handshake_description_t htype,
                  uint8_t * data, size_t data_size, void* seq,
                  unsigned int ms)
{
  int ret;

  if ((type != GNUTLS_ALERT && type != GNUTLS_HEARTBEAT) && (data_size == 0 || data == NULL))
    return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);

  if (session->internals.read_eof != 0)
    {
      /* if we have already read an EOF
       */
      return 0;
    }
  else if (session_is_valid (session) != 0
           || session->internals.may_not_read != 0)
    {
      gnutls_assert ();
      return GNUTLS_E_INVALID_SESSION;
    }
    
  switch(session->internals.recv_state)
    {
      case RECV_STATE_DTLS_RETRANSMIT:
        ret = _dtls_retransmit(session);
        if (ret < 0)
          return gnutls_assert_val(ret);
        
        session->internals.recv_state = RECV_STATE_0;
      case RECV_STATE_0:

        _dtls_async_timer_check(session);
        /* If we have enough data in the cache do not bother receiving
         * a new packet. (in order to flush the cache)
         */ 
        ret = check_buffers (session, type, data, data_size, seq);
        if (ret != 0)
          return ret;

        ret = _gnutls_recv_in_buffers(session, type, htype, ms);
        if (ret < 0 && ret != GNUTLS_E_SESSION_EOF)
          return gnutls_assert_val(ret);

        return check_buffers (session, type, data, data_size, seq);
      default:
        return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
    }
}
Пример #2
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;
}
Пример #3
0
/* This function will check if the received record type is
 * the one we actually expect and adds it to the proper
 * buffer. The bufel will be deinitialized after calling
 * this function, even if it fails.
 */
static int
record_add_to_buffers (gnutls_session_t session,
                   struct tls_record_st *recv, content_type_t type,
                   gnutls_handshake_description_t htype, 
                   uint64* seq,
                   mbuffer_st* bufel)
{

  int ret;

  if ((recv->type == type)
      && (type == GNUTLS_APPLICATION_DATA ||
          type == GNUTLS_CHANGE_CIPHER_SPEC ||
          type == GNUTLS_HANDSHAKE))
    {
      _gnutls_record_buffer_put (session, type, seq, bufel);
      
      /* if we received application data as expected then we
       * deactivate the async timer */
      _dtls_async_timer_delete(session);
    }
  else
    {
      /* if the expected type is different than the received 
       */
      switch (recv->type)
        {
        case GNUTLS_ALERT:
          if (bufel->msg.size < 2)
            {
              ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
              goto unexpected_packet;
            }

          _gnutls_record_log
            ("REC[%p]: Alert[%d|%d] - %s - was received\n", session,
             bufel->msg.data[0], bufel->msg.data[1], gnutls_alert_get_name ((int) bufel->msg.data[1]));

          session->internals.last_alert = bufel->msg.data[1];

          /* if close notify is received and
           * the alert is not fatal
           */
          if (bufel->msg.data[1] == GNUTLS_A_CLOSE_NOTIFY && bufel->msg.data[0] != GNUTLS_AL_FATAL)
            {
              /* If we have been expecting for an alert do 
               */
              session->internals.read_eof = 1;
              ret = GNUTLS_E_SESSION_EOF;
              goto cleanup;
            }
          else
            {
              /* if the alert is FATAL or WARNING
               * return the apropriate message
               */

              gnutls_assert ();
              ret = GNUTLS_E_WARNING_ALERT_RECEIVED;
              if (bufel->msg.data[0] == GNUTLS_AL_FATAL)
                {
                  session_unresumable (session);
                  session_invalidate (session);
                  ret = gnutls_assert_val(GNUTLS_E_FATAL_ALERT_RECEIVED);
                }
              goto cleanup;
            }
          break;

        case GNUTLS_CHANGE_CIPHER_SPEC:
          if (!(IS_DTLS(session)))
            return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
            
          _gnutls_record_buffer_put (session, recv->type, seq, bufel);

          break;

#ifdef ENABLE_HEARTBEAT
        case GNUTLS_HEARTBEAT:
	    ret = _gnutls_heartbeat_handle (session, bufel);
	    goto cleanup;
#endif

        case GNUTLS_APPLICATION_DATA:
          if (session->internals.initial_negotiation_completed == 0)
            {
              ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
              goto unexpected_packet;
            }


          /* the got_application data is only returned
           * if expecting client hello (for rehandshake
           * reasons). Otherwise it is an unexpected packet
           */
          if (type == GNUTLS_ALERT || (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO
                                       && type == GNUTLS_HANDSHAKE))
            {
              /* even if data is unexpected put it into the buffer */
              if ((ret =
                   _gnutls_record_buffer_put (session, recv->type, seq,
                                              bufel)) < 0)
                {
                  gnutls_assert ();
                  goto cleanup;
                }

              return gnutls_assert_val(GNUTLS_E_GOT_APPLICATION_DATA);
            }
          else
            {
              ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
              goto unexpected_packet;
            }

          break;

        case GNUTLS_HANDSHAKE:
          /* In DTLS we might receive a handshake replay from the peer to indicate
           * the our last TLS handshake messages were not received.
           */
          if (IS_DTLS(session))
            {
              if (type == GNUTLS_CHANGE_CIPHER_SPEC)
                {
                  ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
                  goto unexpected_packet;
                }

              if (_dtls_is_async(session) && _dtls_async_timer_active(session))
                {
                  if (session->security_parameters.entity == GNUTLS_SERVER &&
                      bufel->htype == GNUTLS_HANDSHAKE_CLIENT_HELLO)
                    {
                      /* client requested rehandshake. Delete the timer */
                      _dtls_async_timer_delete(session);
                    }
                  else
                    {
                      session->internals.recv_state = RECV_STATE_DTLS_RETRANSMIT;
                      ret = _dtls_retransmit(session);
                      if (ret == 0) 
                        {
                          session->internals.recv_state = RECV_STATE_0;
                          ret = gnutls_assert_val(GNUTLS_E_AGAIN);
                          goto unexpected_packet;
                        }
                      goto cleanup;
                    }
                }
            }

          /* This is legal if HELLO_REQUEST is received - and we are a client.
           * If we are a server, a client may initiate a renegotiation at any time.
           */
          if (session->security_parameters.entity == GNUTLS_SERVER &&
              bufel->htype == GNUTLS_HANDSHAKE_CLIENT_HELLO)
            {
              gnutls_assert ();
              ret =
                _gnutls_record_buffer_put (session, recv->type, seq, bufel);
              if (ret < 0)
                {
                  gnutls_assert ();
                  goto cleanup;
                }
              return GNUTLS_E_REHANDSHAKE;
            }

          /* If we are already in a handshake then a Hello
           * Request is illegal. But here we don't really care
           * since this message will never make it up here.
           */

          /* So we accept it, if it is a Hello. If not, this will
           * fail and trigger flight retransmissions after some time. */
          ret = _gnutls_recv_hello_request (session, bufel->msg.data, bufel->msg.size);
          goto unexpected_packet;

          break;
        default:

          _gnutls_record_log
            ("REC[%p]: Received unexpected packet %d (%s) expecting %d (%s)\n",
             session, recv->type, _gnutls_packet2str(recv->type), type, _gnutls_packet2str(type));

          gnutls_assert ();
          ret = GNUTLS_E_UNEXPECTED_PACKET;
          goto unexpected_packet;
        }
    }

  return 0;

unexpected_packet:
  if (IS_DTLS(session) && ret != GNUTLS_E_REHANDSHAKE)
    {
      _mbuffer_xfree(&bufel);
      RETURN_DTLS_EAGAIN_OR_TIMEOUT(session, ret);
    }

cleanup:
  _mbuffer_xfree(&bufel);
  return ret;

}