activedns

package module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 20, 2026 License: MIT Imports: 14 Imported by: 0

README

activedns-go

Go client for the ActiveDNS API: Query the DNS database by name, address, network and AS number. No dependencies outside the standard library; Go 1.23 or later.

go get github.com/activedns/activedns-go
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/activedns/activedns-go"
)

func main() {
	client, err := activedns.NewClient("mytool/1.0")
	if err != nil {
		log.Fatal(err)
	}
	ctx := context.Background()
	page, err := client.Query(ctx, "*.example.com")
	if err != nil {
		log.Fatal(err)
	}
	for _, record := range page.Records {
		fmt.Println(record.Domain, record.IPAddress, record.Observed)
	}
}

A query is a domain (example.com), a wildcard (*.example.com), an address, a network (192.0.2.0/24) or an AS number (AS13335). QueryCombined matches several at once.

Query returns one page of the result and tells you whether there is more; see Paging.

It works without an account or a key. AS-number and combined searches, wider networks, complete answers and higher rates come with a token of your own.

The client talks to /api/v2/query, and for zones to /api/v2/zone, /api/v2/zones and /api/v2/nameservers. The API has no costs or budgets, only rate limits.

Zones

The API also knows the DNS zones behind the names. Three calls follow them:

info, err := client.Zone(ctx, "www.example.com")   // the zone of a name: NS sets, nameservers, SOA, DS, DNSKEY
if err != nil {
	return err
}
if info.Zone == nil { // no zone is known for the name
	return nil
}
ns := info.Zone.Nameservers[0]
fmt.Println(ns.Name, "is used by", ns.Zones.N, "zones") // a few is a lead, thousands is a provider

page, err := client.Zones(ctx, activedns.ByNameserver, ns.Name) // the zones that share it
addr, err := client.Nameservers(ctx, "192.0.2.1")              // the nameservers at an address

Beside each value of a zone is Count: how many zones share it, counted up to 1000 (Capped). Zones lists them by ByNameserver, ByNSSet (names joined by commas), BySOAContactOf (a zone), BySOAPrimary, ByDNSKey (the fingerprint), ByChildren (a zone) or ByRefusedBy (an address), the most recently seen first. It pages like a query, with WithPageSize, WithCursor and NextZones; see Paging.

Paging

A query returns one page: up to 100 records, or as many as WithPageSize asks for. The page tells you whether that was everything:

field meaning
page.Count how many records match in all (CountEstimated: an estimate; CountCapped: at least that many)
page.HasMore there are records after this page
len(page.Records) how many you got

The client never fetches further pages by itself. Each one is a request against your rate limit, so how much of a large result to retrieve is your decision, made with Next:

page, err := client.Query(ctx, "*.example.com")
for fetched := 0; err == nil; {
	use(page.Records)
	fetched += len(page.Records)
	if !page.HasMore || fetched >= 500 { // enough for this job
		break
	}
	page, err = client.Next(ctx, page)
}

Next repeats the query from where the page ended. NextZones does the same for the ZonePage of Zones. On the last page it returns ErrNoMore. Past the depth your token may page to, it fails with a 403 (IsForbidden).

Limits in SDK default access

activedns-go library does not require a token and provide modest capabilities, but uses a public rate-limiting policy

To provide free API access, the client has and limits to what you can query.

  • Limited CIDR range
  • Limited number of records per request
  • Limited amount of pages
  • Limited request rate
  • Standard prioritization

Custom token

activedns-go library does not require a token and provide modest capabilities, but uses a public rate-limiting policy have and limits to what you can query.

To use a private token, the client is initialized as follows:

client, err := activedns.NewClient("mytool/1.0", activedns.WithToken(os.Getenv("ACTIVEDNS_TOKEN")))
Supported queries using custom token
Query ASN
page, err := client.Query(ctx, "AS64496")
Combined queries

Ask precise questions. What does this company host at that provider? Which names under a domain sit in one particular network? A combined search answers in one request what would otherwise be thousands of records to download and filter yourself:

page, err := client.QueryCombined(ctx, activedns.Combined{Domain: "*.example.com", ASN: 64496})
page, err = client.QueryCombined(ctx, activedns.Combined{Domain: "*.example.com", IP: "192.0.2.0/24"})
Broader CIDR ranges

Sweep whole networks. A /16 in one search instead of 256 separate /24s:

page, err := client.Query(ctx, "198.51.0.0/16")

Get the whole answer. The built-in token shows the first 200 records of a search, which is enough to look around. Large zones, hosting ranges and CDNs have tens of thousands; with your own token Next keeps going:

// every name under a large zone, not only the first 200 records
page, err := client.Query(ctx, "*.example.com")
for err == nil {
	...
	page, err = client.Next(ctx, page)
}
if !errors.Is(err, activedns.ErrNoMore) {
	log.Fatal(err)
}

With the built-in token the same loop ends after 200 records with 403: cursor beyond your token's limit of 100.

Run it where your work runs. The built-in token's allowance belongs to a network address, so an office, a cloud region or a CI provider's address range shares it with whoever else is there. Your own token's allowance is yours wherever it is used: pipelines, scheduled jobs and several machines at once. Keep the token out of the code and hand it to the job as a secret:

Say who you are

NewClient takes the name and version of your program.

User-Agent: mytool/1.0 activedns-go/0.3.0 (go1.25.4; linux/amd64)

Everyone using the SDK's token looks the same to the API otherwise. With a name, a tool that misbehaves can be told apart from the rest, and its author asked about it instead of everyone being limited.

Examples

Example programs are provided in example/, each one file that runs as it is:

go run ./example/query example.com                 one page of a search, and the rate limit
go run ./example/query -limit 10 192.0.2.0/24
go run ./example/subdomains -pages 5 example.com   names under a domain, paging with Next
go run ./example/combined -domain '*.example.com' -asn 64496
go run ./example/zone www.example.com             the zone of a name, and the zones that share its nameservers

They use the SDK's own token; set ACTIVEDNS_TOKEN to use yours. combined needs one, and shows the refusal without it; query with your own token also takes AS numbers (AS64496) and wider networks.

A fair client

The API is shared, so the client holds itself back without being asked:

situation what the client does
several goroutines query at once one request at a time (WithMaxConcurrent to change)
429, 502, 503, 504, connection failure up to 4 retries (WithMaxRetries), waiting 1 s, 2 s, 4 s, 8 s … (at most 30 s) with jitter, and at least Retry-After
told to slow down every goroutine using the client waits, not only the one that was told
asked to wait longer than 30 s (WithMaxWait) no sleeping: the call returns a *RateLimitError with RetryAt
a rate-limit header says a quota has nothing left the next request waits for the reset instead of being sent and refused (or fails at once with a *RateLimitError if the reset is more than 30 s away)
the token is refused (401) nothing more is sent; IsUnauthorized(err) is true
the token may not make that query (403) returned as it is, never retried; IsForbidden(err) is true and other queries go on working
bad query, server error, request timed out returned as it is, never retried
the server timed out on a search (Page.TimedOut) the page is returned as it is, not retried
a result has more pages nothing: further pages are fetched only when you call Next

Share one Client across a program: these limits are kept per client.

page, err := client.Query(ctx, "*.example.com")
var limit *activedns.RateLimitError
if errors.As(err, &limit) {
	// limit.Exceeded: "address", "network" or "token"
	// limit.RetryAt:  when to try again
}
Rate-limit headers

The client reads the rate-limit headers of every answer. The API sends the first form below: the limit closest to running out, with r requests left right now and one more in t seconds. The other usual spellings are understood as well:

RateLimit: "address";r=41;t=2                              IETF httpapi draft
RateLimit-Policy: "address";q=60;w=120, "network";q=300;w=120

RateLimit: limit=100, remaining=50, reset=30               the draft's earlier form
RateLimit-Limit / RateLimit-Remaining / RateLimit-Reset    earlier still
X-RateLimit-Limit / X-RateLimit-Remaining / X-RateLimit-Reset

and Retry-After (seconds or a date) on 429 and 503. client.RateLimits() returns the quotas of the latest answer.

Options

option default
WithToken(token) the SDK's own token
WithMaxConcurrent(n) 1
WithMaxRetries(n) 4
WithMaxWait(d) 30 s
WithHTTPClient(hc) 60 s timeout
WithBaseURL(url) https://activedns.net

Development

go test ./...                             unit tests, against a fake server
go vet ./...
golangci-lint run --build-tags e2e
go test -tags e2e -run E2E -count=1 .     about ten requests to the live API, with the SDK's own token

The same checks run in GitHub Actions on every push and pull request (.github/workflows/ci.yml): gofmt and golangci-lint, the unit tests on Go 1.23 and the current release, and then the end-to-end tests.

A release is a tag: set the version in version.go, commit, and push the tag v<version>. .github/workflows/release.yml runs the checks again, creates the GitHub release and fetches the version with go run …@v<version>, which is also what makes the Go module proxy and pkg.go.dev list it. A published version cannot be changed afterwards: a mistake is fixed with the next version.

Documentation

Overview

Package activedns is a client for the ActiveDNS API (https://activedns.net): DNS search by domain, address, network, and AS number.

client, err := activedns.NewClient("mytool/1.0")
if err != nil {
	return err
}
page, err := client.Query(ctx, "*.example.com")
if err != nil {
	return err
}
for _, record := range page.Records {
	fmt.Println(record.Domain, record.IPAddress)
}

Queries

A query is one of:

example.com      records of a domain
*.example.com    records of a domain and of every name under it
192.0.2.1        names that resolve to an address
192.0.2.0/24     names that resolve into a network
AS64496          names that resolve into an AS

Client.Query runs a query and Client.QueryCombined matches a domain, a network and an AS number at once.

Zones

Client.Zone returns the zone of a name: its nameservers, NS sets, SOA and DNSSEC keys, each with the number of zones that share it. Client.Zones lists those zones, a page at a time like a query (Client.NextZones continues it), and Client.Nameservers returns the nameservers at an address.

info, err := client.Zone(ctx, "www.example.com")
if err == nil && info.Zone != nil {
	pages, err := client.Zones(ctx, activedns.ByNameserver, info.Zone.Nameservers[0].Name)
	...
}

Paging

A query returns one Page: up to 100 records, or as many as WithPageSize asks for. The page says whether that was everything:

  • Page.Count is how many records match in all.
  • Page.HasMore is true when there are records after this page.

Client.Next fetches the page after one. The client never fetches further pages by itself: how much of a large result to retrieve is the caller's decision.

page, err := client.Query(ctx, "*.example.com")
for err == nil {
	use(page.Records)
	if !page.HasMore || enough() {
		break
	}
	page, err = client.Next(ctx, page)
}

Tokens

Requests are authenticated with a token. The SDK has one built in, shared by all its users, so no account is needed. It allows domain, wildcard, address and network queries, small pages and the first records of a result. Its rate limit is counted per client network address.

WithToken sets a token issued to you. Such a token adds AS-number and combined queries, wider networks, larger pages, deeper paging and a higher rate limit that is counted per token, not per address. Request one at https://activedns.net/contact/.

A query that the token does not allow fails with a 403; see IsForbidden.

Errors

Retries and rate limits

A Client is safe for concurrent use and paces its own requests:

  • One request is in flight at a time (WithMaxConcurrent).
  • 429, 502, 503, 504 and connection failures are retried up to four times (WithMaxRetries) with exponential backoff, and no sooner than Retry-After. The pause applies to every goroutine using the client.
  • A wait longer than 30 seconds (WithMaxWait) is not slept through. The call returns a *RateLimitError, and later calls fail without being sent until its RetryAt.
  • When a response reports that no requests are left, the next request waits for the limit to reset.
  • After a 401 no further requests are sent.
  • Other errors, timed-out requests and timed-out searches are not retried.
Example
package main

import (
	"context"
	"fmt"
	"log"
	"time"

	activedns "github.com/activedns/activedns-go"
)

func main() {
	client, err := activedns.NewClient("mytool/1.0")
	if err != nil {
		log.Fatal(err)
	}
	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()

	page, err := client.Query(ctx, "example.com")
	if err != nil {
		log.Fatal(err)
	}
	for _, record := range page.Records {
		fmt.Println(record.Domain, record.IPAddress, record.Observed.Format(time.DateOnly))
	}
}

Index

Examples

Constants

View Source
const DefaultBaseURL = "https://activedns.net"

DefaultBaseURL is the server used unless WithBaseURL is given.

View Source
const Version = "0.3.0"

Version is the SDK version. It is sent in the User-Agent.

Variables

View Source
var ErrNoAppName = errors.New(`activedns: app name is required, as in NewClient("mytool/1.0")`)

ErrNoAppName is returned by NewClient when app is empty.

View Source
var ErrNoMore = errors.New("activedns: no more pages")

ErrNoMore is returned by Client.Next when the page it is given is the last one of the result.

View Source
var ErrNoToken = errors.New("activedns: no token: none is built in and WithToken was not given")

ErrNoToken is returned by NewClient when there is no token to use: the build has none built in, and WithToken was not given or was empty.

Functions

func IsForbidden

func IsForbidden(err error) bool

IsForbidden reports whether err is a 403: the token does not allow the query, because of its type, the width of a network, a wildcard or the cursor. Other queries are unaffected.

func IsUnauthorized

func IsUnauthorized(err error) bool

IsUnauthorized reports whether err is a 401: the token is unknown or revoked. After a 401 the Client sends no further requests, and every later call returns the same error.

Types

type APIError

type APIError struct {
	StatusCode int    // HTTP status
	Message    string // the server's error message
}

APIError is an error response from the API.

func (*APIError) Error

func (e *APIError) Error() string

type Address added in v0.3.0

type Address struct {
	IP string `json:"ip"`
	// Nameservers are the names at the address, the most recently seen first,
	// up to 100. Names counts them all.
	Nameservers []AddressNameserver `json:"nameservers,omitempty"`
	Names       Count               `json:"names"`
	// Refused counts the zones the address would not answer for. They are
	// listed by [ByRefusedBy].
	Refused Count `json:"refused"`
}

Address is the answer of Client.Nameservers.

type AddressNameserver added in v0.3.0

type AddressNameserver struct {
	Name      string    `json:"name"`
	FirstSeen time.Time `json:"first_seen"`
	LastSeen  time.Time `json:"last_seen"`
	// Origins are where it was seen, as NameserverAddress.Origins.
	Origins []string `json:"origins,omitempty"`
	// Zones counts the zones that have it in an NS set.
	Zones Count `json:"zones"`
}

AddressNameserver is a nameserver name seen at an Address.

type Alias

type Alias struct {
	Name string `json:"name"`
	// Chain is the names from Name to Domain, in order.
	Chain []string `json:"chain"`
}

Alias is a CNAME name that resolves to a Record's Domain.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is an ActiveDNS API client. It is safe for concurrent use.

Use one Client per program: request pacing and rate-limit state are kept per client.

func NewClient

func NewClient(app string, opts ...Option) (*Client, error)

NewClient returns a Client.

app names the calling program and its version, such as "mytool/1.2". It is sent in the User-Agent of every request and must be printable ASCII.

NewClient returns ErrNoAppName if app is empty, ErrNoToken if there is no token to use, and an error if an option is invalid.

func (*Client) Nameservers added in v0.3.0

func (c *Client) Nameservers(ctx context.Context, ip string) (*Address, error)

Nameservers returns the nameserver names seen at an IPv4 or IPv6 address, each with the number of zones that use it, and the number of zones the address refused to answer for.

func (*Client) Next

func (c *Client) Next(ctx context.Context, page *Page) (*Page, error)

Next returns the page after page: the same query, continued where page ended. It returns ErrNoMore when page is the last one (Page.HasMore is false), and fails with a 403 (IsForbidden) past the depth the token may page to.

Example

A query returns one page. Next fetches the one after it, for as long as the caller wants more.

package main

import (
	"context"
	"errors"
	"fmt"
	"log"

	activedns "github.com/activedns/activedns-go"
)

func main() {
	client, err := activedns.NewClient("mytool/1.0")
	if err != nil {
		log.Fatal(err)
	}
	ctx := context.Background()

	page, err := client.Query(ctx, "*.example.com")
	for fetched := 0; err == nil; {
		for _, record := range page.Records {
			fmt.Println(record.Domain, record.IPAddress)
		}
		fetched += len(page.Records)
		if !page.HasMore || fetched >= 500 {
			fmt.Printf("%d of %d records\n", fetched, page.Count)
			break
		}
		page, err = client.Next(ctx, page)
	}

	var limit *activedns.RateLimitError
	switch {
	case errors.As(err, &limit):
		log.Fatalf("rate limit %q reached, back at %s", limit.Exceeded, limit.RetryAt)
	case err != nil:
		log.Fatal(err)
	}
}

func (*Client) NextZones added in v0.3.0

func (c *Client) NextZones(ctx context.Context, page *ZonePage) (*ZonePage, error)

NextZones returns the page after page: the same request, continued where page ended. It returns ErrNoMore when page is the last one, and fails with a 403 (IsForbidden) past the depth the token may page to.

func (*Client) Query

func (c *Client) Query(ctx context.Context, query string, opts ...QueryOption) (*Page, error)

Query runs a query and returns one page of results. The package documentation lists the forms a query can take.

func (*Client) QueryCombined

func (c *Client) QueryCombined(ctx context.Context, q Combined, opts ...QueryOption) (*Page, error)

QueryCombined runs a combined query and returns one page of results. At least one field of q must be set. The token must allow combined queries.

func (*Client) RateLimits

func (c *Client) RateLimits() []RateLimit

RateLimits returns the rate limits reported by the most recent response, or nil if it reported none.

func (*Client) Zone added in v0.3.0

func (c *Client) Zone(ctx context.Context, name string) (*ZoneInfo, error)

Zone returns what is known of the zone that name is in: its NS sets, nameservers and their addresses, SOA, DS and DNSKEY records, and the servers that refused it. Beside each value is the number of zones that share it; Client.Zones lists them.

name is a domain name, not a wildcard: the zone of *.example.com is the zone of example.com. ZoneInfo.Zone is nil when no zone is known for it.

func (*Client) Zones added in v0.3.0

func (c *Client) Zones(ctx context.Context, by ZonesBy, value string, opts ...QueryOption) (*ZonePage, error)

Zones returns one page of the zones that share a value: a nameserver, an NS set, an SOA contact or primary, a DNSKEY, the parent they are delegated by, or a server that refused them. WithPageSize and WithCursor apply as they do to Client.Query; Client.NextZones continues from a page.

Example

Zone returns what is known of the zone of a name. Zones lists the zones that share one of its values, a page at a time.

package main

import (
	"context"
	"fmt"
	"log"
	"time"

	activedns "github.com/activedns/activedns-go"
)

func main() {
	client, err := activedns.NewClient("mytool/1.0")
	if err != nil {
		log.Fatal(err)
	}
	ctx := context.Background()

	info, err := client.Zone(ctx, "www.example.com")
	if err != nil {
		log.Fatal(err)
	}
	if info.Zone == nil || len(info.Zone.Nameservers) == 0 {
		return
	}
	ns := info.Zone.Nameservers[0]
	fmt.Println(ns.Name, "is in", ns.Zones.N, "zones")

	page, err := client.Zones(ctx, activedns.ByNameserver, ns.Name, activedns.WithPageSize(20))
	for err == nil {
		for _, zone := range page.Zones {
			fmt.Println(zone.Name, zone.LastSeen.Format(time.DateOnly))
		}
		if !page.HasMore {
			break
		}
		page, err = client.NextZones(ctx, page)
	}
	if err != nil {
		log.Fatal(err)
	}
}

type Combined

type Combined struct {
	Domain string // a domain, or a leading wildcard such as "*.example.com"
	IP     string // an address, or a network in CIDR notation
	ASN    int    // an AS number; 0 for none
}

Combined is a query for records that match every field that is set.

type Count added in v0.3.0

type Count struct {
	N      int  `json:"n"`
	Capped bool `json:"capped,omitempty"`
}

Count is how many zones share a value. N stops at 1000 for the values of a zone and at 10,000 for the size of a list; Capped is then true and N is a lower bound. A few zones is a lead, thousands is a provider.

type DNSKey added in v0.3.0

type DNSKey struct {
	KeyTag    int `json:"key_tag"`
	Flags     int `json:"flags"`
	Algorithm int `json:"algorithm"`
	// Fingerprint is the SHA-256 of the algorithm and the key, in hex. It is
	// the value that finds the zones with the key: see [ByDNSKey].
	Fingerprint string    `json:"fingerprint"`
	FirstSeen   time.Time `json:"first_seen"`
	LastSeen    time.Time `json:"last_seen"`
	// Zones counts the zones with this key.
	Zones Count `json:"zones"`
}

DNSKey is a DNSSEC key of a zone.

type DS added in v0.3.0

type DS struct {
	KeyTag     int `json:"key_tag"`
	Algorithm  int `json:"algorithm"`
	DigestType int `json:"digest_type"`
	// Digest is in hex.
	Digest    string    `json:"digest"`
	FirstSeen time.Time `json:"first_seen"`
	LastSeen  time.Time `json:"last_seen"`
}

DS is a delegation signer record of a zone.

type ListedZone added in v0.3.0

type ListedZone struct {
	Name      string    `json:"name"`
	FirstSeen time.Time `json:"first_seen"`
	LastSeen  time.Time `json:"last_seen"`
	// Reason is why the server would not answer, for [ByRefusedBy].
	Reason string `json:"reason,omitempty"`
}

ListedZone is a zone in a ZonePage, the most recently seen first.

type NSSet added in v0.3.0

type NSSet struct {
	NS []string `json:"ns"`
	// Origin is "parent" (the delegation, from a server of From) or "apex"
	// (from the zone's own servers).
	Origin string `json:"origin"`
	From   string `json:"from,omitempty"`
	// Server is the address of the server that gave it last.
	Server    string    `json:"server,omitempty"`
	FirstSeen time.Time `json:"first_seen"`
	LastSeen  time.Time `json:"last_seen"`
	// Zones counts the zones with exactly this set.
	Zones Count `json:"zones"`
}

NSSet is a set of nameservers seen for a zone.

type Nameserver added in v0.3.0

type Nameserver struct {
	Name string `json:"name"`
	// Provider is the registrable domain of the name: whose nameserver it is.
	Provider  string              `json:"provider,omitempty"`
	Addresses []NameserverAddress `json:"addresses,omitempty"`
	// Zones counts the zones that have it in an NS set.
	Zones Count `json:"zones"`
}

Nameserver is a nameserver of a zone.

type NameserverAddress added in v0.3.0

type NameserverAddress struct {
	IP        string    `json:"ip"`
	FirstSeen time.Time `json:"first_seen"`
	LastSeen  time.Time `json:"last_seen"`
	// Origins are where it was seen: "glue" or "additional" (in a referral)
	// and "answer" (the resolver's lookup of the name).
	Origins []string `json:"origins,omitempty"`
	// Names counts the nameserver names at this address.
	Names Count `json:"names"`
}

NameserverAddress is an address of a Nameserver.

type Option

type Option func(*config)

Option configures a Client in NewClient.

func WithBaseURL

func WithBaseURL(baseURL string) Option

WithBaseURL sets the server, as scheme and host: "http://localhost:8080". The default is DefaultBaseURL.

func WithHTTPClient

func WithHTTPClient(hc *http.Client) Option

WithHTTPClient sets the HTTP client. The default has a 60-second timeout.

func WithMaxConcurrent

func WithMaxConcurrent(n int) Option

WithMaxConcurrent sets how many requests may be in flight at once. The default is 1. It does not raise the server's rate limit.

func WithMaxRetries

func WithMaxRetries(n int) Option

WithMaxRetries sets how many times a request is retried after a 429, 502, 503, 504 or connection failure. The default is 4; 0 disables retries.

func WithMaxWait

func WithMaxWait(d time.Duration) Option

WithMaxWait sets the longest pause the client sleeps through before a retry. The default is 30 seconds. When the server asks for a longer wait, the call returns a *RateLimitError instead.

func WithToken

func WithToken(token string) Option

WithToken sets the API token, replacing the one built into the SDK. See the package documentation for what a token of your own allows.

type Page

type Page struct {
	Records []Record `json:"records"`
	// Count is the number of records that match the query in all, not the
	// number in this page. It is an estimate when CountEstimated is set,
	// and a lower bound when CountCapped is set.
	Count          int  `json:"count"`
	CountEstimated bool `json:"count_estimated,omitempty"`
	CountCapped    bool `json:"count_capped,omitempty"`
	// HasMore is true when there are records after this page. NextCursor
	// is then the offset of the first of them.
	HasMore    bool `json:"has_more"`
	NextCursor int  `json:"next_cursor"`
	// TimedOut is true when the server gave up on the search. Records may
	// be incomplete. The client does not retry such a query.
	TimedOut bool `json:"timed_out,omitempty"`
	// Query is the query as the server normalised it. QueryType is how it
	// was classified: "domain", "wildcard_domain", "ip", "cidr", "asn" or
	// "combined".
	QueryType string `json:"query_type"`
	Query     string `json:"query"`
	// contains filtered or unexported fields
}

Page is one page of results. Count and HasMore say whether there is more than this page; Client.Next fetches the next one.

type QueryOption

type QueryOption func(url.Values)

QueryOption adjusts a single query.

func WithCursor

func WithCursor(n int) QueryOption

WithCursor sets the offset of the first record returned. To continue from a Page, Client.Next is simpler.

func WithPageSize

func WithPageSize(n int) QueryOption

WithPageSize sets the most records returned per page. The default is 100; the token's policy caps it, without an error: a larger request returns the token's maximum.

type RateLimit

type RateLimit struct {
	// Name identifies the limit. The ActiveDNS API uses "address",
	// "network" and "token". Empty if the server gave none.
	Name string
	// Limit is how many requests the limit allows at once, or -1 if not
	// reported.
	Limit int64
	// Remaining is how many requests can be made now.
	Remaining int64
	// Reset is when more requests become available. Zero if not reported.
	Reset time.Time
}

RateLimit is a rate limit as the server reported it in the rate-limit headers of a response.

type RateLimitError

type RateLimitError struct {
	// Exceeded names the limit: "address" (the client's network address),
	// "network" (the network around it) or "token". Empty if the server
	// did not say.
	Exceeded string
	// RetryAt is when the limit allows another request. Zero if unknown.
	RetryAt time.Time
	// Message is the server's error message.
	Message string
	// Local is true when the request was not sent, because an earlier
	// response had already reported the limit.
	Local bool
}

RateLimitError is returned when a rate limit is reached and the client did not wait it out: the wait was longer than WithMaxWait allows, or the retries were used up.

func (*RateLimitError) Error

func (e *RateLimitError) Error() string

type Record

type Record struct {
	ID        int64  `json:"id"`
	IPAddress string `json:"ip_address"`
	// Domain is the name that holds the address. For a name that is a
	// CNAME, it is the name at the end of the chain; see Aliases.
	Domain string `json:"domain"`
	// Observed is the most recent observation, FirstSeen the first.
	Observed  time.Time `json:"observed"`
	FirstSeen time.Time `json:"first_seen"`
	// IPVersion is 4 or 6.
	IPVersion int `json:"ip_version"`
	// ASN and Country are those of the address. Zero when unknown.
	ASN     int    `json:"asn,omitempty"`
	Country string `json:"country,omitempty"`
	// Aliases lists the CNAME names that matched a name query and lead to
	// Domain. Empty when Domain itself matched.
	Aliases []Alias `json:"aliases,omitempty"`
	// KnownAs lists some of the CNAME names that point at Domain. It is
	// set on address, network and AS-number queries. KnownAsTotal is how
	// many there are, capped at 1000.
	KnownAs      []Alias `json:"known_as,omitempty"`
	KnownAsTotal int     `json:"known_as_total,omitempty"`
}

Record is one observation: a name that resolved to an address.

type Refusal added in v0.3.0

type Refusal struct {
	Server string `json:"server"`
	// Reason is refused, servfail, notauth or upward.
	Reason    string    `json:"reason"`
	FirstSeen time.Time `json:"first_seen"`
	LastSeen  time.Time `json:"last_seen"`
}

Refusal is a server that would not answer for a zone.

type SOA added in v0.3.0

type SOA struct {
	Primary string `json:"primary"`
	// Contact is the RNAME as it is in DNS (hostmaster.example.com), Email
	// the address it stands for (hostmaster@example.com).
	Contact string `json:"contact"`
	Email   string `json:"email,omitempty"`
	Serial  int64  `json:"serial"`
	Refresh int    `json:"refresh"`
	Retry   int    `json:"retry"`
	Expire  int    `json:"expire"`
	Minimum int    `json:"minimum"`
	// Since is when these values were first seen without others between.
	Since    time.Time `json:"since"`
	LastSeen time.Time `json:"last_seen"`
	// PrimaryZones counts the zones with this primary, ContactZones those
	// with this contact.
	PrimaryZones Count `json:"primary_zones"`
	ContactZones Count `json:"contact_zones"`
}

SOA is the start-of-authority record of a zone.

type Zone added in v0.3.0

type Zone struct {
	Name      string    `json:"name"`
	FirstSeen time.Time `json:"first_seen"`
	LastSeen  time.Time `json:"last_seen"`
	// Parent is the zone that gave the newest delegation of this one.
	Parent string `json:"parent,omitempty"`
	// Children counts the zones this one delegates.
	Children *Count `json:"children,omitempty"`
	// NSSets are the NS sets seen for the zone, from its parent and from its
	// own servers, newest first.
	NSSets []NSSet `json:"ns_sets,omitempty"`
	// Nameservers are the names in NSSets, each once, with their addresses.
	Nameservers []Nameserver `json:"nameservers,omitempty"`
	SOA         *SOA         `json:"soa,omitempty"`
	DS          []DS         `json:"ds,omitempty"`
	DNSKeys     []DNSKey     `json:"dnskeys,omitempty"`
	// Refusals are the servers that would not answer for the zone.
	Refusals []Refusal `json:"refusals,omitempty"`
}

Zone is what is known of a DNS zone.

type ZoneInfo added in v0.3.0

type ZoneInfo struct {
	// Query is the name asked for, as the server normalised it.
	Query string `json:"query"`
	// QueryIsNameserver is true when the name is itself a nameserver: in an
	// NS set, named as SOA primary by a zone, or with an address. The zone
	// says nothing about it (the zone of ns1.example.net is example.net), so
	// [Client.Nameservers] may have an answer for it too.
	QueryIsNameserver bool `json:"query_is_nameserver,omitempty"`
	// Zone is the zone the name is in. Nil when none is known.
	Zone *Zone `json:"zone"`
	// RegistrableDomains maps the zone and its parent to their registrable
	// domains. A zone that is a public suffix (pl, co.uk) has none.
	RegistrableDomains map[string]string `json:"registrable_domains,omitempty"`
}

ZoneInfo is the answer of Client.Zone.

type ZonePage added in v0.3.0

type ZonePage struct {
	Zones []ListedZone `json:"zones"`
	// By and Value are the request as the server understood it. Subject is
	// the value shared when it is not Value itself: the SOA contact, as an
	// address, for [BySOAContactOf].
	By      ZonesBy `json:"by"`
	Value   string  `json:"value"`
	Subject string  `json:"subject,omitempty"`
	// Count is the number of zones in all, not in this page. It stops at
	// 10,000 (Count.Capped).
	Count Count `json:"count"`
	// RegistrableDomains maps zones to their registrable domains.
	RegistrableDomains map[string]string `json:"registrable_domains,omitempty"`
	// HasMore is true when there are zones after this page. NextCursor is
	// then the offset of the first of them.
	HasMore    bool `json:"has_more"`
	NextCursor int  `json:"next_cursor"`
	// contains filtered or unexported fields
}

ZonePage is one page of the zones that share a value. ZonePage.HasMore and Client.NextZones work as Page.HasMore and Client.Next do.

type ZonesBy added in v0.3.0

type ZonesBy string

ZonesBy is what the zones of a Client.Zones list share.

const (
	// ByNameserver lists the zones with a nameserver in an NS set. The value
	// is the name of the nameserver.
	ByNameserver ZonesBy = "nameserver"
	// ByNSSet lists the zones with exactly an NS set. The value is its
	// names joined by commas.
	ByNSSet ZonesBy = "ns_set"
	// BySOAContactOf lists the zones with the SOA contact of a zone. The
	// value is that zone, not the address.
	BySOAContactOf ZonesBy = "soa_contact_of"
	// BySOAPrimary lists the zones with an SOA primary. The value is its
	// name.
	BySOAPrimary ZonesBy = "soa_primary"
	// ByDNSKey lists the zones with a DNSKEY. The value is its fingerprint:
	// 64 hex digits, see [DNSKey].
	ByDNSKey ZonesBy = "dnskey"
	// ByChildren lists the zones that a zone delegates. The value is that
	// zone.
	ByChildren ZonesBy = "children"
	// ByRefusedBy lists the zones that a server would not answer for. The
	// value is the address of the server.
	ByRefusedBy ZonesBy = "refused_by"
)

Directories

Path Synopsis
example
combined command
Combined searches for records that match a domain and a network and an AS number at once: any two or three of them.
Combined searches for records that match a domain and a network and an AS number at once: any two or three of them.
query command
Query runs one search and prints the first page of the answer.
Query runs one search and prints the first page of the answer.
subdomains command
Subdomains lists the names known under a domain, with the addresses each one has resolved to.
Subdomains lists the names known under a domain, with the addresses each one has resolved to.
zone command
Zone prints what is known of the zone of a name, and lists a few of the zones that share its first nameserver.
Zone prints what is known of the zone of a name, and lists a few of the zones that share its first nameserver.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL