1 /* pdu.c -- CoAP message structure
2  *
3  * Copyright (C) 2010--2014 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 "coap_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 #include "mem.h"
27 
28 void
coap_pdu_clear(coap_pdu_t * pdu,size_t size)29 coap_pdu_clear(coap_pdu_t *pdu, size_t size) {
30   assert(pdu);
31 
32 #ifdef WITH_LWIP
33   /* the pdu itself is not wiped as opposed to the other implementations,
34    * because we have to rely on the pbuf to be set there. */
35   pdu->hdr = pdu->pbuf->payload;
36 #else
37   pdu->max_delta = 0;
38   pdu->data = NULL;
39 #endif
40   memset(pdu->hdr, 0, size);
41   pdu->max_size = size;
42   pdu->hdr->version = COAP_DEFAULT_VERSION;
43 
44   /* data is NULL unless explicitly set by coap_add_data() */
45   pdu->length = sizeof(coap_hdr_t);
46 }
47 
48 #ifdef WITH_LWIP
49 coap_pdu_t *
coap_pdu_from_pbuf(struct pbuf * pbuf)50 coap_pdu_from_pbuf(struct pbuf *pbuf)
51 {
52   if (pbuf == NULL) return NULL;
53 
54   LWIP_ASSERT("Can only deal with contiguous PBUFs", pbuf->tot_len == pbuf->len);
55   LWIP_ASSERT("coap_read needs to receive an exclusive copy of the incoming pbuf", pbuf->ref == 1);
56 
57   coap_pdu_t *result = coap_malloc_type(COAP_PDU, sizeof(coap_pdu_t));
58   if (!result) {
59 	  pbuf_free(pbuf);
60 	  return NULL;
61   }
62 
63   memset(result, 0, sizeof(coap_pdu_t));
64 
65   result->max_size = pbuf->tot_len;
66   result->length = pbuf->tot_len;
67   result->hdr = pbuf->payload;
68   result->pbuf = pbuf;
69 
70   return result;
71 }
72 #endif
73 
74 coap_pdu_t *
coap_pdu_init(unsigned char type,unsigned char code,unsigned short id,size_t size)75 coap_pdu_init(unsigned char type, unsigned char code,
76 	      unsigned short id, size_t size) {
77   coap_pdu_t *pdu;
78 #ifdef WITH_LWIP
79     struct pbuf *p;
80 #endif
81 
82   assert(size <= COAP_MAX_PDU_SIZE);
83   /* Size must be large enough to fit the header. */
84   if (size < sizeof(coap_hdr_t) || size > COAP_MAX_PDU_SIZE)
85     return NULL;
86 
87   /* size must be large enough for hdr */
88 #if defined(WITH_POSIX) || defined(WITH_CONTIKI)
89   pdu = coap_malloc_type(COAP_PDU, sizeof(coap_pdu_t));
90   if (!pdu) return NULL;
91   pdu->hdr = coap_malloc_type(COAP_PDU_BUF, size);
92   if (pdu->hdr == NULL) {
93     coap_free_type(COAP_PDU, pdu);
94     pdu = NULL;
95   }
96 #endif /* WITH_POSIX or WITH_CONTIKI */
97 #ifdef WITH_LWIP
98   pdu = (coap_pdu_t*)coap_malloc_type(COAP_PDU, sizeof(coap_pdu_t));
99   if (!pdu) return NULL;
100   p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM);
101   if (p == NULL) {
102     coap_free_type(COAP_PDU, pdu);
103     pdu = NULL;
104   }
105 #endif
106   if (pdu) {
107 #ifdef WITH_LWIP
108     pdu->pbuf = p;
109 #endif
110     coap_pdu_clear(pdu, size);
111     pdu->hdr->id = id;
112     pdu->hdr->type = type;
113     pdu->hdr->code = code;
114   }
115   return pdu;
116 }
117 
118 coap_pdu_t *
coap_new_pdu(void)119 coap_new_pdu(void) {
120   coap_pdu_t *pdu;
121 
122 #ifndef WITH_CONTIKI
123   pdu = coap_pdu_init(0, 0, ntohs(COAP_INVALID_TID), COAP_MAX_PDU_SIZE);
124 #else /* WITH_CONTIKI */
125   pdu = coap_pdu_init(0, 0, uip_ntohs(COAP_INVALID_TID), COAP_MAX_PDU_SIZE);
126 #endif /* WITH_CONTIKI */
127 
128 #ifndef NDEBUG
129   if (!pdu)
130     coap_log(LOG_CRIT, "coap_new_pdu: cannot allocate memory for new PDU\n");
131 #endif
132   return pdu;
133 }
134 
135 void
coap_delete_pdu(coap_pdu_t * pdu)136 coap_delete_pdu(coap_pdu_t *pdu) {
137 #if defined(WITH_POSIX) || defined(WITH_CONTIKI)
138   if (pdu != NULL) {
139     if (pdu->hdr != NULL) {
140       coap_free_type(COAP_PDU_BUF, pdu->hdr);
141     }
142     coap_free_type(COAP_PDU, pdu);
143   }
144 #endif
145 #ifdef WITH_LWIP
146   if (pdu != NULL) /* accepting double free as the other implementation accept that too */
147     pbuf_free(pdu->pbuf);
148   coap_free_type(COAP_PDU, pdu);
149 #endif
150 }
151 
152 int
coap_add_token(coap_pdu_t * pdu,size_t len,const unsigned char * data)153 coap_add_token(coap_pdu_t *pdu, size_t len, const unsigned char *data) {
154   const size_t HEADERLENGTH = len + 4;
155   /* must allow for pdu == NULL as callers may rely on this */
156   if (!pdu || len > 8 || pdu->max_size < HEADERLENGTH)
157     return 0;
158 
159   pdu->hdr->token_length = len;
160   if (len)
161     memcpy(pdu->hdr->token, data, len);
162   pdu->max_delta = 0;
163   pdu->length = HEADERLENGTH;
164   pdu->data = NULL;
165 
166   return 1;
167 }
168 
169 /** @FIXME de-duplicate code with coap_add_option_later */
170 size_t
coap_add_option(coap_pdu_t * pdu,unsigned short type,unsigned int len,const unsigned char * data)171 coap_add_option(coap_pdu_t *pdu, unsigned short type, unsigned int len, const unsigned char *data) {
172   size_t optsize;
173   coap_opt_t *opt;
174 
175   assert(pdu);
176   pdu->data = NULL;
177 
178   if (type < pdu->max_delta) {
179     warn("coap_add_option: options are not in correct order\n");
180     return 0;
181   }
182 
183   opt = (unsigned char *)pdu->hdr + pdu->length;
184 
185   /* encode option and check length */
186   optsize = coap_opt_encode(opt, pdu->max_size - pdu->length,
187 			    type - pdu->max_delta, data, len);
188 
189   if (!optsize) {
190     warn("coap_add_option: cannot add option\n");
191     /* error */
192     return 0;
193   } else {
194     pdu->max_delta = type;
195     pdu->length += optsize;
196   }
197 
198   return optsize;
199 }
200 
201 /** @FIXME de-duplicate code with coap_add_option */
202 unsigned char*
coap_add_option_later(coap_pdu_t * pdu,unsigned short type,unsigned int len)203 coap_add_option_later(coap_pdu_t *pdu, unsigned short type, unsigned int len) {
204   size_t optsize;
205   coap_opt_t *opt;
206 
207   assert(pdu);
208   pdu->data = NULL;
209 
210   if (type < pdu->max_delta) {
211     warn("coap_add_option: options are not in correct order\n");
212     return NULL;
213   }
214 
215   opt = (unsigned char *)pdu->hdr + pdu->length;
216 
217   /* encode option and check length */
218   optsize = coap_opt_encode(opt, pdu->max_size - pdu->length,
219 			    type - pdu->max_delta, NULL, len);
220 
221   if (!optsize) {
222     warn("coap_add_option: cannot add option\n");
223     /* error */
224     return NULL;
225   } else {
226     pdu->max_delta = type;
227     pdu->length += optsize;
228   }
229 
230   return ((unsigned char*)opt) + optsize - len;
231 }
232 
233 int
coap_add_data(coap_pdu_t * pdu,unsigned int len,const unsigned char * data)234 coap_add_data(coap_pdu_t *pdu, unsigned int len, const unsigned char *data) {
235   assert(pdu);
236   assert(pdu->data == NULL);
237 
238   if (len == 0)
239     return 1;
240 
241   if (pdu->length + len + 1 > pdu->max_size) {
242     warn("coap_add_data: cannot add: data too large for PDU\n");
243     assert(pdu->data == NULL);
244     return 0;
245   }
246 
247   pdu->data = (unsigned char *)pdu->hdr + pdu->length;
248   *pdu->data = COAP_PAYLOAD_START;
249   pdu->data++;
250 
251   memcpy(pdu->data, data, len);
252   pdu->length += len + 1;
253   return 1;
254 }
255 
256 int
coap_get_data(coap_pdu_t * pdu,size_t * len,unsigned char ** data)257 coap_get_data(coap_pdu_t *pdu, size_t *len, unsigned char **data) {
258   assert(pdu);
259   assert(len);
260   assert(data);
261 
262   if (pdu->data) {
263     *len = (unsigned char *)pdu->hdr + pdu->length - pdu->data;
264     *data = pdu->data;
265   } else {			/* no data, clear everything */
266     *len = 0;
267     *data = NULL;
268   }
269 
270   return *data != NULL;
271 }
272 
273 #ifndef SHORT_ERROR_RESPONSE
274 typedef struct {
275   unsigned char code;
276   char *phrase;
277 } error_desc_t;
278 
279 /* if you change anything here, make sure, that the longest string does not
280  * exceed COAP_ERROR_PHRASE_LENGTH. */
281 error_desc_t coap_error[] = {
282   { COAP_RESPONSE_CODE(65),  "2.01 Created" },
283   { COAP_RESPONSE_CODE(66),  "2.02 Deleted" },
284   { COAP_RESPONSE_CODE(67),  "2.03 Valid" },
285   { COAP_RESPONSE_CODE(68),  "2.04 Changed" },
286   { COAP_RESPONSE_CODE(69),  "2.05 Content" },
287   { COAP_RESPONSE_CODE(400), "Bad Request" },
288   { COAP_RESPONSE_CODE(401), "Unauthorized" },
289   { COAP_RESPONSE_CODE(402), "Bad Option" },
290   { COAP_RESPONSE_CODE(403), "Forbidden" },
291   { COAP_RESPONSE_CODE(404), "Not Found" },
292   { COAP_RESPONSE_CODE(405), "Method Not Allowed" },
293   { COAP_RESPONSE_CODE(408), "Request Entity Incomplete" },
294   { COAP_RESPONSE_CODE(413), "Request Entity Too Large" },
295   { COAP_RESPONSE_CODE(415), "Unsupported Media Type" },
296   { COAP_RESPONSE_CODE(500), "Internal Server Error" },
297   { COAP_RESPONSE_CODE(501), "Not Implemented" },
298   { COAP_RESPONSE_CODE(502), "Bad Gateway" },
299   { COAP_RESPONSE_CODE(503), "Service Unavailable" },
300   { COAP_RESPONSE_CODE(504), "Gateway Timeout" },
301   { COAP_RESPONSE_CODE(505), "Proxying Not Supported" },
302   { 0, NULL }			/* end marker */
303 };
304 
305 char *
coap_response_phrase(unsigned char code)306 coap_response_phrase(unsigned char code) {
307   int i;
308   for (i = 0; coap_error[i].code; ++i) {
309     if (coap_error[i].code == code)
310       return coap_error[i].phrase;
311   }
312   return NULL;
313 }
314 #endif
315 
316 /**
317  * Advances *optp to next option if still in PDU. This function
318  * returns the number of bytes opt has been advanced or @c 0
319  * on error.
320  */
321 static size_t
next_option_safe(coap_opt_t ** optp,size_t * length)322 next_option_safe(coap_opt_t **optp, size_t *length) {
323   coap_option_t option;
324   size_t optsize;
325 
326   assert(optp); assert(*optp);
327   assert(length);
328 
329   optsize = coap_opt_parse(*optp, *length, &option);
330   if (optsize) {
331     assert(optsize <= *length);
332 
333     *optp += optsize;
334     *length -= optsize;
335   }
336 
337   return optsize;
338 }
339 
340 int
coap_pdu_parse(unsigned char * data,size_t length,coap_pdu_t * pdu)341 coap_pdu_parse(unsigned char *data, size_t length, coap_pdu_t *pdu) {
342   coap_opt_t *opt;
343 
344   assert(data);
345   assert(pdu);
346 
347   if (pdu->max_size < length) {
348     debug("insufficient space to store parsed PDU\n");
349     return 0;
350   }
351 
352   if (length < sizeof(coap_hdr_t)) {
353     debug("discarded invalid PDU\n");
354   }
355 
356 #ifdef WITH_LWIP
357   LWIP_ASSERT("coap_pdu_parse with unexpected addresses", data == pdu->hdr);
358   LWIP_ASSERT("coap_pdu_parse with unexpected length", length == pdu->length);
359 #else
360 
361   pdu->hdr->version = data[0] >> 6;
362   pdu->hdr->type = (data[0] >> 4) & 0x03;
363   pdu->hdr->token_length = data[0] & 0x0f;
364   pdu->hdr->code = data[1];
365 #endif
366   pdu->data = NULL;
367 
368   /* sanity checks */
369   if (pdu->hdr->code == 0) {
370     if (length != sizeof(coap_hdr_t) || pdu->hdr->token_length) {
371       debug("coap_pdu_parse: empty message is not empty\n");
372       goto discard;
373     }
374   }
375 
376   if (length < sizeof(coap_hdr_t) + pdu->hdr->token_length
377       || pdu->hdr->token_length > 8) {
378     debug("coap_pdu_parse: invalid Token\n");
379     goto discard;
380   }
381 
382 #ifndef WITH_LWIP
383   /* Copy message id in network byte order, so we can easily write the
384    * response back to the network. */
385   memcpy(&pdu->hdr->id, data + 2, 2);
386 
387   /* append data (including the Token) to pdu structure */
388   memcpy(pdu->hdr + 1, data + sizeof(coap_hdr_t), length - sizeof(coap_hdr_t));
389   pdu->length = length;
390 
391   /* Finally calculate beginning of data block and thereby check integrity
392    * of the PDU structure. */
393 #endif
394 
395   /* skip header + token */
396   length -= (pdu->hdr->token_length + sizeof(coap_hdr_t));
397   opt = (unsigned char *)(pdu->hdr + 1) + pdu->hdr->token_length;
398 
399   while (length && *opt != COAP_PAYLOAD_START) {
400     if (!next_option_safe(&opt, (size_t *)&length)) {
401       debug("coap_pdu_parse: drop\n");
402       goto discard;
403     }
404   }
405 
406   /* end of packet or start marker */
407   if (length) {
408     assert(*opt == COAP_PAYLOAD_START);
409     opt++; length--;
410 
411     if (!length) {
412       debug("coap_pdu_parse: message ending in payload start marker\n");
413       goto discard;
414     }
415 
416     debug("set data to %p (pdu ends at %p)\n", (unsigned char *)opt,
417 	  (unsigned char *)pdu->hdr + pdu->length);
418     pdu->data = (unsigned char *)opt;
419   }
420 
421   return 1;
422 
423  discard:
424   return 0;
425 }
426