rspamd/lualib/lua_ffi/spf.lua
2019-04-12 16:32:22 +01:00

143 lines
3.5 KiB
Lua

--[[
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.
]]--
--[[[
-- @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
}