mirror of
https://github.com/cesanta/mongoose.git
synced 2025-08-01 02:16:14 +08:00
334 lines
9.7 KiB
C
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
|
|
}
|