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