• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &lt);
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