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() {
36 memb_init(&pdu_storage);
38 #else /* WITH_CONTIKI */
40 #endif /* WITH_CONTIKI */
43 coap_pdu_clear(coap_pdu_t *pdu, size_t size) {
46 memset(pdu, 0, sizeof(coap_pdu_t) + size);
48 pdu->hdr = (coap_hdr_t *)((unsigned char *)pdu + sizeof(coap_pdu_t));
49 pdu->hdr->version = COAP_DEFAULT_VERSION;
51 /* data is NULL unless explicitly set by coap_add_data() */
52 pdu->length = sizeof(coap_hdr_t);
57 coap_pdu_from_pbuf(struct pbuf *pbuf)
59 LWIP_ASSERT("Can only deal with contiguous PBUFs", pbuf->tot_len == pbuf->len);
60 LWIP_ASSERT("coap_read needs to receive an exclusive copy of the incoming pbuf", pbuf->ref == 1);
62 void *data = pbuf->payload;
65 u8_t header_error = pbuf_header(pbuf, sizeof(coap_pdu_t));
66 LWIP_ASSERT("CoAP PDU header does not fit in existing header space", header_error == 0);
68 result = (coap_pdu_t *)pbuf->payload;
70 memset(result, 0, sizeof(coap_pdu_t));
72 result->max_size = pbuf->tot_len - sizeof(coap_pdu_t);
73 result->length = pbuf->tot_len - sizeof(coap_pdu_t);
82 coap_pdu_init(unsigned char type, unsigned char code,
83 unsigned short id, size_t size) {
89 assert(size <= COAP_MAX_PDU_SIZE);
90 /* Size must be large enough to fit the header. */
91 if (size < sizeof(coap_hdr_t) || size > COAP_MAX_PDU_SIZE)
94 /* size must be large enough for hdr */
96 pdu = (coap_pdu_t*)coap_malloc(sizeof(coap_pdu_t) + size);
99 pdu = (coap_pdu_t *)memb_alloc(&pdu_storage);
102 p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM);
104 u8_t header_error = pbuf_header(p, sizeof(coap_pdu_t));
105 /* we could catch that case and allocate larger memory in advance, but then
106 * again, we'd run into greater trouble with incoming packages anyway */
107 LWIP_ASSERT("CoAP PDU header does not fit in transport header", header_error == 0);
114 coap_pdu_clear(pdu, size);
116 pdu->hdr->type = type;
117 pdu->hdr->code = code;
130 pdu = coap_pdu_init(0, 0, ntohs(COAP_INVALID_TID), COAP_MAX_PDU_SIZE);
131 #else /* WITH_CONTIKI */
132 pdu = coap_pdu_init(0, 0, uip_ntohs(COAP_INVALID_TID), COAP_MAX_PDU_SIZE);
133 #endif /* WITH_CONTIKI */
137 coap_log(LOG_CRIT, "coap_new_pdu: cannot allocate memory for new PDU\n");
143 coap_delete_pdu(coap_pdu_t *pdu) {
148 if (pdu != NULL) /* accepting double free as the other implementation accept that too */
149 pbuf_free(pdu->pbuf);
152 memb_free(&pdu_storage, pdu);
157 coap_add_token(coap_pdu_t *pdu, size_t len, const unsigned char *data) {
158 const size_t HEADERLENGTH = len + 4;
159 /* must allow for pdu == NULL as callers may rely on this */
160 if (!pdu || len > 8 || pdu->max_size < HEADERLENGTH)
163 pdu->hdr->token_length = len;
165 memcpy(pdu->hdr->token, data, len);
167 pdu->length = HEADERLENGTH;
173 /** @FIXME de-duplicate code with coap_add_option_later */
175 coap_add_option(coap_pdu_t *pdu, unsigned short type, unsigned int len, const unsigned char *data) {
182 if (type < pdu->max_delta) {
183 warn("coap_add_option: options are not in correct order\n");
187 opt = (unsigned char *)pdu->hdr + pdu->length;
189 /* encode option and check length */
190 optsize = coap_opt_encode(opt, pdu->max_size - pdu->length,
191 type - pdu->max_delta, data, len);
194 warn("coap_add_option: cannot add option\n");
198 pdu->max_delta = type;
199 pdu->length += optsize;
205 /** @FIXME de-duplicate code with coap_add_option */
207 coap_add_option_later(coap_pdu_t *pdu, unsigned short type, unsigned int len) {
214 if (type < pdu->max_delta) {
215 warn("coap_add_option: options are not in correct order\n");
219 opt = (unsigned char *)pdu->hdr + pdu->length;
221 /* encode option and check length */
222 optsize = coap_opt_encode(opt, pdu->max_size - pdu->length,
223 type - pdu->max_delta, NULL, len);
226 warn("coap_add_option: cannot add option\n");
230 pdu->max_delta = type;
231 pdu->length += optsize;
234 return ((unsigned char*)opt) + optsize - len;
238 coap_add_data(coap_pdu_t *pdu, unsigned int len, const unsigned char *data) {
240 assert(pdu->data == NULL);
245 if (pdu->length + len + 1 > pdu->max_size) {
246 warn("coap_add_data: cannot add: data too large for PDU\n");
247 assert(pdu->data == NULL);
251 pdu->data = (unsigned char *)pdu->hdr + pdu->length;
252 *pdu->data = COAP_PAYLOAD_START;
255 memcpy(pdu->data, data, len);
256 pdu->length += len + 1;
261 coap_get_data(coap_pdu_t *pdu, size_t *len, unsigned char **data) {
267 *len = (unsigned char *)pdu->hdr + pdu->length - pdu->data;
269 } else { /* no data, clear everything */
274 return *data != NULL;
277 #ifndef SHORT_ERROR_RESPONSE
283 /* if you change anything here, make sure, that the longest string does not
284 * exceed COAP_ERROR_PHRASE_LENGTH. */
285 error_desc_t coap_error[] = {
286 { COAP_RESPONSE_CODE(65), "2.01 Created" },
287 { COAP_RESPONSE_CODE(66), "2.02 Deleted" },
288 { COAP_RESPONSE_CODE(67), "2.03 Valid" },
289 { COAP_RESPONSE_CODE(68), "2.04 Changed" },
290 { COAP_RESPONSE_CODE(69), "2.05 Content" },
291 { COAP_RESPONSE_CODE(400), "Bad Request" },
292 { COAP_RESPONSE_CODE(401), "Unauthorized" },
293 { COAP_RESPONSE_CODE(402), "Bad Option" },
294 { COAP_RESPONSE_CODE(403), "Forbidden" },
295 { COAP_RESPONSE_CODE(404), "Not Found" },
296 { COAP_RESPONSE_CODE(405), "Method Not Allowed" },
297 { COAP_RESPONSE_CODE(408), "Request Entity Incomplete" },
298 { COAP_RESPONSE_CODE(413), "Request Entity Too Large" },
299 { COAP_RESPONSE_CODE(415), "Unsupported Media Type" },
300 { COAP_RESPONSE_CODE(500), "Internal Server Error" },
301 { COAP_RESPONSE_CODE(501), "Not Implemented" },
302 { COAP_RESPONSE_CODE(502), "Bad Gateway" },
303 { COAP_RESPONSE_CODE(503), "Service Unavailable" },
304 { COAP_RESPONSE_CODE(504), "Gateway Timeout" },
305 { COAP_RESPONSE_CODE(505), "Proxying Not Supported" },
306 { 0, NULL } /* end marker */
310 coap_response_phrase(unsigned char code) {
312 for (i = 0; coap_error[i].code; ++i) {
313 if (coap_error[i].code == code)
314 return coap_error[i].phrase;
321 * Advances *optp to next option if still in PDU. This function
322 * returns the number of bytes opt has been advanced or @c 0
326 next_option_safe(coap_opt_t **optp, size_t *length) {
327 coap_option_t option;
330 assert(optp); assert(*optp);
333 optsize = coap_opt_parse(*optp, *length, &option);
335 assert(optsize <= *length);
345 coap_pdu_parse(unsigned char *data, size_t length, coap_pdu_t *pdu) {
351 if (pdu->max_size < length) {
352 debug("insufficient space to store parsed PDU\n");
356 if (length < sizeof(coap_hdr_t)) {
357 debug("discarded invalid PDU\n");
360 pdu->hdr->version = data[0] >> 6;
361 pdu->hdr->type = (data[0] >> 4) & 0x03;
362 pdu->hdr->token_length = data[0] & 0x0f;
363 pdu->hdr->code = data[1];
367 if (pdu->hdr->code == 0) {
368 if (length != sizeof(coap_hdr_t) || pdu->hdr->token_length) {
369 debug("coap_pdu_parse: empty message is not empty\n");
374 if (length < sizeof(coap_hdr_t) + pdu->hdr->token_length
375 || pdu->hdr->token_length > 8) {
376 debug("coap_pdu_parse: invalid Token\n");
380 /* Copy message id in network byte order, so we can easily write the
381 * response back to the network. */
382 memcpy(&pdu->hdr->id, data + 2, 2);
384 /* append data (including the Token) to pdu structure */
385 memcpy(pdu->hdr + 1, data + sizeof(coap_hdr_t), length - sizeof(coap_hdr_t));
386 pdu->length = length;
388 /* Finally calculate beginning of data block and thereby check integrity
389 * of the PDU structure. */
391 /* skip header + token */
392 length -= (pdu->hdr->token_length + sizeof(coap_hdr_t));
393 opt = (unsigned char *)(pdu->hdr + 1) + pdu->hdr->token_length;
395 while (length && *opt != COAP_PAYLOAD_START) {
397 if (!next_option_safe(&opt, (size_t *)&length)) {
398 debug("coap_pdu_parse: drop\n");
403 /* end of packet or start marker */
405 assert(*opt == COAP_PAYLOAD_START);
409 debug("coap_pdu_parse: message ending in payload start marker\n");
413 debug("set data to %p (pdu ends at %p)\n", (unsigned char *)opt,
414 (unsigned char *)pdu->hdr + pdu->length);
415 pdu->data = (unsigned char *)opt;