summaryrefslogtreecommitdiffstats
path: root/lualib/lua_ffi/spf.lua
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lualib/lua_ffi/spf.lua143
1 files changed, 143 insertions, 0 deletions
diff --git a/lualib/lua_ffi/spf.lua b/lualib/lua_ffi/spf.lua
new file mode 100644
index 0000000..0f982f2
--- /dev/null
+++ b/lualib/lua_ffi/spf.lua
@@ -0,0 +1,143 @@
+--[[
+Copyright (c) 2022, Vsevolod Stakhov <vsevolod@rspamd.com>
+
+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 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 = {
+ res = convert_mech(ffi_spf_addr.mech),
+ ipnet = ffi.string(ipstr),
+ }
+
+ if ffi_spf_addr.spf_string ~= NULL 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 ~= NULL 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