/* debug.c -- debug utilities * * Copyright (C) 2010,2011 Olaf Bergmann * * This file is part of the CoAP library libcoap. Please see * README for terms of use. */ #ifndef NDEBUG #include "config.h" #include #include #include #ifdef HAVE_ARPA_INET_H #include #endif #ifdef HAVE_TIME_H #include #endif #include "debug.h" #include "net.h" #ifdef HAVE_TIME_H static inline size_t print_timestamp(char *s, size_t len, coap_tick_t t) { struct tm *tmp; time_t now = clock_offset + (t / COAP_TICKS_PER_SECOND); tmp = localtime(&now); return strftime(s, len, "%b %d %H:%M:%S", tmp); } #else /* alternative implementation: just print the timestamp */ static inline size_t print_timestamp(char *s, size_t len, coap_tick_t t) { return snprintf(s, len, "%u.%03u", clock_offset + (t / COAP_TICKS_PER_SECOND), t % COAP_TICKS_PER_SECOND); } #endif /* HAVE_TIME_H */ /** * A length-safe strlen() fake. * * @param s The string to count characters != 0. * @param maxlen The maximum length of @p s. * * @return The length of @p s. */ static inline size_t strnlen(unsigned char *s, size_t maxlen) { size_t n = 0; while(*s++ && n < maxlen) ++n; return n; } void debug(char *format, ...) { static char timebuf[32]; coap_tick_t now; va_list ap; coap_ticks(&now); if (print_timestamp(timebuf,sizeof(timebuf), now)) fprintf(COAP_DEBUG_FD, "%s ", timebuf); va_start(ap, format); vfprintf(COAP_DEBUG_FD, format, ap); va_end(ap); fflush(stdout); } unsigned int print_readable( const unsigned char *data, unsigned int len, unsigned char *result, unsigned int buflen, int encode_always ) { static const unsigned char hex[] = "0123456789ABCDEF"; unsigned int cnt = 0; while ( len && (cnt < buflen-1) ) { if ( !encode_always && isprint( *data ) ) { *result++ = *data; ++cnt; } else { if ( cnt+4 < buflen-1 ) { *result++ = '\\'; *result++ = 'x'; *result++ = hex[(*data & 0xf0) >> 4]; *result++ = hex[*data & 0x0f ]; cnt += 4; } else break; } ++data; --len; } *result = '\0'; return cnt; } #ifndef min #define min(a,b) ((a) < (b) ? (a) : (b)) #endif size_t coap_print_addr(const struct __coap_address_t *addr, unsigned char *buf, size_t len) { #ifdef HAVE_ARPA_INET_H const void *addrptr = NULL; in_port_t port; unsigned char *p = buf; switch (addr->addr.sa.sa_family) { case AF_INET: addrptr = &addr->addr.sin.sin_addr; port = ntohs(addr->addr.sin.sin_port); break; case AF_INET6: if (len < 7) /* do not proceed if buffer is even too short for [::]:0 */ return 0; *p++ = '['; addrptr = &addr->addr.sin6.sin6_addr; port = ntohs(addr->addr.sin6.sin6_port); break; default: memcpy(buf, "(unknown address type)", min(22, len)); return min(22, len); } if (inet_ntop(addr->addr.sa.sa_family, addrptr, (char *)p, len) == 0) { perror("coap_print_addr"); return 0; } p += strnlen(p, len); if (addr->addr.sa.sa_family == AF_INET6) { if (p < buf + len) { *p++ = ']'; } else return 0; } p += snprintf((char *)p, buf + len - p + 1, ":%d", port); return buf + len - p; #else /* TODO: output addresses manually */ #warn "inet_ntop() not available, network addresses will " "not be included in debug output" return 0; #endif } void coap_show_pdu(const coap_pdu_t *pdu) { unsigned char buf[COAP_MAX_PDU_SIZE]; /* need some space for output creation */ fprintf(COAP_DEBUG_FD, "v:%d t:%d oc:%d c:%d id:%u", pdu->hdr->version, pdu->hdr->type, pdu->hdr->optcnt, pdu->hdr->code, ntohs(pdu->hdr->id)); /* show options, if any */ if (pdu->hdr->optcnt) { coap_opt_iterator_t opt_iter; coap_option_iterator_init((coap_pdu_t *)pdu, &opt_iter, COAP_OPT_ALL); fprintf(COAP_DEBUG_FD, " o:"); while (coap_option_next(&opt_iter)) { if (print_readable(COAP_OPT_VALUE(opt_iter.option), COAP_OPT_LENGTH(opt_iter.option), buf, sizeof(buf), 0 )) fprintf(COAP_DEBUG_FD, " %d:%s", opt_iter.type, buf); } } if (pdu->data < (unsigned char *)pdu->hdr + pdu->length) { print_readable(pdu->data, (unsigned char *)pdu->hdr + pdu->length - pdu->data, buf, sizeof(buf), 0 ); fprintf(COAP_DEBUG_FD, " d:%s", buf); } fprintf(COAP_DEBUG_FD, "\n"); fflush(COAP_DEBUG_FD); } #endif /* NDEBUG */