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.

once_received.lua 6.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. --[[
  2. Copyright (c) 2022, Vsevolod Stakhov <vsevolod@rspamd.com>
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ]]--
  13. if confighelp then
  14. return
  15. end
  16. -- 0 or 1 received: = spam
  17. local symbol = 'ONCE_RECEIVED'
  18. local symbol_rdns = 'RDNS_NONE'
  19. local symbol_rdns_dnsfail = 'RDNS_DNSFAIL'
  20. local symbol_mx = 'DIRECT_TO_MX'
  21. -- Symbol for strict checks
  22. local symbol_strict = nil
  23. local bad_hosts = {}
  24. local good_hosts = {}
  25. local whitelist = nil
  26. local rspamd_logger = require "rspamd_logger"
  27. local lua_util = require "lua_util"
  28. local fun = require "fun"
  29. local N = 'once_received'
  30. local check_local = false
  31. local check_authed = false
  32. local function check_quantity_received (task)
  33. local recvh = task:get_received_headers()
  34. local nreceived = fun.reduce(function(acc, _)
  35. return acc + 1
  36. end, 0, fun.filter(function(h)
  37. return not h['flags']['artificial']
  38. end, recvh))
  39. local function recv_dns_cb(_, to_resolve, results, err)
  40. if err and (err ~= 'requested record is not found' and err ~= 'no records with this name') then
  41. rspamd_logger.errx(task, 'error looking up %s: %s', to_resolve, err)
  42. task:insert_result(symbol_rdns_dnsfail, 1.0)
  43. end
  44. if not results then
  45. if nreceived <= 1 then
  46. task:insert_result(symbol, 1)
  47. -- Avoid strict symbol inserting as the remaining symbols have already
  48. -- quote a significant weight, so a message could be rejected by just
  49. -- this property.
  50. --task:insert_result(symbol_strict, 1)
  51. -- Check for MUAs
  52. local ua = task:get_header('User-Agent')
  53. local xm = task:get_header('X-Mailer')
  54. if (ua or xm) then
  55. task:insert_result(symbol_mx, 1, (ua or xm))
  56. end
  57. end
  58. task:insert_result(symbol_rdns, 1)
  59. else
  60. rspamd_logger.infox(task, 'source hostname has not been passed to Rspamd from MTA, ' ..
  61. 'but we could resolve source IP address PTR %s as "%s"',
  62. to_resolve, results[1])
  63. task:set_hostname(results[1])
  64. if good_hosts then
  65. for _,gh in ipairs(good_hosts) do
  66. if string.find(results[1], gh) then
  67. return
  68. end
  69. end
  70. end
  71. if nreceived <= 1 then
  72. task:insert_result(symbol, 1)
  73. for _,h in ipairs(bad_hosts) do
  74. if string.find(results[1], h) then
  75. task:insert_result(symbol_strict, 1, h)
  76. return
  77. end
  78. end
  79. end
  80. end
  81. end
  82. local task_ip = task:get_ip()
  83. if ((not check_authed and task:get_user()) or
  84. (not check_local and task_ip and task_ip:is_local())) then
  85. rspamd_logger.infox(task, 'Skipping once_received for authenticated user or local network')
  86. return
  87. end
  88. if whitelist and task_ip and whitelist:get_key(task_ip) then
  89. rspamd_logger.infox(task, 'whitelisted mail from %s',
  90. task_ip:to_string())
  91. return
  92. end
  93. local hn = task:get_hostname()
  94. -- Here we don't care about received
  95. if (not hn) and task_ip and task_ip:is_valid() then
  96. task:get_resolver():resolve_ptr({task = task,
  97. name = task_ip:to_string(),
  98. callback = recv_dns_cb,
  99. forced = true
  100. })
  101. return
  102. end
  103. if nreceived <= 1 then
  104. local ret = true
  105. local r = recvh[1]
  106. if not r then
  107. return
  108. end
  109. if r['real_hostname'] then
  110. local rhn = string.lower(r['real_hostname'])
  111. -- Check for good hostname
  112. if rhn and good_hosts then
  113. for _,gh in ipairs(good_hosts) do
  114. if string.find(rhn, gh) then
  115. ret = false
  116. break
  117. end
  118. end
  119. end
  120. end
  121. if ret then
  122. -- Strict checks
  123. if symbol_strict then
  124. -- Unresolved host
  125. task:insert_result(symbol, 1)
  126. if not hn then return end
  127. for _,h in ipairs(bad_hosts) do
  128. if string.find(hn, h) then
  129. task:insert_result(symbol_strict, 1, h)
  130. return
  131. end
  132. end
  133. else
  134. task:insert_result(symbol, 1)
  135. end
  136. end
  137. end
  138. end
  139. local auth_and_local_conf = lua_util.config_check_local_or_authed(rspamd_config, N,
  140. false, false)
  141. check_local = auth_and_local_conf[1]
  142. check_authed = auth_and_local_conf[2]
  143. -- Configuration
  144. local opts = rspamd_config:get_all_opt(N)
  145. if opts then
  146. if opts['symbol'] then
  147. symbol = opts['symbol']
  148. local id = rspamd_config:register_symbol({
  149. name = symbol,
  150. callback = check_quantity_received,
  151. })
  152. for n,v in pairs(opts) do
  153. if n == 'symbol_strict' then
  154. symbol_strict = v
  155. elseif n == 'symbol_rdns' then
  156. symbol_rdns = v
  157. elseif n == 'symbol_rdns_dnsfail' then
  158. symbol_rdns_dnsfail = v
  159. elseif n == 'bad_host' then
  160. if type(v) == 'string' then
  161. bad_hosts[1] = v
  162. else
  163. bad_hosts = v
  164. end
  165. elseif n == 'good_host' then
  166. if type(v) == 'string' then
  167. good_hosts[1] = v
  168. else
  169. good_hosts = v
  170. end
  171. elseif n == 'whitelist' then
  172. local lua_maps = require "lua_maps"
  173. whitelist = lua_maps.map_add('once_received', 'whitelist', 'radix',
  174. 'once received whitelist')
  175. elseif n == 'symbol_mx' then
  176. symbol_mx = v
  177. end
  178. end
  179. rspamd_config:register_symbol({
  180. name = symbol_rdns,
  181. type = 'virtual',
  182. parent = id
  183. })
  184. rspamd_config:register_symbol({
  185. name = symbol_rdns_dnsfail,
  186. type = 'virtual',
  187. parent = id
  188. })
  189. rspamd_config:register_symbol({
  190. name = symbol_strict,
  191. type = 'virtual',
  192. parent = id
  193. })
  194. rspamd_config:register_symbol({
  195. name = symbol_mx,
  196. type = 'virtual',
  197. parent = id
  198. })
  199. end
  200. end