| /* |
| * Copyright (c) 2016 Intel Corporation |
| * |
| * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the Institute nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * |
| * This file is part of the Contiki operating system. |
| */ |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <assert.h> |
| |
| #include "os/mynewt.h" |
| |
| #include "oic/port/mynewt/config.h" |
| #include "oic/messaging/coap/coap.h" |
| #include "oic/messaging/coap/transactions.h" |
| |
| #include "oic/oc_buffer.h" |
| #ifdef OC_SECURITY |
| #include "security/oc_dtls.h" |
| #endif |
| |
| STATS_SECT_DECL(coap_stats) coap_stats; |
| STATS_NAME_START(coap_stats) |
| STATS_NAME(coap_stats, iframe) |
| STATS_NAME(coap_stats, ierr) |
| STATS_NAME(coap_stats, itoobig) |
| STATS_NAME(coap_stats, ilen) |
| STATS_NAME(coap_stats, imem) |
| STATS_NAME(coap_stats, oframe) |
| STATS_NAME(coap_stats, oerr) |
| STATS_NAME_END(coap_stats) |
| |
| /*---------------------------------------------------------------------------*/ |
| /*- Variables ---------------------------------------------------------------*/ |
| /*---------------------------------------------------------------------------*/ |
| static uint16_t current_mid = 0; |
| |
| coap_status_t erbium_status_code = NO_ERROR; |
| char *coap_error_message = ""; |
| /*---------------------------------------------------------------------------*/ |
| /*- Local helper functions --------------------------------------------------*/ |
| /*---------------------------------------------------------------------------*/ |
| static uint16_t |
| coap_log_2(uint16_t value) |
| { |
| uint16_t result = 0; |
| |
| do { |
| value = value >> 1; |
| result++; |
| } while (value); |
| |
| return (result - 1); |
| } |
| /*---------------------------------------------------------------------------*/ |
| static uint32_t |
| coap_parse_int_option(struct os_mbuf *m, uint16_t off, size_t length) |
| { |
| uint8_t bytes[4]; |
| uint32_t var = 0; |
| int i = 0; |
| |
| if (length >= 4) { |
| return -1; |
| } |
| if (os_mbuf_copydata(m, off, length, bytes)) { |
| return -1; |
| } |
| while (i < length) { |
| var <<= 8; |
| var |= bytes[i++]; |
| } |
| return var; |
| } |
| /*---------------------------------------------------------------------------*/ |
| static uint8_t |
| coap_option_nibble(unsigned int value) |
| { |
| if (value < 13) { |
| return value; |
| } else if (value <= 0xFF + 13) { |
| return 13; |
| } else { |
| return 14; |
| } |
| } |
| /*---------------------------------------------------------------------------*/ |
| |
| static int |
| coap_append_opt_hdr(struct os_mbuf *m, unsigned int delta, size_t length) |
| { |
| uint8_t buffer[4]; |
| size_t written = 0; |
| |
| buffer[0] = coap_option_nibble(delta) << 4 | coap_option_nibble(length); |
| |
| if (delta > 268) { |
| buffer[++written] = ((delta - 269) >> 8) & 0xff; |
| buffer[++written] = (delta - 269) & 0xff; |
| } else if (delta > 12) { |
| buffer[++written] = (delta - 13); |
| } |
| |
| if (length > 268) { |
| buffer[++written] = ((length - 269) >> 8) & 0xff; |
| buffer[++written] = (length - 269) & 0xff; |
| } else if (length > 12) { |
| buffer[++written] = (length - 13); |
| } |
| |
| return os_mbuf_append(m, buffer, written + 1); |
| } |
| |
| /*---------------------------------------------------------------------------*/ |
| static int |
| coap_append_int_opt(struct os_mbuf *m, unsigned int number, |
| unsigned int current_number, uint32_t value) |
| { |
| size_t i = 0; |
| uint8_t buffer[4]; |
| int rc; |
| |
| if (0xFF000000 & value) { |
| ++i; |
| } |
| if (0xFFFF0000 & value) { |
| ++i; |
| } |
| if (0xFFFFFF00 & value) { |
| ++i; |
| } |
| if (0xFFFFFFFF & value) { |
| ++i; |
| } |
| OC_LOG(DEBUG, "OPTION %u (delta %u, len %zu)\n", |
| number, number - current_number, i); |
| |
| rc = coap_append_opt_hdr(m, number - current_number, i); |
| if (rc) { |
| return rc; |
| } |
| |
| i = 0; |
| if (0xFF000000 & value) { |
| buffer[i++] = (uint8_t)(value >> 24); |
| } |
| if (0xFFFF0000 & value) { |
| buffer[i++] = (uint8_t)(value >> 16); |
| } |
| if (0xFFFFFF00 & value) { |
| buffer[i++] = (uint8_t)(value >> 8); |
| } |
| if (0xFFFFFFFF & value) { |
| buffer[i++] = (uint8_t)(value); |
| } |
| return os_mbuf_append(m, buffer, i); |
| } |
| /*---------------------------------------------------------------------------*/ |
| static int |
| coap_append_array_opt(struct os_mbuf *m, |
| unsigned int number, unsigned int current_number, |
| uint8_t *array, size_t length, char split_char) |
| { |
| int rc; |
| int j; |
| uint8_t *part_start = array; |
| uint8_t *part_end = NULL; |
| size_t blk; |
| |
| OC_LOG(DEBUG, "ARRAY type %u, len %zu\n", number, length); |
| |
| if (split_char != '\0') { |
| for (j = 0; j <= length + 1; ++j) { |
| if (array[j] == split_char || j == length) { |
| part_end = array + j; |
| blk = part_end - part_start; |
| |
| rc = coap_append_opt_hdr(m, number - current_number, blk); |
| if (rc) { |
| return rc; |
| } |
| rc = os_mbuf_append(m, part_start, blk); |
| if (rc) { |
| return rc; |
| } |
| |
| OC_LOG(DEBUG, "OPTION type %u, delta %u, len %zu\n", number, |
| number - current_number, (int)blk); |
| |
| ++j; /* skip the splitter */ |
| current_number = number; |
| part_start = array + j; |
| } |
| } /* for */ |
| } else { |
| rc = coap_append_opt_hdr(m, number - current_number, length); |
| if (rc) { |
| return rc; |
| } |
| rc = os_mbuf_append(m, array, length); |
| if (rc) { |
| return rc; |
| } |
| |
| OC_LOG(DEBUG, "OPTION type %u, delta %u, len %zu\n", number, |
| number - current_number, length); |
| } |
| |
| return 0; |
| } |
| /*---------------------------------------------------------------------------*/ |
| |
| static void |
| coap_merge_multi_option(struct os_mbuf *m, uint16_t *off1, uint16_t *len1, |
| uint16_t off2, uint16_t len2, char separator) |
| { |
| uint8_t tmp[4]; |
| int i; |
| int blk; |
| |
| /* merge multiple options */ |
| if (*len1 > 0) { |
| /* dst already contains an option: concatenate */ |
| os_mbuf_copyinto(m, *off1 + *len1, &separator, 1); |
| *len1 += 1; |
| |
| for (i = 0; i < len2; i += blk) { |
| blk = min(sizeof(tmp), len2 - i); |
| os_mbuf_copydata(m, off2 + i, blk, tmp); |
| os_mbuf_copyinto(m, *off1 + *len1 + i, tmp, blk); |
| } |
| |
| *len1 += len2; |
| } else { |
| /* off1 is empty: set to off2 */ |
| *off1 = off2; |
| *len1 = len2; |
| } |
| } |
| |
| /*---------------------------------------------------------------------------*/ |
| #if 0 |
| static int |
| coap_get_variable(const char *buffer, size_t length, const char *name, |
| const char **output) |
| { |
| const char *start = NULL; |
| const char *end = NULL; |
| const char *value_end = NULL; |
| size_t name_len = 0; |
| |
| /*initialize the output buffer first */ |
| *output = 0; |
| |
| name_len = strlen(name); |
| end = buffer + length; |
| |
| for(start = buffer; start + name_len < end; ++start) { |
| if ((start == buffer || start[-1] == '&') && start[name_len] == '=' && |
| strncmp(name, start, name_len) == 0) { |
| |
| /* Point start to variable value */ |
| start += name_len + 1; |
| |
| /* Point end to the end of the value */ |
| value_end = (const char *)memchr(start, '&', end - start); |
| if(value_end == NULL) { |
| value_end = end; |
| } |
| *output = start; |
| |
| return value_end - start; |
| } |
| } |
| return 0; |
| } |
| #endif |
| /*---------------------------------------------------------------------------*/ |
| /*- Internal API ------------------------------------------------------------*/ |
| /*---------------------------------------------------------------------------*/ |
| void |
| coap_init_connection(void) |
| { |
| /* initialize transaction ID */ |
| current_mid = oc_random_rand(); |
| |
| (void)stats_init_and_reg(STATS_HDR(coap_stats), |
| STATS_SIZE_INIT_PARMS(coap_stats, STATS_SIZE_32), |
| STATS_NAME_INIT_PARMS(coap_stats), "coap"); |
| } |
| /*---------------------------------------------------------------------------*/ |
| uint16_t |
| coap_get_mid(void) |
| { |
| return ++current_mid; |
| } |
| /*---------------------------------------------------------------------------*/ |
| void |
| coap_init_message(coap_packet_t *pkt, coap_message_type_t type, |
| uint8_t code, uint16_t mid) |
| { |
| /* Important thing */ |
| memset(pkt, 0, sizeof(coap_packet_t)); |
| |
| pkt->type = type; |
| pkt->code = code; |
| pkt->mid = mid; |
| } |
| |
| /*---------------------------------------------------------------------------*/ |
| |
| int |
| coap_serialize_message(coap_packet_t *pkt, struct os_mbuf *m) |
| { |
| struct coap_udp_hdr *cuh; |
| struct coap_tcp_hdr0 *cth0; |
| struct coap_tcp_hdr8 *cth8; |
| struct coap_tcp_hdr16 *cth16; |
| struct coap_tcp_hdr32 *cth32; |
| unsigned int current_number = 0; |
| uint16_t u16; |
| uint32_t u32; |
| int tcp_hdr; |
| int len, data_len; |
| |
| /* Initialize */ |
| pkt->version = 1; |
| |
| OC_LOG(DEBUG, "coap_tx: 0x%x\n", (unsigned)m); |
| |
| tcp_hdr = oc_endpoint_use_tcp(OC_MBUF_ENDPOINT(m)); |
| |
| /* |
| * Move data pointer, leave enough space to insert coap header and |
| * token before options. |
| */ |
| m->om_data += (sizeof(struct coap_tcp_hdr32) + pkt->token_len); |
| |
| /* Serialize options */ |
| current_number = 0; |
| |
| #if 0 |
| /* The options must be serialized in the order of their number */ |
| COAP_SERIALIZE_BYTE_OPT(pkt, m, COAP_OPTION_IF_MATCH, if_match, "If-Match"); |
| COAP_SERIALIZE_STRING_OPT(pkt, m, COAP_OPTION_URI_HOST, uri_host, '\0', |
| "Uri-Host"); |
| COAP_SERIALIZE_BYTE_OPT(pkt, m, COAP_OPTION_ETAG, etag, "ETag"); |
| COAP_SERIALIZE_INT_OPT(pkt, m, COAP_OPTION_IF_NONE_MATCH, |
| content_format - pkt-> content_format /* hack to get a zero field */, |
| "If-None-Match"); |
| #endif |
| COAP_SERIALIZE_INT_OPT(pkt, m, COAP_OPTION_OBSERVE, observe, "Observe"); |
| #if 0 |
| COAP_SERIALIZE_INT_OPT(pkt, m, COAP_OPTION_URI_PORT, uri_port, |
| "Uri-Port"); |
| COAP_SERIALIZE_STRING_OPT(pkt, m, COAP_OPTION_LOCATION_PATH, |
| location_path, '/', "Location-Path"); |
| #endif |
| COAP_SERIALIZE_STRING_OPT(pkt, m, COAP_OPTION_URI_PATH, uri_path, '/', |
| "Uri-Path"); |
| |
| COAP_SERIALIZE_INT_OPT(pkt, m, COAP_OPTION_CONTENT_FORMAT, content_format, |
| "Content-Format"); |
| #if 0 |
| COAP_SERIALIZE_INT_OPT(pkt, m, COAP_OPTION_MAX_AGE, max_age, "Max-Age"); |
| #endif |
| COAP_SERIALIZE_STRING_OPT(pkt, m, COAP_OPTION_URI_QUERY, uri_query, '&', |
| "Uri-Query"); |
| COAP_SERIALIZE_INT_OPT(pkt, m, COAP_OPTION_ACCEPT, accept, "Accept"); |
| #if 0 |
| COAP_SERIALIZE_STRING_OPT(pkt, m, COAP_OPTION_LOCATION_QUERY, |
| location_query, '&', "Location-Query"); |
| #endif |
| COAP_SERIALIZE_BLOCK_OPT(pkt, m, COAP_OPTION_BLOCK2, block2, "Block2"); |
| COAP_SERIALIZE_BLOCK_OPT(pkt, m, COAP_OPTION_BLOCK1, block1, "Block1"); |
| COAP_SERIALIZE_INT_OPT(pkt, m, COAP_OPTION_SIZE2, size2, "Size2"); |
| #if 0 |
| COAP_SERIALIZE_STRING_OPT(pkt, m, COAP_OPTION_PROXY_URI, proxy_uri, '\0', |
| "Proxy-Uri"); |
| COAP_SERIALIZE_STRING_OPT(pkt, m, COAP_OPTION_PROXY_SCHEME, proxy_scheme, |
| '\0', "Proxy-Scheme"); |
| #endif |
| COAP_SERIALIZE_INT_OPT(pkt, m, COAP_OPTION_SIZE1, size1, "Size1"); |
| |
| /* Payload marker */ |
| if (pkt->payload_len) { |
| if (os_mbuf_append(m, "\xff", 1)) { |
| goto err_mem; |
| } |
| } |
| data_len = OS_MBUF_PKTLEN(m) + pkt->payload_len; |
| |
| /* |
| * Set header fields, aka CoAP header alignment nightmare. |
| */ |
| if (!tcp_hdr) { |
| len = sizeof(struct coap_udp_hdr) + pkt->token_len; |
| os_mbuf_prepend(m, len); |
| cuh = (struct coap_udp_hdr *)m->om_data; |
| cuh->version = pkt->version; |
| cuh->type = pkt->type; |
| cuh->token_len = pkt->token_len; |
| cuh->code = pkt->code; |
| u16 = htons(pkt->mid); |
| memcpy(&cuh->id, &u16, sizeof(u16)); |
| memcpy(cuh + 1, pkt->token, pkt->token_len); |
| } else { |
| if (data_len < 13) { |
| len = sizeof(struct coap_tcp_hdr0) + pkt->token_len; |
| os_mbuf_prepend(m, len); |
| cth0 = (struct coap_tcp_hdr0 *)m->om_data; |
| cth0->data_len = data_len; |
| cth0->token_len = pkt->token_len; |
| cth0->code = pkt->code; |
| memcpy(cth0 + 1, pkt->token, pkt->token_len); |
| } else if (data_len < 269) { |
| len = sizeof(struct coap_tcp_hdr8) + pkt->token_len; |
| os_mbuf_prepend(m, len); |
| cth8 = (struct coap_tcp_hdr8 *)m->om_data; |
| cth8->type = COAP_TCP_TYPE8; |
| cth8->token_len = pkt->token_len; |
| cth8->data_len = data_len - COAP_TCP_LENGTH8_OFF; |
| cth8->code = pkt->code; |
| memcpy(cth8 + 1, pkt->token, pkt->token_len); |
| } else if (data_len < 65805) { |
| len = sizeof(struct coap_tcp_hdr16) + pkt->token_len; |
| os_mbuf_prepend(m, len); |
| cth16 = (struct coap_tcp_hdr16 *)m->om_data; |
| cth16->type = COAP_TCP_TYPE16; |
| cth16->token_len = pkt->token_len; |
| u16 = htons(data_len - COAP_TCP_LENGTH16_OFF); |
| memcpy(&cth16->data_len, &u16, sizeof(u16)); |
| cth16->code = pkt->code; |
| memcpy(cth16 + 1, pkt->token, pkt->token_len); |
| } else { |
| len = sizeof(struct coap_tcp_hdr32) + pkt->token_len; |
| os_mbuf_prepend(m, len); |
| cth32 = (struct coap_tcp_hdr32 *)m->om_data; |
| cth32->type = COAP_TCP_TYPE32; |
| cth32->token_len = pkt->token_len; |
| u32 = htonl(data_len - COAP_TCP_LENGTH32_OFF); |
| memcpy(&cth32->data_len, &u32, sizeof(u32)); |
| cth32->code = pkt->code; |
| memcpy(cth32 + 1, pkt->token, pkt->token_len); |
| } |
| } |
| |
| if (pkt->payload_m) { |
| assert(pkt->payload_len <= OS_MBUF_PKTLEN(pkt->payload_m)); |
| if (pkt->payload_len < OS_MBUF_PKTLEN(pkt->payload_m)) { |
| os_mbuf_adj(pkt->payload_m, |
| OS_MBUF_PKTLEN(pkt->payload_m) - pkt->payload_len); |
| } |
| os_mbuf_concat(m, pkt->payload_m); |
| } |
| OC_LOG(DEBUG, "coap_tx: serialized %u B (header len %u, payload len %u)\n", |
| OS_MBUF_PKTLEN(m), OS_MBUF_PKTLEN(m) - pkt->payload_len, |
| pkt->payload_len); |
| |
| return 0; |
| err_mem: |
| if (pkt->payload_m) { |
| os_mbuf_free_chain(pkt->payload_m); |
| } |
| STATS_INC(coap_stats, oerr); |
| return -1; |
| } |
| /*---------------------------------------------------------------------------*/ |
| void |
| coap_send_message(struct os_mbuf *m, int dup) |
| { |
| OC_LOG(INFO, "coap_send_message(): (%u) %s\n", OS_MBUF_PKTLEN(m), |
| dup ? "dup" : ""); |
| |
| STATS_INC(coap_stats, oframe); |
| |
| if (dup) { |
| m = os_mbuf_dup(m); |
| if (!m) { |
| STATS_INC(coap_stats, oerr); |
| return; |
| } |
| } |
| oc_send_message(m); |
| } |
| |
| /* |
| * Given COAP message header, return the number of bytes to expect for |
| * this frame. |
| */ |
| uint16_t |
| coap_tcp_msg_size(uint8_t *hdr, int datalen) |
| { |
| struct coap_tcp_hdr0 *cth0; |
| struct coap_tcp_hdr8 *cth8; |
| struct coap_tcp_hdr16 *cth16; |
| struct coap_tcp_hdr32 *cth32; |
| |
| if (datalen < sizeof(*cth32)) { |
| return -1; |
| } |
| cth0 = (struct coap_tcp_hdr0 *)hdr; |
| if (cth0->data_len < COAP_TCP_TYPE8) { |
| return cth0->data_len + sizeof(*cth0) + cth0->token_len; |
| } else if (cth0->data_len == COAP_TCP_TYPE8) { |
| cth8 = (struct coap_tcp_hdr8 *)hdr; |
| return cth8->data_len + sizeof(*cth8) + cth8->token_len + |
| COAP_TCP_LENGTH8_OFF; |
| } else if (cth0->data_len == COAP_TCP_TYPE16) { |
| cth16 = (struct coap_tcp_hdr16 *)hdr; |
| return ntohs(cth16->data_len) + sizeof(*cth16) + cth16->token_len + |
| COAP_TCP_LENGTH16_OFF; |
| } else if (cth0->data_len == COAP_TCP_TYPE32) { |
| cth32 = (struct coap_tcp_hdr32 *)hdr; |
| return ntohl(cth32->data_len) + sizeof(*cth32) + cth32->token_len + |
| COAP_TCP_LENGTH32_OFF; |
| } else { |
| /* never here */ |
| return -1; |
| } |
| } |
| |
| /*---------------------------------------------------------------------------*/ |
| coap_status_t |
| coap_parse_message(struct coap_packet_rx *pkt, struct os_mbuf **mp) |
| { |
| struct os_mbuf *m; |
| int is_tcp; |
| struct coap_udp_hdr udp; |
| union { |
| struct coap_tcp_hdr0 c0; |
| struct coap_tcp_hdr8 c8; |
| struct coap_tcp_hdr16 c16; |
| struct coap_tcp_hdr32 c32; |
| } cth; |
| uint8_t tmp[4]; |
| uint16_t cur_opt; |
| unsigned int opt_num = 0; |
| unsigned int opt_delta = 0; |
| size_t opt_len = 0; |
| uint8_t data_len; |
| int rc; |
| |
| m = *mp; |
| /* initialize packet */ |
| memset(pkt, 0, sizeof(*pkt)); |
| |
| STATS_INC(coap_stats, iframe); |
| |
| is_tcp = oc_endpoint_use_tcp(OC_MBUF_ENDPOINT(m)); |
| |
| /* |
| * Make sure that the header is in contiguous area of memory. |
| */ |
| opt_len = sizeof(struct coap_tcp_hdr32); |
| if (opt_len > OS_MBUF_PKTLEN(m)) { |
| opt_len = OS_MBUF_PKTLEN(m); |
| } |
| if (m->om_len < opt_len) { |
| m = os_mbuf_pullup(m, opt_len); |
| *mp = m; |
| if (!m) { |
| STATS_INC(coap_stats, imem); |
| return INTERNAL_SERVER_ERROR_5_00; |
| } |
| } |
| pkt->m = m; |
| |
| /* parse header fields */ |
| if (!is_tcp) { |
| cur_opt = sizeof(udp); |
| rc = os_mbuf_copydata(m, 0, sizeof(udp), &udp); |
| if (rc != 0) { |
| err_short: |
| STATS_INC(coap_stats, ilen); |
| return BAD_REQUEST_4_00; |
| } |
| pkt->version = udp.version; |
| pkt->type = udp.type; |
| pkt->token_len = udp.token_len; |
| pkt->code = udp.code; |
| pkt->mid = ntohs(udp.id); |
| if (pkt->version != 1) { |
| coap_error_message = "CoAP version must be 1"; |
| STATS_INC(coap_stats, ierr); |
| return BAD_REQUEST_4_00; |
| } |
| } else { |
| /* |
| * We cannot just look at the data length, as token might or might |
| * not be present. Need to figure out which header is present |
| * programmatically. |
| */ |
| rc = os_mbuf_copydata(m, 0, sizeof(cth.c0), &cth.c0); |
| if (rc != 0) { |
| goto err_short; |
| } |
| data_len = cth.c0.data_len; |
| |
| if (data_len < 13) { |
| cur_opt = sizeof(cth.c0); |
| if (m->om_len < cur_opt) { |
| goto err_short; |
| } |
| pkt->token_len = cth.c0.token_len; |
| pkt->code = cth.c0.code; |
| } else if (data_len == 13) { |
| cur_opt = sizeof(cth.c8); |
| rc = os_mbuf_copydata(m, 0, sizeof(cth.c8), &cth.c8); |
| if (rc != 0) { |
| goto err_short; |
| } |
| pkt->token_len = cth.c8.token_len; |
| pkt->code = cth.c8.code; |
| } else if (data_len == 14) { |
| cur_opt = sizeof(cth.c16); |
| rc = os_mbuf_copydata(m, 0, sizeof(cth.c16), &cth.c16); |
| if (rc != 0) { |
| goto err_short; |
| } |
| pkt->token_len = cth.c16.token_len; |
| pkt->code = cth.c16.code; |
| } else { |
| cur_opt = sizeof(cth.c32); |
| rc = os_mbuf_copydata(m, 0, sizeof(cth.c32), &cth.c32); |
| if (rc != 0) { |
| goto err_short; |
| } |
| pkt->token_len = cth.c32.token_len; |
| pkt->code = cth.c32.code; |
| } |
| } |
| if (pkt->token_len > COAP_TOKEN_LEN) { |
| coap_error_message = "Token Length must not be more than 8"; |
| STATS_INC(coap_stats, ierr); |
| return BAD_REQUEST_4_00; |
| } |
| |
| if (os_mbuf_copydata(m, cur_opt, pkt->token_len, pkt->token)) { |
| goto err_short; |
| } |
| cur_opt += pkt->token_len; |
| |
| OC_LOG(DEBUG, "Token (len %u) ", pkt->token_len); |
| OC_LOG_HEX(LOG_LEVEL_DEBUG, pkt->token, pkt->token_len); |
| |
| /* parse options */ |
| memset(pkt->options, 0, sizeof(pkt->options)); |
| |
| while (cur_opt < OS_MBUF_PKTLEN(m)) { |
| /* payload marker 0xFF, currently only checking for 0xF* because rest is |
| * reserved */ |
| if (os_mbuf_copydata(m, cur_opt, 1, tmp)) { |
| goto err_short; |
| } |
| if ((tmp[0] & 0xF0) == 0xF0) { |
| pkt->payload_off = ++cur_opt; |
| pkt->payload_len = OS_MBUF_PKTLEN(m) - cur_opt; |
| |
| /* also for receiving, the Erbium upper bound is MAX_PAYLOAD_SIZE */ |
| if (pkt->payload_len > MAX_PAYLOAD_SIZE) { |
| pkt->payload_len = MAX_PAYLOAD_SIZE; |
| } |
| break; |
| } |
| |
| opt_delta = tmp[0] >> 4; |
| opt_len = tmp[0] & 0x0F; |
| ++cur_opt; |
| |
| if (opt_delta == 13) { |
| if (os_mbuf_copydata(m, cur_opt, 1, tmp)) { |
| goto err_short; |
| } |
| opt_delta += tmp[0]; |
| ++cur_opt; |
| } else if (opt_delta == 14) { |
| if (os_mbuf_copydata(m, cur_opt, 2, tmp)) { |
| goto err_short; |
| } |
| opt_delta += (255 + (tmp[0] << 8) + tmp[1]); |
| cur_opt += 2; |
| } |
| |
| if (opt_len == 13) { |
| if (os_mbuf_copydata(m, cur_opt, 1, tmp)) { |
| goto err_short; |
| } |
| opt_len += tmp[0]; |
| ++cur_opt; |
| } else if (opt_len == 14) { |
| if (os_mbuf_copydata(m, cur_opt, 2, tmp)) { |
| goto err_short; |
| } |
| opt_len += (255 + (tmp[0] << 8) + tmp[1]); |
| cur_opt += 2; |
| } |
| |
| opt_num += opt_delta; |
| |
| if (opt_num < COAP_OPTION_SIZE1) { |
| OC_LOG(DEBUG, "OPTION %u (delta %u, len %zu): ", |
| opt_num, opt_delta, opt_len); |
| SET_OPTION(pkt, opt_num); |
| } |
| |
| switch (opt_num) { |
| case COAP_OPTION_CONTENT_FORMAT: |
| pkt->content_format = coap_parse_int_option(m, cur_opt, opt_len); |
| OC_LOG(DEBUG, "Content-Format [%u]\n", pkt->content_format); |
| break; |
| case COAP_OPTION_MAX_AGE: |
| pkt->max_age = coap_parse_int_option(m, cur_opt, opt_len); |
| OC_LOG(DEBUG, "Max-Age [%lu]\n", (unsigned long)pkt->max_age); |
| break; |
| #if 0 |
| case COAP_OPTION_ETAG: |
| pkt->etag_len = MIN(COAP_ETAG_LEN, opt_len); |
| if (os_mbuf_copydata(m, cur_opt, pkt->etag_len, pkt->etag)) { |
| goto err_short; |
| } |
| OC_LOG(DEBUG, "ETag %u "); |
| OC_LOG_HEX(LOG_LEVEL_DEBUG, pkt->etag, pkt->etag_len); |
| break; |
| #endif |
| case COAP_OPTION_ACCEPT: |
| pkt->accept = coap_parse_int_option(m, cur_opt, opt_len); |
| OC_LOG(DEBUG, "Accept [%u]\n", pkt->accept); |
| break; |
| #if 0 |
| case COAP_OPTION_IF_MATCH: |
| /* TODO support multiple ETags */ |
| pkt->if_match_len = MIN(COAP_ETAG_LEN, opt_len); |
| if (os_mbuf_copydata(m, cur_opt, pkt->if_match_len, |
| pkt->if_match)) { |
| goto err_short; |
| } |
| OC_LOG(DEBUG, "If-Match %u "); |
| OC_LOG_HEX(LOG_LEVEL_DEBUG, pkt->if_match, pkt->if_match_len); |
| break; |
| case COAP_OPTION_IF_NONE_MATCH: |
| pkt->if_none_match = 1; |
| OC_LOG(DEBUG, "If-None-Match\n"); |
| break; |
| |
| case COAP_OPTION_PROXY_URI: |
| #if COAP_PROXY_OPTION_PROCESSING |
| pkt->proxy_uri_off = cur_opt; |
| pkt->proxy_uri_len = opt_len; |
| #endif |
| OC_LOG(DEBUG, "Proxy-Uri NOT IMPLEMENTED\n"); |
| coap_error_message = |
| "This is a constrained server (MyNewt)"; |
| STATS_INC(coap_stats, ierr); |
| return PROXYING_NOT_SUPPORTED_5_05; |
| break; |
| case COAP_OPTION_PROXY_SCHEME: |
| #if COAP_PROXY_OPTION_PROCESSING |
| pkt->proxy_scheme_off = cur_opt; |
| pkt->proxy_scheme_len = opt_len; |
| #endif |
| OC_LOG(DEBUG, "Proxy-Scheme NOT IMPLEMENTED\n"); |
| coap_error_message = |
| "This is a constrained server (MyNewt)"; |
| STATS_INC(coap_stats, ierr); |
| return PROXYING_NOT_SUPPORTED_5_05; |
| break; |
| |
| case COAP_OPTION_URI_HOST: |
| pkt->uri_host_off = cur_opt; |
| pkt->uri_host_len = opt_len; |
| OC_LOG(DEBUG, "Uri-Host "); |
| OC_LOG_STR_MBUF(LOG_LEVEL_DEBUG, m, pkt->uri_host_off, |
| pkt->uri_host_len); |
| break; |
| case COAP_OPTION_URI_PORT: |
| pkt->uri_port = coap_parse_int_option(m, cur_opt, opt_len); |
| OC_LOG(DEBUG, "Uri-Port [%u]\n", pkt->uri_port); |
| break; |
| #endif |
| case COAP_OPTION_URI_PATH: |
| /* coap_merge_multi_option() operates in-place on the buf */ |
| coap_merge_multi_option(m, &pkt->uri_path_off, &pkt->uri_path_len, |
| cur_opt, opt_len, '/'); |
| OC_LOG(DEBUG, "Uri-Path "); |
| OC_LOG_STR_MBUF(LOG_LEVEL_DEBUG, m, pkt->uri_path_off, |
| pkt->uri_path_len); |
| break; |
| case COAP_OPTION_URI_QUERY: |
| /* coap_merge_multi_option() operates in-place on the mbuf */ |
| coap_merge_multi_option(m, &pkt->uri_query_off, &pkt->uri_query_len, |
| cur_opt, opt_len, '&'); |
| OC_LOG(DEBUG, "Uri-Query "); |
| OC_LOG_STR_MBUF(LOG_LEVEL_DEBUG, m, pkt->uri_query_off, |
| pkt->uri_query_len); |
| break; |
| #if 0 |
| case COAP_OPTION_LOCATION_PATH: |
| /* coap_merge_multi_option() operates in-place on the mbuf */ |
| coap_merge_multi_option(m, &pkt->loc_path_off, &pkt->loc_path_len, |
| cur_opt, opt_len, '/'); |
| OC_LOG(DEBUG, "Location-Path "); |
| OC_LOG_STR_MBUF(LOG_LEVEL_DEBUG, m, pkt->loc_path, |
| pkt->loc_path_len); |
| break; |
| case COAP_OPTION_LOCATION_QUERY: |
| /* coap_merge_multi_option() operates in-place on the mbuf */ |
| coap_merge_multi_option(m, &pkt->loc_query_off, &pkt->loc_query_len, |
| cur_opt, opt_len, '&'); |
| OC_LOG(DEBUG, "Location-Query "); |
| OC_LOG_STR_MBUF(LOG_LEVEL_DEBUG, m, pkt->loc_query_off, |
| pkt->loc_query_len); |
| break; |
| #endif |
| case COAP_OPTION_OBSERVE: |
| pkt->observe = coap_parse_int_option(m, cur_opt, opt_len); |
| OC_LOG(DEBUG, "Observe [%lu]\n", (unsigned long)pkt->observe); |
| break; |
| case COAP_OPTION_BLOCK2: |
| pkt->block2_num = coap_parse_int_option(m, cur_opt, opt_len); |
| pkt->block2_more = (pkt->block2_num & 0x08) >> 3; |
| pkt->block2_size = 16 << (pkt->block2_num & 0x07); |
| pkt->block2_offset = |
| (pkt->block2_num & ~0x0000000F) << (pkt->block2_num & 0x07); |
| pkt->block2_num >>= 4; |
| OC_LOG(DEBUG, "Block2 [%lu%s (%u B/blk)]\n", |
| (unsigned long)pkt->block2_num, |
| pkt->block2_more ? "+" : "", pkt->block2_size); |
| break; |
| case COAP_OPTION_BLOCK1: |
| pkt->block1_num = coap_parse_int_option(m, cur_opt, opt_len); |
| pkt->block1_more = (pkt->block1_num & 0x08) >> 3; |
| pkt->block1_size = 16 << (pkt->block1_num & 0x07); |
| pkt->block1_offset = |
| (pkt->block1_num & ~0x0000000F) << (pkt->block1_num & 0x07); |
| pkt->block1_num >>= 4; |
| OC_LOG(DEBUG, "Block1 [%lu%s (%u B/blk)]\n", |
| (unsigned long)pkt->block1_num, |
| pkt->block1_more ? "+" : "", pkt->block1_size); |
| break; |
| case COAP_OPTION_SIZE2: |
| pkt->size2 = coap_parse_int_option(m, cur_opt, opt_len); |
| OC_LOG(DEBUG, "Size2 [%lu]\n", (unsigned long)pkt->size2); |
| break; |
| case COAP_OPTION_SIZE1: |
| pkt->size1 = coap_parse_int_option(m, cur_opt, opt_len); |
| OC_LOG(DEBUG, "Size1 [%lu]\n", (unsigned long)pkt->size1); |
| break; |
| default: |
| OC_LOG(DEBUG, "unknown (%u)\n", opt_num); |
| /* check if critical (odd) */ |
| if (opt_num & 1) { |
| coap_error_message = "Unsupported critical option"; |
| STATS_INC(coap_stats, ierr); |
| return BAD_OPTION_4_02; |
| } |
| } |
| cur_opt += opt_len; |
| } /* for */ |
| |
| return NO_ERROR; |
| } |
| |
| #if 0 |
| int |
| coap_get_query_variable(coap_packet_t *const pkt, const char *name, |
| const char **output) |
| { |
| if (IS_OPTION(pkt, COAP_OPTION_URI_QUERY)) { |
| return coap_get_variable(pkt->uri_query, pkt->uri_query_len, |
| name, output); |
| } |
| return 0; |
| } |
| int |
| coap_get_post_variable(coap_packet_t *pkt, const char *name, |
| const char **output) |
| { |
| if (pkt->payload_len) { |
| return coap_get_variable(pkt->payload, pkt->payload_len, |
| name, output); |
| } |
| return 0; |
| } |
| #endif |
| /*---------------------------------------------------------------------------*/ |
| int |
| coap_set_status_code(coap_packet_t *const packet, unsigned int code) |
| { |
| if (code <= 0xFF) { |
| packet->code = (uint8_t)code; |
| return 1; |
| } else { |
| return 0; |
| } |
| } |
| /*---------------------------------------------------------------------------*/ |
| int |
| coap_set_token(coap_packet_t *pkt, const uint8_t *token, size_t token_len) |
| { |
| pkt->token_len = MIN(COAP_TOKEN_LEN, token_len); |
| memcpy(pkt->token, token, pkt->token_len); |
| |
| return pkt->token_len; |
| } |
| #ifdef OC_CLIENT |
| int |
| coap_get_header_content_format(struct coap_packet_rx *pkt, unsigned int *format) |
| { |
| if (!IS_OPTION(pkt, COAP_OPTION_CONTENT_FORMAT)) { |
| return 0; |
| } |
| *format = pkt->content_format; |
| return 1; |
| } |
| #endif |
| int |
| coap_set_header_content_format(coap_packet_t *pkt, unsigned int format) |
| { |
| pkt->content_format = format; |
| SET_OPTION(pkt, COAP_OPTION_CONTENT_FORMAT); |
| return 1; |
| } |
| /*---------------------------------------------------------------------------*/ |
| #if 0 |
| int |
| coap_get_header_accept(struct coap_packet_rx *pkt, unsigned int *accept) |
| { |
| if (!IS_OPTION(pkt, COAP_OPTION_ACCEPT)) { |
| return 0; |
| } |
| *accept = pkt->accept; |
| return 1; |
| } |
| #endif |
| |
| #ifdef OC_CLIENT |
| int |
| coap_set_header_accept(coap_packet_t *pkt, unsigned int accept) |
| { |
| pkt->accept = accept; |
| SET_OPTION(pkt, COAP_OPTION_ACCEPT); |
| return 1; |
| } |
| #endif |
| /*---------------------------------------------------------------------------*/ |
| #if 0 |
| int |
| coap_get_header_max_age(struct coap_packet_rx *pkt, uint32_t *age) |
| { |
| if (!IS_OPTION(pkt, COAP_OPTION_MAX_AGE)) { |
| *age = COAP_DEFAULT_MAX_AGE; |
| } else { |
| *age = pkt->max_age; |
| } |
| return 1; |
| } |
| #endif |
| int |
| coap_set_header_max_age(coap_packet_t *pkt, uint32_t age) |
| { |
| pkt->max_age = age; |
| SET_OPTION(pkt, COAP_OPTION_MAX_AGE); |
| return 1; |
| } |
| /*---------------------------------------------------------------------------*/ |
| #if 0 |
| int |
| coap_get_header_etag(struct coap_packet_rx *pkt, const uint8_t **etag) |
| { |
| if (!IS_OPTION(pkt, COAP_OPTION_ETAG)) { |
| return 0; |
| } |
| *etag = pkt->etag; |
| return pkt->etag_len; |
| } |
| |
| int |
| coap_set_header_etag(coap_packet_t *pkt, const uint8_t *etag, |
| size_t etag_len) |
| { |
| pkt->etag_len = MIN(COAP_ETAG_LEN, etag_len); |
| memcpy(pkt->etag, etag, pkt->etag_len); |
| |
| SET_OPTION(pkt, COAP_OPTION_ETAG); |
| return pkt->etag_len; |
| } |
| /*---------------------------------------------------------------------------*/ |
| /*FIXME support multiple ETags */ |
| |
| int |
| coap_get_header_if_match(struct coap_packet_rx *pkt, const uint8_t **etag) |
| { |
| if (!IS_OPTION(pkt, COAP_OPTION_IF_MATCH)) { |
| return 0; |
| } |
| *etag = pkt->if_match; |
| return pkt->if_match_len; |
| } |
| |
| int |
| coap_set_header_if_match(coap_packet_t *pkt, const uint8_t *etag, |
| size_t etag_len) |
| { |
| pkt->if_match_len = MIN(COAP_ETAG_LEN, etag_len); |
| memcpy(pkt->if_match, etag, pkt->if_match_len); |
| |
| SET_OPTION(pkt, COAP_OPTION_IF_MATCH); |
| return pkt->if_match_len; |
| } |
| |
| /*---------------------------------------------------------------------------*/ |
| int |
| coap_get_header_if_none_match(struct coap_packet_rx *pkt) |
| { |
| return IS_OPTION(pkt, COAP_OPTION_IF_NONE_MATCH) ? 1 : 0; |
| } |
| |
| int |
| coap_set_header_if_none_match(coap_packet_t *pkt) |
| { |
| SET_OPTION(pkt, COAP_OPTION_IF_NONE_MATCH); |
| return 1; |
| } |
| /*---------------------------------------------------------------------------*/ |
| int |
| coap_get_header_proxy_uri(struct coap_packet_rx *pkt, const char **uri) |
| { |
| if (!IS_OPTION(pkt, COAP_OPTION_PROXY_URI)) { |
| return 0; |
| } |
| *uri = pkt->proxy_uri; |
| return pkt->proxy_uri_len; |
| } |
| |
| int |
| coap_set_header_proxy_uri(coap_packet_t *pkt, const char *uri) |
| { |
| /*TODO Provide alternative that sets Proxy-Scheme and Uri-* options and provide er-coap-conf define */ |
| |
| pkt->proxy_uri = uri; |
| pkt->proxy_uri_len = strlen(uri); |
| |
| SET_OPTION(pkt, COAP_OPTION_PROXY_URI); |
| return pkt->proxy_uri_len; |
| } |
| /*---------------------------------------------------------------------------*/ |
| int |
| coap_get_header_uri_host(struct coap_packet_rx *pkt, const char **host) |
| { |
| if (!IS_OPTION(pkt, COAP_OPTION_URI_HOST)) { |
| return 0; |
| } |
| *host = pkt->uri_host; |
| return pkt->uri_host_len; |
| } |
| |
| int |
| coap_set_header_uri_host(coap_packet_t *pkt, const char *host) |
| { |
| pkt->uri_host = (char *)host; |
| pkt->uri_host_len = strlen(host); |
| |
| SET_OPTION(pkt, COAP_OPTION_URI_HOST); |
| return pkt->uri_host_len; |
| } |
| #endif |
| /*---------------------------------------------------------------------------*/ |
| int |
| coap_get_header_uri_path(struct coap_packet_rx *pkt, char *path, int maxlen) |
| { |
| if (!IS_OPTION(pkt, COAP_OPTION_URI_PATH)) { |
| return 0; |
| } |
| maxlen = min(maxlen, pkt->uri_path_len); |
| os_mbuf_copydata(pkt->m, pkt->uri_path_off, maxlen, path); |
| return maxlen; |
| } |
| #ifdef OC_CLIENT |
| int |
| coap_set_header_uri_path(coap_packet_t *pkt, const char *path) |
| { |
| while (path[0] == '/') { |
| ++path; |
| } |
| pkt->uri_path = (char *)path; |
| pkt->uri_path_len = strlen(path); |
| |
| SET_OPTION(pkt, COAP_OPTION_URI_PATH); |
| return pkt->uri_path_len; |
| } |
| #endif |
| /*---------------------------------------------------------------------------*/ |
| int |
| coap_get_header_uri_query(struct coap_packet_rx *pkt, char *query, int maxlen) |
| { |
| if (!IS_OPTION(pkt, COAP_OPTION_URI_QUERY)) { |
| return 0; |
| } |
| maxlen = min(maxlen, pkt->uri_query_len); |
| os_mbuf_copydata(pkt->m, pkt->uri_query_off, maxlen, query); |
| return maxlen; |
| } |
| #ifdef OC_CLIENT |
| int |
| coap_set_header_uri_query(coap_packet_t *pkt, const char *query) |
| { |
| while (query[0] == '?') { |
| ++query; |
| } |
| pkt->uri_query = (char *)query; |
| pkt->uri_query_len = strlen(query); |
| |
| SET_OPTION(pkt, COAP_OPTION_URI_QUERY); |
| return pkt->uri_query_len; |
| } |
| #endif |
| /*---------------------------------------------------------------------------*/ |
| #if 0 |
| int |
| coap_get_header_location_path(struct coap_packet_rx *pkt, const char **path) |
| { |
| if (!IS_OPTION(pkt, COAP_OPTION_LOCATION_PATH)) { |
| return 0; |
| } |
| *path = pkt->location_path; |
| return pkt->location_path_len; |
| } |
| |
| int |
| coap_set_header_location_path(coap_packet_t *pkt, const char *path) |
| { |
| char *query; |
| |
| while(path[0] == '/') { |
| ++path; |
| } |
| if ((query = strchr(path, '?'))) { |
| coap_set_header_location_query(packet, query + 1); |
| pkt->location_path_len = query - path; |
| } else { |
| pkt->location_path_len = strlen(path); |
| } |
| pkt->location_path = path; |
| |
| if (pkt->location_path_len > 0) { |
| SET_OPTION(pkt, COAP_OPTION_LOCATION_PATH); |
| } |
| return pkt->location_path_len; |
| } |
| |
| #if 0 |
| /*---------------------------------------------------------------------------*/ |
| int |
| coap_get_header_location_query(struct coap_packet_rx *pkt, const char **query) |
| { |
| if (!IS_OPTION(pkt, COAP_OPTION_LOCATION_QUERY)) { |
| return 0; |
| } |
| *query = pkt->location_query; |
| return pkt->location_query_len; |
| } |
| #endif |
| int |
| coap_set_header_location_query(coap_packet_t *pkt, const char *query) |
| { |
| while (query[0] == '?') { |
| ++query; |
| } |
| pkt->loc_query = query; |
| pkt->loc_query_len = strlen(query); |
| |
| SET_OPTION(pkt, COAP_OPTION_LOCATION_QUERY); |
| return pkt->loc_query_len; |
| } |
| #endif |
| /*---------------------------------------------------------------------------*/ |
| int |
| coap_get_header_observe(struct coap_packet_rx *pkt, uint32_t *observe) |
| { |
| if (!IS_OPTION(pkt, COAP_OPTION_OBSERVE)) { |
| return 0; |
| } |
| *observe = pkt->observe; |
| return 1; |
| } |
| |
| int |
| coap_set_header_observe(coap_packet_t *pkt, uint32_t observe) |
| { |
| pkt->observe = observe; |
| SET_OPTION(pkt, COAP_OPTION_OBSERVE); |
| return 1; |
| } |
| |
| /*---------------------------------------------------------------------------*/ |
| int |
| coap_get_header_block2(struct coap_packet_rx *pkt, uint32_t *num, |
| uint8_t *more, uint16_t *size, uint32_t *offset) |
| { |
| if (!IS_OPTION(pkt, COAP_OPTION_BLOCK2)) { |
| return 0; |
| } |
| /* pointers may be NULL to get only specific block parameters */ |
| if (num != NULL) { |
| *num = pkt->block2_num; |
| } |
| if (more != NULL) { |
| *more = pkt->block2_more; |
| } |
| if (size != NULL) { |
| *size = pkt->block2_size; |
| } |
| if (offset != NULL) { |
| *offset = pkt->block2_offset; |
| } |
| return 1; |
| } |
| |
| int |
| coap_set_header_block2(coap_packet_t *pkt, uint32_t num, |
| uint8_t more, uint16_t size) |
| { |
| if (size < 16) { |
| return 0; |
| } |
| if (size > 2048) { |
| return 0; |
| } |
| if (num > 0x0FFFFF) { |
| return 0; |
| } |
| pkt->block2_num = num; |
| pkt->block2_more = more ? 1 : 0; |
| pkt->block2_size = size; |
| |
| SET_OPTION(pkt, COAP_OPTION_BLOCK2); |
| return 1; |
| } |
| /*---------------------------------------------------------------------------*/ |
| int |
| coap_get_header_block1(struct coap_packet_rx *pkt, uint32_t *num, uint8_t *more, |
| uint16_t *size, uint32_t *offset) |
| { |
| if (!IS_OPTION(pkt, COAP_OPTION_BLOCK1)) { |
| return 0; |
| } |
| /* pointers may be NULL to get only specific block parameters */ |
| if (num != NULL) { |
| *num = pkt->block1_num; |
| } |
| if (more != NULL) { |
| *more = pkt->block1_more; |
| } |
| if (size != NULL) { |
| *size = pkt->block1_size; |
| } |
| if (offset != NULL) { |
| *offset = pkt->block1_offset; |
| } |
| return 1; |
| } |
| |
| int |
| coap_set_header_block1(coap_packet_t *pkt, uint32_t num, uint8_t more, |
| uint16_t size) |
| { |
| if (size < 16) { |
| return 0; |
| } |
| if (size > 2048) { |
| return 0; |
| } |
| if (num > 0x0FFFFF) { |
| return 0; |
| } |
| pkt->block1_num = num; |
| pkt->block1_more = more; |
| pkt->block1_size = size; |
| |
| SET_OPTION(pkt, COAP_OPTION_BLOCK1); |
| return 1; |
| } |
| /*---------------------------------------------------------------------------*/ |
| #if 0 |
| int coap_get_header_size2(struct coap_packet_rx * const pkt, uint32_t *size) |
| { |
| if (!IS_OPTION(pkt, COAP_OPTION_SIZE2)) { |
| return 0; |
| } |
| *size = pkt->size2; |
| return 1; |
| } |
| |
| int |
| coap_set_header_size2(coap_packet_t *pkt, uint32_t size) |
| { |
| pkt->size2 = size; |
| SET_OPTION(pkt, COAP_OPTION_SIZE2); |
| return 1; |
| } |
| /*---------------------------------------------------------------------------*/ |
| int |
| coap_get_header_size1(struct coap_packet_rx *pkt, uint32_t *size) |
| { |
| if (!IS_OPTION(pkt, COAP_OPTION_SIZE1)) { |
| return 0; |
| } |
| *size = pkt->size1; |
| return 1; |
| } |
| int |
| coap_set_header_size1(coap_packet_t *pkt, uint32_t size) |
| { |
| pkt->size1 = size; |
| SET_OPTION(pkt, COAP_OPTION_SIZE1); |
| return 1; |
| } |
| #endif |
| /*---------------------------------------------------------------------------*/ |
| int |
| coap_get_payload_copy(struct coap_packet_rx *pkt, uint8_t *payload, int maxlen) |
| { |
| if (pkt->payload_len) { |
| maxlen = min(maxlen, pkt->payload_len); |
| os_mbuf_copydata(pkt->m, pkt->payload_off, maxlen, payload); |
| return maxlen; |
| } else { |
| return 0; |
| } |
| } |
| |
| int |
| coap_get_payload(struct coap_packet_rx *pkt, struct os_mbuf **mp, uint16_t *off) |
| { |
| if (pkt->payload_len) { |
| *off = pkt->payload_off; |
| } else { |
| *off = OS_MBUF_PKTLEN(pkt->m); |
| } |
| *mp = pkt->m; |
| return pkt->payload_len; |
| } |
| |
| int |
| coap_set_payload(coap_packet_t *pkt, struct os_mbuf *m, size_t length) |
| { |
| pkt->payload_m = os_mbuf_dup(m); |
| if (!pkt->payload_m) { |
| return -1; |
| } |
| pkt->payload_len = MIN(OS_MBUF_PKTLEN(m), length); |
| |
| return pkt->payload_len; |
| } |
| /*---------------------------------------------------------------------------*/ |