summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorVinicius Costa Gomes <vinicius.gomes@intel.com>2016-11-04 18:33:46 -0200
committerJukka Rissanen <jukka.rissanen@linux.intel.com>2016-12-02 12:41:05 +0200
commitac152ea8847148ff1dc54d5ae22efc910791a44b (patch)
tree40bf0e4477812d762490407626b55b7ed41b744c /lib
parentec184107befbdf2c0121974401f8b28746fba948 (diff)
iot/zoap: Add support for RFC6690 link format
RFC6690[1] defines a lightweight format for listing and querying resources and their relationships. The RFC defines an '.well-known/core' resource that will list the resources and their associated metadata. It also allows resources to filtered by their attributes. The implementation uses the fact that resources are organized in an array: only the resources present in the array after the "ZOAP_WELL_KNOWN_CORE_RESOURCE" will be considered, this allows a primitive form of visibility control for the attributes. [1] https://tools.ietf.org/html/rfc6690 Change-Id: I2bc21ddf45f20e1f749d8ac36d247474fdaa9d64 Signed-off-by: Vinicius Costa Gomes <vinicius.gomes@intel.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/iot/zoap/Kbuild2
-rw-r--r--lib/iot/zoap/link-format.c312
-rw-r--r--lib/iot/zoap/link-format.h52
3 files changed, 365 insertions, 1 deletions
diff --git a/lib/iot/zoap/Kbuild b/lib/iot/zoap/Kbuild
index a427ac27c..c99bd352f 100644
--- a/lib/iot/zoap/Kbuild
+++ b/lib/iot/zoap/Kbuild
@@ -4,4 +4,4 @@ ccflags-y += -I${srctree}/net/ip/contiki/os/lib
ccflags-y += -I${srctree}/net/ip/contiki/os
ccflags-y += -I${srctree}/net/ip
-obj-y := zoap.o
+obj-y := zoap.o link-format.o
diff --git a/lib/iot/zoap/link-format.c b/lib/iot/zoap/link-format.c
new file mode 100644
index 000000000..b90586833
--- /dev/null
+++ b/lib/iot/zoap/link-format.c
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <misc/byteorder.h>
+#include <net/buf.h>
+#include <net/nbuf.h>
+
+#include <misc/printk.h>
+
+#include "zoap.h"
+#include "link-format.h"
+
+static int format_uri(const char * const *path, struct net_buf *buf)
+{
+ static const char prefix[] = "</";
+ const char * const *p;
+ char *str;
+
+ if (!path) {
+ return -EINVAL;
+ }
+
+ str = net_buf_add(buf, sizeof(prefix) - 1);
+ strncpy(str, prefix, sizeof(prefix) - 1);
+
+ for (p = path; p && *p; ) {
+ uint16_t path_len = strlen(*p);
+
+ str = net_buf_add(buf, path_len);
+ strncpy(str, *p, path_len);
+
+ p++;
+
+ if (*p) {
+ str = net_buf_add(buf, 1);
+ *str = '/';
+ }
+ }
+
+ str = net_buf_add(buf, 1);
+ *str = '>';
+
+ return 0;
+}
+
+static int format_attributes(const char * const *attributes,
+ struct net_buf *buf)
+{
+ const char * const *attr;
+ char *str;
+
+ if (!attributes) {
+ goto terminator;
+ }
+
+ for (attr = attributes; attr && *attr; ) {
+ int attr_len = strlen(*attr);
+
+ str = net_buf_add(buf, attr_len);
+ strncpy(str, *attr, attr_len);
+
+ attr++;
+
+ if (*attr) {
+ str = net_buf_add(buf, 1);
+ *str = ';';
+ }
+ }
+
+terminator:
+ str = net_buf_add(buf, 1);
+ *str = ';';
+
+ return 0;
+}
+
+static int format_resource(const struct zoap_resource *resource,
+ struct net_buf *buf)
+{
+ struct zoap_core_metadata *meta = resource->user_data;
+ const char * const *attributes = NULL;
+ int r;
+
+ r = format_uri(resource->path, buf);
+ if (r < 0) {
+ return r;
+ }
+
+ if (meta && meta->attributes) {
+ attributes = meta->attributes;
+ }
+
+ r = format_attributes(attributes, buf);
+ if (r < 0) {
+ return r;
+ }
+
+ return r;
+}
+
+static bool match_path_uri(const char * const *path,
+ const char *uri, uint16_t len)
+{
+ const char * const *p = NULL;
+ int i, j, plen;
+
+ if (!path) {
+ return false;
+ }
+
+ if (len <= 1 || uri[0] != '/') {
+ return false;
+ }
+
+ p = path;
+ plen = *p ? strlen(*p) : 0;
+ j = 0;
+
+ if (plen == 0) {
+ return false;
+ }
+
+ for (i = 1; i < len; i++) {
+ if (!*p) {
+ return false;
+ }
+
+ if (!p) {
+ p++;
+ plen = *p ? strlen(*p) : 0;
+ j = 0;
+ }
+
+ if (j == plen && uri[i] == '/') {
+ p = NULL;
+ continue;
+ }
+
+ if (uri[i] == '*' && i + 1 == len) {
+ return true;
+ }
+
+ if (uri[i] != (*p)[j]) {
+ return false;
+ }
+
+ j++;
+ }
+
+ return true;
+}
+
+static bool match_attributes(const char * const *attributes,
+ const struct zoap_option *query)
+{
+ const char * const *attr;
+
+ /*
+ * FIXME: deal with the case when there are multiple options in a
+ * query, for example: 'rt=lux temperature', if I want to list
+ * resources with resource type lux or temperature.
+ */
+ for (attr = attributes; attr && *attr; attr++) {
+ uint16_t attr_len = strlen(*attr);
+
+ if (query->len != attr_len) {
+ continue;
+ }
+
+ if (!strncmp((char *) query->value, *attr, attr_len)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool match_queries_resource(const struct zoap_resource *resource,
+ const struct zoap_option *query,
+ int num_queries)
+{
+ struct zoap_core_metadata *meta = resource->user_data;
+ const char * const *attributes = NULL;
+ const int href_len = strlen("href");
+
+ if (num_queries == 0) {
+ return true;
+ }
+
+ if (meta && meta->attributes) {
+ attributes = meta->attributes;
+ }
+
+ if (!attributes) {
+ return false;
+ }
+
+ if (query->len > href_len + 1 &&
+ !strncmp(query->value, "href", href_len)) {
+ const char *uri = query->value + href_len + 1; /* href=... */
+ uint16_t uri_len = query->len - (href_len + 1);
+
+ return match_path_uri(resource->path, uri, uri_len);
+ }
+
+ return match_attributes(attributes, query);
+}
+
+int _zoap_well_known_core_get(struct zoap_resource *resource,
+ struct zoap_packet *request,
+ const struct sockaddr *from)
+{
+ struct net_context *context;
+ struct zoap_packet response;
+ struct zoap_option query;
+ struct net_buf *buf, *frag;
+ const uint8_t *token;
+ unsigned int num_queries;
+ uint16_t id;
+ uint8_t tkl, format = 40; /* application/link-format */
+ int r;
+
+ id = zoap_header_get_id(request);
+ token = zoap_header_get_token(request, &tkl);
+
+ /*
+ * Per RFC 6690, Section 4.1, only one (or none) query parameter may me
+ * provided, use the first if multiple.
+ */
+ r = zoap_find_options(request, ZOAP_OPTION_URI_QUERY, &query, 1);
+ if (r < 0) {
+ return r;
+ }
+
+ num_queries = r;
+
+ context = net_nbuf_context(request->buf);
+
+ buf = net_nbuf_get_tx(context);
+ if (!buf) {
+ return -ENOMEM;
+ }
+
+ frag = net_nbuf_get_data(context);
+ if (!frag) {
+ net_nbuf_unref(buf);
+ return -ENOMEM;
+ }
+
+ net_buf_frag_add(buf, frag);
+
+ r = zoap_packet_init(&response, buf);
+ if (r < 0) {
+ goto done;
+ }
+
+ /* FIXME: Could be that zoap_packet_init() sets some defaults */
+ zoap_header_set_version(&response, 1);
+ zoap_header_set_type(&response, ZOAP_TYPE_ACK);
+ zoap_header_set_code(&response, ZOAP_RESPONSE_CODE_CONTENT);
+ zoap_header_set_id(&response, id);
+ zoap_header_set_token(&response, token, tkl);
+
+ r = zoap_add_option(&response, ZOAP_OPTION_CONTENT_FORMAT,
+ &format, sizeof(format));
+ if (r < 0) {
+ return -EINVAL;
+ }
+
+ r = -ENOENT;
+
+ while (resource++ && resource->path) {
+ if (!match_queries_resource(resource, &query, num_queries)) {
+ continue;
+ }
+
+ frag = zoap_packet_get_buf(&response);
+
+ r = format_resource(resource, frag);
+ if (r < 0) {
+ goto done;
+ }
+ }
+
+done:
+ if (r < 0) {
+ zoap_header_set_code(&response, ZOAP_RESPONSE_CODE_BAD_REQUEST);
+ }
+
+ return net_context_sendto(buf, from, sizeof(struct sockaddr_in6),
+ NULL, 0, NULL, NULL);
+}
diff --git a/lib/iot/zoap/link-format.h b/lib/iot/zoap/link-format.h
new file mode 100644
index 000000000..d5e4f58ca
--- /dev/null
+++ b/lib/iot/zoap/link-format.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file
+ *
+ * @brief CoAP implementation for Zephyr.
+ */
+
+#ifndef __LINK_FORMAT_H__
+#define __LINK_FORMAT_H__
+
+#define _ZOAP_WELL_KNOWN_CORE_PATH \
+ ((const char * const[]) { ".well-known", "core", NULL })
+
+int _zoap_well_known_core_get(struct zoap_resource *resource,
+ struct zoap_packet *request,
+ const struct sockaddr *from);
+
+/**
+ * This resource should be added before all other resources that should be
+ * included in the responses of the .well-known/core resource.
+ */
+#define ZOAP_WELL_KNOWN_CORE_RESOURCE \
+ { .get = _zoap_well_known_core_get, \
+ .path = _ZOAP_WELL_KNOWN_CORE_PATH, \
+ }
+
+/**
+ * In case you want to add attributes to the resources included in the
+ * 'well-known/core' "virtual" resource, the 'user_data' field should point
+ * to a valid zoap_core_metadata structure.
+ */
+struct zoap_core_metadata {
+ const char * const *attributes;
+ void *user_data;
+};
+
+#endif /* __LINK_FORMAT_H__ */