1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 * -*- */
2
3 /* coap -- simple implementation of the Constrained Application Protocol (CoAP)
4 * as defined in RFC 7252
5 *
6 * Copyright (C) 2010--2015,2022-2023 Olaf Bergmann <bergmann@tzi.org>
7 *
8 * SPDX-License-Identifier: BSD-2-Clause
9 *
10 * This file is part of the CoAP library libcoap. Please see README for terms of
11 * use.
12 */
13
14
15 /**
16 * @file coap-rd.c
17 * @brief CoRE resource directory
18 *
19 * @see https://tools.ietf.org/html/draft-ietf-core-resource-directory
20 */
21
22 #include <string.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <ctype.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <errno.h>
29 #include <signal.h>
30 #ifdef _WIN32
31 #define strcasecmp _stricmp
32 #include "getopt.c"
33 #if !defined(S_ISDIR)
34 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
35 #endif
36 #else
37 #include <unistd.h>
38 #include <sys/select.h>
39 #include <sys/socket.h>
40 #include <netinet/in.h>
41 #include <arpa/inet.h>
42 #include <netdb.h>
43 #include <dirent.h>
44 #endif
45
46 #include <coap3/coap.h>
47
48 #define COAP_RESOURCE_CHECK_TIME 2
49
50 #define RD_ROOT_STR "rd"
51 #define RD_ROOT_SIZE 2
52
53 static char *cert_file = NULL; /* Combined certificate and private key in PEM */
54 static char *ca_file = NULL; /* CA for cert_file - for cert checking in PEM */
55 static char *root_ca_file = NULL; /* List of trusted Root CAs in PEM */
56 static int verify_peer_cert = 1; /* PKI granularity - by default set */
57 #define MAX_KEY 64 /* Maximum length of a pre-shared key in bytes. */
58 static uint8_t key[MAX_KEY];
59 static ssize_t key_length = 0;
60 static int key_defined = 0;
61 static const char *hint = "CoAP";
62 static size_t extended_token_size = COAP_TOKEN_DEFAULT_MAX;
63 static int enable_ws = 0;
64 static int ws_port = 80;
65 static int wss_port = 443;
66
67 #ifndef min
68 #define min(a,b) ((a) < (b) ? (a) : (b))
69 #endif
70
71 typedef struct rd_t {
72 size_t etag_len; /**< actual length of @c etag */
73 unsigned char etag[8]; /**< ETag for current description */
74
75 coap_string_t data; /**< points to the resource description */
76 } rd_t;
77
78 rd_t *resources = NULL;
79
80 static ssize_t
cmdline_read_key(char * arg,unsigned char * buf,size_t maxlen)81 cmdline_read_key(char *arg, unsigned char *buf, size_t maxlen) {
82 size_t len = strnlen(arg, maxlen);
83 if (len) {
84 memcpy(buf, arg, len);
85 return len;
86 }
87 return -1;
88 }
89
90 static int
cmdline_ws(char * arg)91 cmdline_ws(char *arg) {
92 char *cp = strchr(arg, ',');
93
94 if (cp) {
95 if (cp != arg)
96 ws_port = atoi(arg);
97 cp++;
98 if (*cp != '\000')
99 wss_port = atoi(cp);
100 } else {
101 ws_port = atoi(arg);
102 }
103 return 1;
104 }
105
106 static inline rd_t *
rd_new(void)107 rd_new(void) {
108 rd_t *rd;
109 rd = (rd_t *)coap_malloc(sizeof(rd_t));
110 if (rd)
111 memset(rd, 0, sizeof(rd_t));
112
113 return rd;
114 }
115
116 static void
rd_delete(rd_t * rd)117 rd_delete(rd_t *rd) {
118 if (rd) {
119 coap_free(rd->data.s);
120 coap_free(rd);
121 }
122 }
123
124 static void
resource_rd_delete(void * ptr)125 resource_rd_delete(void *ptr) {
126 rd_delete(ptr);
127 }
128
129 static int quit = 0;
130
131 /* SIGINT handler: set quit to 1 for graceful termination */
132 static void
handle_sigint(int signum COAP_UNUSED)133 handle_sigint(int signum COAP_UNUSED) {
134 quit = 1;
135 }
136
137 static void
hnd_get_resource(coap_resource_t * resource,coap_session_t * session COAP_UNUSED,const coap_pdu_t * request COAP_UNUSED,const coap_string_t * query COAP_UNUSED,coap_pdu_t * response)138 hnd_get_resource(coap_resource_t *resource,
139 coap_session_t *session COAP_UNUSED,
140 const coap_pdu_t *request COAP_UNUSED,
141 const coap_string_t *query COAP_UNUSED,
142 coap_pdu_t *response) {
143 rd_t *rd = coap_resource_get_userdata(resource);
144 unsigned char buf[3];
145
146 coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
147
148 coap_add_option(response,
149 COAP_OPTION_CONTENT_TYPE,
150 coap_encode_var_safe(buf, sizeof(buf),
151 COAP_MEDIATYPE_APPLICATION_LINK_FORMAT),
152 buf);
153
154 if (rd && rd->etag_len)
155 coap_add_option(response, COAP_OPTION_ETAG, rd->etag_len, rd->etag);
156
157 if (rd && rd->data.s)
158 coap_add_data(response, rd->data.length, rd->data.s);
159 }
160
161 static void
hnd_put_resource(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)162 hnd_put_resource(coap_resource_t *resource COAP_UNUSED,
163 coap_session_t *session COAP_UNUSED,
164 const coap_pdu_t *request COAP_UNUSED,
165 const coap_string_t *query COAP_UNUSED,
166 coap_pdu_t *response) {
167 #if 1
168 coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_IMPLEMENTED);
169 #else /* FIXME */
170 coap_opt_iterator_t opt_iter;
171 coap_opt_t *token, *etag;
172 coap_pdu_t *response;
173 size_t size = sizeof(coap_hdr_t);
174 int type = (request->hdr->type == COAP_MESSAGE_CON)
175 ? COAP_MESSAGE_ACK : COAP_MESSAGE_NON;
176 rd_t *rd = NULL;
177 unsigned char code; /* result code */
178 const uint8_t *data;
179 coap_string_t tmp;
180
181 HASH_FIND(hh, resources, resource->uri_path.s, resource->uri_path.length, rd);
182 if (rd) {
183 /* found resource object, now check Etag */
184 etag = coap_check_option(request, COAP_OPTION_ETAG, &opt_iter);
185 if (!etag || (COAP_OPT_LENGTH(etag) != rd->etag_len)
186 || memcmp(COAP_OPT_VALUE(etag), rd->etag, rd->etag_len) != 0) {
187
188 if (coap_get_data(request, &tmp.length, &data)) {
189
190 tmp.s = (unsigned char *)coap_malloc(tmp.length);
191 if (!tmp.s) {
192 coap_log_debug("hnd_put_rd: cannot allocate storage for new rd\n");
193 code = COAP_RESPONSE_CODE_SERVICE_UNAVAILABLE;
194 goto finish;
195 }
196
197 coap_free(rd->data.s);
198 rd->data.s = tmp.s;
199 rd->data.length = tmp.length;
200 memcpy(rd->data.s, data, rd->data.length);
201 }
202 }
203
204 if (etag) {
205 rd->etag_len = min(COAP_OPT_LENGTH(etag), sizeof(rd->etag));
206 memcpy(rd->etag, COAP_OPT_VALUE(etag), rd->etag_len);
207 }
208
209 code = COAP_RESPONSE_CODE_CHANGED;
210 /* FIXME: update lifetime */
211
212 } else {
213
214 code = COAP_RESPONSE_CODE_SERVICE_UNAVAILABLE;
215 }
216
217 finish:
218 /* FIXME: do not create a new response but use the old one instead */
219 response = coap_pdu_init(type, code, request->hdr->id, size);
220
221 if (!response) {
222 coap_log_debug("cannot create response for mid=0x%x\n",
223 request->hdr->id);
224 return;
225 }
226
227 if (request->hdr->token_length)
228 coap_add_token(response, request->hdr->token_length, request->hdr->token);
229
230 if (coap_send(ctx, peer, response) == COAP_INVALID_MID) {
231 coap_log_debug("hnd_get_rd: cannot send response for mid=0x%x\n",
232 request->hdr->id);
233 }
234 #endif
235 }
236
237 static void
hnd_delete_resource(coap_resource_t * resource,coap_session_t * session COAP_UNUSED,const coap_pdu_t * request COAP_UNUSED,const coap_string_t * query COAP_UNUSED,coap_pdu_t * response)238 hnd_delete_resource(coap_resource_t *resource,
239 coap_session_t *session COAP_UNUSED,
240 const coap_pdu_t *request COAP_UNUSED,
241 const coap_string_t *query COAP_UNUSED,
242 coap_pdu_t *response) {
243 rd_t *rd = coap_resource_get_userdata(resource);
244
245 if (rd) {
246 rd_delete(rd);
247 }
248 /* FIXME: link attributes for resource have been created dynamically
249 * using coap_malloc() and must be released. */
250 coap_delete_resource(NULL, resource);
251
252 coap_pdu_set_code(response, COAP_RESPONSE_CODE_DELETED);
253 }
254
255 static void
hnd_get_rd(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)256 hnd_get_rd(coap_resource_t *resource COAP_UNUSED,
257 coap_session_t *session COAP_UNUSED,
258 const coap_pdu_t *request COAP_UNUSED,
259 const coap_string_t *query COAP_UNUSED,
260 coap_pdu_t *response) {
261 unsigned char buf[3];
262
263 coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
264
265 coap_add_option(response,
266 COAP_OPTION_CONTENT_TYPE,
267 coap_encode_var_safe(buf, sizeof(buf),
268 COAP_MEDIATYPE_APPLICATION_LINK_FORMAT),
269 buf);
270
271 coap_add_option(response,
272 COAP_OPTION_MAXAGE,
273 coap_encode_var_safe(buf, sizeof(buf), 0x2ffff), buf);
274 }
275
276 static int
parse_param(const uint8_t * search,size_t search_len,unsigned char * data,size_t data_len,coap_string_t * result)277 parse_param(const uint8_t *search,
278 size_t search_len,
279 unsigned char *data,
280 size_t data_len,
281 coap_string_t *result) {
282
283 if (result)
284 memset(result, 0, sizeof(coap_string_t));
285
286 if (!search_len)
287 return 0;
288
289 while (search_len <= data_len) {
290
291 /* handle parameter if found */
292 if (memcmp(search, data, search_len) == 0) {
293 data += search_len;
294 data_len -= search_len;
295
296 /* key is only valid if we are at end of string or delimiter follows */
297 if (!data_len || *data == '=' || *data == '&') {
298 while (data_len && *data != '=') {
299 ++data;
300 --data_len;
301 }
302
303 if (data_len > 1 && result) {
304 /* value begins after '=' */
305
306 result->s = ++data;
307 while (--data_len && *data != '&') {
308 ++data;
309 result->length++;
310 }
311 }
312
313 return 1;
314 }
315 }
316
317 /* otherwise proceed to next */
318 while (--data_len && *data++ != '&')
319 ;
320 }
321
322 return 0;
323 }
324
325 static void
add_source_address(coap_resource_t * resource,const coap_address_t * peer)326 add_source_address(coap_resource_t *resource,
327 const coap_address_t *peer) {
328 #define BUFSIZE 64
329 char *buf;
330 size_t n = 1;
331 coap_str_const_t attr_val;
332
333 buf = (char *)coap_malloc(BUFSIZE);
334 if (!buf)
335 return;
336
337 n = coap_print_addr(peer, (uint8_t *)buf, BUFSIZE);
338 if (n) {
339 attr_val.s = (const uint8_t *)buf;
340 attr_val.length = n;
341 coap_add_attr(resource,
342 coap_make_str_const("A"),
343 &attr_val,
344 0);
345 }
346 coap_free(buf);
347 #undef BUFSIZE
348 }
349
350 static rd_t *
make_rd(const coap_pdu_t * pdu)351 make_rd(const coap_pdu_t *pdu) {
352 rd_t *rd;
353 const uint8_t *data;
354 coap_opt_iterator_t opt_iter;
355 coap_opt_t *etag;
356
357 rd = rd_new();
358
359 if (!rd) {
360 coap_log_debug("hnd_get_rd: cannot allocate storage for rd\n");
361 return NULL;
362 }
363
364 if (coap_get_data(pdu, &rd->data.length, &data)) {
365 rd->data.s = (unsigned char *)coap_malloc(rd->data.length);
366 if (!rd->data.s) {
367 coap_log_debug("hnd_get_rd: cannot allocate storage for rd->data\n");
368 rd_delete(rd);
369 return NULL;
370 }
371 memcpy(rd->data.s, data, rd->data.length);
372 }
373
374 etag = coap_check_option(pdu, COAP_OPTION_ETAG, &opt_iter);
375 if (etag) {
376 rd->etag_len = min(coap_opt_length(etag), sizeof(rd->etag));
377 memcpy(rd->etag, coap_opt_value(etag), rd->etag_len);
378 }
379
380 return rd;
381 }
382
383 static void
hnd_post_rd(coap_resource_t * resource COAP_UNUSED,coap_session_t * session,const coap_pdu_t * request,const coap_string_t * query COAP_UNUSED,coap_pdu_t * response)384 hnd_post_rd(coap_resource_t *resource COAP_UNUSED,
385 coap_session_t *session,
386 const coap_pdu_t *request,
387 const coap_string_t *query COAP_UNUSED,
388 coap_pdu_t *response) {
389 coap_resource_t *r;
390 #define LOCSIZE 68
391 unsigned char *loc;
392 size_t loc_size;
393 coap_string_t h = {0, NULL}, ins = {0, NULL}, rt = {0, NULL}, lt = {0, NULL}; /* store query parameters */
394 unsigned char *buf;
395 coap_str_const_t attr_val;
396 coap_str_const_t resource_val;
397
398 loc = (unsigned char *)coap_malloc(LOCSIZE);
399 if (!loc) {
400 coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR);
401 return;
402 }
403 memcpy(loc, RD_ROOT_STR, RD_ROOT_SIZE);
404
405 loc_size = RD_ROOT_SIZE;
406 loc[loc_size++] = '/';
407
408 /* store query parameters for later use */
409 if (query) {
410 parse_param((const uint8_t *)"h", 1, query->s, query->length, &h);
411 parse_param((const uint8_t *)"ins", 3, query->s, query->length, &ins);
412 parse_param((const uint8_t *)"lt", 2, query->s, query->length, <);
413 parse_param((const uint8_t *)"rt", 2, query->s, query->length, &rt);
414 }
415
416 if (h.length) { /* client has specified a node name */
417 memcpy(loc + loc_size, h.s, min(h.length, LOCSIZE - loc_size - 1));
418 loc_size += min(h.length, LOCSIZE - loc_size - 1);
419
420 if (ins.length && loc_size > 1) {
421 loc[loc_size++] = '-';
422 memcpy((char *)(loc + loc_size),
423 ins.s, min(ins.length, LOCSIZE - loc_size - 1));
424 loc_size += min(ins.length, LOCSIZE - loc_size - 1);
425 }
426
427 } else { /* generate node identifier */
428 loc_size +=
429 snprintf((char *)(loc + loc_size), LOCSIZE - loc_size - 1,
430 "%x", coap_pdu_get_mid(request));
431
432 if (loc_size > 1) {
433 if (ins.length) {
434 loc[loc_size++] = '-';
435 memcpy((char *)(loc + loc_size),
436 ins.s,
437 min(ins.length, LOCSIZE - loc_size - 1));
438 loc_size += min(ins.length, LOCSIZE - loc_size - 1);
439 } else {
440 coap_tick_t now;
441 coap_ticks(&now);
442
443 loc_size += snprintf((char *)(loc + loc_size),
444 LOCSIZE - loc_size - 1,
445 "-%x",
446 (unsigned int)(now & (unsigned int)-1));
447 }
448 }
449 }
450
451 /* TODO:
452 * - use lt to check expiration
453 */
454
455 resource_val.s = loc;
456 resource_val.length = loc_size;
457 r = coap_resource_init(&resource_val, 0);
458 coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get_resource);
459 coap_register_request_handler(r, COAP_REQUEST_PUT, hnd_put_resource);
460 coap_register_request_handler(r, COAP_REQUEST_DELETE, hnd_delete_resource);
461
462 if (ins.s) {
463 buf = (unsigned char *)coap_malloc(ins.length + 2);
464 if (buf) {
465 /* add missing quotes */
466 buf[0] = '"';
467 memcpy(buf + 1, ins.s, ins.length);
468 buf[ins.length + 1] = '"';
469 attr_val.s = buf;
470 attr_val.length = ins.length + 2;
471 coap_add_attr(r,
472 coap_make_str_const("ins"),
473 &attr_val,
474 0);
475 coap_free(buf);
476 }
477 }
478
479 if (rt.s) {
480 buf = (unsigned char *)coap_malloc(rt.length + 2);
481 if (buf) {
482 /* add missing quotes */
483 buf[0] = '"';
484 memcpy(buf + 1, rt.s, rt.length);
485 buf[rt.length + 1] = '"';
486 attr_val.s = buf;
487 attr_val.length = rt.length + 2;
488 coap_add_attr(r,
489 coap_make_str_const("rt"),
490 &attr_val,
491 0);
492 coap_free(buf);
493 }
494 }
495
496 add_source_address(r, coap_session_get_addr_remote(session));
497
498 {
499 rd_t *rd;
500 rd = make_rd(request);
501 if (rd) {
502 coap_resource_set_userdata(r, rd);
503 } else {
504 /* FIXME: send error response and delete r */
505 }
506 }
507
508 coap_add_resource(coap_session_get_context(session), r);
509
510
511 /* create response */
512
513 coap_pdu_set_code(response, COAP_RESPONSE_CODE_CREATED);
514
515 {
516 /* split path into segments and add Location-Path options */
517 unsigned char _b[LOCSIZE];
518 unsigned char *b = _b;
519 size_t buflen = sizeof(_b);
520 int nseg;
521
522 nseg = coap_split_path(loc, loc_size, b, &buflen);
523 while (nseg--) {
524 coap_add_option(response,
525 COAP_OPTION_LOCATION_PATH,
526 coap_opt_length(b),
527 coap_opt_value(b));
528 b += coap_opt_size(b);
529 }
530 }
531 coap_free(loc);
532 }
533
534 static void
init_resources(coap_context_t * ctx)535 init_resources(coap_context_t *ctx) {
536 coap_resource_t *r;
537
538 r = coap_resource_init(coap_make_str_const(RD_ROOT_STR), 0);
539 coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get_rd);
540 coap_register_request_handler(r, COAP_REQUEST_POST, hnd_post_rd);
541
542 coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("40"), 0);
543 coap_add_attr(r, coap_make_str_const("rt"), coap_make_str_const("\"core.rd\""), 0);
544 coap_add_attr(r, coap_make_str_const("ins"), coap_make_str_const("\"default\""), 0);
545
546 coap_add_resource(ctx, r);
547
548 coap_resource_release_userdata_handler(ctx, resource_rd_delete);
549 }
550
551 static void
usage(const char * program,const char * version)552 usage(const char *program, const char *version) {
553 const char *p;
554 char buffer[120];
555 const char *lib_build = coap_package_build();
556
557 p = strrchr(program, '/');
558 if (p)
559 program = ++p;
560
561 fprintf(stderr, "%s v%s -- CoRE Resource Directory implementation\n"
562 "(c) 2011-2012,2019-2023 Olaf Bergmann <bergmann@tzi.org> and others\n\n"
563 "Build: %s\n"
564 "%s\n"
565 , program, version, lib_build,
566 coap_string_tls_version(buffer, sizeof(buffer)));
567 fprintf(stderr, "%s\n", coap_string_tls_support(buffer, sizeof(buffer)));
568 fprintf(stderr, "\n"
569 "Usage: %s [-g group] [-G group_if] [-p port] [-v num] [-A address]\n"
570 "\t [-w [port][,secure_port]] [-T max_token_size] [-V num]\n"
571 "\t [[-h hint] [-k key]]\n"
572 "\t [[-c certfile] [-C cafile] [-n] [-R trust_casfile]]\n"
573 "General Options\n"
574 "\t-g group\tJoin the given multicast group.\n"
575 "\t \t\tNote: DTLS over multicast is not currently supported\n"
576 "\t-G group_if\tUse this interface for listening for the multicast\n"
577 "\t \t\tgroup. This can be different from the implied interface\n"
578 "\t \t\tif the -A option is used\n"
579 "\t-p port\t\tListen on specified port\n"
580 "\t-v num \t\tVerbosity level (default 4, maximum is 8) for general\n"
581 "\t \t\tCoAP logging\n"
582 "\t-w [port][,secure_port]\n"
583 "\t \t\tEnable WebSockets support on port (WS) and/or secure_port\n"
584 "\t \t\t(WSS), comma separated\n"
585 "\t-A address\tInterface address to bind to\n"
586 "\t-T max_token_length\tSet the maximum token length (8-65804)\n"
587 "\t-V num \t\tVerbosity level (default 3, maximum is 7) for (D)TLS\n"
588 "\t \t\tlibrary logging\n"
589 "PSK Options (if supported by underlying (D)TLS library)\n"
590 "\t-h hint\t\tIdentity Hint. Default is CoAP. Zero length is no hint\n"
591 "\t-k key \t\tPre-Shared Key. This argument requires (D)TLS with PSK\n"
592 "\t \t\tto be available. This cannot be empty if defined.\n"
593 "\t \t\tNote that both -c and -k need to be defined\n"
594 "\t \t\tfor both PSK and PKI to be concurrently supported\n"
595 "PKI Options (if supported by underlying (D)TLS library)\n"
596 "\t-c certfile\tPEM file containing both CERTIFICATE and PRIVATE KEY\n"
597 "\t \t\tThis argument requires (D)TLS with PKI to be available\n"
598 "\t-n \t\tDisable remote peer certificate checking. This gives\n"
599 "\t \t\tclients the ability to use PKI, but without any defined\n"
600 "\t \t\tcertificates\n"
601 "\t-C cafile\tPEM file that contains a list of one or\n"
602 "\t \t\tmore CAs that are to be passed to the client for the\n"
603 "\t \t\tclient to determine what client certificate to use.\n"
604 "\t \t\tNormally, this list of CAs would be the root CA and and\n"
605 "\t \t\tany intermediate CAs. Ideally the server certificate\n"
606 "\t \t\tshould be signed by the same CA so that mutual\n"
607 "\t \t\tauthentication can take place. The contents of cafile\n"
608 "\t \t\tare added to the trusted store of root CAs.\n"
609 "\t \t\tUsing the -C or -R options will will trigger the\n"
610 "\t \t\tvalidation of the client certificate unless overridden\n"
611 "\t \t\tby the -n option\n"
612 "\t-R trust_casfile\tPEM file containing the set of trusted root CAs\n"
613 "\t \t\tthat are to be used to validate the client certificate.\n"
614 "\t \t\tAlternatively, this can point to a directory containing\n"
615 "\t \t\ta set of CA PEM files.\n"
616 "\t \t\tUsing '-R trust_casfile' disables common CA mutual\n"
617 "\t \t\tauthentication which can only be done by using\n"
618 "\t \t\t'-C cafile'.\n"
619 "\t \t\tUsing the -C or -R options will will trigger the\n"
620 "\t \t\tvalidation of the client certificate unless overridden\n"
621 "\t \t\tby the -n option\n"
622 ,
623 program);
624 }
625
626 static void
fill_keystore(coap_context_t * ctx)627 fill_keystore(coap_context_t *ctx) {
628 if (cert_file == NULL && key_defined == 0) {
629 if (coap_dtls_is_supported() || coap_tls_is_supported()) {
630 coap_log_debug("(D)TLS not enabled as neither -k or -c options specified\n");
631 }
632 }
633 if (cert_file) {
634 coap_dtls_pki_t dtls_pki;
635 memset(&dtls_pki, 0, sizeof(dtls_pki));
636 dtls_pki.version = COAP_DTLS_PKI_SETUP_VERSION;
637 if (ca_file || root_ca_file) {
638 /*
639 * Add in additional certificate checking.
640 * This list of enabled can be tuned for the specific
641 * requirements - see 'man coap_encryption'.
642 */
643 dtls_pki.verify_peer_cert = verify_peer_cert;
644 dtls_pki.check_common_ca = !root_ca_file;
645 dtls_pki.allow_self_signed = 1;
646 dtls_pki.allow_expired_certs = 1;
647 dtls_pki.cert_chain_validation = 1;
648 dtls_pki.cert_chain_verify_depth = 2;
649 dtls_pki.check_cert_revocation = 1;
650 dtls_pki.allow_no_crl = 1;
651 dtls_pki.allow_expired_crl = 1;
652 dtls_pki.validate_cn_call_back = NULL;
653 dtls_pki.cn_call_back_arg = NULL;
654 dtls_pki.validate_sni_call_back = NULL;
655 dtls_pki.sni_call_back_arg = NULL;
656 }
657 dtls_pki.pki_key.key_type = COAP_PKI_KEY_PEM;
658 dtls_pki.pki_key.key.pem.public_cert = cert_file;
659 dtls_pki.pki_key.key.pem.private_key = cert_file;
660 dtls_pki.pki_key.key.pem.ca_file = ca_file;
661 /* If general root CAs are defined */
662 if (root_ca_file) {
663 struct stat stbuf;
664 if ((stat(root_ca_file, &stbuf) == 0) && S_ISDIR(stbuf.st_mode)) {
665 coap_context_set_pki_root_cas(ctx, NULL, root_ca_file);
666 } else {
667 coap_context_set_pki_root_cas(ctx, root_ca_file, NULL);
668 }
669 }
670 coap_context_set_pki(ctx, &dtls_pki);
671 }
672 if (key_defined) {
673 coap_dtls_spsk_t dtls_psk;
674 memset(&dtls_psk, 0, sizeof(dtls_psk));
675 dtls_psk.version = COAP_DTLS_SPSK_SETUP_VERSION;
676 dtls_psk.validate_id_call_back = NULL;
677 dtls_psk.validate_sni_call_back = NULL;
678 dtls_psk.psk_info.hint.s = (const uint8_t *)hint;
679 dtls_psk.psk_info.hint.length = hint ? strlen(hint) : 0;
680 dtls_psk.psk_info.key.s = key;
681 dtls_psk.psk_info.key.length = key_length;
682 coap_context_set_psk2(ctx, &dtls_psk);
683 }
684 }
685
686 static coap_context_t *
get_context(const char * node,const char * port)687 get_context(const char *node, const char *port) {
688 coap_context_t *ctx = NULL;
689 coap_addr_info_t *info = NULL;
690 coap_addr_info_t *info_list = NULL;
691 coap_str_const_t local;
692 int have_ep = 0;
693 uint16_t u_s_port = 0;
694 uint16_t s_port = 0;
695 int scheme_hint_bits = 0;
696
697 ctx = coap_new_context(NULL);
698 if (!ctx) {
699 return NULL;
700 }
701
702 /* Need PKI/RPK/PSK set up before we set up (D)TLS endpoints */
703 fill_keystore(ctx);
704
705 if (node) {
706 local.s = (const uint8_t *)node;
707 local.length = strlen(node);
708 }
709
710 if (port) {
711 u_s_port = atoi(port);
712 s_port = u_s_port + 1;
713 }
714 scheme_hint_bits =
715 coap_get_available_scheme_hint_bits(cert_file != NULL || key_defined != 0,
716 enable_ws, COAP_PROTO_NONE);
717 info_list = coap_resolve_address_info(node ? &local : NULL, u_s_port, s_port,
718 ws_port, wss_port,
719 AI_PASSIVE | AI_NUMERICHOST,
720 scheme_hint_bits,
721 COAP_RESOLVE_TYPE_LOCAL);
722 for (info = info_list; info != NULL; info = info->next) {
723 coap_endpoint_t *ep;
724
725 ep = coap_new_endpoint(ctx, &info->addr, info->proto);
726 if (!ep) {
727 coap_log_warn("cannot create endpoint for proto %u\n",
728 info->proto);
729 } else {
730 have_ep = 1;
731 }
732 }
733 coap_free_address_info(info_list);
734 if (!have_ep) {
735 coap_log_err("No context available for interface '%s'\n", node);
736 coap_free_context(ctx);
737 return NULL;
738 }
739 return ctx;
740 }
741
742 static int
cmdline_read_extended_token_size(char * arg)743 cmdline_read_extended_token_size(char *arg) {
744 extended_token_size = strtoul(arg, NULL, 0);
745 if (extended_token_size < COAP_TOKEN_DEFAULT_MAX) {
746 coap_log_err("Extended Token Length must be 8 or greater\n");
747 return 0;
748 } else if (extended_token_size > COAP_TOKEN_EXT_MAX) {
749 coap_log_err("Extended Token Length must be 65804 or less\n");
750 return 0;
751 }
752 return 1;
753 }
754
755 int
main(int argc,char ** argv)756 main(int argc, char **argv) {
757 coap_context_t *ctx;
758 int result;
759 char addr_str[NI_MAXHOST] = "::";
760 char port_str[NI_MAXSERV] = "5683";
761 char *group = NULL;
762 char *group_if = NULL;
763 int opt;
764 coap_log_t log_level = COAP_LOG_WARN;
765 coap_log_t dtls_log_level = COAP_LOG_ERR;
766 #ifndef _WIN32
767 struct sigaction sa;
768 #endif
769
770 /* Initialize libcoap library */
771 coap_startup();
772
773 while ((opt = getopt(argc, argv, "A:c:C:g:G:h:k:n:R:p:v:T:V:w:")) != -1) {
774 switch (opt) {
775 case 'A' :
776 strncpy(addr_str, optarg, NI_MAXHOST-1);
777 addr_str[NI_MAXHOST - 1] = '\0';
778 break;
779 case 'c' :
780 cert_file = optarg;
781 break;
782 case 'C' :
783 ca_file = optarg;
784 break;
785 case 'g' :
786 group = optarg;
787 break;
788 case 'G' :
789 group_if = optarg;
790 break;
791 case 'h' :
792 if (!optarg[0]) {
793 hint = NULL;
794 break;
795 }
796 hint = optarg;
797 break;
798 case 'k' :
799 key_length = cmdline_read_key(optarg, key, MAX_KEY);
800 if (key_length < 0) {
801 coap_log_crit("Invalid Pre-Shared Key specified\n");
802 break;
803 }
804 key_defined = 1;
805 break;
806 case 'n':
807 verify_peer_cert = 0;
808 break;
809 case 'p' :
810 strncpy(port_str, optarg, NI_MAXSERV-1);
811 port_str[NI_MAXSERV - 1] = '\0';
812 break;
813 case 'R' :
814 root_ca_file = optarg;
815 break;
816 case 'T':
817 if (!cmdline_read_extended_token_size(optarg)) {
818 exit(1);
819 }
820 break;
821 case 'v' :
822 log_level = strtol(optarg, NULL, 10);
823 break;
824 case 'V':
825 dtls_log_level = strtol(optarg, NULL, 10);
826 break;
827 case 'w':
828 if (!coap_ws_is_supported() || !cmdline_ws(optarg)) {
829 fprintf(stderr, "WebSockets not enabled in libcoap\n");
830 exit(1);
831 }
832 enable_ws = 1;
833 break;
834 default:
835 usage(argv[0], LIBCOAP_PACKAGE_VERSION);
836 exit(1);
837 }
838 }
839
840 coap_set_log_level(log_level);
841 coap_dtls_set_log_level(dtls_log_level);
842
843 ctx = get_context(addr_str, port_str);
844 if (!ctx)
845 return -1;
846
847 if (group)
848 coap_join_mcast_group_intf(ctx, group, group_if);
849
850 init_resources(ctx);
851 if (extended_token_size > COAP_TOKEN_DEFAULT_MAX)
852 coap_context_set_max_token_size(ctx, extended_token_size);
853
854 #ifdef _WIN32
855 signal(SIGINT, handle_sigint);
856 #else
857 memset(&sa, 0, sizeof(sa));
858 sigemptyset(&sa.sa_mask);
859 sa.sa_handler = handle_sigint;
860 sa.sa_flags = 0;
861 sigaction(SIGINT, &sa, NULL);
862 sigaction(SIGTERM, &sa, NULL);
863 /* So we do not exit on a SIGPIPE */
864 sa.sa_handler = SIG_IGN;
865 sigaction(SIGPIPE, &sa, NULL);
866 #endif
867
868 while (!quit) {
869 result = coap_io_process(ctx, COAP_RESOURCE_CHECK_TIME * 1000);
870 if (result >= 0) {
871 /* coap_check_resource_list( ctx ); */
872 }
873 }
874
875 coap_free_context(ctx);
876 coap_cleanup();
877
878 return 0;
879 }
880