1
0
mirror of https://github.com/obgm/libcoap.git synced 2025-05-09 07:11:27 +08:00
libcoap/examples/oscore-interop-server.c
2025-02-17 09:37:55 +00:00

792 lines
25 KiB
C

/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* oscore-interop-server
*
* A server for use in the RFC 8613 OSCORE interop testing.
* https://core-wg.github.io/oscore/test-spec5.html
*
* Copyright (C) 2022-2025 Olaf Bergmann <bergmann@tzi.org> and others
*
* SPDX-License-Identifier: BSD-2-Clause
*
* This file is part of the CoAP library libcoap. Please see README for terms
* of use.
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <signal.h>
#ifdef _WIN32
#define strcasecmp _stricmp
#define strncasecmp _strnicmp
#include "getopt.c"
#if !defined(S_ISDIR)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
#ifndef R_OK
#define R_OK 4
#endif
static char *
strndup(const char *s1, size_t n) {
char *copy = (char *)malloc(n + 1);
if (copy) {
memcpy(copy, s1, n);
copy[n] = 0;
}
return copy;
}
#include <io.h>
#define access _access
#define fileno _fileno
#else
#include <unistd.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <dirent.h>
#endif
#include <coap3/coap.h>
#ifndef min
#define min(a,b) ((a) < (b) ? (a) : (b))
#endif
static coap_oscore_conf_t *oscore_conf;
static int doing_oscore = 0;
/* set to 1 to request clean server shutdown */
static int quit = 0;
static coap_resource_t *r_observe_1;
static coap_resource_t *r_observe_2;
static int resource_flags = COAP_RESOURCE_FLAGS_NOTIFY_CON;
static uint32_t block_mode = COAP_BLOCK_USE_LIBCOAP;
static uint32_t csm_max_message_size = 0;
/* SIGINT handler: set quit to 1 for graceful termination */
static void
handle_sigint(int signum COAP_UNUSED) {
quit = 1;
}
#define INDEX "This is a OSCORE test server made with libcoap " \
"(see https://libcoap.net)\n" \
"Copyright (C) 2022-2025 Olaf Bergmann <bergmann@tzi.org> " \
"and others\n\n"
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) {
coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
coap_add_data_large_response(resource, session, request, response,
query, COAP_MEDIATYPE_TEXT_PLAIN,
0x2ffff, 0, strlen(INDEX),
(const uint8_t *)INDEX, NULL, NULL);
}
#define HELLO_WORLD "Hello World!"
static void
hnd_get_hello_coap(coap_resource_t *resource,
coap_session_t *session,
const coap_pdu_t *request,
const coap_string_t *query,
coap_pdu_t *response) {
coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
coap_add_data_large_response(resource, session, request, response,
query, COAP_MEDIATYPE_TEXT_PLAIN,
-1, 0, strlen(HELLO_WORLD),
(const uint8_t *)HELLO_WORLD, NULL, NULL);
}
static void
hnd_get_hello_1(coap_resource_t *resource,
coap_session_t *session,
const coap_pdu_t *request,
const coap_string_t *query,
coap_pdu_t *response) {
coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
coap_add_data_large_response(resource, session, request, response,
query, COAP_MEDIATYPE_TEXT_PLAIN,
-1, 0, strlen(HELLO_WORLD),
(const uint8_t *)HELLO_WORLD, NULL, NULL);
}
static void
hnd_get_hello_2(coap_resource_t *resource,
coap_session_t *session,
const coap_pdu_t *request,
const coap_string_t *query,
coap_pdu_t *response) {
coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
coap_add_data_large_response(resource, session, request, response,
query, COAP_MEDIATYPE_TEXT_PLAIN,
-1, 0x2b, strlen(HELLO_WORLD),
(const uint8_t *)HELLO_WORLD, NULL, NULL);
}
static void
hnd_get_hello_3(coap_resource_t *resource,
coap_session_t *session,
const coap_pdu_t *request,
const coap_string_t *query,
coap_pdu_t *response) {
coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
coap_add_data_large_response(resource, session, request, response,
query, COAP_MEDIATYPE_TEXT_PLAIN,
5, 0, strlen(HELLO_WORLD),
(const uint8_t *)HELLO_WORLD, NULL, NULL);
}
static void
hnd_post_hello_6(coap_resource_t *resource,
coap_session_t *session,
const coap_pdu_t *request,
const coap_string_t *query,
coap_pdu_t *response) {
size_t size;
const uint8_t *data;
(void)coap_get_data(request, &size, &data);
coap_pdu_set_code(response, COAP_RESPONSE_CODE_CHANGED);
coap_add_data_large_response(resource, session, request, response,
query, COAP_MEDIATYPE_TEXT_PLAIN,
-1, 0, size,
data, NULL, NULL);
}
static void
hnd_put_hello_7(coap_resource_t *resource,
coap_session_t *session,
const coap_pdu_t *request,
const coap_string_t *query,
coap_pdu_t *response) {
size_t size;
const uint8_t *data;
coap_opt_iterator_t opt_iter;
coap_opt_t *option;
uint64_t etag;
if ((option = coap_check_option(request, COAP_OPTION_IF_MATCH,
&opt_iter)) != NULL) {
etag = coap_decode_var_bytes8(coap_opt_value(option),
coap_opt_length(option));
if (etag != 0x7b) {
coap_pdu_set_code(response, COAP_RESPONSE_CODE_PRECONDITION_FAILED);
return;
}
}
coap_pdu_set_code(response, COAP_RESPONSE_CODE_CHANGED);
(void)coap_get_data(request, &size, &data);
coap_add_data_large_response(resource, session, request, response,
query, COAP_MEDIATYPE_TEXT_PLAIN,
-1, 0x7b, size,
data, NULL, NULL);
}
static void
hnd_get_observe1(coap_resource_t *resource,
coap_session_t *session,
const coap_pdu_t *request,
const coap_string_t *query,
coap_pdu_t *response) {
static int count = 0;
count++;
switch (count) {
case 1:
coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
coap_add_data_large_response(resource, session, request, response,
query, COAP_MEDIATYPE_TEXT_PLAIN,
1, 0, strlen("one"),
(const uint8_t *)"one", NULL, NULL);
break;
case 2:
coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
coap_add_data_large_response(resource, session, request, response,
query, COAP_MEDIATYPE_TEXT_PLAIN,
1, 0, strlen("two"),
(const uint8_t *)"two", NULL, NULL);
break;
default:
coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR);
coap_add_data_large_response(resource, session, request, response,
query, COAP_MEDIATYPE_TEXT_PLAIN,
-1, 0, strlen("Terminate Observe"),
(const uint8_t *)"Terminate Observe",
NULL, NULL);
r_observe_1 = NULL;
}
}
static void
hnd_get_observe2(coap_resource_t *resource,
coap_session_t *session,
const coap_pdu_t *request,
const coap_string_t *query,
coap_pdu_t *response) {
static int count = 0;
count++;
switch (count) {
case 1:
coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
coap_add_data_large_response(resource, session, request, response,
query, COAP_MEDIATYPE_TEXT_PLAIN,
1, 0, strlen("one"),
(const uint8_t *)"one", NULL, NULL);
break;
case 2:
default:
coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
coap_add_data_large_response(resource, session, request, response,
query, COAP_MEDIATYPE_TEXT_PLAIN,
1, 0, strlen("two"),
(const uint8_t *)"two", NULL, NULL);
r_observe_2 = NULL;
break;
}
}
static void
hnd_del_test(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_pdu_set_code(response, COAP_RESPONSE_CODE_DELETED);
}
static void
init_resources(coap_context_t *ctx) {
coap_resource_t *r;
r = coap_resource_init(NULL, COAP_RESOURCE_FLAGS_HAS_MCAST_SUPPORT);
coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get_index);
coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
coap_add_attr(r, coap_make_str_const("title"),
coap_make_str_const("\"General Info\""), 0);
coap_add_resource(ctx, r);
r = coap_resource_init(coap_make_str_const("oscore/hello/coap"),
resource_flags);
coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get_hello_coap);
coap_add_resource(ctx, r);
r = coap_resource_init(coap_make_str_const("oscore/hello/1"),
resource_flags | COAP_RESOURCE_FLAGS_OSCORE_ONLY);
coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get_hello_1);
coap_add_resource(ctx, r);
r = coap_resource_init(coap_make_str_const("oscore/hello/2"),
resource_flags | COAP_RESOURCE_FLAGS_OSCORE_ONLY);
coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get_hello_2);
coap_add_resource(ctx, r);
r = coap_resource_init(coap_make_str_const("oscore/hello/3"),
resource_flags | COAP_RESOURCE_FLAGS_OSCORE_ONLY);
coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get_hello_3);
coap_add_resource(ctx, r);
r = coap_resource_init(coap_make_str_const("oscore/hello/6"),
resource_flags | COAP_RESOURCE_FLAGS_OSCORE_ONLY);
coap_register_request_handler(r, COAP_REQUEST_POST, hnd_post_hello_6);
coap_add_resource(ctx, r);
r = coap_resource_init(coap_make_str_const("oscore/hello/7"),
resource_flags | COAP_RESOURCE_FLAGS_OSCORE_ONLY);
coap_register_request_handler(r, COAP_REQUEST_PUT, hnd_put_hello_7);
coap_add_resource(ctx, r);
r = coap_resource_init(coap_make_str_const("oscore/observe1"),
resource_flags | COAP_RESOURCE_FLAGS_OSCORE_ONLY);
coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get_observe1);
coap_resource_set_get_observable(r, 1);
coap_add_resource(ctx, r);
r_observe_1 = r;
r = coap_resource_init(coap_make_str_const("oscore/observe2"),
resource_flags | COAP_RESOURCE_FLAGS_OSCORE_ONLY);
coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get_observe2);
coap_resource_set_get_observable(r, 1);
coap_add_resource(ctx, r);
r_observe_2 = r;
r = coap_resource_init(coap_make_str_const("oscore/test"),
resource_flags | COAP_RESOURCE_FLAGS_OSCORE_ONLY);
coap_register_request_handler(r, COAP_REQUEST_DELETE, hnd_del_test);
coap_add_resource(ctx, r);
}
static uint8_t *
read_file_mem(const char *file, size_t *length) {
FILE *f;
uint8_t *buf;
struct stat statbuf;
*length = 0;
if (!file || !(f = fopen(file, "r")))
return NULL;
if (fstat(fileno(f), &statbuf) == -1) {
fclose(f);
return NULL;
}
buf = coap_malloc(statbuf.st_size+1);
if (!buf) {
fclose(f);
return NULL;
}
if (fread(buf, 1, statbuf.st_size, f) != (size_t)statbuf.st_size) {
fclose(f);
coap_free(buf);
return NULL;
}
buf[statbuf.st_size] = '\000';
*length = (size_t)(statbuf.st_size + 1);
fclose(f);
return buf;
}
static void
usage(const char *program, const char *version) {
const char *p;
char buffer[120];
const char *lib_build = coap_package_build();
p = strrchr(program, '/');
if (p)
program = ++p;
fprintf(stderr, "%s v%s -- OSCORE interop implementation\n"
"(c) 2022-2025 Olaf Bergmann <bergmann@tzi.org> and others\n\n"
"Build: %s\n"
"%s\n"
, program, version, lib_build,
coap_string_tls_version(buffer, sizeof(buffer)));
fprintf(stderr, "%s\n", coap_string_tls_support(buffer, sizeof(buffer)));
fprintf(stderr, "\n"
"Usage: %s [-d max] [-g group] [-l loss] [-p port] [-r] [-v num]\n"
"\t\t[-A address] [-E oscore_conf_file[,seq_file]] [-G group_if]\n"
"\t\t[-L value] [-N] [-P scheme://address[:port],[name1[,name2..]]]\n"
"\t\t[-X size]\n"
"General Options\n"
"\t-d max \t\tAllow dynamic creation of up to a total of max\n"
"\t \t\tresources. If max is reached, a 4.06 code is returned\n"
"\t \t\tuntil one of the dynamic resources has been deleted\n"
"\t-g group\tJoin the given multicast group\n"
"\t \t\tNote: DTLS over multicast is not currently supported\n"
"\t-l list\t\tFail to send some datagrams specified by a comma\n"
"\t \t\tseparated list of numbers or number ranges\n"
"\t \t\t(for debugging only)\n"
"\t-l loss%%\tRandomly fail to send datagrams with the specified\n"
"\t \t\tprobability - 100%% all datagrams, 0%% no datagrams\n"
"\t \t\t(for debugging only)\n"
"\t-p port\t\tListen on specified port for UDP and TCP. If (D)TLS is\n"
"\t \t\tenabled, then the coap-server will also listen on\n"
"\t \t\t 'port'+1 for DTLS and TLS. The default port is 5683\n"
"\t-r \t\tEnable multicast per resource support. If enabled,\n"
"\t \t\tonly '/', '/async' and '/.well-known/core' are enabled\n"
"\t \t\tfor multicast requests support, otherwise all\n"
"\t \t\tresources are enabled\n"
"\t-v num \t\tVerbosity level (default 3, maximum is 9). Above 7,\n"
"\t \t\tthere is increased verbosity in GnuTLS and OpenSSL\n"
"\t \t\tlogging\n"
"\t-A address\tInterface address to bind to\n"
"\t-E oscore_conf_file[,seq_file]\n"
"\t \t\toscore_conf_file contains OSCORE configuration. See\n"
"\t \t\tcoap-oscore-conf(5) for definitions.\n"
"\t \t\tOptional seq_file is used to save the current transmit\n"
"\t \t\tsequence number, so on restart sequence numbers continue\n"
"\t-G group_if\tUse this interface for listening for the multicast\n"
"\t \t\tgroup. This can be different from the implied interface\n"
"\t \t\tif the -A option is used\n"
"\t-L value\tSum of one or more COAP_BLOCK_* flag valuess for block\n"
"\t \t\thandling methods. Default is 1 (COAP_BLOCK_USE_LIBCOAP)\n"
"\t \t\t(Sum of one or more of 1,2 and 4)\n"
"\t-N \t\tMake \"observe\" responses NON-confirmable. Even if set\n"
"\t \t\tevery fifth response will still be sent as a confirmable\n"
"\t \t\tresponse (RFC 7641 requirement)\n"
, program);
}
static coap_context_t *
get_context(const char *node, const char *port) {
coap_context_t *ctx = NULL;
int s;
struct addrinfo hints;
struct addrinfo *result, *rp;
ctx = coap_new_context(NULL);
if (!ctx) {
return NULL;
}
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
hints.ai_socktype = SOCK_DGRAM; /* Coap uses UDP */
hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
s = getaddrinfo(node, port, &hints, &result);
if (s != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
coap_free_context(ctx);
return NULL;
}
/* iterate through results until success */
for (rp = result; rp != NULL; rp = rp->ai_next) {
coap_address_t addr;
coap_endpoint_t *ep_udp = NULL;
if (rp->ai_addrlen <= (socklen_t)sizeof(addr.addr)) {
coap_address_init(&addr);
addr.size = (socklen_t)rp->ai_addrlen;
memcpy(&addr.addr, rp->ai_addr, rp->ai_addrlen);
ep_udp = coap_new_endpoint(ctx, &addr, COAP_PROTO_UDP);
if (!ep_udp) {
coap_log_crit("cannot create UDP endpoint\n");
continue;
}
if (coap_tcp_is_supported()) {
coap_endpoint_t *ep_tcp;
ep_tcp = coap_new_endpoint(ctx, &addr, COAP_PROTO_TCP);
if (!ep_tcp) {
coap_log_crit("cannot create TCP endpoint\n");
}
}
if (ep_udp)
goto finish;
}
}
fprintf(stderr, "no context available for interface '%s'\n", node);
coap_free_context(ctx);
ctx = NULL;
finish:
freeaddrinfo(result);
return ctx;
}
static FILE *oscore_seq_num_fp = NULL;
static const char *oscore_conf_file = NULL;
static const char *oscore_seq_save_file = NULL;
static int
oscore_save_seq_num(uint64_t sender_seq_num, void *param COAP_UNUSED) {
if (oscore_seq_num_fp) {
rewind(oscore_seq_num_fp);
fprintf(oscore_seq_num_fp, "%" PRIu64 "\n", sender_seq_num);
fflush(oscore_seq_num_fp);
}
return 1;
}
static coap_oscore_conf_t *
get_oscore_conf(coap_context_t *context) {
uint8_t *buf;
size_t length;
coap_str_const_t file_mem;
uint64_t start_seq_num = 0;
buf = read_file_mem(oscore_conf_file, &length);
if (buf == NULL) {
fprintf(stderr, "OSCORE configuration file error: %s\n", oscore_conf_file);
return NULL;
}
file_mem.s = buf;
file_mem.length = length;
if (oscore_seq_save_file) {
oscore_seq_num_fp = fopen(oscore_seq_save_file, "r+");
if (oscore_seq_num_fp == NULL) {
/* Try creating it */
oscore_seq_num_fp = fopen(oscore_seq_save_file, "w+");
if (oscore_seq_num_fp == NULL) {
fprintf(stderr, "OSCORE save restart info file error: %s\n",
oscore_seq_save_file);
coap_free(buf);
return NULL;
}
}
if (fscanf(oscore_seq_num_fp, "%" PRIu64, &start_seq_num) != 1) {
/* Must be empty */
start_seq_num = 0;
}
}
oscore_conf = coap_new_oscore_conf(file_mem,
oscore_save_seq_num,
NULL, start_seq_num);
coap_free(buf);
if (oscore_conf == NULL) {
fprintf(stderr, "OSCORE configuration file error: %s\n", oscore_conf_file);
return NULL;
}
coap_context_oscore_server(context, oscore_conf);
return oscore_conf;
}
static int
cmdline_oscore(char *arg) {
if (coap_oscore_is_supported()) {
char *sep = strchr(arg, ',');
if (sep)
*sep = '\000';
oscore_conf_file = arg;
if (sep) {
sep++;
oscore_seq_save_file = sep;
}
doing_oscore = 1;
return 1;
}
fprintf(stderr, "OSCORE support not enabled\n");
return 0;
}
int
main(int argc, char **argv) {
coap_context_t *ctx;
char *group = NULL;
char *group_if = NULL;
coap_tick_t now;
char addr_str[NI_MAXHOST] = "::";
char port_str[NI_MAXSERV] = "5683";
int opt;
int mcast_per_resource = 0;
coap_log_t log_level = COAP_LOG_WARN;
unsigned wait_ms;
coap_time_t t_last = 0;
int coap_fd;
fd_set m_readfds;
int nfds = 0;
uint16_t cache_ignore_options[] = { COAP_OPTION_BLOCK1,
COAP_OPTION_BLOCK2,
/* See https://rfc-editor.org/rfc/rfc7959#section-2.10 */
COAP_OPTION_MAXAGE,
/* See https://rfc-editor.org/rfc/rfc7959#section-2.10 */
COAP_OPTION_IF_NONE_MATCH
};
#ifndef _WIN32
struct sigaction sa;
#endif
/* Initialize libcoap library */
coap_startup();
while ((opt = getopt(argc, argv, "g:G:l:p:rv:A:E:L:NX:")) != -1) {
switch (opt) {
case 'A' :
strncpy(addr_str, optarg, NI_MAXHOST-1);
addr_str[NI_MAXHOST - 1] = '\0';
break;
case 'E':
if (!cmdline_oscore(optarg)) {
exit(1);
}
break;
case 'g' :
group = optarg;
break;
case 'G' :
group_if = optarg;
break;
case 'l':
if (!coap_debug_set_packet_loss(optarg)) {
usage(argv[0], LIBCOAP_PACKAGE_VERSION);
exit(1);
}
break;
case 'L':
block_mode = strtoul(optarg, NULL, 0);
if (!(block_mode & COAP_BLOCK_USE_LIBCOAP)) {
fprintf(stderr, "Block mode must include COAP_BLOCK_USE_LIBCOAP (1)\n");
exit(-1);
}
break;
case 'N':
resource_flags = COAP_RESOURCE_FLAGS_NOTIFY_NON;
break;
case 'p' :
strncpy(port_str, optarg, NI_MAXSERV-1);
port_str[NI_MAXSERV - 1] = '\0';
break;
case 'r' :
mcast_per_resource = 1;
break;
case 'v' :
log_level = strtol(optarg, NULL, 10);
break;
case 'X':
csm_max_message_size = strtol(optarg, NULL, 10);
break;
default:
usage(argv[0], LIBCOAP_PACKAGE_VERSION);
exit(1);
}
}
#ifdef _WIN32
signal(SIGINT, handle_sigint);
#else
memset(&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_handler = handle_sigint;
sa.sa_flags = 0;
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
/* So we do not exit on a SIGPIPE */
sa.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &sa, NULL);
#endif
coap_dtls_set_log_level(log_level);
coap_set_log_level(log_level);
ctx = get_context(addr_str, port_str);
if (!ctx)
return -1;
init_resources(ctx);
if (mcast_per_resource)
coap_mcast_per_resource(ctx);
coap_context_set_block_mode(ctx, block_mode);
if (csm_max_message_size)
coap_context_set_csm_max_message_size(ctx, csm_max_message_size);
if (doing_oscore) {
if (get_oscore_conf(ctx) == NULL)
goto finish;
}
/* Define the options to ignore when setting up cache-keys */
coap_cache_ignore_options(ctx, cache_ignore_options,
sizeof(cache_ignore_options)/sizeof(cache_ignore_options[0]));
/* join multicast group if requested at command line */
if (group)
coap_join_mcast_group_intf(ctx, group, group_if);
coap_fd = coap_context_get_coap_fd(ctx);
if (coap_fd != -1) {
/* if coap_fd is -1, then epoll is not supported within libcoap */
FD_ZERO(&m_readfds);
FD_SET(coap_fd, &m_readfds);
nfds = coap_fd + 1;
}
wait_ms = COAP_RESOURCE_CHECK_TIME * 1000;
while (!quit) {
int result;
if (coap_fd != -1) {
/*
* Using epoll. It is more usual to call coap_io_process() with wait_ms
* (as in the non-epoll branch), but doing it this way gives the
* flexibility of potentially working with other file descriptors that
* are not a part of libcoap.
*/
fd_set readfds = m_readfds;
struct timeval tv;
coap_tick_t begin, end;
coap_ticks(&begin);
tv.tv_sec = wait_ms / 1000;
tv.tv_usec = (wait_ms % 1000) * 1000;
/* Wait until any i/o takes place or timeout */
result = select(nfds, &readfds, NULL, NULL, &tv);
if (result == -1) {
if (errno != EAGAIN) {
coap_log_debug("select: %s (%d)\n", coap_socket_strerror(),
errno);
break;
}
}
if (result > 0) {
if (FD_ISSET(coap_fd, &readfds)) {
result = coap_io_process(ctx, COAP_IO_NO_WAIT);
}
}
if (result >= 0) {
coap_ticks(&end);
/* Track the overall time spent in select() and coap_io_process() */
result = (int)(end - begin);
}
} else {
/*
* epoll is not supported within libcoap
*
* result is time spent in coap_io_process()
*/
result = coap_io_process(ctx, wait_ms);
}
if (result < 0) {
break;
} else if (result && (unsigned)result < wait_ms) {
/* decrement if there is a result wait time returned */
wait_ms -= result;
} else {
/*
* result == 0, or result >= wait_ms
* (wait_ms could have decremented to a small value, below
* the granularity of the timer in coap_io_process() and hence
* result == 0)
*/
wait_ms = COAP_RESOURCE_CHECK_TIME * 1000;
}
if (r_observe_1 || r_observe_2) {
coap_time_t t_now;
unsigned int next_sec_ms;
coap_ticks(&now);
t_now = coap_ticks_to_rt(now);
if (t_last != t_now) {
/* Happens once per second */
t_last = t_now;
if (r_observe_1)
coap_resource_notify_observers(r_observe_1, NULL);
if (r_observe_2)
coap_resource_notify_observers(r_observe_2, NULL);
}
/* need to wait until next second starts if wait_ms is too large */
next_sec_ms = 1000 - (now % COAP_TICKS_PER_SECOND) *
1000 / COAP_TICKS_PER_SECOND;
if (next_sec_ms && next_sec_ms < wait_ms)
wait_ms = next_sec_ms;
}
}
finish:
if (oscore_seq_num_fp)
fclose(oscore_seq_num_fp);
coap_free_context(ctx);
coap_cleanup();
return 0;
}