Ejemplo n.º 1
0
/*----------------------------------------------------------------------------*/
static
int
coap_receive(void)
{
    coap_error_code = NO_ERROR;

    PRINTF("handle_incoming_data(): received uip_datalen=%u \n",(uint16_t)uip_datalen());

    /* Static declaration reduces stack peaks and program code size. */
    static coap_packet_t message[1]; /* This way the packet can be treated as pointer as usual. */
    static coap_packet_t response[1];
    static coap_transaction_t *transaction = NULL;

    if (uip_newdata()) {

        PRINTF("receiving UDP datagram from: ");
        PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
        PRINTF(":%u\n  Length: %u\n  Data: ", uip_ntohs(UIP_UDP_BUF->srcport), uip_datalen() );
        PRINTBITS(uip_appdata, uip_datalen());
        PRINTF("\n");

        coap_error_code = coap_parse_message(message, uip_appdata, uip_datalen());

        if (coap_error_code==NO_ERROR)
        {

            /*TODO duplicates suppression, if required by application */

            PRINTF("  Parsed: v %u, t %u, oc %u, c %u, mid %u\n", message->version, message->type, message->option_count, message->code, message->mid);
            PRINTF("  URL: %.*s\n", message->uri_path_len, message->uri_path);
            PRINTF("  Payload: %.*s\n", message->payload_len, message->payload);

            /* Handle requests. */
            if (message->code >= COAP_GET && message->code <= COAP_DELETE)
            {
                /* Use transaction buffer for response to confirmable request. */
                if ( (transaction = coap_new_transaction(message->mid, &UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport)) )
                {
                    uint32_t block_num = 0;
                    uint16_t block_size = REST_MAX_CHUNK_SIZE;
                    uint32_t block_offset = 0;
                    int32_t new_offset = 0;

                    /* prepare response */
                    if (message->type==COAP_TYPE_CON)
                    {
                        /* Reliable CON requests are answered with an ACK. */
                        coap_init_message(response, COAP_TYPE_ACK, CONTENT_2_05, message->mid);
                    }
                    else
                    {
                        /* Unreliable NON requests are answered with a NON as well. */
                        coap_init_message(response, COAP_TYPE_NON, CONTENT_2_05, coap_get_mid());
                    }

                    /* resource handlers must take care of different handling (e.g., TOKEN_OPTION_REQUIRED_240) */
                    if (IS_OPTION(message, COAP_OPTION_TOKEN))
                    {
                        coap_set_header_token(response, message->token, message->token_len);
                        SET_OPTION(response, COAP_OPTION_TOKEN);
                    }

                    /* get offset for blockwise transfers */
                    if (coap_get_header_block2(message, &block_num, NULL, &block_size, &block_offset))
                    {
                        PRINTF("Blockwise: block request %lu (%u/%u) @ %lu bytes\n", block_num, block_size, REST_MAX_CHUNK_SIZE, block_offset);
                        block_size = MIN(block_size, REST_MAX_CHUNK_SIZE);
                        new_offset = block_offset;
                    }

                    /* Invoke resource handler. */
                    if (service_cbk)
                    {
                        /* Call REST framework and check if found and allowed. */
                        if (service_cbk(message, response, transaction->packet+COAP_MAX_HEADER_SIZE, block_size, &new_offset))
                        {
                            if (coap_error_code==NO_ERROR)
                            {
                                /* Apply blockwise transfers. */
                                if ( IS_OPTION(message, COAP_OPTION_BLOCK1) && response->code<BAD_REQUEST_4_00 && !IS_OPTION(response, COAP_OPTION_BLOCK1) )
                                {
                                    PRINTF("Block1 NOT IMPLEMENTED\n");

                                    coap_error_code = NOT_IMPLEMENTED_5_01;
                                    coap_error_message = "NoBlock1Support";
                                }
                                else if ( IS_OPTION(message, COAP_OPTION_BLOCK2) )
                                {
                                    /* unchanged new_offset indicates that resource is unaware of blockwise transfer */
                                    if (new_offset==block_offset)
                                    {
                                        PRINTF("Blockwise: unaware resource with payload length %u/%u\n", response->payload_len, block_size);
                                        if (block_offset >= response->payload_len)
                                        {
                                            PRINTF("handle_incoming_data(): block_offset >= response->payload_len\n");

                                            response->code = BAD_OPTION_4_02;
                                            coap_set_payload(response, "BlockOutOfScope", 15); /* a const char str[] and sizeof(str) produces larger code size */
                                        }
                                        else
                                        {
                                            coap_set_header_block2(response, block_num, response->payload_len - block_offset > block_size, block_size);
                                            coap_set_payload(response, response->payload+block_offset, MIN(response->payload_len - block_offset, block_size));
                                        } /* if (valid offset) */
                                    }
                                    else
                                    {
                                        /* resource provides chunk-wise data */
                                        PRINTF("Blockwise: blockwise resource, new offset %ld\n", new_offset);
                                        coap_set_header_block2(response, block_num, new_offset!=-1 || response->payload_len > block_size, block_size);
                                        if (response->payload_len > block_size) coap_set_payload(response, response->payload, block_size);
                                    } /* if (resource aware of blockwise) */
                                }
                                else if (new_offset!=0)
                                {
                                    PRINTF("Blockwise: no block option for blockwise resource, using block size %u\n", REST_MAX_CHUNK_SIZE);

                                    coap_set_header_block2(response, 0, new_offset!=-1, REST_MAX_CHUNK_SIZE);
                                    coap_set_payload(response, response->payload, MIN(response->payload_len, REST_MAX_CHUNK_SIZE));
                                } /* if (blockwise request) */
                            } /* no errors/hooks */
                        } /* successful service callback */

                        /* Serialize response. */
                        if (coap_error_code==NO_ERROR)
                        {
                            if ((transaction->packet_len = coap_serialize_message(response, transaction->packet))==0)
                            {
                                coap_error_code = PACKET_SERIALIZATION_ERROR;
                            }
                        }

                    }
                    else
                    {
                        coap_error_code = NOT_IMPLEMENTED_5_01;
                        coap_error_message = "NoServiceCallbck"; // no a to fit 16 bytes
                    } /* if (service callback) */

                } else {
                    coap_error_code = SERVICE_UNAVAILABLE_5_03;
                    coap_error_message = "NoFreeTraBuffer";
                } /* if (transaction buffer) */
            }
            else
            {
                /* Responses */

                if (message->type==COAP_TYPE_ACK)
                {
                    PRINTF("Received ACK\n");
                }
                else if (message->type==COAP_TYPE_RST)
                {
                    PRINTF("Received RST\n");
                    /* Cancel possible subscriptions. */
                    coap_remove_observer_by_mid(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, message->mid);
                }

                transaction = coap_get_transaction_by_mid(message->mid);
                if (message->type != COAP_TYPE_CON && transaction)
                {
                    /* Free transaction memory before callback, as it may create a new transaction. */
                    restful_response_handler callback = transaction->callback;
                    void *callback_data = transaction->callback_data;
                    coap_clear_transaction(transaction);

                    /* Check if someone registered for the response */
                    if (callback) {
                        callback(callback_data, message);
                    }
                } /* if (ACKed transaction) */
                /* Observe notification */
                if ((message->type == COAP_TYPE_CON || message->type == COAP_TYPE_NON) \
                        && IS_OPTION(message, COAP_OPTION_OBSERVE)) {
                    PRINTF("Observe [%u]\n", message->observe);
                    coap_handle_notification(&UIP_IP_BUF->srcipaddr,                     \
                                             UIP_UDP_BUF->srcport, message);
                }
                transaction = NULL;

            } /* Request or Response */

        } /* if (parsed correctly) */

        if (coap_error_code==NO_ERROR)
        {
            if (transaction) coap_send_transaction(transaction);
        }
        else if (coap_error_code==MANUAL_RESPONSE)
        {
            PRINTF("Clearing transaction for manual response");
            coap_clear_transaction(transaction);
        }
        else
        {
            PRINTF("ERROR %u: %s\n", coap_error_code, coap_error_message);
            coap_clear_transaction(transaction);

            /* Set to sendable error code. */
            if (coap_error_code >= 192)
            {
                coap_error_code = INTERNAL_SERVER_ERROR_5_00;
            }
            /* Reuse input buffer for error message. */
            coap_init_message(message, COAP_TYPE_ACK, coap_error_code, message->mid);
            coap_set_payload(message, coap_error_message, strlen(coap_error_message));
            coap_send_message(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, uip_appdata, coap_serialize_message(message, uip_appdata));
        }
    } /* if (new data) */

    return coap_error_code;
}
Ejemplo n.º 2
0
/*---------------------------------------------------------------------------*/
int
coap_engine_receive(coap_context_t *coap_ctx)
{
  erbium_status_code = NO_ERROR;
  coap_packet_t message[1]; /* this way the packet can be treated as pointer as usual */
  coap_packet_t response[1];
  coap_transaction_t *transaction = NULL;

  PRINTF("%s(): received data len %u\n", __FUNCTION__,
         (uint16_t)uip_appdatalen(coap_ctx->buf));

  if(uip_newdata(coap_ctx->buf)) {

    PRINTF("receiving UDP datagram from: ");
    PRINT6ADDR(&UIP_IP_BUF(coap_ctx->buf)->srcipaddr);
    PRINTF(":%u\n  Length: %u\n",
	   uip_ntohs(UIP_UDP_BUF(coap_ctx->buf)->srcport),
           uip_appdatalen(coap_ctx->buf));

    erbium_status_code =
    coap_parse_message(message, uip_appdata(coap_ctx->buf), uip_appdatalen(coap_ctx->buf));
    coap_set_context(message, coap_ctx);

    if(erbium_status_code == NO_ERROR) {

      NET_COAP_STAT(recv++);

      /*TODO duplicates suppression, if required by application */

      PRINTF("  Parsed: v %u, t %u, tkl %u, c %u, mid %u\n", message->version,
             message->type, message->token_len, message->code, message->mid);
      PRINTF("  URL[%d]: %.*s\n", message->uri_path_len, message->uri_path_len, message->uri_path);
      PRINTF("  Payload[%d]: %.*s\n", message->payload_len, message->payload_len, message->payload);

      /* handle requests */
      if(message->code >= COAP_GET && message->code <= COAP_DELETE) {

        /* use transaction buffer for response to confirmable request */
        if((transaction = coap_new_transaction(message->mid, coap_ctx,
                                               &UIP_IP_BUF(coap_ctx->buf)->srcipaddr,
                                               UIP_UDP_BUF(coap_ctx->buf)->srcport))) {
          uint32_t block_num = 0;
          uint16_t block_size = REST_MAX_CHUNK_SIZE;
          uint32_t block_offset = 0;
          int32_t new_offset = 0;

          /* prepare response */
          if(message->type == COAP_TYPE_CON) {
            /* reliable CON requests are answered with an ACK */
            coap_init_message(response, COAP_TYPE_ACK, CONTENT_2_05,
                              message->mid);
          } else {
            /* unreliable NON requests are answered with a NON as well */
            coap_init_message(response, COAP_TYPE_NON, CONTENT_2_05,
                              coap_get_mid());
            /* mirror token */
          } if(message->token_len) {
            coap_set_token(response, message->token, message->token_len);
            /* get offset for blockwise transfers */
          }
          if(coap_get_header_block2
               (message, &block_num, NULL, &block_size, &block_offset)) {
            PRINTF("Blockwise: block request %lu (%u/%u) @ %lu bytes\n",
                   (unsigned long)block_num, block_size, REST_MAX_CHUNK_SIZE, (unsigned long)block_offset);
            block_size = MIN(block_size, REST_MAX_CHUNK_SIZE);
            new_offset = block_offset;
          }

          /* invoke resource handler */
          if(service_cbk) {

            /* call REST framework and check if found and allowed */
            if(service_cbk
                 (message, response, transaction->packet + COAP_MAX_HEADER_SIZE,
                 block_size, &new_offset)) {

              if(erbium_status_code == NO_ERROR) {

                /* TODO coap_handle_blockwise(request, response, start_offset, end_offset); */

                /* resource is unaware of Block1 */
                if(IS_OPTION(message, COAP_OPTION_BLOCK1)
                   && response->code < BAD_REQUEST_4_00
                   && !IS_OPTION(response, COAP_OPTION_BLOCK1)) {
                  PRINTF("Block1 NOT IMPLEMENTED\n");

                  erbium_status_code = NOT_IMPLEMENTED_5_01;
                  coap_error_message = "NoBlock1Support";

                  /* client requested Block2 transfer */
                } else if(IS_OPTION(message, COAP_OPTION_BLOCK2)) {

                  /* unchanged new_offset indicates that resource is unaware of blockwise transfer */
                  if(new_offset == block_offset) {
                    PRINTF
                      ("Blockwise: unaware resource with payload length %u/%u\n",
                      response->payload_len, block_size);
                    if(block_offset >= response->payload_len) {
                      PRINTF
                        ("handle_incoming_data(): block_offset >= response->payload_len\n");

                      response->code = BAD_OPTION_4_02;
                      coap_set_payload(response, "BlockOutOfScope", 15); /* a const char str[] and sizeof(str) produces larger code size */
                    } else {
                      coap_set_header_block2(response, block_num,
                                             response->payload_len -
                                             block_offset > block_size,
                                             block_size);
                      coap_set_payload(response,
                                       response->payload + block_offset,
                                       MIN(response->payload_len -
                                           block_offset, block_size));
                    } /* if(valid offset) */

                    /* resource provides chunk-wise data */
                  } else {
                    PRINTF("Blockwise: blockwise resource, new offset %ld\n",
                           (long)new_offset);
                    coap_set_header_block2(response, block_num,
                                           new_offset != -1
                                           || response->payload_len >
                                           block_size, block_size);

                    if(response->payload_len > block_size) {
                      coap_set_payload(response, response->payload,
                                       block_size);
                    }
                  } /* if(resource aware of blockwise) */

                  /* Resource requested Block2 transfer */
                } else if(new_offset != 0) {
                  PRINTF
                    ("Blockwise: no block option for blockwise resource, using block size %u\n",
                    COAP_MAX_BLOCK_SIZE);

                  coap_set_header_block2(response, 0, new_offset != -1,
                                         COAP_MAX_BLOCK_SIZE);
                  coap_set_payload(response, response->payload,
                                   MIN(response->payload_len,
                                       COAP_MAX_BLOCK_SIZE));
                } /* blockwise transfer handling */
              } /* no errors/hooks */
                /* successful service callback */
                /* serialize response */
            }
            if(erbium_status_code == NO_ERROR) {
              if((transaction->packet_len = coap_serialize_message(response,
                                                                   transaction->
                                                                   packet)) ==
                 0) {
                erbium_status_code = PACKET_SERIALIZATION_ERROR;
              }
            }
          } else {
            erbium_status_code = NOT_IMPLEMENTED_5_01;
            coap_error_message = "NoServiceCallbck"; /* no 'a' to fit into 16 bytes */
          } /* if(service callback) */
        } else {
          erbium_status_code = SERVICE_UNAVAILABLE_5_03;
          coap_error_message = "NoFreeTraBuffer";
        } /* if(transaction buffer) */

        /* handle responses */
      } else {

        if(message->type == COAP_TYPE_CON && message->code == 0) {
          PRINTF("Received Ping\n");
          erbium_status_code = PING_RESPONSE;
        } else if(message->type == COAP_TYPE_ACK) {
          /* transactions are closed through lookup below */
          PRINTF("Received ACK\n");
        } else if(message->type == COAP_TYPE_RST) {
          PRINTF("Received RST\n");
          /* cancel possible subscriptions */
          coap_remove_observer_by_mid(coap_ctx, &UIP_IP_BUF(coap_ctx->buf)->srcipaddr,
                                      UIP_UDP_BUF(coap_ctx->buf)->srcport, message->mid);
        }

        if((transaction = coap_get_transaction_by_mid(message->mid))) {
          /* free transaction memory before callback, as it may create a new transaction */
          restful_response_handler callback = transaction->callback;
          void *callback_data = transaction->callback_data;

          coap_clear_transaction(transaction);

          /* check if someone registered for the response */
          if(callback) {
            callback(callback_data, message);
          }
        }
        /* if(ACKed transaction) */
        transaction = NULL;

#if COAP_OBSERVE_CLIENT
	/* if observe notification */
        if((message->type == COAP_TYPE_CON || message->type == COAP_TYPE_NON)
              && IS_OPTION(message, COAP_OPTION_OBSERVE)) {
          PRINTF("Observe [%u]\n", message->observe);
          coap_handle_notification(coap_ctx, &UIP_IP_BUF(coap_ctx->buf)->srcipaddr,
                                   UIP_UDP_BUF(coap_ctx->buf)->srcport, message);
        }
#endif /* COAP_OBSERVE_CLIENT */
      } /* request or response */
    } else { /* parsed correctly */
      NET_COAP_STAT(recv_err++);
    }

    /* if(parsed correctly) */
    if(erbium_status_code == NO_ERROR) {
      if(transaction) {
        coap_send_transaction(transaction);
      }
    } else if(erbium_status_code == MANUAL_RESPONSE) {
      PRINTF("Clearing transaction for manual response");
      coap_clear_transaction(transaction);
    } else {
      coap_message_type_t reply_type = COAP_TYPE_ACK;

      PRINTF("ERROR %u: %s\n", erbium_status_code, coap_error_message);
      coap_clear_transaction(transaction);

      if(erbium_status_code == PING_RESPONSE) {
        erbium_status_code = 0;
        reply_type = COAP_TYPE_RST;
      } else if(erbium_status_code >= 192) {
        /* set to sendable error code */
        erbium_status_code = INTERNAL_SERVER_ERROR_5_00;
        /* reuse input buffer for error message */
      }
      coap_init_message(message, reply_type, erbium_status_code,
                        message->mid);
      coap_set_payload(message, coap_error_message,
                       strlen(coap_error_message));
      coap_send_message(coap_ctx, &UIP_IP_BUF(coap_ctx->buf)->srcipaddr,
			UIP_UDP_BUF(coap_ctx->buf)->srcport,
                        uip_appdata(coap_ctx->buf),
			coap_serialize_message(message,
					       uip_appdata(coap_ctx->buf)));
    }
  }

  /* if(new data) */
  return erbium_status_code;
}