Coverage Report

Created: 2026-07-16 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libcoap/src/coap_proxy.c
Line
Count
Source
1
/* coap_proxy.c -- helper functions for proxy handling
2
 *
3
 * Copyright (C) 2024-2026 Jon Shallow <supjps-libcoap@jpshallow.com>
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_proxy.c
13
 * @brief Proxy handling functions
14
 */
15
16
#include "coap3/coap_libcoap_build.h"
17
18
#if COAP_PROXY_SUPPORT
19
#include <stdio.h>
20
#ifndef INET6_ADDRSTRLEN
21
#define INET6_ADDRSTRLEN 46
22
#endif
23
24
#if COAP_CLIENT_SUPPORT == 0
25
#error For Proxy support, COAP_CLIENT_SUPPORT must be set
26
#endif
27
#if COAP_SERVER_SUPPORT == 0
28
#error For Proxy support, COAP_SERVER_SUPPORT must be set
29
#endif
30
31
#ifdef _WIN32
32
#define strcasecmp _stricmp
33
#define strncasecmp _strnicmp
34
#endif
35
36
int
37
0
coap_proxy_is_supported(void) {
38
0
  return 1;
39
0
}
40
41
void
42
coap_proxy_log_entry(coap_session_t *incoming, const coap_pdu_t *pdu,
43
0
                     coap_bin_const_t *upstream_token, const char *type) {
44
0
  if (coap_get_log_level() >= COAP_LOG_DEBUG) {
45
0
    coap_string_t *url;
46
47
0
    url = coap_get_uri_path(pdu);
48
0
    if (url) {
49
0
      coap_opt_iterator_t opt_iter;
50
0
      uint8_t addr[INET6_ADDRSTRLEN + 8];
51
0
      char scratch_d[24];
52
0
      char scratch_u[24];
53
0
      size_t size;
54
0
      size_t i;
55
56
0
      scratch_d[0] = '\000';
57
0
      for (i = 0; i < pdu->actual_token.length; i++) {
58
0
        size = strlen(scratch_d);
59
0
        snprintf(&scratch_d[size], sizeof(scratch_d)-size,
60
0
                 "%02x", pdu->actual_token.s[i]);
61
0
      }
62
0
      scratch_u[0] = '\000';
63
0
      if (upstream_token) {
64
0
        for (i = 0; i < upstream_token->length; i++) {
65
0
          size = strlen(scratch_u);
66
0
          snprintf(&scratch_u[size], sizeof(scratch_u)-size,
67
0
                   "%02x", upstream_token->s[i]);
68
0
        }
69
0
      }
70
0
      (void)coap_print_addr(coap_session_get_addr_remote(incoming), addr, sizeof(addr));
71
0
      coap_log_debug("   proxy %-4s %s {%s}-{%s} \"%s\"%s\n", type, addr, scratch_u, scratch_d,
72
0
                     url->s, coap_check_option(pdu, COAP_OPTION_OBSERVE, &opt_iter) ? " Observe" : "");
73
0
      coap_delete_string(url);
74
0
    }
75
0
  }
76
0
}
77
78
void
79
0
coap_proxy_del_req(coap_proxy_entry_t *proxy_entry, coap_proxy_req_t *proxy_req) {
80
0
  coap_proxy_log_entry(proxy_req->incoming, proxy_req->pdu,  proxy_req->token_used, "del");
81
82
0
  coap_delete_pdu_lkd(proxy_req->pdu);
83
0
  coap_delete_bin_const(proxy_req->token_used);
84
0
  coap_delete_cache_key(proxy_req->cache_key);
85
0
  if (proxy_req->proxy_cache) {
86
0
    if (coap_get_log_level() >= COAP_LOG_DEBUG) {
87
0
      coap_string_t *url;
88
89
0
      url = coap_get_uri_path(proxy_req->proxy_cache->req_pdu);
90
0
      coap_log_debug("***%s: Removing Proxy Cache \"%s\" (Active %d)\n",
91
0
                     coap_session_str(proxy_req->incoming),
92
0
                     url ? (char *)url->s : "???",
93
0
                     proxy_req->proxy_cache->ref);
94
0
      coap_delete_string(url);
95
0
    }
96
0
    assert(proxy_req->proxy_cache->ref);
97
0
    proxy_req->proxy_cache->ref--;
98
0
    if (proxy_req->proxy_cache->ref == 0) {
99
0
      PROXY_CACHE_DELETE(proxy_entry->rsp_cache, proxy_req->proxy_cache);
100
0
      coap_delete_pdu_lkd(proxy_req->proxy_cache->req_pdu);
101
0
      coap_delete_pdu_lkd(proxy_req->proxy_cache->rsp_pdu);
102
0
      coap_free_type(COAP_STRING, proxy_req->proxy_cache);
103
0
      proxy_req->proxy_cache = NULL;
104
0
    }
105
0
    if (proxy_req->doing_observe) {
106
0
      assert(proxy_req->incoming->ref_proxy_subs);
107
0
      proxy_req->incoming->ref_proxy_subs--;
108
0
      proxy_req->doing_observe = 0;
109
0
    }
110
0
  }
111
  /* To prevent potential loops */
112
0
  proxy_req->incoming = NULL;
113
114
0
  LL_DELETE(proxy_entry->proxy_req, proxy_req);
115
0
  coap_free_type(COAP_STRING, proxy_req);
116
0
}
117
118
static void
119
0
coap_proxy_cleanup_entry(coap_proxy_entry_t *proxy_entry, int send_failure) {
120
0
  coap_proxy_req_t *proxy_req, *treq;
121
122
0
  LL_FOREACH_SAFE(proxy_entry->proxy_req, proxy_req, treq) {
123
0
    if (send_failure) {
124
0
      coap_pdu_t *response;
125
0
      coap_bin_const_t l_token;
126
127
      /* Need to send back a gateway failure */
128
0
      response = coap_pdu_init(proxy_req->pdu->type,
129
0
                               COAP_RESPONSE_CODE(502),
130
0
                               coap_new_message_id_lkd(proxy_entry->incoming),
131
0
                               coap_session_max_pdu_size_lkd(proxy_entry->incoming));
132
0
      if (!response) {
133
0
        coap_log_info("PDU creation issue\n");
134
0
        goto cleanup;
135
0
      }
136
137
0
      l_token = coap_pdu_get_token(proxy_req->pdu);
138
0
      if (!coap_add_token(response, l_token.length,
139
0
                          l_token.s)) {
140
0
        coap_log_debug("Cannot add token to incoming proxy response PDU\n");
141
0
      }
142
143
0
      if (coap_send_lkd(proxy_entry->incoming, response) == COAP_INVALID_MID) {
144
0
        coap_log_info("Failed to send PDU with 5.02 gateway issue\n");
145
0
      }
146
0
    }
147
0
cleanup:
148
0
    coap_proxy_del_req(proxy_entry, proxy_req);
149
0
  }
150
0
  coap_free_type(COAP_STRING, proxy_entry->uri_host_keep);
151
0
}
152
153
void
154
46
coap_proxy_cleanup(coap_context_t *context) {
155
46
  size_t i;
156
157
46
  for (i = 0; i < context->proxy_list_count; i++) {
158
    /* All sessions have now been closed down */
159
0
    coap_log_debug("proxy_entry %p cleaned up\n",
160
0
                   (void *)&context->proxy_list[i]);
161
0
    coap_proxy_cleanup_entry(&context->proxy_list[i], 0);
162
0
  }
163
46
  coap_free_type(COAP_STRING, context->proxy_list);
164
46
}
165
166
static int
167
0
coap_proxy_check_observe(coap_proxy_entry_t *proxy_entry) {
168
0
  if (proxy_entry && proxy_entry->ongoing) {
169
    /* Need to see if there are any Observes active */
170
0
    coap_lg_crcv_t *lg_crcv;
171
172
0
    LL_FOREACH(proxy_entry->ongoing->lg_crcv, lg_crcv) {
173
0
      if (lg_crcv->observe_set) {
174
0
        return 1;
175
0
      }
176
0
    }
177
0
  }
178
0
  return 0;
179
0
}
180
181
/*
182
 * Return 1 if there is a future expire time, else 0.
183
 * Update tim_rem with remaining value if return is 1.
184
 */
185
int
186
coap_proxy_check_timeouts(coap_context_t *context, coap_tick_t now,
187
0
                          coap_tick_t *tim_rem) {
188
0
  size_t i;
189
0
  int ret = 0;
190
191
0
  *tim_rem = COAP_MAX_DELAY_TICKS;
192
0
  for (i = 0; i < context->proxy_list_count; i++) {
193
0
    coap_proxy_entry_t *proxy_entry = &context->proxy_list[i];
194
195
0
    if (coap_proxy_check_observe(proxy_entry))
196
0
      continue;
197
198
0
    if (proxy_entry->ongoing && proxy_entry->idle_timeout_ticks) {
199
0
      if (proxy_entry->last_used + proxy_entry->idle_timeout_ticks <= now) {
200
        /* Drop session to upstream server (which may remove proxy entry) */
201
0
        if (coap_proxy_remove_association(proxy_entry->ongoing, 0))
202
0
          i--;
203
0
      } else {
204
0
        if (*tim_rem > proxy_entry->last_used + proxy_entry->idle_timeout_ticks - now) {
205
0
          *tim_rem = proxy_entry->last_used + proxy_entry->idle_timeout_ticks - now;
206
0
          ret = 1;
207
0
        }
208
0
      }
209
0
    }
210
0
  }
211
0
  return ret;
212
0
}
213
214
static int
215
coap_get_uri_proxy_scheme_info(const coap_pdu_t *request,
216
                               coap_opt_t *opt,
217
0
                               coap_uri_t *uri) {
218
0
  const char *opt_val = (const char *)coap_opt_value(opt);
219
0
  int opt_len = coap_opt_length(opt);
220
0
  coap_opt_iterator_t opt_iter;
221
222
0
  if (opt_len == 9 &&
223
0
      strncasecmp(opt_val, "coaps+tcp", 9) == 0) {
224
0
    uri->scheme = COAP_URI_SCHEME_COAPS_TCP;
225
0
    uri->port = COAPS_DEFAULT_PORT;
226
0
  } else if (opt_len == 8 &&
227
0
             strncasecmp(opt_val, "coap+tcp", 8) == 0) {
228
0
    uri->scheme = COAP_URI_SCHEME_COAP_TCP;
229
0
    uri->port = COAP_DEFAULT_PORT;
230
0
  } else if (opt_len == 5 &&
231
0
             strncasecmp(opt_val, "coaps", 5) == 0) {
232
0
    uri->scheme = COAP_URI_SCHEME_COAPS;
233
0
    uri->port = COAPS_DEFAULT_PORT;
234
0
  } else if (opt_len == 4 &&
235
0
             strncasecmp(opt_val, "coap", 4) == 0) {
236
0
    uri->scheme = COAP_URI_SCHEME_COAP;
237
0
    uri->port = COAP_DEFAULT_PORT;
238
0
  } else if (opt_len == 7 &&
239
0
             strncasecmp(opt_val, "coap+ws", 7) == 0) {
240
0
    uri->scheme = COAP_URI_SCHEME_COAP_WS;
241
0
    uri->port = 80;
242
0
  } else if (opt_len == 8 &&
243
0
             strncasecmp(opt_val, "coaps+ws", 8) == 0) {
244
0
    uri->scheme = COAP_URI_SCHEME_COAPS_WS;
245
0
    uri->port = 443;
246
0
  } else {
247
0
    coap_log_warn("Unsupported Proxy Scheme '%*.*s'\n",
248
0
                  opt_len, opt_len, opt_val);
249
0
    return 0;
250
0
  }
251
252
0
  opt = coap_check_option(request, COAP_OPTION_URI_HOST, &opt_iter);
253
0
  if (opt) {
254
0
    uri->host.length = coap_opt_length(opt);
255
0
    uri->host.s = coap_opt_value(opt);
256
0
  } else {
257
0
    uri->host.s = NULL;
258
0
    uri->host.length = 0;
259
0
    coap_log_warn("Proxy Scheme requires Uri-Host\n");
260
0
    return 0;
261
0
  }
262
0
  opt = coap_check_option(request, COAP_OPTION_URI_PORT, &opt_iter);
263
0
  if (opt) {
264
0
    uri->port =
265
0
        coap_decode_var_bytes(coap_opt_value(opt),
266
0
                              coap_opt_length(opt));
267
0
  }
268
0
  return 1;
269
0
}
270
271
int
272
0
coap_verify_proxy_scheme_supported(coap_uri_scheme_t scheme) {
273
274
  /* Sanity check that the connection can be forwarded on */
275
0
  switch (scheme) {
276
0
  case COAP_URI_SCHEME_HTTP:
277
0
  case COAP_URI_SCHEME_HTTPS:
278
0
    coap_log_warn("Proxy URI http or https not supported\n");
279
0
    return 0;
280
0
  case COAP_URI_SCHEME_COAP:
281
0
    break;
282
0
  case COAP_URI_SCHEME_COAPS:
283
0
    if (!coap_dtls_is_supported()) {
284
0
      coap_log_warn("coaps URI scheme not supported for proxy\n");
285
0
      return 0;
286
0
    }
287
0
    break;
288
0
  case COAP_URI_SCHEME_COAP_TCP:
289
0
    if (!coap_tcp_is_supported()) {
290
0
      coap_log_warn("coap+tcp URI scheme not supported for proxy\n");
291
0
      return 0;
292
0
    }
293
0
    break;
294
0
  case COAP_URI_SCHEME_COAPS_TCP:
295
0
    if (!coap_tls_is_supported()) {
296
0
      coap_log_warn("coaps+tcp URI scheme not supported for proxy\n");
297
0
      return 0;
298
0
    }
299
0
    break;
300
0
  case COAP_URI_SCHEME_COAP_WS:
301
0
    if (!coap_ws_is_supported()) {
302
0
      coap_log_warn("coap+ws URI scheme not supported for proxy\n");
303
0
      return 0;
304
0
    }
305
0
    break;
306
0
  case COAP_URI_SCHEME_COAPS_WS:
307
0
    if (!coap_wss_is_supported()) {
308
0
      coap_log_warn("coaps+ws URI scheme not supported for proxy\n");
309
0
      return 0;
310
0
    }
311
0
    break;
312
0
  case COAP_URI_SCHEME_LAST:
313
0
  default:
314
0
    coap_log_warn("%d URI scheme not supported\n", scheme);
315
0
    return 0;
316
0
  }
317
0
  return 1;
318
0
}
319
320
static coap_proxy_t
321
0
coap_proxy_map_type(coap_proxy_t proxy_type) {
322
0
  if ((proxy_type & COAP_PROXY_NEW_MASK) == 0) {
323
    /* Old version - update to bitwise version */
324
0
    switch ((int)proxy_type) {
325
0
    case COAP_PROXY_REVERSE:
326
0
      proxy_type = COAP_PROXY_REV;
327
0
      break;
328
0
    case COAP_PROXY_REVERSE_STRIP:
329
0
      proxy_type = COAP_PROXY_REV | COAP_PROXY_BIT_STRIP;
330
0
      break;
331
0
    case COAP_PROXY_FORWARD:
332
0
      proxy_type = COAP_PROXY_FWD_STATIC;
333
0
      break;
334
0
    case COAP_PROXY_FORWARD_STRIP:
335
0
      proxy_type = COAP_PROXY_FWD_STATIC | COAP_PROXY_BIT_STRIP;
336
0
      break;
337
0
    case COAP_PROXY_DIRECT:
338
      /* Mcast was always let through */
339
0
      proxy_type = COAP_PROXY_FWD_DYNAMIC | COAP_PROXY_BIT_MCAST;
340
0
      break;
341
0
    case COAP_PROXY_DIRECT_STRIP:
342
      /* Mcast was always let through */
343
0
      proxy_type = COAP_PROXY_FWD_DYNAMIC | COAP_PROXY_BIT_MCAST | COAP_PROXY_BIT_STRIP;
344
0
      break;
345
0
    default:
346
0
      coap_log_warn("Proxy old proxy_type 0x%u unknow\n", proxy_type);
347
0
      proxy_type = COAP_PROXY_FWD_DYNAMIC;
348
0
      break;
349
0
    }
350
0
  }
351
0
  return proxy_type;
352
0
}
353
354
static coap_proxy_entry_t *
355
coap_proxy_get_add_list_entry(coap_session_t *incoming, coap_session_t *ongoing,
356
                              coap_uri_t uri, coap_proxy_server_list_t *server_list,
357
0
                              int allow_add) {
358
0
  coap_context_t *context = incoming ? incoming->context : ongoing->context;
359
0
  coap_proxy_entry_t *proxy_list = context->proxy_list;
360
0
  size_t proxy_list_count = context->proxy_list_count;
361
0
  coap_proxy_entry_t *new_proxy_list;
362
0
  size_t i;
363
364
  /* See if we are already connected to the Server */
365
0
  for (i = 0; i < proxy_list_count; i++) {
366
0
    if (coap_string_equal(&proxy_list[i].uri.host, &uri.host) &&
367
0
        proxy_list[i].uri.port == uri.port &&
368
0
        proxy_list[i].uri.scheme == uri.scheme) {
369
0
      if (!proxy_list[i].track_client_session && context->proxy_response_cb) {
370
0
        coap_ticks(&proxy_list[i].last_used);
371
0
        return &proxy_list[i];
372
0
      } else {
373
0
        if (proxy_list[i].incoming == incoming) {
374
0
          coap_ticks(&proxy_list[i].last_used);
375
0
          return &proxy_list[i];
376
0
        }
377
0
      }
378
0
    }
379
0
  }
380
0
  if ((server_list->type & COAP_PROXY_DYN_DEFINED) && !allow_add)
381
0
    return NULL;
382
383
  /* Need to create a new forwarding mapping */
384
0
  new_proxy_list = coap_realloc_type(COAP_STRING, proxy_list,
385
0
                                     (proxy_list_count+1)*sizeof(proxy_list[0]));
386
387
0
  if (new_proxy_list == NULL) {
388
0
    return NULL;
389
0
  }
390
0
  context->proxy_list = proxy_list = new_proxy_list;
391
0
  memset(&proxy_list[proxy_list_count], 0, sizeof(proxy_list[proxy_list_count]));
392
393
  /* Keep a copy of the host as server_use->uri pointed to will be going away */
394
0
  proxy_list[proxy_list_count].uri = uri;
395
0
  proxy_list[proxy_list_count].uri_host_keep = coap_malloc_type(COAP_STRING,
396
0
                                               uri.host.length);
397
0
  if (!proxy_list[proxy_list_count].uri_host_keep) {
398
0
    return NULL;
399
0
  }
400
0
  memcpy(proxy_list[proxy_list_count].uri_host_keep, uri.host.s, uri.host.length);
401
0
  proxy_list[proxy_list_count].uri.host.s = proxy_list[proxy_list_count].uri_host_keep;
402
  /* Unset uri parts which point to going away information */
403
0
  proxy_list[proxy_list_count].uri.path.s = NULL;
404
0
  proxy_list[proxy_list_count].uri.path.length = 0;
405
0
  proxy_list[proxy_list_count].uri.query.s = NULL;
406
0
  proxy_list[proxy_list_count].uri.query.length = 0;
407
408
0
  proxy_list[proxy_list_count].ongoing = ongoing;
409
0
  proxy_list[proxy_list_count].idle_timeout_ticks = server_list->idle_timeout_secs *
410
0
                                                    COAP_TICKS_PER_SECOND;
411
0
  proxy_list[proxy_list_count].track_client_session = server_list->track_client_session;
412
0
  coap_ticks(&proxy_list[proxy_list_count].last_used);
413
0
  if (incoming) {
414
0
    incoming->proxy_entry = &proxy_list[proxy_list_count];
415
0
    if (server_list->track_client_session)
416
0
      proxy_list[proxy_list_count].incoming = incoming;
417
0
    incoming->proxy_entry = &proxy_list[proxy_list_count];
418
0
  }
419
420
0
  context->proxy_list_count++;
421
0
  return &proxy_list[proxy_list_count];
422
0
}
423
424
static coap_proxy_entry_t *
425
coap_proxy_get_session(coap_session_t *session, const coap_pdu_t *request,
426
                       coap_pdu_t *response,
427
                       coap_proxy_server_list_t *server_list,
428
0
                       coap_proxy_server_t *server_use, int *proxy_entry_created) {
429
0
  size_t i;
430
0
  coap_proxy_entry_t *proxy_entry;
431
0
  coap_proxy_entry_t *proxy_list = session->context->proxy_list;
432
0
  size_t proxy_list_count = session->context->proxy_list_count;
433
0
  coap_opt_iterator_t opt_iter;
434
0
  coap_opt_t *proxy_scheme;
435
0
  coap_opt_t *proxy_uri;
436
0
  coap_proxy_t proxy_type;
437
438
0
  *proxy_entry_created = 0;
439
440
  /*
441
   * Maintain server stickability. server_use not needed as there is
442
   * ongoing session in place.
443
   */
444
0
  if (session->proxy_entry) {
445
0
    for (i = 0; i < proxy_list_count; i++) {
446
0
      if (&proxy_list[i] == session->proxy_entry) {
447
0
        if (session->proxy_entry->ongoing) {
448
0
          memset(server_use, 0, sizeof(*server_use));
449
0
          return session->proxy_entry;
450
0
        }
451
0
      }
452
0
    }
453
0
  }
454
455
  /* Round robin the defined next server list (which usually is just one */
456
0
  server_list->next_entry++;
457
0
  if (server_list->next_entry >= server_list->entry_count)
458
0
    server_list->next_entry = 0;
459
460
0
  if (server_list->entry_count) {
461
0
    memcpy(server_use, &server_list->entry[server_list->next_entry], sizeof(*server_use));
462
0
  } else {
463
0
    memset(server_use, 0, sizeof(*server_use));
464
0
  }
465
466
0
  proxy_type = coap_proxy_map_type(server_list->type);
467
468
0
  if ((proxy_type & COAP_PROXY_NEW_MASK) == COAP_PROXY_FWD_DYNAMIC) {
469
    /* Need to get actual server from CoAP Proxy-Uri or Proxy-Scheme options */
470
    /*
471
     * See if Proxy-Scheme
472
     */
473
0
    proxy_scheme = coap_check_option(request, COAP_OPTION_PROXY_SCHEME, &opt_iter);
474
0
    if (proxy_scheme) {
475
0
      if (!coap_get_uri_proxy_scheme_info(request, proxy_scheme, &server_use->uri)) {
476
0
        response->code = COAP_RESPONSE_CODE(505);
477
0
        return NULL;
478
0
      }
479
0
    }
480
    /*
481
     * See if Proxy-Uri
482
     */
483
0
    proxy_uri = coap_check_option(request, COAP_OPTION_PROXY_URI, &opt_iter);
484
0
    if (proxy_uri) {
485
0
      coap_log_info("Proxy URI '%.*s'\n",
486
0
                    (int)coap_opt_length(proxy_uri),
487
0
                    (const char *)coap_opt_value(proxy_uri));
488
0
      if (coap_split_proxy_uri(coap_opt_value(proxy_uri),
489
0
                               coap_opt_length(proxy_uri),
490
0
                               &server_use->uri) < 0) {
491
        /* Need to return a 5.05 RFC7252 Section 5.7.2 */
492
0
        coap_log_warn("Proxy URI not decodable\n");
493
0
        response->code = COAP_RESPONSE_CODE(505);
494
0
        return NULL;
495
0
      }
496
0
    }
497
498
0
    if (!(proxy_scheme || proxy_uri)) {
499
0
      response->code = COAP_RESPONSE_CODE(404);
500
0
      return NULL;
501
0
    }
502
0
  }
503
504
0
  if (server_use->uri.host.length == 0) {
505
    /* Ongoing connection not well formed */
506
0
    response->code = COAP_RESPONSE_CODE(505);
507
0
    return NULL;
508
0
  }
509
510
0
  if (!coap_verify_proxy_scheme_supported(server_use->uri.scheme)) {
511
0
    response->code = COAP_RESPONSE_CODE(505);
512
0
    return NULL;
513
0
  }
514
515
  /* Check if need to create a new forwarding mapping */
516
0
  proxy_entry = coap_proxy_get_add_list_entry(session, NULL, server_use->uri, server_list, 0);
517
518
0
  if (proxy_entry == NULL) {
519
0
    response->code = COAP_RESPONSE_CODE(500);
520
0
    return NULL;
521
0
  }
522
523
0
  *proxy_entry_created = 1;
524
0
  return proxy_entry;
525
0
}
526
527
int
528
0
coap_proxy_remove_association(coap_session_t *session, int send_failure) {
529
530
0
  size_t i;
531
0
  coap_proxy_entry_t *proxy_list = session->context->proxy_list;
532
0
  size_t proxy_list_count = session->context->proxy_list_count;
533
534
0
  for (i = 0; i < proxy_list_count; i++) {
535
0
    coap_proxy_entry_t *proxy_entry = &proxy_list[i];
536
0
    coap_proxy_req_t *proxy_req;
537
538
    /* Check for incoming match */
539
0
retry:
540
0
    LL_FOREACH(proxy_entry->proxy_req, proxy_req) {
541
0
      if (proxy_req->incoming == session) {
542
0
        coap_proxy_del_req(proxy_entry, proxy_req);
543
0
        goto retry;
544
0
      }
545
0
    }
546
0
    if (proxy_entry->incoming == session) {
547
      /* Only if there is a one-to-one tracking */
548
0
      coap_session_t *ongoing = proxy_entry->ongoing;
549
550
0
      proxy_entry->ongoing = NULL;
551
0
      coap_session_release_lkd(ongoing);
552
0
      return 0;
553
0
    }
554
555
    /* Check for outgoing match */
556
0
    if (proxy_entry->ongoing == session) {
557
0
      coap_session_t *ongoing;
558
559
0
      coap_proxy_cleanup_entry(proxy_entry, send_failure);
560
0
      ongoing = proxy_entry->ongoing;
561
0
      coap_log_debug("*  %s: proxy_entry %p released (rem count = % " PRIdS ")\n",
562
0
                     coap_session_str(ongoing),
563
0
                     (void *)proxy_entry,
564
0
                     session->context->proxy_list_count - 1);
565
0
      if (proxy_list_count-i > 1) {
566
0
        memmove(&proxy_list[i],
567
0
                &proxy_list[i+1],
568
0
                (proxy_list_count-i-1) * sizeof(proxy_list[0]));
569
0
      }
570
0
      session->context->proxy_list_count--;
571
0
      coap_session_release_lkd(ongoing);
572
0
      return 1;
573
0
    }
574
0
  }
575
0
  return 0;
576
0
}
577
578
static coap_proxy_entry_t *
579
coap_proxy_get_ongoing_session(coap_session_t *session,
580
                               const coap_pdu_t *request,
581
                               coap_pdu_t *response,
582
0
                               coap_proxy_server_list_t *server_list) {
583
584
0
  coap_address_t dst;
585
0
  coap_proto_t proto;
586
0
  coap_addr_info_t *info_list = NULL;
587
0
  coap_proxy_entry_t *proxy_entry;
588
0
  coap_context_t *context = session->context;
589
0
  static char client_sni[256];
590
0
  coap_proxy_server_t server_use;
591
0
  int proxy_entry_created;
592
593
0
  proxy_entry = coap_proxy_get_session(session, request, response, server_list,
594
0
                                       &server_use, &proxy_entry_created);
595
0
  if (!proxy_entry) {
596
    /* Error response code already set */
597
0
    return NULL;
598
0
  }
599
600
0
  if (!proxy_entry->ongoing) {
601
    /* Need to create a new session */
602
0
    coap_address_t *local_addr = NULL;
603
604
    /* resolve destination address where data should be sent */
605
0
    info_list = coap_resolve_address_info_lkd(&server_use.uri.host,
606
0
                                              server_use.uri.port,
607
0
                                              server_use.uri.port,
608
0
                                              server_use.uri.port,
609
0
                                              server_use.uri.port,
610
0
                                              0,
611
0
                                              1 << server_use.uri.scheme,
612
0
                                              COAP_RESOLVE_TYPE_REMOTE);
613
614
0
    if (info_list == NULL) {
615
0
      response->code = COAP_RESPONSE_CODE(502);
616
0
      coap_proxy_remove_association(session, 0);
617
0
      return NULL;
618
0
    }
619
0
    proto = info_list->proto;
620
0
    memcpy(&dst, &info_list->addr, sizeof(dst));
621
0
    coap_free_address_info(info_list);
622
0
    if (coap_is_mcast(&dst)) {
623
0
      coap_proxy_t proxy_type = coap_proxy_map_type(server_list->type);
624
625
0
      if ((proxy_type & COAP_PROXY_NEW_MASK) == COAP_PROXY_FWD_DYNAMIC &&
626
0
          (proxy_type & COAP_PROXY_BIT_MCAST) == 0) {
627
0
        response->code = COAP_RESPONSE_CODE(501);
628
0
        coap_proxy_remove_association(session, 0);
629
0
        coap_log_debug("*  %s: mcast proxy forwarding not enabled\n",
630
0
                       coap_session_str(session));
631
0
        return NULL;
632
0
      }
633
0
      if (server_use.uri.scheme != COAP_URI_SCHEME_COAP) {
634
0
        response->code = COAP_RESPONSE_CODE(501);
635
0
        coap_proxy_remove_association(session, 0);
636
0
        coap_log_debug("*  %s: mcast proxy forwarding only supported for 'coap'\n",
637
0
                       coap_session_str(session));
638
0
        return NULL;
639
0
      }
640
0
    }
641
642
0
#if COAP_AF_UNIX_SUPPORT
643
0
    coap_address_t bind_addr;
644
0
    if (coap_is_af_unix(&dst)) {
645
0
      char buf[COAP_UNIX_PATH_MAX];
646
0
      coap_tick_t now;
647
648
      /* Need a unique 'client' address */
649
0
      coap_ticks(&now);
650
0
      snprintf(buf, COAP_UNIX_PATH_MAX,
651
0
               "/tmp/coap-pr-cl-%" PRIu64, (uint64_t)now);
652
0
      if (!coap_address_set_unix_domain(&bind_addr, (const uint8_t *)buf,
653
0
                                        strlen(buf))) {
654
0
        fprintf(stderr, "coap_address_set_unix_domain: %s: failed\n",
655
0
                buf);
656
0
        remove(buf);
657
0
        return NULL;
658
0
      }
659
0
      (void)remove(buf);
660
0
      local_addr = &bind_addr;
661
0
    }
662
0
#endif /* COAP_AF_UNIX_SUPPORT */
663
664
0
    snprintf(client_sni, sizeof(client_sni), "%*.*s", (int)server_use.uri.host.length,
665
0
             (int)server_use.uri.host.length, server_use.uri.host.s);
666
667
0
    switch (server_use.uri.scheme) {
668
0
    case COAP_URI_SCHEME_COAP:
669
0
    case COAP_URI_SCHEME_COAP_TCP:
670
0
    case COAP_URI_SCHEME_COAP_WS:
671
0
#if COAP_OSCORE_SUPPORT
672
0
      if (server_use.oscore_conf) {
673
0
        proxy_entry->ongoing =
674
0
            coap_new_client_session_oscore3_lkd(context, local_addr, &dst,
675
0
                                                proto, server_use.oscore_conf,
676
0
                                                NULL, NULL, NULL);
677
0
      } else {
678
0
#endif /* COAP_OSCORE_SUPPORT */
679
0
        proxy_entry->ongoing =
680
0
            coap_new_client_session3_lkd(context, local_addr, &dst, proto,
681
0
                                         NULL, NULL, NULL);
682
0
#if COAP_OSCORE_SUPPORT
683
0
      }
684
0
#endif /* COAP_OSCORE_SUPPORT */
685
0
      break;
686
0
    case COAP_URI_SCHEME_COAPS:
687
0
    case COAP_URI_SCHEME_COAPS_TCP:
688
0
    case COAP_URI_SCHEME_COAPS_WS:
689
0
#if COAP_OSCORE_SUPPORT
690
0
      if (server_use.oscore_conf) {
691
0
        if (server_use.dtls_pki) {
692
0
          server_use.dtls_pki->client_sni = client_sni;
693
0
          proxy_entry->ongoing =
694
0
              coap_new_client_session_oscore_pki3_lkd(context, local_addr, &dst,
695
0
                                                      proto, server_use.dtls_pki,
696
0
                                                      server_use.oscore_conf,
697
0
                                                      NULL, NULL, NULL);
698
0
        } else if (server_use.dtls_cpsk) {
699
0
          server_use.dtls_cpsk->client_sni = client_sni;
700
0
          proxy_entry->ongoing =
701
0
              coap_new_client_session_oscore_psk3_lkd(context, local_addr, &dst,
702
0
                                                      proto, server_use.dtls_cpsk,
703
0
                                                      server_use.oscore_conf,
704
0
                                                      NULL, NULL, NULL);
705
0
        } else {
706
0
          coap_log_warn("Proxy: (D)TLS not configured for secure session\n");
707
0
        }
708
0
      } else {
709
0
#endif /* COAP_OSCORE_SUPPORT */
710
        /* Not doing OSCORE */
711
0
        if (server_use.dtls_pki) {
712
0
          server_use.dtls_pki->client_sni = client_sni;
713
0
          proxy_entry->ongoing =
714
0
              coap_new_client_session_pki3_lkd(context, local_addr, &dst,
715
0
                                               proto, server_use.dtls_pki,
716
0
                                               NULL, NULL, NULL);
717
0
        } else if (server_use.dtls_cpsk) {
718
0
          server_use.dtls_cpsk->client_sni = client_sni;
719
0
          proxy_entry->ongoing =
720
0
              coap_new_client_session_psk3_lkd(context, local_addr, &dst,
721
0
                                               proto, server_use.dtls_cpsk,
722
0
                                               NULL, NULL, NULL);
723
0
        } else {
724
          /* Using client anonymous PKI */
725
0
          proxy_entry->ongoing =
726
0
              coap_new_client_session3_lkd(context, local_addr, &dst, proto,
727
0
                                           NULL, NULL, NULL);
728
0
        }
729
0
#if COAP_OSCORE_SUPPORT
730
0
      }
731
0
#endif /* COAP_OSCORE_SUPPORT */
732
0
      break;
733
0
    case COAP_URI_SCHEME_HTTP:
734
0
    case COAP_URI_SCHEME_HTTPS:
735
0
    case COAP_URI_SCHEME_LAST:
736
0
    default:
737
0
      assert(0);
738
0
      break;
739
0
    }
740
0
    if (proxy_entry->ongoing == NULL) {
741
0
      response->code = COAP_RESPONSE_CODE(505);
742
0
      coap_proxy_remove_association(session, 0);
743
0
      return NULL;
744
0
    }
745
0
    if (proxy_entry_created) {
746
0
      coap_log_debug("*  %s: proxy_entry %.*s:%d %p created (tot count = % " PRIdS ")\n",
747
0
                     coap_session_str(proxy_entry->ongoing),
748
0
                     (int)proxy_entry->uri.host.length, proxy_entry->uri.host.s,
749
0
                     proxy_entry->uri.port,
750
0
                     (void *)proxy_entry,
751
0
                     session->context->proxy_list_count);
752
0
    }
753
0
  } else if (proxy_entry->ongoing->session_failed) {
754
0
    if (!coap_session_reconnect(proxy_entry->ongoing)) {
755
      /* Server is not yet back up */
756
0
      return NULL;
757
0
    }
758
0
  }
759
760
0
  return proxy_entry;
761
0
}
762
763
static void
764
coap_proxy_release_body_data(coap_session_t *session COAP_UNUSED,
765
0
                             void *app_ptr) {
766
0
  coap_delete_binary(app_ptr);
767
0
}
768
769
static coap_proxy_req_t *
770
coap_proxy_get_req(coap_proxy_entry_t *proxy_entry, coap_proxy_cache_t *proxy_cache,
771
0
                   coap_session_t *session) {
772
0
  coap_proxy_req_t *proxy_req;
773
774
0
  LL_FOREACH(proxy_entry->proxy_req, proxy_req) {
775
0
    if (proxy_req->incoming == session && proxy_req->proxy_cache == proxy_cache) {
776
0
      return proxy_req;
777
0
    }
778
0
  }
779
0
  return NULL;
780
0
}
781
782
static void
783
0
coap_proxy_free_response_data(coap_session_t *session COAP_UNUSED, void *app_ptr) {
784
0
  coap_delete_bin_const(app_ptr);
785
0
}
786
787
static coap_response_t
788
coap_proxy_call_response_handler(coap_session_t *session, const coap_pdu_t *sent,
789
                                 coap_pdu_t *rcvd, coap_bin_const_t *token,
790
                                 coap_proxy_req_t *proxy_req, int replace_mid,
791
0
                                 int remove_observe) {
792
0
  coap_response_t ret = COAP_RESPONSE_FAIL;
793
0
  coap_pdu_t *resp_pdu;
794
0
  coap_pdu_t *fwd_pdu = NULL;
795
0
  size_t size;
796
0
  size_t offset;
797
0
  size_t total;
798
0
  const uint8_t *data;
799
0
  coap_string_t *l_query = NULL;
800
0
  coap_opt_filter_t opt_filter;
801
802
0
  coap_option_filter_clear(&opt_filter);
803
0
  if (!coap_option_check_critical(session, rcvd, &opt_filter, COAP_CRIT_PROXY)) {
804
    /* Need to send back a 5.02 response */
805
0
    coap_send_error_lkd(session, rcvd, COAP_RESPONSE_CODE(502), &opt_filter);
806
0
    return COAP_RESPONSE_FAIL;
807
0
  }
808
809
0
  if (remove_observe) {
810
0
    coap_opt_filter_t drop_options;
811
812
0
    memset(&drop_options, 0, sizeof(coap_opt_filter_t));
813
0
    coap_option_filter_set(&drop_options, COAP_OPTION_OBSERVE);
814
    /* Correct the token */
815
0
    resp_pdu = coap_pdu_duplicate_lkd(rcvd, session, token->length, token->s,
816
0
                                      &drop_options, COAP_BOOL_FALSE);
817
0
  } else {
818
    /* Correct the token */
819
0
    resp_pdu = coap_pdu_duplicate_lkd(rcvd, session, token->length, token->s,
820
0
                                      NULL, COAP_BOOL_FALSE);
821
0
  }
822
0
  if (!resp_pdu)
823
0
    return COAP_RESPONSE_FAIL;
824
825
0
  assert(proxy_req);
826
  /* Incase change in type of request over the proxy */
827
0
  if (proxy_req->pdu)
828
0
    resp_pdu->type = proxy_req->pdu->type;
829
830
0
  if (COAP_PROTO_NOT_RELIABLE(session->proto))
831
0
    resp_pdu->max_size = coap_session_max_pdu_size_lkd(session);
832
833
0
  if (replace_mid)
834
0
    resp_pdu->mid = sent->mid;
835
836
0
  proxy_req->mid =  resp_pdu->mid;
837
838
  /* We sent early ACK, and this is an ACK, need to convert it to CON */
839
0
  if (COAP_PROTO_NOT_RELIABLE(session->proto) && resp_pdu->type == COAP_MESSAGE_ACK) {
840
0
    resp_pdu->type = COAP_MESSAGE_CON;
841
0
  }
842
843
0
  if (coap_get_data_large(rcvd, &size, &data, &offset, &total)) {
844
0
    uint16_t media_type = 0;
845
0
    int maxage = -1;
846
0
    uint64_t etag = 0;
847
0
    coap_opt_t *option;
848
0
    coap_opt_iterator_t opt_iter;
849
0
    coap_bin_const_t *body;
850
851
    /* COAP_BLOCK_SINGLE_BODY is set, so single body should be given */
852
0
    assert(size == total);
853
854
0
    body = coap_new_bin_const(data, size);
855
0
    if (!body) {
856
0
      coap_log_debug("coap_proxy_call_response_handler: copy data error\n");
857
0
      goto failed;
858
0
    }
859
0
    option = coap_check_option(rcvd, COAP_OPTION_CONTENT_FORMAT, &opt_iter);
860
0
    if (option) {
861
0
      media_type = coap_decode_var_bytes(coap_opt_value(option),
862
0
                                         coap_opt_length(option));
863
0
    }
864
0
    option = coap_check_option(rcvd, COAP_OPTION_MAXAGE, &opt_iter);
865
0
    if (option) {
866
0
      maxage = coap_decode_var_bytes(coap_opt_value(option),
867
0
                                     coap_opt_length(option));
868
0
    }
869
0
    option = coap_check_option(rcvd, COAP_OPTION_ETAG, &opt_iter);
870
0
    if (option) {
871
0
      etag = coap_decode_var_bytes8(coap_opt_value(option),
872
0
                                    coap_opt_length(option));
873
0
    }
874
0
    if (sent)
875
0
      l_query = coap_get_query(sent);
876
0
    if (!coap_add_data_large_response_lkd(proxy_req->resource, session, sent,
877
0
                                          resp_pdu,
878
0
                                          l_query,
879
0
                                          media_type, maxage, etag, body->length,
880
0
                                          body->s,
881
0
                                          coap_proxy_free_response_data,
882
0
                                          body)) {
883
0
      coap_log_debug("coap_proxy_call_response_handler: add data error\n");
884
0
      goto failed;
885
0
    }
886
0
  }
887
0
  coap_lock_callback_ret_release(fwd_pdu,
888
0
                                 session->context->proxy_response_cb(session,
889
0
                                     sent,
890
0
                                     resp_pdu,
891
0
                                     proxy_req->cache_key),
892
                                 /* context is being freed off */
893
0
                                 goto failed);
894
0
  if (fwd_pdu) {
895
0
    ret = COAP_RESPONSE_OK;
896
0
    if (coap_send_lkd(session, fwd_pdu) == COAP_INVALID_MID) {
897
0
      ret = COAP_RESPONSE_FAIL;
898
0
    }
899
0
    if (fwd_pdu != resp_pdu) {
900
      /* Application created a new PDU */
901
0
      coap_delete_pdu_lkd(resp_pdu);
902
0
    }
903
0
  } else {
904
0
failed:
905
0
    ret = COAP_RESPONSE_FAIL;
906
0
    coap_delete_pdu_lkd(resp_pdu);
907
0
  }
908
0
  coap_delete_string(l_query);
909
0
  return ret;
910
0
}
911
912
int COAP_API
913
coap_proxy_forward_request(coap_session_t *session,
914
                           const coap_pdu_t *request,
915
                           coap_pdu_t *response,
916
                           coap_resource_t *resource,
917
                           coap_cache_key_t *cache_key,
918
0
                           coap_proxy_server_list_t *server_list) {
919
0
  int ret;
920
921
0
  coap_lock_lock(return 0);
922
0
  ret = coap_proxy_forward_request_lkd(session,
923
0
                                       request,
924
0
                                       response,
925
0
                                       resource,
926
0
                                       cache_key,
927
0
                                       server_list);
928
0
  coap_lock_unlock();
929
0
  return ret;
930
0
}
931
932
/* https://rfc-editor.org/rfc/rfc7641#section-3.6 */
933
static const uint16_t coap_proxy_ignore_options[] = { COAP_OPTION_ETAG,
934
                                                      COAP_OPTION_RTAG,
935
                                                      COAP_OPTION_BLOCK2,
936
                                                      COAP_OPTION_Q_BLOCK2,
937
                                                      COAP_OPTION_OSCORE
938
                                                    };
939
940
int
941
coap_proxy_forward_request_lkd(coap_session_t *session,
942
                               const coap_pdu_t *request,
943
                               coap_pdu_t *response,
944
                               coap_resource_t *resource,
945
                               coap_cache_key_t *cache_key,
946
0
                               coap_proxy_server_list_t *server_list) {
947
0
  coap_proxy_entry_t *proxy_entry;
948
0
  size_t size;
949
0
  size_t offset;
950
0
  size_t total;
951
0
  coap_binary_t *body_data = NULL;
952
0
  const uint8_t *data;
953
0
  coap_pdu_t *pdu = NULL;
954
0
  coap_bin_const_t r_token = coap_pdu_get_token(request);
955
0
  uint8_t token[8];
956
0
  size_t token_len;
957
0
  coap_proxy_req_t *proxy_req = NULL;
958
0
  coap_optlist_t *optlist = NULL;
959
0
  coap_opt_t *option;
960
0
  coap_opt_iterator_t opt_iter;
961
0
  coap_uri_t uri;
962
0
  coap_opt_t *obs_opt = coap_check_option(request,
963
0
                                          COAP_OPTION_OBSERVE,
964
0
                                          &opt_iter);
965
0
  coap_proxy_cache_t *proxy_cache = NULL;
966
0
  coap_proxy_t proxy_type;
967
0
  coap_tick_t now;
968
0
  uint32_t new_maxage;
969
0
  uint8_t buf[4];
970
971
0
  coap_ticks(&now);
972
973
  /* Set up ongoing session (if not already done) */
974
0
  proxy_entry = coap_proxy_get_ongoing_session(session, request, response,
975
0
                                               server_list);
976
0
  if (!proxy_entry) {
977
    /* Error response code already set */
978
0
    return 0;
979
0
  }
980
0
  coap_proxy_log_entry(session, request, NULL, "req");
981
982
  /* Is this a observe cached request? */
983
0
  if (obs_opt && session->context->proxy_response_cb) {
984
0
    coap_cache_key_t *cache_key_l;
985
986
0
    cache_key_l = coap_cache_derive_key_w_ignore(session, request,
987
0
                                                 COAP_CACHE_NOT_SESSION_BASED,
988
0
                                                 coap_proxy_ignore_options,
989
0
                                                 sizeof(coap_proxy_ignore_options)/sizeof(coap_proxy_ignore_options[0]));
990
0
    if (!cache_key_l) {
991
0
      response->code = COAP_RESPONSE_CODE(505);
992
0
      return 0;
993
0
    }
994
0
    PROXY_CACHE_FIND(proxy_entry->rsp_cache, cache_key_l, proxy_cache);
995
0
    coap_delete_cache_key(cache_key_l);
996
0
  }
997
998
0
  if (proxy_cache) {
999
0
    proxy_req = coap_proxy_get_req(proxy_entry, proxy_cache, session);
1000
0
    if (proxy_req) {
1001
0
      if (obs_opt) {
1002
0
        int observe_action;
1003
1004
0
        observe_action = coap_decode_var_bytes(coap_opt_value(obs_opt),
1005
0
                                               coap_opt_length(obs_opt));
1006
1007
0
        if (observe_action == COAP_OBSERVE_CANCEL) {
1008
0
          if (proxy_req->doing_observe) {
1009
0
            assert(proxy_req->incoming->ref_proxy_subs);
1010
0
            proxy_req->incoming->ref_proxy_subs--;
1011
0
            proxy_req->doing_observe = 0;
1012
0
          }
1013
0
          if (proxy_entry->proxy_req && !proxy_entry->proxy_req->next &&
1014
0
              proxy_req->token_used) {
1015
0
            coap_binary_t tmp;
1016
1017
0
            coap_log_debug("coap_proxy_forward_request: Using coap_cancel_observe() to do Proxy OBSERVE cancellation\n");
1018
            /* Unfortunately need to change the ptr type to be r/w */
1019
0
            memcpy(&tmp.s, &proxy_req->token_used->s, sizeof(tmp.s));
1020
0
            tmp.length = proxy_req->token_used->length;
1021
0
            coap_cancel_observe_lkd(proxy_entry->ongoing, &tmp, COAP_MESSAGE_CON);
1022
            /* Let the upstream cancellation be the response */
1023
0
            return 1;
1024
0
          } else {
1025
0
            obs_opt = 0;
1026
0
            goto return_cached_info;
1027
0
          }
1028
0
        } else if (observe_action == COAP_OBSERVE_ESTABLISH) {
1029
          /* Client must be re-registering */
1030
0
          if (proxy_cache && (proxy_cache->expire + COAP_TICKS_PER_SECOND) < now) {
1031
            /* Need to get an updated rsp_pdu */
1032
0
            coap_proxy_del_req(proxy_entry, proxy_req);
1033
0
            proxy_req = NULL;
1034
0
            proxy_cache = NULL;
1035
0
          } else {
1036
0
            goto return_cached_info;
1037
0
          }
1038
0
        }
1039
0
      } else {
1040
0
        if (proxy_cache && (proxy_cache->expire + COAP_TICKS_PER_SECOND) < now) {
1041
          /* Need to get an updated rsp_pdu */
1042
0
          coap_proxy_del_req(proxy_entry, proxy_req);
1043
0
          proxy_req = NULL;
1044
0
          proxy_cache = NULL;
1045
0
        } else {
1046
0
          goto return_cached_info;
1047
0
        }
1048
0
      }
1049
0
    }
1050
0
  }
1051
1052
0
  if (!proxy_req) {
1053
0
    proxy_req = coap_malloc_type(COAP_STRING, sizeof(coap_proxy_req_t));
1054
0
    if (proxy_req == NULL) {
1055
0
      goto failed;
1056
0
    }
1057
0
    memset(proxy_req, 0, sizeof(coap_proxy_req_t));
1058
0
    LL_PREPEND(proxy_entry->proxy_req, proxy_req);
1059
1060
0
    proxy_req->pdu = coap_const_pdu_reference_lkd(request);
1061
0
    proxy_req->resource = resource;
1062
0
    proxy_req->incoming = session;
1063
0
    proxy_req->cache_key = cache_key;
1064
0
    proxy_req->proxy_cache = proxy_cache;
1065
1066
0
    coap_proxy_log_entry(proxy_req->incoming, proxy_req->pdu, proxy_req->token_used,  "add");
1067
0
  }
1068
1069
0
  if (proxy_cache) {
1070
0
    coap_bin_const_t j_token;
1071
1072
0
    if (obs_opt)
1073
0
      proxy_cache->ref++;
1074
0
    coap_delete_bin_const(proxy_req->token_used);
1075
0
    j_token = coap_pdu_get_token(proxy_cache->rsp_pdu);
1076
0
    proxy_req->token_used = coap_new_bin_const(j_token.s, j_token.length);
1077
0
    if (proxy_req->token_used == NULL) {
1078
0
      goto failed;
1079
0
    }
1080
0
    goto return_cached_info;
1081
0
  }
1082
1083
  /* Get a new token for ongoing session */
1084
0
  coap_session_new_token(proxy_entry->ongoing, &token_len, token);
1085
0
  coap_delete_bin_const(proxy_req->token_used);
1086
0
  proxy_req->token_used = coap_new_bin_const(token, token_len);
1087
0
  if (proxy_req->token_used == NULL) {
1088
0
    goto failed;
1089
0
  }
1090
0
  coap_pdu_release_lkd(proxy_req->pdu);
1091
0
  proxy_req->pdu = coap_const_pdu_reference_lkd(request);
1092
1093
  /* Need to create the ongoing request pdu entry */
1094
0
  proxy_type = coap_proxy_map_type(server_list->type);
1095
0
  if (proxy_type & COAP_PROXY_BIT_STRIP) {
1096
    /*
1097
     * Need to replace Proxy-Uri with Uri-Host (and Uri-Port)
1098
     * and strip out Proxy-Scheme.
1099
     */
1100
1101
    /*
1102
     * Build up the ongoing PDU that we are going to send
1103
     */
1104
0
    pdu = coap_pdu_init(request->type, request->code,
1105
0
                        coap_new_message_id_lkd(proxy_entry->ongoing),
1106
0
                        coap_session_max_pdu_size_lkd(proxy_entry->ongoing));
1107
0
    if (!pdu) {
1108
0
      goto failed;
1109
0
    }
1110
1111
0
    if (coap_is_mcast(&proxy_entry->ongoing->addr_info.remote)) {
1112
0
      pdu->type = COAP_MESSAGE_NON;
1113
0
    }
1114
1115
0
    if (!coap_add_token(pdu, token_len, token)) {
1116
0
      goto failed;
1117
0
    }
1118
1119
    /* Copy the remaining options across */
1120
0
    coap_option_iterator_init(request, &opt_iter, COAP_OPT_ALL);
1121
0
    while ((option = coap_option_next(&opt_iter))) {
1122
0
      switch (opt_iter.number) {
1123
0
      case COAP_OPTION_PROXY_URI:
1124
0
        if (coap_split_proxy_uri(coap_opt_value(option),
1125
0
                                 coap_opt_length(option),
1126
0
                                 &uri) < 0) {
1127
          /* Need to return a 5.05 RFC7252 Section 5.7.2 */
1128
0
          coap_log_warn("Proxy URI not decodable\n");
1129
0
          goto failed;
1130
0
        }
1131
0
        if (!coap_uri_into_optlist(&uri, NULL, &optlist, 0)) {
1132
0
          coap_log_err("Failed to create options for URI\n");
1133
0
          goto failed;
1134
0
        }
1135
0
        break;
1136
0
      case COAP_OPTION_PROXY_SCHEME:
1137
0
        break;
1138
0
      case COAP_OPTION_BLOCK1:
1139
0
      case COAP_OPTION_BLOCK2:
1140
0
      case COAP_OPTION_Q_BLOCK1:
1141
0
      case COAP_OPTION_Q_BLOCK2:
1142
        /* These are not passed on */
1143
0
        break;
1144
0
      case COAP_OPTION_URI_HOST:
1145
0
      case COAP_OPTION_URI_PORT:
1146
0
        break;
1147
0
      default:
1148
0
        coap_insert_optlist(&optlist,
1149
0
                            coap_new_optlist(opt_iter.number,
1150
0
                                             coap_opt_length(option),
1151
0
                                             coap_opt_value(option)));
1152
0
        break;
1153
0
      }
1154
0
    }
1155
1156
    /* Update pdu with options */
1157
0
    coap_add_optlist_pdu(pdu, &optlist);
1158
0
    coap_delete_optlist(optlist);
1159
0
    optlist = NULL;
1160
0
  } else {
1161
    /*
1162
     * Duplicate request PDU for onward transmission (with new token).
1163
     */
1164
0
    pdu = coap_pdu_duplicate_lkd(request, proxy_entry->ongoing, token_len,
1165
0
                                 token, NULL, COAP_BOOL_FALSE);
1166
0
    if (!pdu) {
1167
0
      coap_log_debug("proxy: PDU generation error\n");
1168
0
      goto failed;
1169
0
    }
1170
0
    if (COAP_PROTO_NOT_RELIABLE(session->proto))
1171
0
      pdu->max_size = coap_session_max_pdu_size_lkd(proxy_entry->ongoing);
1172
0
  }
1173
1174
0
  if (coap_get_data_large(request, &size, &data, &offset, &total)) {
1175
    /* COAP_BLOCK_SINGLE_BODY is set, so single body should be given */
1176
0
    assert(size == total);
1177
    /*
1178
     * Need to take a copy of the data as request PDU may go away before
1179
     * all data is transmitted.
1180
     */
1181
0
    body_data = coap_new_binary(total);
1182
0
    if (!body_data) {
1183
0
      coap_log_debug("proxy: body build memory error\n");
1184
0
      goto failed;
1185
0
    }
1186
0
    memcpy(body_data->s, data, size);
1187
0
    if (!coap_add_data_large_request_lkd(proxy_entry->ongoing, pdu, total, data,
1188
0
                                         coap_proxy_release_body_data, body_data)) {
1189
0
      coap_log_debug("proxy: add data error\n");
1190
0
      goto failed;
1191
0
    }
1192
0
  }
1193
1194
0
  coap_proxy_log_entry(proxy_req->incoming, proxy_req->pdu, proxy_req->token_used,  "fwd");
1195
0
  if (coap_send_lkd(proxy_entry->ongoing, pdu) == COAP_INVALID_MID) {
1196
0
    pdu = NULL;
1197
0
    coap_log_debug("proxy: upstream PDU send error\n");
1198
0
    goto failed;
1199
0
  }
1200
1201
  /*
1202
   * Do not update the response code (hence empty ACK) as will be sending
1203
   * separate response when response comes back from upstream server
1204
   */
1205
1206
0
  return 1;
1207
1208
0
failed:
1209
0
  response->code = COAP_RESPONSE_CODE(500);
1210
0
  coap_delete_optlist(optlist);
1211
0
  coap_delete_pdu_lkd(pdu);
1212
0
  return 0;
1213
1214
0
return_cached_info:
1215
0
  if (obs_opt && !proxy_req->doing_observe) {
1216
0
    int observe_action;
1217
1218
0
    observe_action = coap_decode_var_bytes(coap_opt_value(obs_opt),
1219
0
                                           coap_opt_length(obs_opt));
1220
1221
0
    if (observe_action == COAP_OBSERVE_ESTABLISH) {
1222
0
      proxy_req->doing_observe = 1;
1223
0
      proxy_req->incoming->ref_proxy_subs++;
1224
0
    }
1225
0
  }
1226
0
  coap_log_debug("*  %s: Using Proxy Cache (Active %d)\n",
1227
0
                 coap_session_str(session),
1228
0
                 proxy_cache->ref);
1229
0
  coap_proxy_log_entry(session, request, &proxy_cache->rsp_pdu->actual_token,  "rspc");
1230
0
  if (now < proxy_cache->expire) {
1231
0
    new_maxage = (uint32_t)((proxy_cache->expire - now) / COAP_TICKS_PER_SECOND);
1232
0
    if (new_maxage == 0)
1233
0
      new_maxage = 1;
1234
0
  } else {
1235
0
    new_maxage = 1;
1236
0
  }
1237
0
  coap_update_option(proxy_cache->rsp_pdu,
1238
0
                     COAP_OPTION_MAXAGE,
1239
0
                     coap_encode_var_safe(buf, sizeof(buf), new_maxage), buf);
1240
0
  coap_proxy_call_response_handler(session, request, proxy_cache->rsp_pdu,
1241
0
                                   &r_token, proxy_req, 1, obs_opt ? 0 : 1);
1242
0
  if (!obs_opt)
1243
0
    coap_proxy_del_req(proxy_entry, proxy_req);
1244
0
  return 1;
1245
0
}
1246
1247
coap_proxy_req_t *
1248
coap_proxy_map_outgoing_request(coap_session_t *ongoing,
1249
                                const coap_pdu_t *received,
1250
0
                                coap_proxy_entry_t **u_proxy_entry) {
1251
0
  coap_proxy_entry_t *proxy_list = ongoing->context->proxy_list;
1252
0
  size_t proxy_list_count = ongoing->context->proxy_list_count;
1253
0
  size_t i;
1254
0
  coap_bin_const_t rcv_token = coap_pdu_get_token(received);
1255
0
  coap_proxy_entry_t *proxy_entry = NULL;
1256
0
  coap_proxy_req_t *proxy_req;
1257
1258
0
  for (i = 0; i < proxy_list_count; i++) {
1259
0
    proxy_entry = &proxy_list[i];
1260
0
    if (proxy_entry->ongoing == ongoing) {
1261
0
      LL_FOREACH(proxy_entry->proxy_req, proxy_req) {
1262
0
        if (coap_binary_equal(&rcv_token, proxy_req->token_used)) {
1263
0
          coap_ticks(&proxy_entry->last_used);
1264
0
          if (u_proxy_entry)
1265
0
            *u_proxy_entry = proxy_entry;
1266
0
          return proxy_req;
1267
0
        }
1268
0
      }
1269
0
    }
1270
0
  }
1271
0
  if (coap_get_log_level() >= COAP_LOG_DEBUG) {
1272
0
    char scratch[24];
1273
0
    size_t size;
1274
1275
0
    scratch[0] = '\000';
1276
0
    for (i = 0; i < rcv_token.length; i++) {
1277
0
      size = strlen(scratch);
1278
0
      snprintf(&scratch[size], sizeof(scratch)-size,
1279
0
               "%02x", rcv_token.s[i]);
1280
0
    }
1281
0
    coap_log_debug("   proxy token to match {%s}\n", scratch);
1282
0
    for (i = 0; i < proxy_list_count; i++) {
1283
0
      proxy_entry = &proxy_list[i];
1284
0
      LL_FOREACH(proxy_entry->proxy_req, proxy_req) {
1285
0
        coap_proxy_log_entry(proxy_req->incoming, proxy_req->pdu,  proxy_req->token_used, "miss");
1286
0
      }
1287
0
    }
1288
0
  }
1289
0
  return NULL;
1290
0
}
1291
1292
coap_response_t COAP_API
1293
coap_proxy_forward_response(coap_session_t *session,
1294
                            const coap_pdu_t *received,
1295
0
                            coap_cache_key_t **cache_key) {
1296
0
  int ret;
1297
1298
0
  coap_lock_lock(return 0);
1299
0
  ret = coap_proxy_forward_response_lkd(session,
1300
0
                                        received,
1301
0
                                        cache_key);
1302
0
  coap_lock_unlock();
1303
0
  return ret;
1304
0
}
1305
1306
coap_response_t
1307
coap_proxy_forward_response_lkd(coap_session_t *session,
1308
                                const coap_pdu_t *received,
1309
0
                                coap_cache_key_t **cache_key) {
1310
0
  coap_pdu_t *pdu = NULL;
1311
0
  coap_session_t *incoming = NULL;
1312
0
  size_t size;
1313
0
  const uint8_t *data;
1314
0
  coap_optlist_t *optlist = NULL;
1315
0
  coap_opt_t *option;
1316
0
  coap_opt_iterator_t opt_iter;
1317
0
  size_t offset;
1318
0
  size_t total;
1319
0
  coap_proxy_entry_t *proxy_entry = NULL;
1320
0
  uint16_t media_type = COAP_MEDIATYPE_TEXT_PLAIN;
1321
0
  int maxage = -1;
1322
0
  uint64_t etag = 0;
1323
0
  coap_pdu_code_t rcv_code = coap_pdu_get_code(received);
1324
0
  coap_bin_const_t req_token;
1325
0
  coap_binary_t *body_data = NULL;
1326
0
  coap_pdu_t *req_pdu;
1327
0
  coap_resource_t *resource;
1328
0
  coap_proxy_req_t *proxy_req = NULL;
1329
1330
0
  proxy_req = coap_proxy_map_outgoing_request(session, received, &proxy_entry);
1331
0
  if (!proxy_req || proxy_req->incoming->server_list) {
1332
0
    coap_log_warn("Unknown proxy ongoing session response received - ignored\n");
1333
0
    return COAP_RESPONSE_OK;
1334
0
  }
1335
1336
0
  req_pdu = proxy_req->pdu;
1337
0
  req_token = coap_pdu_get_token(req_pdu);
1338
0
  resource = proxy_req->resource;
1339
0
  incoming = proxy_req->incoming;
1340
1341
0
  coap_log_debug("** process upstream incoming %d.%02d response:\n",
1342
0
                 COAP_RESPONSE_CLASS(rcv_code), rcv_code & 0x1F);
1343
1344
0
  if (coap_get_data_large(received, &size, &data, &offset, &total)) {
1345
    /* COAP_BLOCK_SINGLE_BODY is set, so single body should be given */
1346
0
    assert(size == total);
1347
0
    body_data = coap_new_binary(total);
1348
0
    if (!body_data) {
1349
0
      coap_log_debug("body build memory error\n");
1350
0
      goto remove_match;
1351
0
    }
1352
0
    memcpy(body_data->s, data, size);
1353
0
    data = body_data->s;
1354
0
  }
1355
1356
  /*
1357
   * Build up the ongoing PDU that we are going to send to proxy originator
1358
   * as separate response
1359
   */
1360
0
  pdu = coap_pdu_init(req_pdu->type, rcv_code,
1361
0
                      coap_new_message_id_lkd(incoming),
1362
0
                      coap_session_max_pdu_size_lkd(incoming));
1363
0
  if (!pdu) {
1364
0
    coap_log_debug("Failed to create ongoing proxy response PDU\n");
1365
0
    goto remove_match;
1366
0
  }
1367
1368
0
  if (!coap_add_token(pdu, req_token.length, req_token.s)) {
1369
0
    coap_log_debug("cannot add token to ongoing proxy response PDU\n");
1370
0
  }
1371
1372
  /*
1373
   * Copy the options across, skipping those needed for
1374
   * coap_add_data_response_large()
1375
   */
1376
0
  coap_option_iterator_init(received, &opt_iter, COAP_OPT_ALL);
1377
0
  while ((option = coap_option_next(&opt_iter))) {
1378
0
    switch (opt_iter.number) {
1379
0
    case COAP_OPTION_CONTENT_FORMAT:
1380
0
      media_type = coap_decode_var_bytes(coap_opt_value(option),
1381
0
                                         coap_opt_length(option));
1382
0
      break;
1383
0
    case COAP_OPTION_MAXAGE:
1384
0
      maxage = coap_decode_var_bytes(coap_opt_value(option),
1385
0
                                     coap_opt_length(option));
1386
0
      break;
1387
0
    case COAP_OPTION_ETAG:
1388
0
      etag = coap_decode_var_bytes8(coap_opt_value(option),
1389
0
                                    coap_opt_length(option));
1390
0
      break;
1391
0
    case COAP_OPTION_BLOCK2:
1392
0
    case COAP_OPTION_Q_BLOCK2:
1393
0
    case COAP_OPTION_SIZE2:
1394
0
      break;
1395
0
    default:
1396
0
      coap_insert_optlist(&optlist,
1397
0
                          coap_new_optlist(opt_iter.number,
1398
0
                                           coap_opt_length(option),
1399
0
                                           coap_opt_value(option)));
1400
0
      break;
1401
0
    }
1402
0
  }
1403
0
  coap_add_optlist_pdu(pdu, &optlist);
1404
0
  coap_delete_optlist(optlist);
1405
1406
0
  if (size > 0) {
1407
0
    coap_string_t *l_query = coap_get_query(req_pdu);
1408
1409
0
    coap_add_data_large_response_lkd(resource, incoming, req_pdu, pdu,
1410
0
                                     l_query,
1411
0
                                     media_type, maxage, etag, size, data,
1412
0
                                     coap_proxy_release_body_data,
1413
0
                                     body_data);
1414
0
    body_data = NULL;
1415
0
    coap_delete_string(l_query);
1416
0
  }
1417
1418
0
  if (cache_key)
1419
0
    *cache_key = proxy_req->cache_key;
1420
1421
0
  coap_send_lkd(incoming, pdu);
1422
1423
0
remove_match:
1424
0
  option = coap_check_option(received, COAP_OPTION_OBSERVE, &opt_iter);
1425
  /* Need to remove matching token entry (apart from an Observe response) */
1426
0
  if (option == NULL) {
1427
0
    if (proxy_entry->proxy_req) {
1428
      /* Do not delete cache key here - caller's responsibility */
1429
0
      proxy_req->cache_key = NULL;
1430
0
      coap_proxy_del_req(proxy_entry, proxy_req);
1431
0
    }
1432
0
  } else if (!proxy_req->doing_observe) {
1433
0
    option = coap_check_option(proxy_req->pdu, COAP_OPTION_OBSERVE, &opt_iter);
1434
0
    if (option &&
1435
0
        coap_decode_var_bytes(coap_opt_value(option),
1436
0
                              coap_opt_length(option)) == COAP_OBSERVE_ESTABLISH) {
1437
0
      proxy_req->doing_observe = 1;
1438
0
      proxy_req->incoming->ref_proxy_subs++;
1439
0
    }
1440
0
  }
1441
0
  coap_delete_binary(body_data);
1442
0
  return COAP_RESPONSE_OK;
1443
0
}
1444
1445
void
1446
coap_proxy_process_incoming(coap_session_t *session,
1447
                            coap_pdu_t *rcvd,
1448
                            void *body_data, coap_proxy_req_t *proxy_req,
1449
0
                            coap_proxy_entry_t *proxy_entry) {
1450
0
  coap_opt_t *obs_opt;
1451
0
  coap_opt_t *option;
1452
0
  coap_opt_iterator_t opt_iter;
1453
0
  coap_response_t ret = COAP_RESPONSE_FAIL;
1454
0
  coap_bin_const_t token;
1455
0
  int can_cache = 0;
1456
1457
0
  if (COAP_RESPONSE_CLASS(rcvd->code) == 2) {
1458
0
    switch ((int)rcvd->code) {
1459
0
    case COAP_RESPONSE_CODE(201):
1460
0
    case COAP_RESPONSE_CODE(202):
1461
0
    case COAP_RESPONSE_CODE(203):
1462
0
    case COAP_RESPONSE_CODE(204):
1463
0
    case COAP_RESPONSE_CODE(205):
1464
0
      can_cache = 1;
1465
0
      break;
1466
    /* 2.13 should be handled by block transfer logic */
1467
0
    case COAP_RESPONSE_CODE(213):
1468
0
    default:
1469
0
      break;
1470
0
    }
1471
0
  }
1472
0
  obs_opt = coap_check_option(rcvd, COAP_OPTION_OBSERVE, &opt_iter);
1473
1474
  /* See if we are doing proxy caching */
1475
0
  if (can_cache && obs_opt) {
1476
0
    coap_proxy_cache_t *proxy_cache;
1477
0
    coap_cache_key_t *cache_key_l;
1478
0
    coap_tick_t now;
1479
0
    uint64_t expire;
1480
1481
    /* Need to cache the response */
1482
0
    if (proxy_req->proxy_cache) {
1483
0
      coap_delete_pdu_lkd(proxy_req->proxy_cache->rsp_pdu);
1484
0
      proxy_cache = proxy_req->proxy_cache;
1485
0
    } else {
1486
0
      proxy_cache = coap_malloc_type(COAP_STRING, sizeof(coap_proxy_cache_t));
1487
0
      if (proxy_cache == NULL) {
1488
0
        goto cache_fail;
1489
0
      }
1490
0
      memset(proxy_cache, 0, sizeof(coap_proxy_cache_t));
1491
0
      cache_key_l = coap_cache_derive_key_w_ignore(session, proxy_req->pdu,
1492
0
                                                   COAP_CACHE_NOT_SESSION_BASED,
1493
0
                                                   coap_proxy_ignore_options,
1494
0
                                                   sizeof(coap_proxy_ignore_options)/sizeof(coap_proxy_ignore_options[0]));
1495
0
      if (!cache_key_l) {
1496
0
        coap_free_type(COAP_STRING, proxy_cache);
1497
0
        goto cache_fail;
1498
0
      }
1499
0
      memcpy(&proxy_cache->cache_req, cache_key_l,
1500
0
             sizeof(proxy_cache->cache_req));
1501
0
      coap_delete_cache_key(cache_key_l);
1502
1503
0
      proxy_cache->req_pdu = coap_pdu_reference_lkd(proxy_req->pdu);
1504
0
      proxy_req->proxy_cache = proxy_cache;
1505
1506
0
      PROXY_CACHE_ADD(proxy_entry->rsp_cache, proxy_cache);
1507
0
      proxy_cache->ref++;
1508
0
    }
1509
0
    if (rcvd->body_data) {
1510
      /* More data than that held in the PDU */
1511
0
      const uint8_t *data;
1512
0
      size_t data_len;
1513
1514
0
      proxy_cache->rsp_pdu = coap_pdu_duplicate_lkd(rcvd,
1515
0
                                                    session,
1516
0
                                                    rcvd->actual_token.length,
1517
0
                                                    rcvd->actual_token.s,
1518
0
                                                    NULL, COAP_BOOL_FALSE);
1519
0
      if (proxy_cache->rsp_pdu) {
1520
0
        if (coap_get_data(rcvd, &data_len, &data)) {
1521
0
          coap_binary_t *copy;
1522
1523
0
          copy = coap_new_binary(data_len);
1524
0
          if (copy) {
1525
0
            memcpy(copy->s, data, data_len);
1526
0
            proxy_cache->rsp_pdu->data_free = copy;
1527
0
            proxy_cache->rsp_pdu->body_data = copy->s;
1528
0
            proxy_cache->rsp_pdu->body_length = copy->length;
1529
0
            proxy_cache->rsp_pdu->body_total = copy->length;
1530
0
          } else {
1531
0
            proxy_cache->rsp_pdu->body_data = NULL;
1532
0
            proxy_cache->rsp_pdu->body_length = 0;
1533
0
            proxy_cache->rsp_pdu->body_total = 0;
1534
0
          }
1535
0
        }
1536
0
      }
1537
0
    } else {
1538
      /* The simple case */
1539
0
      proxy_cache->rsp_pdu = coap_pdu_reference_lkd(rcvd);
1540
0
    }
1541
0
    option = coap_check_option(rcvd, COAP_OPTION_ETAG, &opt_iter);
1542
0
    if (option) {
1543
0
      proxy_cache->etag = coap_decode_var_bytes8(coap_opt_value(option),
1544
0
                                                 coap_opt_length(option));
1545
0
    } else {
1546
0
      proxy_cache->etag = 0;
1547
0
    }
1548
0
    coap_ticks(&now);
1549
0
    option = coap_check_option(rcvd, COAP_OPTION_MAXAGE, &opt_iter);
1550
0
    if (option) {
1551
0
      expire = coap_decode_var_bytes(coap_opt_value(option),
1552
0
                                     coap_opt_length(option));
1553
0
    } else {
1554
      /* Default is 60 seconds */
1555
0
      expire = 60;
1556
0
    }
1557
0
    proxy_cache->expire = now + expire * COAP_TICKS_PER_SECOND;
1558
1559
    /* Update all the cache listeners */
1560
0
    LL_FOREACH(proxy_entry->proxy_req, proxy_req) {
1561
0
      if (proxy_req->proxy_cache == proxy_cache) {
1562
0
        if (!proxy_req->doing_observe) {
1563
0
          coap_opt_t *req_obs;
1564
1565
0
          req_obs = coap_check_option(proxy_req->pdu, COAP_OPTION_OBSERVE, &opt_iter);
1566
0
          if (req_obs &&
1567
0
              coap_decode_var_bytes(coap_opt_value(req_obs),
1568
0
                                    coap_opt_length(req_obs)) == COAP_OBSERVE_ESTABLISH) {
1569
0
            proxy_req->doing_observe = 1;
1570
0
            proxy_req->incoming->ref_proxy_subs++;
1571
0
          }
1572
0
        }
1573
0
        coap_proxy_log_entry(proxy_req->incoming, proxy_req->pdu, proxy_req->token_used,  "rsp");
1574
0
        token = coap_pdu_get_token(proxy_req->pdu);
1575
0
        if (coap_proxy_call_response_handler(proxy_req->incoming, proxy_req->pdu,
1576
0
                                             rcvd, &token,
1577
0
                                             proxy_req, 0, 0) == COAP_RESPONSE_OK) {
1578
          /* At least one success */
1579
0
          ret = COAP_RESPONSE_OK;
1580
0
        }
1581
0
      }
1582
0
    }
1583
0
    goto finish;
1584
0
  }
1585
0
cache_fail:
1586
0
  coap_proxy_log_entry(proxy_req->incoming, proxy_req->pdu, proxy_req->token_used,  "rspn");
1587
0
  token = coap_pdu_get_token(proxy_req->pdu);
1588
0
  ret = coap_proxy_call_response_handler(proxy_req->incoming, proxy_req->pdu,
1589
0
                                         rcvd, &token, proxy_req, 0, 0);
1590
0
  if (!obs_opt)
1591
0
    coap_proxy_del_req(proxy_entry, proxy_req);
1592
1593
0
finish:
1594
0
  if (ret == COAP_RESPONSE_FAIL && rcvd->type != COAP_MESSAGE_ACK) {
1595
0
    coap_send_rst_lkd(session, rcvd);
1596
0
    session->last_con_handler_res = COAP_RESPONSE_FAIL;
1597
0
  } else {
1598
0
    coap_send_ack_lkd(session, rcvd);
1599
0
    session->last_con_handler_res = COAP_RESPONSE_OK;
1600
0
  }
1601
0
  coap_free_type(COAP_STRING, body_data);
1602
0
}
1603
1604
/*
1605
 */
1606
coap_mid_t
1607
0
coap_proxy_local_write(coap_session_t *session, coap_pdu_t *pdu) {
1608
0
  coap_pdu_t *response = NULL;
1609
0
  coap_resource_t *resource;
1610
0
  coap_mid_t mid = COAP_INVALID_MID;
1611
1612
0
  resource = session->context->unknown_resource ?
1613
0
             session->context->unknown_resource :
1614
0
             session->context->proxy_uri_resource;
1615
0
  if (!resource) {
1616
0
    coap_log_err("coap_proxy_local_write: Unknown or Proxy resource not defined\n");
1617
0
    goto fail;
1618
0
  }
1619
1620
0
  response = coap_pdu_init(pdu->type == COAP_MESSAGE_CON ?
1621
0
                           COAP_MESSAGE_ACK : COAP_MESSAGE_NON,
1622
0
                           0, pdu->mid, coap_session_max_pdu_size_lkd(session));
1623
0
  if (!response) {
1624
0
    coap_log_err("coap_proxy_local_write: Could not create response PDU\n");
1625
0
    goto fail;
1626
0
  }
1627
0
  response->session = session;
1628
1629
0
  if (!coap_add_token(response, pdu->actual_token.length,
1630
0
                      pdu->actual_token.s)) {
1631
0
    goto fail;
1632
0
  }
1633
1634
0
  coap_log_debug("*  %s: internal: sent %4" PRIdS " bytes\n",
1635
0
                 coap_session_str(session),
1636
0
                 pdu->used_size + coap_pdu_encode_header(pdu, session->proto));
1637
0
  coap_show_pdu(COAP_LOG_DEBUG, pdu);
1638
1639
0
  mid = pdu->mid;
1640
0
  if (!coap_proxy_forward_request_lkd(session, pdu, response, resource,
1641
0
                                      NULL, session->server_list)) {
1642
0
    coap_log_debug("coap_proxy_local_write: Failed to forward PDU\n");
1643
0
    mid = COAP_INVALID_MID;
1644
0
  }
1645
0
fail:
1646
0
  coap_delete_pdu_lkd(response);
1647
0
  coap_delete_pdu_lkd(pdu);
1648
0
  return mid;
1649
0
}
1650
1651
COAP_API coap_session_t *
1652
coap_new_client_session_proxy(coap_context_t *ctx,
1653
0
                              coap_proxy_server_list_t *server_list) {
1654
0
  coap_session_t *session;
1655
1656
0
  coap_lock_lock(return NULL);
1657
0
  session = coap_new_client_session_proxy_lkd(ctx, server_list);
1658
0
  coap_lock_unlock();
1659
0
  return session;
1660
0
}
1661
1662
coap_session_t *
1663
coap_new_client_session_proxy_lkd(coap_context_t *ctx,
1664
0
                                  coap_proxy_server_list_t *server_list) {
1665
0
  coap_session_t *session;
1666
0
  coap_addr_info_t *info_list = NULL;
1667
0
  coap_str_const_t remote;
1668
1669
0
  coap_lock_check_locked();
1670
1671
0
#if COAP_IPV6_SUPPORT
1672
0
  remote.s = (const uint8_t *)"::1";
1673
#elif COAP_IPV4_SUPPORT
1674
  remote.s = (const uint8_t *)"127.0.0.1";
1675
#else /* !COAP_IPV6_SUPPORT && ! COAP_IPV4_SUPPORT */
1676
  coap_log_warn("coap_new_client_session_proxy: No IPv4 or IPv6 support\n");
1677
  return NULL;
1678
#endif /* !COAP_IPV6_SUPPORT && ! COAP_IPV4_SUPPORT */
1679
0
  remote.length = strlen((const char *)remote.s);
1680
  /* resolve internal remote address where proxy session is 'connecting' to */
1681
0
  info_list = coap_resolve_address_info_lkd(&remote, 0, 0, 0, 0,
1682
0
                                            0,
1683
0
                                            1 << COAP_URI_SCHEME_COAP,
1684
0
                                            COAP_RESOLVE_TYPE_REMOTE);
1685
0
  if (!info_list) {
1686
0
    coap_log_warn("coap_new_client_session_proxy: Unable to resolve IP address\n");
1687
0
    return NULL;
1688
0
  }
1689
1690
0
  session = coap_new_client_session3_lkd(ctx, NULL, &info_list->addr, COAP_PROTO_UDP,
1691
0
                                         NULL, NULL, NULL);
1692
1693
0
  if (session) {
1694
0
    session->server_list = server_list;
1695
0
  }
1696
0
  coap_free_address_info(info_list);
1697
0
  return session;
1698
0
}
1699
1700
COAP_API coap_proxy_entry_t *
1701
coap_proxy_fwd_add_client_session(coap_session_t *session, const char *use_ip,
1702
                                  uint16_t use_port,
1703
0
                                  coap_proxy_server_list_t *server_list) {
1704
0
  coap_proxy_entry_t *ret;
1705
1706
0
  coap_lock_lock(return 0);
1707
0
  ret = coap_proxy_fwd_add_client_session_lkd(session, use_ip, use_port, server_list);
1708
0
  coap_lock_unlock();
1709
0
  return ret;
1710
0
}
1711
1712
coap_proxy_entry_t *
1713
coap_proxy_fwd_add_client_session_lkd(coap_session_t *session, const char *use_ip,
1714
                                      uint16_t use_port,
1715
0
                                      coap_proxy_server_list_t *server_list) {
1716
0
#if !defined(WITH_LWIP) && !defined(WITH_CONTIKI)
1717
0
  char addr[INET6_ADDRSTRLEN];
1718
#else /* WITH_LWIP || WITH_CONTIKI */
1719
  char addr[40];
1720
#endif /* WITH_LWIP || WITH_CONTIKI */
1721
0
  coap_proxy_entry_t *proxy_entry;
1722
0
  coap_uri_t uri;
1723
1724
0
  if (!use_ip && !coap_print_ip_addr(&session->addr_info.remote, addr, sizeof(addr))) {
1725
0
    return NULL;
1726
0
  }
1727
0
  if (!use_ip)
1728
0
    use_ip = addr;
1729
1730
0
  memset(&uri, 0, sizeof(uri));
1731
0
  uri.port = use_port;
1732
0
  switch (session->proto) {
1733
0
  case COAP_PROTO_UDP:
1734
0
    uri.scheme = COAP_URI_SCHEME_COAP;
1735
0
    if (uri.port == 0)
1736
0
      uri.port = 5683;
1737
0
    break;
1738
0
  case COAP_PROTO_DTLS:
1739
0
    uri.scheme = COAP_URI_SCHEME_COAPS;
1740
0
    if (uri.port == 0)
1741
0
      uri.port = 5684;
1742
0
    break;
1743
0
  case COAP_PROTO_TCP:
1744
0
    uri.scheme = COAP_URI_SCHEME_COAP_TCP;
1745
0
    if (uri.port == 0)
1746
0
      uri.port = 5683;
1747
0
    break;
1748
0
  case COAP_PROTO_TLS:
1749
0
    uri.scheme = COAP_URI_SCHEME_COAPS_TCP;
1750
0
    if (uri.port == 0)
1751
0
      uri.port = 5684;
1752
0
    break;
1753
0
  case COAP_PROTO_WS:
1754
0
    uri.scheme = COAP_URI_SCHEME_COAP_WS;
1755
0
    if (uri.port == 0)
1756
0
      uri.port = 80;
1757
0
    break;
1758
0
  case COAP_PROTO_WSS:
1759
0
    uri.scheme = COAP_URI_SCHEME_COAPS_WS;
1760
0
    if (uri.port == 0)
1761
0
      uri.port = 443;
1762
0
    break;
1763
0
  case COAP_PROTO_LAST:
1764
0
  case COAP_PROTO_NONE:
1765
0
  default:
1766
0
    return NULL;
1767
0
  }
1768
0
  if (memcmp(use_ip, "::ffff:", 7) == 0) {
1769
    /* IPv4 in IPv6 format */
1770
0
    uri.host.s = (const uint8_t *)&use_ip[7];
1771
0
    uri.host.length = strlen(&use_ip[7]);
1772
0
  } else {
1773
0
    uri.host.s = (const uint8_t *)use_ip;
1774
0
    uri.host.length = strlen(use_ip);
1775
0
  }
1776
0
  if (!uri.host.length)
1777
0
    return NULL;
1778
1779
0
  proxy_entry = coap_proxy_get_add_list_entry(NULL, session, uri, server_list, 1);
1780
0
  if (proxy_entry) {
1781
0
    coap_log_debug("*  %s: proxy_entry %.*s:%d %p created (tot count = % " PRIdS ")\n",
1782
0
                   coap_session_str(proxy_entry->ongoing),
1783
0
                   (int)proxy_entry->uri.host.length, proxy_entry->uri.host.s,
1784
0
                   proxy_entry->uri.port,
1785
0
                   (void *)proxy_entry,
1786
0
                   session->context->proxy_list_count);
1787
0
    proxy_entry->idle_timeout_ticks = 0;
1788
0
    proxy_entry->track_client_session = 0;
1789
0
    coap_session_set_type_client_lkd(session, 1);
1790
0
    return proxy_entry;
1791
0
  }
1792
0
  return NULL;
1793
0
}
1794
1795
void
1796
coap_delete_proxy_subscriber(coap_session_t *session, coap_bin_const_t *token,
1797
0
                             coap_mid_t mid, coap_proxy_subs_delete_t type) {
1798
  /* Need to check is there is a proxy subscription active and delete it */
1799
0
  coap_context_t *context = session->context;
1800
0
  size_t i;
1801
1802
0
  for (i = 0; i < context->proxy_list_count; i++) {
1803
0
    coap_proxy_entry_t *proxy_entry = &context->proxy_list[i];
1804
0
    coap_proxy_req_t *proxy_req, *treq;
1805
1806
0
    LL_FOREACH_SAFE(proxy_entry->proxy_req, proxy_req, treq) {
1807
0
      if (proxy_req->incoming == session) {
1808
0
        coap_bin_const_t req_token;
1809
0
        int match = 0;
1810
1811
0
        req_token = coap_pdu_get_token(proxy_req->pdu);
1812
0
        switch (type) {
1813
0
        case COAP_PROXY_SUBS_ALL:
1814
0
          match = 1;
1815
0
          break;
1816
0
        case COAP_PROXY_SUBS_TOKEN:
1817
0
          if (token && coap_binary_equal(token, &req_token))
1818
0
            match = 1;
1819
0
          break;
1820
0
        case COAP_PROXY_SUBS_MID:
1821
0
          if (proxy_req->mid == mid)
1822
0
            match = 1;
1823
0
          break;
1824
0
        default:
1825
0
          break;
1826
0
        }
1827
1828
0
        if (match && proxy_entry->proxy_req && ! proxy_entry->proxy_req->next &&
1829
0
            proxy_req->token_used) {
1830
0
          coap_binary_t tmp;
1831
1832
0
          coap_log_debug("coap_delete_proxy_subscriber: Using coap_cancel_observe() to do Proxy OBSERVE cancellation\n");
1833
          /* Unfortunately need to change the ptr type to be r/w */
1834
0
          memcpy(&tmp.s, &proxy_req->token_used->s, sizeof(tmp.s));
1835
0
          tmp.length = proxy_req->token_used->length;
1836
0
          coap_cancel_observe_lkd(proxy_entry->ongoing, &tmp, COAP_MESSAGE_CON);
1837
0
        }
1838
0
        coap_proxy_del_req(proxy_entry, proxy_req);
1839
0
      }
1840
0
    }
1841
0
  }
1842
0
}
1843
1844
#else /* ! COAP_PROXY_SUPPORT */
1845
1846
int
1847
coap_proxy_is_supported(void) {
1848
  return 0;
1849
}
1850
1851
COAP_API int
1852
coap_proxy_forward_request(coap_session_t *session,
1853
                           const coap_pdu_t *request,
1854
                           coap_pdu_t *response,
1855
                           coap_resource_t *resource,
1856
                           coap_cache_key_t *cache_key,
1857
                           coap_proxy_server_list_t *server_list) {
1858
  (void)session;
1859
  (void)request;
1860
  (void)resource;
1861
  (void)cache_key;
1862
  (void)server_list;
1863
  response->code = COAP_RESPONSE_CODE(500);
1864
  return 0;
1865
}
1866
1867
COAP_API coap_response_t
1868
coap_proxy_forward_response(coap_session_t *session,
1869
                            const coap_pdu_t *received,
1870
                            coap_cache_key_t **cache_key) {
1871
  (void)session;
1872
  (void)received;
1873
  (void)cache_key;
1874
  return COAP_RESPONSE_OK;
1875
}
1876
1877
int
1878
coap_verify_proxy_scheme_supported(coap_uri_scheme_t scheme) {
1879
  (void)scheme;
1880
  return 0;
1881
}
1882
#endif /* ! COAP_PROXY_SUPPORT */