summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
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__ */