summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@rambler-co.ru>2012-04-27 20:51:56 +0400
committerVsevolod Stakhov <vsevolod@rambler-co.ru>2012-04-27 20:51:56 +0400
commit8fe7e64dfae9c4da3a6b38769bf5d54d46d50beb (patch)
treec750b1e0a00803fe329977943275be6c4d433d2a
parentfe5e1614f36236654623667df4f317ae5e5e1806 (diff)
downloadrspamd-8fe7e64dfae9c4da3a6b38769bf5d54d46d50beb.tar.gz
rspamd-8fe7e64dfae9c4da3a6b38769bf5d54d46d50beb.zip
* Add ability to specify dnsbls for smtp_proxy.
Fix handling of params with the same name in configuration. Add ability for rspamc to bind on a local address.
-rw-r--r--lib/client/librspamdclient.c48
-rw-r--r--lib/client/librspamdclient.h7
-rw-r--r--src/cfg_xml.c1
-rw-r--r--src/client/rspamc.c18
-rw-r--r--src/smtp_proxy.c163
5 files changed, 202 insertions, 35 deletions
diff --git a/lib/client/librspamdclient.c b/lib/client/librspamdclient.c
index 6351175bc..dac49b5ae 100644
--- a/lib/client/librspamdclient.c
+++ b/lib/client/librspamdclient.c
@@ -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);
}
diff --git a/lib/client/librspamdclient.h b/lib/client/librspamdclient.h
index 54695a34d..6eea8627e 100644
--- a/lib/client/librspamdclient.h
+++ b/lib/client/librspamdclient.h
@@ -28,6 +28,8 @@ struct rspamd_metric {
struct rspamd_connection;
struct rspamd_client;
+struct in_addr;
+
/**
* Result of scan
*/
@@ -44,6 +46,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
*/
gboolean rspamd_add_server (struct rspamd_client* client, const gchar *host,
diff --git a/src/cfg_xml.c b/src/cfg_xml.c
index 924dc283e..d681ec252 100644
--- a/src/cfg_xml.c
+++ b/src/cfg_xml.c
@@ -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);
}
}
diff --git a/src/client/rspamc.c b/src/client/rspamc.c
index 7219d35b5..c96b5246a 100644
--- a/src/client/rspamc.c
+++ b/src/client/rspamc.c
@@ -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 */
diff --git a/src/smtp_proxy.c b/src/smtp_proxy.c
index 97e56a956..6ebc135f5 100644
--- a/src/smtp_proxy.c
+++ b/src/smtp_proxy.c
@@ -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;
}