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