Contiki-NG
coap-observe.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 module for observing resources (draft-ietf-core-observe-11).
35 * \author
36 * Matthias Kovatsch <kovatsch@inf.ethz.ch>
37 */
38
39/**
40 * \addtogroup coap
41 * @{
42 */
43
44#include <stdio.h>
45#include <string.h>
46#include "coap-observe.h"
47#include "coap-engine.h"
48#include "lib/memb.h"
49#include "lib/list.h"
50
51/* Log configuration */
52#include "coap-log.h"
53#define LOG_MODULE "coap"
54#define LOG_LEVEL LOG_LEVEL_COAP
55
56/*---------------------------------------------------------------------------*/
57MEMB(observers_memb, coap_observer_t, COAP_MAX_OBSERVERS);
58LIST(observers_list);
59/*---------------------------------------------------------------------------*/
60/*- Internal API ------------------------------------------------------------*/
61/*---------------------------------------------------------------------------*/
62static coap_observer_t *
63add_observer(const coap_endpoint_t *endpoint, const uint8_t *token,
64 size_t token_len, const char *uri, int uri_len)
65{
66 /* Remove existing observe relationship, if any. */
67 coap_remove_observer_by_uri(endpoint, uri);
68
69 coap_observer_t *o = memb_alloc(&observers_memb);
70
71 if(o) {
72 int max = sizeof(o->url) - 1;
73 if(max > uri_len) {
74 max = uri_len;
75 }
76 memcpy(o->url, uri, max);
77 o->url[max] = 0;
78 coap_endpoint_copy(&o->endpoint, endpoint);
79 o->token_len = token_len;
80 memcpy(o->token, token, token_len);
81 o->last_mid = 0;
82
83 LOG_INFO("Adding observer (%u/%u) for /%s [0x%02X%02X]\n",
84 list_length(observers_list) + 1, COAP_MAX_OBSERVERS,
85 o->url, o->token[0], o->token[1]);
86 list_add(observers_list, o);
87 }
88
89 return o;
90}
91/*---------------------------------------------------------------------------*/
92/*- Removal -----------------------------------------------------------------*/
93/*---------------------------------------------------------------------------*/
94void
95coap_remove_observer(coap_observer_t *o)
96{
97 LOG_INFO("Removing observer for /%s [0x%02X%02X]\n", o->url, o->token[0],
98 o->token[1]);
99
100 memb_free(&observers_memb, o);
101 list_remove(observers_list, o);
102}
103/*---------------------------------------------------------------------------*/
104int
105coap_remove_observer_by_client(const coap_endpoint_t *endpoint)
106{
107 int removed = 0;
108 coap_observer_t *obs = NULL;
109
110 LOG_DBG("Remove check client ");
111 LOG_DBG_COAP_EP(endpoint);
112 LOG_DBG_("\n");
113 for(obs = (coap_observer_t *)list_head(observers_list); obs;
114 obs = obs->next) {
115 if(coap_endpoint_cmp(&obs->endpoint, endpoint)) {
116 coap_remove_observer(obs);
117 removed++;
118 }
119 }
120 return removed;
121}
122/*---------------------------------------------------------------------------*/
123int
124coap_remove_observer_by_token(const coap_endpoint_t *endpoint,
125 uint8_t *token, size_t token_len)
126{
127 int removed = 0;
128 coap_observer_t *obs = NULL;
129
130 for(obs = (coap_observer_t *)list_head(observers_list); obs;
131 obs = obs->next) {
132 LOG_DBG("Remove check Token 0x%02X%02X\n", token[0], token[1]);
133 if(coap_endpoint_cmp(&obs->endpoint, endpoint)
134 && obs->token_len == token_len
135 && memcmp(obs->token, token, token_len) == 0) {
136 coap_remove_observer(obs);
137 removed++;
138 }
139 }
140 return removed;
141}
142/*---------------------------------------------------------------------------*/
143int
144coap_remove_observer_by_uri(const coap_endpoint_t *endpoint,
145 const char *uri)
146{
147 int removed = 0;
148 coap_observer_t *obs = NULL;
149
150 for(obs = (coap_observer_t *)list_head(observers_list); obs;
151 obs = obs->next) {
152 LOG_DBG("Remove check URL %p\n", uri);
153 if((endpoint == NULL
154 || (coap_endpoint_cmp(&obs->endpoint, endpoint)))
155 && (obs->url == uri || memcmp(obs->url, uri, strlen(obs->url)) == 0)) {
156 coap_remove_observer(obs);
157 removed++;
158 }
159 }
160 return removed;
161}
162/*---------------------------------------------------------------------------*/
163int
164coap_remove_observer_by_mid(const coap_endpoint_t *endpoint, uint16_t mid)
165{
166 int removed = 0;
167 coap_observer_t *obs = NULL;
168
169 for(obs = (coap_observer_t *)list_head(observers_list); obs;
170 obs = obs->next) {
171 LOG_DBG("Remove check MID %u\n", mid);
172 if(coap_endpoint_cmp(&obs->endpoint, endpoint)
173 && obs->last_mid == mid) {
174 coap_remove_observer(obs);
175 removed++;
176 }
177 }
178 return removed;
179}
180/*---------------------------------------------------------------------------*/
181/*- Notification ------------------------------------------------------------*/
182/*---------------------------------------------------------------------------*/
183void
184coap_notify_observers(coap_resource_t *resource)
185{
186 coap_notify_observers_sub(resource, NULL);
187}
188/* Can be used either for sub - or when there is not resource - just
189 a handler */
190void
191coap_notify_observers_sub(coap_resource_t *resource, const char *subpath)
192{
193 /* build notification */
194 coap_message_t notification[1]; /* this way the message can be treated as pointer as usual */
195 coap_message_t request[1]; /* this way the message can be treated as pointer as usual */
196 coap_observer_t *obs = NULL;
197 int url_len, obs_url_len;
198 char url[COAP_OBSERVER_URL_LEN];
199 uint8_t sub_ok = 0;
200
201 if(resource != NULL) {
202 url_len = strlen(resource->url);
203 strncpy(url, resource->url, COAP_OBSERVER_URL_LEN - 1);
204 if(url_len < COAP_OBSERVER_URL_LEN - 1 && subpath != NULL) {
205 strncpy(&url[url_len], subpath, COAP_OBSERVER_URL_LEN - url_len - 1);
206 }
207 } else if(subpath != NULL) {
208 strncpy(url, subpath, COAP_OBSERVER_URL_LEN - 1);
209 } else {
210 /* No resource, no subpath */
211 return;
212 }
213
214 /* Ensure url is null terminated because strncpy does not guarantee this */
215 url[COAP_OBSERVER_URL_LEN - 1] = '\0';
216 /* url now contains the notify URL that needs to match the observer */
217 LOG_INFO("Notification from %s\n", url);
218
219 coap_init_message(notification, COAP_TYPE_NON, CONTENT_2_05, 0);
220 /* create a "fake" request for the URI */
221 coap_init_message(request, COAP_TYPE_CON, COAP_GET, 0);
222 coap_set_header_uri_path(request, url);
223
224 /* iterate over observers */
225 url_len = strlen(url);
226 /* Assumes lazy evaluation... */
227 sub_ok = (resource == NULL) || (resource->flags & HAS_SUB_RESOURCES);
228 for(obs = (coap_observer_t *)list_head(observers_list); obs;
229 obs = obs->next) {
230 obs_url_len = strlen(obs->url);
231
232 /* Do a match based on the parent/sub-resource match so that it is
233 possible to do parent-node observe */
234
235 /***** TODO fix here so that we handle the notofication correctly ******/
236 /* All the new-style ... is assuming that the URL might be within */
237 if((obs_url_len == url_len
238 || (obs_url_len > url_len
239 && sub_ok
240 && obs->url[url_len] == '/'))
241 && strncmp(url, obs->url, url_len) == 0) {
242 coap_transaction_t *transaction = NULL;
243
244 /*TODO implement special transaction for CON, sharing the same buffer to allow for more observers */
245
246 if((transaction = coap_new_transaction(coap_get_mid(), &obs->endpoint))) {
247 /* if COAP_OBSERVE_REFRESH_INTERVAL is zero, never send observations as confirmable messages */
248 if(COAP_OBSERVE_REFRESH_INTERVAL != 0
249 && (obs->obs_counter % COAP_OBSERVE_REFRESH_INTERVAL == 0)) {
250 LOG_DBG(" Force Confirmable for\n");
251 notification->type = COAP_TYPE_CON;
252 }
253
254 LOG_DBG(" Observer ");
255 LOG_DBG_COAP_EP(&obs->endpoint);
256 LOG_DBG_("\n");
257
258 /* update last MID for RST matching */
259 obs->last_mid = transaction->mid;
260
261 /* prepare response */
262 notification->mid = transaction->mid;
263
264 int32_t new_offset = 0;
265
266 /* Either old style get_handler or the full handler */
267 if(coap_call_handlers(request, notification, transaction->message +
268 COAP_MAX_HEADER_SIZE, COAP_MAX_CHUNK_SIZE,
269 &new_offset) > 0) {
270 LOG_DBG("Notification on new handlers\n");
271 } else {
272 if(resource != NULL) {
273 resource->get_handler(request, notification,
274 transaction->message + COAP_MAX_HEADER_SIZE,
275 COAP_MAX_CHUNK_SIZE, &new_offset);
276 } else {
277 /* What to do here? */
278 notification->code = BAD_REQUEST_4_00;
279 }
280 }
281
282 if(notification->code < BAD_REQUEST_4_00) {
283 coap_set_header_observe(notification, (obs->obs_counter)++);
284 /* mask out to keep the CoAP observe option length <= 3 bytes */
285 obs->obs_counter &= 0xffffff;
286 }
287 coap_set_token(notification, obs->token, obs->token_len);
288
289 if(new_offset != 0) {
290 coap_set_header_block2(notification,
291 0,
292 new_offset != -1,
293 COAP_MAX_BLOCK_SIZE);
294 coap_set_payload(notification,
295 notification->payload,
296 MIN(notification->payload_len,
297 COAP_MAX_BLOCK_SIZE));
298 }
299
300 transaction->message_len =
301 coap_serialize_message(notification, transaction->message);
302
303 coap_send_transaction(transaction);
304 }
305 }
306 }
307}
308/*---------------------------------------------------------------------------*/
309void
310coap_observe_handler(const coap_resource_t *resource, coap_message_t *coap_req,
311 coap_message_t *coap_res)
312{
313 const coap_endpoint_t *src_ep;
314 coap_observer_t *obs;
315
316 LOG_DBG("CoAP observer handler rsc: %d\n", resource != NULL);
317
318 if(coap_req->code == COAP_GET && coap_res->code < 128) { /* GET request and response without error code */
319 if(coap_is_option(coap_req, COAP_OPTION_OBSERVE)) {
320 src_ep = coap_get_src_endpoint(coap_req);
321 if(src_ep == NULL) {
322 /* No source endpoint, can not add */
323 } else if(coap_req->observe == 0) {
324 obs = add_observer(src_ep,
325 coap_req->token, coap_req->token_len,
326 coap_req->uri_path, coap_req->uri_path_len);
327 if(obs) {
328 coap_set_header_observe(coap_res, (obs->obs_counter)++);
329 /* mask out to keep the CoAP observe option length <= 3 bytes */
330 obs->obs_counter &= 0xffffff;
331 /*
332 * Following payload is for demonstration purposes only.
333 * A subscription should return the same representation as a normal GET.
334 * Uncomment if you want an information about the avaiable observers.
335 */
336#if 0
337 static char content[16];
338 coap_set_payload(coap_res,
339 content,
340 snprintf(content, sizeof(content), "Added %u/%u",
341 list_length(observers_list),
342 COAP_MAX_OBSERVERS));
343#endif
344 } else {
345 coap_res->code = SERVICE_UNAVAILABLE_5_03;
346 coap_set_payload(coap_res, "TooManyObservers", 16);
347 }
348 } else if(coap_req->observe == 1) {
349
350 /* remove client if it is currently observe */
351 coap_remove_observer_by_token(src_ep,
352 coap_req->token, coap_req->token_len);
353 }
354 }
355 }
356}
357/*---------------------------------------------------------------------------*/
358uint8_t
359coap_has_observers(char *path)
360{
361 coap_observer_t *obs = NULL;
362
363 for(obs = (coap_observer_t *)list_head(observers_list); obs;
364 obs = obs->next) {
365 if((strncmp(obs->url, path, strlen(path))) == 0) {
366 return 1;
367 }
368 }
369 return 0;
370}
371/*---------------------------------------------------------------------------*/
372/** @} */
CoAP engine implementation.
Log support for CoAP.
CoAP module for observing resources (draft-ietf-core-observe-11).
int coap_endpoint_cmp(const coap_endpoint_t *e1, const coap_endpoint_t *e2)
Compare two CoAP endpoints.
Definition: coap-uip.c:163
void coap_endpoint_copy(coap_endpoint_t *dest, const coap_endpoint_t *src)
Copy a CoAP endpoint from one memory area to another.
Definition: coap-uip.c:154
#define LIST(name)
Declare a linked list.
Definition: list.h:89
int list_length(const_list_t list)
Get the length of a list.
Definition: list.c:178
void list_add(list_t list, void *item)
Add an item at the end of a list.
Definition: list.c:89
void list_remove(list_t list, const void *item)
Remove a specific element from a list.
Definition: list.c:152
void * list_head(const_list_t list)
Get a pointer to the first element of a list.
Definition: list.c:63
int memb_free(struct memb *m, void *ptr)
Deallocate a memory block from a memory block previously declared with MEMB().
Definition: memb.c:78
void * memb_alloc(struct memb *m)
Allocate a memory block from a block of memory declared with MEMB().
Definition: memb.c:59
#define MEMB(name, structure, num)
Declare a memory block.
Definition: memb.h:91
Linked list manipulation routines.
Memory block allocation routines.