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
coap_package_name(void)51 const char *coap_package_name(void) {
52 return PACKAGE_NAME;
53 }
54
coap_package_version(void)55 const char *coap_package_version(void) {
56 return PACKAGE_STRING;
57 }
58
59 coap_log_t
coap_get_log_level(void)60 coap_get_log_level(void) {
61 return maxlog;
62 }
63
64 void
coap_set_log_level(coap_log_t level)65 coap_set_log_level(coap_log_t level) {
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
print_timestamp(char * s,size_t len,coap_tick_t 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
print_timestamp(char * s,size_t len,coap_tick_t 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 /**
104 * A length-safe strlen() fake.
105 *
106 * @param s The string to count characters != 0.
107 * @param maxlen The maximum length of @p s.
108 *
109 * @return The length of @p s.
110 */
111 static inline size_t
strnlen(const char * s,size_t maxlen)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
print_readable(const unsigned char * data,unsigned int len,unsigned char * result,unsigned int buflen,int encode_always)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
coap_print_addr(const struct coap_address_t * addr,unsigned char * buf,size_t len)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
262 /** Returns a textual description of the message type @p t. */
263 static const char *
msg_type_string(uint8_t t)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
270 /** Returns a textual description of the method or response code. */
271 static const char *
msg_code_string(uint8_t c)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
284 /** Returns a textual description of the option name. */
285 static const char *
msg_option_string(uint16_t option_type)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
print_content_format(unsigned int format_type,unsigned char * result,unsigned int buflen)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
360 /**
361 * Returns 1 if the given @p content_format is either unknown or known
362 * to carry binary data. The return value @c 0 hence indicates
363 * printable data which is also assumed if @p content_format is @c 01.
364 */
365 static inline int
is_binary(int content_format)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
coap_show_pdu(const coap_pdu_t * pdu)375 coap_show_pdu(const coap_pdu_t *pdu) {
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 */
395 coap_option_iterator_init((coap_pdu_t *)pdu, &opt_iter, COAP_OPT_ALL);
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) {
406 case COAP_OPTION_CONTENT_FORMAT:
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 (2 << (COAP_OPT_BLOCK_SZX(option) + 4))); /* block size */
421
422 break;
423
424 case COAP_OPTION_URI_PORT:
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",
430 coap_decode_var_bytes(COAP_OPT_VALUE(option),
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
coap_log_impl(coap_log_t level,const char * format,...)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
507