123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555 |
- #include "user_config.h"
- #include "c_stdio.h"
- #include "c_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)
- {
- c_printf("Header:\n");
- c_printf(" ver 0x%02X\n", hdr->ver);
- c_printf(" t 0x%02X\n", hdr->ver);
- c_printf(" tkl 0x%02X\n", hdr->tkl);
- c_printf(" code 0x%02X\n", hdr->code);
- c_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--)
- c_printf("%02X%s", *buf++, (buflen > 0) ? " " : "");
- }
- else
- {
- c_printf("Dump: ");
- while(buflen--)
- c_printf("%02X%s", *buf++, (buflen > 0) ? " " : "");
- c_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)
- c_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;
- c_printf(" Options:\n");
- for (i=0;i<numopt;i++)
- {
- c_printf(" 0x%02X [ ", opts[i].num);
- coap_dump(opts[i].buf.p, opts[i].buf.len, true);
- c_printf(" ]\n");
- }
- }
- void coap_dumpPacket(coap_packet_t *pkt)
- {
- coap_dumpHeader(&pkt->hdr);
- coap_dumpOptions(pkt->opts, pkt->numopts);
- c_printf("Payload: ");
- coap_dump(pkt->payload.p, pkt->payload.len, true);
- c_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;
- c_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;
- c_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
- c_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 && (c_strlen(addr) != uri->host.length || c_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 != c_strlen(ep->path->elems[i]))
- goto next;
- if (0 != c_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 && c_memcmp(pkt->tok.p, the_token.p, the_token.len) == 0;
- }
|