211 lines
6.9 KiB
Markdown
211 lines
6.9 KiB
Markdown
# go-dns guide
|
|
|
|
How to use the library. Symbol signatures and doc comments are in [API](api.md). Words for the domain are in [CONTEXT.md](../CONTEXT.md). Operators who configure BIND follow [BIND TSIG configuration](bind-tsig-configuration.md).
|
|
|
|
## 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
|
|
|
|
```go
|
|
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
|
|
|
|
```go
|
|
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
|
|
|
|
```go
|
|
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](bind-tsig-configuration.md).
|
|
|
|
### 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
|
|
|
|
```go
|
|
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
|
|
|
|
```go
|
|
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
|
|
|
|
```go
|
|
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))
|
|
}
|
|
```
|