405 lines
12 KiB
C
405 lines
12 KiB
C
#include <errno.h>
|
|
#include <stdarg.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include <sys/random.h>
|
|
|
|
#include "pdbg.h"
|
|
#include "pmem.h"
|
|
#include "putil.h"
|
|
|
|
static char normalize_table[256];
|
|
|
|
const char putil_base64_table[] = {
|
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
|
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
|
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
|
|
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
|
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'};
|
|
|
|
static const char base64_reverse_table[256] = {
|
|
-2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -2, -2, -1, -2, -2, -2, -2, -2,
|
|
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -2, -2, -2, -2, -2,
|
|
-2, -2, -2, -2, -2, 62, -2, 62, -2, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60,
|
|
61, -2, -2, -2, -1, -2, -2, -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
|
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2,
|
|
63, -2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
|
|
43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
|
|
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
|
|
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
|
|
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
|
|
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
|
|
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
|
|
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
|
|
-2, -2, -2, -2, -2, -2, -2, -2, -2};
|
|
|
|
__attribute((constructor)) static void init() {
|
|
unsigned long i;
|
|
for (i = 0; i < 256; i++) {
|
|
normalize_table[i] = i;
|
|
}
|
|
normalize_table[':'] = '_';
|
|
normalize_table['/'] = '_';
|
|
normalize_table['\\'] = '_';
|
|
}
|
|
|
|
// putil_wipe wipes a segment of memory mem of size sz using a DoD 5220.22-M
|
|
// compliant 3-pass wipe.
|
|
void putil_wipe(void *mem, size_t sz) {
|
|
if (!mem || sz == 0) { return; }
|
|
|
|
volatile unsigned char *p = (volatile unsigned char *)mem;
|
|
|
|
memset((void*)p, 0x00, sz);
|
|
memset((void*)p, 0xFF, sz);
|
|
|
|
ssize_t result = getrandom((void*)p, sz, 0);
|
|
if (result != (ssize_t)sz) {
|
|
if (result == -1) {
|
|
pdbg_logf(D_WARNING, "getrandom() failed: %s.", strerror(errno));
|
|
} else {
|
|
pdbg_logf(D_WARNING, "getrandom() returned partial data.");
|
|
}
|
|
|
|
pdbg_logf(D_WARNING, "falling back to less secure third pass. This may occur due to insufficient entropy, early boot state, or kernel incompatibility.");
|
|
srand((unsigned int)(time(NULL) ^ (uintptr_t)&srand));
|
|
for (size_t i = 0; i < sz; i++) {
|
|
p[i] = (unsigned char)rand();
|
|
}
|
|
}
|
|
}
|
|
|
|
void putil_time_format(time_t tm, unsigned long ns, char *result) {
|
|
static const char month_names[12][4] = {"Jan", "Feb", "Mar", "Apr",
|
|
"May", "Jun", "Jul", "Aug",
|
|
"Sep", "Oct", "Nov", "Dec"};
|
|
static const char day_names[7][4] = {"Sun", "Mon", "Tue", "Wed",
|
|
"Thu", "Fri", "Sat"};
|
|
struct tm dt;
|
|
unsigned long y;
|
|
ns /= 1000000;
|
|
gmtime_r(&tm, &dt);
|
|
memcpy(result, day_names[dt.tm_wday], 3);
|
|
result += 3;
|
|
*result++ = ',';
|
|
*result++ = ' ';
|
|
*result++ = dt.tm_mday / 10 + '0';
|
|
*result++ = dt.tm_mday % 10 + '0';
|
|
*result++ = ' ';
|
|
memcpy(result, month_names[dt.tm_mon], 3);
|
|
result += 3;
|
|
*result++ = ' ';
|
|
y = dt.tm_year + 1900;
|
|
*result++ = '0' + y / 1000;
|
|
y = y % 1000;
|
|
*result++ = '0' + y / 100;
|
|
y = y % 100;
|
|
*result++ = '0' + y / 10;
|
|
y = y % 10;
|
|
*result++ = '0' + y;
|
|
*result++ = ' ';
|
|
*result++ = dt.tm_hour / 10 + '0';
|
|
*result++ = dt.tm_hour % 10 + '0';
|
|
*result++ = ':';
|
|
*result++ = dt.tm_min / 10 + '0';
|
|
*result++ = dt.tm_min % 10 + '0';
|
|
*result++ = ':';
|
|
*result++ = dt.tm_sec / 10 + '0';
|
|
*result++ = dt.tm_sec % 10 + '0';
|
|
*result++ = '.';
|
|
*result++ = ns / 100 + '0';
|
|
*result++ = (ns / 10) % 10 + '0';
|
|
*result++ = ns % 10 + '0';
|
|
memcpy(result, " +0000", 7); // copies the null byte
|
|
}
|
|
|
|
char *putil_strdup(const char *str) {
|
|
size_t len;
|
|
char *ptr;
|
|
len = strlen(str) + 1;
|
|
ptr = (char *)pmem_malloc_array(PMEM_SUBSYS_OTHER, len, sizeof(char));
|
|
if (!ptr)
|
|
return NULL;
|
|
return (char *)memcpy(ptr, str, len);
|
|
}
|
|
|
|
char *putil_strnormalize_filename(const char *str) {
|
|
size_t len, i;
|
|
char *ptr;
|
|
len = strlen(str) + 1;
|
|
ptr = pmem_malloc_array(PMEM_SUBSYS_OTHER, len, sizeof(char));
|
|
for (i = 0; i < len; i++)
|
|
ptr[i] = normalize_table[(unsigned char)str[i]];
|
|
return ptr;
|
|
}
|
|
|
|
char *putil_strndup(const char *str, size_t len) {
|
|
char *ptr;
|
|
ptr = (char *)memcpy(pmem_malloc_array(PMEM_SUBSYS_OTHER, len + 1, sizeof(char)), str, len);
|
|
ptr[len] = 0;
|
|
return ptr;
|
|
}
|
|
|
|
char *putil_strcat(const char *str, ...) {
|
|
size_t i, size, len;
|
|
const char *strs[64];
|
|
size_t lengths[64];
|
|
const char *ptr;
|
|
char *ptr2, *ptr3;
|
|
va_list ap;
|
|
va_start(ap, str);
|
|
strs[0] = str;
|
|
len = strlen(str);
|
|
lengths[0] = len;
|
|
size = len + 1;
|
|
i = 1;
|
|
while ((ptr = va_arg(ap, const char *))) {
|
|
pdbg_assert(i < ARRAY_SIZE(strs));
|
|
len = strlen(ptr);
|
|
lengths[i] = len;
|
|
strs[i++] = ptr;
|
|
size += len;
|
|
}
|
|
va_end(ap);
|
|
ptr2 = ptr3 = (char *)pmem_malloc(PMEM_SUBSYS_OTHER, size);
|
|
for (size = 0; size < i; size++) {
|
|
memcpy(ptr2, strs[size], lengths[size]);
|
|
ptr2 += lengths[size];
|
|
}
|
|
*ptr2 = 0;
|
|
return ptr3;
|
|
}
|
|
|
|
int putil_slprintf(char *str, size_t size, const char *format, ...) {
|
|
va_list ap;
|
|
int ret;
|
|
va_start(ap, format);
|
|
ret = vsnprintf(str, size, format, ap);
|
|
va_end(ap);
|
|
if (pdbg_unlikely(ret >= size))
|
|
str[size - 1] = 0;
|
|
return ret;
|
|
}
|
|
|
|
unsigned char *putil_base32_encode(const unsigned char *str, size_t length,
|
|
size_t *ret_length) {
|
|
static const unsigned char *table =
|
|
(const unsigned char *)"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
|
unsigned char *result;
|
|
unsigned char *p;
|
|
uint32_t bits, buff;
|
|
size_t temp, alloc_size;
|
|
|
|
temp = (length + 4) / 5;
|
|
if (temp != 0 && 8 > SIZE_MAX / temp) {
|
|
return NULL;
|
|
}
|
|
alloc_size = temp * 8 + 1;
|
|
result = (unsigned char *)pmem_malloc(PMEM_SUBSYS_OTHER, alloc_size);
|
|
p = result;
|
|
|
|
bits = 0;
|
|
buff = 0; // don't really have to initialize this one, but a compiler that
|
|
// will detect that this is safe is yet to be born
|
|
|
|
while (length) {
|
|
if (bits < 5) {
|
|
buff = (buff << 8) | (*str++);
|
|
length--;
|
|
bits += 8;
|
|
}
|
|
bits -= 5;
|
|
*p++ = table[0x1f & (buff >> bits)];
|
|
}
|
|
|
|
while (bits) {
|
|
if (bits < 5) {
|
|
buff <<= (5 - bits);
|
|
bits = 5;
|
|
}
|
|
bits -= 5;
|
|
*p++ = table[0x1f & (buff >> bits)];
|
|
}
|
|
|
|
*ret_length = p - result;
|
|
*p = 0;
|
|
return result;
|
|
}
|
|
|
|
unsigned char *putil_base32_decode(const unsigned char *str, size_t length,
|
|
size_t *ret_length) {
|
|
unsigned char *result, *p;
|
|
uint32_t bits, buff;
|
|
unsigned char ch;
|
|
size_t temp, alloc_size;
|
|
|
|
temp = (length + 7) / 8;
|
|
if (temp != 0 && 5 > SIZE_MAX / temp) {
|
|
return NULL;
|
|
}
|
|
alloc_size = temp * 5 + 1;
|
|
result = (unsigned char *)pmem_malloc(PMEM_SUBSYS_OTHER, alloc_size);
|
|
p = result;
|
|
bits = 0;
|
|
buff = 0;
|
|
while (length) {
|
|
ch = *str++;
|
|
length--;
|
|
if (ch >= 'A' && ch <= 'Z')
|
|
ch = (ch & 0x1f) - 1;
|
|
else if (ch >= '2' && ch <= '7')
|
|
ch -= '2' - 26;
|
|
else {
|
|
pmem_free(PMEM_SUBSYS_OTHER, result);
|
|
return NULL;
|
|
}
|
|
buff = (buff << 5) + ch;
|
|
bits += 5;
|
|
if (bits >= 8) {
|
|
bits -= 8;
|
|
*p++ = buff >> bits;
|
|
}
|
|
}
|
|
*p = 0;
|
|
*ret_length = p - result;
|
|
return result;
|
|
}
|
|
|
|
unsigned char *putil_base64_encode(const unsigned char *str, size_t length,
|
|
size_t *ret_length) {
|
|
const unsigned char *current = str;
|
|
unsigned char *p;
|
|
unsigned char *result;
|
|
size_t temp, alloc_size;
|
|
|
|
temp = (length + 2) / 3;
|
|
if (temp != 0 && 4 > SIZE_MAX / temp) {
|
|
return NULL;
|
|
}
|
|
alloc_size = temp * 4 + 1;
|
|
result = (unsigned char *)pmem_malloc(PMEM_SUBSYS_OTHER, alloc_size);
|
|
p = result;
|
|
|
|
while (length > 2) {
|
|
*p++ = putil_base64_table[current[0] >> 2];
|
|
*p++ = putil_base64_table[((current[0] & 0x03) << 4) + (current[1] >> 4)];
|
|
*p++ = putil_base64_table[((current[1] & 0x0f) << 2) + (current[2] >> 6)];
|
|
*p++ = putil_base64_table[current[2] & 0x3f];
|
|
current += 3;
|
|
length -= 3;
|
|
}
|
|
|
|
if (length != 0) {
|
|
*p++ = putil_base64_table[current[0] >> 2];
|
|
if (length > 1) {
|
|
*p++ = putil_base64_table[((current[0] & 0x03) << 4) + (current[1] >> 4)];
|
|
*p++ = putil_base64_table[(current[1] & 0x0f) << 2];
|
|
*p++ = '='; /* Add padding */
|
|
} else {
|
|
*p++ = putil_base64_table[(current[0] & 0x03) << 4];
|
|
*p++ = '='; /* Add padding */
|
|
*p++ = '='; /* Add padding */
|
|
}
|
|
}
|
|
|
|
*ret_length = p - result;
|
|
*p = 0;
|
|
return result;
|
|
}
|
|
|
|
unsigned char *putil_base64_decode(const unsigned char *str, size_t length,
|
|
size_t *ret_length) {
|
|
const unsigned char *current = str;
|
|
unsigned char *result;
|
|
size_t i = 0, j = 0;
|
|
ssize_t ch;
|
|
size_t temp, alloc_size;
|
|
|
|
temp = (length + 3) / 4;
|
|
if (temp != 0 && 3 > SIZE_MAX / temp) {
|
|
return NULL;
|
|
}
|
|
alloc_size = temp * 3 + 1;
|
|
result = (unsigned char *)pmem_malloc(PMEM_SUBSYS_OTHER, alloc_size);
|
|
|
|
while (length-- > 0) {
|
|
ch = base64_reverse_table[*current++];
|
|
if (ch == -1)
|
|
continue;
|
|
else if (ch == -2) {
|
|
pmem_free(PMEM_SUBSYS_OTHER, result);
|
|
return NULL;
|
|
}
|
|
switch (i % 4) {
|
|
case 0:
|
|
result[j] = ch << 2;
|
|
break;
|
|
case 1:
|
|
result[j++] |= ch >> 4;
|
|
result[j] = (ch & 0x0f) << 4;
|
|
break;
|
|
case 2:
|
|
result[j++] |= ch >> 2;
|
|
result[j] = (ch & 0x03) << 6;
|
|
break;
|
|
case 3:
|
|
result[j++] |= ch;
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
*ret_length = j;
|
|
result[j] = 0;
|
|
return result;
|
|
}
|
|
|
|
int putil_is_valid_utf8(const char *str) {
|
|
static const int8_t trailing[] = {
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1,
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, -1, -1, -1, -1,
|
|
-1, -1, -1, -1};
|
|
int8_t t;
|
|
while (*str) {
|
|
t = trailing[(unsigned char)*str++];
|
|
if (unlikely(t)) {
|
|
if (t < 0)
|
|
return 0;
|
|
while (t--)
|
|
if ((((unsigned char)*str++) & 0xc0) != 0x80)
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
uint64_t putil_ato64(const char *str) {
|
|
uint64_t n = 0;
|
|
while (*str >= '0' && *str <= '9')
|
|
n = n * 10 + (*str++) - '0';
|
|
return n;
|
|
}
|
|
|
|
uint32_t putil_ato32(const char *str) {
|
|
uint32_t n = 0;
|
|
while (*str >= '0' && *str <= '9')
|
|
n = n * 10 + (*str++) - '0';
|
|
return n;
|
|
}
|