From 8fe7e64dfae9c4da3a6b38769bf5d54d46d50beb Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Fri, 27 Apr 2012 20:51:56 +0400 Subject: [PATCH] * 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. --- lib/client/librspamdclient.c | 48 ++++++++--- lib/client/librspamdclient.h | 7 ++ src/cfg_xml.c | 1 + src/client/rspamc.c | 18 +++- src/smtp_proxy.c | 163 ++++++++++++++++++++++++++++++----- 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 */ @@ -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 */ 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; } -- 2.39.5