gboolean smtp_send_upstream_message (struct smtp_session *session) { rspamd_dispatcher_pause (session->dispatcher); rspamd_dispatcher_restore (session->upstream_dispatcher); session->upstream_state = SMTP_STATE_IN_SENDFILE; session->state = SMTP_STATE_WAIT_UPSTREAM; if (!rspamd_dispatcher_sendfile (session->upstream_dispatcher, session->temp_fd, session->temp_size)) { msg_err ("sendfile failed: %s", strerror (errno)); goto err; } return TRUE; err: session->error = SMTP_ERROR_FILE; session->state = SMTP_STATE_CRITICAL_ERROR; if (!rspamd_dispatcher_write (session->dispatcher, session->error, 0, FALSE, TRUE)) { return FALSE; } destroy_session (session->s); return FALSE; }
static int lua_io_dispatcher_write (lua_State *L) { struct rspamd_io_dispatcher_s *io_dispatcher = lua_check_io_dispatcher (L); gboolean delayed = FALSE, res; const gchar *data; size_t len; if (io_dispatcher) { if (lua_gettop (L) < 2) { msg_err ("invalid number of arguments to io_dispatcher.create: %d", lua_gettop (L)); lua_pushboolean (L, FALSE); } else { data = lua_tolstring (L, 2, &len); if (lua_gettop (L) > 2) { delayed = lua_toboolean (L, 3); } res = rspamd_dispatcher_write (io_dispatcher, (void *)data, len, delayed, FALSE); lua_pushboolean (L, res); } } else { lua_pushnil (L); } return 1; }
void smtp_upstream_err_socket (GError *err, void *arg) { struct smtp_session *session = arg; msg_info ("abnormally closing connection with upstream %s, error: %s", rspamd_upstream_name (session->upstream), err->message); session->error = SMTP_ERROR_UPSTREAM; session->state = SMTP_STATE_CRITICAL_ERROR; /* XXX: assume upstream errors as critical errors */ rspamd_dispatcher_restore (session->dispatcher); if (!rspamd_dispatcher_write (session->dispatcher, session->error, 0, FALSE, TRUE)) { return; } if (!rspamd_dispatcher_write (session->dispatcher, CRLF, sizeof (CRLF) - 1, FALSE, TRUE)) { return; } rspamd_upstream_fail (session->upstream); rspamd_session_destroy (session->s); }
void smtp_upstream_finalize_connection (gpointer data) { struct smtp_session *session = data; if (session->state != SMTP_STATE_CRITICAL_ERROR) { if (!rspamd_dispatcher_write (session->upstream_dispatcher, "QUIT" CRLF, 0, FALSE, TRUE)) { msg_warn ("cannot send correctly closing message to upstream"); } } rspamd_remove_dispatcher (session->upstream_dispatcher); session->upstream_dispatcher = NULL; close (session->upstream_sock); session->upstream_sock = -1; }
gboolean smtp_upstream_write_socket (void *arg) { struct smtp_session *session = arg; if (session->upstream_state == SMTP_STATE_IN_SENDFILE) { session->upstream_state = SMTP_STATE_AFTER_DATA; return rspamd_dispatcher_write (session->upstream_dispatcher, CRLF DATA_END_TRAILER, sizeof (CRLF DATA_END_TRAILER) - 1, FALSE, TRUE); } return TRUE; }
gboolean smtp_upstream_read_socket (rspamd_ftok_t * in, void *arg) { struct smtp_session *session = arg; gchar outbuf[BUFSIZ]; gint r; msg_debug ("in: %T, state: %d", in, session->upstream_state); switch (session->upstream_state) { case SMTP_STATE_GREETING: r = check_smtp_ustream_reply (in, '2'); if (r == -1) { session->error = rspamd_mempool_alloc (session->pool, in->len + 1); rspamd_strlcpy (session->error, in->begin, in->len + 1); /* XXX: assume upstream errors as critical errors */ session->state = SMTP_STATE_CRITICAL_ERROR; rspamd_dispatcher_restore (session->dispatcher); if (!rspamd_dispatcher_write (session->dispatcher, session->error, in->len, FALSE, TRUE)) { goto err; } if (!rspamd_dispatcher_write (session->dispatcher, CRLF, sizeof (CRLF) - 1, FALSE, TRUE)) { goto err; } rspamd_session_destroy (session->s); return FALSE; } else if (r == 1) { if (session->ctx->use_xclient) { r = rspamd_snprintf (outbuf, sizeof (outbuf), "XCLIENT NAME=%s ADDR=%s" CRLF, session->resolved ? session->hostname : "[UNDEFINED]", inet_ntoa (session->client_addr)); session->upstream_state = SMTP_STATE_HELO; return rspamd_dispatcher_write (session->upstream_dispatcher, outbuf, r, FALSE, FALSE); } else { session->upstream_state = SMTP_STATE_FROM; if (session->helo) { r = rspamd_snprintf (outbuf, sizeof (outbuf), "%s %s" CRLF, session->esmtp ? "EHLO" : "HELO", session->helo); } else { return smtp_upstream_read_socket (in, arg); } return rspamd_dispatcher_write (session->upstream_dispatcher, outbuf, r, FALSE, FALSE); } } break; case SMTP_STATE_HELO: r = check_smtp_ustream_reply (in, '2'); if (r == -1) { session->error = rspamd_mempool_alloc (session->pool, in->len + 1); rspamd_strlcpy (session->error, in->begin, in->len + 1); /* XXX: assume upstream errors as critical errors */ session->state = SMTP_STATE_CRITICAL_ERROR; rspamd_dispatcher_restore (session->dispatcher); if (!rspamd_dispatcher_write (session->dispatcher, session->error, in->len, FALSE, TRUE)) { goto err; } if (!rspamd_dispatcher_write (session->dispatcher, CRLF, sizeof (CRLF) - 1, FALSE, TRUE)) { goto err; } rspamd_session_destroy (session->s); return FALSE; } else if (r == 1) { session->upstream_state = SMTP_STATE_FROM; if (session->helo) { r = rspamd_snprintf (outbuf, sizeof (outbuf), "%s %s" CRLF, session->esmtp ? "EHLO" : "HELO", session->helo); } else { return smtp_upstream_read_socket (in, arg); } return rspamd_dispatcher_write (session->upstream_dispatcher, outbuf, r, FALSE, FALSE); } break; case SMTP_STATE_FROM: r = check_smtp_ustream_reply (in, '2'); if (r == -1) { session->error = rspamd_mempool_alloc (session->pool, in->len + 1); rspamd_strlcpy (session->error, in->begin, in->len + 1); /* XXX: assume upstream errors as critical errors */ session->state = SMTP_STATE_CRITICAL_ERROR; rspamd_dispatcher_restore (session->dispatcher); if (!rspamd_dispatcher_write (session->dispatcher, session->error, in->len, FALSE, TRUE)) { goto err; } if (!rspamd_dispatcher_write (session->dispatcher, CRLF, sizeof (CRLF) - 1, FALSE, TRUE)) { goto err; } rspamd_session_destroy (session->s); return FALSE; } else if (r == 1) { r = rspamd_snprintf (outbuf, sizeof (outbuf), "MAIL FROM: "); r += smtp_upstream_write_list (session->from, outbuf + r, sizeof (outbuf) - r); session->upstream_state = SMTP_STATE_RCPT; return rspamd_dispatcher_write (session->upstream_dispatcher, outbuf, r, FALSE, FALSE); } break; case SMTP_STATE_RCPT: r = check_smtp_ustream_reply (in, '2'); if (r == -1) { session->error = rspamd_mempool_alloc (session->pool, in->len + 1); rspamd_strlcpy (session->error, in->begin, in->len + 1); /* XXX: assume upstream errors as critical errors */ session->state = SMTP_STATE_CRITICAL_ERROR; rspamd_dispatcher_restore (session->dispatcher); if (!rspamd_dispatcher_write (session->dispatcher, session->error, in->len, FALSE, TRUE)) { goto err; } if (!rspamd_dispatcher_write (session->dispatcher, CRLF, sizeof (CRLF) - 1, FALSE, TRUE)) { goto err; } rspamd_session_destroy (session->s); return FALSE; } else if (r == 1) { r = rspamd_snprintf (outbuf, sizeof (outbuf), "RCPT TO: "); session->cur_rcpt = g_list_first (session->rcpt); r += smtp_upstream_write_list (session->cur_rcpt->data, outbuf + r, sizeof (outbuf) - r); session->cur_rcpt = g_list_next (session->cur_rcpt); session->upstream_state = SMTP_STATE_BEFORE_DATA; return rspamd_dispatcher_write (session->upstream_dispatcher, outbuf, r, FALSE, FALSE); } break; case SMTP_STATE_BEFORE_DATA: r = check_smtp_ustream_reply (in, '2'); if (r == -1) { session->error = rspamd_mempool_alloc (session->pool, in->len + 1); rspamd_strlcpy (session->error, in->begin, in->len + 1); rspamd_dispatcher_restore (session->dispatcher); if (!rspamd_dispatcher_write (session->dispatcher, session->error, in->len, FALSE, TRUE)) { goto err; } if (!rspamd_dispatcher_write (session->dispatcher, CRLF, sizeof (CRLF) - 1, FALSE, TRUE)) { goto err; } if (session->cur_rcpt) { session->rcpt = g_list_delete_link (session->rcpt, session->cur_rcpt); } else { session->rcpt = g_list_delete_link (session->rcpt, session->rcpt); } session->errors++; session->state = SMTP_STATE_RCPT; return TRUE; } else if (r == 1) { if (session->cur_rcpt != NULL) { r = rspamd_snprintf (outbuf, sizeof (outbuf), "RCPT TO: "); r += smtp_upstream_write_list (session->cur_rcpt, outbuf + r, sizeof (outbuf) - r); session->cur_rcpt = g_list_next (session->cur_rcpt); if (!rspamd_dispatcher_write (session->upstream_dispatcher, outbuf, r, FALSE, FALSE)) { goto err; } } else { session->upstream_state = SMTP_STATE_DATA; rspamd_dispatcher_pause (session->upstream_dispatcher); } session->error = rspamd_mempool_alloc (session->pool, in->len + 1); rspamd_strlcpy (session->error, in->begin, in->len + 1); /* Write to client */ if (!rspamd_dispatcher_write (session->dispatcher, session->error, in->len, FALSE, TRUE)) { goto err; } if (!rspamd_dispatcher_write (session->dispatcher, CRLF, sizeof (CRLF) - 1, FALSE, TRUE)) { goto err; } if (session->state == SMTP_STATE_WAIT_UPSTREAM) { rspamd_dispatcher_restore (session->dispatcher); session->state = SMTP_STATE_RCPT; } } break; case SMTP_STATE_DATA: r = check_smtp_ustream_reply (in, '3'); if (r == -1) { session->error = rspamd_mempool_alloc (session->pool, in->len + 1); rspamd_strlcpy (session->error, in->begin, in->len + 1); /* XXX: assume upstream errors as critical errors */ session->state = SMTP_STATE_CRITICAL_ERROR; rspamd_dispatcher_restore (session->dispatcher); if (!rspamd_dispatcher_write (session->dispatcher, session->error, 0, FALSE, TRUE)) { goto err; } if (!rspamd_dispatcher_write (session->dispatcher, CRLF, sizeof (CRLF) - 1, FALSE, TRUE)) { goto err; } rspamd_session_destroy (session->s); return FALSE; } else if (r == 1) { if (!make_smtp_tempfile (session)) { session->error = SMTP_ERROR_FILE; session->state = SMTP_STATE_CRITICAL_ERROR; rspamd_dispatcher_restore (session->dispatcher); if (!rspamd_dispatcher_write (session->dispatcher, session->error, 0, FALSE, TRUE)) { goto err; } rspamd_session_destroy (session->s); return FALSE; } session->state = SMTP_STATE_AFTER_DATA; session->error = SMTP_ERROR_DATA_OK; rspamd_dispatcher_restore (session->dispatcher); if (!rspamd_dispatcher_write (session->dispatcher, session->error, 0, FALSE, TRUE)) { goto err; } rspamd_dispatcher_pause (session->upstream_dispatcher); rspamd_set_dispatcher_policy (session->dispatcher, BUFFER_LINE, 0); session->dispatcher->strip_eol = FALSE; return TRUE; } break; case SMTP_STATE_AFTER_DATA: session->error = rspamd_mempool_alloc (session->pool, in->len + 1); rspamd_strlcpy (session->error, in->begin, in->len + 1); session->state = SMTP_STATE_DATA; rspamd_dispatcher_restore (session->dispatcher); if (!rspamd_dispatcher_write (session->dispatcher, session->error, 0, FALSE, TRUE)) { goto err; } if (!rspamd_dispatcher_write (session->dispatcher, CRLF, sizeof (CRLF) - 1, FALSE, TRUE)) { goto err; } if (!rspamd_dispatcher_write (session->upstream_dispatcher, "QUIT" CRLF, sizeof ("QUIT" CRLF) - 1, FALSE, TRUE)) { goto err; } session->upstream_state = SMTP_STATE_END; return TRUE; break; case SMTP_STATE_END: r = check_smtp_ustream_reply (in, '5'); if (r == -1) { session->error = rspamd_mempool_alloc (session->pool, in->len + 1); rspamd_strlcpy (session->error, in->begin, in->len + 1); /* XXX: assume upstream errors as critical errors */ session->state = SMTP_STATE_CRITICAL_ERROR; rspamd_dispatcher_restore (session->dispatcher); if (!rspamd_dispatcher_write (session->dispatcher, session->error, 0, FALSE, TRUE)) { goto err; } if (!rspamd_dispatcher_write (session->dispatcher, CRLF, sizeof (CRLF) - 1, FALSE, TRUE)) { goto err; } rspamd_session_destroy (session->s); return FALSE; } else { rspamd_session_remove_event (session->s, (event_finalizer_t)smtp_upstream_finalize_connection, session); } return FALSE; break; default: msg_err ("got upstream reply at unexpected state: %d, reply: %T", session->upstream_state, in); session->state = SMTP_STATE_CRITICAL_ERROR; rspamd_dispatcher_restore (session->dispatcher); if (!rspamd_dispatcher_write (session->dispatcher, session->error, 0, FALSE, TRUE)) { goto err; } if (!rspamd_dispatcher_write (session->dispatcher, CRLF, sizeof (CRLF) - 1, FALSE, TRUE)) { goto err; } rspamd_session_destroy (session->s); return FALSE; } return TRUE; err: msg_warn ("write error occured"); return FALSE; }
gboolean write_smtp_reply (struct smtp_session *session) { gchar logbuf[1024], *new_subject; const gchar *old_subject; struct smtp_metric_callback_data cd; GMimeStream *stream; gint old_fd, sublen; /* Check metrics */ cd.session = session; cd.action = METRIC_ACTION_NOACTION; cd.res = NULL; cd.log_buf = logbuf; cd.log_offset = rspamd_snprintf (logbuf, sizeof (logbuf), "id: <%s>, qid: <%s>, ", session->task->message_id, session->task->queue_id); cd.log_size = sizeof (logbuf); if (session->task->user) { cd.log_offset += rspamd_snprintf (logbuf + cd.log_offset, sizeof (logbuf) - cd.log_offset, "user: %s, ", session->task->user); } g_hash_table_foreach (session->task->results, smtp_metric_callback, &cd); msg_info ("%s", logbuf); if (cd.action <= METRIC_ACTION_REJECT) { if (!rspamd_dispatcher_write (session->dispatcher, session->ctx->reject_message, 0, FALSE, TRUE)) { return FALSE; } if (!rspamd_dispatcher_write (session->dispatcher, CRLF, sizeof (CRLF) - 1, FALSE, TRUE)) { return FALSE; } destroy_session (session->s); return FALSE; } else if (cd.action <= METRIC_ACTION_ADD_HEADER || cd.action <= METRIC_ACTION_REWRITE_SUBJECT) { old_fd = session->temp_fd; if (!make_smtp_tempfile (session)) { session->error = SMTP_ERROR_FILE; session->state = SMTP_STATE_CRITICAL_ERROR; rspamd_dispatcher_restore (session->dispatcher); if (!rspamd_dispatcher_write (session->dispatcher, session->error, 0, FALSE, TRUE)) { goto err; } destroy_session (session->s); return FALSE; } if (cd.action <= METRIC_ACTION_REWRITE_SUBJECT) { /* XXX: add this action */ old_subject = g_mime_message_get_subject (session->task->message); if (old_subject != NULL) { sublen = strlen (old_subject) + sizeof (SPAM_SUBJECT); new_subject = rspamd_mempool_alloc (session->pool, sublen); rspamd_snprintf (new_subject, sublen, "%s%s", SPAM_SUBJECT, old_subject); } else { new_subject = SPAM_SUBJECT; } g_mime_message_set_subject (session->task->message, new_subject); } else if (cd.action <= METRIC_ACTION_ADD_HEADER) { #ifndef GMIME24 g_mime_message_add_header (session->task->message, "X-Spam", "true"); #else g_mime_object_append_header (GMIME_OBJECT ( session->task->message), "X-Spam", "true"); #endif } stream = g_mime_stream_fs_new (session->temp_fd); g_mime_stream_fs_set_owner (GMIME_STREAM_FS (stream), FALSE); close (old_fd); if (g_mime_object_write_to_stream (GMIME_OBJECT (session->task->message), stream) == -1) { msg_err ("cannot write MIME object to stream: %s", strerror (errno)); session->error = SMTP_ERROR_FILE; session->state = SMTP_STATE_CRITICAL_ERROR; rspamd_dispatcher_restore (session->dispatcher); if (!rspamd_dispatcher_write (session->dispatcher, session->error, 0, FALSE, TRUE)) { goto err; } destroy_session (session->s); return FALSE; } g_object_unref (stream); } /* XXX: Add other actions */ return smtp_send_upstream_message (session); err: session->error = SMTP_ERROR_FILE; session->state = SMTP_STATE_CRITICAL_ERROR; if (!rspamd_dispatcher_write (session->dispatcher, session->error, 0, FALSE, TRUE)) { return FALSE; } destroy_session (session->s); return FALSE; }
static gboolean sync_read (f_str_t * in, void *arg) { struct rspamd_sync_ctx *ctx = arg; gchar buf[256]; guint64 rev = 0; time_t ti = 0; if (in->len == 0) { /* Skip empty lines */ return TRUE; } switch (ctx->state) { case SYNC_STATE_GREETING: /* Skip greeting line and write sync command */ /* Write initial data */ statfile_get_revision (ctx->real_statfile, &rev, &ti); rev = rspamd_snprintf (buf, sizeof (buf), "sync %s %uL %T" CRLF, ctx->st->symbol, rev, ti); ctx->state = SYNC_STATE_READ_LINE; return rspamd_dispatcher_write (ctx->dispatcher, buf, rev, FALSE, FALSE); break; case SYNC_STATE_READ_LINE: /* Try to parse line from server */ if (!parse_revision_line (ctx, in)) { msg_info ("cannot parse line of length %z: '%*s'", in->len, (gint)in->len, in->begin); close (ctx->sock); rspamd_remove_dispatcher (ctx->dispatcher); ctx->is_busy = FALSE; return FALSE; } else if (ctx->state != SYNC_STATE_QUIT) { if (ctx->new_len > 0) { ctx->state = SYNC_STATE_READ_REV; rspamd_set_dispatcher_policy (ctx->dispatcher, BUFFER_CHARACTER, ctx->new_len); } } else { /* Quit this session */ msg_info ("sync ended for: %s", ctx->st->symbol); close (ctx->sock); rspamd_remove_dispatcher (ctx->dispatcher); ctx->is_busy = FALSE; /* Immediately return from callback */ return FALSE; } break; case SYNC_STATE_READ_REV: /* In now contains all blocks of specified revision, so we can read them directly */ if (!read_blocks (ctx, in)) { msg_info ("cannot read blocks"); close (ctx->sock); rspamd_remove_dispatcher (ctx->dispatcher); ctx->is_busy = FALSE; return FALSE; } statfile_set_revision (ctx->real_statfile, ctx->new_rev, ctx->new_time); msg_info ("set new revision: %uL, readed %z bytes", ctx->new_rev, in->len); /* Now try to read other revision or END line */ ctx->state = SYNC_STATE_READ_LINE; rspamd_set_dispatcher_policy (ctx->dispatcher, BUFFER_LINE, 0); break; case SYNC_STATE_QUIT: close (ctx->sock); rspamd_remove_dispatcher (ctx->dispatcher); ctx->is_busy = FALSE; return FALSE; } return TRUE; }