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