From 4824ea2d2b8fecd5f19059e775914d8d64d1dbc9 Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Fri, 12 Apr 2019 16:01:11 +0100 Subject: [PATCH] [Feature] Add SPF FFI library for Lua --- lualib/lua_ffi/common.lua | 11 +++ lualib/lua_ffi/init.lua | 1 + lualib/lua_ffi/spf.lua | 141 ++++++++++++++++++++++++++++++++++++++ lualib/rspamadm/mime.lua | 2 +- 4 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 lualib/lua_ffi/spf.lua diff --git a/lualib/lua_ffi/common.lua b/lualib/lua_ffi/common.lua index d8f23e998..652b1dc6e 100644 --- a/lualib/lua_ffi/common.lua +++ b/lualib/lua_ffi/common.lua @@ -27,8 +27,19 @@ struct GString { size_t len; size_t allocated_len; }; +struct GArray { + char *data; + unsigned len; +}; +typedef void (*ref_dtor_cb_t)(void *data); +struct ref_entry_s { + unsigned int refcount; + ref_dtor_cb_t dtor; +}; void g_string_free (struct GString *st, int free_data); +void g_free (void *p); +long rspamd_snprintf (char *buf, long max, const char *fmt, ...); ]] return {} \ No newline at end of file diff --git a/lualib/lua_ffi/init.lua b/lualib/lua_ffi/init.lua index b0254bdd8..e5ca24a11 100644 --- a/lualib/lua_ffi/init.lua +++ b/lualib/lua_ffi/init.lua @@ -49,5 +49,6 @@ end pcall(ffi.load, "rspamd-server", true) exports.common = require "lua_ffi/common" exports.dkim = require "lua_ffi/dkim" +exports.spf = require "lua_ffi/spf" return exports \ No newline at end of file diff --git a/lualib/lua_ffi/spf.lua b/lualib/lua_ffi/spf.lua new file mode 100644 index 000000000..e65405160 --- /dev/null +++ b/lualib/lua_ffi/spf.lua @@ -0,0 +1,141 @@ +--[[ +Copyright (c) 2019, Vsevolod Stakhov + +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. +]]-- + +--[[[ +-- @module lua_ffi/spf +-- This module contains ffi interfaces to SPF +--]] + +local ffi = require 'ffi' + +ffi.cdef[[ +enum spf_mech_e { + SPF_FAIL, + SPF_SOFT_FAIL, + SPF_PASS, + SPF_NEUTRAL +}; +static const unsigned RSPAMD_SPF_FLAG_IPV6 = (1 << 0); +static const unsigned RSPAMD_SPF_FLAG_IPV4 = (1 << 1); +static const unsigned RSPAMD_SPF_FLAG_ANY = (1 << 3); +struct spf_addr { + unsigned char addr6[16]; + unsigned char addr4[4]; + union { + struct { + uint16_t mask_v4; + uint16_t mask_v6; + } dual; + uint32_t idx; + } m; + unsigned flags; + enum spf_mech_e mech; + char *spf_string; + struct spf_addr *prev, *next; +}; + +struct spf_resolved { + char *domain; + unsigned ttl; + int temp_failed; + int na; + int perm_failed; + uint64_t digest; + struct GArray *elts; + struct ref_entry_s ref; +}; + +typedef void (*spf_cb_t)(struct spf_resolved *record, + struct rspamd_task *task, void *data); +struct rspamd_task; +int rspamd_spf_resolve(struct rspamd_task *task, spf_cb_t callback, + void *cbdata); +const char * rspamd_spf_get_domain (struct rspamd_task *task); +struct spf_resolved * spf_record_ref (struct spf_resolved *rec); +void spf_record_unref (struct spf_resolved *rec); +char * spf_addr_mask_to_string (struct spf_addr *addr); +struct spf_addr * spf_addr_match_task (struct rspamd_task *task, struct spf_resolved *rec); +]] + +local function convert_mech(mech) + if mech == ffi.C.SPF_FAIL then + return 'fail' + elseif mech == ffi.C.SPF_SOFT_FAIL then + return 'softfail' + elseif mech == ffi.C.SPF_PASS then + return 'pass' + elseif mech == ffi.C.SPF_NEUTRAL then + return 'neutral' + end +end + +local function spf_addr_tolua(ffi_spf_addr) + local ipstr = ffi.C.spf_addr_mask_to_string(ffi_spf_addr) + local ret = { + res = convert_mech(ffi_spf_addr.mech), + ipnet = ffi.string(ipstr), + } + + if ffi_spf_addr.spf_string then + ret.spf_str = ffi.string(ffi_spf_addr.spf_string) + end + + ffi.C.g_free(ipstr) + return ret +end + +local function spf_resolve(task, cb) + local function spf_cb(rec, _, _) + if not rec then + cb(false, 'record is empty') + else + local nelts = rec.elts.len + local elts = ffi.cast("struct spf_addr *", rec.elts.data) + local res = { + addrs = {} + } + local digstr = ffi.new("char[64]") + ffi.C.rspamd_snprintf(digstr, 64, "0x%xuL", rec.digest) + res.digest = ffi.string(digstr) + for i = 1,nelts do + res.addrs[i] = spf_addr_tolua(elts[i - 1]) + end + + local matched = ffi.C.spf_addr_match_task(task:topointer(), rec) + + if matched then + cb(true, res, spf_addr_tolua(matched)) + else + cb(true, res, nil) + end + end + end + + local ret = ffi.C.rspamd_spf_resolve(task:topointer(), spf_cb, nil) + + if not ret then + cb(false, 'cannot perform resolving') + end +end + +local function spf_unref(rec) + ffi.C.spf_record_unref(rec) +end + +return { + spf_resolve = spf_resolve, + spf_unref = spf_unref +} \ No newline at end of file diff --git a/lualib/rspamadm/mime.lua b/lualib/rspamadm/mime.lua index 16803b8c0..91bc06993 100644 --- a/lualib/rspamadm/mime.lua +++ b/lualib/rspamadm/mime.lua @@ -43,7 +43,7 @@ parser:mutex( :description "UCL output" ) parser:flag "-C --compact" - :description "Use compactl format" + :description "Use compact format" parser:flag "--no-file" :description "Do not print filename" -- 2.39.5