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