• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* coap_debug.c -- debug utilities
2  *
3  * Copyright (C) 2010--2012,2014--2023 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 /**
12  * @file coap_debug.c
13  * @brief Debug utilities
14  */
15 
16 #include "coap3/coap_internal.h"
17 
18 #if defined(HAVE_STRNLEN) && defined(__GNUC__) && !defined(_GNU_SOURCE)
19 #define _GNU_SOURCE 1
20 #endif
21 
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <ctype.h>
26 
27 #ifdef HAVE_ARPA_INET_H
28 #include <arpa/inet.h>
29 #endif
30 #ifdef HAVE_WS2TCPIP_H
31 #include <ws2tcpip.h>
32 #endif
33 
34 #ifdef HAVE_TIME_H
35 #include <time.h>
36 #endif
37 
38 #ifdef WITH_LWIP
39 # define fprintf(fd, ...) LWIP_PLATFORM_DIAG((__VA_ARGS__))
40 # define fflush(...)
41 #endif
42 
43 #ifdef WITH_CONTIKI
44 # define fprintf(fd, ...) { (void)fd; printf(__VA_ARGS__); }
45 # define fflush(...)
46 # define vfprintf(fd, ...) { (void)fd; printf(__VA_ARGS__); }
47 
48 # ifndef LOG_CONF_LEVEL_COAP
49 #  define LOG_CONF_LEVEL_COAP 2 /* = LOG_LEVEL_WARN */
50 # endif
51 static coap_log_t maxlog = LOG_CONF_LEVEL_COAP == 0 ? /* = LOG_LEVEL_NONE */
52                            COAP_LOG_EMERG :
53                            (LOG_CONF_LEVEL_COAP == 1 ?  /* = LOG_LEVEL_ERR */
54                             COAP_LOG_ERR :
55                             (LOG_CONF_LEVEL_COAP == 2 ?  /* = LOG_LEVEL_WARN */
56                              COAP_LOG_WARN :
57                              (LOG_CONF_LEVEL_COAP == 3 ? /* = LOG_LEVEL_INFO */
58                               COAP_LOG_INFO :
59                               COAP_LOG_DEBUG)));
60 #else /* WITH_CONTIKI */
61 static coap_log_t maxlog = COAP_LOG_WARN;  /* default maximum CoAP log level */
62 #endif /* WITH_CONTIKI */
63 
64 static int use_fprintf_for_show_pdu = 1; /* non zero to output with fprintf */
65 
66 const char *
coap_package_name(void)67 coap_package_name(void) {
68   return PACKAGE_NAME;
69 }
70 
71 const char *
coap_package_version(void)72 coap_package_version(void) {
73   return PACKAGE_STRING;
74 }
75 
76 const char *
coap_package_build(void)77 coap_package_build(void) {
78 #ifdef LIBCOAP_PACKAGE_BUILD
79   return LIBCOAP_PACKAGE_BUILD;
80 #else /* !LIBCOAP_PACKAGE_BUILD */
81   return PACKAGE_STRING;
82 #endif /* !LIBCOAP_PACKAGE_BUILD */
83 }
84 
85 void
coap_set_show_pdu_output(int use_fprintf)86 coap_set_show_pdu_output(int use_fprintf) {
87   use_fprintf_for_show_pdu = use_fprintf;
88 }
89 
90 coap_log_t
coap_get_log_level(void)91 coap_get_log_level(void) {
92   return maxlog;
93 }
94 
95 void
coap_set_log_level(coap_log_t level)96 coap_set_log_level(coap_log_t level) {
97   if (level > COAP_MAX_LOGGING_LEVEL)
98     level = COAP_MAX_LOGGING_LEVEL;
99   maxlog = level;
100 }
101 
102 /* this array has the same order as the type coap_log_t with the (D)TLS
103    entries added to the list with a COAP_LOG_DTLS_BASE offset */
104 static const char *loglevels[] = {
105   /* General logging */
106   "EMRG", "ALRT", "CRIT", "ERR ", "WARN", "NOTE", "INFO", "DEBG", "OSC ",
107   /* (D)TLS logging */
108   "Emrg", "Alrt", "Crit", "Err ", "Warn", "Note", "Info", "Debg"
109 };
110 
111 #ifdef WITH_CONTIKI
112 void
coap_print_contiki_prefix(coap_log_t level)113 coap_print_contiki_prefix(coap_log_t level) {
114   printf("[%s: COAP      ] ", loglevels[level]);
115 }
116 #endif /* WITH_CONTIKI */
117 
118 #ifdef HAVE_TIME_H
119 
120 COAP_STATIC_INLINE size_t
print_timestamp(char * s,size_t len,coap_tick_t t)121 print_timestamp(char *s, size_t len, coap_tick_t t) {
122   struct tm *tmp;
123   size_t lensofar;
124   time_t now = coap_ticks_to_rt(t);
125   tmp = localtime(&now);
126   lensofar = strftime(s, len, "%b %d %H:%M:%S", tmp);
127   if (len > lensofar + 4) {
128     lensofar += snprintf(&s[lensofar], len-lensofar, ".%03u",
129                          (unsigned int)((coap_ticks_to_rt_us(t) % 1000000)/1000));
130   }
131   return lensofar;
132 }
133 
134 #else /* alternative implementation: just print the timestamp */
135 
136 COAP_STATIC_INLINE size_t
print_timestamp(char * s,size_t len,coap_tick_t t)137 print_timestamp(char *s, size_t len, coap_tick_t t) {
138 #ifdef HAVE_SNPRINTF
139   return snprintf(s, len, "%u.%03u",
140                   (unsigned int)coap_ticks_to_rt(t),
141                   (unsigned int)((coap_ticks_to_rt_us(t) % 1000000)/1000));
142 #else /* HAVE_SNPRINTF */
143   /* @todo do manual conversion of timestamp */
144   return 0;
145 #endif /* HAVE_SNPRINTF */
146 }
147 
148 #endif /* HAVE_TIME_H */
149 
150 #if !defined(HAVE_STRNLEN) && !defined(__MINGW32__)
151 /**
152  * A length-safe strlen() fake.
153  *
154  * @param s      The string to count characters != 0.
155  * @param maxlen The maximum length of @p s.
156  *
157  * @return The length of @p s.
158  */
159 static inline size_t
strnlen(const char * s,size_t maxlen)160 strnlen(const char *s, size_t maxlen) {
161   size_t n = 0;
162   while (*s++ && n < maxlen)
163     ++n;
164   return n;
165 }
166 #endif /* HAVE_STRNLEN && !__MINGW32__ */
167 
168 static size_t
print_readable(const uint8_t * data,size_t len,unsigned char * result,size_t buflen,int encode_always)169 print_readable(const uint8_t *data, size_t len,
170                unsigned char *result, size_t buflen, int encode_always) {
171   const uint8_t hex[] = "0123456789ABCDEF";
172   size_t cnt = 0;
173   assert(data || len == 0);
174 
175   if (buflen == 0) { /* there is nothing we can do here but return */
176     return 0;
177   }
178 
179   while (len) {
180     if (!encode_always && isprint(*data)) {
181       if (cnt+1 < buflen) { /* keep one byte for terminating zero */
182         *result++ = *data;
183         ++cnt;
184       } else {
185         break;
186       }
187     } else {
188       if (cnt+4 < buflen) { /* keep one byte for terminating zero */
189         *result++ = '\\';
190         *result++ = 'x';
191         *result++ = hex[(*data & 0xf0) >> 4];
192         *result++ = hex[*data & 0x0f];
193         cnt += 4;
194       } else
195         break;
196     }
197 
198     ++data;
199     --len;
200   }
201 
202   *result = '\0'; /* add a terminating zero */
203   return cnt;
204 }
205 
206 #ifndef min
207 #define min(a,b) ((a) < (b) ? (a) : (b))
208 #endif
209 
210 #ifndef INET6_ADDRSTRLEN
211 #define INET6_ADDRSTRLEN 46
212 #endif
213 /*
214  * Returned buf is always NULL terminated.
215  * Returned size is number of characters, not including NULL terminator.
216  */
217 size_t
coap_print_addr(const coap_address_t * addr,unsigned char * buf,size_t len)218 coap_print_addr(const coap_address_t *addr, unsigned char *buf, size_t len) {
219 #if defined( HAVE_ARPA_INET_H ) || defined( HAVE_WS2TCPIP_H )
220   char scratch[INET6_ADDRSTRLEN];
221 
222   assert(buf);
223   assert(len);
224   buf[0] = '\000';
225 
226   switch (addr->addr.sa.sa_family) {
227 #if COAP_IPV4_SUPPORT
228   case AF_INET:
229     snprintf((char *)buf, len, "%s:%d",
230              coap_print_ip_addr(addr, scratch, sizeof(scratch)),
231              coap_address_get_port(addr));
232     break;
233 #endif /* COAP_IPV4_SUPPORT */
234 #if COAP_IPV6_SUPPORT
235   case AF_INET6:
236     snprintf((char *)buf, len, "[%s]:%d",
237              coap_print_ip_addr(addr, scratch, sizeof(scratch)),
238              coap_address_get_port(addr));
239     break;
240 #endif /* COAP_IPV6_SUPPORT */
241 #if COAP_AF_UNIX_SUPPORT
242   case AF_UNIX:
243     snprintf((char *)buf, len, "'%s'", addr->addr.cun.sun_path);
244     break;
245 #endif /* COAP_AF_UNIX_SUPPORT */
246   default:
247     /* Include trailing NULL if possible */
248     memcpy(buf, "(unknown address type)", min(22+1, len));
249     buf[len-1] = '\000';
250     break;
251   }
252   return strlen((char *)buf);
253 
254 #else /* HAVE_ARPA_INET_H */
255 
256 # if WITH_CONTIKI
257 
258   char scratch[INET6_ADDRSTRLEN];
259 #ifdef HAVE_SNPRINTF
260 
261   snprintf((char *)buf, len, "[%s]:%d",
262            coap_print_ip_addr(addr, scratch, sizeof(scratch)),
263            coap_address_get_port(addr));
264   return strlen((char *)buf);
265 #else /* HAVE_SNPRINTF */
266   unsigned char *p = buf;
267 #  if NETSTACK_CONF_WITH_IPV6
268 
269   assert(buf);
270   assert(len);
271   buf[0] = '\000';
272   if (len < 40 + 2 + 6)
273     return 0;
274 
275   *p++ = '[';
276   memcpy(p, coap_print_ip_addr(addr, scratch, sizeof(scratch)), 40);
277   p += 40 - 1;
278   *p++ = ']';
279 #  else /* WITH_UIP6 */
280 #   warning "IPv4 network addresses will not be included in debug output"
281 
282   if (len < 21) {
283     *p = '\000';
284     return 0;
285   }
286 #  endif /* WITH_UIP6 */
287 
288   *p++ = ':';
289   *p++ = '0' + (coap_address_get_port(addr) / 10000) % 10;
290   *p++ = '0' + (coap_address_get_port(addr) / 1000) % 10;
291   *p++ = '0' + (coap_address_get_port(addr) / 100) % 10;
292   *p++ = '0' + (coap_address_get_port(addr) / 10) % 10;
293   *p++ = '0' + coap_address_get_port(addr) % 10;
294   *p = '\000';
295 
296   return strlen((char *)buf);
297 #endif /* HAVE_SNPRINTF */
298 
299 # elif WITH_LWIP
300 
301   char scratch[INET6_ADDRSTRLEN];
302 #ifdef HAVE_SNPRINTF
303 
304   snprintf((char *)buf, len, "[%s]:%d",
305            coap_print_ip_addr(addr, scratch, sizeof(scratch)),
306            addr->port);
307   return strlen((char *)buf);
308 #else /* HAVE_SNPRINTF */
309   unsigned char *p = buf;
310 
311   assert(buf);
312   assert(len);
313   buf[0] = '\000';
314 
315   switch (IP_GET_TYPE(addr->addr)) {
316   case IPADDR_TYPE_V4:
317     if (len < IP4ADDR_STRLEN_MAX + 6)
318       return 0;
319     memcpy(buf, coap_print_ip_addr(addr, scratch, sizeof(scratch)), IP4ADDR_STRLEN_MAX);
320     p += strlen((char *)buf);
321     break;
322 #if LWIP_IPV6
323   case IPADDR_TYPE_V6:
324   case IPADDR_TYPE_ANY:
325     if (len < 40 + 2 + 6)
326       return 0;
327     *p++ = '[';
328     memcpy(p, coap_print_ip_addr(addr, scratch, sizeof(scratch)), 40);
329     p += strlen((char *)buf);
330     *p++ = ']';
331     break;
332 #endif /* LWIP_IPV6 */
333   }
334 
335   *p++ = ':';
336   *p++ = '0' + (addr->port / 10000) % 10;
337   *p++ = '0' + (addr->port / 1000) % 10;
338   *p++ = '0' + (addr->port / 100) % 10;
339   *p++ = '0' + (addr->port / 10) % 10;
340   *p++ = '0' + addr->port % 10;
341   *p = '\000';
342 
343   return strlen((char *)buf);
344 #endif /* HAVE_SNPRINTF */
345 
346 # else /* ! WITH_CONTIKI && ! WITH_LWIP */
347 
348   (void)addr;
349   (void)len;
350 
351   /* TODO: output addresses manually */
352 #   warning "inet_ntop() not available, network addresses will not be included in debug output"
353 # endif /* ! WITH_CONTIKI  && ! WITH_LWIP */
354   buf[0] = '\000';
355   return 0;
356 #endif
357 }
358 
359 /*
360  * Returned buf is always NULL terminated with as much as possible of the
361  * IP address filled in.
362  */
363 const char *
coap_print_ip_addr(const coap_address_t * addr,char * buf,size_t len)364 coap_print_ip_addr(const coap_address_t *addr, char *buf, size_t len) {
365 #if defined( HAVE_ARPA_INET_H ) || defined( HAVE_WS2TCPIP_H )
366   const void *addrptr = NULL;
367 
368   assert(buf);
369   assert(len);
370   buf[0] = '\000';
371 
372   switch (addr->addr.sa.sa_family) {
373 #if COAP_IPV4_SUPPORT
374   case AF_INET:
375     if (len < INET_ADDRSTRLEN)
376       return buf;
377     addrptr = &addr->addr.sin.sin_addr;
378     break;
379 #endif /* COAP_IPV4_SUPPORT */
380 #if COAP_IPV6_SUPPORT
381   case AF_INET6:
382     if (len < INET6_ADDRSTRLEN)
383       return buf;
384     addrptr = &addr->addr.sin6.sin6_addr;
385     break;
386 #endif /* COAP_IPV6_SUPPORT */
387 #if COAP_AF_UNIX_SUPPORT
388   case AF_UNIX:
389     snprintf(buf, len, "'%s'", addr->addr.cun.sun_path);
390     return buf;
391 #endif /* COAP_AF_UNIX_SUPPORT */
392   default:
393     /* Include trailing NULL if possible */
394     memcpy(buf, "(unknown address type)", min(22+1, len));
395     buf[len-1] = '\000';
396     return buf;
397   }
398 
399   /* Cast needed for Windows, since it doesn't have the correct API signature. */
400   if (inet_ntop(addr->addr.sa.sa_family, addrptr, (char *)buf, len) == 0) {
401     coap_log_err("coap_print_ip_addr: inet_ntop\n");
402     buf[0] = '\000';
403     return buf;
404   }
405   return buf;
406 
407 #else /* HAVE_ARPA_INET_H */
408 
409 # if WITH_CONTIKI
410   char *p = buf;
411   uint8_t i;
412 #  if NETSTACK_CONF_WITH_IPV6
413   const char hex[] = "0123456789ABCDEF";
414 
415   assert(buf);
416   assert(len);
417   buf[0] = '\000';
418   if (len < 40)
419     return 0;
420 
421   for (i=0; i < 16; i += 2) {
422     if (i) {
423       *p++ = ':';
424     }
425     *p++ = hex[(addr->addr.u8[i] & 0xf0) >> 4];
426     *p++ = hex[(addr->addr.u8[i] & 0x0f)];
427     *p++ = hex[(addr->addr.u8[i+1] & 0xf0) >> 4];
428     *p++ = hex[(addr->addr.u8[i+1] & 0x0f)];
429   }
430   *p = '\000';
431 #  else /* WITH_UIP6 */
432 #   warning "IPv4 network addresses will not be included in debug output"
433 
434   if (len < 21) {
435     return buf;
436   }
437 #  endif /* WITH_UIP6 */
438   return buf;
439 
440 # elif WITH_LWIP
441 
442   assert(buf);
443   assert(len);
444   buf[0] = '\000';
445 
446   switch (IP_GET_TYPE(&addr->addr)) {
447 #if LWIP_IPV4
448   case IPADDR_TYPE_V4:
449     if (len < IP4ADDR_STRLEN_MAX)
450       return buf;
451     memcpy(buf, ip4addr_ntoa(ip_2_ip4(&addr->addr)), IP4ADDR_STRLEN_MAX);
452     break;
453 #endif /* LWIP_IPV4 */
454 #if LWIP_IPV6
455   case IPADDR_TYPE_V6:
456   case IPADDR_TYPE_ANY:
457     if (len < 40)
458       return buf;
459 #if LWIP_IPV4
460     memcpy(buf, ip6addr_ntoa(&addr->addr.u_addr.ip6), 40);
461 #else /* LWIP_IPV4 */
462     memcpy(buf, ip6addr_ntoa(&addr->addr), 40);
463 #endif /* LWIP_IPV4 */
464     break;
465 #endif /* LWIP_IPV6 */
466   }
467   return buf;
468 
469 # else /* ! WITH_CONTIKI && ! WITH_LWIP */
470 
471   (void)addr;
472   (void)len;
473 
474   /* TODO: output addresses manually */
475 #   warning "inet_ntop() not available, network addresses will not be included in debug output"
476 # endif /* WITH_CONTIKI */
477   buf[0] = '\000';
478   return buf;
479 #endif
480 }
481 
482 /** Returns a textual description of the message type @p t. */
483 static const char *
msg_type_string(uint16_t t)484 msg_type_string(uint16_t t) {
485   static const char *types[] = { "CON", "NON", "ACK", "RST", "???" };
486 
487   return types[min(t, sizeof(types)/sizeof(char *) - 1)];
488 }
489 
490 /** Returns a textual description of the method or response code. */
491 static const char *
msg_code_string(uint16_t c)492 msg_code_string(uint16_t c) {
493   static const char *methods[] = { "0.00", "GET", "POST", "PUT", "DELETE",
494                                    "FETCH", "PATCH", "iPATCH"
495                                  };
496   static const char *signals[] = { "7.00", "CSM", "Ping", "Pong", "Release",
497                                    "Abort"
498                                  };
499   static char buf[5];
500 
501   if (c < sizeof(methods)/sizeof(const char *)) {
502     return methods[c];
503   } else if (c >= 224 && c - 224 < (int)(sizeof(signals)/sizeof(const char *))) {
504     return signals[c-224];
505   } else {
506     snprintf(buf, sizeof(buf), "%u.%02u", (c >> 5) & 0x7, c & 0x1f);
507     return buf;
508   }
509 }
510 
511 /** Returns a textual description of the option name. */
512 static const char *
msg_option_string(uint8_t code,uint16_t option_type)513 msg_option_string(uint8_t code, uint16_t option_type) {
514   struct option_desc_t {
515     uint16_t type;
516     const char *name;
517   };
518 
519   static struct option_desc_t options[] = {
520     { COAP_OPTION_IF_MATCH, "If-Match" },
521     { COAP_OPTION_URI_HOST, "Uri-Host" },
522     { COAP_OPTION_ETAG, "ETag" },
523     { COAP_OPTION_IF_NONE_MATCH, "If-None-Match" },
524     { COAP_OPTION_OBSERVE, "Observe" },
525     { COAP_OPTION_URI_PORT, "Uri-Port" },
526     { COAP_OPTION_LOCATION_PATH, "Location-Path" },
527     { COAP_OPTION_OSCORE, "Oscore" },
528     { COAP_OPTION_URI_PATH, "Uri-Path" },
529     { COAP_OPTION_CONTENT_FORMAT, "Content-Format" },
530     { COAP_OPTION_MAXAGE, "Max-Age" },
531     { COAP_OPTION_URI_QUERY, "Uri-Query" },
532     { COAP_OPTION_HOP_LIMIT, "Hop-Limit" },
533     { COAP_OPTION_ACCEPT, "Accept" },
534     { COAP_OPTION_LOCATION_QUERY, "Location-Query" },
535     { COAP_OPTION_BLOCK2, "Block2" },
536     { COAP_OPTION_BLOCK1, "Block1" },
537     { COAP_OPTION_SIZE2, "Size2" },
538     { COAP_OPTION_PROXY_URI, "Proxy-Uri" },
539     { COAP_OPTION_PROXY_SCHEME, "Proxy-Scheme" },
540     { COAP_OPTION_SIZE1, "Size1" },
541     { COAP_OPTION_ECHO, "Echo" },
542     { COAP_OPTION_NORESPONSE, "No-Response" },
543     { COAP_OPTION_RTAG, "Request-Tag" },
544     { COAP_OPTION_Q_BLOCK1, "Q-Block1" },
545     { COAP_OPTION_Q_BLOCK2, "Q-Block2" }
546   };
547 
548   static struct option_desc_t options_csm[] = {
549     { COAP_SIGNALING_OPTION_MAX_MESSAGE_SIZE, "Max-Message-Size" },
550     { COAP_SIGNALING_OPTION_BLOCK_WISE_TRANSFER, "Block-Wise-Transfer" },
551     { COAP_SIGNALING_OPTION_EXTENDED_TOKEN_LENGTH, "Extended-Token-Length" }
552   };
553 
554   static struct option_desc_t options_pingpong[] = {
555     { COAP_SIGNALING_OPTION_CUSTODY, "Custody" }
556   };
557 
558   static struct option_desc_t options_release[] = {
559     { COAP_SIGNALING_OPTION_ALTERNATIVE_ADDRESS, "Alternative-Address" },
560     { COAP_SIGNALING_OPTION_HOLD_OFF, "Hold-Off" }
561   };
562 
563   static struct option_desc_t options_abort[] = {
564     { COAP_SIGNALING_OPTION_BAD_CSM_OPTION, "Bad-CSM-Option" }
565   };
566 
567   static char buf[6];
568   size_t i;
569 
570   if (code == COAP_SIGNALING_CSM) {
571     for (i = 0; i < sizeof(options_csm)/sizeof(struct option_desc_t); i++) {
572       if (option_type == options_csm[i].type) {
573         return options_csm[i].name;
574       }
575     }
576   } else if (code == COAP_SIGNALING_PING || code == COAP_SIGNALING_PONG) {
577     for (i = 0; i < sizeof(options_pingpong)/sizeof(struct option_desc_t); i++) {
578       if (option_type == options_pingpong[i].type) {
579         return options_pingpong[i].name;
580       }
581     }
582   } else if (code == COAP_SIGNALING_RELEASE) {
583     for (i = 0; i < sizeof(options_release)/sizeof(struct option_desc_t); i++) {
584       if (option_type == options_release[i].type) {
585         return options_release[i].name;
586       }
587     }
588   } else if (code == COAP_SIGNALING_ABORT) {
589     for (i = 0; i < sizeof(options_abort)/sizeof(struct option_desc_t); i++) {
590       if (option_type == options_abort[i].type) {
591         return options_abort[i].name;
592       }
593     }
594   } else {
595     /* search option_type in list of known options */
596     for (i = 0; i < sizeof(options)/sizeof(struct option_desc_t); i++) {
597       if (option_type == options[i].type) {
598         return options[i].name;
599       }
600     }
601   }
602   /* unknown option type, just print to buf */
603   snprintf(buf, sizeof(buf), "%u", option_type);
604   return buf;
605 }
606 
607 static unsigned int
print_content_format(unsigned int format_type,unsigned char * result,unsigned int buflen)608 print_content_format(unsigned int format_type,
609                      unsigned char *result, unsigned int buflen) {
610   struct desc_t {
611     unsigned int type;
612     const char *name;
613   };
614 
615   static struct desc_t formats[] = {
616     { COAP_MEDIATYPE_TEXT_PLAIN, "text/plain" },
617     { COAP_MEDIATYPE_APPLICATION_LINK_FORMAT, "application/link-format" },
618     { COAP_MEDIATYPE_APPLICATION_XML, "application/xml" },
619     { COAP_MEDIATYPE_APPLICATION_OCTET_STREAM, "application/octet-stream" },
620     { COAP_MEDIATYPE_APPLICATION_RDF_XML, "application/rdf+xml" },
621     { COAP_MEDIATYPE_APPLICATION_EXI, "application/exi" },
622     { COAP_MEDIATYPE_APPLICATION_JSON, "application/json" },
623     { COAP_MEDIATYPE_APPLICATION_CBOR, "application/cbor" },
624     { COAP_MEDIATYPE_APPLICATION_CWT, "application/cwt" },
625     { COAP_MEDIATYPE_APPLICATION_COAP_GROUP_JSON, "application/coap-group+json" },
626     { COAP_MEDIATYPE_APPLICATION_COSE_SIGN, "application/cose; cose-type=\"cose-sign\"" },
627     { COAP_MEDIATYPE_APPLICATION_COSE_SIGN1, "application/cose; cose-type=\"cose-sign1\"" },
628     { COAP_MEDIATYPE_APPLICATION_COSE_ENCRYPT, "application/cose; cose-type=\"cose-encrypt\"" },
629     { COAP_MEDIATYPE_APPLICATION_COSE_ENCRYPT0, "application/cose; cose-type=\"cose-encrypt0\"" },
630     { COAP_MEDIATYPE_APPLICATION_COSE_MAC, "application/cose; cose-type=\"cose-mac\"" },
631     { COAP_MEDIATYPE_APPLICATION_COSE_MAC0, "application/cose; cose-type=\"cose-mac0\"" },
632     { COAP_MEDIATYPE_APPLICATION_COSE_KEY, "application/cose-key" },
633     { COAP_MEDIATYPE_APPLICATION_COSE_KEY_SET, "application/cose-key-set" },
634     { COAP_MEDIATYPE_APPLICATION_SENML_JSON, "application/senml+json" },
635     { COAP_MEDIATYPE_APPLICATION_SENSML_JSON, "application/sensml+json" },
636     { COAP_MEDIATYPE_APPLICATION_SENML_CBOR, "application/senml+cbor" },
637     { COAP_MEDIATYPE_APPLICATION_SENSML_CBOR, "application/sensml+cbor" },
638     { COAP_MEDIATYPE_APPLICATION_SENML_EXI, "application/senml-exi" },
639     { COAP_MEDIATYPE_APPLICATION_SENSML_EXI, "application/sensml-exi" },
640     { COAP_MEDIATYPE_APPLICATION_SENML_XML, "application/senml+xml" },
641     { COAP_MEDIATYPE_APPLICATION_SENSML_XML, "application/sensml+xml" },
642     { COAP_MEDIATYPE_APPLICATION_DOTS_CBOR, "application/dots+cbor" },
643     { COAP_MEDIATYPE_APPLICATION_ACE_CBOR, "application/ace+cbor" },
644     { COAP_MEDIATYPE_APPLICATION_MB_CBOR_SEQ, "application/missing-blocks+cbor-seq" },
645     { COAP_MEDIATYPE_APPLICATION_OSCORE, "application/oscore" },
646     { 75, "application/dcaf+cbor" }
647   };
648 
649   size_t i;
650 
651   /* search format_type in list of known content formats */
652   for (i = 0; i < sizeof(formats)/sizeof(struct desc_t); i++) {
653     if (format_type == formats[i].type) {
654       return snprintf((char *)result, buflen, "%s", formats[i].name);
655     }
656   }
657 
658   /* unknown content format, just print numeric value to buf */
659   return snprintf((char *)result, buflen, "%d", format_type);
660 }
661 
662 /**
663  * Returns 1 if the given @p content_format is either unknown or known
664  * to carry binary data. The return value @c 0 hence indicates
665  * printable data which is also assumed if @p content_format is @c 01.
666  */
667 COAP_STATIC_INLINE int
is_binary(int content_format)668 is_binary(int content_format) {
669   return !(content_format == -1 ||
670            content_format == COAP_MEDIATYPE_TEXT_PLAIN ||
671            content_format == COAP_MEDIATYPE_APPLICATION_LINK_FORMAT ||
672            content_format == COAP_MEDIATYPE_APPLICATION_XML ||
673            content_format == COAP_MEDIATYPE_APPLICATION_JSON);
674 }
675 
676 #define COAP_DO_SHOW_OUTPUT_LINE           \
677   do {                                      \
678     if (use_fprintf_for_show_pdu) {         \
679       fprintf(COAP_DEBUG_FD, "%s", outbuf); \
680     }                                       \
681     else {                                  \
682       coap_log(level, "%s", outbuf);        \
683     }                                       \
684   } while (0)
685 
686 /*
687  * It is possible to override the output debug buffer size and hence control
688  * the amount of information printed out about a CoAP PDU.
689  * Note: Adding a byte may be insufficient to output the next byte of the PDU.
690  *
691  * This is done by the adding of a -DCOAP_DEBUG_BUF_SIZE=nnnn option to the
692  * CPPFLAGS parameter that is optionally used on the ./configure command line.
693  *
694  * E.g.  ./configure CPPFLAGS="-DCOAP_DEBUG_BUF_SIZE=4096"
695  *
696  */
697 
698 #if COAP_DEBUG_BUF_SIZE < 5
699 #error "COAP_DEBUG_BUF_SIZE must be at least 5, should be >= 32 to be useful"
700 #endif /* COAP_DEBUG_BUF_SIZE < 5 */
701 
702 void
coap_show_pdu(coap_log_t level,const coap_pdu_t * pdu)703 coap_show_pdu(coap_log_t level, const coap_pdu_t *pdu) {
704 #if COAP_CONSTRAINED_STACK
705   /* Proxy-Uri: can be 1034 bytes long */
706   /* buf and outbuf protected by mutex m_show_pdu */
707   static unsigned char buf[min(COAP_DEBUG_BUF_SIZE, 1035)];
708   static char outbuf[COAP_DEBUG_BUF_SIZE];
709 #else /* ! COAP_CONSTRAINED_STACK */
710   /* Proxy-Uri: can be 1034 bytes long */
711   unsigned char buf[min(COAP_DEBUG_BUF_SIZE, 1035)];
712   char outbuf[COAP_DEBUG_BUF_SIZE];
713 #endif /* ! COAP_CONSTRAINED_STACK */
714   size_t buf_len = 0; /* takes the number of bytes written to buf */
715   int encode = 0, have_options = 0;
716   uint32_t i;
717   coap_opt_iterator_t opt_iter;
718   coap_opt_t *option;
719   int content_format = -1;
720   size_t data_len;
721   const uint8_t *data;
722   uint32_t opt_len;
723   const uint8_t *opt_val;
724   size_t outbuflen = 0;
725   int is_oscore_payload = 0;
726 
727   /* Save time if not needed */
728   if (level > coap_get_log_level())
729     return;
730 
731 #if COAP_CONSTRAINED_STACK
732   coap_mutex_lock(&m_show_pdu);
733 #endif /* COAP_CONSTRAINED_STACK */
734 
735   if (!pdu->session || COAP_PROTO_NOT_RELIABLE(pdu->session->proto)) {
736     snprintf(outbuf, sizeof(outbuf), "v:%d t:%s c:%s i:%04x {",
737              COAP_DEFAULT_VERSION, msg_type_string(pdu->type),
738              msg_code_string(pdu->code), pdu->mid);
739   } else if (pdu->session->proto == COAP_PROTO_WS ||
740              pdu->session->proto == COAP_PROTO_WSS) {
741     if (pdu->type != COAP_MESSAGE_CON)
742       coap_log_alert("WebSocket: type != CON\n");
743     snprintf(outbuf, sizeof(outbuf), "v:WebSocket c:%s {",
744              msg_code_string(pdu->code));
745   } else {
746     if (pdu->type != COAP_MESSAGE_CON)
747       coap_log_alert("Reliable: type != CON\n");
748     snprintf(outbuf, sizeof(outbuf), "v:Reliable c:%s {",
749              msg_code_string(pdu->code));
750   }
751 
752   for (i = 0; i < pdu->actual_token.length; i++) {
753     outbuflen = strlen(outbuf);
754     snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen,
755              "%02x", pdu->actual_token.s[i]);
756   }
757   outbuflen = strlen(outbuf);
758   snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen,  "}");
759 
760   /* show options, if any */
761   coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL);
762 
763   outbuflen = strlen(outbuf);
764   snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen,  " [");
765   while ((option = coap_option_next(&opt_iter))) {
766     buf[0] = '\000';
767     if (!have_options) {
768       have_options = 1;
769     } else {
770       outbuflen = strlen(outbuf);
771       snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen,  ",");
772     }
773 
774     if (pdu->code == COAP_SIGNALING_CODE_CSM) {
775       switch (opt_iter.number) {
776       case COAP_SIGNALING_OPTION_EXTENDED_TOKEN_LENGTH:
777       case COAP_SIGNALING_OPTION_MAX_MESSAGE_SIZE:
778         buf_len = snprintf((char *)buf, sizeof(buf), "%u",
779                            coap_decode_var_bytes(coap_opt_value(option),
780                                                  coap_opt_length(option)));
781         break;
782       default:
783         buf_len = 0;
784         break;
785       }
786     } else if (pdu->code == COAP_SIGNALING_CODE_PING ||
787                pdu->code == COAP_SIGNALING_CODE_PONG) {
788       buf_len = 0;
789     } else if (pdu->code == COAP_SIGNALING_CODE_RELEASE) {
790       switch (opt_iter.number) {
791       case COAP_SIGNALING_OPTION_ALTERNATIVE_ADDRESS:
792         buf_len = print_readable(coap_opt_value(option),
793                                  coap_opt_length(option),
794                                  buf, sizeof(buf), 0);
795         break;
796       case COAP_SIGNALING_OPTION_HOLD_OFF:
797         buf_len = snprintf((char *)buf, sizeof(buf), "%u",
798                            coap_decode_var_bytes(coap_opt_value(option),
799                                                  coap_opt_length(option)));
800         break;
801       default:
802         buf_len = 0;
803         break;
804       }
805     } else if (pdu->code == COAP_SIGNALING_CODE_ABORT) {
806       switch (opt_iter.number) {
807       case COAP_SIGNALING_OPTION_BAD_CSM_OPTION:
808         buf_len = snprintf((char *)buf, sizeof(buf), "%u",
809                            coap_decode_var_bytes(coap_opt_value(option),
810                                                  coap_opt_length(option)));
811         break;
812       default:
813         buf_len = 0;
814         break;
815       }
816     } else {
817       switch (opt_iter.number) {
818       case COAP_OPTION_CONTENT_FORMAT:
819       case COAP_OPTION_ACCEPT:
820         content_format = (int)coap_decode_var_bytes(coap_opt_value(option),
821                                                     coap_opt_length(option));
822 
823         buf_len = print_content_format(content_format, buf, sizeof(buf));
824         break;
825 
826       case COAP_OPTION_BLOCK1:
827       case COAP_OPTION_BLOCK2:
828       case COAP_OPTION_Q_BLOCK1:
829       case COAP_OPTION_Q_BLOCK2:
830         /* split block option into number/more/size where more is the
831          * letter M if set, the _ otherwise */
832         if (COAP_OPT_BLOCK_SZX(option) == 7) {
833           if (coap_get_data(pdu, &data_len, &data))
834             buf_len = snprintf((char *)buf, sizeof(buf), "%u/%c/BERT(%zu)",
835                                coap_opt_block_num(option), /* block number */
836                                COAP_OPT_BLOCK_MORE(option) ? 'M' : '_', /* M bit */
837                                data_len);
838           else
839             buf_len = snprintf((char *)buf, sizeof(buf), "%u/%c/BERT",
840                                coap_opt_block_num(option), /* block number */
841                                COAP_OPT_BLOCK_MORE(option) ? 'M' : '_'); /* M bit */
842         } else {
843           buf_len = snprintf((char *)buf, sizeof(buf), "%u/%c/%u",
844                              coap_opt_block_num(option), /* block number */
845                              COAP_OPT_BLOCK_MORE(option) ? 'M' : '_', /* M bit */
846                              (1 << (COAP_OPT_BLOCK_SZX(option) + 4))); /* block size */
847         }
848 
849         break;
850 
851       case COAP_OPTION_OSCORE:
852         opt_len = coap_opt_length(option);
853         buf[0] = '\000';
854         if (opt_len) {
855           size_t ofs = 1;
856           size_t cnt;
857 
858           opt_val = coap_opt_value(option);
859           if (opt_val[0] & 0x20) {
860             /* Group Flag */
861             snprintf((char *)buf, sizeof(buf), "grp");
862           }
863           if (opt_val[0] & 0x07) {
864             /* Partial IV */
865             cnt = opt_val[0] & 0x07;
866             if (cnt > opt_len - ofs)
867               goto no_more;
868             buf_len = strlen((char *)buf);
869             snprintf((char *)&buf[buf_len], sizeof(buf)-buf_len, "%spIV=0x",
870                      buf_len ? "," : "");
871             for (i = 0; (uint32_t)i < cnt; i++) {
872               buf_len = strlen((char *)buf);
873               snprintf((char *)&buf[buf_len], sizeof(buf)-buf_len,
874                        "%02x", opt_val[ofs + i]);
875             }
876             ofs += cnt;
877           }
878           if (opt_val[0] & 0x10) {
879             /* kid context */
880             if (ofs >= opt_len)
881               goto no_more;
882             cnt = opt_val[ofs];
883             if (cnt > opt_len - ofs - 1)
884               goto no_more;
885             ofs++;
886             buf_len = strlen((char *)buf);
887             snprintf((char *)&buf[buf_len], sizeof(buf)-buf_len, "%skc=0x",
888                      buf_len ? "," : "");
889             for (i = 0; (uint32_t)i < cnt; i++) {
890               buf_len = strlen((char *)buf);
891               snprintf((char *)&buf[buf_len], sizeof(buf)-buf_len,
892                        "%02x", opt_val[ofs + i]);
893             }
894             ofs += cnt;
895           }
896           if (opt_val[0] & 0x08) {
897             /* kid */
898             if (ofs >= opt_len)
899               goto no_more;
900             cnt = opt_len - ofs;
901             buf_len = strlen((char *)buf);
902             snprintf((char *)&buf[buf_len], sizeof(buf)-buf_len, "%skid=0x",
903                      buf_len ? "," : "");
904             for (i = 0; (uint32_t)i < cnt; i++) {
905               buf_len = strlen((char *)buf);
906               snprintf((char *)&buf[buf_len], sizeof(buf)-buf_len,
907                        "%02x", opt_val[ofs + i]);
908             }
909           }
910         }
911 no_more:
912         buf_len = strlen((char *)buf);
913         is_oscore_payload = 1;
914         break;
915 
916       case COAP_OPTION_URI_PORT:
917       case COAP_OPTION_MAXAGE:
918       case COAP_OPTION_OBSERVE:
919       case COAP_OPTION_SIZE1:
920       case COAP_OPTION_SIZE2:
921       case COAP_OPTION_HOP_LIMIT:
922         if (coap_opt_length(option)) {
923           /* show values as unsigned decimal value */
924           buf_len = snprintf((char *)buf, sizeof(buf), "%u",
925                              coap_decode_var_bytes(coap_opt_value(option),
926                                                    coap_opt_length(option)));
927         }
928         break;
929 
930       case COAP_OPTION_IF_MATCH:
931       case COAP_OPTION_ETAG:
932       case COAP_OPTION_ECHO:
933       case COAP_OPTION_NORESPONSE:
934       case COAP_OPTION_RTAG:
935         opt_len = coap_opt_length(option);
936         opt_val = coap_opt_value(option);
937         snprintf((char *)buf, sizeof(buf), "0x");
938         for (i = 0; (uint32_t)i < opt_len; i++) {
939           buf_len = strlen((char *)buf);
940           snprintf((char *)&buf[buf_len], sizeof(buf)-buf_len,
941                    "%02x", opt_val[i]);
942         }
943         buf_len = strlen((char *)buf);
944         break;
945       default:
946         /* generic output function for all other option types */
947         if (opt_iter.number == COAP_OPTION_URI_PATH ||
948             opt_iter.number == COAP_OPTION_PROXY_URI ||
949             opt_iter.number == COAP_OPTION_URI_HOST ||
950             opt_iter.number == COAP_OPTION_LOCATION_PATH ||
951             opt_iter.number == COAP_OPTION_LOCATION_QUERY ||
952             opt_iter.number == COAP_OPTION_PROXY_SCHEME ||
953             opt_iter.number == COAP_OPTION_URI_QUERY) {
954           encode = 0;
955         } else {
956           encode = 1;
957         }
958         buf_len = print_readable(coap_opt_value(option),
959                                  coap_opt_length(option),
960                                  buf, sizeof(buf), encode);
961       }
962     }
963     outbuflen = strlen(outbuf);
964     snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen,
965              " %s:%.*s", msg_option_string(pdu->code, opt_iter.number),
966              (int)buf_len, buf);
967   }
968 
969   outbuflen = strlen(outbuf);
970   snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen,  " ]");
971 
972   if (coap_get_data(pdu, &data_len, &data)) {
973 
974     outbuflen = strlen(outbuf);
975     snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen,  " :: ");
976 
977     if (is_binary(content_format) || !isprint(data[0]) || is_oscore_payload) {
978       size_t keep_data_len = data_len;
979       const uint8_t *keep_data = data;
980 
981       outbuflen = strlen(outbuf);
982       snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen,
983                "binary data length %zu\n", data_len);
984       COAP_DO_SHOW_OUTPUT_LINE;
985       /*
986        * Output hex dump of binary data as a continuous entry
987        */
988       outbuf[0] = '\000';
989       snprintf(outbuf, sizeof(outbuf),  "<<");
990       while (data_len--) {
991         outbuflen = strlen(outbuf);
992         snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen,
993                  "%02x", *data++);
994       }
995       outbuflen = strlen(outbuf);
996       snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen,  ">>");
997       data_len = keep_data_len;
998       data = keep_data;
999       outbuflen = strlen(outbuf);
1000       if (outbuflen == sizeof(outbuf)-1)
1001         outbuflen--;
1002       snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen,  "\n");
1003       COAP_DO_SHOW_OUTPUT_LINE;
1004       /*
1005        * Output ascii readable (if possible), immediately under the
1006        * hex value of the character output above to help binary debugging
1007        */
1008       outbuf[0] = '\000';
1009       snprintf(outbuf, sizeof(outbuf),  "<<");
1010       while (data_len--) {
1011         outbuflen = strlen(outbuf);
1012         snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen,
1013                  "%c ", isprint(*data) ? *data : '.');
1014         data++;
1015       }
1016       outbuflen = strlen(outbuf);
1017       snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen,  ">>");
1018     } else {
1019       size_t max_length;
1020 
1021       outbuflen = strlen(outbuf);
1022       max_length = sizeof(outbuf)-outbuflen;
1023       if (max_length > 1) {
1024         outbuf[outbuflen++] = '\'';
1025         outbuf[outbuflen] = '\000';
1026         max_length--;
1027       }
1028       if (max_length > 1) {
1029         outbuflen += print_readable(data, data_len,
1030                                     (unsigned char *)&outbuf[outbuflen],
1031                                     max_length, 0);
1032       }
1033       /* print_readable may be handling unprintables - hence headroom of 4 */
1034       if (outbuflen < sizeof(outbuf)-4-1) {
1035         outbuf[outbuflen++] = '\'';
1036         outbuf[outbuflen] = '\000';
1037       }
1038     }
1039   }
1040 
1041   outbuflen = strlen(outbuf);
1042   if (outbuflen == sizeof(outbuf)-1)
1043     outbuflen--;
1044   snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen,  "\n");
1045   COAP_DO_SHOW_OUTPUT_LINE;
1046 
1047 #if COAP_CONSTRAINED_STACK
1048   coap_mutex_unlock(&m_show_pdu);
1049 #endif /* COAP_CONSTRAINED_STACK */
1050 }
1051 
1052 void
coap_show_tls_version(coap_log_t level)1053 coap_show_tls_version(coap_log_t level) {
1054   char buffer[128];
1055   coap_string_tls_version(buffer, sizeof(buffer));
1056   coap_log(level, "%s\n", buffer);
1057 }
1058 
1059 char *
coap_string_tls_version(char * buffer,size_t bufsize)1060 coap_string_tls_version(char *buffer, size_t bufsize) {
1061   coap_tls_version_t *tls_version = coap_get_tls_library_version();
1062   char beta[8];
1063   char sub[2];
1064   char b_beta[8];
1065   char b_sub[2];
1066 
1067   switch (tls_version->type) {
1068   case COAP_TLS_LIBRARY_NOTLS:
1069     snprintf(buffer, bufsize, "TLS Library: None");
1070     break;
1071   case COAP_TLS_LIBRARY_TINYDTLS:
1072     snprintf(buffer, bufsize, "TLS Library: TinyDTLS - runtime %lu.%lu.%lu, "
1073              "libcoap built for %lu.%lu.%lu",
1074              (unsigned long)(tls_version->version >> 16),
1075              (unsigned long)((tls_version->version >> 8) & 0xff),
1076              (unsigned long)(tls_version->version & 0xff),
1077              (unsigned long)(tls_version->built_version >> 16),
1078              (unsigned long)((tls_version->built_version >> 8) & 0xff),
1079              (unsigned long)(tls_version->built_version & 0xff));
1080     break;
1081   case COAP_TLS_LIBRARY_OPENSSL:
1082     switch (tls_version->version &0xf) {
1083     case 0:
1084       strcpy(beta, "-dev");
1085       break;
1086     case 0xf:
1087       strcpy(beta, "");
1088       break;
1089     default:
1090       strcpy(beta, "-beta");
1091       beta[5] = (tls_version->version &0xf) + '0';
1092       beta[6] = '\000';
1093       break;
1094     }
1095     sub[0] = ((tls_version->version >> 4) & 0xff) ?
1096              ((tls_version->version >> 4) & 0xff) + 'a' -1 : '\000';
1097     sub[1] = '\000';
1098     switch (tls_version->built_version &0xf) {
1099     case 0:
1100       strcpy(b_beta, "-dev");
1101       break;
1102     case 0xf:
1103       strcpy(b_beta, "");
1104       break;
1105     default:
1106       strcpy(b_beta, "-beta");
1107       b_beta[5] = (tls_version->built_version &0xf) + '0';
1108       b_beta[6] = '\000';
1109       break;
1110     }
1111     b_sub[0] = ((tls_version->built_version >> 4) & 0xff) ?
1112                ((tls_version->built_version >> 4) & 0xff) + 'a' -1 : '\000';
1113     b_sub[1] = '\000';
1114     snprintf(buffer, bufsize, "TLS Library: OpenSSL - runtime "
1115              "%lu.%lu.%lu%s%s, libcoap built for %lu.%lu.%lu%s%s",
1116              (unsigned long)(tls_version->version >> 28),
1117              (unsigned long)((tls_version->version >> 20) & 0xff),
1118              (unsigned long)((tls_version->version >> 12) & 0xff), sub, beta,
1119              (unsigned long)(tls_version->built_version >> 28),
1120              (unsigned long)((tls_version->built_version >> 20) & 0xff),
1121              (unsigned long)((tls_version->built_version >> 12) & 0xff),
1122              b_sub, b_beta);
1123     break;
1124   case COAP_TLS_LIBRARY_GNUTLS:
1125     snprintf(buffer, bufsize, "TLS Library: GnuTLS - runtime %lu.%lu.%lu, "
1126              "libcoap built for %lu.%lu.%lu",
1127              (unsigned long)(tls_version->version >> 16),
1128              (unsigned long)((tls_version->version >> 8) & 0xff),
1129              (unsigned long)(tls_version->version & 0xff),
1130              (unsigned long)(tls_version->built_version >> 16),
1131              (unsigned long)((tls_version->built_version >> 8) & 0xff),
1132              (unsigned long)(tls_version->built_version & 0xff));
1133     break;
1134   case COAP_TLS_LIBRARY_MBEDTLS:
1135     snprintf(buffer, bufsize, "TLS Library: Mbed TLS - runtime %lu.%lu.%lu, "
1136              "libcoap built for %lu.%lu.%lu",
1137              (unsigned long)(tls_version->version >> 24),
1138              (unsigned long)((tls_version->version >> 16) & 0xff),
1139              (unsigned long)((tls_version->version >> 8) & 0xff),
1140              (unsigned long)(tls_version->built_version >> 24),
1141              (unsigned long)((tls_version->built_version >> 16) & 0xff),
1142              (unsigned long)((tls_version->built_version >> 8) & 0xff));
1143     break;
1144   default:
1145     snprintf(buffer, bufsize, "Library type %d unknown", tls_version->type);
1146     break;
1147   }
1148   return buffer;
1149 }
1150 
1151 char *
coap_string_tls_support(char * buffer,size_t bufsize)1152 coap_string_tls_support(char *buffer, size_t bufsize) {
1153   const int have_tls = coap_tls_is_supported();
1154   const int have_dtls = coap_dtls_is_supported();
1155   const int have_psk = coap_dtls_psk_is_supported();
1156   const int have_pki = coap_dtls_pki_is_supported();
1157   const int have_pkcs11 = coap_dtls_pkcs11_is_supported();
1158   const int have_rpk = coap_dtls_rpk_is_supported();
1159   const int have_oscore = coap_oscore_is_supported();
1160   const int have_ws = coap_ws_is_supported();
1161 
1162   if (have_dtls == 0 && have_tls == 0) {
1163     snprintf(buffer, bufsize, "(No DTLS or TLS support)");
1164     return buffer;
1165   }
1166   snprintf(buffer, bufsize,
1167            "(%sDTLS and %sTLS support; %sPSK, %sPKI, %sPKCS11, and %sRPK support)\n(%sOSCORE)\n(%sWebSockets)",
1168            have_dtls ? "" : "No ",
1169            have_tls ? "" : "no ",
1170            have_psk ? "" : "no ",
1171            have_pki ? "" : "no ",
1172            have_pkcs11 ? "" : "no ",
1173            have_rpk ? "" : "no ",
1174            have_oscore ? "Have " : "No ",
1175            have_ws ? "Have " : "No ");
1176   return buffer;
1177 }
1178 
1179 static coap_log_handler_t log_handler = NULL;
1180 
1181 void
coap_set_log_handler(coap_log_handler_t handler)1182 coap_set_log_handler(coap_log_handler_t handler) {
1183   log_handler = handler;
1184 }
1185 
1186 void
coap_log_impl(coap_log_t level,const char * format,...)1187 coap_log_impl(coap_log_t level, const char *format, ...) {
1188 
1189   if (log_handler) {
1190 #if COAP_CONSTRAINED_STACK
1191     /* message protected by mutex m_log_impl */
1192     static char message[COAP_DEBUG_BUF_SIZE];
1193 #else /* ! COAP_CONSTRAINED_STACK */
1194     char message[COAP_DEBUG_BUF_SIZE];
1195 #endif /* ! COAP_CONSTRAINED_STACK */
1196     va_list ap;
1197     va_start(ap, format);
1198 #if COAP_CONSTRAINED_STACK
1199     coap_mutex_lock(&m_log_impl);
1200 #endif /* COAP_CONSTRAINED_STACK */
1201 
1202     vsnprintf(message, sizeof(message), format, ap);
1203     va_end(ap);
1204     log_handler(level, message);
1205 #if COAP_CONSTRAINED_STACK
1206     coap_mutex_unlock(&m_log_impl);
1207 #endif /* COAP_CONSTRAINED_STACK */
1208   } else {
1209     char timebuf[32];
1210     coap_tick_t now;
1211     va_list ap;
1212     FILE *log_fd;
1213     size_t len;
1214 
1215     log_fd = level <= COAP_LOG_CRIT ? COAP_ERR_FD : COAP_DEBUG_FD;
1216 
1217     coap_ticks(&now);
1218     len = print_timestamp(timebuf,sizeof(timebuf), now);
1219     if (len)
1220       fprintf(log_fd, "%.*s ", (int)len, timebuf);
1221 
1222     if (level >= sizeof(loglevels)/sizeof(loglevels[0])) {
1223       fprintf(log_fd, "%4d ", level);
1224     } else {
1225       fprintf(log_fd, "%s ", loglevels[level]);
1226     }
1227 
1228     va_start(ap, format);
1229     vfprintf(log_fd, format, ap);
1230     va_end(ap);
1231     fflush(log_fd);
1232   }
1233 }
1234 
1235 static struct packet_num_interval {
1236   int start;
1237   int end;
1238 } packet_loss_intervals[10];
1239 static int num_packet_loss_intervals = 0;
1240 static int packet_loss_level = 0;
1241 static int send_packet_count = 0;
1242 
1243 int
coap_debug_set_packet_loss(const char * loss_level)1244 coap_debug_set_packet_loss(const char *loss_level) {
1245   const char *p = loss_level;
1246   char *end = NULL;
1247   int n = (int)strtol(p, &end, 10), i = 0;
1248   if (end == p || n < 0)
1249     return 0;
1250   if (*end == '%') {
1251     if (n > 100)
1252       n = 100;
1253     packet_loss_level = n * 65536 / 100;
1254     coap_log_debug("packet loss level set to %d%%\n", n);
1255   } else {
1256     if (n <= 0)
1257       return 0;
1258     while (i < 10) {
1259       packet_loss_intervals[i].start = n;
1260       if (*end == '-') {
1261         p = end + 1;
1262         n = (int)strtol(p, &end, 10);
1263         if (end == p || n <= 0)
1264           return 0;
1265       }
1266       packet_loss_intervals[i++].end = n;
1267       if (*end == 0)
1268         break;
1269       if (*end != ',')
1270         return 0;
1271       p = end + 1;
1272       n = (int)strtol(p, &end, 10);
1273       if (end == p || n <= 0)
1274         return 0;
1275     }
1276     if (i == 10)
1277       return 0;
1278     num_packet_loss_intervals = i;
1279   }
1280   send_packet_count = 0;
1281   return 1;
1282 }
1283 
1284 int
coap_debug_send_packet(void)1285 coap_debug_send_packet(void) {
1286   ++send_packet_count;
1287   if (num_packet_loss_intervals > 0) {
1288     int i;
1289     for (i = 0; i < num_packet_loss_intervals; i++) {
1290       if (send_packet_count >= packet_loss_intervals[i].start &&
1291           send_packet_count <= packet_loss_intervals[i].end) {
1292         coap_log_debug("Packet %u dropped\n", send_packet_count);
1293         return 0;
1294       }
1295     }
1296   }
1297   if (packet_loss_level > 0) {
1298     uint16_t r = 0;
1299     coap_prng((uint8_t *)&r, 2);
1300     if (r < packet_loss_level) {
1301       coap_log_debug("Packet %u dropped\n", send_packet_count);
1302       return 0;
1303     }
1304   }
1305   return 1;
1306 }
1307 
1308 void
coap_debug_reset(void)1309 coap_debug_reset(void) {
1310   maxlog = COAP_LOG_WARN;
1311   use_fprintf_for_show_pdu = 1;
1312   num_packet_loss_intervals = 0;
1313   packet_loss_level = 0;
1314   send_packet_count = 0;
1315 }
1316