|
Packit |
4e8bc4 |
#include <stdio.h>
|
|
Packit |
4e8bc4 |
#include <assert.h>
|
|
Packit |
4e8bc4 |
#include <ctype.h>
|
|
Packit |
4e8bc4 |
#include <errno.h>
|
|
Packit |
4e8bc4 |
#include <string.h>
|
|
Packit |
4e8bc4 |
#include <stdlib.h>
|
|
Packit |
4e8bc4 |
#include <stdarg.h>
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
#include "memcached.h"
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
static char *uriencode_map[256];
|
|
Packit |
4e8bc4 |
static char uriencode_str[768];
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
void uriencode_init(void) {
|
|
Packit |
4e8bc4 |
int x;
|
|
Packit |
4e8bc4 |
char *str = uriencode_str;
|
|
Packit |
4e8bc4 |
for (x = 0; x < 256; x++) {
|
|
Packit |
4e8bc4 |
if (isalnum(x) || x == '-' || x == '.' || x == '_' || x == '~') {
|
|
Packit |
4e8bc4 |
uriencode_map[x] = NULL;
|
|
Packit |
4e8bc4 |
} else {
|
|
Packit |
4e8bc4 |
snprintf(str, 4, "%%%02hhX", (unsigned char)x);
|
|
Packit |
4e8bc4 |
uriencode_map[x] = str;
|
|
Packit |
4e8bc4 |
str += 3; /* lobbing off the \0 is fine */
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
bool uriencode(const char *src, char *dst, const size_t srclen, const size_t dstlen) {
|
|
Packit |
4e8bc4 |
int x;
|
|
Packit |
4e8bc4 |
size_t d = 0;
|
|
Packit |
4e8bc4 |
for (x = 0; x < srclen; x++) {
|
|
Packit |
4e8bc4 |
if (d + 4 > dstlen)
|
|
Packit |
4e8bc4 |
return false;
|
|
Packit |
4e8bc4 |
if (uriencode_map[(unsigned char) src[x]] != NULL) {
|
|
Packit |
4e8bc4 |
memcpy(&dst[d], uriencode_map[(unsigned char) src[x]], 3);
|
|
Packit |
4e8bc4 |
d += 3;
|
|
Packit |
4e8bc4 |
} else {
|
|
Packit |
4e8bc4 |
dst[d] = src[x];
|
|
Packit |
4e8bc4 |
d++;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
dst[d] = '\0';
|
|
Packit |
4e8bc4 |
return true;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
/* Avoid warnings on solaris, where isspace() is an index into an array, and gcc uses signed chars */
|
|
Packit |
4e8bc4 |
#define xisspace(c) isspace((unsigned char)c)
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
bool safe_strtoull(const char *str, uint64_t *out) {
|
|
Packit |
4e8bc4 |
assert(out != NULL);
|
|
Packit |
4e8bc4 |
errno = 0;
|
|
Packit |
4e8bc4 |
*out = 0;
|
|
Packit |
4e8bc4 |
char *endptr;
|
|
Packit |
4e8bc4 |
unsigned long long ull = strtoull(str, &endptr, 10);
|
|
Packit |
4e8bc4 |
if ((errno == ERANGE) || (str == endptr)) {
|
|
Packit |
4e8bc4 |
return false;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
if (xisspace(*endptr) || (*endptr == '\0' && endptr != str)) {
|
|
Packit |
4e8bc4 |
if ((long long) ull < 0) {
|
|
Packit |
4e8bc4 |
/* only check for negative signs in the uncommon case when
|
|
Packit |
4e8bc4 |
* the unsigned number is so big that it's negative as a
|
|
Packit |
4e8bc4 |
* signed number. */
|
|
Packit |
4e8bc4 |
if (strchr(str, '-') != NULL) {
|
|
Packit |
4e8bc4 |
return false;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
*out = ull;
|
|
Packit |
4e8bc4 |
return true;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
return false;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
/* Could macro this. Decided to keep this unrolled for safety rather than add
|
|
Packit |
4e8bc4 |
* the base parameter to all callers. Very few places need to parse a number
|
|
Packit |
4e8bc4 |
* outside of base 10, currently exactly once, so splitting this up should
|
|
Packit |
4e8bc4 |
* help avoid typo bugs.
|
|
Packit |
4e8bc4 |
*/
|
|
Packit |
4e8bc4 |
bool safe_strtoull_hex(const char *str, uint64_t *out) {
|
|
Packit |
4e8bc4 |
assert(out != NULL);
|
|
Packit |
4e8bc4 |
errno = 0;
|
|
Packit |
4e8bc4 |
*out = 0;
|
|
Packit |
4e8bc4 |
char *endptr;
|
|
Packit |
4e8bc4 |
unsigned long long ull = strtoull(str, &endptr, 16);
|
|
Packit |
4e8bc4 |
if ((errno == ERANGE) || (str == endptr)) {
|
|
Packit |
4e8bc4 |
return false;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
if (xisspace(*endptr) || (*endptr == '\0' && endptr != str)) {
|
|
Packit |
4e8bc4 |
if ((long long) ull < 0) {
|
|
Packit |
4e8bc4 |
/* only check for negative signs in the uncommon case when
|
|
Packit |
4e8bc4 |
* the unsigned number is so big that it's negative as a
|
|
Packit |
4e8bc4 |
* signed number. */
|
|
Packit |
4e8bc4 |
if (strchr(str, '-') != NULL) {
|
|
Packit |
4e8bc4 |
return false;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
*out = ull;
|
|
Packit |
4e8bc4 |
return true;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
return false;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
bool safe_strtoll(const char *str, int64_t *out) {
|
|
Packit |
4e8bc4 |
assert(out != NULL);
|
|
Packit |
4e8bc4 |
errno = 0;
|
|
Packit |
4e8bc4 |
*out = 0;
|
|
Packit |
4e8bc4 |
char *endptr;
|
|
Packit |
4e8bc4 |
long long ll = strtoll(str, &endptr, 10);
|
|
Packit |
4e8bc4 |
if ((errno == ERANGE) || (str == endptr)) {
|
|
Packit |
4e8bc4 |
return false;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
if (xisspace(*endptr) || (*endptr == '\0' && endptr != str)) {
|
|
Packit |
4e8bc4 |
*out = ll;
|
|
Packit |
4e8bc4 |
return true;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
return false;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
bool safe_strtoul(const char *str, uint32_t *out) {
|
|
Packit |
4e8bc4 |
char *endptr = NULL;
|
|
Packit |
4e8bc4 |
unsigned long l = 0;
|
|
Packit |
4e8bc4 |
assert(out);
|
|
Packit |
4e8bc4 |
assert(str);
|
|
Packit |
4e8bc4 |
*out = 0;
|
|
Packit |
4e8bc4 |
errno = 0;
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
l = strtoul(str, &endptr, 10);
|
|
Packit |
4e8bc4 |
if ((errno == ERANGE) || (str == endptr)) {
|
|
Packit |
4e8bc4 |
return false;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
if (xisspace(*endptr) || (*endptr == '\0' && endptr != str)) {
|
|
Packit |
4e8bc4 |
if ((long) l < 0) {
|
|
Packit |
4e8bc4 |
/* only check for negative signs in the uncommon case when
|
|
Packit |
4e8bc4 |
* the unsigned number is so big that it's negative as a
|
|
Packit |
4e8bc4 |
* signed number. */
|
|
Packit |
4e8bc4 |
if (strchr(str, '-') != NULL) {
|
|
Packit |
4e8bc4 |
return false;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
*out = l;
|
|
Packit |
4e8bc4 |
return true;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
return false;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
bool safe_strtol(const char *str, int32_t *out) {
|
|
Packit |
4e8bc4 |
assert(out != NULL);
|
|
Packit |
4e8bc4 |
errno = 0;
|
|
Packit |
4e8bc4 |
*out = 0;
|
|
Packit |
4e8bc4 |
char *endptr;
|
|
Packit |
4e8bc4 |
long l = strtol(str, &endptr, 10);
|
|
Packit |
4e8bc4 |
if ((errno == ERANGE) || (str == endptr)) {
|
|
Packit |
4e8bc4 |
return false;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
if (xisspace(*endptr) || (*endptr == '\0' && endptr != str)) {
|
|
Packit |
4e8bc4 |
*out = l;
|
|
Packit |
4e8bc4 |
return true;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
return false;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
bool safe_strtod(const char *str, double *out) {
|
|
Packit |
4e8bc4 |
assert(out != NULL);
|
|
Packit |
4e8bc4 |
errno = 0;
|
|
Packit |
4e8bc4 |
*out = 0;
|
|
Packit |
4e8bc4 |
char *endptr;
|
|
Packit |
4e8bc4 |
double d = strtod(str, &endptr);
|
|
Packit |
4e8bc4 |
if ((errno == ERANGE) || (str == endptr)) {
|
|
Packit |
4e8bc4 |
return false;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
if (xisspace(*endptr) || (*endptr == '\0' && endptr != str)) {
|
|
Packit |
4e8bc4 |
*out = d;
|
|
Packit |
4e8bc4 |
return true;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
return false;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
// slow, safe function for copying null terminated buffers.
|
|
Packit |
4e8bc4 |
// ensures null terminator set on destination buffer. copies at most dstmax-1
|
|
Packit |
4e8bc4 |
// non-null bytes.
|
|
Packit |
4e8bc4 |
// Explicitly avoids over-reading src while looking for the null byte.
|
|
Packit |
4e8bc4 |
// returns true if src was fully copied.
|
|
Packit |
4e8bc4 |
// returns false if src was truncated into dst.
|
|
Packit |
4e8bc4 |
bool safe_strcpy(char *dst, const char *src, const size_t dstmax) {
|
|
Packit |
4e8bc4 |
size_t x;
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
for (x = 0; x < dstmax - 1 && src[x] != '\0'; x++) {
|
|
Packit |
4e8bc4 |
dst[x] = src[x];
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
dst[x] = '\0';
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
if (src[x] == '\0') {
|
|
Packit |
4e8bc4 |
return true;
|
|
Packit |
4e8bc4 |
} else {
|
|
Packit |
4e8bc4 |
return false;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
bool safe_memcmp(const void *a, const void *b, size_t len) {
|
|
Packit |
4e8bc4 |
const volatile unsigned char *ua = (const volatile unsigned char *)a;
|
|
Packit |
4e8bc4 |
const volatile unsigned char *ub = (const volatile unsigned char *)b;
|
|
Packit |
4e8bc4 |
int delta = 0;
|
|
Packit |
4e8bc4 |
size_t x;
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
for (x = 0; x < len; x++) {
|
|
Packit |
4e8bc4 |
delta |= ua[x] ^ ub[x];
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
if (delta == 0) {
|
|
Packit |
4e8bc4 |
return true;
|
|
Packit |
4e8bc4 |
} else {
|
|
Packit |
4e8bc4 |
return false;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
void vperror(const char *fmt, ...) {
|
|
Packit |
4e8bc4 |
int old_errno = errno;
|
|
Packit |
4e8bc4 |
char buf[1024];
|
|
Packit |
4e8bc4 |
va_list ap;
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
va_start(ap, fmt);
|
|
Packit |
4e8bc4 |
if (vsnprintf(buf, sizeof(buf), fmt, ap) == -1) {
|
|
Packit |
4e8bc4 |
buf[sizeof(buf) - 1] = '\0';
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
va_end(ap);
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
errno = old_errno;
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
perror(buf);
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
#ifndef HAVE_HTONLL
|
|
Packit |
4e8bc4 |
static uint64_t mc_swap64(uint64_t in) {
|
|
Packit |
4e8bc4 |
#ifdef ENDIAN_LITTLE
|
|
Packit |
4e8bc4 |
/* Little endian, flip the bytes around until someone makes a faster/better
|
|
Packit |
4e8bc4 |
* way to do this. */
|
|
Packit |
4e8bc4 |
int64_t rv = 0;
|
|
Packit |
4e8bc4 |
int i = 0;
|
|
Packit |
4e8bc4 |
for(i = 0; i<8; i++) {
|
|
Packit |
4e8bc4 |
rv = (rv << 8) | (in & 0xff);
|
|
Packit |
4e8bc4 |
in >>= 8;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
return rv;
|
|
Packit |
4e8bc4 |
#else
|
|
Packit |
4e8bc4 |
/* big-endian machines don't need byte swapping */
|
|
Packit |
4e8bc4 |
return in;
|
|
Packit |
4e8bc4 |
#endif
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
uint64_t ntohll(uint64_t val) {
|
|
Packit |
4e8bc4 |
return mc_swap64(val);
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
uint64_t htonll(uint64_t val) {
|
|
Packit |
4e8bc4 |
return mc_swap64(val);
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
#endif
|
|
Packit |
4e8bc4 |
|