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 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  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, rcvd)
  35. return acc + 1
  36. end, 0, fun.filter(function(h)
  37. return not h['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. task:insert_result(symbol_strict, 1)
  48. -- Check for MUAs
  49. local ua = task:get_header('User-Agent')
  50. local xm = task:get_header('X-Mailer')
  51. if (ua or xm) then
  52. task:insert_result(symbol_mx, 1, (ua or xm))
  53. end
  54. end
  55. task:insert_result(symbol_rdns, 1)
  56. else
  57. rspamd_logger.infox(task, 'source hostname has not been passed to Rspamd from MTA, ' ..
  58. 'but we could resolve source IP address PTR %s as "%s"',
  59. to_resolve, results[1])
  60. task:set_hostname(results[1])
  61. if good_hosts then
  62. for _,gh in ipairs(good_hosts) do
  63. if string.find(results[1], gh) then
  64. return
  65. end
  66. end
  67. end
  68. if nreceived <= 1 then
  69. task:insert_result(symbol, 1)
  70. for _,h in ipairs(bad_hosts) do
  71. if string.find(results[1], h) then
  72. task:insert_result(symbol_strict, 1, h)
  73. return
  74. end
  75. end
  76. end
  77. end
  78. end
  79. local task_ip = task:get_ip()
  80. if ((not check_authed and task:get_user()) or
  81. (not check_local and task_ip and task_ip:is_local())) then
  82. rspamd_logger.infox(task, 'Skipping once_received for authenticated user or local network')
  83. return
  84. end
  85. if whitelist and task_ip and whitelist:get_key(task_ip) then
  86. rspamd_logger.infox(task, 'whitelisted mail from %s',
  87. task_ip:to_string())
  88. return
  89. end
  90. local hn = task:get_hostname()
  91. -- Here we don't care about received
  92. if (not hn) and task_ip and task_ip:is_valid() then
  93. task:get_resolver():resolve_ptr({task = task,
  94. name = task_ip:to_string(),
  95. callback = recv_dns_cb,
  96. forced = true
  97. })
  98. return
  99. end
  100. if nreceived <= 1 then
  101. local ret = true
  102. local r = recvh[1]
  103. if not r then
  104. return
  105. end
  106. if r['real_hostname'] then
  107. local rhn = string.lower(r['real_hostname'])
  108. -- Check for good hostname
  109. if rhn and good_hosts then
  110. for _,gh in ipairs(good_hosts) do
  111. if string.find(rhn, gh) then
  112. ret = false
  113. break
  114. end
  115. end
  116. end
  117. end
  118. if ret then
  119. -- Strict checks
  120. if symbol_strict then
  121. -- Unresolved host
  122. task:insert_result(symbol, 1)
  123. if not hn then return end
  124. for _,h in ipairs(bad_hosts) do
  125. if string.find(hn, h) then
  126. task:insert_result(symbol_strict, 1, h)
  127. return
  128. end
  129. end
  130. else
  131. task:insert_result(symbol, 1)
  132. end
  133. end
  134. end
  135. end
  136. local auth_and_local_conf = lua_util.config_check_local_or_authed(rspamd_config, N,
  137. false, false)
  138. check_local = auth_and_local_conf[1]
  139. check_authed = auth_and_local_conf[2]
  140. -- Configuration
  141. local opts = rspamd_config:get_all_opt(N)
  142. if opts then
  143. if opts['symbol'] then
  144. symbol = opts['symbol']
  145. local id = rspamd_config:register_symbol({
  146. name = symbol,
  147. callback = check_quantity_received,
  148. })
  149. for n,v in pairs(opts) do
  150. if n == 'symbol_strict' then
  151. symbol_strict = v
  152. elseif n == 'symbol_rdns' then
  153. symbol_rdns = v
  154. elseif n == 'symbol_rdns_dnsfail' then
  155. symbol_rdns_dnsfail = v
  156. elseif n == 'bad_host' then
  157. if type(v) == 'string' then
  158. bad_hosts[1] = v
  159. else
  160. bad_hosts = v
  161. end
  162. elseif n == 'good_host' then
  163. if type(v) == 'string' then
  164. good_hosts[1] = v
  165. else
  166. good_hosts = v
  167. end
  168. elseif n == 'whitelist' then
  169. whitelist = rspamd_map_add('once_received', 'whitelist', 'radix',
  170. 'once received whitelist')
  171. elseif n == 'symbol_mx' then
  172. symbol_mx = v
  173. end
  174. end
  175. rspamd_config:register_symbol({
  176. name = symbol_rdns,
  177. type = 'virtual',
  178. parent = id
  179. })
  180. rspamd_config:register_symbol({
  181. name = symbol_rdns_dnsfail,
  182. type = 'virtual',
  183. parent = id
  184. })
  185. rspamd_config:register_symbol({
  186. name = symbol_strict,
  187. type = 'virtual',
  188. parent = id
  189. })
  190. rspamd_config:register_symbol({
  191. name = symbol_mx,
  192. type = 'virtual',
  193. parent = id
  194. })
  195. end
  196. end