1 /* pdu.c -- CoAP message structure
3 * Copyright (C) 2010,2011 Olaf Bergmann <bergmann@tzi.org>
5 * This file is part of the CoAP library libcoap. Please see
6 * README for terms of use.
11 #if defined(HAVE_ASSERT_H) && !defined(assert)
18 #ifdef HAVE_ARPA_INET_H
19 #include <arpa/inet.h>
30 typedef unsigned char _pdu[sizeof(coap_pdu_t) + COAP_MAX_PDU_SIZE];
32 MEMB(pdu_storage, _pdu, COAP_PDU_MAXCNT);
35 coap_pdu_resources_init()
37 memb_init(&pdu_storage);
39 #else /* WITH_CONTIKI */
41 #endif /* WITH_CONTIKI */
43 void coap_pdu_clear(coap_pdu_t *pdu, size_t size)
47 memset(pdu, 0, sizeof(coap_pdu_t) + size);
49 pdu->hdr = (coap_hdr_t *) ((unsigned char *) pdu + sizeof(coap_pdu_t));
50 pdu->hdr->version = COAP_DEFAULT_VERSION;
52 /* data is NULL unless explicitly set by coap_add_data() */
53 pdu->length = sizeof(coap_hdr_t);
58 coap_pdu_from_pbuf(struct pbuf *pbuf)
60 LWIP_ASSERT("Can only deal with contiguous PBUFs", pbuf->tot_len == pbuf->len);
61 LWIP_ASSERT("coap_read needs to receive an exclusive copy of the incoming pbuf", pbuf->ref == 1);
63 void *data = pbuf->payload;
66 u8_t header_error = pbuf_header(pbuf, sizeof(coap_pdu_t));
67 LWIP_ASSERT("CoAP PDU header does not fit in existing header space", header_error == 0);
69 result = (coap_pdu_t *)pbuf->payload;
71 memset(result, 0, sizeof(coap_pdu_t));
73 result->max_size = pbuf->tot_len - sizeof(coap_pdu_t);
74 result->length = pbuf->tot_len - sizeof(coap_pdu_t);
83 coap_pdu_init(unsigned char type, unsigned char code, unsigned short id, size_t size)
90 assert(size <= COAP_MAX_PDU_SIZE);
91 /* Size must be large enough to fit the header. */
92 if (size < sizeof(coap_hdr_t) || size > COAP_MAX_PDU_SIZE)
95 /* size must be large enough for hdr */
97 pdu = coap_malloc(sizeof(coap_pdu_t) + size);
100 pdu = (coap_pdu_t *)memb_alloc(&pdu_storage);
103 p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM);
106 u8_t header_error = pbuf_header(p, sizeof(coap_pdu_t));
107 /* we could catch that case and allocate larger memory in advance, but then
108 * again, we'd run into greater trouble with incoming packages anyway */
109 LWIP_ASSERT("CoAP PDU header does not fit in transport header", header_error == 0);
119 coap_pdu_clear(pdu, size);
121 pdu->hdr->type = type;
122 pdu->hdr->code = code;
136 pdu = coap_pdu_init(0, 0, ntohs(COAP_INVALID_TID), COAP_MAX_PDU_SIZE);
137 #else /* WITH_CONTIKI */
138 pdu = coap_pdu_init(0, 0, uip_ntohs(COAP_INVALID_TID), COAP_MAX_PDU_SIZE);
139 #endif /* WITH_CONTIKI */
143 coap_log(LOG_CRIT, "coap_new_pdu: cannot allocate memory for new PDU\n");
148 void coap_delete_pdu(coap_pdu_t *pdu)
154 if (pdu != NULL) /* accepting double free as the other implementation accept that too */
155 pbuf_free(pdu->pbuf);
158 memb_free(&pdu_storage, pdu);
162 int coap_add_token(coap_pdu_t *pdu, size_t len, const unsigned char *data)
164 const size_t HEADERLENGTH = len + 4;
165 /* must allow for pdu == NULL as callers may rely on this */
166 if (!pdu || len > 8 || pdu->max_size < HEADERLENGTH)
169 pdu->hdr->token_length = len;
171 memcpy(pdu->hdr->token, data, len);
173 pdu->length = HEADERLENGTH;
179 /** @FIXME de-duplicate code with coap_add_option_later */
180 size_t coap_add_option(coap_pdu_t *pdu, unsigned short type, unsigned int len,
181 const unsigned char *data)
189 if (type < pdu->max_delta)
191 warn("coap_add_option: options are not in correct order\n");
195 opt = (unsigned char *) pdu->hdr + pdu->length;
197 /* encode option and check length */
198 optsize = coap_opt_encode(opt, pdu->max_size - pdu->length, type - pdu->max_delta, data, len);
202 warn("coap_add_option: cannot add option\n");
208 pdu->max_delta = type;
209 pdu->length += optsize;
215 /** @FIXME de-duplicate code with coap_add_option */
217 coap_add_option_later(coap_pdu_t *pdu, unsigned short type, unsigned int len)
225 if (type < pdu->max_delta)
227 warn("coap_add_option: options are not in correct order\n");
231 opt = (unsigned char *) pdu->hdr + pdu->length;
233 /* encode option and check length */
234 optsize = coap_opt_encode(opt, pdu->max_size - pdu->length, type - pdu->max_delta, NULL, len);
238 warn("coap_add_option: cannot add option\n");
244 pdu->max_delta = type;
245 pdu->length += optsize;
248 return ((unsigned char*) opt) + optsize - len;
251 int coap_add_data(coap_pdu_t *pdu, unsigned int len, const unsigned char *data)
254 assert(pdu->data == NULL);
259 if (pdu->length + len + 1 > pdu->max_size)
261 warn("coap_add_data: cannot add: data too large for PDU\n");
262 assert(pdu->data == NULL);
266 pdu->data = (unsigned char *) pdu->hdr + pdu->length;
267 *pdu->data = COAP_PAYLOAD_START;
270 memcpy(pdu->data, data, len);
271 pdu->length += len + 1;
275 int coap_get_data(coap_pdu_t *pdu, size_t *len, unsigned char **data)
283 *len = (unsigned char *) pdu->hdr + pdu->length - pdu->data;
287 { /* no data, clear everything */
292 return *data != NULL;
295 #ifndef SHORT_ERROR_RESPONSE
302 /* if you change anything here, make sure, that the longest string does not
303 * exceed COAP_ERROR_PHRASE_LENGTH. */
304 error_desc_t coap_error[] =
306 { COAP_RESPONSE_CODE(65), "2.01 Created" },
307 { COAP_RESPONSE_CODE(66), "2.02 Deleted" },
308 { COAP_RESPONSE_CODE(67), "2.03 Valid" },
309 { COAP_RESPONSE_CODE(68), "2.04 Changed" },
310 { COAP_RESPONSE_CODE(69), "2.05 Content" },
311 { COAP_RESPONSE_CODE(400), "Bad Request" },
312 { COAP_RESPONSE_CODE(401), "Unauthorized" },
313 { COAP_RESPONSE_CODE(402), "Bad Option" },
314 { COAP_RESPONSE_CODE(403), "Forbidden" },
315 { COAP_RESPONSE_CODE(404), "Not Found" },
316 { COAP_RESPONSE_CODE(405), "Method Not Allowed" },
317 { COAP_RESPONSE_CODE(408), "Request Entity Incomplete" },
318 { COAP_RESPONSE_CODE(413), "Request Entity Too Large" },
319 { COAP_RESPONSE_CODE(415), "Unsupported Media Type" },
320 { COAP_RESPONSE_CODE(500), "Internal Server Error" },
321 { COAP_RESPONSE_CODE(501), "Not Implemented" },
322 { COAP_RESPONSE_CODE(502), "Bad Gateway" },
323 { COAP_RESPONSE_CODE(503), "Service Unavailable" },
324 { COAP_RESPONSE_CODE(504), "Gateway Timeout" },
325 { COAP_RESPONSE_CODE(505), "Proxying Not Supported" },
326 { 0, NULL } /* end marker */
330 coap_response_phrase(unsigned char code)
333 for (i = 0; coap_error[i].code; ++i)
335 if (coap_error[i].code == code)
336 return coap_error[i].phrase;
343 * Advances *optp to next option if still in PDU. This function
344 * returns the number of bytes opt has been advanced or @c 0
347 static size_t next_option_safe(coap_opt_t **optp, size_t *length, coap_option_t* option)
349 //coap_option_t option;
355 optsize = coap_opt_parse(*optp, *length, option);
358 assert(optsize <= *length);
367 int coap_pdu_parse(unsigned char *data, size_t length, coap_pdu_t *pdu)
371 char optionResult[256] =
377 if (pdu->max_size < length)
379 debug("insufficient space to store parsed PDU\n");
380 printf("[COAP] insufficient space to store parsed PDU\n");
384 if (length < sizeof(coap_hdr_t))
386 debug("discarded invalid PDU\n");
389 pdu->hdr->version = data[0] >> 6;
390 pdu->hdr->type = (data[0] >> 4) & 0x03;
391 pdu->hdr->token_length = data[0] & 0x0f;
392 pdu->hdr->code = data[1];
394 printf("[COAP] pdu - version : %d\n", pdu->hdr->version);
395 printf("[COAP] pdu - type : %d\n", pdu->hdr->type);
396 printf("[COAP] pdu - token_length : %d\n", pdu->hdr->token_length);
397 printf("[COAP] pdu - code : %d\n", pdu->hdr->code);
402 if (pdu->hdr->code == 0)
404 if (length != sizeof(coap_hdr_t) || pdu->hdr->token_length)
406 debug("coap_pdu_parse: empty message is not empty\n");
411 if (length < sizeof(coap_hdr_t) + pdu->hdr->token_length || pdu->hdr->token_length > 8)
413 debug("coap_pdu_parse: invalid Token\n");
417 /* Copy message id in network byte order, so we can easily write the
418 * response back to the network. */
419 memcpy(&pdu->hdr->id, data + 2, 2);
421 //printf("[COAP] pdu - id : %d\n", pdu->hdr->id);
423 /* append data (including the Token) to pdu structure */
424 memcpy(pdu->hdr + 1, data + sizeof(coap_hdr_t), length - sizeof(coap_hdr_t));
425 pdu->length = length;
427 /* Finally calculate beginning of data block and thereby check integrity
428 * of the PDU structure. */
430 /* skip header + token */
431 length -= (pdu->hdr->token_length + sizeof(coap_hdr_t));
432 opt = (unsigned char *) (pdu->hdr + 1) + pdu->hdr->token_length;
434 while (length && *opt != COAP_PAYLOAD_START)
436 coap_option_t option;
437 memset(&option, 0, sizeof(coap_option_t));
438 if (!next_option_safe(&opt, (size_t *) &length, &option))
440 debug("coap_pdu_parse: drop\n");
445 /* end of packet or start marker */
448 assert(*opt == COAP_PAYLOAD_START);
454 debug("coap_pdu_parse: message ending in payload start marker\n");
459 "set data to %p (pdu ends at %p)\n", (unsigned char *)opt, (unsigned char *)pdu->hdr + pdu->length);
460 pdu->data = (unsigned char *) opt;
461 //printf("[COAP] pdu - data : %s\n", pdu->data);