/* Listen for an incoming CoAP message. */ static void _listen(sock_udp_t *sock) { coap_pkt_t pdu; uint8_t buf[GCOAP_PDU_BUF_SIZE]; sock_udp_ep_t remote; gcoap_request_memo_t *memo = NULL; uint8_t open_reqs = gcoap_op_state(); ssize_t res = sock_udp_recv(sock, buf, sizeof(buf), open_reqs > 0 ? GCOAP_RECV_TIMEOUT : SOCK_NO_TIMEOUT, &remote); if (res <= 0) { #if ENABLE_DEBUG if (res < 0 && res != -ETIMEDOUT) { DEBUG("gcoap: udp recv failure: %d\n", res); } #endif return; } res = coap_parse(&pdu, buf, res); if (res < 0) { DEBUG("gcoap: parse failure: %d\n", res); /* If a response, can't clear memo, but it will timeout later. */ return; } if (pdu.hdr->code == COAP_CODE_EMPTY) { DEBUG("gcoap: empty messages not handled yet\n"); return; /* incoming request */ } else if (coap_get_code_class(&pdu) == COAP_CLASS_REQ) { if (coap_get_type(&pdu) == COAP_TYPE_NON || coap_get_type(&pdu) == COAP_TYPE_CON) { size_t pdu_len = _handle_req(&pdu, buf, sizeof(buf), &remote); if (pdu_len > 0) { sock_udp_send(sock, buf, pdu_len, &remote); } } else { DEBUG("gcoap: illegal request type: %u\n", coap_get_type(&pdu)); return; } } /* incoming response */ else { _find_req_memo(&memo, &pdu, &remote); if (memo) { xtimer_remove(&memo->response_timer); memo->state = GCOAP_MEMO_RESP; memo->resp_handler(memo->state, &pdu, &remote); memo->state = GCOAP_MEMO_UNUSED; } } }
void coap_pretty_print(coap_pdu *pdu) { coap_option opt; opt.num = 0; if (coap_validate_pkt(pdu) == CE_NONE){ printf(" ------ Valid CoAP Packet (%zi) ------ \n", pdu->len); printf("Type: %i\n",coap_get_type(pdu)); printf("Code: %i.%02i\n", coap_get_code_class(pdu), coap_get_code_detail(pdu)); printf("MID: 0x%X\n", coap_get_mid(pdu)); printf("Token: 0x%llX\n", coap_get_token(pdu)); while ((opt = coap_get_option(pdu, &opt)).num != 0){ if (opt.num == 0) break; printf("Option: %i\n", opt.num); if (opt.val != NULL && opt.len != 0) printf(" Value: %.*s (%zi)\n", (int)opt.len, opt.val, opt.len); } // Note: get_option returns payload pointer when it finds the payload marker if (opt.val != NULL && opt.len != 0) printf("Payload: %.*s (%zi)\n", (int)opt.len, opt.val, opt.len); } else { printf(" ------ Non-CoAP Message (%zi) ------ \n", pdu->len); hex_dump(pdu->buf, pdu->len); } }
/* * Client GET response success case. Test parsing response. * Response for /time resource from libcoap example */ static void test_gcoap__client_get_resp(void) { uint8_t buf[GCOAP_PDU_BUF_SIZE]; coap_pkt_t pdu; int res; char exp_payload[] = "Oct 22 10:46:48"; uint8_t pdu_data[] = { 0x52, 0x45, 0xe6, 0x02, 0x9b, 0xce, 0xc0, 0x21, 0x01, 0xff, 0x4f, 0x63, 0x74, 0x20, 0x32, 0x32, 0x20, 0x31, 0x30, 0x3a, 0x34, 0x36, 0x3a, 0x34, 0x38 }; memcpy(buf, pdu_data, sizeof(pdu_data)); res = coap_parse(&pdu, &buf[0], sizeof(pdu_data)); TEST_ASSERT_EQUAL_INT(0, res); TEST_ASSERT_EQUAL_INT(COAP_CLASS_SUCCESS, coap_get_code_class(&pdu)); TEST_ASSERT_EQUAL_INT(GCOAP_TOKENLEN, coap_get_token_len(&pdu)); TEST_ASSERT_EQUAL_INT(4 + GCOAP_TOKENLEN, coap_get_total_hdr_len(&pdu)); TEST_ASSERT_EQUAL_INT(COAP_TYPE_NON, coap_get_type(&pdu)); TEST_ASSERT_EQUAL_INT(strlen(exp_payload), pdu.payload_len); for (size_t i = 0; i < strlen(exp_payload); i++) { TEST_ASSERT_EQUAL_INT(exp_payload[i], pdu.payload[i]); } }
/* * Server GET response success case. Test writing response. * Response for libcoap example for gcoap_cli /cli/stats resource */ static void test_gcoap__server_get_resp(void) { uint8_t buf[GCOAP_PDU_BUF_SIZE]; coap_pkt_t pdu; /* read request */ _read_cli_stats_req(&pdu, &buf[0]); /* generate response */ gcoap_resp_init(&pdu, &buf[0], sizeof(buf), COAP_CODE_CONTENT); char resp_payload[] = "2"; memcpy(&pdu.payload[0], &resp_payload[0], strlen(resp_payload)); ssize_t res = gcoap_finish(&pdu, strlen(resp_payload), COAP_FORMAT_TEXT); uint8_t resp_data[] = { 0x52, 0x45, 0x20, 0xb6, 0x35, 0x61, 0xc0, 0xff, 0x32 }; TEST_ASSERT_EQUAL_INT(COAP_CLASS_SUCCESS, coap_get_code_class(&pdu)); TEST_ASSERT_EQUAL_INT(2, coap_get_token_len(&pdu)); TEST_ASSERT_EQUAL_INT(4 + 2, coap_get_total_hdr_len(&pdu)); TEST_ASSERT_EQUAL_INT(COAP_TYPE_NON, coap_get_type(&pdu)); TEST_ASSERT_EQUAL_INT(strlen(resp_payload), pdu.payload_len); TEST_ASSERT_EQUAL_INT(sizeof(resp_data), res); for (size_t i = 0; i < strlen(resp_payload); i++) { TEST_ASSERT_EQUAL_INT(resp_payload[i], pdu.payload[i]); } }
/* * Client GET request with simple path. Test request generation. * Request /time resource from libcoap example */ static void test_nanocoap__get_req(void) { uint8_t buf[128]; coap_pkt_t pkt; uint16_t msgid = 0xABCD; uint8_t token[2] = {0xDA, 0xEC}; char path[] = "/time"; size_t total_hdr_len = 6; size_t total_opt_len = 5; size_t len = coap_build_hdr((coap_hdr_t *)&buf[0], COAP_TYPE_NON, &token[0], 2, COAP_METHOD_GET, msgid); TEST_ASSERT_EQUAL_INT(total_hdr_len, len); coap_pkt_init(&pkt, &buf[0], sizeof(buf), len); TEST_ASSERT_EQUAL_INT(COAP_METHOD_GET, coap_get_code(&pkt)); TEST_ASSERT_EQUAL_INT(2, coap_get_token_len(&pkt)); TEST_ASSERT_EQUAL_INT(total_hdr_len, coap_get_total_hdr_len(&pkt)); TEST_ASSERT_EQUAL_INT(COAP_TYPE_NON, coap_get_type(&pkt)); TEST_ASSERT_EQUAL_INT(122, pkt.payload_len); len = coap_opt_add_string(&pkt, COAP_OPT_URI_PATH, &path[0], '/'); TEST_ASSERT_EQUAL_INT(total_opt_len, len); char uri[10] = {0}; coap_get_uri(&pkt, (uint8_t *)&uri[0]); TEST_ASSERT_EQUAL_STRING((char *)path, (char *)uri); len = coap_opt_finish(&pkt, COAP_OPT_FINISH_NONE); TEST_ASSERT_EQUAL_INT(total_hdr_len + total_opt_len, len); }
/* Builds on test_nanocoap__server_get_req to test confirmable request. */ static void test_nanocoap__server_get_req_con(void) { uint8_t buf[_BUF_SIZE]; coap_pkt_t pkt; int res = _read_riot_value_req_con(&pkt, &buf[0]); TEST_ASSERT_EQUAL_INT(0, res); TEST_ASSERT_EQUAL_INT(COAP_METHOD_GET, coap_get_code(&pkt)); TEST_ASSERT_EQUAL_INT(COAP_TYPE_CON, coap_get_type(&pkt)); }
/* Builds on test_nanocoap__server_reply_simple to test confirmable request. */ static void test_nanocoap__server_reply_simple_con(void) { uint8_t buf[_BUF_SIZE]; coap_pkt_t pkt; char *payload = "0"; _read_riot_value_req_con(&pkt, &buf[0]); coap_reply_simple(&pkt, COAP_CODE_CONTENT, buf, _BUF_SIZE, COAP_FORMAT_TEXT, (uint8_t *)payload, 1); TEST_ASSERT_EQUAL_INT(COAP_TYPE_ACK, coap_get_type(&pkt)); }
/* Server GET request success case. Validate request example. */ static void test_gcoap__server_get_req(void) { uint8_t buf[GCOAP_PDU_BUF_SIZE]; coap_pkt_t pdu; int res = _read_cli_stats_req(&pdu, &buf[0]); TEST_ASSERT_EQUAL_INT(0, res); TEST_ASSERT_EQUAL_INT(COAP_METHOD_GET, coap_get_code(&pdu)); TEST_ASSERT_EQUAL_INT(2, coap_get_token_len(&pdu)); TEST_ASSERT_EQUAL_INT(4 + 2, coap_get_total_hdr_len(&pdu)); TEST_ASSERT_EQUAL_INT(COAP_TYPE_NON, coap_get_type(&pdu)); TEST_ASSERT_EQUAL_INT(0, pdu.payload_len); TEST_ASSERT_EQUAL_STRING("/cli/stats", (char *) &pdu.url[0]); }
/* Response for server GET request using coap_reply_simple(). */ static void test_nanocoap__server_reply_simple(void) { uint8_t buf[_BUF_SIZE]; coap_pkt_t pkt; char *payload = "0"; int res = _read_riot_value_req(&pkt, &buf[0]); coap_reply_simple(&pkt, COAP_CODE_CONTENT, buf, _BUF_SIZE, COAP_FORMAT_TEXT, (uint8_t *)payload, 1); TEST_ASSERT_EQUAL_INT(0, res); TEST_ASSERT_EQUAL_INT(COAP_CODE_CONTENT, coap_get_code_raw(&pkt)); TEST_ASSERT_EQUAL_INT(2, coap_get_token_len(&pkt)); TEST_ASSERT_EQUAL_INT(4 + 2, coap_get_total_hdr_len(&pkt)); TEST_ASSERT_EQUAL_INT(COAP_TYPE_NON, coap_get_type(&pkt)); }
/* Server GET request success case. */ static void test_nanocoap__server_get_req(void) { uint8_t buf[_BUF_SIZE]; coap_pkt_t pkt; char path[] = "/riot/value"; int res = _read_riot_value_req(&pkt, &buf[0]); TEST_ASSERT_EQUAL_INT(0, res); TEST_ASSERT_EQUAL_INT(COAP_METHOD_GET, coap_get_code(&pkt)); TEST_ASSERT_EQUAL_INT(2, coap_get_token_len(&pkt)); TEST_ASSERT_EQUAL_INT(4 + 2, coap_get_total_hdr_len(&pkt)); TEST_ASSERT_EQUAL_INT(COAP_TYPE_NON, coap_get_type(&pkt)); TEST_ASSERT_EQUAL_INT(0, pkt.payload_len); char uri[64] = {0}; coap_get_uri_path(&pkt, (uint8_t *)&uri[0]); TEST_ASSERT_EQUAL_STRING((char *)path, (char *)uri); }
/* * Client GET request success case. Test request generation. * Request /time resource from libcoap example * Includes 2-byte token */ static void test_gcoap__client_get_req(void) { uint8_t buf[GCOAP_PDU_BUF_SIZE]; coap_pkt_t pdu; size_t len; char path[] = "/time"; uint8_t pdu_data[] = { 0x52, 0x01, 0xe6, 0x02, 0x9b, 0xce, 0xb4, 0x74, 0x69, 0x6d, 0x65 }; len = gcoap_request(&pdu, &buf[0], GCOAP_PDU_BUF_SIZE, COAP_METHOD_GET, &path[0]); TEST_ASSERT_EQUAL_INT(COAP_METHOD_GET, coap_get_code(&pdu)); TEST_ASSERT_EQUAL_INT(GCOAP_TOKENLEN, coap_get_token_len(&pdu)); TEST_ASSERT_EQUAL_INT(4 + GCOAP_TOKENLEN, coap_get_total_hdr_len(&pdu)); TEST_ASSERT_EQUAL_INT(COAP_TYPE_NON, coap_get_type(&pdu)); TEST_ASSERT_EQUAL_STRING(&path[0], (char *)&pdu.url[0]); TEST_ASSERT_EQUAL_INT(0, pdu.payload_len); TEST_ASSERT_EQUAL_INT(sizeof(pdu_data), len); }
/* Listen for an incoming CoAP message. */ static void _listen(sock_udp_t *sock) { coap_pkt_t pdu; uint8_t buf[GCOAP_PDU_BUF_SIZE]; sock_udp_ep_t remote; gcoap_request_memo_t *memo = NULL; uint8_t open_reqs = gcoap_op_state(); /* We expect a -EINTR response here when unlimited waiting (SOCK_NO_TIMEOUT) * is interrupted when sending a message in gcoap_req_send2(). While a * request is outstanding, sock_udp_recv() is called here with limited * waiting so the request's timeout can be handled in a timely manner in * _event_loop(). */ ssize_t res = sock_udp_recv(sock, buf, sizeof(buf), open_reqs > 0 ? GCOAP_RECV_TIMEOUT : SOCK_NO_TIMEOUT, &remote); if (res <= 0) { #if ENABLE_DEBUG if (res < 0 && res != -ETIMEDOUT) { DEBUG("gcoap: udp recv failure: %d\n", res); } #endif return; } res = coap_parse(&pdu, buf, res); if (res < 0) { DEBUG("gcoap: parse failure: %d\n", (int)res); /* If a response, can't clear memo, but it will timeout later. */ return; } if (pdu.hdr->code == COAP_CODE_EMPTY) { DEBUG("gcoap: empty messages not handled yet\n"); return; } /* validate class and type for incoming */ switch (coap_get_code_class(&pdu)) { /* incoming request */ case COAP_CLASS_REQ: if (coap_get_type(&pdu) == COAP_TYPE_NON || coap_get_type(&pdu) == COAP_TYPE_CON) { size_t pdu_len = _handle_req(&pdu, buf, sizeof(buf), &remote); if (pdu_len > 0) { ssize_t bytes = sock_udp_send(sock, buf, pdu_len, &remote); if (bytes <= 0) { DEBUG("gcoap: send response failed: %d\n", (int)bytes); } } } else { DEBUG("gcoap: illegal request type: %u\n", coap_get_type(&pdu)); } break; /* incoming response */ case COAP_CLASS_SUCCESS: case COAP_CLASS_CLIENT_FAILURE: case COAP_CLASS_SERVER_FAILURE: _find_req_memo(&memo, &pdu, &remote); if (memo) { switch (coap_get_type(&pdu)) { case COAP_TYPE_NON: case COAP_TYPE_ACK: xtimer_remove(&memo->response_timer); memo->state = GCOAP_MEMO_RESP; if (memo->resp_handler) { memo->resp_handler(memo->state, &pdu, &remote); } if (memo->send_limit >= 0) { /* if confirmable */ *memo->msg.data.pdu_buf = 0; /* clear resend PDU buffer */ } memo->state = GCOAP_MEMO_UNUSED; break; case COAP_TYPE_CON: DEBUG("gcoap: separate CON response not handled yet\n"); break; default: DEBUG("gcoap: illegal response type: %u\n", coap_get_type(&pdu)); break; } } else { DEBUG("gcoap: msg not found for ID: %u\n", coap_get_id(&pdu)); } break; default: DEBUG("gcoap: illegal code class: %u\n", coap_get_code_class(&pdu)); } }