libcoap  4.1.1
 All Data Structures Files Functions Variables Typedefs Macros Groups Pages
subscribe.c
Go to the documentation of this file.
1 /* subscribe.c -- subscription handling for CoAP
2  * see draft-ietf-coap-observe-09
3  *
4  * Copyright (C) 2010--2013 Olaf Bergmann <bergmann@tzi.org>
5  *
6  * This file is part of the CoAP library libcoap. Please see
7  * README for terms of use.
8  */
9 
10 #include "config.h"
11 
12 #if defined(HAVE_ASSERT_H) && !defined(assert)
13 # include <assert.h>
14 #endif
15 
16 #include <stdio.h>
17 #include <limits.h>
18 #ifdef HAVE_ARPA_INET_H
19 # include <arpa/inet.h>
20 #endif
21 
22 /* #include "resource.h" */
23 
24 #include "mem.h"
25 #include "encode.h"
26 #include "debug.h"
27 #include "subscribe.h"
28 
29 void
31  assert(s);
32  memset(s, 0, sizeof(coap_subscription_t));
33 }
34 
35 #if 0
36 #define HMASK (ULONG_MAX >> 1)
37 
38 void
39 notify(coap_context_t *context, coap_resource_t *res,
40  coap_subscription_t *sub, unsigned int duration, int code) {
41 #if 0
42  coap_pdu_t *pdu;
43  int ls, finished=0;
44  unsigned char ct, d;
45  unsigned int length;
46 #ifndef NDEBUG
47  char addr[INET6_ADDRSTRLEN];
48 #endif
49 
50  if ( !context || !res || !sub || !(pdu = coap_new_pdu()) )
51  return;
52 
53  pdu->hdr->type = COAP_MESSAGE_CON;
54  pdu->hdr->id = rand(); /* use a random transaction id */
55  pdu->hdr->code = code;
56 
57  /* FIXME: content-type and data (how about block?) */
58  if (res->uri->host.length)
60  res->uri->host.length,
61  res->uri->host.s );
62 
63  if (res->uri->path.length)
65  res->uri->path.length,
66  res->uri->path.s);
67 
68  d = COAP_PSEUDOFP_ENCODE_8_4_DOWN(duration, ls);
69 
71 
72  if (sub->token.length) {
73  coap_add_option (pdu, COAP_OPTION_TOKEN,
74  sub->token.length,
75  sub->token.s);
76  }
77 
78  if (res->uri->query.length)
80  res->uri->query.length,
81  res->uri->query.s );
82 
83  if (res->data) {
84  length = (unsigned char *)pdu->hdr + COAP_MAX_PDU_SIZE - pdu->data;
85  ct = res->mediatype;
86  res->data(res->uri, &ct, 0, pdu->data, &length, &finished);
87  pdu->length += length;
88 
89  /* TODO: add block option if not finished */
90  /* TODO: add mediatype */
91  }
92 
93 #ifndef NDEBUG
94  if ( inet_ntop(sub->subscriber.addr.sa.sa_family,
95  &sub->subscriber.addr, addr, sizeof(addr)) ) {
96  debug("*** notify for %s to [%s]\n", res->uri->path.s, addr);
97  }
98 #endif
99  if (pdu && coap_send_confirmed(context,
100  &sub->subscriber.addr.sa,
101  sub->subscriber.size, pdu)
102  == COAP_INVALID_TID) {
103 #ifndef NDEBUG
104  debug("coap_check_resource_list: error sending notification\n");
105 #endif
106  coap_delete_pdu(pdu);
107  }
108 #endif
109 }
110 
111 void
112 coap_check_resource_list(coap_context_t *context) {
113  coap_resource_t *res, *tmp;
114  coap_list_t *sub;
115  coap_key_t key;
116  time_t now;
117 
118  if ( !context || !context->resources /* || !context->subscribers */)
119  return;
120 
121  time(&now); /* FIXME: use coap_ticks() */
122 
123  HASH_ITER(hh, context->resources, res, tmp) {
124  if (res->dirty) {
125  debug("FIXME: notify subscribers\n");
126 #if 0
127  key = coap_uri_hash( COAP_RESOURCE(res)->uri ) ;
128 
129  /* is subscribed? */
130  for (sub = context->subscriptions; sub; sub = sub->next) {
131  if ( COAP_SUBSCRIPTION(sub)->resource == key ) {
132  /* notify subscriber */
133  notify(context, COAP_RESOURCE(res), COAP_SUBSCRIPTION(sub),
134  COAP_SUBSCRIPTION(sub)->expires - now, COAP_RESPONSE_200);
135  }
136 
137  }
138 
139  COAP_RESOURCE(res)->dirty = 0;
140 #endif
141  }
142  }
143 }
144 
145 #if 0
148  coap_list_t *node;
149 
150  if (ctx) {
151  /* TODO: use hash table for resources with key to access */
152  for (node = ctx->resources; node; node = node->next) {
153  printf("check %ux\n", coap_uri_hash(COAP_RESOURCE(node)->uri));
154  if ( key == coap_uri_hash(COAP_RESOURCE(node)->uri) ) {
155  printf("found\n");
156  return COAP_RESOURCE(node);
157  }
158  }
159  }
160 
161  printf("not found\n");
162  return NULL;
163 }
164 
166 coap_get_resource(coap_context_t *ctx, coap_uri_t *uri) {
167 #ifndef NDEBUG
168  int i;
169  printf("search resource %ux", coap_uri_hash(uri));
170  for (i=0; i < uri->path.length; ++i) {
171  printf(" %02x", uri->path.s[i]);
172  }
173  printf("\n");
174 #endif
175  return uri ? coap_get_resource_from_key(ctx, coap_uri_hash(uri)) : NULL;
176 }
177 #endif
178 
179 void
180 coap_check_subscriptions(coap_context_t *context) {
181  time_t now;
182  coap_list_t *node;
183 #ifndef NDEBUG
184  char addr[INET6_ADDRSTRLEN];
185 #endif
186 
187  if ( !context )
188  return;
189 
190  time(&now);
191 
192  node = context->subscriptions;
193  while ( node && COAP_SUBSCRIPTION(node)->expires < now ) {
194 #ifndef NDEBUG
195  if (inet_ntop(COAP_SUBSCRIPTION(node)->subscriber.addr.sa.sa_family,
196  &COAP_SUBSCRIPTION(node)->subscriber.addr,
197  addr, sizeof(addr))) {
198 
199  debug("** removed expired subscription from [%s]\n", addr);
200  }
201 #endif
202 #if 0
203  notify(context,
204  coap_get_resource_from_key(context, COAP_SUBSCRIPTION(node)->resource),
205  COAP_SUBSCRIPTION(node),
206  0, COAP_RESPONSE_400);
207 #endif
208  context->subscriptions = node->next;
209  coap_delete(node);
210  node = context->subscriptions;
211  }
212 }
213 
214 void
215 coap_free_resource(void *res) {
216  if ( res ) {
217 #if 0
218  coap_free(((coap_resource_t *)res)->uri);
219  coap_delete_string(((coap_resource_t *)res)->name);
220 #endif
221  }
222 }
223 
224 #if 0
225 
229 int
231  coap_list_t *prev, *node;
232 
233  if (!context || key == COAP_INVALID_HASHKEY)
234  return 0;
235 
236  for (prev = NULL, node = context->resources; node;
237  prev = node, node = node->next) {
238  if (coap_uri_hash(COAP_RESOURCE(node)->uri) == key) {
239 #ifndef NDEBUG
240  debug("removed key %lu (%s)\n",key,COAP_RESOURCE(node)->uri->path.s);
241 #endif
242  if (!prev)
243  context->resources = node->next;
244  else
245  prev->next = node->next;
246 
247  coap_delete(node);
248  return 1;
249  }
250  }
251  return 0;
252 }
253 #endif
254 
256 coap_new_subscription(coap_context_t *context, const coap_uri_t *resource,
257  const struct sockaddr *addr, socklen_t addrlen, time_t expiry) {
258  coap_subscription_t *result;
259 
260  if ( !context || !resource || !addr
261  || !(result = coap_malloc(sizeof(coap_subscription_t))))
262  return NULL;
263 
264  result->resource = coap_uri_hash(resource);
265  result->expires = expiry;
266  memcpy(&result->subscriber.addr.sa, addr, addrlen);
267 
268  memset(&result->token, 0, sizeof(str));
269 
270  return result;
271 
272 }
273 
274 coap_list_t *
275 coap_list_push_first(coap_list_t **list, void *data, void (*delete_func)(void *) ) {
276  coap_list_t *node;
277  node = coap_new_listnode(data, delete_func);
278  if ( !node || !list )
279  return NULL;
280 
281  if ( !*list ) {
282  *list = node;
283  } else {
284  node->next = *list;
285  *list = node;
286  }
287 
288  return node;
289 }
290 
291 int
292 _order_subscription(void *a, void *b) {
293  if ( !a || !b )
294  return a < b ? -1 : 1;
295 
296  return ((coap_subscription_t *)a)->expires < ((coap_subscription_t *)b)->expires ? -1 : 1;
297 }
298 
300 coap_subscription_hash(coap_subscription_t *subscription) {
301  if ( !subscription )
302  return COAP_INVALID_HASHKEY;
303 
304  return _hash2( subscription->resource, (unsigned char *)&subscription->subscriber,
305  sizeof(subscription->subscriber) );
306 }
307 
309 coap_add_subscription(coap_context_t *context,
310  coap_subscription_t *subscription) {
311  coap_list_t *node;
312  if ( !context || !subscription )
313  return COAP_INVALID_HASHKEY;
314 
315  if ( !(node = coap_new_listnode(subscription, NULL)) )
316  return COAP_INVALID_HASHKEY;
317 
318  if ( !coap_insert(&context->subscriptions, node, _order_subscription ) ) {
319  coap_free( node ); /* do not call coap_delete(), so subscription object will survive */
320  return COAP_INVALID_HASHKEY;
321  }
322 
323  return coap_subscription_hash(subscription);
324 }
325 
327 coap_find_subscription(coap_context_t *context,
328  coap_key_t hashkey,
329  struct sockaddr *addr,
330  str *token) {
331 #if 0
332  coap_list_t *node;
333 #endif
334 
335  if (!context || !addr || hashkey == COAP_INVALID_HASHKEY)
336  return NULL;
337 
338  /* FIXME: I do not like the way subscriptions work right now. To be fixed. */
339 
340 #if 0
341  for (node = context->subscriptions; node; node = node->next) {
342  if (COAP_SUBSCRIPTION(node)->resource == hashkey) {
343 
344  if (token) { /* do not proceed if tokens do not match */
345  if (token->length != COAP_SUBSCRIPTION(node)->token.length ||
346  memcmp(token->s, COAP_SUBSCRIPTION(node)->token.s,
347  token->length) != 0)
348  continue;
349  }
350 
351  if (subscriber->sin6_port == COAP_SUBSCRIPTION(node)->subscriber.sin6_port
352  && memcmp(&subscriber->sin6_addr,
353  &COAP_SUBSCRIPTION(node)->subscriber.sin6_addr,
354  sizeof(struct in6_addr)) == 0)
355  return COAP_SUBSCRIPTION(node);
356  }
357  }
358 #endif
359  return NULL;
360 }
361 
362 int
363 coap_delete_subscription(coap_context_t *context,
364  coap_key_t key,
365  struct sockaddr *addr) {
366 #if 0
367  coap_list_t *prev, *node;
368 #endif
369 
370  if (!context || !addr || key == COAP_INVALID_HASHKEY)
371  return 0;
372 
373  /* FIXME: I do not like the way subscriptions work right now. To be fixed. */
374 
375 #if 0
376  for (prev = NULL, node = context->subscriptions; node;
377  prev = node, node = node->next) {
378  if (COAP_SUBSCRIPTION(node)->resource == key) {
379  if (subscriber->sin6_port == COAP_SUBSCRIPTION(node)->subscriber.sin6_port
380  && memcmp(&subscriber->sin6_addr,
381  &COAP_SUBSCRIPTION(node)->subscriber.sin6_addr,
382  sizeof(struct in6_addr)) == 0) {
383 
384  if (!prev) {
385  context->subscriptions = node->next;
386  coap_free(COAP_SUBSCRIPTION(node)->token.s);
387  coap_delete(node);
388  } else {
389  prev->next = node->next;
390  coap_free(COAP_SUBSCRIPTION(node)->token.s);
391  coap_delete(node);
392  }
393  return 1;
394  }
395  }
396  }
397 #endif
398 
399  return 0;
400 }
401 #endif
Representation of parsed URI.
Definition: uri.h:18
int coap_delete(coap_list_t *node)
Definition: coap_list.c:52
unsigned char coap_key_t[4]
Definition: hashkey.h:19
int coap_insert(coap_list_t **queue, coap_list_t *node, int(*order)(void *, void *node))
Definition: coap_list.c:19
int coap_delete_resource(coap_context_t *context, coap_key_t key)
Deletes a resource identified by key.
Definition: resource.c:451
coap_tid_t coap_send_confirmed(coap_context_t *context, const coap_address_t *dst, coap_pdu_t *pdu)
Sends a confirmed CoAP message to given destination.
Definition: net.c:699
unsigned short id
Definition: pdu.h:173
#define HASH_ITER(hh, head, el, tmp)
Definition: uthash.h:897
unsigned short length
PDU length (including header, options, data)
Definition: pdu.h:211
struct coap_linkedlistnode * next
Definition: coap_list.h:13
str path
Beginning of the first path segment.
Definition: uri.h:21
#define coap_malloc(size)
Definition: mem.h:15
size_t length
Definition: str.h:15
static coap_uri_t uri
Definition: client.c:36
coap_hdr_t * hdr
Definition: pdu.h:209
#define COAP_RESPONSE_400
Definition: pdu.h:125
#define COAP_OPTION_SUBSCRIPTION
Definition: pdu.h:77
#define debug(...)
Definition: debug.h:55
#define COAP_INVALID_TID
Definition: pdu.h:156
str uri
Request URI for this resource.
Definition: resource.h:96
unsigned int code
Definition: pdu.h:172
#define coap_free(size)
Definition: mem.h:16
Header structure for CoAP PDUs.
Definition: pdu.h:206
unsigned int dirty
set to 1 if resource has changed
Definition: resource.h:62
#define COAP_MESSAGE_CON
Definition: pdu.h:43
size_t coap_add_option(coap_pdu_t *pdu, unsigned short type, unsigned int len, const unsigned char *data)
de-duplicate code with coap_add_option_later
Definition: pdu.c:175
#define COAP_MAX_PDU_SIZE
Definition: pdu.h:27
#define INET6_ADDRSTRLEN
coap_list_t * coap_new_listnode(void *data, void(*delete_func)(void *))
Creates a new list node and adds the given data object.
Definition: coap_list.c:74
Subscriber information.
Definition: subscribe.h:41
void coap_delete_pdu(coap_pdu_t *pdu)
Definition: pdu.c:143
Definition: str.h:14
#define COAP_OPTION_URI_PATH
Definition: pdu.h:63
Definition: t_list.h:78
#define COAP_RESPONSE_200
Definition: pdu.h:122
void coap_delete_string(str *s)
Deletes the given string and releases any memory allocated.
Definition: str.c:31
#define COAP_OPTION_URI_QUERY
Definition: pdu.h:67
unsigned int type
Definition: pdu.h:170
unsigned char token[8]
token used for subscription
Definition: subscribe.h:51
coap_pdu_t * coap_new_pdu()
Creates a new CoAP PDU.
Definition: pdu.c:126
unsigned char * s
Definition: str.h:16
void coap_subscription_init(coap_subscription_t *s)
Definition: subscribe.c:30
#define COAP_OPTION_URI_HOST
Definition: pdu.h:58
unsigned char * data
payload
Definition: pdu.h:212
coap_address_t subscriber
address and port of subscriber
Definition: subscribe.h:43
The CoAP stack's global state is stored in a coap_context_t object.
Definition: net.h:97
#define COAP_PSEUDOFP_ENCODE_8_4_DOWN(v, ls)
Definition: encode.h:35
coap_resource_t * coap_get_resource_from_key(coap_context_t *context, coap_key_t key)
Returns the resource identified by the unique string key.
Definition: resource.c:503
struct coap_resource_t * resources
hash table or list of known resources
Definition: net.h:100