Skip to content

Panic when CNAME points to external domains #1867

@Renerick

Description

@Renerick

When CNAME record in the custom zone file points to external domains, blocky panics and returns no respone

Custom DNS config (it is provisioned by a script, so formatting is not "human")

customDNS:
  customTTL: 30m
  rewrite:
    [REDACTED]: internal
  zone: '$ORIGIN internal.

    a 3600 A 1.1.1.1

    c 3600 CNAME example.com.


    '

Testing

$ dig a.internal @10.10.10.10

; <<>> DiG 9.18.39 <<>> a.internal @10.10.10.10
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 7011
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;a.internal.                    IN      A

;; ANSWER SECTION:
a.internal.             3600    IN      A       1.1.1.1

;; Query time: 1 msec
;; SERVER: 10.10.10.10#53(10.10.10.10) (UDP)
;; WHEN: Tue Sep 16 01:55:54 +04 2025
;; MSG SIZE  rcvd: 44

$ dig c.internal @10.10.10.10

; <<>> DiG 9.18.39 <<>> c.internal @10.10.10.10
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 573
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;c.internal.                    IN      A

;; Query time: 1 msec
;; SERVER: 10.10.10.10#53(10.10.10.10) (UDP)
;; WHEN: Tue Sep 16 01:55:57 +04 2025
;; MSG SIZE  rcvd: 28

Logs:

Sep 15 21:55:54 dns-1 blocky[3760]: [2025-09-15 21:55:54] TRACE new incoming request client_id= client_ip=10.10.0.1 client_request_id=7011 protocol=UDP question=A (a.internal.) req_id=09894299-7520-4f9c-9fd2-f01c87012c99
Sep 15 21:55:54 dns-1 blocky[3760]: [2025-09-15 21:55:54] TRACE query_logging.rewrite: go to inner resolver client_ip=10.10.0.1 client_names=10.10.0.1 question=A (a.internal.) req_id=09894299-7520-4f9c-9fd2-f01c87012c99 resolver=custom_dns
Sep 15 21:55:54 dns-1 blocky[3760]: [2025-09-15 21:55:54] DEBUG query_logging.rewrite.custom_dns: returning custom dns entry answer=A (1.1.1.1) client_ip=10.10.0.1 client_names=10.10.0.1 domain=a.internal question=A (a.internal.) req_id=09894299-7520-4f9c-9fd2-f01c87012c99
Sep 15 21:55:54 dns-1 blocky[3760]: [2025-09-15 21:55:54]  INFO queryLog: query resolved answer=A (1.1.1.1) client_ip=10.10.0.1 client_names=10.10.0.1 instance=dns-1 question_name=a.internal. question_type=A response_code=NOERROR response_reason=CUSTOM DNS response_type=CUSTOMDNS
Sep 15 21:55:57 dns-1 blocky[3760]: [2025-09-15 21:55:57] TRACE new incoming request client_id= client_ip=10.10.0.1 client_request_id=573 protocol=UDP question=A (c.internal.) req_id=c49e209b-626a-4c70-ba22-53fb93ac2979
Sep 15 21:55:57 dns-1 blocky[3760]: [2025-09-15 21:55:57] TRACE query_logging.rewrite: go to inner resolver client_ip=10.10.0.1 client_names=10.10.0.1 question=A (c.internal.) req_id=c49e209b-626a-4c70-ba22-53fb93ac2979 resolver=custom_dns
Sep 15 21:55:57 dns-1 blocky[3760]: [2025-09-15 21:55:57] TRACE query_logging.rewrite.custom_dns: go to next resolver client_ip=10.10.0.1 client_names=10.10.0.1 next_resolver=noop question=A (c.internal.) req_id=c49e209b-626a-4c70-ba22-53fb93ac2979
Sep 15 21:55:57 dns-1 blocky[3760]: [2025-09-15 21:55:57] ERROR error on processing request:panic occurred: runtime error: invalid memory address or nil pointer dereference client_ip=10.10.0.1 question=A (c.internal.) req_id=c49e209b-626a-4c70-ba22-53fb93ac2979

I have looked through the code, it seems the culprit is

// resolve the target recursively
targetResp, err := r.processRequest(ctx, logger, targetRequest, cnames)
if err != nil {
return nil, err
}
result = append(result, targetResp.Res.Answer...)

which calls to this (because external domain is not specified in the zone file)

logger.WithField("next_resolver", Name(r.next)).Trace("go to next resolver")

but next is noop, as initialized in here

resolver.NewRewriterResolver(cfg.CustomDNS.RewriterConfig, resolver.NewCustomDNSResolver(cfg.CustomDNS)),

func NewRewriterResolver(cfg config.RewriterConfig, inner ChainedResolver) ChainedResolver {
if len(cfg.Rewrite) == 0 {
return inner
}
// ensures that the rewrites map contains all rewrites in lower case
for k, v := range cfg.Rewrite {
cfg.Rewrite[strings.ToLower(k)] = strings.ToLower(v)
}
inner.Next(NewNoOpResolver())
return &RewriterResolver{
configurable: withConfig(&cfg),
typed: withType("rewrite"),
inner: inner,
}
}

which returns nil in the response somewhere I think, so when CNAME handler tries to access it and append it to result slice, it panics.

I'm not sure what behavior was intended here, so I'm couldn't make a PR to fix it myself. I stumbled upon this bug when trying to implement CNAMES to my router DNS, populated from DHCP. I was hoping the CNAME resolution would go through my conditional config, which points unqualified DNS requests to the router and so I would have DHCP+DNS service discovery, which would allow me to setup something like

conditional:
  mapping:
    .: 10.0.0.1 # router with DNS enabled
application 3600 CNAME reverse-proxy. ;; this is the hostname of the proxy VM, registered by DHCP and resolvable by router's DNS

I'm more than willing to take upon fixing this, but I would like to hear maintainer's opinion on this case

Thank you for blocky btw, aside from this edge case it has been rock solid local DNS solution

Metadata

Metadata

Assignees

No one assigned

    Labels

    🐞 bugSomething isn't working

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions