preload cachedb redis backend using python

Bino Oetomo wowon01 at gmail.com
Wed Sep 10 10:30:15 UTC 2025


dear all

I use cachedb with redis backend.

try to preload redis using python.

```
import dns.message
import dns.rcode
import dns.flags
import struct
import time
import redis

def make_minimal_nxdomain(domain: str) -> bytes:
    q = dns.message.make_query(domain, 'A')
    r = dns.message.make_response(q)
    r.set_rcode(dns.rcode.NXDOMAIN)
    r.flags |= dns.flags.QR
    r.flags |= dns.flags.AA
    r.flags &= ~dns.flags.RD  # Clear RD
    r.flags &= ~dns.flags.RA  # Clear RA
    r.answer = []
    wire = r.to_wire()

    now = int(time.time())
    wire += struct.pack('>QQ', now, now + 31536000)  # 1 year
    return wire

def make_key(domain: str, qtype: int = 1, qclass: int = 1, secret_seed: str
= "mysecretseed2025") -> str:
    """
    Generate Redis key using SHA256(secret_seed + wire_name + qtype +
qclass)
    """
    import hashlib
    import struct

    if not domain.endswith('.'):
        domain += '.'
    labels = domain.rstrip('.').split('.')
    wire = b''.join(bytes([len(label)]) + label.encode() for label in
labels) + b'\x00'
    qtype_n = struct.pack('!H', qtype)
    qclass_n = struct.pack('!H', qclass)
    data = wire + qtype_n + qclass_n + secret_seed.encode()
    return hashlib.sha256(data).hexdigest().upper()

if __name__ == '__main__':
    # Test domain
    domain = "tulisan.nggonku.xyz"

    # Generate NXDOMAIN response
    response_wire = make_minimal_nxdomain(domain)
    print(f"Generated NXDOMAIN response for {domain}, {len(response_wire)}
bytes")

    # Generate Redis key
    key = make_key(domain)
    print(f"Redis key: {key}")

    # Connect to Redis
    r = redis.Redis(host='127.0.0.1', port=6379, db=0,
decode_responses=False)

    # Set in Redis
    r.set(key, response_wire)
    print(f"Loaded into Redis: {key}")

    # Optional: Verify
    print("\nVerifying from Redis...")
    retrieved = r.get(key)
    if retrieved:
        print(f"Retrieved {len(retrieved)} bytes")
        timestamp, expiry = struct.unpack('>QQ', retrieved[-16:])
        print(f"Timestamp: {time.ctime(timestamp)}")
        print(f"Expiry:    {time.ctime(expiry)}")
    else:
        print("Not found in Redis")
```

When I dig for my domain name (dig @127.0.0.1 tulisan.nggonku.xyz) , I got
this from unbound debug

```
Sep 10 17:22:05 unbound unbound[25748]: [25748:0] info: 127.0.0.1
tulisan.nggonku.xyz. A IN
Sep 10 17:22:05 unbound unbound[25748]: [25748:0] debug: udp request from
ip4 127.0.0.1 port 41514 (len 16)
Sep 10 17:22:05 unbound unbound[25748]: [25748:0] debug: mesh_run: start
Sep 10 17:22:05 unbound unbound[25748]: [25748:0] debug: cachedb[module 0]
operate: extstate:module_state_initial event:module_event_new
Sep 10 17:22:05 unbound unbound[25748]: [25748:0] debug: redis_lookup of
BE0C12BF95D4BA92F06B0F3291977F068DE61AD2161DB013AAC4A6B999877A02
Sep 10 17:22:05 unbound unbound[25748]: [25748:0] debug: redis_lookup found
53 bytes
Sep 10 17:22:05 unbound unbound[25748]: [25748:0] debug: cachedb msg expired
Sep 10 17:22:05 unbound unbound[25748]: [25748:0] debug: cachedb msg
adjusted down by -1
Sep 10 17:22:05 unbound unbound[25748]: [25748:0] info: redis ;;
->>HEADER<<- opcode: QUERY, rcode: NXDOMAIN, id: 0
                                        ;; flags: qr aa ; QUERY: 1, ANSWER:
0, AUTHORITY: 0, ADDITIONAL: 0
                                        ;; QUESTION SECTION:
                                        tulisan.nggonku.xyz.        IN
   A

                                        ;; ANSWER SECTION:

                                        ;; AUTHORITY SECTION:

                                        ;; ADDITIONAL SECTION:
                                        ;; MSG SIZE  rcvd: 37
Sep 10 17:22:05 unbound unbound[25748]: [25748:0] debug: mesh_run: cachedb
module exit state is module_wait_module
Sep 10 17:22:05 unbound unbound[25748]: [25748:0] debug: validator[module
1] operate: extstate:module_state_initial event:module_event_pass
Sep 10 17:22:05 unbound unbound[25748]: [25748:0] info: validator operate:
query tulisan.nggonku.xyz. A IN
Sep 10 17:22:05 unbound unbound[25748]: [25748:0] debug: validator: pass to
next module
Sep 10 17:22:05 unbound unbound[25748]: [25748:0] debug: mesh_run:
validator module exit state is module_wait_module
Sep 10 17:22:05 unbound unbound[25748]: [25748:0] debug: iterator[module 2]
operate: extstate:module_state_initial event:module_event_pass
Sep 10 17:22:05 unbound unbound[25748]: [25748:0] debug: process_request:
new external request event
Sep 10 17:22:05 unbound unbound[25748]: [25748:0] debug: iter_handle
processing q with state INIT REQUEST STATE
Sep 10 17:22:05 unbound unbound[25748]: [25748:0] info: resolving
tulisan.nggonku.xyz. A IN

```

Looks like the message "content"  is right, but timestamp/ttl/expiry is
wrong.
How to write good wire message?

-wowon-
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.nlnetlabs.nl/pipermail/unbound-users/attachments/20250910/cab70ab3/attachment-0001.htm>


More information about the Unbound-users mailing list