• 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 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, &lt);
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