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.

received.hxx 7.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. /*
  2. * Copyright 2024 Vsevolod Stakhov
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #ifndef RSPAMD_RECEIVED_HXX
  17. #define RSPAMD_RECEIVED_HXX
  18. #pragma once
  19. #include "config.h"
  20. #include "received.h"
  21. #include "mime_string.hxx"
  22. #include "libmime/email_addr.h"
  23. #include "libserver/task.h"
  24. #include "contrib/ankerl/unordered_dense.h"
  25. #include <vector>
  26. #include <string_view>
  27. #include <utility>
  28. #include <optional>
  29. namespace rspamd::mime {
  30. static inline auto
  31. received_char_filter(UChar32 uc) -> UChar32
  32. {
  33. if (u_isprint(uc)) {
  34. return u_tolower(uc);
  35. }
  36. return 0;
  37. }
  38. enum class received_flags {
  39. DEFAULT = 0,
  40. SMTP = 1u << 0u,
  41. ESMTP = 1u << 1u,
  42. ESMTPA = 1u << 2u,
  43. ESMTPS = 1u << 3u,
  44. ESMTPSA = 1u << 4u,
  45. LMTP = 1u << 5u,
  46. IMAP = 1u << 6u,
  47. LOCAL = 1u << 7u,
  48. HTTP = 1u << 8u,
  49. MAPI = 1u << 9u,
  50. UNKNOWN = 1u << 10u,
  51. ARTIFICIAL = (1u << 11u),
  52. SSL = (1u << 12u),
  53. AUTHENTICATED = (1u << 13u),
  54. UTF8 = 1u << 14u,
  55. };
  56. constexpr received_flags operator|(received_flags lhs, received_flags rhs)
  57. {
  58. using ut = std::underlying_type<received_flags>::type;
  59. return static_cast<received_flags>(static_cast<ut>(lhs) | static_cast<ut>(rhs));
  60. }
  61. constexpr received_flags operator|=(received_flags &lhs, const received_flags rhs)
  62. {
  63. using ut = std::underlying_type<received_flags>::type;
  64. lhs = static_cast<received_flags>(static_cast<ut>(lhs) | static_cast<ut>(rhs));
  65. return lhs;
  66. }
  67. constexpr received_flags operator&(received_flags lhs, received_flags rhs)
  68. {
  69. using ut = std::underlying_type<received_flags>::type;
  70. return static_cast<received_flags>(static_cast<ut>(lhs) & static_cast<ut>(rhs));
  71. }
  72. constexpr bool operator!(received_flags fl)
  73. {
  74. return fl == received_flags::DEFAULT;
  75. }
  76. constexpr received_flags received_type_apply_protocols_mask(received_flags fl)
  77. {
  78. return fl & (received_flags::SMTP |
  79. received_flags::ESMTP |
  80. received_flags::ESMTPA |
  81. received_flags::ESMTPS |
  82. received_flags::ESMTPSA |
  83. received_flags::IMAP |
  84. received_flags::HTTP |
  85. received_flags::LOCAL |
  86. received_flags::MAPI |
  87. received_flags::LMTP);
  88. }
  89. constexpr const char *received_protocol_to_string(received_flags fl)
  90. {
  91. const auto *proto = "unknown";
  92. switch (received_type_apply_protocols_mask(fl)) {
  93. case received_flags::SMTP:
  94. proto = "smtp";
  95. break;
  96. case received_flags::ESMTP:
  97. proto = "esmtp";
  98. break;
  99. case received_flags::ESMTPS:
  100. proto = "esmtps";
  101. break;
  102. case received_flags::ESMTPA:
  103. proto = "esmtpa";
  104. break;
  105. case received_flags::ESMTPSA:
  106. proto = "esmtpsa";
  107. break;
  108. case received_flags::LMTP:
  109. proto = "lmtp";
  110. break;
  111. case received_flags::IMAP:
  112. proto = "imap";
  113. break;
  114. case received_flags::HTTP:
  115. proto = "http";
  116. break;
  117. case received_flags::LOCAL:
  118. proto = "local";
  119. break;
  120. case received_flags::MAPI:
  121. proto = "mapi";
  122. break;
  123. default:
  124. break;
  125. }
  126. return proto;
  127. }
  128. struct received_header {
  129. mime_string from_hostname;
  130. mime_string real_hostname;
  131. mime_string real_ip;
  132. mime_string by_hostname;
  133. mime_string for_mbox;
  134. struct rspamd_email_address *for_addr = nullptr;
  135. rspamd_inet_addr_t *addr = nullptr;
  136. struct rspamd_mime_header *hdr = nullptr;
  137. time_t timestamp = 0;
  138. received_flags flags = received_flags::DEFAULT; /* See enum rspamd_received_type */
  139. received_header() noexcept
  140. : from_hostname(received_char_filter),
  141. real_hostname(received_char_filter),
  142. real_ip(received_char_filter),
  143. by_hostname(received_char_filter),
  144. for_mbox()
  145. {
  146. }
  147. /* We have raw C pointers, so copy is explicitly disabled */
  148. received_header(const received_header &other) = delete;
  149. received_header(received_header &&other) noexcept
  150. {
  151. *this = std::move(other);
  152. }
  153. received_header &operator=(received_header &&other) noexcept
  154. {
  155. if (this != &other) {
  156. from_hostname = std::move(other.from_hostname);
  157. real_hostname = std::move(other.real_hostname);
  158. real_ip = std::move(other.real_ip);
  159. by_hostname = std::move(other.by_hostname);
  160. for_mbox = std::move(other.for_mbox);
  161. timestamp = other.timestamp;
  162. flags = other.flags;
  163. std::swap(for_addr, other.for_addr);
  164. std::swap(addr, other.addr);
  165. std::swap(hdr, other.hdr);
  166. }
  167. return *this;
  168. }
  169. /* Unit tests helper */
  170. static auto from_map(const ankerl::unordered_dense::map<std::string_view, std::string_view> &map) -> received_header
  171. {
  172. using namespace std::string_view_literals;
  173. received_header rh;
  174. if (map.contains("from_hostname")) {
  175. rh.from_hostname.assign_copy(map.at("from_hostname"sv));
  176. }
  177. if (map.contains("real_hostname")) {
  178. rh.real_hostname.assign_copy(map.at("real_hostname"sv));
  179. }
  180. if (map.contains("by_hostname")) {
  181. rh.by_hostname.assign_copy(map.at("by_hostname"sv));
  182. }
  183. if (map.contains("real_ip")) {
  184. rh.real_ip.assign_copy(map.at("real_ip"sv));
  185. }
  186. if (map.contains("for_mbox")) {
  187. rh.for_mbox.assign_copy(map.at("for_mbox"sv));
  188. }
  189. return rh;
  190. }
  191. auto as_map() const -> ankerl::unordered_dense::map<std::string_view, std::string_view>
  192. {
  193. ankerl::unordered_dense::map<std::string_view, std::string_view> map;
  194. if (!from_hostname.empty()) {
  195. map["from_hostname"] = from_hostname.as_view();
  196. }
  197. if (!real_hostname.empty()) {
  198. map["real_hostname"] = real_hostname.as_view();
  199. }
  200. if (!by_hostname.empty()) {
  201. map["by_hostname"] = by_hostname.as_view();
  202. }
  203. if (!real_ip.empty()) {
  204. map["real_ip"] = real_ip.as_view();
  205. }
  206. if (!for_mbox.empty()) {
  207. map["for_mbox"] = for_mbox.as_view();
  208. }
  209. return map;
  210. }
  211. ~received_header()
  212. {
  213. if (for_addr) {
  214. rspamd_email_address_free(for_addr);
  215. }
  216. }
  217. };
  218. class received_header_chain {
  219. public:
  220. explicit received_header_chain(struct rspamd_task *task)
  221. {
  222. headers.reserve(2);
  223. rspamd_mempool_add_destructor(task->task_pool,
  224. received_header_chain::received_header_chain_pool_dtor, this);
  225. }
  226. explicit received_header_chain()
  227. {
  228. headers.reserve(2);
  229. }
  230. enum class append_type {
  231. append_tail,
  232. append_head
  233. };
  234. auto new_received(append_type how = append_type::append_tail) -> received_header &
  235. {
  236. if (how == append_type::append_tail) {
  237. headers.emplace_back();
  238. return headers.back();
  239. }
  240. else {
  241. headers.insert(std::begin(headers), received_header());
  242. return headers.front();
  243. }
  244. }
  245. auto new_received(received_header &&hdr, append_type how = append_type::append_tail) -> received_header &
  246. {
  247. if (how == append_type::append_tail) {
  248. headers.emplace_back(std::move(hdr));
  249. return headers.back();
  250. }
  251. else {
  252. headers.insert(std::begin(headers), std::move(hdr));
  253. return headers.front();
  254. }
  255. }
  256. auto get_received(std::size_t nth) -> std::optional<std::reference_wrapper<received_header>>
  257. {
  258. if (nth < headers.size()) {
  259. return headers[nth];
  260. }
  261. return std::nullopt;
  262. }
  263. auto size() const -> std::size_t
  264. {
  265. return headers.size();
  266. }
  267. constexpr auto as_vector() const -> const std::vector<received_header> &
  268. {
  269. return headers;
  270. }
  271. private:
  272. static auto received_header_chain_pool_dtor(void *ptr) -> void
  273. {
  274. delete static_cast<received_header_chain *>(ptr);
  275. }
  276. std::vector<received_header> headers;
  277. };
  278. }// namespace rspamd::mime
  279. #endif//RSPAMD_RECEIVED_HXX