]> source.dussan.org Git - rspamd.git/commitdiff
[Minor] More parsing functions for milter proto
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Sat, 29 Apr 2017 13:59:29 +0000 (14:59 +0100)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Sat, 29 Apr 2017 13:59:29 +0000 (14:59 +0100)
src/libserver/milter.c
src/libserver/milter_internal.h

index c1d8ea259b9d7451bcdabce189cfb6b7d55b5abd..9425eb579d31284ec267bf9bdfe28d33aefd9b7d 100644 (file)
@@ -64,13 +64,62 @@ rspamd_milter_obuf_free (struct rspamd_milter_outbuf *obuf)
 }
 
 static void
-rspamd_milter_session_dtor (struct rspamd_milter_session *session)
+rspamd_milter_session_reset (struct rspamd_milter_session *session)
 {
        struct rspamd_milter_outbuf *obuf, *obuf_tmp;
-       struct rspamd_milter_private *priv;
+       struct rspamd_milter_private *priv = session->priv;
        struct rspamd_email_address *cur;
        guint i;
 
+       DL_FOREACH_SAFE (priv->out_chain, obuf, obuf_tmp) {
+               rspamd_milter_obuf_free (obuf);
+       }
+
+       if (priv->parser.buf) {
+               priv->parser.buf->len = 0;
+       }
+
+       if (session->message) {
+               session->message->len = 0;
+       }
+
+       if (session->addr) {
+               rspamd_inet_address_free (session->addr);
+       }
+
+       if (session->rcpts) {
+               PTR_ARRAY_FOREACH (session->rcpts, i, cur) {
+                       rspamd_email_address_unref (cur);
+               }
+
+               g_ptr_array_free (session->rcpts, TRUE);
+       }
+
+       if (session->from) {
+               rspamd_email_address_unref (session->from);
+       }
+
+       if (session->macros) {
+               g_hash_table_unref (session->macros);
+               session->macros = NULL;
+       }
+
+       if (session->helo) {
+               session->helo->len = 0;
+       }
+
+       if (session->hostname) {
+               session->hostname->len = 0;
+       }
+
+       priv->out_chain = NULL;
+}
+
+static void
+rspamd_milter_session_dtor (struct rspamd_milter_session *session)
+{
+       struct rspamd_milter_private *priv;
+
        if (session) {
                priv = session->priv;
 
@@ -78,9 +127,7 @@ rspamd_milter_session_dtor (struct rspamd_milter_session *session)
                        event_del (&priv->ev);
                }
 
-               DL_FOREACH_SAFE (priv->out_chain, obuf, obuf_tmp) {
-                       rspamd_milter_obuf_free (obuf);
-               }
+               rspamd_milter_session_reset (session);
 
                if (priv->parser.buf) {
                        rspamd_fstring_free (priv->parser.buf);
@@ -90,26 +137,6 @@ rspamd_milter_session_dtor (struct rspamd_milter_session *session)
                        rspamd_fstring_free (session->message);
                }
 
-               if (session->addr) {
-                       rspamd_inet_address_free (session->addr);
-               }
-
-               if (session->rcpts) {
-                       PTR_ARRAY_FOREACH (session->rcpts, i, cur) {
-                               rspamd_email_address_unref (cur);
-                       }
-
-                       g_ptr_array_free (session->rcpts, TRUE);
-               }
-
-               if (session->from) {
-                       rspamd_email_address_unref (session->from);
-               }
-
-               if (session->macros) {
-                       g_hash_table_unref (session->macros);
-               }
-
                if (session->helo) {
                        rspamd_fstring_free (session->helo);
                }
@@ -118,7 +145,7 @@ rspamd_milter_session_dtor (struct rspamd_milter_session *session)
                        rspamd_fstring_free (session->hostname);
                }
 
-               priv->out_chain = NULL;
+               g_free (session);
        }
 }
 
@@ -160,6 +187,11 @@ rspamd_milter_on_protocol_error (struct rspamd_milter_session *session,
        (pos) += sizeof (var); \
        (var) = ntohl (var); \
 } while (0)
+#define READ_INT_16(pos, var) do { \
+       memcpy (&(var), (pos), sizeof (var)); \
+       (pos) += sizeof (var); \
+       (var) = ntohs (var); \
+} while (0)
 
 static gboolean
 rspamd_milter_process_command (struct rspamd_milter_session *session,
@@ -173,11 +205,12 @@ rspamd_milter_process_command (struct rspamd_milter_session *session,
 
        buf = priv->parser.buf;
        pos = buf->str + priv->parser.pos;
-       end = pos + cmdlen;
        cmdlen = priv->parser.datalen;
+       end = pos + cmdlen;
 
        switch (priv->parser.cur_cmd) {
        case RSPAMD_MILTER_CMD_ABORT:
+               msg_debug_milter ("got abort command");
                err = g_error_new (rspamd_milter_quark (), ECONNABORTED, "connection "
                                "aborted");
                rspamd_milter_on_protocol_error (session, priv, err);
@@ -193,20 +226,183 @@ rspamd_milter_process_command (struct rspamd_milter_session *session,
                                pos, cmdlen);
                break;
        case RSPAMD_MILTER_CMD_CONNECT:
+               msg_debug_milter ("got connect command");
+
+               /*
+                * char hostname[]: Hostname, NUL terminated
+                * char family: Protocol family
+                * uint16 port: Port number (SMFIA_INET or SMFIA_INET6 only)
+                * char address[]: IP address (ASCII) or unix socket path, NUL terminated
+                */
+               zero = memchr (pos, '\0', cmdlen);
+
+               if (zero == NULL || zero > (end - sizeof (guint16) + 1)) {
+                       err = g_error_new (rspamd_milter_quark (), EINVAL, "invalid "
+                                       "connect command (no name)");
+                       rspamd_milter_on_protocol_error (session, priv, err);
+
+                       return FALSE;
+               }
+               else {
+                       guchar proto;
+                       guint16 port;
+
+                       if (session->hostname == NULL) {
+                               session->hostname = rspamd_fstring_new_init (pos, zero - pos);
+                       }
+                       else {
+                               session->hostname = rspamd_fstring_assign (session->hostname,
+                                               pos, zero - pos);
+                       }
+
+                       pos = zero + 1;
+                       proto = *pos ++;
+
+                       if (proto == RSPAMD_MILTER_CONN_UNKNOWN) {
+                               /* We have no information about host */
+                               msg_debug_milter ("unknown connect address");
+                       }
+                       else {
+                               READ_INT_16 (pos, port);
+
+                               if (pos >= end) {
+                                       /* No IP somehow */
+                                       msg_debug_milter ("unknown connect IP/socket");
+                               }
+                               else {
+                                       zero = memchr (pos, '\0', end - pos);
+
+                                       if (zero == NULL) {
+                                               err = g_error_new (rspamd_milter_quark (), EINVAL, "invalid "
+                                                               "connect command (no zero terminated IP)");
+                                               rspamd_milter_on_protocol_error (session, priv, err);
+
+                                               return FALSE;
+                                       }
+
+                                       switch (proto) {
+                                       case RSPAMD_MILTER_CONN_UNIX:
+                                               session->addr = rspamd_inet_address_new (AF_UNIX,
+                                                               pos);
+                                               break;
+
+                                       case RSPAMD_MILTER_CONN_INET:
+                                               session->addr = rspamd_inet_address_new (AF_INET, NULL);
+
+                                               if (!rspamd_parse_inet_address_ip (pos, zero - pos,
+                                                               session->addr)) {
+                                                       err = g_error_new (rspamd_milter_quark (), EINVAL,
+                                                                       "invalid connect command (bad IPv4)");
+                                                       rspamd_milter_on_protocol_error (session, priv,
+                                                                       err);
+
+                                                       return FALSE;
+                                               }
+
+                                               rspamd_inet_address_set_port (session->addr, port);
+                                               break;
+
+                                       case RSPAMD_MILTER_CONN_INET6:
+                                               session->addr = rspamd_inet_address_new (AF_INET, NULL);
+
+                                               if (!rspamd_parse_inet_address_ip (pos, zero - pos,
+                                                               session->addr)) {
+                                                       err = g_error_new (rspamd_milter_quark (), EINVAL,
+                                                                       "invalid connect command (bad IPv6)");
+                                                       rspamd_milter_on_protocol_error (session, priv,
+                                                                       err);
+
+                                                       return FALSE;
+                                               }
+
+                                               rspamd_inet_address_set_port (session->addr, port);
+                                               break;
+
+                                       default:
+                                               err = g_error_new (rspamd_milter_quark (), EINVAL,
+                                                               "invalid connect command (bad protocol: %c)",
+                                                               proto);
+                                               rspamd_milter_on_protocol_error (session, priv,
+                                                               err);
+
+                                               return FALSE;
+                                       }
+                               }
+                       }
+               }
                break;
        case RSPAMD_MILTER_CMD_MACRO:
+               msg_debug_milter ("got macro command");
+               /*
+                * Format is
+                * 1 byte - command associated (we don't care about it)
+                * 0-terminated name
+                * 0-terminated value
+                */
+               if (session->macros == NULL) {
+                       session->macros = g_hash_table_new_full (rspamd_ftok_icase_hash,
+                                       rspamd_ftok_icase_equal,
+                                       rspamd_fstring_mapped_ftok_free,
+                                       rspamd_fstring_mapped_ftok_free);
+               }
+
+               pos ++;
+               zero = memchr (pos, '\0', cmdlen);
+
+               if (zero == NULL) {
+                       err = g_error_new (rspamd_milter_quark (), EINVAL, "invalid "
+                                       "macro command (no name)");
+                       rspamd_milter_on_protocol_error (session, priv, err);
+
+                       return FALSE;
+               }
+               else {
+                       rspamd_fstring_t *name, *value;
+                       rspamd_ftok_t *name_tok, *value_tok;
+
+                       if (end > zero && *(end - 1) == '\0') {
+                               name = rspamd_fstring_new_init (pos, zero - pos);
+                               value = rspamd_fstring_new_init (zero + 1, end - zero - 2);
+                               name_tok = rspamd_ftok_map (name);
+                               value_tok = rspamd_ftok_map (value);
+
+                               g_hash_table_replace (session->macros, name_tok, value_tok);
+                               msg_debug_milter ("got macro: %T -> %T",
+                                               name_tok, value_tok);
+                       }
+                       else {
+                               err = g_error_new (rspamd_milter_quark (), EINVAL, "invalid "
+                                               "macro command (bad value)");
+                               rspamd_milter_on_protocol_error (session, priv, err);
+
+                               return FALSE;
+                       }
+               }
                break;
        case RSPAMD_MILTER_CMD_BODYEOB:
+               msg_debug_milter ("got eob command");
                break;
        case RSPAMD_MILTER_CMD_HELO:
                msg_debug_milter ("got helo command");
 
                if (end > pos && *(end - 1) == '\0') {
-                       session->helo = rspamd_fstring_new_init (pos, cmdlen - 1);
+                       if (session->helo == NULL) {
+                               session->helo = rspamd_fstring_new_init (pos, cmdlen - 1);
+                       }
+                       else {
+                               session->helo = rspamd_fstring_assign (session->helo,
+                                               pos, cmdlen - 1);
+                       }
                }
                else if (end > pos) {
                        /* Should not happen */
-                       session->helo = rspamd_fstring_new_init (pos, cmdlen - 1);
+                       if (session->helo == NULL) {
+                               session->helo = rspamd_fstring_new_init (pos, cmdlen);
+                       }
+                       else {
+                               session->helo = rspamd_fstring_assign (session->helo,
+                                               pos, cmdlen);
+                       }
                }
                break;
        case RSPAMD_MILTER_CMD_QUIT_NC:
@@ -270,6 +466,15 @@ rspamd_milter_process_command (struct rspamd_milter_session *session,
                }
                break;
        case RSPAMD_MILTER_CMD_EOH:
+               msg_debug_milter ("got eoh command");
+
+               if (!session->message) {
+                       session->message = rspamd_fstring_sized_new (
+                                       RSPAMD_MILTER_MESSAGE_CHUNK);
+               }
+
+               session->message = rspamd_fstring_append (session->message,
+                               "\r\n", 2);
                break;
        case RSPAMD_MILTER_CMD_OPTNEG:
                if (cmdlen != sizeof (guint32) * 3) {
@@ -312,7 +517,7 @@ rspamd_milter_process_command (struct rspamd_milter_session *session,
                msg_debug_milter ("rcpt command");
 
                while (pos < end) {
-                       const guchar *zero = memchr (pos, '\0', end - pos);
+                       zero = memchr (pos, '\0', end - pos);
                        struct rspamd_email_address *addr;
 
                        if (zero) {
index 9a99ad8369f045e9797a9675eb906ed5f4ac1831..f40f007ef61d25a26415f7f9be1efeddeb8f37f4 100644 (file)
@@ -123,6 +123,13 @@ enum rspamd_milter_io_cmd {
        (RSPAMD_MILTER_FLAG_ADDHDRS | RSPAMD_MILTER_FLAG_ADDRCPT | \
        RSPAMD_MILTER_FLAG_DELRCPT | RSPAMD_MILTER_FLAG_CHGHDRS)
 
+enum rspamd_milter_connect_proto {
+       RSPAMD_MILTER_CONN_UNKNOWN = 'U',
+       RSPAMD_MILTER_CONN_UNIX = 'L',
+       RSPAMD_MILTER_CONN_INET = '4',
+       RSPAMD_MILTER_CONN_INET6 = '6',
+};
+
 /*
  * Rspamd supports just version 6 of the protocol, failing all versions below
  * this one