Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
picocoap/src/coap.c
- Committer:
- Patrick Barrett
- Date:
- 2014-12-19
- Revision:
- 7:f9df43829cea
- Child:
- 22:6a6810897eb9
File content as of revision 7:f9df43829cea:
#include <stddef.h>
#include <string.h>
#include "coap.h"
//
// Getters
//
coap_error coap_validate_pkt(coap_pdu *pdu) //uint8_t *pkt, size_t pkt_len)
{
coap_error err;
size_t ol;
uint8_t *ov;
pdu->opt_ptr = NULL;
if (pdu->len > pdu->max)
return CE_INVALID_PACKET;
if (pdu->len < 4)
return CE_INVALID_PACKET;
// Check Version
if (coap_get_version(pdu) != 1)
return CE_INVALID_PACKET;
// Check TKL
if (coap_get_tkl(pdu) > 8)
return CE_INVALID_PACKET;
// Check Options
ov = pdu->buf + 4 + coap_get_tkl(pdu);
ol = 0;
while((err = coap_decode_option(ov + ol, pdu->len-(ov-pdu->buf), NULL, &ol, &ov)) != 0){
if (err == CE_NONE){
continue;
} else if (err == CE_END_OF_PACKET){
break;
} else if (err == CE_FOUND_PAYLOAD_MARKER){
// Payload Marker, but No Payload
if (pdu->len == (ov + ol - pdu->buf)){
return CE_INVALID_PACKET;
} else {
break;
}
} else {
return err;
}
}
return CE_NONE;
}
uint64_t coap_get_token(coap_pdu *pdu)
{
uint8_t tkl;
uint64_t token = 0;
// Extract TKL.
tkl = pdu->buf[0] & 0x0F;
// Check that we were given enough packet.
if (pdu->len < 4 + tkl)
return 0;
// Set token.
memcpy(&token, &pdu->buf[4], tkl);
return token;
}
coap_option coap_get_option(coap_pdu *pdu, coap_option *last)
{
uint8_t *opt_ptr;
coap_option option;
coap_error err;
if (last != NULL && last->num != 0){
option.num = last->num;
option.len = 0;
option.val = NULL;
opt_ptr = last->val + last->len;
} else {
option.num = 0;
option.len = 0;
option.val = NULL;
opt_ptr = pdu->buf + 4 + coap_get_tkl(pdu);
}
// If opt_ptr is outside pkt range, put it at first opt.
if (opt_ptr > (pdu->buf + pdu->len) || opt_ptr <= pdu->buf){
opt_ptr = pdu->buf + 4 + coap_get_tkl(pdu);
}
err = coap_decode_option(opt_ptr, pdu->len-(opt_ptr-pdu->buf), &option.num, &option.len, &option.val);
if (err != CE_NONE){
if (err == CE_FOUND_PAYLOAD_MARKER){
if (option.num == 0){
option.val = opt_ptr + 1;
option.len = pdu->len-(opt_ptr-pdu->buf) - 1;
} else {
option.val = option.val + option.len;
option.len = pdu->len - (option.val - pdu->buf);
}
} else {
option.val = NULL;
option.len = 0;
}
option.num = 0;
}
opt_ptr = option.val + option.len;
return option;
}
coap_option coap_get_option_by_num(coap_pdu *pdu, coap_option_number num, uint8_t occ)
{
coap_option option;
uint8_t i = 0;
option.num = 0;
do {
option = coap_get_option(pdu, &option);
if (option.num == num) {
i++;
}
} while (i < occ);
return option;
}
//
// Decoding Functions (Intended for Internal Use)
//
coap_error coap_decode_option(uint8_t *pkt_ptr, size_t pkt_len,
uint16_t *option_number, size_t *option_length, uint8_t **value)
{
uint8_t *ptr = pkt_ptr;
uint16_t delta, length;
// Check for end of Packet
if (pkt_len == 0){
return CE_END_OF_PACKET;
}
// Check for Payload Marker
if (*ptr == 0xFF){
return CE_FOUND_PAYLOAD_MARKER;
}
// Get Base Delta and Length
delta = *ptr >> 4;
length = *ptr & 0x0F;
ptr++;
// Check for and Get Extended Delta
if (delta < 13) {
//delta = delta;
}else if (delta == 13) {
delta = *ptr + 13;
ptr += 1;
}else if (delta == 14) {
delta = (*ptr << 8) + *(ptr+1) + 269;
ptr += 2;
}else{
return CE_INVALID_PACKET;
}
// Check for and Get Extended Length
if (length < 13) {
//length = length;
}else if (length == 13) {
length = *ptr + 13;
ptr += 1;
}else if (length == 14) {
length = (*ptr << 8) + *(ptr+1) + 269;
ptr += 2;
}else{
return CE_INVALID_PACKET;
}
if (option_number != NULL)
*option_number += delta;
if (option_length != NULL)
*option_length = length;
if (value != NULL)
*value = ptr;
return CE_NONE;
}
coap_payload coap_get_payload(coap_pdu *pdu)
{
size_t offset = 4 + coap_get_tkl(pdu);
coap_option option;
coap_payload payload;
coap_error err;
// Defaults
payload.len = 0;
payload.val = NULL;
// Find Last Option
do {
err = coap_decode_option(pdu->buf+offset, pdu->len-offset, NULL, &option.len, &option.val);
if (err == CE_FOUND_PAYLOAD_MARKER || err == CE_END_OF_PACKET)
break;
if (err != CE_NONE)
return payload;
// Add this option header and value length to offset.
offset += (option.val - (pdu->buf+offset)) + option.len;
} while (1);
if (err == CE_FOUND_PAYLOAD_MARKER){
payload.len = pdu->len - offset - 1;
payload.val = pdu->buf + offset + 1;
}
return payload;
}
//
// Setters
//
coap_error coap_init_pdu(coap_pdu *pdu)
{
// Check that we were given enough packet.
if (pdu->max < 4)
return CE_INSUFFICIENT_BUFFER;
coap_set_version(pdu, COAP_V1);
coap_set_type(pdu, CT_RST);
coap_set_token(pdu, 0, 0);
coap_set_code(pdu, CC_EMPTY);
coap_set_mid(pdu, 0);
pdu->len = 4;
pdu->opt_ptr = NULL;
return CE_NONE;
}
coap_error coap_set_version(coap_pdu *pdu, coap_version ver)
{
// Check that we were given enough packet.
if (pdu->max < 1)
return CE_INSUFFICIENT_BUFFER;
pdu->buf[0] = (ver << 6) | (pdu->buf[0] & 0x3F);
if (pdu->len < 1)
pdu->len = 1;
return CE_NONE;
}
coap_error coap_set_type(coap_pdu *pdu, coap_type mtype)
{
// Check that we were given enough packet.
if (pdu->max < 1)
return CE_INSUFFICIENT_BUFFER;
pdu->buf[0] = (mtype << 4) | (pdu->buf[0] & 0xCF);
if (pdu->len < 1)
pdu->len = 1;
return CE_NONE;
}
coap_error coap_set_code(coap_pdu *pdu, coap_code code)
{
// Check that we were given enough packet.
if (pdu->max < 2)
return CE_INSUFFICIENT_BUFFER;
pdu->buf[1] = code;
if (pdu->len < 2)
pdu->len = 2;
return CE_NONE;
}
coap_error coap_set_mid(coap_pdu *pdu, uint16_t mid)
{
// Check that we were given enough packet.
if (pdu->max < 4)
return CE_INSUFFICIENT_BUFFER;
pdu->buf[2] = mid >> 8;
pdu->buf[3] = mid & 0xFF;
if (pdu->len < 4)
pdu->len = 4;
return CE_NONE;
}
coap_error coap_set_token(coap_pdu *pdu, uint64_t token, uint8_t tkl)
{
// Check that we were given enough buffer.
if (pdu->max < 4 + tkl)
return CE_INSUFFICIENT_BUFFER;
// Check token length for spec.
if (tkl > 8)
return CE_INVALID_PACKET;
// Check if we may need to make or take room.
if (pdu->len > 4){
// Check that we were given enough buffer.
if (pdu->max < pdu->len + (tkl - coap_get_tkl(pdu)))
return CE_INSUFFICIENT_BUFFER;
// Move rest of packet to make room or take empty space.
memmove(pdu->buf + 4 + tkl, pdu->buf + 4 + coap_get_tkl(pdu), pdu->len - 4 - coap_get_tkl(pdu));
}
// Set token.
memcpy(pdu->buf+4, &token, tkl);
pdu->len += tkl - coap_get_tkl(pdu);
pdu->buf[0] = (tkl & 0x0F) | (pdu->buf[0] & 0xF0);
return CE_NONE;
}
coap_error coap_add_option(coap_pdu *pdu, int32_t opt_num, uint8_t* value, uint16_t opt_len)
{
uint8_t *pkt_ptr, *fopt_val, nopt_hdr_len;
uint16_t fopt_num, lopt_num;
size_t fopt_len, opts_len;
coap_error err;
// Set pointer to "zeroth option's value" which is really first option header.
fopt_val = pdu->buf + 4 + coap_get_tkl(pdu); // ptr to start of options
fopt_len = 0;
// Option number delta starts at zero.
fopt_num = 0;
// Find insertion point
do{
pkt_ptr = fopt_val + fopt_len;
lopt_num = fopt_num;
err = coap_decode_option(pkt_ptr, (pdu->len)-(pkt_ptr-pdu->buf), &fopt_num, &fopt_len, &fopt_val);
}while (err == CE_NONE && fopt_num <= opt_num && (pkt_ptr-pdu->buf) + fopt_len < pdu->len);
if (err != CE_FOUND_PAYLOAD_MARKER && err != CE_END_OF_PACKET && err != CE_NONE)
return err;
// Build New Header
nopt_hdr_len = coap_compute_option_header_len(opt_num - lopt_num, opt_len);
// Check that we were given enough buffer.
if (pdu->max < pdu->len + nopt_hdr_len + opt_len)
return CE_INSUFFICIENT_BUFFER;
// Check if we're adding an option in the middle of a packet.
// But seriously, don't do this.
if (pdu->len != pkt_ptr- pdu->buf){
// Slide packet tail to make room.
memmove(pkt_ptr + nopt_hdr_len + opt_len, pkt_ptr, pdu->len - (pkt_ptr - pdu->buf));
pdu->len += nopt_hdr_len + opt_len;
// Find Current Length of Remaining Options
opts_len = pdu->len - (pkt_ptr-pdu->buf);
// Adjust the option deltas for the rest of the options.
coap_adjust_option_deltas(pkt_ptr + nopt_hdr_len + opt_len,
&opts_len, pdu->max - (pkt_ptr - pdu->buf),
lopt_num - opt_num);
// Update Total Packet Length
pdu->len += opts_len - (pdu->len - (pkt_ptr-pdu->buf));
}else{
// Update Packet Length
pdu->len = pdu->len + nopt_hdr_len + opt_len;
}
// Insert the Header
coap_build_option_header(pkt_ptr, nopt_hdr_len, opt_num - lopt_num, opt_len);
// Insert the Value
memcpy(pkt_ptr + nopt_hdr_len, value, opt_len);
return CE_NONE;
}
coap_error coap_set_payload(coap_pdu *pdu, uint8_t *payload, size_t payload_len){
uint8_t *pkt_ptr, *fopt_val;
uint16_t fopt_num;
size_t fopt_len;
coap_error err;
// Set pointer to "zeroth option's value" which is really first option header.
fopt_val = pdu->buf + 4 + coap_get_tkl(pdu);
fopt_len = 0;
// Option number delta starts at zero.
fopt_num = 0;
// Find insertion point
do{
pkt_ptr = fopt_val + fopt_len;
err = coap_decode_option(pkt_ptr, (pdu->len)-(pkt_ptr-pdu->buf), &fopt_num, &fopt_len, &fopt_val);
}while (err == CE_NONE && (pkt_ptr-pdu->buf) + fopt_len < pdu->len);
if (err != CE_FOUND_PAYLOAD_MARKER && err != CE_END_OF_PACKET && err != CE_NONE)
return err;
if (err == CE_END_OF_PACKET){
// Check that we were given enough buffer.
if (pdu->max < pdu->len + payload_len + 1)
return CE_INSUFFICIENT_BUFFER;
*(pkt_ptr++) = 0xFF;
}else if (err == CE_FOUND_PAYLOAD_MARKER){
// Check that we were given enough buffer.
if (pdu->max < pdu->len + payload_len)
return CE_INSUFFICIENT_BUFFER;
}
pdu->len = (pkt_ptr - pdu->buf) + payload_len;
memcpy(pkt_ptr, payload, payload_len);
return CE_NONE;
}
coap_error coap_adjust_option_deltas(uint8_t *opts_start, size_t *opts_len, size_t max_len, int32_t offset)
{
uint8_t *ptr, *fopt_val;
uint16_t fopt_num, nopt_num;
size_t fopt_len;
int8_t nhdr_len, fhdr_len;
coap_error err;
fopt_val = opts_start;
fopt_len = 0;
fopt_num = 0;
do{
ptr = fopt_val + fopt_len;
if (ptr - opts_start > *opts_len)
break;
err = coap_decode_option(ptr, *opts_len-(ptr-opts_start), &fopt_num, &fopt_len, &fopt_val);
if (err == CE_FOUND_PAYLOAD_MARKER || err == CE_END_OF_PACKET)
break;
else if (err != CE_NONE)
return err;
// New Option Number
nopt_num = fopt_num + offset;
// Find the length of the found header.
fhdr_len = fopt_val - ptr;
// Compute the length of the new header.
nhdr_len = coap_compute_option_header_len(nopt_num, fopt_len);
// Make/Take room for new header size
if (fhdr_len != nhdr_len){
if (max_len < *opts_len + (nhdr_len - fhdr_len))
return CE_INSUFFICIENT_BUFFER;
memmove(fopt_val + (nhdr_len - fhdr_len), fopt_val, fopt_len);
// Adjust Options Length
*opts_len += (nhdr_len - fhdr_len);
}
// Write New Header
nhdr_len = coap_build_option_header(ptr, nhdr_len, nopt_num, fopt_len);
}while (1);
return CE_NONE;
}
int8_t coap_build_option_header(uint8_t *buf, size_t max_len, int32_t opt_delta, int32_t opt_len)
{
uint8_t *ptr, base_num, base_len;
if (max_len < 1)
return CE_INSUFFICIENT_BUFFER;
ptr = buf+1;
if (opt_delta < 13) {
base_num = opt_delta;
}else if (opt_delta < 269) {
if (max_len < ptr-buf + 1)
return CE_INSUFFICIENT_BUFFER;
base_num = 13;
*(ptr++) = opt_delta - 13;
}else {
if (max_len < ptr-buf + 2)
return CE_INSUFFICIENT_BUFFER;
base_num = 14;
*(ptr++) = (opt_delta - 269) >> 8;
*(ptr++) = (opt_delta - 269) & 0xFF;
}
if (opt_len < 13) {
base_len = opt_len;
}else if (opt_len < 269) {
if (max_len < ptr-buf + 1)
return CE_INSUFFICIENT_BUFFER;
base_len = 13;
*(ptr++) = opt_len - 13;
}else {
if (max_len < ptr-buf + 2)
return CE_INSUFFICIENT_BUFFER;
base_len = 14;
*(ptr++) = (opt_len - 269) >> 8;
*(ptr++) = (opt_len - 269) & 0xFF;
}
buf[0] = (base_num << 4) | base_len;
// Return the length of the new header.
return ptr-buf;
}
int8_t coap_compute_option_header_len(int32_t opt_delta, int32_t opt_len)
{
int8_t len = 1;
if (opt_delta < 13) {
}else if (opt_delta < 269) {
len += 1;
}else {
len += 2;
}
if (opt_len < 13) {
}else if (opt_len < 269) {
len += 1;
}else {
len += 2;
}
return len;
}