{"id":24071029,"url":"https://github.com/vegertar/dnsrouter","last_synced_at":"2025-06-21T15:06:22.609Z","repository":{"id":77460333,"uuid":"115473770","full_name":"vegertar/dnsrouter","owner":"vegertar","description":"DnsRouter is a lightweight high performance DNS request router with chaining middlewares","archived":false,"fork":false,"pushed_at":"2018-02-06T01:33:02.000Z","size":90,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-21T15:06:16.501Z","etag":null,"topics":["dns","dnssec","go","middleware","mux","router"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vegertar.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-12-27T02:43:57.000Z","updated_at":"2023-03-30T09:24:07.000Z","dependencies_parsed_at":"2024-04-24T19:21:29.142Z","dependency_job_id":null,"html_url":"https://github.com/vegertar/dnsrouter","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/vegertar/dnsrouter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vegertar%2Fdnsrouter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vegertar%2Fdnsrouter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vegertar%2Fdnsrouter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vegertar%2Fdnsrouter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vegertar","download_url":"https://codeload.github.com/vegertar/dnsrouter/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vegertar%2Fdnsrouter/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261143143,"owners_count":23115672,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dns","dnssec","go","middleware","mux","router"],"created_at":"2025-01-09T16:24:48.577Z","updated_at":"2025-06-21T15:06:17.598Z","avatar_url":"https://github.com/vegertar.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://travis-ci.org/vegertar/dnsrouter.svg?branch=master)](https://travis-ci.org/vegertar/dnsrouter)\n[![codecov](https://codecov.io/gh/vegertar/dnsrouter/branch/master/graph/badge.svg)](https://codecov.io/gh/vegertar/dnsrouter)\n[![Go Report Card](https://goreportcard.com/badge/github.com/vegertar/dnsrouter)](https://goreportcard.com/report/github.com/vegertar/dnsrouter)\n[![GoDoc](https://godoc.org/github.com/vegertar/dnsrouter?status.svg)](https://godoc.org/github.com/vegertar/dnsrouter)\n\u003c!-- TOC --\u003e\n\n- [DnsRouter](#dnsrouter)\n    - [Features](#features)\n    - [Dependencies](#dependencies)\n    - [Install](#install)\n    - [Usage](#usage)\n        - [Named parameters \u0026 Catch-All parameters](#named-parameters--catch-all-parameters)\n    - [Benchmarks](#benchmarks)\n        - [Without DNSSEC](#without-dnssec)\n        - [With DNSSEC](#with-dnssec)\n    - [TODO](#todo)\n\n\u003c!-- /TOC --\u003e\n# DnsRouter\n\nDnsRouter is a lightweight high performance DNS request router with chaining middlewares.\n\nHighly inspired by julienschmidt's [HttpRouter](https://github.com/julienschmidt/httprouter) and miekg's [CoreDNS](https://github.com/coredns/coredns), actually, this router is developed upon HttpRouter's extremely fast radix tree, and passed all test cases of [file](https://github.com/coredns/coredns/blob/master/plugin/file) plugin from CoreDNS.\n\nIn contrast to CoreDNS which is a complete DNS server, DnsRouter is targeting a library of a tree of stub name servers, all other resolving functions like filling out `ANSWER`, `AUTHORITY`, `ADDITIONAL` sections are designed as middlewares, which makes name server efficient and flexible.\n\n## Features\n\n**Named parameters in routing patterns**: Directly inheriting parameterized patterns from [HttpRouter](https://github.com/julienschmidt/httprouter), includes both `:param` and `*catchAll`, anyone who confuses [wildcard DNS records](https://en.wikipedia.org/wiki/Wildcard_DNS_record) or used to conventional HTTP mux patterns would feel easy to use it.\n\n**Anonymous parameters in routing patterns**: Against *named parameters in routing patterns*, an anonymous asterisk in the beginning of domain patterns, e.g. `*.`, is interpreted in DNS wildcard semantics, as [RFC 4592](https://tools.ietf.org/html/rfc4592), which makes DnsRouter compatible with traditional DNS wildcard matching rules.\n\n**Multi-Zone in one tree**: A router instance could safely contain multiple zones simultaneously, the underlying radix tree promises the best performance if you have lots of records with lots of domains, or zones.\n\n**Nearly Zero Garbage**: As [HttpRouter](https://github.com/julienschmidt/httprouter), the tree related processes generate zero bytes of garbage, the actual up to 4 more heap allocations that are made, is from zone slice (1 alloc), domain name reversing (2 allocs), and a returning of an interface (1 alloc).\n\n**Out-of-box stub name server**: The builtin middlewares are organized into two schemes, `DefaultScheme` and `SimpleScheme`. Use these schemes make DnsRouter working as an out-of-box stub name server, i.e. looking up name records, following CNAME redirection, expanding wildcards, supplying DNSSEC RRset and recovering panic, etc. What the different of `DefaultScheme` and `SimpleScheme` is that the later doesn't filling out `AUTHORITY` and `ADDITIONAL` sections.\n\n**Chaining middlewares**: DnsRoute scales well by chaining middlewares, enjoyably choose what you need from builtin middlewares or implement your owns to extend stub name server, e.g. a recursive resolver or cache.\n\n**Fast**: [Benchmarks](#benchmarks) show DnsRouter is **2x to 4x** faster than [file](https://github.com/coredns/coredns/blob/master/plugin/file) plugin of CoreDNS.\n\n## Dependencies\n\nGolang 1.9.x and miekg's awesome [DNS library](https://github.com/miekg/dns).\n\n## Install\n\nUsing the default `go tool` commands:\n\n```bash\ngo get -v github.com/vegertar/dnsrouter\n```\n\n## Usage\n\nLet's start with a trivial example:\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\n\t\"github.com/miekg/dns\"\n\t\"github.com/vegertar/dnsrouter\"\n)\n\nfunc main() {\n\trouter := dnsrouter.New()\n\trouter.HandleFunc(\"local A\", func(w dnsrouter.ResponseWriter, req *dnsrouter.Request) {\n\t\tlo, err := dns.NewRR(\"local A 127.0.0.1\")\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\tresult := w.Msg()\n\t\tresult.Answer = append(result.Answer, lo)\n\t})\n\n\terr := dns.ListenAndServe(\":10053\", \"udp\", dnsrouter.Classic(context.Background(), router))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n```\n\nTests with `dig` command and omits unnecessary output, the sample print is shown below.\n\n```bash\n$ dig @127.0.0.1 -p 10053 local\n\n;; ANSWER SECTION:\nlocal.                  3600    IN      A       127.0.0.1\n```\n\nThen adds a SRV record.\n\n```go\n\tsrv := new(dns.SRV)\n\tsrv.Hdr.Name = \"_dns._udp.\"\n\tsrv.Hdr.Rrtype = dns.TypeSRV\n\tsrv.Hdr.Class = dns.ClassINET\n\tsrv.Port = 10053\n\tsrv.Target = \"local.\"\n\n\trouter.HandleFunc(\"local. SRV\", func(w dnsrouter.ResponseWriter, req *dnsrouter.Request) {\n\t\tresult := w.Msg()\n\t\tresult.Answer = append(result.Answer, srv)\n\t})\n```\n\n`dig` with `SRV` could display the service, with additional A record that we set before as well.\n\n```bash\n$ dig @127.0.0.1 -p 10053 local SRV\n\n;; ANSWER SECTION:\n_dns._udp.              0       IN      SRV     0 0 10053 local.\n\n;; ADDITIONAL SECTION:\nlocal.                  3600    IN      A       127.0.0.1\n```\n\nAlternatively, you could try `dig` with `ANY` type.\n\n```bash\n$ dig @127.0.0.1 -p 10053 local ANY\n\n;; ANSWER SECTION:\nlocal.                  3600    IN      A       127.0.0.1\n_dns._udp.              0       IN      SRV     0 0 10053 local.\n```\n\nWants to known what happens when raises an exception? Adds some lines like below.\n\n```go\n\trouter.HandleFunc(\"local. SRV\", func(w dnsrouter.ResponseWriter, req *dnsrouter.Request) {\n\t\tpanic(\"oops: an exception\")\n\t})\n```\n\n`dig` with `SRV` option again.\n\n```bash\n$ dig @127.0.0.1 -p 10053 local. SRV\n\n;; ANSWER SECTION:\n_dns._udp.              0       IN      SRV     0 0 10053 local.\n\n;; ADDITIONAL SECTION:\nlocal.                  0       IN      TXT     \"panic\" \"oops: an exception\" \"main.main.func3:35\"\n```\n\nThis time the ADDITIONAL section contains a TXT record instead, which describes errors in shortly, includes a flag literal string \"panic\", an error message, and the trace information.\n\nAll above records are writing out by builtin middlewares, but there is no logging middleware to log every incoming DNS queries, let's implement a simple logger in here.\n\n```go\nfunc LoggerHandler(h dnsrouter.Handler) dnsrouter.Handler {\n\treturn dnsrouter.HandlerFunc(func(w dnsrouter.ResponseWriter, req *dnsrouter.Request) {\n\t\tsince := time.Now()\n\t\tq := req.Question[0]\n\n\t\tdefer func() {\n\t\t\tlog.Printf(`\"%s %s %s\" %s %v`,\n\t\t\t\tdns.TypeToString[q.Qtype],\n\t\t\t\tdns.ClassToString[q.Qclass],\n\t\t\t\tq.Name,\n\t\t\t\tdns.RcodeToString[w.Msg().Rcode],\n\t\t\t\ttime.Since(since).String())\n\t\t}()\n\n\t\th.ServeDNS(w, req)\n\t})\n}\n```\n\nThen insert the `LoggerHandler` into chains.\n\n```go\n\trouter.Middleware = append(router.Middleware, LoggerHandler)\n\trouter.Middleware = append(router.Middleware, dnsrouter.DefaultScheme...)\n```\n\nRunning and testing again.\n\n```bash\n$ go run a.go\n2018/02/05 15:57:01 \"SRV IN local.\" SERVFAIL 46.248µs\n2018/02/05 15:57:07 \"A IN local.\" NOERROR 139.3µs\n2018/02/05 15:57:10 \"ANY IN local.\" SERVFAIL 204.684µs\n2018/02/05 15:58:41 \"A IN hello.\" REFUSED 34.333µs\n```\n\n### Named parameters \u0026 Catch-All parameters\n\nThese features are derived from [HttpRouter](https://github.com/julienschmidt/httprouter), the only difference is that DnsRouter uses dot ('.') as the label separator, and matches from right to left.\n\n```bash\nPattern: :user.example.org.\n\njoe.example.org                 match, captures \"joe\"\nlily.example.org                match, captures \"lily\"\nzuck.mark.example.org           no match\nexample.org.                    no match\n```\n\n```bash\nPattern: *user.example.org.\n\njoe.example.org                 match, captures \"joe.\"\nlily.example.org                match, captures \"lily.\"\nzuck.mark.example.org           match, captures \"zuck.mark.\"\nexample.org.                    no match\n.example.org.                   match, captures \".\", but an illegal domain\n```\n\n## Benchmarks\n\nThe testing environment is running on Ubuntu-16.04-amd64 with i7-7700HQ CPU @ 2.80GHz. Since all test cases are completely copied from `file` plugin of CoreDNS, so the bench codes are the same as well.\n\n### Without DNSSEC\n\nFor `DnsRouter`:\n```bash\n$ go test -v -benchmem -run=^$ github.com/vegertar/dnsrouter -bench ^BenchmarkLookup$\n\ngoos: linux\ngoarch: amd64\npkg: github.com/vegertar/dnsrouter\nBenchmarkLookup/DefaultScheme-8         \t  300000\t      5842 ns/op\t    3264 B/op\t      57 allocs/op\nBenchmarkLookup/SimpleScheme-8          \t  500000\t      2824 ns/op\t    1632 B/op\t      29 allocs/op\nPASS\nok  \tgithub.com/vegertar/dnsrouter\t3.254s\nSuccess: Benchmarks passed.\n```\n\nFor `file` plugin of CoreDNS:\n```bash\n$ go test -benchmem -run=^$ github.com/coredns/coredns/plugin/file -bench ^BenchmarkFileLookup$\n\ngoos: linux\ngoarch: amd64\npkg: github.com/coredns/coredns/plugin/file\nBenchmarkFileLookup-8   \t  100000\t     14265 ns/op\t    6243 B/op\t      99 allocs/op\nPASS\nok  \tgithub.com/coredns/coredns/plugin/file\t1.591s\nSuccess: Benchmarks passed.\n```\n\n### With DNSSEC\n\nFor `DnsRouter`:\n```bash\n$ go test -v -benchmem -run=^$ github.com/vegertar/dnsrouter -bench ^BenchmarkLookupDNSSEC$\n\ngoos: linux\ngoarch: amd64\npkg: github.com/vegertar/dnsrouter\nBenchmarkLookupDNSSEC-8   \t  300000\t      5605 ns/op\t    3312 B/op\t      56 allocs/op\nPASS\nok  \tgithub.com/vegertar/dnsrouter\t1.747s\nSuccess: Benchmarks passed.\n```\n\nFor `file` plugin of CoreDNS:\n```bash\n$ go test -benchmem -run=^$ github.com/coredns/coredns/plugin/file -bench ^BenchmarkFileLookupDNSSEC$\n\ngoos: linux\ngoarch: amd64\npkg: github.com/coredns/coredns/plugin/file\nBenchmarkFileLookupDNSSEC-8   \t  100000\t     21723 ns/op\t    9163 B/op\t     232 allocs/op\nPASS\nok  \tgithub.com/coredns/coredns/plugin/file\t2.419s\nSuccess: Benchmarks passed.\n```\n\n## TODO\n\nThere are some works in planed:\n\n- NSEC3\n- 100% code coverage\n- Better examples and docs.\n\nAny contributions are welcome.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvegertar%2Fdnsrouter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvegertar%2Fdnsrouter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvegertar%2Fdnsrouter/lists"}