Contiki-NG
coap-engine.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the Institute nor the names of its contributors
14  * may be used to endorse or promote products derived from this software
15  * without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * This file is part of the Contiki operating system.
30  */
31 
32 /**
33  * \file
34  * CoAP implementation Engine.
35  * \author
36  * Matthias Kovatsch <kovatsch@inf.ethz.ch>
37  */
38 
39 /**
40  * \addtogroup coap
41  * @{
42  */
43 
44 #include "coap-engine.h"
45 #include "sys/cc.h"
46 #include "lib/list.h"
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <inttypes.h>
50 #include <string.h>
51 
52 /* Log configuration */
53 #include "coap-log.h"
54 #define LOG_MODULE "coap-eng"
55 #define LOG_LEVEL LOG_LEVEL_COAP
56 
57 static void process_callback(coap_timer_t *t);
58 
59 /*
60  * To be called by HTTP/COAP server as a callback function when a new service
61  * request appears. This function dispatches the corresponding CoAP service.
62  */
63 static int invoke_coap_resource_service(coap_message_t *request,
64  coap_message_t *response,
65  uint8_t *buffer, uint16_t buffer_size,
66  int32_t *offset);
67 
68 /*---------------------------------------------------------------------------*/
69 /*- Variables ---------------------------------------------------------------*/
70 /*---------------------------------------------------------------------------*/
71 LIST(coap_handlers);
72 LIST(coap_resource_services);
73 static uint8_t is_initialized = 0;
74 
75 /*---------------------------------------------------------------------------*/
76 /*- CoAP service handlers---------------------------------------------------*/
77 /*---------------------------------------------------------------------------*/
78 void
79 coap_add_handler(coap_handler_t *handler)
80 {
81  list_add(coap_handlers, handler);
82 }
83 /*---------------------------------------------------------------------------*/
84 void
85 coap_remove_handler(coap_handler_t *handler)
86 {
87  list_remove(coap_handlers, handler);
88 }
89 /*---------------------------------------------------------------------------*/
90 coap_handler_status_t
91 coap_call_handlers(coap_message_t *request, coap_message_t *response,
92  uint8_t *buffer, uint16_t buffer_size, int32_t *offset)
93 {
94  coap_handler_status_t status;
95  coap_handler_t *r;
96  for(r = list_head(coap_handlers); r != NULL; r = r->next) {
97  if(r->handler) {
98  status = r->handler(request, response, buffer, buffer_size, offset);
99  if(status != COAP_HANDLER_STATUS_CONTINUE) {
100  /* Request handled. */
101 
102  /* Check response code before doing observe! */
103  if(request->code == COAP_GET) {
104  coap_observe_handler(NULL, request, response);
105  }
106 
107  return status;
108  }
109  }
110  }
111  return COAP_HANDLER_STATUS_CONTINUE;
112 }
113 /*---------------------------------------------------------------------------*/
114 static CC_INLINE coap_handler_status_t
115 call_service(coap_message_t *request, coap_message_t *response,
116  uint8_t *buffer, uint16_t buffer_size, int32_t *offset)
117 {
118  coap_handler_status_t status;
119  status = coap_call_handlers(request, response, buffer, buffer_size, offset);
120  if(status != COAP_HANDLER_STATUS_CONTINUE) {
121  return status;
122  }
123  status = invoke_coap_resource_service(request, response, buffer, buffer_size, offset);
124  if(status != COAP_HANDLER_STATUS_CONTINUE) {
125  return status;
126  }
127 
128  coap_set_status_code(response, NOT_FOUND_4_04);
129 
130  return COAP_HANDLER_STATUS_CONTINUE;
131 }
132 
133 /*---------------------------------------------------------------------------*/
134 /*- Server Part -------------------------------------------------------------*/
135 /*---------------------------------------------------------------------------*/
136 
137 /* the discover resource is automatically included for CoAP */
138 extern coap_resource_t res_well_known_core;
139 
140 /*---------------------------------------------------------------------------*/
141 /*- Internal API ------------------------------------------------------------*/
142 /*---------------------------------------------------------------------------*/
143 int
144 coap_receive(const coap_endpoint_t *src,
145  uint8_t *payload, uint16_t payload_length)
146 {
147  /* static declaration reduces stack peaks and program code size */
148  static coap_message_t message[1]; /* this way the message can be treated as pointer as usual */
149  static coap_message_t response[1];
150  coap_transaction_t *transaction = NULL;
151  coap_handler_status_t status;
152 
153  coap_status_code = coap_parse_message(message, payload, payload_length);
154  coap_set_src_endpoint(message, src);
155 
156  if(coap_status_code == NO_ERROR) {
157 
158  /*TODO duplicates suppression, if required by application */
159 
160  LOG_DBG(" Parsed: v %u, t %u, tkl %u, c %u, mid %u\n", message->version,
161  message->type, message->token_len, message->code, message->mid);
162  LOG_DBG(" URL:");
163  LOG_DBG_COAP_STRING(message->uri_path, message->uri_path_len);
164  LOG_DBG_("\n");
165  LOG_DBG(" Payload: ");
166  LOG_DBG_COAP_STRING((const char *)message->payload, message->payload_len);
167  LOG_DBG_("\n");
168 
169  /* handle requests */
170  if(message->code >= COAP_GET && message->code <= COAP_DELETE) {
171 
172  /* use transaction buffer for response to confirmable request */
173  if((transaction = coap_new_transaction(message->mid, src))) {
174  uint32_t block_num = 0;
175  uint16_t block_size = COAP_MAX_BLOCK_SIZE;
176  uint32_t block_offset = 0;
177  int32_t new_offset = 0;
178 
179  /* prepare response */
180  if(message->type == COAP_TYPE_CON) {
181  /* reliable CON requests are answered with an ACK */
182  coap_init_message(response, COAP_TYPE_ACK, CONTENT_2_05,
183  message->mid);
184  } else {
185  /* unreliable NON requests are answered with a NON as well */
186  coap_init_message(response, COAP_TYPE_NON, CONTENT_2_05,
187  coap_get_mid());
188  /* mirror token */
189  }
190  if(message->token_len) {
191  coap_set_token(response, message->token, message->token_len);
192  /* get offset for blockwise transfers */
193  }
194  if(coap_get_header_block2
195  (message, &block_num, NULL, &block_size, &block_offset)) {
196  LOG_DBG("Blockwise: block request %"PRIu32" (%u/%u) @ %"PRIu32" bytes\n",
197  block_num, block_size, COAP_MAX_BLOCK_SIZE, block_offset);
198  block_size = MIN(block_size, COAP_MAX_BLOCK_SIZE);
199  new_offset = block_offset;
200  }
201 
202  if(new_offset < 0) {
203  LOG_DBG("Blockwise: block request offset overflow\n");
204  coap_status_code = BAD_OPTION_4_02;
205  coap_error_message = "BlockOutOfScope";
206  status = COAP_HANDLER_STATUS_CONTINUE;
207  } else {
208  /* call CoAP framework and check if found and allowed */
209  status = call_service(message, response,
210  transaction->message + COAP_MAX_HEADER_SIZE,
211  block_size, &new_offset);
212  }
213 
214  if(status != COAP_HANDLER_STATUS_CONTINUE) {
215 
216  if(coap_status_code == NO_ERROR) {
217 
218  /* TODO coap_handle_blockwise(request, response, start_offset, end_offset); */
219 
220  /* resource is unaware of Block1 */
221  if(coap_is_option(message, COAP_OPTION_BLOCK1)
222  && response->code < BAD_REQUEST_4_00
223  && !coap_is_option(response, COAP_OPTION_BLOCK1)) {
224  LOG_DBG("Block1 NOT IMPLEMENTED\n");
225 
226  coap_status_code = NOT_IMPLEMENTED_5_01;
227  coap_error_message = "NoBlock1Support";
228 
229  /* client requested Block2 transfer */
230  } else if(coap_is_option(message, COAP_OPTION_BLOCK2)) {
231 
232  /* unchanged new_offset indicates that resource is unaware of blockwise transfer */
233  if(new_offset == block_offset) {
234  LOG_DBG("Blockwise: unaware resource with payload length %u/%u\n",
235  response->payload_len, block_size);
236  if(block_offset >= response->payload_len) {
237  LOG_DBG("handle_incoming_data(): block_offset >= response->payload_len\n");
238 
239  response->code = BAD_OPTION_4_02;
240  coap_set_payload(response, "BlockOutOfScope", 15); /* a const char str[] and sizeof(str) produces larger code size */
241  } else {
242  coap_set_header_block2(response, block_num,
243  response->payload_len -
244  block_offset > block_size,
245  block_size);
246  coap_set_payload(response,
247  response->payload + block_offset,
248  MIN(response->payload_len -
249  block_offset, block_size));
250  } /* if(valid offset) */
251 
252  /* resource provides chunk-wise data */
253  } else {
254  LOG_DBG("Blockwise: blockwise resource, new offset %"PRId32"\n",
255  new_offset);
256  coap_set_header_block2(response, block_num,
257  new_offset != -1
258  || response->payload_len >
259  block_size, block_size);
260 
261  if(response->payload_len > block_size) {
262  coap_set_payload(response, response->payload,
263  block_size);
264  }
265  } /* if(resource aware of blockwise) */
266 
267  /* Resource requested Block2 transfer */
268  } else if(new_offset != 0) {
269  LOG_DBG("Blockwise: no block option for blockwise resource, using block size %u\n",
270  COAP_MAX_BLOCK_SIZE);
271 
272  coap_set_header_block2(response, 0, new_offset != -1,
273  COAP_MAX_BLOCK_SIZE);
274  coap_set_payload(response, response->payload,
275  MIN(response->payload_len,
276  COAP_MAX_BLOCK_SIZE));
277  } /* blockwise transfer handling */
278  } /* no errors/hooks */
279  /* successful service callback */
280  /* serialize response */
281  }
282  if(coap_status_code == NO_ERROR) {
283  if((transaction->message_len = coap_serialize_message(response,
284  transaction->
285  message)) ==
286  0) {
287  coap_status_code = PACKET_SERIALIZATION_ERROR;
288  }
289  }
290  } else {
291  coap_status_code = SERVICE_UNAVAILABLE_5_03;
292  coap_error_message = "NoFreeTraBuffer";
293  } /* if(transaction buffer) */
294 
295  /* handle responses */
296  } else {
297 
298  if(message->type == COAP_TYPE_CON && message->code == 0) {
299  LOG_INFO("Received Ping\n");
300  coap_status_code = PING_RESPONSE;
301  } else if(message->type == COAP_TYPE_ACK) {
302  /* transactions are closed through lookup below */
303  LOG_DBG("Received ACK\n");
304  } else if(message->type == COAP_TYPE_RST) {
305  LOG_INFO("Received RST\n");
306  /* cancel possible subscriptions */
307  coap_remove_observer_by_mid(src, message->mid);
308  }
309 
310  if((transaction = coap_get_transaction_by_mid(message->mid))) {
311  /* free transaction memory before callback, as it may create a new transaction */
312  coap_resource_response_handler_t callback = transaction->callback;
313  void *callback_data = transaction->callback_data;
314 
315  coap_clear_transaction(transaction);
316 
317  /* check if someone registered for the response */
318  if(callback) {
319  callback(callback_data, message);
320  }
321  }
322  /* if(ACKed transaction) */
323  transaction = NULL;
324 
325 #if COAP_OBSERVE_CLIENT
326  /* if observe notification */
327  if((message->type == COAP_TYPE_CON || message->type == COAP_TYPE_NON)
328  && coap_is_option(message, COAP_OPTION_OBSERVE)) {
329  LOG_DBG("Observe [%"PRId32"]\n", message->observe);
330  coap_handle_notification(src, message);
331  }
332 #endif /* COAP_OBSERVE_CLIENT */
333  } /* request or response */
334  } /* parsed correctly */
335 
336  /* if(parsed correctly) */
337  if(coap_status_code == NO_ERROR) {
338  if(transaction) {
339  coap_send_transaction(transaction);
340  }
341  } else if(coap_status_code == MANUAL_RESPONSE) {
342  LOG_DBG("Clearing transaction for manual response");
343  coap_clear_transaction(transaction);
344  } else {
345  coap_message_type_t reply_type = COAP_TYPE_ACK;
346 
347  LOG_WARN("ERROR %u: %s\n", coap_status_code, coap_error_message);
348  coap_clear_transaction(transaction);
349 
350  if(coap_status_code == PING_RESPONSE) {
351  coap_status_code = 0;
352  reply_type = COAP_TYPE_RST;
353  } else if(coap_status_code >= 192) {
354  /* set to sendable error code */
355  coap_status_code = INTERNAL_SERVER_ERROR_5_00;
356  /* reuse input buffer for error message */
357  }
358  coap_init_message(message, reply_type, coap_status_code,
359  message->mid);
360  coap_set_payload(message, coap_error_message,
361  strlen(coap_error_message));
362  coap_sendto(src, payload, coap_serialize_message(message, payload));
363  }
364 
365  /* if(new data) */
366  return coap_status_code;
367 }
368 /*---------------------------------------------------------------------------*/
369 void
370 coap_engine_init(void)
371 {
372  /* avoid initializing twice */
373  if(is_initialized) {
374  return;
375  }
376  is_initialized = 1;
377 
378  LOG_INFO("Starting CoAP engine...\n");
379 
380  list_init(coap_handlers);
381  list_init(coap_resource_services);
382 
383  coap_activate_resource(&res_well_known_core, ".well-known/core");
384 
386  coap_init_connection();
387 }
388 /*---------------------------------------------------------------------------*/
389 /**
390  * \brief Makes a resource available under the given URI path
391  * \param resource A pointer to a resource implementation
392  * \param path The URI path string for this resource
393  *
394  * The resource implementation must be imported first using the
395  * extern keyword. The build system takes care of compiling every
396  * *.c file in the ./resources/ sub-directory (see example Makefile).
397  */
398 void
399 coap_activate_resource(coap_resource_t *resource, const char *path)
400 {
401  coap_periodic_resource_t *periodic;
402  resource->url = path;
403  list_add(coap_resource_services, resource);
404 
405  LOG_INFO("Activating: %s\n", resource->url);
406 
407  /* Only add periodic resources with a periodic_handler and a period > 0. */
408  if(resource->flags & IS_PERIODIC && resource->periodic
409  && resource->periodic->periodic_handler
410  && resource->periodic->period) {
411  LOG_DBG("Periodic resource: %p (%s)\n", resource->periodic, path);
412  periodic = resource->periodic;
413  coap_timer_set_callback(&periodic->periodic_timer, process_callback);
414  coap_timer_set_user_data(&periodic->periodic_timer, resource);
415  coap_timer_set(&periodic->periodic_timer, periodic->period);
416  }
417 }
418 /*---------------------------------------------------------------------------*/
419 
420 /*---------------------------------------------------------------------------*/
421 /*- Internal API ------------------------------------------------------------*/
422 /*---------------------------------------------------------------------------*/
423 coap_resource_t *
425 {
426  return list_head(coap_resource_services);
427 }
428 /*---------------------------------------------------------------------------*/
429 coap_resource_t *
430 coap_get_next_resource(coap_resource_t *resource)
431 {
432  return list_item_next(resource);
433 }
434 /*---------------------------------------------------------------------------*/
435 static int
436 invoke_coap_resource_service(coap_message_t *request, coap_message_t *response,
437  uint8_t *buffer, uint16_t buffer_size,
438  int32_t *offset)
439 {
440  uint8_t found = 0;
441  uint8_t allowed = 1;
442 
443  coap_resource_t *resource = NULL;
444  const char *url = NULL;
445  int url_len, res_url_len;
446 
447  url_len = coap_get_header_uri_path(request, &url);
448  for(resource = list_head(coap_resource_services);
449  resource; resource = resource->next) {
450 
451  /* if the web service handles that kind of requests and urls matches */
452  res_url_len = strlen(resource->url);
453  if((url_len == res_url_len
454  || (url_len > res_url_len
455  && (resource->flags & HAS_SUB_RESOURCES)
456  && url[res_url_len] == '/'))
457  && strncmp(resource->url, url, res_url_len) == 0) {
458  coap_resource_flags_t method = coap_get_method_type(request);
459  found = 1;
460 
461  LOG_INFO("/%s, method %u, resource->flags %u\n", resource->url,
462  (uint16_t)method, resource->flags);
463 
464  if((method & METHOD_GET) && resource->get_handler != NULL) {
465  /* call handler function */
466  resource->get_handler(request, response, buffer, buffer_size, offset);
467  } else if((method & METHOD_POST) && resource->post_handler != NULL) {
468  /* call handler function */
469  resource->post_handler(request, response, buffer, buffer_size,
470  offset);
471  } else if((method & METHOD_PUT) && resource->put_handler != NULL) {
472  /* call handler function */
473  resource->put_handler(request, response, buffer, buffer_size, offset);
474  } else if((method & METHOD_DELETE) && resource->delete_handler != NULL) {
475  /* call handler function */
476  resource->delete_handler(request, response, buffer, buffer_size,
477  offset);
478  } else {
479  allowed = 0;
480  coap_set_status_code(response, METHOD_NOT_ALLOWED_4_05);
481  }
482  break;
483  }
484  }
485  if(!found) {
486  coap_set_status_code(response, NOT_FOUND_4_04);
487  } else if(allowed) {
488  /* final handler for special flags */
489  if(resource->flags & IS_OBSERVABLE) {
490  coap_observe_handler(resource, request, response);
491  }
492  }
493  return found & allowed;
494 }
495 /*---------------------------------------------------------------------------*/
496 /* This callback occurs when t is expired */
497 static void
498 process_callback(coap_timer_t *t)
499 {
500  coap_resource_t *resource;
501  resource = coap_timer_get_user_data(t);
502  if(resource != NULL && (resource->flags & IS_PERIODIC)
503  && resource->periodic != NULL && resource->periodic->period) {
504  LOG_DBG("Periodic: timer expired for /%s (period: %"PRIu32")\n",
505  resource->url, resource->periodic->period);
506 
507  if(!is_initialized) {
508  /* CoAP has not yet been initialized. */
509  } else if(resource->periodic->periodic_handler) {
510  /* Call the periodic_handler function. */
511  resource->periodic->periodic_handler();
512  }
513 
514  coap_timer_set(t, resource->periodic->period);
515  }
516 }
517 /*---------------------------------------------------------------------------*/
518 /** @} */
Log support for CoAP
static void * coap_timer_get_user_data(coap_timer_t *timer)
Get user data that has been attached to a CoAP timer.
Definition: coap-timer.h:118
void coap_transport_init(void)
Initialize the CoAP transport.
Definition: coap-uip.c:328
CoAP engine implementation.
void coap_activate_resource(coap_resource_t *resource, const char *path)
Makes a resource available under the given URI path.
Definition: coap-engine.c:399
static void coap_timer_set_callback(coap_timer_t *timer, void(*callback)(coap_timer_t *))
Set a callback function to be called when a CoAP timer expires.
Definition: coap-timer.h:105
Linked list manipulation routines.
void * list_head(list_t list)
Get a pointer to the first element of a list.
Definition: list.c:82
int coap_sendto(const coap_endpoint_t *ep, const uint8_t *data, uint16_t len)
Send a message to the specified CoAP endpoint.
Definition: coap-uip.c:369
void list_add(list_t list, void *item)
Add an item at the end of a list.
Definition: list.c:142
void list_init(list_t list)
Initialize a list.
Definition: list.c:65
#define LIST(name)
Declare a linked list.
Definition: list.h:89
static void coap_timer_set_user_data(coap_timer_t *timer, void *data)
Attach user data to a CoAP timer.
Definition: coap-timer.h:130
void coap_timer_set(coap_timer_t *timer, uint64_t time)
Set a CoAP timer to expire after the specified time.
Definition: coap-timer.c:103
Default definitions of C compiler quirk work-arounds.
coap_resource_t * coap_get_first_resource(void)
Returns the first of registered CoAP resources.
Definition: coap-engine.c:424
coap_resource_t * coap_get_next_resource(coap_resource_t *resource)
Returns the next registered CoAP resource.
Definition: coap-engine.c:430
coap_resource_flags_t
Resource flags for allowed methods and special functionalities.
void list_remove(list_t list, void *item)
Remove a specific element from a list.
Definition: list.c:237
void * list_item_next(void *item)
Get the next item following this item.
Definition: list.c:322