dns

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Mar 1, 2019 License: BSD-3-Clause Imports: 23 Imported by: 13

Documentation

Overview

Package dns implement DNS client and server.

This library implemented in reference to,

  • RFC1034 DOMAIN NAMES - CONCEPTS AND FACILITIES
  • RFC1035 DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION
  • RFC1886 DNS Extensions to support IP version 6.
  • RFC2782 A DNS RR for specifying the location of services (DNS SRV)
  • RFC6891 Extension Mechanisms for DNS (EDNS(0))

Index

Examples

Constants

View Source
const (
	// DefaultPort define default DNS remote or listen port for UDP and
	// TCP connection.
	DefaultPort uint16 = 53

	// DefaultDoHPort define default port for DoH.
	DefaultDoHPort uint16 = 443
)
View Source
const (
	QueryTypeZERO  uint16 = iota // Empty query type.
	QueryTypeA                   //  1 - A host address
	QueryTypeNS                  //  2 - An authoritative name server
	QueryTypeMD                  //  3 - A mail destination (Obsolete - use MX)
	QueryTypeMF                  //  4 - A mail forwarder (Obsolete - use MX)
	QueryTypeCNAME               //  5 - The canonical name for an alias
	QueryTypeSOA                 //  6 - Marks the start of a zone of authority
	QueryTypeMB                  //  7 - A mailbox domain name (EXPERIMENTAL)
	QueryTypeMG                  //  8 - A mail group member (EXPERIMENTAL)
	QueryTypeMR                  //  9 - A mail rename domain name (EXPERIMENTAL)
	QueryTypeNULL                // 10 - A null RR (EXPERIMENTAL)
	QueryTypeWKS                 // 11 - A well known service description
	QueryTypePTR                 // 12 - A domain name pointer
	QueryTypeHINFO               // 13 - Host information
	QueryTypeMINFO               // 14 - Mailbox or mail list information
	QueryTypeMX                  // 15 - Mail exchange
	QueryTypeTXT                 // 16 - Text strings
	QueryTypeAAAA  uint16 = 28   // IPv6 address
	QueryTypeSRV   uint16 = 33   // A SRV RR for locating service.
	QueryTypeOPT   uint16 = 41   // An OPT pseudo-RR (sometimes called a meta-RR)
	QueryTypeAXFR  uint16 = 252  // A request for a transfer of an entire zone
	QueryTypeMAILB uint16 = 253  // A request for mailbox-related records (MB, MG or MR)
	QueryTypeMAILA uint16 = 254  // A request for mail agent RRs (Obsolete - see MX)
	QueryTypeALL   uint16 = 255  // A request for all records
)

List of code for known DNS query types.

View Source
const (
	QueryClassZERO uint16 = iota // Empty query class.
	QueryClassIN                 // The Internet
	QueryClassCS                 // The CSNET class (Obsolete - used only for examples in some obsolete RFCs)
	QueryClassCH                 // The CHAOS class
	QueryClassHS                 // Hesiod [Dyer 87]
	QueryClassANY  uint16 = 255  // Any class
)

List of code known DNS query class.

View Source
const (
	HostsFilePOSIX   = "/etc/hosts"
	HostsFileWindows = "C:\\Windows\\System32\\Drivers\\etc\\hosts"
)

List of known hosts file by OS.

Variables

View Source
var (
	ErrNewConnection  = errors.New("lookup: can't create new connection")
	ErrLabelSizeLimit = errors.New("labels should be 63 octet or less")
	ErrInvalidAddress = errors.New("invalid address")
	ErrIPv4Length     = errors.New("invalid length of A RDATA format")
	ErrIPv6Length     = errors.New("invalid length of AAAA RDATA format")
)

List of error messages.

View Source
var ConnTypeNames = map[ConnType]string{
	ConnTypeUDP: "UDP",
	ConnTypeTCP: "TCP",
	ConnTypeDoH: "DoH",
}

ConnTypeNames contains a mapping between connection type and its name.

View Source
var QueryClasses = map[string]uint16{
	"IN": QueryClassIN,
	"CH": QueryClassCH,
	"HS": QueryClassHS,
}

QueryClasses contains a mapping between string representation of DNS query class with their decimal value.

View Source
var QueryTypeNames = map[uint16]string{
	QueryTypeA:     "A",
	QueryTypeNS:    "NS",
	QueryTypeCNAME: "CNAME",
	QueryTypeSOA:   "SOA",
	QueryTypeMB:    "MB",
	QueryTypeMG:    "MG",
	QueryTypeMR:    "MR",
	QueryTypeNULL:  "NULL",
	QueryTypeWKS:   "WKS",
	QueryTypePTR:   "PTR",
	QueryTypeHINFO: "HINFO",
	QueryTypeMINFO: "MINFO",
	QueryTypeMX:    "MX",
	QueryTypeTXT:   "TXT",
	QueryTypeAAAA:  "AAAA",
	QueryTypeSRV:   "SRV",
	QueryTypeOPT:   "OPT",
}

QueryTypeNames contains mapping between query type and and their string representation.

View Source
var QueryTypes = map[string]uint16{
	"A":     QueryTypeA,
	"NS":    QueryTypeNS,
	"CNAME": QueryTypeCNAME,
	"SOA":   QueryTypeSOA,
	"MB":    QueryTypeMB,
	"MG":    QueryTypeMG,
	"MR":    QueryTypeMR,
	"NULL":  QueryTypeNULL,
	"WKS":   QueryTypeWKS,
	"PTR":   QueryTypePTR,
	"HINFO": QueryTypeHINFO,
	"MINFO": QueryTypeMINFO,
	"MX":    QueryTypeMX,
	"TXT":   QueryTypeTXT,
	"AAAA":  QueryTypeAAAA,
	"SRV":   QueryTypeSRV,
	"OPT":   QueryTypeOPT,
}

QueryTypes contains a mapping between string representation of DNS query type with their decimal value.

Functions

func GetSystemHosts

func GetSystemHosts() string

GetSystemHosts return path to system hosts file.

func GetSystemNameServers

func GetSystemNameServers(path string) []string

GetSystemNameServers return list of system name servers by reading resolv.conf formatted file in path.

Default path is "/etc/resolv.conf".

func ParseNameServers

func ParseNameServers(nameservers []string) ([]*net.UDPAddr, error)

ParseNameServers parse list of nameserver into UDP addresses. If one of nameserver is invalid it will stop parsing and return only valid nameserver addresses with error.

Types

type Client

type Client interface {
	Close() error
	RemoteAddr() string
	Query(req *Message, ns net.Addr) (*Message, error)
	SetTimeout(t time.Duration)
	SetRemoteAddr(addr string) error
	Sender
	Receiver
}

Client is interface that implement sending and receiving DNS message.

type ConnType

type ConnType byte
const (
	ConnTypeUDP ConnType = 1 << iota
	ConnTypeTCP
	ConnTypeDoH
)

List of known connection type.

type DoHClient

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

DoHClient client for DNS over HTTPS.

func NewDoHClient

func NewDoHClient(nameserver string, allowInsecure bool) (*DoHClient, error)

NewDoHClient will create new DNS client with HTTP connection.

func (*DoHClient) Close

func (cl *DoHClient) Close() error

Close all idle connections.

func (*DoHClient) Get

func (cl *DoHClient) Get(msg *Message) (*Message, error)

Get send query to name server using HTTP GET and return the response as unpacked message.

func (*DoHClient) Lookup

func (cl *DoHClient) Lookup(qtype, qclass uint16, qname []byte) (*Message, error)

Lookup will query the DoH server with specific type, class, and name in synchronous mode.

func (*DoHClient) Post

func (cl *DoHClient) Post(msg *Message) (*Message, error)

Post send query to name server using HTTP POST and return the response as unpacked message.

func (*DoHClient) Query

func (cl *DoHClient) Query(msg *Message, ns net.Addr) (*Message, error)

Query send DNS query to name server. This is an alias to Get method. The addr parameter is unused.

func (*DoHClient) Recv

func (cl *DoHClient) Recv(msg *Message) (int, error)

Recv read response from channel.

func (*DoHClient) RemoteAddr

func (cl *DoHClient) RemoteAddr() string

RemoteAddr return client remote nameserver address.

func (*DoHClient) Send

func (cl *DoHClient) Send(msg *Message, ns net.Addr) (int, error)

Send DNS message to name server using Get method. Since HTTP client is synchronous, the response is forwarded to channel to be consumed by Recv().

func (*DoHClient) SetRemoteAddr

func (cl *DoHClient) SetRemoteAddr(addr string) (err error)

SetRemoteAddr set the remote address for sending the packet.

func (*DoHClient) SetTimeout

func (cl *DoHClient) SetTimeout(t time.Duration)

SetTimeout set the timeout for sending and receiving packet.

type Handler

type Handler interface {
	ServeDNS(*Request)
}

A Handler responds to DNS request.

type Message

type Message struct {
	Header     *SectionHeader
	Question   *SectionQuestion
	Answer     []*ResourceRecord
	Authority  []*ResourceRecord
	Additional []*ResourceRecord

	// Slice that hold the result of packing the message or original
	// message from unpacking.
	Packet []byte
	// contains filtered or unexported fields
}

Message represent a DNS message.

All communications inside of the domain protocol are carried in a single format called a message. The top level format of message is divided into 5 sections (some of which are empty in certain cases) shown below:

+---------------------+
|        Header       |
+---------------------+
|       Question      | the question for the name server
+---------------------+
|        Answer       | RRs answering the question
+---------------------+
|      Authority      | RRs pointing toward an authority
+---------------------+
|      Additional     | RRs holding additional information
+---------------------+

The names of the sections after the header are derived from their use in standard queries. The question section contains fields that describe a question to a name server. These fields are a query type (QTYPE), a query class (QCLASS), and a query domain name (QNAME). The last three sections have the same format: a possibly empty list of concatenated resource records (RRs). The answer section contains RRs that answer the question; the authority section contains RRs that point toward an authoritative name server; the additional records section contains RRs which relate to the query, but are not strictly answers for the question. [1]

[1] RFC 1035 - 4.1. Format

func HostsLoad

func HostsLoad(path string) (msgs []*Message, err error)

HostsLoad parse the content of hosts file as packed DNS message. If path is empty, it will load from the system hosts file.

func MasterLoad

func MasterLoad(file, origin string, ttl uint32) ([]*Message, error)

MasterLoad parse master file and return it as list of Message. The base path of file will be assumed as origin.

func NewMessage

func NewMessage() *Message

NewMessage create, initialize, and return new message.

func (*Message) FilterAnswers

func (msg *Message) FilterAnswers(t uint16) (answers []*ResourceRecord)

FilterAnswers return resource record in Answer that match only with specific query type.

func (*Message) IsExpired

func (msg *Message) IsExpired(elapsed uint32) bool

IsExpired will return true if at least one resource record is expired, their TTL value is equal or less than elapsed value; otherwise it will return false.

func (*Message) Pack

func (msg *Message) Pack() ([]byte, error)

Pack convert message into datagram packet. The result of packing a message will be saved in Packet field and returned.

func (*Message) Reset

func (msg *Message) Reset()

Reset the message fields.

func (*Message) ResetRR

func (msg *Message) ResetRR()

ResetRR free allocated resource records in message. This function can be used to release some memory after message has been packed, but the raw packet may still be in use.

func (*Message) SetID

func (msg *Message) SetID(id uint16)

SetID in section header and in packet.

func (*Message) String

func (msg *Message) String() string

String return the message representation as string.

func (*Message) SubTTL

func (msg *Message) SubTTL(n uint32)

SubTTL subtract TTL in each resource records and in packet by n seconds. If TTL is less than n, it will set to 0.

func (*Message) Unpack

func (msg *Message) Unpack() (err error)

Unpack the packet to fill the message fields.

func (*Message) UnpackHeaderQuestion

func (msg *Message) UnpackHeaderQuestion()

UnpackHeaderQuestion extract only DNS header and question from message packet. This method assume that message.Packet already set to DNS raw message.

type OpCode

type OpCode byte

OpCode define a custom type for DNS header operation code.

const (
	OpCodeQuery  OpCode = iota // a standard query (QUERY)
	OpCodeIQuery               // an inverse query (IQUERY), obsolete by RFC3425
	OpCodeStatus               // a server status request (STATUS)
)

List of valid operation code.

type RDataHINFO

type RDataHINFO struct {
	CPU []byte
	OS  []byte
}

RDataHINFO HINFO records are used to acquire general information about a host. The main use is for protocols such as FTP that can use special procedures when talking between machines or operating systems of the same type.

func (*RDataHINFO) String

func (hinfo *RDataHINFO) String() string

String return readable representation of HINFO record.

type RDataMINFO

type RDataMINFO struct {
	// A <domain-name> which specifies a mailbox which is responsible for
	// the mailing list or mailbox.  If this domain name names the root,
	// the owner of the MINFO RR is responsible for itself.  Note that
	// many existing mailing lists use a mailbox X-request for the RMAILBX
	// field of mailing list X, e.g., Msgroup-request for Msgroup.  This
	// field provides a more general mechanism.
	RMailBox []byte

	// A <domain-name> which specifies a mailbox which is to receive error
	// messages related to the mailing list or mailbox specified by the
	// owner of the MINFO RR (similar to the ERRORS-TO: field which has
	// been proposed).  If this domain name names the root, errors should
	// be returned to the sender of the message.
	EmailBox []byte
}

RDataMINFO define a resource record for type MINFO.

func (*RDataMINFO) String

func (minfo *RDataMINFO) String() string

String return readable representation of MINFO record.

type RDataMX

type RDataMX struct {
	// A 16 bit integer which specifies the preference given to this RR
	// among others at the same owner.  Lower values are preferred.
	Preference int16

	// A <domain-name> which specifies a host willing to act as a mail
	// exchange for the owner name.
	Exchange []byte
}

RDataMX MX records cause type A additional section processing for the host specified by EXCHANGE. The use of MX RRs is explained in detail in [RFC-974].

func (*RDataMX) String

func (mx *RDataMX) String() string

String return readable representation of MX record.

type RDataOPT

type RDataOPT struct {
	// Forms the upper 8 bits of extended 12-bit RCODE (together with the
	// 4 bits defined in [RFC1035].  Note that EXTENDED-RCODE value 0
	// indicates that an unextended RCODE is in use (values 0 through 15).
	ExtRCode byte

	// Indicates the implementation level of the setter.  Full conformance
	// with this specification is indicated by version '0'.  Requestors
	// are encouraged to set this to the lowest implemented level capable
	// of expressing a transaction, to minimise the responder and network
	// load of discovering the greatest common implementation level
	// between requestor and responder.  A requestor's version numbering
	// strategy MAY ideally be a run-time configuration option.
	Version byte

	// DNSSEC OK bit as defined by [RFC3225].
	DO bool

	// Assigned by the Expert Review process as defined by the DNSEXT
	// working group and the IESG.
	Code uint16

	// Size (in octets) of OPTION-DATA.
	Length uint16

	// Varies per OPTION-CODE.  MUST be treated as a bit field.
	Data []byte
}

RDataOPT define format of RDATA for OPT.

The extended RCODE and flags, which OPT stores in the RR Time to Live (TTL) field, contains ExtRCode, Version

func (*RDataOPT) String

func (opt *RDataOPT) String() string

String return readable representation of OPT record.

type RDataSOA

type RDataSOA struct {
	// The <domain-name> of the name server that was the original or
	// primary source of data for this zone.
	MName []byte

	// A <domain-name> which specifies the mailbox of the person
	// responsible for this zone.
	RName []byte

	// The unsigned 32 bit version number of the original copy of the
	// zone.  Zone transfers preserve this value.  This value wraps and
	// should be compared using sequence space arithmetic.
	Serial uint32

	// A 32 bit time interval before the zone should be refreshed.
	Refresh int32

	// A 32 bit time interval that should elapse before a failed refresh
	// should be retried.
	Retry int32

	// A 32 bit time value that specifies the upper limit on the time
	// interval that can elapse before the zone is no longer
	// authoritative.
	Expire int32

	// The unsigned 32 bit minimum TTL field that should be exported with
	// any RR from this zone.
	Minimum uint32
}

RDataSOA represent SOA RDATA format in resource record.

All times are in units of seconds.

Most of these fields are pertinent only for name server maintenance operations. However, MINIMUM is used in all query operations that retrieve RRs from a zone. Whenever a RR is sent in a response to a query, the TTL field is set to the maximum of the TTL field from the RR and the MINIMUM field in the appropriate SOA. Thus MINIMUM is a lower bound on the TTL field for all RRs in a zone. Note that this use of MINIMUM should occur when the RRs are copied into the response and not when the zone is loaded from a master file or via a zone transfer. The reason for this provison is to allow future dynamic update facilities to change the SOA RR with known semantics.

func (*RDataSOA) String

func (soa *RDataSOA) String() string

String return readable representation of SOA record.

type RDataSRV

type RDataSRV struct {
	// The symbolic name of the desired service, as defined in Assigned
	// Numbers [STD 2] or locally.  An underscore (_) is prepended to
	// the service identifier to avoid collisions with DNS labels that
	// occur in nature.
	//
	// Some widely used services, notably POP, don't have a single
	// universal name.  If Assigned Numbers names the service
	// indicated, that name is the only name which is legal for SRV
	// lookups.  The Service is case insensitive.
	Service []byte

	// The symbolic name of the desired protocol, with an underscore
	// (_) prepended to prevent collisions with DNS labels that occur
	// in nature.  _TCP and _UDP are at present the most useful values
	// for this field, though any name defined by Assigned Numbers or
	// locally may be used (as for Service).  The Proto is case
	// insensitive.
	Proto []byte

	// The domain this RR refers to.  The SRV RR is unique in that the
	// name one searches for is not this name; the example near the end
	// shows this clearly.
	Name []byte

	// The priority of this target host.  A client MUST attempt to
	// contact the target host with the lowest-numbered priority it can
	// reach; target hosts with the same priority SHOULD be tried in an
	// order defined by the weight field.  The range is 0-65535.  This
	// is a 16 bit unsigned integer in network byte order.
	Priority uint16

	// A server selection mechanism.  The weight field specifies a
	// relative weight for entries with the same priority. Larger
	// weights SHOULD be given a proportionately higher probability of
	// being selected. The range of this number is 0-65535.  This is a
	// 16 bit unsigned integer in network byte order.  Domain
	// administrators SHOULD use Weight 0 when there isn't any server
	// selection to do, to make the RR easier to read for humans (less
	// noisy).  In the presence of records containing weights greater
	// than 0, records with weight 0 should have a very small chance of
	// being selected.
	//
	// In the absence of a protocol whose specification calls for the
	// use of other weighting information, a client arranges the SRV
	// RRs of the same Priority in the order in which target hosts,
	// specified by the SRV RRs, will be contacted. The following
	// algorithm SHOULD be used to order the SRV RRs of the same
	// priority:
	//
	// To select a target to be contacted next, arrange all SRV RRs
	// (that have not been ordered yet) in any order, except that all
	// those with weight 0 are placed at the beginning of the list.
	//
	// Compute the sum of the weights of those RRs, and with each RR
	// associate the running sum in the selected order. Then choose a
	// uniform random number between 0 and the sum computed
	// (inclusive), and select the RR whose running sum value is the
	// first in the selected order which is greater than or equal to
	// the random number selected. The target host specified in the
	// selected SRV RR is the next one to be contacted by the client.
	// Remove this SRV RR from the set of the unordered SRV RRs and
	// apply the described algorithm to the unordered SRV RRs to select
	// the next target host.  Continue the ordering process until there
	// are no unordered SRV RRs.  This process is repeated for each
	// Priority.
	Weight uint16

	// The port on this target host of this service.  The range is 0-
	// 65535.  This is a 16 bit unsigned integer in network byte order.
	// This is often as specified in Assigned Numbers but need not be.
	Port uint16

	// The domain name of the target host.  There MUST be one or more
	// address records for this name, the name MUST NOT be an alias (in
	// the sense of RFC 1034 or RFC 2181).  Implementors are urged, but
	// not required, to return the address record(s) in the Additional
	// Data section.  Unless and until permitted by future standards
	// action, name compression is not to be used for this field.
	//
	// A Target of "." means that the service is decidedly not
	// available at this domain.
	Target []byte
}

RDataSRV define a resource record for type SRV.

func (*RDataSRV) String

func (srv *RDataSRV) String() string

String return readable representation of SRV record.

type RDataText

type RDataText struct {
	Value []byte
}

RDataText represent generic domain-name or text for NS, CNAME, MB, MG, and TEXT RDATA format.

func (*RDataText) String

func (text *RDataText) String() string

String return string representation of RDATA.

type RDataWKS

type RDataWKS struct {
	Address  []byte
	Protocol byte
	BitMap   []byte
}

RDataWKS The WKS record is used to describe the well known services supported by a particular protocol on a particular internet address. The PROTOCOL field specifies an IP protocol number, and the bit map has one bit per port of the specified protocol. The first bit corresponds to port 0, the second to port 1, etc. If the bit map does not include a bit for a protocol of interest, that bit is assumed zero. The appropriate values and mnemonics for ports and protocols are specified in [RFC-1010].

For example, if PROTOCOL=TCP (6), the 26th bit corresponds to TCP port 25 (SMTP). If this bit is set, a SMTP server should be listening on TCP port 25; if zero, SMTP service is not supported on the specified address.

The purpose of WKS RRs is to provide availability information for servers for TCP and UDP. If a server supports both TCP and UDP, or has multiple Internet addresses, then multiple WKS RRs are used.

func (*RDataWKS) String

func (wks *RDataWKS) String() string

String return readable representation of WKS record.

type Receiver

type Receiver interface {
	Recv(msg *Message) (n int, err error)
}

Receiver is interface for implementing receiving DNS message.

type Request

type Request struct {
	// Kind define the connection type that this request is belong to,
	// e.g. UDP, TCP, or DoH.
	Kind ConnType

	// Message define the DNS query.
	Message *Message

	// UDPAddr is address of client if connection is from UDP.
	UDPAddr *net.UDPAddr

	// Sender is server connection that receive the query and responsible
	// to answer back to client.
	Sender Sender

	// ResponseWriter is HTTP response writer, where answer for DoH
	// client query will be written.
	ResponseWriter http.ResponseWriter

	// ChanResponded is a channel that notify the DoH handler when answer
	// has been written to ResponseWriter.
	ChanResponded chan bool
}

Request contains UDP address and DNS query message from client.

If Kind is UDP, Sender and UDPAddr must be non nil. If Kind is TCP, Sender must be non nil. If Kind is DoH, both Sender and UDPAddr must be nil and ResponseWriter and ChanResponded must be non nil and initialized.

func NewRequest

func NewRequest() *Request

NewRequest create and initialize request.

func (*Request) Reset

func (req *Request) Reset()

Reset message and UDP address in request.

type ResourceRecord

type ResourceRecord struct {
	// A domain name to which this resource record pertains.
	Name []byte

	// Two octets containing one of the RR type codes.  This field
	// specifies the meaning of the data in the RDATA field.
	Type uint16

	// Two octets which specify the class of the data in the RDATA field.
	Class uint16

	// A 32 bit unsigned integer that specifies the time interval (in
	// seconds) that the resource record may be cached before it should be
	// discarded.  Zero values are interpreted to mean that the RR can
	// only be used for the transaction in progress, and should not be
	// cached.
	TTL uint32

	// Text represent A, NS, CNAME, MB, MG, NULL, PTR, and TXT.
	Text  *RDataText
	SOA   *RDataSOA
	WKS   *RDataWKS
	HInfo *RDataHINFO
	MInfo *RDataMINFO
	MX    *RDataMX
	OPT   *RDataOPT
	SRV   *RDataSRV
	// contains filtered or unexported fields
}

ResourceRecord The answer, authority, and additional sections all share the same format: a variable number of resource records, where the number of records is specified in the corresponding count field in the header. Each resource record has the following format:

func (*ResourceRecord) RData

func (rr *ResourceRecord) RData() interface{}

RData will return slice of bytes, the pointer that hold specific record data, or nil for obsolete type.

For RR with type A, NS, CNAME, MB, MG, MR, NULL, PTR, TXT or AAAA, it will return it as slice of bytes.

For RR with type SOA, WKS, HINFO, MINFO, MX, OPT, or SRV it will return pointer to specific record type.

For RR with obsolete type (MD or MF) it will return nil.

func (*ResourceRecord) Reset

func (rr *ResourceRecord) Reset()

Reset the resource record fields to zero values.

func (*ResourceRecord) String

func (rr *ResourceRecord) String() string

type ResponseCode

type ResponseCode byte

ResponseCode define response code in message header.

const (
	RCodeOK ResponseCode = iota //  No error condition

	// Format error - The name server was unable to interpret the query.
	RCodeErrFormat

	// Server failure - The name server was unable to process this query
	// due to a problem with the name server.
	RCodeErrServer

	// Name Error - Meaningful only for responses from an authoritative
	// name server, this code signifies that the domain name referenced in
	// the query does not exist.
	RCodeErrName

	// Not Implemented - The name server does not support the requested
	// kind of query.
	RCodeNotImplemented

	// Refused - The name server refuses to perform the specified
	// operation for policy reasons.  For example, a name server may not
	// wish to provide the information to the particular requester, or a
	// name server may not wish to perform a particular operation (e.g.,
	// zone transfer) for particular data.
	RCodeRefused
)

List of response codes.

type SectionHeader

type SectionHeader struct {
	//
	// A 16 bit identifier assigned by the program that generates
	// any kind of query.  This identifier is copied the corresponding
	// reply and can be used by the requester to match up replies to
	// outstanding queries.
	//
	ID uint16

	//
	// A one bit field that specifies whether this message is a query (0),
	// or a response (1).
	//
	IsQuery bool

	//
	// A four bit field that specifies kind of query in this message.
	// This value is set by the originator of a query and copied into the
	// response.
	//
	Op OpCode

	//
	// Authoritative Answer - this bit is valid in responses, and
	// specifies that the responding name server is an authority for the
	// domain name in question section.  Note that the contents of the
	// answer section may have multiple owner names because of aliases.
	// The AA bit corresponds to the name which matches the query name, or
	// the first owner name in the answer section.
	//
	IsAA bool

	//
	// TrunCation - specifies that this message was truncated due to
	// length greater than that permitted on the transmission channel.
	//
	IsTC bool

	//
	// Recursion Desired - this bit may be set in a query and is copied
	// into the response.  If RD is set, it directs the name server to
	// pursue the query recursively.  Recursive query support is optional.
	//
	IsRD bool

	//
	// Recursion Available - this bit is set or cleared in a response, and
	// denotes whether recursive query support is available in the name
	// server.
	//
	IsRA bool

	//
	// Response code - this 4 bit field is set as part of responses.
	//
	RCode ResponseCode

	// An unsigned 16 bit integer specifying the number of entries in the
	// question section.
	QDCount uint16

	// An unsigned 16 bit integer specifying the number of resource
	// records in the answer section.
	ANCount uint16

	// An unsigned 16 bit integer specifying the number of name server
	// resource records in the authority records section.
	NSCount uint16

	// An unsigned 16 bit integer specifying the number of resource
	// records in the additional records section.
	ARCount uint16
}

SectionHeader The header section is always present. The header includes fields that specify which of the remaining sections are present, and also specify whether the message is a query or a response, a standard query or some other opcode, etc. [1]

[1] RFC 1035 P-25 - 4.1. Format

func (*SectionHeader) Reset

func (hdr *SectionHeader) Reset()

Reset the header to default (query) values.

type SectionQuestion

type SectionQuestion struct {
	// A domain name represented as a sequence of labels, where each label
	// consists of a length octet followed by that number of octets.  The
	// domain name terminates with the zero length octet for the null
	// label of the root.  Note that this field may be an odd number of
	// octets; no padding is used.
	Name []byte

	// A two octet code which specifies the type of the query.  The values
	// for this field include all codes valid for a TYPE field, together
	// with some more general codes which can match more than one type of
	// RR.
	Type uint16

	// A two octet code that specifies the class of the query.  For
	// example, the QCLASS field is IN for the Internet.
	Class uint16
}

SectionQuestion The question section is used to carry the "question" in most queries, i.e., the parameters that define what is being asked. The section contains QDCOUNT (usually 1) entries, each of the following format:

func (*SectionQuestion) Reset

func (question *SectionQuestion) Reset()

Reset the message question field to it's default values for query.

func (*SectionQuestion) String

func (question *SectionQuestion) String() string

String will return the string representation of section question structure.

type Sender

type Sender interface {
	Send(msg *Message, addr net.Addr) (n int, err error)
}

Sender is interface that for implementing sending DNS message.

type Server

type Server struct {
	Handler Handler
	// contains filtered or unexported fields
}

Server defines DNS server.

Example
package main

import (
	"fmt"
	"log"

	"github.com/shuLhan/share/lib/dns"
)

type serverHandler struct {
	responses []*dns.Message
}

func (h *serverHandler) generateResponses() {
	// kilabit.info A
	res := &dns.Message{
		Header: &dns.SectionHeader{
			ID:      1,
			QDCount: 1,
			ANCount: 1,
		},
		Question: &dns.SectionQuestion{
			Name:  []byte("kilabit.info"),
			Type:  dns.QueryTypeA,
			Class: dns.QueryClassIN,
		},
		Answer: []*dns.ResourceRecord{{
			Name:  []byte("kilabit.info"),
			Type:  dns.QueryTypeA,
			Class: dns.QueryClassIN,
			TTL:   3600,
			Text: &dns.RDataText{
				Value: []byte("127.0.0.1"),
			},
		}},
		Authority:  []*dns.ResourceRecord{},
		Additional: []*dns.ResourceRecord{},
	}

	_, err := res.Pack()
	if err != nil {
		log.Fatal("Pack: ", err)
	}

	h.responses = append(h.responses, res)

	// kilabit.info SOA
	res = &dns.Message{
		Header: &dns.SectionHeader{
			ID:      2,
			QDCount: 1,
			ANCount: 1,
		},
		Question: &dns.SectionQuestion{
			Name:  []byte("kilabit.info"),
			Type:  dns.QueryTypeSOA,
			Class: dns.QueryClassIN,
		},
		Answer: []*dns.ResourceRecord{{
			Name:  []byte("kilabit.info"),
			Type:  dns.QueryTypeSOA,
			Class: dns.QueryClassIN,
			TTL:   3600,
			SOA: &dns.RDataSOA{
				MName:   []byte("kilabit.info"),
				RName:   []byte("admin.kilabit.info"),
				Serial:  20180832,
				Refresh: 3600,
				Retry:   60,
				Expire:  3600,
				Minimum: 3600,
			},
		}},
		Authority:  []*dns.ResourceRecord{},
		Additional: []*dns.ResourceRecord{},
	}

	_, err = res.Pack()
	if err != nil {
		log.Fatal("Pack: ", err)
	}

	h.responses = append(h.responses, res)

	// kilabit.info TXT
	res = &dns.Message{
		Header: &dns.SectionHeader{
			ID:      3,
			QDCount: 1,
			ANCount: 1,
		},
		Question: &dns.SectionQuestion{
			Name:  []byte("kilabit.info"),
			Type:  dns.QueryTypeTXT,
			Class: dns.QueryClassIN,
		},
		Answer: []*dns.ResourceRecord{{
			Name:  []byte("kilabit.info"),
			Type:  dns.QueryTypeTXT,
			Class: dns.QueryClassIN,
			TTL:   3600,
			Text: &dns.RDataText{
				Value: []byte("This is a test server"),
			},
		}},
		Authority:  []*dns.ResourceRecord{},
		Additional: []*dns.ResourceRecord{},
	}

	_, err = res.Pack()
	if err != nil {
		log.Fatal("Pack: ", err)
	}

	h.responses = append(h.responses, res)
}

func (h *serverHandler) ServeDNS(req *dns.Request) {
	var (
		res *dns.Message
		err error
	)

	qname := string(req.Message.Question.Name)
	if qname == "kilabit.info" {
		switch req.Message.Question.Type {
		case dns.QueryTypeA:
			res = h.responses[0]
		case dns.QueryTypeSOA:
			res = h.responses[1]
		case dns.QueryTypeTXT:
			res = h.responses[2]
		}
	}

	// Return empty answer
	if res == nil {
		res := &dns.Message{
			Header: &dns.SectionHeader{
				ID:      req.Message.Header.ID,
				QDCount: 1,
			},
			Question: req.Message.Question,
		}

		_, err = res.Pack()
		if err != nil {
			return
		}
	} else {
		res.SetID(req.Message.Header.ID)
	}

	switch req.Kind {
	case dns.ConnTypeUDP:
		if req.Sender != nil {
			_, err = req.Sender.Send(res, req.UDPAddr)
			if err != nil {
				log.Println("! ServeDNS: Sender.Send: ", err)
			}
		}

	case dns.ConnTypeTCP:
		if req.Sender != nil {
			_, err = req.Sender.Send(res, nil)
			if err != nil {
				log.Println("! ServeDNS: Sender.Send: ", err)
			}
		}

	case dns.ConnTypeDoH:
		if req.ResponseWriter != nil {
			_, err = req.ResponseWriter.Write(res.Packet)
			if err != nil {
				log.Println("! ServeDNS: ResponseWriter.Write: ", err)
			}
			req.ChanResponded <- true
		}
	}
}

func clientLookup(nameserver string) {
	cl, err := dns.NewUDPClient(nameserver)
	if err != nil {
		log.Println(err)
		return
	}

	msg, err := cl.Lookup(dns.QueryTypeA, dns.QueryClassIN, []byte("kilabit.info"))
	if err != nil {
		log.Println(err)
		return
	}

	fmt.Printf("Receiving DNS message: %s\n", msg)
	for x, answer := range msg.Answer {
		fmt.Printf("Answer %d: %s\n", x, answer.RData())
	}
	for x, auth := range msg.Authority {
		fmt.Printf("Authority %d: %s\n", x, auth.RData())
	}
	for x, add := range msg.Additional {
		fmt.Printf("Additional %d: %s\n", x, add.RData())
	}
}

func main() {
	serverAddress := "127.0.0.1:5300"

	handler := &serverHandler{}

	handler.generateResponses()

	server := &dns.Server{
		Handler: handler,
	}

	serverOptions := &dns.ServerOptions{
		IPAddress:        "127.0.0.1",
		TCPPort:          5300,
		UDPPort:          5300,
		DoHPort:          8443,
		DoHCert:          "testdata/domain.crt",
		DoHCertKey:       "testdata/domain.key",
		DoHAllowInsecure: true,
	}

	go func() {
		err := server.ListenAndServe(serverOptions)
		if err != nil {
			log.Fatal("ListenAndServe: ", err)
		}
	}()

	clientLookup(serverAddress)
}
Output:

func (*Server) ListenAndServe

func (srv *Server) ListenAndServe(opts *ServerOptions) error

ListenAndServe run DNS server on UDP, TCP, or DNS over HTTP (DoH).

func (*Server) ListenAndServeDoH

func (srv *Server) ListenAndServeDoH(opts *ServerOptions) error

ListenAndServeDoH listen for request over HTTPS using certificate and key file in parameter. The path to request is static "/dns-query".

func (*Server) ListenAndServeTCP

func (srv *Server) ListenAndServeTCP(tcpAddr *net.TCPAddr) error

ListenAndServeTCP listen for request with TCP socket.

func (*Server) ListenAndServeUDP

func (srv *Server) ListenAndServeUDP(udpAddr *net.UDPAddr) (err error)

ListenAndServeUDP listen for request with UDP socket.

func (*Server) ServeHTTP

func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)

type ServerOptions

type ServerOptions struct {
	// IPAddress of server to listen to, without port number.
	IPAddress string

	// DoHCert path to certificate file for serving DoH.
	DoHCert string

	// DoHCertKey path to certificate key file for serving DoH.
	DoHCertKey string

	// DoHIdleTimeout number of seconds before considering the client of
	// DoH connection to be closed.
	DoHIdleTimeout time.Duration

	// UDPPort port for UDP server, default to 53.
	UDPPort uint16

	// TCPPort port for TCP server, default to 53.
	TCPPort uint16

	// DoHPort port for listening DNS over HTTP, default to 443.
	DoHPort uint16

	// DoHAllowInsecure options to allow to serve DoH with self-signed
	// certificate.
	DoHAllowInsecure bool
	// contains filtered or unexported fields
}

ServerOptions describes options for running a DNS server. If certificate or key file is empty, server will not run with DNS over HTTPS (DoH).

type TCPClient

type TCPClient struct {
	Timeout time.Duration
	// contains filtered or unexported fields
}

TCPClient for DNS with TCP connection and list of remote addresses.

func NewTCPClient

func NewTCPClient(nameserver string) (*TCPClient, error)

NewTCPClient will create new DNS client with TCP network connection.

The nameserver contains the IP address, not host name, of parent DNS server. Default port is 53, if not set.

func (*TCPClient) Close

func (cl *TCPClient) Close() error

Close client connection.

func (*TCPClient) Connect

func (cl *TCPClient) Connect(raddr *net.TCPAddr) (err error)

Connect to remote address.

func (*TCPClient) Lookup

func (cl *TCPClient) Lookup(qtype uint16, qclass uint16, qname []byte) (
	*Message, error,
)

Lookup will query one of the name server with specific type, class, and name in synchronous mode.

Name could be a host name for standard query or IP address for inverse query.

This function is safe to be used concurrently.

Example
package main

import (
	"fmt"
	"log"

	"github.com/shuLhan/share/lib/dns"
)

func main() {
	cl, err := dns.NewTCPClient("127.0.0.1:53")
	if err != nil {
		log.Println(err)
		return
	}

	msg, err := cl.Lookup(dns.QueryTypeA, dns.QueryClassIN, []byte("kilabit.info"))
	if err != nil {
		log.Println(err)
		return
	}

	fmt.Printf("Receiving DNS message: %s\n", msg)
	for x, answer := range msg.Answer {
		fmt.Printf("Answer %d: %s\n", x, answer.RData())
	}
	for x, auth := range msg.Authority {
		fmt.Printf("Authority %d: %s\n", x, auth.RData())
	}
	for x, add := range msg.Additional {
		fmt.Printf("Additional %d: %s\n", x, add.RData())
	}
}
Output:

func (*TCPClient) Query

func (cl *TCPClient) Query(msg *Message, ns net.Addr) (*Message, error)

Query send DNS query to name server. The addr parameter is unused.

func (*TCPClient) Recv

func (cl *TCPClient) Recv(msg *Message) (n int, err error)

Recv will read DNS message from active connection in client into `msg`.

func (*TCPClient) RemoteAddr

func (cl *TCPClient) RemoteAddr() string

RemoteAddr return client remote nameserver address.

func (*TCPClient) Send

func (cl *TCPClient) Send(msg *Message, addr net.Addr) (n int, err error)

Send DNS message to name server using active connection in client.

The message packet must already been filled, using Pack(). The addr parameter is unused.

func (*TCPClient) SetRemoteAddr

func (cl *TCPClient) SetRemoteAddr(addr string) (err error)

SetRemoteAddr set the remote address for sending the packet.

func (*TCPClient) SetTimeout

func (cl *TCPClient) SetTimeout(t time.Duration)

SetTimeout set the timeout for sending and receiving packet.

type UDPClient

type UDPClient struct {
	Timeout time.Duration
	Addr    *net.UDPAddr // Addr contains address of remote nameserver.
	Conn    *net.UDPConn
	sync.Mutex
}

UDPClient for DNS with UDP connection.

Any implementation that need to query DNS message in multiple Go routines should create one client per routine.

Example

The following example show how to use send and Recv to query domain name address.

package main

import (
	"fmt"
	"log"

	"github.com/shuLhan/share/lib/dns"
	libnet "github.com/shuLhan/share/lib/net"
)

func main() {
	cl, err := dns.NewUDPClient("127.0.0.1:53")
	if err != nil {
		log.Println(err)
		return
	}

	ns, err := libnet.ParseUDPAddr("127.0.0.1", 53)
	if err != nil {
		log.Println(err)
		return
	}

	req := &dns.Message{
		Header: &dns.SectionHeader{},
		Question: &dns.SectionQuestion{
			Name:  []byte("kilabit.info"),
			Type:  dns.QueryTypeA,
			Class: dns.QueryClassIN,
		},
	}

	_, err = req.Pack()
	if err != nil {
		log.Println(err)
		return
	}

	_, err = cl.Send(req, ns)
	if err != nil {
		log.Println(err)
		return
	}

	res := dns.NewMessage()

	_, err = cl.Recv(res)
	if err != nil {
		log.Println(err)
		return
	}

	err = res.Unpack()
	if err != nil {
		log.Fatal("res.Unpack: ", err)
	}

	fmt.Printf("Receiving DNS message: %s\n", res)
	for x, answer := range res.Answer {
		fmt.Printf("Answer %d: %s\n", x, answer.RData())
	}
	for x, auth := range res.Authority {
		fmt.Printf("Authority %d: %s\n", x, auth.RData())
	}
	for x, add := range res.Additional {
		fmt.Printf("Additional %d: %s\n", x, add.RData())
	}
}
Output:

func NewUDPClient

func NewUDPClient(nameserver string) (cl *UDPClient, err error)

NewUDPClient will create new DNS client with UDP network connection.

The nameserver contains the IP address, not host name, of parent DNS server. Default port is 53, if not set.

func (*UDPClient) Close

func (cl *UDPClient) Close() error

Close client connection.

func (*UDPClient) Lookup

func (cl *UDPClient) Lookup(qtype uint16, qclass uint16, qname []byte) (
	*Message, error,
)

Lookup will query one of the name server with specific type, class, and name in synchronous mode.

Name could be a host name for standard query or IP address for inverse query.

This function is safe to be used concurrently.

Example
package main

import (
	"fmt"
	"log"

	"github.com/shuLhan/share/lib/dns"
)

func main() {
	cl, err := dns.NewUDPClient("127.0.0.1:53")
	if err != nil {
		log.Println(err)
		return
	}

	msg, err := cl.Lookup(dns.QueryTypeA, dns.QueryClassIN, []byte("kilabit.info"))
	if err != nil {
		log.Println(err)
		return
	}

	fmt.Printf("Receiving DNS message: %s\n", msg)
	for x, answer := range msg.Answer {
		fmt.Printf("Answer %d: %s\n", x, answer.RData())
	}
	for x, auth := range msg.Authority {
		fmt.Printf("Authority %d: %s\n", x, auth.RData())
	}
	for x, add := range msg.Additional {
		fmt.Printf("Additional %d: %s\n", x, add.RData())
	}
}
Output:

func (*UDPClient) Query

func (cl *UDPClient) Query(msg *Message, ns net.Addr) (*Message, error)

Query send DNS query to name server "ns" and return the unpacked response.

func (*UDPClient) Recv

func (cl *UDPClient) Recv(msg *Message) (n int, err error)

Recv will read DNS message from active connection in client into `msg`.

func (*UDPClient) RemoteAddr

func (cl *UDPClient) RemoteAddr() string

RemoteAddr return client remote nameserver address.

func (*UDPClient) Send

func (cl *UDPClient) Send(msg *Message, ns net.Addr) (n int, err error)

Send DNS message to name server using active connection in client.

The message packet must already been filled, using Pack(). The addr parameter must not be nil.

func (*UDPClient) SetRemoteAddr

func (cl *UDPClient) SetRemoteAddr(addr string) (err error)

SetRemoteAddr set the remote address for sending the packet.

func (*UDPClient) SetTimeout

func (cl *UDPClient) SetTimeout(t time.Duration)

SetTimeout set the timeout for sending and receiving packet.

type UDPClientPool

type UDPClientPool struct {
	sync.Mutex
	// contains filtered or unexported fields
}

UDPClientPool contains a pool of UDP client connections.

Any implementation that access UDPClient in multiple Go routines should create one client per Go routine; instead of using a single UDP client. The reason for this is because UDP packet is asynchronous.

WARNING: using pooling is only works if client only call Lookup or Query. If implementation call Send() n client connection, make sure, it also call Recv on the same routine before putting the client back to pool.

func NewUDPClientPool

func NewUDPClientPool(nameServers []string) (ucp *UDPClientPool, err error)

NewUDPClientPool create pool for UDP connection using list of name servers. If no name servers is defined it will return nil.

func (*UDPClientPool) Get

func (ucp *UDPClientPool) Get() *UDPClient

Get return UDP client.

func (*UDPClientPool) Put

func (ucp *UDPClientPool) Put(cl interface{})

Put the UDP client into pool.

WARNING: any client connection that call Send(), MUST call Recv() before putting client back to pool. You have been warned.

Jump to

Keyboard shortcuts

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