• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
33  char *strndup(const char *s1, size_t n);
34  char *
strndup(const char * s1,size_t n)35  strndup(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  
74  static coap_oscore_conf_t *oscore_conf;
75  static int doing_oscore = 0;
76  
77  /* set to 1 to request clean server shutdown */
78  static int quit = 0;
79  
80  /* set to 1 if persist information is to be kept on server shutdown */
81  static int keep_persist = 0;
82  
83  /* changeable clock base (see handle_put_time()) */
84  static time_t clock_offset;
85  static time_t my_clock_base = 0;
86  
87  coap_resource_t *time_resource = NULL;
88  
89  static int resource_flags = COAP_RESOURCE_FLAGS_NOTIFY_CON;
90  static 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   */
98  static char *cert_file = NULL; /* certificate and optional private key in PEM,
99                                    or PKCS11 URI*/
100  static char *key_file = NULL; /* private key in PEM, DER or PKCS11 URI */
101  static char *pkcs11_pin = NULL; /* PKCS11 pin to unlock access to token */
102  static char *ca_file = NULL;   /* CA for cert_file - for cert checking in PEM,
103                                    DER or PKCS11 URI */
104  static char *root_ca_file = NULL; /* List of trusted Root CAs in PEM */
105  static int use_pem_buf = 0; /* Map these cert/key files into memory to test
106                                 PEM_BUF logic if set */
107  static int is_rpk_not_cert = 0; /* Cert is RPK if set */
108  /* Used to hold initial PEM_BUF setup */
109  static uint8_t *cert_mem_base = NULL; /* certificate and private key in PEM_BUF */
110  static uint8_t *key_mem_base = NULL; /* private key in PEM_BUF */
111  static uint8_t *ca_mem_base = NULL;   /* CA for cert checking in PEM_BUF */
112  /* Used for verify_pki_sni_callback PEM_BUF temporary holding */
113  static uint8_t *cert_mem = NULL; /* certificate and private key in PEM_BUF */
114  static uint8_t *key_mem = NULL; /* private key in PEM_BUF */
115  static uint8_t *ca_mem = NULL;   /* CA for cert checking in PEM_BUF */
116  static size_t cert_mem_len = 0;
117  static size_t key_mem_len = 0;
118  static size_t ca_mem_len = 0;
119  static int verify_peer_cert = 1; /* PKI granularity - by default set */
120  #define MAX_KEY   64 /* Maximum length of a pre-shared key in bytes. */
121  static uint8_t *key = NULL;
122  static ssize_t key_length = 0;
123  int key_defined = 0;
124  static const char *hint = "CoAP";
125  static int support_dynamic = 0;
126  static uint32_t block_mode = COAP_BLOCK_USE_LIBCOAP;
127  static int echo_back = 0;
128  static uint32_t csm_max_message_size = 0;
129  static size_t extended_token_size = COAP_TOKEN_DEFAULT_MAX;
130  static coap_proto_t use_unix_proto = COAP_PROTO_NONE;
131  static int enable_ws = 0;
132  static int ws_port = 80;
133  static int wss_port = 443;
134  
135  static coap_dtls_pki_t *setup_pki(coap_context_t *ctx, coap_dtls_role_t role, char *sni);
136  
137  typedef 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  
143  typedef struct valid_psk_snis_t {
144    size_t count;
145    psk_sni_def_t *psk_sni_list;
146  } valid_psk_snis_t;
147  
148  static valid_psk_snis_t valid_psk_snis = {0, NULL};
149  
150  typedef 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  
156  typedef struct valid_ids_t {
157    size_t count;
158    id_def_t *id_list;
159  } valid_ids_t;
160  
161  static valid_ids_t valid_ids = {0, NULL};
162  typedef struct pki_sni_def_t {
163    char *sni_match;
164    char *new_cert;
165    char *new_ca;
166  } pki_sni_def_t;
167  
168  typedef struct valid_pki_snis_t {
169    size_t count;
170    pki_sni_def_t *pki_sni_list;
171  } valid_pki_snis_t;
172  
173  static valid_pki_snis_t valid_pki_snis = {0, NULL};
174  
175  typedef 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 */
181  static transient_value_t *example_data_value = NULL;
182  static int example_data_media_type = COAP_MEDIATYPE_TEXT_PLAIN;
183  
184  /* SIGINT handler: set quit to 1 for graceful termination */
185  static void
handle_sigint(int signum COAP_UNUSED)186  handle_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   */
196  static void
handle_sigusr2(int signum COAP_UNUSED)197  handle_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   */
208  static transient_value_t *
alloc_resource_data(coap_binary_t * value)209  alloc_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   */
227  static void
release_resource_data(coap_session_t * session COAP_UNUSED,void * app_ptr)228  release_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   */
244  static coap_binary_t
reference_resource_data(transient_value_t * entry)245  reference_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  
263  static void
hnd_get_index(coap_resource_t * resource,coap_session_t * session,const coap_pdu_t * request,const coap_string_t * query COAP_UNUSED,coap_pdu_t * response)264  hnd_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  
277  static void
hnd_get_fetch_time(coap_resource_t * resource,coap_session_t * session,const coap_pdu_t * request,const coap_string_t * query,coap_pdu_t * response)278  hnd_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  
332  static void
hnd_put_time(coap_resource_t * resource,coap_session_t * session COAP_UNUSED,const coap_pdu_t * request,const coap_string_t * query COAP_UNUSED,coap_pdu_t * response)333  hnd_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  
380  static void
hnd_delete_time(coap_resource_t * resource COAP_UNUSED,coap_session_t * session COAP_UNUSED,const coap_pdu_t * request COAP_UNUSED,const coap_string_t * query COAP_UNUSED,coap_pdu_t * response COAP_UNUSED)381  hnd_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   */
396  static void
hnd_get_async(coap_resource_t * resource,coap_session_t * session,const coap_pdu_t * request,const coap_string_t * query,coap_pdu_t * response)397  hnd_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
465  static void
hnd_get_example_data(coap_resource_t * resource,coap_session_t * session,const coap_pdu_t * request,const coap_string_t * query,coap_pdu_t * response)466  hnd_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  
496  static void
cache_free_app_data(void * data)497  cache_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  
506  static void
hnd_put_example_data(coap_resource_t * resource,coap_session_t * session,const coap_pdu_t * request,const coap_string_t * query COAP_UNUSED,coap_pdu_t * response)507  hnd_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. */
632  static unsigned char *user = NULL;
633  static ssize_t user_length = -1;
634  
635  static coap_uri_t proxy = { {0, NULL}, 0, {0, NULL}, {0, NULL}, 0 };
636  static size_t proxy_host_name_count = 0;
637  static const char **proxy_host_name_list = NULL;
638  
639  typedef 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  
648  static proxy_list_t *proxy_list = NULL;
649  static size_t proxy_list_count = 0;
650  static coap_resource_t *proxy_resource = NULL;
651  
652  static int
get_uri_proxy_scheme_info(const coap_pdu_t * request,coap_opt_t * opt,coap_uri_t * uri,coap_string_t ** uri_path,coap_string_t ** uri_query)653  get_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  
712  static int
verify_proxy_scheme_supported(coap_uri_scheme_t scheme)713  verify_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  
761  static coap_dtls_cpsk_t *
setup_cpsk(char * client_sni)762  setup_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  
775  static proxy_list_t *
get_proxy_session(coap_session_t * session,coap_pdu_t * response,const coap_bin_const_t * token,const coap_string_t * query,coap_pdu_code_t req_code,coap_pdu_type_t req_type)776  get_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  
826  static void
remove_proxy_association(coap_session_t * session,int send_failure)827  remove_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  
877  static coap_session_t *
get_ongoing_proxy_session(coap_session_t * session,coap_pdu_t * response,const coap_bin_const_t * token,const coap_string_t * query,coap_pdu_code_t req_code,coap_pdu_type_t req_type,const coap_uri_t * uri)878  get_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  
972  static void
release_proxy_body_data(coap_session_t * session COAP_UNUSED,void * app_ptr)973  release_proxy_body_data(coap_session_t *session COAP_UNUSED,
974                          void *app_ptr) {
975    coap_delete_binary(app_ptr);
976  }
977  
978  static void
hnd_proxy_uri(coap_resource_t * resource COAP_UNUSED,coap_session_t * session,const coap_pdu_t * request,const coap_string_t * query,coap_pdu_t * response)979  hnd_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:
1139  add_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    }
1174  cleanup:
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  
1182  typedef 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  
1190  static int dynamic_count = 0;
1191  static 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  
1198  static void
hnd_delete(coap_resource_t * resource,coap_session_t * session COAP_UNUSED,const coap_pdu_t * request,const coap_string_t * query COAP_UNUSED,coap_pdu_t * response)1199  hnd_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  
1240  static void
hnd_get(coap_resource_t * resource,coap_session_t * session,const coap_pdu_t * request,const coap_string_t * query,coap_pdu_t * response)1241  hnd_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  
1288  static void
hnd_put_post(coap_resource_t * resource,coap_session_t * session,const coap_pdu_t * request,const coap_string_t * query COAP_UNUSED,coap_pdu_t * response)1289  hnd_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  
1503  fail:
1504    coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR);
1505    return;
1506  }
1507  
1508  /*
1509   * Unknown Resource PUT handler
1510   */
1511  
1512  static void
hnd_put_post_unknown(coap_resource_t * resource COAP_UNUSED,coap_session_t * session,const coap_pdu_t * request,const coap_string_t * query,coap_pdu_t * response)1513  hnd_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
1555  static int
proxy_event_handler(coap_session_t * session,coap_event_t event)1556  proxy_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  
1595  static coap_response_t
proxy_response_handler(coap_session_t * session,const coap_pdu_t * sent COAP_UNUSED,const coap_pdu_t * received,const coap_mid_t id COAP_UNUSED)1596  proxy_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  
1718  static void
proxy_nack_handler(coap_session_t * session,const coap_pdu_t * sent COAP_UNUSED,const coap_nack_reason_t reason,const coap_mid_t mid COAP_UNUSED)1719  proxy_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  
1745  static void
init_resources(coap_context_t * ctx)1746  init_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  
1816  static int
verify_cn_callback(const char * cn,const uint8_t * asn1_public_cert COAP_UNUSED,size_t asn1_length COAP_UNUSED,coap_session_t * session COAP_UNUSED,unsigned depth,int validated COAP_UNUSED,void * arg)1817  verify_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  
1835  static uint8_t *
read_file_mem(const char * file,size_t * length)1836  read_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  
1867  static void
update_pki_key(coap_dtls_key_t * dtls_key,const char * key_name,const char * cert_name,const char * ca_name)1868  update_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  
1904  static coap_dtls_key_t *
verify_pki_sni_callback(const char * sni,void * arg COAP_UNUSED)1905  verify_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  
1932  static const coap_dtls_spsk_info_t *
verify_psk_sni_callback(const char * sni,coap_session_t * c_session COAP_UNUSED,void * arg COAP_UNUSED)1933  verify_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  
1967  static const coap_bin_const_t *
verify_id_callback(coap_bin_const_t * identity,coap_session_t * c_session,void * arg COAP_UNUSED)1968  verify_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  
2010  static coap_dtls_pki_t *
setup_pki(coap_context_t * ctx,coap_dtls_role_t role,char * client_sni)2011  setup_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  
2070  static coap_dtls_spsk_t *
setup_spsk(void)2071  setup_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  
2087  static void
fill_keystore(coap_context_t * ctx)2088  fill_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  
2117  static void
usage(const char * program,const char * version)2118  usage(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  
2297  static coap_context_t *
get_context(const char * node,const char * port)2298  get_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
2354  static int
cmdline_proxy(char * arg)2355  cmdline_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  
2391  static ssize_t
cmdline_read_user(char * arg,unsigned char ** buf,size_t maxlen)2392  cmdline_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  
2404  static FILE *oscore_seq_num_fp = NULL;
2405  static const char *oscore_conf_file = NULL;
2406  static const char *oscore_seq_save_file = NULL;
2407  
2408  static int
oscore_save_seq_num(uint64_t sender_seq_num,void * param COAP_UNUSED)2409  oscore_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  
2418  static coap_oscore_conf_t *
get_oscore_conf(coap_context_t * context)2419  get_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  
2461  static int
cmdline_oscore(char * arg)2462  cmdline_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  
2480  static ssize_t
cmdline_read_key(char * arg,unsigned char ** buf,size_t maxlen)2481  cmdline_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  
2492  static int
cmdline_read_psk_sni_check(char * arg)2493  cmdline_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  
2537  static int
cmdline_read_identity_check(char * arg)2538  cmdline_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  
2582  static int
cmdline_unix(char * arg)2583  cmdline_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  
2612  static int
cmdline_ws(char * arg)2613  cmdline_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  
2628  static int
cmdline_read_pki_sni_check(char * arg)2629  cmdline_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  
2694  static int
cmdline_read_extended_token_size(char * arg)2695  cmdline_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  
2707  int
main(int argc,char ** argv)2708  main(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  
3045  finish:
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  
3108  failed:
3109    exit_code = 1;
3110    goto finish;
3111  }
3112