mirror of
https://github.com/obgm/libcoap.git
synced 2026-11-12 11:40:39 +03:00
357 lines
7.5 KiB
C
357 lines
7.5 KiB
C
/* uri.c -- helper functions for URI treatment
|
|
*
|
|
* Copyright (C) 2010,2011 Olaf Bergmann <bergmann@tzi.org>
|
|
*
|
|
* This file is part of the CoAP library libcoap. Please see
|
|
* README for terms of use.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#include "mem.h"
|
|
#include "debug.h"
|
|
#include "pdu.h"
|
|
#include "option.h"
|
|
#include "uri.h"
|
|
|
|
int
|
|
coap_split_uri(unsigned char *str_var, size_t len, coap_uri_t *uri) {
|
|
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 && tolower(*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 = tolower(*p) == 's') )
|
|
++p;
|
|
|
|
q = (unsigned char *)"://";
|
|
while (len && *q && tolower(*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, p);
|
|
++q; --len;
|
|
} else { /* IPv4 address or FQDN */
|
|
while (len && *q != ':' && *q != '/' && *q != '?') {
|
|
*q = tolower(*q);
|
|
++q;
|
|
--len;
|
|
}
|
|
|
|
if (p == q) {
|
|
res = -3;
|
|
goto error;
|
|
}
|
|
|
|
COAP_SET_STR(&uri->host, q - p, 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');
|
|
|
|
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, p);
|
|
p = q;
|
|
}
|
|
}
|
|
|
|
/* Uri_Query */
|
|
if (len && *p == '?') {
|
|
++p;
|
|
COAP_SET_STR(&uri->query, len, p);
|
|
len = 0;
|
|
}
|
|
|
|
end:
|
|
return len ? -1 : 0;
|
|
|
|
error:
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Calculates decimal value from hexadecimal ASCII character given in
|
|
* @p c. The caller must ensure that @p c actually represents a valid
|
|
* heaxdecimal character, e.g. with isxdigit(3).
|
|
*
|
|
* @hideinitializer
|
|
*/
|
|
#define hexchar_to_dec(c) ((c) & 0x40 ? ((c) & 0x0F) + 9 : ((c) & 0x0F))
|
|
|
|
/**
|
|
* Decodes percent-encoded characters while copying the string @p seg
|
|
* of size @p length to @p buf. The caller of this function must
|
|
* ensure that the percent-encodings are correct (i.e. the character
|
|
* '%' is always followed by two hex digits. and that @p buf provides
|
|
* sufficient space to hold the result. This function is supposed to
|
|
* be called by make_decoded_option() only.
|
|
*
|
|
* @param seg The segment to decode and copy.
|
|
* @param length Length of @p seg.
|
|
* @param buf The result buffer.
|
|
*/
|
|
void
|
|
decode_segment(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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Runs through the given path (or query) segment and checks if
|
|
* percent-encodings are correct. This function returns @c -1 on error
|
|
* or the length of @p s when decoded.
|
|
*/
|
|
int
|
|
check_segment(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;
|
|
}
|
|
|
|
/**
|
|
* Writes a coap option from given string @p s to @p buf. @p s should
|
|
* point to a (percent-encoded) path or query segment of a coap_uri_t
|
|
* object. The created option will have type @c 0, and the length
|
|
* parameter will be set according to the size of the decoded string.
|
|
* On success, this function returns the option's size, or a value
|
|
* less than zero on error. This function must be called from
|
|
* coap_split_path_impl() only.
|
|
*
|
|
* @param s The string to decode.
|
|
* @param length The size of the percent-encoded string @p s.
|
|
* @param buf The buffer to store the new coap option.
|
|
* @param buflen The maximum size of @p buf.
|
|
*
|
|
* @return The option's size, or @c -1 on error.
|
|
*/
|
|
int
|
|
make_decoded_option(unsigned char *s, size_t length,
|
|
unsigned char *buf, size_t buflen) {
|
|
int res;
|
|
|
|
res = check_segment(s, length);
|
|
if (res < 0)
|
|
return -1;
|
|
|
|
if (buflen < sizeof(coap_opt_t) + res) {
|
|
debug("buffer too small for option\n");
|
|
return -1;
|
|
}
|
|
|
|
COAP_OPT_SETDELTA(*(coap_opt_t *)buf, 0);
|
|
COAP_OPT_SETLENGTH(*(coap_opt_t *)buf, res);
|
|
|
|
decode_segment(s, length,
|
|
COAP_OPT_VALUE(*(coap_opt_t *)buf));
|
|
|
|
return COAP_OPT_SIZE(*(coap_opt_t *)buf);
|
|
}
|
|
|
|
int
|
|
coap_split_path_impl(unsigned char *s, size_t length, int is_path,
|
|
unsigned char *buf, size_t buflen) {
|
|
unsigned char *p = s;
|
|
int n = 0, dots = 0;
|
|
int res;
|
|
|
|
if (!length)
|
|
return 0;
|
|
|
|
while (1) {
|
|
|
|
if ((is_path && *p == '/') || *p == '&') {
|
|
if (!dots || dots > 2 || dots != (p - s)) {
|
|
|
|
res = make_decoded_option(s, p - s, buf, buflen);
|
|
if (res < 0)
|
|
goto error;
|
|
|
|
++n;
|
|
|
|
buf += res;
|
|
buflen -= res;
|
|
}
|
|
|
|
s = p + 1;
|
|
dots = 0;
|
|
} else if (is_path && *p == '.') {
|
|
++dots;
|
|
}
|
|
|
|
++p;
|
|
--length;
|
|
if (!length) {
|
|
if (!dots || dots > 2 || dots != (p - s)) {
|
|
if (make_decoded_option(s, p - s, buf, buflen) < 0)
|
|
goto error;
|
|
|
|
return ++n;
|
|
} else
|
|
return n;
|
|
}
|
|
}
|
|
|
|
/* never reached */
|
|
|
|
error:
|
|
|
|
debug("invalid segment in URI\n");
|
|
return -1;
|
|
}
|
|
|
|
#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 = 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 */
|
|
|
|
coap_split_uri( URI_DATA(result), length, (coap_uri_t *)result );
|
|
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;
|
|
}
|