<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Unbound (DNS Resolver) and Python module How-to]]></title><description><![CDATA[<p dir="auto">Hi, want to share how Python module can be used with Undound.<br />
Unbound support only one python module at once.<br />
In my case I was need 2 tasks in one module:</p>
<ul>
<li>return loopback (IPv6 <code>::1</code> and IPv4 <code>127.0.0.1</code>) for some TLDs</li>
<li>remove AAAA (IPv6) records from resolving for some domains (and all their subdomains) to workaround issues related to their broken realization of IPv6 stack</li>
</ul>
<p dir="auto">So I googled for some examples at Internet and with basic knowledge of python I ended in with this:</p>
<pre><code>def init(id, cfg):
    return True

def deinit(id):
    return True

def inform_super(id, qstate, superqstate, qdata):
    return True

localhost_domains = [
    "localdomain.",
    "localtest.me.",
    "lvh.me."
]

ignore_aaaa_domains = [
    "lync.com.",
    "netflix.com.",
    "nflxso.net."
]

def operate(id, event, qstate, qdata):
    if event == MODULE_EVENT_NEW or event == MODULE_EVENT_PASS:

        if qstate.qinfo.qname_str.endswith(tuple(localhost_domains)):
            if (qstate.qinfo.qtype == RR_TYPE_AAAA):
                msg = DNSMessage(qstate.qinfo.qname_str, RR_TYPE_AAAA, RR_CLASS_IN, PKT_QR | PKT_RA | PKT_AA)
                msg.answer.append("%s 10 IN AAAA ::1" % qstate.qinfo.qname_str)
            if (qstate.qinfo.qtype == RR_TYPE_A):
                msg = DNSMessage(qstate.qinfo.qname_str, RR_TYPE_A, RR_CLASS_IN, PKT_QR | PKT_RA | PKT_AA)
                msg.answer.append("%s 10 IN A 127.0.0.1" % qstate.qinfo.qname_str)
            if (qstate.qinfo.qtype == RR_TYPE_ANY):
                msg = DNSMessage(qstate.qinfo.qname_str, RR_TYPE_AAAA, RR_TYPE_A, RR_CLASS_IN, PKT_QR | PKT_RA | PKT_AA)
                msg.answer.append("%s 10 IN AAAA ::1" % qstate.qinfo.qname_str)
                msg.answer.append("%s 10 IN A 127.0.0.1" % qstate.qinfo.qname_str)
            if not msg.set_return_msg(qstate):
                qstate.ext_state[id] = MODULE_ERROR
                return True
            # We don't need validation, result is valid
            qstate.return_msg.rep.security = 2
            qstate.return_rcode = RCODE_NOERROR
            qstate.ext_state[id] = MODULE_FINISHED
            log_info("mod.py: returned localhost for %s" % qstate.qinfo.qname_str)
            return True

        if qstate.qinfo.qname_str.endswith(tuple(ignore_aaaa_domains)):
            if (qstate.qinfo.qtype == RR_TYPE_AAAA):
                msg = DNSMessage(qstate.qinfo.qname_str, RR_TYPE_A, RR_CLASS_IN, PKT_QR | PKT_RA | PKT_AA)
                if not msg.set_return_msg(qstate):
                    qstate.ext_state[id] = MODULE_ERROR
                    return True
                # We don't need validation, result is valid
                qstate.return_msg.rep.security = 2
                qstate.return_rcode = RCODE_NOERROR
                qstate.ext_state[id] = MODULE_FINISHED
                log_info("mod.py: removed AAAA record from %s" % qstate.qinfo.qname_str)
                return True

        qstate.ext_state[id] = MODULE_WAIT_MODULE
        return True

    if event == MODULE_EVENT_MODDONE:
        qstate.ext_state[id] = MODULE_FINISHED
        return True

    log_err("mod.py: error in module occured")
    qstate.ext_state[id] = MODULE_ERROR
    return True

log_info("mod.py: script loaded")
</code></pre>
<p dir="auto">If you know how exchange it - critics are welcome.</p>
<p dir="auto">To not broke all in case of full-wipe-restore I use Filer plugin, more: after change of this file in this plugin - Unbound will automatically reload by itself via start script command, config:<br />
File: <code>/var/unbound/mod.py</code><br />
Description: <code>Unbound python module</code><br />
Permissions: <code>644</code><br />
File Contents: python script above, modify domains list to your needs, and not forget <code>.</code> at end of each domain<br />
Script/Command: <code>unbound-control -c /var/unbound/unbound.conf reload</code><br />
Execute mode: <code>Background (default)</code></p>
<p dir="auto">Another examples can be found at https://github.com/NLnetLabs/unbound/tree/master/pythonmod/examples</p>
]]></description><link>https://forum.netgate.com/topic/152483/unbound-dns-resolver-and-python-module-how-to</link><generator>RSS for Node</generator><lastBuildDate>Sun, 19 Apr 2026 02:54:11 GMT</lastBuildDate><atom:link href="https://forum.netgate.com/topic/152483.rss" rel="self" type="application/rss+xml"/><pubDate>Mon, 13 Apr 2020 18:19:35 GMT</pubDate><ttl>60</ttl></channel></rss>