aboutsummaryrefslogtreecommitdiffstats
path: root/lualib/lua_scanners
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@highsecure.ru>2021-11-04 20:22:36 +0000
committerGitHub <noreply@github.com>2021-11-04 20:22:36 +0000
commitbcf3daa54b31c23665030e50f6fa73d155895496 (patch)
treedd7407290ecc0d343fc599c006ae19a96bd011fd /lualib/lua_scanners
parentb248bd715a92e93cfbd97bb80c12cf1375f9c014 (diff)
parent78ca6debc2a472f375e1c3e3e9c1266c9d42187f (diff)
downloadrspamd-bcf3daa54b31c23665030e50f6fa73d155895496.tar.gz
rspamd-bcf3daa54b31c23665030e50f6fa73d155895496.zip
Merge pull request #3960 from HeinleinSupport/lua_scanners/icap
lua_scanners/icap
Diffstat (limited to 'lualib/lua_scanners')
-rw-r--r--lualib/lua_scanners/icap.lua440
1 files changed, 345 insertions, 95 deletions
diff --git a/lualib/lua_scanners/icap.lua b/lualib/lua_scanners/icap.lua
index 86def8edd..d2a418fb1 100644
--- a/lualib/lua_scanners/icap.lua
+++ b/lualib/lua_scanners/icap.lua
@@ -15,17 +15,69 @@ See the License for the specific language governing permissions and
limitations under the License.
]]--
---[[[
--- @module icap
--- This module contains icap access functions.
--- Currently tested with
--- - Symantec
--- - Sophos Savdi
--- - ClamAV/c-icap
--- - Kaspersky Web Traffic Security
--- - Trend Micro IWSVA
--- - F-Secure Internet Gatekeeper Strings
---]]
+--[[
+@module icap
+This module contains icap access functions.
+Currently tested with
+ - C-ICAP Squidclamav / echo
+ - F-Secure Internet Gatekeeper
+ - Kaspersky Web Traffic Security
+ - Kaspersky Scan Engine 2.0
+ - McAfee Web Gateway 11
+ - Sophos Savdi
+ - Symantec (Rspamd <3.2, >=3.2 untested)
+ - Trend Micro IWSVA 6.0
+ - Trend Micro Web Gateway
+
+@TODO
+ - Preview / Continue
+ - Reqmod URL's
+ - Content-Type / Filename
+]] --
+
+--[[
+Configuration Notes:
+
+C-ICAP Squidclamav
+ scheme = "squidclamav";
+
+ESET Gateway Security / Antivirus for Linux example:
+ scheme = "scan";
+
+F-Secure Internet Gatekeeper example:
+ scheme = "respmod";
+ x_client_header = true;
+ x_rcpt_header = true;
+ x_from_header = true;
+
+Kaspersky Web Traffic Security example:
+ scheme = "av/respmod";
+ x_client_header = true;
+
+Kaspersky Web Traffic Security (as configured in kavicapd.xml):
+ scheme = "resp";
+ x_client_header = true;
+
+McAfee Web Gateway 11 (Headers must be activated with personal extra Rules)
+ scheme = "respmod";
+ x_client_header = true;
+
+Sophos SAVDI example:
+ # scheme as configured in savdi.conf (name option in service section)
+ scheme = "respmod";
+
+Symantec example:
+ scheme = "avscan";
+
+Trend Micro IWSVA example (X-Virus-ID/X-Infection-Found headers must be activated):
+ scheme = "avscan";
+ x_client_header = true;
+
+Trend Micro Web Gateway example (X-Virus-ID/X-Infection-Found headers must be activated):
+ scheme = "interscan";
+ x_client_header = true;
+]] --
+
local lua_util = require "lua_util"
local tcp = require "rspamd_tcp"
@@ -48,6 +100,8 @@ local function icap_config(opts)
scan_image_mime = false,
scheme = "scan",
default_port = 1344,
+ ssl = false,
+ no_ssl_verify = false,
timeout = 10.0,
log_clean = false,
retransmits = 2,
@@ -58,6 +112,14 @@ local function icap_config(opts)
action = false,
dynamic_scan = false,
user_agent = "Rspamd",
+ x_client_header = false,
+ x_rcpt_header = false,
+ x_from_header = false,
+ req_headers_enabled = true,
+ req_fake_url = "http://127.0.0.1/mail",
+ http_headers_enabled = true,
+ use_http_result_header = true,
+ use_http_3xx_as_threat = false,
}
icap_conf = lua_util.override_defaults(icap_conf, opts)
@@ -103,7 +165,9 @@ local function icap_check(task, content, digest, rule, maybe_part)
local upstream = rule.upstreams:get_upstream_round_robin()
local addr = upstream:get_addr()
local retransmits = rule.retransmits
- local respond_headers = {}
+ local http_headers = {}
+ local req_headers = {}
+ local tcp_options = {}
-- Build extended User Agent
if rule.user_agent == "extended" then
@@ -116,16 +180,24 @@ local function icap_check(task, content, digest, rule, maybe_part)
-- Build the icap queries
local options_request = {
- string.format("OPTIONS icap://%s/%s ICAP/1.0\r\n", addr:to_string(true), rule.scheme),
+ string.format("OPTIONS icap://%s/%s ICAP/1.0\r\n", addr:to_string(), rule.scheme),
string.format('Host: %s\r\n', addr:to_string()),
string.format("User-Agent: %s\r\n", rule.user_agent),
+ "Connection: keep-alive\r\n",
"Encapsulated: null-body=0\r\n\r\n",
}
if rule.user_agent == "none" then
table.remove(options_request, 3)
end
- local size = string.format("%x", tonumber(#content))
+ local respond_headers = {
+ -- Add main RESPMOD header before any other
+ string.format('RESPMOD icap://%s/%s ICAP/1.0\r\n', addr:to_string(), rule.scheme),
+ string.format('Host: %s\r\n', addr:to_string()),
+ }
+
+ local size = tonumber(#content)
+ local chunked_size = string.format("%x", size)
local function icap_callback(err, conn)
@@ -149,16 +221,13 @@ local function icap_check(task, content, digest, rule, maybe_part)
lua_util.debugm(rule.name, task, '%s: retry IP: %s:%s',
rule.log_prefix, addr, addr:get_port())
- tcp.request({
- task = task,
- host = addr:to_string(),
- port = addr:get_port(),
- timeout = rule.timeout,
- stop_pattern = '\r\n',
- data = options_request,
- read = false,
- callback = icap_callback,
- })
+ tcp_options.host = addr:to_string()
+ tcp_options.port = addr:get_port()
+ tcp_options.callback = icap_callback
+ tcp_options.data = options_request
+
+ tcp.request(tcp_options)
+
else
rspamd_logger.errx(task, '%s: failed to scan, maximum retransmits '..
'exceed - error: %s', rule.log_prefix, err_m or '')
@@ -167,11 +236,76 @@ local function icap_check(task, content, digest, rule, maybe_part)
end
end
+ local function get_req_headers()
+
+ local req_hlen = 2
+ table.insert(req_headers, string.format('GET %s HTTP/1.0\r\n', rule.req_fake_url))
+ table.insert(req_headers, string.format('Date: %s\r\n', rspamd_util.time_to_string(rspamd_util.get_time())))
+ --table.insert(http_headers, string.format('Content-Type: %s\r\n', 'text/html'))
+ if rule.user_agent ~= "none" then
+ table.insert(req_headers, string.format("User-Agent: %s\r\n", rule.user_agent))
+ end
+
+ for _, h in ipairs(req_headers) do
+ req_hlen = req_hlen + tonumber(#h)
+ end
+
+ return req_hlen, req_headers
+
+ end
+
+ local function get_http_headers()
+ local http_hlen = 2
+ table.insert(http_headers, 'HTTP/1.0 200 OK\r\n')
+ table.insert(http_headers, string.format('Date: %s\r\n', rspamd_util.time_to_string(rspamd_util.get_time())))
+ table.insert(http_headers, string.format('Server: %s\r\n', 'Apache/2.4'))
+ if rule.user_agent ~= "none" then
+ table.insert(http_headers, string.format("User-Agent: %s\r\n", rule.user_agent))
+ end
+ --table.insert(http_headers, string.format('Content-Type: %s\r\n', 'text/html'))
+ table.insert(http_headers, string.format('Content-Length: %s\r\n', size))
+
+ for _, h in ipairs(http_headers) do
+ http_hlen = http_hlen + tonumber(#h)
+ end
+
+ return http_hlen, http_headers
+
+ end
+
local function get_respond_query()
- table.insert(respond_headers, 1, string.format(
- 'RESPMOD icap://%s:%s/%s ICAP/1.0\r\n', addr:to_string(), addr:get_port(), rule.scheme))
+ local req_hlen = 0
+ local resp_req_headers
+ local http_hlen = 0
+ local resp_http_headers
+
+ -- Append all extra headers
+ if rule.user_agent ~= "none" then
+ table.insert(respond_headers, string.format("User-Agent: %s\r\n", rule.user_agent))
+ end
+
+ if rule.req_headers_enabled then
+ req_hlen, resp_req_headers = get_req_headers()
+ end
+ if rule.http_headers_enabled then
+ http_hlen, resp_http_headers = get_http_headers()
+ end
+
+ if rule.req_headers_enabled and rule.http_headers_enabled then
+ local res_body_hlen = req_hlen + http_hlen
+ table.insert(respond_headers, string.format('Encapsulated: req-hdr=0, res-hdr=%s, res-body=%s\r\n', req_hlen, res_body_hlen))
+ elseif rule.http_headers_enabled then
+ table.insert(respond_headers, string.format('Encapsulated: res-hdr=0, res-body=%s\r\n', http_hlen))
+ else
+ table.insert(respond_headers, 'Encapsulated: res-body=0\r\n')
+ end
+
+ table.insert(respond_headers, '\r\n')
+ for _,h in ipairs(resp_req_headers) do table.insert(respond_headers, h) end
table.insert(respond_headers, '\r\n')
- table.insert(respond_headers, size .. '\r\n')
+ for _,h in ipairs(resp_http_headers) do table.insert(respond_headers, h) end
+ table.insert(respond_headers, '\r\n')
+ table.insert(respond_headers, chunked_size .. '\r\n')
table.insert(respond_headers, content)
table.insert(respond_headers, '\r\n0\r\n\r\n')
return respond_headers
@@ -183,16 +317,17 @@ local function icap_check(task, content, digest, rule, maybe_part)
end
end
- local function icap_result_header_table(result)
+ local function result_header_table(result)
local icap_headers = {}
for s in result:gmatch("[^\r\n]+") do
if string.find(s, '^ICAP') then
- icap_headers['icap'] = s
- end
- if string.find(s, '[%a%d-+]-:') then
+ icap_headers['icap'] = tostring(s)
+ elseif string.find(s, '^HTTP') then
+ icap_headers['http'] = tostring(s)
+ elseif string.find(s, '[%a%d-+]-:') then
local _,_,key,value = tostring(s):find("([%a%d-+]-):%s?(.+)")
if key ~= nil then
- icap_headers[key] = value
+ icap_headers[key:lower()] = tostring(value)
end
end
end
@@ -201,7 +336,7 @@ local function icap_check(task, content, digest, rule, maybe_part)
return icap_headers
end
- local function icap_parse_result(icap_headers)
+ local function icap_parse_result(headers)
local threat_string = {}
@@ -209,41 +344,65 @@ local function icap_check(task, content, digest, rule, maybe_part)
@ToDo: handle type in response
Generic Strings:
- X-Infection-Found: Type=0; Resolution=2; Threat=Troj/DocDl-OYC;
- X-Infection-Found: Type=0; Resolution=2; Threat=W97M.Downloader;
+ icap: X-Infection-Found: Type=0; Resolution=2; Threat=Troj/DocDl-OYC;
+ icap: X-Infection-Found: Type=0; Resolution=2; Threat=W97M.Downloader;
Symantec String:
- X-Infection-Found: Type=2; Resolution=2; Threat=Container size violation
- X-Infection-Found: Type=2; Resolution=2; Threat=Encrypted container violation;
+ icap: X-Infection-Found: Type=2; Resolution=2; Threat=Container size violation
+ icap: X-Infection-Found: Type=2; Resolution=2; Threat=Encrypted container violation;
Sophos Strings:
- X-Virus-ID: Troj/DocDl-OYC
+ icap: X-Virus-ID: Troj/DocDl-OYC
+ http: X-Blocked: Virus found during virus scan
+ http: X-Blocked-By: Sophos Anti-Virus
Kaspersky Web Traffic Security Strings:
- X-Virus-ID: HEUR:Backdoor.Java.QRat.gen
- X-Response-Info: blocked
- X-Virus-ID: no threats
- X-Response-Info: blocked
- X-Response-Info: passed
-
- Trend Micro IWSVA Strings:
- X-Virus-ID: Trojan.W97M.POWLOAD.SMTHF1
- X-Infection-Found: Type=0; Resolution=2; Threat=Trojan.W97M.POWLOAD.SMTHF1;
+ icap: X-Virus-ID: HEUR:Backdoor.Java.QRat.gen
+ icap: X-Response-Info: blocked
+ icap: X-Virus-ID: no threats
+ icap: X-Response-Info: blocked
+ icap: X-Response-Info: passed
+ http: HTTP/1.1 403 Forbidden
+
+ Kaspersky Scan Engine 2.0 (ICAP mode)
+ icap: X-Virus-ID: EICAR-Test-File
+ http: HTTP/1.0 403 Forbidden
+
+ Trend Micro Strings:
+ icap: X-Virus-ID: Trojan.W97M.POWLOAD.SMTHF1
+ icap: X-Infection-Found: Type=0; Resolution=2; Threat=Trojan.W97M.POWLOAD.SMTHF1;
+ http: HTTP/1.1 403 Forbidden (TMWS Blocked)
+ http: HTTP/1.1 403 Forbidden
F-Secure Internet Gatekeeper Strings:
- X-FSecure-Scan-Result: infected
- X-FSecure-Infection-Name: "Malware.W97M/Agent.32584203"
- X-FSecure-Infected-Filename: "virus.doc"
+ icap: X-FSecure-Scan-Result: infected
+ icap: X-FSecure-Infection-Name: "Malware.W97M/Agent.32584203"
+ icap: X-FSecure-Infected-Filename: "virus.doc"
ESET File Security for Linux 7.0
- X-Infection-Found: Type=0; Resolution=0; Threat=VBA/TrojanDownloader.Agent.JOA;
- X-Virus-ID: Trojaner
- X-Response-Info: Blocked
+ icap: X-Infection-Found: Type=0; Resolution=0; Threat=VBA/TrojanDownloader.Agent.JOA;
+ icap: X-Virus-ID: Trojaner
+ icap: X-Response-Info: Blocked
+
+ McAfee Web Gateway 11 (Headers must be activated with personal extra Rules)
+ icap: X-Virus-ID: EICAR test file
+ icap: X-Media-Type: text/plain
+ icap: X-Block-Result: 80
+ icap: X-Block-Reason: Malware found
+ icap: X-Block-Reason: Archive not supported
+ icap: X-Block-Reason: Media Type (Block List)
+ http: HTTP/1.0 403 VirusFound
+
+ C-ICAP Squidclamav
+ icap/http: X-Infection-Found: Type=0; Resolution=2; Threat={HEX}EICAR.TEST.3.UNOFFICIAL;
+ icap/http: X-Virus-ID: {HEX}EICAR.TEST.3.UNOFFICIAL
+ http: HTTP/1.0 307 Temporary Redirect
]] --
- if icap_headers['X-Infection-Found'] then
+ -- Generic ICAP Headers
+ if headers['x-infection-found'] then
local _,_,icap_type,_,icap_threat =
- icap_headers['X-Infection-Found']:find("Type=(.-); Resolution=(.-); Threat=(.-);$")
+ headers['x-infection-found']:find("Type=(.-); Resolution=(.-); Threat=(.-);$")
if not icap_type or icap_type == 2 then
-- error returned
@@ -251,35 +410,37 @@ local function icap_check(task, content, digest, rule, maybe_part)
'%s: icap error X-Infection-Found: %s', rule.log_prefix, icap_threat)
common.yield_result(task, rule, icap_threat, 0,
'fail', maybe_part)
+ return true
else
lua_util.debugm(rule.name, task,
'%s: icap X-Infection-Found: %s', rule.log_prefix, icap_threat)
table.insert(threat_string, icap_threat)
end
- elseif icap_headers['X-Virus-ID'] and icap_headers['X-Virus-ID'] ~= "no threats" then
+ elseif headers['x-virus-id'] and headers['x-virus-id'] ~= "no threats" then
lua_util.debugm(rule.name, task,
- '%s: icap X-Virus-ID: %s', rule.log_prefix, icap_headers['X-Virus-ID'])
+ '%s: icap X-Virus-ID: %s', rule.log_prefix, headers['x-virus-id'])
- if string.find(icap_headers['X-Virus-ID'], ', ') then
- local vnames = lua_util.str_split(string.gsub(icap_headers['X-Virus-ID'], "%s", ""), ',') or {}
+ if string.find(headers['x-virus-id'], ', ') then
+ local vnames = lua_util.str_split(string.gsub(headers['x-virus-id'], "%s", ""), ',') or {}
for _,v in ipairs(vnames) do
table.insert(threat_string, v)
end
else
- table.insert(threat_string, icap_headers['X-Virus-ID'])
+ table.insert(threat_string, headers['x-virus-id'])
end
- elseif icap_headers['X-FSecure-Scan-Result'] and icap_headers['X-FSecure-Scan-Result'] ~= "clean" then
+ -- FSecure X-Headers
+ elseif headers['x-fsecure-scan-result'] and headers['x-fsecure-scan-result'] ~= "clean" then
local infected_filename = ""
local infection_name = "-unknown-"
- if icap_headers['X-FSecure-Infected-Filename'] then
- infected_filename = string.gsub(icap_headers['X-FSecure-Infected-Filename'], '[%s"]', '')
+ if headers['x-fsecure-infected-filename'] then
+ infected_filename = string.gsub(headers['x-fsecure-infected-filename'], '[%s"]', '')
end
- if icap_headers['X-FSecure-Infection-Name'] then
- infection_name = string.gsub(icap_headers['X-FSecure-Infection-Name'], '[%s"]', '')
+ if headers['x-fsecure-infection-name'] then
+ infection_name = string.gsub(headers['x-fsecure-infection-name'], '[%s"]', '')
end
lua_util.debugm(rule.name, task,
@@ -295,13 +456,54 @@ local function icap_check(task, content, digest, rule, maybe_part)
else
table.insert(threat_string, infection_name)
end
+ -- McAfee Web Gateway manual extra headers
+ elseif headers['x-mwg-block-reason'] and headers['x-mwg-block-reason'] ~= "" then
+ table.insert(threat_string, headers['x-mwg-block-reason'])
+ -- Sophos SAVDI special http headers
+ elseif headers['x-blocked'] and headers['x-blocked'] ~= "" then
+ table.insert(threat_string, headers['x-blocked'])
+ -- last try HTTP [4]xx return
+ elseif headers.http and string.find(headers.http, '^HTTP%/[12]%.. [4]%d%d') then
+ local message = string.format("pseudo-virus (blocked): %s", string.gsub(headers.http, 'HTTP%/[12]%.. ', ''))
+ table.insert(threat_string, message)
+ elseif rule.use_http_3xx_as_threat and headers.http and string.find(headers.http, '^HTTP%/[12]%.. [3]%d%d') then
+ local message = string.format("pseudo-virus (redirect): %s", string.gsub(headers.http, 'HTTP%/[12]%.. ', ''))
+ table.insert(threat_string, message)
end
+
if #threat_string > 0 then
common.yield_result(task, rule, threat_string, rule.default_score, nil, maybe_part)
common.save_cache(task, digest, rule, threat_string, rule.default_score, maybe_part)
+ return true
else
- common.save_cache(task, digest, rule, 'OK', 0, maybe_part)
- common.log_clean(task, rule)
+ return false
+ end
+ end
+
+ local function icap_r_respond_http_cb(err_m, data, connection)
+ if err_m or connection == nil then
+ icap_requery(err_m, "icap_r_respond_http_cb")
+ else
+ local result = tostring(data)
+
+ local icap_http_headers = result_header_table(result) or {}
+ -- Find HTTP/[12].x [234]xx response
+ if icap_http_headers.http and string.find(icap_http_headers.http, 'HTTP%/[12]%.. [234]%d%d') then
+ local icap_http_header_result = icap_parse_result(icap_http_headers)
+ if icap_http_header_result then
+ -- Threat found - close connection
+ connection:close()
+ else
+ common.save_cache(task, digest, rule, 'OK', 0, maybe_part)
+ common.log_clean(task, rule)
+ end
+ else
+ rspamd_logger.errx(task, '%s: unhandled response |%s|',
+ rule.log_prefix, string.gsub(result, "\r\n", ", "))
+ common.yield_result(task, rule,
+ 'unhandled icap response: ' .. icap_http_headers.icap or "-",
+ 0.0, 'fail', maybe_part)
+ end
end
end
@@ -310,12 +512,28 @@ local function icap_check(task, content, digest, rule, maybe_part)
icap_requery(err_m, "icap_r_respond_cb")
else
local result = tostring(data)
- conn:close()
- local icap_headers = icap_result_header_table(result) or {}
+ local icap_headers = result_header_table(result) or {}
-- Find ICAP/1.x 2xx response
if icap_headers.icap and string.find(icap_headers.icap, 'ICAP%/1%.. 2%d%d') then
- icap_parse_result(icap_headers)
+ local icap_header_result = icap_parse_result(icap_headers)
+ if icap_header_result then
+ -- Threat found - close connection
+ connection:close()
+ elseif not icap_header_result
+ and rule.use_http_result_header
+ and icap_headers.encapsulated
+ and not string.find(icap_headers.encapsulated, 'null%-body=0')
+ then
+ -- Try to read encapsulated HTTP Headers
+ lua_util.debugm(rule.name, task, '%s: no ICAP virus header found - try HTTP headers',
+ rule.log_prefix)
+ connection:add_read(icap_r_respond_http_cb, '\r\n\r\n')
+ else
+ connection:close()
+ common.save_cache(task, digest, rule, 'OK', 0, maybe_part)
+ common.log_clean(task, rule)
+ end
elseif icap_headers.icap and string.find(icap_headers.icap, 'ICAP%/1%.. [45]%d%d') then
-- Find ICAP/1.x 5/4xx response
--[[
@@ -323,6 +541,12 @@ local function icap_check(task, content, digest, rule, maybe_part)
ICAP/1.0 539 Aborted - No AV scanning license
SquidClamAV/C-ICAP:
ICAP/1.0 500 Server error
+ Eset:
+ ICAP/1.0 405 Forbidden
+ TrendMicro:
+ ICAP/1.0 400 Bad request
+ McAfee:
+ ICAP/1.0 418 Bad composition
]]--
rspamd_logger.errx(task, '%s: ICAP ERROR: %s', rule.log_prefix, icap_headers.icap)
common.yield_result(task, rule, icap_headers.icap, 0.0,
@@ -350,32 +574,50 @@ local function icap_check(task, content, digest, rule, maybe_part)
if err_m or connection == nil then
icap_requery(err_m, "icap_r_options_cb")
else
- local icap_headers = icap_result_header_table(tostring(data))
+ local icap_headers = result_header_table(tostring(data))
if icap_headers.icap and string.find(icap_headers.icap, 'ICAP%/1%.. 2%d%d') then
- if icap_headers['Methods'] and string.find(icap_headers['Methods'], 'RESPMOD') then
- if icap_headers['Allow'] and string.find(icap_headers['Allow'], '204') then
+ if icap_headers['methods'] and string.find(icap_headers['methods'], 'RESPMOD') then
+ -- Allow "204 No Content" responses
+ -- https://datatracker.ietf.org/doc/html/rfc3507#section-4.6
+ if icap_headers['allow'] and string.find(icap_headers['allow'], '204') then
add_respond_header('Allow', '204')
end
- if icap_headers['Service'] and string.find(icap_headers['Service'], 'IWSVA 6.5') then
- add_respond_header('Encapsulated', 'res-hdr=0 res-body=0')
- else
- add_respond_header('Encapsulated', 'res-body=0')
- end
- if icap_headers['Server'] and string.find(icap_headers['Server'], 'F-Secure ICAP Server') then
- local from = task:get_from('mime')
- local rcpt_to = task:get_principal_recipient()
+
+ if rule.x_client_header then
local client = task:get_from_ip()
if client then add_respond_header('X-Client-IP', client:to_string()) end
- add_respond_header('X-Mail-From', from[1].addr)
- add_respond_header('X-Rcpt-To', rcpt_to)
end
- conn:add_write(icap_w_respond_cb, get_respond_query())
+ -- F-Secure extra headers
+ if icap_headers['server'] and string.find(icap_headers['server'], 'f-secure icap server') then
+
+ if rule.x_rcpt_header then
+ local rcpt_to = task:get_principal_recipient()
+ if rcpt_to then add_respond_header('X-Rcpt-To', rcpt_to) end
+ end
+
+ if rule.x_from_header then
+ local mail_from = task:get_principal_recipient()
+ if mail_from and mail_from[1] then add_respond_header('X-Rcpt-To', mail_from[1].addr) end
+ end
+
+ end
+
+ if icap_headers.connection and icap_headers.connection:lower() == 'close' then
+ lua_util.debugm(rule.name, task, '%s: OPTIONS request Connection: %s - using new connection',
+ rule.log_prefix, icap_headers.connection)
+ connection:close()
+ tcp_options.callback = icap_w_respond_cb
+ tcp_options.data = get_respond_query()
+ tcp.request(tcp_options)
+ else
+ connection:add_write(icap_w_respond_cb, get_respond_query())
+ end
else
rspamd_logger.errx(task, '%s: RESPMOD method not advertised: Methods: %s',
- rule.log_prefix, icap_headers['Methods'])
+ rule.log_prefix, icap_headers['methods'])
common.yield_result(task, rule, 'NO RESPMOD', 0.0,
'fail', maybe_part)
end
@@ -397,16 +639,24 @@ local function icap_check(task, content, digest, rule, maybe_part)
end
end
- tcp.request({
- task = task,
- host = addr:to_string(),
- port = addr:get_port(),
- timeout = rule.timeout,
- stop_pattern = '\r\n',
- data = options_request,
- read = false,
- callback = icap_callback,
- })
+ tcp_options.task = task
+ tcp_options.stop_pattern = '\r\n'
+ tcp_options.read = false
+ tcp_options.timeout = rule.timeout
+ tcp_options.callback = icap_callback
+ tcp_options.data = options_request
+
+ if rule.ssl then
+ tcp_options.ssl = true
+ if rule.no_ssl_verify then
+ tcp_options.no_ssl_verify = true
+ end
+ end
+
+ tcp_options.host = addr:to_string()
+ tcp_options.port = addr:get_port()
+
+ tcp.request(tcp_options)
end
if common.condition_check_and_continue(task, content, rule, digest,