1 /* coap -- simple implementation of the Constrained Application Protocol (CoAP)
2 * as defined in draft-ietf-core-coap
4 * Copyright (C) 2010--2013 Olaf Bergmann <bergmann@tzi.org>
6 * This file is part of the CoAP library libcoap. Please see
7 * README for terms of use.
12 * @brief CoRE resource directory
14 * @see http://tools.ietf.org/id/draft-shelby-core-resource-directory
22 #include <sys/select.h>
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
38 #define COAP_RESOURCE_CHECK_TIME 2
40 #define RD_ROOT_STR ((unsigned char *)"rd")
41 #define RD_ROOT_SIZE 2
44 #define min(a,b) ((a) < (b) ? (a) : (b))
49 UT_hash_handle hh; /**< hash handle (for internal use only) */
50 coap_key_t key; /**< the actual key bytes for this resource */
52 size_t etag_len; /**< actual length of @c etag */
53 unsigned char etag[8]; /**< ETag for current description */
55 str data; /**< points to the resource description */
58 rd_t *resources = NULL;
64 rd = (rd_t *) coap_malloc(sizeof(rd_t));
66 memset(rd, 0, sizeof(rd_t));
71 inline void rd_delete(rd_t *rd)
75 coap_free(rd->data.s);
80 /* temporary storage for dynamic resource representations */
83 /* SIGINT handler: set quit to 1 for graceful termination */
84 void handle_sigint(int signum)
89 void hnd_get_resource(coap_context_t *ctx, struct coap_resource_t *resource, coap_address_t *peer,
90 coap_pdu_t *request, str *token, coap_pdu_t *response)
95 HASH_FIND(hh, resources, resource->key, sizeof(coap_key_t), rd);
97 response->hdr->code = COAP_RESPONSE_CODE(205);
99 coap_add_option(response, COAP_OPTION_CONTENT_TYPE,
100 coap_encode_var_bytes(buf, COAP_MEDIATYPE_APPLICATION_LINK_FORMAT), buf);
102 if (rd && rd->etag_len)
103 coap_add_option(response, COAP_OPTION_ETAG, rd->etag_len, rd->etag);
105 if (rd && rd->data.s)
106 coap_add_data(response, rd->data.length, rd->data.s);
109 void hnd_put_resource(coap_context_t *ctx, struct coap_resource_t *resource, coap_address_t *peer,
110 coap_pdu_t *request, str *token, coap_pdu_t *response)
113 response->hdr->code = COAP_RESPONSE_CODE(501);
115 coap_opt_iterator_t opt_iter;
116 coap_opt_t *token, *etag;
117 coap_pdu_t *response;
118 size_t size = sizeof(coap_hdr_t);
119 int type = (request->hdr->type == COAP_MESSAGE_CON)
120 ? COAP_MESSAGE_ACK : COAP_MESSAGE_NON;
122 unsigned char code; /* result code */
126 HASH_FIND(hh, resources, resource->key, sizeof(coap_key_t), rd);
129 /* found resource object, now check Etag */
130 etag = coap_check_option(request, COAP_OPTION_ETAG, &opt_iter);
131 if (!etag || (COAP_OPT_LENGTH(etag) != rd->etag_len)
132 || memcmp(COAP_OPT_VALUE(etag), rd->etag, rd->etag_len) != 0)
135 if (coap_get_data(request, &tmp.length, &data))
138 tmp.s = (unsigned char *)coap_malloc(tmp.length);
141 debug("hnd_put_rd: cannot allocate storage for new rd\n");
142 code = COAP_RESPONSE_CODE(503);
146 coap_free(rd->data.s);
148 rd->data.length = tmp.length;
149 memcpy(rd->data.s, data, rd->data.length);
155 rd->etag_len = min(COAP_OPT_LENGTH(etag), sizeof(rd->etag));
156 memcpy(rd->etag, COAP_OPT_VALUE(etag), rd->etag_len);
159 code = COAP_RESPONSE_CODE(204);
160 /* FIXME: update lifetime */
166 code = COAP_RESPONSE_CODE(503);
170 /* FIXME: do not create a new response but use the old one instead */
171 response = coap_pdu_init(type, code, request->hdr->id, size);
175 debug("cannot create response for message %d\n", request->hdr->id);
179 if (request->hdr->token_length)
180 coap_add_token(response, request->hdr->token_length, request->hdr->token);
182 if (coap_send(ctx, peer, response) == COAP_INVALID_TID)
184 debug("hnd_get_rd: cannot send response for message %d\n",
187 coap_delete_pdu(response);
191 void hnd_delete_resource(coap_context_t *ctx, struct coap_resource_t *resource,
192 coap_address_t *peer, coap_pdu_t *request, str *token, coap_pdu_t *response)
196 HASH_FIND(hh, resources, resource->key, sizeof(coap_key_t), rd);
199 HASH_DELETE(hh, resources, rd);
202 /* FIXME: link attributes for resource have been created dynamically
203 * using coap_malloc() and must be released. */
204 coap_delete_resource(ctx, resource->key);
206 response->hdr->code = COAP_RESPONSE_CODE(202);
209 void hnd_get_rd(coap_context_t *ctx, struct coap_resource_t *resource, coap_address_t *peer,
210 coap_pdu_t *request, str *token, coap_pdu_t *response)
212 unsigned char buf[3];
214 response->hdr->code = COAP_RESPONSE_CODE(205);
216 coap_add_option(response, COAP_OPTION_CONTENT_TYPE,
217 coap_encode_var_bytes(buf, COAP_MEDIATYPE_APPLICATION_LINK_FORMAT), buf);
219 coap_add_option(response, COAP_OPTION_MAXAGE, coap_encode_var_bytes(buf, 0x2ffff), buf);
222 int parse_param(unsigned char *search, size_t search_len, unsigned char *data, size_t data_len,
227 memset(result, 0, sizeof(str));
232 while (search_len <= data_len)
235 /* handle parameter if found */
236 if (memcmp(search, data, search_len) == 0)
239 data_len -= search_len;
241 /* key is only valid if we are at end of string or delimiter follows */
242 if (!data_len || *data == '=' || *data == '&')
244 while (data_len && *data != '=')
250 if (data_len > 1 && result)
252 /* value begins after '=' */
255 while (--data_len && *data != '&')
266 /* otherwise proceed to next */
267 while (--data_len && *data++ != '&')
274 void add_source_address(struct coap_resource_t *resource, coap_address_t *peer)
280 buf = (char *) coap_malloc(BUFSIZE);
286 switch (peer->addr.sa.sa_family)
294 n += snprintf(buf + n, BUFSIZE - n, "[%02x%02x:%02x%02x:%02x%02x:%02x%02x"
295 ":%02x%02x:%02x%02x:%02x%02x:%02x%02x]", peer->addr.sin6.sin6_addr.s6_addr[0],
296 peer->addr.sin6.sin6_addr.s6_addr[1], peer->addr.sin6.sin6_addr.s6_addr[2],
297 peer->addr.sin6.sin6_addr.s6_addr[3], peer->addr.sin6.sin6_addr.s6_addr[4],
298 peer->addr.sin6.sin6_addr.s6_addr[5], peer->addr.sin6.sin6_addr.s6_addr[6],
299 peer->addr.sin6.sin6_addr.s6_addr[7], peer->addr.sin6.sin6_addr.s6_addr[8],
300 peer->addr.sin6.sin6_addr.s6_addr[9], peer->addr.sin6.sin6_addr.s6_addr[10],
301 peer->addr.sin6.sin6_addr.s6_addr[11], peer->addr.sin6.sin6_addr.s6_addr[12],
302 peer->addr.sin6.sin6_addr.s6_addr[13], peer->addr.sin6.sin6_addr.s6_addr[14],
303 peer->addr.sin6.sin6_addr.s6_addr[15]);
305 if (peer->addr.sin6.sin6_port != htons(COAP_DEFAULT_PORT))
307 n += snprintf(buf + n, BUFSIZE - n, ":%d", peer->addr.sin6.sin6_port);
317 coap_add_attr(resource, (unsigned char *) "A", 1, (unsigned char *) buf, n,
318 COAP_ATTR_FLAGS_RELEASE_VALUE);
323 make_rd(coap_address_t *peer, coap_pdu_t *pdu)
327 coap_opt_iterator_t opt_iter;
334 debug("hnd_get_rd: cannot allocate storage for rd\n");
338 if (coap_get_data(pdu, &rd->data.length, &data))
340 rd->data.s = (unsigned char *) coap_malloc(rd->data.length);
343 debug("hnd_get_rd: cannot allocate storage for rd->data\n");
347 memcpy(rd->data.s, data, rd->data.length);
350 etag = coap_check_option(pdu, COAP_OPTION_ETAG, &opt_iter);
353 rd->etag_len = min(COAP_OPT_LENGTH(etag), sizeof(rd->etag));
354 memcpy(rd->etag, COAP_OPT_VALUE(etag), rd->etag_len);
360 void hnd_post_rd(coap_context_t *ctx, struct coap_resource_t *resource, coap_address_t *peer,
361 coap_pdu_t *request, str *token, coap_pdu_t *response)
364 coap_opt_iterator_t opt_iter;
373 { 0, NULL }; /* store query parameters */
376 loc = (unsigned char *) coap_malloc(LOCSIZE);
379 response->hdr->code = COAP_RESPONSE_CODE(500);
382 memcpy(loc, RD_ROOT_STR, RD_ROOT_SIZE);
384 loc_size = RD_ROOT_SIZE;
385 loc[loc_size++] = '/';
387 /* store query parameters for later use */
388 query = coap_check_option(request, COAP_OPTION_URI_QUERY, &opt_iter);
391 parse_param((unsigned char *) "h", 1, COAP_OPT_VALUE(query), COAP_OPT_LENGTH(query), &h);
392 parse_param((unsigned char *) "ins", 3, COAP_OPT_VALUE(query), COAP_OPT_LENGTH(query),
394 parse_param((unsigned char *) "lt", 2, COAP_OPT_VALUE(query), COAP_OPT_LENGTH(query), <);
395 parse_param((unsigned char *) "rt", 2, COAP_OPT_VALUE(query), COAP_OPT_LENGTH(query), &rt);
399 { /* client has specified a node name */
400 memcpy(loc + loc_size, h.s, min(h.length, LOCSIZE - loc_size - 1));
401 loc_size += min(h.length, LOCSIZE - loc_size - 1);
403 if (ins.length && loc_size > 1)
405 loc[loc_size++] = '-';
406 memcpy((char *) (loc + loc_size), ins.s, min(ins.length, LOCSIZE - loc_size - 1));
407 loc_size += min(ins.length, LOCSIZE - loc_size - 1);
412 { /* generate node identifier */
413 loc_size += snprintf((char *) (loc + loc_size), LOCSIZE - loc_size - 1, "%x",
420 loc[loc_size++] = '-';
421 memcpy((char *) (loc + loc_size), ins.s, min(ins.length, LOCSIZE - loc_size - 1));
422 loc_size += min(ins.length, LOCSIZE - loc_size - 1);
429 loc_size += snprintf((char *) (loc + loc_size), LOCSIZE - loc_size - 1, "-%x", now);
435 * - use lt to check expiration
438 r = coap_resource_init(loc, loc_size, COAP_RESOURCE_FLAGS_RELEASE_URI);
439 coap_register_handler(r, COAP_REQUEST_GET, hnd_get_resource);
440 coap_register_handler(r, COAP_REQUEST_PUT, hnd_put_resource);
441 coap_register_handler(r, COAP_REQUEST_DELETE, hnd_delete_resource);
445 buf = (unsigned char *) coap_malloc(ins.length + 2);
448 /* add missing quotes */
450 memcpy(buf + 1, ins.s, ins.length);
451 buf[ins.length + 1] = '"';
452 coap_add_attr(r, (unsigned char *) "ins", 3, buf, ins.length + 2,
453 COAP_ATTR_FLAGS_RELEASE_VALUE);
459 buf = (unsigned char *) coap_malloc(rt.length + 2);
462 /* add missing quotes */
464 memcpy(buf + 1, rt.s, rt.length);
465 buf[rt.length + 1] = '"';
466 coap_add_attr(r, (unsigned char *) "rt", 2, buf, rt.length + 2,
467 COAP_ATTR_FLAGS_RELEASE_VALUE);
471 add_source_address(r, peer);
475 rd = make_rd(peer, request);
478 coap_hash_path(loc, loc_size, rd->key);
479 HASH_ADD(hh, resources, key, sizeof(coap_key_t), rd);
483 /* FIXME: send error response and delete r */
487 coap_add_resource(ctx, r);
489 /* create response */
491 response->hdr->code = COAP_RESPONSE_CODE(201);
493 { /* split path into segments and add Location-Path options */
494 unsigned char _b[LOCSIZE];
495 unsigned char *b = _b;
496 size_t buflen = sizeof(_b);
499 nseg = coap_split_path(loc, loc_size, b, &buflen);
502 coap_add_option(response, COAP_OPTION_LOCATION_PATH, COAP_OPT_LENGTH(b),
504 b += COAP_OPT_SIZE(b);
509 void init_resources(coap_context_t *ctx)
513 r = coap_resource_init(RD_ROOT_STR, RD_ROOT_SIZE, 0);
514 coap_register_handler(r, COAP_REQUEST_GET, hnd_get_rd);
515 coap_register_handler(r, COAP_REQUEST_POST, hnd_post_rd);
517 coap_add_attr(r, (unsigned char *) "ct", 2, (unsigned char *) "40", 2, 0);
518 coap_add_attr(r, (unsigned char *) "rt", 2, (unsigned char *) "\"core-rd\"", 9, 0);
519 coap_add_attr(r, (unsigned char *) "ins", 2, (unsigned char *) "\"default\"", 9, 0);
521 coap_add_resource(ctx, r);
525 void usage(const char *program, const char *version)
529 p = strrchr(program, '/');
533 fprintf(stderr, "%s v%s -- CoRE Resource Directory implementation\n"
534 "(c) 2011-2012 Olaf Bergmann <bergmann@tzi.org>\n\n"
535 "usage: %s [-A address] [-p port]\n\n"
536 "\t-A address\tinterface address to bind to\n"
537 "\t-p port\t\tlisten on specified port\n"
538 "\t-v num\t\tverbosity level (default: 3)\n", program, version, program);
542 get_context(const char *node, const char *port)
544 coap_context_t *ctx = NULL;
546 struct addrinfo hints;
547 struct addrinfo *result, *rp;
549 memset(&hints, 0, sizeof(struct addrinfo));
550 hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
551 hints.ai_socktype = SOCK_DGRAM; /* Coap uses UDP */
552 hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
554 s = getaddrinfo(node, port, &hints, &result);
557 fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
561 /* iterate through results until success */
562 for (rp = result; rp != NULL; rp = rp->ai_next)
566 if (rp->ai_addrlen <= sizeof(addr.addr))
568 coap_address_init(&addr);
569 addr.size = rp->ai_addrlen;
570 memcpy(&addr.addr, rp->ai_addr, rp->ai_addrlen);
572 ctx = coap_new_context(&addr);
575 /* TODO: output address:port for successful binding */
581 fprintf(stderr, "no context available for interface '%s'\n", node);
583 finish: freeaddrinfo(result);
587 int join(coap_context_t *ctx, char *group_name)
589 struct ipv6_mreq mreq;
590 struct addrinfo *reslocal = NULL, *resmulti = NULL, hints, *ainfo;
593 /* we have to resolve the link-local interface to get the interface id */
594 memset(&hints, 0, sizeof(hints));
595 hints.ai_family = AF_INET6;
596 hints.ai_socktype = SOCK_DGRAM;
598 result = getaddrinfo("::", NULL, &hints, &reslocal);
601 perror("join: cannot resolve link-local interface");
605 /* get the first suitable interface identifier */
606 for (ainfo = reslocal; ainfo != NULL; ainfo = ainfo->ai_next)
608 if (ainfo->ai_family == AF_INET6)
610 mreq.ipv6mr_interface = ((struct sockaddr_in6 *) ainfo->ai_addr)->sin6_scope_id;
615 memset(&hints, 0, sizeof(hints));
616 hints.ai_family = AF_INET6;
617 hints.ai_socktype = SOCK_DGRAM;
619 /* resolve the multicast group address */
620 result = getaddrinfo(group_name, NULL, &hints, &resmulti);
624 perror("join: cannot resolve multicast address");
628 for (ainfo = resmulti; ainfo != NULL; ainfo = ainfo->ai_next)
630 if (ainfo->ai_family == AF_INET6)
632 mreq.ipv6mr_multiaddr = ((struct sockaddr_in6 *) ainfo->ai_addr)->sin6_addr;
637 result = setsockopt(ctx->sockfd, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *) &mreq, sizeof(mreq));
639 perror("join: setsockopt");
641 finish: freeaddrinfo(resmulti);
642 freeaddrinfo(reslocal);
647 int main(int argc, char **argv)
651 struct timeval tv, *timeout;
654 coap_queue_t *nextpdu;
655 char addr_str[NI_MAXHOST] = "::";
656 char port_str[NI_MAXSERV] = "5683";
659 coap_log_t log_level = LOG_WARNING;
661 while ((opt = getopt(argc, argv, "A:g:p:v:")) != -1)
666 strncpy(addr_str, optarg, NI_MAXHOST - 1);
667 addr_str[NI_MAXHOST - 1] = '\0';
673 strncpy(port_str, optarg, NI_MAXSERV - 1);
674 port_str[NI_MAXSERV - 1] = '\0';
677 log_level = strtol(optarg, NULL, 10);
680 usage(argv[0], PACKAGE_VERSION);
685 coap_set_log_level(log_level);
687 ctx = get_context(addr_str, port_str);
696 signal(SIGINT, handle_sigint);
701 FD_SET(ctx->sockfd, &readfds);
703 nextpdu = coap_peek_next(ctx);
706 while (nextpdu && nextpdu->t <= now)
708 coap_retransmit(ctx, coap_pop_next(ctx));
709 nextpdu = coap_peek_next(ctx);
712 if (nextpdu && nextpdu->t <= now + COAP_RESOURCE_CHECK_TIME)
714 /* set timeout if there is a pdu to send before our automatic timeout occurs */
715 tv.tv_usec = ((nextpdu->t - now) % COAP_TICKS_PER_SECOND) * 1000000
716 / COAP_TICKS_PER_SECOND;
717 tv.tv_sec = (nextpdu->t - now) / COAP_TICKS_PER_SECOND;
723 tv.tv_sec = COAP_RESOURCE_CHECK_TIME;
726 result = select(FD_SETSIZE, &readfds, 0, 0, timeout);
734 { /* read from socket */
735 if (FD_ISSET(ctx->sockfd, &readfds))
737 coap_read(ctx); /* read received data */
738 coap_dispatch(ctx); /* and dispatch PDUs from receivequeue */
743 /* coap_check_resource_list( ctx ); */
747 coap_free_context(ctx);