coap.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <stdint.h>
  4. #include <stdbool.h>
  5. #include <string.h>
  6. #include <stddef.h>
  7. #include "coap.h"
  8. extern void endpoint_setup(void);
  9. extern const coap_endpoint_t endpoints[];
  10. #ifdef DEBUG
  11. void coap_dumpHeader(coap_header_t *hdr)
  12. {
  13. printf("Header:\n");
  14. printf(" ver 0x%02X\n", hdr->ver);
  15. printf(" t 0x%02X\n", hdr->t);
  16. printf(" tkl 0x%02X\n", hdr->tkl);
  17. printf(" code 0x%02X\n", hdr->code);
  18. printf(" id 0x%02X%02X\n", hdr->id[0], hdr->id[1]);
  19. }
  20. #endif
  21. #ifdef DEBUG
  22. void coap_dump(const uint8_t *buf, size_t buflen, _Bool bare)
  23. {
  24. if (bare)
  25. {
  26. while(buflen--)
  27. printf("%02X%s", *buf++, (buflen > 0) ? " " : "");
  28. }
  29. else
  30. {
  31. printf("Dump: ");
  32. while(buflen--)
  33. printf("%02X%s", *buf++, (buflen > 0) ? " " : "");
  34. printf("\n");
  35. }
  36. }
  37. #endif
  38. int coap_parseHeader(coap_header_t *hdr, const uint8_t *buf, size_t buflen)
  39. {
  40. if (buflen < 4)
  41. return COAP_ERR_HEADER_TOO_SHORT;
  42. hdr->ver = (buf[0] & 0xC0) >> 6;
  43. if (hdr->ver != 1)
  44. return COAP_ERR_VERSION_NOT_1;
  45. hdr->t = (buf[0] & 0x30) >> 4;
  46. hdr->tkl = buf[0] & 0x0F;
  47. hdr->code = buf[1];
  48. hdr->id[0] = buf[2];
  49. hdr->id[1] = buf[3];
  50. return 0;
  51. }
  52. int coap_parseToken(coap_buffer_t *tokbuf, const coap_header_t *hdr, const uint8_t *buf, size_t buflen)
  53. {
  54. if (hdr->tkl == 0)
  55. {
  56. tokbuf->p = NULL;
  57. tokbuf->len = 0;
  58. return 0;
  59. }
  60. else
  61. if (hdr->tkl <= 8)
  62. {
  63. if (4U + hdr->tkl > buflen)
  64. return COAP_ERR_TOKEN_TOO_SHORT; // tok bigger than packet
  65. tokbuf->p = buf+4; // past header
  66. tokbuf->len = hdr->tkl;
  67. return 0;
  68. }
  69. else
  70. {
  71. // invalid size
  72. return COAP_ERR_TOKEN_TOO_SHORT;
  73. }
  74. }
  75. // advances p
  76. int coap_parseOption(coap_option_t *option, uint16_t *running_delta, const uint8_t **buf, size_t buflen)
  77. {
  78. const uint8_t *p = *buf;
  79. uint8_t headlen = 1;
  80. uint16_t len, delta;
  81. if (buflen < headlen) // too small
  82. return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
  83. delta = (p[0] & 0xF0) >> 4;
  84. len = p[0] & 0x0F;
  85. // These are untested and may be buggy
  86. if (delta == 13)
  87. {
  88. headlen++;
  89. if (buflen < headlen)
  90. return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
  91. delta = p[1] + 13;
  92. p++;
  93. }
  94. else
  95. if (delta == 14)
  96. {
  97. headlen += 2;
  98. if (buflen < headlen)
  99. return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
  100. delta = ((p[1] << 8) | p[2]) + 269;
  101. p+=2;
  102. }
  103. else
  104. if (delta == 15)
  105. return COAP_ERR_OPTION_DELTA_INVALID;
  106. if (len == 13)
  107. {
  108. headlen++;
  109. if (buflen < headlen)
  110. return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
  111. len = p[1] + 13;
  112. p++;
  113. }
  114. else
  115. if (len == 14)
  116. {
  117. headlen += 2;
  118. if (buflen < headlen)
  119. return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
  120. len = ((p[1] << 8) | p[2]) + 269;
  121. p+=2;
  122. }
  123. else
  124. if (len == 15)
  125. return COAP_ERR_OPTION_LEN_INVALID;
  126. if ((p + 1 + len) > (*buf + buflen))
  127. return COAP_ERR_OPTION_TOO_BIG;
  128. //printf("option num=%d\n", delta + *running_delta);
  129. option->num = delta + *running_delta;
  130. option->buf.p = p+1;
  131. option->buf.len = len;
  132. //coap_dump(p+1, len, false);
  133. // advance buf
  134. *buf = p + 1 + len;
  135. *running_delta += delta;
  136. return 0;
  137. }
  138. // http://tools.ietf.org/html/rfc7252#section-3.1
  139. 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)
  140. {
  141. size_t optionIndex = 0;
  142. uint16_t delta = 0;
  143. const uint8_t *p = buf + 4 + hdr->tkl;
  144. const uint8_t *end = buf + buflen;
  145. int rc;
  146. if (p > end)
  147. return COAP_ERR_OPTION_OVERRUNS_PACKET; // out of bounds
  148. //coap_dump(p, end - p);
  149. // 0xFF is payload marker
  150. while((optionIndex < *numOptions) && (p < end) && (*p != 0xFF))
  151. {
  152. if (0 != (rc = coap_parseOption(&options[optionIndex], &delta, &p, end-p)))
  153. return rc;
  154. optionIndex++;
  155. }
  156. *numOptions = optionIndex;
  157. if (p+1 < end && *p == 0xFF) // payload marker
  158. {
  159. payload->p = p+1;
  160. payload->len = end-(p+1);
  161. }
  162. else
  163. {
  164. payload->p = NULL;
  165. payload->len = 0;
  166. }
  167. return 0;
  168. }
  169. #ifdef DEBUG
  170. void coap_dumpOptions(coap_option_t *opts, size_t numopt)
  171. {
  172. size_t i;
  173. printf(" Options:\n");
  174. for (i=0;i<numopt;i++)
  175. {
  176. printf(" 0x%02X [ ", opts[i].num);
  177. coap_dump(opts[i].buf.p, opts[i].buf.len, true);
  178. printf(" ]\n");
  179. }
  180. }
  181. #endif
  182. #ifdef DEBUG
  183. void coap_dumpPacket(coap_packet_t *pkt)
  184. {
  185. coap_dumpHeader(&pkt->hdr);
  186. coap_dumpOptions(pkt->opts, pkt->numopts);
  187. printf("Payload: ");
  188. coap_dump(pkt->payload.p, pkt->payload.len, true);
  189. printf("\n");
  190. }
  191. #endif
  192. int coap_parse(coap_packet_t *pkt, const uint8_t *buf, size_t buflen)
  193. {
  194. int rc;
  195. // coap_dump(buf, buflen, false);
  196. if (0 != (rc = coap_parseHeader(&pkt->hdr, buf, buflen)))
  197. return rc;
  198. // coap_dumpHeader(&hdr);
  199. if (0 != (rc = coap_parseToken(&pkt->tok, &pkt->hdr, buf, buflen)))
  200. return rc;
  201. pkt->numopts = MAXOPT;
  202. if (0 != (rc = coap_parseOptionsAndPayload(pkt->opts, &(pkt->numopts), &(pkt->payload), &pkt->hdr, buf, buflen)))
  203. return rc;
  204. // coap_dumpOptions(opts, numopt);
  205. return 0;
  206. }
  207. // options are always stored consecutively, so can return a block with same option num
  208. const coap_option_t *coap_findOptions(const coap_packet_t *pkt, uint8_t num, uint8_t *count)
  209. {
  210. // FIXME, options is always sorted, can find faster than this
  211. size_t i;
  212. const coap_option_t *first = NULL;
  213. *count = 0;
  214. for (i=0;i<pkt->numopts;i++)
  215. {
  216. if (pkt->opts[i].num == num)
  217. {
  218. if (NULL == first)
  219. first = &pkt->opts[i];
  220. (*count)++;
  221. }
  222. else
  223. {
  224. if (NULL != first)
  225. break;
  226. }
  227. }
  228. return first;
  229. }
  230. int coap_buffer_to_string(char *strbuf, size_t strbuflen, const coap_buffer_t *buf)
  231. {
  232. if (buf->len+1 > strbuflen)
  233. return COAP_ERR_BUFFER_TOO_SMALL;
  234. memcpy(strbuf, buf->p, buf->len);
  235. strbuf[buf->len] = 0;
  236. return 0;
  237. }
  238. int coap_build(uint8_t *buf, size_t *buflen, const coap_packet_t *pkt)
  239. {
  240. size_t opts_len = 0;
  241. size_t i;
  242. uint8_t *p;
  243. uint16_t running_delta = 0;
  244. // build header
  245. if (*buflen < (4U + pkt->hdr.tkl))
  246. return COAP_ERR_BUFFER_TOO_SMALL;
  247. buf[0] = (pkt->hdr.ver & 0x03) << 6;
  248. buf[0] |= (pkt->hdr.t & 0x03) << 4;
  249. buf[0] |= (pkt->hdr.tkl & 0x0F);
  250. buf[1] = pkt->hdr.code;
  251. buf[2] = pkt->hdr.id[0];
  252. buf[3] = pkt->hdr.id[1];
  253. // inject token
  254. p = buf + 4;
  255. if ((pkt->hdr.tkl > 0) && (pkt->hdr.tkl != pkt->tok.len))
  256. return COAP_ERR_UNSUPPORTED;
  257. if (pkt->hdr.tkl > 0)
  258. memcpy(p, pkt->tok.p, pkt->hdr.tkl);
  259. // // http://tools.ietf.org/html/rfc7252#section-3.1
  260. // inject options
  261. p += pkt->hdr.tkl;
  262. for (i=0;i<pkt->numopts;i++)
  263. {
  264. uint32_t optDelta;
  265. uint8_t len, delta = 0;
  266. if (((size_t)(p-buf)) > *buflen)
  267. return COAP_ERR_BUFFER_TOO_SMALL;
  268. optDelta = pkt->opts[i].num - running_delta;
  269. coap_option_nibble(optDelta, &delta);
  270. coap_option_nibble((uint32_t)pkt->opts[i].buf.len, &len);
  271. *p++ = (0xFF & (delta << 4 | len));
  272. if (delta == 13)
  273. {
  274. *p++ = (optDelta - 13);
  275. }
  276. else
  277. if (delta == 14)
  278. {
  279. *p++ = ((optDelta-269) >> 8);
  280. *p++ = (0xFF & (optDelta-269));
  281. }
  282. if (len == 13)
  283. {
  284. *p++ = (pkt->opts[i].buf.len - 13);
  285. }
  286. else
  287. if (len == 14)
  288. {
  289. *p++ = (pkt->opts[i].buf.len >> 8);
  290. *p++ = (0xFF & (pkt->opts[i].buf.len-269));
  291. }
  292. memcpy(p, pkt->opts[i].buf.p, pkt->opts[i].buf.len);
  293. p += pkt->opts[i].buf.len;
  294. running_delta = pkt->opts[i].num;
  295. }
  296. opts_len = (p - buf) - 4; // number of bytes used by options
  297. if (pkt->payload.len > 0)
  298. {
  299. if (*buflen < 4 + 1 + pkt->payload.len + opts_len)
  300. return COAP_ERR_BUFFER_TOO_SMALL;
  301. buf[4 + opts_len] = 0xFF; // payload marker
  302. memcpy(buf+5 + opts_len, pkt->payload.p, pkt->payload.len);
  303. *buflen = opts_len + 5 + pkt->payload.len;
  304. }
  305. else
  306. *buflen = opts_len + 4;
  307. return 0;
  308. }
  309. void coap_option_nibble(uint32_t value, uint8_t *nibble)
  310. {
  311. if (value<13)
  312. {
  313. *nibble = (0xFF & value);
  314. }
  315. else
  316. if (value<=0xFF+13)
  317. {
  318. *nibble = 13;
  319. } else if (value<=0xFFFF+269)
  320. {
  321. *nibble = 14;
  322. }
  323. }
  324. 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)
  325. {
  326. pkt->hdr.ver = 0x01;
  327. pkt->hdr.t = COAP_TYPE_ACK;
  328. pkt->hdr.tkl = 0;
  329. pkt->hdr.code = rspcode;
  330. pkt->hdr.id[0] = msgid_hi;
  331. pkt->hdr.id[1] = msgid_lo;
  332. pkt->numopts = 1;
  333. // need token in response
  334. if (tok) {
  335. pkt->hdr.tkl = tok->len;
  336. pkt->tok = *tok;
  337. }
  338. // safe because 1 < MAXOPT
  339. pkt->opts[0].num = COAP_OPTION_CONTENT_FORMAT;
  340. pkt->opts[0].buf.p = scratch->p;
  341. if (scratch->len < 2)
  342. return COAP_ERR_BUFFER_TOO_SMALL;
  343. scratch->p[0] = ((uint16_t)content_type & 0xFF00) >> 8;
  344. scratch->p[1] = ((uint16_t)content_type & 0x00FF);
  345. pkt->opts[0].buf.len = 2;
  346. pkt->payload.p = content;
  347. pkt->payload.len = content_len;
  348. return 0;
  349. }
  350. // FIXME, if this looked in the table at the path before the method then
  351. // it could more easily return 405 errors
  352. int coap_handle_req(coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt)
  353. {
  354. const coap_option_t *opt;
  355. uint8_t count;
  356. int i;
  357. const coap_endpoint_t *ep = endpoints;
  358. while(NULL != ep->handler)
  359. {
  360. if (ep->method != inpkt->hdr.code)
  361. goto next;
  362. if (NULL != (opt = coap_findOptions(inpkt, COAP_OPTION_URI_PATH, &count)))
  363. {
  364. if (count != ep->path->count)
  365. goto next;
  366. for (i=0;i<count;i++)
  367. {
  368. if (opt[i].buf.len != strlen(ep->path->elems[i]))
  369. goto next;
  370. if (0 != memcmp(ep->path->elems[i], opt[i].buf.p, opt[i].buf.len))
  371. goto next;
  372. }
  373. // match!
  374. return ep->handler(scratch, inpkt, outpkt, inpkt->hdr.id[0], inpkt->hdr.id[1]);
  375. }
  376. next:
  377. ep++;
  378. }
  379. coap_make_response(scratch, outpkt, NULL, 0, inpkt->hdr.id[0], inpkt->hdr.id[1], &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE);
  380. return 0;
  381. }
  382. void coap_setup(void)
  383. {
  384. }