Integrate the reputation service in your mail filter
Classic DNSBL at dnsbl.bsnsoft-hostings.de. HTTP API available as an alternative transport.
Rate limits & registration
Queries are rate-limited per source IP (the IP we see — not your mailserver, but the DNS resolver it uses):
| Tier | Limit | Over-limit behaviour |
|---|---|---|
| Free / unregistered | 20 queries/hour per IP | NXDOMAIN (silent drop) |
| Registered (sign up free) | 100 queries/hour per registered IP | Answered, but flagged — dashboard shows a sales hint after first overage |
| Paid / Enterprise | Higher volumes & SLA | On request — contact details after signup |
If your mailserver uses a public resolver like 8.8.8.8, 1.1.1.1,
or 9.9.9.9 as DNS forwarder, we see the resolver's IP — shared with thousands
of other users. That traffic hits the 20/hour free-tier limit collectively, and you get
silently dropped. Fix:
- Run your own recursive resolver on the mailserver (
unbound,bind9). That resolver forwards only your traffic to us, not the resolver-pool's. - Alternatively point rspamd at a small internal forwarder
(
dns.servers = 127.0.0.1) which does the recursion. - Then register the resolver's IP (that's the one reaching us) here → 100/hour with no caveat.
Classification
The service answers with one of five classifications, encoded in the DNS return code:
| Score | Class | Symbol | DNS return | Recommended weight |
|---|---|---|---|---|
| 9 | TOP | BSN_REP_TOP | 127.0.0.0 | −2.0 (bonus) |
| 7–8 | HIGH | BSN_REP_HIGH | 127.0.0.[1-2] | −0.5 (small bonus) |
| 4–6 | MEDIUM | BSN_REP_MEDIUM | 127.0.0.[3-5] | +1.0 |
| 2–3 | LOW | BSN_REP_LOW | 127.0.0.[6-7] | +4.0 |
| 0–1 | BAD | BSN_REP_BAD | 127.0.0.[8-9] | +6.0 |
With an RSpamD reject threshold of 12, our maximum contribution (+6.0) stays below half – our signal alone never rejects a message; only in combination with another indicator (DMARC fail, Bayes, IP RBL, …).
RSpamD · Lua plugin (recommended)
Sender-domain lookup via our DNSBL. Small Lua file, loaded once, runs for every message. This is the cleanest integration for sender-domain reputation.
Step 1 – save as /etc/rspamd/local.d/bsn_reputation.lua:
local rspamd_logger = require "rspamd_logger"
local N = "bsn_reputation"
local RBL = "dnsbl.bsnsoft-hostings.de"
local function reputation_cb(task)
local from = task:get_from("smtp")
if not from or not from[1] then from = task:get_from("mime") end
if not from or not from[1] then return false end
local domain = (from[1]["domain"] or ""):lower()
if domain == "" then return false end
local resolver = task:get_resolver()
if not resolver then return false end
resolver:resolve_a({
task = task,
name = domain .. "." .. RBL,
callback = function(_, _, results, err)
if err or not results or #results == 0 then return end
local last = tonumber(tostring(results[1]):match("%.(%d+)$"))
if not last then return end
local sym
if last == 0 then sym = "BSN_REP_TOP"
elseif last <= 2 then sym = "BSN_REP_HIGH"
elseif last <= 5 then sym = "BSN_REP_MEDIUM"
elseif last <= 7 then sym = "BSN_REP_LOW"
else sym = "BSN_REP_BAD"
end
task:insert_result(sym, 1.0, domain)
end,
})
return false
end
local id = rspamd_config:register_symbol({
name = "BSN_REPUTATION_CHECK", type = "callback", callback = reputation_cb,
})
for _, sub in ipairs({"BSN_REP_TOP","BSN_REP_HIGH","BSN_REP_MEDIUM","BSN_REP_LOW","BSN_REP_BAD"}) do
rspamd_config:register_symbol({ name = sub, type = "virtual", parent = id, group = N })
end
rspamd_logger.infox(rspamd_config, "%s: loaded (zone=%s)", N, RBL)
Step 2 – activate it by adding this line to /etc/rspamd/rspamd.local.lua:
dofile("/etc/rspamd/local.d/bsn_reputation.lua")
Step 3 – set weights in /etc/rspamd/local.d/scores.conf:
BSN_REP_TOP = -2.0;
BSN_REP_HIGH = -0.5;
BSN_REP_MEDIUM = 1.0;
BSN_REP_LOW = 4.0;
BSN_REP_BAD = 6.0;
Step 4 – systemctl restart rspamd. Verify with:
printf "From: t@heise.de\nTo: r@x.de\nSubject: t\n\nbody\n" | \
rspamc -F "t@heise.de" - | grep BSN_REP
RSpamD · rbl module (optional, for body-URLs)
The Lua plugin above covers sender-domain reputation. If you additionally
want to match domains that appear as URLs inside the body text, add the native
rbl module on top. Be careful not to set weights that double-count
between sender-based (Lua) and URL-based (rbl) hits.
File /etc/rspamd/local.d/rbl.conf:
rbls {
bsnsoft_reputation_urls {
symbol = "BSN_URL_REPUTATION";
rbl = "dnsbl.bsnsoft-hostings.de";
checks = ["urls", "content_urls"];
emails_domainonly = true;
unknown = false;
returncodes {
BSN_URL_TOP = "127.0.0.0";
BSN_URL_HIGH = "127.0.0.[1-2]";
BSN_URL_MEDIUM = "127.0.0.[3-5]";
BSN_URL_LOW = "127.0.0.[6-7]";
BSN_URL_BAD = "127.0.0.[8-9]";
}
}
}
Separate symbol names (BSN_URL_*) prevent double-scoring with the Lua sender-check
(BSN_REP_*). Typical weights: use smaller numbers for URL hits, since most legit mails
contain URLs to mid-reputation domains (shorteners, trackers).
Weights in /etc/rspamd/local.d/scores.conf:
symbols {
"BSN_REP_TOP" { score = -2.0; description = "Top reputation"; }
"BSN_REP_HIGH" { score = -0.5; description = "Strong reputation"; }
"BSN_REP_MEDIUM" { score = 1.0; description = "Neutral / unclear reputation"; }
"BSN_REP_LOW" { score = 4.0; description = "Weak reputation"; }
"BSN_REP_BAD" { score = 6.0; description = "Throwaway / spam infrastructure"; }
}
Reload after changes: systemctl reload rspamd.
Postfix · postfwd policy
If RSpamD is not in use, a policy daemon such as postfwd can query the DNSBL:
id=BSN_REP_REJECT ; client_address!~=^127\. ; sender_domain_in_dnsbl==dnsbl.bsnsoft-hostings.de ; action=REJECT Poor sender reputation
HTTP API
Single lookup:
curl https://dnsbl.bsnsoft-hostings.de/api/score/example.com
Response format:
{
"rawInput": "example.com",
"normalizedDomain": "example.com",
"score": 8,
"classification": "HIGH",
"status": "INDEXED"
}
Status values:
| Status | Meaning |
|---|---|
INDEXED | Score available in score / classification |
INVALID_DOMAIN | Input is not a valid domain |
Set generous client timeouts (at least 90 seconds).
Questions or enterprise usage?
Contact: info@bsnsoft.de · bsnsoft.de