uri.c
Go to the documentation of this file.
/* uri.c -- helper functions for URI treatment
*
* Copyright (C) 2010--2012,2015-2016 Olaf Bergmann <bergmann@tzi.org>
*
* This file is part of the CoAP library libcoap. Please see
* README for terms of use.
*/
#include "coap_config.h"
#if defined(HAVE_ASSERT_H) && !defined(assert)
# include <assert.h>
#endif
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "mem.h"
#include "debug.h"
#include "pdu.h"
#include "option.h"
#include "uri.h"
static inline unsigned char *
strnchr(unsigned char *s, size_t len, unsigned char c) {
while (len && *s++ != c)
--len;
return len ? s : NULL;
}
#define ISEQUAL_CI(a,b) \
((a) == (b) || (islower(b) && ((a) == ((b) - 0x20))))
int
coap_split_uri(const unsigned char *str_var, size_t len, coap_uri_t *uri) {
const unsigned char *p, *q;
int secure = 0, res = 0;
if (!str_var || !uri)
return -1;
memset(uri, 0, sizeof(coap_uri_t));
uri->port = COAP_DEFAULT_PORT;
/* search for scheme */
p = str_var;
if (*p == '/') {
q = p;
goto path;
}
q = (unsigned char *)COAP_DEFAULT_SCHEME;
while (len && *q && ISEQUAL_CI(*p, *q)) {
++p; ++q; --len;
}
/* If q does not point to the string end marker '\0', the schema
* identifier is wrong. */
if (*q) {
res = -1;
goto error;
}
/* There might be an additional 's', indicating the secure version: */
if (len && (secure = *p == 's')) {
++p; --len;
}
q = (unsigned char *)"://";
while (len && *q && *p == *q) {
++p; ++q; --len;
}
if (*q) {
res = -2;
goto error;
}
/* p points to beginning of Uri-Host */
q = p;
if (len && *p == '[') { /* IPv6 address reference */
++p;
while (len && *q != ']') {
++q; --len;
}
if (!len || *q != ']' || p == q) {
res = -3;
goto error;
}
COAP_SET_STR(&uri->host, q - p, (unsigned char *)p);
++q; --len;
} else { /* IPv4 address or FQDN */
while (len && *q != ':' && *q != '/' && *q != '?') {
++q;
--len;
}
if (p == q) {
res = -3;
goto error;
}
COAP_SET_STR(&uri->host, q - p, (unsigned char *)p);
}
/* check for Uri-Port */
if (len && *q == ':') {
p = ++q;
--len;
while (len && isdigit(*q)) {
++q;
--len;
}
if (p < q) { /* explicit port number given */
int uri_port = 0;
while (p < q)
uri_port = uri_port * 10 + (*p++ - '0');
/* check if port number is in allowed range */
if (uri_port > 65535) {
res = -4;
goto error;
}
uri->port = uri_port;
}
}
path: /* at this point, p must point to an absolute path */
if (!len)
goto end;
if (*q == '/') {
p = ++q;
--len;
while (len && *q != '?') {
++q;
--len;
}
if (p < q) {
COAP_SET_STR(&uri->path, q - p, (unsigned char *)p);
p = q;
}
}
/* Uri_Query */
if (len && *p == '?') {
++p;
--len;
COAP_SET_STR(&uri->query, len, (unsigned char *)p);
len = 0;
}
end:
return len ? -1 : 0;
error:
return res;
}
#define hexchar_to_dec(c) ((c) & 0x40 ? ((c) & 0x0F) + 9 : ((c) & 0x0F))
static void
decode_segment(const unsigned char *seg, size_t length, unsigned char *buf) {
while (length--) {
if (*seg == '%') {
*buf = (hexchar_to_dec(seg[1]) << 4) + hexchar_to_dec(seg[2]);
seg += 2; length -= 2;
} else {
*buf = *seg;
}
++buf; ++seg;
}
}
static int
check_segment(const unsigned char *s, size_t length) {
size_t n = 0;
while (length) {
if (*s == '%') {
if (length < 2 || !(isxdigit(s[1]) && isxdigit(s[2])))
return -1;
s += 2;
length -= 2;
}
++s; ++n; --length;
}
return n;
}
static int
make_decoded_option(const unsigned char *s, size_t length,
unsigned char *buf, size_t buflen) {
int res;
size_t written;
if (!buflen) {
debug("make_decoded_option(): buflen is 0!\n");
return -1;
}
res = check_segment(s, length);
if (res < 0)
return -1;
/* write option header using delta 0 and length res */
written = coap_opt_setheader(buf, buflen, 0, res);
assert(written <= buflen);
if (!written) /* encoding error */
return -1;
buf += written; /* advance past option type/length */
buflen -= written;
if (buflen < (size_t)res) {
debug("buffer too small for option\n");
return -1;
}
decode_segment(s, length, buf);
return written + res;
}
#ifndef min
#define min(a,b) ((a) < (b) ? (a) : (b))
#endif
typedef void (*segment_handler_t)(unsigned char *, size_t, void *);
static inline int
dots(unsigned char *s, size_t len) {
return *s == '.' && (len == 1 || (*(s+1) == '.' && len == 2));
}
static size_t
coap_split_path_impl(const unsigned char *s, size_t length,
segment_handler_t h, void *data) {
const unsigned char *p, *q;
p = q = s;
while (length > 0 && !strnchr((unsigned char *)"?#", 2, *q)) {
if (*q == '/') { /* start new segment */
if (!dots((unsigned char *)p, q - p)) {
h((unsigned char *)p, q - p, data);
}
p = q + 1;
}
q++;
length--;
}
/* write last segment */
if (!dots((unsigned char *)p, q - p)) {
h((unsigned char *)p, q - p, data);
}
return q - s;
}
struct cnt_str {
str buf;
int n;
};
static void
write_option(unsigned char *s, size_t len, void *data) {
struct cnt_str *state = (struct cnt_str *)data;
int res;
assert(state);
res = make_decoded_option(s, len, state->buf.s, state->buf.length);
if (res > 0) {
state->buf.s += res;
state->buf.length -= res;
state->n++;
}
}
int
coap_split_path(const unsigned char *s, size_t length,
unsigned char *buf, size_t *buflen) {
struct cnt_str tmp = { { *buflen, buf }, 0 };
coap_split_path_impl(s, length, write_option, &tmp);
*buflen = *buflen - tmp.buf.length;
return tmp.n;
}
int
coap_split_query(const unsigned char *s, size_t length,
unsigned char *buf, size_t *buflen) {
struct cnt_str tmp = { { *buflen, buf }, 0 };
const unsigned char *p;
p = s;
while (length > 0 && *s != '#') {
if (*s == '&') { /* start new query element */
write_option((unsigned char *)p, s - p, &tmp);
p = s + 1;
}
s++;
length--;
}
/* write last query element */
write_option((unsigned char *)p, s - p, &tmp);
*buflen = *buflen - tmp.buf.length;
return tmp.n;
}
#define URI_DATA(uriobj) ((unsigned char *)(uriobj) + sizeof(coap_uri_t))
coap_uri_t *
coap_new_uri(const unsigned char *uri, unsigned int length) {
unsigned char *result;
result = coap_malloc(length + 1 + sizeof(coap_uri_t));
if (!result)
return NULL;
memcpy(URI_DATA(result), uri, length);
URI_DATA(result)[length] = '\0'; /* make it zero-terminated */
if (coap_split_uri(URI_DATA(result), length, (coap_uri_t *)result) < 0) {
coap_free(result);
return NULL;
}
return (coap_uri_t *)result;
}
coap_uri_t *
coap_clone_uri(const coap_uri_t *uri) {
coap_uri_t *result;
if ( !uri )
return NULL;
result = (coap_uri_t *)coap_malloc( uri->query.length + uri->host.length +
uri->path.length + sizeof(coap_uri_t) + 1);
if ( !result )
return NULL;
memset( result, 0, sizeof(coap_uri_t) );
result->port = uri->port;
if ( uri->host.length ) {
result->host.s = URI_DATA(result);
result->host.length = uri->host.length;
memcpy(result->host.s, uri->host.s, uri->host.length);
}
if ( uri->path.length ) {
result->path.s = URI_DATA(result) + uri->host.length;
result->path.length = uri->path.length;
memcpy(result->path.s, uri->path.s, uri->path.length);
}
if ( uri->query.length ) {
result->query.s = URI_DATA(result) + uri->host.length + uri->path.length;
result->query.length = uri->query.length;
memcpy(result->query.s, uri->query.s, uri->query.length);
}
return result;
}
/* hash URI path segments */
/* The function signature of coap_hash() is different from
* segment_handler_t hence we use this wrapper as safe typecast. */
static inline void
hash_segment(unsigned char *s, size_t len, void *data) {
coap_hash(s, len, data);
}
int
coap_hash_path(const unsigned char *path, size_t len, coap_key_t key) {
if (!path)
return 0;
memset(key, 0, sizeof(coap_key_t));
coap_split_path_impl(path, len, hash_segment, key);
return 1;
}
segment_handler_t
void(* segment_handler_t)(unsigned char *, size_t, void *)
Definition: uri.c:306
coap_uri_t
Representation of parsed URI.
Definition: uri.h:20
coap_uri_t::query
str query
The query part if present.
Definition: uri.h:25
decode_segment
static void decode_segment(const unsigned char *seg, size_t length, unsigned char *buf)
Decodes percent-encoded characters while copying the string seg of size length to buf...
Definition: uri.c:205
coap_key_t
unsigned char coap_key_t[4]
Definition: hashkey.h:20
URI_DATA
#define URI_DATA(uriobj)
Definition: uri.c:411
write_option
static void write_option(unsigned char *s, size_t len, void *data)
Definition: uri.c:362
check_segment
static int check_segment(const unsigned char *s, size_t length)
Runs through the given path (or query) segment and checks if percent-encodings are correct...
Definition: uri.c:227
mem.h
coap_malloc
static void * coap_malloc(size_t size)
Wrapper function to coap_malloc_type() for backwards compatibility.
Definition: mem.h:70
debug.h
coap_uri_t::host
str host
host part of the URI
Definition: uri.h:21
coap_uri_t::path
str path
Beginning of the first path segment.
Definition: uri.h:23
cnt_str
Definition: uri.c:356
str::length
size_t length
Definition: str.h:16
uri.h
option.h
Helpers for handling options in CoAP PDUs.
coap_split_uri
int coap_split_uri(const unsigned char *str_var, size_t len, coap_uri_t *uri)
Parses a given string into URI components.
Definition: uri.c:48
coap_uri_t::port
unsigned short port
The port in host byte order.
Definition: uri.h:22
COAP_SET_STR
#define COAP_SET_STR(st, l, v)
Definition: str.h:20
cnt_str::buf
str buf
Definition: uri.c:357
debug
#define debug(...)
Definition: debug.h:66
strnchr
static unsigned char * strnchr(unsigned char *s, size_t len, unsigned char c)
A length-safe version of strchr().
Definition: uri.c:37
ISEQUAL_CI
#define ISEQUAL_CI(a, b)
Definition: uri.c:44
hash_segment
static void hash_segment(unsigned char *s, size_t len, void *data)
Definition: uri.c:478
assert
#define assert(...)
Definition: mem.c:17
coap_new_uri
coap_uri_t * coap_new_uri(const unsigned char *uri, unsigned int length)
Creates a new coap_uri_t object from the specified URI.
Definition: uri.c:414
coap_split_path_impl
static size_t coap_split_path_impl(const unsigned char *s, size_t length, segment_handler_t h, void *data)
Splits the given string into segments.
Definition: uri.c:328
coap_split_path
int coap_split_path(const unsigned char *s, size_t length, unsigned char *buf, size_t *buflen)
Splits the given URI path into segments.
Definition: uri.c:376
coap_hash
#define coap_hash(String, Length, Result)
Definition: hashkey.h:34
COAP_DEFAULT_SCHEME
#define COAP_DEFAULT_SCHEME
Definition: pdu.h:31
COAP_DEFAULT_PORT
#define COAP_DEFAULT_PORT
Definition: pdu.h:24
pdu.h
Pre-defined constants that reflect defaults for CoAP.
dots
static int dots(unsigned char *s, size_t len)
Checks if path segment s consists of one or two dots.
Definition: uri.c:312
coap_hash_path
int coap_hash_path(const unsigned char *path, size_t len, coap_key_t key)
Calculates a hash over the given path and stores the result in key.
Definition: uri.c:483
str
Definition: str.h:15
coap_clone_uri
coap_uri_t * coap_clone_uri(const coap_uri_t *uri)
Clones the specified coap_uri_t object.
Definition: uri.c:433
coap_split_query
int coap_split_query(const unsigned char *s, size_t length, unsigned char *buf, size_t *buflen)
Splits the given URI query into segments.
Definition: uri.c:388
coap_free
static void coap_free(void *object)
Wrapper function to coap_free_type() for backwards compatibility.
Definition: mem.h:77
coap_opt_setheader
size_t coap_opt_setheader(coap_opt_t *opt, size_t maxlen, unsigned short delta, size_t length)
Encodes the given delta and length values into opt.
Definition: option.c:329
cnt_str::n
int n
Definition: uri.c:358
str::s
unsigned char * s
Definition: str.h:17
make_decoded_option
static int make_decoded_option(const unsigned char *s, size_t length, unsigned char *buf, size_t buflen)
Writes a coap option from given string s to buf.
Definition: uri.c:266
hexchar_to_dec
#define hexchar_to_dec(c)
Calculates decimal value from hexadecimal ASCII character given in c.
Definition: uri.c:190