hoverdnsapi

package module
v1.0.1 Latest Latest
Warning

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

Go to latest
Published: Sep 30, 2020 License: MIT Imports: 10 Imported by: 0

README

hoverdnsapi -- Hover DNS API

Hover DNS API is a tool to query Hover's DNS API. I use it to pull out my domains in JSON and see if I've made a consistency mistake.

Also, when you move house, and your domains are tagged to your home address (even if hidden from Whois), Hover offers no API for bulk changes, so you need to make manual changes. For each contact address (Admin, Tech, Billing, Owner). For each domain. They mostly work, but it's easy to miss one.

With a long-term goal somewhere like how Rancid used to commit changes to a SCM, I'm drawing from Dan Krause's Example to see how to read and parse DNS from Hover.

How to use

It's Go (Golang if you need more letters). Just import and roll forward.

I should apologize, I'm new to Go, so my Go might be of poor-quality. Please PR me improvements if you see me doing a really silly thing.

How to build

$ git clone http://github.com/chickenandpork/hoverdnsapi hoverdnsapi
$ cd hoverdnsapi && go test ./...

By itself, it's just the parsing structures which are the common code behind what I'm doing.

This builds with Go-1.10, I hope it won't break going forward.

Automatic builds

go get github.com/githubnemo/CompileDaemon
cd hoverdnsapi && CompileDaemon \
	-build="go build ./cmd/hoverdns" -exclude-dir=.git -exclude=".*.swp" \
	-command="./hoverdns -a -m allanc@smallfoot.org --domains smallfoot.org info"


# Why?

I didn't find one, so I had to build it.  I hope the next person can leverage this to cruise on at peak efficiency and invent truly useful things.


# License

MIT.  Use as you like.  Everywhere.

A thanks would be cool, or kudos on https://www.linkedin.com/in/goldfish, but it's totally OK if you're too busy fighting truly criminal coding errors to feed my curiosity.

Documentation

Overview

Package hoverdnsapi offers some API actions to control your Hover/TuCOWS DNS entries programmatically. This isn't a supported utility form Hover, it's just a thing I wrote because I needed it at the time.

Package hoverdnsapi offers some API actions to control your Hover/TuCOWS DNS entries programmatically. This isn't a supported utility form Hover, it's just a thing I wrote because I needed it at the time.

Index

Constants

This section is empty.

Variables

View Source
var (
	// HoverAddress is a constant-ish var that I use to ensure that within my domains, the ones
	// I expect to have Hovers contact info (their default) do.  For example, Tech Contacts
	// where I don't want to be that guy (for managed domains, they should be the tech
	// contact).  Of course, if the values in this constant are incorrect, TuCows is the
	// authority, but please PR me a correction to help me maintain accuracy.
	HoverAddress = Address{
		Status:           "active",
		OrganizationName: "Hover, a service of Tucows.com Co",
		FirstName:        "Support",
		LastName:         "Contact",
		Address1:         "96 Mowat Ave.",
		City:             "Toronto",
		State:            "ON",
		Zip:              "M6K 3M1",
		Country:          "CA",
		Phone:            "+1.8667316556",
		Email:            "help@hover.com",
	}
)

Functions

func APIURL

func APIURL(resource string) string

APIURL is an attempt to keep the URLs all based from parsedBaseURL, but more symbollically generated and less risk of typos. The gain on this function is dubious, and this may disappear

TODO: consider rolling in c.BaseURL

func APIURLDNS

func APIURLDNS(domainID string) string

APIURLDNS extends the consistency objectives of APIURL by bookending a domain unique ID with the /domains/ and /dns pre/post wrappers

Types

type Action

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

Action is a single action (Add, Update, Delete) to complete in a DoActions() call

func NewAction

func NewAction(action HoverAct, fqdn, domain, value string, ttl uint) Action

NewAction returns a newly-created Action as a way to allow external access to create actions during test runs using cmd/hoverdns app

func (Action) String

func (a Action) String() string

type Address

type Address struct {
	Status           string `json:"status"`     // Status seems to be "active" in all my zones
	OrganizationName string `json:"org_name"`   // Name of Organization
	FirstName        string `json:"first_name"` // First naem seems to be given non-family name, not positional
	LastName         string `json:"last_name"`  // Last Name seems to be family name, not positional
	Address1         string `json:"address1"`
	Address2         string `json:"address2"`
	Address3         string `json:"address3"`
	City             string `json:"city"`
	State            string `json:"state"`   // State seems to be the US state or the Canadian province
	Zip              string `json:"zip"`     // 5-digit US (ie 10001) or 6-char slammed Canadian (V0H1X0 no space)
	Country          string `json:"country"` // 2-leter state code; this seems to match the second (non-country) of a ISO-3166-2 code
	Phone            string `json:"phone"`   // phone format all over the map, but thy seem to write it as a ITU E164, but a "." separating country code and subscriber number
	Facsimile        string `json:"fax"`     // same format as phone
	Email            string `json:"email"`   // rfc2822 format email address such as rfc2822 para 3.4.1
}

Address holds an address used for admin, billing, or tech contact. Empirically, it seems at least US and Canada formats are squeezed into a US format. Please PR if you discover additional formats.

type Client

type Client struct {
	HTTPClient *http.Client

	Username string
	Password string
	// contains filtered or unexported fields
}

Client is the client context for communicating with Hover DNS API; should only need one of these but keeping state isolated to instances rather than global where possible.

func NewClient

func NewClient(username, password, filename string, timeout time.Duration, opt ...interface{}) *Client

NewClient Creates a Hover client using plaintext passwords against plain username. Consider the risk of where the text is stored.

func (*Client) Delete

func (c *Client) Delete(fqdn, domain string) error

Delete merely enqueues a delete action for DoActions to process

func (*Client) DoActions

func (c *Client) DoActions(actions ...Action) (err error)

DoActions is a way to burn down an accumulated list of actions. Mostly, this stack will be one or two deep, but this offers the chance to go grab a GetAuth() (for authentication cookie) if needed, or a detailed DNS list if needed, in a sort of lazy-evaluation logic that avoid these actions if not needed.

func (*Client) ExistingTXTRecords

func (c *Client) ExistingTXTRecords(fqdn string) error

ExistingTXTRecords checks whether the given TXT record exists; err != nil if not found

func (*Client) FillDomains

func (c *Client) FillDomains() error

FillDomains fills the list of domains allocated to the usernamr and password to the Domains structure. It will use GetAuth() to perform a login if necessary.

func (*Client) GetAuth

func (c *Client) GetAuth() (string, error)

GetAuth returns the authentication key for the username and password, performing a login if the key is not already known from a previous login.

func (*Client) GetCookie

func (c *Client) GetCookie(key string) (value string, ok bool)

GetCookie searches existing cookies from a login to Hover's API to find the given cookie.

func (*Client) GetDomainByName

func (c *Client) GetDomainByName(domainname string) (*Domain, bool)

GetDomainByName searches iteratively and returns the Domain record that has the given name

func (*Client) GetDomainEntries

func (c *Client) GetDomainEntries(domain string) error

GetDomainEntries gets the entries for a specific domain -- essentially the zone records

func (*Client) HTTPDelete

func (c *Client) HTTPDelete(url string) (err error)

HTTPDelete actually does an HTTP call with the DELETE method. BOG-standard Go only offers GET and POST.

TODO: move to a separate file as a layer onto net/http

func (*Client) Upsert

func (c *Client) Upsert(fqdn, domain, value string, ttl uint) error

Upsert inserts or updates a TXT record using the specified parameters

type ContactBlock

type ContactBlock struct {
	Admin   Address `json:"admin"`
	Billing Address `json:"billing"`
	Tech    Address `json:"tech"`
	Owner   Address `json:"owner"`
}

ContactBlock is merely the four contact addresses that Hover uses, but it's easier to work with a defined type in static constants during testing

type Domain

type Domain struct {
	ID             string       `json:"id"`                        // A unique opaque identifier defined by Hover
	DomainName     string       `json:"domain_name"`               // the actual domain name.  ie: "example.com"
	NumEmails      int          `json:"num_emails,omitempty"`      // This appears to be the number of email accounts either permitted or defined for the domain
	RenewalDate    string       `json:"renewal_date,omitempty"`    // This renewal date appears to be the first day of non-service after a purchased year of valid service: the first day offline if you don't renew.  RFC3339/ISO8601 -formatted yyyy-mm-dd.
	DisplayDate    string       `json:"display_date"`              // Display Date seems to be the same as Renewal Date but perhaps can allow for odd display corner-cases such as leap-years, leap-seconds, or timezones oddities.  RFC3339/ISO8601 to granularity of day as well.
	RegisteredDate string       `json:"registered_date,omitempty"` // Date the domain was first registered, which is likely also the first day of service (or partial-day, technically)  RFC3339/ISO8601 to granularity of day as well.
	Active         bool         `json:"active,omitempty"`          // Domain Entries also show which zones are active
	Contacts       ContactBlock `json:"contacts"`
	Entries        []Entry      `json:"entries,omitempty"` // entries in a zone, if expanded
	HoverUser      User         `json:"hover_user,omitempty"`
	Glue           struct{}     `json:"glue,omitempty"` // I'm not sure how Hover records Glue Records here, or whether they're still used.  Please PR a suggested format!
	NameServers    []string     `json:"nameservers,omitempty"`
	Locked         bool         `json:"locked,omitempty"`
	Renewable      bool         `json:"renewable,omitempty"`
	AutoRenew      bool         `json:"auto_renew,omitempty"`
	Status         string       `json:"status,omitempty"`        // Status seems to be "active" in all my zones
	WhoisPrivacy   bool         `json:"whois_privacy,omitempty"` // boolean as lower-case string: keep your real address out of whois?
}

Domain structure describes the config for an entire domain within Hover: the dates involved, contact addresses, nameservers, etc: it seems to cover everything about the domain in one structure, which is convenient when you want to compare data across many domains.

func (Domain) GetEntryByFQDN

func (d Domain) GetEntryByFQDN(fqdn string) (e *Entry, ok bool)

GetEntryByFQDN attempts to find a single Entry in the Domain, returning a non-nil result if found. "ok" is manipulated so that an "if" can be used to check whether it was found without having to rely on sentinel or implicit values of the returned (ie a nil Entry might not always mean "not found", but it does today)

type DomainList

type DomainList struct {
	Succeeded bool     `json:"succeeded"`
	Domains   []Domain `json:"domains"`
}

DomainList is a structure mapping the json response to a request for a list of domains. It tends to be a very rich response including an array of full Domain instances.

type Entry

type Entry struct {
	CanRevert bool   `json:"can_revert"`
	Content   string `json:"content"`    // free-form text of verbatim value to store (ie "192.168.0.1" for A-rec)
	ID        string `json:"id"`         // A unique opaque identifier defined by Hover
	Default   bool   `json:"is_default"` // seems to track the default @ or "*" record
	Name      string `json:"name"`       // entry name, or "*" for default
	TTL       int    `json:"ttl"`        // TimeToLive, seconds
	Type      string `json:"type"`       // record type: A, MX, PTR, TXT, etc
}

Entry is a single DNS record, such as a single NS, TXT, A, PTR, AAAA record within a zone.

type HoverAct

type HoverAct int

HoverAct is simply an enum type to typecheck various actions we can perform in a queue

const (
	// Error is the zero-value, catch a fencepost error
	Error HoverAct = iota
	// Add a domain record
	Add
	// Delete of a record will require the list of records
	Delete
	// Update will also need the list of records and IDs
	Update
	// Expand is an internal state that will expand a domain to include entries
	Expand
)

func (HoverAct) String

func (h HoverAct) String() string

String of course gives a string representation of the Act code

type NopLogger

type NopLogger struct {
	*log.Logger
}

NopLogger reduces spin while not logging https://gist.github.com/Avinash-Bhat/48c4f06b0cc840d9fd6c#file-log_test-go

Intended to be a compatible implementation for YALI for low-cost log discarding

func (*NopLogger) Printf

func (l *NopLogger) Printf(format string, v ...interface{})

Printf offers a relatively efficient discarding function for log messages when not logging

func (*NopLogger) Println

func (l *NopLogger) Println(v ...interface{})

Println offers a relatively efficient discarding function for log messages when not logging

type PlaintextAuth

type PlaintextAuth struct {
	Username          string `json:"username"`          // username such as 'chickenandpork', exactly as typed in the login form
	PlaintextPassword string `json:"plaintextpassword"` // password, in plaintext, for login, exactly as typed in the login form
}

PlaintextAuth is a structure into which the username and password are read from a plaintext file. This is necessary because when this code is written, Hover offers no API, so raw logins are mimicked as clients. This has risks, of course. The trade-off is that plaintext risk versus no functionality means we have no alternative. For this reason, reading the auth from a file on disk means it cannot be offered in-code during integration tests, and similarly, can be provided by a configmap or similar during a production deployment.

For versatility, the intent is to accept JSON, YAML, and even XML if it's trivial to do.

func ReadConfigFile

func ReadConfigFile(filename string) (*PlaintextAuth, error)

ReadConfigFile reads a Plaintext Password struct from a JSON file. This is loosely named because it's intended to eventually try different formats on parse failure (ie try JSON, fallback to YAML, fallback to XML, shoot: fallback to CSV if we need to provide the most options with sufficient return on investment.

type User

type User struct {
	Billing struct {
		Description string `json:"description,omitempty"` // This seems to be a description of my card, such as "Visa ending 1234"
		PayMode     string `json:"pay_mode,omitempty"`    // some reference to how payments are processed:  mine all say "apple_pay", and they're in my Apple

	} `json:"billing,omitempty"`
	Email          string `json:"email,omitempty"`
	EmailSecondary string `json:"email_secondary,omitempty"`
}

The User record in a Domain seems to record additional contact information that augments the Billing Contact with the credit card used and some metadata around it.

type YALI

type YALI interface {
	Printf(format string, v ...interface{})
	Println(v ...interface{})
}

YALI -- Yet Another Logger Interface -- reduces the logger facility needed to as few functions as possible to allow others to be slotted in.

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

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