mongoose/src/util.c
2021-12-21 21:50:18 +00:00

334 lines
9.7 KiB
C

#include "util.h"
#if MG_ARCH == MG_ARCH_UNIX && defined(__APPLE__)
#include <mach/mach_time.h>
#endif
#if MG_ENABLE_FILE
char *mg_file_read(const char *path, size_t *sizep) {
FILE *fp;
char *data = NULL;
size_t size = 0;
if ((fp = fopen(path, "rb")) != NULL) {
fseek(fp, 0, SEEK_END);
size = (size_t) ftell(fp);
rewind(fp);
data = (char *) calloc(1, size + 1);
if (data != NULL) {
if (fread(data, 1, size, fp) != size) {
free(data);
data = NULL;
} else {
data[size] = '\0';
if (sizep != NULL) *sizep = size;
}
}
fclose(fp);
}
return data;
}
bool mg_file_write(const char *path, const void *buf, size_t len) {
bool result = false;
FILE *fp;
char tmp[MG_PATH_MAX];
snprintf(tmp, sizeof(tmp), "%s.%d", path, rand());
fp = fopen(tmp, "wb");
if (fp != NULL) {
result = fwrite(buf, 1, len, fp) == len;
fclose(fp);
if (result) {
remove(path);
rename(tmp, path);
} else {
remove(tmp);
}
}
return result;
}
#endif
bool mg_file_printf(const char *path, const char *fmt, ...) {
char tmp[256], *buf = tmp;
bool result;
int len;
va_list ap;
va_start(ap, fmt);
len = mg_vasprintf(&buf, sizeof(tmp), fmt, ap);
va_end(ap);
result = mg_file_write(path, buf, len > 0 ? (size_t) len : 0);
if (buf != tmp) free(buf);
return result;
}
#if MG_ENABLE_CUSTOM_RANDOM
#else
void mg_random(void *buf, size_t len) {
bool done = false;
unsigned char *p = (unsigned char *) buf;
#if MG_ARCH == MG_ARCH_ESP32
while (len--) *p++ = (unsigned char) (esp_random() & 255);
done = true;
#elif MG_ARCH == MG_ARCH_WIN32
#elif MG_ARCH_UNIX
FILE *fp = fopen("/dev/urandom", "rb");
if (fp != NULL) {
if (fread(buf, 1, len, fp) == len) done = true;
fclose(fp);
}
#endif
// If everything above did not work, fallback to a pseudo random generator
while (!done && len--) *p++ = (unsigned char) (rand() & 255);
}
#endif
bool mg_globmatch(const char *s1, size_t n1, const char *s2, size_t n2) {
size_t i = 0, j = 0, ni = 0, nj = 0;
while (i < n1 || j < n2) {
if (i < n1 && j < n2 && (s1[i] == '?' || s2[j] == s1[i])) {
i++, j++;
} else if (i < n1 && (s1[i] == '*' || s1[i] == '#')) {
ni = i, nj = j + 1, i++;
} else if (nj > 0 && nj <= n2 && (s1[ni] == '#' || s2[j] != '/')) {
i = ni, j = nj;
} else {
// printf(">>: [%s] [%s] %d %d %d %d\n", s1, s2, i, j, ni, nj);
return false;
}
}
return true;
}
static size_t mg_nce(const char *s, size_t n, size_t ofs, size_t *koff,
size_t *klen, size_t *voff, size_t *vlen) {
size_t kvlen, kl;
for (kvlen = 0; ofs + kvlen < n && s[ofs + kvlen] != ',';) kvlen++;
for (kl = 0; kl < kvlen && s[ofs + kl] != '=';) kl++;
if (koff != NULL) *koff = ofs;
if (klen != NULL) *klen = kl;
if (voff != NULL) *voff = kl < kvlen ? ofs + kl + 1 : 0;
if (vlen != NULL) *vlen = kl < kvlen ? kvlen - kl - 1 : 0;
ofs += kvlen + 1;
return ofs > n ? n : ofs;
}
bool mg_commalist(struct mg_str *s, struct mg_str *k, struct mg_str *v) {
size_t koff = 0, klen = 0, voff = 0, vlen = 0;
size_t off = mg_nce(s->ptr, s->len, 0, &koff, &klen, &voff, &vlen);
if (k != NULL) *k = mg_str_n(s->ptr + koff, klen);
if (v != NULL) *v = mg_str_n(s->ptr + voff, vlen);
*s = mg_str_n(s->ptr + off, s->len - off);
return off > 0;
}
uint32_t mg_ntohl(uint32_t net) {
uint8_t data[4] = {0, 0, 0, 0};
memcpy(&data, &net, sizeof(data));
return (((uint32_t) data[3]) << 0) | (((uint32_t) data[2]) << 8) |
(((uint32_t) data[1]) << 16) | (((uint32_t) data[0]) << 24);
}
uint16_t mg_ntohs(uint16_t net) {
uint8_t data[2] = {0, 0};
memcpy(&data, &net, sizeof(data));
return (uint16_t) ((uint16_t) data[1] | (((uint16_t) data[0]) << 8));
}
char *mg_hexdump(const void *buf, size_t len) {
const unsigned char *p = (const unsigned char *) buf;
size_t i, idx, n = 0, ofs = 0, dlen = len * 5 + 100;
char ascii[17] = "", *dst = (char *) calloc(1, dlen);
if (dst == NULL) return dst;
for (i = 0; i < len; i++) {
idx = i % 16;
if (idx == 0) {
if (i > 0 && dlen > n)
n += (size_t) snprintf(dst + n, dlen - n, " %s\n", ascii);
if (dlen > n)
n += (size_t) snprintf(dst + n, dlen - n, "%04x ", (int) (i + ofs));
}
if (dlen < n) break;
n += (size_t) snprintf(dst + n, dlen - n, " %02x", p[i]);
ascii[idx] = (char) (p[i] < 0x20 || p[i] > 0x7e ? '.' : p[i]);
ascii[idx + 1] = '\0';
}
while (i++ % 16) {
if (n < dlen) n += (size_t) snprintf(dst + n, dlen - n, "%s", " ");
}
if (n < dlen) n += (size_t) snprintf(dst + n, dlen - n, " %s\n", ascii);
if (n > dlen - 1) n = dlen - 1;
dst[n] = '\0';
return dst;
}
char *mg_hex(const void *buf, size_t len, char *to) {
const unsigned char *p = (const unsigned char *) buf;
static const char *hex = "0123456789abcdef";
size_t i = 0;
for (; len--; p++) {
to[i++] = hex[p[0] >> 4];
to[i++] = hex[p[0] & 0x0f];
}
to[i] = '\0';
return to;
}
static unsigned char mg_unhex_nimble(unsigned char c) {
return (c >= '0' && c <= '9') ? (unsigned char) (c - '0')
: (c >= 'A' && c <= 'F') ? (unsigned char) (c - '7')
: (unsigned char) (c - 'W');
}
unsigned long mg_unhexn(const char *s, size_t len) {
unsigned long i = 0, v = 0;
for (i = 0; i < len; i++) v <<= 4, v |= mg_unhex_nimble(((uint8_t *) s)[i]);
return v;
}
void mg_unhex(const char *buf, size_t len, unsigned char *to) {
size_t i;
for (i = 0; i < len; i += 2) {
to[i >> 1] = (unsigned char) mg_unhexn(&buf[i], 2);
}
}
int mg_vasprintf(char **buf, size_t size, const char *fmt, va_list ap) {
va_list ap_copy;
int len;
va_copy(ap_copy, ap);
len = vsnprintf(*buf, size, fmt, ap_copy);
va_end(ap_copy);
if (len < 0) {
// eCos and Windows are not standard-compliant and return -1 when
// the buffer is too small. Keep allocating larger buffers until we
// succeed or out of memory.
// LCOV_EXCL_START
*buf = NULL;
while (len < 0) {
free(*buf);
if (size == 0) size = 5;
size *= 2;
if ((*buf = (char *) calloc(1, size)) == NULL) {
len = -1;
break;
}
va_copy(ap_copy, ap);
len = vsnprintf(*buf, size - 1, fmt, ap_copy);
va_end(ap_copy);
}
// Microsoft version of vsnprintf() is not always null-terminated, so put
// the terminator manually
if (*buf != NULL) (*buf)[len] = 0;
// LCOV_EXCL_STOP
} else if (len >= (int) size) {
/// Standard-compliant code path. Allocate a buffer that is large enough
if ((*buf = (char *) calloc(1, (size_t) len + 1)) == NULL) {
len = -1; // LCOV_EXCL_LINE
} else { // LCOV_EXCL_LINE
va_copy(ap_copy, ap);
len = vsnprintf(*buf, (size_t) len + 1, fmt, ap_copy);
va_end(ap_copy);
}
}
return len;
}
int mg_asprintf(char **buf, size_t size, const char *fmt, ...) {
int ret;
va_list ap;
va_start(ap, fmt);
ret = mg_vasprintf(buf, size, fmt, ap);
va_end(ap);
return ret;
}
int64_t mg_to64(struct mg_str str) {
int64_t result = 0, neg = 1, max = 922337203685477570 /* INT64_MAX/10-10 */;
size_t i = 0;
while (i < str.len && (str.ptr[i] == ' ' || str.ptr[i] == '\t')) i++;
if (i < str.len && str.ptr[i] == '-') neg = -1, i++;
while (i < str.len && str.ptr[i] >= '0' && str.ptr[i] <= '9') {
if (result > max) return 0;
result *= 10;
result += (str.ptr[i] - '0');
i++;
}
return result * neg;
}
uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len) {
int i;
crc = ~crc;
while (len--) {
crc ^= *(unsigned char *) buf++;
for (i = 0; i < 8; i++) crc = crc & 1 ? (crc >> 1) ^ 0xedb88320 : crc >> 1;
}
return ~crc;
}
static int isbyte(int n) {
return n >= 0 && n <= 255;
}
static int parse_net(const char *spec, uint32_t *net, uint32_t *mask) {
int n, a, b, c, d, slash = 32, len = 0;
if ((sscanf(spec, "%d.%d.%d.%d/%d%n", &a, &b, &c, &d, &slash, &n) == 5 ||
sscanf(spec, "%d.%d.%d.%d%n", &a, &b, &c, &d, &n) == 4) &&
isbyte(a) && isbyte(b) && isbyte(c) && isbyte(d) && slash >= 0 &&
slash < 33) {
len = n;
*net = ((uint32_t) a << 24) | ((uint32_t) b << 16) | ((uint32_t) c << 8) |
(uint32_t) d;
*mask = slash ? (uint32_t) (0xffffffffU << (32 - slash)) : (uint32_t) 0;
}
return len;
}
int mg_check_ip_acl(struct mg_str acl, uint32_t remote_ip) {
struct mg_str k, v;
int allowed = acl.len == 0 ? '+' : '-'; // If any ACL is set, deny by default
while (mg_commalist(&acl, &k, &v)) {
uint32_t net, mask;
if (k.ptr[0] != '+' && k.ptr[0] != '-') return -1;
if (parse_net(&k.ptr[1], &net, &mask) == 0) return -2;
if ((remote_ip & mask) == net) allowed = k.ptr[0];
}
return allowed == '+';
}
int64_t mg_millis(void) {
#if MG_ARCH == MG_ARCH_WIN32
return GetTickCount();
#elif MG_ARCH == MG_ARCH_ESP32
return esp_timer_get_time() / 1000;
#elif MG_ARCH == MG_ARCH_ESP8266
return xTaskGetTickCount() * portTICK_PERIOD_MS;
#elif MG_ARCH == MG_ARCH_FREERTOS_TCP || MG_ARCH == MG_ARCH_FREERTOS_LWIP
return xTaskGetTickCount() * portTICK_PERIOD_MS;
#elif MG_ARCH == MG_ARCH_AZURERTOS
return tx_time_get() * (1000 /* MS per SEC */ / TX_TIMER_TICKS_PER_SECOND);
#elif MG_ARCH == MG_ARCH_UNIX && defined(__APPLE__)
uint64_t ticks = mach_absolute_time();
static mach_timebase_info_data_t timebase;
mach_timebase_info(&timebase);
double ticks_to_nanos = (double) timebase.numer / timebase.denom;
uint64_t uptime_nanos = (uint64_t) (ticks_to_nanos * ticks);
return (int64_t) (uptime_nanos / 1000000);
#else
struct timespec ts;
#ifdef _POSIX_MONOTONIC_CLOCK
#ifdef CLOCK_MONOTONIC_RAW
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
#else
clock_gettime(CLOCK_MONOTONIC, &ts);
#endif
#else
clock_gettime(CLOCK_REALTIME, &ts);
#endif
return ((int64_t) ts.tv_sec * 1000 + (int64_t) ts.tv_nsec / 1000000);
#endif
}