mirror of
https://github.com/obgm/libcoap.git
synced 2026-11-02 07:55:46 +03:00
322 lines
8.3 KiB
C
322 lines
8.3 KiB
C
/* pdu.c -- CoAP message structure
|
|
*
|
|
* Copyright (C) 2010,2011 Olaf Bergmann <bergmann@tzi.org>
|
|
*
|
|
* This file is part of the CoAP library libcoap. Please see
|
|
* README for terms of use.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#if defined(HAVE_ASSERT_H) && !defined(assert)
|
|
# include <assert.h>
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#ifdef HAVE_ARPA_INET_H
|
|
#include <arpa/inet.h>
|
|
#endif
|
|
|
|
#include "debug.h"
|
|
#include "pdu.h"
|
|
#include "option.h"
|
|
#include "encode.h"
|
|
|
|
#ifdef WITH_CONTIKI
|
|
#include "memb.h"
|
|
|
|
typedef unsigned char _pdu[sizeof(coap_pdu_t) + COAP_MAX_PDU_SIZE];
|
|
|
|
MEMB(pdu_storage, _pdu, COAP_PDU_MAXCNT);
|
|
|
|
void
|
|
coap_pdu_resources_init() {
|
|
memb_init(&pdu_storage);
|
|
}
|
|
#else /* WITH_CONTIKI */
|
|
#include "mem.h"
|
|
#endif /* WITH_CONTIKI */
|
|
|
|
void
|
|
coap_pdu_clear(coap_pdu_t *pdu, size_t size) {
|
|
assert(pdu);
|
|
|
|
memset(pdu, 0, sizeof(coap_pdu_t) + size);
|
|
pdu->max_size = size;
|
|
pdu->hdr = (coap_hdr_t *)((unsigned char *)pdu + sizeof(coap_pdu_t));
|
|
pdu->hdr->version = COAP_DEFAULT_VERSION;
|
|
|
|
/* data points after the header; when options are added, the data
|
|
pointer is moved to the back */
|
|
pdu->length = sizeof(coap_hdr_t);
|
|
pdu->data = (unsigned char *)pdu->hdr + pdu->length;
|
|
}
|
|
|
|
coap_pdu_t *
|
|
coap_pdu_init(unsigned char type, unsigned char code,
|
|
unsigned short id, size_t size) {
|
|
coap_pdu_t *pdu;
|
|
|
|
assert(size <= COAP_MAX_PDU_SIZE);
|
|
/* Size must be large enough to fit the header. */
|
|
if (size < sizeof(coap_hdr_t) || size > COAP_MAX_PDU_SIZE)
|
|
return NULL;
|
|
|
|
/* size must be large enough for hdr */
|
|
#ifndef WITH_CONTIKI
|
|
pdu = coap_malloc(sizeof(coap_pdu_t) + size);
|
|
#else /* WITH_CONTIKI */
|
|
pdu = (coap_pdu_t *)memb_alloc(&pdu_storage);
|
|
#endif /* WITH_CONTIKI */
|
|
if (pdu) {
|
|
coap_pdu_clear(pdu, size);
|
|
pdu->hdr->id = id;
|
|
pdu->hdr->type = type;
|
|
pdu->hdr->code = code;
|
|
}
|
|
return pdu;
|
|
}
|
|
|
|
coap_pdu_t *
|
|
coap_new_pdu() {
|
|
coap_pdu_t *pdu;
|
|
|
|
#ifndef WITH_CONTIKI
|
|
pdu = coap_pdu_init(0, 0, ntohs(COAP_INVALID_TID), COAP_MAX_PDU_SIZE);
|
|
#else /* WITH_CONTIKI */
|
|
pdu = coap_pdu_init(0, 0, uip_ntohs(COAP_INVALID_TID), COAP_MAX_PDU_SIZE);
|
|
#endif /* WITH_CONTIKI */
|
|
|
|
#ifndef NDEBUG
|
|
if (!pdu)
|
|
coap_log(LOG_CRIT, "coap_new_pdu: cannot allocate memory for new PDU\n");
|
|
#endif
|
|
return pdu;
|
|
}
|
|
|
|
void
|
|
coap_delete_pdu(coap_pdu_t *pdu) {
|
|
#ifndef WITH_CONTIKI
|
|
coap_free( pdu );
|
|
#else /* WITH_CONTIKI */
|
|
memb_free(&pdu_storage, pdu);
|
|
#endif /* WITH_CONTIKI */
|
|
}
|
|
|
|
int
|
|
coap_add_option(coap_pdu_t *pdu, unsigned short type, unsigned int len, const unsigned char *data) {
|
|
size_t optsize, cnt, optcnt, opt_code = 0;
|
|
unsigned char fence;
|
|
coap_opt_t *opt;
|
|
size_t old_optcnt; /* for restoring old values */
|
|
coap_opt_t *old_opt;
|
|
|
|
if (!pdu)
|
|
return -1;
|
|
|
|
/* Get last option from pdu to calculate the delta. For optcnt ==
|
|
* 0x0F, opt will point at the end marker, so the new option will
|
|
* overwrite it.
|
|
*/
|
|
opt = options_start(pdu);
|
|
cnt = pdu->hdr->optcnt;
|
|
while ((pdu->hdr->optcnt == COAP_OPT_LONG && opt &&
|
|
opt < ((unsigned char *)pdu->hdr + pdu->max_size) &&
|
|
*opt != COAP_OPT_END)
|
|
|| cnt--) {
|
|
opt_code += COAP_OPT_DELTA(opt);
|
|
opt = options_next(opt);
|
|
}
|
|
|
|
if ((unsigned char *)opt > (unsigned char *)pdu->hdr + pdu->max_size) {
|
|
debug("illegal option list\n");
|
|
return -1;
|
|
}
|
|
|
|
if ( type < opt_code ) {
|
|
#ifndef NDEBUG
|
|
coap_log(LOG_WARN, "options not added in correct order\n");
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
optcnt = old_optcnt = pdu->hdr->optcnt;
|
|
old_opt = opt;
|
|
optsize = len + (len > 14 ? 2 : 1);
|
|
/* Create new option after last existing option: First check if we
|
|
* need fence posts between type and last opt_code (i.e. delta >
|
|
* 15), and then add actual option.
|
|
*/
|
|
|
|
/* add fence post */
|
|
fence = ((COAP_OPTION_NOOP * ((opt_code / COAP_OPTION_NOOP) + 1))
|
|
- opt_code) << 4;
|
|
while (opt_code + 15 < type) {
|
|
if ((unsigned char *)opt + optsize + 1 >
|
|
(unsigned char *)pdu->hdr + pdu->max_size)
|
|
goto error;
|
|
|
|
optcnt += 1;
|
|
*opt = fence;
|
|
|
|
opt_code += COAP_OPT_DELTA(opt);
|
|
opt = options_next(opt);
|
|
fence = COAP_OPTION_NOOP << 4;
|
|
}
|
|
|
|
if ((unsigned char *)opt + optsize + 1 >
|
|
(unsigned char *)pdu->hdr + pdu->max_size)
|
|
goto error;
|
|
|
|
/* here, the actual option is added (delta <= 15) */
|
|
optcnt += 1;
|
|
COAP_OPT_SETDELTA( opt, type - opt_code );
|
|
|
|
COAP_OPT_SETLENGTH( opt, len );
|
|
memcpy(COAP_OPT_VALUE(opt), data, len);
|
|
pdu->data = (unsigned char *)COAP_OPT_VALUE(opt) + len ;
|
|
|
|
if (optcnt < COAP_OPT_LONG) {
|
|
pdu->hdr->optcnt = optcnt;
|
|
} else {
|
|
pdu->hdr->optcnt = COAP_OPT_LONG;
|
|
*pdu->data = COAP_OPT_END;
|
|
pdu->data++;
|
|
}
|
|
pdu->length = pdu->data - (unsigned char *)pdu->hdr;
|
|
return len;
|
|
|
|
error:
|
|
/* Restore state before we have tried to add fencepost options.
|
|
* Because pdu->data and pdu->length have not yet been updated,
|
|
* there is no need to touch them. Only the end marker must been
|
|
* written anew if optcnt == 15;
|
|
*/
|
|
|
|
pdu->hdr->optcnt = old_optcnt;
|
|
if (pdu->hdr->optcnt == COAP_OPT_LONG)
|
|
*old_opt = COAP_OPT_END;
|
|
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
coap_add_data(coap_pdu_t *pdu, unsigned int len, const unsigned char *data) {
|
|
if ( !pdu )
|
|
return 0;
|
|
|
|
if ( pdu->length + len > pdu->max_size ) {
|
|
#ifndef NDEBUG
|
|
coap_log(LOG_WARN, "coap_add_data: cannot add: data too large for PDU\n");
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
memcpy( (unsigned char *)pdu->hdr + pdu->length, data, len );
|
|
pdu->length += len;
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
coap_get_data(coap_pdu_t *pdu, size_t *len, unsigned char **data) {
|
|
if ( !pdu )
|
|
return 0;
|
|
|
|
if ( pdu->data < (unsigned char *)pdu->hdr + pdu->length ) {
|
|
/* pdu contains data */
|
|
|
|
*len = (unsigned char *)pdu->hdr + pdu->length - pdu->data;
|
|
*data = pdu->data;
|
|
} else { /* no data, clear everything */
|
|
*len = 0;
|
|
*data = NULL;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
#ifndef SHORT_ERROR_RESPONSE
|
|
typedef struct {
|
|
unsigned char code;
|
|
char *phrase;
|
|
} error_desc_t;
|
|
|
|
/* if you change anything here, make sure, that the longest string does not
|
|
* exceed COAP_ERROR_PHRASE_LENGTH. */
|
|
error_desc_t coap_error[] = {
|
|
{ COAP_RESPONSE_CODE(65), "2.01 Created" },
|
|
{ COAP_RESPONSE_CODE(66), "2.02 Deleted" },
|
|
{ COAP_RESPONSE_CODE(67), "2.03 Valid" },
|
|
{ COAP_RESPONSE_CODE(68), "2.04 Changed" },
|
|
{ COAP_RESPONSE_CODE(69), "2.05 Content" },
|
|
{ COAP_RESPONSE_CODE(400), "Bad Request" },
|
|
{ COAP_RESPONSE_CODE(401), "Unauthorized" },
|
|
{ COAP_RESPONSE_CODE(402), "Bad Option" },
|
|
{ COAP_RESPONSE_CODE(403), "Forbidden" },
|
|
{ COAP_RESPONSE_CODE(404), "Not Found" },
|
|
{ COAP_RESPONSE_CODE(405), "Method Not Allowed" },
|
|
{ COAP_RESPONSE_CODE(408), "Request Entity Incomplete" },
|
|
{ COAP_RESPONSE_CODE(413), "Request Entity Too Large" },
|
|
{ COAP_RESPONSE_CODE(415), "Unsupported Media Type" },
|
|
{ COAP_RESPONSE_CODE(500), "Internal Server Error" },
|
|
{ COAP_RESPONSE_CODE(501), "Not Implemented" },
|
|
{ COAP_RESPONSE_CODE(502), "Bad Gateway" },
|
|
{ COAP_RESPONSE_CODE(503), "Service Unavailable" },
|
|
{ COAP_RESPONSE_CODE(504), "Gateway Timeout" },
|
|
{ COAP_RESPONSE_CODE(505), "Proxying Not Supported" },
|
|
{ 0, NULL } /* end marker */
|
|
};
|
|
|
|
char *
|
|
coap_response_phrase(unsigned char code) {
|
|
int i;
|
|
for (i = 0; coap_error[i].code; ++i) {
|
|
if (coap_error[i].code == code)
|
|
return coap_error[i].phrase;
|
|
}
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
int
|
|
coap_get_request_uri(coap_pdu_t *pdu, coap_uri_t *result) {
|
|
coap_opt_t *opt;
|
|
coap_opt_iterator_t opt_iter;
|
|
|
|
if (!pdu || !result)
|
|
return 0;
|
|
|
|
memset(result, 0, sizeof(*result));
|
|
|
|
if ((opt = coap_check_option(pdu, COAP_OPTION_URI_HOST, &opt_iter)))
|
|
COAP_SET_STR(&result->host, COAP_OPT_LENGTH(*opt), COAP_OPT_VALUE(*opt));
|
|
|
|
if ((opt = coap_check_option(pdu, COAP_OPTION_URI_PORT, &opt_iter)))
|
|
result->port =
|
|
coap_decode_var_bytes(COAP_OPT_VALUE(*opt), COAP_OPT_LENGTH(*opt));
|
|
else
|
|
result->port = COAP_DEFAULT_PORT;
|
|
|
|
if ((opt = coap_check_option(pdu, COAP_OPTION_URI_PATH, &opt_iter))) {
|
|
result->path.s = COAP_OPT_VALUE(*opt);
|
|
result->path.length = COAP_OPT_LENGTH(*opt);
|
|
|
|
while (coap_option_next(&opt_iter) && opt_iter.type == COAP_OPTION_URI_PATH)
|
|
result->path.length += COAP_OPT_SIZE(*opt_iter.option);
|
|
}
|
|
|
|
if ((opt = coap_check_option(pdu, COAP_OPTION_URI_QUERY, &opt_iter))) {
|
|
result->query.s = COAP_OPT_VALUE(*opt);
|
|
result->query.length = COAP_OPT_LENGTH(*opt);
|
|
|
|
while (coap_option_next(&opt_iter) && opt_iter.type == COAP_OPTION_URI_QUERY)
|
|
result->query.length += COAP_OPT_SIZE(*opt_iter.option);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
#endif
|