1/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 3/* oscore-interop-server 4 * 5 * A server for use in the RFC 8613 OSCORE interop testing. 6 * https://core-wg.github.io/oscore/test-spec5.html 7 * 8 * Copyright (C) 2022-2023 Olaf Bergmann <bergmann@tzi.org> and others 9 * 10 * SPDX-License-Identifier: BSD-2-Clause 11 * 12 * This file is part of the CoAP library libcoap. Please see README for terms 13 * of use. 14 */ 15 16#include <string.h> 17#include <stdlib.h> 18#include <stdio.h> 19#include <ctype.h> 20#include <inttypes.h> 21#include <sys/types.h> 22#include <sys/stat.h> 23#include <errno.h> 24#include <signal.h> 25#ifdef _WIN32 26#define strcasecmp _stricmp 27#define strncasecmp _strnicmp 28#include "getopt.c" 29#if !defined(S_ISDIR) 30#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) 31#endif 32#ifndef R_OK 33#define R_OK 4 34#endif 35static char * 36strndup(const char *s1, size_t n) { 37 char *copy = (char *)malloc(n + 1); 38 if (copy) { 39 memcpy(copy, s1, n); 40 copy[n] = 0; 41 } 42 return copy; 43} 44#include <io.h> 45#define access _access 46#define fileno _fileno 47#else 48#include <unistd.h> 49#include <sys/select.h> 50#include <sys/socket.h> 51#include <netinet/in.h> 52#include <arpa/inet.h> 53#include <netdb.h> 54#include <dirent.h> 55#endif 56 57#include <coap3/coap.h> 58 59#ifndef min 60#define min(a,b) ((a) < (b) ? (a) : (b)) 61#endif 62 63static coap_oscore_conf_t *oscore_conf; 64static int doing_oscore = 0; 65 66/* set to 1 to request clean server shutdown */ 67static int quit = 0; 68 69static coap_resource_t *r_observe_1; 70static coap_resource_t *r_observe_2; 71 72static int resource_flags = COAP_RESOURCE_FLAGS_NOTIFY_CON; 73 74static uint32_t block_mode = COAP_BLOCK_USE_LIBCOAP; 75static uint32_t csm_max_message_size = 0; 76 77/* SIGINT handler: set quit to 1 for graceful termination */ 78static void 79handle_sigint(int signum COAP_UNUSED) { 80 quit = 1; 81} 82 83#define INDEX "This is a OSCORE test server made with libcoap " \ 84 "(see https://libcoap.net)\n" \ 85 "Copyright (C) 2023 Olaf Bergmann <bergmann@tzi.org> " \ 86 "and others\n\n" 87 88static void 89hnd_get_index(coap_resource_t *resource, 90 coap_session_t *session, 91 const coap_pdu_t *request, 92 const coap_string_t *query COAP_UNUSED, 93 coap_pdu_t *response) { 94 95 coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT); 96 coap_add_data_large_response(resource, session, request, response, 97 query, COAP_MEDIATYPE_TEXT_PLAIN, 98 0x2ffff, 0, strlen(INDEX), 99 (const uint8_t *)INDEX, NULL, NULL); 100} 101 102#define HELLO_WORLD "Hello World!" 103 104static void 105hnd_get_hello_coap(coap_resource_t *resource, 106 coap_session_t *session, 107 const coap_pdu_t *request, 108 const coap_string_t *query, 109 coap_pdu_t *response) { 110 coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT); 111 coap_add_data_large_response(resource, session, request, response, 112 query, COAP_MEDIATYPE_TEXT_PLAIN, 113 -1, 0, strlen(HELLO_WORLD), 114 (const uint8_t *)HELLO_WORLD, NULL, NULL); 115} 116 117static void 118hnd_get_hello_1(coap_resource_t *resource, 119 coap_session_t *session, 120 const coap_pdu_t *request, 121 const coap_string_t *query, 122 coap_pdu_t *response) { 123 coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT); 124 coap_add_data_large_response(resource, session, request, response, 125 query, COAP_MEDIATYPE_TEXT_PLAIN, 126 -1, 0, strlen(HELLO_WORLD), 127 (const uint8_t *)HELLO_WORLD, NULL, NULL); 128} 129 130static void 131hnd_get_hello_2(coap_resource_t *resource, 132 coap_session_t *session, 133 const coap_pdu_t *request, 134 const coap_string_t *query, 135 coap_pdu_t *response) { 136 coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT); 137 coap_add_data_large_response(resource, session, request, response, 138 query, COAP_MEDIATYPE_TEXT_PLAIN, 139 -1, 0x2b, strlen(HELLO_WORLD), 140 (const uint8_t *)HELLO_WORLD, NULL, NULL); 141} 142 143static void 144hnd_get_hello_3(coap_resource_t *resource, 145 coap_session_t *session, 146 const coap_pdu_t *request, 147 const coap_string_t *query, 148 coap_pdu_t *response) { 149 coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT); 150 coap_add_data_large_response(resource, session, request, response, 151 query, COAP_MEDIATYPE_TEXT_PLAIN, 152 5, 0, strlen(HELLO_WORLD), 153 (const uint8_t *)HELLO_WORLD, NULL, NULL); 154} 155 156static void 157hnd_post_hello_6(coap_resource_t *resource, 158 coap_session_t *session, 159 const coap_pdu_t *request, 160 const coap_string_t *query, 161 coap_pdu_t *response) { 162 size_t size; 163 const uint8_t *data; 164 165 (void)coap_get_data(request, &size, &data); 166 coap_pdu_set_code(response, COAP_RESPONSE_CODE_CHANGED); 167 coap_add_data_large_response(resource, session, request, response, 168 query, COAP_MEDIATYPE_TEXT_PLAIN, 169 -1, 0, size, 170 data, NULL, NULL); 171} 172 173static void 174hnd_put_hello_7(coap_resource_t *resource, 175 coap_session_t *session, 176 const coap_pdu_t *request, 177 const coap_string_t *query, 178 coap_pdu_t *response) { 179 size_t size; 180 const uint8_t *data; 181 coap_opt_iterator_t opt_iter; 182 coap_opt_t *option; 183 uint64_t etag; 184 185 186 if ((option = coap_check_option(request, COAP_OPTION_IF_MATCH, 187 &opt_iter)) != NULL) { 188 etag = coap_decode_var_bytes8(coap_opt_value(option), 189 coap_opt_length(option)); 190 if (etag != 0x7b) { 191 coap_pdu_set_code(response, COAP_RESPONSE_CODE_PRECONDITION_FAILED); 192 return; 193 } 194 } 195 196 coap_pdu_set_code(response, COAP_RESPONSE_CODE_CHANGED); 197 (void)coap_get_data(request, &size, &data); 198 coap_add_data_large_response(resource, session, request, response, 199 query, COAP_MEDIATYPE_TEXT_PLAIN, 200 -1, 0x7b, size, 201 data, NULL, NULL); 202} 203 204static void 205hnd_get_observe1(coap_resource_t *resource, 206 coap_session_t *session, 207 const coap_pdu_t *request, 208 const coap_string_t *query, 209 coap_pdu_t *response) { 210 static int count = 0; 211 212 count++; 213 switch (count) { 214 case 1: 215 coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT); 216 coap_add_data_large_response(resource, session, request, response, 217 query, COAP_MEDIATYPE_TEXT_PLAIN, 218 1, 0, strlen("one"), 219 (const uint8_t *)"one", NULL, NULL); 220 break; 221 case 2: 222 coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT); 223 coap_add_data_large_response(resource, session, request, response, 224 query, COAP_MEDIATYPE_TEXT_PLAIN, 225 1, 0, strlen("two"), 226 (const uint8_t *)"two", NULL, NULL); 227 break; 228 default: 229 coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR); 230 coap_add_data_large_response(resource, session, request, response, 231 query, COAP_MEDIATYPE_TEXT_PLAIN, 232 -1, 0, strlen("Terminate Observe"), 233 (const uint8_t *)"Terminate Observe", 234 NULL, NULL); 235 r_observe_1 = NULL; 236 } 237} 238 239static void 240hnd_get_observe2(coap_resource_t *resource, 241 coap_session_t *session, 242 const coap_pdu_t *request, 243 const coap_string_t *query, 244 coap_pdu_t *response) { 245 static int count = 0; 246 247 count++; 248 switch (count) { 249 case 1: 250 coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT); 251 coap_add_data_large_response(resource, session, request, response, 252 query, COAP_MEDIATYPE_TEXT_PLAIN, 253 1, 0, strlen("one"), 254 (const uint8_t *)"one", NULL, NULL); 255 break; 256 case 2: 257 default: 258 coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT); 259 coap_add_data_large_response(resource, session, request, response, 260 query, COAP_MEDIATYPE_TEXT_PLAIN, 261 1, 0, strlen("two"), 262 (const uint8_t *)"two", NULL, NULL); 263 r_observe_2 = NULL; 264 break; 265 } 266} 267 268static void 269hnd_del_test(coap_resource_t *resource COAP_UNUSED, 270 coap_session_t *session COAP_UNUSED, 271 const coap_pdu_t *request COAP_UNUSED, 272 const coap_string_t *query COAP_UNUSED, 273 coap_pdu_t *response) { 274 coap_pdu_set_code(response, COAP_RESPONSE_CODE_DELETED); 275} 276 277static void 278init_resources(coap_context_t *ctx) { 279 coap_resource_t *r; 280 281 r = coap_resource_init(NULL, COAP_RESOURCE_FLAGS_HAS_MCAST_SUPPORT); 282 coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get_index); 283 284 coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0); 285 coap_add_attr(r, coap_make_str_const("title"), 286 coap_make_str_const("\"General Info\""), 0); 287 coap_add_resource(ctx, r); 288 289 r = coap_resource_init(coap_make_str_const("oscore/hello/coap"), 290 resource_flags); 291 coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get_hello_coap); 292 coap_add_resource(ctx, r); 293 294 r = coap_resource_init(coap_make_str_const("oscore/hello/1"), 295 resource_flags | COAP_RESOURCE_FLAGS_OSCORE_ONLY); 296 coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get_hello_1); 297 coap_add_resource(ctx, r); 298 299 r = coap_resource_init(coap_make_str_const("oscore/hello/2"), 300 resource_flags | COAP_RESOURCE_FLAGS_OSCORE_ONLY); 301 coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get_hello_2); 302 coap_add_resource(ctx, r); 303 304 r = coap_resource_init(coap_make_str_const("oscore/hello/3"), 305 resource_flags | COAP_RESOURCE_FLAGS_OSCORE_ONLY); 306 coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get_hello_3); 307 coap_add_resource(ctx, r); 308 309 r = coap_resource_init(coap_make_str_const("oscore/hello/6"), 310 resource_flags | COAP_RESOURCE_FLAGS_OSCORE_ONLY); 311 coap_register_request_handler(r, COAP_REQUEST_POST, hnd_post_hello_6); 312 coap_add_resource(ctx, r); 313 314 r = coap_resource_init(coap_make_str_const("oscore/hello/7"), 315 resource_flags | COAP_RESOURCE_FLAGS_OSCORE_ONLY); 316 coap_register_request_handler(r, COAP_REQUEST_PUT, hnd_put_hello_7); 317 coap_add_resource(ctx, r); 318 319 r = coap_resource_init(coap_make_str_const("oscore/observe1"), 320 resource_flags | COAP_RESOURCE_FLAGS_OSCORE_ONLY); 321 coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get_observe1); 322 coap_resource_set_get_observable(r, 1); 323 coap_add_resource(ctx, r); 324 r_observe_1 = r; 325 326 r = coap_resource_init(coap_make_str_const("oscore/observe2"), 327 resource_flags | COAP_RESOURCE_FLAGS_OSCORE_ONLY); 328 coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get_observe2); 329 coap_resource_set_get_observable(r, 1); 330 coap_add_resource(ctx, r); 331 r_observe_2 = r; 332 333 r = coap_resource_init(coap_make_str_const("oscore/test"), 334 resource_flags | COAP_RESOURCE_FLAGS_OSCORE_ONLY); 335 coap_register_request_handler(r, COAP_REQUEST_DELETE, hnd_del_test); 336 coap_add_resource(ctx, r); 337} 338 339static uint8_t * 340read_file_mem(const char *file, size_t *length) { 341 FILE *f; 342 uint8_t *buf; 343 struct stat statbuf; 344 345 *length = 0; 346 if (!file || !(f = fopen(file, "r"))) 347 return NULL; 348 349 if (fstat(fileno(f), &statbuf) == -1) { 350 fclose(f); 351 return NULL; 352 } 353 354 buf = coap_malloc(statbuf.st_size+1); 355 if (!buf) { 356 fclose(f); 357 return NULL; 358 } 359 360 if (fread(buf, 1, statbuf.st_size, f) != (size_t)statbuf.st_size) { 361 fclose(f); 362 coap_free(buf); 363 return NULL; 364 } 365 buf[statbuf.st_size] = '\000'; 366 *length = (size_t)(statbuf.st_size + 1); 367 fclose(f); 368 return buf; 369} 370 371static void 372usage(const char *program, const char *version) { 373 const char *p; 374 char buffer[120]; 375 const char *lib_build = coap_package_build(); 376 377 p = strrchr(program, '/'); 378 if (p) 379 program = ++p; 380 381 fprintf(stderr, "%s v%s -- OSCORE interop implementation\n" 382 "(c) 2023 Olaf Bergmann <bergmann@tzi.org> and others\n\n" 383 "Build: %s\n" 384 "%s\n" 385 , program, version, lib_build, 386 coap_string_tls_version(buffer, sizeof(buffer))); 387 fprintf(stderr, "%s\n", coap_string_tls_support(buffer, sizeof(buffer))); 388 fprintf(stderr, "\n" 389 "Usage: %s [-d max] [-g group] [-l loss] [-p port] [-r] [-v num]\n" 390 "\t\t[-A address] [-E oscore_conf_file[,seq_file]] [-G group_if]\n" 391 "\t\t[-L value] [-N] [-P scheme://address[:port],[name1[,name2..]]]\n" 392 "\t\t[-X size]\n" 393 "General Options\n" 394 "\t-d max \t\tAllow dynamic creation of up to a total of max\n" 395 "\t \t\tresources. If max is reached, a 4.06 code is returned\n" 396 "\t \t\tuntil one of the dynamic resources has been deleted\n" 397 "\t-g group\tJoin the given multicast group\n" 398 "\t \t\tNote: DTLS over multicast is not currently supported\n" 399 "\t-l list\t\tFail to send some datagrams specified by a comma\n" 400 "\t \t\tseparated list of numbers or number ranges\n" 401 "\t \t\t(for debugging only)\n" 402 "\t-l loss%%\tRandomly fail to send datagrams with the specified\n" 403 "\t \t\tprobability - 100%% all datagrams, 0%% no datagrams\n" 404 "\t \t\t(for debugging only)\n" 405 "\t-p port\t\tListen on specified port for UDP and TCP. If (D)TLS is\n" 406 "\t \t\tenabled, then the coap-server will also listen on\n" 407 "\t \t\t 'port'+1 for DTLS and TLS. The default port is 5683\n" 408 "\t-r \t\tEnable multicast per resource support. If enabled,\n" 409 "\t \t\tonly '/', '/async' and '/.well-known/core' are enabled\n" 410 "\t \t\tfor multicast requests support, otherwise all\n" 411 "\t \t\tresources are enabled\n" 412 "\t-v num \t\tVerbosity level (default 3, maximum is 9). Above 7,\n" 413 "\t \t\tthere is increased verbosity in GnuTLS and OpenSSL\n" 414 "\t \t\tlogging\n" 415 "\t-A address\tInterface address to bind to\n" 416 "\t-E oscore_conf_file[,seq_file]\n" 417 "\t \t\toscore_conf_file contains OSCORE configuration. See\n" 418 "\t \t\tcoap-oscore-conf(5) for definitions.\n" 419 "\t \t\tOptional seq_file is used to save the current transmit\n" 420 "\t \t\tsequence number, so on restart sequence numbers continue\n" 421 "\t-G group_if\tUse this interface for listening for the multicast\n" 422 "\t \t\tgroup. This can be different from the implied interface\n" 423 "\t \t\tif the -A option is used\n" 424 "\t-L value\tSum of one or more COAP_BLOCK_* flag valuess for block\n" 425 "\t \t\thandling methods. Default is 1 (COAP_BLOCK_USE_LIBCOAP)\n" 426 "\t \t\t(Sum of one or more of 1,2 and 4)\n" 427 "\t-N \t\tMake \"observe\" responses NON-confirmable. Even if set\n" 428 "\t \t\tevery fifth response will still be sent as a confirmable\n" 429 "\t \t\tresponse (RFC 7641 requirement)\n" 430 , program); 431} 432 433static coap_context_t * 434get_context(const char *node, const char *port) { 435 coap_context_t *ctx = NULL; 436 int s; 437 struct addrinfo hints; 438 struct addrinfo *result, *rp; 439 440 ctx = coap_new_context(NULL); 441 if (!ctx) { 442 return NULL; 443 } 444 445 memset(&hints, 0, sizeof(struct addrinfo)); 446 hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ 447 hints.ai_socktype = SOCK_DGRAM; /* Coap uses UDP */ 448 hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; 449 450 s = getaddrinfo(node, port, &hints, &result); 451 if (s != 0) { 452 fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s)); 453 coap_free_context(ctx); 454 return NULL; 455 } 456 457 /* iterate through results until success */ 458 for (rp = result; rp != NULL; rp = rp->ai_next) { 459 coap_address_t addr; 460 coap_endpoint_t *ep_udp = NULL; 461 462 if (rp->ai_addrlen <= (socklen_t)sizeof(addr.addr)) { 463 coap_address_init(&addr); 464 addr.size = (socklen_t)rp->ai_addrlen; 465 memcpy(&addr.addr, rp->ai_addr, rp->ai_addrlen); 466 467 ep_udp = coap_new_endpoint(ctx, &addr, COAP_PROTO_UDP); 468 if (!ep_udp) { 469 coap_log_crit("cannot create UDP endpoint\n"); 470 continue; 471 } 472 if (coap_tcp_is_supported()) { 473 coap_endpoint_t *ep_tcp; 474 ep_tcp = coap_new_endpoint(ctx, &addr, COAP_PROTO_TCP); 475 if (!ep_tcp) { 476 coap_log_crit("cannot create TCP endpoint\n"); 477 } 478 } 479 if (ep_udp) 480 goto finish; 481 } 482 } 483 484 fprintf(stderr, "no context available for interface '%s'\n", node); 485 coap_free_context(ctx); 486 ctx = NULL; 487 488finish: 489 freeaddrinfo(result); 490 return ctx; 491} 492 493static FILE *oscore_seq_num_fp = NULL; 494static const char *oscore_conf_file = NULL; 495static const char *oscore_seq_save_file = NULL; 496 497static int 498oscore_save_seq_num(uint64_t sender_seq_num, void *param COAP_UNUSED) { 499 if (oscore_seq_num_fp) { 500 rewind(oscore_seq_num_fp); 501 fprintf(oscore_seq_num_fp, "%" PRIu64 "\n", sender_seq_num); 502 fflush(oscore_seq_num_fp); 503 } 504 return 1; 505} 506 507static coap_oscore_conf_t * 508get_oscore_conf(coap_context_t *context) { 509 uint8_t *buf; 510 size_t length; 511 coap_str_const_t file_mem; 512 uint64_t start_seq_num = 0; 513 514 buf = read_file_mem(oscore_conf_file, &length); 515 if (buf == NULL) { 516 fprintf(stderr, "OSCORE configuration file error: %s\n", oscore_conf_file); 517 return NULL; 518 } 519 file_mem.s = buf; 520 file_mem.length = length; 521 if (oscore_seq_save_file) { 522 oscore_seq_num_fp = fopen(oscore_seq_save_file, "r+"); 523 if (oscore_seq_num_fp == NULL) { 524 /* Try creating it */ 525 oscore_seq_num_fp = fopen(oscore_seq_save_file, "w+"); 526 if (oscore_seq_num_fp == NULL) { 527 fprintf(stderr, "OSCORE save restart info file error: %s\n", 528 oscore_seq_save_file); 529 return NULL; 530 } 531 } 532 if (fscanf(oscore_seq_num_fp, "%" PRIu64, &start_seq_num) != 1) { 533 /* Must be empty */ 534 start_seq_num = 0; 535 } 536 } 537 oscore_conf = coap_new_oscore_conf(file_mem, 538 oscore_save_seq_num, 539 NULL, start_seq_num); 540 coap_free(buf); 541 if (oscore_conf == NULL) { 542 fprintf(stderr, "OSCORE configuration file error: %s\n", oscore_conf_file); 543 return NULL; 544 } 545 coap_context_oscore_server(context, oscore_conf); 546 return oscore_conf; 547} 548 549static int 550cmdline_oscore(char *arg) { 551 if (coap_oscore_is_supported()) { 552 char *sep = strchr(arg, ','); 553 554 if (sep) 555 *sep = '\000'; 556 oscore_conf_file = arg; 557 558 if (sep) { 559 sep++; 560 oscore_seq_save_file = sep; 561 } 562 doing_oscore = 1; 563 return 1; 564 } 565 fprintf(stderr, "OSCORE support not enabled\n"); 566 return 0; 567} 568 569int 570main(int argc, char **argv) { 571 coap_context_t *ctx; 572 char *group = NULL; 573 char *group_if = NULL; 574 coap_tick_t now; 575 char addr_str[NI_MAXHOST] = "::"; 576 char port_str[NI_MAXSERV] = "5683"; 577 int opt; 578 int mcast_per_resource = 0; 579 coap_log_t log_level = COAP_LOG_WARN; 580 unsigned wait_ms; 581 coap_time_t t_last = 0; 582 int coap_fd; 583 fd_set m_readfds; 584 int nfds = 0; 585 uint16_t cache_ignore_options[] = { COAP_OPTION_BLOCK1, 586 COAP_OPTION_BLOCK2, 587 /* See https://rfc-editor.org/rfc/rfc7959#section-2.10 */ 588 COAP_OPTION_MAXAGE, 589 /* See https://rfc-editor.org/rfc/rfc7959#section-2.10 */ 590 COAP_OPTION_IF_NONE_MATCH 591 }; 592#ifndef _WIN32 593 struct sigaction sa; 594#endif 595 596 /* Initialize libcoap library */ 597 coap_startup(); 598 599 while ((opt = getopt(argc, argv, "g:G:l:p:rv:A:E:L:NX:")) != -1) { 600 switch (opt) { 601 case 'A' : 602 strncpy(addr_str, optarg, NI_MAXHOST-1); 603 addr_str[NI_MAXHOST - 1] = '\0'; 604 break; 605 case 'E': 606 if (!cmdline_oscore(optarg)) { 607 exit(1); 608 } 609 break; 610 case 'g' : 611 group = optarg; 612 break; 613 case 'G' : 614 group_if = optarg; 615 break; 616 case 'l': 617 if (!coap_debug_set_packet_loss(optarg)) { 618 usage(argv[0], LIBCOAP_PACKAGE_VERSION); 619 exit(1); 620 } 621 break; 622 case 'L': 623 block_mode = strtoul(optarg, NULL, 0); 624 if (!(block_mode & COAP_BLOCK_USE_LIBCOAP)) { 625 fprintf(stderr, "Block mode must include COAP_BLOCK_USE_LIBCOAP (1)\n"); 626 exit(-1); 627 } 628 break; 629 case 'N': 630 resource_flags = COAP_RESOURCE_FLAGS_NOTIFY_NON; 631 break; 632 case 'p' : 633 strncpy(port_str, optarg, NI_MAXSERV-1); 634 port_str[NI_MAXSERV - 1] = '\0'; 635 break; 636 case 'r' : 637 mcast_per_resource = 1; 638 break; 639 case 'v' : 640 log_level = strtol(optarg, NULL, 10); 641 break; 642 case 'X': 643 csm_max_message_size = strtol(optarg, NULL, 10); 644 break; 645 default: 646 usage(argv[0], LIBCOAP_PACKAGE_VERSION); 647 exit(1); 648 } 649 } 650 651#ifdef _WIN32 652 signal(SIGINT, handle_sigint); 653#else 654 memset(&sa, 0, sizeof(sa)); 655 sigemptyset(&sa.sa_mask); 656 sa.sa_handler = handle_sigint; 657 sa.sa_flags = 0; 658 sigaction(SIGINT, &sa, NULL); 659 sigaction(SIGTERM, &sa, NULL); 660 /* So we do not exit on a SIGPIPE */ 661 sa.sa_handler = SIG_IGN; 662 sigaction(SIGPIPE, &sa, NULL); 663#endif 664 665 coap_dtls_set_log_level(log_level); 666 coap_set_log_level(log_level); 667 668 ctx = get_context(addr_str, port_str); 669 if (!ctx) 670 return -1; 671 672 init_resources(ctx); 673 if (mcast_per_resource) 674 coap_mcast_per_resource(ctx); 675 coap_context_set_block_mode(ctx, block_mode); 676 if (csm_max_message_size) 677 coap_context_set_csm_max_message_size(ctx, csm_max_message_size); 678 if (doing_oscore) { 679 if (get_oscore_conf(ctx) == NULL) 680 goto finish; 681 } 682 683 /* Define the options to ignore when setting up cache-keys */ 684 coap_cache_ignore_options(ctx, cache_ignore_options, 685 sizeof(cache_ignore_options)/sizeof(cache_ignore_options[0])); 686 /* join multicast group if requested at command line */ 687 if (group) 688 coap_join_mcast_group_intf(ctx, group, group_if); 689 690 coap_fd = coap_context_get_coap_fd(ctx); 691 if (coap_fd != -1) { 692 /* if coap_fd is -1, then epoll is not supported within libcoap */ 693 FD_ZERO(&m_readfds); 694 FD_SET(coap_fd, &m_readfds); 695 nfds = coap_fd + 1; 696 } 697 698 wait_ms = COAP_RESOURCE_CHECK_TIME * 1000; 699 700 while (!quit) { 701 int result; 702 703 if (coap_fd != -1) { 704 /* 705 * Using epoll. It is more usual to call coap_io_process() with wait_ms 706 * (as in the non-epoll branch), but doing it this way gives the 707 * flexibility of potentially working with other file descriptors that 708 * are not a part of libcoap. 709 */ 710 fd_set readfds = m_readfds; 711 struct timeval tv; 712 coap_tick_t begin, end; 713 714 coap_ticks(&begin); 715 716 tv.tv_sec = wait_ms / 1000; 717 tv.tv_usec = (wait_ms % 1000) * 1000; 718 /* Wait until any i/o takes place or timeout */ 719 result = select(nfds, &readfds, NULL, NULL, &tv); 720 if (result == -1) { 721 if (errno != EAGAIN) { 722 coap_log_debug("select: %s (%d)\n", coap_socket_strerror(), 723 errno); 724 break; 725 } 726 } 727 if (result > 0) { 728 if (FD_ISSET(coap_fd, &readfds)) { 729 result = coap_io_process(ctx, COAP_IO_NO_WAIT); 730 } 731 } 732 if (result >= 0) { 733 coap_ticks(&end); 734 /* Track the overall time spent in select() and coap_io_process() */ 735 result = (int)(end - begin); 736 } 737 } else { 738 /* 739 * epoll is not supported within libcoap 740 * 741 * result is time spent in coap_io_process() 742 */ 743 result = coap_io_process(ctx, wait_ms); 744 } 745 if (result < 0) { 746 break; 747 } else if (result && (unsigned)result < wait_ms) { 748 /* decrement if there is a result wait time returned */ 749 wait_ms -= result; 750 } else { 751 /* 752 * result == 0, or result >= wait_ms 753 * (wait_ms could have decremented to a small value, below 754 * the granularity of the timer in coap_io_process() and hence 755 * result == 0) 756 */ 757 wait_ms = COAP_RESOURCE_CHECK_TIME * 1000; 758 } 759 if (r_observe_1 || r_observe_2) { 760 coap_time_t t_now; 761 unsigned int next_sec_ms; 762 763 coap_ticks(&now); 764 t_now = coap_ticks_to_rt(now); 765 if (t_last != t_now) { 766 /* Happens once per second */ 767 t_last = t_now; 768 if (r_observe_1) 769 coap_resource_notify_observers(r_observe_1, NULL); 770 if (r_observe_2) 771 coap_resource_notify_observers(r_observe_2, NULL); 772 } 773 /* need to wait until next second starts if wait_ms is too large */ 774 next_sec_ms = 1000 - (now % COAP_TICKS_PER_SECOND) * 775 1000 / COAP_TICKS_PER_SECOND; 776 if (next_sec_ms && next_sec_ms < wait_ms) 777 wait_ms = next_sec_ms; 778 } 779 } 780 781finish: 782 783 if (oscore_seq_num_fp) 784 fclose(oscore_seq_num_fp); 785 786 coap_free_context(ctx); 787 coap_cleanup(); 788 789 return 0; 790} 791