• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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