diff options
-rw-r--r-- | lib/iot/zoap/Kbuild | 2 | ||||
-rw-r--r-- | lib/iot/zoap/link-format.c | 312 | ||||
-rw-r--r-- | lib/iot/zoap/link-format.h | 52 |
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__ */ |