diff options
-rw-r--r-- | lualib/lua_ffi/spf.lua | 6 | ||||
-rw-r--r-- | lualib/rspamadm/dns_tool.lua | 177 |
2 files changed, 181 insertions, 2 deletions
diff --git a/lualib/lua_ffi/spf.lua b/lualib/lua_ffi/spf.lua index e65405160..c75eee0d2 100644 --- a/lualib/lua_ffi/spf.lua +++ b/lualib/lua_ffi/spf.lua @@ -82,6 +82,8 @@ local function convert_mech(mech) end end +local NULL = ffi.new 'void*' + local function spf_addr_tolua(ffi_spf_addr) local ipstr = ffi.C.spf_addr_mask_to_string(ffi_spf_addr) local ret = { @@ -89,7 +91,7 @@ local function spf_addr_tolua(ffi_spf_addr) ipnet = ffi.string(ipstr), } - if ffi_spf_addr.spf_string then + if ffi_spf_addr.spf_string ~= NULL then ret.spf_str = ffi.string(ffi_spf_addr.spf_string) end @@ -116,7 +118,7 @@ local function spf_resolve(task, cb) local matched = ffi.C.spf_addr_match_task(task:topointer(), rec) - if matched then + if matched ~= NULL then cb(true, res, spf_addr_tolua(matched)) else cb(true, res, nil) diff --git a/lualib/rspamadm/dns_tool.lua b/lualib/rspamadm/dns_tool.lua new file mode 100644 index 000000000..685785183 --- /dev/null +++ b/lualib/rspamadm/dns_tool.lua @@ -0,0 +1,177 @@ +--[[ +Copyright (c) 2019, Vsevolod Stakhov <vsevolod@highsecure.ru> + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +]]-- + + +local argparse = require "argparse" +local rspamd_logger = require "rspamd_logger" +local lua_util = require "lua_util" +local ansicolors = require "ansicolors" + +local parser = argparse() + :name "rspamadm dns_tool" + :description "DNS tools provided by Rspamd" + :help_description_margin(30) + :command_target("command") + :require_command(true) + +parser:option "-c --config" + :description "Path to config file" + :argname("<cfg>") + :default(rspamd_paths["CONFDIR"] .. "/" .. "rspamd.conf") + +local spf = parser:command "spf" + :description "Extracts spf records" +parser:mutex( + spf:option "-d --domain" + :description "Domain to use" + :argname("<domain>"), + spf:option "-f --from" + :description "SMTP from to use" + :argname("<from>") +) + +spf:option "-i --ip" + :description "Source IP address to use" + :argname("<ip>") +spf:flag "-a --all" + :description "Print all records" + +local function printf(fmt, ...) + if fmt then + io.write(string.format(fmt, ...)) + end + io.write('\n') +end + +local function highlight(str) + return ansicolors.white .. str .. ansicolors.reset +end + +local function green(str) + return ansicolors.green .. str .. ansicolors.reset +end + +local function red(str) + return ansicolors.red .. str .. ansicolors.reset +end + +local function load_config(opts) + local _r,err = rspamd_config:load_ucl(opts['config']) + + if not _r then + rspamd_logger.errx('cannot parse %s: %s', opts['config'], err) + os.exit(1) + end + + _r,err = rspamd_config:parse_rcl({'logging', 'worker'}) + if not _r then + rspamd_logger.errx('cannot process %s: %s', opts['config'], err) + os.exit(1) + end +end + +local function spf_handler(opts) + local lua_spf = require("lua_ffi").spf + local rspamd_task = require "rspamd_task" + + local task = rspamd_task:create(rspamd_config, rspamadm_ev_base) + task:set_session(rspamadm_session) + task:set_resolver(rspamadm_dns_resolver) + + if opts.ip then + task:set_from_ip(opts.ip) + end + + if opts.from then + task:set_from('smtp', {addr = opts.from}) + elseif opts.domain then + task:set_from('smtp', {user = 'user', domain = opts.domain}) + else + io.stderr:write('Neither domain nor from specified\n') + os.exit(1) + end + + local function display_spf_results(elt, colored) + local dec = function(e) return e end + + if colored then + dec = function(e) return highlight(e) end + + if elt.res == 'pass' then + dec = function(e) return green(e) end + elseif elt.res == 'fail' then + dec = function(e) return red(e) end + end + + end + printf('%s: %s', highlight('Result'), dec(elt.res)) + printf('%s: %s', highlight('Network'), dec(elt.ipnet)) + + if elt.spf_str then + printf('%s: %s', highlight('Original'), elt.spf_str) + end + end + + local function cb(success, res, matched) + if success then + if opts.ip and not opts.all then + if matched then + display_spf_results(matched, true) + else + printf('Not matched') + end + + os.exit(0) + end + + printf('SPF record for %s; digest: %s', + highlight(opts.domain or opts.from), highlight(res.digest)) + for _,elt in ipairs(res.addrs) do + if lua_util.table_cmp(elt, matched) then + printf("%s", highlight('*** Matched ***')) + display_spf_results(elt, true) + printf('------') + else + display_spf_results(elt, false) + printf('------') + end + end + else + printf('Cannot get SPF record: %s', res) + end + end + lua_spf.spf_resolve(task, cb) +end + +local function handler(args) + local opts = parser:parse(args) + load_config(opts) + + local command = opts.command + + if command == 'spf' then + spf_handler(opts) + else + parser:error('command %s is not implemented', command) + end +end + +return { + name = 'dns_tool', + aliases = {'dns', 'dns_tool'}, + handler = handler, + description = parser._description +}
\ No newline at end of file |