6
84
Fork
You've already forked dns
39

Documentation suggestions #645

Closed
opened 2026年01月27日 23:18:17 +01:00 by TomOnTime · 9 comments
Contributor
Copy link

As I migrate dnscontrol to dns v2, I have some suggestions that would be useful for https://codeberg.org/miekg/dns/src/branch/main/README-diff-with-v1.md

I can turn these into a PR if you wish.

General tips:

  1. Before you convert to dns v2, convert from net.IP to netip.Addr. DNS v2 uses netip.Addr to represent IP addresses. It is easier to only do one conversion at a time.

  2. Rename the old code "dnsv1".

Change all imports from

 "github.com/miekg/dns"

to

 dnsv1 "github.com/miekg/dns"

Now v1 and v2 can co-exist. dnsv1.RR is the old code and dns.RR is the new code.

This is not required, but it will make things easier. In fact, first I made a release that changed nothing but dns to dnsv1. Once that worked, it was easy to search for dnsv1 and convert things one at a time.

VSCode makes this easy. Find a user of dns and use "Rename Symbol" (i.e. F2). For example, I found dns.IP, moved the cursor
to the "d" in "dns", and pressed F2. Renamed dns to dnsv1. This changed the import and all uses of that package. However you need to do this once for each file.

You can do something similar for dnsutil (dnsutilv1) and so on.

Again, this isn't required. I just found it useful.

  1. Make a temporary conversion function

I wanted v1 and v2 to co-exist so that I could work and test incrementally. To make that easy I created a way to convert RR from v1 to v2 and back. The functions are slow and ugly, but accuracy is more important. I actually like that the functions are slow because it gives me incentive to finish porting to v2.

Here are the functions I am using:

package dnsrr
import (
 dnsv2 "codeberg.org/miekg/dns"
 dnsv1 "github.com/miekg/dns"
)
// RRv1tov2 converts github.com/miekg/dns (v1) RR to codeberg.org/miekg/dns (v2) RR.
// Typically used in providers that still use v1.
// Note: this function is not efficient. It converts via string representation.
// Use it until you are able to convert to v2 fully.
// Note: Panics on error.
func RRv1tov2(rrv1 dnsv1.RR) dnsv2.RR {
 rrv2, err := dnsv2.New(rrv1.String())
 if err != nil {
 panic("Failed to convert RR to v2: " + err.Error())
 }
 return rrv2
}
// RRv2tov1 converts codeberg.org/miekg/dns (v2) RR to github.com/miekg/dns (v1) RR.
// Typically used in providers that still use v1.
// Note: this function is not efficient. It converts via string representation.
// Use it until you are able to convert to v1 fully.
// Note: Panics on error.
func RRv2tov1(rrv2 dnsv2.RR) dnsv1.RR {
 rrv1, err := dnsv1.NewRR(rrv2.String())
 if err != nil {
 panic("Failed to convert RR to v1: " + err.Error())
 }
 return rrv1
}

Converting idioms

Some of these took a while to figure out. Hopefully this will save time for others:

What type is this RR? (and convert to a string)

OLD:

header := rr.Header()
rrtype := header.Rrtype
typeStr := dns.TypeToString[header.Rrtype]

NEW:

rrtype := dns.RRToType(rr)
rrtypeStr := dns.TypeToString[rrtype]

Canonicalize a DNS name:

OLD:

 id := dns.CanonicalName(foo)

NEW:

 id := dnsutil.Canonical(foo)

Find the TTL

OLD:

 rr.Header().Ttl

NEW:

 rr.Header().TTL

Allow includes

OLD:

 zp := dns.NewZoneParser(...)
 zp.SetIncludeAllowed(true)

NEW:

 zp := dns.NewZoneParser(...)
 (nothing. that's the default)

Don't allow includes:

OLD:

 zp := dns.NewZoneParser(...)
 zp.SetIncludeAllowed(false)

NEW:

 zp := dns.NewZoneParser(...)
 zp.IncludeAllowFunc = func() bool { return false }

Useful idioms

These aren't for the conversion guide. These are for the general documentation.

Create a RR of a specific record type:

 rr := dns.TypeToRR[rdtype]()

Update the header of an RR:

 *(rr.Header()) = dns.Header{Name: rc.NameFQDN + ".", Class: dns.ClassINET}
or
 hdr := rr.Header()
 *hdr = dns.Header{Name: rc.NameFQDN + ".", Class: dns.ClassINET}
or
 rr.Header().Name = "example.com."
 rr.Header().Class = dns.ClassINET

By the way... the example in https://pkg.go.dev/codeberg.org/miekg/dns

r := &MX{Header{Name:"miek.nl.", Class: dns.ClassINET, TTL: 3600}, MX: rdata.MX{Preference: 10, Mx: "mx.miek.nl."}}

it might be easier for newcomers if &MX was written &dns.MX

r := &dns.MX{Header{Name:"miek.nl.", Class: dns.ClassINET, TTL: 3600}, MX: rdata.MX{Preference: 10, Mx: "mx.miek.nl."}}

Hope that helps!

As I migrate dnscontrol to dns v2, I have some suggestions that would be useful for https://codeberg.org/miekg/dns/src/branch/main/README-diff-with-v1.md I can turn these into a PR if you wish. ## General tips: 1. Before you convert to dns v2, convert from net.IP to netip.Addr. DNS v2 uses netip.Addr to represent IP addresses. It is easier to only do one conversion at a time. 2. Rename the old code "dnsv1". Change all imports from "github.com/miekg/dns" to dnsv1 "github.com/miekg/dns" Now v1 and v2 can co-exist. dnsv1.RR is the old code and dns.RR is the new code. This is not required, but it will make things easier. In fact, first I made a release that changed nothing but dns to dnsv1. Once that worked, it was easy to search for dnsv1 and convert things one at a time. VSCode makes this easy. Find a user of dns and use "Rename Symbol" (i.e. F2). For example, I found dns.IP, moved the cursor to the "d" in "dns", and pressed F2. Renamed dns to dnsv1. This changed the import and all uses of that package. However you need to do this once for each file. You can do something similar for dnsutil (dnsutilv1) and so on. Again, this isn't required. I just found it useful. 2. Make a temporary conversion function I wanted v1 and v2 to co-exist so that I could work and test incrementally. To make that easy I created a way to convert RR from v1 to v2 and back. The functions are slow and ugly, but accuracy is more important. I actually like that the functions are slow because it gives me incentive to finish porting to v2. Here are the functions I am using: ``` package dnsrr import ( dnsv2 "codeberg.org/miekg/dns" dnsv1 "github.com/miekg/dns" ) // RRv1tov2 converts github.com/miekg/dns (v1) RR to codeberg.org/miekg/dns (v2) RR. // Typically used in providers that still use v1. // Note: this function is not efficient. It converts via string representation. // Use it until you are able to convert to v2 fully. // Note: Panics on error. func RRv1tov2(rrv1 dnsv1.RR) dnsv2.RR { rrv2, err := dnsv2.New(rrv1.String()) if err != nil { panic("Failed to convert RR to v2: " + err.Error()) } return rrv2 } // RRv2tov1 converts codeberg.org/miekg/dns (v2) RR to github.com/miekg/dns (v1) RR. // Typically used in providers that still use v1. // Note: this function is not efficient. It converts via string representation. // Use it until you are able to convert to v1 fully. // Note: Panics on error. func RRv2tov1(rrv2 dnsv2.RR) dnsv1.RR { rrv1, err := dnsv1.NewRR(rrv2.String()) if err != nil { panic("Failed to convert RR to v1: " + err.Error()) } return rrv1 } ``` ## Converting idioms Some of these took a while to figure out. Hopefully this will save time for others: ### What type is this RR? (and convert to a string) OLD: header := rr.Header() rrtype := header.Rrtype typeStr := dns.TypeToString[header.Rrtype] NEW: rrtype := dns.RRToType(rr) rrtypeStr := dns.TypeToString[rrtype] ### Canonicalize a DNS name: OLD: id := dns.CanonicalName(foo) NEW: id := dnsutil.Canonical(foo) ### Find the TTL OLD: rr.Header().Ttl NEW: rr.Header().TTL ### Allow includes OLD: zp := dns.NewZoneParser(...) zp.SetIncludeAllowed(true) NEW: zp := dns.NewZoneParser(...) (nothing. that's the default) ### Don't allow includes: OLD: zp := dns.NewZoneParser(...) zp.SetIncludeAllowed(false) NEW: zp := dns.NewZoneParser(...) zp.IncludeAllowFunc = func() bool { return false } ## Useful idioms These aren't for the conversion guide. These are for the general documentation. Create a RR of a specific record type: rr := dns.TypeToRR[rdtype]() Update the header of an RR: *(rr.Header()) = dns.Header{Name: rc.NameFQDN + ".", Class: dns.ClassINET} or hdr := rr.Header() *hdr = dns.Header{Name: rc.NameFQDN + ".", Class: dns.ClassINET} or rr.Header().Name = "example.com." rr.Header().Class = dns.ClassINET By the way... the example in https://pkg.go.dev/codeberg.org/miekg/dns r := &MX{Header{Name:"miek.nl.", Class: dns.ClassINET, TTL: 3600}, MX: rdata.MX{Preference: 10, Mx: "mx.miek.nl."}} it might be easier for newcomers if &MX was written &dns.MX r := &dns.MX{Header{Name:"miek.nl.", Class: dns.ClassINET, TTL: 3600}, MX: rdata.MX{Preference: 10, Mx: "mx.miek.nl."}} Hope that helps!
Owner
Copy link

Looks good. PR sounds awesome. I have some very minor nitpicking, but can always do this after a merge.

Thanks!

Looks good. PR sounds awesome. I have some very minor nitpicking, but can always do this after a merge. Thanks!
Owner
Copy link

I'll add the converting idioms.

I'll add the converting idioms.
miekg referenced this issue from a commit 2026年01月28日 14:12:28 +01:00
Author
Contributor
Copy link

One thing that would make it easier to adopt dns is if there was a "cookbook" page that lists common idioms. I'd be glad to contribute to it. If you agree, let me know where to create the file and I make a PR (or if you create the empty page, I'll start filling it up)

One thing that would make it easier to adopt dns is if there was a "cookbook" page that lists common idioms. I'd be glad to contribute to it. If you agree, let me know where to create the file and I make a PR (or if you create the empty page, I'll start filling it up)
Owner
Copy link

Go ahead. We can do a wiki, although I hijacked that for atomdns (while it is still in this repo)

Go ahead. We can do a wiki, although I hijacked that for atomdns (while it is still in this repo)
Owner
Copy link

The whole new rdata parsing might also need some docs

The whole new rdata parsing might also need some docs
Contributor
Copy link

If I may chime in, I'd like to see documentation to spell out how to reuse dns.Msg:

  • For the lib: whether I can reuse the message struct and its contents (raw data and/or RRs) after writing
  • For atomdns (which I did not explore just yet, but if it's anything alike CoreDNS): whether handlers can hold onto message contents after it's passed downstream or fetched via upstream and whether contents' lifetime can be extended beyond handler's return
  • Examples of hijacking
If I may chime in, I'd like to see documentation to spell out how to reuse dns.Msg: - For the lib: whether I can reuse the message struct and its contents (raw data and/or RRs) after writing - For atomdns (which I did not explore just yet, but if it's anything alike CoreDNS): whether handlers can hold onto message contents after it's passed downstream or fetched via upstream and whether contents' lifetime can be extended beyond handler's return - Examples of [hijacking](https://codeberg.org/miekg/dns/src/commit/558f266fcee19d9572f6a856c0488c3236a339f6/README-diff-with-v1.md?display=source#L39-L41)
Owner
Copy link

@Kentzo wrote in #645 (comment):

If I may chime in, I'd like to see documentation to spell out how to reuse dns.Msg:

* For the lib: whether I can reuse the message struct and its contents (raw data and/or RRs) after writing
* For atomdns (which I did not explore just yet, but if it's anything alike CoreDNS): whether handlers can hold onto message contents after it's passed downstream or fetched via upstream and whether contents' lifetime can be extended beyond handler's return
* Examples of [hijacking](https://codeberg.org/miekg/dns/src/commit/558f266fcee19d9572f6a856c0488c3236a339f6/README-diff-with-v1.md?display=source#L39-L41)
  1. Good point
  2. Guess no?
  3. Yes, should be added
@Kentzo wrote in https://codeberg.org/miekg/dns/issues/645#issuecomment-10252554: > If I may chime in, I'd like to see documentation to spell out how to reuse dns.Msg: > > * For the lib: whether I can reuse the message struct and its contents (raw data and/or RRs) after writing > > * For atomdns (which I did not explore just yet, but if it's anything alike CoreDNS): whether handlers can hold onto message contents after it's passed downstream or fetched via upstream and whether contents' lifetime can be extended beyond handler's return > > * Examples of [hijacking](https://codeberg.org/miekg/dns/src/commit/558f266fcee19d9572f6a856c0488c3236a339f6/README-diff-with-v1.md?display=source#L39-L41) 1. Good point 2. Guess no? 3. Yes, should be added
Contributor
Copy link

[2] at least with CoreDNS, while it's not spelled out, my understanding is that:

  • If unhandled and processing is continued downstream: cannot reuse
  • If received via upstream lookup: yes for readonly since handler's ServeDNS is called concurrently and therefore data it returns is either a unique copy or logically immutable (although holding onto it may inadvertently extend lifetime of a much larger data structure / backing array)

Perhaps touch these topics as guidance for handlers developers and document it for builtin ones?

[2] at least with CoreDNS, while it's not spelled out, my understanding is that: - If unhandled and processing is continued downstream: cannot reuse - If received via upstream lookup: yes for readonly since handler's ServeDNS is called concurrently and therefore data it returns is either a unique copy or logically immutable (although holding onto it may inadvertently extend lifetime of a much larger data structure / backing array) Perhaps touch these topics as guidance for handlers developers and document it for builtin ones?
Owner
Copy link

For 1. normal Go rules apply and WriteTo explains what happens to the buffer, for 2. too.

WriteTo explains what happens when hijacking, but this could be copied/referenced as well.

The aboves docs have found there why into godoc or the _doc directory. Also cleaned up doc.go which also had non-compiling examples, hence having Example() test functions.

For 1. normal Go rules apply and WriteTo explains what happens to the buffer, for 2. too. WriteTo explains what happens when hijacking, but this could be copied/referenced as well. The aboves docs have found there why into godoc or the _doc directory. Also cleaned up doc.go which also had non-compiling examples, hence having Example() test functions.
Sign in to join this conversation.
No Branch/Tag specified
main
miek/26/jul13ma/06
miek/26/mrt24di/tls
miek/26/mrt09ma/17
miek/26/feb15zo/13
miek/2025-10-26@1431
miek/2025-10-18@2017
v0.6.83
v0.6.82
v0.6.81
v0.6.80
v0.6.79
v0.6.78
v0.6.77
v0.6.76
v0.6.75
v0.6.74
v0.6.73
v0.6.72
v0.6.71
v0.6.70
v0.6.69
v0.6.68
v0.6.67
v0.6.66
v0.6.65
v0.6.64
v0.6.63
v0.6.62
v0.6.61
v0.6.60
v0.6.59
v0.6.58
v0.6.57
v0.6.56
v0.6.55
v0.6.54
v0.6.53
v0.6.52
v0.6.51
v0.6.50
v0.6.49
v0.6.48
v0.6.47
v0.6.46
v0.6.45
v0.6.44
v0.6.43
v0.6.42
v0.6.41
v0.6.40
v0.6.39
v0.6.38
v0.6.37
v0.6.36
v0.6.35
v0.6.34
v0.6.33
v0.6.32
v0.6.31
v0.6.30
v0.6.29
v0.6.28
v0.6.27
v0.6.26
v0.6.25
v0.6.24
v0.6.23
v0.6.22
v0.6.21
v0.6.20
v0.6.19
v0.6.18
v0.6.17
v0.6.16
v0.6.15
v0.6.14
v0.6.13
v0.6.12
v0.6.11
v0.6.10
v0.6.9
v0.6.8
v0.6.7
v0.6.6
v0.6.5
v0.6.4
v0.6.3
v0.6.2
v0.6.1
v0.6.0
v0.5.38
v0.5.37
v0.5.36
v0.5.35
v0.5.34
v0.5.33
v0.5.32
v0.5.31
v0.5.30
v0.5.29
v0.5.28
v0.5.27
v0.5.26
v0.5.25
v0.5.24
v0.5.23
v0.5.22
v0.5.21
v0.5.20
v0.5.19
v0.5.18
v0.5.17
v0.5.16
v0.5.15
v0.5.14
v0.5.13
v0.5.12
v0.5.11
v0.5.10
v0.5.9
v0.5.8
v0.5.7
v0.5.6
v0.5.5
v0.5.4
v0.5.3
v0.5.2
v0.5.1
v0.5.0
v0.1.12
v0.1.11
v0.1.10
v0.1.9
v0.1.8
v0.1.7
v0.1.6
v0.1.5
v0.1.4
v0.0.4
v0.0.3
v0.0.2
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
3 participants
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
miekg/dns#645
Reference in a new issue
miekg/dns
No description provided.
Delete branch "%!s()"

Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?