From: Vsevolod Stakhov Date: Thu, 27 Apr 2017 19:46:25 +0000 (+0100) Subject: [Rework] Initial milter protocol support X-Git-Tag: 1.6.0~308 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=4d0e9795292a161a93e0345c2d59c5fb90e40e39;p=rspamd.git [Rework] Initial milter protocol support --- 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 + +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