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.

maps.lua 5.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  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. -- Controller maps plugin
  14. local maps_cache
  15. local maps_aliases
  16. local lua_util = require "lua_util"
  17. local ts = require("tableshape").types
  18. local ucl = require "ucl"
  19. local function maybe_fill_maps_cache()
  20. if not maps_cache then
  21. maps_cache = {}
  22. maps_aliases = {}
  23. local maps = rspamd_config:get_maps()
  24. for _, m in ipairs(maps) do
  25. -- We get the first url here and that's it
  26. local url = m:get_uri()
  27. if url ~= 'static' then
  28. if not maps_cache[url] then
  29. local alias = url:match('/([^/]+)$')
  30. maps_cache[url] = m
  31. if not maps_aliases[alias] then
  32. maps_aliases[alias] = url
  33. end
  34. else
  35. -- Do not override, as we don't care about duplicate maps that come from different
  36. -- sources.
  37. -- In theory, that should be cached but there are some exceptions even so far...
  38. url = math.random() -- to shut luacheck about empty branch with a comment
  39. end
  40. end
  41. end
  42. end
  43. end
  44. local function check_specific_map(input, uri, m, results, report_misses)
  45. local value = m:get_key(input)
  46. if value then
  47. local result = {
  48. map = uri,
  49. alias = uri:match('/([^/]+)$'),
  50. value = value,
  51. key = input,
  52. hit = true,
  53. }
  54. table.insert(results, result)
  55. elseif report_misses then
  56. local result = {
  57. map = uri,
  58. alias = uri:match('/([^/]+)$'),
  59. key = input,
  60. hit = false,
  61. }
  62. table.insert(results, result)
  63. end
  64. end
  65. local function handle_query_map(_, conn, req_params)
  66. maybe_fill_maps_cache()
  67. local keys_to_check = {}
  68. if req_params.value and req_params.value ~= '' then
  69. keys_to_check[1] = req_params.value
  70. elseif req_params.values then
  71. keys_to_check = lua_util.str_split(req_params.values, ',')
  72. end
  73. local results = {}
  74. for _, key in ipairs(keys_to_check) do
  75. for uri, m in pairs(maps_cache) do
  76. check_specific_map(key, uri, m, results, req_params.report_misses)
  77. end
  78. end
  79. conn:send_ucl {
  80. success = (#results > 0),
  81. results = results
  82. }
  83. end
  84. local function handle_query_specific_map(_, conn, req_params)
  85. maybe_fill_maps_cache()
  86. -- Fill keys to check
  87. local keys_to_check = {}
  88. if req_params.value and req_params.value ~= '' then
  89. keys_to_check[1] = req_params.value
  90. elseif req_params.values then
  91. keys_to_check = lua_util.str_split(req_params.values, ',')
  92. end
  93. local maps_to_check = maps_cache
  94. -- Fill maps to check
  95. if req_params.maps then
  96. local map_names = lua_util.str_split(req_params.maps, ',')
  97. maps_to_check = {}
  98. for _, mn in ipairs(map_names) do
  99. if maps_cache[mn] then
  100. maps_to_check[mn] = maps_cache[mn]
  101. else
  102. local alias = maps_aliases[mn]
  103. if alias then
  104. maps_to_check[alias] = maps_cache[alias]
  105. else
  106. conn:send_error(404, 'no such map: ' .. mn)
  107. end
  108. end
  109. end
  110. end
  111. local results = {}
  112. for _, key in ipairs(keys_to_check) do
  113. for uri, m in pairs(maps_to_check) do
  114. check_specific_map(key, uri, m, results, req_params.report_misses)
  115. end
  116. end
  117. conn:send_ucl {
  118. success = (#results > 0),
  119. results = results
  120. }
  121. end
  122. local function handle_list_maps(_, conn, _)
  123. maybe_fill_maps_cache()
  124. conn:send_ucl {
  125. maps = lua_util.keys(maps_cache),
  126. aliases = maps_aliases
  127. }
  128. end
  129. local query_json_schema = ts.shape {
  130. maps = ts.array_of(ts.string):is_optional(),
  131. report_misses = ts.boolean:is_optional(),
  132. values = ts.array_of(ts.string),
  133. }
  134. local function handle_query_json(task, conn)
  135. maybe_fill_maps_cache()
  136. local parser = ucl.parser()
  137. local ok, err = parser:parse_text(task:get_rawbody())
  138. if not ok then
  139. conn:send_error(400, err)
  140. return
  141. end
  142. local obj = parser:get_object()
  143. ok, err = query_json_schema:transform(obj)
  144. if not ok then
  145. conn:send_error(400, err)
  146. return
  147. end
  148. local maps_to_check = {}
  149. local report_misses = obj.report_misses
  150. local results = {}
  151. if obj.maps then
  152. for _, mn in ipairs(obj.maps) do
  153. if maps_cache[mn] then
  154. maps_to_check[mn] = maps_cache[mn]
  155. else
  156. local alias = maps_aliases[mn]
  157. if alias then
  158. maps_to_check[alias] = maps_cache[alias]
  159. else
  160. conn:send_error(400, 'no such map: ' .. mn)
  161. return
  162. end
  163. end
  164. end
  165. else
  166. maps_to_check = maps_cache
  167. end
  168. for _, key in ipairs(obj.values) do
  169. for uri, m in pairs(maps_to_check) do
  170. check_specific_map(key, uri, m, results, report_misses)
  171. end
  172. end
  173. conn:send_ucl {
  174. success = (#results > 0),
  175. results = results
  176. }
  177. end
  178. return {
  179. query = {
  180. handler = handle_query_map,
  181. enable = false,
  182. },
  183. query_json = {
  184. handler = handle_query_json,
  185. enable = false,
  186. need_task = true,
  187. },
  188. query_specific = {
  189. handler = handle_query_specific_map,
  190. enable = false,
  191. },
  192. list = {
  193. handler = handle_list_maps,
  194. enable = false,
  195. },
  196. }