void buffer_append_strftime(buffer *b, const char *format, const struct tm *tm) { size_t r; char* buf; force_assert(NULL != b); force_assert(NULL != tm); if (NULL == format || '\0' == format[0]) { /* empty format */ buffer_string_prepare_append(b, 0); return; } buf = buffer_string_prepare_append(b, 255); r = strftime(buf, buffer_string_space(b), format, tm); /* 0 (in some apis buffer_string_space(b)) signals the string may have * been too small; but the format could also just have lead to an empty * string */ if (0 == r || r >= buffer_string_space(b)) { /* give it a second try with a larger string */ buf = buffer_string_prepare_append(b, 4095); r = strftime(buf, buffer_string_space(b), format, tm); } if (r >= buffer_string_space(b)) r = 0; buffer_commit(b, r); }
void chunkqueue_get_memory(chunkqueue *cq, char **mem, size_t *len, size_t min_size, size_t alloc_size) { static const size_t REALLOC_MAX_SIZE = 256; chunk *c; buffer *b; char *dummy_mem; size_t dummy_len; force_assert(NULL != cq); if (NULL == mem) mem = &dummy_mem; if (NULL == len) len = &dummy_len; /* default values: */ if (0 == min_size) min_size = 1024; if (0 == alloc_size) alloc_size = 4096; if (alloc_size < min_size) alloc_size = min_size; if (NULL != cq->last && MEM_CHUNK == cq->last->type) { size_t have; b = cq->last->mem; have = buffer_string_space(b); /* unused buffer: allocate space */ if (buffer_string_is_empty(b)) { buffer_string_prepare_copy(b, alloc_size); have = buffer_string_space(b); } /* if buffer is really small just make it bigger */ else if (have < min_size && b->size <= REALLOC_MAX_SIZE) { size_t cur_len = buffer_string_length(b); size_t new_size = cur_len + min_size, append; if (new_size < alloc_size) new_size = alloc_size; append = new_size - cur_len; if (append >= min_size) { buffer_string_prepare_append(b, append); have = buffer_string_space(b); } } /* return pointer into existing buffer if large enough */ if (have >= min_size) { *mem = b->ptr + buffer_string_length(b); *len = have; return; } } /* allocate new chunk */ c = chunkqueue_get_unused_chunk(cq); c->type = MEM_CHUNK; chunkqueue_append_chunk(cq, c); b = c->mem; buffer_string_prepare_append(b, alloc_size); *mem = b->ptr + buffer_string_length(b); *len = buffer_string_space(b); }
static void accesslog_append_escaped(buffer *dest, buffer *str) { char *ptr, *start, *end; /* replaces non-printable chars with \xHH where HH is the hex representation of the byte */ /* exceptions: " => \", \ => \\, whitespace chars => \n \t etc. */ if (buffer_string_is_empty(str)) return; buffer_string_prepare_append(dest, buffer_string_length(str)); for (ptr = start = str->ptr, end = str->ptr + buffer_string_length(str); ptr < end; ptr++) { unsigned char const c = (unsigned char) *ptr; if (c >= ' ' && c <= '~' && c != '"' && c != '\\') { /* nothing to change, add later as one block */ } else { /* copy previous part */ if (start < ptr) { buffer_append_string_len(dest, start, ptr - start); } start = ptr + 1; switch (c) { case '"': BUFFER_APPEND_STRING_CONST(dest, "\\\""); break; case '\\': BUFFER_APPEND_STRING_CONST(dest, "\\\\"); break; case '\b': BUFFER_APPEND_STRING_CONST(dest, "\\b"); break; case '\n': BUFFER_APPEND_STRING_CONST(dest, "\\n"); break; case '\r': BUFFER_APPEND_STRING_CONST(dest, "\\r"); break; case '\t': BUFFER_APPEND_STRING_CONST(dest, "\\t"); break; case '\v': BUFFER_APPEND_STRING_CONST(dest, "\\v"); break; default: { /* non printable char => \xHH */ char hh[5] = {'\\','x',0,0,0}; char h = c / 16; hh[2] = (h > 9) ? (h - 10 + 'A') : (h + '0'); h = c % 16; hh[3] = (h > 9) ? (h - 10 + 'A') : (h + '0'); buffer_append_string_len(dest, &hh[0], 4); } break; } } } if (start < end) { buffer_append_string_len(dest, start, end - start); } }
char* buffer_append_base64_encode(buffer *out, const unsigned char* in, size_t in_length, base64_charset charset) { size_t reserve = 4*(in_length/3) + 4; char* result = buffer_string_prepare_append(out, reserve); size_t out_pos = li_to_base64(result, reserve, in, in_length, charset); buffer_commit(out, out_pos); return result; }
void buffer_append_string_len(buffer *b, const char *s, size_t s_len) { char *target_buf; force_assert(NULL != b); force_assert(NULL != s || s_len == 0); target_buf = buffer_string_prepare_append(b, s_len); if (0 == s_len) return; /* nothing to append */ memcpy(target_buf, s, s_len); buffer_commit(b, s_len); }
void buffer_append_uint_hex(buffer *b, uintmax_t value) { char *buf; int shift = 0; { uintmax_t copy = value; do { copy >>= 8; shift += 2; /* counting nibbles (4 bits) */ } while (0 != copy); } buf = buffer_string_prepare_append(b, shift); buffer_commit(b, shift); /* will fill below */ shift <<= 2; /* count bits now */ while (shift > 0) { shift -= 4; *(buf++) = hex_chars[(value >> shift) & 0x0F]; } }
void buffer_append_string_encoded(buffer *b, const char *s, size_t s_len, buffer_encoding_t encoding) { unsigned char *ds, *d; size_t d_len, ndx; const char *map = NULL; force_assert(NULL != b); force_assert(NULL != s || 0 == s_len); if (0 == s_len) return; switch(encoding) { case ENCODING_REL_URI: map = encoded_chars_rel_uri; break; case ENCODING_REL_URI_PART: map = encoded_chars_rel_uri_part; break; case ENCODING_HTML: map = encoded_chars_html; break; case ENCODING_MINIMAL_XML: map = encoded_chars_minimal_xml; break; case ENCODING_HEX: map = encoded_chars_hex; break; case ENCODING_HTTP_HEADER: map = encoded_chars_http_header; break; } force_assert(NULL != map); /* count to-be-encoded-characters */ for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) { if (map[*ds]) { switch(encoding) { case ENCODING_REL_URI: case ENCODING_REL_URI_PART: d_len += 3; break; case ENCODING_HTML: case ENCODING_MINIMAL_XML: d_len += 6; break; case ENCODING_HTTP_HEADER: case ENCODING_HEX: d_len += 2; break; } } else { d_len++; } } d = (unsigned char*) buffer_string_prepare_append(b, d_len); buffer_commit(b, d_len); /* fill below */ force_assert('\0' == *d); for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) { if (map[*ds]) { switch(encoding) { case ENCODING_REL_URI: case ENCODING_REL_URI_PART: d[d_len++] = '%'; d[d_len++] = hex_chars[((*ds) >> 4) & 0x0F]; d[d_len++] = hex_chars[(*ds) & 0x0F]; break; case ENCODING_HTML: case ENCODING_MINIMAL_XML: d[d_len++] = '&'; d[d_len++] = '#'; d[d_len++] = 'x'; d[d_len++] = hex_chars[((*ds) >> 4) & 0x0F]; d[d_len++] = hex_chars[(*ds) & 0x0F]; d[d_len++] = ';'; break; case ENCODING_HEX: d[d_len++] = hex_chars[((*ds) >> 4) & 0x0F]; d[d_len++] = hex_chars[(*ds) & 0x0F]; break; case ENCODING_HTTP_HEADER: d[d_len++] = *ds; d[d_len++] = '\t'; break; } } else { d[d_len++] = *ds; } }
static int proxy_demux_response(server *srv, handler_ctx *hctx) { int fin = 0; int b; ssize_t r; plugin_data *p = hctx->plugin_data; connection *con = hctx->remote_conn; int proxy_fd = hctx->fd; /* check how much we have to read */ if (ioctl(hctx->fd, FIONREAD, &b)) { log_error_write(srv, __FILE__, __LINE__, "sd", "ioctl failed: ", proxy_fd); return -1; } if (p->conf.debug) { log_error_write(srv, __FILE__, __LINE__, "sd", "proxy - have to read:", b); } if (b > 0) { buffer_string_prepare_append(hctx->response, b); if (-1 == (r = read(hctx->fd, hctx->response->ptr + buffer_string_length(hctx->response), buffer_string_space(hctx->response)))) { if (errno == EAGAIN) return 0; log_error_write(srv, __FILE__, __LINE__, "sds", "unexpected end-of-file (perhaps the proxy process died):", proxy_fd, strerror(errno)); return -1; } /* this should be catched by the b > 0 above */ force_assert(r); buffer_commit(hctx->response, r); #if 0 log_error_write(srv, __FILE__, __LINE__, "sdsbs", "demux: Response buffer len", hctx->response->used, ":", hctx->response, ":"); #endif if (0 == con->got_response) { con->got_response = 1; buffer_string_prepare_copy(hctx->response_header, 1023); } if (0 == con->file_started) { char *c; /* search for the \r\n\r\n in the string */ if (NULL != (c = buffer_search_string_len(hctx->response, CONST_STR_LEN("\r\n\r\n")))) { size_t hlen = c - hctx->response->ptr + 4; size_t blen = buffer_string_length(hctx->response) - hlen; /* found */ buffer_append_string_len(hctx->response_header, hctx->response->ptr, hlen); #if 0 log_error_write(srv, __FILE__, __LINE__, "sb", "Header:", hctx->response_header); #endif /* parse the response header */ proxy_response_parse(srv, con, p, hctx->response_header); /* enable chunked-transfer-encoding */ if (con->request.http_version == HTTP_VERSION_1_1 && !(con->parsed_response & HTTP_CONTENT_LENGTH)) { con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED; } con->file_started = 1; if (blen > 0) http_chunk_append_mem(srv, con, c + 4, blen); buffer_reset(hctx->response); joblist_append(srv, con); } } else { http_chunk_append_buffer(srv, con, hctx->response); joblist_append(srv, con); buffer_reset(hctx->response); } } else { /* reading from upstream done */ con->file_finished = 1; http_chunk_close(srv, con); joblist_append(srv, con); fin = 1; } return fin; }
unsigned char* buffer_append_base64_decode(buffer *out, const char* in, size_t in_length, base64_charset charset) { unsigned char *result; size_t out_pos = 0; /* current output character (position) that is decoded. can contain partial result */ unsigned int group = 0; /* how many base64 digits in the current group were decoded already. each group has up to 4 digits */ size_t i; const short* base64_reverse_table; switch (charset) { case BASE64_STANDARD: base64_reverse_table = base64_standard_reverse_table; break; case BASE64_URL: base64_reverse_table = base64_url_reverse_table; break; default: return NULL; } result = (unsigned char *) buffer_string_prepare_append(out, 3*(in_length / 4) + 3); /* run through the whole string, converting as we go */ for (i = 0; i < in_length; i++) { unsigned char c = (unsigned char) in[i]; short ch; if (c == '\0') break; if (c >= 128) return NULL; /* only 7-bit characters allowed */ ch = base64_reverse_table[c]; if (-3 == ch) { /* pad character; can only come after 2 base64 digits in a group */ if (group < 2) return NULL; break; } else if (-2 == ch) { continue; /* skip character */ } else if (ch < 0) { return NULL; /* invalid character, abort */ } switch(group) { case 0: result[out_pos] = ch << 2; group = 1; break; case 1: result[out_pos++] |= ch >> 4; result[out_pos] = (ch & 0x0f) << 4; group = 2; break; case 2: result[out_pos++] |= ch >>2; result[out_pos] = (ch & 0x03) << 6; group = 3; break; case 3: result[out_pos++] |= ch; group = 0; break; } } switch(group) { case 0: /* ended on boundary */ break; case 1: /* need at least 2 base64 digits per group */ return NULL; case 2: /* have 2 base64 digits in last group => one real octect, two zeroes padded */ case 3: /* have 3 base64 digits in last group => two real octects, one zero padded */ /* for both cases the current index already is on the first zero padded octet * - check it really is zero (overlapping bits) */ if (0 != result[out_pos]) return NULL; break; } buffer_commit(out, out_pos); return result; }