1 /* debug.c -- debug utilities
2 *
3 * Copyright (C) 2010--2012,2014--2019 Olaf Bergmann <bergmann@tzi.org> and others
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 *
7 * This file is part of the CoAP library libcoap. Please see
8 * README for terms of use.
9 */
10
11 #include "coap3/coap_internal.h"
12
13 #if defined(HAVE_STRNLEN) && defined(__GNUC__) && !defined(_GNU_SOURCE)
14 #define _GNU_SOURCE 1
15 #endif
16
17 #include <stdarg.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include <ctype.h>
21
22 #ifdef HAVE_ARPA_INET_H
23 #include <arpa/inet.h>
24 #endif
25 #ifdef HAVE_WS2TCPIP_H
26 #include <ws2tcpip.h>
27 #endif
28
29 #ifdef HAVE_TIME_H
30 #include <time.h>
31 #endif
32
33 #ifdef WITH_LWIP
34 # define fprintf(fd, ...) LWIP_PLATFORM_DIAG((__VA_ARGS__))
35 # define fflush(...)
36 #endif
37
38 #ifdef WITH_CONTIKI
39 # ifndef DEBUG
40 # define DEBUG DEBUG_PRINT
41 # endif /* DEBUG */
42 #include "net/ip/uip-debug.h"
43 #endif
44
45 static coap_log_t maxlog = LOG_WARNING; /* default maximum log level */
46
47 static int use_fprintf_for_show_pdu = 1; /* non zero to output with fprintf */
48
coap_package_name(void)49 const char *coap_package_name(void) {
50 return PACKAGE_NAME;
51 }
52
coap_package_version(void)53 const char *coap_package_version(void) {
54 return PACKAGE_STRING;
55 }
56
57 void
coap_set_show_pdu_output(int use_fprintf)58 coap_set_show_pdu_output(int use_fprintf) {
59 use_fprintf_for_show_pdu = use_fprintf;
60 }
61
62 coap_log_t
coap_get_log_level(void)63 coap_get_log_level(void) {
64 return maxlog;
65 }
66
67 void
coap_set_log_level(coap_log_t level)68 coap_set_log_level(coap_log_t level) {
69 maxlog = level;
70 }
71
72 /* this array has the same order as the type log_t */
73 static const char *loglevels[] = {
74 "EMRG", "ALRT", "CRIT", "ERR ", "WARN", "NOTE", "INFO", "DEBG", "????", "CIPH"
75 };
76
77 #ifdef HAVE_TIME_H
78
79 COAP_STATIC_INLINE size_t
print_timestamp(char * s,size_t len,coap_tick_t t)80 print_timestamp(char *s, size_t len, coap_tick_t t) {
81 struct tm *tmp;
82 size_t lensofar;
83 time_t now = coap_ticks_to_rt(t);
84 tmp = localtime(&now);
85 lensofar = strftime(s, len, "%b %d %H:%M:%S", tmp);
86 if (len > lensofar + 4) {
87 lensofar += snprintf(&s[lensofar], len-lensofar, ".%03u",
88 (unsigned int)((coap_ticks_to_rt_us(t) % 1000000)/1000));
89 }
90 return lensofar;
91 }
92
93 #else /* alternative implementation: just print the timestamp */
94
95 COAP_STATIC_INLINE size_t
print_timestamp(char * s,size_t len,coap_tick_t t)96 print_timestamp(char *s, size_t len, coap_tick_t t) {
97 #ifdef HAVE_SNPRINTF
98 return snprintf(s, len, "%u.%03u",
99 (unsigned int)coap_ticks_to_rt(t),
100 (unsigned int)((coap_ticks_to_rt_us(t) % 1000000)/1000));
101 #else /* HAVE_SNPRINTF */
102 /* @todo do manual conversion of timestamp */
103 return 0;
104 #endif /* HAVE_SNPRINTF */
105 }
106
107 #endif /* HAVE_TIME_H */
108
109 #ifndef HAVE_STRNLEN
110 /**
111 * A length-safe strlen() fake.
112 *
113 * @param s The string to count characters != 0.
114 * @param maxlen The maximum length of @p s.
115 *
116 * @return The length of @p s.
117 */
118 static inline size_t
strnlen(const char * s,size_t maxlen)119 strnlen(const char *s, size_t maxlen) {
120 size_t n = 0;
121 while(*s++ && n < maxlen)
122 ++n;
123 return n;
124 }
125 #endif /* HAVE_STRNLEN */
126
127 static size_t
print_readable(const uint8_t * data,size_t len,unsigned char * result,size_t buflen,int encode_always)128 print_readable( const uint8_t *data, size_t len,
129 unsigned char *result, size_t buflen, int encode_always ) {
130 const uint8_t hex[] = "0123456789ABCDEF";
131 size_t cnt = 0;
132 assert(data || len == 0);
133
134 if (buflen == 0) { /* there is nothing we can do here but return */
135 return 0;
136 }
137
138 while (len) {
139 if (!encode_always && isprint(*data)) {
140 if (cnt+1 < buflen) { /* keep one byte for terminating zero */
141 *result++ = *data;
142 ++cnt;
143 } else {
144 break;
145 }
146 } else {
147 if (cnt+4 < buflen) { /* keep one byte for terminating zero */
148 *result++ = '\\';
149 *result++ = 'x';
150 *result++ = hex[(*data & 0xf0) >> 4];
151 *result++ = hex[*data & 0x0f];
152 cnt += 4;
153 } else
154 break;
155 }
156
157 ++data; --len;
158 }
159
160 *result = '\0'; /* add a terminating zero */
161 return cnt;
162 }
163
164 #ifndef min
165 #define min(a,b) ((a) < (b) ? (a) : (b))
166 #endif
167
168 /*
169 * Returned buf is always NULL terminated.
170 * Returned size is number of characters, not including NULL terminator.
171 */
172 size_t
coap_print_addr(const coap_address_t * addr,unsigned char * buf,size_t len)173 coap_print_addr(const coap_address_t *addr, unsigned char *buf, size_t len) {
174 #if defined( HAVE_ARPA_INET_H ) || defined( HAVE_WS2TCPIP_H )
175 const void *addrptr = NULL;
176 in_port_t port;
177 unsigned char *p = buf;
178 size_t need_buf;
179
180 assert(buf);
181 assert(len);
182 buf[0] = '\000';
183
184 switch (addr->addr.sa.sa_family) {
185 case AF_INET:
186 if (len < INET_ADDRSTRLEN + 1) /* Include : */
187 return 0;
188 addrptr = &addr->addr.sin.sin_addr;
189 port = ntohs(addr->addr.sin.sin_port);
190 need_buf = INET_ADDRSTRLEN;
191 break;
192 case AF_INET6:
193 if (len < INET6_ADDRSTRLEN + 3) /* Include [ ] : */
194 return 0;
195
196 *p++ = '[';
197
198 addrptr = &addr->addr.sin6.sin6_addr;
199 port = ntohs(addr->addr.sin6.sin6_port);
200 need_buf = INET6_ADDRSTRLEN;
201
202 break;
203 default:
204 /* Include trailing NULL if possible */
205 memcpy(buf, "(unknown address type)", min(22+1, len));
206 buf[len-1] = '\000';
207 return min(22, len);
208 }
209
210 /* Cast needed for Windows, since it doesn't have the correct API signature. */
211 if (inet_ntop(addr->addr.sa.sa_family, addrptr, (char *)p,
212 min(len, need_buf)) == 0) {
213 perror("coap_print_addr");
214 buf[0] = '\000';
215 return 0;
216 }
217
218 p += strlen((char *)p);
219
220 if (addr->addr.sa.sa_family == AF_INET6) {
221 if (p + 1 < buf + len) {
222 *p++ = ']';
223 } else
224 return p - buf; /* Already NULL terminated */
225 }
226
227 /* Cannot rely on snprintf() return value for short buffers */
228 snprintf((char *)p, buf + len - p, ":%d", port);
229
230 return strlen((char *)buf);
231 #else /* HAVE_ARPA_INET_H */
232 # if WITH_CONTIKI
233 unsigned char *p = buf;
234 uint8_t i;
235 # if NETSTACK_CONF_WITH_IPV6
236 const uint8_t hex[] = "0123456789ABCDEF";
237
238 assert(buf);
239 assert(len);
240 buf[0] = '\000';
241 if (len < 42)
242 return 0;
243
244 *p++ = '[';
245
246 for (i=0; i < 16; i += 2) {
247 if (i) {
248 *p++ = ':';
249 }
250 *p++ = hex[(addr->addr.u8[i] & 0xf0) >> 4];
251 *p++ = hex[(addr->addr.u8[i] & 0x0f)];
252 *p++ = hex[(addr->addr.u8[i+1] & 0xf0) >> 4];
253 *p++ = hex[(addr->addr.u8[i+1] & 0x0f)];
254 }
255 *p++ = ']';
256 # else /* WITH_UIP6 */
257 # warning "IPv4 network addresses will not be included in debug output"
258
259 if (len < 21) {
260 *p = '\000';
261 return 0;
262 }
263 # endif /* WITH_UIP6 */
264 if (buf + len - p < 6) {
265 *p = '\000';
266 return p - buf;
267 }
268
269 #ifdef HAVE_SNPRINTF
270 /* Cannot rely on snprintf() return value for short buffers */
271 snprintf((char *)p, buf + len - p, ":%d", uip_htons(addr->port));
272 #else /* HAVE_SNPRINTF */
273 /* @todo manual conversion of port number */
274 *p = '\000';
275 #endif /* HAVE_SNPRINTF */
276
277 return strlen((char *)p);
278 # else /* WITH_CONTIKI */
279 /* TODO: output addresses manually */
280 # warning "inet_ntop() not available, network addresses will not be included in debug output"
281 # endif /* WITH_CONTIKI */
282 buf[0] = '\000';
283 return 0;
284 #endif
285 }
286
287 #ifdef WITH_CONTIKI
288 # define fprintf(fd, ...) { (void)fd; PRINTF(__VA_ARGS__); }
289 # define fflush(...)
290
291 # ifdef HAVE_VPRINTF
292 # define vfprintf(fd, ...) { (void)fd; vprintf(__VA_ARGS__); }
293 # else /* HAVE_VPRINTF */
294 # define vfprintf(fd, ...) { (void)fd; PRINTF(__VA_ARGS__); }
295 # endif /* HAVE_VPRINTF */
296 #endif /* WITH_CONTIKI */
297
298 /** Returns a textual description of the message type @p t. */
299 static const char *
msg_type_string(uint16_t t)300 msg_type_string(uint16_t t) {
301 static const char *types[] = { "CON", "NON", "ACK", "RST", "???" };
302
303 return types[min(t, sizeof(types)/sizeof(char *) - 1)];
304 }
305
306 /** Returns a textual description of the method or response code. */
307 static const char *
msg_code_string(uint16_t c)308 msg_code_string(uint16_t c) {
309 static const char *methods[] = { "0.00", "GET", "POST", "PUT", "DELETE",
310 "FETCH", "PATCH", "iPATCH" };
311 static const char *signals[] = { "7.00", "CSM", "Ping", "Pong", "Release",
312 "Abort" };
313 static char buf[5];
314
315 if (c < sizeof(methods)/sizeof(const char *)) {
316 return methods[c];
317 } else if (c >= 224 && c - 224 < (int)(sizeof(signals)/sizeof(const char *))) {
318 return signals[c-224];
319 } else {
320 snprintf(buf, sizeof(buf), "%u.%02u", (c >> 5) & 0x7, c & 0x1f);
321 return buf;
322 }
323 }
324
325 /** Returns a textual description of the option name. */
326 static const char *
msg_option_string(uint8_t code,uint16_t option_type)327 msg_option_string(uint8_t code, uint16_t option_type) {
328 struct option_desc_t {
329 uint16_t type;
330 const char *name;
331 };
332
333 static struct option_desc_t options[] = {
334 { COAP_OPTION_IF_MATCH, "If-Match" },
335 { COAP_OPTION_URI_HOST, "Uri-Host" },
336 { COAP_OPTION_ETAG, "ETag" },
337 { COAP_OPTION_IF_NONE_MATCH, "If-None-Match" },
338 { COAP_OPTION_OBSERVE, "Observe" },
339 { COAP_OPTION_URI_PORT, "Uri-Port" },
340 { COAP_OPTION_LOCATION_PATH, "Location-Path" },
341 { COAP_OPTION_URI_PATH, "Uri-Path" },
342 { COAP_OPTION_CONTENT_FORMAT, "Content-Format" },
343 { COAP_OPTION_MAXAGE, "Max-Age" },
344 { COAP_OPTION_URI_QUERY, "Uri-Query" },
345 { COAP_OPTION_HOP_LIMIT, "Hop-Limit" },
346 { COAP_OPTION_ACCEPT, "Accept" },
347 { COAP_OPTION_LOCATION_QUERY, "Location-Query" },
348 { COAP_OPTION_BLOCK2, "Block2" },
349 { COAP_OPTION_BLOCK1, "Block1" },
350 { COAP_OPTION_SIZE2, "Size2" },
351 { COAP_OPTION_PROXY_URI, "Proxy-Uri" },
352 { COAP_OPTION_PROXY_SCHEME, "Proxy-Scheme" },
353 { COAP_OPTION_SIZE1, "Size1" },
354 { COAP_OPTION_NORESPONSE, "No-Response" }
355 };
356
357 static struct option_desc_t options_csm[] = {
358 { COAP_SIGNALING_OPTION_MAX_MESSAGE_SIZE, "Max-Message-Size" },
359 { COAP_SIGNALING_OPTION_BLOCK_WISE_TRANSFER, "Block-wise-Transfer" }
360 };
361
362 static struct option_desc_t options_pingpong[] = {
363 { COAP_SIGNALING_OPTION_CUSTODY, "Custody" }
364 };
365
366 static struct option_desc_t options_release[] = {
367 { COAP_SIGNALING_OPTION_ALTERNATIVE_ADDRESS, "Alternative-Address" },
368 { COAP_SIGNALING_OPTION_HOLD_OFF, "Hold-Off" }
369 };
370
371 static struct option_desc_t options_abort[] = {
372 { COAP_SIGNALING_OPTION_BAD_CSM_OPTION, "Bad-CSM-Option" }
373 };
374
375 static char buf[6];
376 size_t i;
377
378 if (code == COAP_SIGNALING_CSM) {
379 for (i = 0; i < sizeof(options_csm)/sizeof(struct option_desc_t); i++) {
380 if (option_type == options_csm[i].type) {
381 return options_csm[i].name;
382 }
383 }
384 } else if (code == COAP_SIGNALING_PING || code == COAP_SIGNALING_PONG) {
385 for (i = 0; i < sizeof(options_pingpong)/sizeof(struct option_desc_t); i++) {
386 if (option_type == options_pingpong[i].type) {
387 return options_pingpong[i].name;
388 }
389 }
390 } else if (code == COAP_SIGNALING_RELEASE) {
391 for (i = 0; i < sizeof(options_release)/sizeof(struct option_desc_t); i++) {
392 if (option_type == options_release[i].type) {
393 return options_release[i].name;
394 }
395 }
396 } else if (code == COAP_SIGNALING_ABORT) {
397 for (i = 0; i < sizeof(options_abort)/sizeof(struct option_desc_t); i++) {
398 if (option_type == options_abort[i].type) {
399 return options_abort[i].name;
400 }
401 }
402 } else {
403 /* search option_type in list of known options */
404 for (i = 0; i < sizeof(options)/sizeof(struct option_desc_t); i++) {
405 if (option_type == options[i].type) {
406 return options[i].name;
407 }
408 }
409 }
410 /* unknown option type, just print to buf */
411 snprintf(buf, sizeof(buf), "%u", option_type);
412 return buf;
413 }
414
415 static unsigned int
print_content_format(unsigned int format_type,unsigned char * result,unsigned int buflen)416 print_content_format(unsigned int format_type,
417 unsigned char *result, unsigned int buflen) {
418 struct desc_t {
419 unsigned int type;
420 const char *name;
421 };
422
423 static struct desc_t formats[] = {
424 { COAP_MEDIATYPE_TEXT_PLAIN, "text/plain" },
425 { COAP_MEDIATYPE_APPLICATION_LINK_FORMAT, "application/link-format" },
426 { COAP_MEDIATYPE_APPLICATION_XML, "application/xml" },
427 { COAP_MEDIATYPE_APPLICATION_OCTET_STREAM, "application/octet-stream" },
428 { COAP_MEDIATYPE_APPLICATION_RDF_XML, "application/rdf+xml" },
429 { COAP_MEDIATYPE_APPLICATION_EXI, "application/exi" },
430 { COAP_MEDIATYPE_APPLICATION_JSON, "application/json" },
431 { COAP_MEDIATYPE_APPLICATION_CBOR, "application/cbor" },
432 { COAP_MEDIATYPE_APPLICATION_CWT, "application/cwt" },
433 { COAP_MEDIATYPE_APPLICATION_COSE_SIGN, "application/cose; cose-type=\"cose-sign\"" },
434 { COAP_MEDIATYPE_APPLICATION_COSE_SIGN1, "application/cose; cose-type=\"cose-sign1\"" },
435 { COAP_MEDIATYPE_APPLICATION_COSE_ENCRYPT, "application/cose; cose-type=\"cose-encrypt\"" },
436 { COAP_MEDIATYPE_APPLICATION_COSE_ENCRYPT0, "application/cose; cose-type=\"cose-encrypt0\"" },
437 { COAP_MEDIATYPE_APPLICATION_COSE_MAC, "application/cose; cose-type=\"cose-mac\"" },
438 { COAP_MEDIATYPE_APPLICATION_COSE_MAC0, "application/cose; cose-type=\"cose-mac0\"" },
439 { COAP_MEDIATYPE_APPLICATION_COSE_KEY, "application/cose-key" },
440 { COAP_MEDIATYPE_APPLICATION_COSE_KEY_SET, "application/cose-key-set" },
441 { COAP_MEDIATYPE_APPLICATION_SENML_JSON, "application/senml+json" },
442 { COAP_MEDIATYPE_APPLICATION_SENSML_JSON, "application/sensml+json" },
443 { COAP_MEDIATYPE_APPLICATION_SENML_CBOR, "application/senml+cbor" },
444 { COAP_MEDIATYPE_APPLICATION_SENSML_CBOR, "application/sensml+cbor" },
445 { COAP_MEDIATYPE_APPLICATION_SENML_EXI, "application/senml-exi" },
446 { COAP_MEDIATYPE_APPLICATION_SENSML_EXI, "application/sensml-exi" },
447 { COAP_MEDIATYPE_APPLICATION_SENML_XML, "application/senml+xml" },
448 { COAP_MEDIATYPE_APPLICATION_SENSML_XML, "application/sensml+xml" },
449 { COAP_MEDIATYPE_APPLICATION_DOTS_CBOR, "application/dots+cbor" },
450 { 75, "application/dcaf+cbor" }
451 };
452
453 size_t i;
454
455 /* search format_type in list of known content formats */
456 for (i = 0; i < sizeof(formats)/sizeof(struct desc_t); i++) {
457 if (format_type == formats[i].type) {
458 return snprintf((char *)result, buflen, "%s", formats[i].name);
459 }
460 }
461
462 /* unknown content format, just print numeric value to buf */
463 return snprintf((char *)result, buflen, "%d", format_type);
464 }
465
466 /**
467 * Returns 1 if the given @p content_format is either unknown or known
468 * to carry binary data. The return value @c 0 hence indicates
469 * printable data which is also assumed if @p content_format is @c 01.
470 */
471 COAP_STATIC_INLINE int
is_binary(int content_format)472 is_binary(int content_format) {
473 return !(content_format == -1 ||
474 content_format == COAP_MEDIATYPE_TEXT_PLAIN ||
475 content_format == COAP_MEDIATYPE_APPLICATION_LINK_FORMAT ||
476 content_format == COAP_MEDIATYPE_APPLICATION_XML ||
477 content_format == COAP_MEDIATYPE_APPLICATION_JSON);
478 }
479
480 #define COAP_DO_SHOW_OUTPUT_LINE \
481 do { \
482 if (use_fprintf_for_show_pdu) { \
483 fprintf(COAP_DEBUG_FD, "%s", outbuf); \
484 } \
485 else { \
486 coap_log(level, "%s", outbuf); \
487 } \
488 } while (0)
489
490 /*
491 * It is possible to override the output debug buffer size and hence control
492 * the amount of information printed out about a CoAP PDU.
493 * Note: Adding a byte may be insufficient to output the next byte of the PDU.
494 *
495 * This is done by the adding of a -DCOAP_DEBUG_BUF_SIZE=nnnn option to the
496 * CPPFLAGS parameter that is optionally used on the ./configure command line.
497 *
498 * E.g. ./configure CPPFLAGS="-DCOAP_DEBUG_BUF_SIZE=4096"
499 *
500 */
501
502 #if COAP_DEBUG_BUF_SIZE < 5
503 #error "COAP_DEBUG_BUF_SIZE must be at least 5, should be >= 32 to be useful"
504 #endif /* COAP_DEBUG_BUF_SIZE < 5 */
505
506 void
coap_show_pdu(coap_log_t level,const coap_pdu_t * pdu)507 coap_show_pdu(coap_log_t level, const coap_pdu_t *pdu) {
508 #if COAP_CONSTRAINED_STACK
509 static coap_mutex_t static_show_pdu_mutex = COAP_MUTEX_INITIALIZER;
510 /* Proxy-Uri: can be 1034 bytes long */
511 static unsigned char buf[min(COAP_DEBUG_BUF_SIZE, 1035)];
512 static char outbuf[COAP_DEBUG_BUF_SIZE];
513 #else /* ! COAP_CONSTRAINED_STACK */
514 /* Proxy-Uri: can be 1034 bytes long */
515 unsigned char buf[min(COAP_DEBUG_BUF_SIZE, 1035)];
516 char outbuf[COAP_DEBUG_BUF_SIZE];
517 #endif /* ! COAP_CONSTRAINED_STACK */
518 size_t buf_len = 0; /* takes the number of bytes written to buf */
519 int encode = 0, have_options = 0, i;
520 coap_opt_iterator_t opt_iter;
521 coap_opt_t *option;
522 int content_format = -1;
523 size_t data_len;
524 const uint8_t *data;
525 uint32_t opt_len;
526 const uint8_t* opt_val;
527 size_t outbuflen = 0;
528
529 /* Save time if not needed */
530 if (level > coap_get_log_level())
531 return;
532
533 #if COAP_CONSTRAINED_STACK
534 coap_mutex_lock(&static_show_pdu_mutex);
535 #endif /* COAP_CONSTRAINED_STACK */
536
537 snprintf(outbuf, sizeof(outbuf), "v:%d t:%s c:%s i:%04x {",
538 COAP_DEFAULT_VERSION, msg_type_string(pdu->type),
539 msg_code_string(pdu->code), pdu->mid);
540
541 for (i = 0; i < pdu->token_length; i++) {
542 outbuflen = strlen(outbuf);
543 snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen,
544 "%02x", pdu->token[i]);
545 }
546 outbuflen = strlen(outbuf);
547 snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, "}");
548
549 /* show options, if any */
550 coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL);
551
552 outbuflen = strlen(outbuf);
553 snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, " [");
554 while ((option = coap_option_next(&opt_iter))) {
555 buf[0] = '\000';
556 if (!have_options) {
557 have_options = 1;
558 } else {
559 outbuflen = strlen(outbuf);
560 snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, ",");
561 }
562
563 if (pdu->code == COAP_SIGNALING_CODE_CSM) switch(opt_iter.number) {
564 case COAP_SIGNALING_OPTION_MAX_MESSAGE_SIZE:
565 buf_len = snprintf((char *)buf, sizeof(buf), "%u",
566 coap_decode_var_bytes(coap_opt_value(option),
567 coap_opt_length(option)));
568 break;
569 default:
570 buf_len = 0;
571 break;
572 } else if (pdu->code == COAP_SIGNALING_CODE_PING
573 || pdu->code == COAP_SIGNALING_CODE_PONG) {
574 buf_len = 0;
575 } else if (pdu->code == COAP_SIGNALING_CODE_RELEASE) switch(opt_iter.number) {
576 case COAP_SIGNALING_OPTION_ALTERNATIVE_ADDRESS:
577 buf_len = print_readable(coap_opt_value(option),
578 coap_opt_length(option),
579 buf, sizeof(buf), 0);
580 break;
581 case COAP_SIGNALING_OPTION_HOLD_OFF:
582 buf_len = snprintf((char *)buf, sizeof(buf), "%u",
583 coap_decode_var_bytes(coap_opt_value(option),
584 coap_opt_length(option)));
585 break;
586 default:
587 buf_len = 0;
588 break;
589 } else if (pdu->code == COAP_SIGNALING_CODE_ABORT) switch(opt_iter.number) {
590 case COAP_SIGNALING_OPTION_BAD_CSM_OPTION:
591 buf_len = snprintf((char *)buf, sizeof(buf), "%u",
592 coap_decode_var_bytes(coap_opt_value(option),
593 coap_opt_length(option)));
594 break;
595 default:
596 buf_len = 0;
597 break;
598 } else switch (opt_iter.number) {
599 case COAP_OPTION_CONTENT_FORMAT:
600 case COAP_OPTION_ACCEPT:
601 content_format = (int)coap_decode_var_bytes(coap_opt_value(option),
602 coap_opt_length(option));
603
604 buf_len = print_content_format(content_format, buf, sizeof(buf));
605 break;
606
607 case COAP_OPTION_BLOCK1:
608 case COAP_OPTION_BLOCK2:
609 /* split block option into number/more/size where more is the
610 * letter M if set, the _ otherwise */
611 buf_len = snprintf((char *)buf, sizeof(buf), "%u/%c/%u",
612 coap_opt_block_num(option), /* block number */
613 COAP_OPT_BLOCK_MORE(option) ? 'M' : '_', /* M bit */
614 (1 << (COAP_OPT_BLOCK_SZX(option) + 4))); /* block size */
615
616 break;
617
618 case COAP_OPTION_URI_PORT:
619 case COAP_OPTION_MAXAGE:
620 case COAP_OPTION_OBSERVE:
621 case COAP_OPTION_SIZE1:
622 case COAP_OPTION_SIZE2:
623 case COAP_OPTION_HOP_LIMIT:
624 /* show values as unsigned decimal value */
625 buf_len = snprintf((char *)buf, sizeof(buf), "%u",
626 coap_decode_var_bytes(coap_opt_value(option),
627 coap_opt_length(option)));
628 break;
629
630 case COAP_OPTION_IF_MATCH:
631 case COAP_OPTION_ETAG:
632 opt_len = coap_opt_length(option);
633 opt_val = coap_opt_value(option);
634 snprintf((char *)buf, sizeof(buf), "0x");
635 for (i = 0; (uint32_t)i < opt_len; i++) {
636 buf_len = strlen((char *)buf);
637 snprintf((char *)&buf[buf_len], sizeof(buf)-buf_len,
638 "%02x", opt_val[i]);
639 }
640 buf_len = strlen((char *)buf);
641 break;
642 default:
643 /* generic output function for all other option types */
644 if (opt_iter.number == COAP_OPTION_URI_PATH ||
645 opt_iter.number == COAP_OPTION_PROXY_URI ||
646 opt_iter.number == COAP_OPTION_URI_HOST ||
647 opt_iter.number == COAP_OPTION_LOCATION_PATH ||
648 opt_iter.number == COAP_OPTION_LOCATION_QUERY ||
649 opt_iter.number == COAP_OPTION_PROXY_SCHEME ||
650 opt_iter.number == COAP_OPTION_URI_QUERY) {
651 encode = 0;
652 } else {
653 encode = 1;
654 }
655
656 buf_len = print_readable(coap_opt_value(option),
657 coap_opt_length(option),
658 buf, sizeof(buf), encode);
659 }
660
661 outbuflen = strlen(outbuf);
662 snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen,
663 " %s:%.*s", msg_option_string(pdu->code, opt_iter.number),
664 (int)buf_len, buf);
665 }
666
667 outbuflen = strlen(outbuf);
668 snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, " ]");
669
670 if (coap_get_data(pdu, &data_len, &data)) {
671
672 outbuflen = strlen(outbuf);
673 snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, " :: ");
674
675 if (is_binary(content_format) || !isprint(data[0])) {
676 size_t keep_data_len = data_len;
677 const uint8_t *keep_data = data;
678
679 outbuflen = strlen(outbuf);
680 snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen,
681 "binary data length %zu\n", data_len);
682 COAP_DO_SHOW_OUTPUT_LINE;
683 /*
684 * Output hex dump of binary data as a continuous entry
685 */
686 outbuf[0] = '\000';
687 snprintf(outbuf, sizeof(outbuf), "<<");
688 while (data_len--) {
689 outbuflen = strlen(outbuf);
690 snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen,
691 "%02x", *data++);
692 }
693 outbuflen = strlen(outbuf);
694 snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, ">>");
695 data_len = keep_data_len;
696 data = keep_data;
697 outbuflen = strlen(outbuf);
698 snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, "\n");
699 COAP_DO_SHOW_OUTPUT_LINE;
700 /*
701 * Output ascii readable (if possible), immediately under the
702 * hex value of the character output above to help binary debugging
703 */
704 outbuf[0] = '\000';
705 snprintf(outbuf, sizeof(outbuf), "<<");
706 while (data_len--) {
707 outbuflen = strlen(outbuf);
708 snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen,
709 "%c ", isprint (*data) ? *data : '.');
710 data++;
711 }
712 outbuflen = strlen(outbuf);
713 snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, ">>");
714 } else {
715 size_t max_length;
716 outbuflen = strlen(outbuf);
717 max_length = sizeof(outbuf)-outbuflen;
718 if (max_length > 1) {
719 outbuf[outbuflen++] = '\'';
720 outbuf[outbuflen] = '\000';
721 max_length--;
722 }
723 if (max_length > 1) {
724 outbuflen += print_readable(data, data_len,
725 (unsigned char*)&outbuf[outbuflen],
726 max_length, 0);
727 }
728 /* print_readable may be handling unprintables - hence headroom of 4 */
729 if (outbuflen < sizeof(outbuf)-4-1) {
730 outbuf[outbuflen++] = '\'';
731 outbuf[outbuflen] = '\000';
732 }
733 }
734 }
735
736 outbuflen = strlen(outbuf);
737 if (outbuflen == sizeof(outbuf)-1) outbuflen--;
738 snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, "\n");
739 COAP_DO_SHOW_OUTPUT_LINE;
740
741 #if COAP_CONSTRAINED_STACK
742 coap_mutex_unlock(&static_show_pdu_mutex);
743 #endif /* COAP_CONSTRAINED_STACK */
744 }
745
coap_show_tls_version(coap_log_t level)746 void coap_show_tls_version(coap_log_t level)
747 {
748 char buffer[128];
749 coap_string_tls_version(buffer, sizeof(buffer));
750 coap_log(level, "%s\n", buffer);
751 }
752
coap_string_tls_version(char * buffer,size_t bufsize)753 char *coap_string_tls_version(char *buffer, size_t bufsize)
754 {
755 coap_tls_version_t *tls_version = coap_get_tls_library_version();
756 char beta[8];
757 char sub[2];
758 char b_beta[8];
759 char b_sub[2];
760
761 switch (tls_version->type) {
762 case COAP_TLS_LIBRARY_NOTLS:
763 snprintf(buffer, bufsize, "TLS Library: None");
764 break;
765 case COAP_TLS_LIBRARY_TINYDTLS:
766 snprintf(buffer, bufsize, "TLS Library: TinyDTLS - runtime %lu.%lu.%lu, "
767 "libcoap built for %lu.%lu.%lu",
768 (unsigned long)(tls_version->version >> 16),
769 (unsigned long)((tls_version->version >> 8) & 0xff),
770 (unsigned long)(tls_version->version & 0xff),
771 (unsigned long)(tls_version->built_version >> 16),
772 (unsigned long)((tls_version->built_version >> 8) & 0xff),
773 (unsigned long)(tls_version->built_version & 0xff));
774 break;
775 case COAP_TLS_LIBRARY_OPENSSL:
776 switch (tls_version->version &0xf) {
777 case 0:
778 strcpy(beta, "-dev");
779 break;
780 case 0xf:
781 strcpy(beta, "");
782 break;
783 default:
784 strcpy(beta, "-beta");
785 beta[5] = (tls_version->version &0xf) + '0';
786 beta[6] = '\000';
787 break;
788 }
789 sub[0] = ((tls_version->version >> 4) & 0xff) ?
790 ((tls_version->version >> 4) & 0xff) + 'a' -1 : '\000';
791 sub[1] = '\000';
792 switch (tls_version->built_version &0xf) {
793 case 0:
794 strcpy(b_beta, "-dev");
795 break;
796 case 0xf:
797 strcpy(b_beta, "");
798 break;
799 default:
800 strcpy(b_beta, "-beta");
801 b_beta[5] = (tls_version->built_version &0xf) + '0';
802 b_beta[6] = '\000';
803 break;
804 }
805 b_sub[0] = ((tls_version->built_version >> 4) & 0xff) ?
806 ((tls_version->built_version >> 4) & 0xff) + 'a' -1 : '\000';
807 b_sub[1] = '\000';
808 snprintf(buffer, bufsize, "TLS Library: OpenSSL - runtime "
809 "%lu.%lu.%lu%s%s, libcoap built for %lu.%lu.%lu%s%s",
810 (unsigned long)(tls_version->version >> 28),
811 (unsigned long)((tls_version->version >> 20) & 0xff),
812 (unsigned long)((tls_version->version >> 12) & 0xff), sub, beta,
813 (unsigned long)(tls_version->built_version >> 28),
814 (unsigned long)((tls_version->built_version >> 20) & 0xff),
815 (unsigned long)((tls_version->built_version >> 12) & 0xff),
816 b_sub, b_beta);
817 break;
818 case COAP_TLS_LIBRARY_GNUTLS:
819 snprintf(buffer, bufsize, "TLS Library: GnuTLS - runtime %lu.%lu.%lu, "
820 "libcoap built for %lu.%lu.%lu",
821 (unsigned long)(tls_version->version >> 16),
822 (unsigned long)((tls_version->version >> 8) & 0xff),
823 (unsigned long)(tls_version->version & 0xff),
824 (unsigned long)(tls_version->built_version >> 16),
825 (unsigned long)((tls_version->built_version >> 8) & 0xff),
826 (unsigned long)(tls_version->built_version & 0xff));
827 break;
828 case COAP_TLS_LIBRARY_MBEDTLS:
829 snprintf(buffer, bufsize, "TLS Library: Mbed TLS - runtime %lu.%lu.%lu, "
830 "libcoap built for %lu.%lu.%lu",
831 (unsigned long)(tls_version->version >> 24),
832 (unsigned long)((tls_version->version >> 16) & 0xff),
833 (unsigned long)((tls_version->version >> 8) & 0xff),
834 (unsigned long)(tls_version->built_version >> 24),
835 (unsigned long)((tls_version->built_version >> 16) & 0xff),
836 (unsigned long)((tls_version->built_version >> 8) & 0xff));
837 break;
838 default:
839 snprintf(buffer, bufsize, "Library type %d unknown", tls_version->type);
840 break;
841 }
842 return buffer;
843 }
844
coap_string_tls_support(char * buffer,size_t bufsize)845 char *coap_string_tls_support(char *buffer, size_t bufsize)
846 {
847 coap_tls_version_t *tls_version = coap_get_tls_library_version();
848
849 switch (tls_version->type) {
850 case COAP_TLS_LIBRARY_NOTLS:
851 snprintf(buffer, bufsize, "(No DTLS or TLS support)");
852 break;
853 case COAP_TLS_LIBRARY_TINYDTLS:
854 snprintf(buffer, bufsize,
855 "(DTLS and no TLS support; PSK and RPK support)");
856 break;
857 case COAP_TLS_LIBRARY_OPENSSL:
858 snprintf(buffer, bufsize,
859 "(DTLS and TLS support; PSK, PKI, PKCS11 and no RPK support)");
860 break;
861 case COAP_TLS_LIBRARY_GNUTLS:
862 if (tls_version->version >= 0x030606)
863 snprintf(buffer, bufsize,
864 "(DTLS and TLS support; PSK, PKI, PKCS11 and RPK support)");
865 else
866 snprintf(buffer, bufsize,
867 "(DTLS and TLS support; PSK, PKI, PKCS11 and no RPK support)");
868 break;
869 case COAP_TLS_LIBRARY_MBEDTLS:
870 snprintf(buffer, bufsize,
871 "(DTLS and no TLS support; PSK, PKI and no RPK support)");
872 break;
873 default:
874 buffer[0] = '\000';
875 break;
876 }
877 return buffer;
878 }
879
880 static coap_log_handler_t log_handler = NULL;
881
coap_set_log_handler(coap_log_handler_t handler)882 void coap_set_log_handler(coap_log_handler_t handler) {
883 log_handler = handler;
884 }
885
886 void
coap_log_impl(coap_log_t level,const char * format,...)887 coap_log_impl(coap_log_t level, const char *format, ...) {
888
889 if (maxlog < level)
890 return;
891
892 if (log_handler) {
893 #if COAP_CONSTRAINED_STACK
894 static coap_mutex_t static_log_mutex = COAP_MUTEX_INITIALIZER;
895 static char message[COAP_DEBUG_BUF_SIZE];
896 #else /* ! COAP_CONSTRAINED_STACK */
897 char message[COAP_DEBUG_BUF_SIZE];
898 #endif /* ! COAP_CONSTRAINED_STACK */
899 va_list ap;
900 va_start(ap, format);
901 #if COAP_CONSTRAINED_STACK
902 coap_mutex_lock(&static_log_mutex);
903 #endif /* COAP_CONSTRAINED_STACK */
904
905 vsnprintf( message, sizeof(message), format, ap);
906 va_end(ap);
907 log_handler(level, message);
908 #if COAP_CONSTRAINED_STACK
909 coap_mutex_unlock(&static_log_mutex);
910 #endif /* COAP_CONSTRAINED_STACK */
911 } else {
912 char timebuf[32];
913 coap_tick_t now;
914 va_list ap;
915 FILE *log_fd;
916 size_t len;
917
918 log_fd = level <= LOG_CRIT ? COAP_ERR_FD : COAP_DEBUG_FD;
919
920 coap_ticks(&now);
921 len = print_timestamp(timebuf,sizeof(timebuf), now);
922 if (len)
923 fprintf(log_fd, "%.*s ", (int)len, timebuf);
924
925 if (level <= COAP_LOG_CIPHERS)
926 fprintf(log_fd, "%s ", loglevels[level]);
927
928 va_start(ap, format);
929 vfprintf(log_fd, format, ap);
930 va_end(ap);
931 fflush(log_fd);
932 }
933 }
934
935 static struct packet_num_interval {
936 int start;
937 int end;
938 } packet_loss_intervals[10];
939 static int num_packet_loss_intervals = 0;
940 static int packet_loss_level = 0;
941 static int send_packet_count = 0;
942
coap_debug_set_packet_loss(const char * loss_level)943 int coap_debug_set_packet_loss(const char *loss_level) {
944 const char *p = loss_level;
945 char *end = NULL;
946 int n = (int)strtol(p, &end, 10), i = 0;
947 if (end == p || n < 0)
948 return 0;
949 if (*end == '%') {
950 if (n > 100)
951 n = 100;
952 packet_loss_level = n * 65536 / 100;
953 coap_log(LOG_DEBUG, "packet loss level set to %d%%\n", n);
954 } else {
955 if (n <= 0)
956 return 0;
957 while (i < 10) {
958 packet_loss_intervals[i].start = n;
959 if (*end == '-') {
960 p = end + 1;
961 n = (int)strtol(p, &end, 10);
962 if (end == p || n <= 0)
963 return 0;
964 }
965 packet_loss_intervals[i++].end = n;
966 if (*end == 0)
967 break;
968 if (*end != ',')
969 return 0;
970 p = end + 1;
971 n = (int)strtol(p, &end, 10);
972 if (end == p || n <= 0)
973 return 0;
974 }
975 if (i == 10)
976 return 0;
977 num_packet_loss_intervals = i;
978 }
979 send_packet_count = 0;
980 return 1;
981 }
982
coap_debug_send_packet(void)983 int coap_debug_send_packet(void) {
984 ++send_packet_count;
985 if (num_packet_loss_intervals > 0) {
986 int i;
987 for (i = 0; i < num_packet_loss_intervals; i++) {
988 if (send_packet_count >= packet_loss_intervals[i].start
989 && send_packet_count <= packet_loss_intervals[i].end) {
990 coap_log(LOG_DEBUG, "Packet %u dropped\n", send_packet_count);
991 return 0;
992 }
993 }
994 }
995 if ( packet_loss_level > 0 ) {
996 uint16_t r = 0;
997 coap_prng( (uint8_t*)&r, 2 );
998 if ( r < packet_loss_level ) {
999 coap_log(LOG_DEBUG, "Packet %u dropped\n", send_packet_count);
1000 return 0;
1001 }
1002 }
1003 return 1;
1004 }
1005