Blame util.c

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