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.
SpamAssassin · AskDNS (Postfix + amavis)
The zone answers in graded form: the last octet is 9 − score, so
127.0.0.0 means TOP and 127.0.0.9 means
BAD (see Classification above). Every answer is a valid
A record, including the good ones. Two traps when you set this up in SpamAssassin:
- A rule without a subtest matches every answer, TOP included. A plain
urirhsbl, or anaskdnsrule without a filter, adds its spam score to the mails from the best-rated senders too. - A single number as subtest is a bitmask, not an exact value:
2matches.2,.3,.6and.7. Use the regular expressions or address ranges shown below.
Sender-domain lookup via the AskDNS plugin (SpamAssassin 3.4 or later). Make sure it is
loaded: loadplugin Mail::SpamAssassin::Plugin::AskDNS in /etc/spamassassin/v340.pre
(active by default on most distributions). _AUTHORDOMAIN_ is the domain of the
From: header. Save as /etc/spamassassin/bsn_reputation.cf:
ifplugin Mail::SpamAssassin::Plugin::AskDNS
askdns BSN_REP_TOP _AUTHORDOMAIN_.dnsbl.bsnsoft-hostings.de A /^127\.0\.0\.0$/
describe BSN_REP_TOP Sender domain: top reputation (BSNSoft)
tflags BSN_REP_TOP net nice
score BSN_REP_TOP -0.8
askdns BSN_REP_HIGH _AUTHORDOMAIN_.dnsbl.bsnsoft-hostings.de A /^127\.0\.0\.[12]$/
describe BSN_REP_HIGH Sender domain: high reputation (BSNSoft)
tflags BSN_REP_HIGH net nice
score BSN_REP_HIGH -0.2
askdns BSN_REP_MEDIUM _AUTHORDOMAIN_.dnsbl.bsnsoft-hostings.de A /^127\.0\.0\.[345]$/
describe BSN_REP_MEDIUM Sender domain: medium reputation (BSNSoft)
tflags BSN_REP_MEDIUM net
score BSN_REP_MEDIUM 0.4
askdns BSN_REP_LOW _AUTHORDOMAIN_.dnsbl.bsnsoft-hostings.de A /^127\.0\.0\.[67]$/
describe BSN_REP_LOW Sender domain: low reputation (BSNSoft)
tflags BSN_REP_LOW net
score BSN_REP_LOW 1.7
askdns BSN_REP_BAD _AUTHORDOMAIN_.dnsbl.bsnsoft-hostings.de A /^127\.0\.0\.[89]$/
describe BSN_REP_BAD Sender domain: bad reputation (BSNSoft)
tflags BSN_REP_BAD net
score BSN_REP_BAD 2.5
endif
The weights are the rspamd weights above, scaled to SpamAssassin's default threshold of 5 (rspamd: reject at 12). As with rspamd, our maximum contribution (+2.5) stays at half the threshold – our signal alone never marks a message as spam.
Optional: domains in body URIs
To also check domains that appear as links in the body, add a URIDNSBL rule. Match
only the BAD range and keep the weight small, since most legit mails link to mid-reputation domains
(shorteners, trackers):
ifplugin Mail::SpamAssassin::Plugin::URIDNSBL
urirhssub BSN_URI_BAD dnsbl.bsnsoft-hostings.de. A 127.0.0.8-127.0.0.9
body BSN_URI_BAD eval:check_uridnsbl('BSN_URI_BAD')
describe BSN_URI_BAD URI domain: bad reputation (BSNSoft)
tflags BSN_URI_BAD net domains_only
score BSN_URI_BAD 0.5
endif
Verify
spamassassin --lint
spamassassin -D askdns -t < mail.eml 2>&1 | grep -E 'BSN_|X-Spam-Status'
The debug output shows the query (launching query (BSN_REP_TOP): example.com.dnsbl.bsnsoft-hostings.de)
and the answer for each rule; the report lists the rule that matched. With amavis, restart it afterwards
(systemctl restart amavis) so it loads the new rules.
A domain we have not seen before returns NXDOMAIN on its first lookup and is rated in the background; later lookups get the score.
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