Coverage Report

Created: 2026-09-01 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libcoap/src/coap_uri.c
Line
Count
Source
1
/* coap_uri.c -- helper functions for URI treatment
2
 *
3
 * Copyright (C) 2010--2012,2015-2016,2022-2026 Olaf Bergmann <bergmann@tzi.org>
4
 *
5
 * SPDX-License-Identifier: BSD-2-Clause
6
 *
7
 * This file is part of the CoAP library libcoap. Please see
8
 * README for terms of use.
9
 */
10
11
/**
12
 * @file coap_uri.c
13
 * @brief URI handling functions
14
 */
15
16
#include "coap3/coap_libcoap_build.h"
17
18
#if defined(HAVE_LIMITS_H)
19
#include <limits.h>
20
#endif
21
22
#include <stdint.h>
23
#include <stdio.h>
24
#include <string.h>
25
#include <ctype.h>
26
27
#ifdef _WIN32
28
#define strcasecmp _stricmp
29
#define strncasecmp _strnicmp
30
#endif
31
32
coap_upa_chain_t *coap_upa_client_fallback_chain = NULL;
33
coap_upa_chain_t *coap_upa_server_mapping_chain = NULL;
34
35
/**
36
 * A length-safe version of strchr(). This function returns a pointer
37
 * to the first occurrence of @p c  in @p s, or @c NULL if not found.
38
 *
39
 * @param s   The string to search for @p c.
40
 * @param len The length of @p s.
41
 * @param c   The character to search.
42
 *
43
 * @return A pointer to the first occurrence of @p c, or @c NULL
44
 * if not found.
45
 */
46
COAP_STATIC_INLINE const uint8_t *
47
0
strnchr(const uint8_t *s, size_t len, unsigned char c) {
48
0
  while (len && *s++ != c)
49
0
    --len;
50
51
0
  return len ? s : NULL;
52
0
}
53
54
typedef enum coap_uri_check_t {
55
  COAP_URI_CHECK_URI,
56
  COAP_URI_CHECK_PROXY
57
} coap_uri_check_t;
58
59
coap_uri_info_t coap_uri_scheme[COAP_URI_SCHEME_LAST] = {
60
  { "coap",       COAP_DEFAULT_PORT,  0, COAP_URI_SCHEME_COAP },
61
  { "coaps",      COAPS_DEFAULT_PORT, 0, COAP_URI_SCHEME_COAPS },
62
  { "coap+tcp",   COAP_DEFAULT_PORT,  0, COAP_URI_SCHEME_COAP_TCP },
63
  { "coaps+tcp",  COAPS_DEFAULT_PORT, 0, COAP_URI_SCHEME_COAPS_TCP },
64
  { "http",         80,               1, COAP_URI_SCHEME_HTTP },
65
  { "https",       443,               1, COAP_URI_SCHEME_HTTPS },
66
  { "coap+ws",      80,               0, COAP_URI_SCHEME_COAP_WS },
67
  { "coaps+ws",    443,               0, COAP_URI_SCHEME_COAPS_WS }
68
};
69
70
/*
71
 * Returns  0 All OK
72
 *         -1 Insufficient / Invalid parameters
73
 *         -2 No '://'
74
 *         -3 Ipv6 definition error or no host defined after scheme://
75
 *         -4 Invalid port value
76
 *         -5 Port defined for Unix domain or LLC host
77
 *         -6 Hostname > 255 chars
78
 *         -7 Invalid LLC address
79
 */
80
static int
81
coap_split_uri_sub(const uint8_t *str_var,
82
                   size_t len,
83
                   coap_uri_t *uri,
84
0
                   coap_uri_check_t check_proxy) {
85
0
  const uint8_t *p, *q;
86
0
  int res = 0;
87
0
  size_t i;
88
0
  int is_unix_domain = 0;
89
0
  int is_llc = 0;
90
91
0
  if (!str_var || !uri || len == 0)
92
0
    return -1;
93
94
0
  memset(uri, 0, sizeof(coap_uri_t));
95
0
  uri->port = COAP_DEFAULT_PORT;
96
97
  /* search for scheme */
98
0
  p = str_var;
99
0
  if (*p == '/') {
100
    /* no scheme, host or port */
101
0
    if (check_proxy == COAP_URI_CHECK_PROXY) {
102
      /* Must have ongoing host if proxy definition */
103
0
      return -1;
104
0
    }
105
0
    q = p;
106
0
    goto path;
107
0
  }
108
109
  /* find scheme terminating :// */
110
0
  while (len >= 3 && !(p[0] == ':' && p[1] == '/' && p[2] == '/')) {
111
0
    ++p;
112
0
    --len;
113
0
  }
114
0
  if (len < 3) {
115
    /* scheme not defined with a :// terminator */
116
0
    res = -2;
117
0
    goto error;
118
0
  }
119
0
  for (i = 0; i < COAP_URI_SCHEME_LAST; i++) {
120
0
    if ((p - str_var) == (int)strlen(coap_uri_scheme[i].name) &&
121
0
        memcmp(str_var, coap_uri_scheme[i].name, p - str_var) == 0) {
122
0
      if (check_proxy != COAP_URI_CHECK_PROXY && coap_uri_scheme[i].proxy_only) {
123
0
        coap_log_err("%.*s URI scheme not enabled (not a proxy)\n",
124
0
                     (int)(p - str_var), str_var);
125
0
        return -1;
126
0
      }
127
0
      uri->scheme = coap_uri_scheme[i].scheme;
128
0
      uri->port = coap_uri_scheme[i].port;
129
0
      break;
130
0
    }
131
0
  }
132
0
  if (i == COAP_URI_SCHEME_LAST) {
133
    /* scheme unknown */
134
0
    coap_log_err("%.*s URI scheme unknown\n", (int)(p - str_var), str_var);
135
0
    res = -1;
136
0
    goto error;
137
0
  }
138
0
  switch (uri->scheme) {
139
0
  case COAP_URI_SCHEME_COAP:
140
0
    break;
141
0
  case COAP_URI_SCHEME_COAPS:
142
0
    if (!coap_dtls_is_supported()) {
143
0
      coap_log_err("coaps URI scheme not supported in this version of libcoap\n");
144
0
      return -1;
145
0
    }
146
0
    break;
147
0
  case COAP_URI_SCHEME_COAP_TCP:
148
0
    if (!coap_tcp_is_supported()) {
149
0
      coap_log_err("coap+tcp URI scheme not supported in this version of libcoap\n");
150
0
      return -1;
151
0
    }
152
0
    break;
153
0
  case COAP_URI_SCHEME_COAPS_TCP:
154
0
    if (!coap_tls_is_supported()) {
155
0
      coap_log_err("coaps+tcp URI scheme not supported in this version of libcoap\n");
156
0
      return -1;
157
0
    }
158
0
    break;
159
0
  case COAP_URI_SCHEME_COAP_WS:
160
0
    if (!coap_ws_is_supported()) {
161
0
      coap_log_err("coap+ws URI scheme not supported in this version of libcoap\n");
162
0
      return -1;
163
0
    }
164
0
    break;
165
0
  case COAP_URI_SCHEME_COAPS_WS:
166
0
    if (!coap_wss_is_supported()) {
167
0
      coap_log_err("coaps+ws URI scheme not supported in this version of libcoap\n");
168
0
      return -1;
169
0
    }
170
0
    break;
171
0
  case COAP_URI_SCHEME_HTTP:
172
0
  case COAP_URI_SCHEME_HTTPS:
173
    /* Not proxy, caught above.  For proxy, assume app is doing CoAP <> HTTP mapping. */
174
0
    break;
175
0
  case COAP_URI_SCHEME_LAST:
176
0
  default:
177
0
    coap_log_warn("Unsupported URI type %d\n", uri->scheme);
178
0
    return -1;
179
0
  }
180
  /* skip :// */
181
0
  p += 3;
182
0
  len -= 3;
183
184
  /* p points to beginning of Uri-Host */
185
0
  q = p;
186
187
0
  if (len && *p == '[') {
188
    /* IPv6 address reference or Unix domain */
189
0
    ++p;
190
0
    ++q;
191
0
    --len;
192
193
0
    while (len && *q != ']') {
194
0
      ++q;
195
0
      --len;
196
0
    }
197
198
0
    if (!len || *q != ']' || p == q) {
199
0
      res = -3;
200
0
      goto error;
201
0
    }
202
203
0
    COAP_SET_STR(&uri->host, q - p, p);
204
0
    ++q;
205
0
    --len;
206
0
#if COAP_AF_LLC_SUPPORT
207
0
  } else if ((len >= LLC_HOST_LEN) && strncmp((const char *)p, "llc[", 4) == 0) {
208
0
    unsigned long sap = 0;
209
210
0
    is_llc = 1;
211
212
0
    while (len && *q != ']') {
213
0
      ++q;
214
0
      --len;
215
0
    }
216
217
0
    if (!len || *q != ']' || p == q) {
218
0
      res = -7;
219
0
      goto error;
220
0
    }
221
222
0
    ++q;
223
0
    --len;
224
225
0
    if (!len || *q != ':') {
226
0
      coap_log_warn("LLC SAP missing in URI\n");
227
0
      res = -7;
228
0
      goto error;
229
0
    }
230
231
0
    ++q;
232
0
    --len;
233
234
0
    while (len && isxdigit(*q)) {
235
0
      if (*q >= 'a' && *q <= 'f')
236
0
        sap = sap * 16 + (*q - 'a' + 10);
237
0
      else if (*q >= 'A' && *q <= 'F')
238
0
        sap = sap * 16 + (*q - 'A' + 10);
239
240
0
      ++q;
241
0
      --len;
242
0
    }
243
244
0
    if (sap > UINT8_MAX) {
245
0
      coap_log_warn("LLC SAP invalid (%lu > 255)\n", sap);
246
0
      res = -7;
247
0
      goto error;
248
0
    }
249
250
0
    COAP_SET_STR(&uri->host, q - p, p);
251
0
#endif /* COAP_AF_LLC_SUPPORT */
252
0
  } else {
253
    /* IPv4 address, FQDN or Unix domain socket */
254
0
    if (len >= 3 && p[0] == '%' && p[1] == '2' &&
255
0
        (p[2] == 'F' || p[2] == 'f')) {
256
      /* Unix domain definition */
257
0
      uri->port = 0;
258
0
      is_unix_domain = 1;
259
0
    }
260
0
    while (len && *q != ':' && *q != '/' && *q != '?') {
261
0
      ++q;
262
0
      --len;
263
0
    }
264
265
0
    if (p == q) {
266
0
      res = -3;
267
0
      goto error;
268
0
    }
269
270
0
    if ((int)(q - p) > 255) {
271
0
      coap_log_warn("Host name length too long (%d > 255)\n", (int)(q - p));
272
0
      res = -6;
273
0
      goto error;
274
0
    }
275
276
0
    COAP_SET_STR(&uri->host, q - p, p);
277
0
  }
278
279
  /* check for Uri-Port (invalid for Unix) */
280
0
  if (len && *q == ':') {
281
0
    if (is_unix_domain || is_llc) {
282
0
      res = -5;
283
0
      goto error;
284
0
    }
285
0
    p = ++q;
286
0
    --len;
287
288
0
    while (len && isdigit(*q)) {
289
0
      ++q;
290
0
      --len;
291
0
    }
292
293
0
    if (p < q) {                /* explicit port number given */
294
0
      long uri_port = 0;
295
296
0
      while ((p < q) && (uri_port <= UINT16_MAX))
297
0
        uri_port = uri_port * 10 + (*p++ - '0');
298
299
      /* check if port number is in allowed range */
300
0
      if (uri_port > UINT16_MAX) {
301
0
        coap_log_warn("Port number too big (%ld > 65535)\n", uri_port);
302
0
        res = -4;
303
0
        goto error;
304
0
      }
305
306
0
      uri->port = (uint16_t)uri_port;
307
0
    }
308
0
  }
309
310
0
path:                 /* at this point, p must point to an absolute path */
311
312
0
  if (!len)
313
0
    goto end;
314
315
0
  if (*q == '/') {
316
0
    p = ++q;
317
0
    --len;
318
319
0
    while (len && *q != '?') {
320
0
      ++q;
321
0
      --len;
322
0
    }
323
324
0
    if (p < q) {
325
0
      COAP_SET_STR(&uri->path, q - p, p);
326
0
      p = q;
327
0
    }
328
0
  }
329
330
  /* Uri_Query */
331
0
  if (len && *p == '?') {
332
0
    ++p;
333
0
    --len;
334
0
    COAP_SET_STR(&uri->query, len, p);
335
0
    len = 0;
336
0
  }
337
338
0
end:
339
0
  return len ? -1 : 0;
340
341
0
error:
342
0
  return res;
343
0
}
344
345
int
346
0
coap_split_uri(const uint8_t *str_var, size_t len, coap_uri_t *uri) {
347
0
  return coap_split_uri_sub(str_var, len, uri, COAP_URI_CHECK_URI);
348
0
}
349
350
int
351
0
coap_split_proxy_uri(const uint8_t *str_var, size_t len, coap_uri_t *uri) {
352
0
  return coap_split_uri_sub(str_var, len, uri, COAP_URI_CHECK_PROXY);
353
0
}
354
355
static void
356
0
coap_replace_upper_lower(coap_optlist_t *optlist) {
357
0
  size_t i;
358
359
0
  for (i = 0; i < optlist->length; i++) {
360
0
    if (optlist->data[i] >= 'A' && optlist->data[i] <= 'Z') {
361
0
      optlist->data[i] += 'a' - 'A';
362
0
    }
363
0
  }
364
0
}
365
366
int
367
coap_uri_into_options(const coap_uri_t *uri, const coap_address_t *dst,
368
                      coap_optlist_t **optlist_chain, int create_port_host_opt,
369
0
                      uint8_t *_buf COAP_UNUSED, size_t buflen COAP_UNUSED) {
370
0
  return !coap_uri_into_optlist(uri, dst, optlist_chain, create_port_host_opt) ? -1 : 0;
371
0
}
372
373
int
374
coap_uri_into_optlist(const coap_uri_t *uri, const coap_address_t *dst,
375
0
                      coap_optlist_t **optlist_chain, int create_port_host_opt) {
376
0
  return coap_uri_into_optlist_abbrev(uri, dst, optlist_chain, create_port_host_opt, NULL, 0);
377
0
}
378
379
int
380
coap_uri_into_optlist_abbrev(const coap_uri_t *uri, const coap_address_t *dst,
381
                             coap_optlist_t **optlist_chain, int create_port_host_opt,
382
0
                             coap_upa_abbrev_t *mapping, uint32_t count) {
383
0
  if (create_port_host_opt && !coap_host_is_unix_domain(&uri->host)) {
384
0
    int add_option = 0;
385
386
0
    if (dst && uri->host.length) {
387
0
#if !defined(WITH_LWIP) && !defined(WITH_CONTIKI)
388
0
      char addr[INET6_ADDRSTRLEN];
389
#else /* WITH_LWIP || WITH_CONTIKI */
390
      char addr[40];
391
#endif /* WITH_LWIP || WITH_CONTIKI */
392
0
      coap_optlist_t *optlist;
393
394
      /* Add in Uri-Host if not match (need to strip off %iface) */
395
0
      size_t uri_host_len = uri->host.length;
396
0
      const uint8_t *cp = uri->host.s;
397
398
      /* Unfortunately not null terminated */
399
0
      for (size_t i = 0; i < uri_host_len; i++) {
400
0
        if (cp[i] == '%') {
401
          /* %iface specified in host name */
402
0
          uri_host_len = i;
403
0
          break;
404
0
        }
405
0
      }
406
407
0
      if (coap_print_ip_addr(dst, addr, sizeof(addr)) &&
408
0
          (strlen(addr) != uri_host_len ||
409
0
           strncasecmp(addr, (const char *)uri->host.s, uri_host_len) != 0)) {
410
        /* add Uri-Host */
411
0
        optlist = coap_new_optlist(COAP_OPTION_URI_HOST, uri_host_len,
412
0
                                   uri->host.s);
413
0
        if (!coap_host_is_unix_domain(&uri->host)) {
414
0
          coap_replace_percents(optlist);
415
0
          coap_replace_upper_lower(optlist);
416
0
        }
417
0
        if (!coap_insert_optlist(optlist_chain, optlist)) {
418
0
          return 0;
419
0
        }
420
0
      }
421
0
    }
422
    /* Add in UriPort if not default */
423
0
    switch ((int)uri->scheme) {
424
0
    case COAP_URI_SCHEME_HTTP:
425
0
    case COAP_URI_SCHEME_COAP_WS:
426
0
      if (uri->port != 80)
427
0
        add_option = 1;
428
0
      break;
429
0
    case COAP_URI_SCHEME_HTTPS:
430
0
    case COAP_URI_SCHEME_COAPS_WS:
431
0
      if (uri->port != 443)
432
0
        add_option = 1;
433
0
      break;
434
0
    default:
435
0
      if (uri->port != (coap_uri_scheme_is_secure(uri) ? COAPS_DEFAULT_PORT :
436
0
                        COAP_DEFAULT_PORT))
437
0
        add_option = 1;
438
0
      break;
439
0
    }
440
0
    if (add_option) {
441
0
      uint8_t tbuf[4];
442
443
0
      coap_insert_optlist(optlist_chain,
444
0
                          coap_new_optlist(COAP_OPTION_URI_PORT,
445
0
                                           coap_encode_var_safe(tbuf, 4,
446
0
                                                                (uri->port & 0xffff)),
447
0
                                           tbuf));
448
0
    }
449
0
  }
450
451
0
  if (uri->path.length) {
452
0
    if (!coap_path_into_optlist_abbrev(uri->path.s, uri->path.length, COAP_OPTION_URI_PATH,
453
0
                                       optlist_chain, mapping, count))
454
0
      return 0;
455
0
  }
456
457
0
  if (uri->query.length) {
458
0
    if (!coap_query_into_optlist(uri->query.s, uri->query.length, COAP_OPTION_URI_QUERY,
459
0
                                 optlist_chain))
460
0
      return 0;
461
0
  }
462
0
  return 1;
463
0
}
464
465
int
466
0
coap_host_is_unix_domain(const coap_str_const_t *host) {
467
0
  if (host->length >= 3 && host->s[0] == '%' &&
468
0
      host->s[1] == '2' &&
469
0
      (host->s[2] == 'F' || host->s[2] == 'f')) {
470
0
    return 1;
471
0
  }
472
0
  if (host->length >= 1 && host->s[0] == '/')
473
0
    return 1;
474
0
  return 0;
475
0
}
476
477
int
478
0
coap_host_is_llc(const coap_str_const_t *host) {
479
0
#if COAP_AF_LLC_SUPPORT
480
0
  size_t length;
481
0
  const uint8_t *s = NULL;
482
0
  int i = 0;
483
484
0
  length = host->length;
485
0
  if (length != LLC_HOST_LEN)
486
0
    return 0;
487
488
0
  s = host->s;
489
490
0
  if (strncmp((const char *)s, "llc[", 4) != 0)
491
0
    return 0;
492
493
0
  length -= 4;
494
0
  s += 4;
495
496
0
  while (length) {
497
0
    if (!isxdigit(s[0]) || !isxdigit(s[1]))
498
0
      return 0;
499
500
0
    length -= 2;
501
0
    s += 2;
502
503
0
    i++;
504
505
0
    if (length <= 0 || i >= 6)
506
0
      break;
507
508
0
    if (s[0] != ':')
509
0
      return 0;
510
511
0
    length--;
512
0
    s++;
513
0
  }
514
515
0
  if (s[0] != ']' || s[1] != ':' || !isxdigit(s[2]) || !isxdigit(s[3]))
516
0
    return 0;
517
518
0
  return 1;
519
#else /* COAP_AF_LLC_SUPPORT */
520
  (void)host;
521
  return 0;
522
#endif /* COAP_AF_LLC_SUPPORT */
523
0
}
524
525
/**
526
 * Calculates decimal value from hexadecimal ASCII character given in
527
 * @p c. The caller must ensure that @p c actually represents a valid
528
 * heaxdecimal character, e.g. with isxdigit(3).
529
 *
530
 * @hideinitializer
531
 */
532
0
#define hexchar_to_dec(c) ((c) & 0x40 ? ((c) & 0x0F) + 9 : ((c) & 0x0F))
533
534
/**
535
 * Decodes percent-encoded characters while copying the string @p seg
536
 * of size @p length to @p buf. The caller of this function must
537
 * ensure that the percent-encodings are correct (i.e. the character
538
 * '%' is always followed by two hex digits. and that @p buf provides
539
 * sufficient space to hold the result. This function is supposed to
540
 * be called by make_decoded_option() only.
541
 *
542
 * @param seg     The segment to decode and copy.
543
 * @param length  Length of @p seg.
544
 * @param buf     The result buffer.
545
 */
546
static void
547
0
decode_segment(const uint8_t *seg, size_t length, unsigned char *buf) {
548
549
0
  while (length) {
550
0
    length--;
551
552
0
    if (*seg == '%') {
553
0
      if (length < 2)
554
0
        return;
555
0
      *buf = (hexchar_to_dec(seg[1]) << 4) + hexchar_to_dec(seg[2]);
556
557
0
      seg += 2;
558
0
      length -= 2;
559
0
    } else {
560
0
      *buf = *seg;
561
0
    }
562
563
0
    ++buf;
564
0
    ++seg;
565
0
  }
566
0
}
567
568
/**
569
 * Runs through the given path (or query) segment and checks if
570
 * percent-encodings are correct. This function returns @c 0 on success
571
 * and @c -1 on error.
572
 */
573
static int
574
0
check_segment(const uint8_t *s, size_t length, size_t *segment_size) {
575
0
  size_t n = 0;
576
577
0
  while (length) {
578
0
    if (*s == '%') {
579
0
      if (length < 3 || !(isxdigit(s[1]) && isxdigit(s[2])))
580
0
        return -1;
581
582
0
      s += 2;
583
0
      length -= 2;
584
0
    }
585
586
0
    ++s;
587
0
    ++n;
588
0
    --length;
589
0
  }
590
591
0
  *segment_size = n;
592
593
0
  return 0;
594
0
}
595
596
/**
597
 * Writes a coap option from given string @p s to @p buf. @p s should
598
 * point to a (percent-encoded) path or query segment of a coap_uri_t
599
 * object.  The created option will have type @c 0, and the length
600
 * parameter will be set according to the size of the decoded string.
601
 * On success, this function returns @c 0 and sets @p optionsize to the option's
602
 * size. On error the function returns a value less than zero. This function
603
 * must be called from coap_split_path_impl() only.
604
 *
605
 * @param s           The string to decode.
606
 * @param length      The size of the percent-encoded string @p s.
607
 * @param buf         The buffer to store the new coap option.
608
 * @param buflen      The maximum size of @p buf.
609
 * @param optionsize  The option's size.
610
 *
611
 * @return @c 0 on success and @c -1 on error.
612
 *
613
 * @bug This function does not split segments that are bigger than 270
614
 * bytes.
615
 */
616
static int
617
make_decoded_option(const uint8_t *s, size_t length,
618
0
                    unsigned char *buf, size_t buflen, size_t *optionsize) {
619
0
  int res;
620
0
  size_t segmentlen;
621
0
  size_t written;
622
623
0
  if (!buflen) {
624
0
    coap_log_debug("make_decoded_option(): buflen is 0!\n");
625
0
    return -1;
626
0
  }
627
628
0
  res = check_segment(s, length, &segmentlen);
629
0
  if (res < 0)
630
0
    return -1;
631
632
  /* write option header using delta 0 and length res */
633
0
  written = coap_opt_setheader(buf, buflen, 0, segmentlen);
634
635
0
  assert(written <= buflen);
636
637
0
  if (!written)                        /* encoding error */
638
0
    return -1;
639
640
0
  buf += written;                /* advance past option type/length */
641
0
  buflen -= written;
642
643
0
  if (buflen < segmentlen) {
644
0
    coap_log_debug("buffer too small for option\n");
645
0
    return -1;
646
0
  }
647
648
0
  decode_segment(s, length, buf);
649
650
0
  *optionsize = written + segmentlen;
651
652
0
  return 0;
653
0
}
654
655
656
#ifndef min
657
#define min(a,b) ((a) < (b) ? (a) : (b))
658
#endif
659
660
typedef void (*segment_handler_t)(const uint8_t *, size_t, void *);
661
662
/**
663
 * Checks if path segment @p s consists of one or two dots.
664
 *
665
 * returns 1 if . , 2 if .. else 0.
666
 */
667
int
668
0
coap_check_dots(const uint8_t *s, size_t len) {
669
0
  uint8_t p;
670
671
0
  if (!len)
672
0
    return 0;
673
674
0
  p = *s;
675
676
  /* Check 'first' char */
677
0
  if (p == '%' && len >=3) {
678
0
    if (s[1] == '2' && (s[2] == 'E' || s[2] == 'e')) {
679
0
      s += 2;
680
0
      len -= 2;
681
0
    }
682
0
    p = '.';
683
0
  }
684
0
  if (p != '.')
685
0
    return 0;
686
0
  if (len == 1)
687
0
    return 1;
688
689
  /* Check 'second' char, first is '.' */
690
0
  s++;
691
0
  len--;
692
0
  assert(len);
693
0
  p = *s;
694
0
  if (p == '%' && len >=3) {
695
0
    if (s[1] == '2' && (s[2] == 'E' || s[2] == 'e')) {
696
0
      len -= 2;
697
0
    }
698
0
    p = '.';
699
0
  }
700
0
  if (p != '.')
701
0
    return 0;
702
0
  if (len == 1)
703
0
    return 2;
704
705
0
  return 0;
706
0
}
707
708
struct cnt_str {
709
  coap_string_t base_buf;
710
  coap_string_t buf;
711
  int n;
712
};
713
714
static void
715
0
backup_segment(void *data) {
716
0
  struct cnt_str *state = (struct cnt_str *)data;
717
0
  int i;
718
0
  uint8_t *buf;
719
720
0
  if (state->n == 0)
721
0
    return;
722
723
0
  state->n--;
724
0
  buf = state->base_buf.s;
725
0
  for (i = 0; i < state->n; i++) {
726
0
    buf += coap_opt_size(buf);
727
0
  }
728
0
  state->buf.s = buf;
729
0
  state->buf.length = state->base_buf.length - (buf - state->base_buf.s);
730
0
}
731
732
/**
733
 * Splits the given string into segments. You should call one of the
734
 * functions coap_split_path() or coap_split_query() instead.
735
 *
736
 * @param path   The URI path string to be tokenized.
737
 * @param len    The length of @p path.
738
 * @param h      A handler that is called with every token.
739
 * @param data   Opaque data that is passed to @p h when called.
740
 *
741
 * @return The number of characters that have been parsed from @p s.
742
 */
743
static size_t
744
coap_split_path_impl(const uint8_t *path, size_t len,
745
0
                     segment_handler_t h, void *data) {
746
0
  const uint8_t *p, *q;
747
0
  size_t length = len;
748
0
  int num_dots;
749
750
0
  p = q = path;
751
0
  while (length > 0 && !strnchr((const uint8_t *)"?#", 2, *q)) {
752
0
    if (*q == '/') {
753
      /* start new segment */
754
0
      num_dots = coap_check_dots(p, q - p);
755
0
      switch (num_dots) {
756
0
      case 1:
757
        /* drop segment */
758
0
        break;
759
0
      case 2:
760
        /* backup segment */
761
0
        backup_segment(data);
762
0
        break;
763
0
      case 0:
764
0
      default:
765
        /* add segment */
766
0
        h(p, q - p, data);
767
0
        break;
768
0
      }
769
770
0
      p = q + 1;
771
0
    }
772
773
0
    q++;
774
0
    length--;
775
0
  }
776
777
  /* write last segment */
778
0
  num_dots = coap_check_dots(p, q - p);
779
0
  switch (num_dots) {
780
0
  case 1:
781
    /* drop segment */
782
0
    break;
783
0
  case 2:
784
    /* backup segment */
785
0
    backup_segment(data);
786
0
    break;
787
0
  case 0:
788
0
  default:
789
    /* add segment */
790
0
    h(p, q - p, data);
791
0
    break;
792
0
  }
793
794
0
  return q - path;
795
0
}
796
797
static void
798
0
write_option(const uint8_t *s, size_t len, void *data) {
799
0
  struct cnt_str *state = (struct cnt_str *)data;
800
0
  int res;
801
0
  size_t optionsize;
802
0
  assert(state);
803
804
0
  res = make_decoded_option(s, len, state->buf.s, state->buf.length, &optionsize);
805
0
  if (res == 0) {
806
0
    state->buf.s += optionsize;
807
0
    state->buf.length -= optionsize;
808
0
    state->n++;
809
0
  }
810
0
}
811
812
int
813
coap_split_path(const uint8_t *s, size_t length,
814
0
                unsigned char *buf, size_t *buflen) {
815
0
  struct cnt_str tmp = { { *buflen, buf }, { *buflen, buf }, 0 };
816
817
0
  coap_split_path_impl(s, length, write_option, &tmp);
818
819
0
  *buflen = *buflen - tmp.buf.length;
820
821
0
  return tmp.n;
822
0
}
823
824
void
825
0
coap_replace_percents(coap_optlist_t *optlist) {
826
0
  size_t i;
827
0
  size_t o = 0;
828
829
0
  for (i = 0; i < optlist->length; i++) {
830
0
    if (optlist->data[i] == '%' && optlist->length - i >= 3) {
831
0
      optlist->data[o] = (hexchar_to_dec(optlist->data[i+1]) << 4) +
832
0
                         hexchar_to_dec(optlist->data[i+2]);
833
0
      i+= 2;
834
0
    } else if (o != i) {
835
0
      optlist->data[o] = optlist->data[i];
836
0
    }
837
0
    o++;
838
0
  }
839
0
  optlist->length = o;
840
0
}
841
842
static void
843
0
backup_optlist(coap_optlist_t **optlist_begin) {
844
0
  coap_optlist_t *last = NULL;
845
0
  coap_optlist_t *cur = *optlist_begin;
846
847
0
  if (!cur)
848
0
    return;
849
850
0
  while (cur) {
851
0
    if (!cur->next)
852
0
      break;
853
0
    last = cur;
854
0
    cur = cur->next;
855
0
  }
856
0
  coap_delete_optlist(cur);
857
0
  if (last) {
858
0
    last->next = NULL;
859
0
  } else {
860
0
    *optlist_begin = NULL;
861
0
  }
862
0
}
863
864
int
865
coap_path_into_optlist(const uint8_t *s, size_t length, coap_option_num_t optnum,
866
0
                       coap_optlist_t **optlist_chain) {
867
0
  return coap_path_into_optlist_abbrev(s, length, optnum, optlist_chain, NULL, 0);
868
0
}
869
870
int
871
coap_path_into_optlist_abbrev(const uint8_t *s, size_t length, coap_option_num_t optnum,
872
                              coap_optlist_t **optlist_chain, coap_upa_abbrev_t *mapping,
873
0
                              uint32_t count) {
874
0
  const uint8_t *p = s;
875
0
  coap_optlist_t *optlist;
876
0
  int num_dots;
877
0
  coap_optlist_t **optlist_start;
878
879
0
  if (*optlist_chain) {
880
    /* Something previously in optlist_chain. Need to make that the start */
881
0
    optlist_start = &((*optlist_chain)->next);
882
0
  } else {
883
0
    optlist_start = optlist_chain;
884
0
  }
885
886
0
  if (length > 0 && mapping) {
887
0
    uint32_t i;
888
0
    uint8_t buf[4];
889
890
0
    for (i = 0; i < count; i++) {
891
0
      if (strlen(mapping[i].upa_path) == length &&
892
0
          memcmp(mapping[i].upa_path, s, length) == 0) {
893
        /* add in as Uri_path-Abbrev */
894
0
        optlist = coap_new_optlist(COAP_OPTION_URI_PATH_ABB,
895
0
                                   coap_encode_var_safe(buf, sizeof(buf), mapping[i].upa_value), buf);
896
0
        if (!coap_insert_optlist(optlist_chain, optlist)) {
897
0
          return 0;
898
0
        }
899
0
        return 1;
900
0
      }
901
0
    }
902
0
  }
903
0
  while (length > 0 && !strnchr((const uint8_t *)"?#", 2, *s)) {
904
0
    if (*s == '/') {                /* start of new path element */
905
      /* start new segment */
906
0
      num_dots = coap_check_dots(p, s - p);
907
0
      switch (num_dots) {
908
0
      case 1:
909
        /* drop segment */
910
0
        break;
911
0
      case 2:
912
        /* backup segment */
913
0
        backup_optlist(optlist_start);
914
0
        break;
915
0
      case 0:
916
0
      default:
917
        /* add segment */
918
0
        optlist = coap_new_optlist(optnum, s - p, p);
919
0
        if (!optlist) {
920
0
          return 0;
921
0
        }
922
0
        coap_replace_percents(optlist);
923
0
        if (!coap_insert_optlist(optlist_chain, optlist)) {
924
0
          return 0;
925
0
        }
926
0
        break;
927
0
      }
928
0
      p = s + 1;
929
0
    }
930
0
    s++;
931
0
    length--;
932
933
0
  }
934
  /* add last path element */
935
0
  num_dots = coap_check_dots(p, s - p);
936
0
  switch (num_dots) {
937
0
  case 1:
938
    /* drop segment */
939
0
    break;
940
0
  case 2:
941
    /* backup segment */
942
0
    backup_optlist(optlist_start);
943
0
    break;
944
0
  case 0:
945
0
  default:
946
    /* add segment */
947
0
    optlist = coap_new_optlist(optnum, s - p, p);
948
0
    if (!optlist) {
949
0
      return 0;
950
0
    }
951
0
    coap_replace_percents(optlist);
952
0
    if (!coap_insert_optlist(optlist_chain, optlist)) {
953
0
      return 0;
954
0
    }
955
0
    break;
956
0
  }
957
0
  return 1;
958
0
}
959
960
int
961
coap_split_query(const uint8_t *s, size_t length,
962
0
                 unsigned char *buf, size_t *buflen) {
963
0
  struct cnt_str tmp = { { *buflen, buf }, { *buflen, buf }, 0 };
964
0
  const uint8_t *p;
965
966
0
  p = s;
967
0
  while (length > 0 && *s != '#') {
968
0
    if (*s == '&') {                /* start new query element */
969
0
      write_option(p, s - p, &tmp);
970
0
      p = s + 1;
971
0
    }
972
973
0
    s++;
974
0
    length--;
975
0
  }
976
977
  /* write last query element */
978
0
  write_option(p, s - p, &tmp);
979
980
0
  *buflen = *buflen - tmp.buf.length;
981
0
  return tmp.n;
982
0
}
983
984
int
985
coap_query_into_optlist(const uint8_t *s, size_t length, coap_option_num_t optnum,
986
0
                        coap_optlist_t **optlist_chain) {
987
0
  const uint8_t *p = s;
988
0
  coap_optlist_t *optlist;
989
990
0
  while (length > 0 && *s != '#') {
991
0
    if (*s == '&') {                /* start of new query element */
992
      /* add previous query element */
993
0
      optlist = coap_new_optlist(optnum, s - p, p);
994
0
      if (!optlist) {
995
0
        return 0;
996
0
      }
997
0
      coap_replace_percents(optlist);
998
0
      if (!coap_insert_optlist(optlist_chain, optlist)) {
999
0
        return 0;
1000
0
      }
1001
0
      p = s + 1;
1002
0
    }
1003
0
    s++;
1004
0
    length--;
1005
0
  }
1006
  /* add last query element */
1007
0
  optlist = coap_new_optlist(optnum, s - p, p);
1008
0
  if (!optlist) {
1009
0
    return 0;
1010
0
  }
1011
0
  coap_replace_percents(optlist);
1012
0
  if (!coap_insert_optlist(optlist_chain, optlist)) {
1013
0
    return 0;
1014
0
  }
1015
0
  return 1;
1016
0
}
1017
1018
0
#define URI_DATA(uriobj) ((unsigned char *)(uriobj) + sizeof(coap_uri_t))
1019
1020
coap_uri_t *
1021
0
coap_new_uri(const uint8_t *uri, unsigned int length) {
1022
0
  uint8_t *result;
1023
0
  coap_uri_t *out_uri;
1024
1025
0
  out_uri = (coap_uri_t *)coap_malloc_type(COAP_STRING, length + 1 + sizeof(coap_uri_t));
1026
1027
0
  if (!out_uri)
1028
0
    return NULL;
1029
1030
0
  result = (uint8_t *)out_uri;
1031
0
  memcpy(URI_DATA(result), uri, length);
1032
0
  URI_DATA(result)[length] = '\0'; /* make it zero-terminated */
1033
1034
0
  if (coap_split_uri(URI_DATA(result), length, out_uri) < 0) {
1035
0
    coap_free_type(COAP_STRING, out_uri);
1036
0
    return NULL;
1037
0
  }
1038
0
  return out_uri;
1039
0
}
1040
1041
coap_uri_t *
1042
0
coap_clone_uri(const coap_uri_t *uri) {
1043
0
  coap_uri_t *result;
1044
0
  uint8_t *p;
1045
1046
0
  if (!uri)
1047
0
    return  NULL;
1048
1049
0
  result = (coap_uri_t *)coap_malloc_type(COAP_STRING,  uri->query.length + uri->host.length +
1050
0
                                          uri->path.length + sizeof(coap_uri_t) + 1);
1051
1052
0
  if (!result)
1053
0
    return NULL;
1054
1055
0
  memset(result, 0, sizeof(coap_uri_t));
1056
1057
0
  result->port = uri->port;
1058
1059
0
  if (uri->host.length) {
1060
0
    result->host.s = p = URI_DATA(result);
1061
0
    result->host.length = uri->host.length;
1062
1063
0
    memcpy(p, uri->host.s, uri->host.length);
1064
0
  }
1065
1066
0
  if (uri->path.length) {
1067
0
    result->path.s = p = URI_DATA(result) + uri->host.length;
1068
0
    result->path.length = uri->path.length;
1069
1070
0
    memcpy(p, uri->path.s, uri->path.length);
1071
0
  }
1072
1073
0
  if (uri->query.length) {
1074
0
    result->query.s = p = URI_DATA(result) + uri->host.length + uri->path.length;
1075
0
    result->query.length = uri->query.length;
1076
1077
0
    memcpy(p, uri->query.s, uri->query.length);
1078
0
  }
1079
1080
0
  return result;
1081
0
}
1082
1083
void
1084
0
coap_delete_uri(coap_uri_t *uri) {
1085
0
  coap_free_type(COAP_STRING, uri);
1086
0
}
1087
1088
static int
1089
0
is_unescaped_in_path(const uint8_t c) {
1090
0
  return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
1091
0
         (c >= '0' && c <= '9') || c == '-' || c == '.' || c == '_' ||
1092
0
         c == '~' || c == '!' || c == '$' || c == '\'' || c == '(' ||
1093
0
         c == ')' || c == '*' || c == '+' || c == ',' || c == ';' ||
1094
0
         c=='=' || c==':' || c=='@' || c == '&';
1095
0
}
1096
1097
static int
1098
0
is_unescaped_in_query(const uint8_t c) {
1099
0
  return is_unescaped_in_path(c) || c=='/' || c=='?';
1100
0
}
1101
1102
coap_string_t *
1103
0
coap_get_query(const coap_pdu_t *request) {
1104
0
  coap_opt_iterator_t opt_iter;
1105
0
  coap_opt_filter_t f;
1106
0
  coap_opt_t *q;
1107
0
  coap_string_t *query = NULL;
1108
0
  size_t length = 0;
1109
0
  static const uint8_t hex[] = "0123456789ABCDEF";
1110
1111
0
  coap_option_filter_clear(&f);
1112
0
  coap_option_filter_set(&f, COAP_OPTION_URI_QUERY);
1113
0
  coap_option_iterator_init(request, &opt_iter, &f);
1114
0
  while ((q = coap_option_next(&opt_iter))) {
1115
0
    uint16_t seg_len = coap_opt_length(q), i;
1116
0
    const uint8_t *seg= coap_opt_value(q);
1117
0
    for (i = 0; i < seg_len; i++) {
1118
0
      if (is_unescaped_in_query(seg[i]))
1119
0
        length += 1;
1120
0
      else
1121
0
        length += 3;
1122
0
    }
1123
0
    length += 1;
1124
0
  }
1125
0
  if (length > 0)
1126
0
    length -= 1;
1127
0
  if (length > 0) {
1128
0
    query = coap_new_string(length);
1129
0
    if (query) {
1130
0
      query->length = length;
1131
0
      unsigned char *s = query->s;
1132
0
      coap_option_iterator_init(request, &opt_iter, &f);
1133
0
      while ((q = coap_option_next(&opt_iter))) {
1134
0
        if (s != query->s)
1135
0
          *s++ = '&';
1136
0
        uint16_t seg_len = coap_opt_length(q), i;
1137
0
        const uint8_t *seg= coap_opt_value(q);
1138
0
        for (i = 0; i < seg_len; i++) {
1139
0
          if (is_unescaped_in_query(seg[i])) {
1140
0
            *s++ = seg[i];
1141
0
          } else {
1142
0
            *s++ = '%';
1143
0
            *s++ = hex[seg[i]>>4];
1144
0
            *s++ = hex[seg[i]&0x0F];
1145
0
          }
1146
0
        }
1147
0
      }
1148
0
    }
1149
0
  }
1150
0
  return query;
1151
0
}
1152
1153
const char *
1154
0
coap_map_abbrev_uri_path(coap_upa_chain_t *chain, uint32_t value) {
1155
0
  while (chain) {
1156
0
    coap_upa_chain_t *next = chain->next;
1157
1158
0
    if (chain->upa_value == value) {
1159
0
      return chain->upa_path;
1160
0
    }
1161
0
    chain = next;
1162
0
  }
1163
0
  return NULL;
1164
0
}
1165
1166
int
1167
coap_map_uri_path_abbrev(coap_upa_chain_t *chain, const char *path, size_t length,
1168
0
                         uint32_t *value) {
1169
0
  while (chain) {
1170
0
    coap_upa_chain_t *next = chain->next;
1171
1172
0
    if (strlen(chain->upa_path) == length && memcmp(chain->upa_path, path, length) == 0) {
1173
0
      *value = chain->upa_value;
1174
0
      return 1;
1175
0
    }
1176
0
    chain = next;
1177
0
  }
1178
0
  return 0;
1179
0
}
1180
1181
coap_string_t *
1182
0
coap_get_uri_path(const coap_pdu_t *request) {
1183
0
  coap_opt_iterator_t opt_iter;
1184
0
  coap_opt_filter_t f;
1185
0
  coap_opt_t *q;
1186
0
  coap_string_t *uri_path = NULL;
1187
0
  size_t length = 0;
1188
0
  static const uint8_t hex[] = "0123456789ABCDEF";
1189
1190
0
  q = coap_check_option(request, COAP_OPTION_URI_PATH_ABB, &opt_iter);
1191
0
  if (q) {
1192
0
    uint32_t value;
1193
0
    const char *exp;
1194
0
    if (coap_check_option(request, COAP_OPTION_PROXY_URI, &opt_iter) ||
1195
0
        coap_check_option(request, COAP_OPTION_URI_PATH, &opt_iter)) {
1196
0
      return NULL;
1197
0
    }
1198
0
    value = coap_decode_var_bytes(coap_opt_value(q), coap_opt_length(q));
1199
0
    exp = coap_map_abbrev_uri_path(coap_upa_server_mapping_chain, value);
1200
0
    if (exp) {
1201
0
      uri_path = coap_new_string(strlen(exp));
1202
0
      if (uri_path) {
1203
0
        memcpy(uri_path->s, exp, strlen(exp));
1204
0
      }
1205
0
      return uri_path;
1206
0
    }
1207
0
    return NULL;
1208
0
  }
1209
0
  q = coap_check_option(request, COAP_OPTION_PROXY_URI, &opt_iter);
1210
0
  if (q) {
1211
0
    coap_uri_t uri;
1212
1213
0
    if (coap_split_proxy_uri(coap_opt_value(q),
1214
0
                             coap_opt_length(q), &uri) < 0) {
1215
0
      return NULL;
1216
0
    }
1217
0
    uri_path = coap_new_string(uri.path.length);
1218
0
    if (uri_path && uri.path.length) {
1219
0
      memcpy(uri_path->s, uri.path.s, uri.path.length);
1220
0
    }
1221
0
    return uri_path;
1222
0
  }
1223
1224
0
  coap_option_filter_clear(&f);
1225
0
  coap_option_filter_set(&f, COAP_OPTION_URI_PATH);
1226
0
  coap_option_iterator_init(request, &opt_iter, &f);
1227
0
  while ((q = coap_option_next(&opt_iter))) {
1228
0
    uint16_t seg_len = coap_opt_length(q), i;
1229
0
    const uint8_t *seg = coap_opt_value(q);
1230
0
    for (i = 0; i < seg_len; i++) {
1231
0
      if (is_unescaped_in_path(seg[i]))
1232
0
        length += 1;
1233
0
      else
1234
0
        length += 3;
1235
0
    }
1236
    /* bump for the leading "/" */
1237
0
    length += 1;
1238
0
  }
1239
  /* The first entry does not have a leading "/" */
1240
0
  if (length > 0)
1241
0
    length -= 1;
1242
1243
  /* if 0, either no URI_PATH Option, or the first one was empty */
1244
0
  uri_path = coap_new_string(length);
1245
0
  if (uri_path) {
1246
0
    uri_path->length = length;
1247
0
    unsigned char *s = uri_path->s;
1248
0
    int n = 0;
1249
0
    coap_option_iterator_init(request, &opt_iter, &f);
1250
0
    while ((q = coap_option_next(&opt_iter))) {
1251
0
      if (n++) {
1252
0
        *s++ = '/';
1253
0
      }
1254
0
      uint16_t seg_len = coap_opt_length(q), i;
1255
0
      const uint8_t *seg= coap_opt_value(q);
1256
0
      for (i = 0; i < seg_len; i++) {
1257
0
        if (is_unescaped_in_path(seg[i])) {
1258
0
          *s++ = seg[i];
1259
0
        } else {
1260
0
          *s++ = '%';
1261
0
          *s++ = hex[seg[i]>>4];
1262
0
          *s++ = hex[seg[i]&0x0F];
1263
0
        }
1264
0
      }
1265
0
    }
1266
0
  }
1267
0
  return uri_path;
1268
0
}
1269
1270
void
1271
58
coap_delete_upa_chain(coap_upa_chain_t *chain) {
1272
58
  while (chain) {
1273
0
    coap_upa_chain_t *next = chain->next;
1274
1275
0
    coap_free_type(COAP_STRING, chain);
1276
0
    chain = next;
1277
0
  }
1278
58
}
1279
1280
static coap_upa_chain_t *
1281
0
coap_build_upa_chain(coap_upa_abbrev_t *list, uint32_t count) {
1282
0
  uint32_t i;
1283
0
  coap_upa_chain_t *chain = NULL;
1284
0
  coap_upa_chain_t *next;
1285
1286
0
  for (i = 0; i < count; i++) {
1287
0
    next = coap_malloc_type(COAP_STRING, sizeof(coap_upa_chain_t) + strlen(list[i].upa_path) + 1);
1288
0
    if (next == NULL)
1289
0
      goto cleanup;
1290
1291
0
    next->upa_value = list[i].upa_value;
1292
0
    next->upa_path = (char *)next + sizeof(coap_upa_chain_t);
1293
0
    strcpy(next->upa_path, list[i].upa_path);
1294
0
    next->next = chain;
1295
0
    chain = next;
1296
0
  }
1297
0
  return chain;
1298
1299
0
cleanup:
1300
0
  coap_delete_upa_chain(chain);
1301
0
  return NULL;
1302
0
}
1303
1304
void
1305
0
coap_upa_client_fallback(coap_upa_abbrev_t *list, uint32_t count) {
1306
0
  coap_lock_lock(return);
1307
0
  coap_delete_upa_chain(coap_upa_client_fallback_chain);
1308
0
  coap_upa_client_fallback_chain = coap_build_upa_chain(list, count);
1309
0
  coap_lock_unlock();
1310
0
}
1311
1312
void
1313
0
coap_upa_server_mapping(coap_upa_abbrev_t *list, uint32_t count) {
1314
0
  coap_lock_lock(return);
1315
0
  coap_delete_upa_chain(coap_upa_server_mapping_chain);
1316
0
  coap_upa_server_mapping_chain = coap_build_upa_chain(list, count);
1317
0
  coap_lock_unlock();
1318
0
}