Maintained by: NLnet Labs

[Unbound-users] Python API extension patch proposal

Stephane Lapie
Mon Jan 5 09:03:40 CET 2015


I am currently in the process of dealing with water torture attacks on 
our cache DNS servers (<randomstring> queries that never 
resolve and end up causing enormous upstream traffic, ultimately 
crushing the authoritative server for

To this end, I toyed around with unbound, and noticed that 
unbound-control's lookup function has a very interesting feature :
$ sudo unbound-control lookup
The following name servers are used for lookup of
;rrset 14224 4 0 8 0	273424	IN	NS	273424	IN	NS	273424	IN	NS	273424	IN	NS
;rrset 7350 1 0 8 0	266550	IN	A
;rrset 57726 1 0 8 0	316926	IN	A
;rrset 56988 1 0 8 0	316188	IN	A
;rrset 28120 1 0 8 0	287320	IN	A
Delegation with 4 names, of which 4 can be examined to query further 
It provides 4 IP addresses.   	rto 87 msec, ttl 628, ping 71 var 4 rtt 87, tA 0, tAAAA 
0, tother 0, EDNS 0 probed.   	rto 645 msec, ttl 19, ping 101 var 136 rtt 645, tA 0, 
tAAAA 0, tother 0, EDNS 0 probed.   	rto 113 msec, ttl 31, ping 97 var 4 rtt 113, tA 0, 
tAAAA 0, tother 0, EDNS 0 probed.   	rto 99 msec, ttl 328, ping 47 var 13 rtt 99, tA 0, 
tAAAA 0, tother 0, EDNS 0 probed.

Namely, for any given hostname, it can find the closest delegation point 
(in this case, remove unambiguously and with no danger the random part 
of the attack query, since it goes to the deepest component that retains 
any meaning DNS-wise).

I thought that since the information was available within the Unbound 
process, coding a Python module that would keep track of the count of 
queries to a DDoSed delegation point would be a good start to an 
algorithm for effectively blocking water torture attacks, but the 
required function, dns_cache_find_delegation() was not available readily 
from the Python API.

Therefore, I extended the Python API as per the attached file (also 
available at, to 
export struct delegpt and a find_delegation() function that would allow 
to acquire the delegation point name, records and servers, from the 
Python module environment.

Example of use case (extremely simplified, implementation of 
allow_query() not disclosed) :
def operate(id, event, qstate, qdata):
     delegation = find_delegation(qstate, qstate.qinfo.qname, 

     if (event == MODULE_EVENT_NEW) or (event == MODULE_EVENT_PASS):
         policy_result = allow_query(qstate.qinfo, delegation, 
         if (policy_result): # Pass query to next module
             qstate.ext_state[id] = MODULE_WAIT_MODULE
             qstate.ext_state[id] = MODULE_ERROR
         return True

This patch, along with an actual module that will SERVFAIL (as above) 
cache-missing queries going over threshold (therefore reducing upstream 
traffic to a tenth of what it would be if honoring DDoS-related queries, 
AND keeping it within our AS), has been running in our production 
environment at ASAHI Net for several months now, and has been approved 
for upstream contribution on our side.

I thought this feature would be very useful to have in the Python module 
environment, so would it be possible for you to consider integrating 
this patch as a standard feature in Unbound?

Thanks for your time,
Stephane LAPIE, EPITA SRS, Promo 2005
"Even when they have digital readouts, I can't understand them."
-------------- next part --------------
A non-text attachment was scrubbed...
Name: lookup-api-extension.patch
Type: text/x-diff
Size: 7066 bytes
Desc: not available
URL: <>