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