1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 * -*- */
2 
3 /* coap -- simple implementation of the Constrained Application Protocol (CoAP)
4  *         as defined in RFC 7252
5  *
6  * Copyright (C) 2010--2015 Olaf Bergmann <bergmann@tzi.org>
7  *
8  * This file is part of the CoAP library libcoap. Please see README for terms of
9  * use.
10  */
11 
12 
13 /**
14  * @file rd.c
15  * @brief CoRE resource directory
16  *
17  * @see http://tools.ietf.org/id/draft-shelby-core-resource-directory
18  */
19 
20 #include <string.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <stdio.h>
24 #include <ctype.h>
25 #include <sys/select.h>
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30 #include <netdb.h>
31 #include <sys/stat.h>
32 #include <dirent.h>
33 #include <errno.h>
34 #include <signal.h>
35 
36 #include "coap_config.h"
37 #include "utlist.h"
38 #include "resource.h"
39 #include "coap.h"
40 
41 #define COAP_RESOURCE_CHECK_TIME 2
42 
43 #define RD_ROOT_STR   ((unsigned char *)"rd")
44 #define RD_ROOT_SIZE  2
45 
46 #ifndef min
47 #define min(a,b) ((a) < (b) ? (a) : (b))
48 #endif
49 
50 typedef struct rd_t {
51   UT_hash_handle hh;      /**< hash handle (for internal use only) */
52   coap_key_t key;         /**< the actual key bytes for this resource */
53 
54   size_t etag_len;        /**< actual length of @c etag */
55   unsigned char etag[8];  /**< ETag for current description */
56 
57   str data;               /**< points to the resource description  */
58 } rd_t;
59 
60 rd_t *resources = NULL;
61 
62 #ifdef __GNUC__
63 #define UNUSED_PARAM __attribute__ ((unused))
64 #else /* not a GCC */
65 #define UNUSED_PARAM
66 #endif /* GCC */
67 
68 static inline rd_t *
rd_new(void)69 rd_new(void) {
70   rd_t *rd;
71   rd = (rd_t *)coap_malloc(sizeof(rd_t));
72   if (rd)
73     memset(rd, 0, sizeof(rd_t));
74 
75   return rd;
76 }
77 
78 static inline void
rd_delete(rd_t * rd)79 rd_delete(rd_t *rd) {
80   if (rd) {
81     coap_free(rd->data.s);
82     coap_free(rd);
83   }
84 }
85 
86 /* temporary storage for dynamic resource representations */
87 static int quit = 0;
88 
89 /* SIGINT handler: set quit to 1 for graceful termination */
90 static void
handle_sigint(int signum UNUSED_PARAM)91 handle_sigint(int signum UNUSED_PARAM) {
92   quit = 1;
93 }
94 
95 static void
hnd_get_resource(coap_context_t * ctx UNUSED_PARAM,struct coap_resource_t * resource,const coap_endpoint_t * local_interface UNUSED_PARAM,coap_address_t * peer UNUSED_PARAM,coap_pdu_t * request UNUSED_PARAM,str * token UNUSED_PARAM,coap_pdu_t * response)96 hnd_get_resource(coap_context_t  *ctx UNUSED_PARAM,
97                  struct coap_resource_t *resource,
98                  const coap_endpoint_t *local_interface UNUSED_PARAM,
99                  coap_address_t *peer UNUSED_PARAM,
100                  coap_pdu_t *request UNUSED_PARAM,
101                  str *token UNUSED_PARAM,
102                  coap_pdu_t *response) {
103   rd_t *rd = NULL;
104   unsigned char buf[3];
105 
106   HASH_FIND(hh, resources, resource->key, sizeof(coap_key_t), rd);
107 
108   response->hdr->code = COAP_RESPONSE_CODE(205);
109 
110   coap_add_option(response,
111                   COAP_OPTION_CONTENT_TYPE,
112                   coap_encode_var_bytes(buf,
113                                         COAP_MEDIATYPE_APPLICATION_LINK_FORMAT),
114                                         buf);
115 
116   if (rd && rd->etag_len)
117     coap_add_option(response, COAP_OPTION_ETAG, rd->etag_len, rd->etag);
118 
119   if (rd && rd->data.s)
120     coap_add_data(response, rd->data.length, rd->data.s);
121 }
122 
123 static void
hnd_put_resource(coap_context_t * ctx UNUSED_PARAM,struct coap_resource_t * resource UNUSED_PARAM,const coap_endpoint_t * local_interface UNUSED_PARAM,coap_address_t * peer UNUSED_PARAM,coap_pdu_t * request UNUSED_PARAM,str * token UNUSED_PARAM,coap_pdu_t * response)124 hnd_put_resource(coap_context_t  *ctx UNUSED_PARAM,
125                  struct coap_resource_t *resource UNUSED_PARAM,
126                  const coap_endpoint_t *local_interface UNUSED_PARAM,
127                  coap_address_t *peer UNUSED_PARAM,
128                  coap_pdu_t *request UNUSED_PARAM,
129                  str *token UNUSED_PARAM,
130                  coap_pdu_t *response) {
131 #if 1
132   response->hdr->code = COAP_RESPONSE_CODE(501);
133 #else /* FIXME */
134   coap_opt_iterator_t opt_iter;
135   coap_opt_t *token, *etag;
136   coap_pdu_t *response;
137   size_t size = sizeof(coap_hdr_t);
138   int type = (request->hdr->type == COAP_MESSAGE_CON)
139     ? COAP_MESSAGE_ACK : COAP_MESSAGE_NON;
140   rd_t *rd = NULL;
141   unsigned char code;     /* result code */
142   unsigned char *data;
143   str tmp;
144 
145   HASH_FIND(hh, resources, resource->key, sizeof(coap_key_t), rd);
146   if (rd) {
147     /* found resource object, now check Etag */
148     etag = coap_check_option(request, COAP_OPTION_ETAG, &opt_iter);
149     if (!etag || (COAP_OPT_LENGTH(etag) != rd->etag_len)
150         || memcmp(COAP_OPT_VALUE(etag), rd->etag, rd->etag_len) != 0) {
151 
152       if (coap_get_data(request, &tmp.length, &data)) {
153 
154         tmp.s = (unsigned char *)coap_malloc(tmp.length);
155         if (!tmp.s) {
156           debug("hnd_put_rd: cannot allocate storage for new rd\n");
157           code = COAP_RESPONSE_CODE(503);
158           goto finish;
159         }
160 
161         coap_free(rd->data.s);
162         rd->data.s = tmp.s;
163         rd->data.length = tmp.length;
164         memcpy(rd->data.s, data, rd->data.length);
165       }
166     }
167 
168     if (etag) {
169       rd->etag_len = min(COAP_OPT_LENGTH(etag), sizeof(rd->etag));
170       memcpy(rd->etag, COAP_OPT_VALUE(etag), rd->etag_len);
171     }
172 
173     code = COAP_RESPONSE_CODE(204);
174     /* FIXME: update lifetime */
175 
176     } else {
177 
178     code = COAP_RESPONSE_CODE(503);
179   }
180 
181   finish:
182   /* FIXME: do not create a new response but use the old one instead */
183   response = coap_pdu_init(type, code, request->hdr->id, size);
184 
185   if (!response) {
186     debug("cannot create response for message %d\n", request->hdr->id);
187     return;
188   }
189 
190   if (request->hdr->token_length)
191     coap_add_token(response, request->hdr->token_length, request->hdr->token);
192 
193   if (coap_send(ctx, peer, response) == COAP_INVALID_TID) {
194     debug("hnd_get_rd: cannot send response for message %d\n",
195     request->hdr->id);
196   }
197   coap_delete_pdu(response);
198 #endif
199 }
200 
201 static void
hnd_delete_resource(coap_context_t * ctx,struct coap_resource_t * resource,const coap_endpoint_t * local_interface UNUSED_PARAM,coap_address_t * peer UNUSED_PARAM,coap_pdu_t * request UNUSED_PARAM,str * token UNUSED_PARAM,coap_pdu_t * response)202 hnd_delete_resource(coap_context_t  *ctx,
203                     struct coap_resource_t *resource,
204                     const coap_endpoint_t *local_interface UNUSED_PARAM,
205                     coap_address_t *peer UNUSED_PARAM,
206                     coap_pdu_t *request UNUSED_PARAM,
207                     str *token UNUSED_PARAM,
208                     coap_pdu_t *response) {
209   rd_t *rd = NULL;
210 
211   HASH_FIND(hh, resources, resource->key, sizeof(coap_key_t), rd);
212   if (rd) {
213     HASH_DELETE(hh, resources, rd);
214     rd_delete(rd);
215   }
216   /* FIXME: link attributes for resource have been created dynamically
217    * using coap_malloc() and must be released. */
218   coap_delete_resource(ctx, resource->key);
219 
220   response->hdr->code = COAP_RESPONSE_CODE(202);
221 }
222 
223 static void
hnd_get_rd(coap_context_t * ctx UNUSED_PARAM,struct coap_resource_t * resource UNUSED_PARAM,const coap_endpoint_t * local_interface UNUSED_PARAM,coap_address_t * peer UNUSED_PARAM,coap_pdu_t * request UNUSED_PARAM,str * token UNUSED_PARAM,coap_pdu_t * response)224 hnd_get_rd(coap_context_t  *ctx UNUSED_PARAM,
225            struct coap_resource_t *resource UNUSED_PARAM,
226            const coap_endpoint_t *local_interface UNUSED_PARAM,
227            coap_address_t *peer UNUSED_PARAM,
228            coap_pdu_t *request UNUSED_PARAM,
229            str *token UNUSED_PARAM,
230            coap_pdu_t *response) {
231   unsigned char buf[3];
232 
233   response->hdr->code = COAP_RESPONSE_CODE(205);
234 
235   coap_add_option(response,
236                   COAP_OPTION_CONTENT_TYPE,
237                   coap_encode_var_bytes(buf,
238                                         COAP_MEDIATYPE_APPLICATION_LINK_FORMAT),
239                                         buf);
240 
241   coap_add_option(response,
242                   COAP_OPTION_MAXAGE,
243                   coap_encode_var_bytes(buf, 0x2ffff), buf);
244 }
245 
246 static int
parse_param(unsigned char * search,size_t search_len,unsigned char * data,size_t data_len,str * result)247 parse_param(unsigned char *search,
248             size_t search_len,
249             unsigned char *data,
250             size_t data_len,
251             str *result) {
252 
253   if (result)
254     memset(result, 0, sizeof(str));
255 
256   if (!search_len)
257     return 0;
258 
259   while (search_len <= data_len) {
260 
261     /* handle parameter if found */
262     if (memcmp(search, data, search_len) == 0) {
263       data += search_len;
264       data_len -= search_len;
265 
266       /* key is only valid if we are at end of string or delimiter follows */
267       if (!data_len || *data == '=' || *data == '&') {
268         while (data_len && *data != '=') {
269           ++data; --data_len;
270         }
271 
272         if (data_len > 1 && result) {
273           /* value begins after '=' */
274 
275           result->s = ++data;
276           while (--data_len && *data != '&') {
277             ++data; result->length++;
278           }
279         }
280 
281         return 1;
282       }
283     }
284 
285     /* otherwise proceed to next */
286     while (--data_len && *data++ != '&')
287       ;
288   }
289 
290   return 0;
291 }
292 
293 static void
add_source_address(struct coap_resource_t * resource,coap_address_t * peer)294 add_source_address(struct coap_resource_t *resource,
295                    coap_address_t *peer) {
296 #define BUFSIZE 64
297   char *buf;
298   size_t n = 1;
299 
300   buf = (char *)coap_malloc(BUFSIZE);
301   if (!buf)
302     return;
303 
304   buf[0] = '"';
305 
306   switch(peer->addr.sa.sa_family) {
307 
308   case AF_INET:
309     /* FIXME */
310     break;
311 
312   case AF_INET6:
313     n += snprintf(buf + n, BUFSIZE - n,
314       "[%02x%02x:%02x%02x:%02x%02x:%02x%02x" \
315       ":%02x%02x:%02x%02x:%02x%02x:%02x%02x]",
316       peer->addr.sin6.sin6_addr.s6_addr[0],
317       peer->addr.sin6.sin6_addr.s6_addr[1],
318       peer->addr.sin6.sin6_addr.s6_addr[2],
319       peer->addr.sin6.sin6_addr.s6_addr[3],
320       peer->addr.sin6.sin6_addr.s6_addr[4],
321       peer->addr.sin6.sin6_addr.s6_addr[5],
322       peer->addr.sin6.sin6_addr.s6_addr[6],
323       peer->addr.sin6.sin6_addr.s6_addr[7],
324       peer->addr.sin6.sin6_addr.s6_addr[8],
325       peer->addr.sin6.sin6_addr.s6_addr[9],
326       peer->addr.sin6.sin6_addr.s6_addr[10],
327       peer->addr.sin6.sin6_addr.s6_addr[11],
328       peer->addr.sin6.sin6_addr.s6_addr[12],
329       peer->addr.sin6.sin6_addr.s6_addr[13],
330       peer->addr.sin6.sin6_addr.s6_addr[14],
331       peer->addr.sin6.sin6_addr.s6_addr[15]);
332 
333     if (peer->addr.sin6.sin6_port != htons(COAP_DEFAULT_PORT)) {
334       n +=
335       snprintf(buf + n, BUFSIZE - n, ":%d", peer->addr.sin6.sin6_port);
336     }
337     break;
338     default:
339     ;
340   }
341 
342   if (n < BUFSIZE)
343     buf[n++] = '"';
344 
345   coap_add_attr(resource,
346                 (unsigned char *)"A",
347                 1,
348                 (unsigned char *)buf,
349                 n,
350                 COAP_ATTR_FLAGS_RELEASE_VALUE);
351 #undef BUFSIZE
352 }
353 
354 static rd_t *
make_rd(coap_address_t * peer UNUSED_PARAM,coap_pdu_t * pdu)355 make_rd(coap_address_t *peer UNUSED_PARAM, coap_pdu_t *pdu) {
356   rd_t *rd;
357   unsigned char *data;
358   coap_opt_iterator_t opt_iter;
359   coap_opt_t *etag;
360 
361   rd = rd_new();
362 
363   if (!rd) {
364     debug("hnd_get_rd: cannot allocate storage for rd\n");
365     return NULL;
366   }
367 
368   if (coap_get_data(pdu, &rd->data.length, &data)) {
369     rd->data.s = (unsigned char *)coap_malloc(rd->data.length);
370     if (!rd->data.s) {
371       debug("hnd_get_rd: cannot allocate storage for rd->data\n");
372       rd_delete(rd);
373       return NULL;
374     }
375     memcpy(rd->data.s, data, rd->data.length);
376   }
377 
378   etag = coap_check_option(pdu, COAP_OPTION_ETAG, &opt_iter);
379   if (etag) {
380     rd->etag_len = min(COAP_OPT_LENGTH(etag), sizeof(rd->etag));
381     memcpy(rd->etag, COAP_OPT_VALUE(etag), rd->etag_len);
382   }
383 
384   return rd;
385 }
386 
387 static void
hnd_post_rd(coap_context_t * ctx,struct coap_resource_t * resource UNUSED_PARAM,const coap_endpoint_t * local_interface UNUSED_PARAM,coap_address_t * peer,coap_pdu_t * request,str * token UNUSED_PARAM,coap_pdu_t * response)388 hnd_post_rd(coap_context_t  *ctx,
389             struct coap_resource_t *resource UNUSED_PARAM,
390             const coap_endpoint_t *local_interface UNUSED_PARAM,
391             coap_address_t *peer,
392             coap_pdu_t *request,
393             str *token UNUSED_PARAM,
394             coap_pdu_t *response) {
395   coap_resource_t *r;
396   coap_opt_iterator_t opt_iter;
397   coap_opt_t *query;
398 #define LOCSIZE 68
399   unsigned char *loc;
400   size_t loc_size;
401   str h = {0, NULL}, ins = {0, NULL}, rt = {0, NULL}, lt = {0, NULL}; /* store query parameters */
402   unsigned char *buf;
403 
404   loc = (unsigned char *)coap_malloc(LOCSIZE);
405   if (!loc) {
406     response->hdr->code = COAP_RESPONSE_CODE(500);
407     return;
408   }
409   memcpy(loc, RD_ROOT_STR, RD_ROOT_SIZE);
410 
411   loc_size = RD_ROOT_SIZE;
412   loc[loc_size++] = '/';
413 
414   /* store query parameters for later use */
415   query = coap_check_option(request, COAP_OPTION_URI_QUERY, &opt_iter);
416   if (query) {
417     parse_param((unsigned char *)"h", 1,
418     COAP_OPT_VALUE(query), COAP_OPT_LENGTH(query), &h);
419     parse_param((unsigned char *)"ins", 3,
420     COAP_OPT_VALUE(query), COAP_OPT_LENGTH(query), &ins);
421     parse_param((unsigned char *)"lt", 2,
422     COAP_OPT_VALUE(query), COAP_OPT_LENGTH(query), &lt);
423     parse_param((unsigned char *)"rt", 2,
424     COAP_OPT_VALUE(query), COAP_OPT_LENGTH(query), &rt);
425   }
426 
427   if (h.length) {   /* client has specified a node name */
428     memcpy(loc + loc_size, h.s, min(h.length, LOCSIZE - loc_size - 1));
429     loc_size += min(h.length, LOCSIZE - loc_size - 1);
430 
431     if (ins.length && loc_size > 1) {
432       loc[loc_size++] = '-';
433       memcpy((char *)(loc + loc_size),
434       ins.s, min(ins.length, LOCSIZE - loc_size - 1));
435       loc_size += min(ins.length, LOCSIZE - loc_size - 1);
436     }
437 
438   } else {      /* generate node identifier */
439     loc_size +=
440       snprintf((char *)(loc + loc_size), LOCSIZE - loc_size - 1,
441                "%x", request->hdr->id);
442 
443     if (loc_size > 1) {
444       if (ins.length) {
445         loc[loc_size++] = '-';
446         memcpy((char *)(loc + loc_size),
447                 ins.s,
448                 min(ins.length, LOCSIZE - loc_size - 1));
449         loc_size += min(ins.length, LOCSIZE - loc_size - 1);
450       } else {
451         coap_tick_t now;
452         coap_ticks(&now);
453 
454         loc_size += snprintf((char *)(loc + loc_size),
455                              LOCSIZE - loc_size - 1,
456                              "-%x",
457                              (unsigned int)(now & (unsigned int)-1));
458       }
459     }
460   }
461 
462   /* TODO:
463    *   - use lt to check expiration
464    */
465 
466   r = coap_resource_init(loc, loc_size, COAP_RESOURCE_FLAGS_RELEASE_URI);
467   coap_register_handler(r, COAP_REQUEST_GET, hnd_get_resource);
468   coap_register_handler(r, COAP_REQUEST_PUT, hnd_put_resource);
469   coap_register_handler(r, COAP_REQUEST_DELETE, hnd_delete_resource);
470 
471   if (ins.s) {
472     buf = (unsigned char *)coap_malloc(ins.length + 2);
473     if (buf) {
474       /* add missing quotes */
475       buf[0] = '"';
476       memcpy(buf + 1, ins.s, ins.length);
477       buf[ins.length + 1] = '"';
478       coap_add_attr(r,
479                     (unsigned char *)"ins",
480                     3,
481                     buf,
482                     ins.length + 2,
483                     COAP_ATTR_FLAGS_RELEASE_VALUE);
484     }
485   }
486 
487   if (rt.s) {
488     buf = (unsigned char *)coap_malloc(rt.length + 2);
489     if (buf) {
490       /* add missing quotes */
491       buf[0] = '"';
492       memcpy(buf + 1, rt.s, rt.length);
493       buf[rt.length + 1] = '"';
494       coap_add_attr(r,
495                     (unsigned char *)"rt",
496                     2,
497                     buf,
498                     rt.length + 2,COAP_ATTR_FLAGS_RELEASE_VALUE);
499     }
500   }
501 
502   add_source_address(r, peer);
503 
504   {
505     rd_t *rd;
506     rd = make_rd(peer, request);
507     if (rd) {
508       coap_hash_path(loc, loc_size, rd->key);
509       HASH_ADD(hh, resources, key, sizeof(coap_key_t), rd);
510     } else {
511       /* FIXME: send error response and delete r */
512     }
513   }
514 
515   coap_add_resource(ctx, r);
516 
517 
518   /* create response */
519 
520   response->hdr->code = COAP_RESPONSE_CODE(201);
521 
522   { /* split path into segments and add Location-Path options */
523     unsigned char _b[LOCSIZE];
524     unsigned char *b = _b;
525     size_t buflen = sizeof(_b);
526     int nseg;
527 
528     nseg = coap_split_path(loc, loc_size, b, &buflen);
529     while (nseg--) {
530       coap_add_option(response,
531                       COAP_OPTION_LOCATION_PATH,
532                       COAP_OPT_LENGTH(b),
533                       COAP_OPT_VALUE(b));
534       b += COAP_OPT_SIZE(b);
535     }
536   }
537 }
538 
539 static void
init_resources(coap_context_t * ctx)540 init_resources(coap_context_t *ctx) {
541   coap_resource_t *r;
542 
543   r = coap_resource_init(RD_ROOT_STR, RD_ROOT_SIZE, 0);
544   coap_register_handler(r, COAP_REQUEST_GET, hnd_get_rd);
545   coap_register_handler(r, COAP_REQUEST_POST, hnd_post_rd);
546 
547   coap_add_attr(r, (unsigned char *)"ct", 2, (unsigned char *)"40", 2, 0);
548   coap_add_attr(r, (unsigned char *)"rt", 2, (unsigned char *)"\"core.rd\"", 9, 0);
549   coap_add_attr(r, (unsigned char *)"ins", 2, (unsigned char *)"\"default\"", 9, 0);
550 
551   coap_add_resource(ctx, r);
552 
553 }
554 
555 static void
usage(const char * program,const char * version)556 usage( const char *program, const char *version) {
557   const char *p;
558 
559   p = strrchr( program, '/' );
560   if ( p )
561     program = ++p;
562 
563   fprintf( stderr, "%s v%s -- CoRE Resource Directory implementation\n"
564      "(c) 2011-2012 Olaf Bergmann <bergmann@tzi.org>\n\n"
565      "usage: %s [-A address] [-p port]\n\n"
566      "\t-A address\tinterface address to bind to\n"
567      "\t-p port\t\tlisten on specified port\n"
568      "\t-v num\t\tverbosity level (default: 3)\n",
569      program, version, program );
570 }
571 
572 static coap_context_t *
get_context(const char * node,const char * port)573 get_context(const char *node, const char *port) {
574   coap_context_t *ctx = NULL;
575   int s;
576   struct addrinfo hints;
577   struct addrinfo *result, *rp;
578 
579   memset(&hints, 0, sizeof(struct addrinfo));
580   hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
581   hints.ai_socktype = SOCK_DGRAM; /* Coap uses UDP */
582   hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
583 
584   s = getaddrinfo(node, port, &hints, &result);
585   if ( s != 0 ) {
586     fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
587     return NULL;
588   }
589 
590   /* iterate through results until success */
591   for (rp = result; rp != NULL; rp = rp->ai_next) {
592     coap_address_t addr;
593 
594     if (rp->ai_addrlen <= sizeof(addr.addr)) {
595       coap_address_init(&addr);
596       addr.size = rp->ai_addrlen;
597       memcpy(&addr.addr, rp->ai_addr, rp->ai_addrlen);
598 
599       ctx = coap_new_context(&addr);
600       if (ctx) {
601         /* TODO: output address:port for successful binding */
602         goto finish;
603       }
604     }
605   }
606 
607   fprintf(stderr, "no context available for interface '%s'\n", node);
608 
609  finish:
610   freeaddrinfo(result);
611   return ctx;
612 }
613 
614 static int
join(coap_context_t * ctx,char * group_name)615 join(coap_context_t *ctx, char *group_name) {
616   struct ipv6_mreq mreq;
617   struct addrinfo   *reslocal = NULL, *resmulti = NULL, hints, *ainfo;
618   int result = -1;
619 
620   /* we have to resolve the link-local interface to get the interface id */
621   memset(&hints, 0, sizeof(hints));
622   hints.ai_family = AF_INET6;
623   hints.ai_socktype = SOCK_DGRAM;
624 
625   result = getaddrinfo("::", NULL, &hints, &reslocal);
626   if ( result < 0 ) {
627     perror("join: cannot resolve link-local interface");
628     goto finish;
629   }
630 
631   /* get the first suitable interface identifier */
632   for (ainfo = reslocal; ainfo != NULL; ainfo = ainfo->ai_next) {
633     if ( ainfo->ai_family == AF_INET6 ) {
634       mreq.ipv6mr_interface =
635               ((struct sockaddr_in6 *)ainfo->ai_addr)->sin6_scope_id;
636       break;
637     }
638   }
639 
640   memset(&hints, 0, sizeof(hints));
641   hints.ai_family = AF_INET6;
642   hints.ai_socktype = SOCK_DGRAM;
643 
644   /* resolve the multicast group address */
645   result = getaddrinfo(group_name, NULL, &hints, &resmulti);
646 
647   if ( result < 0 ) {
648     perror("join: cannot resolve multicast address");
649     goto finish;
650   }
651 
652   for (ainfo = resmulti; ainfo != NULL; ainfo = ainfo->ai_next) {
653     if ( ainfo->ai_family == AF_INET6 ) {
654       mreq.ipv6mr_multiaddr =
655         ((struct sockaddr_in6 *)ainfo->ai_addr)->sin6_addr;
656       break;
657     }
658   }
659 
660   result = setsockopt(ctx->sockfd,
661                       IPPROTO_IPV6, IPV6_JOIN_GROUP,
662                       (char *)&mreq, sizeof(mreq) );
663   if ( result < 0 )
664     perror("join: setsockopt");
665 
666  finish:
667   freeaddrinfo(resmulti);
668   freeaddrinfo(reslocal);
669 
670   return result;
671 }
672 
673 int
main(int argc,char ** argv)674 main(int argc, char **argv) {
675   coap_context_t  *ctx;
676   fd_set readfds;
677   struct timeval tv, *timeout;
678   int result;
679   coap_tick_t now;
680   coap_queue_t *nextpdu;
681   char addr_str[NI_MAXHOST] = "::";
682   char port_str[NI_MAXSERV] = "5683";
683   char *group = NULL;
684   int opt;
685   coap_log_t log_level = LOG_WARNING;
686 
687   while ((opt = getopt(argc, argv, "A:g:p:v:")) != -1) {
688     switch (opt) {
689     case 'A' :
690       strncpy(addr_str, optarg, NI_MAXHOST-1);
691       addr_str[NI_MAXHOST - 1] = '\0';
692       break;
693     case 'g' :
694       group = optarg;
695       break;
696     case 'p' :
697       strncpy(port_str, optarg, NI_MAXSERV-1);
698       port_str[NI_MAXSERV - 1] = '\0';
699       break;
700     case 'v' :
701       log_level = strtol(optarg, NULL, 10);
702       break;
703     default:
704       usage( argv[0], PACKAGE_VERSION );
705       exit( 1 );
706     }
707   }
708 
709   coap_set_log_level(log_level);
710 
711   ctx = get_context(addr_str, port_str);
712   if (!ctx)
713     return -1;
714 
715   if (group)
716     join(ctx, group);
717 
718   init_resources(ctx);
719 
720   signal(SIGINT, handle_sigint);
721 
722   while ( !quit ) {
723     FD_ZERO(&readfds);
724     FD_SET( ctx->sockfd, &readfds );
725 
726     nextpdu = coap_peek_next( ctx );
727 
728     coap_ticks(&now);
729     while ( nextpdu && nextpdu->t <= now ) {
730       coap_retransmit( ctx, coap_pop_next( ctx ) );
731       nextpdu = coap_peek_next( ctx );
732     }
733 
734     if ( nextpdu && nextpdu->t <= now + COAP_RESOURCE_CHECK_TIME ) {
735       /* set timeout if there is a pdu to send before our automatic
736          timeout occurs */
737       tv.tv_usec = ((nextpdu->t - now) % COAP_TICKS_PER_SECOND) * 1000000 / COAP_TICKS_PER_SECOND;
738       tv.tv_sec = (nextpdu->t - now) / COAP_TICKS_PER_SECOND;
739       timeout = &tv;
740     } else {
741       tv.tv_usec = 0;
742       tv.tv_sec = COAP_RESOURCE_CHECK_TIME;
743       timeout = &tv;
744     }
745     result = select( FD_SETSIZE, &readfds, 0, 0, timeout );
746 
747     if ( result < 0 ) {     /* error */
748       if (errno != EINTR)
749         perror("select");
750       } else if ( result > 0 ) {  /* read from socket */
751         if ( FD_ISSET( ctx->sockfd, &readfds ) ) {
752           coap_read( ctx ); /* read received data */
753           /* coap_dispatch( ctx );  /\* and dispatch PDUs from receivequeue *\/ */
754         }
755       } else {            /* timeout */
756         /* coap_check_resource_list( ctx ); */
757     }
758   }
759 
760   coap_free_context( ctx );
761 
762   return 0;
763 }
764