[GH-ISSUE #13] Feature request: Add support for local resource records (DNS blackholes / overrides) #307

Closed
opened 2026-03-15 21:52:09 +03:00 by kerem · 7 comments
Owner

Originally created by @pwrdwnsys on GitHub (Jun 2, 2016).
Original GitHub issue: https://github.com/hickory-dns/hickory-dns/issues/13

It would be nice to have the ability to load lists of local resource records to override any upstream records, without having to specify a complete zone.

Our primary use case for this is being able to "blackhole" requests made to known malicious domains (e.g. malware C&C), or other domains that we wish to block (e.g. advertising content). We achieve this with Unbound by downloading and processing feeds from various threat intelligence sources (e.g. malwaredomains), and having the domains generated into local-data records which Unbound then resolves to 127.0.0.1. An example of local-data being used in this way can be seen here.

An advanced extension to this concept (which I believe is not currently achievable in Unbound) would be to match IP addresses (e.g. resolved from an A record lookup, or received from a client for PTR lookup) and block/replace the request as necessary.

Originally created by @pwrdwnsys on GitHub (Jun 2, 2016). Original GitHub issue: https://github.com/hickory-dns/hickory-dns/issues/13 It would be nice to have the ability to load lists of local resource records to override any upstream records, without having to specify a complete zone. Our primary use case for this is being able to "blackhole" requests made to known malicious domains (e.g. malware C&C), or other domains that we wish to block (e.g. advertising content). We achieve this with Unbound by downloading and processing feeds from various threat intelligence sources (e.g. [malwaredomains](http://mirror1.malwaredomains.com/files/domains.txt)), and having the domains generated into local-data records which Unbound then resolves to 127.0.0.1. An example of local-data being used in this way can be seen [here](https://www.bentasker.co.uk/documentation/linux/279-unbound-adding-custom-dns-records). An advanced extension to this concept (which I believe is not currently achievable in Unbound) would be to match IP addresses (e.g. resolved from an A record lookup, or received from a client for PTR lookup) and block/replace the request as necessary.
kerem 2026-03-15 21:52:09 +03:00
Author
Owner

@bluejekyll commented on GitHub (Jun 2, 2016):

I get the reasoning behind this, but I have some concerns. Specifically with DNSSec, masking a zone, i.e. your override upstream records request, seems like it would always fail DNSSec validation.

As to denying access via resolution seems perfectly reasonable.

To be clear I'm perfectly open to blocking zones, I'm less inclined to try and mask, because it goes against one thing that I want to do with this project which is DNSSec by default.

<!-- gh-comment-id:223354669 --> @bluejekyll commented on GitHub (Jun 2, 2016): I get the reasoning behind this, but I have some concerns. Specifically with DNSSec, masking a zone, i.e. your override upstream records request, seems like it would always fail DNSSec validation. As to denying access via resolution seems perfectly reasonable. To be clear I'm perfectly open to blocking zones, I'm less inclined to try and mask, because it goes against one thing that I want to do with this project which is DNSSec by default.
Author
Owner

@pwrdwnsys commented on GitHub (Jun 3, 2016):

I had a look at RFC 5074 and an article on DLV-based DNS blacklists, but it doesn't look like it would be a particularly elegant solution to this problem. Perhaps it would be simpler to just return a REFUSED response (or optionally log and silently drop the request) when a query is received for one of the listed domains, rather than attempt to interfere with signed records.

<!-- gh-comment-id:223465650 --> @pwrdwnsys commented on GitHub (Jun 3, 2016): I had a look at [RFC 5074](https://tools.ietf.org/html/rfc5074) and an article on [DLV-based DNS blacklists](https://www.bfk.de/notes/dlvbl/), but it doesn't look like it would be a particularly elegant solution to this problem. Perhaps it would be simpler to just return a REFUSED response (or optionally log and silently drop the request) when a query is received for one of the listed domains, rather than attempt to interfere with signed records.
Author
Owner

@balboah commented on GitHub (Feb 5, 2020):

@pwrdwnsys did you go ahead to try to implement something for blocking lists of domains in trust-dns? Any suggestions on where to start? I'm new to both rust and trust-dns :)
perhaps a modified ForwardAuthority which returns NoRecordsFound on Lookup()

<!-- gh-comment-id:582475016 --> @balboah commented on GitHub (Feb 5, 2020): @pwrdwnsys did you go ahead to try to implement something for blocking lists of domains in trust-dns? Any suggestions on where to start? I'm new to both rust and trust-dns :) perhaps a modified ForwardAuthority which returns NoRecordsFound on Lookup()
Author
Owner

@bluejekyll commented on GitHub (Feb 5, 2020):

@balboah I think this would be more possible today than it was before. This configuration file is for a forwarder:

https://github.com/bluejekyll/trust-dns/blob/master/tests/test-data/named_test_configs/example_forwarder.toml#L40-L41

I had an intention of allowing for chained resolvers, for example, you could have a Blackhole resolver type that would come before the Forwarder, something like:

stores = [{ type = "blackhole", zone_file = "zones/blackhole.zone" },
          { type = "forward", name_servers = [{ socket_addr = "8.8.8.8:53", protocol = "Udp" },
                                              { socket_addr = "8.8.8.8:53", protocol = "Tcp" }] }]

The stores configuration is defined here: https://github.com/bluejekyll/trust-dns/blob/master/crates/server/src/config/mod.rs#L173

Clearly that is just an option today. I think serde can allow a transparent node that would reference a new ChainedStore, that would end up being something like:

struct ChainedStore { authorities = Vec<dyn Authority> }

impl Authority for ChainedStore { ... }  

Which would be similar to a Catalog: https://github.com/bluejekyll/trust-dns/blob/master/crates/server/src/authority/catalog.rs#L43-L45

At least, this is the way that I've been thinking about how to implement this feature. I just haven't gotten back to doing it. An additional fun thing would be to also look into if we could sync this zone configuration around in some manner so that we could manage a distributed list people could subscribe to.

<!-- gh-comment-id:582520351 --> @bluejekyll commented on GitHub (Feb 5, 2020): @balboah I think this would be more possible today than it was before. This configuration file is for a forwarder: https://github.com/bluejekyll/trust-dns/blob/master/tests/test-data/named_test_configs/example_forwarder.toml#L40-L41 I had an intention of allowing for chained resolvers, for example, you could have a `Blackhole` resolver type that would come before the `Forwarder`, something like: ```toml stores = [{ type = "blackhole", zone_file = "zones/blackhole.zone" }, { type = "forward", name_servers = [{ socket_addr = "8.8.8.8:53", protocol = "Udp" }, { socket_addr = "8.8.8.8:53", protocol = "Tcp" }] }] ``` The stores configuration is defined here: https://github.com/bluejekyll/trust-dns/blob/master/crates/server/src/config/mod.rs#L173 Clearly that is just an option today. I think serde can allow a transparent node that would reference a new `ChainedStore`, that would end up being something like: ```rust struct ChainedStore { authorities = Vec<dyn Authority> } impl Authority for ChainedStore { ... } ``` Which would be similar to a Catalog: https://github.com/bluejekyll/trust-dns/blob/master/crates/server/src/authority/catalog.rs#L43-L45 At least, this is the way that I've been thinking about how to implement this feature. I just haven't gotten back to doing it. An additional fun thing would be to also look into if we could sync this zone configuration around in some manner so that we could manage a distributed list people could subscribe to.
Author
Owner

@balboah commented on GitHub (Feb 8, 2020):

@bluejekyll thanks for the pointers!
I'm very new to rust and I really struggle with that learning curve still and didn't want to modify your code. But I managed to implement a version of the "forwarder" authority that simply returns an error future from the lookup(). Chaining authorities sounds like a cleaner solution :)
However I'm thinking that I might also want to check for resolved CNAME's after the resolve as well as IP lists in the future.

My use case will be mostly in-memory blacklist or possibly try something with an Sqlite store.

Your rust coding style looks really clean, I'm eager to learn more about all this magic :) thanks for making this project!

<!-- gh-comment-id:583746434 --> @balboah commented on GitHub (Feb 8, 2020): @bluejekyll thanks for the pointers! I'm very new to rust and I really struggle with that learning curve still and didn't want to modify your code. But I managed to implement a version of the "forwarder" authority that simply returns an error future from the lookup(). Chaining authorities sounds like a cleaner solution :) However I'm thinking that I might also want to check for resolved CNAME's after the resolve as well as IP lists in the future. My use case will be mostly in-memory blacklist or possibly try something with an Sqlite store. Your rust coding style looks really clean, I'm eager to learn more about all this magic :) thanks for making this project!
Author
Owner

@ecclarke42 commented on GitHub (Dec 17, 2021):

@balboah, did you ever take a stab at this? If not, @bluejekyll, I'd love to try implementing it.

Coming from some Rust experience, but a little less DNS, the serde part is easy enough, but I'm not 100% sure what the Authority implementation would look like. The zone information should be common to each, so a naive pub struct ChainedAuthority(Arc<[Box<dyn AuthorityObject>]>); could just call those getters on the first child (or we could pull that info out into the struct). Should the lookup methods just call down the line until one returns without an error?

It might be easier to only implement AuthorityObject, since then we can just return a Box<dyn LookupObject> instead of dealing with the inner Lookup associated types.

<!-- gh-comment-id:996347558 --> @ecclarke42 commented on GitHub (Dec 17, 2021): @balboah, did you ever take a stab at this? If not, @bluejekyll, I'd love to try implementing it. Coming from some Rust experience, but a little less DNS, the `serde` part is easy enough, but I'm not 100% sure what the `Authority` implementation would look like. The zone information should be common to each, so a naive `pub struct ChainedAuthority(Arc<[Box<dyn AuthorityObject>]>);` could just call those getters on the first child (or we could pull that info out into the struct). Should the lookup methods just call down the line until one returns without an error? It might be easier to only implement `AuthorityObject`, since then we can just return a `Box<dyn LookupObject>` instead of dealing with the inner `Lookup` associated types.
Author
Owner

@marcus0x62 commented on GitHub (Dec 13, 2023):

I have a functioning implementation of the chained store and a new blocklist authority. I need to do a little bit of cleanup, but should have something ready to review within a few days... Are you still interested in including these features @bluejekyll and if so do you want to see separate PRs for the chained store and blocklist authority or just one with both?

<!-- gh-comment-id:1854668992 --> @marcus0x62 commented on GitHub (Dec 13, 2023): I have a functioning implementation of the chained store and a new blocklist authority. I need to do a little bit of cleanup, but should have something ready to review within a few days... Are you still interested in including these features @bluejekyll and if so do you want to see separate PRs for the chained store and blocklist authority or just one with both?
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/hickory-dns#307
No description provided.