diff --git a/uri.c b/uri.c index 6cea5898..5f0d19ef 100644 --- a/uri.c +++ b/uri.c @@ -6,6 +6,7 @@ * README for terms of use. */ +#include #include #include #include @@ -16,8 +17,27 @@ #include "option.h" #include "uri.h" +/** + * A length-safe version of strchr(). This function returns a pointer + * to the first occurrence of @p c in @p s, or @c NULL if not found. + * + * @param s The string to search for @p c. + * @param len The length of @p s. + * @param c The character to search. + * + * @return A pointer to the first occurence of @p c, or @c NULL + * if not found. + */ +static inline unsigned char * +strnchr(unsigned char *s, size_t len, unsigned char c) { + while (len && *s++ != c) + --len; + + return len ? s : NULL; +} + int -coap_split_uri(const unsigned char *str_var, size_t len, coap_uri_t *uri) { +coap_split_uri(unsigned char *str_var, size_t len, coap_uri_t *uri) { unsigned char *p, *q; int secure = 0, res = 0; @@ -167,7 +187,7 @@ coap_split_uri(const unsigned char *str_var, size_t len, coap_uri_t *uri) { * @param buf The result buffer. */ void -decode_segment(unsigned char *seg, size_t length, unsigned char *buf) { +decode_segment(const unsigned char *seg, size_t length, unsigned char *buf) { while (length--) { @@ -189,7 +209,7 @@ decode_segment(unsigned char *seg, size_t length, unsigned char *buf) { * or the length of @p s when decoded. */ int -check_segment(unsigned char *s, size_t length) { +check_segment(const unsigned char *s, size_t length) { size_t n = 0; @@ -223,9 +243,12 @@ check_segment(unsigned char *s, size_t length) { * @param buflen The maximum size of @p buf. * * @return The option's size, or @c -1 on error. + * + * @bug This function does not split segments that are bigger than 270 + * bytes. */ int -make_decoded_option(unsigned char *s, size_t length, +make_decoded_option(const unsigned char *s, size_t length, unsigned char *buf, size_t buflen) { int res; @@ -238,15 +261,104 @@ make_decoded_option(unsigned char *s, size_t length, return -1; } - COAP_OPT_SETDELTA(*(coap_opt_t *)buf, 0); - COAP_OPT_SETLENGTH(*(coap_opt_t *)buf, res); + COAP_OPT_SETDELTA(buf, 0); + COAP_OPT_SETLENGTH(buf, res); - decode_segment(s, length, - COAP_OPT_VALUE(*(coap_opt_t *)buf)); + decode_segment(s, length, COAP_OPT_VALUE(buf)); - return COAP_OPT_SIZE(*(coap_opt_t *)buf); + return COAP_OPT_SIZE(buf); } + +#ifndef min +#define min(a,b) ((a) < (b) ? (a) : (b)) +#endif + +typedef void (*segment_handler_t)(unsigned char *, size_t, void *); + +/** + * Splits the given string into segments. You should call one of the + * macros coap_split_path() or coap_split_query() instead. + * + * @param parse_iter The iterator used for tokenizing. + * @param h A handler that is called with every token. + * @param data Opaque data that is passed to @p h when called. + * + * @return The number of characters that have been parsed from @p s. + */ +size_t +coap_split_path_impl(coap_parse_iterator_t *parse_iter, + segment_handler_t h, void *data) { + unsigned char *seg; + size_t length; + + assert(parse_iter); + assert(h); + + length = parse_iter->n; + + while ( (seg = coap_parse_next(parse_iter)) ) { + + /* any valid path segment is handled here: */ + h(seg, parse_iter->segment_length, data); + } + + return length - (parse_iter->n - parse_iter->segment_length); +} + +struct cnt_str { + str buf; + int n; +}; + +void +write_option(unsigned char *s, size_t len, void *data) { + struct cnt_str *state = (struct cnt_str *)data; + int res; + assert(state); + + /* skip empty segments and those that consist of only one or two dots */ + if (memcmp(s, "..", min(len,2)) == 0) + return; + + 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_parse_iterator_t pi; + + coap_parse_iterator_init((unsigned char *)s, length, + '/', (unsigned char *)"?#", 2, &pi); + coap_split_path_impl(&pi, write_option, &tmp); + + *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 }; + coap_parse_iterator_t pi; + + coap_parse_iterator_init((unsigned char *)s, length, + '&', (unsigned char *)"#", 1, &pi); + + coap_split_path_impl(&pi, write_option, &tmp); + + *buflen = tmp.buf.length; + return tmp.n; +} + +#if 0 int coap_split_path_impl(unsigned char *s, size_t length, int is_path, unsigned char *buf, size_t buflen) { @@ -298,6 +410,7 @@ coap_split_path_impl(unsigned char *s, size_t length, int is_path, debug("invalid segment in URI\n"); return -1; } +#endif #define URI_DATA(uriobj) ((unsigned char *)(uriobj) + sizeof(coap_uri_t)) @@ -305,12 +418,6 @@ coap_uri_t * coap_new_uri(const unsigned char *uri, unsigned int length) { unsigned char *result; - /** - * @bug Some additional storage is needed to split path and query - * into segments. Unfortunately, we do not know in advance, how - * many segments we will get. A quick hack is to assume that every - * segment will have 4 characters in average and hope for the best. - */ result = coap_malloc(length + 1 + sizeof(coap_uri_t)); if (!result) @@ -366,3 +473,91 @@ coap_clone_uri(const coap_uri_t *uri) { return result; } + +/* hash URI path segments */ + +static inline void +hash_segment(unsigned char *s, size_t len, void *data) { + coap_hash(s, len, *(coap_key_t *)data); +} + +int +coap_hash_path(const unsigned char *path, size_t len, coap_key_t key) { + coap_parse_iterator_t pi; + + if (!path) + return 0; + + memset(key, 0, sizeof(coap_key_t)); + + coap_parse_iterator_init((unsigned char *)path, len, + '/', (unsigned char *)"?#", 2, &pi); + coap_split_path_impl(&pi, hash_segment, &key); + + return 1; +} + +/* iterator functions */ + +coap_parse_iterator_t * +coap_parse_iterator_init(unsigned char *s, size_t n, + unsigned char separator, + unsigned char *delim, size_t dlen, + coap_parse_iterator_t *pi) { + assert(s); + assert(pi); + assert(separator); + + if (!s || !n) + return NULL; + + pi->separator = separator; + pi->delim = delim; + pi->dlen = dlen; + pi->pos = s; + pi->n = n; + pi->segment_length = 0; + + return pi; +} + +unsigned char * +coap_parse_next(coap_parse_iterator_t *pi) { + unsigned char *p; + + if (!pi) + return NULL; + + /* proceed to the next segment */ + pi->n -= pi->segment_length; + pi->pos += pi->segment_length; + pi->segment_length = 0; + + /* last segment? */ + if (!pi->n || strnchr(pi->delim, pi->dlen, *pi->pos)) { + pi->pos = NULL; + return NULL; + } + + /* skip following separator (the first segment might not have one) */ + if (*pi->pos == pi->separator) { + ++pi->pos; + --pi->n; + } + + p = pi->pos; + + while (pi->segment_length < pi->n && *p != pi->separator && + !strnchr(pi->delim, pi->dlen, *p)) { + ++p; + ++pi->segment_length; + } + + if (!pi->n) { + pi->pos = NULL; + pi->segment_length = 0; + } + + return pi->pos; +} + diff --git a/uri.h b/uri.h index a70f609d..474d9ef0 100644 --- a/uri.h +++ b/uri.h @@ -9,6 +9,7 @@ #ifndef _COAP_URI_H_ #define _COAP_URI_H_ +#include "hashkey.h" #include "str.h" /** Representation of parsed URI. Components may be filled from a @@ -22,6 +23,98 @@ typedef struct { str query; /**< The query part if present */ } coap_uri_t; +/** + * Creates a new coap_uri_t object from the specified URI. Returns the new + * object or NULL on error. The memory allocated by the new coap_uri_t + * must be released using coap_free(). + * @param uri The URI path to copy. + * @para length The length of uri. + * + * @return New URI object or NULL on error. + */ +coap_uri_t *coap_new_uri(const unsigned char *uri, unsigned int length); + +/** + * Clones the specified coap_uri_t object. Thie function allocates sufficient + * memory to hold the coap_uri_t structure and its contents. The object must + * be released with coap_free(). */ +coap_uri_t *coap_clone_uri(const coap_uri_t *uri); + +/** + * Calculates a hash over the given path and stores the result in + * @p key. This function returns @c 0 on error or @c 1 on success. + * + * @param path The URI path to generate hash for. + * @param len The length of @p path. + * @param key The output buffer. + * + * @return @c 1 if @p key was set, @c 0 otherwise. + */ +int coap_hash_path(const unsigned char *path, size_t len, coap_key_t key); + +/** + * @defgroup uri_parse URI Parsing Functions + * + * CoAP PDUs contain normalized URIs with their path and query split into + * multiple segments. The functions in this module help splitting strings. + * @{ + */ + +/** + * Iterator to for tokenizing a URI path or query. This structure must + * be initialized with coap_parse_iterator_init(). Call + * coap_parse_next() to walk through the tokens. + * + * @code + * unsigned char *token; + * coap_parse_iterator_t pi; + * coap_parse_iterator_init(uri.path.s, uri.path.length, '/', "?#", 2, &pi); + * + * while ((token = coap_parse_next(&pi))) { + * ... do something with token ... + * } + * @endcode + */ +typedef struct { + size_t n; /**< number of remaining characters in buffer */ + unsigned char separator; /**< segment separators */ + unsigned char *delim; /**< delimiters where to split the string */ + size_t dlen; /**< length of separator */ + unsigned char *pos; /**< current position in buffer */ + size_t segment_length; /**< length of current segment */ +} coap_parse_iterator_t; + +/** + * Initializes the given iterator @p pi. + * + * @param s The string to tokenize. + * @param n The length of @p s. + * @param separator The separator character that delimits tokens. + * @param delim A set of characters that delimit @s. + * @param dlen The length of @p delim. + * @param pi The iterator object to initialize. + * + * @return The initialized iterator object @p pi. + */ +coap_parse_iterator_t * +coap_parse_iterator_init(unsigned char *s, size_t n, + unsigned char separator, + unsigned char *delim, size_t dlen, + coap_parse_iterator_t *pi); + +/** + * Updates the iterator @p pi to point to the next token. This + * function returns a pointer to that token or @c NULL if no more + * tokens exist. The contents of @p pi will be updated. In particular, + * @c pi->segment_length specifies the length of the current token, @c + * pi->pos points to its beginning. + * + * @param pi The iterator to update. + * + * @return The next token or @c NULL if no more tokens exist. + */ +unsigned char *coap_parse_next(coap_parse_iterator_t *pi); + /** * Parses a given string into URI components. The identified syntactic * components are stored in the result parameter @p uri. Optional URI @@ -34,69 +127,41 @@ typedef struct { * @param len The actual length of @p str_var * @param uri The coap_uri_t object to store the result. * @return @c 0 on success, or < 0 on error. + * + * @note The host name part will be converted to lower case by this + * function. */ int -coap_split_uri(const unsigned char *str_var, size_t len, coap_uri_t *uri); - -/** - * Splits the given string into segments. You should call one of the - * macros coap_split_path() or coap_split_query() instead. - * - * @param s The string to split. - * @param is_path Set to @c 1 to split path segments, @c 0 for query segments. - * @param length The actual length of @p uri. - * @param buf Result buffer for parsed segments. - * @param buflen Maximum length of @p buf. - * - * @return The number of segments created or @c -1 on error. - */ -int coap_split_path_impl(unsigned char *s, size_t length, int is_path, - unsigned char *buf, size_t buflen); +coap_split_uri(unsigned char *str_var, size_t len, coap_uri_t *uri); /** * Splits the given URI path into segments. * - * @param Path The path string to split. - * @param Length The actual length of @p Path. - * @param Buf Result buffer for parsed segments. - * @param Buflen Maximum length of @p Buf. + * @param s The path string to split. + * @param length The actual length of @p s. + * @param buf Result buffer for parsed segments. + * @param buflen Maximum length of @p buf. Will be set to the actual number + * of bytes written into buf on success. * * @return The number of segments created or @c -1 on error. */ -#define coap_split_path(Path, Length, Buf, Buflen) \ - coap_split_path_impl((Path), (Length), 1, (Buf), (Buflen)) +int coap_split_path(const unsigned char *s, size_t length, + unsigned char *buf, size_t *buflen); /** * Splits the given URI query into segments. * - * @param Query The query string to split. - * @param Length The actual length of @p Query. - * @param Buf Result buffer for parsed segments. - * @param Buflen Maximum length of @p Buf. + * @param s The query string to split. + * @param length The actual length of @p s. + * @param buf Result buffer for parsed segments. + * @param buflen Maximum length of @p buf. Will be set to the actual number + * of bytes written into buf on success. * * @return The number of segments created or @c -1 on error. */ -#define coap_split_query(Query, Length, Buf, Buflen) \ - coap_split_path_impl((Query), (Length), 0, (Buf), (Buflen)) +int coap_split_query(const unsigned char *s, size_t length, + unsigned char *buf, size_t *buflen); -/** - * Creates a new coap_uri_t object from the specified URI. Returns the new - * object or NULL on error. The memory allocated by the new coap_uri_t - * must be released using coap_free(). - * @param uri The URI path to copy. - * @para length The length of uri. - * @return New URI object or NULL on error. - * - * @depreated This function has inconvenient storage allocation - * characteristics to split URI path and query. Better do that - * manually. - */ -coap_uri_t *coap_new_uri(const unsigned char *uri, unsigned int length); - -/** - * Clones the specified coap_uri_t object. Thie function allocates sufficient - * memory to hold the coap_uri_t structure and its contents. The object must - * be released with coap_free(). */ -coap_uri_t *coap_clone_uri(const coap_uri_t *uri); +/** @} */ #endif /* _COAP_URI_H_ */