commit 541b15d: [Rework] Re-add files

Vsevolod Stakhov vsevolod at highsecure.ru
Fri Feb 15 18:28:05 UTC 2019


Author: Vsevolod Stakhov
Date: 2019-02-15 16:41:46 +0000
URL: https://github.com/rspamd/rspamd/commit/541b15df290f0aa40b71a85379f3faa909de30b6

[Rework] Re-add files

---
 src/libutil/http_connection.c | 3971 +++++++++++++++++++++++++++++++++++++++++
 src/libutil/http_connection.h |  578 ++++++
 2 files changed, 4549 insertions(+)

diff --git a/src/libutil/http_connection.c b/src/libutil/http_connection.c
new file mode 100644
index 000000000..a82fc24f7
--- /dev/null
+++ b/src/libutil/http_connection.c
@@ -0,0 +1,3971 @@
+/*-
+ * Copyright 2016 Vsevolod Stakhov
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "config.h"
+#include "../../contrib/mumhash/mum.h"
+#include "http_private.h"
+#include "utlist.h"
+#include "util.h"
+#include "printf.h"
+#include "logger.h"
+#include "ref.h"
+#include "ottery.h"
+#include "keypair_private.h"
+#include "cryptobox.h"
+#include "unix-std.h"
+#include "libutil/ssl_util.h"
+#include "libutil/regexp.h"
+#include "libserver/url.h"
+
+#include <openssl/err.h>
+
+#define ENCRYPTED_VERSION " HTTP/1.0"
+
+struct _rspamd_http_privbuf {
+	rspamd_fstring_t *data;
+	const gchar *zc_buf;
+	gsize zc_remain;
+	ref_entry_t ref;
+};
+
+enum rspamd_http_priv_flags {
+	RSPAMD_HTTP_CONN_FLAG_ENCRYPTED = 1 << 0,
+	RSPAMD_HTTP_CONN_FLAG_NEW_HEADER = 1 << 1,
+	RSPAMD_HTTP_CONN_FLAG_RESETED = 1 << 2,
+	RSPAMD_HTTP_CONN_FLAG_TOO_LARGE = 1 << 3,
+	RSPAMD_HTTP_CONN_FLAG_ENCRYPTION_NEEDED = 1 << 4,
+};
+
+#define IS_CONN_ENCRYPTED(c) ((c)->flags & RSPAMD_HTTP_CONN_FLAG_ENCRYPTED)
+#define IS_CONN_RESETED(c) ((c)->flags & RSPAMD_HTTP_CONN_FLAG_RESETED)
+
+struct rspamd_http_connection_private {
+	gpointer ssl_ctx;
+	struct rspamd_ssl_connection *ssl;
+	struct _rspamd_http_privbuf *buf;
+	struct rspamd_cryptobox_pubkey *peer_key;
+	struct rspamd_cryptobox_keypair *local_key;
+	struct rspamd_http_header *header;
+	struct http_parser parser;
+	struct http_parser_settings parser_cb;
+	struct event ev;
+	struct timeval tv;
+	struct timeval *ptv;
+	struct rspamd_http_message *msg;
+	struct iovec *out;
+	guint outlen;
+	enum rspamd_http_priv_flags flags;
+	gsize wr_pos;
+	gsize wr_total;
+};
+
+enum http_magic_type {
+	HTTP_MAGIC_PLAIN = 0,
+	HTTP_MAGIC_HTML,
+	HTTP_MAGIC_CSS,
+	HTTP_MAGIC_JS,
+	HTTP_MAGIC_PNG,
+	HTTP_MAGIC_JPG
+};
+
+static const struct _rspamd_http_magic {
+	const gchar *ext;
+	const gchar *ct;
+} http_file_types[] = {
+	[HTTP_MAGIC_PLAIN] = { "txt", "text/plain" },
+	[HTTP_MAGIC_HTML] = { "html", "text/html" },
+	[HTTP_MAGIC_CSS] = { "css", "text/css" },
+	[HTTP_MAGIC_JS] = { "js", "application/javascript" },
+	[HTTP_MAGIC_PNG] = { "png", "image/png" },
+	[HTTP_MAGIC_JPG] = { "jpg", "image/jpeg" },
+};
+
+static const gchar *http_week[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+static const gchar *http_month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+							   "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+static const rspamd_ftok_t key_header = {
+		.begin = "Key",
+		.len = 3
+};
+static const rspamd_ftok_t date_header = {
+		.begin = "Date",
+		.len = 4
+};
+static const rspamd_ftok_t last_modified_header = {
+		.begin = "Last-Modified",
+		.len = 13
+};
+
+static void rspamd_http_message_storage_cleanup (struct rspamd_http_message *msg);
+static gboolean rspamd_http_message_grow_body (struct rspamd_http_message *msg,
+		gsize len);
+
+#define HTTP_ERROR http_error_quark ()
+GQuark
+http_error_quark (void)
+{
+	return g_quark_from_static_string ("http-error-quark");
+}
+
+static void
+rspamd_http_privbuf_dtor (gpointer ud)
+{
+	struct _rspamd_http_privbuf *p = (struct _rspamd_http_privbuf *)ud;
+
+	if (p->data) {
+		rspamd_fstring_free (p->data);
+	}
+
+	g_free (p);
+}
+
+static const gchar *
+rspamd_http_code_to_str (gint code)
+{
+	if (code == 200) {
+		return "OK";
+	}
+	else if (code == 404) {
+		return "Not found";
+	}
+	else if (code == 403 || code == 401) {
+		return "Not authorized";
+	}
+	else if (code >= 400 && code < 500) {
+		return "Bad request";
+	}
+	else if (code >= 300 && code < 400) {
+		return "See Other";
+	}
+	else if (code >= 500 && code < 600) {
+		return "Internal server error";
+	}
+
+	return "Unknown error";
+}
+
+/*
+ * Obtained from nginx
+ * Copyright (C) Igor Sysoev
+ */
+static guint mday[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+time_t
+rspamd_http_parse_date (const gchar *header, gsize len)
+{
+	const gchar *p, *end;
+	gint month;
+	guint day, year, hour, min, sec;
+	guint64 time;
+	enum {
+		no = 0, rfc822, /* Tue, 10 Nov 2002 23:50:13   */
+		rfc850, /* Tuesday, 10-Dec-02 23:50:13 */
+		isoc /* Tue Dec 10 23:50:13 2002    */
+	} fmt;
+
+	fmt = 0;
+	if (len > 0) {
+		end = header + len;
+	}
+	else {
+		end = header + strlen (header);
+	}
+
+	day = 32;
+	year = 2038;
+
+	for (p = header; p < end; p++) {
+		if (*p == ',') {
+			break;
+		}
+
+		if (*p == ' ') {
+			fmt = isoc;
+			break;
+		}
+	}
+
+	for (p++; p < end; p++)
+		if (*p != ' ') {
+			break;
+		}
+
+	if (end - p < 18) {
+		return (time_t)-1;
+	}
+
+	if (fmt != isoc) {
+		if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
+			return (time_t)-1;
+		}
+
+		day = (*p - '0') * 10 + *(p + 1) - '0';
+		p += 2;
+
+		if (*p == ' ') {
+			if (end - p < 18) {
+				return (time_t)-1;
+			}
+			fmt = rfc822;
+
+		}
+		else if (*p == '-') {
+			fmt = rfc850;
+
+		}
+		else {
+			return (time_t)-1;
+		}
+
+		p++;
+	}
+
+	switch (*p) {
+
+	case 'J':
+		month = *(p + 1) == 'a' ? 0 : *(p + 2) == 'n' ? 5 : 6;
+		break;
+
+	case 'F':
+		month = 1;
+		break;
+
+	case 'M':
+		month = *(p + 2) == 'r' ? 2 : 4;
+		break;
+
+	case 'A':
+		month = *(p + 1) == 'p' ? 3 : 7;
+		break;
+
+	case 'S':
+		month = 8;
+		break;
+
+	case 'O':
+		month = 9;
+		break;
+
+	case 'N':
+		month = 10;
+		break;
+
+	case 'D':
+		month = 11;
+		break;
+
+	default:
+		return (time_t)-1;
+	}
+
+	p += 3;
+
+	if ((fmt == rfc822 && *p != ' ') || (fmt == rfc850 && *p != '-')) {
+		return (time_t)-1;
+	}
+
+	p++;
+
+	if (fmt == rfc822) {
+		if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9'
+			|| *(p + 2) < '0' || *(p + 2) > '9' || *(p + 3) < '0'
+			|| *(p + 3) > '9') {
+			return (time_t)-1;
+		}
+
+		year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100
+			+ (*(p + 2) - '0') * 10 + *(p + 3) - '0';
+		p += 4;
+
+	}
+	else if (fmt == rfc850) {
+		if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
+			return (time_t)-1;
+		}
+
+		year = (*p - '0') * 10 + *(p + 1) - '0';
+		year += (year < 70) ? 2000 : 1900;
+		p += 2;
+	}
+
+	if (fmt == isoc) {
+		if (*p == ' ') {
+			p++;
+		}
+
+		if (*p < '0' || *p > '9') {
+			return (time_t)-1;
+		}
+
+		day = *p++ - '0';
+
+		if (*p != ' ') {
+			if (*p < '0' || *p > '9') {
+				return (time_t)-1;
+			}
+
+			day = day * 10 + *p++ - '0';
+		}
+
+		if (end - p < 14) {
+			return (time_t)-1;
+		}
+	}
+
+	if (*p++ != ' ') {
+		return (time_t)-1;
+	}
+
+	if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
+		return (time_t)-1;
+	}
+
+	hour = (*p - '0') * 10 + *(p + 1) - '0';
+	p += 2;
+
+	if (*p++ != ':') {
+		return (time_t)-1;
+	}
+
+	if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
+		return (time_t)-1;
+	}
+
+	min = (*p - '0') * 10 + *(p + 1) - '0';
+	p += 2;
+
+	if (*p++ != ':') {
+		return (time_t)-1;
+	}
+
+	if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
+		return (time_t)-1;
+	}
+
+	sec = (*p - '0') * 10 + *(p + 1) - '0';
+
+	if (fmt == isoc) {
+		p += 2;
+
+		if (*p++ != ' ') {
+			return (time_t)-1;
+		}
+
+		if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9'
+			|| *(p + 2) < '0' || *(p + 2) > '9' || *(p + 3) < '0'
+			|| *(p + 3) > '9') {
+			return (time_t)-1;
+		}
+
+		year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100
+			+ (*(p + 2) - '0') * 10 + *(p + 3) - '0';
+	}
+
+	if (hour > 23 || min > 59 || sec > 59) {
+		return (time_t)-1;
+	}
+
+	if (day == 29 && month == 1) {
+		if ((year & 3) || ((year % 100 == 0) && (year % 400) != 0)) {
+			return (time_t)-1;
+		}
+
+	}
+	else if (day > mday[month]) {
+		return (time_t)-1;
+	}
+
+	/*
+	 * shift new year to March 1 and start months from 1 (not 0),
+	 * it is needed for Gauss' formula
+	 */
+
+	if (--month <= 0) {
+		month += 12;
+		year -= 1;
+	}
+
+	/* Gauss' formula for Gregorian days since March 1, 1 BC */
+
+	time = (guint64) (
+	    /* days in years including leap years since March 1, 1 BC */
+
+		365 * year + year / 4 - year / 100 + year / 400
+
+	    /* days before the month */
+
+		+ 367 * month / 12 - 30
+
+	    /* days before the day */
+
+		+ day - 1
+
+	    /*
+	     * 719527 days were between March 1, 1 BC and March 1, 1970,
+	     * 31 and 28 days were in January and February 1970
+	     */
+
+		- 719527 + 31 + 28) * 86400 + hour * 3600 + min * 60 + sec;
+
+	return (time_t) time;
+}
+
+static void
+rspamd_http_parse_key (rspamd_ftok_t *data, struct rspamd_http_connection *conn,
+		struct rspamd_http_connection_private *priv)
+{
+	guchar *decoded_id;
+	const gchar *eq_pos;
+	gsize id_len;
+	struct rspamd_cryptobox_pubkey *pk;
+
+	if (priv->local_key == NULL) {
+		/* In this case we cannot do anything, e.g. we cannot decrypt payload */
+		priv->flags &= ~RSPAMD_HTTP_CONN_FLAG_ENCRYPTED;
+	}
+	else {
+		/* Check sanity of what we have */
+		eq_pos = memchr (data->begin, '=', data->len);
+		if (eq_pos != NULL) {
+			decoded_id = rspamd_decode_base32 (data->begin, eq_pos - data->begin,
+					&id_len);
+
+			if (decoded_id != NULL && id_len >= RSPAMD_KEYPAIR_SHORT_ID_LEN) {
+				pk = rspamd_pubkey_from_base32 (eq_pos + 1,
+						data->begin + data->len - eq_pos - 1,
+						RSPAMD_KEYPAIR_KEX,
+						RSPAMD_CRYPTOBOX_MODE_25519);
+				if (pk != NULL) {
+					if (memcmp (rspamd_keypair_get_id (priv->local_key),
+							decoded_id,
+							RSPAMD_KEYPAIR_SHORT_ID_LEN) == 0) {
+						priv->msg->peer_key = pk;
+
+						if (conn->cache && priv->msg->peer_key) {
+							rspamd_keypair_cache_process (conn->cache,
+									priv->local_key, priv->msg->peer_key);
+						}
+					}
+					else {
+						rspamd_pubkey_unref (pk);
+					}
+				}
+			}
+
+			priv->flags |= RSPAMD_HTTP_CONN_FLAG_ENCRYPTED;
+			g_free (decoded_id);
+		}
+	}
+}
+
+static inline void
+rspamd_http_check_special_header (struct rspamd_http_connection *conn,
+		struct rspamd_http_connection_private *priv)
+{
+	if (rspamd_ftok_casecmp (&priv->header->name, &date_header) == 0) {
+		priv->msg->date = rspamd_http_parse_date (priv->header->value.begin,
+				priv->header->value.len);
+	}
+	else if (rspamd_ftok_casecmp (&priv->header->name, &key_header) == 0) {
+		rspamd_http_parse_key (&priv->header->value, conn, priv);
+	}
+	else if (rspamd_ftok_casecmp (&priv->header->name, &last_modified_header) == 0) {
+		priv->msg->last_modified = rspamd_http_parse_date (
+				priv->header->value.begin,
+				priv->header->value.len);
+	}
+}
+
+static gint
+rspamd_http_on_url (http_parser * parser, const gchar *at, size_t length)
+{
+	struct rspamd_http_connection *conn =
+		(struct rspamd_http_connection *)parser->data;
+	struct rspamd_http_connection_private *priv;
+
+	priv = conn->priv;
+
+	priv->msg->url = rspamd_fstring_append (priv->msg->url, at, length);
+
+	return 0;
+}
+
+static gint
+rspamd_http_on_status (http_parser * parser, const gchar *at, size_t length)
+{
+	struct rspamd_http_connection *conn =
+		(struct rspamd_http_connection *)parser->data;
+	struct rspamd_http_connection_private *priv;
+
+	priv = conn->priv;
+
+	if (parser->status_code != 200) {
+		if (priv->msg->status == NULL) {
+			priv->msg->status = rspamd_fstring_new ();
+		}
+
+		priv->msg->status = rspamd_fstring_append (priv->msg->status, at, length);
+	}
+
+	return 0;
+}
+
+static void
+rspamd_http_finish_header (struct rspamd_http_connection *conn,
+		struct rspamd_http_connection_private *priv)
+{
+	struct rspamd_http_header *hdr;
+
+	priv->header->combined = rspamd_fstring_append (priv->header->combined,
+			"\r\n", 2);
+	priv->header->value.len = priv->header->combined->len -
+			priv->header->name.len - 4;
+	priv->header->value.begin = priv->header->combined->str +
+			priv->header->name.len + 2;
+	priv->header->name.begin = priv->header->combined->str;
+
+	HASH_FIND (hh, priv->msg->headers, priv->header->name.begin,
+			priv->header->name.len, hdr);
+
+	if (hdr == NULL) {
+		HASH_ADD_KEYPTR (hh, priv->msg->headers, priv->header->name.begin,
+				priv->header->name.len, priv->header);
+	}
+
+	DL_APPEND (hdr, priv->header);
+
+	rspamd_http_check_special_header (conn, priv);
+}
+
+static void
+rspamd_http_init_header (struct rspamd_http_connection_private *priv)
+{
+	priv->header = g_malloc0 (sizeof (struct rspamd_http_header));
+	priv->header->combined = rspamd_fstring_new ();
+}
+
+static gint
+rspamd_http_on_header_field (http_parser * parser,
+	const gchar *at,
+	size_t length)
+{
+	struct rspamd_http_connection *conn =
+		(struct rspamd_http_connection *)parser->data;
+	struct rspamd_http_connection_private *priv;
+
+	priv = conn->priv;
+
+	if (priv->header == NULL) {
+		rspamd_http_init_header (priv);
+	}
+	else if (priv->flags & RSPAMD_HTTP_CONN_FLAG_NEW_HEADER) {
+		rspamd_http_finish_header (conn, priv);
+		rspamd_http_init_header (priv);
+	}
+
+	priv->flags &= ~RSPAMD_HTTP_CONN_FLAG_NEW_HEADER;
+	priv->header->combined = rspamd_fstring_append (priv->header->combined,
+			at, length);
+
+	return 0;
+}
+
+static gint
+rspamd_http_on_header_value (http_parser * parser,
+	const gchar *at,
+	size_t length)
+{
+	struct rspamd_http_connection *conn =
+		(struct rspamd_http_connection *)parser->data;
+	struct rspamd_http_connection_private *priv;
+
+	priv = conn->priv;
+
+	if (priv->header == NULL) {
+		/* Should not happen */
+		return -1;
+	}
+
+	if (!(priv->flags & RSPAMD_HTTP_CONN_FLAG_NEW_HEADER)) {
+		priv->flags |= RSPAMD_HTTP_CONN_FLAG_NEW_HEADER;
+		priv->header->combined = rspamd_fstring_append (priv->header->combined,
+				": ", 2);
+		priv->header->name.len = priv->header->combined->len - 2;
+	}
+
+	priv->header->combined = rspamd_fstring_append (priv->header->combined,
+			at, length);
+
+	return 0;
+}
+
+static int
+rspamd_http_on_headers_complete (http_parser * parser)
+{
+	struct rspamd_http_connection *conn =
+		(struct rspamd_http_connection *)parser->data;
+	struct rspamd_http_connection_private *priv;
+	struct rspamd_http_message *msg;
+	int ret;
+
+	priv = conn->priv;
+	msg = priv->msg;
+
+	if (priv->header != NULL) {
+		rspamd_http_finish_header (conn, priv);
+
+		priv->header = NULL;
+		priv->flags &= ~RSPAMD_HTTP_CONN_FLAG_NEW_HEADER;
+	}
+
+	if (msg->method == HTTP_HEAD) {
+		/* We don't care about the rest */
+		if (rspamd_event_pending (&priv->ev, EV_READ)) {
+			event_del (&priv->ev);
+		}
+
+		msg->code = parser->status_code;
+		rspamd_http_connection_ref (conn);
+		ret = conn->finish_handler (conn, msg);
+		conn->finished = TRUE;
+		rspamd_http_connection_unref (conn);
+
+		return ret;
+	}
+
+	/*
+	 * HTTP parser sets content length to (-1) when it doesn't know the real
+	 * length, for example, in case of chunked encoding.
+	 *
+	 * Hence, we skip body setup here
+	 */
+	if (parser->content_length != ULLONG_MAX && parser->content_length != 0) {
+		if (conn->max_size > 0 &&
+				parser->content_length > conn->max_size) {
+			/* Too large message */
+			priv->flags |= RSPAMD_HTTP_CONN_FLAG_TOO_LARGE;
+			return -1;
+		}
+
+		if (!rspamd_http_message_set_body (msg, NULL, parser->content_length)) {
+			return -1;
+		}
+	}
+
+	if (parser->flags & F_SPAMC) {
+		msg->flags |= RSPAMD_HTTP_FLAG_SPAMC;
+	}
+
+
+	msg->method = parser->method;
+	msg->code = parser->status_code;
+
+	return 0;
+}
+
+static void
+rspamd_http_switch_zc (struct _rspamd_http_privbuf *pbuf,
+		struct rspamd_http_message *msg)
+{
+	pbuf->zc_buf = msg->body_buf.begin + msg->body_buf.len;
+	pbuf->zc_remain = msg->body_buf.allocated_len - msg->body_buf.len;
*** OUTPUT TRUNCATED, 3873 LINES SKIPPED ***


More information about the Commits mailing list