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); }
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_copy_string_len(buffer *b, const char *s, size_t s_len) { force_assert(NULL != b); force_assert(NULL != s || s_len == 0); buffer_string_prepare_copy(b, s_len); if (0 != s_len) memcpy(b->ptr, s, s_len); buffer_commit(b, s_len); }
static int stat_cache_attr_get(buffer *buf, char *name) { int attrlen; int ret; buffer_string_prepare_copy(buf, 1023); attrlen = buf->size - 1; if(0 == (ret = attr_get(name, "Content-Type", buf->ptr, &attrlen, 0))) { buffer_commit(buf, attrlen); } return ret; }
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 chunkqueue_use_memory(chunkqueue *cq, size_t len) { buffer *b; force_assert(NULL != cq); force_assert(NULL != cq->last && MEM_CHUNK == cq->last->type); b = cq->last->mem; if (len > 0) { buffer_commit(b, len); } else if (buffer_string_is_empty(b)) { /* unused buffer: can't remove chunk easily from * end of list, so just reset the buffer */ buffer_reset(b); } }
static void test_buffer_string_space(void) { buffer *b = buffer_init(); size_t space; space = buffer_string_space(b); assert(0 == space); buffer_copy_string_len(b, CONST_STR_LEN("")); space = buffer_string_space(b); assert(space > 0); assert(space + buffer_string_length(b) == b->size - 1); buffer_commit(b, b->size - 1); assert(b->used == b->size); space = buffer_string_space(b); assert(0 == space); buffer_free(b); }
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]; } }
static int cgi_demux_response(server *srv, handler_ctx *hctx) { plugin_data *p = hctx->plugin_data; connection *con = hctx->remote_conn; while(1) { int n; int toread; #if defined(__WIN32) buffer_string_prepare_copy(hctx->response, 4 * 1024); #else if (ioctl(con->fd, FIONREAD, &toread) || toread <= 4*1024) { buffer_string_prepare_copy(hctx->response, 4 * 1024); } else { if (toread > MAX_READ_LIMIT) toread = MAX_READ_LIMIT; buffer_string_prepare_copy(hctx->response, toread); } #endif if (-1 == (n = read(hctx->fd, hctx->response->ptr, hctx->response->size - 1))) { if (errno == EAGAIN || errno == EINTR) { /* would block, wait for signal */ fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); return FDEVENT_HANDLED_NOT_FINISHED; } /* error */ log_error_write(srv, __FILE__, __LINE__, "sdd", strerror(errno), con->fd, hctx->fd); return FDEVENT_HANDLED_ERROR; } if (n == 0) { /* read finished */ return FDEVENT_HANDLED_FINISHED; } buffer_commit(hctx->response, n); /* split header from body */ if (con->file_started == 0) { int is_header = 0; int is_header_end = 0; size_t last_eol = 0; size_t i, header_len; buffer_append_string_buffer(hctx->response_header, hctx->response); /** * we have to handle a few cases: * * nph: * * HTTP/1.0 200 Ok\n * Header: Value\n * \n * * CGI: * Header: Value\n * Status: 200\n * \n * * and different mixes of \n and \r\n combinations * * Some users also forget about CGI and just send a response and hope * we handle it. No headers, no header-content seperator * */ /* nph (non-parsed headers) */ if (0 == strncmp(hctx->response_header->ptr, "HTTP/1.", 7)) is_header = 1; header_len = buffer_string_length(hctx->response_header); for (i = 0; !is_header_end && i < header_len; i++) { char c = hctx->response_header->ptr[i]; switch (c) { case ':': /* we found a colon * * looks like we have a normal header */ is_header = 1; break; case '\n': /* EOL */ if (is_header == 0) { /* we got a EOL but we don't seem to got a HTTP header */ is_header_end = 1; break; } /** * check if we saw a \n(\r)?\n sequence */ if (last_eol > 0 && ((i - last_eol == 1) || (i - last_eol == 2 && hctx->response_header->ptr[i - 1] == '\r'))) { is_header_end = 1; break; } last_eol = i; break; } } if (is_header_end) { if (!is_header) { /* no header, but a body */ if (0 != http_chunk_append_buffer(srv, con, hctx->response_header)) { return FDEVENT_HANDLED_ERROR; } } else { const char *bstart; size_t blen; /* the body starts after the EOL */ bstart = hctx->response_header->ptr + i; blen = header_len - i; /** * i still points to the char after the terminating EOL EOL * * put it on the last \n again */ i--; /* string the last \r?\n */ if (i > 0 && (hctx->response_header->ptr[i - 1] == '\r')) { i--; } buffer_string_set_length(hctx->response_header, i); /* parse the response header */ cgi_response_parse(srv, con, p, hctx->response_header); if (con->http_status >= 300 && con->http_status < 400) { /*(con->parsed_response & HTTP_LOCATION)*/ data_string *ds; if (NULL != (ds = (data_string *) array_get_element(con->response.headers, "Location")) && ds->value->ptr[0] == '/') { if (++con->loops_per_request > 5) { log_error_write(srv, __FILE__, __LINE__, "sb", "too many internal loops while processing request:", con->request.orig_uri); con->http_status = 500; /* Internal Server Error */ con->mode = DIRECT; return FDEVENT_HANDLED_FINISHED; } buffer_copy_buffer(con->request.uri, ds->value); if (con->request.content_length) { if (con->request.content_length != con->request_content_queue->bytes_in) { con->keep_alive = 0; } con->request.content_length = 0; chunkqueue_reset(con->request_content_queue); } if (con->http_status != 307 && con->http_status != 308) { /* Note: request body (if any) sent to initial dynamic handler * and is not available to the internal redirect */ con->request.http_method = HTTP_METHOD_GET; } connection_response_reset(srv, con); /*(includes con->http_status = 0)*/ con->mode = DIRECT; return FDEVENT_HANDLED_COMEBACK; } } if (hctx->conf.xsendfile_allow) { data_string *ds; if (NULL != (ds = (data_string *) array_get_element(con->response.headers, "X-Sendfile"))) { http_response_xsendfile(srv, con, ds->value, hctx->conf.xsendfile_docroot); return FDEVENT_HANDLED_FINISHED; } } if (blen > 0) { if (0 != http_chunk_append_mem(srv, con, bstart, blen)) { return FDEVENT_HANDLED_ERROR; } } } con->file_started = 1; } else { /*(reuse MAX_HTTP_REQUEST_HEADER as max size for response headers from backends)*/ if (header_len > MAX_HTTP_REQUEST_HEADER) { log_error_write(srv, __FILE__, __LINE__, "sb", "response headers too large for", con->uri.path); con->http_status = 502; /* Bad Gateway */ con->mode = DIRECT; return FDEVENT_HANDLED_FINISHED; } } } else { if (0 != http_chunk_append_buffer(srv, con, hctx->response)) { return FDEVENT_HANDLED_ERROR; } if ((con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) && chunkqueue_length(con->write_queue) > 65536 - 4096) { if (!con->is_writable) { /*(defer removal of FDEVENT_IN interest since * connection_state_machine() might be able to send data * immediately, unless !con->is_writable, where * connection_state_machine() might not loop back to call * mod_cgi_handle_subrequest())*/ fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); } break; } } #if 0 log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), b->ptr); #endif } return FDEVENT_HANDLED_NOT_FINISHED; }
static void https_add_ssl_entries(connection *con) { X509 *xs; X509_NAME *xn; X509_NAME_ENTRY *xe; int i, nentries; if ( SSL_get_verify_result(con->ssl) != X509_V_OK || !(xs = SSL_get_peer_certificate(con->ssl)) ) { return; } xn = X509_get_subject_name(xs); for (i = 0, nentries = X509_NAME_entry_count(xn); i < nentries; ++i) { int xobjnid; const char * xobjsn; data_string *envds; if (!(xe = X509_NAME_get_entry(xn, i))) { continue; } xobjnid = OBJ_obj2nid((ASN1_OBJECT*)X509_NAME_ENTRY_get_object(xe)); xobjsn = OBJ_nid2sn(xobjnid); if (!xobjsn) { continue; } if (NULL == (envds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) { envds = data_string_init(); } buffer_copy_string_len(envds->key, CONST_STR_LEN("SSL_CLIENT_S_DN_")); buffer_append_string(envds->key, xobjsn); buffer_copy_string_len( envds->value, (const char *)xe->value->data, xe->value->length ); /* pick one of the exported values as "REMOTE_USER", for example * ssl.verifyclient.username = "******" or "SSL_CLIENT_S_DN_emailAddress" */ if (buffer_is_equal(con->conf.ssl_verifyclient_username, envds->key)) { data_string *ds; if (NULL == (ds = (data_string *)array_get_element(con->environment, "REMOTE_USER"))) { if (NULL == (ds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) { ds = data_string_init(); } buffer_copy_string(ds->key, "REMOTE_USER"); array_insert_unique(con->environment, (data_unset *)ds); } buffer_copy_buffer(ds->value, envds->value); } array_insert_unique(con->environment, (data_unset *)envds); } if (con->conf.ssl_verifyclient_export_cert) { BIO *bio; if (NULL != (bio = BIO_new(BIO_s_mem()))) { data_string *envds; int n; PEM_write_bio_X509(bio, xs); n = BIO_pending(bio); if (NULL == (envds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) { envds = data_string_init(); } buffer_copy_string_len(envds->key, CONST_STR_LEN("SSL_CLIENT_CERT")); buffer_string_prepare_copy(envds->value, n); BIO_read(bio, envds->value->ptr, n); BIO_free(bio); buffer_commit(envds->value, n); array_insert_unique(con->environment, (data_unset *)envds); } } X509_free(xs); }
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 mod_rrdtool_create_rrd(server *srv, plugin_data *p, plugin_config *s) { struct stat st; int r; /* check if DB already exists */ if (0 == stat(s->path_rrd->ptr, &st)) { /* check if it is plain file */ if (!S_ISREG(st.st_mode)) { log_error_write(srv, __FILE__, __LINE__, "sb", "not a regular file:", s->path_rrd); return HANDLER_ERROR; } /* still create DB if it's empty file */ if (st.st_size > 0) { return HANDLER_GO_ON; } } /* create a new one */ buffer_copy_string_len(p->cmd, CONST_STR_LEN("create ")); buffer_append_string_buffer(p->cmd, s->path_rrd); buffer_append_string_len(p->cmd, CONST_STR_LEN( " --step 60 " "DS:InOctets:ABSOLUTE:600:U:U " "DS:OutOctets:ABSOLUTE:600:U:U " "DS:Requests:ABSOLUTE:600:U:U " "RRA:AVERAGE:0.5:1:600 " "RRA:AVERAGE:0.5:6:700 " "RRA:AVERAGE:0.5:24:775 " "RRA:AVERAGE:0.5:288:797 " "RRA:MAX:0.5:1:600 " "RRA:MAX:0.5:6:700 " "RRA:MAX:0.5:24:775 " "RRA:MAX:0.5:288:797 " "RRA:MIN:0.5:1:600 " "RRA:MIN:0.5:6:700 " "RRA:MIN:0.5:24:775 " "RRA:MIN:0.5:288:797\n")); if (-1 == (safe_write(p->write_fd, CONST_BUF_LEN(p->cmd)))) { log_error_write(srv, __FILE__, __LINE__, "ss", "rrdtool-write: failed", strerror(errno)); return HANDLER_ERROR; } buffer_string_prepare_copy(p->resp, 4095); if (-1 == (r = safe_read(p->read_fd, p->resp->ptr, p->resp->size - 1))) { log_error_write(srv, __FILE__, __LINE__, "ss", "rrdtool-read: failed", strerror(errno)); return HANDLER_ERROR; } buffer_commit(p->resp, r); if (p->resp->ptr[0] != 'O' || p->resp->ptr[1] != 'K') { log_error_write(srv, __FILE__, __LINE__, "sbb", "rrdtool-response:", p->cmd, p->resp); return HANDLER_ERROR; } return HANDLER_GO_ON; }
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; }