You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

smtp_utils.c 9.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. /*-
  2. * Copyright 2016 Vsevolod Stakhov
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "config.h"
  17. #include "rspamd.h"
  18. #include "smtp.h"
  19. #include "unix-std.h"
  20. void
  21. free_smtp_session (gpointer arg)
  22. {
  23. struct smtp_session *session = arg;
  24. if (session) {
  25. if (session->task) {
  26. if (session->task->msg.begin) {
  27. munmap ((gpointer)session->task->msg.begin,
  28. session->task->msg.len);
  29. }
  30. rspamd_task_free (session->task);
  31. }
  32. if (session->rcpt) {
  33. g_list_free (session->rcpt);
  34. }
  35. if (session->dispatcher) {
  36. rspamd_remove_dispatcher (session->dispatcher);
  37. }
  38. close (session->sock);
  39. if (session->temp_name != NULL) {
  40. unlink (session->temp_name);
  41. }
  42. if (session->temp_fd != -1) {
  43. close (session->temp_fd);
  44. }
  45. rspamd_mempool_delete (session->pool);
  46. g_free (session);
  47. }
  48. }
  49. gboolean
  50. create_smtp_upstream_connection (struct smtp_session *session)
  51. {
  52. struct upstream *selected;
  53. /* Try to select upstream */
  54. selected = rspamd_upstream_get (session->ctx->upstreams,
  55. RSPAMD_UPSTREAM_ROUND_ROBIN, NULL, 0);
  56. if (selected == NULL) {
  57. msg_err ("no upstreams suitable found");
  58. return FALSE;
  59. }
  60. session->upstream = selected;
  61. /* Now try to create socket */
  62. session->upstream_sock = rspamd_inet_address_connect (
  63. rspamd_upstream_addr (selected), SOCK_STREAM, TRUE);
  64. if (session->upstream_sock == -1) {
  65. msg_err ("cannot make a connection to %s", rspamd_upstream_name (selected));
  66. rspamd_upstream_fail (selected);
  67. return FALSE;
  68. }
  69. /* Create a dispatcher for upstream connection */
  70. session->upstream_dispatcher = rspamd_create_dispatcher (session->ev_base,
  71. session->upstream_sock,
  72. BUFFER_LINE,
  73. smtp_upstream_read_socket,
  74. smtp_upstream_write_socket,
  75. smtp_upstream_err_socket,
  76. &session->ctx->smtp_timeout,
  77. session);
  78. session->state = SMTP_STATE_WAIT_UPSTREAM;
  79. session->upstream_state = SMTP_STATE_GREETING;
  80. rspamd_session_add_event (session->s,
  81. (event_finalizer_t)smtp_upstream_finalize_connection,
  82. session,
  83. g_quark_from_static_string ("smtp proxy"));
  84. return TRUE;
  85. }
  86. gboolean
  87. smtp_send_upstream_message (struct smtp_session *session)
  88. {
  89. rspamd_dispatcher_pause (session->dispatcher);
  90. rspamd_dispatcher_restore (session->upstream_dispatcher);
  91. session->upstream_state = SMTP_STATE_IN_SENDFILE;
  92. session->state = SMTP_STATE_WAIT_UPSTREAM;
  93. if (!rspamd_dispatcher_sendfile (session->upstream_dispatcher,
  94. session->temp_fd, session->temp_size)) {
  95. msg_err ("sendfile failed: %s", strerror (errno));
  96. goto err;
  97. }
  98. return TRUE;
  99. err:
  100. session->error = SMTP_ERROR_FILE;
  101. session->state = SMTP_STATE_CRITICAL_ERROR;
  102. if (!rspamd_dispatcher_write (session->dispatcher, session->error, 0, FALSE,
  103. TRUE)) {
  104. return FALSE;
  105. }
  106. rspamd_session_destroy (session->s);
  107. return FALSE;
  108. }
  109. struct smtp_metric_callback_data {
  110. struct smtp_session *session;
  111. enum rspamd_metric_action action;
  112. struct metric_result *res;
  113. gchar *log_buf;
  114. gint log_offset;
  115. gint log_size;
  116. gboolean alive;
  117. };
  118. static void
  119. smtp_metric_symbols_callback (gpointer key, gpointer value, void *user_data)
  120. {
  121. struct smtp_metric_callback_data *cd = user_data;
  122. cd->log_offset += rspamd_snprintf (cd->log_buf + cd->log_offset,
  123. cd->log_size - cd->log_offset,
  124. "%s,",
  125. (gchar *)key);
  126. }
  127. static void
  128. smtp_metric_callback (gpointer key, gpointer value, gpointer ud)
  129. {
  130. struct smtp_metric_callback_data *cd = ud;
  131. struct metric_result *metric_res = value;
  132. enum rspamd_metric_action action = METRIC_ACTION_NOACTION;
  133. double ms = 0, rs = 0;
  134. gboolean is_spam = FALSE;
  135. struct rspamd_task *task;
  136. task = cd->session->task;
  137. /* XXX rewrite */
  138. ms = metric_res->metric->actions[METRIC_ACTION_REJECT].score;
  139. rs = metric_res->metric->actions[METRIC_ACTION_REJECT].score;
  140. #if 0
  141. if (!check_metric_settings (metric_res, &ms, &rs)) {
  142. ms = metric_res->metric->actions[METRIC_ACTION_REJECT].score;
  143. rs = metric_res->metric->actions[METRIC_ACTION_REJECT].score;
  144. }
  145. if (!check_metric_action_settings (task, metric_res, metric_res->score,
  146. &action)) {
  147. action =
  148. check_metric_action (metric_res->score, ms, metric_res->metric);
  149. }
  150. #endif
  151. if (metric_res->score >= ms) {
  152. is_spam = 1;
  153. }
  154. if (action < cd->action) {
  155. cd->action = action;
  156. cd->res = metric_res;
  157. }
  158. if (!RSPAMD_TASK_IS_SKIPPED (task)) {
  159. cd->log_offset += rspamd_snprintf (cd->log_buf + cd->log_offset,
  160. cd->log_size - cd->log_offset,
  161. "(%s: %c (%s): [%.2f/%.2f/%.2f] [",
  162. (gchar *)key,
  163. is_spam ? 'T' : 'F',
  164. rspamd_action_to_str (action),
  165. metric_res->score,
  166. ms,
  167. rs);
  168. }
  169. else {
  170. cd->log_offset += rspamd_snprintf (cd->log_buf + cd->log_offset,
  171. cd->log_size - cd->log_offset,
  172. "(%s: %c (default): [%.2f/%.2f/%.2f] [",
  173. (gchar *)key,
  174. 'S',
  175. metric_res->score,
  176. ms,
  177. rs);
  178. }
  179. g_hash_table_foreach (metric_res->symbols, smtp_metric_symbols_callback,
  180. cd);
  181. /* Remove last , from log buf */
  182. if (cd->log_buf[cd->log_offset - 1] == ',') {
  183. cd->log_buf[--cd->log_offset] = '\0';
  184. }
  185. cd->log_offset += rspamd_snprintf (cd->log_buf + cd->log_offset,
  186. cd->log_size - cd->log_offset,
  187. "]), len: %z, time: %s,",
  188. task->msg.len,
  189. rspamd_log_check_time (task->time_real, task->time_virtual,
  190. task->cfg->clock_res));
  191. }
  192. gboolean
  193. make_smtp_tempfile (struct smtp_session *session)
  194. {
  195. gsize r;
  196. r = strlen (session->cfg->temp_dir) + sizeof ("/rspamd-XXXXXX");
  197. session->temp_name = rspamd_mempool_alloc (session->pool, r);
  198. rspamd_snprintf (session->temp_name,
  199. r,
  200. "%s/rspamd-XXXXXX",
  201. session->cfg->temp_dir);
  202. #ifdef HAVE_MKSTEMP
  203. /* Umask is set before */
  204. session->temp_fd = mkstemp (session->temp_name);
  205. #else
  206. session->temp_fd = g_mkstemp_full (session->temp_name,
  207. O_RDWR,
  208. S_IWUSR | S_IRUSR);
  209. #endif
  210. if (session->temp_fd == -1) {
  211. msg_err ("mkstemp error: %s", strerror (errno));
  212. return FALSE;
  213. }
  214. return TRUE;
  215. }
  216. gboolean
  217. write_smtp_reply (struct smtp_session *session)
  218. {
  219. gchar logbuf[1024], *new_subject;
  220. const gchar *old_subject;
  221. struct smtp_metric_callback_data cd;
  222. GMimeStream *stream;
  223. gint old_fd, sublen;
  224. /* Check metrics */
  225. cd.session = session;
  226. cd.action = METRIC_ACTION_NOACTION;
  227. cd.res = NULL;
  228. cd.log_buf = logbuf;
  229. cd.log_offset = rspamd_snprintf (logbuf,
  230. sizeof (logbuf),
  231. "id: <%s>, qid: <%s>, ",
  232. session->task->message_id,
  233. session->task->queue_id);
  234. cd.log_size = sizeof (logbuf);
  235. if (session->task->user) {
  236. cd.log_offset += rspamd_snprintf (logbuf + cd.log_offset,
  237. sizeof (logbuf) - cd.log_offset,
  238. "user: %s, ",
  239. session->task->user);
  240. }
  241. g_hash_table_foreach (session->task->results, smtp_metric_callback, &cd);
  242. msg_info ("%s", logbuf);
  243. if (cd.action <= METRIC_ACTION_REJECT) {
  244. if (!rspamd_dispatcher_write (session->dispatcher,
  245. session->ctx->reject_message, 0, FALSE, TRUE)) {
  246. return FALSE;
  247. }
  248. if (!rspamd_dispatcher_write (session->dispatcher, CRLF, sizeof (CRLF) -
  249. 1, FALSE, TRUE)) {
  250. return FALSE;
  251. }
  252. rspamd_session_destroy (session->s);
  253. return FALSE;
  254. }
  255. else if (cd.action <= METRIC_ACTION_ADD_HEADER || cd.action <=
  256. METRIC_ACTION_REWRITE_SUBJECT) {
  257. old_fd = session->temp_fd;
  258. if (!make_smtp_tempfile (session)) {
  259. session->error = SMTP_ERROR_FILE;
  260. session->state = SMTP_STATE_CRITICAL_ERROR;
  261. rspamd_dispatcher_restore (session->dispatcher);
  262. if (!rspamd_dispatcher_write (session->dispatcher, session->error,
  263. 0, FALSE, TRUE)) {
  264. goto err;
  265. }
  266. rspamd_session_destroy (session->s);
  267. return FALSE;
  268. }
  269. if (cd.action <= METRIC_ACTION_REWRITE_SUBJECT) {
  270. /* XXX: add this action */
  271. old_subject = g_mime_message_get_subject (session->task->message);
  272. if (old_subject != NULL) {
  273. sublen = strlen (old_subject) + sizeof (SPAM_SUBJECT);
  274. new_subject = rspamd_mempool_alloc (session->pool, sublen);
  275. rspamd_snprintf (new_subject,
  276. sublen,
  277. "%s%s",
  278. SPAM_SUBJECT,
  279. old_subject);
  280. }
  281. else {
  282. new_subject = SPAM_SUBJECT;
  283. }
  284. g_mime_message_set_subject (session->task->message, new_subject);
  285. }
  286. else if (cd.action <= METRIC_ACTION_ADD_HEADER) {
  287. #ifndef GMIME24
  288. g_mime_message_add_header (session->task->message, "X-Spam",
  289. "true");
  290. #else
  291. g_mime_object_append_header (GMIME_OBJECT (
  292. session->task->message), "X-Spam", "true");
  293. #endif
  294. }
  295. stream = g_mime_stream_fs_new (session->temp_fd);
  296. g_mime_stream_fs_set_owner (GMIME_STREAM_FS (stream), FALSE);
  297. close (old_fd);
  298. if (g_mime_object_write_to_stream (GMIME_OBJECT (session->task->message),
  299. stream) == -1) {
  300. msg_err ("cannot write MIME object to stream: %s",
  301. strerror (errno));
  302. session->error = SMTP_ERROR_FILE;
  303. session->state = SMTP_STATE_CRITICAL_ERROR;
  304. rspamd_dispatcher_restore (session->dispatcher);
  305. if (!rspamd_dispatcher_write (session->dispatcher, session->error,
  306. 0, FALSE, TRUE)) {
  307. goto err;
  308. }
  309. rspamd_session_destroy (session->s);
  310. return FALSE;
  311. }
  312. g_object_unref (stream);
  313. }
  314. /* XXX: Add other actions */
  315. return smtp_send_upstream_message (session);
  316. err:
  317. session->error = SMTP_ERROR_FILE;
  318. session->state = SMTP_STATE_CRITICAL_ERROR;
  319. if (!rspamd_dispatcher_write (session->dispatcher, session->error, 0, FALSE,
  320. TRUE)) {
  321. return FALSE;
  322. }
  323. rspamd_session_destroy (session->s);
  324. return FALSE;
  325. }