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