• 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--2019 Olaf Bergmann <bergmann@tzi.org> and others
7  *
8  * This file is part of the CoAP library libcoap. Please see README for terms
9  * of use.
10  */
11 
12 #include <string.h>
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <ctype.h>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <errno.h>
19 #include <signal.h>
20 #ifdef _WIN32
21 #define strcasecmp _stricmp
22 #include "getopt.c"
23 #if !defined(S_ISDIR)
24 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
25 #endif
26 #else
27 #include <unistd.h>
28 #include <sys/select.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include <netdb.h>
33 #include <dirent.h>
34 #endif
35 
36 /* Need to refresh time once per sec */
37 #define COAP_RESOURCE_CHECK_TIME 1
38 
39 #include <coap2/coap.h>
40 
41 #ifndef min
42 #define min(a,b) ((a) < (b) ? (a) : (b))
43 #endif
44 
45 /* temporary storage for dynamic resource representations */
46 static int quit = 0;
47 
48 /* changeable clock base (see handle_put_time()) */
49 static time_t clock_offset;
50 static time_t my_clock_base = 0;
51 
52 struct coap_resource_t *time_resource = NULL;
53 
54 static int resource_flags = COAP_RESOURCE_FLAGS_NOTIFY_CON;
55 
56 static char *cert_file = NULL; /* Combined certificate and private key in PEM */
57 static char *ca_file = NULL;   /* CA for cert_file - for cert checking in PEM */
58 static char *root_ca_file = NULL; /* List of trusted Root CAs in PEM */
59 static int require_peer_cert = 1; /* By default require peer cert */
60 #define MAX_KEY   64 /* Maximum length of a key (i.e., PSK) in bytes. */
61 static uint8_t key[MAX_KEY];
62 static ssize_t key_length = 0;
63 int key_defined = 0;
64 static const char *hint = "CoAP";
65 static int support_dynamic = 0;
66 
67 #ifndef WITHOUT_ASYNC
68 /* This variable is used to mimic long-running tasks that require
69  * asynchronous responses. */
70 static coap_async_state_t *async = NULL;
71 #endif /* WITHOUT_ASYNC */
72 
73 #ifdef __GNUC__
74 #define UNUSED_PARAM __attribute__ ((unused))
75 #else /* not a GCC */
76 #define UNUSED_PARAM
77 #endif /* GCC */
78 
79 /* SIGINT handler: set quit to 1 for graceful termination */
80 static void
handle_sigint(int signum UNUSED_PARAM)81 handle_sigint(int signum UNUSED_PARAM) {
82   quit = 1;
83 }
84 
85 #define INDEX "This is a test server made with libcoap (see https://libcoap.net)\n" \
86               "Copyright (C) 2010--2019 Olaf Bergmann <bergmann@tzi.org> and others\n\n"
87 
88 static void
hnd_get_index(coap_context_t * ctx UNUSED_PARAM,struct coap_resource_t * resource,coap_session_t * session,coap_pdu_t * request,coap_binary_t * token,coap_string_t * query UNUSED_PARAM,coap_pdu_t * response)89 hnd_get_index(coap_context_t *ctx UNUSED_PARAM,
90               struct coap_resource_t *resource,
91               coap_session_t *session,
92               coap_pdu_t *request,
93               coap_binary_t *token,
94               coap_string_t *query UNUSED_PARAM,
95               coap_pdu_t *response) {
96 
97   coap_add_data_blocked_response(resource, session, request, response, token,
98                                  COAP_MEDIATYPE_TEXT_PLAIN, 0x2ffff,
99                                  strlen(INDEX),
100                                  (const uint8_t *)INDEX);
101 }
102 
103 static void
hnd_get_time(coap_context_t * ctx UNUSED_PARAM,struct coap_resource_t * resource,coap_session_t * session,coap_pdu_t * request,coap_binary_t * token,coap_string_t * query,coap_pdu_t * response)104 hnd_get_time(coap_context_t  *ctx UNUSED_PARAM,
105              struct coap_resource_t *resource,
106              coap_session_t *session,
107              coap_pdu_t *request,
108              coap_binary_t *token,
109              coap_string_t *query,
110              coap_pdu_t *response) {
111   unsigned char buf[40];
112   size_t len;
113   time_t now;
114   coap_tick_t t;
115   (void)request;
116 
117   /* FIXME: return time, e.g. in human-readable by default and ticks
118    * when query ?ticks is given. */
119 
120   if (my_clock_base) {
121 
122     /* calculate current time */
123     coap_ticks(&t);
124     now = my_clock_base + (t / COAP_TICKS_PER_SECOND);
125 
126     if (query != NULL
127         && coap_string_equal(query, coap_make_str_const("ticks"))) {
128           /* output ticks */
129           len = snprintf((char *)buf, sizeof(buf), "%u", (unsigned int)now);
130 
131     } else {      /* output human-readable time */
132       struct tm *tmp;
133       tmp = gmtime(&now);
134       if (!tmp) {
135         /* If 'now' is not valid */
136         response->code = COAP_RESPONSE_CODE(404);
137         return;
138       }
139       else {
140         len = strftime((char *)buf, sizeof(buf), "%b %d %H:%M:%S", tmp);
141       }
142     }
143     coap_add_data_blocked_response(resource, session, request, response, token,
144                                    COAP_MEDIATYPE_TEXT_PLAIN, 1,
145                                    len,
146                                    buf);
147   }
148   else {
149     /* if my_clock_base was deleted, we pretend to have no such resource */
150     response->code = COAP_RESPONSE_CODE(404);
151   }
152 }
153 
154 static void
hnd_put_time(coap_context_t * ctx UNUSED_PARAM,struct coap_resource_t * resource,coap_session_t * session UNUSED_PARAM,coap_pdu_t * request,coap_binary_t * token UNUSED_PARAM,coap_string_t * query UNUSED_PARAM,coap_pdu_t * response)155 hnd_put_time(coap_context_t *ctx UNUSED_PARAM,
156              struct coap_resource_t *resource,
157              coap_session_t *session UNUSED_PARAM,
158              coap_pdu_t *request,
159              coap_binary_t *token UNUSED_PARAM,
160              coap_string_t *query UNUSED_PARAM,
161              coap_pdu_t *response) {
162   coap_tick_t t;
163   size_t size;
164   unsigned char *data;
165 
166   /* FIXME: re-set my_clock_base to clock_offset if my_clock_base == 0
167    * and request is empty. When not empty, set to value in request payload
168    * (insist on query ?ticks). Return Created or Ok.
169    */
170 
171   /* if my_clock_base was deleted, we pretend to have no such resource */
172   response->code =
173     my_clock_base ? COAP_RESPONSE_CODE(204) : COAP_RESPONSE_CODE(201);
174 
175   coap_resource_notify_observers(resource, NULL);
176 
177   /* coap_get_data() sets size to 0 on error */
178   (void)coap_get_data(request, &size, &data);
179 
180   if (size == 0)        /* re-init */
181     my_clock_base = clock_offset;
182   else {
183     my_clock_base = 0;
184     coap_ticks(&t);
185     while(size--)
186       my_clock_base = my_clock_base * 10 + *data++;
187     my_clock_base -= t / COAP_TICKS_PER_SECOND;
188 
189     /* Sanity check input value */
190     if (!gmtime(&my_clock_base)) {
191       unsigned char buf[3];
192       response->code = COAP_RESPONSE_CODE(400);
193       coap_add_option(response,
194                       COAP_OPTION_CONTENT_FORMAT,
195                       coap_encode_var_safe(buf, sizeof(buf),
196                       COAP_MEDIATYPE_TEXT_PLAIN), buf);
197       coap_add_data(response, 22, (const uint8_t*)"Invalid set time value");
198       /* re-init as value is bad */
199       my_clock_base = clock_offset;
200     }
201   }
202 }
203 
204 static void
hnd_delete_time(coap_context_t * ctx UNUSED_PARAM,struct coap_resource_t * resource UNUSED_PARAM,coap_session_t * session UNUSED_PARAM,coap_pdu_t * request UNUSED_PARAM,coap_binary_t * token UNUSED_PARAM,coap_string_t * query UNUSED_PARAM,coap_pdu_t * response UNUSED_PARAM)205 hnd_delete_time(coap_context_t *ctx UNUSED_PARAM,
206                 struct coap_resource_t *resource UNUSED_PARAM,
207                 coap_session_t *session UNUSED_PARAM,
208                 coap_pdu_t *request UNUSED_PARAM,
209                 coap_binary_t *token UNUSED_PARAM,
210                 coap_string_t *query UNUSED_PARAM,
211                 coap_pdu_t *response UNUSED_PARAM) {
212   my_clock_base = 0;    /* mark clock as "deleted" */
213 
214   /* type = request->hdr->type == COAP_MESSAGE_CON  */
215   /*   ? COAP_MESSAGE_ACK : COAP_MESSAGE_NON; */
216 }
217 
218 #ifndef WITHOUT_ASYNC
219 static void
hnd_get_async(coap_context_t * ctx,struct coap_resource_t * resource UNUSED_PARAM,coap_session_t * session,coap_pdu_t * request,coap_binary_t * token UNUSED_PARAM,coap_string_t * query UNUSED_PARAM,coap_pdu_t * response)220 hnd_get_async(coap_context_t *ctx,
221               struct coap_resource_t *resource UNUSED_PARAM,
222               coap_session_t *session,
223               coap_pdu_t *request,
224               coap_binary_t *token UNUSED_PARAM,
225               coap_string_t *query UNUSED_PARAM,
226               coap_pdu_t *response) {
227   unsigned long delay = 5;
228   size_t size;
229 
230   if (async) {
231     if (async->id != request->tid) {
232       coap_opt_filter_t f;
233       coap_option_filter_clear(f);
234       response->code = COAP_RESPONSE_CODE(503);
235     }
236     return;
237   }
238 
239   if (query) {
240     const uint8_t *p = query->s;
241 
242     delay = 0;
243     for (size = query->length; size; --size, ++p)
244       delay = delay * 10 + (*p - '0');
245   }
246 
247   async = coap_register_async(ctx,
248                               session,
249                               request,
250                               COAP_ASYNC_SEPARATE | COAP_ASYNC_CONFIRM,
251                               (void *)(COAP_TICKS_PER_SECOND * delay));
252 }
253 
254 static void
check_async(coap_context_t * ctx,coap_tick_t now)255 check_async(coap_context_t *ctx,
256             coap_tick_t now) {
257   coap_pdu_t *response;
258   coap_async_state_t *tmp;
259 
260   size_t size = 13;
261 
262   if (!async || now < async->created + (unsigned long)async->appdata)
263     return;
264 
265   response = coap_pdu_init(async->flags & COAP_ASYNC_CONFIRM
266              ? COAP_MESSAGE_CON
267              : COAP_MESSAGE_NON,
268              COAP_RESPONSE_CODE(205), 0, size);
269   if (!response) {
270     coap_log(LOG_DEBUG, "check_async: insufficient memory, we'll try later\n");
271     async->appdata =
272       (void *)((unsigned long)async->appdata + 15 * COAP_TICKS_PER_SECOND);
273     return;
274   }
275 
276   response->tid = coap_new_message_id(async->session);
277 
278   if (async->tokenlen)
279     coap_add_token(response, async->tokenlen, async->token);
280 
281   coap_add_data(response, 4, (const uint8_t *)"done");
282 
283   if (coap_send(async->session, response) == COAP_INVALID_TID) {
284     coap_log(LOG_DEBUG, "check_async: cannot send response for message\n");
285   }
286   coap_remove_async(ctx, async->session, async->id, &tmp);
287   coap_free_async(async);
288   async = NULL;
289 }
290 #endif /* WITHOUT_ASYNC */
291 
292 typedef struct dynamic_resource_t {
293   coap_string_t *uri_path;
294   coap_string_t *value;
295   coap_resource_t *resource;
296   int created;
297   uint16_t media_type;
298 } dynamic_resource_t;
299 
300 static int dynamic_count = 0;
301 static dynamic_resource_t *dynamic_entry = NULL;
302 
303 /*
304  * Regular DELETE handler - used by resources created by the
305  * Unknown Resource PUT handler
306  */
307 
308 static void
hnd_delete(coap_context_t * ctx,coap_resource_t * resource,coap_session_t * session UNUSED_PARAM,coap_pdu_t * request UNUSED_PARAM,coap_binary_t * token UNUSED_PARAM,coap_string_t * query UNUSED_PARAM,coap_pdu_t * response UNUSED_PARAM)309 hnd_delete(coap_context_t *ctx,
310            coap_resource_t *resource,
311            coap_session_t *session UNUSED_PARAM,
312            coap_pdu_t *request UNUSED_PARAM,
313            coap_binary_t *token UNUSED_PARAM,
314            coap_string_t *query UNUSED_PARAM,
315            coap_pdu_t *response UNUSED_PARAM
316 ) {
317   int i;
318   coap_string_t *uri_path;
319 
320   /* get the uri_path */
321   uri_path = coap_get_uri_path(request);
322   if (!uri_path) {
323     response->code = COAP_RESPONSE_CODE(404);
324     return;
325   }
326 
327   for (i = 0; i < dynamic_count; i++) {
328     if (coap_string_equal(uri_path, dynamic_entry[i].uri_path)) {
329       /* Dynamic entry no longer required - delete it */
330       coap_delete_string(dynamic_entry[i].value);
331       if (dynamic_count-i > 1) {
332          memmove (&dynamic_entry[i],
333                   &dynamic_entry[i+1],
334                  (dynamic_count-i-1) * sizeof (dynamic_entry[0]));
335       }
336       dynamic_count--;
337       break;
338     }
339   }
340 
341   /* Dynamic resource no longer required - delete it */
342   coap_delete_resource(ctx, resource);
343   response->code = COAP_RESPONSE_CODE(202);
344   return;
345 }
346 
347 /*
348  * Regular GET handler - used by resources created by the
349  * Unknown Resource PUT handler
350  */
351 
352 static void
hnd_get(coap_context_t * ctx UNUSED_PARAM,coap_resource_t * resource,coap_session_t * session,coap_pdu_t * request,coap_binary_t * token,coap_string_t * query UNUSED_PARAM,coap_pdu_t * response)353 hnd_get(coap_context_t *ctx UNUSED_PARAM,
354         coap_resource_t *resource,
355         coap_session_t *session,
356         coap_pdu_t *request,
357         coap_binary_t *token,
358         coap_string_t *query UNUSED_PARAM,
359         coap_pdu_t *response
360 ) {
361   coap_str_const_t *uri_path;
362   int i;
363   dynamic_resource_t *resource_entry = NULL;
364   coap_str_const_t value = { 0, NULL };
365   /*
366    * request will be NULL if an Observe triggered request, so the uri_path,
367    * if needed, must be abstracted from the resource.
368    * The uri_path string is a const pointer
369    */
370 
371   uri_path = coap_resource_get_uri_path(resource);
372   if (!uri_path) {
373     response->code = COAP_RESPONSE_CODE(404);
374     return;
375   }
376 
377   for (i = 0; i < dynamic_count; i++) {
378     if (coap_string_equal(uri_path, dynamic_entry[i].uri_path)) {
379       break;
380     }
381   }
382   if (i == dynamic_count) {
383     response->code = COAP_RESPONSE_CODE(404);
384     return;
385   }
386 
387   resource_entry = &dynamic_entry[i];
388 
389   if (resource_entry->value) {
390     value.length = resource_entry->value->length;
391     value.s = resource_entry->value->s;
392   }
393   coap_add_data_blocked_response(resource, session, request, response, token,
394                                  resource_entry->media_type, -1,
395                                  value.length,
396                                  value.s);
397   return;
398 }
399 
400 /*
401  * Regular PUT handler - used by resources created by the
402  * Unknown Resource PUT handler
403  */
404 
405 static void
hnd_put(coap_context_t * ctx UNUSED_PARAM,coap_resource_t * resource,coap_session_t * session UNUSED_PARAM,coap_pdu_t * request,coap_binary_t * token UNUSED_PARAM,coap_string_t * query UNUSED_PARAM,coap_pdu_t * response)406 hnd_put(coap_context_t *ctx UNUSED_PARAM,
407         coap_resource_t *resource,
408         coap_session_t *session UNUSED_PARAM,
409         coap_pdu_t *request,
410         coap_binary_t *token UNUSED_PARAM,
411         coap_string_t *query UNUSED_PARAM,
412         coap_pdu_t *response
413 ) {
414   coap_string_t *uri_path;
415   int i;
416   size_t size;
417   uint8_t *data;
418   coap_block_t block1;
419   dynamic_resource_t *resource_entry = NULL;
420   unsigned char buf[6];      /* space to hold encoded/decoded uints */
421   coap_opt_iterator_t opt_iter;
422   coap_opt_t *option;
423 
424   /* get the uri_path */
425   uri_path = coap_get_uri_path(request);
426   if (!uri_path) {
427     response->code = COAP_RESPONSE_CODE(404);
428     return;
429   }
430 
431   /*
432    * Locate the correct dynamic block for this request
433    */
434   for (i = 0; i < dynamic_count; i++) {
435     if (coap_string_equal(uri_path, dynamic_entry[i].uri_path)) {
436       break;
437     }
438   }
439   if (i == dynamic_count) {
440     if (dynamic_count >= support_dynamic) {
441       /* Should have been caught in hnd_unknown_put() */
442       response->code = COAP_RESPONSE_CODE(406);
443       coap_delete_string(uri_path);
444       return;
445     }
446     dynamic_count++;
447     dynamic_entry = realloc (dynamic_entry, dynamic_count * sizeof(dynamic_entry[0]));
448     if (dynamic_entry) {
449       dynamic_entry[i].uri_path = uri_path;
450       dynamic_entry[i].value = NULL;
451       dynamic_entry[i].resource = resource;
452       dynamic_entry[i].created = 1;
453       response->code = COAP_RESPONSE_CODE(201);
454       if ((option = coap_check_option(request, COAP_OPTION_CONTENT_TYPE, &opt_iter)) != NULL) {
455         dynamic_entry[i].media_type =
456             coap_decode_var_bytes (coap_opt_value (option), coap_opt_length (option));
457       }
458       else {
459         dynamic_entry[i].media_type = COAP_MEDIATYPE_TEXT_PLAIN;
460       }
461       /* Store media type of new resource in ct. We can use buf here
462        * as coap_add_attr() will copy the passed string. */
463       memset(buf, 0, sizeof(buf));
464       snprintf((char *)buf, sizeof(buf), "%d", dynamic_entry[i].media_type);
465       /* ensure that buf is always zero-terminated */
466       assert(buf[sizeof(buf) - 1] == '\0');
467       buf[sizeof(buf) - 1] = '\0';
468       coap_add_attr(resource,
469                     coap_make_str_const("ct"),
470                     coap_make_str_const((char*)buf),
471                     0);
472     } else {
473       dynamic_count--;
474       response->code = COAP_RESPONSE_CODE(500);
475       return;
476     }
477   } else {
478     /* Need to do this as coap_get_uri_path() created it */
479     coap_delete_string(uri_path);
480     response->code = COAP_RESPONSE_CODE(204);
481     dynamic_entry[i].created = 0;
482     coap_resource_notify_observers(dynamic_entry[i].resource, NULL);
483   }
484 
485   resource_entry = &dynamic_entry[i];
486 
487   if (coap_get_block(request, COAP_OPTION_BLOCK1, &block1)) {
488     /* handle BLOCK1 */
489     if (coap_get_data(request, &size, &data) && (size > 0)) {
490       size_t offset = block1.num << (block1.szx + 4);
491       coap_string_t *value = resource_entry->value;
492       if (offset == 0) {
493         if (value) {
494           coap_delete_string(value);
495           value = NULL;
496         }
497       }
498       else if (offset >
499             (resource_entry->value ? resource_entry->value->length : 0)) {
500         /* Upload is not sequential - block missing */
501         response->code = COAP_RESPONSE_CODE(408);
502         return;
503       }
504       else if (offset <
505             (resource_entry->value ? resource_entry->value->length : 0)) {
506         /* Upload is not sequential - block duplicated */
507         goto just_respond;
508       }
509       /* Add in new block to end of current data */
510       resource_entry->value = coap_new_string(offset + size);
511       memcpy (&resource_entry->value->s[offset], data, size);
512       resource_entry->value->length = offset + size;
513       if (value) {
514         memcpy (resource_entry->value->s, value->s, value->length);
515         coap_delete_string(value);
516       }
517     }
518 just_respond:
519     if (block1.m) {
520       response->code = COAP_RESPONSE_CODE(231);
521     }
522     else if (resource_entry->created) {
523       response->code = COAP_RESPONSE_CODE(201);
524     }
525     else {
526       response->code = COAP_RESPONSE_CODE(204);
527     }
528     coap_add_option(response,
529                     COAP_OPTION_BLOCK1,
530                     coap_encode_var_safe(buf, sizeof(buf),
531                                          ((block1.num << 4) |
532                                           (block1.m << 3) |
533                                           block1.szx)),
534                     buf);
535   }
536   else if (coap_get_data(request, &size, &data) && (size > 0)) {
537     /* Not a BLOCK1 with data */
538     if (resource_entry->value) {
539       coap_delete_string(resource_entry->value);
540       resource_entry->value = NULL;
541     }
542     resource_entry->value = coap_new_string(size);
543     memcpy (resource_entry->value->s, data, size);
544     resource_entry->value->length = size;
545   }
546   else {
547     /* Not a BLOCK1 and no data */
548     if (resource_entry->value) {
549       coap_delete_string(resource_entry->value);
550       resource_entry->value = NULL;
551     }
552   }
553 }
554 
555 /*
556  * Unknown Resource PUT handler
557  */
558 
559 static void
hnd_unknown_put(coap_context_t * ctx,coap_resource_t * resource UNUSED_PARAM,coap_session_t * session,coap_pdu_t * request,coap_binary_t * token,coap_string_t * query,coap_pdu_t * response)560 hnd_unknown_put(coap_context_t *ctx,
561                 coap_resource_t *resource UNUSED_PARAM,
562                 coap_session_t *session,
563                 coap_pdu_t *request,
564                 coap_binary_t *token,
565                 coap_string_t *query,
566                 coap_pdu_t *response
567 ) {
568   coap_resource_t *r;
569   coap_string_t *uri_path;
570 
571   /* get the uri_path - will will get used by coap_resource_init() */
572   uri_path = coap_get_uri_path(request);
573   if (!uri_path) {
574     response->code = COAP_RESPONSE_CODE(404);
575     return;
576   }
577 
578   if (dynamic_count >= support_dynamic) {
579     response->code = COAP_RESPONSE_CODE(406);
580     return;
581   }
582 
583   /*
584    * Create a resource to handle the new URI
585    * uri_path will get deleted when the resource is removed
586    */
587   r = coap_resource_init((coap_str_const_t*)uri_path,
588         COAP_RESOURCE_FLAGS_RELEASE_URI | resource_flags);
589   coap_add_attr(r, coap_make_str_const("title"), coap_make_str_const("\"Dynamic\""), 0);
590   coap_register_handler(r, COAP_REQUEST_PUT, hnd_put);
591   coap_register_handler(r, COAP_REQUEST_DELETE, hnd_delete);
592   /* We possibly want to Observe the GETs */
593   coap_resource_set_get_observable(r, 1);
594   coap_register_handler(r, COAP_REQUEST_GET, hnd_get);
595   coap_add_resource(ctx, r);
596 
597   /* Do the PUT for this first call */
598   hnd_put(ctx, r, session, request, token, query, response);
599 
600   return;
601 }
602 
603 static void
init_resources(coap_context_t * ctx)604 init_resources(coap_context_t *ctx) {
605   coap_resource_t *r;
606 
607   r = coap_resource_init(NULL, 0);
608   coap_register_handler(r, COAP_REQUEST_GET, hnd_get_index);
609 
610   coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
611   coap_add_attr(r, coap_make_str_const("title"), coap_make_str_const("\"General Info\""), 0);
612   coap_add_resource(ctx, r);
613 
614   /* store clock base to use in /time */
615   my_clock_base = clock_offset;
616 
617   r = coap_resource_init(coap_make_str_const("time"), resource_flags);
618   coap_register_handler(r, COAP_REQUEST_GET, hnd_get_time);
619   coap_register_handler(r, COAP_REQUEST_PUT, hnd_put_time);
620   coap_register_handler(r, COAP_REQUEST_DELETE, hnd_delete_time);
621   coap_resource_set_get_observable(r, 1);
622 
623   coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
624   coap_add_attr(r, coap_make_str_const("title"), coap_make_str_const("\"Internal Clock\""), 0);
625   coap_add_attr(r, coap_make_str_const("rt"), coap_make_str_const("\"ticks\""), 0);
626   coap_add_attr(r, coap_make_str_const("if"), coap_make_str_const("\"clock\""), 0);
627 
628   coap_add_resource(ctx, r);
629   time_resource = r;
630 
631   if (support_dynamic > 0) {
632     /* Create a resource to handle PUTs to unknown URIs */
633     r = coap_resource_unknown_init(hnd_unknown_put);
634     coap_add_resource(ctx, r);
635   }
636 #ifndef WITHOUT_ASYNC
637   r = coap_resource_init(coap_make_str_const("async"), 0);
638   coap_register_handler(r, COAP_REQUEST_GET, hnd_get_async);
639 
640   coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
641   coap_add_resource(ctx, r);
642 #endif /* WITHOUT_ASYNC */
643 }
644 
645 static int
verify_cn_callback(const char * cn,const uint8_t * asn1_public_cert UNUSED_PARAM,size_t asn1_length UNUSED_PARAM,coap_session_t * session UNUSED_PARAM,unsigned depth,int validated UNUSED_PARAM,void * arg UNUSED_PARAM)646 verify_cn_callback(const char *cn,
647                    const uint8_t *asn1_public_cert UNUSED_PARAM,
648                    size_t asn1_length UNUSED_PARAM,
649                    coap_session_t *session UNUSED_PARAM,
650                    unsigned depth,
651                    int validated UNUSED_PARAM,
652                    void *arg UNUSED_PARAM
653 ) {
654   coap_log(LOG_INFO, "CN '%s' presented by client (%s)\n",
655            cn, depth ? "CA" : "Certificate");
656   return 1;
657 }
658 
659 static coap_dtls_key_t *
verify_sni_callback(const char * sni,void * arg UNUSED_PARAM)660 verify_sni_callback(const char *sni,
661                     void *arg UNUSED_PARAM
662 ) {
663   static coap_dtls_key_t dtls_key;
664 
665   /* Just use the defined keys for now */
666   memset (&dtls_key, 0, sizeof(dtls_key));
667   dtls_key.key_type = COAP_PKI_KEY_PEM;
668   dtls_key.key.pem.public_cert = cert_file;
669   dtls_key.key.pem.private_key = cert_file;
670   dtls_key.key.pem.ca_file = ca_file;
671   if (sni[0]) {
672     coap_log(LOG_INFO, "SNI '%s' requested\n", sni);
673   }
674   else {
675     coap_log(LOG_DEBUG, "SNI not requested\n");
676   }
677   return &dtls_key;
678 }
679 
680 static void
fill_keystore(coap_context_t * ctx)681 fill_keystore(coap_context_t *ctx) {
682   if (cert_file) {
683     coap_dtls_pki_t dtls_pki;
684     memset (&dtls_pki, 0, sizeof(dtls_pki));
685     dtls_pki.version = COAP_DTLS_PKI_SETUP_VERSION;
686     if (ca_file) {
687       /*
688        * Add in additional certificate checking.
689        * This list of enabled can be tuned for the specific
690        * requirements - see 'man coap_encryption'.
691        */
692       dtls_pki.verify_peer_cert        = 1;
693       dtls_pki.require_peer_cert       = require_peer_cert;
694       dtls_pki.allow_self_signed       = 1;
695       dtls_pki.allow_expired_certs     = 1;
696       dtls_pki.cert_chain_validation   = 1;
697       dtls_pki.cert_chain_verify_depth = 2;
698       dtls_pki.check_cert_revocation   = 1;
699       dtls_pki.allow_no_crl            = 1;
700       dtls_pki.allow_expired_crl       = 1;
701       dtls_pki.validate_cn_call_back   = verify_cn_callback;
702       dtls_pki.cn_call_back_arg        = NULL;
703       dtls_pki.validate_sni_call_back  = verify_sni_callback;
704       dtls_pki.sni_call_back_arg       = NULL;
705     }
706     dtls_pki.pki_key.key_type = COAP_PKI_KEY_PEM;
707     dtls_pki.pki_key.key.pem.public_cert = cert_file;
708     dtls_pki.pki_key.key.pem.private_key = cert_file;
709     dtls_pki.pki_key.key.pem.ca_file = ca_file;
710     /* If general root CAs are defined */
711     if (root_ca_file) {
712       struct stat stbuf;
713       if ((stat(root_ca_file, &stbuf) == 0) && S_ISDIR(stbuf.st_mode)) {
714         coap_context_set_pki_root_cas(ctx, NULL, root_ca_file);
715       } else {
716         coap_context_set_pki_root_cas(ctx, root_ca_file, NULL);
717       }
718     }
719     if (key_defined)
720       coap_context_set_psk(ctx, hint, key, key_length);
721     coap_context_set_pki(ctx, &dtls_pki);
722   }
723   else if (key_defined) {
724     coap_context_set_psk(ctx, hint, key, key_length);
725   }
726   else if (coap_dtls_is_supported() || coap_tls_is_supported()) {
727     coap_log(LOG_DEBUG,
728              "(D)TLS not enabled as neither -k or -c options specified\n");
729   }
730 }
731 
732 static void
usage(const char * program,const char * version)733 usage( const char *program, const char *version) {
734   const char *p;
735   char buffer[64];
736 
737   p = strrchr( program, '/' );
738   if ( p )
739     program = ++p;
740 
741   fprintf( stderr, "%s v%s -- a small CoAP implementation\n"
742      "(c) 2010,2011,2015-2018 Olaf Bergmann <bergmann@tzi.org> and others\n\n"
743      "%s\n\n"
744      "Usage: %s [-d max] [-g group] [-l loss] [-p port] [-v num]\n"
745      "\t\t[-A address] [-N]\n"
746      "\t\t[[-k key] [-h hint]]\n"
747      "\t\t[[-c certfile][-C cafile] [-n] [-R root_cafile]]\n"
748      "General Options\n"
749      "\t-d max \t\tAllow dynamic creation of up to a total of max\n"
750      "\t       \t\tresources. If max is reached, a 4.06 code is returned\n"
751      "\t       \t\tuntil one of the dynamic resources has been deleted\n"
752      "\t-g group\tJoin the given multicast group\n"
753      "\t-l list\t\tFail to send some datagrams specified by a comma\n"
754      "\t       \t\tseparated list of numbers or number ranges\n"
755      "\t       \t\t(for debugging only)\n"
756      "\t-l loss%%\tRandomly fail to send datagrams with the specified\n"
757      "\t       \t\tprobability - 100%% all datagrams, 0%% no datagrams\n"
758      "\t       \t\t(for debugging only)\n"
759      "\t-p port\t\tListen on specified port\n"
760      "\t-v num \t\tVerbosity level (default 3, maximum is 9). Above 7,\n"
761      "\t       \t\tthere is increased verbosity in GnuTLS logging\n"
762      "\t-A address\tInterface address to bind to\n"
763      "\t-N     \t\tMake \"observe\" responses NON-confirmable. Even if set\n"
764      "\t       \t\tevery fifth response will still be sent as a confirmable\n"
765      "\t       \t\tresponse (RFC 7641 requirement)\n"
766      "PSK Options (if supported by underlying (D)TLS library)\n"
767      "\t-h hint\t\tPSK Hint.  Default is CoAP\n"
768      "\t-k key \t\tPre-shared key. This argument requires (D)TLS with PSK\n"
769      "\t       \t\tto be available. This cannot be empty if defined.\n"
770      "\t       \t\tNote that both -c and -k need to be defined\n"
771      "\t       \t\tfor both PSK and PKI to be concurrently supported\n"
772      "PKI Options (if supported by underlying (D)TLS library)\n"
773      "\t-c certfile\tPEM file containing both CERTIFICATE and PRIVATE KEY\n"
774      "\t       \t\tThis argument requires (D)TLS with PKI to be available\n"
775      "\t-n     \t\tDisable the requirement for clients to have defined\n"
776      "\t       \t\tclient certificates\n"
777      "\t-C cafile\tPEM file containing the CA Certificate that was used to\n"
778      "\t       \t\tsign the certfile. If defined, then the client will be\n"
779      "\t       \t\tgiven this CA Certificate during the TLS set up.\n"
780      "\t       \t\tFurthermore, this will trigger the validation of the\n"
781      "\t       \t\tclient certificate.  If certfile is self-signed (as\n"
782      "\t       \t\tdefined by '-c certfile'), then you need to have on the\n"
783      "\t       \t\tcommand line the same filename for both the certfile and\n"
784      "\t       \t\tcafile (as in  '-c certfile -C certfile') to trigger\n"
785      "\t       \t\tvalidation\n"
786      "\t-R root_cafile\tPEM file containing the set of trusted root CAs that\n"
787      "\t       \t\tare to be used to validate the client certificate.\n"
788      "\t       \t\tThe '-C cafile' does not have to be in this list and is\n"
789      "\t       \t\t'trusted' for the verification.\n"
790      "\t       \t\tAlternatively, this can point to a directory containing\n"
791      "\t       \t\ta set of CA PEM files\n"
792     , program, version, coap_string_tls_version(buffer, sizeof(buffer)),
793     program);
794 }
795 
796 static coap_context_t *
get_context(const char * node,const char * port)797 get_context(const char *node, const char *port) {
798   coap_context_t *ctx = NULL;
799   int s;
800   struct addrinfo hints;
801   struct addrinfo *result, *rp;
802 
803   ctx = coap_new_context(NULL);
804   if (!ctx) {
805     return NULL;
806   }
807   /* Need PSK set up before we set up (D)TLS endpoints */
808   fill_keystore(ctx);
809 
810   memset(&hints, 0, sizeof(struct addrinfo));
811   hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
812   hints.ai_socktype = SOCK_DGRAM; /* Coap uses UDP */
813   hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
814 
815   s = getaddrinfo(node, port, &hints, &result);
816   if ( s != 0 ) {
817     fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
818     coap_free_context(ctx);
819     return NULL;
820   }
821 
822   /* iterate through results until success */
823   for (rp = result; rp != NULL; rp = rp->ai_next) {
824     coap_address_t addr, addrs;
825     coap_endpoint_t *ep_udp = NULL, *ep_dtls = NULL, *ep_tcp = NULL, *ep_tls = NULL;
826 
827     if (rp->ai_addrlen <= sizeof(addr.addr)) {
828       coap_address_init(&addr);
829       addr.size = rp->ai_addrlen;
830       memcpy(&addr.addr, rp->ai_addr, rp->ai_addrlen);
831       addrs = addr;
832       if (addr.addr.sa.sa_family == AF_INET) {
833         uint16_t temp = ntohs(addr.addr.sin.sin_port) + 1;
834         addrs.addr.sin.sin_port = htons(temp);
835       } else if (addr.addr.sa.sa_family == AF_INET6) {
836         uint16_t temp = ntohs(addr.addr.sin6.sin6_port) + 1;
837         addrs.addr.sin6.sin6_port = htons(temp);
838       } else {
839         goto finish;
840       }
841 
842       ep_udp = coap_new_endpoint(ctx, &addr, COAP_PROTO_UDP);
843       if (ep_udp) {
844         if (coap_dtls_is_supported() && (key_defined || cert_file)) {
845           ep_dtls = coap_new_endpoint(ctx, &addrs, COAP_PROTO_DTLS);
846           if (!ep_dtls)
847             coap_log(LOG_CRIT, "cannot create DTLS endpoint\n");
848         }
849       } else {
850         coap_log(LOG_CRIT, "cannot create UDP endpoint\n");
851         continue;
852       }
853       ep_tcp = coap_new_endpoint(ctx, &addr, COAP_PROTO_TCP);
854       if (ep_tcp) {
855         if (coap_tls_is_supported() && (key_defined || cert_file)) {
856           ep_tls = coap_new_endpoint(ctx, &addrs, COAP_PROTO_TLS);
857           if (!ep_tls)
858             coap_log(LOG_CRIT, "cannot create TLS endpoint\n");
859         }
860       } else {
861         coap_log(LOG_CRIT, "cannot create TCP endpoint\n");
862       }
863       if (ep_udp)
864         goto finish;
865     }
866   }
867 
868   fprintf(stderr, "no context available for interface '%s'\n", node);
869 
870 finish:
871   freeaddrinfo(result);
872   return ctx;
873 }
874 
875 static ssize_t
cmdline_read_key(char * arg,unsigned char * buf,size_t maxlen)876 cmdline_read_key(char *arg, unsigned char *buf, size_t maxlen) {
877   size_t len = strnlen(arg, maxlen);
878   if (len) {
879     memcpy(buf, arg, len);
880     return len;
881   }
882   return -1;
883 }
884 
885 int
main(int argc,char ** argv)886 main(int argc, char **argv) {
887   coap_context_t  *ctx;
888   char *group = NULL;
889   coap_tick_t now;
890   char addr_str[NI_MAXHOST] = "::";
891   char port_str[NI_MAXSERV] = "5683";
892   int opt;
893   coap_log_t log_level = LOG_WARNING;
894   unsigned wait_ms;
895   time_t t_last = 0;
896   int coap_fd;
897   fd_set m_readfds;
898   int nfds = 0;
899 #ifndef _WIN32
900   struct sigaction sa;
901 #endif
902 
903   clock_offset = time(NULL);
904 
905   while ((opt = getopt(argc, argv, "A:d:c:C:g:h:k:l:nNp:R:v:")) != -1) {
906     switch (opt) {
907     case 'A' :
908       strncpy(addr_str, optarg, NI_MAXHOST-1);
909       addr_str[NI_MAXHOST - 1] = '\0';
910       break;
911     case 'c' :
912       cert_file = optarg;
913       break;
914     case 'C' :
915       ca_file = optarg;
916       break;
917     case 'd' :
918       support_dynamic = atoi(optarg);
919       break;
920     case 'g' :
921       group = optarg;
922       break;
923     case 'h' :
924       if (!optarg[0]) {
925         coap_log( LOG_CRIT, "Invalid PSK hint specified\n" );
926         break;
927       }
928       hint = optarg;
929       break;
930     case 'k' :
931       key_length = cmdline_read_key(optarg, key, MAX_KEY);
932       if (key_length < 0) {
933         coap_log( LOG_CRIT, "Invalid PSK key specified\n" );
934         break;
935       }
936       key_defined = 1;
937       break;
938     case 'l':
939       if (!coap_debug_set_packet_loss(optarg)) {
940         usage(argv[0], LIBCOAP_PACKAGE_VERSION);
941         exit(1);
942       }
943       break;
944     case 'n':
945       require_peer_cert = 0;
946       break;
947     case 'N':
948       resource_flags = COAP_RESOURCE_FLAGS_NOTIFY_NON;
949       break;
950     case 'p' :
951       strncpy(port_str, optarg, NI_MAXSERV-1);
952       port_str[NI_MAXSERV - 1] = '\0';
953       break;
954     case 'R' :
955       root_ca_file = optarg;
956       break;
957     case 'v' :
958       log_level = strtol(optarg, NULL, 10);
959       break;
960     default:
961       usage( argv[0], LIBCOAP_PACKAGE_VERSION );
962       exit( 1 );
963     }
964   }
965 
966   coap_startup();
967   coap_dtls_set_log_level(log_level);
968   coap_set_log_level(log_level);
969 
970   ctx = get_context(addr_str, port_str);
971   if (!ctx)
972     return -1;
973 
974   init_resources(ctx);
975 
976   /* join multicast group if requested at command line */
977   if (group)
978     coap_join_mcast_group(ctx, group);
979 
980   coap_fd = coap_context_get_coap_fd(ctx);
981   if (coap_fd != -1) {
982     /* if coap_fd is -1, then epoll is not supported within libcoap */
983     FD_ZERO(&m_readfds);
984     FD_SET(coap_fd, &m_readfds);
985     nfds = coap_fd + 1;
986   }
987 
988 #ifdef _WIN32
989   signal(SIGINT, handle_sigint);
990 #else
991   memset (&sa, 0, sizeof(sa));
992   sigemptyset(&sa.sa_mask);
993   sa.sa_handler = handle_sigint;
994   sa.sa_flags = 0;
995   sigaction (SIGINT, &sa, NULL);
996   sigaction (SIGTERM, &sa, NULL);
997   /* So we do not exit on a SIGPIPE */
998   sa.sa_handler = SIG_IGN;
999   sigaction (SIGPIPE, &sa, NULL);
1000 #endif
1001 
1002   wait_ms = COAP_RESOURCE_CHECK_TIME * 1000;
1003 
1004   while ( !quit ) {
1005     int result;
1006 
1007     if (coap_fd != -1) {
1008       fd_set readfds = m_readfds;
1009       struct timeval tv;
1010 
1011       tv.tv_sec = wait_ms / 1000;
1012       tv.tv_usec = (wait_ms % 1000) * 1000;
1013       /* Wait until any i/o takes place */
1014       result = select (nfds, &readfds, NULL, NULL, &tv);
1015       if (result == -1) {
1016         if (errno != EAGAIN) {
1017           coap_log(LOG_DEBUG, "select: %s (%d)\n", coap_socket_strerror(), errno);
1018           break;
1019         }
1020       }
1021       if (result > 0) {
1022         if (FD_ISSET(coap_fd, &readfds)) {
1023           result = coap_run_once(ctx, COAP_RUN_NONBLOCK);
1024         }
1025       }
1026     }
1027     else {
1028       /* epoll is not supported within libcoap */
1029       result = coap_run_once( ctx, wait_ms );
1030     }
1031     if ( result < 0 ) {
1032       break;
1033     } else if ( result && (unsigned)result < wait_ms ) {
1034       /* decrement if there is a result wait time returned */
1035       wait_ms -= result;
1036     } else {
1037       /*
1038        * result == 0, or result >= wait_ms
1039        * (wait_ms could have decremented to a small value, below
1040        * the granularity of the timer in coap_run_once() and hence
1041        * result == 0)
1042        */
1043       time_t t_now = time(NULL);
1044       if (t_last != t_now) {
1045         /* Happens once per second */
1046         t_last = t_now;
1047         if (time_resource) {
1048           coap_resource_notify_observers(time_resource, NULL);
1049         }
1050       }
1051       if (result) {
1052         /* result must have been >= wait_ms, so reset wait_ms */
1053         wait_ms = COAP_RESOURCE_CHECK_TIME * 1000;
1054       }
1055     }
1056 
1057 #ifndef WITHOUT_ASYNC
1058     /* check if we have to send asynchronous responses */
1059     coap_ticks( &now );
1060     check_async(ctx, now);
1061 #endif /* WITHOUT_ASYNC */
1062   }
1063 
1064   coap_free_context(ctx);
1065   coap_cleanup();
1066 
1067   return 0;
1068 }
1069