Files

6.9 KiB

go-dns guide

How to use the library. Symbol signatures and doc comments are in API. Words for the domain are in CONTEXT.md. Operators who configure BIND follow BIND TSIG configuration.

Session

Purpose

A session is a Client bound to one nameserver address, one zone name, and one TSIG key. AXFR and UPDATE both use that session. There is no unsigned mode.

When to use it

Create one client per nameserver, zone, and key. A second key, even for the same zone, is a second client.

Call sequence

  1. Fill a TSIG with the key name, algorithm, and base64 secret.
  2. Call NewClient with the server, the zone, and that key.
  3. Call AXFR or Update on the returned client.

server is a host or host:port. A missing port becomes 53. An IPv6 host must be written in brackets, such as [2001:db8::1]:53.

Errors

NewClient returns an error when the server, zone, or any TSIG field is empty, when the zone or the key name is not ASCII, or when the server is a bare IPv6 address. It does not contact the nameserver.

Example

key := godns.TSIG{
    Name:      "exapp-key.",
    Algorithm: "hmac-sha256",
    Secret:    "base64-secret-from-bind",
}
client, err := godns.NewClient("ns.example:53", "example.com.", key)
if err != nil {
    return err
}
_ = client

AXFR

Purpose

AXFR reads the full zone over TCP and returns it as a Zone plus the SOA serial.

When to use it

Use AXFR when the caller needs the nameserver's current records for the session's zone. Incremental transfer (IXFR) is not available.

Call sequence

  1. Start a session.
  2. Call Client.AXFR with a context.
  3. Read TransferResult.Zone and TransferResult.Serial. Serial is the same value stored on the zone.

The request is signed with the session TSIG key. The closing SOA of the transfer is not appended a second time.

Errors

AXFR returns an error when the context is done, the transfer fails, a record owner is not ASCII, or the stream contains no SOA. Transport errors are wrapped with the server address.

Example

tr, err := client.AXFR(ctx)
if err != nil {
    return err
}
zone := tr.Zone
_ = zone.Serial

UPDATE

Purpose

UPDATE sends one RFC 2136 batch that adds records, deletes records, or does both. The nameserver applies the batch.

When to use it

Use UPDATE to change the session's zone. Each change is one record, built with Add or Delete.

Call sequence

  1. Build records with the typed builders.
  2. Wrap each record in Add or Delete.
  3. Call Client.Update with a context and the changes.
  4. On success, UpdateResult.Rcode is NOERROR.

The client tries UDP first. It retries on TCP when the UDP exchange fails or the response is truncated. An empty change list returns success and does not contact the nameserver. Delete removes one record that matches owner, type, and rdata. It does not delete a whole RRset by type alone.

Errors

Update returns an error when the context is done, a record cannot be encoded, the exchange fails, or the nameserver rcode is not NOERROR. An Action other than ActionAdd or ActionDelete is rejected before the exchange. A refused or badly signed update comes back as an rcode error such as REFUSED or BADSIG.

Example

rec, err := godns.A("www.example.com.", 300, net.ParseIP("192.0.2.10"))
if err != nil {
    return err
}
result, err := client.Update(ctx, godns.Add(rec))
if err != nil {
    return err
}
_ = result.Rcode

TSIG

Purpose

TSIG is the caller-supplied key that signs AXFR and UPDATE. The library checks the fields and sends them. It does not generate keys or write BIND configuration.

When to use it

Fill a TSIG before NewClient. The same value is used for every call on that client.

Call sequence

  1. Take the key name, algorithm, and base64 secret from the operator's BIND key.
  2. Store them on TSIG.
  3. Pass the value to NewClient.

Algorithms the client maps to known TSIG names are hmac-md5, hmac-sha1, hmac-sha224, hmac-sha256, hmac-sha384, and hmac-sha512. Any other non-empty algorithm is sent as a trailing-dot name. Prefer hmac-sha256 for new keys. How to create the key on BIND is in BIND TSIG configuration.

Errors

A missing name, algorithm, or secret fails in NewClient, as does a non-ASCII key name. A wrong secret does not fail at construction. The nameserver reports it on AXFR or UPDATE, usually as BADSIG.

Example

key := godns.TSIG{
    Name:      "go-dns-key.",
    Algorithm: "hmac-sha256",
    Secret:    os.Getenv("TSIG_SECRET"),
}
client, err := godns.NewClient("192.0.2.53", "example.com", key)

Typed RR builders

Purpose

The builders turn an owner, a TTL, and typed fields into one Record of class IN. The owner is stored as a trailing-dot FQDN. RData is presentation-format rdata.

When to use it

Use a builder when the caller has an address, a target name, or text and wants a record for UPDATE or for an in-memory zone. The builders are A, AAAA, MX, TXT, CNAME, NS, SRV, PTR, and CAA.

Call sequence

  1. Call the builder for the record type.
  2. Pass the Record to Add or Delete, or store it on a Zone.

A takes an IPv4 net.IP. AAAA takes an IPv6 net.IP. Name targets (MX, CNAME, NS, SRV, PTR) are ASCII domain names. CAA takes a flag, a tag, and a value (RFC 8659).

Errors

A builder returns an error when the owner is empty or not ASCII. A returns an error when the address is not IPv4. AAAA returns an error when the address is missing or is IPv4. Builders that take a target name return an error when that name is empty or not ASCII.

Example

a, err := godns.A("www.example.com.", 300, net.ParseIP("192.0.2.10"))
if err != nil {
    return err
}
mx, err := godns.MX("example.com.", 300, 10, "mail.example.com.")
if err != nil {
    return err
}
_, err = client.Update(ctx, godns.Add(a), godns.Add(mx))

Zone, RRSet, and Record

Purpose

Record is one resource record: owner, class, type, TTL, and presentation-format rdata. Zone is an origin, an SOA serial, and a flat record list. RRSet groups records that share owner, class, and type.

When to use it

AXFR returns a Zone. Call Zone.RRSets when the caller wants records grouped. A caller can also assemble a Zone from builder output and group it the same way. Grouping does not contact the nameserver.

Call sequence

  1. Take TransferResult.Zone from AXFR, or fill Zone.Origin, Zone.Serial, and Zone.Records.
  2. Call RRSets to get groups in first-seen order.
  3. Read each group's Owner, Class, Type, and Records.

Errors

RRSets does not return an error. It copies the records already on the zone. Bad names are reported earlier, by AXFR or by a builder.

Example

tr, err := client.AXFR(ctx)
if err != nil {
    return err
}
for _, set := range tr.Zone.RRSets() {
    fmt.Println(set.Owner, set.Type, len(set.Records))
}