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