#include "usuals.h" #include "httpauthd.h" #include "stringx.h" #include /* ----------------------------------------------------------------------- * Memory Buffer */ #define BUF_IS_EMPTY(b) ((b)->_pp == (b)->_rp) #define BUF_IN_JOIN(b) (!BUF_IS_EMPTY(b) && *((b)->_rp - 1) != 0) #define BUF_NEW_BLOCK(b) ((b)->_pp = (b)->_rp) /* TODO: Increase this size to 2048 once we're done debugging */ #define BUF_INITIAL 80 #define BUF_DELTA 16 #define INTERNAL_DATA(intl) ((void*)(((unsigned char*)(intl)) + sizeof(*(intl)))) #define FILL_BETWEEN(b, e, x) memset((b), x, ((char*)(e)) - ((char*)(b))); /* * LEGEND: * _ft: first buffer block * _dt: current buffer block * _rp: read/write point * _pp: parse/begin point */ typedef struct ha_buffer_internal { char* end; struct ha_buffer_internal* next; } internal_t; void buffer_bump(ha_buffer_t* buf, int count) { int allocated; internal_t* intl; ASSERT(buf && count); if(ha_buferr(buf)) return; ASSERT(BUF_DELTA < BUF_INITIAL); /* Handle this common case first for efficiency */ if(buf->_rp + (count + BUF_DELTA) < buf->_dt->end) return; /* Now when in joins we let it go closer */ if(BUF_IN_JOIN(buf) && (buf->_rp + count < buf->_dt->end)) return; /* Okay now we know we have to extend */ allocated = buf->_dt->end - (char*)(buf->_dt); ASSERT(allocated > 0 && (allocated % BUF_INITIAL) == 0); /* Enlarge the size as much as needed */ do { allocated *= 2; } while(allocated < count); ASSERT(sizeof(internal_t) < allocated); intl = (internal_t*)malloc(allocated); if(!intl) { ha_messagex(LOG_CRIT, "out of memory"); buf->_dt = NULL; buf->_pp = buf->_rp = NULL; return; } else { void* beg = INTERNAL_DATA(intl); intl->end = ((char*)(intl) + allocated); intl->next = NULL; #ifdef _DEBUG FILL_BETWEEN(beg, intl->end, 0xCD); #endif if(BUF_IN_JOIN(buf)) { int diff = buf->_rp - buf->_pp; /* Always true in a join */ ASSERT(buf->_pp < buf->_rp); /* Copy the memory and blank out old */ memcpy(beg, buf->_pp, diff); #ifdef _DEBUG FILL_BETWEEN(buf->_pp, buf->_rp, 0xDD); #endif buf->_pp = beg; buf->_rp = buf->_pp + diff; } else { buf->_rp = buf->_pp = INTERNAL_DATA(intl); } buf->_dt->next = intl; buf->_dt = intl; } } void ha_bufinit(ha_buffer_t* buf) { ASSERT(buf); memset(buf, 0, sizeof(*buf)); ASSERT(BUF_INITIAL > sizeof(internal_t)); buf->_ft = (internal_t*)malloc(BUF_INITIAL); if(buf->_ft) { buf->_ft->end = ((char*)(buf->_ft) + BUF_INITIAL); buf->_ft->next = NULL; #ifdef _DEBUG FILL_BETWEEN(INTERNAL_DATA(buf->_ft), buf->_ft->end, 0xCD); #endif ha_bufreset(buf); } } void ha_bufreset(ha_buffer_t* buf) { #ifdef _DEBUG internal_t* intl; #endif ASSERT(buf); ASSERT(buf->_ft); #ifdef _DEBUG /* Go through all the buffers and set them to 0xCD */ for(intl = buf->_ft; intl; intl = intl->next) FILL_BETWEEN(INTERNAL_DATA(intl), intl->end, 0xCD); #endif buf->_dt = buf->_ft; buf->_rp = buf->_pp = (char*)INTERNAL_DATA(buf->_ft); } void ha_buffree(ha_buffer_t* buf) { internal_t* intl; internal_t* next; ASSERT(buf); /* Go through free all the buffers and set them to 0xDD */ for(intl = buf->_ft; intl; intl = next) { next = intl->next; FILL_BETWEEN(INTERNAL_DATA(intl), intl->end, 0xDD); free(intl); } buf->_ft = buf->_dt = NULL; buf->_rp = buf->_pp = NULL; } int ha_bufreadline(int fd, ha_buffer_t* buf) { int l; ASSERT(buf); ASSERT(fd != -1); if(ha_buferr(buf)) return 0; for(;;) { buffer_bump(buf, 1); l = read(fd, (void*)buf->_rp, sizeof(char)); /* We got a character */ if(l == 1) { /* Skip junky CRLFs */ if(*(buf->_rp) == '\r') { *(buf->_rp) = ' '; continue; } /* End of line */ else if(*(buf->_rp) == '\n') { buf->_rp++; break; } /* All other characters */ else { buf->_rp++; } } /* If it's the end of file then return that */ else if(l == 0) return 0; /* Transient errors */ else if(l == -1 && errno == EAGAIN) continue; /* Fatal errors */ else if(l == -1) { if(errno != EINTR) ha_message(LOG_ERR, "couldn't read data"); return 0; } } return 1; } char* ha_bufparseword(ha_buffer_t* buf, const char* delims) { char* word = NULL; ASSERT(buf && delims); if(ha_buferr(buf)) return NULL; /* Knock out any previous delims */ while(buf->_pp < buf->_rp && strchr(delims, *(buf->_pp))) buf->_pp++; /* If at end of buffer or end of line return null */ if(buf->_pp == buf->_rp || *(buf->_pp) == '\n') return NULL; /* We do this before we stash away a pointer */ buffer_bump(buf, 1); word = buf->_pp; while(!strchr(delims, *(buf->_pp))) { buf->_pp++; /* At the end of the buffer */ if(buf->_pp == buf->_rp) { *(buf->_rp) = 0; buf->_rp++; break; } /* At the end of a line */ else if(*(buf->_pp) == '\n') break; } /* Now null terminate what we found */ *(buf->_pp) = 0; buf->_pp++; /* We don't return empty strings */ if(word[0] == 0) return NULL; return word; } char* ha_bufparseline(ha_buffer_t* buf, int trim) { char* t; char* line = NULL; ASSERT(buf); if(ha_buferr(buf)) return NULL; if(trim) { /* Knock out any previous whitespace */ while(buf->_pp < buf->_rp && isblank(*(buf->_pp))) buf->_pp++; } if(buf->_pp == buf->_rp) return NULL; /* We do this before we stash away a pointer */ buffer_bump(buf, 1); line = buf->_pp; t = (char*)memchr(buf->_pp, '\n', ha_buflen(buf)); if(t == NULL) { t = (buf->_rp); buf->_rp++; } *t = 0; buf->_pp = t + 1; if(trim) line = trim_space(line); /* We don't return empty strings */ if(line[0] == 0) return NULL; return line; } char* ha_bufmcat(ha_buffer_t* buf, ...) { const char* str; va_list ap; ASSERT(buf); va_start(ap, buf); if(ha_buferr(buf)) return NULL; if(!BUF_IN_JOIN(buf)) BUF_NEW_BLOCK(buf); while((str = va_arg(ap, char*)) != NULL) { int len = strlen(str); /* Always add one for the terminating char */ buffer_bump(buf, len + 1); if(ha_buferr(buf)) return NULL; /* _rp always points to the next write point */ strcpy(buf->_rp, str); buf->_rp += len; } buf->_rp++; return buf->_pp; } char* ha_bufcpy(ha_buffer_t* buf, const char* src) { size_t len; ASSERT(buf && src); len = strlen(src); return ha_bufncpy(buf, src, len); } char* ha_bufncpy(ha_buffer_t* buf, const char* src, size_t len) { ASSERT(buf && src); if(ha_buferr(buf)) return NULL; /* Always add one for the terminating char */ buffer_bump(buf, len + 1); if(ha_buferr(buf)) return NULL; if(!BUF_IN_JOIN(buf)) BUF_NEW_BLOCK(buf); memcpy(buf->_rp, src, len * sizeof(char)); buf->_rp += (len + 1); *(buf->_rp - 1) = 0; return buf->_pp; } void* ha_bufmalloc(ha_buffer_t* buf, size_t sz) { void* ret; ASSERT(buf && sz); if(ha_buferr(buf)) return NULL; /* TODO: Align memory on an appropriate boundary here */ buffer_bump(buf, sz); if(ha_buferr(buf)) return NULL; BUF_NEW_BLOCK(buf); ret = (void*)buf->_pp; buf->_rp += sz; buf->_pp = buf->_rp; return ret; } void* ha_bufmemdup(ha_buffer_t* buf, const void* src, size_t bytes) { void* mem; ASSERT(buf && src && bytes); if((mem = ha_bufmalloc(buf, bytes)) != NULL) memcpy(mem, src, bytes); return mem; } /* * Portions Copyright (c) 1995 by International Business Machines, Inc. * * International Business Machines, Inc. (hereinafter called IBM) grants * permission under its copyrights to use, copy, modify, and distribute this * Software with or without fee, provided that the above copyright notice and * all paragraphs of this notice appear in all copies, and that the name of IBM * not be used in connection with the marketing of any product incorporating * the Software or modifications thereof, without specific, written prior * permission. * * To the extent it has a right to do so, IBM grants an immunity from suit * under its patents, if any, for the use, sale or manufacture of products to * the extent that such products are used for performing Domain Name System * dynamic updates in TCP/IP networks by means of the Software. No immunity is * granted for any product per se or for any other function of any product. * * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. */ static const char BASE64C[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static const char PAD64C = '='; char* ha_bufenc64(ha_buffer_t* buf, const void* source, size_t len) { unsigned char input[3]; unsigned char output[4]; unsigned char* src = (unsigned char*)source; size_t i; ASSERT(buf && source && len); if(ha_buferr(buf)) return NULL; if(!BUF_IN_JOIN(buf)) BUF_NEW_BLOCK(buf); while(2 < len) { input[0] = *src++; input[1] = *src++; input[2] = *src++; len -= 3; output[0] = input[0] >> 2; output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); output[3] = input[2] & 0x3f; /* This also accounts for the null terminator */ buffer_bump(buf, 5); if(ha_buferr(buf)) return NULL; *(buf->_rp++) = BASE64C[output[0]]; *(buf->_rp++) = BASE64C[output[1]]; *(buf->_rp++) = BASE64C[output[2]]; *(buf->_rp++) = BASE64C[output[3]]; } /* Now we worry about padding. */ if(0 != len) { /* Get what's left. */ input[0] = input[1] = input[2] = '\0'; for(i = 0; i < len; i++) input[i] = *src++; output[0] = input[0] >> 2; output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); /* This also accounts for the null terminator */ buffer_bump(buf, 5); if(ha_buferr(buf)) return NULL; *(buf->_rp++) = BASE64C[output[0]]; *(buf->_rp++) = BASE64C[output[1]]; if(len == 1) *(buf->_rp++) = PAD64C; else *(buf->_rp++) = BASE64C[output[2]]; *(buf->_rp++) = PAD64C; } *(buf->_rp++) = '\0'; return buf->_pp; } void* ha_bufdec64(ha_buffer_t* buf, const char* src, size_t* bytes) { int state = 0; int ch; char* pos; void* ret; size_t todo = 0; size_t done = 0; ASSERT(buf && src); if(ha_buferr(buf)) return NULL; BUF_NEW_BLOCK(buf); if(!bytes || *bytes == 0) todo = ~0; else todo = *bytes; while((ch = *src++) != '\0' && done < todo) { if(isspace(ch)) /* Skip whitespace anywhere. */ continue; if(ch == PAD64C) break; pos = strchr(BASE64C, ch); if(pos == 0) /* A non-base64 character. */ break; buffer_bump(buf, 4); if(ha_buferr(buf)) return NULL; switch(state) { case 0: *(buf->_rp) = (pos - BASE64C) << 2; state = 1; break; case 1: *(buf->_rp++) |= (pos - BASE64C) >> 4; done++; *(buf->_rp) = ((pos - BASE64C) & 0x0f) << 4; state = 2; break; case 2: *(buf->_rp++) |= (pos - BASE64C) >> 2; done++; *(buf->_rp) = ((pos - BASE64C) & 0x03) << 6; state = 3; break; case 3: *(buf->_rp++) |= (pos - BASE64C); done++; state = 0; break; }; } if(state != 3) buf->_rp++; /* TODO: Validate ending and return error if invalid somehow */ /* We always null terminate anyway */ *(buf->_rp++) = 0; if(bytes) *bytes = done; ret = (void*)buf->_pp; buf->_pp = buf->_rp; return ret; } static const char HEXC[] = "0123456789abcdef"; char* ha_bufenchex(ha_buffer_t* buf, const void* source, size_t len) { unsigned char* src = (unsigned char*)source; unsigned char j; ASSERT(buf && source && len); buffer_bump(buf, (len * 2) + 1); if(ha_buferr(buf)) return NULL; if(!BUF_IN_JOIN(buf)) BUF_NEW_BLOCK(buf); while(len > 0) { j = *(src) >> 4 & 0xf; *(buf->_rp++) = HEXC[j]; j = *(src++) & 0xf; *(buf->_rp++) = HEXC[j]; len--; } *(buf->_rp++) = 0; return buf->_pp; } void* ha_bufdechex(ha_buffer_t* buf, const char* src, size_t* bytes) { unsigned short j; int state = 0; char* pos; void* ret; size_t done = 0; size_t todo = 0; ASSERT(buf && src); if(bytes && *bytes != 0) { buffer_bump(buf, *bytes + 1); todo = *bytes; } else { todo = ~0; buffer_bump(buf, (strlen(src) / 2) + 1); } if(ha_buferr(buf)) return NULL; BUF_NEW_BLOCK(buf); while(src[0] && done < todo) { /* Find the position */ pos = strchr(HEXC, tolower(src[0])); if(pos == 0) break; j = pos - HEXC; if(!state) { *(buf->_rp) = (j & 0xf) << 4; state = 1; } else { *(buf->_rp++) |= (j & 0xf); done++; state = 0; } src++; } /* We always null terminate anyway */ *(buf->_rp++) = 0; /* All bytes have to come in pairs */ if(state != 0) return NULL; if(bytes) *bytes = done; ret = (void*)buf->_pp; buf->_pp = buf->_rp; return ret; }