libcoap  4.0.3
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pdu.c
Go to the documentation of this file.
1 /* pdu.c -- CoAP message structure
2  *
3  * Copyright (C) 2010,2011 Olaf Bergmann <bergmann@tzi.org>
4  *
5  * This file is part of the CoAP library libcoap. Please see
6  * README for terms of use.
7  */
8 
9 #include "config.h"
10 
11 #if defined(HAVE_ASSERT_H) && !defined(assert)
12 # include <assert.h>
13 #endif
14 
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string.h>
18 #ifdef HAVE_ARPA_INET_H
19 #include <arpa/inet.h>
20 #endif
21 
22 #include "debug.h"
23 #include "pdu.h"
24 #include "option.h"
25 #include "encode.h"
26 
27 #ifdef WITH_CONTIKI
28 #include "memb.h"
29 
30 typedef unsigned char _pdu[sizeof(coap_pdu_t) + COAP_MAX_PDU_SIZE];
31 
32 MEMB(pdu_storage, _pdu, COAP_PDU_MAXCNT);
33 
34 void
35 coap_pdu_resources_init() {
36  memb_init(&pdu_storage);
37 }
38 #else /* WITH_CONTIKI */
39 #include "mem.h"
40 #endif /* WITH_CONTIKI */
41 
42 void
43 coap_pdu_clear(coap_pdu_t *pdu, size_t size) {
44  assert(pdu);
45 
46  memset(pdu, 0, sizeof(coap_pdu_t) + size);
47  pdu->max_size = size;
48  pdu->hdr = (coap_hdr_t *)((unsigned char *)pdu + sizeof(coap_pdu_t));
50 
51  /* data is NULL unless explicitly set by coap_add_data() */
52  pdu->length = sizeof(coap_hdr_t);
53 }
54 
55 coap_pdu_t *
56 coap_pdu_init(unsigned char type, unsigned char code,
57  unsigned short id, size_t size) {
58  coap_pdu_t *pdu;
59 
60  assert(size <= COAP_MAX_PDU_SIZE);
61  /* Size must be large enough to fit the header. */
62  if (size < sizeof(coap_hdr_t) || size > COAP_MAX_PDU_SIZE)
63  return NULL;
64 
65  /* size must be large enough for hdr */
66 #ifndef WITH_CONTIKI
67  pdu = coap_malloc(sizeof(coap_pdu_t) + size);
68 #else /* WITH_CONTIKI */
69  pdu = (coap_pdu_t *)memb_alloc(&pdu_storage);
70 #endif /* WITH_CONTIKI */
71  if (pdu) {
72  coap_pdu_clear(pdu, size);
73  pdu->hdr->id = id;
74  pdu->hdr->type = type;
75  pdu->hdr->code = code;
76  }
77  return pdu;
78 }
79 
80 coap_pdu_t *
82  coap_pdu_t *pdu;
83 
84 #ifndef WITH_CONTIKI
86 #else /* WITH_CONTIKI */
87  pdu = coap_pdu_init(0, 0, uip_ntohs(COAP_INVALID_TID), COAP_MAX_PDU_SIZE);
88 #endif /* WITH_CONTIKI */
89 
90 #ifndef NDEBUG
91  if (!pdu)
92  coap_log(LOG_CRIT, "coap_new_pdu: cannot allocate memory for new PDU\n");
93 #endif
94  return pdu;
95 }
96 
97 void
99 #ifndef WITH_CONTIKI
100  coap_free( pdu );
101 #else /* WITH_CONTIKI */
102  memb_free(&pdu_storage, pdu);
103 #endif /* WITH_CONTIKI */
104 }
105 
106 int
107 coap_add_token(coap_pdu_t *pdu, size_t len, const unsigned char *data) {
108  const size_t HEADERLENGTH = len + 4;
109  /* must allow for pdu == NULL as callers may rely on this */
110  if (!pdu || len > 8 || pdu->max_size < HEADERLENGTH)
111  return 0;
112 
113  pdu->hdr->token_length = len;
114  if (len)
115  memcpy(pdu->hdr->token, data, len);
116  pdu->max_delta = 0;
117  pdu->length = HEADERLENGTH;
118  pdu->data = NULL;
119 
120  return 1;
121 }
122 
123 size_t
124 coap_add_option(coap_pdu_t *pdu, unsigned short type, unsigned int len, const unsigned char *data) {
125  size_t optsize;
126  coap_opt_t *opt;
127 
128  assert(pdu);
129  pdu->data = NULL;
130 
131  if (type < pdu->max_delta) {
132  warn("coap_add_option: options are not in correct order\n");
133  return 0;
134  }
135 
136  opt = (unsigned char *)pdu->hdr + pdu->length;
137 
138  /* encode option and check length */
139  optsize = coap_opt_encode(opt, pdu->max_size - pdu->length,
140  type - pdu->max_delta, data, len);
141 
142  if (!optsize) {
143  warn("coap_add_option: cannot add option\n");
144  /* error */
145  return 0;
146  } else {
147  pdu->max_delta = type;
148  pdu->length += optsize;
149  }
150 
151  return optsize;
152 }
153 
154 int
155 coap_add_data(coap_pdu_t *pdu, unsigned int len, const unsigned char *data) {
156  assert(pdu);
157  assert(pdu->data == NULL);
158 
159  if (len == 0)
160  return 1;
161 
162  if (pdu->length + len + 1 > pdu->max_size) {
163  warn("coap_add_data: cannot add: data too large for PDU\n");
164  assert(pdu->data == NULL);
165  return 0;
166  }
167 
168  pdu->data = (unsigned char *)pdu->hdr + pdu->length;
169  *pdu->data = COAP_PAYLOAD_START;
170  pdu->data++;
171 
172  memcpy(pdu->data, data, len);
173  pdu->length += len + 1;
174  return 1;
175 }
176 
177 int
178 coap_get_data(coap_pdu_t *pdu, size_t *len, unsigned char **data) {
179  assert(pdu);
180  assert(len);
181  assert(data);
182 
183  if (pdu->data) {
184  *len = (unsigned char *)pdu->hdr + pdu->length - pdu->data;
185  *data = pdu->data;
186  } else { /* no data, clear everything */
187  *len = 0;
188  *data = NULL;
189  }
190 
191  return *data != NULL;
192 }
193 
194 #ifndef SHORT_ERROR_RESPONSE
195 typedef struct {
196  unsigned char code;
197  char *phrase;
198 } error_desc_t;
199 
200 /* if you change anything here, make sure, that the longest string does not
201  * exceed COAP_ERROR_PHRASE_LENGTH. */
203  { COAP_RESPONSE_CODE(65), "2.01 Created" },
204  { COAP_RESPONSE_CODE(66), "2.02 Deleted" },
205  { COAP_RESPONSE_CODE(67), "2.03 Valid" },
206  { COAP_RESPONSE_CODE(68), "2.04 Changed" },
207  { COAP_RESPONSE_CODE(69), "2.05 Content" },
208  { COAP_RESPONSE_CODE(400), "Bad Request" },
209  { COAP_RESPONSE_CODE(401), "Unauthorized" },
210  { COAP_RESPONSE_CODE(402), "Bad Option" },
211  { COAP_RESPONSE_CODE(403), "Forbidden" },
212  { COAP_RESPONSE_CODE(404), "Not Found" },
213  { COAP_RESPONSE_CODE(405), "Method Not Allowed" },
214  { COAP_RESPONSE_CODE(408), "Request Entity Incomplete" },
215  { COAP_RESPONSE_CODE(413), "Request Entity Too Large" },
216  { COAP_RESPONSE_CODE(415), "Unsupported Media Type" },
217  { COAP_RESPONSE_CODE(500), "Internal Server Error" },
218  { COAP_RESPONSE_CODE(501), "Not Implemented" },
219  { COAP_RESPONSE_CODE(502), "Bad Gateway" },
220  { COAP_RESPONSE_CODE(503), "Service Unavailable" },
221  { COAP_RESPONSE_CODE(504), "Gateway Timeout" },
222  { COAP_RESPONSE_CODE(505), "Proxying Not Supported" },
223  { 0, NULL } /* end marker */
224 };
225 
226 char *
227 coap_response_phrase(unsigned char code) {
228  int i;
229  for (i = 0; coap_error[i].code; ++i) {
230  if (coap_error[i].code == code)
231  return coap_error[i].phrase;
232  }
233  return NULL;
234 }
235 #endif
236 
242 static size_t
243 next_option_safe(coap_opt_t **optp, size_t *length) {
244  coap_option_t option;
245  size_t optsize;
246 
247  assert(optp); assert(*optp);
248  assert(length);
249 
250  optsize = coap_opt_parse(*optp, *length, &option);
251  if (optsize) {
252  assert(optsize <= *length);
253 
254  *optp += optsize;
255  *length -= optsize;
256  }
257 
258  return optsize;
259 }
260 
261 int
262 coap_pdu_parse(unsigned char *data, size_t length, coap_pdu_t *pdu) {
263  coap_opt_t *opt;
264 
265  assert(data);
266  assert(pdu);
267 
268  if (pdu->max_size < length) {
269  debug("insufficient space to store parsed PDU\n");
270  return 0;
271  }
272 
273  if (length < sizeof(coap_hdr_t)) {
274  debug("discarded invalid PDU\n");
275  }
276 
277  pdu->hdr->version = data[0] >> 6;
278  pdu->hdr->type = (data[0] >> 4) & 0x03;
279  pdu->hdr->token_length = data[0] & 0x0f;
280  pdu->hdr->code = data[1];
281  pdu->data = NULL;
282 
283  /* sanity checks */
284  if (pdu->hdr->code == 0) {
285  if (length != sizeof(coap_hdr_t) || pdu->hdr->token_length) {
286  debug("coap_pdu_parse: empty message is not empty\n");
287  goto discard;
288  }
289  }
290 
291  if (length < sizeof(coap_hdr_t) + pdu->hdr->token_length
292  || pdu->hdr->token_length > 8) {
293  debug("coap_pdu_parse: invalid Token\n");
294  goto discard;
295  }
296 
297  /* Copy message id in network byte order, so we can easily write the
298  * response back to the network. */
299  memcpy(&pdu->hdr->id, data + 2, 2);
300 
301  /* append data (including the Token) to pdu structure */
302  memcpy(pdu->hdr + 1, data + sizeof(coap_hdr_t), length - sizeof(coap_hdr_t));
303  pdu->length = length;
304 
305  /* Finally calculate beginning of data block and thereby check integrity
306  * of the PDU structure. */
307 
308  /* skip header + token */
309  length -= (pdu->hdr->token_length + sizeof(coap_hdr_t));
310  opt = (unsigned char *)(pdu->hdr + 1) + pdu->hdr->token_length;
311 
312  while (length && *opt != COAP_PAYLOAD_START) {
313 
314  if (!next_option_safe(&opt, (size_t *)&length)) {
315  debug("coap_pdu_parse: drop\n");
316  goto discard;
317  }
318  }
319 
320  /* end of packet or start marker */
321  if (length) {
322  assert(*opt == COAP_PAYLOAD_START);
323  opt++; length--;
324 
325  if (!length) {
326  debug("coap_pdu_parse: message ending in payload start marker\n");
327  goto discard;
328  }
329 
330  debug("set data to %p (pdu ends at %p)\n", (unsigned char *)opt,
331  (unsigned char *)pdu->hdr + pdu->length);
332  pdu->data = (unsigned char *)opt;
333  }
334 
335  return 1;
336 
337  discard:
338  return 0;
339 }