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.

forwarding.lua 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. --[[
  2. Copyright (c) 2011-2016, Vsevolod Stakhov <vsevolod@highsecure.ru>
  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. -- Rules to detect forwarding
  14. rspamd_config.FWD_GOOGLE = {
  15. callback = function (task)
  16. if not (task:has_from(1) and task:has_recipients(1)) then
  17. return false
  18. end
  19. local envfrom = task:get_from(1)
  20. local envrcpts = task:get_recipients(1)
  21. -- Forwarding will only be to a single recipient
  22. if #envrcpts > 1 then return false end
  23. -- Get recipient and compute VERP address
  24. local rcpt = envrcpts[1].addr:lower()
  25. local verp = rcpt:gsub('@','=')
  26. -- Get the user portion of the envfrom
  27. local ef_user = envfrom[1].user:lower()
  28. -- Check for a match
  29. if ef_user:find('+caf_=' .. verp, 1, true) then
  30. local _,_,user = ef_user:find('^(.+)+caf_=')
  31. if user then
  32. user = user .. '@' .. envfrom[1].domain
  33. return true, user
  34. end
  35. end
  36. return false
  37. end,
  38. score = 0.0,
  39. description = "Message was forwarded by Google",
  40. group = "forwarding"
  41. }
  42. rspamd_config.FWD_YANDEX = {
  43. callback = function (task)
  44. if not (task:has_from(1) and task:has_recipients(1)) then
  45. return false
  46. end
  47. local hostname = task:get_hostname()
  48. if hostname and hostname:lower():find('%.yandex%.[a-z]+$') then
  49. if task:get_header_raw('X-Yandex-Forward') then
  50. return true
  51. end
  52. end
  53. return false
  54. end,
  55. score = 0.0,
  56. description = "Message was forwarded by Yandex",
  57. group = "forwarding"
  58. }
  59. rspamd_config.FWD_MAILRU = {
  60. callback = function (task)
  61. if not (task:has_from(1) and task:has_recipients(1)) then
  62. return false
  63. end
  64. local hostname = task:get_hostname()
  65. if hostname and hostname:lower():find('%.mail%.ru$') then
  66. if task:get_header_raw('X-MailRu-Forward') then
  67. return true
  68. end
  69. end
  70. return false
  71. end,
  72. score = 0.0,
  73. description = "Message was forwarded by Mail.ru",
  74. group = "forwarding"
  75. }
  76. rspamd_config.FWD_SRS = {
  77. callback = function (task)
  78. if not (task:has_from(1) and task:has_recipients(1)) then
  79. return false
  80. end
  81. local envfrom = task:get_from(1)
  82. local envrcpts = task:get_recipients(1)
  83. -- Forwarding is only to a single recipient
  84. if #envrcpts > 1 then return false end
  85. -- Get recipient and compute rewritten SRS address
  86. local srs = '=' .. envrcpts[1].domain:lower() ..
  87. '=' .. envrcpts[1].user:lower()
  88. if envfrom[1].user:lower():find('^srs[01]=') and
  89. envfrom[1].user:lower():find(srs, 1, false)
  90. then
  91. return true
  92. end
  93. return false
  94. end,
  95. score = 0.0,
  96. description = "Message was forwarded using SRS",
  97. group = "forwarding"
  98. }
  99. rspamd_config.FORWARDED = {
  100. callback = function (task)
  101. if not task:has_recipients(1) then return false end
  102. local envrcpts = task:get_recipients(1)
  103. -- Forwarding will only be for single recipient messages
  104. if #envrcpts > 1 then return false end
  105. -- Get any other headers we might need
  106. local lu = task:get_header('List-Unsubscribe')
  107. local to = task:get_recipients(2)
  108. local matches = 0
  109. -- Retrieve and loop through all Received headers
  110. local rcvds = task:get_header_full('Received')
  111. if rcvds then
  112. for _, rcvd in ipairs(rcvds) do
  113. local _,_,addr = rcvd['decoded']:lower():find("%sfor%s<(.-)>")
  114. if addr then
  115. matches = matches + 1
  116. -- Check that it doesn't match the envrcpt
  117. -- TODO: remove any plus addressing?
  118. if addr ~= envrcpts[1].addr:lower() then
  119. -- Check for mailing-lists as they will have the same signature
  120. if matches < 2 and lu and to and to[1].addr:lower() == addr then
  121. return false
  122. else
  123. return true, addr
  124. end
  125. end
  126. -- Prevent any other iterations as we only want
  127. -- process the first matching Received header
  128. return false
  129. end
  130. end
  131. end
  132. return false
  133. end,
  134. score = 0.0,
  135. description = "Message was forwarded",
  136. group = "forwarding"
  137. }