]> source.dussan.org Git - rspamd.git/commitdiff
* Add ability to specify dnsbls for smtp_proxy.
authorVsevolod Stakhov <vsevolod@rambler-co.ru>
Fri, 27 Apr 2012 16:51:56 +0000 (20:51 +0400)
committerVsevolod Stakhov <vsevolod@rambler-co.ru>
Fri, 27 Apr 2012 16:51:56 +0000 (20:51 +0400)
Fix handling of params with the same name in configuration.
Add ability for rspamc to bind on a local address.

lib/client/librspamdclient.c
lib/client/librspamdclient.h
src/cfg_xml.c
src/client/rspamc.c
src/smtp_proxy.c

index 6351175bc3c2050d6716d8fd2289b51150707b83..dac49b5aedaf66578465fe50f9983c5ec9be9c87 100644 (file)
@@ -49,6 +49,7 @@ struct rspamd_client {
        guint servers_num;
        guint connect_timeout;
        guint read_timeout;
+       struct in_addr *bind_addr;
 };
 
 struct rspamd_connection {
@@ -110,11 +111,12 @@ poll_sync_socket (gint fd, gint timeout, short events)
 }
 
 static gint
-make_inet_socket (gint family, struct in_addr *addr, u_short port, gboolean is_server, gboolean async)
+lib_make_inet_socket (gint family, struct in_addr *addr, struct in_addr *local_addr,
+               u_short port, gboolean is_server, gboolean async)
 {
        gint                            fd, r, optlen, on = 1, s_error;
        gint                            serrno;
-       struct sockaddr_in              sin;
+       struct sockaddr_in              sin, local;
 
        /* Create socket */
        fd = socket (AF_INET, family, 0);
@@ -140,6 +142,18 @@ make_inet_socket (gint family, struct in_addr *addr, u_short port, gboolean is_s
        sin.sin_port = htons (port);
        sin.sin_addr.s_addr = addr->s_addr;
 
+       if (!is_server && local_addr != NULL) {
+               /* Bind to local addr */
+               memset (&local, 0, sizeof (struct sockaddr_in));
+               memcpy (&local.sin_addr,local_addr, sizeof (struct in_addr));
+               local.sin_family = AF_INET;
+               setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&on, sizeof (gint));
+               if (bind (fd, (struct sockaddr *)&local, sizeof (local)) == -1) {
+                       msg_warn ("bind/connect failed: %d, '%s'", errno, strerror (errno));
+                       goto out;
+               }
+       }
+
        if (is_server) {
                setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&on, sizeof (gint));
                r = bind (fd, (struct sockaddr *)&sin, sizeof (struct sockaddr_in));
@@ -188,16 +202,10 @@ make_inet_socket (gint family, struct in_addr *addr, u_short port, gboolean is_s
        return (-1);
 }
 
-gint
-make_tcp_socket (struct in_addr *addr, u_short port, gboolean is_server, gboolean async)
-{
-       return make_inet_socket (SOCK_STREAM, addr, port, is_server, async);
-}
-
-gint
-make_udp_socket (struct in_addr *addr, u_short port, gboolean is_server, gboolean async)
+static gint
+lib_make_tcp_socket (struct in_addr *addr, struct in_addr *local_addr, u_short port, gboolean is_server, gboolean async)
 {
-       return make_inet_socket (SOCK_DGRAM, addr, port, is_server, async);
+       return lib_make_inet_socket (SOCK_STREAM, addr, local_addr, port, is_server, async);
 }
 
 /** Private functions **/
@@ -262,7 +270,7 @@ rspamd_connect_random_server (struct rspamd_client *client, gboolean is_control,
        new->connection_time = now;
        new->client = client;
        /* Create socket */
-       new->socket = make_tcp_socket (&selected->addr,
+       new->socket = lib_make_tcp_socket (&selected->addr, client->bind_addr,
                                                                is_control ? selected->controller_port : selected->client_port,
                                                                        FALSE, TRUE);
        if (new->socket == -1) {
@@ -1228,7 +1236,7 @@ rspamd_read_controller_greeting (struct rspamd_connection *c, GError **err)
  * Init rspamd client library
  */
 struct rspamd_client*
-rspamd_client_init (void)
+rspamd_client_init_binded (const struct in_addr *addr)
 {
        struct rspamd_client           *client;
 
@@ -1236,9 +1244,20 @@ rspamd_client_init (void)
        client->read_timeout = DEFAULT_READ_TIMEOUT;
        client->connect_timeout = DEFAULT_CONNECT_TIMEOUT;
 
+       if (addr != NULL) {
+               client->bind_addr = g_malloc (sizeof (struct in_addr));
+               memcpy (client->bind_addr, addr, sizeof (struct in_addr));
+       }
+
        return client;
 }
 
+struct rspamd_client*
+rspamd_client_init (void)
+{
+       return rspamd_client_init_binded (NULL);
+}
+
 /*
  * Add rspamd server
  */
@@ -1969,5 +1988,8 @@ rspamd_free_result (struct rspamd_result *result)
 void
 rspamd_client_close (struct rspamd_client *client)
 {
+       if (client->bind_addr) {
+               g_free (client->bind_addr);
+       }
        g_free (client);
 }
index 54695a34d35d19eaf7a2f87afaab1628ff6b7e15..6eea8627e42dd433a46cbd7d60303f8fd9448374 100644 (file)
@@ -28,6 +28,8 @@ struct rspamd_metric {
 
 struct rspamd_connection;
 struct rspamd_client;
+struct in_addr;
+
 /**
  * Result of scan
  */
@@ -43,6 +45,11 @@ struct rspamd_result {
  */
 struct rspamd_client* rspamd_client_init (void);
 
+/*
+ * Init rspamd client library and bind it
+ */
+struct rspamd_client* rspamd_client_init_binded (const struct in_addr *local_addr);
+
 /*
  * Add rspamd server
  */
index 924dc283e28346ffdb722e1c4b4eda8320b7ab37..d681ec25278ae127cf05d0a6aeb495a40be5198f 100644 (file)
@@ -916,6 +916,7 @@ worker_handle_param (struct config_file *cfg, struct rspamd_xml_userdata *ctx, c
                        param->is_list = TRUE;
                        tmp = param->d.param;
                        param->d.list = g_list_prepend (NULL, (gpointer)tmp);
+                       param->d.list = g_list_append (param->d.list, memory_pool_strdup (cfg->cfg_pool, data));
                        memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func)g_list_free, param->d.list);
                }
        }
index 7219d35b55c518e7670522308ca709574029f761..c96b5246a7127921f86344545a1b7e63612600af 100644 (file)
@@ -39,6 +39,7 @@ static gchar                   *deliver_to = NULL;
 static gchar                   *rcpt = NULL;
 static gchar                   *user = NULL;
 static gchar                   *classifier = NULL;
+static gchar                   *local_addr = NULL;
 static gint                     weight = 1;
 static gint                     flag;
 static gint                     timeout = 5;
@@ -63,6 +64,7 @@ static GOptionEntry entries[] =
                { "from", 'F', 0, G_OPTION_ARG_STRING, &from, "Emulate that message is from specified user", NULL },
                { "rcpt", 'r', 0, G_OPTION_ARG_STRING, &rcpt, "Emulate that message is for specified user", NULL },
                { "timeout", 't', 0, G_OPTION_ARG_INT, &timeout, "Timeout for waiting for a reply", NULL },
+               { "bind", 'b', 0, G_OPTION_ARG_STRING, &local_addr, "Bind to specified ip address", NULL },
                { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL }
 };
 
@@ -639,10 +641,24 @@ main (gint argc, gchar **argv, gchar **env)
 {
        enum rspamc_command             cmd;
        gint                            i;
+       struct in_addr                                  ina;
 
-       client = rspamd_client_init ();
 
        read_cmd_line (&argc, &argv);
+
+       if (local_addr) {
+               if (inet_aton (local_addr, &ina) != 0) {
+                       client = rspamd_client_init_binded (&ina);
+               }
+               else {
+                       fprintf (stderr, "%s is not a valid ip address\n", local_addr);
+                       exit (EXIT_FAILURE);
+               }
+       }
+       else {
+               client = rspamd_client_init ();
+       }
+
        rspamd_set_timeout (client, 1000, timeout * 1000);
        tty = isatty (STDOUT_FILENO);
        /* Now read other args from argc and argv */
index 97e56a95676d174da7fbe08977eee9201cccc233..6ebc135f552014ade6bc642f4e3786418d0bdcb1 100644 (file)
@@ -80,15 +80,19 @@ struct smtp_proxy_ctx {
 
        struct rspamd_dns_resolver *resolver;
        struct event_base *ev_base;
+
+       GList *rbls;
 };
 
 enum rspamd_smtp_proxy_state {
        SMTP_PROXY_STATE_RESOLVE_REVERSE = 0,
        SMTP_PROXY_STATE_RESOLVE_NORMAL,
+       SMTP_PROXY_STATE_RESOLVE_RBL,
        SMTP_PROXY_STATE_DELAY,
        SMTP_PROXY_STATE_GREETING,
        SMTP_PROXY_STATE_XCLIENT,
-       SMTP_PROXY_STATE_PROXY
+       SMTP_PROXY_STATE_PROXY,
+       SMTP_PROXY_STATE_REJECT
 };
 
 struct smtp_proxy_session {
@@ -119,6 +123,9 @@ struct smtp_proxy_session {
        struct event_base *ev_base;
 
        GString *upstream_greeting;
+
+       guint rbl_requests;
+       gchar *dnsbl_applied;
 };
 
 #ifndef HAVE_SA_SIGINFO
@@ -197,7 +204,7 @@ free_smtp_proxy_session (gpointer arg)
                        g_string_free (session->upstream_greeting, TRUE);
                }
 
-               if (session->state != SMTP_PROXY_STATE_PROXY) {
+               if (session->state != SMTP_PROXY_STATE_PROXY && session->state != SMTP_PROXY_STATE_REJECT) {
                        /* Send 521 fatal error */
                        write (session->sock, fatal_smtp_error, sizeof (fatal_smtp_error));
                }
@@ -226,6 +233,7 @@ smtp_proxy_err_proxy (GError * err, void *arg)
                msg_info ("abnormally closing connection, error: %s", err->message);
        }
        /* Free buffers */
+       session->state = SMTP_PROXY_STATE_REJECT;
        destroy_session (session->s);
 }
 
@@ -455,6 +463,90 @@ create_smtp_proxy_upstream_connection (struct smtp_proxy_session *session)
        return TRUE;
 }
 
+static void
+smtp_dnsbl_cb (struct rspamd_dns_reply *reply, void *arg)
+{
+       struct smtp_proxy_session                                               *session = arg;
+       const gchar                                                                             *p;
+       gint                                                                                     dots = 0;
+
+       session->rbl_requests --;
+
+       msg_debug ("got reply for %s: %s", reply->request->requested_name, dns_strerror (reply->code));
+
+       if (session->state != SMTP_PROXY_STATE_REJECT) {
+
+               if (reply->code == DNS_RC_NOERROR) {
+                       /* This means that address is in dnsbl */
+                       p = reply->request->requested_name;
+                       while (*p) {
+                               if (*p == '.') {
+                                       dots ++;
+                               }
+                               if (dots == 4) {
+                                       session->dnsbl_applied = (gchar *)p + 1;
+                                       break;
+                               }
+                               p ++;
+                       }
+                       session->state = SMTP_PROXY_STATE_REJECT;
+               }
+       }
+
+       if (session->rbl_requests == 0) {
+               if (session->state != SMTP_PROXY_STATE_REJECT) {
+                       /* Make proxy */
+                       if (!create_smtp_proxy_upstream_connection (session)) {
+                               rspamd_dispatcher_restore (session->dispatcher);
+                       }
+               }
+               else {
+                       /* TODO: add handler for denied connections */
+                       msg_info ("%s is denied by dnsbl: %s", inet_ntoa (session->client_addr), session->dnsbl_applied);
+                       if (!rspamd_dispatcher_write (session->dispatcher,
+                                       make_smtp_error (session->pool, 521, "%s Client denied by %s", "5.2.1", session->dnsbl_applied),
+                                       0, FALSE, TRUE)) {
+                               msg_err ("cannot write smtp error");
+                       }
+                       rspamd_dispatcher_restore (session->dispatcher);
+               }
+       }
+}
+
+/*
+ * Create requests to all rbls
+ */
+static void
+make_rbl_requests (struct smtp_proxy_session *session)
+{
+       GList                                                                   *cur;
+       gchar                                                                   *p, *dst;
+       guint                                                                    len;
+
+       cur = session->ctx->rbls;
+       while (cur) {
+               len = INET_ADDRSTRLEN + strlen (cur->data) + 1;
+               dst = memory_pool_alloc (session->pool, len);
+               /* Print ipv4 addr */
+               p = (gchar *)&session->client_addr.s_addr;
+               rspamd_snprintf (dst, len, "%ud.%ud.%ud.%ud.%s", (guint)p[3],
+                               (guint)p[2], (guint)p[1], (guint)p[0], cur->data);
+               if (make_dns_request (session->resolver, session->s, session->pool,
+                                                               smtp_dnsbl_cb, session, DNS_REQUEST_A, dst)) {
+                       session->rbl_requests ++;
+                       msg_debug ("send request to %s", dst);
+               }
+               cur = g_list_next (cur);
+       }
+
+       if (session->rbl_requests == 0) {
+               /* Create proxy */
+               if (! create_smtp_proxy_upstream_connection (session)) {
+                       rspamd_dispatcher_restore (session->dispatcher);
+               }
+       }
+}
+
 /* Resolving and delay handlers */
 /*
  * Return from a delay
@@ -468,15 +560,24 @@ smtp_delay_handler (gint fd, short what, void *arg)
                        session->delay_timer);
        if (session->state == SMTP_PROXY_STATE_DELAY) {
                /* TODO: Create upstream connection here */
-               create_smtp_proxy_upstream_connection (session);
+               if (session->ctx->rbls) {
+                       make_rbl_requests (session);
+               }
+               else {
+                       if (!create_smtp_proxy_upstream_connection (session)) {
+                               rspamd_dispatcher_restore (session->dispatcher);
+                       }
+               }
        }
        else {
                /* TODO: Write error here */
-               if (rspamd_dispatcher_write (session->dispatcher,
-                               make_smtp_error (session->pool, 550, "%s Improper use of SMTP command pipelining", "5.5.0"),
+               session->state = SMTP_PROXY_STATE_REJECT;
+               if (!rspamd_dispatcher_write (session->dispatcher,
+                               make_smtp_error (session->pool, 521, "%s Improper use of SMTP command pipelining", "5.2.1"),
                                0, FALSE, TRUE)) {
-                       destroy_session (session->s);
+                       msg_err ("cannot write smtp error");
                }
+               rspamd_dispatcher_restore (session->dispatcher);
        }
 }
 
@@ -509,7 +610,14 @@ smtp_make_delay (struct smtp_proxy_session *session)
        }
        else if (session->state == SMTP_PROXY_STATE_DELAY) {
                /* TODO: Create upstream connection here */
-               create_smtp_proxy_upstream_connection (session);
+               if (session->ctx->rbls) {
+                       make_rbl_requests (session);
+               }
+               else {
+                       if (!create_smtp_proxy_upstream_connection (session)) {
+                               rspamd_dispatcher_restore (session->dispatcher);
+                       }
+               }
        }
 }
 
@@ -612,16 +720,27 @@ smtp_proxy_read_socket (f_str_t * in, void *arg)
        struct smtp_proxy_session            *session = arg;
 
        /* This can be called only if client is using invalid pipelining */
-       if (rspamd_dispatcher_write (session->dispatcher,
-                       make_smtp_error (session->pool, 550, "%s Improper use of SMTP command pipelining", "5.5.0"),
+       session->state = SMTP_PROXY_STATE_REJECT;
+       if (!rspamd_dispatcher_write (session->dispatcher,
+                       make_smtp_error (session->pool, 521, "%s Improper use of SMTP command pipelining", "5.2.1"),
                        0, FALSE, TRUE)) {
-               destroy_session (session->s);
-       }
-       else {
-               return FALSE;
+               msg_err ("cannot write smtp error");
        }
+       destroy_session (session->s);
 
-       return TRUE;
+       return FALSE;
+}
+
+/*
+ * Actually called only if something goes wrong
+ */
+static                          gboolean
+smtp_proxy_write_socket (void *arg)
+{
+       struct smtp_proxy_session            *session = arg;
+
+       destroy_session (session->s);
+       return FALSE;
 }
 
 /*
@@ -633,8 +752,8 @@ smtp_proxy_err_socket (GError * err, void *arg)
        struct smtp_proxy_session            *session = arg;
 
        if (err) {
-               g_error_free (err);
                msg_info ("abnormally closing connection, error: %s", err->message);
+               g_error_free (err);
        }
        /* Free buffers */
        destroy_session (session->s);
@@ -697,7 +816,7 @@ accept_socket (gint fd, short what, void *arg)
        }
        else {
                session->dispatcher = rspamd_create_dispatcher (session->ev_base, nfd, BUFFER_ANY,
-                                                               smtp_proxy_read_socket, NULL, smtp_proxy_err_socket,
+                                                               smtp_proxy_read_socket, smtp_proxy_write_socket, smtp_proxy_err_socket,
                                                                &session->ctx->smtp_timeout, session);
                session->dispatcher->peer_addr = session->client_addr.s_addr;
        }
@@ -721,15 +840,17 @@ init_smtp_proxy (void)
        register_worker_opt (type, "upstreams", xml_handle_string, ctx,
                                G_STRUCT_OFFSET (struct smtp_proxy_ctx, upstreams_str));
        register_worker_opt (type, "timeout", xml_handle_seconds, ctx,
-                                       G_STRUCT_OFFSET (struct smtp_proxy_ctx, smtp_timeout_raw));
+                               G_STRUCT_OFFSET (struct smtp_proxy_ctx, smtp_timeout_raw));
        register_worker_opt (type, "delay", xml_handle_seconds, ctx,
-                                       G_STRUCT_OFFSET (struct smtp_proxy_ctx, smtp_delay));
+                               G_STRUCT_OFFSET (struct smtp_proxy_ctx, smtp_delay));
        register_worker_opt (type, "jitter", xml_handle_seconds, ctx,
-                                               G_STRUCT_OFFSET (struct smtp_proxy_ctx, delay_jitter));
+                               G_STRUCT_OFFSET (struct smtp_proxy_ctx, delay_jitter));
        register_worker_opt (type, "xclient", xml_handle_boolean, ctx,
-                                                       G_STRUCT_OFFSET (struct smtp_proxy_ctx, use_xclient));
+                               G_STRUCT_OFFSET (struct smtp_proxy_ctx, use_xclient));
        register_worker_opt (type, "proxy_buffer", xml_handle_size, ctx,
-                                                               G_STRUCT_OFFSET (struct smtp_proxy_ctx, proxy_buf_len));
+                               G_STRUCT_OFFSET (struct smtp_proxy_ctx, proxy_buf_len));
+       register_worker_opt (type, "dnsbl", xml_handle_list, ctx,
+                               G_STRUCT_OFFSET (struct smtp_proxy_ctx, rbls));
 
        return ctx;
 }