Unbound (DNS Resolver) and Python module How-to



  • Hi, want to share how Python module can be used with Undound.
    Unbound support only one python module at once.
    In my case I was need 2 tasks in one module:

    • return loopback (IPv6 ::1 and IPv4 127.0.0.1) for some TLDs
    • remove AAAA (IPv6) records from resolving for some domains (and all their subdomains) to workaround issues related to their broken realization of IPv6 stack

    So I googled for some examples at Internet and with basic knowledge of python I ended in with this:

    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")
    

    If you know how exchange it - critics are welcome.

    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:
    File: /var/unbound/mod.py
    Description: Unbound python module
    Permissions: 644
    File Contents: python script above, modify domains list to your needs, and not forget . at end of each domain
    Script/Command: unbound-control -c /var/unbound/unbound.conf reload
    Execute mode: Background (default)

    Another examples can be found at https://github.com/NLnetLabs/unbound/tree/master/pythonmod/examples


Log in to reply