#include #include #include #include #include #include "blob.h" /* RFC 3986 section 2.3 Unreserved Characters (January 2005) */ #define CTYPE_UNRESERVED 1 static const char *xd = "0123456789abcdefghijklmnopqrstuvwxyz"; static const unsigned char chartype[128] = { ['a'] = CTYPE_UNRESERVED, ['b'] = CTYPE_UNRESERVED, ['c'] = CTYPE_UNRESERVED, ['d'] = CTYPE_UNRESERVED, ['e'] = CTYPE_UNRESERVED, ['f'] = CTYPE_UNRESERVED, ['g'] = CTYPE_UNRESERVED, ['h'] = CTYPE_UNRESERVED, ['i'] = CTYPE_UNRESERVED, ['j'] = CTYPE_UNRESERVED, ['k'] = CTYPE_UNRESERVED, ['l'] = CTYPE_UNRESERVED, ['m'] = CTYPE_UNRESERVED, ['n'] = CTYPE_UNRESERVED, ['o'] = CTYPE_UNRESERVED, ['p'] = CTYPE_UNRESERVED, ['q'] = CTYPE_UNRESERVED, ['r'] = CTYPE_UNRESERVED, ['s'] = CTYPE_UNRESERVED, ['t'] = CTYPE_UNRESERVED, ['u'] = CTYPE_UNRESERVED, ['v'] = CTYPE_UNRESERVED, ['w'] = CTYPE_UNRESERVED, ['x'] = CTYPE_UNRESERVED, ['y'] = CTYPE_UNRESERVED, ['z'] = CTYPE_UNRESERVED, ['A'] = CTYPE_UNRESERVED, ['B'] = CTYPE_UNRESERVED, ['C'] = CTYPE_UNRESERVED, ['D'] = CTYPE_UNRESERVED, ['E'] = CTYPE_UNRESERVED, ['F'] = CTYPE_UNRESERVED, ['G'] = CTYPE_UNRESERVED, ['H'] = CTYPE_UNRESERVED, ['I'] = CTYPE_UNRESERVED, ['J'] = CTYPE_UNRESERVED, ['K'] = CTYPE_UNRESERVED, ['L'] = CTYPE_UNRESERVED, ['M'] = CTYPE_UNRESERVED, ['N'] = CTYPE_UNRESERVED, ['O'] = CTYPE_UNRESERVED, ['P'] = CTYPE_UNRESERVED, ['Q'] = CTYPE_UNRESERVED, ['R'] = CTYPE_UNRESERVED, ['S'] = CTYPE_UNRESERVED, ['T'] = CTYPE_UNRESERVED, ['U'] = CTYPE_UNRESERVED, ['V'] = CTYPE_UNRESERVED, ['W'] = CTYPE_UNRESERVED, ['X'] = CTYPE_UNRESERVED, ['Y'] = CTYPE_UNRESERVED, ['Z'] = CTYPE_UNRESERVED, ['0'] = CTYPE_UNRESERVED, ['1'] = CTYPE_UNRESERVED, ['2'] = CTYPE_UNRESERVED, ['3'] = CTYPE_UNRESERVED, ['4'] = CTYPE_UNRESERVED, ['5'] = CTYPE_UNRESERVED, ['6'] = CTYPE_UNRESERVED, ['7'] = CTYPE_UNRESERVED, ['8'] = CTYPE_UNRESERVED, ['9'] = CTYPE_UNRESERVED, ['-'] = CTYPE_UNRESERVED, ['_'] = CTYPE_UNRESERVED, ['.'] = CTYPE_UNRESERVED, ['~'] = CTYPE_UNRESERVED, }; static inline int dx(int c) { if (likely(c >= '0' && c <= '9')) return c - '0'; if (likely(c >= 'a' && c <= 'f')) return c - 'a' + 0xa; if (c >= 'A' && c <= 'F') return c - 'A' + 0xa; return -1; } char *blob_cstr_dup(blob_t b) { char *p; if (blob_is_null(b)) return NULL; p = malloc(b.len+1); if (p != NULL) { memcpy(p, b.ptr, b.len); p[b.len] = 0; } return p; } blob_t blob_dup(blob_t b) { blob_t p; if (blob_is_null(b)) return BLOB_NULL; p.ptr = malloc(b.len); if (p.ptr != NULL) { memcpy(p.ptr, b.ptr, b.len); p.len = b.len; } else { p.len = 0; } return p; } int blob_cmp(blob_t a, blob_t b) { if (a.len != b.len) return a.len - b.len; return memcmp(a.ptr, b.ptr, a.len); } int blob_icmp(blob_t a, blob_t b) { if (a.len != b.len) return a.len - b.len; return strncasecmp(a.ptr, b.ptr, a.len); } int blob_pull_inet_addr(blob_t *b, struct in_addr *saddr) { unsigned long ip = 0; int i; for (i = 0; i < 3; i++) { ip += blob_pull_uint(b, 10); ip <<= 8; if (!blob_pull_matching(b, BLOB_STR("."))) return 0; } ip += blob_pull_uint(b, 10); saddr->s_addr = htonl(ip); return 1; } unsigned long blob_inet_addr(blob_t b) { struct in_addr saddr; if (blob_pull_inet_addr(&b, &saddr) && b.len == 0) return saddr.s_addr; return 0; } blob_t blob_pushed(blob_t buffer, blob_t left) { if (buffer.ptr + buffer.len != left.ptr + left.len) return BLOB_NULL; return BLOB_PTR_LEN(buffer.ptr, left.ptr - buffer.ptr); } void blob_push(blob_t *b, blob_t d) { if (b->len >= d.len) { memcpy(b->ptr, d.ptr, d.len); b->ptr += d.len; b->len -= d.len; } else { *b = BLOB_NULL; } } void blob_push_lower(blob_t *b, blob_t d) { int i; if (b->len < d.len) { *b = BLOB_NULL; return; } for (i = 0; i < d.len; i++) b->ptr[i] = tolower(d.ptr[i]); b->ptr += d.len; b->len -= d.len; } void blob_push_byte(blob_t *b, unsigned char byte) { if (b->len) { b->ptr[0] = byte; b->ptr ++; b->len --; } else { *b = BLOB_NULL; } } void blob_push_uint(blob_t *to, unsigned int value, int radix) { char buf[64]; char *ptr = &buf[sizeof(buf)-1]; if (value == 0) { blob_push_byte(to, '0'); return; } while (value != 0) { *(ptr--) = xd[value % radix]; value /= radix; } blob_push(to, BLOB_PTR_PTR(ptr+1, &buf[sizeof(buf)-1])); } void blob_push_ctime(blob_t *to, time_t t) { char buf[128]; blob_t b; ctime_r(&t, buf); b = BLOB_STRLEN(buf); b.len--; blob_push(to, b); } void blob_push_hexdump(blob_t *to, blob_t binary) { char *d; int i; if (blob_is_null(*to)) return; if (to->len < binary.len * 2) { *to = BLOB_NULL; return; } for (i = 0, d = to->ptr; i < binary.len; i++) { *(d++) = xd[(binary.ptr[i] >> 4) & 0xf]; *(d++) = xd[binary.ptr[i] & 0xf]; } to->ptr = d; to->len -= binary.len * 2; } void blob_push_urldecode(blob_t *to, blob_t url) { blob_t b, orig = *to; do { blob_pull_matching(&url, BLOB_STR("/")); b = blob_pull_cspn(&url, BLOB_STR("/")); if (blob_is_null(b) || blob_cmp(b, BLOB_STR(".")) == 0) { /* skip '.' or two consecutive / */ } else if (blob_cmp(b, BLOB_STR("..")) == 0) { /* go up one path component */ blob_shrink_tail(to, blob_pushed(orig, b), '/'); } else { /* copy decoded; FIXME decode percent encoding */ blob_push_byte(to, '/'); blob_push(to, b); } } while (!blob_is_null(url)); } void blob_push_urlencode(blob_t *to, blob_t url) { unsigned char c; int i; for (i = 0; i < url.len; i++) { c = url.ptr[i]; if (c <= 127 && (chartype[c] & CTYPE_UNRESERVED)) { blob_push_byte(to, c); } else { blob_push_byte(to, '%'); blob_push_uint(to, c, 16); } } } blob_t blob_pull(blob_t *b, int len) { blob_t r; if (b->len >= len) { r = BLOB_PTR_LEN(b->ptr, len); b->ptr += len; b->len -= len; return r; } *b = BLOB_NULL; return BLOB_NULL; } void blob_pull_skip(blob_t *b, int len) { if (b->len >= len) { b->ptr += len; b->len -= len; } else { *b = BLOB_NULL; } } int blob_pull_matching(blob_t *b, blob_t e) { if (b->len < e.len) return 0; if (memcmp(b->ptr, e.ptr, e.len) != 0) return 0; b->ptr += e.len; b->len -= e.len; return 1; } unsigned int blob_pull_uint(blob_t *b, int radix) { unsigned int val; int ch; val = 0; while (b->len && b->ptr[0] != 0) { ch = dx(b->ptr[0]); if (ch < 0 || ch >= radix) break; val *= radix; val += ch; b->ptr++; b->len--; } return val; } blob_t blob_pull_spn(blob_t *b, const blob_t reject) { blob_t t = *b; int i; for (i = 0; i < t.len; i++) { if (memchr(reject.ptr, t.ptr[i], reject.len) == NULL) { *b = BLOB_PTR_LEN(t.ptr + i, t.len - i); return BLOB_PTR_LEN(t.ptr, i); } } *b = BLOB_NULL; return t; } blob_t blob_pull_cspn(blob_t *b, const blob_t reject) { blob_t t = *b; int i; for (i = 0; i < t.len; i++) { if (memchr(reject.ptr, t.ptr[i], reject.len) != NULL) { *b = BLOB_PTR_LEN(t.ptr + i, t.len - i); return BLOB_PTR_LEN(t.ptr, i); } } *b = BLOB_NULL; return t; } blob_t blob_expand_head(blob_t *b, blob_t limits, unsigned char sep) { blob_t t = *b; blob_t r; if (t.ptr < limits.ptr || t.ptr+t.len > limits.ptr+limits.len) return BLOB_NULL; while (t.ptr > limits.ptr && t.ptr[-1] == sep) t.ptr--, t.len++; r.ptr = t.ptr; r.len = 0; while (t.ptr > limits.ptr && t.ptr[-1] != sep) { t.ptr--, t.len++; r.ptr--, r.len++; } *b = t; return r; } blob_t blob_expand_tail(blob_t *b, blob_t limits, unsigned char sep) { blob_t t = *b; blob_t r; if (t.ptr < limits.ptr || t.ptr+t.len > limits.ptr+limits.len) return BLOB_NULL; while (t.ptr + t.len < limits.ptr + limits.len && t.ptr[t.len] == sep) t.len++; r.ptr = t.ptr + t.len; r.len = 0; while (t.ptr + t.len < limits.ptr + limits.len && t.ptr[t.len] != sep) { t.len++; r.len++; } *b = t; return r; } blob_t blob_shrink_tail(blob_t *b, blob_t limits, unsigned char sep) { blob_t t = *b; blob_t r; if (t.ptr <= limits.ptr || t.ptr+t.len > limits.ptr+limits.len) return BLOB_NULL; while (t.len && t.ptr[t.len-1] == sep) t.len--; r.ptr = t.ptr; r.len = 0; while (t.len && t.ptr[t.len-1] != sep) { t.len--; r.ptr--, r.len++; } *b = t; return r; }