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