Contiki 3.x
er-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 for the REST Engine.
35  * \author
36  * Matthias Kovatsch <kovatsch@inf.ethz.ch>
37  */
38 
39 #include "sys/cc.h"
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include "er-coap-engine.h"
44 
45 #define DEBUG 0
46 #if DEBUG
47 #include <stdio.h>
48 #define PRINTF(...) printf(__VA_ARGS__)
49 #define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
50 #define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]", (lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3], (lladdr)->addr[4], (lladdr)->addr[5])
51 #else
52 #define PRINTF(...)
53 #define PRINT6ADDR(addr)
54 #define PRINTLLADDR(addr)
55 #endif
56 
57 PROCESS(coap_engine, "CoAP Engine");
58 
59 /*---------------------------------------------------------------------------*/
60 /*- Variables ---------------------------------------------------------------*/
61 /*---------------------------------------------------------------------------*/
62 static service_callback_t service_cbk = NULL;
63 
64 /*---------------------------------------------------------------------------*/
65 /*- Internal API ------------------------------------------------------------*/
66 /*---------------------------------------------------------------------------*/
67 static int
68 coap_receive(void)
69 {
70  erbium_status_code = NO_ERROR;
71 
72  PRINTF("handle_incoming_data(): received uip_datalen=%u \n",
73  (uint16_t)uip_datalen());
74 
75  /* static declaration reduces stack peaks and program code size */
76  static coap_packet_t message[1]; /* this way the packet can be treated as pointer as usual */
77  static coap_packet_t response[1];
78  static coap_transaction_t *transaction = NULL;
79 
80  if(uip_newdata()) {
81 
82  PRINTF("receiving UDP datagram from: ");
83  PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
84  PRINTF(":%u\n Length: %u\n", uip_ntohs(UIP_UDP_BUF->srcport),
85  uip_datalen());
86 
87  erbium_status_code =
88  coap_parse_message(message, uip_appdata, uip_datalen());
89 
90  if(erbium_status_code == NO_ERROR) {
91 
92  /*TODO duplicates suppression, if required by application */
93 
94  PRINTF(" Parsed: v %u, t %u, tkl %u, c %u, mid %u\n", message->version,
95  message->type, message->token_len, message->code, message->mid);
96  PRINTF(" URL: %.*s\n", message->uri_path_len, message->uri_path);
97  PRINTF(" Payload: %.*s\n", message->payload_len, message->payload);
98 
99  /* handle requests */
100  if(message->code >= COAP_GET && message->code <= COAP_DELETE) {
101 
102  /* use transaction buffer for response to confirmable request */
103  if((transaction =
104  coap_new_transaction(message->mid, &UIP_IP_BUF->srcipaddr,
105  UIP_UDP_BUF->srcport))) {
106  uint32_t block_num = 0;
107  uint16_t block_size = COAP_MAX_BLOCK_SIZE;
108  uint32_t block_offset = 0;
109  int32_t new_offset = 0;
110 
111  /* prepare response */
112  if(message->type == COAP_TYPE_CON) {
113  /* reliable CON requests are answered with an ACK */
114  coap_init_message(response, COAP_TYPE_ACK, CONTENT_2_05,
115  message->mid);
116  } else {
117  /* unreliable NON requests are answered with a NON as well */
118  coap_init_message(response, COAP_TYPE_NON, CONTENT_2_05,
119  coap_get_mid());
120  /* mirror token */
121  } if(message->token_len) {
122  coap_set_token(response, message->token, message->token_len);
123  /* get offset for blockwise transfers */
124  }
125  if(coap_get_header_block2
126  (message, &block_num, NULL, &block_size, &block_offset)) {
127  PRINTF("Blockwise: block request %lu (%u/%u) @ %lu bytes\n",
128  block_num, block_size, COAP_MAX_BLOCK_SIZE, block_offset);
129  block_size = MIN(block_size, COAP_MAX_BLOCK_SIZE);
130  new_offset = block_offset;
131  }
132 
133  /* invoke resource handler */
134  if(service_cbk) {
135 
136  /* call REST framework and check if found and allowed */
137  if(service_cbk
138  (message, response, transaction->packet + COAP_MAX_HEADER_SIZE,
139  block_size, &new_offset)) {
140 
141  if(erbium_status_code == NO_ERROR) {
142 
143  /* TODO coap_handle_blockwise(request, response, start_offset, end_offset); */
144 
145  /* resource is unaware of Block1 */
146  if(IS_OPTION(message, COAP_OPTION_BLOCK1)
147  && response->code < BAD_REQUEST_4_00
148  && !IS_OPTION(response, COAP_OPTION_BLOCK1)) {
149  PRINTF("Block1 NOT IMPLEMENTED\n");
150 
151  erbium_status_code = NOT_IMPLEMENTED_5_01;
152  coap_error_message = "NoBlock1Support";
153 
154  /* client requested Block2 transfer */
155  } else if(IS_OPTION(message, COAP_OPTION_BLOCK2)) {
156 
157  /* unchanged new_offset indicates that resource is unaware of blockwise transfer */
158  if(new_offset == block_offset) {
159  PRINTF
160  ("Blockwise: unaware resource with payload length %u/%u\n",
161  response->payload_len, block_size);
162  if(block_offset >= response->payload_len) {
163  PRINTF
164  ("handle_incoming_data(): block_offset >= response->payload_len\n");
165 
166  response->code = BAD_OPTION_4_02;
167  coap_set_payload(response, "BlockOutOfScope", 15); /* a const char str[] and sizeof(str) produces larger code size */
168  } else {
169  coap_set_header_block2(response, block_num,
170  response->payload_len -
171  block_offset > block_size,
172  block_size);
173  coap_set_payload(response,
174  response->payload + block_offset,
175  MIN(response->payload_len -
176  block_offset, block_size));
177  } /* if(valid offset) */
178 
179  /* resource provides chunk-wise data */
180  } else {
181  PRINTF("Blockwise: blockwise resource, new offset %ld\n",
182  new_offset);
183  coap_set_header_block2(response, block_num,
184  new_offset != -1
185  || response->payload_len >
186  block_size, block_size);
187 
188  if(response->payload_len > block_size) {
189  coap_set_payload(response, response->payload,
190  block_size);
191  }
192  } /* if(resource aware of blockwise) */
193 
194  /* Resource requested Block2 transfer */
195  } else if(new_offset != 0) {
196  PRINTF
197  ("Blockwise: no block option for blockwise resource, using block size %u\n",
198  COAP_MAX_BLOCK_SIZE);
199 
200  coap_set_header_block2(response, 0, new_offset != -1,
201  COAP_MAX_BLOCK_SIZE);
202  coap_set_payload(response, response->payload,
203  MIN(response->payload_len,
204  COAP_MAX_BLOCK_SIZE));
205  } /* blockwise transfer handling */
206  } /* no errors/hooks */
207  /* successful service callback */
208  /* serialize response */
209  }
210  if(erbium_status_code == NO_ERROR) {
211  if((transaction->packet_len = coap_serialize_message(response,
212  transaction->
213  packet)) ==
214  0) {
215  erbium_status_code = PACKET_SERIALIZATION_ERROR;
216  }
217  }
218  } else {
219  erbium_status_code = NOT_IMPLEMENTED_5_01;
220  coap_error_message = "NoServiceCallbck"; /* no 'a' to fit into 16 bytes */
221  } /* if(service callback) */
222  } else {
223  erbium_status_code = SERVICE_UNAVAILABLE_5_03;
224  coap_error_message = "NoFreeTraBuffer";
225  } /* if(transaction buffer) */
226 
227  /* handle responses */
228  } else {
229 
230  if(message->type == COAP_TYPE_CON && message->code == 0) {
231  PRINTF("Received Ping\n");
232  erbium_status_code = PING_RESPONSE;
233  } else if(message->type == COAP_TYPE_ACK) {
234  /* transactions are closed through lookup below */
235  PRINTF("Received ACK\n");
236  } else if(message->type == COAP_TYPE_RST) {
237  PRINTF("Received RST\n");
238  /* cancel possible subscriptions */
239  coap_remove_observer_by_mid(&UIP_IP_BUF->srcipaddr,
240  UIP_UDP_BUF->srcport, message->mid);
241  }
242 
243  if((transaction = coap_get_transaction_by_mid(message->mid))) {
244  /* free transaction memory before callback, as it may create a new transaction */
245  restful_response_handler callback = transaction->callback;
246  void *callback_data = transaction->callback_data;
247 
248  coap_clear_transaction(transaction);
249 
250  /* check if someone registered for the response */
251  if(callback) {
252  callback(callback_data, message);
253  }
254  }
255  /* if(ACKed transaction) */
256  transaction = NULL;
257 
258 #if COAP_OBSERVE_CLIENT
259  /* if observe notification */
260  if((message->type == COAP_TYPE_CON || message->type == COAP_TYPE_NON)
261  && IS_OPTION(message, COAP_OPTION_OBSERVE)) {
262  PRINTF("Observe [%u]\n", message->observe);
263  coap_handle_notification(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport,
264  message);
265  }
266 #endif /* COAP_OBSERVE_CLIENT */
267  } /* request or response */
268  } /* parsed correctly */
269 
270  /* if(parsed correctly) */
271  if(erbium_status_code == NO_ERROR) {
272  if(transaction) {
273  coap_send_transaction(transaction);
274  }
275  } else if(erbium_status_code == MANUAL_RESPONSE) {
276  PRINTF("Clearing transaction for manual response");
277  coap_clear_transaction(transaction);
278  } else {
279  coap_message_type_t reply_type = COAP_TYPE_ACK;
280 
281  PRINTF("ERROR %u: %s\n", erbium_status_code, coap_error_message);
282  coap_clear_transaction(transaction);
283 
284  if(erbium_status_code == PING_RESPONSE) {
285  erbium_status_code = 0;
286  reply_type = COAP_TYPE_RST;
287  } else if(erbium_status_code >= 192) {
288  /* set to sendable error code */
289  erbium_status_code = INTERNAL_SERVER_ERROR_5_00;
290  /* reuse input buffer for error message */
291  }
292  coap_init_message(message, reply_type, erbium_status_code,
293  message->mid);
294  coap_set_payload(message, coap_error_message,
295  strlen(coap_error_message));
296  coap_send_message(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport,
297  uip_appdata, coap_serialize_message(message,
298  uip_appdata));
299  }
300  }
301 
302  /* if(new data) */
303  return erbium_status_code;
304 }
305 /*---------------------------------------------------------------------------*/
306 void
307 coap_init_engine(void)
308 {
309  process_start(&coap_engine, NULL);
310 }
311 /*---------------------------------------------------------------------------*/
312 void
313 coap_set_service_callback(service_callback_t callback)
314 {
315  service_cbk = callback;
316 }
317 /*---------------------------------------------------------------------------*/
319 coap_get_rest_method(void *packet)
320 {
321  return (rest_resource_flags_t)(1 <<
322  (((coap_packet_t *)packet)->code - 1));
323 }
324 /*---------------------------------------------------------------------------*/
325 /*- Server Part -------------------------------------------------------------*/
326 /*---------------------------------------------------------------------------*/
327 
328 /* the discover resource is automatically included for CoAP */
329 #ifndef COAP_RES_WITHOUT_WELL_KNOWN
330 extern resource_t res_well_known_core;
331 #endif
332 #ifdef WITH_DTLS
333 extern resource_t res_dtls;
334 #endif
335 
336 /*---------------------------------------------------------------------------*/
337 PROCESS_THREAD(coap_engine, ev, data)
338 {
339  PROCESS_BEGIN();
340  PRINTF("Starting %s receiver...\n", coap_rest_implementation.name);
341 
342 #ifndef COAP_RES_WITHOUT_WELL_KNOWN
343  rest_activate_resource(&res_well_known_core, ".well-known/core");
344 #endif
345  coap_register_as_transaction_handler();
346  coap_init_connection(SERVER_LISTEN_PORT);
347 
348  while(1) {
349  PROCESS_YIELD();
350 
351  if(ev == tcpip_event) {
352  coap_receive();
353  } else if(ev == PROCESS_EVENT_TIMER) {
354  /* retransmissions are handled here */
355  coap_check_transactions();
356  }
357  } /* while (1) */
358 
359  PROCESS_END();
360 }
361 /*---------------------------------------------------------------------------*/
362 /*- Client Part -------------------------------------------------------------*/
363 /*---------------------------------------------------------------------------*/
364 void
365 coap_blocking_request_callback(void *callback_data, void *response)
366 {
367  struct request_state_t *state = (struct request_state_t *)callback_data;
368 
369  state->response = (coap_packet_t *)response;
370  process_poll(state->process);
371 }
372 /*---------------------------------------------------------------------------*/
373 PT_THREAD(coap_blocking_request
374  (struct request_state_t *state, process_event_t ev,
375  uip_ipaddr_t *remote_ipaddr, uint16_t remote_port,
376  coap_packet_t *request,
377  blocking_response_handler request_callback))
378 {
379  PT_BEGIN(&state->pt);
380 
381  static uint8_t more;
382  static uint32_t res_block;
383  static uint8_t block_error;
384 
385  state->block_num = 0;
386  state->response = NULL;
387  state->process = PROCESS_CURRENT();
388 
389  more = 0;
390  res_block = 0;
391  block_error = 0;
392 
393  do {
394  request->mid = coap_get_mid();
395  if((state->transaction = coap_new_transaction(request->mid, remote_ipaddr,
396  remote_port))) {
397  state->transaction->callback = coap_blocking_request_callback;
398  state->transaction->callback_data = state;
399 
400  if(state->block_num > 0) {
401  coap_set_header_block2(request, state->block_num, 0,
402  REST_MAX_CHUNK_SIZE);
403  }
404  state->transaction->packet_len = coap_serialize_message(request,
405  state->
406  transaction->
407  packet);
408 
409  coap_send_transaction(state->transaction);
410  PRINTF("Requested #%lu (MID %u)\n", state->block_num, request->mid);
411 
412  PT_YIELD_UNTIL(&state->pt, ev == PROCESS_EVENT_POLL);
413 
414  if(!state->response) {
415  PRINTF("Server not responding\n");
416  PT_EXIT(&state->pt);
417  }
418 
419  coap_get_header_block2(state->response, &res_block, &more, NULL, NULL);
420 
421  PRINTF("Received #%lu%s (%u bytes)\n", res_block, more ? "+" : "",
422  state->response->payload_len);
423 
424  if(res_block == state->block_num) {
425  request_callback(state->response);
426  ++(state->block_num);
427  } else {
428  PRINTF("WRONG BLOCK %lu/%lu\n", res_block, state->block_num);
429  ++block_error;
430  }
431  } else {
432  PRINTF("Could not allocate transaction buffer");
433  PT_EXIT(&state->pt);
434  }
435  } while(more && block_error < COAP_MAX_ATTEMPTS);
436 
437  PT_END(&state->pt);
438 }
439 /*---------------------------------------------------------------------------*/
440 /*- REST Engine Interface ---------------------------------------------------*/
441 /*---------------------------------------------------------------------------*/
442 const struct rest_implementation coap_rest_implementation = {
443  "CoAP-18",
444 
445  coap_init_engine,
446  coap_set_service_callback,
447 
448  coap_get_header_uri_path,
449  coap_get_rest_method,
450  coap_set_status_code,
451 
452  coap_get_header_content_format,
453  coap_set_header_content_format,
454  coap_get_header_accept,
455  coap_get_header_size2,
456  coap_set_header_size2,
457  coap_get_header_max_age,
458  coap_set_header_max_age,
459  coap_set_header_etag,
460  coap_get_header_if_match,
461  coap_get_header_if_none_match,
462  coap_get_header_uri_host,
463  coap_set_header_location_path,
464 
465  coap_get_payload,
466  coap_set_payload,
467 
468  coap_get_header_uri_query,
469  coap_get_query_variable,
470  coap_get_post_variable,
471 
472  coap_notify_observers,
473  coap_observe_handler,
474 
475  {
476  CONTENT_2_05,
477  CREATED_2_01,
478  CHANGED_2_04,
479  DELETED_2_02,
480  VALID_2_03,
481  BAD_REQUEST_4_00,
482  UNAUTHORIZED_4_01,
483  BAD_OPTION_4_02,
484  FORBIDDEN_4_03,
485  NOT_FOUND_4_04,
486  METHOD_NOT_ALLOWED_4_05,
487  NOT_ACCEPTABLE_4_06,
488  REQUEST_ENTITY_TOO_LARGE_4_13,
489  UNSUPPORTED_MEDIA_TYPE_4_15,
490  INTERNAL_SERVER_ERROR_5_00,
491  NOT_IMPLEMENTED_5_01,
492  BAD_GATEWAY_5_02,
493  SERVICE_UNAVAILABLE_5_03,
494  GATEWAY_TIMEOUT_5_04,
495  PROXYING_NOT_SUPPORTED_5_05
496  },
497 
498  {
499  TEXT_PLAIN,
500  TEXT_XML,
501  TEXT_CSV,
502  TEXT_HTML,
503  IMAGE_GIF,
504  IMAGE_JPEG,
505  IMAGE_PNG,
506  IMAGE_TIFF,
507  AUDIO_RAW,
508  VIDEO_RAW,
509  APPLICATION_LINK_FORMAT,
510  APPLICATION_XML,
511  APPLICATION_OCTET_STREAM,
512  APPLICATION_RDF_XML,
513  APPLICATION_SOAP_XML,
514  APPLICATION_ATOM_XML,
515  APPLICATION_XMPP_XML,
516  APPLICATION_EXI,
517  APPLICATION_FASTINFOSET,
518  APPLICATION_SOAP_FASTINFOSET,
519  APPLICATION_JSON,
520  APPLICATION_X_OBIX_BINARY
521  }
522 };
523 /*---------------------------------------------------------------------------*/
void process_poll(struct process *p)
Request a process to be polled.
Definition: process.c:371
process_event_t tcpip_event
The uIP event.
Definition: tcpip.c:80
rest_resource_flags_t
Resource flags for allowed methods and special functionalities.
Default definitions of C compiler quirk work-arounds.
#define PROCESS_END()
Define the end of a process.
Definition: process.h:131
#define PROCESS(name, strname)
Declare a process.
Definition: process.h:307
#define PROCESS_THREAD(name, ev, data)
Define the body of a process.
Definition: process.h:273
uip_appdata
Pointer to the application data in the packet buffer.
Definition: tcp_loader.c:74
#define UIP_IP_BUF
Pointer to IP header.
Definition: uip-nd6.c:104
#define PT_EXIT(pt)
Exit the protothread.
Definition: pt.h:245
#define uip_datalen()
The length of any incoming data that is currently available (if available) in the uip_appdata buffer...
Definition: uip.h:652
void rest_activate_resource(resource_t *resource, char *path)
Makes a resource available under the given URI path.
Definition: rest-engine.c:103
#define NULL
The null pointer.
#define PT_YIELD_UNTIL(pt, cond)
Yield from the protothread until a condition occurs.
Definition: pt.h:309
#define PROCESS_YIELD()
Yield the currently running process.
Definition: process.h:164
#define PT_THREAD(name_args)
Declaration of a protothread.
Definition: pt.h:99
#define PT_BEGIN(pt)
Declare the start of a protothread inside the C function implementing the protothread.
Definition: pt.h:114
void process_start(struct process *p, process_data_t data)
Start a process.
Definition: process.c:99
#define PT_END(pt)
Declare the end of a protothread.
Definition: pt.h:126
#define PROCESS_CURRENT()
Get a pointer to the currently running process.
Definition: process.h:402
CoAP implementation for the REST Engine.
#define uip_newdata()
Is new incoming data available?
Definition: uip.h:739
#define PROCESS_BEGIN()
Define the beginning of a process.
Definition: process.h:120