libcoap  4.1.2
debug.c
Go to the documentation of this file.
1 /* debug.c -- debug utilities
2  *
3  * Copyright (C) 2010--2012,2014--2015 Olaf Bergmann <bergmann@tzi.org>
4  *
5  * This file is part of the CoAP library libcoap. Please see
6  * README for terms of use.
7  */
8 
9 #include "coap_config.h"
10 
11 #if defined(HAVE_STRNLEN) && defined(__GNUC__) && !defined(_GNU_SOURCE)
12 #define _GNU_SOURCE 1
13 #endif
14 
15 #if defined(HAVE_ASSERT_H) && !defined(assert)
16 # include <assert.h>
17 #endif
18 
19 #include <stdarg.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <ctype.h>
23 
24 #ifdef HAVE_ARPA_INET_H
25 #include <arpa/inet.h>
26 #endif
27 
28 #ifdef HAVE_TIME_H
29 #include <time.h>
30 #endif
31 
32 #include "block.h"
33 #include "debug.h"
34 #include "encode.h"
35 #include "net.h"
36 
37 #ifdef WITH_LWIP
38 # define fprintf(fd, ...) LWIP_PLATFORM_DIAG((__VA_ARGS__))
39 # define fflush(...)
40 #endif
41 
42 #ifdef WITH_CONTIKI
43 # ifndef DEBUG
44 # define DEBUG DEBUG_PRINT
45 # endif /* DEBUG */
46 #include "net/ip/uip-debug.h"
47 #endif
48 
49 static coap_log_t maxlog = LOG_WARNING; /* default maximum log level */
50 
51 const char *coap_package_name(void) {
52  return PACKAGE_NAME;
53 }
54 
55 const char *coap_package_version(void) {
56  return PACKAGE_STRING;
57 }
58 
61  return maxlog;
62 }
63 
64 void
66  maxlog = level;
67 }
68 
69 /* this array has the same order as the type log_t */
70 static char *loglevels[] = {
71  "EMRG", "ALRT", "CRIT", "ERR", "WARN", "NOTE", "INFO", "DEBG"
72 };
73 
74 #ifdef HAVE_TIME_H
75 
76 static inline size_t
77 print_timestamp(char *s, size_t len, coap_tick_t t) {
78  struct tm *tmp;
79  time_t now = coap_ticks_to_rt(t);
80  tmp = localtime(&now);
81  return strftime(s, len, "%b %d %H:%M:%S", tmp);
82 }
83 
84 #else /* alternative implementation: just print the timestamp */
85 
86 static inline size_t
87 print_timestamp(char *s, size_t len, coap_tick_t t) {
88 #ifdef HAVE_SNPRINTF
89  return snprintf(s, len, "%u.%03u",
90  (unsigned int)coap_ticks_to_rt(t),
91  (unsigned int)(t % COAP_TICKS_PER_SECOND));
92 #else /* HAVE_SNPRINTF */
93  /* @todo do manual conversion of timestamp */
94  return 0;
95 #endif /* HAVE_SNPRINTF */
96 }
97 
98 #endif /* HAVE_TIME_H */
99 
100 #ifndef NDEBUG
101 
102 #ifndef HAVE_STRNLEN
103 
111 static inline size_t
112 strnlen(const char *s, size_t maxlen) {
113  size_t n = 0;
114  while(*s++ && n < maxlen)
115  ++n;
116  return n;
117 }
118 #endif /* HAVE_STRNLEN */
119 
120 static unsigned int
121 print_readable( const unsigned char *data, unsigned int len,
122  unsigned char *result, unsigned int buflen, int encode_always ) {
123  const unsigned char hex[] = "0123456789ABCDEF";
124  unsigned int cnt = 0;
125  assert(data || len == 0);
126 
127  if (buflen == 0) { /* there is nothing we can do here but return */
128  return 0;
129  }
130 
131  while (len) {
132  if (!encode_always && isprint(*data)) {
133  if (cnt+1 < buflen) { /* keep one byte for terminating zero */
134  *result++ = *data;
135  ++cnt;
136  } else {
137  break;
138  }
139  } else {
140  if (cnt+4 < buflen) { /* keep one byte for terminating zero */
141  *result++ = '\\';
142  *result++ = 'x';
143  *result++ = hex[(*data & 0xf0) >> 4];
144  *result++ = hex[*data & 0x0f];
145  cnt += 4;
146  } else
147  break;
148  }
149 
150  ++data; --len;
151  }
152 
153  *result = '\0'; /* add a terminating zero */
154  return cnt;
155 }
156 
157 #ifndef min
158 #define min(a,b) ((a) < (b) ? (a) : (b))
159 #endif
160 
161 size_t
162 coap_print_addr(const struct coap_address_t *addr, unsigned char *buf, size_t len) {
163 #ifdef HAVE_ARPA_INET_H
164  const void *addrptr = NULL;
165  in_port_t port;
166  unsigned char *p = 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  break;
173  case AF_INET6:
174  if (len < 7) /* do not proceed if buffer is even too short for [::]:0 */
175  return 0;
176 
177  *p++ = '[';
178 
179  addrptr = &addr->addr.sin6.sin6_addr;
180  port = ntohs(addr->addr.sin6.sin6_port);
181 
182  break;
183  default:
184  memcpy(buf, "(unknown address type)", min(22, len));
185  return min(22, len);
186  }
187 
188  if (inet_ntop(addr->addr.sa.sa_family, addrptr, (char *)p, len) == 0) {
189  perror("coap_print_addr");
190  return 0;
191  }
192 
193  p += strnlen((char *)p, len);
194 
195  if (addr->addr.sa.sa_family == AF_INET6) {
196  if (p < buf + len) {
197  *p++ = ']';
198  } else
199  return 0;
200  }
201 
202  p += snprintf((char *)p, buf + len - p + 1, ":%d", port);
203 
204  return buf + len - p;
205 #else /* HAVE_ARPA_INET_H */
206 # if WITH_CONTIKI
207  unsigned char *p = buf;
208  uint8_t i;
209 # if NETSTACK_CONF_WITH_IPV6
210  const unsigned char hex[] = "0123456789ABCDEF";
211 
212  if (len < 41)
213  return 0;
214 
215  *p++ = '[';
216 
217  for (i=0; i < 16; i += 2) {
218  if (i) {
219  *p++ = ':';
220  }
221  *p++ = hex[(addr->addr.u8[i] & 0xf0) >> 4];
222  *p++ = hex[(addr->addr.u8[i] & 0x0f)];
223  *p++ = hex[(addr->addr.u8[i+1] & 0xf0) >> 4];
224  *p++ = hex[(addr->addr.u8[i+1] & 0x0f)];
225  }
226  *p++ = ']';
227 # else /* WITH_UIP6 */
228 # warning "IPv4 network addresses will not be included in debug output"
229 
230  if (len < 21)
231  return 0;
232 # endif /* WITH_UIP6 */
233  if (buf + len - p < 6)
234  return 0;
235 
236 #ifdef HAVE_SNPRINTF
237  p += snprintf((char *)p, buf + len - p + 1, ":%d", uip_htons(addr->port));
238 #else /* HAVE_SNPRINTF */
239  /* @todo manual conversion of port number */
240 #endif /* HAVE_SNPRINTF */
241 
242  return p - buf;
243 # else /* WITH_CONTIKI */
244  /* TODO: output addresses manually */
245 # warning "inet_ntop() not available, network addresses will not be included in debug output"
246 # endif /* WITH_CONTIKI */
247  return 0;
248 #endif
249 }
250 
251 #ifdef WITH_CONTIKI
252 # define fprintf(fd, ...) PRINTF(__VA_ARGS__)
253 # define fflush(...)
254 
255 # ifdef HAVE_VPRINTF
256 # define vfprintf(fd, ...) vprintf(__VA_ARGS__)
257 # else /* HAVE_VPRINTF */
258 # define vfprintf(fd, ...) PRINTF(__VA_ARGS__)
259 # endif /* HAVE_VPRINTF */
260 #endif /* WITH_CONTIKI */
261 
263 static const char *
264 msg_type_string(uint8_t t) {
265  static char *types[] = { "CON", "NON", "ACK", "RST", "???" };
266 
267  return types[min(t, sizeof(types)/sizeof(char *) - 1)];
268 }
269 
271 static const char *
272 msg_code_string(uint8_t c) {
273  static char *methods[] = { "0.00", "GET", "POST", "PUT", "DELETE", "PATCH" };
274  static char buf[5];
275 
276  if (c < sizeof(methods)/sizeof(char *)) {
277  return methods[c];
278  } else {
279  snprintf(buf, sizeof(buf), "%u.%02u", c >> 5, c & 0x1f);
280  return buf;
281  }
282 }
283 
285 static const char *
286 msg_option_string(uint16_t option_type) {
287  struct option_desc_t {
288  uint16_t type;
289  const char *name;
290  };
291 
292  static struct option_desc_t options[] = {
293  { COAP_OPTION_IF_MATCH, "If-Match" },
294  { COAP_OPTION_URI_HOST, "Uri-Host" },
295  { COAP_OPTION_ETAG, "ETag" },
296  { COAP_OPTION_IF_NONE_MATCH, "If-None-Match" },
297  { COAP_OPTION_OBSERVE, "Observe" },
298  { COAP_OPTION_URI_PORT, "Uri-Port" },
299  { COAP_OPTION_LOCATION_PATH, "Location-Path" },
300  { COAP_OPTION_URI_PATH, "Uri-Path" },
301  { COAP_OPTION_CONTENT_FORMAT, "Content-Format" },
302  { COAP_OPTION_MAXAGE, "Max-Age" },
303  { COAP_OPTION_URI_QUERY, "Uri-Query" },
304  { COAP_OPTION_ACCEPT, "Accept" },
305  { COAP_OPTION_LOCATION_QUERY, "Location-Query" },
306  { COAP_OPTION_BLOCK2, "Block2" },
307  { COAP_OPTION_BLOCK1, "Block1" },
308  { COAP_OPTION_PROXY_URI, "Proxy-Uri" },
309  { COAP_OPTION_PROXY_SCHEME, "Proxy-Scheme" },
310  { COAP_OPTION_SIZE1, "Size1" },
311  { COAP_OPTION_NORESPONSE, "No-Response" }
312  };
313 
314  static char buf[6];
315  size_t i;
316 
317  /* search option_type in list of known options */
318  for (i = 0; i < sizeof(options)/sizeof(struct option_desc_t); i++) {
319  if (option_type == options[i].type) {
320  return options[i].name;
321  }
322  }
323 
324  /* unknown option type, just print to buf */
325  snprintf(buf, sizeof(buf), "%u", option_type);
326  return buf;
327 }
328 
329 static unsigned int
330 print_content_format(unsigned int format_type,
331  unsigned char *result, unsigned int buflen) {
332  struct desc_t {
333  unsigned int type;
334  const char *name;
335  };
336 
337  static struct desc_t formats[] = {
338  { COAP_MEDIATYPE_TEXT_PLAIN, "text/plain" },
339  { COAP_MEDIATYPE_APPLICATION_LINK_FORMAT, "application/link-format" },
340  { COAP_MEDIATYPE_APPLICATION_XML, "application/xml" },
341  { COAP_MEDIATYPE_APPLICATION_OCTET_STREAM, "application/octet-stream" },
342  { COAP_MEDIATYPE_APPLICATION_EXI, "application/exi" },
343  { COAP_MEDIATYPE_APPLICATION_JSON, "application/json" },
344  { COAP_MEDIATYPE_APPLICATION_CBOR, "application/cbor" }
345  };
346 
347  size_t i;
348 
349  /* search format_type in list of known content formats */
350  for (i = 0; i < sizeof(formats)/sizeof(struct desc_t); i++) {
351  if (format_type == formats[i].type) {
352  return snprintf((char *)result, buflen, "%s", formats[i].name);
353  }
354  }
355 
356  /* unknown content format, just print numeric value to buf */
357  return snprintf((char *)result, buflen, "%d", format_type);
358 }
359 
365 static inline int
366 is_binary(int content_format) {
367  return !(content_format == -1 ||
368  content_format == COAP_MEDIATYPE_TEXT_PLAIN ||
369  content_format == COAP_MEDIATYPE_APPLICATION_LINK_FORMAT ||
370  content_format == COAP_MEDIATYPE_APPLICATION_XML ||
371  content_format == COAP_MEDIATYPE_APPLICATION_JSON);
372 }
373 
374 void
376  unsigned char buf[COAP_MAX_PDU_SIZE]; /* need some space for output creation */
377  size_t buf_len = 0; /* takes the number of bytes written to buf */
378  int encode = 0, have_options = 0, i;
379  coap_opt_iterator_t opt_iter;
380  coap_opt_t *option;
381  int content_format = -1;
382  size_t data_len;
383  unsigned char *data;
384 
385  fprintf(COAP_DEBUG_FD, "v:%d t:%s c:%s i:%04x {",
386  pdu->hdr->version, msg_type_string(pdu->hdr->type),
387  msg_code_string(pdu->hdr->code), ntohs(pdu->hdr->id));
388 
389  for (i = 0; i < pdu->hdr->token_length; i++) {
390  fprintf(COAP_DEBUG_FD, "%02x", pdu->hdr->token[i]);
391  }
392  fprintf(COAP_DEBUG_FD, "}");
393 
394  /* show options, if any */
396 
397  fprintf(COAP_DEBUG_FD, " [");
398  while ((option = coap_option_next(&opt_iter))) {
399  if (!have_options) {
400  have_options = 1;
401  } else {
402  fprintf(COAP_DEBUG_FD, ",");
403  }
404 
405  switch (opt_iter.type) {
407  content_format = (int)coap_decode_var_bytes(COAP_OPT_VALUE(option),
408  COAP_OPT_LENGTH(option));
409 
410  buf_len = print_content_format(content_format, buf, sizeof(buf));
411  break;
412 
413  case COAP_OPTION_BLOCK1:
414  case COAP_OPTION_BLOCK2:
415  /* split block option into number/more/size where more is the
416  * letter M if set, the _ otherwise */
417  buf_len = snprintf((char *)buf, sizeof(buf), "%u/%c/%u",
418  coap_opt_block_num(option), /* block number */
419  COAP_OPT_BLOCK_MORE(option) ? 'M' : '_', /* M bit */
420  (1 << (COAP_OPT_BLOCK_SZX(option) + 4))); /* block size */
421 
422  break;
423 
425  case COAP_OPTION_MAXAGE:
426  case COAP_OPTION_OBSERVE:
427  case COAP_OPTION_SIZE1:
428  /* show values as unsigned decimal value */
429  buf_len = snprintf((char *)buf, sizeof(buf), "%u",
431  COAP_OPT_LENGTH(option)));
432  break;
433 
434  default:
435  /* generic output function for all other option types */
436  if (opt_iter.type == COAP_OPTION_URI_PATH ||
437  opt_iter.type == COAP_OPTION_PROXY_URI ||
438  opt_iter.type == COAP_OPTION_URI_HOST ||
439  opt_iter.type == COAP_OPTION_LOCATION_PATH ||
440  opt_iter.type == COAP_OPTION_LOCATION_QUERY ||
441  opt_iter.type == COAP_OPTION_URI_QUERY) {
442  encode = 0;
443  } else {
444  encode = 1;
445  }
446 
447  buf_len = print_readable(COAP_OPT_VALUE(option),
448  COAP_OPT_LENGTH(option),
449  buf, sizeof(buf), encode);
450  }
451 
452  fprintf(COAP_DEBUG_FD, " %s:%.*s", msg_option_string(opt_iter.type),
453  (int)buf_len, buf);
454  }
455 
456  fprintf(COAP_DEBUG_FD, " ]");
457 
458  if (coap_get_data((coap_pdu_t *)pdu, &data_len, &data)) {
459 
460  fprintf(COAP_DEBUG_FD, " :: ");
461 
462  if (is_binary(content_format)) {
463  fprintf(COAP_DEBUG_FD, "<<");
464  while (data_len--) {
465  fprintf(COAP_DEBUG_FD, "%02x", *data++);
466  }
467  fprintf(COAP_DEBUG_FD, ">>");
468  } else {
469  if (print_readable(data, data_len, buf, sizeof(buf), 0)) {
470  fprintf(COAP_DEBUG_FD, "'%s'", buf);
471  }
472  }
473  }
474 
475  fprintf(COAP_DEBUG_FD, "\n");
476  fflush(COAP_DEBUG_FD);
477 }
478 
479 
480 #endif /* NDEBUG */
481 
482 void
483 coap_log_impl(coap_log_t level, const char *format, ...) {
484  char timebuf[32];
485  coap_tick_t now;
486  va_list ap;
487  FILE *log_fd;
488 
489  if (maxlog < level)
490  return;
491 
492  log_fd = level <= LOG_CRIT ? COAP_ERR_FD : COAP_DEBUG_FD;
493 
494  coap_ticks(&now);
495  if (print_timestamp(timebuf,sizeof(timebuf), now))
496  fprintf(log_fd, "%s ", timebuf);
497 
498  if (level <= LOG_DEBUG)
499  fprintf(log_fd, "%s ", loglevels[level]);
500 
501  va_start(ap, format);
502  vfprintf(log_fd, format, ap);
503  va_end(ap);
504  fflush(log_fd);
505 }
506 
#define COAP_DEBUG_FD
Definition: debug.h:14
#define COAP_OPTION_IF_MATCH
Definition: pdu.h:57
unsigned int coap_opt_block_num(const coap_opt_t *block_opt)
Returns the value of field num in the given block option block_opt.
Definition: block.c:26
unsigned char token[]
Definition: pdu.h:192
#define COAP_ERR_FD
Definition: debug.h:18
#define COAP_OPTION_PROXY_URI
Definition: pdu.h:70
#define COAP_OPTION_CONTENT_FORMAT
Definition: pdu.h:64
struct sockaddr_in6 sin6
Definition: address.h:65
struct sockaddr_in sin
Definition: address.h:64
static char * loglevels[]
Definition: debug.c:70
void coap_show_pdu(const coap_pdu_t *pdu)
Definition: debug.c:375
#define COAP_OPTION_NORESPONSE
Definition: pdu.h:86
multi-purpose address abstraction
Definition: address.h:59
unsigned short id
Definition: pdu.h:191
static const char * msg_type_string(uint8_t t)
Returns a textual description of the message type t.
Definition: debug.c:264
#define COAP_OPT_ALL
Pre-defined filter that includes all options.
Definition: option.h:138
#define min(a, b)
Definition: debug.c:158
#define COAP_OPTION_OBSERVE
Definition: pdu.h:76
#define COAP_OPTION_ETAG
Definition: pdu.h:59
#define COAP_MEDIATYPE_APPLICATION_JSON
Definition: pdu.h:151
coap_time_t coap_ticks_to_rt(coap_tick_t t)
Helper function that converts coap ticks to wallclock time.
Definition: coap_time.c:82
#define COAP_OPTION_BLOCK1
Definition: pdu.h:82
int coap_get_data(coap_pdu_t *pdu, size_t *len, unsigned char **data)
Retrieves the length and data pointer of specified PDU.
Definition: pdu.c:257
#define COAP_OPTION_MAXAGE
Definition: pdu.h:66
coap_opt_t * coap_option_next(coap_opt_iterator_t *oi)
Updates the iterator oi to point to the next option.
Definition: option.c:159
static const char * msg_option_string(uint16_t option_type)
Returns a textual description of the option name.
Definition: debug.c:286
coap_hdr_t * hdr
Address of the first byte of the CoAP message.
Definition: pdu.h:229
unsigned long coap_tick_t
This data type represents internal timer ticks with COAP_TICKS_PER_SECOND resolution.
Definition: coap_time.h:84
#define COAP_OPTION_LOCATION_QUERY
Definition: pdu.h:69
#define COAP_OPTION_PROXY_SCHEME
Definition: pdu.h:71
#define COAP_MEDIATYPE_APPLICATION_EXI
Definition: pdu.h:150
size_t coap_print_addr(const struct coap_address_t *addr, unsigned char *buf, size_t len)
Definition: debug.c:162
#define COAP_TICKS_PER_SECOND
Use ms resolution on POSIX systems.
Definition: coap_time.h:99
unsigned int code
Definition: pdu.h:189
Header structure for CoAP PDUs.
Definition: pdu.h:227
coap_opt_iterator_t * coap_option_iterator_init(coap_pdu_t *pdu, coap_opt_iterator_t *oi, const coap_opt_filter_t filter)
Initializes the given option iterator oi to point to the beginning of the pdu&#39;s option list...
Definition: option.c:120
coap_log_t
Pre-defined log levels akin to what is used in syslog.
Definition: debug.h:26
const char * coap_package_version(void)
Returns a zero-terminated string with the library version.
Definition: debug.c:55
#define COAP_OPTION_URI_PORT
Definition: pdu.h:61
#define assert(...)
Definition: mem.c:17
static coap_log_t maxlog
Definition: debug.c:49
void coap_ticks(coap_tick_t *t)
Sets t to the internal time with COAP_TICKS_PER_SECOND resolution.
Definition: coap_time.c:49
#define COAP_OPTION_IF_NONE_MATCH
Definition: pdu.h:60
Iterator to run through PDU options.
Definition: option.h:253
#define COAP_MEDIATYPE_APPLICATION_LINK_FORMAT
Definition: pdu.h:146
static const char * msg_code_string(uint8_t c)
Returns a textual description of the method or response code.
Definition: debug.c:272
#define COAP_MAX_PDU_SIZE
Definition: pdu.h:27
#define COAP_OPTION_SIZE1
Definition: pdu.h:72
static size_t strnlen(const char *s, size_t maxlen)
A length-safe strlen() fake.
Definition: debug.c:112
Definition: debug.h:29
#define COAP_MEDIATYPE_APPLICATION_XML
Definition: pdu.h:147
unsigned short type
decoded option type
Definition: option.h:255
void coap_log_impl(coap_log_t level, const char *format,...)
Writes the given text to COAP_ERR_FD (for level <= LOG_CRIT) or COAP_DEBUG_FD (for level >= LOG_WARNI...
Definition: debug.c:483
#define COAP_OPTION_BLOCK2
Definition: pdu.h:81
#define COAP_OPTION_LOCATION_PATH
Definition: pdu.h:62
const char * coap_package_name(void)
Returns a zero-terminated string with the name of this library.
Definition: debug.c:51
#define COAP_OPT_LENGTH(opt)
Definition: option.h:392
unsigned int token_length
Definition: pdu.h:186
#define COAP_OPT_BLOCK_SZX(opt)
Returns the value of the SZX-field of a Block option opt.
Definition: block.h:52
static unsigned int print_readable(const unsigned char *data, unsigned int len, unsigned char *result, unsigned int buflen, int encode_always)
Definition: debug.c:121
#define COAP_OPTION_URI_PATH
Definition: pdu.h:63
#define COAP_MEDIATYPE_TEXT_PLAIN
Definition: pdu.h:145
unsigned int version
Definition: pdu.h:188
static size_t print_timestamp(char *s, size_t len, coap_tick_t t)
Definition: debug.c:87
void coap_set_log_level(coap_log_t level)
Sets the log level to the specified value.
Definition: debug.c:65
#define COAP_OPTION_URI_QUERY
Definition: pdu.h:67
unsigned int type
Definition: pdu.h:187
unsigned char coap_opt_t
Use byte-oriented access methods here because sliding a complex struct coap_opt_t over the data buffe...
Definition: option.h:25
union coap_address_t::@0 addr
#define COAP_OPTION_ACCEPT
Definition: pdu.h:68
#define COAP_MEDIATYPE_APPLICATION_CBOR
Definition: pdu.h:152
static int is_binary(int content_format)
Returns 1 if the given content_format is either unknown or known to carry binary data.
Definition: debug.c:366
#define COAP_OPT_BLOCK_MORE(opt)
Returns the value of the More-bit of a Block option opt.
Definition: block.h:48
coap_log_t coap_get_log_level(void)
Returns the current log level.
Definition: debug.c:60
#define COAP_MEDIATYPE_APPLICATION_OCTET_STREAM
Definition: pdu.h:148
unsigned int coap_decode_var_bytes(unsigned char *buf, unsigned int len)
Decodes multiple-length byte sequences.
Definition: encode.c:25
#define COAP_OPTION_URI_HOST
Definition: pdu.h:58
static unsigned int print_content_format(unsigned int format_type, unsigned char *result, unsigned int buflen)
Definition: debug.c:330
#define COAP_OPT_VALUE(opt)
Definition: option.h:406
struct sockaddr sa
Definition: address.h:62