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