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