X-Git-Url: https://vcs.fsf.org/?p=exim.git;a=blobdiff_plain;f=src%2Fsrc%2Fspam.c;h=5eff1ad5cb43b3c47241082786646767f55b4d8d;hp=db6cf8495719a6a054e934bc50184fd56a076c0c;hb=1e1ddfac79fbcd052f199500a6493c7f79cb8462;hpb=590795f4282e84ce9c2730bb17c7e6bb23615966 diff --git a/src/src/spam.c b/src/src/spam.c index db6cf8495..5eff1ad5c 100644 --- a/src/src/spam.c +++ b/src/src/spam.c @@ -4,7 +4,7 @@ /* Copyright (c) Tom Kistner 2003 - 2015 * License: GPL - * Copyright (c) The Exim Maintainers 2016 - 2018 + * Copyright (c) The Exim Maintainers 2016 - 2020 */ /* Code for calling spamassassin's spamd. Called from acl.c. */ @@ -16,7 +16,7 @@ uschar spam_score_buffer[16]; uschar spam_score_int_buffer[16]; uschar spam_bar_buffer[128]; -uschar * spam_action_buffer; +uschar spam_action_buffer[32]; uschar spam_report_buffer[32600]; uschar prev_user_name[128] = ""; int spam_ok = 0; @@ -30,6 +30,7 @@ static int spamd_param_init(spamd_address_container *spamd) { /* default spamd server weight, time and priority value */ +spamd->is_rspamd = FALSE; spamd->is_failed = FALSE; spamd->weight = SPAMD_WEIGHT; spamd->timeout = SPAMD_TIMEOUT; @@ -94,6 +95,12 @@ if (Ustrncmp(param, "time=", 5) == 0) return 0; /* OK */ } +if (Ustrcmp(param, "variant=rspamd") == 0) + { + spamd->is_rspamd = TRUE; + return 0; + } + if (Ustrncmp(param, "tmo=", 4) == 0) { int sec = readconf_readtime((s = param+4), '\0', FALSE); @@ -130,7 +137,7 @@ spamd_get_server(spamd_address_container ** spamds, int num_servers) { unsigned int i; spamd_address_container * sd; -long rnd, weights; +long weights; unsigned pri; static BOOL srandomed = FALSE; @@ -163,7 +170,7 @@ for (weights = 0, i = 0; i < num_servers; i++) if (weights == 0) /* all servers failed */ return -1; -for (rnd = random() % weights, i = 0; i < num_servers; i++) +for (long rnd = random() % weights, i = 0; i < num_servers; i++) { sd = spamds[i]; if (!sd->is_failed && sd->priority == pri) @@ -286,7 +293,7 @@ start = time(NULL); uschar * s; DEBUG(D_acl) debug_printf_indent("spamd: addr entry '%s'\n", address); - sd = (spamd_address_container *)store_get(sizeof(spamd_address_container)); + sd = store_get(sizeof(spamd_address_container), FALSE); for (sublist = address, args = 0, spamd_param_init(sd); (s = string_nextinlist(&sublist, &sublist_sep, NULL, 0)); @@ -337,7 +344,7 @@ start = time(NULL); for (;;) { /*XXX could potentially use TFO early-data here */ - if ( (spamd_cctx.sock = ip_streamsocket(sd->hostspec, &errstr, 5)) >= 0 + if ( (spamd_cctx.sock = ip_streamsocket(sd->hostspec, &errstr, 5, NULL)) >= 0 || sd->retry <= 0 ) break; @@ -361,15 +368,42 @@ start = time(NULL); } (void)fcntl(spamd_cctx.sock, F_SETFL, O_NONBLOCK); -/* now we are connected to spamd on spamd_sock */ - -(void)string_format(spamd_buffer, - sizeof(spamd_buffer), - "REPORT SPAMC/1.2\r\nUser: %s\r\nContent-length: %ld\r\n\r\n", - user_name, - mbox_size); -/* send our request */ -wrote = send(spamd_cctx.sock, spamd_buffer, Ustrlen(spamd_buffer), 0); +/* now we are connected to spamd on spamd_cctx.sock */ +if (sd->is_rspamd) + { + gstring * req_str; + const uschar * s; + + req_str = string_append(NULL, 8, + "CHECK RSPAMC/1.3\r\nContent-length: ", string_sprintf("%lu\r\n", mbox_size), + "Queue-Id: ", message_id, + "\r\nFrom: <", sender_address, + ">\r\nRecipient-Number: ", string_sprintf("%d\r\n", recipients_count)); + + for (int i = 0; i < recipients_count; i++) + req_str = string_append(req_str, 3, + "Rcpt: <", recipients_list[i].address, ">\r\n"); + if ((s = expand_string(US"$sender_helo_name")) && *s) + req_str = string_append(req_str, 3, "Helo: ", s, "\r\n"); + if ((s = expand_string(US"$sender_host_name")) && *s) + req_str = string_append(req_str, 3, "Hostname: ", s, "\r\n"); + if (sender_host_address) + req_str = string_append(req_str, 3, "IP: ", sender_host_address, "\r\n"); + if ((s = expand_string(US"$authenticated_id")) && *s) + req_str = string_append(req_str, 3, "User: ", s, "\r\n"); + req_str = string_catn(req_str, US"\r\n", 2); + wrote = send(spamd_cctx.sock, req_str->s, req_str->ptr, 0); + } +else + { /* spamassassin variant */ + (void)string_format(spamd_buffer, + sizeof(spamd_buffer), + "REPORT SPAMC/1.2\r\nUser: %s\r\nContent-length: %ld\r\n\r\n", + user_name, + mbox_size); + /* send our request */ + wrote = send(spamd_cctx.sock, spamd_buffer, Ustrlen(spamd_buffer), 0); + } if (wrote == -1) { @@ -393,6 +427,7 @@ if (wrote == -1) pollfd.fd = spamd_cctx.sock; pollfd.events = POLLOUT; #endif +(void)fcntl(spamd_cctx.sock, F_SETFL, O_NONBLOCK); do { read = fread(spamd_buffer,1,sizeof(spamd_buffer),mbox_file); @@ -459,7 +494,8 @@ if (ferror(mbox_file)) (void)fclose(mbox_file); /* we're done sending, close socket for writing */ -shutdown(spamd_cctx.sock, SHUT_WR); +if (!sd->is_rspamd) + shutdown(spamd_cctx.sock,SHUT_WR); /* read spamd response using what's left of the timeout. */ memset(spamd_buffer, 0, sizeof(spamd_buffer)); @@ -467,7 +503,7 @@ offset = 0; while ((i = ip_recv(&spamd_cctx, spamd_buffer + offset, sizeof(spamd_buffer) - offset - 1, - sd->timeout - time(NULL) + start)) > 0) + sd->timeout + start)) > 0) offset += i; spamd_buffer[offset] = '\0'; /* guard byte */ @@ -483,6 +519,33 @@ if (i <= 0 && errno != 0) /* reading done */ (void)close(spamd_cctx.sock); +if (sd->is_rspamd) + { /* rspamd variant of reply */ + int r; + if ( (r = sscanf(CS spamd_buffer, + "RSPAMD/%7s 0 EX_OK\r\nMetric: default; %7s %lf / %lf / %lf\r\n%n", + spamd_version, spamd_short_result, &spamd_score, &spamd_threshold, + &spamd_reject_score, &spamd_report_offset)) != 5 + || spamd_report_offset >= offset /* verify within buffer */ + ) + { + log_write(0, LOG_MAIN|LOG_PANIC, + "%s cannot parse spamd %s, output: %d", loglabel, callout_address, r); + return DEFER; + } + /* now parse action */ + p = &spamd_buffer[spamd_report_offset]; + + if (Ustrncmp(p, "Action: ", sizeof("Action: ") - 1) == 0) + { + p += sizeof("Action: ") - 1; + q = &spam_action_buffer[0]; + while (*p && *p != '\r' && (q - spam_action_buffer) < sizeof(spam_action_buffer) - 1) + *q++ = *p++; + *q = '\0'; + } + } +else { /* spamassassin */ /* dig in the spamd output and put the report in a multiline header, if requested */ @@ -501,7 +564,8 @@ if (i <= 0 && errno != 0) } } - spam_action_buffer = spamd_score >= spamd_threshold ? US"reject" : US"no action"; + Ustrcpy(spam_action_buffer, + spamd_score >= spamd_threshold ? US"reject" : US"no action"); } /* Create report. Since this is a multiline string,