Beispiel #1
0
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);
}
Beispiel #2
0
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;
}
Beispiel #3
0
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);
}
Beispiel #4
0
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;
}
Beispiel #5
0
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);
}
Beispiel #6
0
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);
	}
}
Beispiel #7
0
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);
}
Beispiel #8
0
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];
	}
}
Beispiel #9
0
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;
}
Beispiel #10
0
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);
}
Beispiel #11
0
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;
		}
	}
Beispiel #12
0
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;
}
Beispiel #13
0
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;
}
Beispiel #14
0
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;
}