Johny Mattsson 526d21dab4 Major cleanup - c_whatever is finally history. (#2838)
The PR removed the bulk of non-newlib headers from the NodeMCU source base.  
app/libc has now been cut down to the bare minimum overrides to shadow the 
corresponding functions in the SDK's libc. The old c_xyz.h headerfiles have been 
nuked in favour of the standard <xyz.h> headers, with a few exceptions over in 
sdk-overrides. Again, shipping a libc.a without headers is a terrible thing to do. We're 
still living on a prayer that libc was configured the same was as a default-configured
xtensa gcc toolchain assumes it is. That part I cannot do anything about, unfortunately, 
but it's no worse than it has been before.

This enables our source files to compile successfully using the standard header files, 
and use the typical malloc()/calloc()/realloc()/free(), the strwhatever()s and 
memwhatever()s. These end up, through macro and linker magic, mapped to the 
appropriate SDK or ROM functions.
2019-07-22 00:58:21 +03:00

556 lines
14 KiB
C

#include "user_config.h"
#include <stdio.h>
#include <string.h>
#include "coap.h"
#include "uri.h"
extern void endpoint_setup(void);
extern const coap_endpoint_t endpoints[];
#ifdef COAP_DEBUG
void coap_dumpHeader(coap_header_t *hdr)
{
printf("Header:\n");
printf(" ver 0x%02X\n", hdr->ver);
printf(" t 0x%02X\n", hdr->ver);
printf(" tkl 0x%02X\n", hdr->tkl);
printf(" code 0x%02X\n", hdr->code);
printf(" id 0x%02X%02X\n", hdr->id[0], hdr->id[1]);
}
void coap_dump(const uint8_t *buf, size_t buflen, bool bare)
{
if (bare)
{
while(buflen--)
printf("%02X%s", *buf++, (buflen > 0) ? " " : "");
}
else
{
printf("Dump: ");
while(buflen--)
printf("%02X%s", *buf++, (buflen > 0) ? " " : "");
printf("\n");
}
}
#endif
int coap_parseHeader(coap_header_t *hdr, const uint8_t *buf, size_t buflen)
{
if (buflen < 4)
return COAP_ERR_HEADER_TOO_SHORT;
hdr->ver = (buf[0] & 0xC0) >> 6;
if (hdr->ver != 1)
return COAP_ERR_VERSION_NOT_1;
hdr->t = (buf[0] & 0x30) >> 4;
hdr->tkl = buf[0] & 0x0F;
hdr->code = buf[1];
hdr->id[0] = buf[2];
hdr->id[1] = buf[3];
return 0;
}
int coap_buildHeader(const coap_header_t *hdr, uint8_t *buf, size_t buflen)
{
// build header
if (buflen < 4)
return COAP_ERR_BUFFER_TOO_SMALL;
buf[0] = (hdr->ver & 0x03) << 6;
buf[0] |= (hdr->t & 0x03) << 4;
buf[0] |= (hdr->tkl & 0x0F);
buf[1] = hdr->code;
buf[2] = hdr->id[0];
buf[3] = hdr->id[1];
return 4;
}
int coap_parseToken(coap_buffer_t *tokbuf, const coap_header_t *hdr, const uint8_t *buf, size_t buflen)
{
if (hdr->tkl == 0)
{
tokbuf->p = NULL;
tokbuf->len = 0;
return 0;
}
else
if (hdr->tkl <= 8)
{
if (4U + hdr->tkl > buflen)
return COAP_ERR_TOKEN_TOO_SHORT; // tok bigger than packet
tokbuf->p = buf+4; // past header
tokbuf->len = hdr->tkl;
return 0;
}
else
{
// invalid size
return COAP_ERR_TOKEN_TOO_SHORT;
}
}
int coap_buildToken(const coap_buffer_t *tokbuf, const coap_header_t *hdr, uint8_t *buf, size_t buflen)
{
// inject token
uint8_t *p;
if (buflen < (4U + hdr->tkl))
return COAP_ERR_BUFFER_TOO_SMALL;
p = buf + 4;
if ((hdr->tkl > 0) && (hdr->tkl != tokbuf->len))
return COAP_ERR_UNSUPPORTED;
if (hdr->tkl > 0)
memcpy(p, tokbuf->p, hdr->tkl);
// http://tools.ietf.org/html/rfc7252#section-3.1
// inject options
return hdr->tkl;
}
// advances p
int coap_parseOption(coap_option_t *option, uint16_t *running_delta, const uint8_t **buf, size_t buflen)
{
const uint8_t *p = *buf;
uint8_t headlen = 1;
uint16_t len, delta;
if (buflen < headlen) // too small
return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
delta = (p[0] & 0xF0) >> 4;
len = p[0] & 0x0F;
// These are untested and may be buggy
if (delta == 13)
{
headlen++;
if (buflen < headlen)
return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
delta = p[1] + 13;
p++;
}
else
if (delta == 14)
{
headlen += 2;
if (buflen < headlen)
return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
delta = ((p[1] << 8) | p[2]) + 269;
p+=2;
}
else
if (delta == 15)
return COAP_ERR_OPTION_DELTA_INVALID;
if (len == 13)
{
headlen++;
if (buflen < headlen)
return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
len = p[1] + 13;
p++;
}
else
if (len == 14)
{
headlen += 2;
if (buflen < headlen)
return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
len = ((p[1] << 8) | p[2]) + 269;
p+=2;
}
else
if (len == 15)
return COAP_ERR_OPTION_LEN_INVALID;
if ((p + 1 + len) > (*buf + buflen))
return COAP_ERR_OPTION_TOO_BIG;
//printf("option num=%d\n", delta + *running_delta);
option->num = delta + *running_delta;
option->buf.p = p+1;
option->buf.len = len;
//coap_dump(p+1, len, false);
// advance buf
*buf = p + 1 + len;
*running_delta += delta;
return 0;
}
// http://tools.ietf.org/html/rfc7252#section-3.1
int coap_parseOptionsAndPayload(coap_option_t *options, uint8_t *numOptions, coap_buffer_t *payload, const coap_header_t *hdr, const uint8_t *buf, size_t buflen)
{
size_t optionIndex = 0;
uint16_t delta = 0;
const uint8_t *p = buf + 4 + hdr->tkl;
const uint8_t *end = buf + buflen;
int rc;
if (p > end)
return COAP_ERR_OPTION_OVERRUNS_PACKET; // out of bounds
//coap_dump(p, end - p);
// 0xFF is payload marker
while((optionIndex < *numOptions) && (p < end) && (*p != 0xFF))
{
if (0 != (rc = coap_parseOption(&options[optionIndex], &delta, &p, end-p)))
return rc;
optionIndex++;
}
*numOptions = optionIndex;
if (p+1 < end && *p == 0xFF) // payload marker
{
payload->p = p+1;
payload->len = end-(p+1);
}
else
{
payload->p = NULL;
payload->len = 0;
}
return 0;
}
int coap_buildOptionHeader(uint32_t optDelta, size_t length, uint8_t *buf, size_t buflen)
{
int n = 0;
uint8_t *p = buf;
uint8_t len, delta = 0;
if (buflen < 5)
return COAP_ERR_BUFFER_TOO_SMALL;
coap_option_nibble(optDelta, &delta);
coap_option_nibble(length, &len);
*p++ = (0xFF & (delta << 4 | len));
n++;
if (delta == 13)
{
*p++ = (optDelta - 13);
n++;
}
else
if (delta == 14)
{
*p++ = ((optDelta-269) >> 8);
*p++ = (0xFF & (optDelta-269));
n+=2;
}
if (len == 13)
{
*p++ = (length - 13);
n++;
}
else
if (len == 14)
{
*p++ = (length >> 8);
*p++ = (0xFF & (length-269));
n+=2;
}
return n;
}
#ifdef COAP_DEBUG
void coap_dumpOptions(coap_option_t *opts, size_t numopt)
{
size_t i;
printf(" Options:\n");
for (i=0;i<numopt;i++)
{
printf(" 0x%02X [ ", opts[i].num);
coap_dump(opts[i].buf.p, opts[i].buf.len, true);
printf(" ]\n");
}
}
void coap_dumpPacket(coap_packet_t *pkt)
{
coap_dumpHeader(&pkt->hdr);
coap_dumpOptions(pkt->opts, pkt->numopts);
printf("Payload: ");
coap_dump(pkt->payload.p, pkt->payload.len, true);
printf("\n");
}
#endif
int coap_parse(coap_packet_t *pkt, const uint8_t *buf, size_t buflen)
{
int rc;
// coap_dump(buf, buflen, false);
if (0 != (rc = coap_parseHeader(&pkt->hdr, buf, buflen)))
return rc;
// coap_dumpHeader(&hdr);
if (0 != (rc = coap_parseToken(&pkt->tok, &pkt->hdr, buf, buflen)))
return rc;
pkt->numopts = MAXOPT;
if (0 != (rc = coap_parseOptionsAndPayload(pkt->opts, &(pkt->numopts), &(pkt->payload), &pkt->hdr, buf, buflen)))
return rc;
// coap_dumpOptions(opts, numopt);
return 0;
}
// options are always stored consecutively, so can return a block with same option num
const coap_option_t *coap_findOptions(const coap_packet_t *pkt, uint8_t num, uint8_t *count)
{
// FIXME, options is always sorted, can find faster than this
size_t i;
const coap_option_t *first = NULL;
*count = 0;
for (i=0;i<pkt->numopts;i++)
{
if (pkt->opts[i].num == num)
{
if (NULL == first)
first = &pkt->opts[i];
(*count)++;
}
else
{
if (NULL != first)
break;
}
}
return first;
}
int coap_buffer_to_string(char *strbuf, size_t strbuflen, const coap_buffer_t *buf)
{
if (buf->len+1 > strbuflen)
return COAP_ERR_BUFFER_TOO_SMALL;
memcpy(strbuf, buf->p, buf->len);
strbuf[buf->len] = 0;
return 0;
}
int coap_build(uint8_t *buf, size_t *buflen, const coap_packet_t *pkt)
{
size_t opts_len = 0, hdr_len = 0, tok_len = 0;
size_t i;
uint8_t *p = buf;
size_t left = *buflen;
uint16_t running_delta = 0;
hdr_len = coap_buildHeader(&(pkt->hdr), buf, *buflen);
p += hdr_len;
left -= hdr_len;
tok_len = coap_buildToken(&(pkt->tok), &(pkt->hdr), buf, *buflen);
p += tok_len;
left -= tok_len;
for (i=0;i<pkt->numopts;i++)
{
uint8_t len, delta = 0;
uint16_t optDelta = 0;
int rc = 0;
if (((size_t)(p-buf)) > *buflen)
return COAP_ERR_BUFFER_TOO_SMALL;
optDelta = pkt->opts[i].num - running_delta;
rc = coap_buildOptionHeader(optDelta, pkt->opts[i].buf.len, p, left);
p += rc;
left -= rc;
memcpy(p, pkt->opts[i].buf.p, pkt->opts[i].buf.len);
p += pkt->opts[i].buf.len;
left -= pkt->opts[i].buf.len;
running_delta = pkt->opts[i].num;
}
opts_len = (p - buf) - 4; // number of bytes used by options
if (pkt->payload.len > 0)
{
if (*buflen < 4 + 1 + pkt->payload.len + opts_len)
return COAP_ERR_BUFFER_TOO_SMALL;
buf[4 + opts_len] = 0xFF; // payload marker
memcpy(buf+5 + opts_len, pkt->payload.p, pkt->payload.len);
*buflen = opts_len + 5 + pkt->payload.len;
}
else
*buflen = opts_len + 4;
return 0;
}
void coap_option_nibble(uint32_t value, uint8_t *nibble)
{
if (value<13)
{
*nibble = (0xFF & value);
}
else
if (value<=0xFF+13)
{
*nibble = 13;
} else if (value<=0xFFFF+269)
{
*nibble = 14;
}
}
int coap_make_response(coap_rw_buffer_t *scratch, coap_packet_t *pkt, const uint8_t *content, size_t content_len, uint8_t msgid_hi, uint8_t msgid_lo, const coap_buffer_t* tok, coap_responsecode_t rspcode, coap_content_type_t content_type)
{
pkt->hdr.ver = 0x01;
pkt->hdr.t = COAP_TYPE_ACK;
pkt->hdr.tkl = 0;
pkt->hdr.code = rspcode;
pkt->hdr.id[0] = msgid_hi;
pkt->hdr.id[1] = msgid_lo;
pkt->numopts = 1;
// need token in response
if (tok) {
pkt->hdr.tkl = tok->len;
pkt->tok = *tok;
}
// safe because 1 < MAXOPT
pkt->opts[0].num = COAP_OPTION_CONTENT_FORMAT;
pkt->opts[0].buf.p = scratch->p;
if (scratch->len < 2)
return COAP_ERR_BUFFER_TOO_SMALL;
scratch->p[0] = ((uint16_t)content_type & 0xFF00) >> 8;
scratch->p[1] = ((uint16_t)content_type & 0x00FF);
pkt->opts[0].buf.len = 2;
pkt->payload.p = content;
pkt->payload.len = content_len;
return 0;
}
unsigned int coap_encode_var_bytes(unsigned char *buf, unsigned int val) {
unsigned int n, i;
for (n = 0, i = val; i && n < sizeof(val); ++n)
i >>= 8;
i = n;
while (i--) {
buf[i] = val & 0xff;
val >>= 8;
}
return n;
}
static uint8_t _token_data[4] = {'n','o','d','e'};
coap_buffer_t the_token = { _token_data, 4 };
static unsigned short message_id;
int coap_make_request(coap_rw_buffer_t *scratch, coap_packet_t *pkt, coap_msgtype_t t, coap_method_t m, coap_uri_t *uri, const uint8_t *payload, size_t payload_len)
{
int res;
pkt->hdr.ver = 0x01;
pkt->hdr.t = t;
pkt->hdr.tkl = 0;
pkt->hdr.code = m;
pkt->hdr.id[0] = (message_id >> 8) & 0xFF; //msgid_hi;
pkt->hdr.id[1] = message_id & 0xFF; //msgid_lo;
message_id++;
NODE_DBG("message_id: %d.\n", message_id);
pkt->numopts = 0;
if (the_token.len) {
pkt->hdr.tkl = the_token.len;
pkt->tok = the_token;
}
if (scratch->len < 2) // TBD...
return COAP_ERR_BUFFER_TOO_SMALL;
uint8_t *saved = scratch->p;
/* split arg into Uri-* options */
// const char *addr = uri->host.s;
// if(uri->host.length && (strlen(addr) != uri->host.length || memcmp(addr, uri->host.s, uri->host.length) != 0)){
if(uri->host.length){
/* add Uri-Host */
// addr is destination address
pkt->opts[pkt->numopts].num = COAP_OPTION_URI_HOST;
pkt->opts[pkt->numopts].buf.p = uri->host.s;
pkt->opts[pkt->numopts].buf.len = uri->host.length;
pkt->numopts++;
}
if (uri->port != COAP_DEFAULT_PORT) {
pkt->opts[pkt->numopts].num = COAP_OPTION_URI_PORT;
res = coap_encode_var_bytes(scratch->p, uri->port);
pkt->opts[pkt->numopts].buf.len = res;
pkt->opts[pkt->numopts].buf.p = scratch->p;
scratch->p += res;
scratch->len -= res;
pkt->numopts++;
}
if (uri->path.length) {
res = coap_split_path(scratch, pkt, uri->path.s, uri->path.length);
}
if (uri->query.length) {
res = coap_split_query(scratch, pkt, uri->query.s, uri->query.length);
}
pkt->payload.p = payload;
pkt->payload.len = payload_len;
scratch->p = saved; // save back the pointer.
return 0;
}
// FIXME, if this looked in the table at the path before the method then
// it could more easily return 405 errors
int coap_handle_req(coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt)
{
const coap_option_t *opt;
int i;
uint8_t count;
const coap_endpoint_t *ep = endpoints;
while(NULL != ep->handler)
{
if (ep->method != inpkt->hdr.code)
goto next;
if (NULL != (opt = coap_findOptions(inpkt, COAP_OPTION_URI_PATH, &count)))
{
// if (count != ep->path->count)
if ((count != ep->path->count ) && (count != ep->path->count + 1)) // +1 for /f/[function], /v/[variable]
goto next;
for (i=0;i<ep->path->count;i++)
{
if (opt[i].buf.len != strlen(ep->path->elems[i]))
goto next;
if (0 != memcmp(ep->path->elems[i], opt[i].buf.p, opt[i].buf.len))
goto next;
}
// pre-path match!
if (count==ep->path->count+1 && ep->user_entry == NULL)
goto next;
return ep->handler(ep, scratch, inpkt, outpkt, inpkt->hdr.id[0], inpkt->hdr.id[1]);
}
next:
ep++;
}
coap_make_response(scratch, outpkt, NULL, 0, inpkt->hdr.id[0], inpkt->hdr.id[1], &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE);
return 0;
}
void coap_setup(void)
{
message_id = (unsigned short)os_random(); // calculate only once
}
int
check_token(coap_packet_t *pkt) {
return pkt->tok.len == the_token.len && memcmp(pkt->tok.p, the_token.p, the_token.len) == 0;
}