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