1/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
3/* coap -- simple implementation of the Constrained Application Protocol (CoAP)
4 *         as defined in RFC 7252
5 *
6 * Copyright (C) 2010--2023 Olaf Bergmann <bergmann@tzi.org> and others
7 *
8 * SPDX-License-Identifier: BSD-2-Clause
9 *
10 * This file is part of the CoAP library libcoap. Please see README for terms
11 * of use.
12 */
13
14#include <string.h>
15#include <stdlib.h>
16#include <stdio.h>
17#include <ctype.h>
18#include <inttypes.h>
19#include <sys/types.h>
20#include <sys/stat.h>
21#include <errno.h>
22#include <signal.h>
23#ifdef _WIN32
24#define strcasecmp _stricmp
25#define strncasecmp _strnicmp
26#include "getopt.c"
27#if !defined(S_ISDIR)
28#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
29#endif
30#ifndef R_OK
31#define R_OK 4
32#endif
33char *strndup(const char *s1, size_t n);
34char *
35strndup(const char *s1, size_t n) {
36  char *copy = (char *)malloc(n + 1);
37  if (copy) {
38    memcpy(copy, s1, n);
39    copy[n] = 0;
40  }
41  return copy;
42}
43#include <io.h>
44#define access _access
45#define fileno _fileno
46#else
47#include <unistd.h>
48#include <sys/select.h>
49#include <sys/socket.h>
50#include <netinet/in.h>
51#include <arpa/inet.h>
52#include <netdb.h>
53#include <dirent.h>
54#include <syslog.h>
55#endif
56
57/*
58 * SERVER_CAN_PROXY=0 can be set by build system if
59 * "./configure --disable-client-mode" is used.
60 */
61#ifndef SERVER_CAN_PROXY
62#define SERVER_CAN_PROXY 1
63#endif
64
65/* Need to refresh time once per sec */
66#define COAP_RESOURCE_CHECK_TIME 1
67
68#include <coap3/coap.h>
69
70#ifndef min
71#define min(a,b) ((a) < (b) ? (a) : (b))
72#endif
73
74static coap_oscore_conf_t *oscore_conf;
75static int doing_oscore = 0;
76
77/* set to 1 to request clean server shutdown */
78static int quit = 0;
79
80/* set to 1 if persist information is to be kept on server shutdown */
81static int keep_persist = 0;
82
83/* changeable clock base (see handle_put_time()) */
84static time_t clock_offset;
85static time_t my_clock_base = 0;
86
87coap_resource_t *time_resource = NULL;
88
89static int resource_flags = COAP_RESOURCE_FLAGS_NOTIFY_CON;
90static int track_observes = 0;
91
92/*
93 * For PKI, if one or more of cert_file, key_file and ca_file is in PKCS11 URI
94 * format, then the remainder of cert_file, key_file and ca_file are treated
95 * as being in DER format to provide consistency across the underlying (D)TLS
96 * libraries.
97 */
98static char *cert_file = NULL; /* certificate and optional private key in PEM,
99                                  or PKCS11 URI*/
100static char *key_file = NULL; /* private key in PEM, DER or PKCS11 URI */
101static char *pkcs11_pin = NULL; /* PKCS11 pin to unlock access to token */
102static char *ca_file = NULL;   /* CA for cert_file - for cert checking in PEM,
103                                  DER or PKCS11 URI */
104static char *root_ca_file = NULL; /* List of trusted Root CAs in PEM */
105static int use_pem_buf = 0; /* Map these cert/key files into memory to test
106                               PEM_BUF logic if set */
107static int is_rpk_not_cert = 0; /* Cert is RPK if set */
108/* Used to hold initial PEM_BUF setup */
109static uint8_t *cert_mem_base = NULL; /* certificate and private key in PEM_BUF */
110static uint8_t *key_mem_base = NULL; /* private key in PEM_BUF */
111static uint8_t *ca_mem_base = NULL;   /* CA for cert checking in PEM_BUF */
112/* Used for verify_pki_sni_callback PEM_BUF temporary holding */
113static uint8_t *cert_mem = NULL; /* certificate and private key in PEM_BUF */
114static uint8_t *key_mem = NULL; /* private key in PEM_BUF */
115static uint8_t *ca_mem = NULL;   /* CA for cert checking in PEM_BUF */
116static size_t cert_mem_len = 0;
117static size_t key_mem_len = 0;
118static size_t ca_mem_len = 0;
119static int verify_peer_cert = 1; /* PKI granularity - by default set */
120#define MAX_KEY   64 /* Maximum length of a pre-shared key in bytes. */
121static uint8_t *key = NULL;
122static ssize_t key_length = 0;
123int key_defined = 0;
124static const char *hint = "CoAP";
125static int support_dynamic = 0;
126static uint32_t block_mode = COAP_BLOCK_USE_LIBCOAP;
127static int echo_back = 0;
128static uint32_t csm_max_message_size = 0;
129static size_t extended_token_size = COAP_TOKEN_DEFAULT_MAX;
130static coap_proto_t use_unix_proto = COAP_PROTO_NONE;
131static int enable_ws = 0;
132static int ws_port = 80;
133static int wss_port = 443;
134
135static coap_dtls_pki_t *setup_pki(coap_context_t *ctx, coap_dtls_role_t role, char *sni);
136
137typedef struct psk_sni_def_t {
138  char *sni_match;
139  coap_bin_const_t *new_key;
140  coap_bin_const_t *new_hint;
141} psk_sni_def_t;
142
143typedef struct valid_psk_snis_t {
144  size_t count;
145  psk_sni_def_t *psk_sni_list;
146} valid_psk_snis_t;
147
148static valid_psk_snis_t valid_psk_snis = {0, NULL};
149
150typedef struct id_def_t {
151  char *hint_match;
152  coap_bin_const_t *identity_match;
153  coap_bin_const_t *new_key;
154} id_def_t;
155
156typedef struct valid_ids_t {
157  size_t count;
158  id_def_t *id_list;
159} valid_ids_t;
160
161static valid_ids_t valid_ids = {0, NULL};
162typedef struct pki_sni_def_t {
163  char *sni_match;
164  char *new_cert;
165  char *new_ca;
166} pki_sni_def_t;
167
168typedef struct valid_pki_snis_t {
169  size_t count;
170  pki_sni_def_t *pki_sni_list;
171} valid_pki_snis_t;
172
173static valid_pki_snis_t valid_pki_snis = {0, NULL};
174
175typedef struct transient_value_t {
176  coap_binary_t *value;
177  size_t ref_cnt;
178} transient_value_t;
179
180/* temporary storage for dynamic resource representations */
181static transient_value_t *example_data_value = NULL;
182static int example_data_media_type = COAP_MEDIATYPE_TEXT_PLAIN;
183
184/* SIGINT handler: set quit to 1 for graceful termination */
185static void
186handle_sigint(int signum COAP_UNUSED) {
187  quit = 1;
188}
189
190#ifndef _WIN32
191/*
192 * SIGUSR2 handler: set quit to 1 for graceful termination
193 * Disable sending out 4.04 for any active observations.
194 * Note: coap_*() functions should not be called at sig interrupt.
195 */
196static void
197handle_sigusr2(int signum COAP_UNUSED) {
198  quit = 1;
199  keep_persist = 1;
200}
201#endif /* ! _WIN32 */
202
203/*
204 * This will return a correctly formed transient_value_t *, or NULL.
205 * If an error, the passed in coap_binary_t * will get deleted.
206 * Note: transient_value->value will never be returned as NULL.
207 */
208static transient_value_t *
209alloc_resource_data(coap_binary_t *value) {
210  transient_value_t *transient_value;
211  if (!value)
212    return NULL;
213  transient_value = coap_malloc(sizeof(transient_value_t));
214  if (!transient_value) {
215    coap_delete_binary(value);
216    return NULL;
217  }
218  transient_value->ref_cnt = 1;
219  transient_value->value = value;
220  return transient_value;
221}
222
223/*
224 * Need to handle race conditions of data being updated (by PUT) and
225 * being read by a blocked response to GET.
226 */
227static void
228release_resource_data(coap_session_t *session COAP_UNUSED,
229                      void *app_ptr) {
230  transient_value_t *transient_value = (transient_value_t *)app_ptr;
231
232  if (!transient_value)
233    return;
234
235  if (--transient_value->ref_cnt > 0)
236    return;
237  coap_delete_binary(transient_value->value);
238  coap_free(transient_value);
239}
240
241/*
242 * Bump the reference count and return reference to data
243 */
244static coap_binary_t
245reference_resource_data(transient_value_t *entry) {
246  coap_binary_t body;
247  if (entry) {
248    /* Bump reference so not removed elsewhere */
249    entry->ref_cnt++;
250    assert(entry->value);
251    body.length = entry->value->length;
252    body.s = entry->value->s;
253  } else {
254    body.length = 0;
255    body.s = NULL;
256  }
257  return body;
258}
259
260#define INDEX "This is a test server made with libcoap (see https://libcoap.net)\n" \
261  "Copyright (C) 2010--2023 Olaf Bergmann <bergmann@tzi.org> and others\n\n"
262
263static void
264hnd_get_index(coap_resource_t *resource,
265              coap_session_t *session,
266              const coap_pdu_t *request,
267              const coap_string_t *query COAP_UNUSED,
268              coap_pdu_t *response) {
269
270  coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
271  coap_add_data_large_response(resource, session, request, response,
272                               query, COAP_MEDIATYPE_TEXT_PLAIN,
273                               0x2ffff, 0, strlen(INDEX),
274                               (const uint8_t *)INDEX, NULL, NULL);
275}
276
277static void
278hnd_get_fetch_time(coap_resource_t *resource,
279                   coap_session_t *session,
280                   const coap_pdu_t *request,
281                   const coap_string_t *query,
282                   coap_pdu_t *response) {
283  unsigned char buf[40];
284  size_t len;
285  time_t now;
286  coap_tick_t t;
287  (void)request;
288  coap_pdu_code_t code = coap_pdu_get_code(request);
289  size_t size;
290  const uint8_t *data;
291  coap_str_const_t *ticks = coap_make_str_const("ticks");
292
293  if (my_clock_base) {
294
295    /* calculate current time */
296    coap_ticks(&t);
297    now = my_clock_base + (t / COAP_TICKS_PER_SECOND);
298
299    /* coap_get_data() sets size to 0 on error */
300    (void)coap_get_data(request, &size, &data);
301
302    if (code == COAP_REQUEST_CODE_GET && query != NULL &&
303        coap_string_equal(query, ticks)) {
304      /* parameter is in query, output ticks */
305      len = snprintf((char *)buf, sizeof(buf), "%" PRIi64, (int64_t)now);
306    } else if (code == COAP_REQUEST_CODE_FETCH && size == ticks->length &&
307               memcmp(data, ticks->s, ticks->length) == 0) {
308      /* parameter is in data, output ticks */
309      len = snprintf((char *)buf, sizeof(buf), "%" PRIi64, (int64_t)now);
310    } else {      /* output human-readable time */
311      struct tm *tmp;
312      tmp = gmtime(&now);
313      if (!tmp) {
314        /* If 'now' is not valid */
315        coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_FOUND);
316        return;
317      } else {
318        len = strftime((char *)buf, sizeof(buf), "%b %d %H:%M:%S", tmp);
319      }
320    }
321    coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
322    coap_add_data_large_response(resource, session, request, response,
323                                 query, COAP_MEDIATYPE_TEXT_PLAIN, 1, 0,
324                                 len,
325                                 buf, NULL, NULL);
326  } else {
327    /* if my_clock_base was deleted, we pretend to have no such resource */
328    coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_FOUND);
329  }
330}
331
332static void
333hnd_put_time(coap_resource_t *resource,
334             coap_session_t *session COAP_UNUSED,
335             const coap_pdu_t *request,
336             const coap_string_t *query COAP_UNUSED,
337             coap_pdu_t *response) {
338  coap_tick_t t;
339  size_t size;
340  const uint8_t *data;
341
342  /* FIXME: re-set my_clock_base to clock_offset if my_clock_base == 0
343   * and request is empty. When not empty, set to value in request payload
344   * (insist on query ?ticks). Return Created or Ok.
345   */
346
347  /* if my_clock_base was deleted, we pretend to have no such resource */
348  coap_pdu_set_code(response, my_clock_base ? COAP_RESPONSE_CODE_CHANGED :
349                    COAP_RESPONSE_CODE_CREATED);
350
351  coap_resource_notify_observers(resource, NULL);
352
353  /* coap_get_data() sets size to 0 on error */
354  (void)coap_get_data(request, &size, &data);
355
356  if (size == 0) {      /* re-init */
357    my_clock_base = clock_offset;
358  } else {
359    my_clock_base = 0;
360    coap_ticks(&t);
361    while (size--)
362      my_clock_base = my_clock_base * 10 + *data++;
363    my_clock_base -= t / COAP_TICKS_PER_SECOND;
364
365    /* Sanity check input value */
366    if (!gmtime(&my_clock_base)) {
367      unsigned char buf[3];
368      coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_REQUEST);
369      coap_add_option(response,
370                      COAP_OPTION_CONTENT_FORMAT,
371                      coap_encode_var_safe(buf, sizeof(buf),
372                                           COAP_MEDIATYPE_TEXT_PLAIN), buf);
373      coap_add_data(response, 22, (const uint8_t *)"Invalid set time value");
374      /* re-init as value is bad */
375      my_clock_base = clock_offset;
376    }
377  }
378}
379
380static void
381hnd_delete_time(coap_resource_t *resource COAP_UNUSED,
382                coap_session_t *session COAP_UNUSED,
383                const coap_pdu_t *request COAP_UNUSED,
384                const coap_string_t *query COAP_UNUSED,
385                coap_pdu_t *response COAP_UNUSED) {
386  my_clock_base = 0;    /* mark clock as "deleted" */
387
388  /* type = request->hdr->type == COAP_MESSAGE_CON  */
389  /*   ? COAP_MESSAGE_ACK : COAP_MESSAGE_NON; */
390}
391
392/*
393 * This logic is used to test out that the client correctly handles a
394 * "separate" response (empty ACK followed by data response at a later stage).
395 */
396static void
397hnd_get_async(coap_resource_t *resource,
398              coap_session_t *session,
399              const coap_pdu_t *request,
400              const coap_string_t *query,
401              coap_pdu_t *response) {
402  unsigned long delay = 4; /* Less than COAP_DEFAULT_LEISURE */
403  size_t size;
404  coap_async_t *async;
405  coap_bin_const_t token = coap_pdu_get_token(request);
406
407  /*
408   * See if this is the initial, or delayed request
409   */
410
411  async = coap_find_async(session, token);
412  if (!async) {
413    /* Set up an async request to trigger delay in the future */
414    if (query) {
415      /* Expect the query to just be the number of seconds to delay */
416      const uint8_t *p = query->s;
417
418      if (isdigit(*p)) {
419        delay = 0;
420        for (size = query->length; size; --size, ++p) {
421          if (!isdigit(*p))
422            break;
423          delay = delay * 10 + (*p - '0');
424        }
425      } else {
426        coap_log_debug("async: query is just a number of seconds to alter delay\n");
427      }
428      if (delay == 0) {
429        coap_log_info("async: delay of 0 not supported\n");
430        coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_REQUEST);
431        return;
432      }
433    }
434    async = coap_register_async(session,
435                                request,
436                                COAP_TICKS_PER_SECOND * delay);
437    if (async == NULL) {
438      coap_pdu_set_code(response, COAP_RESPONSE_CODE_SERVICE_UNAVAILABLE);
439      return;
440    }
441    /*
442     * Not setting response code will cause empty ACK to be sent
443     * if Confirmable
444     */
445    return;
446  }
447  /* no request (observe) or async set up, so this is the delayed request */
448
449  /* Send back the appropriate data */
450  coap_add_data_large_response(resource, session, request, response,
451                               query, COAP_MEDIATYPE_TEXT_PLAIN, -1, 0, 4,
452                               (const uint8_t *)"done", NULL, NULL);
453  coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
454
455  /* async is automatically removed by libcoap on return from this handler */
456}
457
458/*
459 * Large Data GET handler
460 */
461
462#ifndef INITIAL_EXAMPLE_SIZE
463#define INITIAL_EXAMPLE_SIZE 1500
464#endif
465static void
466hnd_get_example_data(coap_resource_t *resource,
467                     coap_session_t *session,
468                     const coap_pdu_t *request,
469                     const coap_string_t *query,
470                     coap_pdu_t *response) {
471  coap_binary_t body;
472  if (!example_data_value) {
473    /* Initialise for the first time */
474    int i;
475    coap_binary_t *value = coap_new_binary(INITIAL_EXAMPLE_SIZE);
476    if (value) {
477      for (i = 0; i < INITIAL_EXAMPLE_SIZE; i++) {
478        if ((i % 10) == 0) {
479          value->s[i] = 'a' + (i/10) % 26;
480        } else {
481          value->s[i] = '0' + i%10;
482        }
483      }
484    }
485    example_data_value = alloc_resource_data(value);
486  }
487  coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
488  body = reference_resource_data(example_data_value);
489  coap_add_data_large_response(resource, session, request, response,
490                               query, example_data_media_type, -1, 0,
491                               body.length,
492                               body.s,
493                               release_resource_data, example_data_value);
494}
495
496static void
497cache_free_app_data(void *data) {
498  coap_binary_t *bdata = (coap_binary_t *)data;
499  coap_delete_binary(bdata);
500}
501
502/*
503 * Large Data PUT handler
504 */
505
506static void
507hnd_put_example_data(coap_resource_t *resource,
508                     coap_session_t *session,
509                     const coap_pdu_t *request,
510                     const coap_string_t *query COAP_UNUSED,
511                     coap_pdu_t *response) {
512  size_t size;
513  const uint8_t *data;
514  coap_opt_iterator_t opt_iter;
515  coap_opt_t *option;
516  size_t offset;
517  size_t total;
518  coap_binary_t *data_so_far;
519
520  if (coap_get_data_large(request, &size, &data, &offset, &total) &&
521      size != total) {
522    /*
523     * A part of the data has been received (COAP_BLOCK_SINGLE_BODY not set).
524     * However, total unfortunately is only an indication, so it is not safe to
525     * allocate a block based on total.  As per
526     * https://rfc-editor.org/rfc/rfc7959#section-4
527     *   o  In a request carrying a Block1 Option, to indicate the current
528     *         estimate the client has of the total size of the resource
529     *         representation, measured in bytes ("size indication").
530     *
531     * coap_cache_ignore_options() must have previously been called with at
532     * least COAP_OPTION_BLOCK1 set as the option value will change per block.
533     */
534    coap_cache_entry_t *cache_entry = coap_cache_get_by_pdu(session,
535                                                            request,
536                                                            COAP_CACHE_IS_SESSION_BASED);
537
538    if (offset == 0) {
539      if (!cache_entry) {
540        /*
541         * Set idle_timeout parameter to COAP_MAX_TRANSMIT_WAIT if you want
542         * early removal on transmission failure. 0 means only delete when
543         * the session is deleted as session_based is set here.
544         */
545        cache_entry = coap_new_cache_entry(session, request,
546                                           COAP_CACHE_NOT_RECORD_PDU,
547                                           COAP_CACHE_IS_SESSION_BASED, 0);
548      } else {
549        data_so_far = coap_cache_get_app_data(cache_entry);
550        if (data_so_far) {
551          coap_delete_binary(data_so_far);
552          data_so_far = NULL;
553        }
554        coap_cache_set_app_data(cache_entry, NULL, NULL);
555      }
556    }
557    if (!cache_entry) {
558      if (offset == 0) {
559        coap_log_warn("Unable to create a new cache entry\n");
560      } else {
561        coap_log_warn("No cache entry available for the non-first BLOCK\n");
562      }
563      coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR);
564      return;
565    }
566
567    if (size) {
568      /* Add in the new data to cache entry */
569      data_so_far = coap_cache_get_app_data(cache_entry);
570      data_so_far = coap_block_build_body(data_so_far, size, data,
571                                          offset, total);
572      /* Yes, data_so_far can be NULL if error */
573      coap_cache_set_app_data(cache_entry, data_so_far, cache_free_app_data);
574    }
575    if (offset + size == total) {
576      /* All the data is now in */
577      data_so_far = coap_cache_get_app_data(cache_entry);
578      coap_cache_set_app_data(cache_entry, NULL, NULL);
579    } else {
580      /* Give us the next block response */
581      coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTINUE);
582      return;
583    }
584  } else {
585    /* single body of data received */
586    data_so_far = coap_new_binary(size);
587    if (data_so_far) {
588      memcpy(data_so_far->s, data, size);
589    }
590  }
591
592  if (example_data_value) {
593    /* pre-existed response */
594    coap_pdu_set_code(response, COAP_RESPONSE_CODE_CHANGED);
595    /* Need to de-reference as value may be in use elsewhere */
596    release_resource_data(session, example_data_value);
597  } else
598    /* just generated response */
599    coap_pdu_set_code(response, COAP_RESPONSE_CODE_CREATED);
600
601  example_data_value = alloc_resource_data(data_so_far);
602  if (!example_data_value) {
603    coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR);
604    return;
605  }
606  if ((option = coap_check_option(request, COAP_OPTION_CONTENT_FORMAT,
607                                  &opt_iter)) != NULL) {
608    example_data_media_type =
609        coap_decode_var_bytes(coap_opt_value(option),
610                              coap_opt_length(option));
611  } else {
612    example_data_media_type = COAP_MEDIATYPE_TEXT_PLAIN;
613  }
614
615  coap_resource_notify_observers(resource, NULL);
616  if (echo_back) {
617    coap_binary_t body;
618
619    body = reference_resource_data(example_data_value);
620    coap_add_data_large_response(resource, session, request, response,
621                                 query, example_data_media_type, -1, 0,
622                                 body.length,
623                                 body.s,
624                                 release_resource_data, example_data_value);
625  }
626}
627
628#if SERVER_CAN_PROXY
629
630#define MAX_USER 128 /* Maximum length of a user name (i.e., PSK
631                      * identity) in bytes. */
632static unsigned char *user = NULL;
633static ssize_t user_length = -1;
634
635static coap_uri_t proxy = { {0, NULL}, 0, {0, NULL}, {0, NULL}, 0 };
636static size_t proxy_host_name_count = 0;
637static const char **proxy_host_name_list = NULL;
638
639typedef struct proxy_list_t {
640  coap_session_t *ongoing;  /* Ongoing session */
641  coap_session_t *incoming; /* Incoming session */
642  coap_binary_t *token;     /* Incoming token */
643  coap_string_t *query;     /* Incoming query */
644  coap_pdu_code_t req_code; /* Incoming request code */
645  coap_pdu_type_t req_type; /* Incoming request type */
646} proxy_list_t;
647
648static proxy_list_t *proxy_list = NULL;
649static size_t proxy_list_count = 0;
650static coap_resource_t *proxy_resource = NULL;
651
652static int
653get_uri_proxy_scheme_info(const coap_pdu_t *request,
654                          coap_opt_t *opt,
655                          coap_uri_t *uri,
656                          coap_string_t **uri_path,
657                          coap_string_t **uri_query) {
658
659  const char *opt_val = (const char *)coap_opt_value(opt);
660  int opt_len = coap_opt_length(opt);
661  coap_opt_iterator_t opt_iter;
662
663  if (opt_len == 9 &&
664      strncasecmp(opt_val, "coaps+tcp", 9) == 0) {
665    uri->scheme = COAP_URI_SCHEME_COAPS_TCP;
666    uri->port = COAPS_DEFAULT_PORT;
667  } else if (opt_len == 8 &&
668             strncasecmp(opt_val, "coap+tcp", 8) == 0) {
669    uri->scheme = COAP_URI_SCHEME_COAP_TCP;
670    uri->port = COAP_DEFAULT_PORT;
671  } else if (opt_len == 5 &&
672             strncasecmp(opt_val, "coaps", 5) == 0) {
673    uri->scheme = COAP_URI_SCHEME_COAPS;
674    uri->port = COAPS_DEFAULT_PORT;
675  } else if (opt_len == 4 &&
676             strncasecmp(opt_val, "coap", 4) == 0) {
677    uri->scheme = COAP_URI_SCHEME_COAP;
678    uri->port = COAP_DEFAULT_PORT;
679  } else {
680    coap_log_warn("Unsupported Proxy Scheme '%*.*s'\n",
681                  opt_len, opt_len, opt_val);
682    return 0;
683  }
684
685  opt = coap_check_option(request, COAP_OPTION_URI_HOST, &opt_iter);
686  if (opt) {
687    uri->host.length = coap_opt_length(opt);
688    uri->host.s = coap_opt_value(opt);
689  } else {
690    coap_log_warn("Proxy Scheme requires Uri-Host\n");
691    return 0;
692  }
693  opt = coap_check_option(request, COAP_OPTION_URI_PORT, &opt_iter);
694  if (opt) {
695    uri->port =
696        coap_decode_var_bytes(coap_opt_value(opt),
697                              coap_opt_length(opt));
698  }
699  *uri_path = coap_get_uri_path(request);
700  if (*uri_path) {
701    uri->path.s = (*uri_path)->s;
702    uri->path.length = (*uri_path)->length;
703  }
704  *uri_query = coap_get_query(request);
705  if (*uri_query) {
706    uri->query.s = (*uri_query)->s;
707    uri->query.length = (*uri_query)->length;
708  }
709  return 1;
710}
711
712static int
713verify_proxy_scheme_supported(coap_uri_scheme_t scheme) {
714
715  /* Sanity check that the connection can be forwarded on */
716  switch (scheme) {
717  case COAP_URI_SCHEME_HTTP:
718  case COAP_URI_SCHEME_HTTPS:
719    coap_log_warn("Proxy URI http or https not supported\n");
720    return 0;
721  case COAP_URI_SCHEME_COAP:
722    break;
723  case COAP_URI_SCHEME_COAPS:
724    if (!coap_dtls_is_supported()) {
725      coap_log_warn("coaps URI scheme not supported for proxy\n");
726      return 0;
727    }
728    break;
729  case COAP_URI_SCHEME_COAP_TCP:
730    if (!coap_tcp_is_supported()) {
731      coap_log_warn("coap+tcp URI scheme not supported for proxy\n");
732      return 0;
733    }
734    break;
735  case COAP_URI_SCHEME_COAPS_TCP:
736    if (!coap_tls_is_supported()) {
737      coap_log_warn("coaps+tcp URI scheme not supported for proxy\n");
738      return 0;
739    }
740    break;
741  case COAP_URI_SCHEME_COAP_WS:
742    if (!coap_ws_is_supported()) {
743      coap_log_warn("coap+ws URI scheme not supported for proxy\n");
744      return 0;
745    }
746    break;
747  case COAP_URI_SCHEME_COAPS_WS:
748    if (!coap_wss_is_supported()) {
749      coap_log_warn("coaps+ws URI scheme not supported for proxy\n");
750      return 0;
751    }
752    break;
753  case COAP_URI_SCHEME_LAST:
754  default:
755    coap_log_warn("%d URI scheme not supported\n", scheme);
756    break;
757  }
758  return 1;
759}
760
761static coap_dtls_cpsk_t *
762setup_cpsk(char *client_sni) {
763  static coap_dtls_cpsk_t dtls_cpsk;
764
765  memset(&dtls_cpsk, 0, sizeof(dtls_cpsk));
766  dtls_cpsk.version = COAP_DTLS_CPSK_SETUP_VERSION;
767  dtls_cpsk.client_sni = client_sni;
768  dtls_cpsk.psk_info.identity.s = user;
769  dtls_cpsk.psk_info.identity.length = user_length;
770  dtls_cpsk.psk_info.key.s = key;
771  dtls_cpsk.psk_info.key.length = key_length;
772  return &dtls_cpsk;
773}
774
775static proxy_list_t *
776get_proxy_session(coap_session_t *session, coap_pdu_t *response,
777                  const coap_bin_const_t *token, const coap_string_t *query,
778                  coap_pdu_code_t req_code, coap_pdu_type_t req_type) {
779
780  size_t i;
781  proxy_list_t *new_proxy_list;
782
783  /* Locate existing forwarding relationship */
784  for (i = 0; i < proxy_list_count; i++) {
785    if (proxy_list[i].incoming == session) {
786      return &proxy_list[i];
787    }
788  }
789
790  /* Need to create a new forwarding mapping */
791  new_proxy_list = realloc(proxy_list, (i+1)*sizeof(proxy_list[0]));
792
793  if (new_proxy_list == NULL) {
794    coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR);
795    return NULL;
796  }
797  proxy_list = new_proxy_list;
798  proxy_list[i].incoming = session;
799  if (token) {
800    proxy_list[i].token = coap_new_binary(token->length);
801    if (!proxy_list[i].token) {
802      coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR);
803      return NULL;
804    }
805    memcpy(proxy_list[i].token->s, token->s, token->length);
806  } else
807    proxy_list[i].token = NULL;
808
809  if (query) {
810    proxy_list[i].query = coap_new_string(query->length);
811    if (!proxy_list[i].query) {
812      coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR);
813      return NULL;
814    }
815    memcpy(proxy_list[i].query->s, query->s, query->length);
816  } else
817    proxy_list[i].query = NULL;
818
819  proxy_list[i].ongoing = NULL;
820  proxy_list[i].req_code = req_code;
821  proxy_list[i].req_type = req_type;
822  proxy_list_count++;
823  return &proxy_list[i];
824}
825
826static void
827remove_proxy_association(coap_session_t *session, int send_failure) {
828
829  size_t i;
830
831  for (i = 0; i < proxy_list_count; i++) {
832    if (proxy_list[i].incoming == session) {
833      coap_session_release(proxy_list[i].ongoing);
834      break;
835    }
836    if (proxy_list[i].ongoing == session && send_failure) {
837      coap_pdu_t *response;
838
839      coap_session_release(proxy_list[i].ongoing);
840
841      /* Need to send back a gateway failure */
842      response = coap_pdu_init(proxy_list[i].req_type,
843                               COAP_RESPONSE_CODE_BAD_GATEWAY,
844                               coap_new_message_id(proxy_list[i].incoming),
845                               coap_session_max_pdu_size(proxy_list[i].incoming));
846      if (!response) {
847        coap_log_info("PDU creation issue\n");
848        return;
849      }
850
851      if (proxy_list[i].token &&
852          !coap_add_token(response, proxy_list[i].token->length,
853                          proxy_list[i].token->s)) {
854        coap_log_debug("Cannot add token to incoming proxy response PDU\n");
855      }
856
857      if (coap_send(proxy_list[i].incoming, response) ==
858          COAP_INVALID_MID) {
859        coap_log_info("Failed to send PDU with 5.02 gateway issue\n");
860      }
861      break;
862    }
863  }
864  if (i != proxy_list_count) {
865    coap_delete_binary(proxy_list[i].token);
866    coap_delete_string(proxy_list[i].query);
867    if (proxy_list_count-i > 1) {
868      memmove(&proxy_list[i],
869              &proxy_list[i+1],
870              (proxy_list_count-i-1) * sizeof(proxy_list[0]));
871    }
872    proxy_list_count--;
873  }
874}
875
876
877static coap_session_t *
878get_ongoing_proxy_session(coap_session_t *session,
879                          coap_pdu_t *response, const coap_bin_const_t *token,
880                          const coap_string_t *query, coap_pdu_code_t req_code,
881                          coap_pdu_type_t req_type, const coap_uri_t *uri) {
882
883  coap_address_t dst;
884  coap_uri_scheme_t scheme;
885  coap_proto_t proto;
886  static char client_sni[256];
887  coap_str_const_t server;
888  uint16_t port;
889  coap_addr_info_t *info_list = NULL;
890  proxy_list_t *new_proxy_list;
891  coap_context_t *context = coap_session_get_context(session);
892
893  new_proxy_list = get_proxy_session(session, response, token, query, req_code,
894                                     req_type);
895  if (!new_proxy_list)
896    return NULL;
897
898  if (new_proxy_list->ongoing)
899    return new_proxy_list->ongoing;
900
901  if (proxy.host.length) {
902    server = proxy.host;
903    port = proxy.port;
904    scheme = proxy.scheme;
905  } else {
906    server = uri->host;
907    port = uri->port;
908    scheme = uri->scheme;
909  }
910
911  /* resolve destination address where data should be sent */
912  info_list = coap_resolve_address_info(&server, port, port, port, port,
913                                        0,
914                                        1 << scheme,
915                                        COAP_RESOLVE_TYPE_REMOTE);
916
917  if (info_list == NULL) {
918    coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_GATEWAY);
919    remove_proxy_association(session, 0);
920    return NULL;
921  }
922  proto = info_list->proto;
923  memcpy(&dst, &info_list->addr, sizeof(dst));
924  coap_free_address_info(info_list);
925
926  switch (scheme) {
927  case COAP_URI_SCHEME_COAP:
928  case COAP_URI_SCHEME_COAP_TCP:
929  case COAP_URI_SCHEME_COAP_WS:
930    new_proxy_list->ongoing =
931        coap_new_client_session(context, NULL, &dst, proto);
932    break;
933  case COAP_URI_SCHEME_COAPS:
934  case COAP_URI_SCHEME_COAPS_TCP:
935  case COAP_URI_SCHEME_COAPS_WS:
936    memset(client_sni, 0, sizeof(client_sni));
937    if ((server.length == 3 && memcmp(server.s, "::1", 3) != 0) ||
938        (server.length == 9 && memcmp(server.s, "127.0.0.1", 9) != 0))
939      memcpy(client_sni, server.s, min(server.length, sizeof(client_sni)-1));
940    else
941      memcpy(client_sni, "localhost", 9);
942
943    if (!key_defined) {
944      /* Use our defined PKI certs (or NULL)  */
945      coap_dtls_pki_t *dtls_pki = setup_pki(context, COAP_DTLS_ROLE_CLIENT,
946                                            client_sni);
947      new_proxy_list->ongoing =
948          coap_new_client_session_pki(context, NULL, &dst, proto, dtls_pki);
949    } else {
950      /* Use our defined PSK */
951      coap_dtls_cpsk_t *dtls_cpsk = setup_cpsk(client_sni);
952
953      new_proxy_list->ongoing =
954          coap_new_client_session_psk2(context, NULL, &dst, proto, dtls_cpsk);
955    }
956    break;
957  case COAP_URI_SCHEME_HTTP:
958  case COAP_URI_SCHEME_HTTPS:
959  case COAP_URI_SCHEME_LAST:
960  default:
961    assert(0);
962    break;
963  }
964  if (new_proxy_list->ongoing == NULL) {
965    coap_pdu_set_code(response, COAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED);
966    remove_proxy_association(session, 0);
967    return NULL;
968  }
969  return new_proxy_list->ongoing;
970}
971
972static void
973release_proxy_body_data(coap_session_t *session COAP_UNUSED,
974                        void *app_ptr) {
975  coap_delete_binary(app_ptr);
976}
977
978static void
979hnd_proxy_uri(coap_resource_t *resource COAP_UNUSED,
980              coap_session_t *session,
981              const coap_pdu_t *request,
982              const coap_string_t *query,
983              coap_pdu_t *response) {
984  coap_opt_iterator_t opt_iter;
985  coap_opt_t *opt;
986  coap_opt_t *proxy_uri;
987  int proxy_scheme_option = 0;
988  coap_uri_t uri;
989  coap_string_t *uri_path = NULL;
990  coap_string_t *uri_query = NULL;
991  coap_session_t *ongoing = NULL;
992  size_t size;
993  size_t offset;
994  size_t total;
995  coap_binary_t *body_data = NULL;
996  const uint8_t *data;
997  coap_pdu_t *pdu;
998  coap_optlist_t *optlist = NULL;
999  coap_opt_t *option;
1000#define BUFSIZE 100
1001  unsigned char buf[BUFSIZE];
1002  coap_bin_const_t token = coap_pdu_get_token(request);
1003
1004  memset(&uri, 0, sizeof(uri));
1005  /*
1006   * See if Proxy-Scheme
1007   */
1008  opt = coap_check_option(request, COAP_OPTION_PROXY_SCHEME, &opt_iter);
1009  if (opt) {
1010    if (!get_uri_proxy_scheme_info(request, opt, &uri, &uri_path,
1011                                   &uri_query)) {
1012      coap_pdu_set_code(response,
1013                        COAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED);
1014      goto cleanup;
1015    }
1016    proxy_scheme_option = 1;
1017  }
1018  /*
1019   * See if Proxy-Uri
1020   */
1021  proxy_uri = coap_check_option(request, COAP_OPTION_PROXY_URI, &opt_iter);
1022  if (proxy_uri) {
1023    coap_log_info("Proxy URI '%.*s'\n",
1024                  coap_opt_length(proxy_uri),
1025                  (const char *)coap_opt_value(proxy_uri));
1026    if (coap_split_proxy_uri(coap_opt_value(proxy_uri),
1027                             coap_opt_length(proxy_uri),
1028                             &uri) < 0) {
1029      /* Need to return a 5.05 RFC7252 Section 5.7.2 */
1030      coap_log_warn("Proxy URI not decodable\n");
1031      coap_pdu_set_code(response,
1032                        COAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED);
1033      goto cleanup;
1034    }
1035  }
1036
1037  if (!(proxy_scheme_option || proxy_uri)) {
1038    coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_FOUND);
1039    goto cleanup;
1040  }
1041
1042  if (uri.host.length == 0) {
1043    /* Ongoing connection not well formed */
1044    coap_pdu_set_code(response, COAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED);
1045    goto cleanup;
1046  }
1047
1048  if (!verify_proxy_scheme_supported(uri.scheme)) {
1049    coap_pdu_set_code(response, COAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED);
1050    goto cleanup;
1051  }
1052
1053  /* Handle the CoAP forwarding mapping */
1054  if (uri.scheme == COAP_URI_SCHEME_COAP ||
1055      uri.scheme == COAP_URI_SCHEME_COAPS ||
1056      uri.scheme == COAP_URI_SCHEME_COAP_TCP ||
1057      uri.scheme == COAP_URI_SCHEME_COAPS_TCP) {
1058    coap_pdu_code_t req_code = coap_pdu_get_code(request);
1059    coap_pdu_type_t req_type = coap_pdu_get_type(request);
1060
1061    if (!get_proxy_session(session, response, &token, query, req_code, req_type))
1062      goto cleanup;
1063
1064    if (coap_get_data_large(request, &size, &data, &offset, &total)) {
1065      /* COAP_BLOCK_SINGLE_BODY is set, so single body should be given */
1066      assert(size == total);
1067      body_data = coap_new_binary(total);
1068      if (!body_data) {
1069        coap_log_debug("body build memory error\n");
1070        goto cleanup;
1071      }
1072      memcpy(body_data->s, data, size);
1073      data = body_data->s;
1074    }
1075
1076    /* Send data on (opening session if appropriate) */
1077
1078    ongoing = get_ongoing_proxy_session(session, response, &token,
1079                                        query, req_code, req_type, &uri);
1080    if (!ongoing)
1081      goto cleanup;
1082    /*
1083     * Build up the ongoing PDU that we are going to send
1084     */
1085    pdu = coap_pdu_init(req_type, req_code,
1086                        coap_new_message_id(ongoing),
1087                        coap_session_max_pdu_size(ongoing));
1088    if (!pdu) {
1089      coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR);
1090      goto cleanup;
1091    }
1092
1093    if (!coap_add_token(pdu, token.length, token.s)) {
1094      coap_log_debug("cannot add token to proxy request\n");
1095      coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR);
1096      coap_delete_pdu(pdu);
1097      goto cleanup;
1098    }
1099
1100    if (proxy.host.length == 0) {
1101      /* Use  Uri-Path and Uri-Query - direct session */
1102      proxy_uri = NULL;
1103      proxy_scheme_option = 0;
1104      const coap_address_t *dst = coap_session_get_addr_remote(ongoing);
1105
1106      if (coap_uri_into_options(&uri, dst, &optlist, 1,
1107                                buf, sizeof(buf)) < 0) {
1108        coap_log_err("Failed to create options for URI\n");
1109        goto cleanup;
1110      }
1111    }
1112
1113    /* Copy the remaining options across */
1114    coap_option_iterator_init(request, &opt_iter, COAP_OPT_ALL);
1115    while ((option = coap_option_next(&opt_iter))) {
1116      switch (opt_iter.number) {
1117      case COAP_OPTION_PROXY_URI:
1118        if (proxy_uri) {
1119          /* Need to add back in */
1120          goto add_in;
1121        }
1122        break;
1123      case COAP_OPTION_PROXY_SCHEME:
1124      case COAP_OPTION_URI_PATH:
1125      case COAP_OPTION_URI_PORT:
1126      case COAP_OPTION_URI_QUERY:
1127        if (proxy_scheme_option) {
1128          /* Need to add back in */
1129          goto add_in;
1130        }
1131        break;
1132      case COAP_OPTION_BLOCK1:
1133      case COAP_OPTION_BLOCK2:
1134      case COAP_OPTION_Q_BLOCK1:
1135      case COAP_OPTION_Q_BLOCK2:
1136        /* These are not passed on */
1137        break;
1138      default:
1139add_in:
1140        coap_insert_optlist(&optlist,
1141                            coap_new_optlist(opt_iter.number,
1142                                             coap_opt_length(option),
1143                                             coap_opt_value(option)));
1144        break;
1145      }
1146    }
1147
1148    /* Update pdu with options */
1149    coap_add_optlist_pdu(pdu, &optlist);
1150    coap_delete_optlist(optlist);
1151
1152    if (size) {
1153      if (!coap_add_data_large_request(ongoing, pdu, size, data,
1154                                       release_proxy_body_data, body_data)) {
1155        coap_log_debug("cannot add data to proxy request\n");
1156      } else {
1157        body_data = NULL;
1158      }
1159    }
1160
1161    if (coap_get_log_level() < COAP_LOG_DEBUG)
1162      coap_show_pdu(COAP_LOG_INFO, pdu);
1163
1164    coap_send(ongoing, pdu);
1165    /*
1166     * Do not update with response code (hence empty ACK) as will be sending
1167     * separate response when response comes back from upstream server
1168     */
1169    goto cleanup;
1170  } else {
1171    /* TODO http & https */
1172    coap_log_err("Proxy-Uri scheme %d unknown\n", uri.scheme);
1173  }
1174cleanup:
1175  coap_delete_string(uri_path);
1176  coap_delete_string(uri_query);
1177  coap_delete_binary(body_data);
1178}
1179
1180#endif /* SERVER_CAN_PROXY */
1181
1182typedef struct dynamic_resource_t {
1183  coap_string_t *uri_path;
1184  transient_value_t *value;
1185  coap_resource_t *resource;
1186  int created;
1187  uint16_t media_type;
1188} dynamic_resource_t;
1189
1190static int dynamic_count = 0;
1191static dynamic_resource_t *dynamic_entry = NULL;
1192
1193/*
1194 * Regular DELETE handler - used by resources created by the
1195 * Unknown Resource PUT handler
1196 */
1197
1198static void
1199hnd_delete(coap_resource_t *resource,
1200           coap_session_t *session COAP_UNUSED,
1201           const coap_pdu_t *request,
1202           const coap_string_t *query COAP_UNUSED,
1203           coap_pdu_t *response) {
1204  int i;
1205  coap_string_t *uri_path;
1206
1207  /* get the uri_path */
1208  uri_path = coap_get_uri_path(request);
1209  if (!uri_path) {
1210    coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_FOUND);
1211    return;
1212  }
1213
1214  for (i = 0; i < dynamic_count; i++) {
1215    if (coap_string_equal(uri_path, dynamic_entry[i].uri_path)) {
1216      /* Dynamic entry no longer required - delete it */
1217      release_resource_data(session, dynamic_entry[i].value);
1218      coap_delete_string(dynamic_entry[i].uri_path);
1219      if (dynamic_count-i > 1) {
1220        memmove(&dynamic_entry[i],
1221                &dynamic_entry[i+1],
1222                (dynamic_count-i-1) * sizeof(dynamic_entry[0]));
1223      }
1224      dynamic_count--;
1225      break;
1226    }
1227  }
1228
1229  /* Dynamic resource no longer required - delete it */
1230  coap_delete_resource(NULL, resource);
1231  coap_delete_string(uri_path);
1232  coap_pdu_set_code(response, COAP_RESPONSE_CODE_DELETED);
1233}
1234
1235/*
1236 * Regular GET handler - used by resources created by the
1237 * Unknown Resource PUT handler
1238 */
1239
1240static void
1241hnd_get(coap_resource_t *resource,
1242        coap_session_t *session,
1243        const coap_pdu_t *request,
1244        const coap_string_t *query,
1245        coap_pdu_t *response) {
1246  coap_str_const_t *uri_path;
1247  int i;
1248  dynamic_resource_t *resource_entry = NULL;
1249  coap_binary_t body;
1250  /*
1251   * request will be NULL if an Observe triggered request, so the uri_path,
1252   * if needed, must be abstracted from the resource.
1253   * The uri_path string is a const pointer
1254   */
1255
1256  uri_path = coap_resource_get_uri_path(resource);
1257  if (!uri_path) {
1258    coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_FOUND);
1259    return;
1260  }
1261
1262  for (i = 0; i < dynamic_count; i++) {
1263    if (coap_string_equal(uri_path, dynamic_entry[i].uri_path)) {
1264      break;
1265    }
1266  }
1267  if (i == dynamic_count) {
1268    coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_FOUND);
1269    return;
1270  }
1271
1272  resource_entry = &dynamic_entry[i];
1273
1274  coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
1275  body = reference_resource_data(resource_entry->value);
1276  coap_add_data_large_response(resource, session, request, response,
1277                               query, resource_entry->media_type, -1, 0,
1278                               body.length,
1279                               body.s,
1280                               release_resource_data, resource_entry->value);
1281}
1282
1283/*
1284 * Regular PUT or POST handler - used by resources created by the
1285 * Unknown Resource PUT/POST handler
1286 */
1287
1288static void
1289hnd_put_post(coap_resource_t *resource,
1290             coap_session_t *session,
1291             const coap_pdu_t *request,
1292             const coap_string_t *query COAP_UNUSED,
1293             coap_pdu_t *response) {
1294  coap_string_t *uri_path;
1295  int i;
1296  size_t size;
1297  const uint8_t *data;
1298  size_t offset;
1299  size_t total;
1300  dynamic_resource_t *resource_entry = NULL;
1301  unsigned char buf[6];      /* space to hold encoded/decoded uints */
1302  coap_opt_iterator_t opt_iter;
1303  coap_opt_t *option;
1304  coap_binary_t *data_so_far;
1305  transient_value_t *transient_value;
1306
1307  /* get the uri_path */
1308  uri_path = coap_get_uri_path(request);
1309  if (!uri_path) {
1310    coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_FOUND);
1311    return;
1312  }
1313
1314  /*
1315   * Locate the correct dynamic block for this request
1316   */
1317  for (i = 0; i < dynamic_count; i++) {
1318    if (coap_string_equal(uri_path, dynamic_entry[i].uri_path)) {
1319      break;
1320    }
1321  }
1322  if (i == dynamic_count) {
1323    if (dynamic_count >= support_dynamic) {
1324      /* Should have been caught hnd_put_post_unknown() */
1325      coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_ACCEPTABLE);
1326      coap_delete_string(uri_path);
1327      return;
1328    }
1329    dynamic_count++;
1330    dynamic_entry = realloc(dynamic_entry,
1331                            dynamic_count * sizeof(dynamic_entry[0]));
1332    if (dynamic_entry) {
1333      dynamic_entry[i].uri_path = uri_path;
1334      dynamic_entry[i].value = NULL;
1335      dynamic_entry[i].resource = resource;
1336      dynamic_entry[i].created = 1;
1337      if ((option = coap_check_option(request, COAP_OPTION_CONTENT_FORMAT,
1338                                      &opt_iter)) != NULL) {
1339        dynamic_entry[i].media_type =
1340            coap_decode_var_bytes(coap_opt_value(option),
1341                                  coap_opt_length(option));
1342      } else {
1343        dynamic_entry[i].media_type = COAP_MEDIATYPE_TEXT_PLAIN;
1344      }
1345      /* Store media type of new resource in ct. We can use buf here
1346       * as coap_add_attr() will copy the passed string. */
1347      memset(buf, 0, sizeof(buf));
1348      snprintf((char *)buf, sizeof(buf), "%d", dynamic_entry[i].media_type);
1349      /* ensure that buf is always zero-terminated */
1350      assert(buf[sizeof(buf) - 1] == '\0');
1351      buf[sizeof(buf) - 1] = '\0';
1352      coap_add_attr(resource,
1353                    coap_make_str_const("ct"),
1354                    coap_make_str_const((char *)buf),
1355                    0);
1356    } else {
1357      dynamic_count--;
1358      coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR);
1359      coap_delete_string(uri_path);
1360      return;
1361    }
1362  } else {
1363    /* Need to do this as coap_get_uri_path() created it */
1364    coap_delete_string(uri_path);
1365  }
1366
1367  resource_entry = &dynamic_entry[i];
1368
1369  if (coap_get_data_large(request, &size, &data, &offset, &total) &&
1370      size != total) {
1371    /*
1372     * A part of the data has been received (COAP_BLOCK_SINGLE_BODY not set).
1373     * However, total unfortunately is only an indication, so it is not safe to
1374     * allocate a block based on total.  As per
1375     * https://rfc-editor.org/rfc/rfc7959#section-4
1376     *   o  In a request carrying a Block1 Option, to indicate the current
1377     *         estimate the client has of the total size of the resource
1378     *         representation, measured in bytes ("size indication").
1379     *
1380     * coap_cache_ignore_options() must have previously been called with at
1381     * least COAP_OPTION_BLOCK1 set as the option value will change per block.
1382     */
1383    coap_cache_entry_t *cache_entry = coap_cache_get_by_pdu(session,
1384                                                            request,
1385                                                            COAP_CACHE_IS_SESSION_BASED);
1386
1387    if (offset == 0) {
1388      if (!cache_entry) {
1389        cache_entry = coap_new_cache_entry(session, request,
1390                                           COAP_CACHE_NOT_RECORD_PDU,
1391                                           COAP_CACHE_IS_SESSION_BASED, 0);
1392      } else {
1393        data_so_far = coap_cache_get_app_data(cache_entry);
1394        if (data_so_far) {
1395          coap_delete_binary(data_so_far);
1396          data_so_far = NULL;
1397        }
1398        coap_cache_set_app_data(cache_entry, NULL, NULL);
1399      }
1400    }
1401    if (!cache_entry) {
1402      if (offset == 0) {
1403        coap_log_warn("Unable to create a new cache entry\n");
1404      } else {
1405        coap_log_warn("No cache entry available for the non-first BLOCK\n");
1406      }
1407      coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR);
1408      return;
1409    }
1410
1411    if (size) {
1412      /* Add in the new data to cache entry */
1413      data_so_far = coap_cache_get_app_data(cache_entry);
1414      if (!data_so_far) {
1415        data_so_far = coap_new_binary(size);
1416        if (data_so_far)
1417          memcpy(data_so_far->s, data, size);
1418      } else {
1419        /* Add in new block to end of current data */
1420        coap_binary_t *new = coap_resize_binary(data_so_far, offset + size);
1421
1422        if (new) {
1423          data_so_far = new;
1424          memcpy(&data_so_far->s[offset], data, size);
1425        } else {
1426          /* Insufficient space to extend data_so_far */
1427          coap_delete_binary(data_so_far);
1428          data_so_far = NULL;
1429        }
1430      }
1431      /* Yes, data_so_far can be NULL */
1432      coap_cache_set_app_data(cache_entry, data_so_far, cache_free_app_data);
1433    }
1434    if (offset + size == total) {
1435      /* All the data is now in */
1436      data_so_far = coap_cache_get_app_data(cache_entry);
1437      coap_cache_set_app_data(cache_entry, NULL, NULL);
1438    } else {
1439      coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTINUE);
1440      return;
1441    }
1442  } else {
1443    /* single body of data received */
1444    data_so_far = coap_new_binary(size);
1445    if (data_so_far && size) {
1446      memcpy(data_so_far->s, data, size);
1447    }
1448  }
1449  /* Need to de-reference as value may be in use elsewhere */
1450  release_resource_data(session, resource_entry->value);
1451  resource_entry->value = NULL;
1452  transient_value = alloc_resource_data(data_so_far);
1453  if (!transient_value) {
1454    coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR);
1455    return;
1456  }
1457  resource_entry->value = transient_value;
1458
1459  if (resource_entry->created) {
1460    coap_pdu_code_t code = coap_pdu_get_code(request);
1461
1462    resource_entry->created = 0;
1463    coap_pdu_set_code(response, COAP_RESPONSE_CODE_CREATED);
1464    if (code == COAP_REQUEST_CODE_POST) {
1465      /* Add in Location-Path / Location-Query Options */
1466      coap_option_iterator_init(request, &opt_iter, COAP_OPT_ALL);
1467      while ((option = coap_option_next(&opt_iter))) {
1468        switch (opt_iter.number) {
1469        case COAP_OPTION_URI_PATH:
1470          if (!coap_add_option(response, COAP_OPTION_LOCATION_PATH,
1471                               coap_opt_length(option),
1472                               coap_opt_value(option)))
1473            goto fail;
1474          break;
1475        case COAP_OPTION_URI_QUERY:
1476          if (!coap_add_option(response, COAP_OPTION_LOCATION_QUERY,
1477                               coap_opt_length(option),
1478                               coap_opt_value(option)))
1479            goto fail;
1480          break;
1481        default:
1482          break;
1483        }
1484      }
1485    }
1486  } else {
1487    coap_pdu_set_code(response, COAP_RESPONSE_CODE_CHANGED);
1488    coap_resource_notify_observers(resource_entry->resource, NULL);
1489  }
1490
1491  if (echo_back) {
1492    coap_binary_t body;
1493
1494    body = reference_resource_data(resource_entry->value);
1495    coap_add_data_large_response(resource, session, request, response,
1496                                 query, resource_entry->media_type, -1, 0,
1497                                 body.length,
1498                                 body.s,
1499                                 release_resource_data, resource_entry->value);
1500  }
1501  return;
1502
1503fail:
1504  coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR);
1505  return;
1506}
1507
1508/*
1509 * Unknown Resource PUT handler
1510 */
1511
1512static void
1513hnd_put_post_unknown(coap_resource_t *resource COAP_UNUSED,
1514                     coap_session_t *session,
1515                     const coap_pdu_t *request,
1516                     const coap_string_t *query,
1517                     coap_pdu_t *response) {
1518  coap_resource_t *r;
1519  coap_string_t *uri_path;
1520
1521  /* check if creating a new resource is allowed */
1522  if (dynamic_count >= support_dynamic) {
1523    coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_ACCEPTABLE);
1524    return;
1525  }
1526
1527  /* get the uri_path - will get used by coap_resource_init() */
1528  uri_path = coap_get_uri_path(request);
1529  if (!uri_path) {
1530    coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_FOUND);
1531    return;
1532  }
1533
1534  /*
1535   * Create a resource to handle the new URI
1536   * uri_path will get deleted when the resource is removed
1537   */
1538  r = coap_resource_init((coap_str_const_t *)uri_path,
1539                         COAP_RESOURCE_FLAGS_RELEASE_URI | resource_flags);
1540  coap_add_attr(r, coap_make_str_const("title"), coap_make_str_const("\"Dynamic\""), 0);
1541  coap_register_request_handler(r, COAP_REQUEST_PUT, hnd_put_post);
1542  coap_register_request_handler(r, COAP_REQUEST_POST, hnd_put_post);
1543  coap_register_request_handler(r, COAP_REQUEST_DELETE, hnd_delete);
1544  coap_register_request_handler(r, COAP_REQUEST_FETCH, hnd_get);
1545  /* We possibly want to Observe the GETs */
1546  coap_resource_set_get_observable(r, 1);
1547  coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get);
1548  coap_add_resource(coap_session_get_context(session), r);
1549
1550  /* Do the PUT/POST for this first call */
1551  hnd_put_post(r, session, request, query, response);
1552}
1553
1554#if SERVER_CAN_PROXY
1555static int
1556proxy_event_handler(coap_session_t *session,
1557                    coap_event_t event) {
1558
1559  switch (event) {
1560  case COAP_EVENT_DTLS_CLOSED:
1561  case COAP_EVENT_TCP_CLOSED:
1562  case COAP_EVENT_SESSION_CLOSED:
1563  case COAP_EVENT_OSCORE_DECRYPTION_FAILURE:
1564  case COAP_EVENT_OSCORE_NOT_ENABLED:
1565  case COAP_EVENT_OSCORE_NO_PROTECTED_PAYLOAD:
1566  case COAP_EVENT_OSCORE_NO_SECURITY:
1567  case COAP_EVENT_OSCORE_INTERNAL_ERROR:
1568  case COAP_EVENT_OSCORE_DECODE_ERROR:
1569  case COAP_EVENT_WS_PACKET_SIZE:
1570  case COAP_EVENT_WS_CLOSED:
1571    /* Need to remove any proxy associations */
1572    remove_proxy_association(session, 0);
1573    break;
1574  case COAP_EVENT_DTLS_CONNECTED:
1575  case COAP_EVENT_DTLS_RENEGOTIATE:
1576  case COAP_EVENT_DTLS_ERROR:
1577  case COAP_EVENT_TCP_CONNECTED:
1578  case COAP_EVENT_TCP_FAILED:
1579  case COAP_EVENT_SESSION_CONNECTED:
1580  case COAP_EVENT_SESSION_FAILED:
1581  case COAP_EVENT_PARTIAL_BLOCK:
1582  case COAP_EVENT_XMIT_BLOCK_FAIL:
1583  case COAP_EVENT_SERVER_SESSION_NEW:
1584  case COAP_EVENT_SERVER_SESSION_DEL:
1585  case COAP_EVENT_BAD_PACKET:
1586  case COAP_EVENT_MSG_RETRANSMITTED:
1587  case COAP_EVENT_WS_CONNECTED:
1588  case COAP_EVENT_KEEPALIVE_FAILURE:
1589  default:
1590    break;
1591  }
1592  return 0;
1593}
1594
1595static coap_response_t
1596proxy_response_handler(coap_session_t *session,
1597                       const coap_pdu_t *sent COAP_UNUSED,
1598                       const coap_pdu_t *received,
1599                       const coap_mid_t id COAP_UNUSED) {
1600
1601  coap_pdu_t *pdu = NULL;
1602  coap_session_t *incoming = NULL;
1603  size_t i;
1604  size_t size;
1605  const uint8_t *data;
1606  coap_optlist_t *optlist = NULL;
1607  coap_opt_t *option;
1608  coap_opt_iterator_t opt_iter;
1609  size_t offset;
1610  size_t total;
1611  proxy_list_t *proxy_entry = NULL;
1612  uint16_t media_type = COAP_MEDIATYPE_TEXT_PLAIN;
1613  int maxage = -1;
1614  uint64_t etag = 0;
1615  coap_pdu_code_t rcv_code = coap_pdu_get_code(received);
1616  coap_bin_const_t rcv_token = coap_pdu_get_token(received);
1617  coap_binary_t *body_data = NULL;
1618
1619  for (i = 0; i < proxy_list_count; i++) {
1620    if (proxy_list[i].ongoing == session) {
1621      proxy_entry = &proxy_list[i];
1622      incoming = proxy_entry->incoming;
1623      break;
1624    }
1625  }
1626  if (i == proxy_list_count) {
1627    coap_log_debug("Unknown proxy ongoing session response received\n");
1628    return COAP_RESPONSE_OK;
1629  }
1630
1631  coap_log_debug("** process upstream incoming %d.%02d response:\n",
1632                 COAP_RESPONSE_CLASS(rcv_code), rcv_code & 0x1F);
1633  if (coap_get_log_level() < COAP_LOG_DEBUG)
1634    coap_show_pdu(COAP_LOG_INFO, received);
1635
1636  if (coap_get_data_large(received, &size, &data, &offset, &total)) {
1637    /* COAP_BLOCK_SINGLE_BODY is set, so single body should be given */
1638    assert(size == total);
1639    body_data = coap_new_binary(total);
1640    if (!body_data) {
1641      coap_log_debug("body build memory error\n");
1642      return COAP_RESPONSE_OK;
1643    }
1644    memcpy(body_data->s, data, size);
1645    data = body_data->s;
1646  }
1647
1648  /*
1649   * Build up the ongoing PDU that we are going to send to proxy originator
1650   * as separate response
1651   */
1652  pdu = coap_pdu_init(proxy_entry->req_type, rcv_code,
1653                      coap_new_message_id(incoming),
1654                      coap_session_max_pdu_size(incoming));
1655  if (!pdu) {
1656    coap_log_debug("Failed to create ongoing proxy response PDU\n");
1657    return COAP_RESPONSE_OK;
1658  }
1659
1660  if (!coap_add_token(pdu, rcv_token.length, rcv_token.s)) {
1661    coap_log_debug("cannot add token to ongoing proxy response PDU\n");
1662  }
1663
1664  /*
1665   * Copy the options across, skipping those needed for
1666   * coap_add_data_response_large()
1667   */
1668  coap_option_iterator_init(received, &opt_iter, COAP_OPT_ALL);
1669  while ((option = coap_option_next(&opt_iter))) {
1670    switch (opt_iter.number) {
1671    case COAP_OPTION_CONTENT_FORMAT:
1672      media_type = coap_decode_var_bytes(coap_opt_value(option),
1673                                         coap_opt_length(option));
1674      break;
1675    case COAP_OPTION_MAXAGE:
1676      maxage = coap_decode_var_bytes(coap_opt_value(option),
1677                                     coap_opt_length(option));
1678      break;
1679    case COAP_OPTION_ETAG:
1680      etag = coap_decode_var_bytes8(coap_opt_value(option),
1681                                    coap_opt_length(option));
1682      break;
1683    case COAP_OPTION_BLOCK2:
1684    case COAP_OPTION_Q_BLOCK2:
1685    case COAP_OPTION_SIZE2:
1686      break;
1687    default:
1688      coap_insert_optlist(&optlist,
1689                          coap_new_optlist(opt_iter.number,
1690                                           coap_opt_length(option),
1691                                           coap_opt_value(option)));
1692      break;
1693    }
1694  }
1695  coap_add_optlist_pdu(pdu, &optlist);
1696  coap_delete_optlist(optlist);
1697
1698  if (size > 0) {
1699    coap_pdu_t *dummy_pdu = coap_pdu_init(proxy_entry->req_type,
1700                                          proxy_entry->req_code, 0,
1701                                          coap_session_max_pdu_size(incoming));
1702
1703    coap_add_data_large_response(proxy_resource, incoming, dummy_pdu, pdu,
1704                                 proxy_entry->query,
1705                                 media_type, maxage, etag, size, data,
1706                                 release_proxy_body_data,
1707                                 body_data);
1708    coap_delete_pdu(dummy_pdu);
1709  }
1710
1711  if (coap_get_log_level() < COAP_LOG_DEBUG)
1712    coap_show_pdu(COAP_LOG_INFO, pdu);
1713
1714  coap_send(incoming, pdu);
1715  return COAP_RESPONSE_OK;
1716}
1717
1718static void
1719proxy_nack_handler(coap_session_t *session,
1720                   const coap_pdu_t *sent COAP_UNUSED,
1721                   const coap_nack_reason_t reason,
1722                   const coap_mid_t mid COAP_UNUSED) {
1723
1724  switch (reason) {
1725  case COAP_NACK_TOO_MANY_RETRIES:
1726  case COAP_NACK_NOT_DELIVERABLE:
1727  case COAP_NACK_RST:
1728  case COAP_NACK_TLS_FAILED:
1729  case COAP_NACK_WS_FAILED:
1730  case COAP_NACK_TLS_LAYER_FAILED:
1731  case COAP_NACK_WS_LAYER_FAILED:
1732    /* Need to remove any proxy associations */
1733    remove_proxy_association(session, 1);
1734    break;
1735  case COAP_NACK_ICMP_ISSUE:
1736  case COAP_NACK_BAD_RESPONSE:
1737  default:
1738    break;
1739  }
1740  return;
1741}
1742
1743#endif /* SERVER_CAN_PROXY */
1744
1745static void
1746init_resources(coap_context_t *ctx) {
1747  coap_resource_t *r;
1748
1749  r = coap_resource_init(NULL, COAP_RESOURCE_FLAGS_HAS_MCAST_SUPPORT);
1750  coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get_index);
1751
1752  coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
1753  coap_add_attr(r, coap_make_str_const("title"), coap_make_str_const("\"General Info\""), 0);
1754  coap_add_resource(ctx, r);
1755
1756  /* store clock base to use in /time */
1757  my_clock_base = clock_offset;
1758
1759  r = coap_resource_init(coap_make_str_const("time"), resource_flags);
1760  coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get_fetch_time);
1761  coap_register_request_handler(r, COAP_REQUEST_FETCH, hnd_get_fetch_time);
1762  coap_register_request_handler(r, COAP_REQUEST_PUT, hnd_put_time);
1763  coap_register_request_handler(r, COAP_REQUEST_DELETE, hnd_delete_time);
1764  coap_resource_set_get_observable(r, 1);
1765
1766  coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
1767  coap_add_attr(r, coap_make_str_const("title"), coap_make_str_const("\"Internal Clock\""), 0);
1768  coap_add_attr(r, coap_make_str_const("rt"), coap_make_str_const("\"ticks\""), 0);
1769  coap_add_attr(r, coap_make_str_const("if"), coap_make_str_const("\"clock\""), 0);
1770
1771  coap_add_resource(ctx, r);
1772  time_resource = r;
1773
1774  if (support_dynamic > 0) {
1775    /* Create a resource to handle PUTs to unknown URIs */
1776    r = coap_resource_unknown_init2(hnd_put_post_unknown, 0);
1777    /* Add in handling POST as well */
1778    coap_register_handler(r, COAP_REQUEST_POST, hnd_put_post_unknown);
1779    coap_add_resource(ctx, r);
1780  }
1781
1782  if (coap_async_is_supported()) {
1783    r = coap_resource_init(coap_make_str_const("async"),
1784                           resource_flags |
1785                           COAP_RESOURCE_FLAGS_HAS_MCAST_SUPPORT |
1786                           COAP_RESOURCE_FLAGS_LIB_DIS_MCAST_DELAYS);
1787    coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get_async);
1788
1789    coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
1790    coap_add_resource(ctx, r);
1791  }
1792
1793  r = coap_resource_init(coap_make_str_const("example_data"), resource_flags);
1794  coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get_example_data);
1795  coap_register_request_handler(r, COAP_REQUEST_PUT, hnd_put_example_data);
1796  coap_register_request_handler(r, COAP_REQUEST_FETCH, hnd_get_example_data);
1797  coap_resource_set_get_observable(r, 1);
1798
1799  coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
1800  coap_add_attr(r, coap_make_str_const("title"), coap_make_str_const("\"Example Data\""), 0);
1801  coap_add_resource(ctx, r);
1802
1803#if SERVER_CAN_PROXY
1804  if (proxy_host_name_count) {
1805    r = coap_resource_proxy_uri_init2(hnd_proxy_uri, proxy_host_name_count,
1806                                      proxy_host_name_list, 0);
1807    coap_add_resource(ctx, r);
1808    coap_register_event_handler(ctx, proxy_event_handler);
1809    coap_register_response_handler(ctx, proxy_response_handler);
1810    coap_register_nack_handler(ctx, proxy_nack_handler);
1811    proxy_resource = r;
1812  }
1813#endif /* SERVER_CAN_PROXY */
1814}
1815
1816static int
1817verify_cn_callback(const char *cn,
1818                   const uint8_t *asn1_public_cert COAP_UNUSED,
1819                   size_t asn1_length COAP_UNUSED,
1820                   coap_session_t *session COAP_UNUSED,
1821                   unsigned depth,
1822                   int validated COAP_UNUSED,
1823                   void *arg) {
1824  union {
1825    coap_dtls_role_t r;
1826    void *v;
1827  } role = { .v = arg };
1828
1829  coap_log_info("CN '%s' presented by %s (%s)\n",
1830                cn, role.r == COAP_DTLS_ROLE_SERVER ? "client" : "server",
1831                depth ? "CA" : "Certificate");
1832  return 1;
1833}
1834
1835static uint8_t *
1836read_file_mem(const char *file, size_t *length) {
1837  FILE *f;
1838  uint8_t *buf;
1839  struct stat statbuf;
1840
1841  *length = 0;
1842  if (!file || !(f = fopen(file, "r")))
1843    return NULL;
1844
1845  if (fstat(fileno(f), &statbuf) == -1) {
1846    fclose(f);
1847    return NULL;
1848  }
1849
1850  buf = coap_malloc(statbuf.st_size+1);
1851  if (!buf) {
1852    fclose(f);
1853    return NULL;
1854  }
1855
1856  if (fread(buf, 1, statbuf.st_size, f) != (size_t)statbuf.st_size) {
1857    fclose(f);
1858    coap_free(buf);
1859    return NULL;
1860  }
1861  buf[statbuf.st_size] = '\000';
1862  *length = (size_t)(statbuf.st_size + 1);
1863  fclose(f);
1864  return buf;
1865}
1866
1867static void
1868update_pki_key(coap_dtls_key_t *dtls_key, const char *key_name,
1869               const char *cert_name, const char *ca_name) {
1870  memset(dtls_key, 0, sizeof(*dtls_key));
1871  if ((key_name && strncasecmp(key_name, "pkcs11:", 7) == 0) ||
1872      (cert_name && strncasecmp(cert_name, "pkcs11:", 7) == 0) ||
1873      (ca_name && strncasecmp(ca_name, "pkcs11:", 7) == 0)) {
1874    dtls_key->key_type = COAP_PKI_KEY_PKCS11;
1875    dtls_key->key.pkcs11.public_cert = cert_name;
1876    dtls_key->key.pkcs11.private_key = key_name ?  key_name : cert_name;
1877    dtls_key->key.pkcs11.ca = ca_name;
1878    dtls_key->key.pkcs11.user_pin = pkcs11_pin;
1879  } else if (!use_pem_buf && !is_rpk_not_cert) {
1880    dtls_key->key_type = COAP_PKI_KEY_PEM;
1881    dtls_key->key.pem.public_cert = cert_name;
1882    dtls_key->key.pem.private_key = key_name ? key_name : cert_name;
1883    dtls_key->key.pem.ca_file = ca_name;
1884  } else {
1885    /* Map file into memory */
1886    coap_free(ca_mem);
1887    coap_free(cert_mem);
1888    coap_free(key_mem);
1889    ca_mem = read_file_mem(ca_name, &ca_mem_len);
1890    cert_mem = read_file_mem(cert_name, &cert_mem_len);
1891    key_mem = read_file_mem(key_name, &key_mem_len);
1892
1893    dtls_key->key_type = COAP_PKI_KEY_PEM_BUF;
1894    dtls_key->key.pem_buf.ca_cert = ca_mem;
1895    dtls_key->key.pem_buf.public_cert = cert_mem;
1896    dtls_key->key.pem_buf.private_key = key_mem ? key_mem : cert_mem;
1897    dtls_key->key.pem_buf.ca_cert_len = ca_mem_len;
1898    dtls_key->key.pem_buf.public_cert_len = cert_mem_len;
1899    dtls_key->key.pem_buf.private_key_len = key_mem ?
1900                                            key_mem_len : cert_mem_len;
1901  }
1902}
1903
1904static coap_dtls_key_t *
1905verify_pki_sni_callback(const char *sni,
1906                        void *arg COAP_UNUSED) {
1907  static coap_dtls_key_t dtls_key;
1908
1909  update_pki_key(&dtls_key, key_file, cert_file, ca_file);
1910
1911  if (sni[0]) {
1912    size_t i;
1913    coap_log_info("SNI '%s' requested\n", sni);
1914    for (i = 0; i < valid_pki_snis.count; i++) {
1915      /* Test for SNI to change cert + ca */
1916      if (strcasecmp(sni, valid_pki_snis.pki_sni_list[i].sni_match) == 0) {
1917        coap_log_info("Switching to using cert '%s' + ca '%s'\n",
1918                      valid_pki_snis.pki_sni_list[i].new_cert,
1919                      valid_pki_snis.pki_sni_list[i].new_ca);
1920        update_pki_key(&dtls_key, valid_pki_snis.pki_sni_list[i].new_cert,
1921                       valid_pki_snis.pki_sni_list[i].new_cert,
1922                       valid_pki_snis.pki_sni_list[i].new_ca);
1923        break;
1924      }
1925    }
1926  } else {
1927    coap_log_debug("SNI not requested\n");
1928  }
1929  return &dtls_key;
1930}
1931
1932static const coap_dtls_spsk_info_t *
1933verify_psk_sni_callback(const char *sni,
1934                        coap_session_t *c_session COAP_UNUSED,
1935                        void *arg COAP_UNUSED) {
1936  static coap_dtls_spsk_info_t psk_info;
1937
1938  /* Preset with the defined keys */
1939  memset(&psk_info, 0, sizeof(psk_info));
1940  psk_info.hint.s = (const uint8_t *)hint;
1941  psk_info.hint.length = hint ? strlen(hint) : 0;
1942  psk_info.key.s = key;
1943  psk_info.key.length = key_length;
1944  if (sni) {
1945    size_t i;
1946    coap_log_info("SNI '%s' requested\n", sni);
1947    for (i = 0; i < valid_psk_snis.count; i++) {
1948      /* Test for identity match to change key */
1949      if (strcasecmp(sni,
1950                     valid_psk_snis.psk_sni_list[i].sni_match) == 0) {
1951        coap_log_info("Switching to using '%.*s' hint + '%.*s' key\n",
1952                      (int)valid_psk_snis.psk_sni_list[i].new_hint->length,
1953                      valid_psk_snis.psk_sni_list[i].new_hint->s,
1954                      (int)valid_psk_snis.psk_sni_list[i].new_key->length,
1955                      valid_psk_snis.psk_sni_list[i].new_key->s);
1956        psk_info.hint = *valid_psk_snis.psk_sni_list[i].new_hint;
1957        psk_info.key = *valid_psk_snis.psk_sni_list[i].new_key;
1958        break;
1959      }
1960    }
1961  } else {
1962    coap_log_debug("SNI not requested\n");
1963  }
1964  return &psk_info;
1965}
1966
1967static const coap_bin_const_t *
1968verify_id_callback(coap_bin_const_t *identity,
1969                   coap_session_t *c_session,
1970                   void *arg COAP_UNUSED) {
1971  static coap_bin_const_t psk_key;
1972  const coap_bin_const_t *s_psk_hint = coap_session_get_psk_hint(c_session);
1973  const coap_bin_const_t *s_psk_key;
1974  size_t i;
1975
1976  coap_log_info("Identity '%.*s' requested, current hint '%.*s'\n", (int)identity->length,
1977                identity->s,
1978                s_psk_hint ? (int)s_psk_hint->length : 0,
1979                s_psk_hint ? (const char *)s_psk_hint->s : "");
1980
1981  for (i = 0; i < valid_ids.count; i++) {
1982    /* Check for hint match */
1983    if (s_psk_hint &&
1984        strcmp((const char *)s_psk_hint->s,
1985               valid_ids.id_list[i].hint_match)) {
1986      continue;
1987    }
1988    /* Test for identity match to change key */
1989    if (coap_binary_equal(identity, valid_ids.id_list[i].identity_match)) {
1990      coap_log_info("Switching to using '%.*s' key\n",
1991                    (int)valid_ids.id_list[i].new_key->length,
1992                    valid_ids.id_list[i].new_key->s);
1993      return valid_ids.id_list[i].new_key;
1994    }
1995  }
1996
1997  s_psk_key = coap_session_get_psk_key(c_session);
1998  if (s_psk_key) {
1999    /* Been updated by SNI callback */
2000    psk_key = *s_psk_key;
2001    return &psk_key;
2002  }
2003
2004  /* Just use the defined key for now */
2005  psk_key.s = key;
2006  psk_key.length = key_length;
2007  return &psk_key;
2008}
2009
2010static coap_dtls_pki_t *
2011setup_pki(coap_context_t *ctx, coap_dtls_role_t role, char *client_sni) {
2012  static coap_dtls_pki_t dtls_pki;
2013
2014  /* If general root CAs are defined */
2015  if (role == COAP_DTLS_ROLE_SERVER && root_ca_file) {
2016    struct stat stbuf;
2017    if ((stat(root_ca_file, &stbuf) == 0) && S_ISDIR(stbuf.st_mode)) {
2018      coap_context_set_pki_root_cas(ctx, NULL, root_ca_file);
2019    } else {
2020      coap_context_set_pki_root_cas(ctx, root_ca_file, NULL);
2021    }
2022  }
2023
2024  memset(&dtls_pki, 0, sizeof(dtls_pki));
2025  dtls_pki.version = COAP_DTLS_PKI_SETUP_VERSION;
2026  if (ca_file || root_ca_file) {
2027    /*
2028     * Add in additional certificate checking.
2029     * This list of enabled can be tuned for the specific
2030     * requirements - see 'man coap_encryption'.
2031     *
2032     * Note: root_ca_file is setup separately using
2033     * coap_context_set_pki_root_cas(), but this is used to define what
2034     * checking actually takes place.
2035     */
2036    dtls_pki.verify_peer_cert        = verify_peer_cert;
2037    dtls_pki.check_common_ca         = !root_ca_file;
2038    dtls_pki.allow_self_signed       = 1;
2039    dtls_pki.allow_expired_certs     = 1;
2040    dtls_pki.cert_chain_validation   = 1;
2041    dtls_pki.cert_chain_verify_depth = 2;
2042    dtls_pki.check_cert_revocation   = 1;
2043    dtls_pki.allow_no_crl            = 1;
2044    dtls_pki.allow_expired_crl       = 1;
2045  } else if (is_rpk_not_cert) {
2046    dtls_pki.verify_peer_cert        = verify_peer_cert;
2047  }
2048  dtls_pki.is_rpk_not_cert        = is_rpk_not_cert;
2049  dtls_pki.validate_cn_call_back  = verify_cn_callback;
2050  dtls_pki.cn_call_back_arg       = (void *)role;
2051  dtls_pki.validate_sni_call_back = role == COAP_DTLS_ROLE_SERVER ?
2052                                    verify_pki_sni_callback : NULL;
2053  dtls_pki.sni_call_back_arg      = NULL;
2054
2055  if (role == COAP_DTLS_ROLE_CLIENT) {
2056    dtls_pki.client_sni = client_sni;
2057  }
2058
2059  update_pki_key(&dtls_pki.pki_key, key_file, cert_file, ca_file);
2060  /* Need to keep base initialization copies of any COAP_PKI_KEY_PEM_BUF */
2061  ca_mem_base = ca_mem;
2062  cert_mem_base = cert_mem;
2063  key_mem_base = key_mem;
2064  ca_mem = NULL;
2065  cert_mem = NULL;
2066  key_mem = NULL;
2067  return &dtls_pki;
2068}
2069
2070static coap_dtls_spsk_t *
2071setup_spsk(void) {
2072  static coap_dtls_spsk_t dtls_spsk;
2073
2074  memset(&dtls_spsk, 0, sizeof(dtls_spsk));
2075  dtls_spsk.version = COAP_DTLS_SPSK_SETUP_VERSION;
2076  dtls_spsk.validate_id_call_back = valid_ids.count ?
2077                                    verify_id_callback : NULL;
2078  dtls_spsk.validate_sni_call_back = valid_psk_snis.count ?
2079                                     verify_psk_sni_callback : NULL;
2080  dtls_spsk.psk_info.hint.s = (const uint8_t *)hint;
2081  dtls_spsk.psk_info.hint.length = hint ? strlen(hint) : 0;
2082  dtls_spsk.psk_info.key.s = key;
2083  dtls_spsk.psk_info.key.length = key_length;
2084  return &dtls_spsk;
2085}
2086
2087static void
2088fill_keystore(coap_context_t *ctx) {
2089
2090  if (cert_file == NULL && key_defined == 0) {
2091    if (coap_dtls_is_supported() || coap_tls_is_supported()) {
2092      coap_log_debug("(D)TLS not enabled as none of -k, -c or -M options specified\n");
2093    }
2094    return;
2095  }
2096  if (cert_file) {
2097    coap_dtls_pki_t *dtls_pki = setup_pki(ctx,
2098                                          COAP_DTLS_ROLE_SERVER, NULL);
2099    if (!coap_context_set_pki(ctx, dtls_pki)) {
2100      coap_log_info("Unable to set up %s keys\n",
2101                    is_rpk_not_cert ? "RPK" : "PKI");
2102      /* So we do not set up DTLS */
2103      cert_file = NULL;
2104    }
2105  }
2106  if (key_defined) {
2107    coap_dtls_spsk_t *dtls_spsk = setup_spsk();
2108
2109    if (!coap_context_set_psk2(ctx, dtls_spsk)) {
2110      coap_log_info("Unable to set up PSK\n");
2111      /* So we do not set up DTLS */
2112      key_defined = 0;
2113    }
2114  }
2115}
2116
2117static void
2118usage(const char *program, const char *version) {
2119  const char *p;
2120  char buffer[120];
2121  const char *lib_build = coap_package_build();
2122
2123  p = strrchr(program, '/');
2124  if (p)
2125    program = ++p;
2126
2127  fprintf(stderr, "%s v%s -- a small CoAP implementation\n"
2128          "(c) 2010,2011,2015-2023 Olaf Bergmann <bergmann@tzi.org> and others\n\n"
2129          "Build: %s\n"
2130          "%s\n"
2131          , program, version, lib_build,
2132          coap_string_tls_version(buffer, sizeof(buffer)));
2133  fprintf(stderr, "%s\n", coap_string_tls_support(buffer, sizeof(buffer)));
2134  fprintf(stderr, "\n"
2135          "Usage: %s [-d max] [-e] [-g group] [-l loss] [-p port] [-r] [-v num]\n"
2136          "\t\t[-w [port][,secure_port]] [-A address]\n"
2137          "\t\t[-E oscore_conf_file[,seq_file]] [-G group_if] [-L value] [-N]\n"
2138          "\t\t[-P scheme://address[:port],[name1[,name2..]]]\n"
2139          "\t\t[-T max_token_size] [-U type] [-V num] [-X size]\n"
2140          "\t\t[[-h hint] [-i match_identity_file] [-k key]\n"
2141          "\t\t[-s match_psk_sni_file] [-u user]]\n"
2142          "\t\t[[-c certfile] [-j keyfile] [-m] [-n] [-C cafile]\n"
2143          "\t\t[-J pkcs11_pin] [-M rpk_file] [-R trust_casfile]\n"
2144          "\t\t[-S match_pki_sni_file]]\n"
2145          "General Options\n"
2146          "\t-d max \t\tAllow dynamic creation of up to a total of max\n"
2147          "\t       \t\tresources. If max is reached, a 4.06 code is returned\n"
2148          "\t       \t\tuntil one of the dynamic resources has been deleted\n"
2149          "\t-e     \t\tEcho back the data sent with a PUT\n"
2150          "\t-g group\tJoin the given multicast group\n"
2151          "\t       \t\tNote: DTLS over multicast is not currently supported\n"
2152          "\t-l list\t\tFail to send some datagrams specified by a comma\n"
2153          "\t       \t\tseparated list of numbers or number ranges\n"
2154          "\t       \t\t(for debugging only)\n"
2155          "\t-l loss%%\tRandomly fail to send datagrams with the specified\n"
2156          "\t       \t\tprobability - 100%% all datagrams, 0%% no datagrams\n"
2157          "\t       \t\t(for debugging only)\n"
2158          "\t-p port\t\tListen on specified port for UDP and TCP. If (D)TLS is\n"
2159          "\t       \t\tenabled, then the coap-server will also listen on\n"
2160          "\t       \t\t'port'+1 for DTLS and TLS.  The default port is 5683\n"
2161          "\t-r     \t\tEnable multicast per resource support.  If enabled,\n"
2162          "\t       \t\tonly '/', '/async' and '/.well-known/core' are enabled\n"
2163          "\t       \t\tfor multicast requests support, otherwise all\n"
2164          "\t       \t\tresources are enabled\n"
2165          "\t-t     \t\tTrack resource's observe values so observe\n"
2166          "\t       \t\tsubscriptions can be maintained over a server restart.\n"
2167          "\t       \t\tNote: Use 'kill SIGUSR2 <pid>' for controlled shutdown\n"
2168          "\t-v num \t\tVerbosity level (default 4, maximum is 8) for general\n"
2169          "\t       \t\tCoAP logging\n"
2170          "\t-w [port][,secure_port]\n"
2171          "\t       \t\tEnable WebSockets support on port (WS) and/or secure_port\n"
2172          "\t       \t\t(WSS), comma separated\n"
2173          "\t-A address\tInterface address to bind to\n"
2174          "\t-E oscore_conf_file[,seq_file]\n"
2175          "\t       \t\toscore_conf_file contains OSCORE configuration. See\n"
2176          "\t       \t\tcoap-oscore-conf(5) for definitions.\n"
2177          "\t       \t\tOptional seq_file is used to save the current transmit\n"
2178          "\t       \t\tsequence number, so on restart sequence numbers continue\n"
2179          "\t-G group_if\tUse this interface for listening for the multicast\n"
2180          "\t       \t\tgroup. This can be different from the implied interface\n"
2181          "\t       \t\tif the -A option is used\n"
2182          "\t-L value\tSum of one or more COAP_BLOCK_* flag valuess for block\n"
2183          "\t       \t\thandling methods. Default is 1 (COAP_BLOCK_USE_LIBCOAP)\n"
2184          "\t       \t\t(Sum of one or more of 1,2 and 4)\n"
2185          "\t-N     \t\tMake \"observe\" responses NON-confirmable. Even if set\n"
2186          "\t       \t\tevery fifth response will still be sent as a confirmable\n"
2187          "\t       \t\tresponse (RFC 7641 requirement)\n"
2188          , program);
2189  fprintf(stderr,
2190          "\t-P scheme://address[:port],[name1[,name2[,name3..]]]\n"
2191          "\t       \t\tScheme, address, optional port of how to connect to the\n"
2192          "\t       \t\tnext proxy server and zero or more names (comma\n"
2193          "\t       \t\tseparated) that this proxy server is known by. The\n"
2194          "\t       \t\t, (comma) is required. If there is no name1 or if the\n"
2195          "\t       \t\thostname of the incoming proxy request matches one of\n"
2196          "\t       \t\tthese names, then this server is considered to be the\n"
2197          "\t       \t\tfinal endpoint. If scheme://address[:port] is not\n"
2198          "\t       \t\tdefined before the leading , (comma) of the first name,\n"
2199          "\t       \t\tthen the ongoing connection will be a direct connection.\n"
2200          "\t       \t\tScheme is one of coap, coaps, coap+tcp and coaps+tcp\n"
2201          "\t-T max_token_length\tSet the maximum token length (8-65804)\n"
2202          "\t-U type\t\tTreat address defined by -A as a Unix socket address.\n"
2203          "\t       \t\ttype is 'coap', 'coaps', 'coap+tcp' or 'coaps+tcp'\n"
2204          "\t-V num \t\tVerbosity level (default 3, maximum is 7) for (D)TLS\n"
2205          "\t       \t\tlibrary logging\n"
2206          "\t-X size\t\tMaximum message size to use for TCP based connections\n"
2207          "\t       \t\t(default is 8388864). Maximum value of 2^32 -1\n"
2208          "PSK Options (if supported by underlying (D)TLS library)\n"
2209          "\t-h hint\t\tIdentity Hint to send. Default is CoAP. Zero length is\n"
2210          "\t       \t\tno hint\n"
2211          "\t-i match_identity_file\n"
2212          "\t       \t\tThis is a file that contains one or more lines of\n"
2213          "\t       \t\tIdentity Hints and (user) Identities to match for\n"
2214          "\t       \t\ta different new Pre-Shared Key (PSK) (comma separated)\n"
2215          "\t       \t\tto be used. E.g., per line\n"
2216          "\t       \t\t hint_to_match,identity_to_match,use_key\n"
2217          "\t       \t\tNote: -k still needs to be defined for the default case\n"
2218          "\t       \t\tNote: A match using the -s option may mean that the\n"
2219          "\t       \t\tcurrent Identity Hint is different to that defined by -h\n"
2220          "\t-k key \t\tPre-Shared Key. This argument requires (D)TLS with PSK\n"
2221          "\t       \t\tto be available. This cannot be empty if defined.\n"
2222          "\t       \t\tNote that both -c and -k need to be defined for both\n"
2223          "\t       \t\tPSK and PKI to be concurrently supported\n"
2224          "\t-s match_psk_sni_file\n"
2225          "\t       \t\tThis is a file that contains one or more lines of\n"
2226          "\t       \t\treceived Subject Name Identifier (SNI) to match to use\n"
2227          "\t       \t\ta different Identity Hint and associated Pre-Shared Key\n"
2228          "\t       \t\t(PSK) (comma separated) instead of the '-h hint' and\n"
2229          "\t       \t\t'-k key' options. E.g., per line\n"
2230          "\t       \t\t sni_to_match,use_hint,with_key\n"
2231          "\t       \t\tNote: -k still needs to be defined for the default case\n"
2232          "\t       \t\tif there is not a match\n"
2233          "\t       \t\tNote: The associated Pre-Shared Key will get updated if\n"
2234          "\t       \t\tthere is also a -i match.  The update checking order is\n"
2235          "\t       \t\t-s followed by -i\n"
2236          "\t-u user\t\tUser identity for pre-shared key mode (only used if\n"
2237          "\t       \t\toption -P is set)\n"
2238         );
2239  fprintf(stderr,
2240          "PKI Options (if supported by underlying (D)TLS library)\n"
2241          "\tNote: If any one of '-c certfile', '-j keyfile' or '-C cafile' is in\n"
2242          "\tPKCS11 URI naming format (pkcs11: prefix), then any remaining non\n"
2243          "\tPKCS11 URI file definitions have to be in DER, not PEM, format.\n"
2244          "\tOtherwise all of '-c certfile', '-j keyfile' or '-C cafile' are in\n"
2245          "\tPEM format.\n\n"
2246          "\t-c certfile\tPEM file or PKCS11 URI for the certificate. The private\n"
2247          "\t       \t\tkey can also be in the PEM file, or has the same PKCS11\n"
2248          "\t       \t\tURI. If not, the private key is defined by '-j keyfile'.\n"
2249          "\t       \t\tNote that both -c and -k need to be defined for both\n"
2250          "\t       \t\tPSK and PKI to be concurrently supported\n"
2251          "\t-j keyfile\tPEM file or PKCS11 URI for the private key for the\n"
2252          "\t       \t\tcertificate in '-c certfile' if the parameter is\n"
2253          "\t       \t\tdifferent from certfile in '-c certfile'\n"
2254          "\t-m     \t\tUse COAP_PKI_KEY_PEM_BUF instead of COAP_PKI_KEY_PEM i/f\n"
2255          "\t       \t\tby reading into memory the Cert / CA file (for testing)\n"
2256          "\t-n     \t\tDisable remote peer certificate checking. This gives\n"
2257          "\t       \t\tclients the ability to use PKI, but without any defined\n"
2258          "\t       \t\tcertificates\n"
2259          "\t-C cafile\tPEM file or PKCS11 URI that contains a list of one or\n"
2260          "\t       \t\tmore CAs that are to be passed to the client for the\n"
2261          "\t       \t\tclient to determine what client certificate to use.\n"
2262          "\t       \t\tNormally, this list of CAs would be the root CA and and\n"
2263          "\t       \t\tany intermediate CAs. Ideally the server certificate\n"
2264          "\t       \t\tshould be signed by the same CA so that mutual\n"
2265          "\t       \t\tauthentication can take place. The contents of cafile\n"
2266          "\t       \t\tare added to the trusted store of root CAs.\n"
2267          "\t       \t\tUsing the -C or -R options will will trigger the\n"
2268          "\t       \t\tvalidation of the client certificate unless overridden\n"
2269          "\t       \t\tby the -n option\n"
2270          "\t-J pkcs11_pin\tThe user pin to unlock access to the PKCS11 token\n"
2271          "\t-M rpk_file\tRaw Public Key (RPK) PEM file or PKCS11 URI that\n"
2272          "\t       \t\tcontains both PUBLIC KEY and PRIVATE KEY or just\n"
2273          "\t       \t\tEC PRIVATE KEY. (GnuTLS and TinyDTLS(PEM) support only).\n"
2274          "\t       \t\t'-C cafile' or '-R trust_casfile' are not required\n"
2275          "\t-R trust_casfile\n"
2276          "\t       \t\tPEM file containing the set of trusted root CAs\n"
2277          "\t       \t\tthat are to be used to validate the client certificate.\n"
2278          "\t       \t\tAlternatively, this can point to a directory containing\n"
2279          "\t       \t\ta set of CA PEM files.\n"
2280          "\t       \t\tUsing '-R trust_casfile' disables common CA mutual\n"
2281          "\t       \t\tauthentication which can only be done by using\n"
2282          "\t       \t\t'-C cafile'.\n"
2283          "\t       \t\tUsing the -C or -R options will will trigger the\n"
2284          "\t       \t\tvalidation of the client certificate unless overridden\n"
2285          "\t       \t\tby the -n option\n"
2286          "\t-S match_pki_sni_file\n"
2287          "\t       \t\tThis option denotes a file that contains one or more\n"
2288          "\t       \t\tlines of Subject Name Identifier (SNI) to match for new\n"
2289          "\t       \t\tCert file and new CA file (comma separated) to be used.\n"
2290          "\t       \t\tE.g., per line\n"
2291          "\t       \t\t sni_to_match,new_cert_file,new_ca_file\n"
2292          "\t       \t\tNote: -c and -C still need to be defined for the default\n"
2293          "\t       \t\tcase\n"
2294         );
2295}
2296
2297static coap_context_t *
2298get_context(const char *node, const char *port) {
2299  coap_context_t *ctx = NULL;
2300  coap_addr_info_t *info = NULL;
2301  coap_addr_info_t *info_list = NULL;
2302  coap_str_const_t local;
2303  int have_ep = 0;
2304  uint16_t u_s_port = 0;
2305  uint16_t s_port = 0;
2306  uint32_t scheme_hint_bits = 0;
2307
2308  ctx = coap_new_context(NULL);
2309  if (!ctx) {
2310    return NULL;
2311  }
2312
2313  /* Need PKI/RPK/PSK set up before we set up (D)TLS endpoints */
2314  fill_keystore(ctx);
2315
2316  if (node) {
2317    local.s = (const uint8_t *)node;
2318    local.length = strlen(node);
2319  }
2320
2321  if (port) {
2322    u_s_port = atoi(port);
2323    s_port = u_s_port + 1;
2324  }
2325  scheme_hint_bits =
2326      coap_get_available_scheme_hint_bits(cert_file != NULL || key_defined != 0,
2327                                          enable_ws, use_unix_proto);
2328  info_list = coap_resolve_address_info(node ? &local : NULL, u_s_port, s_port,
2329                                        ws_port, wss_port,
2330                                        AI_PASSIVE | AI_NUMERICHOST,
2331                                        scheme_hint_bits,
2332                                        COAP_RESOLVE_TYPE_LOCAL);
2333  for (info = info_list; info != NULL; info = info->next) {
2334    coap_endpoint_t *ep;
2335
2336    ep = coap_new_endpoint(ctx, &info->addr, info->proto);
2337    if (!ep) {
2338      coap_log_warn("cannot create endpoint for proto %u\n",
2339                    info->proto);
2340    } else {
2341      have_ep = 1;
2342    }
2343  }
2344  coap_free_address_info(info_list);
2345  if (!have_ep) {
2346    coap_log_err("No context available for interface '%s'\n", node);
2347    coap_free_context(ctx);
2348    return NULL;
2349  }
2350  return ctx;
2351}
2352
2353#if SERVER_CAN_PROXY
2354static int
2355cmdline_proxy(char *arg) {
2356  char *host_start = strchr(arg, ',');
2357  char *next_name = host_start;
2358  size_t ofs;
2359
2360  if (!host_start) {
2361    coap_log_warn("Zero or more proxy host names not defined\n");
2362    return 0;
2363  }
2364  *host_start = '\000';
2365
2366  if (host_start != arg) {
2367    /* Next upstream proxy is defined */
2368    if (coap_split_uri((unsigned char *)arg, strlen(arg), &proxy) < 0 ||
2369        proxy.path.length != 0 || proxy.query.length != 0) {
2370      coap_log_err("invalid CoAP Proxy definition\n");
2371      return 0;
2372    }
2373  }
2374  proxy_host_name_count = 0;
2375  while (next_name) {
2376    proxy_host_name_count++;
2377    next_name = strchr(next_name+1, ',');
2378  }
2379  proxy_host_name_list = coap_malloc(proxy_host_name_count * sizeof(char *));
2380  next_name = host_start;
2381  ofs = 0;
2382  while (next_name) {
2383    proxy_host_name_list[ofs++] = next_name+1;
2384    next_name = strchr(next_name+1, ',');
2385    if (next_name)
2386      *next_name = '\000';
2387  }
2388  return 1;
2389}
2390
2391static ssize_t
2392cmdline_read_user(char *arg, unsigned char **buf, size_t maxlen) {
2393  size_t len = strnlen(arg, maxlen);
2394  if (len) {
2395    *buf = (unsigned char *)arg;
2396    /* len is the size or less, so 0 terminate to maxlen */
2397    (*buf)[len] = '\000';
2398  }
2399  /* 0 length Identity is valid */
2400  return len;
2401}
2402#endif /* SERVER_CAN_PROXY */
2403
2404static FILE *oscore_seq_num_fp = NULL;
2405static const char *oscore_conf_file = NULL;
2406static const char *oscore_seq_save_file = NULL;
2407
2408static int
2409oscore_save_seq_num(uint64_t sender_seq_num, void *param COAP_UNUSED) {
2410  if (oscore_seq_num_fp) {
2411    rewind(oscore_seq_num_fp);
2412    fprintf(oscore_seq_num_fp, "%" PRIu64 "\n", sender_seq_num);
2413    fflush(oscore_seq_num_fp);
2414  }
2415  return 1;
2416}
2417
2418static coap_oscore_conf_t *
2419get_oscore_conf(coap_context_t *context) {
2420  uint8_t *buf;
2421  size_t length;
2422  coap_str_const_t file_mem;
2423  uint64_t start_seq_num = 0;
2424
2425  /* Need a rw var to free off later and file_mem.s is a const */
2426  buf = read_file_mem(oscore_conf_file, &length);
2427  if (buf == NULL) {
2428    fprintf(stderr, "OSCORE configuration file error: %s\n", oscore_conf_file);
2429    return NULL;
2430  }
2431  file_mem.s = buf;
2432  file_mem.length = length;
2433  if (oscore_seq_save_file) {
2434    oscore_seq_num_fp = fopen(oscore_seq_save_file, "r+");
2435    if (oscore_seq_num_fp == NULL) {
2436      /* Try creating it */
2437      oscore_seq_num_fp = fopen(oscore_seq_save_file, "w+");
2438      if (oscore_seq_num_fp == NULL) {
2439        fprintf(stderr, "OSCORE save restart info file error: %s\n",
2440                oscore_seq_save_file);
2441        return NULL;
2442      }
2443    }
2444    if (fscanf(oscore_seq_num_fp, "%" PRIu64, &start_seq_num) != 1) {
2445      /* Must be empty */
2446      start_seq_num = 0;
2447    }
2448  }
2449  oscore_conf = coap_new_oscore_conf(file_mem,
2450                                     oscore_save_seq_num,
2451                                     NULL, start_seq_num);
2452  coap_free(buf);
2453  if (oscore_conf == NULL) {
2454    fprintf(stderr, "OSCORE configuration file error: %s\n", oscore_conf_file);
2455    return NULL;
2456  }
2457  coap_context_oscore_server(context, oscore_conf);
2458  return oscore_conf;
2459}
2460
2461static int
2462cmdline_oscore(char *arg) {
2463  if (coap_oscore_is_supported()) {
2464    char *sep = strchr(arg, ',');
2465
2466    if (sep)
2467      *sep = '\000';
2468    oscore_conf_file = arg;
2469
2470    if (sep) {
2471      sep++;
2472      oscore_seq_save_file = sep;
2473    }
2474    return 1;
2475  }
2476  fprintf(stderr, "OSCORE support not enabled\n");
2477  return 0;
2478}
2479
2480static ssize_t
2481cmdline_read_key(char *arg, unsigned char **buf, size_t maxlen) {
2482  size_t len = strnlen(arg, maxlen);
2483  if (len) {
2484    *buf = (unsigned char *)arg;
2485    return len;
2486  }
2487  /* Need at least one byte for the pre-shared key */
2488  coap_log_crit("Invalid Pre-Shared Key specified\n");
2489  return -1;
2490}
2491
2492static int
2493cmdline_read_psk_sni_check(char *arg) {
2494  FILE *fp = fopen(arg, "r");
2495  static char tmpbuf[256];
2496  if (fp == NULL) {
2497    coap_log_err("SNI file: %s: Unable to open\n", arg);
2498    return 0;
2499  }
2500  while (fgets(tmpbuf, sizeof(tmpbuf), fp) != NULL) {
2501    char *cp = tmpbuf;
2502    char *tcp = strchr(cp, '\n');
2503
2504    if (tmpbuf[0] == '#')
2505      continue;
2506    if (tcp)
2507      *tcp = '\000';
2508
2509    tcp = strchr(cp, ',');
2510    if (tcp) {
2511      psk_sni_def_t *new_psk_sni_list;
2512      new_psk_sni_list = realloc(valid_psk_snis.psk_sni_list,
2513                                 (valid_psk_snis.count + 1)*sizeof(valid_psk_snis.psk_sni_list[0]));
2514      if (new_psk_sni_list == NULL) {
2515        break;
2516      }
2517      valid_psk_snis.psk_sni_list = new_psk_sni_list;
2518      valid_psk_snis.psk_sni_list[valid_psk_snis.count].sni_match = strndup(cp, tcp-cp);
2519      cp = tcp+1;
2520      tcp = strchr(cp, ',');
2521      if (tcp) {
2522        valid_psk_snis.psk_sni_list[valid_psk_snis.count].new_hint =
2523            coap_new_bin_const((const uint8_t *)cp, tcp-cp);
2524        cp = tcp+1;
2525        valid_psk_snis.psk_sni_list[valid_psk_snis.count].new_key =
2526            coap_new_bin_const((const uint8_t *)cp, strlen(cp));
2527        valid_psk_snis.count++;
2528      } else {
2529        free(valid_psk_snis.psk_sni_list[valid_psk_snis.count].sni_match);
2530      }
2531    }
2532  }
2533  fclose(fp);
2534  return valid_psk_snis.count > 0;
2535}
2536
2537static int
2538cmdline_read_identity_check(char *arg) {
2539  FILE *fp = fopen(arg, "r");
2540  static char tmpbuf[256];
2541  if (fp == NULL) {
2542    coap_log_err("Identity file: %s: Unable to open\n", arg);
2543    return 0;
2544  }
2545  while (fgets(tmpbuf, sizeof(tmpbuf), fp) != NULL) {
2546    char *cp = tmpbuf;
2547    char *tcp = strchr(cp, '\n');
2548
2549    if (tmpbuf[0] == '#')
2550      continue;
2551    if (tcp)
2552      *tcp = '\000';
2553
2554    tcp = strchr(cp, ',');
2555    if (tcp) {
2556      id_def_t *new_id_list;
2557      new_id_list = realloc(valid_ids.id_list,
2558                            (valid_ids.count + 1)*sizeof(valid_ids.id_list[0]));
2559      if (new_id_list == NULL) {
2560        break;
2561      }
2562      valid_ids.id_list = new_id_list;
2563      valid_ids.id_list[valid_ids.count].hint_match = strndup(cp, tcp-cp);
2564      cp = tcp+1;
2565      tcp = strchr(cp, ',');
2566      if (tcp) {
2567        valid_ids.id_list[valid_ids.count].identity_match =
2568            coap_new_bin_const((const uint8_t *)cp, tcp-cp);
2569        cp = tcp+1;
2570        valid_ids.id_list[valid_ids.count].new_key =
2571            coap_new_bin_const((const uint8_t *)cp, strlen(cp));
2572        valid_ids.count++;
2573      } else {
2574        free(valid_ids.id_list[valid_ids.count].hint_match);
2575      }
2576    }
2577  }
2578  fclose(fp);
2579  return valid_ids.count > 0;
2580}
2581
2582static int
2583cmdline_unix(char *arg) {
2584  if (!strcmp("coap", arg)) {
2585    use_unix_proto = COAP_PROTO_UDP;
2586    return 1;
2587  } else if (!strcmp("coaps", arg)) {
2588    if (!coap_dtls_is_supported()) {
2589      coap_log_err("unix with dtls is not supported\n");
2590      return 0;
2591    }
2592    use_unix_proto = COAP_PROTO_DTLS;
2593    return 1;
2594  } else if (!strcmp("coap+tcp", arg)) {
2595    if (!coap_tcp_is_supported()) {
2596      coap_log_err("unix with stream is not supported\n");
2597      return 0;
2598    }
2599    use_unix_proto = COAP_PROTO_TCP;
2600    return 1;
2601  } else if (!strcmp("coaps+tcp", arg)) {
2602    if (!coap_tls_is_supported()) {
2603      coap_log_err("unix with tls is not supported\n");
2604      return 0;
2605    }
2606    use_unix_proto = COAP_PROTO_TLS;
2607    return 1;
2608  }
2609  return 0;
2610}
2611
2612static int
2613cmdline_ws(char *arg) {
2614  char *cp = strchr(arg, ',');
2615
2616  if (cp) {
2617    if (cp != arg)
2618      ws_port = atoi(arg);
2619    cp++;
2620    if (*cp != '\000')
2621      wss_port = atoi(cp);
2622  } else {
2623    ws_port = atoi(arg);
2624  }
2625  return 1;
2626}
2627
2628static int
2629cmdline_read_pki_sni_check(char *arg) {
2630  FILE *fp = fopen(arg, "r");
2631  static char tmpbuf[256];
2632  if (fp == NULL) {
2633    coap_log_err("SNI file: %s: Unable to open\n", arg);
2634    return 0;
2635  }
2636  while (fgets(tmpbuf, sizeof(tmpbuf), fp) != NULL) {
2637    char *cp = tmpbuf;
2638    char *tcp = strchr(cp, '\n');
2639
2640    if (tmpbuf[0] == '#')
2641      continue;
2642    if (tcp)
2643      *tcp = '\000';
2644
2645    tcp = strchr(cp, ',');
2646    if (tcp) {
2647      pki_sni_def_t *new_pki_sni_list;
2648      new_pki_sni_list = realloc(valid_pki_snis.pki_sni_list,
2649                                 (valid_pki_snis.count + 1)*sizeof(valid_pki_snis.pki_sni_list[0]));
2650      if (new_pki_sni_list == NULL) {
2651        break;
2652      }
2653      valid_pki_snis.pki_sni_list = new_pki_sni_list;
2654      valid_pki_snis.pki_sni_list[valid_pki_snis.count].sni_match =
2655          strndup(cp, tcp-cp);
2656      cp = tcp+1;
2657      tcp = strchr(cp, ',');
2658      if (tcp) {
2659        int fail = 0;
2660        valid_pki_snis.pki_sni_list[valid_pki_snis.count].new_cert =
2661            strndup(cp, tcp-cp);
2662        cp = tcp+1;
2663        valid_pki_snis.pki_sni_list[valid_pki_snis.count].new_ca =
2664            strndup(cp, strlen(cp));
2665        if (access(valid_pki_snis.pki_sni_list[valid_pki_snis.count].new_cert,
2666                   R_OK)) {
2667          coap_log_err("SNI file: Cert File: %s: Unable to access\n",
2668                       valid_pki_snis.pki_sni_list[valid_pki_snis.count].new_cert);
2669          fail = 1;
2670        }
2671        if (access(valid_pki_snis.pki_sni_list[valid_pki_snis.count].new_ca,
2672                   R_OK)) {
2673          coap_log_err("SNI file: CA File: %s: Unable to access\n",
2674                       valid_pki_snis.pki_sni_list[valid_pki_snis.count].new_ca);
2675          fail = 1;
2676        }
2677        if (fail) {
2678          free(valid_pki_snis.pki_sni_list[valid_pki_snis.count].sni_match);
2679          free(valid_pki_snis.pki_sni_list[valid_pki_snis.count].new_cert);
2680          free(valid_pki_snis.pki_sni_list[valid_pki_snis.count].new_ca);
2681        } else {
2682          valid_pki_snis.count++;
2683        }
2684      } else {
2685        coap_log_err("SNI file: SNI_match,Use_Cert_file,Use_CA_file not defined\n");
2686        free(valid_pki_snis.pki_sni_list[valid_pki_snis.count].sni_match);
2687      }
2688    }
2689  }
2690  fclose(fp);
2691  return valid_pki_snis.count > 0;
2692}
2693
2694static int
2695cmdline_read_extended_token_size(char *arg) {
2696  extended_token_size = strtoul(arg, NULL, 0);
2697  if (extended_token_size < COAP_TOKEN_DEFAULT_MAX) {
2698    coap_log_err("Extended Token Length must be 8 or greater\n");
2699    return 0;
2700  } else if (extended_token_size > COAP_TOKEN_EXT_MAX) {
2701    coap_log_err("Extended Token Length must be 65804 or less\n");
2702    return 0;
2703  }
2704  return 1;
2705}
2706
2707int
2708main(int argc, char **argv) {
2709  coap_context_t *ctx = NULL;
2710  char *group = NULL;
2711  char *group_if = NULL;
2712  coap_tick_t now;
2713  char addr_str[NI_MAXHOST] = "::";
2714  char *port_str = NULL;
2715  int opt;
2716  int mcast_per_resource = 0;
2717  coap_log_t log_level = COAP_LOG_WARN;
2718  coap_log_t dtls_log_level = COAP_LOG_ERR;
2719  unsigned wait_ms;
2720  coap_time_t t_last = 0;
2721  int coap_fd;
2722  fd_set m_readfds;
2723  int nfds = 0;
2724  size_t i;
2725  int exit_code = 0;
2726  uint16_t cache_ignore_options[] = { COAP_OPTION_BLOCK1,
2727                                      COAP_OPTION_BLOCK2,
2728                                      /* See https://rfc-editor.org/rfc/rfc7959#section-2.10 */
2729                                      COAP_OPTION_MAXAGE,
2730                                      /* See https://rfc-editor.org/rfc/rfc7959#section-2.10 */
2731                                      COAP_OPTION_IF_NONE_MATCH
2732                                    };
2733#ifndef _WIN32
2734  struct sigaction sa;
2735#endif
2736
2737  /* Initialize libcoap library */
2738  coap_startup();
2739
2740  clock_offset = time(NULL);
2741
2742  while ((opt = getopt(argc, argv,
2743                       "c:d:eg:G:h:i:j:J:k:l:mnp:rs:tu:v:w:A:C:E:L:M:NP:R:S:T:U:V:X:")) != -1) {
2744    switch (opt) {
2745    case 'A' :
2746      strncpy(addr_str, optarg, NI_MAXHOST-1);
2747      addr_str[NI_MAXHOST - 1] = '\0';
2748      break;
2749    case 'c' :
2750      cert_file = optarg;
2751      break;
2752    case 'C' :
2753      ca_file = optarg;
2754      break;
2755    case 'd' :
2756      support_dynamic = atoi(optarg);
2757      break;
2758    case 'e':
2759      echo_back = 1;
2760      break;
2761    case 'E':
2762      doing_oscore = cmdline_oscore(optarg);
2763      if (!doing_oscore) {
2764        goto failed;
2765      }
2766      break;
2767    case 'g' :
2768      group = optarg;
2769      break;
2770    case 'G' :
2771      group_if = optarg;
2772      break;
2773    case 'h' :
2774      if (!optarg[0]) {
2775        hint = NULL;
2776        break;
2777      }
2778      hint = optarg;
2779      break;
2780    case 'i':
2781      if (!cmdline_read_identity_check(optarg)) {
2782        usage(argv[0], LIBCOAP_PACKAGE_VERSION);
2783        goto failed;
2784      }
2785      break;
2786    case 'j' :
2787      key_file = optarg;
2788      break;
2789    case 'J' :
2790      pkcs11_pin = optarg;
2791      break;
2792    case 'k' :
2793      key_length = cmdline_read_key(optarg, &key, MAX_KEY);
2794      if (key_length < 0) {
2795        break;
2796      }
2797      key_defined = 1;
2798      break;
2799    case 'l':
2800      if (!coap_debug_set_packet_loss(optarg)) {
2801        usage(argv[0], LIBCOAP_PACKAGE_VERSION);
2802        goto failed;
2803      }
2804      break;
2805    case 'L':
2806      block_mode = strtoul(optarg, NULL, 0);
2807      if (!(block_mode & COAP_BLOCK_USE_LIBCOAP)) {
2808        fprintf(stderr, "Block mode must include COAP_BLOCK_USE_LIBCOAP (1)\n");
2809        goto failed;
2810      }
2811      break;
2812    case 'm':
2813      use_pem_buf = 1;
2814      break;
2815    case 'M':
2816      cert_file = optarg;
2817      is_rpk_not_cert = 1;
2818      break;
2819    case 'n':
2820      verify_peer_cert = 0;
2821      break;
2822    case 'N':
2823      resource_flags = COAP_RESOURCE_FLAGS_NOTIFY_NON;
2824      break;
2825    case 'p' :
2826      port_str = optarg;
2827      break;
2828    case 'P':
2829#if SERVER_CAN_PROXY
2830      if (!cmdline_proxy(optarg)) {
2831        fprintf(stderr, "error specifying proxy address or host names\n");
2832        goto failed;
2833      }
2834      block_mode |= COAP_BLOCK_SINGLE_BODY;
2835#else /* ! SERVER_CAN_PROXY */
2836      fprintf(stderr, "Proxy support not available as no Client mode code\n");
2837      goto failed;
2838#endif /* ! SERVER_CAN_PROXY */
2839      break;
2840    case 'r' :
2841      mcast_per_resource = 1;
2842      break;
2843    case 'R' :
2844      root_ca_file = optarg;
2845      break;
2846    case 's':
2847      if (!cmdline_read_psk_sni_check(optarg)) {
2848        goto failed;
2849      }
2850      break;
2851    case 'S':
2852      if (!cmdline_read_pki_sni_check(optarg)) {
2853        goto failed;
2854      }
2855      break;
2856    case 'T':
2857      if (!cmdline_read_extended_token_size(optarg)) {
2858        goto failed;
2859      }
2860      break;
2861    case 't':
2862      track_observes = 1;
2863      break;
2864    case 'u':
2865#if SERVER_CAN_PROXY
2866      user_length = cmdline_read_user(optarg, &user, MAX_USER);
2867#else /* ! SERVER_CAN_PROXY */
2868      fprintf(stderr, "Proxy support not available as no Client mode code\n");
2869      goto failed;
2870#endif /* ! SERVER_CAN_PROXY */
2871      break;
2872    case 'U':
2873      if (!cmdline_unix(optarg)) {
2874        usage(argv[0], LIBCOAP_PACKAGE_VERSION);
2875        goto failed;
2876      }
2877      break;
2878    case 'v' :
2879      log_level = strtol(optarg, NULL, 10);
2880      break;
2881    case 'V':
2882      dtls_log_level = strtol(optarg, NULL, 10);
2883      break;
2884    case 'w':
2885      if (!coap_ws_is_supported() || !cmdline_ws(optarg)) {
2886        fprintf(stderr, "WebSockets not enabled in libcoap\n");
2887        exit(1);
2888      }
2889      enable_ws = 1;
2890      break;
2891    case 'X':
2892      csm_max_message_size = strtol(optarg, NULL, 10);
2893      break;
2894    default:
2895      usage(argv[0], LIBCOAP_PACKAGE_VERSION);
2896      goto failed;
2897    }
2898  }
2899
2900#ifdef _WIN32
2901  signal(SIGINT, handle_sigint);
2902#else
2903  memset(&sa, 0, sizeof(sa));
2904  sigemptyset(&sa.sa_mask);
2905  sa.sa_handler = handle_sigint;
2906  sa.sa_flags = 0;
2907  sigaction(SIGINT, &sa, NULL);
2908  sigaction(SIGTERM, &sa, NULL);
2909  sa.sa_handler = handle_sigusr2;
2910  sigaction(SIGUSR2, &sa, NULL);
2911  /* So we do not exit on a SIGPIPE */
2912  sa.sa_handler = SIG_IGN;
2913  sigaction(SIGPIPE, &sa, NULL);
2914#endif
2915
2916  coap_set_log_level(log_level);
2917  coap_dtls_set_log_level(dtls_log_level);
2918
2919  ctx = get_context(addr_str, port_str);
2920  if (!ctx)
2921    return -1;
2922
2923  init_resources(ctx);
2924  if (mcast_per_resource)
2925    coap_mcast_per_resource(ctx);
2926  coap_context_set_block_mode(ctx, block_mode);
2927  if (csm_max_message_size)
2928    coap_context_set_csm_max_message_size(ctx, csm_max_message_size);
2929  if (doing_oscore) {
2930    if (get_oscore_conf(ctx) == NULL)
2931      goto failed;
2932  }
2933  if (extended_token_size > COAP_TOKEN_DEFAULT_MAX)
2934    coap_context_set_max_token_size(ctx, extended_token_size);
2935
2936  /* Define the options to ignore when setting up cache-keys */
2937  coap_cache_ignore_options(ctx, cache_ignore_options,
2938                            sizeof(cache_ignore_options)/sizeof(cache_ignore_options[0]));
2939  /* join multicast group if requested at command line */
2940  if (group)
2941    coap_join_mcast_group_intf(ctx, group, group_if);
2942
2943  if (track_observes) {
2944    /*
2945     * Read in and set up appropriate persist information.
2946     * Note that this should be done after ctx is properly set up.
2947     */
2948    if (!coap_persist_startup(ctx,
2949                              "/tmp/coap_dyn_resource_save_file",
2950                              "/tmp/coap_observe_save_file",
2951                              "/tmp/coap_obs_cnt_save_file", 10)) {
2952      fprintf(stderr, "Unable to set up persist logic\n");
2953      goto finish;
2954    }
2955  }
2956
2957  coap_fd = coap_context_get_coap_fd(ctx);
2958  if (coap_fd != -1) {
2959    /* if coap_fd is -1, then epoll is not supported within libcoap */
2960    FD_ZERO(&m_readfds);
2961    FD_SET(coap_fd, &m_readfds);
2962    nfds = coap_fd + 1;
2963  }
2964
2965  wait_ms = COAP_RESOURCE_CHECK_TIME * 1000;
2966
2967  while (!quit) {
2968    int result;
2969
2970    if (coap_fd != -1) {
2971      /*
2972       * Using epoll.  It is more usual to call coap_io_process() with wait_ms
2973       * (as in the non-epoll branch), but doing it this way gives the
2974       * flexibility of potentially working with other file descriptors that
2975       * are not a part of libcoap.
2976       */
2977      fd_set readfds = m_readfds;
2978      struct timeval tv;
2979      coap_tick_t begin, end;
2980
2981      coap_ticks(&begin);
2982
2983      tv.tv_sec = wait_ms / 1000;
2984      tv.tv_usec = (wait_ms % 1000) * 1000;
2985      /* Wait until any i/o takes place or timeout */
2986      result = select(nfds, &readfds, NULL, NULL, &tv);
2987      if (result == -1) {
2988        if (errno != EAGAIN) {
2989          coap_log_debug("select: %s (%d)\n", coap_socket_strerror(), errno);
2990          break;
2991        }
2992      }
2993      if (result > 0) {
2994        if (FD_ISSET(coap_fd, &readfds)) {
2995          result = coap_io_process(ctx, COAP_IO_NO_WAIT);
2996        }
2997      }
2998      if (result >= 0) {
2999        coap_ticks(&end);
3000        /* Track the overall time spent in select() and coap_io_process() */
3001        result = (int)(end - begin);
3002      }
3003    } else {
3004      /*
3005       * epoll is not supported within libcoap
3006       *
3007       * result is time spent in coap_io_process()
3008       */
3009      result = coap_io_process(ctx, wait_ms);
3010    }
3011    if (result < 0) {
3012      break;
3013    } else if (result && (unsigned)result < wait_ms) {
3014      /* decrement if there is a result wait time returned */
3015      wait_ms -= result;
3016    } else {
3017      /*
3018       * result == 0, or result >= wait_ms
3019       * (wait_ms could have decremented to a small value, below
3020       * the granularity of the timer in coap_io_process() and hence
3021       * result == 0)
3022       */
3023      wait_ms = COAP_RESOURCE_CHECK_TIME * 1000;
3024    }
3025    if (time_resource) {
3026      coap_time_t t_now;
3027      unsigned int next_sec_ms;
3028
3029      coap_ticks(&now);
3030      t_now = coap_ticks_to_rt(now);
3031      if (t_last != t_now) {
3032        /* Happens once per second */
3033        t_last = t_now;
3034        coap_resource_notify_observers(time_resource, NULL);
3035      }
3036      /* need to wait until next second starts if wait_ms is too large */
3037      next_sec_ms = 1000 - (now % COAP_TICKS_PER_SECOND) *
3038                    1000 / COAP_TICKS_PER_SECOND;
3039      if (next_sec_ms && next_sec_ms < wait_ms)
3040        wait_ms = next_sec_ms;
3041    }
3042  }
3043  exit_code = 0;
3044
3045finish:
3046  /* Clean up local usage */
3047  if (keep_persist)
3048    coap_persist_stop(ctx);
3049
3050  coap_free(ca_mem);
3051  coap_free(cert_mem);
3052  coap_free(key_mem);
3053  coap_free(ca_mem_base);
3054  coap_free(cert_mem_base);
3055  coap_free(key_mem_base);
3056  for (i = 0; i < valid_psk_snis.count; i++) {
3057    free(valid_psk_snis.psk_sni_list[i].sni_match);
3058    coap_delete_bin_const(valid_psk_snis.psk_sni_list[i].new_hint);
3059    coap_delete_bin_const(valid_psk_snis.psk_sni_list[i].new_key);
3060  }
3061  if (valid_psk_snis.count)
3062    free(valid_psk_snis.psk_sni_list);
3063
3064  for (i = 0; i < valid_ids.count; i++) {
3065    free(valid_ids.id_list[i].hint_match);
3066    coap_delete_bin_const(valid_ids.id_list[i].identity_match);
3067    coap_delete_bin_const(valid_ids.id_list[i].new_key);
3068  }
3069  if (valid_ids.count)
3070    free(valid_ids.id_list);
3071
3072  for (i = 0; i < valid_pki_snis.count; i++) {
3073    free(valid_pki_snis.pki_sni_list[i].sni_match);
3074    free(valid_pki_snis.pki_sni_list[i].new_cert);
3075    free(valid_pki_snis.pki_sni_list[i].new_ca);
3076  }
3077  if (valid_pki_snis.count)
3078    free(valid_pki_snis.pki_sni_list);
3079
3080  for (i = 0; i < (size_t)dynamic_count; i++) {
3081    coap_delete_string(dynamic_entry[i].uri_path);
3082    release_resource_data(NULL, dynamic_entry[i].value);
3083  }
3084  free(dynamic_entry);
3085  release_resource_data(NULL, example_data_value);
3086#if SERVER_CAN_PROXY
3087  for (i = 0; i < proxy_list_count; i++) {
3088    coap_delete_binary(proxy_list[i].token);
3089    coap_delete_string(proxy_list[i].query);
3090  }
3091  free(proxy_list);
3092  proxy_list = NULL;
3093  proxy_list_count = 0;
3094#if defined(_WIN32) && !defined(__MINGW32__)
3095#pragma warning( disable : 4090 )
3096#endif
3097  coap_free(proxy_host_name_list);
3098#endif /* SERVER_CAN_PROXY */
3099  if (oscore_seq_num_fp)
3100    fclose(oscore_seq_num_fp);
3101
3102  /* Clean up library usage */
3103  coap_free_context(ctx);
3104  coap_cleanup();
3105
3106  return exit_code;
3107
3108failed:
3109  exit_code = 1;
3110  goto finish;
3111}
3112