Document the package, exported constants, and remaining helpers.
godoc now covers ActionAdd/ActionDelete, internal helpers, and UDP-then-TCP UPDATE behaviour. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -71,6 +71,7 @@ func (c *Client) AXFR(ctx context.Context) (*TransferResult, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// tsigAlgorithm maps a BIND algorithm name to miekg/dns constants.
|
||||
func tsigAlgorithm(name string) string {
|
||||
trimmed := strings.ToLower(strings.TrimSpace(name))
|
||||
trimmed = strings.TrimSuffix(trimmed, ".")
|
||||
@@ -92,6 +93,7 @@ func tsigAlgorithm(name string) string {
|
||||
}
|
||||
}
|
||||
|
||||
// timeoutFrom uses the context deadline, or 30s, with a 1s floor.
|
||||
func timeoutFrom(ctx context.Context) time.Duration {
|
||||
deadline, ok := ctx.Deadline()
|
||||
if !ok {
|
||||
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
|
||||
// TSIG is caller-supplied key material. go-dns does not generate keys.
|
||||
type TSIG struct {
|
||||
Name string
|
||||
Algorithm string
|
||||
Secret string
|
||||
Name string // key name presented to BIND
|
||||
Algorithm string // BIND algorithm name, e.g. hmac-sha256
|
||||
Secret string // base64 secret from the Key File
|
||||
}
|
||||
|
||||
// Client is a session against one nameserver and zone, authenticated with TSIG.
|
||||
@@ -39,6 +39,7 @@ func NewClient(server, zone string, key TSIG) (*Client, error) {
|
||||
return &Client{server: addr, zone: strings.TrimSpace(zone), tsig: key}, nil
|
||||
}
|
||||
|
||||
// validateTSIG requires name, algorithm, and secret, and an ASCII key name.
|
||||
func validateTSIG(key TSIG) error {
|
||||
if strings.TrimSpace(key.Name) == "" {
|
||||
return fmt.Errorf("TSIG name is required")
|
||||
@@ -55,6 +56,7 @@ func validateTSIG(key TSIG) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// withDNSPort appends :53 when server has no port.
|
||||
func withDNSPort(server string) (string, error) {
|
||||
host := strings.TrimSpace(server)
|
||||
if _, _, err := net.SplitHostPort(host); err == nil {
|
||||
@@ -67,6 +69,7 @@ func withDNSPort(server string) (string, error) {
|
||||
return net.JoinHostPort(host, "53"), nil
|
||||
}
|
||||
|
||||
// asciiName rejects empty or non-ASCII DNS names.
|
||||
func asciiName(label, name string) error {
|
||||
trimmed := strings.TrimSpace(name)
|
||||
if trimmed == "" {
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
// Package godns is a library for authoritative DNS against BIND-compatible
|
||||
// nameservers: full zone transfer (AXFR) over TCP and RFC 2136 dynamic UPDATE,
|
||||
// authenticated with caller-supplied TSIG. Domain names are ASCII only in v1.
|
||||
package godns
|
||||
@@ -124,6 +124,7 @@ func PTR(owner string, ttl uint32, target string) (Record, error) {
|
||||
return fromRR(&dns.PTR{Hdr: rrHeader(owner, dns.TypePTR, ttl), Ptr: name})
|
||||
}
|
||||
|
||||
// rrHeader builds an INET header. Owner ASCII is checked in fromRR.
|
||||
func rrHeader(owner string, typ uint16, ttl uint32) dns.RR_Header {
|
||||
name, err := fqdnASCII(owner)
|
||||
if err != nil {
|
||||
@@ -134,6 +135,7 @@ func rrHeader(owner string, typ uint16, ttl uint32) dns.RR_Header {
|
||||
return dns.RR_Header{Name: name, Rrtype: typ, Class: dns.ClassINET, Ttl: ttl}
|
||||
}
|
||||
|
||||
// fromRR maps a miekg/dns RR onto the public Record type.
|
||||
func fromRR(rr dns.RR) (Record, error) {
|
||||
hdr := rr.Header()
|
||||
if err := asciiName("owner", strings.TrimSuffix(hdr.Name, ".")); err != nil {
|
||||
@@ -151,6 +153,7 @@ func fromRR(rr dns.RR) (Record, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// recordToRR encodes a Record as a miekg/dns RR for UPDATE.
|
||||
func recordToRR(rec Record) (dns.RR, error) {
|
||||
if err := asciiName("owner", rec.Owner); err != nil {
|
||||
return nil, err
|
||||
@@ -162,6 +165,7 @@ func recordToRR(rec Record) (dns.RR, error) {
|
||||
return rr, nil
|
||||
}
|
||||
|
||||
// fqdnASCII validates ASCII and returns a trailing-dot FQDN.
|
||||
func fqdnASCII(name string) (string, error) {
|
||||
if err := asciiName("name", name); err != nil {
|
||||
return "", err
|
||||
@@ -169,6 +173,7 @@ func fqdnASCII(name string) (string, error) {
|
||||
return dns.Fqdn(strings.TrimSpace(name)), nil
|
||||
}
|
||||
|
||||
// isASCII reports whether s contains only bytes <= 127.
|
||||
func isASCII(s string) bool {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] > 127 {
|
||||
|
||||
@@ -12,7 +12,9 @@ import (
|
||||
type Action int
|
||||
|
||||
const (
|
||||
// ActionAdd inserts a record.
|
||||
ActionAdd Action = iota
|
||||
// ActionDelete removes a matching RR (name, type, and rdata).
|
||||
ActionDelete
|
||||
)
|
||||
|
||||
@@ -38,7 +40,7 @@ type UpdateResult struct {
|
||||
}
|
||||
|
||||
// Update applies RFC 2136 changes. It tries UDP first and retries on TCP when
|
||||
// the response is truncated.
|
||||
// the UDP exchange fails or the response is truncated.
|
||||
func (c *Client) Update(ctx context.Context, changes ...Change) (*UpdateResult, error) {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
@@ -72,6 +74,7 @@ func (c *Client) Update(ctx context.Context, changes ...Change) (*UpdateResult,
|
||||
return &UpdateResult{Rcode: dns.RcodeToString[resp.Rcode]}, nil
|
||||
}
|
||||
|
||||
// updateMessage builds a TSIG-signed RFC 2136 UPDATE message.
|
||||
func (c *Client) updateMessage(changes []Change) (*dns.Msg, error) {
|
||||
zone := dns.Fqdn(c.zone)
|
||||
msg := new(dns.Msg)
|
||||
|
||||
Reference in New Issue
Block a user