123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468 |
- /* uri.c -- helper functions for URI treatment
- */
- #include "c_stdio.h"
- #include "c_stdlib.h"
- #include "c_string.h"
- #include "c_ctype.h"
- #include "coap.h"
- #include "uri.h"
- #ifndef assert
- // #warning "assertions are disabled"
- # define assert(x) do { \
- if(!x) NODE_ERR("uri.c assert!\n"); \
- } while (0)
- #endif
- /**
- * 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(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;
- c_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; --len;
- }
- 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;
- --len;
- 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(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;
- }
- }
- /**
- * 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(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;
- }
- /**
- * 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.
- *
- * @bug This function does not split segments that are bigger than 270
- * bytes.
- */
- int make_decoded_option(const unsigned char *s, size_t length,
- unsigned char *buf, size_t buflen) {
- int res;
- size_t written;
- if (!buflen) {
- NODE_DBG("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);
- written = coap_buildOptionHeader(0, res, buf, buflen);
- assert(written <= buflen);
- if (!written) /* encoding error */
- return -1;
- buf += written; /* advance past option type/length */
- buflen -= written;
- if (buflen < (size_t)res) {
- NODE_DBG("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 *);
- /**
- * 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 pkt_scr {
- coap_packet_t *pkt;
- coap_rw_buffer_t *scratch;
- int n;
- };
- void write_option(unsigned char *s, size_t len, void *data) {
- struct pkt_scr *state = (struct pkt_scr *)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 = check_segment(s, len);
- if (res < 0){
- NODE_DBG("not a valid segment\n");
- return;
- }
- if (state->scratch->len < (size_t)res) {
- NODE_DBG("buffer too small for option\n");
- return;
- }
- decode_segment(s, len, state->scratch->p);
- if (res > 0) {
- state->pkt->opts[state->pkt->numopts].buf.p = state->scratch->p;
- state->pkt->opts[state->pkt->numopts].buf.len = res;
- state->scratch->p += res;
- state->scratch->len -= res;
- state->pkt->numopts++;
- state->n++;
- }
- }
- int coap_split_path(coap_rw_buffer_t *scratch, coap_packet_t *pkt, const unsigned char *s, size_t length) {
- struct pkt_scr tmp = { pkt, scratch, 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);
- int i;
- for(i=0;i<tmp.n;i++){
- pkt->opts[pkt->numopts - i - 1].num = COAP_OPTION_URI_PATH;
- }
- return tmp.n;
- }
- int coap_split_query(coap_rw_buffer_t *scratch, coap_packet_t *pkt, const unsigned char *s, size_t length) {
- struct pkt_scr tmp = { pkt, scratch, 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);
- int i;
- for(i=0;i<tmp.n;i++){
- pkt->opts[pkt->numopts - i - 1].num = COAP_OPTION_URI_QUERY;
- }
- 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 = (unsigned char *)c_malloc(length + 1 + sizeof(coap_uri_t));
- if (!result)
- return NULL;
- c_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) {
- c_free(result);
- return NULL;
- }
- return (coap_uri_t *)result;
- }
- /* 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(pi);
- assert(separator);
- 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;
- }
|