commit 4824ea2: [Feature] Add SPF FFI library for Lua

Vsevolod Stakhov vsevolod at highsecure.ru
Fri Apr 12 15:35:04 UTC 2019


Author: Vsevolod Stakhov
Date: 2019-04-12 16:01:11 +0100
URL: https://github.com/rspamd/rspamd/commit/4824ea2d2b8fecd5f19059e775914d8d64d1dbc9

[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(-)

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 <vsevolod at 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.
+]]--
+
+--[[[
+-- @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"
 


More information about the Commits mailing list