/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */ /* * Utility functions */ #include #include #include #include #include #ifndef WIN32 #include #include #else #include #endif #include #include #if !(defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0) && defined(__APPLE__) #include #endif #include "lsquic_int_types.h" #include "lsquic_util.h" #if LSQUIC_COUNT_TIME_CALLS #include #include "lsquic_types.h" #include "lsquic_logger.h" #endif #if defined(__APPLE__) static mach_timebase_info_data_t timebase; #endif #if defined(WIN32) static LARGE_INTEGER perf_frequency; #endif #if LSQUIC_COUNT_TIME_CALLS static volatile unsigned long n_time_now_calls; static void print_call_stats (void) { LSQ_NOTICE("number of lsquic_time_now() calls: %lu", n_time_now_calls); } #endif void lsquic_init_timers (void) { #if LSQUIC_COUNT_TIME_CALLS atexit(print_call_stats); #endif #if defined(__APPLE__) mach_timebase_info(&timebase); #endif #if defined(WIN32) QueryPerformanceFrequency(&perf_frequency); #endif } lsquic_time_t lsquic_time_now (void) { #if LSQUIC_COUNT_TIME_CALLS ++n_time_now_calls; #endif #if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 struct timespec ts; (void) clock_gettime(CLOCK_MONOTONIC, &ts); return (lsquic_time_t) ts.tv_sec * 1000000 + ts.tv_nsec / 1000; #elif defined(__APPLE__) lsquic_time_t t = mach_absolute_time(); t *= timebase.numer; t /= timebase.denom; t /= 1000; return t; #elif defined(WIN32) LARGE_INTEGER counter; lsquic_time_t t; QueryPerformanceCounter(&counter); t = counter.QuadPart; t *= 1000000; t /= perf_frequency.QuadPart; return t; #else # warn Monotonically increasing clock is not available on this platform struct timeval tv; (void) gettimeofday(&tv, NULL); return (lsquic_time_t) tv.tv_sec * 1000000 + tv.tv_usec; #endif } int lsquic_is_zero (const void *pbuf, size_t bufsz) { const unsigned char *buf, *end; const unsigned long *buf_ul; unsigned n_ul; unsigned long n_non_zero; buf = pbuf; end = buf + bufsz; buf_ul = (unsigned long *) buf; n_ul = bufsz / sizeof(buf_ul[0]); buf += n_ul * sizeof(buf_ul[0]); n_non_zero = 0; while (n_ul--) n_non_zero |= buf_ul[n_ul]; while (buf < end) n_non_zero |= *buf++; return n_non_zero == 0; } /* XXX this function uses static buffer. Replace it with lsquic_hexdump() if possible */ char *get_bin_str(const void *s, size_t len, size_t max_display_len) { const unsigned char *p, *pEnd; char *pOutput; size_t lenOrg = len; static char str[512 * 2 + 40] = {0}; /** * We alloc fixed size buffer, at most max_display_len is 512 */ size_t fit_display_len = (max_display_len > 512 ? 512 : max_display_len); if (len > fit_display_len) len = fit_display_len; pOutput = &str[0] + sprintf(str, "(%zd/%zd)=0x", len, lenOrg); for(p = s, pEnd = (unsigned char*)s + len; p < pEnd; ++p) { sprintf(pOutput, "%02X", *p); pOutput += 2; } if (lenOrg > len) { sprintf(pOutput, "..."); pOutput += 3; } return str; } static char hex_digit(uint8_t n) { return (n < 10) ? (n + '0') : ((n - 10) + 'a'); } size_t lsquic_hex_encode (const void *src, size_t src_sz, void *dst, size_t dst_sz) { size_t src_cur, dst_cur; const uint8_t *src_hex; char *dst_char; src_hex = (const uint8_t *)src; dst_char = (char *)dst; src_cur = dst_cur = 0; while (src_cur < src_sz && dst_cur < (dst_sz - 2)) { dst_char[dst_cur++] = hex_digit((src_hex[src_cur] & 0xf0) >> 4); dst_char[dst_cur++] = hex_digit(src_hex[src_cur++] & 0x0f); } dst_char[dst_cur++] = '\0'; return dst_cur; } void lsquic_hexstr (const unsigned char *buf, size_t bufsz, char *out, size_t outsz) { static const char b2c[16] = "0123456789ABCDEF"; const unsigned char *const end_input = buf + bufsz; char *const end_output = out + outsz; while (buf < end_input && out + 2 < end_output) { *out++ = b2c[ *buf >> 4 ]; *out++ = b2c[ *buf & 0xF ]; ++buf; } if (buf < end_input) out[-1] = '!'; *out = '\0'; } size_t lsquic_hexdump (const void *src_void, size_t src_sz, char *out, size_t out_sz) { /* Ruler: * 6 31 57 73 | | | | 0000 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F |................| * */ #define LINE_SIZE (74 + 1 /* newline */) const unsigned char *src = src_void; const unsigned char *const src_end = src + src_sz; char *const out_end = out + out_sz; unsigned line = 0; while (src < src_end && out_end - out >= LINE_SIZE) { const unsigned char *limit = src + 16; if (limit > src_end) limit = src_end; unsigned hex_off = 6; unsigned alpha_off = 57; sprintf(out, "%03X0", line++); out[4] = ' '; out[5] = ' '; while (src < limit) { sprintf(out + hex_off, "%02X ", *src); sprintf(out + alpha_off, "%c", isprint(*src) ? *src : '.'); hex_off += 3; out[hex_off] = ' '; hex_off += 30 == hex_off; out[hex_off] = ' '; ++alpha_off; out[alpha_off] = ' '; ++src; } memset(out + hex_off, ' ', 56 - hex_off); memset(out + alpha_off, '.', 73 - alpha_off); out[56] = '|'; out[73] = '|'; out[74] = '\n'; out += LINE_SIZE; } if (out < out_end) *out = '\0'; else out_end[-1] = '\0'; return out + out_sz - out_end; } /* Returns true if socket addresses are equal, false otherwise. Only * families, IP addresses, and ports are compared. */ int lsquic_sockaddr_eq (const struct sockaddr *a, const struct sockaddr *b) { if (a->sa_family == AF_INET) return a->sa_family == b->sa_family && ((struct sockaddr_in *) a)->sin_addr.s_addr == ((struct sockaddr_in *) b)->sin_addr.s_addr && ((struct sockaddr_in *) a)->sin_port == ((struct sockaddr_in *) b)->sin_port; else return a->sa_family == b->sa_family && ((struct sockaddr_in6 *) a)->sin6_port == ((struct sockaddr_in6 *) b)->sin6_port && 0 == memcmp(&((struct sockaddr_in6 *) a)->sin6_addr, &((struct sockaddr_in6 *) b)->sin6_addr, sizeof(((struct sockaddr_in6 *) b)->sin6_addr)); } void lsquic_sockaddr2str (const struct sockaddr *addr, char *buf, size_t sz) { unsigned short port; int len; switch (addr->sa_family) { case AF_INET: port = ntohs(((struct sockaddr_in *) addr)->sin_port); if (!inet_ntop(AF_INET, &((struct sockaddr_in *) addr)->sin_addr, buf, sz)) buf[0] = '\0'; break; case AF_INET6: port = ntohs(((struct sockaddr_in6 *) addr)->sin6_port); if (!inet_ntop(AF_INET6, &((struct sockaddr_in6 *) addr)->sin6_addr, buf, sz)) buf[0] = '\0'; break; default: port = 0; (void) snprintf(buf, sz, "", addr->sa_family); break; } len = strlen(buf); if (len < (int) sz) snprintf(buf + len, sz - (size_t) len, ":%hu", port); }