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