Coverage Report

Created: 2026-11-12 06:14

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