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