guint servers_num;
guint connect_timeout;
guint read_timeout;
+ struct in_addr *bind_addr;
};
struct rspamd_connection {
}
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);
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));
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 **/
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) {
* Init rspamd client library
*/
struct rspamd_client*
-rspamd_client_init (void)
+rspamd_client_init_binded (const struct in_addr *addr)
{
struct rspamd_client *client;
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
*/
void
rspamd_client_close (struct rspamd_client *client)
{
+ if (client->bind_addr) {
+ g_free (client->bind_addr);
+ }
g_free (client);
}
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;
{ "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 }
};
{
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 */
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 {
struct event_base *ev_base;
GString *upstream_greeting;
+
+ guint rbl_requests;
+ gchar *dnsbl_applied;
};
#ifndef HAVE_SA_SIGINFO
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));
}
msg_info ("abnormally closing connection, error: %s", err->message);
}
/* Free buffers */
+ session->state = SMTP_PROXY_STATE_REJECT;
destroy_session (session->s);
}
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
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);
}
}
}
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);
+ }
+ }
}
}
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;
}
/*
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);
}
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;
}
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;
}