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
35 static char *
strndup(const char *s1, size_t n)36 strndup(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 
63 static coap_oscore_conf_t *oscore_conf;
64 static int doing_oscore = 0;
65 
66 /* set to 1 to request clean server shutdown */
67 static int quit = 0;
68 
69 static coap_resource_t *r_observe_1;
70 static coap_resource_t *r_observe_2;
71 
72 static int resource_flags = COAP_RESOURCE_FLAGS_NOTIFY_CON;
73 
74 static uint32_t block_mode = COAP_BLOCK_USE_LIBCOAP;
75 static uint32_t csm_max_message_size = 0;
76 
77 /* SIGINT handler: set quit to 1 for graceful termination */
78 static void
handle_sigint(int signum COAP_UNUSED)79 handle_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 
88 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)89 hnd_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 
104 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)105 hnd_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 
117 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)118 hnd_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 
130 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)131 hnd_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 
143 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)144 hnd_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 
156 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)157 hnd_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 
173 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)174 hnd_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 
204 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)205 hnd_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 
239 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)240 hnd_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 
268 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)269 hnd_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 
277 static void
init_resources(coap_context_t *ctx)278 init_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 
339 static uint8_t *
read_file_mem(const char *file, size_t *length)340 read_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 
371 static void
usage(const char *program, const char *version)372 usage(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 
433 static coap_context_t *
get_context(const char *node, const char *port)434 get_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 
488 finish:
489   freeaddrinfo(result);
490   return ctx;
491 }
492 
493 static FILE *oscore_seq_num_fp = NULL;
494 static const char *oscore_conf_file = NULL;
495 static const char *oscore_seq_save_file = NULL;
496 
497 static int
oscore_save_seq_num(uint64_t sender_seq_num, void *param COAP_UNUSED)498 oscore_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 
507 static coap_oscore_conf_t *
get_oscore_conf(coap_context_t *context)508 get_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 
549 static int
cmdline_oscore(char *arg)550 cmdline_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 
569 int
main(int argc, char **argv)570 main(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 
781 finish:
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