aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@highsecure.ru>2017-04-27 20:46:25 +0100
committerVsevolod Stakhov <vsevolod@highsecure.ru>2017-04-27 20:46:46 +0100
commit4d0e9795292a161a93e0345c2d59c5fb90e40e39 (patch)
tree185c4b244b68a04422dad6a67e4172c1a7886636
parent9dda2e431874b5c1d70213e952a223b3cb22c0a5 (diff)
downloadrspamd-4d0e9795292a161a93e0345c2d59c5fb90e40e39.tar.gz
rspamd-4d0e9795292a161a93e0345c2d59c5fb90e40e39.zip
[Rework] Initial milter protocol support
-rw-r--r--src/libserver/CMakeLists.txt1
-rw-r--r--src/libserver/milter.c217
-rw-r--r--src/libserver/milter.h118
-rw-r--r--src/libserver/milter_internal.h67
4 files changed, 403 insertions, 0 deletions
diff --git a/src/libserver/CMakeLists.txt b/src/libserver/CMakeLists.txt
index 474e695d1..03d11acb6 100644
--- a/src/libserver/CMakeLists.txt
+++ b/src/libserver/CMakeLists.txt
@@ -10,6 +10,7 @@ SET(LIBRSPAMDSERVERSRC
${CMAKE_CURRENT_SOURCE_DIR}/fuzzy_backend.c
${CMAKE_CURRENT_SOURCE_DIR}/fuzzy_backend_sqlite.c
${CMAKE_CURRENT_SOURCE_DIR}/html.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/milter.c
${CMAKE_CURRENT_SOURCE_DIR}/monitored.c
${CMAKE_CURRENT_SOURCE_DIR}/protocol.c
${CMAKE_CURRENT_SOURCE_DIR}/re_cache.c
diff --git a/src/libserver/milter.c b/src/libserver/milter.c
new file mode 100644
index 000000000..9bb6ead39
--- /dev/null
+++ b/src/libserver/milter.c
@@ -0,0 +1,217 @@
+/*-
+ * Copyright 2017 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 "milter.h"
+#include "milter_internal.h"
+#include "email_addr.h"
+#include "addr.h"
+#include "unix-std.h"
+
+static gboolean rspamd_milter_handle_session (
+ struct rspamd_milter_session *session,
+ struct rspamd_milter_private *priv);
+
+static GQuark
+rspamd_milter_quark (void)
+{
+ return g_quark_from_static_string ("milter");
+}
+
+static void
+rspamd_milter_io_handler (gint fd, gshort what, void *ud)
+{
+ struct rspamd_milter_session *session = ud;
+ struct rspamd_milter_private *priv;
+
+ priv = session->priv;
+}
+
+static inline void
+rspamd_milter_plan_io (struct rspamd_milter_session *session,
+ struct rspamd_milter_private *priv, gshort what)
+{
+ if (event_get_base (&priv->ev)) {
+ event_del (&priv->ev);
+ }
+
+ event_set (&priv->ev, priv->fd, what, rspamd_milter_io_handler,
+ session);
+ event_base_set (priv->ev_base, &priv->ev);
+ event_add (&priv->ev, priv->ptv);
+}
+
+static gboolean
+rspamd_milter_consume_input (struct rspamd_milter_session *session,
+ struct rspamd_milter_private *priv)
+{
+ const guchar *p, *end;
+
+ p = priv->parser.buf->str + priv->parser.pos;
+ end = priv->parser.buf->str + priv->parser.buf->len;
+
+ while (p < end) {
+ switch (priv->parser.state) {
+ case st_read_cmd:
+ priv->parser.cur_cmd = *p;
+ priv->parser.state = st_len_1;
+ priv->parser.datalen = 0;
+ p++;
+ break;
+ case st_len_1:
+ /* The first length byte in big endian order */
+ priv->parser.datalen |= *p << 24;
+ priv->parser.state = st_len_2;
+ p++;
+ break;
+ case st_len_2:
+ /* The second length byte in big endian order */
+ priv->parser.datalen |= *p << 16;
+ priv->parser.state = st_len_3;
+ p++;
+ break;
+ case st_len_3:
+ /* The third length byte in big endian order */
+ priv->parser.datalen |= *p << 8;
+ priv->parser.state = st_len_4;
+ p++;
+ break;
+ case st_len_4:
+ /* The fourth length byte in big endian order */
+ priv->parser.datalen |= *p;
+ priv->parser.state = st_read_data;
+ p++;
+ break;
+ case st_read_data:
+ /* We might need some more data in buffer for further steps */
+ break;
+ }
+ }
+}
+
+static gboolean
+rspamd_milter_handle_session (struct rspamd_milter_session *session,
+ struct rspamd_milter_private *priv)
+{
+ gssize r;
+ GError *err;
+
+ g_assert (session != NULL);
+
+ switch (priv->state) {
+ case RSPAMD_MILTER_READ_MORE:
+ if (priv->parser.pos >= priv->parser.buf->allocated) {
+ priv->parser.buf = rspamd_fstring_grow (priv->parser.buf,
+ priv->parser.pos * 2);
+ }
+
+ r = read (priv->fd, priv->parser.buf->str + priv->parser.pos,
+ priv->parser.buf->allocated - priv->parser.pos);
+
+ if (r == -1) {
+ if (errno == EAGAIN || errno == EINTR) {
+ rspamd_milter_plan_io (session, priv, EV_READ);
+ }
+ else {
+ /* Fatal IO error */
+ err = g_error_new (rspamd_milter_quark (), errno,
+ "IO error: %s", strerror (errno));
+ REF_RETAIN (session);
+ priv->err_cb (priv->fd, session, priv->ud, err);
+ REF_RELEASE (session);
+ g_error_free (err);
+ }
+ }
+ else if (r == 0) {
+ err = g_error_new (rspamd_milter_quark (), ECONNRESET,
+ "Unexpected EOF");
+ REF_RETAIN (session);
+ priv->err_cb (priv->fd, session, priv->ud, err);
+ REF_RELEASE (session);
+ g_error_free (err);
+ }
+ else {
+ priv->parser.buf->len += r;
+
+ return rspamd_milter_consume_input (session, priv);
+ }
+ }
+}
+
+
+gboolean
+rspamd_milter_handle_socket (gint fd, const struct timeval *tv,
+ struct event_base *ev_base, rspamd_milter_finish finish_cb,
+ rspamd_milter_error error_cb, void *ud)
+{
+ struct rspamd_milter_session *session;
+ struct rspamd_milter_private *priv;
+
+ g_assert (finish_cb != NULL);
+ g_assert (error_cb != NULL);
+
+ session = g_malloc0 (sizeof (*session));
+ priv = g_malloc0 (sizeof (*priv));
+ priv->fd = fd;
+ priv->ud = ud;
+ priv->fin_cb = finish_cb;
+ priv->err_cb = error_cb;
+ priv->parser.state = st_read_cmd;
+ priv->parser.buf = rspamd_fstring_sized_new (100);
+ priv->ev_base = ev_base;
+ priv->state = RSPAMD_MILTER_READ_MORE;
+
+ if (tv) {
+ memcpy (&priv->tv, tv, sizeof (*tv));
+ priv->ptv = &priv->tv;
+ }
+ else {
+ priv->ptv = NULL;
+ }
+
+ session->priv = priv;
+
+ return rspamd_milter_handle_session (session, priv);
+}
+
+gboolean rspamd_milter_set_reply (struct rspamd_milter_session *session,
+ rspamd_fstring_t *xcode,
+ rspamd_fstring_t *rcode,
+ rspamd_fstring_t *reply);
+
+gboolean rspamd_milter_send_action (gint fd,
+ struct rspamd_milter_session *session,
+ enum rspamd_milter_reply act);
+
+gboolean rspamd_milter_add_header (struct rspamd_milter_session *session,
+ GString *name, GString *value);
+
+gboolean rspamd_milter_del_header (struct rspamd_milter_session *session,
+ GString *name);
+
+void
+rspamd_milter_session_unref (struct rspamd_milter_session *session)
+{
+ REF_RELEASE (session);
+}
+
+struct rspamd_milter_session *
+rspamd_milter_session_ref (struct rspamd_milter_session *session)
+{
+ REF_RETAIN (session);
+
+ return session;
+}
diff --git a/src/libserver/milter.h b/src/libserver/milter.h
new file mode 100644
index 000000000..c3708d53f
--- /dev/null
+++ b/src/libserver/milter.h
@@ -0,0 +1,118 @@
+/*-
+ * Copyright 2017 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.
+ */
+#ifndef RSPAMD_MILTER_H
+#define RSPAMD_MILTER_H
+
+#include "config.h"
+#include "fstring.h"
+#include "addr.h"
+#include "ref.h"
+
+enum rspamd_milter_reply {
+ RSPAMD_MILTER_ADDRCPT = '+',
+ RSPAMD_MILTER_DELRCPT = '-',
+ RSPAMD_MILTER_ACCEPT = 'a',
+ RSPAMD_MILTER_CONTINUE = 'c',
+ RSPAMD_MILTER_DISCARD = 'd',
+ RSPAMD_MILTER_ADDHEADER = 'h',
+ RSPAMD_MILTER_CHGHEADER = 'm',
+ RSPAMD_MILTER_REJECT = 'r',
+ RSPAMD_MILTER_TEMPFAIL = 't',
+ RSPAMD_MILTER_REPLYCODE = 'y'
+};
+
+struct rspamd_email_address;
+struct event_base;
+
+struct rspamd_milter_session {
+ GHashTable *macros;
+ rspamd_inet_addr_t *addr;
+ struct rspamd_email_address *from;
+ GPtrArray *rcpts;
+ rspamd_fstring_t *helo;
+ rspamd_fstring_t *hostname;
+ rspamd_fstring_t *message;
+ void *priv;
+ ref_entry_t ref;
+};
+
+typedef void (*rspamd_milter_finish) (gint fd,
+ struct rspamd_milter_session *session, void *ud);
+typedef void (*rspamd_milter_error) (gint fd,
+ struct rspamd_milter_session *session,
+ void *ud, GError *err);
+
+/**
+ * Handles socket with milter protocol
+ * @param fd
+ * @param finish_cb
+ * @param error_cb
+ * @param ud
+ * @return
+ */
+gboolean rspamd_milter_handle_socket (gint fd, const struct timeval *tv,
+ struct event_base *ev_base, rspamd_milter_finish finish_cb,
+ rspamd_milter_error error_cb, void *ud);
+
+/**
+ * Sets SMTP reply string
+ * @param session
+ * @param xcode
+ * @param rcode
+ * @param reply
+ * @return
+ */
+gboolean rspamd_milter_set_reply (struct rspamd_milter_session *session,
+ rspamd_fstring_t *xcode,
+ rspamd_fstring_t *rcode,
+ rspamd_fstring_t *reply);
+
+/**
+ * Send some action to the MTA
+ * @param fd
+ * @param session
+ * @param act
+ * @return
+ */
+gboolean rspamd_milter_send_action (gint fd,
+ struct rspamd_milter_session *session,
+ enum rspamd_milter_reply act);
+
+/**
+ * Adds some header
+ * @param session
+ * @param name
+ * @param value
+ * @return
+ */
+gboolean rspamd_milter_add_header (struct rspamd_milter_session *session,
+ GString *name, GString *value);
+
+/**
+ * Removes some header
+ * @param session
+ * @param name
+ * @return
+ */
+gboolean rspamd_milter_del_header (struct rspamd_milter_session *session,
+ GString *name);
+
+void rspamd_milter_session_unref (struct rspamd_milter_session *session);
+
+struct rspamd_milter_session * rspamd_milter_session_ref (
+ struct rspamd_milter_session *session);
+
+#endif
diff --git a/src/libserver/milter_internal.h b/src/libserver/milter_internal.h
new file mode 100644
index 000000000..fed2e6f11
--- /dev/null
+++ b/src/libserver/milter_internal.h
@@ -0,0 +1,67 @@
+/*-
+ * Copyright 2017 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.
+ */
+
+#ifndef RSPAMD_MILTER_INTERNAL_H
+#define RSPAMD_MILTER_INTERNAL_H
+
+#include "config.h"
+#include <event.h>
+
+enum rspamd_milter_state {
+ st_read_cmd,
+ st_len_1,
+ st_len_2,
+ st_len_3,
+ st_len_4,
+ st_read_data
+};
+
+struct rspamd_milter_parser {
+ rspamd_fstring_t *buf;
+ goffset pos;
+ gsize datalen;
+ enum rspamd_milter_state state;
+ gchar cur_cmd;
+};
+
+struct rspamd_milter_outbuf {
+ rspamd_fstring_t *buf;
+ goffset pos;
+ struct rspamd_milter_outbuf *next, *prev;
+};
+
+enum rspamd_milter_io_state {
+ RSPAMD_MILTER_READ_MORE,
+ RSPAMD_MILTER_PROCESS_DATA,
+ RSPAMD_MILTER_WRITE_REPLY,
+ RSPAMD_MILTER_WANNA_DIE
+};
+
+struct rspamd_milter_private {
+ struct rspamd_milter_parser parser;
+ struct rspamd_milter_outbuf *out_chain;
+ struct event ev;
+ struct timeval tv;
+ struct timeval *ptv;
+ struct event_base *ev_base;
+ rspamd_milter_finish fin_cb;
+ rspamd_milter_error err_cb;
+ void *ud;
+ enum rspamd_milter_io_state state;
+ int fd;
+};
+
+#endif