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_pause (lua_State *L) { struct rspamd_io_dispatcher_s *io_dispatcher = lua_check_io_dispatcher (L); if (io_dispatcher) { rspamd_dispatcher_pause (io_dispatcher); return 0; } else { lua_pushnil (L); } return 1; }
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; }