mirror of
https://github.com/fumiama/terasu-cloudflared.git
synced 2026-06-08 03:55:11 +08:00
TUN-528: Move cloudflared into a separate repo
This commit is contained in:
8
vendor/github.com/miekg/dns/.codecov.yml
generated
vendored
Normal file
8
vendor/github.com/miekg/dns/.codecov.yml
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
target: 40%
|
||||
threshold: null
|
||||
patch: false
|
||||
changes: false
|
||||
4
vendor/github.com/miekg/dns/.gitignore
generated
vendored
Normal file
4
vendor/github.com/miekg/dns/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
*.6
|
||||
tags
|
||||
test.out
|
||||
a.out
|
||||
20
vendor/github.com/miekg/dns/.travis.yml
generated
vendored
Normal file
20
vendor/github.com/miekg/dns/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
language: go
|
||||
sudo: false
|
||||
go:
|
||||
- 1.9.x
|
||||
- tip
|
||||
|
||||
env:
|
||||
- TESTS="-race -v -bench=. -coverprofile=coverage.txt -covermode=atomic"
|
||||
- TESTS="-race -v ./..."
|
||||
|
||||
before_install:
|
||||
# don't use the miekg/dns when testing forks
|
||||
- mkdir -p $GOPATH/src/github.com/miekg
|
||||
- ln -s $TRAVIS_BUILD_DIR $GOPATH/src/github.com/miekg/ || true
|
||||
|
||||
script:
|
||||
- go test $TESTS
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
1
vendor/github.com/miekg/dns/AUTHORS
generated
vendored
Normal file
1
vendor/github.com/miekg/dns/AUTHORS
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Miek Gieben <miek@miek.nl>
|
||||
10
vendor/github.com/miekg/dns/CONTRIBUTORS
generated
vendored
Normal file
10
vendor/github.com/miekg/dns/CONTRIBUTORS
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
Alex A. Skinner
|
||||
Andrew Tunnell-Jones
|
||||
Ask Bjørn Hansen
|
||||
Dave Cheney
|
||||
Dusty Wilson
|
||||
Marek Majkowski
|
||||
Peter van Dijk
|
||||
Omri Bahumi
|
||||
Alex Sergeyev
|
||||
James Hartig
|
||||
9
vendor/github.com/miekg/dns/COPYRIGHT
generated
vendored
Normal file
9
vendor/github.com/miekg/dns/COPYRIGHT
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
Copyright 2009 The Go Authors. All rights reserved. Use of this source code
|
||||
is governed by a BSD-style license that can be found in the LICENSE file.
|
||||
Extensions of the original work are copyright (c) 2011 Miek Gieben
|
||||
|
||||
Copyright 2011 Miek Gieben. All rights reserved. Use of this source code is
|
||||
governed by a BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
Copyright 2014 CloudFlare. All rights reserved. Use of this source code is
|
||||
governed by a BSD-style license that can be found in the LICENSE file.
|
||||
21
vendor/github.com/miekg/dns/Gopkg.lock
generated
vendored
Normal file
21
vendor/github.com/miekg/dns/Gopkg.lock
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = ["ed25519","ed25519/internal/edwards25519"]
|
||||
revision = "b080dc9a8c480b08e698fb1219160d598526310f"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
packages = ["bpf","internal/iana","internal/socket","ipv4","ipv6"]
|
||||
revision = "894f8ed5849b15b810ae41e9590a0d05395bba27"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "c4abc38abaeeeeb9be92455c9c02cae32841122b8982aaa067ef25bb8e86ff9d"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
26
vendor/github.com/miekg/dns/Gopkg.toml
generated
vendored
Normal file
26
vendor/github.com/miekg/dns/Gopkg.toml
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
32
vendor/github.com/miekg/dns/LICENSE
generated
vendored
Normal file
32
vendor/github.com/miekg/dns/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
Extensions of the original work are copyright (c) 2011 Miek Gieben
|
||||
|
||||
As this is fork of the official Go code the same license applies:
|
||||
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
33
vendor/github.com/miekg/dns/Makefile.fuzz
generated
vendored
Normal file
33
vendor/github.com/miekg/dns/Makefile.fuzz
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
# Makefile for fuzzing
|
||||
#
|
||||
# Use go-fuzz and needs the tools installed.
|
||||
# See https://blog.cloudflare.com/dns-parser-meet-go-fuzzer/
|
||||
#
|
||||
# Installing go-fuzz:
|
||||
# $ make -f Makefile.fuzz get
|
||||
# Installs:
|
||||
# * github.com/dvyukov/go-fuzz/go-fuzz
|
||||
# * get github.com/dvyukov/go-fuzz/go-fuzz-build
|
||||
|
||||
all: build
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
go-fuzz-build -tags fuzz github.com/miekg/dns
|
||||
|
||||
.PHONY: build-newrr
|
||||
build-newrr:
|
||||
go-fuzz-build -func FuzzNewRR -tags fuzz github.com/miekg/dns
|
||||
|
||||
.PHONY: fuzz
|
||||
fuzz:
|
||||
go-fuzz -bin=dns-fuzz.zip -workdir=fuzz
|
||||
|
||||
.PHONY: get
|
||||
get:
|
||||
go get github.com/dvyukov/go-fuzz/go-fuzz
|
||||
go get github.com/dvyukov/go-fuzz/go-fuzz-build
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm *-fuzz.zip
|
||||
52
vendor/github.com/miekg/dns/Makefile.release
generated
vendored
Normal file
52
vendor/github.com/miekg/dns/Makefile.release
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
# Makefile for releasing.
|
||||
#
|
||||
# The release is controlled from version.go. The version found there is
|
||||
# used to tag the git repo, we're not building any artifects so there is nothing
|
||||
# to upload to github.
|
||||
#
|
||||
# * Up the version in version.go
|
||||
# * Run: make -f Makefile.release release
|
||||
# * will *commit* your change with 'Release $VERSION'
|
||||
# * push to github
|
||||
#
|
||||
|
||||
define GO
|
||||
//+build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(dns.Version.String())
|
||||
}
|
||||
endef
|
||||
|
||||
$(file > version_release.go,$(GO))
|
||||
VERSION:=$(shell go run version_release.go)
|
||||
TAG="v$(VERSION)"
|
||||
|
||||
all:
|
||||
@echo Use the \'release\' target to start a release $(VERSION)
|
||||
rm -f version_release.go
|
||||
|
||||
.PHONY: release
|
||||
release: commit push
|
||||
@echo Released $(VERSION)
|
||||
rm -f version_release.go
|
||||
|
||||
.PHONY: commit
|
||||
commit:
|
||||
@echo Committing release $(VERSION)
|
||||
git commit -am"Release $(VERSION)"
|
||||
git tag $(TAG)
|
||||
|
||||
.PHONY: push
|
||||
push:
|
||||
@echo Pushing release $(VERSION) to master
|
||||
git push --tags
|
||||
git push
|
||||
168
vendor/github.com/miekg/dns/README.md
generated
vendored
Normal file
168
vendor/github.com/miekg/dns/README.md
generated
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
[](https://travis-ci.org/miekg/dns)
|
||||
[](https://codecov.io/github/miekg/dns?branch=master)
|
||||
[](https://goreportcard.com/report/miekg/dns)
|
||||
[](https://godoc.org/github.com/miekg/dns)
|
||||
|
||||
# Alternative (more granular) approach to a DNS library
|
||||
|
||||
> Less is more.
|
||||
|
||||
Complete and usable DNS library. All widely used Resource Records are supported, including the
|
||||
DNSSEC types. It follows a lean and mean philosophy. If there is stuff you should know as a DNS
|
||||
programmer there isn't a convenience function for it. Server side and client side programming is
|
||||
supported, i.e. you can build servers and resolvers with it.
|
||||
|
||||
We try to keep the "master" branch as sane as possible and at the bleeding edge of standards,
|
||||
avoiding breaking changes wherever reasonable. We support the last two versions of Go.
|
||||
|
||||
# Goals
|
||||
|
||||
* KISS;
|
||||
* Fast;
|
||||
* Small API. If it's easy to code in Go, don't make a function for it.
|
||||
|
||||
# Users
|
||||
|
||||
A not-so-up-to-date-list-that-may-be-actually-current:
|
||||
|
||||
* https://github.com/coredns/coredns
|
||||
* https://cloudflare.com
|
||||
* https://github.com/abh/geodns
|
||||
* http://www.statdns.com/
|
||||
* http://www.dnsinspect.com/
|
||||
* https://github.com/chuangbo/jianbing-dictionary-dns
|
||||
* http://www.dns-lg.com/
|
||||
* https://github.com/fcambus/rrda
|
||||
* https://github.com/kenshinx/godns
|
||||
* https://github.com/skynetservices/skydns
|
||||
* https://github.com/hashicorp/consul
|
||||
* https://github.com/DevelopersPL/godnsagent
|
||||
* https://github.com/duedil-ltd/discodns
|
||||
* https://github.com/StalkR/dns-reverse-proxy
|
||||
* https://github.com/tianon/rawdns
|
||||
* https://mesosphere.github.io/mesos-dns/
|
||||
* https://pulse.turbobytes.com/
|
||||
* https://play.google.com/store/apps/details?id=com.turbobytes.dig
|
||||
* https://github.com/fcambus/statzone
|
||||
* https://github.com/benschw/dns-clb-go
|
||||
* https://github.com/corny/dnscheck for http://public-dns.info/
|
||||
* https://namesmith.io
|
||||
* https://github.com/miekg/unbound
|
||||
* https://github.com/miekg/exdns
|
||||
* https://dnslookup.org
|
||||
* https://github.com/looterz/grimd
|
||||
* https://github.com/phamhongviet/serf-dns
|
||||
* https://github.com/mehrdadrad/mylg
|
||||
* https://github.com/bamarni/dockness
|
||||
* https://github.com/fffaraz/microdns
|
||||
* http://kelda.io
|
||||
* https://github.com/ipdcode/hades (JD.COM)
|
||||
* https://github.com/StackExchange/dnscontrol/
|
||||
* https://www.dnsperf.com/
|
||||
* https://dnssectest.net/
|
||||
* https://dns.apebits.com
|
||||
* https://github.com/oif/apex
|
||||
* https://github.com/jedisct1/dnscrypt-proxy
|
||||
* https://github.com/jedisct1/rpdns
|
||||
|
||||
Send pull request if you want to be listed here.
|
||||
|
||||
# Features
|
||||
|
||||
* UDP/TCP queries, IPv4 and IPv6;
|
||||
* RFC 1035 zone file parsing ($INCLUDE, $ORIGIN, $TTL and $GENERATE (for all record types) are supported;
|
||||
* Fast:
|
||||
* Reply speed around ~ 80K qps (faster hardware results in more qps);
|
||||
* Parsing RRs ~ 100K RR/s, that's 5M records in about 50 seconds;
|
||||
* Server side programming (mimicking the net/http package);
|
||||
* Client side programming;
|
||||
* DNSSEC: signing, validating and key generation for DSA, RSA, ECDSA and Ed25519;
|
||||
* EDNS0, NSID, Cookies;
|
||||
* AXFR/IXFR;
|
||||
* TSIG, SIG(0);
|
||||
* DNS over TLS: optional encrypted connection between client and server;
|
||||
* DNS name compression;
|
||||
* Depends only on the standard library.
|
||||
|
||||
Have fun!
|
||||
|
||||
Miek Gieben - 2010-2012 - <miek@miek.nl>
|
||||
|
||||
# Building
|
||||
|
||||
Building is done with the `go` tool. If you have setup your GOPATH correctly, the following should
|
||||
work:
|
||||
|
||||
go get github.com/miekg/dns
|
||||
go build github.com/miekg/dns
|
||||
|
||||
## Examples
|
||||
|
||||
A short "how to use the API" is at the beginning of doc.go (this also will show
|
||||
when you call `godoc github.com/miekg/dns`).
|
||||
|
||||
Example programs can be found in the `github.com/miekg/exdns` repository.
|
||||
|
||||
## Supported RFCs
|
||||
|
||||
*all of them*
|
||||
|
||||
* 103{4,5} - DNS standard
|
||||
* 1348 - NSAP record (removed the record)
|
||||
* 1982 - Serial Arithmetic
|
||||
* 1876 - LOC record
|
||||
* 1995 - IXFR
|
||||
* 1996 - DNS notify
|
||||
* 2136 - DNS Update (dynamic updates)
|
||||
* 2181 - RRset definition - there is no RRset type though, just []RR
|
||||
* 2537 - RSAMD5 DNS keys
|
||||
* 2065 - DNSSEC (updated in later RFCs)
|
||||
* 2671 - EDNS record
|
||||
* 2782 - SRV record
|
||||
* 2845 - TSIG record
|
||||
* 2915 - NAPTR record
|
||||
* 2929 - DNS IANA Considerations
|
||||
* 3110 - RSASHA1 DNS keys
|
||||
* 3225 - DO bit (DNSSEC OK)
|
||||
* 340{1,2,3} - NAPTR record
|
||||
* 3445 - Limiting the scope of (DNS)KEY
|
||||
* 3597 - Unknown RRs
|
||||
* 403{3,4,5} - DNSSEC + validation functions
|
||||
* 4255 - SSHFP record
|
||||
* 4343 - Case insensitivity
|
||||
* 4408 - SPF record
|
||||
* 4509 - SHA256 Hash in DS
|
||||
* 4592 - Wildcards in the DNS
|
||||
* 4635 - HMAC SHA TSIG
|
||||
* 4701 - DHCID
|
||||
* 4892 - id.server
|
||||
* 5001 - NSID
|
||||
* 5155 - NSEC3 record
|
||||
* 5205 - HIP record
|
||||
* 5702 - SHA2 in the DNS
|
||||
* 5936 - AXFR
|
||||
* 5966 - TCP implementation recommendations
|
||||
* 6605 - ECDSA
|
||||
* 6725 - IANA Registry Update
|
||||
* 6742 - ILNP DNS
|
||||
* 6840 - Clarifications and Implementation Notes for DNS Security
|
||||
* 6844 - CAA record
|
||||
* 6891 - EDNS0 update
|
||||
* 6895 - DNS IANA considerations
|
||||
* 6975 - Algorithm Understanding in DNSSEC
|
||||
* 7043 - EUI48/EUI64 records
|
||||
* 7314 - DNS (EDNS) EXPIRE Option
|
||||
* 7477 - CSYNC RR
|
||||
* 7828 - edns-tcp-keepalive EDNS0 Option
|
||||
* 7553 - URI record
|
||||
* 7858 - DNS over TLS: Initiation and Performance Considerations
|
||||
* 7871 - EDNS0 Client Subnet
|
||||
* 7873 - Domain Name System (DNS) Cookies (draft-ietf-dnsop-cookies)
|
||||
* 8080 - EdDSA for DNSSEC
|
||||
|
||||
## Loosely based upon
|
||||
|
||||
* `ldns`
|
||||
* `NSD`
|
||||
* `Net::DNS`
|
||||
* `GRONG`
|
||||
586
vendor/github.com/miekg/dns/client.go
generated
vendored
Normal file
586
vendor/github.com/miekg/dns/client.go
generated
vendored
Normal file
@@ -0,0 +1,586 @@
|
||||
package dns
|
||||
|
||||
// A client implementation.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
dnsTimeout time.Duration = 2 * time.Second
|
||||
tcpIdleTimeout time.Duration = 8 * time.Second
|
||||
|
||||
dohMimeType = "application/dns-message"
|
||||
)
|
||||
|
||||
// A Conn represents a connection to a DNS server.
|
||||
type Conn struct {
|
||||
net.Conn // a net.Conn holding the connection
|
||||
UDPSize uint16 // minimum receive buffer for UDP messages
|
||||
TsigSecret map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2)
|
||||
tsigRequestMAC string
|
||||
}
|
||||
|
||||
// A Client defines parameters for a DNS client.
|
||||
type Client struct {
|
||||
Net string // if "tcp" or "tcp-tls" (DNS over TLS) a TCP query will be initiated, otherwise an UDP one (default is "" for UDP)
|
||||
UDPSize uint16 // minimum receive buffer for UDP messages
|
||||
TLSConfig *tls.Config // TLS connection configuration
|
||||
Dialer *net.Dialer // a net.Dialer used to set local address, timeouts and more
|
||||
// Timeout is a cumulative timeout for dial, write and read, defaults to 0 (disabled) - overrides DialTimeout, ReadTimeout,
|
||||
// WriteTimeout when non-zero. Can be overridden with net.Dialer.Timeout (see Client.ExchangeWithDialer and
|
||||
// Client.Dialer) or context.Context.Deadline (see the deprecated ExchangeContext)
|
||||
Timeout time.Duration
|
||||
DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds, or net.Dialer.Timeout if expiring earlier - overridden by Timeout when that value is non-zero
|
||||
ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
|
||||
WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
|
||||
HTTPClient *http.Client // The http.Client to use for DNS-over-HTTPS
|
||||
TsigSecret map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2)
|
||||
SingleInflight bool // if true suppress multiple outstanding queries for the same Qname, Qtype and Qclass
|
||||
group singleflight
|
||||
}
|
||||
|
||||
// Exchange performs a synchronous UDP query. It sends the message m to the address
|
||||
// contained in a and waits for a reply. Exchange does not retry a failed query, nor
|
||||
// will it fall back to TCP in case of truncation.
|
||||
// See client.Exchange for more information on setting larger buffer sizes.
|
||||
func Exchange(m *Msg, a string) (r *Msg, err error) {
|
||||
client := Client{Net: "udp"}
|
||||
r, _, err = client.Exchange(m, a)
|
||||
return r, err
|
||||
}
|
||||
|
||||
func (c *Client) dialTimeout() time.Duration {
|
||||
if c.Timeout != 0 {
|
||||
return c.Timeout
|
||||
}
|
||||
if c.DialTimeout != 0 {
|
||||
return c.DialTimeout
|
||||
}
|
||||
return dnsTimeout
|
||||
}
|
||||
|
||||
func (c *Client) readTimeout() time.Duration {
|
||||
if c.ReadTimeout != 0 {
|
||||
return c.ReadTimeout
|
||||
}
|
||||
return dnsTimeout
|
||||
}
|
||||
|
||||
func (c *Client) writeTimeout() time.Duration {
|
||||
if c.WriteTimeout != 0 {
|
||||
return c.WriteTimeout
|
||||
}
|
||||
return dnsTimeout
|
||||
}
|
||||
|
||||
// Dial connects to the address on the named network.
|
||||
func (c *Client) Dial(address string) (conn *Conn, err error) {
|
||||
// create a new dialer with the appropriate timeout
|
||||
var d net.Dialer
|
||||
if c.Dialer == nil {
|
||||
d = net.Dialer{Timeout:c.getTimeoutForRequest(c.dialTimeout())}
|
||||
} else {
|
||||
d = net.Dialer(*c.Dialer)
|
||||
}
|
||||
|
||||
network := "udp"
|
||||
useTLS := false
|
||||
|
||||
switch c.Net {
|
||||
case "tcp-tls":
|
||||
network = "tcp"
|
||||
useTLS = true
|
||||
case "tcp4-tls":
|
||||
network = "tcp4"
|
||||
useTLS = true
|
||||
case "tcp6-tls":
|
||||
network = "tcp6"
|
||||
useTLS = true
|
||||
default:
|
||||
if c.Net != "" {
|
||||
network = c.Net
|
||||
}
|
||||
}
|
||||
|
||||
conn = new(Conn)
|
||||
if useTLS {
|
||||
conn.Conn, err = tls.DialWithDialer(&d, network, address, c.TLSConfig)
|
||||
} else {
|
||||
conn.Conn, err = d.Dial(network, address)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// Exchange performs a synchronous query. It sends the message m to the address
|
||||
// contained in a and waits for a reply. Basic use pattern with a *dns.Client:
|
||||
//
|
||||
// c := new(dns.Client)
|
||||
// in, rtt, err := c.Exchange(message, "127.0.0.1:53")
|
||||
//
|
||||
// Exchange does not retry a failed query, nor will it fall back to TCP in
|
||||
// case of truncation.
|
||||
// It is up to the caller to create a message that allows for larger responses to be
|
||||
// returned. Specifically this means adding an EDNS0 OPT RR that will advertise a larger
|
||||
// buffer, see SetEdns0. Messages without an OPT RR will fallback to the historic limit
|
||||
// of 512 bytes
|
||||
// To specify a local address or a timeout, the caller has to set the `Client.Dialer`
|
||||
// attribute appropriately
|
||||
func (c *Client) Exchange(m *Msg, address string) (r *Msg, rtt time.Duration, err error) {
|
||||
if !c.SingleInflight {
|
||||
if c.Net == "https" {
|
||||
// TODO(tmthrgd): pipe timeouts into exchangeDOH
|
||||
return c.exchangeDOH(context.TODO(), m, address)
|
||||
}
|
||||
|
||||
return c.exchange(m, address)
|
||||
}
|
||||
|
||||
t := "nop"
|
||||
if t1, ok := TypeToString[m.Question[0].Qtype]; ok {
|
||||
t = t1
|
||||
}
|
||||
cl := "nop"
|
||||
if cl1, ok := ClassToString[m.Question[0].Qclass]; ok {
|
||||
cl = cl1
|
||||
}
|
||||
r, rtt, err, shared := c.group.Do(m.Question[0].Name+t+cl, func() (*Msg, time.Duration, error) {
|
||||
if c.Net == "https" {
|
||||
// TODO(tmthrgd): pipe timeouts into exchangeDOH
|
||||
return c.exchangeDOH(context.TODO(), m, address)
|
||||
}
|
||||
|
||||
return c.exchange(m, address)
|
||||
})
|
||||
if r != nil && shared {
|
||||
r = r.Copy()
|
||||
}
|
||||
return r, rtt, err
|
||||
}
|
||||
|
||||
func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
|
||||
var co *Conn
|
||||
|
||||
co, err = c.Dial(a)
|
||||
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
defer co.Close()
|
||||
|
||||
opt := m.IsEdns0()
|
||||
// If EDNS0 is used use that for size.
|
||||
if opt != nil && opt.UDPSize() >= MinMsgSize {
|
||||
co.UDPSize = opt.UDPSize()
|
||||
}
|
||||
// Otherwise use the client's configured UDP size.
|
||||
if opt == nil && c.UDPSize >= MinMsgSize {
|
||||
co.UDPSize = c.UDPSize
|
||||
}
|
||||
|
||||
co.TsigSecret = c.TsigSecret
|
||||
t := time.Now()
|
||||
// write with the appropriate write timeout
|
||||
co.SetWriteDeadline(t.Add(c.getTimeoutForRequest(c.writeTimeout())))
|
||||
if err = co.WriteMsg(m); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
co.SetReadDeadline(time.Now().Add(c.getTimeoutForRequest(c.readTimeout())))
|
||||
r, err = co.ReadMsg()
|
||||
if err == nil && r.Id != m.Id {
|
||||
err = ErrId
|
||||
}
|
||||
rtt = time.Since(t)
|
||||
return r, rtt, err
|
||||
}
|
||||
|
||||
func (c *Client) exchangeDOH(ctx context.Context, m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
|
||||
p, err := m.Pack()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, a, bytes.NewReader(p))
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", dohMimeType)
|
||||
req.Header.Set("Accept", dohMimeType)
|
||||
|
||||
hc := http.DefaultClient
|
||||
if c.HTTPClient != nil {
|
||||
hc = c.HTTPClient
|
||||
}
|
||||
|
||||
if ctx != context.Background() && ctx != context.TODO() {
|
||||
req = req.WithContext(ctx)
|
||||
}
|
||||
|
||||
t := time.Now()
|
||||
|
||||
resp, err := hc.Do(req)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
defer closeHTTPBody(resp.Body)
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, 0, fmt.Errorf("dns: server returned HTTP %d error: %q", resp.StatusCode, resp.Status)
|
||||
}
|
||||
|
||||
if ct := resp.Header.Get("Content-Type"); ct != dohMimeType {
|
||||
return nil, 0, fmt.Errorf("dns: unexpected Content-Type %q; expected %q", ct, dohMimeType)
|
||||
}
|
||||
|
||||
p, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
rtt = time.Since(t)
|
||||
|
||||
r = new(Msg)
|
||||
if err := r.Unpack(p); err != nil {
|
||||
return r, 0, err
|
||||
}
|
||||
|
||||
// TODO: TSIG? Is it even supported over DoH?
|
||||
|
||||
return r, rtt, nil
|
||||
}
|
||||
|
||||
func closeHTTPBody(r io.ReadCloser) error {
|
||||
io.Copy(ioutil.Discard, io.LimitReader(r, 8<<20))
|
||||
return r.Close()
|
||||
}
|
||||
|
||||
// ReadMsg reads a message from the connection co.
|
||||
// If the received message contains a TSIG record the transaction signature
|
||||
// is verified. This method always tries to return the message, however if an
|
||||
// error is returned there are no guarantees that the returned message is a
|
||||
// valid representation of the packet read.
|
||||
func (co *Conn) ReadMsg() (*Msg, error) {
|
||||
p, err := co.ReadMsgHeader(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m := new(Msg)
|
||||
if err := m.Unpack(p); err != nil {
|
||||
// If an error was returned, we still want to allow the user to use
|
||||
// the message, but naively they can just check err if they don't want
|
||||
// to use an erroneous message
|
||||
return m, err
|
||||
}
|
||||
if t := m.IsTsig(); t != nil {
|
||||
if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
|
||||
return m, ErrSecret
|
||||
}
|
||||
// Need to work on the original message p, as that was used to calculate the tsig.
|
||||
err = TsigVerify(p, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false)
|
||||
}
|
||||
return m, err
|
||||
}
|
||||
|
||||
// ReadMsgHeader reads a DNS message, parses and populates hdr (when hdr is not nil).
|
||||
// Returns message as a byte slice to be parsed with Msg.Unpack later on.
|
||||
// Note that error handling on the message body is not possible as only the header is parsed.
|
||||
func (co *Conn) ReadMsgHeader(hdr *Header) ([]byte, error) {
|
||||
var (
|
||||
p []byte
|
||||
n int
|
||||
err error
|
||||
)
|
||||
|
||||
switch t := co.Conn.(type) {
|
||||
case *net.TCPConn, *tls.Conn:
|
||||
r := t.(io.Reader)
|
||||
|
||||
// First two bytes specify the length of the entire message.
|
||||
l, err := tcpMsgLen(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p = make([]byte, l)
|
||||
n, err = tcpRead(r, p)
|
||||
default:
|
||||
if co.UDPSize > MinMsgSize {
|
||||
p = make([]byte, co.UDPSize)
|
||||
} else {
|
||||
p = make([]byte, MinMsgSize)
|
||||
}
|
||||
n, err = co.Read(p)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if n < headerSize {
|
||||
return nil, ErrShortRead
|
||||
}
|
||||
|
||||
p = p[:n]
|
||||
if hdr != nil {
|
||||
dh, _, err := unpackMsgHdr(p, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
*hdr = dh
|
||||
}
|
||||
return p, err
|
||||
}
|
||||
|
||||
// tcpMsgLen is a helper func to read first two bytes of stream as uint16 packet length.
|
||||
func tcpMsgLen(t io.Reader) (int, error) {
|
||||
p := []byte{0, 0}
|
||||
n, err := t.Read(p)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// As seen with my local router/switch, returns 1 byte on the above read,
|
||||
// resulting a a ShortRead. Just write it out (instead of loop) and read the
|
||||
// other byte.
|
||||
if n == 1 {
|
||||
n1, err := t.Read(p[1:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n += n1
|
||||
}
|
||||
|
||||
if n != 2 {
|
||||
return 0, ErrShortRead
|
||||
}
|
||||
l := binary.BigEndian.Uint16(p)
|
||||
if l == 0 {
|
||||
return 0, ErrShortRead
|
||||
}
|
||||
return int(l), nil
|
||||
}
|
||||
|
||||
// tcpRead calls TCPConn.Read enough times to fill allocated buffer.
|
||||
func tcpRead(t io.Reader, p []byte) (int, error) {
|
||||
n, err := t.Read(p)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
for n < len(p) {
|
||||
j, err := t.Read(p[n:])
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
n += j
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Read implements the net.Conn read method.
|
||||
func (co *Conn) Read(p []byte) (n int, err error) {
|
||||
if co.Conn == nil {
|
||||
return 0, ErrConnEmpty
|
||||
}
|
||||
if len(p) < 2 {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
switch t := co.Conn.(type) {
|
||||
case *net.TCPConn, *tls.Conn:
|
||||
r := t.(io.Reader)
|
||||
|
||||
l, err := tcpMsgLen(r)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if l > len(p) {
|
||||
return int(l), io.ErrShortBuffer
|
||||
}
|
||||
return tcpRead(r, p[:l])
|
||||
}
|
||||
// UDP connection
|
||||
n, err = co.Conn.Read(p)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// WriteMsg sends a message through the connection co.
|
||||
// If the message m contains a TSIG record the transaction
|
||||
// signature is calculated.
|
||||
func (co *Conn) WriteMsg(m *Msg) (err error) {
|
||||
var out []byte
|
||||
if t := m.IsTsig(); t != nil {
|
||||
mac := ""
|
||||
if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
|
||||
return ErrSecret
|
||||
}
|
||||
out, mac, err = TsigGenerate(m, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false)
|
||||
// Set for the next read, although only used in zone transfers
|
||||
co.tsigRequestMAC = mac
|
||||
} else {
|
||||
out, err = m.Pack()
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = co.Write(out); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write implements the net.Conn Write method.
|
||||
func (co *Conn) Write(p []byte) (n int, err error) {
|
||||
switch t := co.Conn.(type) {
|
||||
case *net.TCPConn, *tls.Conn:
|
||||
w := t.(io.Writer)
|
||||
|
||||
lp := len(p)
|
||||
if lp < 2 {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
if lp > MaxMsgSize {
|
||||
return 0, &Error{err: "message too large"}
|
||||
}
|
||||
l := make([]byte, 2, lp+2)
|
||||
binary.BigEndian.PutUint16(l, uint16(lp))
|
||||
p = append(l, p...)
|
||||
n, err := io.Copy(w, bytes.NewReader(p))
|
||||
return int(n), err
|
||||
}
|
||||
n, err = co.Conn.Write(p)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Return the appropriate timeout for a specific request
|
||||
func (c *Client) getTimeoutForRequest(timeout time.Duration) time.Duration {
|
||||
var requestTimeout time.Duration
|
||||
if c.Timeout != 0 {
|
||||
requestTimeout = c.Timeout
|
||||
} else {
|
||||
requestTimeout = timeout
|
||||
}
|
||||
// net.Dialer.Timeout has priority if smaller than the timeouts computed so
|
||||
// far
|
||||
if c.Dialer != nil && c.Dialer.Timeout != 0 {
|
||||
if c.Dialer.Timeout < requestTimeout {
|
||||
requestTimeout = c.Dialer.Timeout
|
||||
}
|
||||
}
|
||||
return requestTimeout
|
||||
}
|
||||
|
||||
// Dial connects to the address on the named network.
|
||||
func Dial(network, address string) (conn *Conn, err error) {
|
||||
conn = new(Conn)
|
||||
conn.Conn, err = net.Dial(network, address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// ExchangeContext performs a synchronous UDP query, like Exchange. It
|
||||
// additionally obeys deadlines from the passed Context.
|
||||
func ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, err error) {
|
||||
client := Client{Net: "udp"}
|
||||
r, _, err = client.ExchangeContext(ctx, m, a)
|
||||
// ignorint rtt to leave the original ExchangeContext API unchanged, but
|
||||
// this function will go away
|
||||
return r, err
|
||||
}
|
||||
|
||||
// ExchangeConn performs a synchronous query. It sends the message m via the connection
|
||||
// c and waits for a reply. The connection c is not closed by ExchangeConn.
|
||||
// This function is going away, but can easily be mimicked:
|
||||
//
|
||||
// co := &dns.Conn{Conn: c} // c is your net.Conn
|
||||
// co.WriteMsg(m)
|
||||
// in, _ := co.ReadMsg()
|
||||
// co.Close()
|
||||
//
|
||||
func ExchangeConn(c net.Conn, m *Msg) (r *Msg, err error) {
|
||||
println("dns: ExchangeConn: this function is deprecated")
|
||||
co := new(Conn)
|
||||
co.Conn = c
|
||||
if err = co.WriteMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r, err = co.ReadMsg()
|
||||
if err == nil && r.Id != m.Id {
|
||||
err = ErrId
|
||||
}
|
||||
return r, err
|
||||
}
|
||||
|
||||
// DialTimeout acts like Dial but takes a timeout.
|
||||
func DialTimeout(network, address string, timeout time.Duration) (conn *Conn, err error) {
|
||||
client := Client{Net: network, Dialer: &net.Dialer{Timeout: timeout}}
|
||||
conn, err = client.Dial(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// DialWithTLS connects to the address on the named network with TLS.
|
||||
func DialWithTLS(network, address string, tlsConfig *tls.Config) (conn *Conn, err error) {
|
||||
if !strings.HasSuffix(network, "-tls") {
|
||||
network += "-tls"
|
||||
}
|
||||
client := Client{Net: network, TLSConfig: tlsConfig}
|
||||
conn, err = client.Dial(address)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// DialTimeoutWithTLS acts like DialWithTLS but takes a timeout.
|
||||
func DialTimeoutWithTLS(network, address string, tlsConfig *tls.Config, timeout time.Duration) (conn *Conn, err error) {
|
||||
if !strings.HasSuffix(network, "-tls") {
|
||||
network += "-tls"
|
||||
}
|
||||
client := Client{Net: network, Dialer: &net.Dialer{Timeout: timeout}, TLSConfig: tlsConfig}
|
||||
conn, err = client.Dial(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// ExchangeContext acts like Exchange, but honors the deadline on the provided
|
||||
// context, if present. If there is both a context deadline and a configured
|
||||
// timeout on the client, the earliest of the two takes effect.
|
||||
func (c *Client) ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
|
||||
if !c.SingleInflight && c.Net == "https" {
|
||||
return c.exchangeDOH(ctx, m, a)
|
||||
}
|
||||
|
||||
var timeout time.Duration
|
||||
if deadline, ok := ctx.Deadline(); !ok {
|
||||
timeout = 0
|
||||
} else {
|
||||
timeout = deadline.Sub(time.Now())
|
||||
}
|
||||
// not passing the context to the underlying calls, as the API does not support
|
||||
// context. For timeouts you should set up Client.Dialer and call Client.Exchange.
|
||||
// TODO(tmthrgd): this is a race condition
|
||||
c.Dialer = &net.Dialer{Timeout: timeout}
|
||||
return c.Exchange(m, a)
|
||||
}
|
||||
612
vendor/github.com/miekg/dns/client_test.go
generated
vendored
Normal file
612
vendor/github.com/miekg/dns/client_test.go
generated
vendored
Normal file
@@ -0,0 +1,612 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestDialUDP(t *testing.T) {
|
||||
HandleFunc("miek.nl.", HelloServer)
|
||||
defer HandleRemove("miek.nl.")
|
||||
|
||||
s, addrstr, err := RunLocalUDPServer(":0")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to run test server: %v", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl.", TypeSOA)
|
||||
|
||||
c := new(Client)
|
||||
conn, err := c.Dial(addrstr)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to dial: %v", err)
|
||||
}
|
||||
if conn == nil {
|
||||
t.Fatalf("conn is nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientSync(t *testing.T) {
|
||||
HandleFunc("miek.nl.", HelloServer)
|
||||
defer HandleRemove("miek.nl.")
|
||||
|
||||
s, addrstr, err := RunLocalUDPServer(":0")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to run test server: %v", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl.", TypeSOA)
|
||||
|
||||
c := new(Client)
|
||||
r, _, err := c.Exchange(m, addrstr)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to exchange: %v", err)
|
||||
}
|
||||
if r == nil {
|
||||
t.Fatal("response is nil")
|
||||
}
|
||||
if r.Rcode != RcodeSuccess {
|
||||
t.Errorf("failed to get an valid answer\n%v", r)
|
||||
}
|
||||
// And now with plain Exchange().
|
||||
r, err = Exchange(m, addrstr)
|
||||
if err != nil {
|
||||
t.Errorf("failed to exchange: %v", err)
|
||||
}
|
||||
if r == nil || r.Rcode != RcodeSuccess {
|
||||
t.Errorf("failed to get an valid answer\n%v", r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientLocalAddress(t *testing.T) {
|
||||
HandleFunc("miek.nl.", HelloServerEchoAddrPort)
|
||||
defer HandleRemove("miek.nl.")
|
||||
|
||||
s, addrstr, err := RunLocalUDPServer(":0")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to run test server: %v", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl.", TypeSOA)
|
||||
|
||||
c := new(Client)
|
||||
laddr := net.UDPAddr{IP: net.ParseIP("0.0.0.0"), Port: 12345, Zone: ""}
|
||||
c.Dialer = &net.Dialer{LocalAddr: &laddr}
|
||||
r, _, err := c.Exchange(m, addrstr)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to exchange: %v", err)
|
||||
}
|
||||
if r != nil && r.Rcode != RcodeSuccess {
|
||||
t.Errorf("failed to get an valid answer\n%v", r)
|
||||
}
|
||||
if len(r.Extra) != 1 {
|
||||
t.Errorf("failed to get additional answers\n%v", r)
|
||||
}
|
||||
txt := r.Extra[0].(*TXT)
|
||||
if txt == nil {
|
||||
t.Errorf("invalid TXT response\n%v", txt)
|
||||
}
|
||||
if len(txt.Txt) != 1 || !strings.Contains(txt.Txt[0], ":12345") {
|
||||
t.Errorf("invalid TXT response\n%v", txt.Txt)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientTLSSyncV4(t *testing.T) {
|
||||
HandleFunc("miek.nl.", HelloServer)
|
||||
defer HandleRemove("miek.nl.")
|
||||
|
||||
cert, err := tls.X509KeyPair(CertPEMBlock, KeyPEMBlock)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to build certificate: %v", err)
|
||||
}
|
||||
|
||||
config := tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
}
|
||||
|
||||
s, addrstr, err := RunLocalTLSServer(":0", &config)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to run test server: %v", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl.", TypeSOA)
|
||||
|
||||
c := new(Client)
|
||||
|
||||
// test tcp-tls
|
||||
c.Net = "tcp-tls"
|
||||
c.TLSConfig = &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
|
||||
r, _, err := c.Exchange(m, addrstr)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to exchange: %v", err)
|
||||
}
|
||||
if r == nil {
|
||||
t.Fatal("response is nil")
|
||||
}
|
||||
if r.Rcode != RcodeSuccess {
|
||||
t.Errorf("failed to get an valid answer\n%v", r)
|
||||
}
|
||||
|
||||
// test tcp4-tls
|
||||
c.Net = "tcp4-tls"
|
||||
c.TLSConfig = &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
|
||||
r, _, err = c.Exchange(m, addrstr)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to exchange: %v", err)
|
||||
}
|
||||
if r == nil {
|
||||
t.Fatal("response is nil")
|
||||
}
|
||||
if r.Rcode != RcodeSuccess {
|
||||
t.Errorf("failed to get an valid answer\n%v", r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientSyncBadID(t *testing.T) {
|
||||
HandleFunc("miek.nl.", HelloServerBadID)
|
||||
defer HandleRemove("miek.nl.")
|
||||
|
||||
s, addrstr, err := RunLocalUDPServer(":0")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to run test server: %v", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl.", TypeSOA)
|
||||
|
||||
c := new(Client)
|
||||
if _, _, err := c.Exchange(m, addrstr); err != ErrId {
|
||||
t.Errorf("did not find a bad Id")
|
||||
}
|
||||
// And now with plain Exchange().
|
||||
if _, err := Exchange(m, addrstr); err != ErrId {
|
||||
t.Errorf("did not find a bad Id")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientEDNS0(t *testing.T) {
|
||||
HandleFunc("miek.nl.", HelloServer)
|
||||
defer HandleRemove("miek.nl.")
|
||||
|
||||
s, addrstr, err := RunLocalUDPServer(":0")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to run test server: %v", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl.", TypeDNSKEY)
|
||||
|
||||
m.SetEdns0(2048, true)
|
||||
|
||||
c := new(Client)
|
||||
r, _, err := c.Exchange(m, addrstr)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to exchange: %v", err)
|
||||
}
|
||||
|
||||
if r != nil && r.Rcode != RcodeSuccess {
|
||||
t.Errorf("failed to get a valid answer\n%v", r)
|
||||
}
|
||||
}
|
||||
|
||||
// Validates the transmission and parsing of local EDNS0 options.
|
||||
func TestClientEDNS0Local(t *testing.T) {
|
||||
optStr1 := "1979:0x0707"
|
||||
optStr2 := strconv.Itoa(EDNS0LOCALSTART) + ":0x0601"
|
||||
|
||||
handler := func(w ResponseWriter, req *Msg) {
|
||||
m := new(Msg)
|
||||
m.SetReply(req)
|
||||
|
||||
m.Extra = make([]RR, 1, 2)
|
||||
m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello local edns"}}
|
||||
|
||||
// If the local options are what we expect, then reflect them back.
|
||||
ec1 := req.Extra[0].(*OPT).Option[0].(*EDNS0_LOCAL).String()
|
||||
ec2 := req.Extra[0].(*OPT).Option[1].(*EDNS0_LOCAL).String()
|
||||
if ec1 == optStr1 && ec2 == optStr2 {
|
||||
m.Extra = append(m.Extra, req.Extra[0])
|
||||
}
|
||||
|
||||
w.WriteMsg(m)
|
||||
}
|
||||
|
||||
HandleFunc("miek.nl.", handler)
|
||||
defer HandleRemove("miek.nl.")
|
||||
|
||||
s, addrstr, err := RunLocalUDPServer(":0")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to run test server: %s", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl.", TypeTXT)
|
||||
|
||||
// Add two local edns options to the query.
|
||||
ec1 := &EDNS0_LOCAL{Code: 1979, Data: []byte{7, 7}}
|
||||
ec2 := &EDNS0_LOCAL{Code: EDNS0LOCALSTART, Data: []byte{6, 1}}
|
||||
o := &OPT{Hdr: RR_Header{Name: ".", Rrtype: TypeOPT}, Option: []EDNS0{ec1, ec2}}
|
||||
m.Extra = append(m.Extra, o)
|
||||
|
||||
c := new(Client)
|
||||
r, _, err := c.Exchange(m, addrstr)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to exchange: %s", err)
|
||||
}
|
||||
|
||||
if r == nil {
|
||||
t.Fatal("response is nil")
|
||||
}
|
||||
if r.Rcode != RcodeSuccess {
|
||||
t.Fatal("failed to get a valid answer")
|
||||
}
|
||||
|
||||
txt := r.Extra[0].(*TXT).Txt[0]
|
||||
if txt != "Hello local edns" {
|
||||
t.Error("Unexpected result for miek.nl", txt, "!= Hello local edns")
|
||||
}
|
||||
|
||||
// Validate the local options in the reply.
|
||||
got := r.Extra[1].(*OPT).Option[0].(*EDNS0_LOCAL).String()
|
||||
if got != optStr1 {
|
||||
t.Errorf("failed to get local edns0 answer; got %s, expected %s", got, optStr1)
|
||||
}
|
||||
|
||||
got = r.Extra[1].(*OPT).Option[1].(*EDNS0_LOCAL).String()
|
||||
if got != optStr2 {
|
||||
t.Errorf("failed to get local edns0 answer; got %s, expected %s", got, optStr2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientConn(t *testing.T) {
|
||||
HandleFunc("miek.nl.", HelloServer)
|
||||
defer HandleRemove("miek.nl.")
|
||||
|
||||
// This uses TCP just to make it slightly different than TestClientSync
|
||||
s, addrstr, err := RunLocalTCPServer(":0")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to run test server: %v", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl.", TypeSOA)
|
||||
|
||||
cn, err := Dial("tcp", addrstr)
|
||||
if err != nil {
|
||||
t.Errorf("failed to dial %s: %v", addrstr, err)
|
||||
}
|
||||
|
||||
err = cn.WriteMsg(m)
|
||||
if err != nil {
|
||||
t.Errorf("failed to exchange: %v", err)
|
||||
}
|
||||
r, err := cn.ReadMsg()
|
||||
if err != nil {
|
||||
t.Errorf("failed to get a valid answer: %v", err)
|
||||
}
|
||||
if r == nil || r.Rcode != RcodeSuccess {
|
||||
t.Errorf("failed to get an valid answer\n%v", r)
|
||||
}
|
||||
|
||||
err = cn.WriteMsg(m)
|
||||
if err != nil {
|
||||
t.Errorf("failed to exchange: %v", err)
|
||||
}
|
||||
h := new(Header)
|
||||
buf, err := cn.ReadMsgHeader(h)
|
||||
if buf == nil {
|
||||
t.Errorf("failed to get an valid answer\n%v", r)
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("failed to get a valid answer: %v", err)
|
||||
}
|
||||
if int(h.Bits&0xF) != RcodeSuccess {
|
||||
t.Errorf("failed to get an valid answer in ReadMsgHeader\n%v", r)
|
||||
}
|
||||
if h.Ancount != 0 || h.Qdcount != 1 || h.Nscount != 0 || h.Arcount != 1 {
|
||||
t.Errorf("expected to have question and additional in response; got something else: %+v", h)
|
||||
}
|
||||
if err = r.Unpack(buf); err != nil {
|
||||
t.Errorf("unable to unpack message fully: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTruncatedMsg(t *testing.T) {
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl.", TypeSRV)
|
||||
cnt := 10
|
||||
for i := 0; i < cnt; i++ {
|
||||
r := &SRV{
|
||||
Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeSRV, Class: ClassINET, Ttl: 0},
|
||||
Port: uint16(i + 8000),
|
||||
Target: "target.miek.nl.",
|
||||
}
|
||||
m.Answer = append(m.Answer, r)
|
||||
|
||||
re := &A{
|
||||
Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeA, Class: ClassINET, Ttl: 0},
|
||||
A: net.ParseIP(fmt.Sprintf("127.0.0.%d", i)).To4(),
|
||||
}
|
||||
m.Extra = append(m.Extra, re)
|
||||
}
|
||||
buf, err := m.Pack()
|
||||
if err != nil {
|
||||
t.Errorf("failed to pack: %v", err)
|
||||
}
|
||||
|
||||
r := new(Msg)
|
||||
if err = r.Unpack(buf); err != nil {
|
||||
t.Errorf("unable to unpack message: %v", err)
|
||||
}
|
||||
if len(r.Answer) != cnt {
|
||||
t.Errorf("answer count after regular unpack doesn't match: %d", len(r.Answer))
|
||||
}
|
||||
if len(r.Extra) != cnt {
|
||||
t.Errorf("extra count after regular unpack doesn't match: %d", len(r.Extra))
|
||||
}
|
||||
|
||||
m.Truncated = true
|
||||
buf, err = m.Pack()
|
||||
if err != nil {
|
||||
t.Errorf("failed to pack truncated: %v", err)
|
||||
}
|
||||
|
||||
r = new(Msg)
|
||||
if err = r.Unpack(buf); err != nil && err != ErrTruncated {
|
||||
t.Errorf("unable to unpack truncated message: %v", err)
|
||||
}
|
||||
if !r.Truncated {
|
||||
t.Errorf("truncated message wasn't unpacked as truncated")
|
||||
}
|
||||
if len(r.Answer) != cnt {
|
||||
t.Errorf("answer count after truncated unpack doesn't match: %d", len(r.Answer))
|
||||
}
|
||||
if len(r.Extra) != cnt {
|
||||
t.Errorf("extra count after truncated unpack doesn't match: %d", len(r.Extra))
|
||||
}
|
||||
|
||||
// Now we want to remove almost all of the extra records
|
||||
// We're going to loop over the extra to get the count of the size of all
|
||||
// of them
|
||||
off := 0
|
||||
buf1 := make([]byte, m.Len())
|
||||
for i := 0; i < len(m.Extra); i++ {
|
||||
off, err = PackRR(m.Extra[i], buf1, off, nil, m.Compress)
|
||||
if err != nil {
|
||||
t.Errorf("failed to pack extra: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all of the extra bytes but 10 bytes from the end of buf
|
||||
off -= 10
|
||||
buf1 = buf[:len(buf)-off]
|
||||
|
||||
r = new(Msg)
|
||||
if err = r.Unpack(buf1); err != nil && err != ErrTruncated {
|
||||
t.Errorf("unable to unpack cutoff message: %v", err)
|
||||
}
|
||||
if !r.Truncated {
|
||||
t.Error("truncated cutoff message wasn't unpacked as truncated")
|
||||
}
|
||||
if len(r.Answer) != cnt {
|
||||
t.Errorf("answer count after cutoff unpack doesn't match: %d", len(r.Answer))
|
||||
}
|
||||
if len(r.Extra) != 0 {
|
||||
t.Errorf("extra count after cutoff unpack is not zero: %d", len(r.Extra))
|
||||
}
|
||||
|
||||
// Now we want to remove almost all of the answer records too
|
||||
buf1 = make([]byte, m.Len())
|
||||
as := 0
|
||||
for i := 0; i < len(m.Extra); i++ {
|
||||
off1 := off
|
||||
off, err = PackRR(m.Extra[i], buf1, off, nil, m.Compress)
|
||||
as = off - off1
|
||||
if err != nil {
|
||||
t.Errorf("failed to pack extra: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Keep exactly one answer left
|
||||
// This should still cause Answer to be nil
|
||||
off -= as
|
||||
buf1 = buf[:len(buf)-off]
|
||||
|
||||
r = new(Msg)
|
||||
if err = r.Unpack(buf1); err != nil && err != ErrTruncated {
|
||||
t.Errorf("unable to unpack cutoff message: %v", err)
|
||||
}
|
||||
if !r.Truncated {
|
||||
t.Error("truncated cutoff message wasn't unpacked as truncated")
|
||||
}
|
||||
if len(r.Answer) != 0 {
|
||||
t.Errorf("answer count after second cutoff unpack is not zero: %d", len(r.Answer))
|
||||
}
|
||||
|
||||
// Now leave only 1 byte of the question
|
||||
// Since the header is always 12 bytes, we just need to keep 13
|
||||
buf1 = buf[:13]
|
||||
|
||||
r = new(Msg)
|
||||
err = r.Unpack(buf1)
|
||||
if err == nil || err == ErrTruncated {
|
||||
t.Errorf("error should not be ErrTruncated from question cutoff unpack: %v", err)
|
||||
}
|
||||
|
||||
// Finally, if we only have the header, we don't return an error.
|
||||
buf1 = buf[:12]
|
||||
|
||||
r = new(Msg)
|
||||
if err = r.Unpack(buf1); err != nil {
|
||||
t.Errorf("from header-only unpack should not return an error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimeout(t *testing.T) {
|
||||
// Set up a dummy UDP server that won't respond
|
||||
addr, err := net.ResolveUDPAddr("udp", ":0")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to resolve local udp address: %v", err)
|
||||
}
|
||||
conn, err := net.ListenUDP("udp", addr)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to run test server: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
addrstr := conn.LocalAddr().String()
|
||||
|
||||
// Message to send
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl.", TypeTXT)
|
||||
|
||||
// Use a channel + timeout to ensure we don't get stuck if the
|
||||
// Client Timeout is not working properly
|
||||
done := make(chan struct{}, 2)
|
||||
|
||||
timeout := time.Millisecond
|
||||
allowable := timeout + (10 * time.Millisecond)
|
||||
abortAfter := timeout + (100 * time.Millisecond)
|
||||
|
||||
start := time.Now()
|
||||
|
||||
go func() {
|
||||
c := &Client{Timeout: timeout}
|
||||
_, _, err := c.Exchange(m, addrstr)
|
||||
if err == nil {
|
||||
t.Error("no timeout using Client.Exchange")
|
||||
}
|
||||
done <- struct{}{}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
c := &Client{}
|
||||
_, _, err := c.ExchangeContext(ctx, m, addrstr)
|
||||
if err == nil {
|
||||
t.Error("no timeout using Client.ExchangeContext")
|
||||
}
|
||||
done <- struct{}{}
|
||||
}()
|
||||
|
||||
// Wait for both the Exchange and ExchangeContext tests to be done.
|
||||
for i := 0; i < 2; i++ {
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(abortAfter):
|
||||
}
|
||||
}
|
||||
|
||||
length := time.Since(start)
|
||||
|
||||
if length > allowable {
|
||||
t.Errorf("exchange took longer %v than specified Timeout %v", length, allowable)
|
||||
}
|
||||
}
|
||||
|
||||
// Check that responses from deduplicated requests aren't shared between callers
|
||||
func TestConcurrentExchanges(t *testing.T) {
|
||||
cases := make([]*Msg, 2)
|
||||
cases[0] = new(Msg)
|
||||
cases[1] = new(Msg)
|
||||
cases[1].Truncated = true
|
||||
for _, m := range cases {
|
||||
block := make(chan struct{})
|
||||
waiting := make(chan struct{})
|
||||
|
||||
handler := func(w ResponseWriter, req *Msg) {
|
||||
r := m.Copy()
|
||||
r.SetReply(req)
|
||||
|
||||
waiting <- struct{}{}
|
||||
<-block
|
||||
w.WriteMsg(r)
|
||||
}
|
||||
|
||||
HandleFunc("miek.nl.", handler)
|
||||
defer HandleRemove("miek.nl.")
|
||||
|
||||
s, addrstr, err := RunLocalUDPServer(":0")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to run test server: %s", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl.", TypeSRV)
|
||||
c := &Client{
|
||||
SingleInflight: true,
|
||||
}
|
||||
r := make([]*Msg, 2)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(r))
|
||||
for i := 0; i < len(r); i++ {
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
r[i], _, _ = c.Exchange(m.Copy(), addrstr)
|
||||
if r[i] == nil {
|
||||
t.Errorf("response %d is nil", i)
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
select {
|
||||
case <-waiting:
|
||||
case <-time.After(time.Second):
|
||||
t.FailNow()
|
||||
}
|
||||
close(block)
|
||||
wg.Wait()
|
||||
|
||||
if r[0] == r[1] {
|
||||
t.Errorf("got same response, expected non-shared responses")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDoHExchange(t *testing.T) {
|
||||
const addrstr = "https://dns.cloudflare.com/dns-query"
|
||||
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl.", TypeSOA)
|
||||
|
||||
cl := &Client{Net: "https"}
|
||||
|
||||
r, _, err := cl.Exchange(m, addrstr)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to exchange: %v", err)
|
||||
}
|
||||
|
||||
if r == nil || r.Rcode != RcodeSuccess {
|
||||
t.Errorf("failed to get an valid answer\n%v", r)
|
||||
}
|
||||
|
||||
t.Log(r)
|
||||
|
||||
// TODO: proper tests for this
|
||||
}
|
||||
139
vendor/github.com/miekg/dns/clientconfig.go
generated
vendored
Normal file
139
vendor/github.com/miekg/dns/clientconfig.go
generated
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ClientConfig wraps the contents of the /etc/resolv.conf file.
|
||||
type ClientConfig struct {
|
||||
Servers []string // servers to use
|
||||
Search []string // suffixes to append to local name
|
||||
Port string // what port to use
|
||||
Ndots int // number of dots in name to trigger absolute lookup
|
||||
Timeout int // seconds before giving up on packet
|
||||
Attempts int // lost packets before giving up on server, not used in the package dns
|
||||
}
|
||||
|
||||
// ClientConfigFromFile parses a resolv.conf(5) like file and returns
|
||||
// a *ClientConfig.
|
||||
func ClientConfigFromFile(resolvconf string) (*ClientConfig, error) {
|
||||
file, err := os.Open(resolvconf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
return ClientConfigFromReader(file)
|
||||
}
|
||||
|
||||
// ClientConfigFromReader works like ClientConfigFromFile but takes an io.Reader as argument
|
||||
func ClientConfigFromReader(resolvconf io.Reader) (*ClientConfig, error) {
|
||||
c := new(ClientConfig)
|
||||
scanner := bufio.NewScanner(resolvconf)
|
||||
c.Servers = make([]string, 0)
|
||||
c.Search = make([]string, 0)
|
||||
c.Port = "53"
|
||||
c.Ndots = 1
|
||||
c.Timeout = 5
|
||||
c.Attempts = 2
|
||||
|
||||
for scanner.Scan() {
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
line := scanner.Text()
|
||||
f := strings.Fields(line)
|
||||
if len(f) < 1 {
|
||||
continue
|
||||
}
|
||||
switch f[0] {
|
||||
case "nameserver": // add one name server
|
||||
if len(f) > 1 {
|
||||
// One more check: make sure server name is
|
||||
// just an IP address. Otherwise we need DNS
|
||||
// to look it up.
|
||||
name := f[1]
|
||||
c.Servers = append(c.Servers, name)
|
||||
}
|
||||
|
||||
case "domain": // set search path to just this domain
|
||||
if len(f) > 1 {
|
||||
c.Search = make([]string, 1)
|
||||
c.Search[0] = f[1]
|
||||
} else {
|
||||
c.Search = make([]string, 0)
|
||||
}
|
||||
|
||||
case "search": // set search path to given servers
|
||||
c.Search = make([]string, len(f)-1)
|
||||
for i := 0; i < len(c.Search); i++ {
|
||||
c.Search[i] = f[i+1]
|
||||
}
|
||||
|
||||
case "options": // magic options
|
||||
for i := 1; i < len(f); i++ {
|
||||
s := f[i]
|
||||
switch {
|
||||
case len(s) >= 6 && s[:6] == "ndots:":
|
||||
n, _ := strconv.Atoi(s[6:])
|
||||
if n < 0 {
|
||||
n = 0
|
||||
} else if n > 15 {
|
||||
n = 15
|
||||
}
|
||||
c.Ndots = n
|
||||
case len(s) >= 8 && s[:8] == "timeout:":
|
||||
n, _ := strconv.Atoi(s[8:])
|
||||
if n < 1 {
|
||||
n = 1
|
||||
}
|
||||
c.Timeout = n
|
||||
case len(s) >= 9 && s[:9] == "attempts:":
|
||||
n, _ := strconv.Atoi(s[9:])
|
||||
if n < 1 {
|
||||
n = 1
|
||||
}
|
||||
c.Attempts = n
|
||||
case s == "rotate":
|
||||
/* not imp */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// NameList returns all of the names that should be queried based on the
|
||||
// config. It is based off of go's net/dns name building, but it does not
|
||||
// check the length of the resulting names.
|
||||
func (c *ClientConfig) NameList(name string) []string {
|
||||
// if this domain is already fully qualified, no append needed.
|
||||
if IsFqdn(name) {
|
||||
return []string{name}
|
||||
}
|
||||
|
||||
// Check to see if the name has more labels than Ndots. Do this before making
|
||||
// the domain fully qualified.
|
||||
hasNdots := CountLabel(name) > c.Ndots
|
||||
// Make the domain fully qualified.
|
||||
name = Fqdn(name)
|
||||
|
||||
// Make a list of names based off search.
|
||||
names := []string{}
|
||||
|
||||
// If name has enough dots, try that first.
|
||||
if hasNdots {
|
||||
names = append(names, name)
|
||||
}
|
||||
for _, s := range c.Search {
|
||||
names = append(names, Fqdn(name+s))
|
||||
}
|
||||
// If we didn't have enough dots, try after suffixes.
|
||||
if !hasNdots {
|
||||
names = append(names, name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
181
vendor/github.com/miekg/dns/clientconfig_test.go
generated
vendored
Normal file
181
vendor/github.com/miekg/dns/clientconfig_test.go
generated
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const normal string = `
|
||||
# Comment
|
||||
domain somedomain.com
|
||||
nameserver 10.28.10.2
|
||||
nameserver 11.28.10.1
|
||||
`
|
||||
|
||||
const missingNewline string = `
|
||||
domain somedomain.com
|
||||
nameserver 10.28.10.2
|
||||
nameserver 11.28.10.1` // <- NOTE: NO newline.
|
||||
|
||||
func testConfig(t *testing.T, data string) {
|
||||
cc, err := ClientConfigFromReader(strings.NewReader(data))
|
||||
if err != nil {
|
||||
t.Errorf("error parsing resolv.conf: %v", err)
|
||||
}
|
||||
if l := len(cc.Servers); l != 2 {
|
||||
t.Errorf("incorrect number of nameservers detected: %d", l)
|
||||
}
|
||||
if l := len(cc.Search); l != 1 {
|
||||
t.Errorf("domain directive not parsed correctly: %v", cc.Search)
|
||||
} else {
|
||||
if cc.Search[0] != "somedomain.com" {
|
||||
t.Errorf("domain is unexpected: %v", cc.Search[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNameserver(t *testing.T) { testConfig(t, normal) }
|
||||
func TestMissingFinalNewLine(t *testing.T) { testConfig(t, missingNewline) }
|
||||
|
||||
func TestNdots(t *testing.T) {
|
||||
ndotsVariants := map[string]int{
|
||||
"options ndots:0": 0,
|
||||
"options ndots:1": 1,
|
||||
"options ndots:15": 15,
|
||||
"options ndots:16": 15,
|
||||
"options ndots:-1": 0,
|
||||
"": 1,
|
||||
}
|
||||
|
||||
for data := range ndotsVariants {
|
||||
cc, err := ClientConfigFromReader(strings.NewReader(data))
|
||||
if err != nil {
|
||||
t.Errorf("error parsing resolv.conf: %v", err)
|
||||
}
|
||||
if cc.Ndots != ndotsVariants[data] {
|
||||
t.Errorf("Ndots not properly parsed: (Expected: %d / Was: %d)", ndotsVariants[data], cc.Ndots)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientConfigFromReaderAttempts(t *testing.T) {
|
||||
testCases := []struct {
|
||||
data string
|
||||
expected int
|
||||
}{
|
||||
{data: "options attempts:0", expected: 1},
|
||||
{data: "options attempts:1", expected: 1},
|
||||
{data: "options attempts:15", expected: 15},
|
||||
{data: "options attempts:16", expected: 16},
|
||||
{data: "options attempts:-1", expected: 1},
|
||||
{data: "options attempt:", expected: 2},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(strings.Replace(test.data, ":", " ", -1), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cc, err := ClientConfigFromReader(strings.NewReader(test.data))
|
||||
if err != nil {
|
||||
t.Errorf("error parsing resolv.conf: %v", err)
|
||||
}
|
||||
if cc.Attempts != test.expected {
|
||||
t.Errorf("A attempts not properly parsed: (Expected: %d / Was: %d)", test.expected, cc.Attempts)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadFromFile(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("tempDir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
path := filepath.Join(tempDir, "resolv.conf")
|
||||
if err := ioutil.WriteFile(path, []byte(normal), 0644); err != nil {
|
||||
t.Fatalf("writeFile: %v", err)
|
||||
}
|
||||
cc, err := ClientConfigFromFile(path)
|
||||
if err != nil {
|
||||
t.Errorf("error parsing resolv.conf: %v", err)
|
||||
}
|
||||
if l := len(cc.Servers); l != 2 {
|
||||
t.Errorf("incorrect number of nameservers detected: %d", l)
|
||||
}
|
||||
if l := len(cc.Search); l != 1 {
|
||||
t.Errorf("domain directive not parsed correctly: %v", cc.Search)
|
||||
} else {
|
||||
if cc.Search[0] != "somedomain.com" {
|
||||
t.Errorf("domain is unexpected: %v", cc.Search[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNameListNdots1(t *testing.T) {
|
||||
cfg := ClientConfig{
|
||||
Ndots: 1,
|
||||
}
|
||||
// fqdn should be only result returned
|
||||
names := cfg.NameList("miek.nl.")
|
||||
if len(names) != 1 {
|
||||
t.Errorf("NameList returned != 1 names: %v", names)
|
||||
} else if names[0] != "miek.nl." {
|
||||
t.Errorf("NameList didn't return sent fqdn domain: %v", names[0])
|
||||
}
|
||||
|
||||
cfg.Search = []string{
|
||||
"test",
|
||||
}
|
||||
// Sent domain has NDots and search
|
||||
names = cfg.NameList("miek.nl")
|
||||
if len(names) != 2 {
|
||||
t.Errorf("NameList returned != 2 names: %v", names)
|
||||
} else if names[0] != "miek.nl." {
|
||||
t.Errorf("NameList didn't return sent domain first: %v", names[0])
|
||||
} else if names[1] != "miek.nl.test." {
|
||||
t.Errorf("NameList didn't return search last: %v", names[1])
|
||||
}
|
||||
}
|
||||
func TestNameListNdots2(t *testing.T) {
|
||||
cfg := ClientConfig{
|
||||
Ndots: 2,
|
||||
}
|
||||
|
||||
// Sent domain has less than NDots and search
|
||||
cfg.Search = []string{
|
||||
"test",
|
||||
}
|
||||
names := cfg.NameList("miek.nl")
|
||||
|
||||
if len(names) != 2 {
|
||||
t.Errorf("NameList returned != 2 names: %v", names)
|
||||
} else if names[0] != "miek.nl.test." {
|
||||
t.Errorf("NameList didn't return search first: %v", names[0])
|
||||
} else if names[1] != "miek.nl." {
|
||||
t.Errorf("NameList didn't return sent domain last: %v", names[1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestNameListNdots0(t *testing.T) {
|
||||
cfg := ClientConfig{
|
||||
Ndots: 0,
|
||||
}
|
||||
cfg.Search = []string{
|
||||
"test",
|
||||
}
|
||||
// Sent domain has less than NDots and search
|
||||
names := cfg.NameList("miek")
|
||||
if len(names) != 2 {
|
||||
t.Errorf("NameList returned != 2 names: %v", names)
|
||||
} else if names[0] != "miek." {
|
||||
t.Errorf("NameList didn't return search first: %v", names[0])
|
||||
} else if names[1] != "miek.test." {
|
||||
t.Errorf("NameList didn't return sent domain last: %v", names[1])
|
||||
}
|
||||
}
|
||||
198
vendor/github.com/miekg/dns/compress_generate.go
generated
vendored
Normal file
198
vendor/github.com/miekg/dns/compress_generate.go
generated
vendored
Normal file
@@ -0,0 +1,198 @@
|
||||
//+build ignore
|
||||
|
||||
// compression_generate.go is meant to run with go generate. It will use
|
||||
// go/{importer,types} to track down all the RR struct types. Then for each type
|
||||
// it will look to see if there are (compressible) names, if so it will add that
|
||||
// type to compressionLenHelperType and comressionLenSearchType which "fake" the
|
||||
// compression so that Len() is fast.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"go/importer"
|
||||
"go/types"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
var packageHdr = `
|
||||
// Code generated by "go run compress_generate.go"; DO NOT EDIT.
|
||||
|
||||
package dns
|
||||
|
||||
`
|
||||
|
||||
// getTypeStruct will take a type and the package scope, and return the
|
||||
// (innermost) struct if the type is considered a RR type (currently defined as
|
||||
// those structs beginning with a RR_Header, could be redefined as implementing
|
||||
// the RR interface). The bool return value indicates if embedded structs were
|
||||
// resolved.
|
||||
func getTypeStruct(t types.Type, scope *types.Scope) (*types.Struct, bool) {
|
||||
st, ok := t.Underlying().(*types.Struct)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
if st.Field(0).Type() == scope.Lookup("RR_Header").Type() {
|
||||
return st, false
|
||||
}
|
||||
if st.Field(0).Anonymous() {
|
||||
st, _ := getTypeStruct(st.Field(0).Type(), scope)
|
||||
return st, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Import and type-check the package
|
||||
pkg, err := importer.Default().Import("github.com/miekg/dns")
|
||||
fatalIfErr(err)
|
||||
scope := pkg.Scope()
|
||||
|
||||
var domainTypes []string // Types that have a domain name in them (either compressible or not).
|
||||
var cdomainTypes []string // Types that have a compressible domain name in them (subset of domainType)
|
||||
Names:
|
||||
for _, name := range scope.Names() {
|
||||
o := scope.Lookup(name)
|
||||
if o == nil || !o.Exported() {
|
||||
continue
|
||||
}
|
||||
st, _ := getTypeStruct(o.Type(), scope)
|
||||
if st == nil {
|
||||
continue
|
||||
}
|
||||
if name == "PrivateRR" {
|
||||
continue
|
||||
}
|
||||
|
||||
if scope.Lookup("Type"+o.Name()) == nil && o.Name() != "RFC3597" {
|
||||
log.Fatalf("Constant Type%s does not exist.", o.Name())
|
||||
}
|
||||
|
||||
for i := 1; i < st.NumFields(); i++ {
|
||||
if _, ok := st.Field(i).Type().(*types.Slice); ok {
|
||||
if st.Tag(i) == `dns:"domain-name"` {
|
||||
domainTypes = append(domainTypes, o.Name())
|
||||
continue Names
|
||||
}
|
||||
if st.Tag(i) == `dns:"cdomain-name"` {
|
||||
cdomainTypes = append(cdomainTypes, o.Name())
|
||||
domainTypes = append(domainTypes, o.Name())
|
||||
continue Names
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
switch {
|
||||
case st.Tag(i) == `dns:"domain-name"`:
|
||||
domainTypes = append(domainTypes, o.Name())
|
||||
continue Names
|
||||
case st.Tag(i) == `dns:"cdomain-name"`:
|
||||
cdomainTypes = append(cdomainTypes, o.Name())
|
||||
domainTypes = append(domainTypes, o.Name())
|
||||
continue Names
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
b := &bytes.Buffer{}
|
||||
b.WriteString(packageHdr)
|
||||
|
||||
// compressionLenHelperType - all types that have domain-name/cdomain-name can be used for compressing names
|
||||
|
||||
fmt.Fprint(b, "func compressionLenHelperType(c map[string]int, r RR, initLen int) int {\n")
|
||||
fmt.Fprint(b, "currentLen := initLen\n")
|
||||
fmt.Fprint(b, "switch x := r.(type) {\n")
|
||||
for _, name := range domainTypes {
|
||||
o := scope.Lookup(name)
|
||||
st, _ := getTypeStruct(o.Type(), scope)
|
||||
|
||||
fmt.Fprintf(b, "case *%s:\n", name)
|
||||
for i := 1; i < st.NumFields(); i++ {
|
||||
out := func(s string) {
|
||||
fmt.Fprintf(b, "currentLen -= len(x.%s) + 1\n", st.Field(i).Name())
|
||||
fmt.Fprintf(b, "currentLen += compressionLenHelper(c, x.%s, currentLen)\n", st.Field(i).Name())
|
||||
}
|
||||
|
||||
if _, ok := st.Field(i).Type().(*types.Slice); ok {
|
||||
switch st.Tag(i) {
|
||||
case `dns:"domain-name"`:
|
||||
fallthrough
|
||||
case `dns:"cdomain-name"`:
|
||||
// For HIP we need to slice over the elements in this slice.
|
||||
fmt.Fprintf(b, `for i := range x.%s {
|
||||
currentLen -= len(x.%s[i]) + 1
|
||||
}
|
||||
`, st.Field(i).Name(), st.Field(i).Name())
|
||||
fmt.Fprintf(b, `for i := range x.%s {
|
||||
currentLen += compressionLenHelper(c, x.%s[i], currentLen)
|
||||
}
|
||||
`, st.Field(i).Name(), st.Field(i).Name())
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
switch {
|
||||
case st.Tag(i) == `dns:"cdomain-name"`:
|
||||
fallthrough
|
||||
case st.Tag(i) == `dns:"domain-name"`:
|
||||
out(st.Field(i).Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Fprintln(b, "}\nreturn currentLen - initLen\n}\n\n")
|
||||
|
||||
// compressionLenSearchType - search cdomain-tags types for compressible names.
|
||||
|
||||
fmt.Fprint(b, "func compressionLenSearchType(c map[string]int, r RR) (int, bool, int) {\n")
|
||||
fmt.Fprint(b, "switch x := r.(type) {\n")
|
||||
for _, name := range cdomainTypes {
|
||||
o := scope.Lookup(name)
|
||||
st, _ := getTypeStruct(o.Type(), scope)
|
||||
|
||||
fmt.Fprintf(b, "case *%s:\n", name)
|
||||
j := 1
|
||||
for i := 1; i < st.NumFields(); i++ {
|
||||
out := func(s string, j int) {
|
||||
fmt.Fprintf(b, "k%d, ok%d, sz%d := compressionLenSearch(c, x.%s)\n", j, j, j, st.Field(i).Name())
|
||||
}
|
||||
|
||||
// There are no slice types with names that can be compressed.
|
||||
|
||||
switch {
|
||||
case st.Tag(i) == `dns:"cdomain-name"`:
|
||||
out(st.Field(i).Name(), j)
|
||||
j++
|
||||
}
|
||||
}
|
||||
k := "k1"
|
||||
ok := "ok1"
|
||||
sz := "sz1"
|
||||
for i := 2; i < j; i++ {
|
||||
k += fmt.Sprintf(" + k%d", i)
|
||||
ok += fmt.Sprintf(" && ok%d", i)
|
||||
sz += fmt.Sprintf(" + sz%d", i)
|
||||
}
|
||||
fmt.Fprintf(b, "return %s, %s, %s\n", k, ok, sz)
|
||||
}
|
||||
fmt.Fprintln(b, "}\nreturn 0, false, 0\n}\n\n")
|
||||
|
||||
// gofmt
|
||||
res, err := format.Source(b.Bytes())
|
||||
if err != nil {
|
||||
b.WriteTo(os.Stderr)
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
f, err := os.Create("zcompress.go")
|
||||
fatalIfErr(err)
|
||||
defer f.Close()
|
||||
f.Write(res)
|
||||
}
|
||||
|
||||
func fatalIfErr(err error) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
43
vendor/github.com/miekg/dns/dane.go
generated
vendored
Normal file
43
vendor/github.com/miekg/dns/dane.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// CertificateToDANE converts a certificate to a hex string as used in the TLSA or SMIMEA records.
|
||||
func CertificateToDANE(selector, matchingType uint8, cert *x509.Certificate) (string, error) {
|
||||
switch matchingType {
|
||||
case 0:
|
||||
switch selector {
|
||||
case 0:
|
||||
return hex.EncodeToString(cert.Raw), nil
|
||||
case 1:
|
||||
return hex.EncodeToString(cert.RawSubjectPublicKeyInfo), nil
|
||||
}
|
||||
case 1:
|
||||
h := sha256.New()
|
||||
switch selector {
|
||||
case 0:
|
||||
h.Write(cert.Raw)
|
||||
return hex.EncodeToString(h.Sum(nil)), nil
|
||||
case 1:
|
||||
h.Write(cert.RawSubjectPublicKeyInfo)
|
||||
return hex.EncodeToString(h.Sum(nil)), nil
|
||||
}
|
||||
case 2:
|
||||
h := sha512.New()
|
||||
switch selector {
|
||||
case 0:
|
||||
h.Write(cert.Raw)
|
||||
return hex.EncodeToString(h.Sum(nil)), nil
|
||||
case 1:
|
||||
h.Write(cert.RawSubjectPublicKeyInfo)
|
||||
return hex.EncodeToString(h.Sum(nil)), nil
|
||||
}
|
||||
}
|
||||
return "", errors.New("dns: bad MatchingType or Selector")
|
||||
}
|
||||
288
vendor/github.com/miekg/dns/defaults.go
generated
vendored
Normal file
288
vendor/github.com/miekg/dns/defaults.go
generated
vendored
Normal file
@@ -0,0 +1,288 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const hexDigit = "0123456789abcdef"
|
||||
|
||||
// Everything is assumed in ClassINET.
|
||||
|
||||
// SetReply creates a reply message from a request message.
|
||||
func (dns *Msg) SetReply(request *Msg) *Msg {
|
||||
dns.Id = request.Id
|
||||
dns.Response = true
|
||||
dns.Opcode = request.Opcode
|
||||
if dns.Opcode == OpcodeQuery {
|
||||
dns.RecursionDesired = request.RecursionDesired // Copy rd bit
|
||||
dns.CheckingDisabled = request.CheckingDisabled // Copy cd bit
|
||||
}
|
||||
dns.Rcode = RcodeSuccess
|
||||
if len(request.Question) > 0 {
|
||||
dns.Question = make([]Question, 1)
|
||||
dns.Question[0] = request.Question[0]
|
||||
}
|
||||
return dns
|
||||
}
|
||||
|
||||
// SetQuestion creates a question message, it sets the Question
|
||||
// section, generates an Id and sets the RecursionDesired (RD)
|
||||
// bit to true.
|
||||
func (dns *Msg) SetQuestion(z string, t uint16) *Msg {
|
||||
dns.Id = Id()
|
||||
dns.RecursionDesired = true
|
||||
dns.Question = make([]Question, 1)
|
||||
dns.Question[0] = Question{z, t, ClassINET}
|
||||
return dns
|
||||
}
|
||||
|
||||
// SetNotify creates a notify message, it sets the Question
|
||||
// section, generates an Id and sets the Authoritative (AA)
|
||||
// bit to true.
|
||||
func (dns *Msg) SetNotify(z string) *Msg {
|
||||
dns.Opcode = OpcodeNotify
|
||||
dns.Authoritative = true
|
||||
dns.Id = Id()
|
||||
dns.Question = make([]Question, 1)
|
||||
dns.Question[0] = Question{z, TypeSOA, ClassINET}
|
||||
return dns
|
||||
}
|
||||
|
||||
// SetRcode creates an error message suitable for the request.
|
||||
func (dns *Msg) SetRcode(request *Msg, rcode int) *Msg {
|
||||
dns.SetReply(request)
|
||||
dns.Rcode = rcode
|
||||
return dns
|
||||
}
|
||||
|
||||
// SetRcodeFormatError creates a message with FormError set.
|
||||
func (dns *Msg) SetRcodeFormatError(request *Msg) *Msg {
|
||||
dns.Rcode = RcodeFormatError
|
||||
dns.Opcode = OpcodeQuery
|
||||
dns.Response = true
|
||||
dns.Authoritative = false
|
||||
dns.Id = request.Id
|
||||
return dns
|
||||
}
|
||||
|
||||
// SetUpdate makes the message a dynamic update message. It
|
||||
// sets the ZONE section to: z, TypeSOA, ClassINET.
|
||||
func (dns *Msg) SetUpdate(z string) *Msg {
|
||||
dns.Id = Id()
|
||||
dns.Response = false
|
||||
dns.Opcode = OpcodeUpdate
|
||||
dns.Compress = false // BIND9 cannot handle compression
|
||||
dns.Question = make([]Question, 1)
|
||||
dns.Question[0] = Question{z, TypeSOA, ClassINET}
|
||||
return dns
|
||||
}
|
||||
|
||||
// SetIxfr creates message for requesting an IXFR.
|
||||
func (dns *Msg) SetIxfr(z string, serial uint32, ns, mbox string) *Msg {
|
||||
dns.Id = Id()
|
||||
dns.Question = make([]Question, 1)
|
||||
dns.Ns = make([]RR, 1)
|
||||
s := new(SOA)
|
||||
s.Hdr = RR_Header{z, TypeSOA, ClassINET, defaultTtl, 0}
|
||||
s.Serial = serial
|
||||
s.Ns = ns
|
||||
s.Mbox = mbox
|
||||
dns.Question[0] = Question{z, TypeIXFR, ClassINET}
|
||||
dns.Ns[0] = s
|
||||
return dns
|
||||
}
|
||||
|
||||
// SetAxfr creates message for requesting an AXFR.
|
||||
func (dns *Msg) SetAxfr(z string) *Msg {
|
||||
dns.Id = Id()
|
||||
dns.Question = make([]Question, 1)
|
||||
dns.Question[0] = Question{z, TypeAXFR, ClassINET}
|
||||
return dns
|
||||
}
|
||||
|
||||
// SetTsig appends a TSIG RR to the message.
|
||||
// This is only a skeleton TSIG RR that is added as the last RR in the
|
||||
// additional section. The Tsig is calculated when the message is being send.
|
||||
func (dns *Msg) SetTsig(z, algo string, fudge uint16, timesigned int64) *Msg {
|
||||
t := new(TSIG)
|
||||
t.Hdr = RR_Header{z, TypeTSIG, ClassANY, 0, 0}
|
||||
t.Algorithm = algo
|
||||
t.Fudge = fudge
|
||||
t.TimeSigned = uint64(timesigned)
|
||||
t.OrigId = dns.Id
|
||||
dns.Extra = append(dns.Extra, t)
|
||||
return dns
|
||||
}
|
||||
|
||||
// SetEdns0 appends a EDNS0 OPT RR to the message.
|
||||
// TSIG should always the last RR in a message.
|
||||
func (dns *Msg) SetEdns0(udpsize uint16, do bool) *Msg {
|
||||
e := new(OPT)
|
||||
e.Hdr.Name = "."
|
||||
e.Hdr.Rrtype = TypeOPT
|
||||
e.SetUDPSize(udpsize)
|
||||
if do {
|
||||
e.SetDo()
|
||||
}
|
||||
dns.Extra = append(dns.Extra, e)
|
||||
return dns
|
||||
}
|
||||
|
||||
// IsTsig checks if the message has a TSIG record as the last record
|
||||
// in the additional section. It returns the TSIG record found or nil.
|
||||
func (dns *Msg) IsTsig() *TSIG {
|
||||
if len(dns.Extra) > 0 {
|
||||
if dns.Extra[len(dns.Extra)-1].Header().Rrtype == TypeTSIG {
|
||||
return dns.Extra[len(dns.Extra)-1].(*TSIG)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsEdns0 checks if the message has a EDNS0 (OPT) record, any EDNS0
|
||||
// record in the additional section will do. It returns the OPT record
|
||||
// found or nil.
|
||||
func (dns *Msg) IsEdns0() *OPT {
|
||||
// EDNS0 is at the end of the additional section, start there.
|
||||
// We might want to change this to *only* look at the last two
|
||||
// records. So we see TSIG and/or OPT - this a slightly bigger
|
||||
// change though.
|
||||
for i := len(dns.Extra) - 1; i >= 0; i-- {
|
||||
if dns.Extra[i].Header().Rrtype == TypeOPT {
|
||||
return dns.Extra[i].(*OPT)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsDomainName checks if s is a valid domain name, it returns the number of
|
||||
// labels and true, when a domain name is valid. Note that non fully qualified
|
||||
// domain name is considered valid, in this case the last label is counted in
|
||||
// the number of labels. When false is returned the number of labels is not
|
||||
// defined. Also note that this function is extremely liberal; almost any
|
||||
// string is a valid domain name as the DNS is 8 bit protocol. It checks if each
|
||||
// label fits in 63 characters, but there is no length check for the entire
|
||||
// string s. I.e. a domain name longer than 255 characters is considered valid.
|
||||
func IsDomainName(s string) (labels int, ok bool) {
|
||||
_, labels, err := packDomainName(s, nil, 0, nil, false)
|
||||
return labels, err == nil
|
||||
}
|
||||
|
||||
// IsSubDomain checks if child is indeed a child of the parent. If child and parent
|
||||
// are the same domain true is returned as well.
|
||||
func IsSubDomain(parent, child string) bool {
|
||||
// Entire child is contained in parent
|
||||
return CompareDomainName(parent, child) == CountLabel(parent)
|
||||
}
|
||||
|
||||
// IsMsg sanity checks buf and returns an error if it isn't a valid DNS packet.
|
||||
// The checking is performed on the binary payload.
|
||||
func IsMsg(buf []byte) error {
|
||||
// Header
|
||||
if len(buf) < 12 {
|
||||
return errors.New("dns: bad message header")
|
||||
}
|
||||
// Header: Opcode
|
||||
// TODO(miek): more checks here, e.g. check all header bits.
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsFqdn checks if a domain name is fully qualified.
|
||||
func IsFqdn(s string) bool {
|
||||
l := len(s)
|
||||
if l == 0 {
|
||||
return false
|
||||
}
|
||||
return s[l-1] == '.'
|
||||
}
|
||||
|
||||
// IsRRset checks if a set of RRs is a valid RRset as defined by RFC 2181.
|
||||
// This means the RRs need to have the same type, name, and class. Returns true
|
||||
// if the RR set is valid, otherwise false.
|
||||
func IsRRset(rrset []RR) bool {
|
||||
if len(rrset) == 0 {
|
||||
return false
|
||||
}
|
||||
if len(rrset) == 1 {
|
||||
return true
|
||||
}
|
||||
rrHeader := rrset[0].Header()
|
||||
rrType := rrHeader.Rrtype
|
||||
rrClass := rrHeader.Class
|
||||
rrName := rrHeader.Name
|
||||
|
||||
for _, rr := range rrset[1:] {
|
||||
curRRHeader := rr.Header()
|
||||
if curRRHeader.Rrtype != rrType || curRRHeader.Class != rrClass || curRRHeader.Name != rrName {
|
||||
// Mismatch between the records, so this is not a valid rrset for
|
||||
//signing/verifying
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Fqdn return the fully qualified domain name from s.
|
||||
// If s is already fully qualified, it behaves as the identity function.
|
||||
func Fqdn(s string) string {
|
||||
if IsFqdn(s) {
|
||||
return s
|
||||
}
|
||||
return s + "."
|
||||
}
|
||||
|
||||
// Copied from the official Go code.
|
||||
|
||||
// ReverseAddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP
|
||||
// address suitable for reverse DNS (PTR) record lookups or an error if it fails
|
||||
// to parse the IP address.
|
||||
func ReverseAddr(addr string) (arpa string, err error) {
|
||||
ip := net.ParseIP(addr)
|
||||
if ip == nil {
|
||||
return "", &Error{err: "unrecognized address: " + addr}
|
||||
}
|
||||
if ip.To4() != nil {
|
||||
return strconv.Itoa(int(ip[15])) + "." + strconv.Itoa(int(ip[14])) + "." + strconv.Itoa(int(ip[13])) + "." +
|
||||
strconv.Itoa(int(ip[12])) + ".in-addr.arpa.", nil
|
||||
}
|
||||
// Must be IPv6
|
||||
buf := make([]byte, 0, len(ip)*4+len("ip6.arpa."))
|
||||
// Add it, in reverse, to the buffer
|
||||
for i := len(ip) - 1; i >= 0; i-- {
|
||||
v := ip[i]
|
||||
buf = append(buf, hexDigit[v&0xF])
|
||||
buf = append(buf, '.')
|
||||
buf = append(buf, hexDigit[v>>4])
|
||||
buf = append(buf, '.')
|
||||
}
|
||||
// Append "ip6.arpa." and return (buf already has the final .)
|
||||
buf = append(buf, "ip6.arpa."...)
|
||||
return string(buf), nil
|
||||
}
|
||||
|
||||
// String returns the string representation for the type t.
|
||||
func (t Type) String() string {
|
||||
if t1, ok := TypeToString[uint16(t)]; ok {
|
||||
return t1
|
||||
}
|
||||
return "TYPE" + strconv.Itoa(int(t))
|
||||
}
|
||||
|
||||
// String returns the string representation for the class c.
|
||||
func (c Class) String() string {
|
||||
if s, ok := ClassToString[uint16(c)]; ok {
|
||||
// Only emit mnemonics when they are unambiguous, specically ANY is in both.
|
||||
if _, ok := StringToType[s]; !ok {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return "CLASS" + strconv.Itoa(int(c))
|
||||
}
|
||||
|
||||
// String returns the string representation for the name n.
|
||||
func (n Name) String() string {
|
||||
return sprintName(string(n))
|
||||
}
|
||||
97
vendor/github.com/miekg/dns/dns.go
generated
vendored
Normal file
97
vendor/github.com/miekg/dns/dns.go
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
package dns
|
||||
|
||||
import "strconv"
|
||||
|
||||
const (
|
||||
year68 = 1 << 31 // For RFC1982 (Serial Arithmetic) calculations in 32 bits.
|
||||
defaultTtl = 3600 // Default internal TTL.
|
||||
|
||||
// DefaultMsgSize is the standard default for messages larger than 512 bytes.
|
||||
DefaultMsgSize = 4096
|
||||
// MinMsgSize is the minimal size of a DNS packet.
|
||||
MinMsgSize = 512
|
||||
// MaxMsgSize is the largest possible DNS packet.
|
||||
MaxMsgSize = 65535
|
||||
)
|
||||
|
||||
// Error represents a DNS error.
|
||||
type Error struct{ err string }
|
||||
|
||||
func (e *Error) Error() string {
|
||||
if e == nil {
|
||||
return "dns: <nil>"
|
||||
}
|
||||
return "dns: " + e.err
|
||||
}
|
||||
|
||||
// An RR represents a resource record.
|
||||
type RR interface {
|
||||
// Header returns the header of an resource record. The header contains
|
||||
// everything up to the rdata.
|
||||
Header() *RR_Header
|
||||
// String returns the text representation of the resource record.
|
||||
String() string
|
||||
|
||||
// copy returns a copy of the RR
|
||||
copy() RR
|
||||
// len returns the length (in octets) of the uncompressed RR in wire format.
|
||||
len() int
|
||||
// pack packs an RR into wire format.
|
||||
pack([]byte, int, map[string]int, bool) (int, error)
|
||||
}
|
||||
|
||||
// RR_Header is the header all DNS resource records share.
|
||||
type RR_Header struct {
|
||||
Name string `dns:"cdomain-name"`
|
||||
Rrtype uint16
|
||||
Class uint16
|
||||
Ttl uint32
|
||||
Rdlength uint16 // Length of data after header.
|
||||
}
|
||||
|
||||
// Header returns itself. This is here to make RR_Header implements the RR interface.
|
||||
func (h *RR_Header) Header() *RR_Header { return h }
|
||||
|
||||
// Just to implement the RR interface.
|
||||
func (h *RR_Header) copy() RR { return nil }
|
||||
|
||||
func (h *RR_Header) String() string {
|
||||
var s string
|
||||
|
||||
if h.Rrtype == TypeOPT {
|
||||
s = ";"
|
||||
// and maybe other things
|
||||
}
|
||||
|
||||
s += sprintName(h.Name) + "\t"
|
||||
s += strconv.FormatInt(int64(h.Ttl), 10) + "\t"
|
||||
s += Class(h.Class).String() + "\t"
|
||||
s += Type(h.Rrtype).String() + "\t"
|
||||
return s
|
||||
}
|
||||
|
||||
func (h *RR_Header) len() int {
|
||||
l := len(h.Name) + 1
|
||||
l += 10 // rrtype(2) + class(2) + ttl(4) + rdlength(2)
|
||||
return l
|
||||
}
|
||||
|
||||
// ToRFC3597 converts a known RR to the unknown RR representation from RFC 3597.
|
||||
func (rr *RFC3597) ToRFC3597(r RR) error {
|
||||
buf := make([]byte, r.len()*2)
|
||||
off, err := PackRR(r, buf, 0, nil, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buf = buf[:off]
|
||||
if int(r.Header().Rdlength) > off {
|
||||
return ErrBuf
|
||||
}
|
||||
|
||||
rfc3597, _, err := unpackRFC3597(*r.Header(), buf, off-int(r.Header().Rdlength))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*rr = *rfc3597.(*RFC3597)
|
||||
return nil
|
||||
}
|
||||
230
vendor/github.com/miekg/dns/dns_bench_test.go
generated
vendored
Normal file
230
vendor/github.com/miekg/dns/dns_bench_test.go
generated
vendored
Normal file
@@ -0,0 +1,230 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkMsgLength(b *testing.B) {
|
||||
b.StopTimer()
|
||||
makeMsg := func(question string, ans, ns, e []RR) *Msg {
|
||||
msg := new(Msg)
|
||||
msg.SetQuestion(Fqdn(question), TypeANY)
|
||||
msg.Answer = append(msg.Answer, ans...)
|
||||
msg.Ns = append(msg.Ns, ns...)
|
||||
msg.Extra = append(msg.Extra, e...)
|
||||
msg.Compress = true
|
||||
return msg
|
||||
}
|
||||
name1 := "12345678901234567890123456789012345.12345678.123."
|
||||
rrMx := testRR(name1 + " 3600 IN MX 10 " + name1)
|
||||
msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
msg.Len()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMsgLengthNoCompression(b *testing.B) {
|
||||
b.StopTimer()
|
||||
makeMsg := func(question string, ans, ns, e []RR) *Msg {
|
||||
msg := new(Msg)
|
||||
msg.SetQuestion(Fqdn(question), TypeANY)
|
||||
msg.Answer = append(msg.Answer, ans...)
|
||||
msg.Ns = append(msg.Ns, ns...)
|
||||
msg.Extra = append(msg.Extra, e...)
|
||||
return msg
|
||||
}
|
||||
name1 := "12345678901234567890123456789012345.12345678.123."
|
||||
rrMx := testRR(name1 + " 3600 IN MX 10 " + name1)
|
||||
msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
msg.Len()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMsgLengthPack(b *testing.B) {
|
||||
makeMsg := func(question string, ans, ns, e []RR) *Msg {
|
||||
msg := new(Msg)
|
||||
msg.SetQuestion(Fqdn(question), TypeANY)
|
||||
msg.Answer = append(msg.Answer, ans...)
|
||||
msg.Ns = append(msg.Ns, ns...)
|
||||
msg.Extra = append(msg.Extra, e...)
|
||||
msg.Compress = true
|
||||
return msg
|
||||
}
|
||||
name1 := "12345678901234567890123456789012345.12345678.123."
|
||||
rrMx := testRR(name1 + " 3600 IN MX 10 " + name1)
|
||||
msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = msg.Pack()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPackDomainName(b *testing.B) {
|
||||
name1 := "12345678901234567890123456789012345.12345678.123."
|
||||
buf := make([]byte, len(name1)+1)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = PackDomainName(name1, buf, 0, nil, false)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnpackDomainName(b *testing.B) {
|
||||
name1 := "12345678901234567890123456789012345.12345678.123."
|
||||
buf := make([]byte, len(name1)+1)
|
||||
_, _ = PackDomainName(name1, buf, 0, nil, false)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _, _ = UnpackDomainName(buf, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnpackDomainNameUnprintable(b *testing.B) {
|
||||
name1 := "\x02\x02\x02\x025\x02\x02\x02\x02.12345678.123."
|
||||
buf := make([]byte, len(name1)+1)
|
||||
_, _ = PackDomainName(name1, buf, 0, nil, false)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _, _ = UnpackDomainName(buf, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCopy(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl.", TypeA)
|
||||
rr := testRR("miek.nl. 2311 IN A 127.0.0.1")
|
||||
m.Answer = []RR{rr}
|
||||
rr = testRR("miek.nl. 2311 IN NS 127.0.0.1")
|
||||
m.Ns = []RR{rr}
|
||||
rr = testRR("miek.nl. 2311 IN A 127.0.0.1")
|
||||
m.Extra = []RR{rr}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m.Copy()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPackA(b *testing.B) {
|
||||
a := &A{Hdr: RR_Header{Name: ".", Rrtype: TypeA, Class: ClassANY}, A: net.IPv4(127, 0, 0, 1)}
|
||||
|
||||
buf := make([]byte, a.len())
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = PackRR(a, buf, 0, nil, false)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnpackA(b *testing.B) {
|
||||
a := &A{Hdr: RR_Header{Name: ".", Rrtype: TypeA, Class: ClassANY}, A: net.IPv4(127, 0, 0, 1)}
|
||||
|
||||
buf := make([]byte, a.len())
|
||||
PackRR(a, buf, 0, nil, false)
|
||||
a = nil
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _, _ = UnpackRR(buf, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPackMX(b *testing.B) {
|
||||
m := &MX{Hdr: RR_Header{Name: ".", Rrtype: TypeA, Class: ClassANY}, Mx: "mx.miek.nl."}
|
||||
|
||||
buf := make([]byte, m.len())
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = PackRR(m, buf, 0, nil, false)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnpackMX(b *testing.B) {
|
||||
m := &MX{Hdr: RR_Header{Name: ".", Rrtype: TypeA, Class: ClassANY}, Mx: "mx.miek.nl."}
|
||||
|
||||
buf := make([]byte, m.len())
|
||||
PackRR(m, buf, 0, nil, false)
|
||||
m = nil
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _, _ = UnpackRR(buf, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPackAAAAA(b *testing.B) {
|
||||
aaaa := testRR(". IN A ::1")
|
||||
|
||||
buf := make([]byte, aaaa.len())
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = PackRR(aaaa, buf, 0, nil, false)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnpackAAAA(b *testing.B) {
|
||||
aaaa := testRR(". IN A ::1")
|
||||
|
||||
buf := make([]byte, aaaa.len())
|
||||
PackRR(aaaa, buf, 0, nil, false)
|
||||
aaaa = nil
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _, _ = UnpackRR(buf, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPackMsg(b *testing.B) {
|
||||
makeMsg := func(question string, ans, ns, e []RR) *Msg {
|
||||
msg := new(Msg)
|
||||
msg.SetQuestion(Fqdn(question), TypeANY)
|
||||
msg.Answer = append(msg.Answer, ans...)
|
||||
msg.Ns = append(msg.Ns, ns...)
|
||||
msg.Extra = append(msg.Extra, e...)
|
||||
msg.Compress = true
|
||||
return msg
|
||||
}
|
||||
name1 := "12345678901234567890123456789012345.12345678.123."
|
||||
rrMx := testRR(name1 + " 3600 IN MX 10 " + name1)
|
||||
msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)
|
||||
buf := make([]byte, 512)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = msg.PackBuffer(buf)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnpackMsg(b *testing.B) {
|
||||
makeMsg := func(question string, ans, ns, e []RR) *Msg {
|
||||
msg := new(Msg)
|
||||
msg.SetQuestion(Fqdn(question), TypeANY)
|
||||
msg.Answer = append(msg.Answer, ans...)
|
||||
msg.Ns = append(msg.Ns, ns...)
|
||||
msg.Extra = append(msg.Extra, e...)
|
||||
msg.Compress = true
|
||||
return msg
|
||||
}
|
||||
name1 := "12345678901234567890123456789012345.12345678.123."
|
||||
rrMx := testRR(name1 + " 3600 IN MX 10 " + name1)
|
||||
msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)
|
||||
msgBuf, _ := msg.Pack()
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = msg.Unpack(msgBuf)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIdGeneration(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = id()
|
||||
}
|
||||
}
|
||||
320
vendor/github.com/miekg/dns/dns_test.go
generated
vendored
Normal file
320
vendor/github.com/miekg/dns/dns_test.go
generated
vendored
Normal file
@@ -0,0 +1,320 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPackUnpack(t *testing.T) {
|
||||
out := new(Msg)
|
||||
out.Answer = make([]RR, 1)
|
||||
key := new(DNSKEY)
|
||||
key = &DNSKEY{Flags: 257, Protocol: 3, Algorithm: RSASHA1}
|
||||
key.Hdr = RR_Header{Name: "miek.nl.", Rrtype: TypeDNSKEY, Class: ClassINET, Ttl: 3600}
|
||||
key.PublicKey = "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ"
|
||||
|
||||
out.Answer[0] = key
|
||||
msg, err := out.Pack()
|
||||
if err != nil {
|
||||
t.Error("failed to pack msg with DNSKEY")
|
||||
}
|
||||
in := new(Msg)
|
||||
if in.Unpack(msg) != nil {
|
||||
t.Error("failed to unpack msg with DNSKEY")
|
||||
}
|
||||
|
||||
sig := new(RRSIG)
|
||||
sig = &RRSIG{TypeCovered: TypeDNSKEY, Algorithm: RSASHA1, Labels: 2,
|
||||
OrigTtl: 3600, Expiration: 4000, Inception: 4000, KeyTag: 34641, SignerName: "miek.nl.",
|
||||
Signature: "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ"}
|
||||
sig.Hdr = RR_Header{Name: "miek.nl.", Rrtype: TypeRRSIG, Class: ClassINET, Ttl: 3600}
|
||||
|
||||
out.Answer[0] = sig
|
||||
msg, err = out.Pack()
|
||||
if err != nil {
|
||||
t.Error("failed to pack msg with RRSIG")
|
||||
}
|
||||
|
||||
if in.Unpack(msg) != nil {
|
||||
t.Error("failed to unpack msg with RRSIG")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPackUnpack2(t *testing.T) {
|
||||
m := new(Msg)
|
||||
m.Extra = make([]RR, 1)
|
||||
m.Answer = make([]RR, 1)
|
||||
dom := "miek.nl."
|
||||
rr := new(A)
|
||||
rr.Hdr = RR_Header{Name: dom, Rrtype: TypeA, Class: ClassINET, Ttl: 0}
|
||||
rr.A = net.IPv4(127, 0, 0, 1)
|
||||
|
||||
x := new(TXT)
|
||||
x.Hdr = RR_Header{Name: dom, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}
|
||||
x.Txt = []string{"heelalaollo"}
|
||||
|
||||
m.Extra[0] = x
|
||||
m.Answer[0] = rr
|
||||
_, err := m.Pack()
|
||||
if err != nil {
|
||||
t.Error("Packing failed: ", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestPackUnpack3(t *testing.T) {
|
||||
m := new(Msg)
|
||||
m.Extra = make([]RR, 2)
|
||||
m.Answer = make([]RR, 1)
|
||||
dom := "miek.nl."
|
||||
rr := new(A)
|
||||
rr.Hdr = RR_Header{Name: dom, Rrtype: TypeA, Class: ClassINET, Ttl: 0}
|
||||
rr.A = net.IPv4(127, 0, 0, 1)
|
||||
|
||||
x1 := new(TXT)
|
||||
x1.Hdr = RR_Header{Name: dom, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}
|
||||
x1.Txt = []string{}
|
||||
|
||||
x2 := new(TXT)
|
||||
x2.Hdr = RR_Header{Name: dom, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}
|
||||
x2.Txt = []string{"heelalaollo"}
|
||||
|
||||
m.Extra[0] = x1
|
||||
m.Extra[1] = x2
|
||||
m.Answer[0] = rr
|
||||
b, err := m.Pack()
|
||||
if err != nil {
|
||||
t.Error("packing failed: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
var unpackMsg Msg
|
||||
err = unpackMsg.Unpack(b)
|
||||
if err != nil {
|
||||
t.Error("unpacking failed")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestBailiwick(t *testing.T) {
|
||||
yes := map[string]string{
|
||||
"miek1.nl": "miek1.nl",
|
||||
"miek.nl": "ns.miek.nl",
|
||||
".": "miek.nl",
|
||||
}
|
||||
for parent, child := range yes {
|
||||
if !IsSubDomain(parent, child) {
|
||||
t.Errorf("%s should be child of %s", child, parent)
|
||||
t.Errorf("comparelabels %d", CompareDomainName(parent, child))
|
||||
t.Errorf("lenlabels %d %d", CountLabel(parent), CountLabel(child))
|
||||
}
|
||||
}
|
||||
no := map[string]string{
|
||||
"www.miek.nl": "ns.miek.nl",
|
||||
"m\\.iek.nl": "ns.miek.nl",
|
||||
"w\\.iek.nl": "w.iek.nl",
|
||||
"p\\\\.iek.nl": "ns.p.iek.nl", // p\\.iek.nl , literal \ in domain name
|
||||
"miek.nl": ".",
|
||||
}
|
||||
for parent, child := range no {
|
||||
if IsSubDomain(parent, child) {
|
||||
t.Errorf("%s should not be child of %s", child, parent)
|
||||
t.Errorf("comparelabels %d", CompareDomainName(parent, child))
|
||||
t.Errorf("lenlabels %d %d", CountLabel(parent), CountLabel(child))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPackNAPTR(t *testing.T) {
|
||||
for _, n := range []string{
|
||||
`apple.com. IN NAPTR 100 50 "se" "SIP+D2U" "" _sip._udp.apple.com.`,
|
||||
`apple.com. IN NAPTR 90 50 "se" "SIP+D2T" "" _sip._tcp.apple.com.`,
|
||||
`apple.com. IN NAPTR 50 50 "se" "SIPS+D2T" "" _sips._tcp.apple.com.`,
|
||||
} {
|
||||
rr := testRR(n)
|
||||
msg := make([]byte, rr.len())
|
||||
if off, err := PackRR(rr, msg, 0, nil, false); err != nil {
|
||||
t.Errorf("packing failed: %v", err)
|
||||
t.Errorf("length %d, need more than %d", rr.len(), off)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestToRFC3597(t *testing.T) {
|
||||
a := testRR("miek.nl. IN A 10.0.1.1")
|
||||
x := new(RFC3597)
|
||||
x.ToRFC3597(a)
|
||||
if x.String() != `miek.nl. 3600 CLASS1 TYPE1 \# 4 0a000101` {
|
||||
t.Errorf("string mismatch, got: %s", x)
|
||||
}
|
||||
|
||||
b := testRR("miek.nl. IN MX 10 mx.miek.nl.")
|
||||
x.ToRFC3597(b)
|
||||
if x.String() != `miek.nl. 3600 CLASS1 TYPE15 \# 14 000a026d78046d69656b026e6c00` {
|
||||
t.Errorf("string mismatch, got: %s", x)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoRdataPack(t *testing.T) {
|
||||
data := make([]byte, 1024)
|
||||
for typ, fn := range TypeToRR {
|
||||
r := fn()
|
||||
*r.Header() = RR_Header{Name: "miek.nl.", Rrtype: typ, Class: ClassINET, Ttl: 16}
|
||||
_, err := PackRR(r, data, 0, nil, false)
|
||||
if err != nil {
|
||||
t.Errorf("failed to pack RR with zero rdata: %s: %v", TypeToString[typ], err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoRdataUnpack(t *testing.T) {
|
||||
data := make([]byte, 1024)
|
||||
for typ, fn := range TypeToRR {
|
||||
if typ == TypeSOA || typ == TypeTSIG || typ == TypeTKEY {
|
||||
// SOA, TSIG will not be seen (like this) in dyn. updates?
|
||||
// TKEY requires length fields to be present for the Key and OtherData fields
|
||||
continue
|
||||
}
|
||||
r := fn()
|
||||
*r.Header() = RR_Header{Name: "miek.nl.", Rrtype: typ, Class: ClassINET, Ttl: 16}
|
||||
off, err := PackRR(r, data, 0, nil, false)
|
||||
if err != nil {
|
||||
// Should always works, TestNoDataPack should have caught this
|
||||
t.Errorf("failed to pack RR: %v", err)
|
||||
continue
|
||||
}
|
||||
if _, _, err := UnpackRR(data[:off], 0); err != nil {
|
||||
t.Errorf("failed to unpack RR with zero rdata: %s: %v", TypeToString[typ], err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRdataOverflow(t *testing.T) {
|
||||
rr := new(RFC3597)
|
||||
rr.Hdr.Name = "."
|
||||
rr.Hdr.Class = ClassINET
|
||||
rr.Hdr.Rrtype = 65280
|
||||
rr.Rdata = hex.EncodeToString(make([]byte, 0xFFFF))
|
||||
buf := make([]byte, 0xFFFF*2)
|
||||
if _, err := PackRR(rr, buf, 0, nil, false); err != nil {
|
||||
t.Fatalf("maximum size rrdata pack failed: %v", err)
|
||||
}
|
||||
rr.Rdata += "00"
|
||||
if _, err := PackRR(rr, buf, 0, nil, false); err != ErrRdata {
|
||||
t.Fatalf("oversize rrdata pack didn't return ErrRdata - instead: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopy(t *testing.T) {
|
||||
rr := testRR("miek.nl. 2311 IN A 127.0.0.1") // Weird TTL to avoid catching TTL
|
||||
rr1 := Copy(rr)
|
||||
if rr.String() != rr1.String() {
|
||||
t.Fatalf("Copy() failed %s != %s", rr.String(), rr1.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgCopy(t *testing.T) {
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl.", TypeA)
|
||||
rr := testRR("miek.nl. 2311 IN A 127.0.0.1")
|
||||
m.Answer = []RR{rr}
|
||||
rr = testRR("miek.nl. 2311 IN NS 127.0.0.1")
|
||||
m.Ns = []RR{rr}
|
||||
|
||||
m1 := m.Copy()
|
||||
if m.String() != m1.String() {
|
||||
t.Fatalf("Msg.Copy() failed %s != %s", m.String(), m1.String())
|
||||
}
|
||||
|
||||
m1.Answer[0] = testRR("somethingelse.nl. 2311 IN A 127.0.0.1")
|
||||
if m.String() == m1.String() {
|
||||
t.Fatalf("Msg.Copy() failed; change to copy changed template %s", m.String())
|
||||
}
|
||||
|
||||
rr = testRR("miek.nl. 2311 IN A 127.0.0.2")
|
||||
m1.Answer = append(m1.Answer, rr)
|
||||
if m1.Ns[0].String() == m1.Answer[1].String() {
|
||||
t.Fatalf("Msg.Copy() failed; append changed underlying array %s", m1.Ns[0].String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgPackBuffer(t *testing.T) {
|
||||
var testMessages = []string{
|
||||
// news.ycombinator.com.in.escapemg.com. IN A, response
|
||||
"586285830001000000010000046e6577730b79636f6d62696e61746f7203636f6d02696e086573636170656d6703636f6d0000010001c0210006000100000e10002c036e7332c02103646e730b67726f6f7665736861726bc02d77ed50e600002a3000000e1000093a8000000e10",
|
||||
|
||||
// news.ycombinator.com.in.escapemg.com. IN A, question
|
||||
"586201000001000000000000046e6577730b79636f6d62696e61746f7203636f6d02696e086573636170656d6703636f6d0000010001",
|
||||
|
||||
"398781020001000000000000046e6577730b79636f6d62696e61746f7203636f6d0000010001",
|
||||
}
|
||||
|
||||
for i, hexData := range testMessages {
|
||||
// we won't fail the decoding of the hex
|
||||
input, _ := hex.DecodeString(hexData)
|
||||
m := new(Msg)
|
||||
if err := m.Unpack(input); err != nil {
|
||||
t.Errorf("packet %d failed to unpack", i)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we can decode a TKEY packet from the string, modify the RR, and then pack it again.
|
||||
func TestTKEY(t *testing.T) {
|
||||
// An example TKEY RR captured. There is no known accepted standard text format for a TKEY
|
||||
// record so we do this from a hex string instead of from a text readable string.
|
||||
tkeyStr := "0737362d6d732d370932322d3332633233332463303439663961662d633065612d313165372d363839362d6463333937396666656666640000f900ff0000000000d2086773732d747369670059fd01f359fe53730003000000b8a181b53081b2a0030a0100a10b06092a864882f712010202a2819d04819a60819706092a864886f71201020202006f8187308184a003020105a10302010fa2783076a003020112a26f046db29b1b1d2625da3b20b49dafef930dd1e9aad335e1c5f45dcd95e0005d67a1100f3e573d70506659dbed064553f1ab890f68f65ae10def0dad5b423b39f240ebe666f2886c5fe03819692d29182bbed87b83e1f9d16b7334ec16a3c4fc5ad4a990088e0be43f0c6957916f5fe60000"
|
||||
tkeyBytes, err := hex.DecodeString(tkeyStr)
|
||||
if err != nil {
|
||||
t.Fatal("unable to decode TKEY string ", err)
|
||||
}
|
||||
// Decode the RR
|
||||
rr, tkeyLen, unPackErr := UnpackRR(tkeyBytes, 0)
|
||||
if unPackErr != nil {
|
||||
t.Fatal("unable to decode TKEY RR", unPackErr)
|
||||
}
|
||||
// Make sure it's a TKEY record
|
||||
if rr.Header().Rrtype != TypeTKEY {
|
||||
t.Fatal("Unable to decode TKEY")
|
||||
}
|
||||
// Make sure we get back the same length
|
||||
if rr.len() != len(tkeyBytes) {
|
||||
t.Fatalf("Lengths don't match %d != %d", rr.len(), len(tkeyBytes))
|
||||
}
|
||||
// make space for it with some fudge room
|
||||
msg := make([]byte, tkeyLen+1000)
|
||||
offset, packErr := PackRR(rr, msg, 0, nil, false)
|
||||
if packErr != nil {
|
||||
t.Fatal("unable to pack TKEY RR", packErr)
|
||||
}
|
||||
if offset != len(tkeyBytes) {
|
||||
t.Fatalf("mismatched TKEY RR size %d != %d", len(tkeyBytes), offset)
|
||||
}
|
||||
if bytes.Compare(tkeyBytes, msg[0:offset]) != 0 {
|
||||
t.Fatal("mismatched TKEY data after rewriting bytes")
|
||||
}
|
||||
t.Logf("got TKEY of: " + rr.String())
|
||||
// Now add some bytes to this and make sure we can encode OtherData properly
|
||||
tkey := rr.(*TKEY)
|
||||
tkey.OtherData = "abcd"
|
||||
tkey.OtherLen = 2
|
||||
offset, packErr = PackRR(tkey, msg, 0, nil, false)
|
||||
if packErr != nil {
|
||||
t.Fatal("unable to pack TKEY RR after modification", packErr)
|
||||
}
|
||||
if offset != (len(tkeyBytes) + 2) {
|
||||
t.Fatalf("mismatched TKEY RR size %d != %d", offset, len(tkeyBytes)+2)
|
||||
}
|
||||
t.Logf("modified to TKEY of: " + rr.String())
|
||||
|
||||
// Make sure we can parse our string output
|
||||
tkey.Hdr.Class = ClassINET // https://github.com/miekg/dns/issues/577
|
||||
newRR, newError := NewRR(tkey.String())
|
||||
if newError != nil {
|
||||
t.Fatalf("unable to parse TKEY string: %s", newError)
|
||||
}
|
||||
t.Log("got reparsed TKEY of newRR: " + newRR.String())
|
||||
}
|
||||
785
vendor/github.com/miekg/dns/dnssec.go
generated
vendored
Normal file
785
vendor/github.com/miekg/dns/dnssec.go
generated
vendored
Normal file
@@ -0,0 +1,785 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
_ "crypto/md5"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
_ "crypto/sha1"
|
||||
_ "crypto/sha256"
|
||||
_ "crypto/sha512"
|
||||
"encoding/asn1"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"math/big"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
)
|
||||
|
||||
// DNSSEC encryption algorithm codes.
|
||||
const (
|
||||
_ uint8 = iota
|
||||
RSAMD5
|
||||
DH
|
||||
DSA
|
||||
_ // Skip 4, RFC 6725, section 2.1
|
||||
RSASHA1
|
||||
DSANSEC3SHA1
|
||||
RSASHA1NSEC3SHA1
|
||||
RSASHA256
|
||||
_ // Skip 9, RFC 6725, section 2.1
|
||||
RSASHA512
|
||||
_ // Skip 11, RFC 6725, section 2.1
|
||||
ECCGOST
|
||||
ECDSAP256SHA256
|
||||
ECDSAP384SHA384
|
||||
ED25519
|
||||
ED448
|
||||
INDIRECT uint8 = 252
|
||||
PRIVATEDNS uint8 = 253 // Private (experimental keys)
|
||||
PRIVATEOID uint8 = 254
|
||||
)
|
||||
|
||||
// AlgorithmToString is a map of algorithm IDs to algorithm names.
|
||||
var AlgorithmToString = map[uint8]string{
|
||||
RSAMD5: "RSAMD5",
|
||||
DH: "DH",
|
||||
DSA: "DSA",
|
||||
RSASHA1: "RSASHA1",
|
||||
DSANSEC3SHA1: "DSA-NSEC3-SHA1",
|
||||
RSASHA1NSEC3SHA1: "RSASHA1-NSEC3-SHA1",
|
||||
RSASHA256: "RSASHA256",
|
||||
RSASHA512: "RSASHA512",
|
||||
ECCGOST: "ECC-GOST",
|
||||
ECDSAP256SHA256: "ECDSAP256SHA256",
|
||||
ECDSAP384SHA384: "ECDSAP384SHA384",
|
||||
ED25519: "ED25519",
|
||||
ED448: "ED448",
|
||||
INDIRECT: "INDIRECT",
|
||||
PRIVATEDNS: "PRIVATEDNS",
|
||||
PRIVATEOID: "PRIVATEOID",
|
||||
}
|
||||
|
||||
// StringToAlgorithm is the reverse of AlgorithmToString.
|
||||
var StringToAlgorithm = reverseInt8(AlgorithmToString)
|
||||
|
||||
// AlgorithmToHash is a map of algorithm crypto hash IDs to crypto.Hash's.
|
||||
var AlgorithmToHash = map[uint8]crypto.Hash{
|
||||
RSAMD5: crypto.MD5, // Deprecated in RFC 6725
|
||||
DSA: crypto.SHA1,
|
||||
RSASHA1: crypto.SHA1,
|
||||
RSASHA1NSEC3SHA1: crypto.SHA1,
|
||||
RSASHA256: crypto.SHA256,
|
||||
ECDSAP256SHA256: crypto.SHA256,
|
||||
ECDSAP384SHA384: crypto.SHA384,
|
||||
RSASHA512: crypto.SHA512,
|
||||
ED25519: crypto.Hash(0),
|
||||
}
|
||||
|
||||
// DNSSEC hashing algorithm codes.
|
||||
const (
|
||||
_ uint8 = iota
|
||||
SHA1 // RFC 4034
|
||||
SHA256 // RFC 4509
|
||||
GOST94 // RFC 5933
|
||||
SHA384 // Experimental
|
||||
SHA512 // Experimental
|
||||
)
|
||||
|
||||
// HashToString is a map of hash IDs to names.
|
||||
var HashToString = map[uint8]string{
|
||||
SHA1: "SHA1",
|
||||
SHA256: "SHA256",
|
||||
GOST94: "GOST94",
|
||||
SHA384: "SHA384",
|
||||
SHA512: "SHA512",
|
||||
}
|
||||
|
||||
// StringToHash is a map of names to hash IDs.
|
||||
var StringToHash = reverseInt8(HashToString)
|
||||
|
||||
// DNSKEY flag values.
|
||||
const (
|
||||
SEP = 1
|
||||
REVOKE = 1 << 7
|
||||
ZONE = 1 << 8
|
||||
)
|
||||
|
||||
// The RRSIG needs to be converted to wireformat with some of the rdata (the signature) missing.
|
||||
type rrsigWireFmt struct {
|
||||
TypeCovered uint16
|
||||
Algorithm uint8
|
||||
Labels uint8
|
||||
OrigTtl uint32
|
||||
Expiration uint32
|
||||
Inception uint32
|
||||
KeyTag uint16
|
||||
SignerName string `dns:"domain-name"`
|
||||
/* No Signature */
|
||||
}
|
||||
|
||||
// Used for converting DNSKEY's rdata to wirefmt.
|
||||
type dnskeyWireFmt struct {
|
||||
Flags uint16
|
||||
Protocol uint8
|
||||
Algorithm uint8
|
||||
PublicKey string `dns:"base64"`
|
||||
/* Nothing is left out */
|
||||
}
|
||||
|
||||
func divRoundUp(a, b int) int {
|
||||
return (a + b - 1) / b
|
||||
}
|
||||
|
||||
// KeyTag calculates the keytag (or key-id) of the DNSKEY.
|
||||
func (k *DNSKEY) KeyTag() uint16 {
|
||||
if k == nil {
|
||||
return 0
|
||||
}
|
||||
var keytag int
|
||||
switch k.Algorithm {
|
||||
case RSAMD5:
|
||||
// Look at the bottom two bytes of the modules, which the last
|
||||
// item in the pubkey. We could do this faster by looking directly
|
||||
// at the base64 values. But I'm lazy.
|
||||
modulus, _ := fromBase64([]byte(k.PublicKey))
|
||||
if len(modulus) > 1 {
|
||||
x := binary.BigEndian.Uint16(modulus[len(modulus)-2:])
|
||||
keytag = int(x)
|
||||
}
|
||||
default:
|
||||
keywire := new(dnskeyWireFmt)
|
||||
keywire.Flags = k.Flags
|
||||
keywire.Protocol = k.Protocol
|
||||
keywire.Algorithm = k.Algorithm
|
||||
keywire.PublicKey = k.PublicKey
|
||||
wire := make([]byte, DefaultMsgSize)
|
||||
n, err := packKeyWire(keywire, wire)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
wire = wire[:n]
|
||||
for i, v := range wire {
|
||||
if i&1 != 0 {
|
||||
keytag += int(v) // must be larger than uint32
|
||||
} else {
|
||||
keytag += int(v) << 8
|
||||
}
|
||||
}
|
||||
keytag += (keytag >> 16) & 0xFFFF
|
||||
keytag &= 0xFFFF
|
||||
}
|
||||
return uint16(keytag)
|
||||
}
|
||||
|
||||
// ToDS converts a DNSKEY record to a DS record.
|
||||
func (k *DNSKEY) ToDS(h uint8) *DS {
|
||||
if k == nil {
|
||||
return nil
|
||||
}
|
||||
ds := new(DS)
|
||||
ds.Hdr.Name = k.Hdr.Name
|
||||
ds.Hdr.Class = k.Hdr.Class
|
||||
ds.Hdr.Rrtype = TypeDS
|
||||
ds.Hdr.Ttl = k.Hdr.Ttl
|
||||
ds.Algorithm = k.Algorithm
|
||||
ds.DigestType = h
|
||||
ds.KeyTag = k.KeyTag()
|
||||
|
||||
keywire := new(dnskeyWireFmt)
|
||||
keywire.Flags = k.Flags
|
||||
keywire.Protocol = k.Protocol
|
||||
keywire.Algorithm = k.Algorithm
|
||||
keywire.PublicKey = k.PublicKey
|
||||
wire := make([]byte, DefaultMsgSize)
|
||||
n, err := packKeyWire(keywire, wire)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
wire = wire[:n]
|
||||
|
||||
owner := make([]byte, 255)
|
||||
off, err1 := PackDomainName(strings.ToLower(k.Hdr.Name), owner, 0, nil, false)
|
||||
if err1 != nil {
|
||||
return nil
|
||||
}
|
||||
owner = owner[:off]
|
||||
// RFC4034:
|
||||
// digest = digest_algorithm( DNSKEY owner name | DNSKEY RDATA);
|
||||
// "|" denotes concatenation
|
||||
// DNSKEY RDATA = Flags | Protocol | Algorithm | Public Key.
|
||||
|
||||
var hash crypto.Hash
|
||||
switch h {
|
||||
case SHA1:
|
||||
hash = crypto.SHA1
|
||||
case SHA256:
|
||||
hash = crypto.SHA256
|
||||
case SHA384:
|
||||
hash = crypto.SHA384
|
||||
case SHA512:
|
||||
hash = crypto.SHA512
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
s := hash.New()
|
||||
s.Write(owner)
|
||||
s.Write(wire)
|
||||
ds.Digest = hex.EncodeToString(s.Sum(nil))
|
||||
return ds
|
||||
}
|
||||
|
||||
// ToCDNSKEY converts a DNSKEY record to a CDNSKEY record.
|
||||
func (k *DNSKEY) ToCDNSKEY() *CDNSKEY {
|
||||
c := &CDNSKEY{DNSKEY: *k}
|
||||
c.Hdr = k.Hdr
|
||||
c.Hdr.Rrtype = TypeCDNSKEY
|
||||
return c
|
||||
}
|
||||
|
||||
// ToCDS converts a DS record to a CDS record.
|
||||
func (d *DS) ToCDS() *CDS {
|
||||
c := &CDS{DS: *d}
|
||||
c.Hdr = d.Hdr
|
||||
c.Hdr.Rrtype = TypeCDS
|
||||
return c
|
||||
}
|
||||
|
||||
// Sign signs an RRSet. The signature needs to be filled in with the values:
|
||||
// Inception, Expiration, KeyTag, SignerName and Algorithm. The rest is copied
|
||||
// from the RRset. Sign returns a non-nill error when the signing went OK.
|
||||
// There is no check if RRSet is a proper (RFC 2181) RRSet. If OrigTTL is non
|
||||
// zero, it is used as-is, otherwise the TTL of the RRset is used as the
|
||||
// OrigTTL.
|
||||
func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error {
|
||||
if k == nil {
|
||||
return ErrPrivKey
|
||||
}
|
||||
// s.Inception and s.Expiration may be 0 (rollover etc.), the rest must be set
|
||||
if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 {
|
||||
return ErrKey
|
||||
}
|
||||
|
||||
rr.Hdr.Rrtype = TypeRRSIG
|
||||
rr.Hdr.Name = rrset[0].Header().Name
|
||||
rr.Hdr.Class = rrset[0].Header().Class
|
||||
if rr.OrigTtl == 0 { // If set don't override
|
||||
rr.OrigTtl = rrset[0].Header().Ttl
|
||||
}
|
||||
rr.TypeCovered = rrset[0].Header().Rrtype
|
||||
rr.Labels = uint8(CountLabel(rrset[0].Header().Name))
|
||||
|
||||
if strings.HasPrefix(rrset[0].Header().Name, "*") {
|
||||
rr.Labels-- // wildcard, remove from label count
|
||||
}
|
||||
|
||||
sigwire := new(rrsigWireFmt)
|
||||
sigwire.TypeCovered = rr.TypeCovered
|
||||
sigwire.Algorithm = rr.Algorithm
|
||||
sigwire.Labels = rr.Labels
|
||||
sigwire.OrigTtl = rr.OrigTtl
|
||||
sigwire.Expiration = rr.Expiration
|
||||
sigwire.Inception = rr.Inception
|
||||
sigwire.KeyTag = rr.KeyTag
|
||||
// For signing, lowercase this name
|
||||
sigwire.SignerName = strings.ToLower(rr.SignerName)
|
||||
|
||||
// Create the desired binary blob
|
||||
signdata := make([]byte, DefaultMsgSize)
|
||||
n, err := packSigWire(sigwire, signdata)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
signdata = signdata[:n]
|
||||
wire, err := rawSignatureData(rrset, rr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hash, ok := AlgorithmToHash[rr.Algorithm]
|
||||
if !ok {
|
||||
return ErrAlg
|
||||
}
|
||||
|
||||
switch rr.Algorithm {
|
||||
case ED25519:
|
||||
// ed25519 signs the raw message and performs hashing internally.
|
||||
// All other supported signature schemes operate over the pre-hashed
|
||||
// message, and thus ed25519 must be handled separately here.
|
||||
//
|
||||
// The raw message is passed directly into sign and crypto.Hash(0) is
|
||||
// used to signal to the crypto.Signer that the data has not been hashed.
|
||||
signature, err := sign(k, append(signdata, wire...), crypto.Hash(0), rr.Algorithm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rr.Signature = toBase64(signature)
|
||||
default:
|
||||
h := hash.New()
|
||||
h.Write(signdata)
|
||||
h.Write(wire)
|
||||
|
||||
signature, err := sign(k, h.Sum(nil), hash, rr.Algorithm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rr.Signature = toBase64(signature)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func sign(k crypto.Signer, hashed []byte, hash crypto.Hash, alg uint8) ([]byte, error) {
|
||||
signature, err := k.Sign(rand.Reader, hashed, hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch alg {
|
||||
case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512:
|
||||
return signature, nil
|
||||
|
||||
case ECDSAP256SHA256, ECDSAP384SHA384:
|
||||
ecdsaSignature := &struct {
|
||||
R, S *big.Int
|
||||
}{}
|
||||
if _, err := asn1.Unmarshal(signature, ecdsaSignature); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var intlen int
|
||||
switch alg {
|
||||
case ECDSAP256SHA256:
|
||||
intlen = 32
|
||||
case ECDSAP384SHA384:
|
||||
intlen = 48
|
||||
}
|
||||
|
||||
signature := intToBytes(ecdsaSignature.R, intlen)
|
||||
signature = append(signature, intToBytes(ecdsaSignature.S, intlen)...)
|
||||
return signature, nil
|
||||
|
||||
// There is no defined interface for what a DSA backed crypto.Signer returns
|
||||
case DSA, DSANSEC3SHA1:
|
||||
// t := divRoundUp(divRoundUp(p.PublicKey.Y.BitLen(), 8)-64, 8)
|
||||
// signature := []byte{byte(t)}
|
||||
// signature = append(signature, intToBytes(r1, 20)...)
|
||||
// signature = append(signature, intToBytes(s1, 20)...)
|
||||
// rr.Signature = signature
|
||||
|
||||
case ED25519:
|
||||
return signature, nil
|
||||
}
|
||||
|
||||
return nil, ErrAlg
|
||||
}
|
||||
|
||||
// Verify validates an RRSet with the signature and key. This is only the
|
||||
// cryptographic test, the signature validity period must be checked separately.
|
||||
// This function copies the rdata of some RRs (to lowercase domain names) for the validation to work.
|
||||
func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
|
||||
// First the easy checks
|
||||
if !IsRRset(rrset) {
|
||||
return ErrRRset
|
||||
}
|
||||
if rr.KeyTag != k.KeyTag() {
|
||||
return ErrKey
|
||||
}
|
||||
if rr.Hdr.Class != k.Hdr.Class {
|
||||
return ErrKey
|
||||
}
|
||||
if rr.Algorithm != k.Algorithm {
|
||||
return ErrKey
|
||||
}
|
||||
if strings.ToLower(rr.SignerName) != strings.ToLower(k.Hdr.Name) {
|
||||
return ErrKey
|
||||
}
|
||||
if k.Protocol != 3 {
|
||||
return ErrKey
|
||||
}
|
||||
|
||||
// IsRRset checked that we have at least one RR and that the RRs in
|
||||
// the set have consistent type, class, and name. Also check that type and
|
||||
// class matches the RRSIG record.
|
||||
if rrset[0].Header().Class != rr.Hdr.Class {
|
||||
return ErrRRset
|
||||
}
|
||||
if rrset[0].Header().Rrtype != rr.TypeCovered {
|
||||
return ErrRRset
|
||||
}
|
||||
|
||||
// RFC 4035 5.3.2. Reconstructing the Signed Data
|
||||
// Copy the sig, except the rrsig data
|
||||
sigwire := new(rrsigWireFmt)
|
||||
sigwire.TypeCovered = rr.TypeCovered
|
||||
sigwire.Algorithm = rr.Algorithm
|
||||
sigwire.Labels = rr.Labels
|
||||
sigwire.OrigTtl = rr.OrigTtl
|
||||
sigwire.Expiration = rr.Expiration
|
||||
sigwire.Inception = rr.Inception
|
||||
sigwire.KeyTag = rr.KeyTag
|
||||
sigwire.SignerName = strings.ToLower(rr.SignerName)
|
||||
// Create the desired binary blob
|
||||
signeddata := make([]byte, DefaultMsgSize)
|
||||
n, err := packSigWire(sigwire, signeddata)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
signeddata = signeddata[:n]
|
||||
wire, err := rawSignatureData(rrset, rr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sigbuf := rr.sigBuf() // Get the binary signature data
|
||||
if rr.Algorithm == PRIVATEDNS { // PRIVATEOID
|
||||
// TODO(miek)
|
||||
// remove the domain name and assume its ours?
|
||||
}
|
||||
|
||||
hash, ok := AlgorithmToHash[rr.Algorithm]
|
||||
if !ok {
|
||||
return ErrAlg
|
||||
}
|
||||
|
||||
switch rr.Algorithm {
|
||||
case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512, RSAMD5:
|
||||
// TODO(mg): this can be done quicker, ie. cache the pubkey data somewhere??
|
||||
pubkey := k.publicKeyRSA() // Get the key
|
||||
if pubkey == nil {
|
||||
return ErrKey
|
||||
}
|
||||
|
||||
h := hash.New()
|
||||
h.Write(signeddata)
|
||||
h.Write(wire)
|
||||
return rsa.VerifyPKCS1v15(pubkey, hash, h.Sum(nil), sigbuf)
|
||||
|
||||
case ECDSAP256SHA256, ECDSAP384SHA384:
|
||||
pubkey := k.publicKeyECDSA()
|
||||
if pubkey == nil {
|
||||
return ErrKey
|
||||
}
|
||||
|
||||
// Split sigbuf into the r and s coordinates
|
||||
r := new(big.Int).SetBytes(sigbuf[:len(sigbuf)/2])
|
||||
s := new(big.Int).SetBytes(sigbuf[len(sigbuf)/2:])
|
||||
|
||||
h := hash.New()
|
||||
h.Write(signeddata)
|
||||
h.Write(wire)
|
||||
if ecdsa.Verify(pubkey, h.Sum(nil), r, s) {
|
||||
return nil
|
||||
}
|
||||
return ErrSig
|
||||
|
||||
case ED25519:
|
||||
pubkey := k.publicKeyED25519()
|
||||
if pubkey == nil {
|
||||
return ErrKey
|
||||
}
|
||||
|
||||
if ed25519.Verify(pubkey, append(signeddata, wire...), sigbuf) {
|
||||
return nil
|
||||
}
|
||||
return ErrSig
|
||||
|
||||
default:
|
||||
return ErrAlg
|
||||
}
|
||||
}
|
||||
|
||||
// ValidityPeriod uses RFC1982 serial arithmetic to calculate
|
||||
// if a signature period is valid. If t is the zero time, the
|
||||
// current time is taken other t is. Returns true if the signature
|
||||
// is valid at the given time, otherwise returns false.
|
||||
func (rr *RRSIG) ValidityPeriod(t time.Time) bool {
|
||||
var utc int64
|
||||
if t.IsZero() {
|
||||
utc = time.Now().UTC().Unix()
|
||||
} else {
|
||||
utc = t.UTC().Unix()
|
||||
}
|
||||
modi := (int64(rr.Inception) - utc) / year68
|
||||
mode := (int64(rr.Expiration) - utc) / year68
|
||||
ti := int64(rr.Inception) + (modi * year68)
|
||||
te := int64(rr.Expiration) + (mode * year68)
|
||||
return ti <= utc && utc <= te
|
||||
}
|
||||
|
||||
// Return the signatures base64 encodedig sigdata as a byte slice.
|
||||
func (rr *RRSIG) sigBuf() []byte {
|
||||
sigbuf, err := fromBase64([]byte(rr.Signature))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return sigbuf
|
||||
}
|
||||
|
||||
// publicKeyRSA returns the RSA public key from a DNSKEY record.
|
||||
func (k *DNSKEY) publicKeyRSA() *rsa.PublicKey {
|
||||
keybuf, err := fromBase64([]byte(k.PublicKey))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RFC 2537/3110, section 2. RSA Public KEY Resource Records
|
||||
// Length is in the 0th byte, unless its zero, then it
|
||||
// it in bytes 1 and 2 and its a 16 bit number
|
||||
explen := uint16(keybuf[0])
|
||||
keyoff := 1
|
||||
if explen == 0 {
|
||||
explen = uint16(keybuf[1])<<8 | uint16(keybuf[2])
|
||||
keyoff = 3
|
||||
}
|
||||
if explen > 4 {
|
||||
// Larger exponent than supported by the crypto package.
|
||||
return nil
|
||||
}
|
||||
pubkey := new(rsa.PublicKey)
|
||||
|
||||
pubkey.N = big.NewInt(0)
|
||||
expo := uint64(0)
|
||||
for i := 0; i < int(explen); i++ {
|
||||
expo <<= 8
|
||||
expo |= uint64(keybuf[keyoff+i])
|
||||
}
|
||||
if expo > 1<<31-1 {
|
||||
// Larger exponent than supported by the crypto package.
|
||||
return nil
|
||||
}
|
||||
pubkey.E = int(expo)
|
||||
|
||||
pubkey.N.SetBytes(keybuf[keyoff+int(explen):])
|
||||
return pubkey
|
||||
}
|
||||
|
||||
// publicKeyECDSA returns the Curve public key from the DNSKEY record.
|
||||
func (k *DNSKEY) publicKeyECDSA() *ecdsa.PublicKey {
|
||||
keybuf, err := fromBase64([]byte(k.PublicKey))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
pubkey := new(ecdsa.PublicKey)
|
||||
switch k.Algorithm {
|
||||
case ECDSAP256SHA256:
|
||||
pubkey.Curve = elliptic.P256()
|
||||
if len(keybuf) != 64 {
|
||||
// wrongly encoded key
|
||||
return nil
|
||||
}
|
||||
case ECDSAP384SHA384:
|
||||
pubkey.Curve = elliptic.P384()
|
||||
if len(keybuf) != 96 {
|
||||
// Wrongly encoded key
|
||||
return nil
|
||||
}
|
||||
}
|
||||
pubkey.X = big.NewInt(0)
|
||||
pubkey.X.SetBytes(keybuf[:len(keybuf)/2])
|
||||
pubkey.Y = big.NewInt(0)
|
||||
pubkey.Y.SetBytes(keybuf[len(keybuf)/2:])
|
||||
return pubkey
|
||||
}
|
||||
|
||||
func (k *DNSKEY) publicKeyDSA() *dsa.PublicKey {
|
||||
keybuf, err := fromBase64([]byte(k.PublicKey))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if len(keybuf) < 22 {
|
||||
return nil
|
||||
}
|
||||
t, keybuf := int(keybuf[0]), keybuf[1:]
|
||||
size := 64 + t*8
|
||||
q, keybuf := keybuf[:20], keybuf[20:]
|
||||
if len(keybuf) != 3*size {
|
||||
return nil
|
||||
}
|
||||
p, keybuf := keybuf[:size], keybuf[size:]
|
||||
g, y := keybuf[:size], keybuf[size:]
|
||||
pubkey := new(dsa.PublicKey)
|
||||
pubkey.Parameters.Q = big.NewInt(0).SetBytes(q)
|
||||
pubkey.Parameters.P = big.NewInt(0).SetBytes(p)
|
||||
pubkey.Parameters.G = big.NewInt(0).SetBytes(g)
|
||||
pubkey.Y = big.NewInt(0).SetBytes(y)
|
||||
return pubkey
|
||||
}
|
||||
|
||||
func (k *DNSKEY) publicKeyED25519() ed25519.PublicKey {
|
||||
keybuf, err := fromBase64([]byte(k.PublicKey))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if len(keybuf) != ed25519.PublicKeySize {
|
||||
return nil
|
||||
}
|
||||
return keybuf
|
||||
}
|
||||
|
||||
type wireSlice [][]byte
|
||||
|
||||
func (p wireSlice) Len() int { return len(p) }
|
||||
func (p wireSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
func (p wireSlice) Less(i, j int) bool {
|
||||
_, ioff, _ := UnpackDomainName(p[i], 0)
|
||||
_, joff, _ := UnpackDomainName(p[j], 0)
|
||||
return bytes.Compare(p[i][ioff+10:], p[j][joff+10:]) < 0
|
||||
}
|
||||
|
||||
// Return the raw signature data.
|
||||
func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) {
|
||||
wires := make(wireSlice, len(rrset))
|
||||
for i, r := range rrset {
|
||||
r1 := r.copy()
|
||||
r1.Header().Ttl = s.OrigTtl
|
||||
labels := SplitDomainName(r1.Header().Name)
|
||||
// 6.2. Canonical RR Form. (4) - wildcards
|
||||
if len(labels) > int(s.Labels) {
|
||||
// Wildcard
|
||||
r1.Header().Name = "*." + strings.Join(labels[len(labels)-int(s.Labels):], ".") + "."
|
||||
}
|
||||
// RFC 4034: 6.2. Canonical RR Form. (2) - domain name to lowercase
|
||||
r1.Header().Name = strings.ToLower(r1.Header().Name)
|
||||
// 6.2. Canonical RR Form. (3) - domain rdata to lowercase.
|
||||
// NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR,
|
||||
// HINFO, MINFO, MX, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX,
|
||||
// SRV, DNAME, A6
|
||||
//
|
||||
// RFC 6840 - Clarifications and Implementation Notes for DNS Security (DNSSEC):
|
||||
// Section 6.2 of [RFC4034] also erroneously lists HINFO as a record
|
||||
// that needs conversion to lowercase, and twice at that. Since HINFO
|
||||
// records contain no domain names, they are not subject to case
|
||||
// conversion.
|
||||
switch x := r1.(type) {
|
||||
case *NS:
|
||||
x.Ns = strings.ToLower(x.Ns)
|
||||
case *MD:
|
||||
x.Md = strings.ToLower(x.Md)
|
||||
case *MF:
|
||||
x.Mf = strings.ToLower(x.Mf)
|
||||
case *CNAME:
|
||||
x.Target = strings.ToLower(x.Target)
|
||||
case *SOA:
|
||||
x.Ns = strings.ToLower(x.Ns)
|
||||
x.Mbox = strings.ToLower(x.Mbox)
|
||||
case *MB:
|
||||
x.Mb = strings.ToLower(x.Mb)
|
||||
case *MG:
|
||||
x.Mg = strings.ToLower(x.Mg)
|
||||
case *MR:
|
||||
x.Mr = strings.ToLower(x.Mr)
|
||||
case *PTR:
|
||||
x.Ptr = strings.ToLower(x.Ptr)
|
||||
case *MINFO:
|
||||
x.Rmail = strings.ToLower(x.Rmail)
|
||||
x.Email = strings.ToLower(x.Email)
|
||||
case *MX:
|
||||
x.Mx = strings.ToLower(x.Mx)
|
||||
case *RP:
|
||||
x.Mbox = strings.ToLower(x.Mbox)
|
||||
x.Txt = strings.ToLower(x.Txt)
|
||||
case *AFSDB:
|
||||
x.Hostname = strings.ToLower(x.Hostname)
|
||||
case *RT:
|
||||
x.Host = strings.ToLower(x.Host)
|
||||
case *SIG:
|
||||
x.SignerName = strings.ToLower(x.SignerName)
|
||||
case *PX:
|
||||
x.Map822 = strings.ToLower(x.Map822)
|
||||
x.Mapx400 = strings.ToLower(x.Mapx400)
|
||||
case *NAPTR:
|
||||
x.Replacement = strings.ToLower(x.Replacement)
|
||||
case *KX:
|
||||
x.Exchanger = strings.ToLower(x.Exchanger)
|
||||
case *SRV:
|
||||
x.Target = strings.ToLower(x.Target)
|
||||
case *DNAME:
|
||||
x.Target = strings.ToLower(x.Target)
|
||||
}
|
||||
// 6.2. Canonical RR Form. (5) - origTTL
|
||||
wire := make([]byte, r1.len()+1) // +1 to be safe(r)
|
||||
off, err1 := PackRR(r1, wire, 0, nil, false)
|
||||
if err1 != nil {
|
||||
return nil, err1
|
||||
}
|
||||
wire = wire[:off]
|
||||
wires[i] = wire
|
||||
}
|
||||
sort.Sort(wires)
|
||||
for i, wire := range wires {
|
||||
if i > 0 && bytes.Equal(wire, wires[i-1]) {
|
||||
continue
|
||||
}
|
||||
buf = append(buf, wire...)
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func packSigWire(sw *rrsigWireFmt, msg []byte) (int, error) {
|
||||
// copied from zmsg.go RRSIG packing
|
||||
off, err := packUint16(sw.TypeCovered, msg, 0)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
off, err = packUint8(sw.Algorithm, msg, off)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
off, err = packUint8(sw.Labels, msg, off)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
off, err = packUint32(sw.OrigTtl, msg, off)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
off, err = packUint32(sw.Expiration, msg, off)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
off, err = packUint32(sw.Inception, msg, off)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
off, err = packUint16(sw.KeyTag, msg, off)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
off, err = PackDomainName(sw.SignerName, msg, off, nil, false)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
return off, nil
|
||||
}
|
||||
|
||||
func packKeyWire(dw *dnskeyWireFmt, msg []byte) (int, error) {
|
||||
// copied from zmsg.go DNSKEY packing
|
||||
off, err := packUint16(dw.Flags, msg, 0)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
off, err = packUint8(dw.Protocol, msg, off)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
off, err = packUint8(dw.Algorithm, msg, off)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
off, err = packStringBase64(dw.PublicKey, msg, off)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
return off, nil
|
||||
}
|
||||
178
vendor/github.com/miekg/dns/dnssec_keygen.go
generated
vendored
Normal file
178
vendor/github.com/miekg/dns/dnssec_keygen.go
generated
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"math/big"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
)
|
||||
|
||||
// Generate generates a DNSKEY of the given bit size.
|
||||
// The public part is put inside the DNSKEY record.
|
||||
// The Algorithm in the key must be set as this will define
|
||||
// what kind of DNSKEY will be generated.
|
||||
// The ECDSA algorithms imply a fixed keysize, in that case
|
||||
// bits should be set to the size of the algorithm.
|
||||
func (k *DNSKEY) Generate(bits int) (crypto.PrivateKey, error) {
|
||||
switch k.Algorithm {
|
||||
case DSA, DSANSEC3SHA1:
|
||||
if bits != 1024 {
|
||||
return nil, ErrKeySize
|
||||
}
|
||||
case RSAMD5, RSASHA1, RSASHA256, RSASHA1NSEC3SHA1:
|
||||
if bits < 512 || bits > 4096 {
|
||||
return nil, ErrKeySize
|
||||
}
|
||||
case RSASHA512:
|
||||
if bits < 1024 || bits > 4096 {
|
||||
return nil, ErrKeySize
|
||||
}
|
||||
case ECDSAP256SHA256:
|
||||
if bits != 256 {
|
||||
return nil, ErrKeySize
|
||||
}
|
||||
case ECDSAP384SHA384:
|
||||
if bits != 384 {
|
||||
return nil, ErrKeySize
|
||||
}
|
||||
case ED25519:
|
||||
if bits != 256 {
|
||||
return nil, ErrKeySize
|
||||
}
|
||||
}
|
||||
|
||||
switch k.Algorithm {
|
||||
case DSA, DSANSEC3SHA1:
|
||||
params := new(dsa.Parameters)
|
||||
if err := dsa.GenerateParameters(params, rand.Reader, dsa.L1024N160); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
priv := new(dsa.PrivateKey)
|
||||
priv.PublicKey.Parameters = *params
|
||||
err := dsa.GenerateKey(priv, rand.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
k.setPublicKeyDSA(params.Q, params.P, params.G, priv.PublicKey.Y)
|
||||
return priv, nil
|
||||
case RSAMD5, RSASHA1, RSASHA256, RSASHA512, RSASHA1NSEC3SHA1:
|
||||
priv, err := rsa.GenerateKey(rand.Reader, bits)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
k.setPublicKeyRSA(priv.PublicKey.E, priv.PublicKey.N)
|
||||
return priv, nil
|
||||
case ECDSAP256SHA256, ECDSAP384SHA384:
|
||||
var c elliptic.Curve
|
||||
switch k.Algorithm {
|
||||
case ECDSAP256SHA256:
|
||||
c = elliptic.P256()
|
||||
case ECDSAP384SHA384:
|
||||
c = elliptic.P384()
|
||||
}
|
||||
priv, err := ecdsa.GenerateKey(c, rand.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
k.setPublicKeyECDSA(priv.PublicKey.X, priv.PublicKey.Y)
|
||||
return priv, nil
|
||||
case ED25519:
|
||||
pub, priv, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
k.setPublicKeyED25519(pub)
|
||||
return priv, nil
|
||||
default:
|
||||
return nil, ErrAlg
|
||||
}
|
||||
}
|
||||
|
||||
// Set the public key (the value E and N)
|
||||
func (k *DNSKEY) setPublicKeyRSA(_E int, _N *big.Int) bool {
|
||||
if _E == 0 || _N == nil {
|
||||
return false
|
||||
}
|
||||
buf := exponentToBuf(_E)
|
||||
buf = append(buf, _N.Bytes()...)
|
||||
k.PublicKey = toBase64(buf)
|
||||
return true
|
||||
}
|
||||
|
||||
// Set the public key for Elliptic Curves
|
||||
func (k *DNSKEY) setPublicKeyECDSA(_X, _Y *big.Int) bool {
|
||||
if _X == nil || _Y == nil {
|
||||
return false
|
||||
}
|
||||
var intlen int
|
||||
switch k.Algorithm {
|
||||
case ECDSAP256SHA256:
|
||||
intlen = 32
|
||||
case ECDSAP384SHA384:
|
||||
intlen = 48
|
||||
}
|
||||
k.PublicKey = toBase64(curveToBuf(_X, _Y, intlen))
|
||||
return true
|
||||
}
|
||||
|
||||
// Set the public key for DSA
|
||||
func (k *DNSKEY) setPublicKeyDSA(_Q, _P, _G, _Y *big.Int) bool {
|
||||
if _Q == nil || _P == nil || _G == nil || _Y == nil {
|
||||
return false
|
||||
}
|
||||
buf := dsaToBuf(_Q, _P, _G, _Y)
|
||||
k.PublicKey = toBase64(buf)
|
||||
return true
|
||||
}
|
||||
|
||||
// Set the public key for Ed25519
|
||||
func (k *DNSKEY) setPublicKeyED25519(_K ed25519.PublicKey) bool {
|
||||
if _K == nil {
|
||||
return false
|
||||
}
|
||||
k.PublicKey = toBase64(_K)
|
||||
return true
|
||||
}
|
||||
|
||||
// Set the public key (the values E and N) for RSA
|
||||
// RFC 3110: Section 2. RSA Public KEY Resource Records
|
||||
func exponentToBuf(_E int) []byte {
|
||||
var buf []byte
|
||||
i := big.NewInt(int64(_E)).Bytes()
|
||||
if len(i) < 256 {
|
||||
buf = make([]byte, 1, 1+len(i))
|
||||
buf[0] = uint8(len(i))
|
||||
} else {
|
||||
buf = make([]byte, 3, 3+len(i))
|
||||
buf[0] = 0
|
||||
buf[1] = uint8(len(i) >> 8)
|
||||
buf[2] = uint8(len(i))
|
||||
}
|
||||
buf = append(buf, i...)
|
||||
return buf
|
||||
}
|
||||
|
||||
// Set the public key for X and Y for Curve. The two
|
||||
// values are just concatenated.
|
||||
func curveToBuf(_X, _Y *big.Int, intlen int) []byte {
|
||||
buf := intToBytes(_X, intlen)
|
||||
buf = append(buf, intToBytes(_Y, intlen)...)
|
||||
return buf
|
||||
}
|
||||
|
||||
// Set the public key for X and Y for Curve. The two
|
||||
// values are just concatenated.
|
||||
func dsaToBuf(_Q, _P, _G, _Y *big.Int) []byte {
|
||||
t := divRoundUp(divRoundUp(_G.BitLen(), 8)-64, 8)
|
||||
buf := []byte{byte(t)}
|
||||
buf = append(buf, intToBytes(_Q, 20)...)
|
||||
buf = append(buf, intToBytes(_P, 64+t*8)...)
|
||||
buf = append(buf, intToBytes(_G, 64+t*8)...)
|
||||
buf = append(buf, intToBytes(_Y, 64+t*8)...)
|
||||
return buf
|
||||
}
|
||||
297
vendor/github.com/miekg/dns/dnssec_keyscan.go
generated
vendored
Normal file
297
vendor/github.com/miekg/dns/dnssec_keyscan.go
generated
vendored
Normal file
@@ -0,0 +1,297 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"io"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
)
|
||||
|
||||
// NewPrivateKey returns a PrivateKey by parsing the string s.
|
||||
// s should be in the same form of the BIND private key files.
|
||||
func (k *DNSKEY) NewPrivateKey(s string) (crypto.PrivateKey, error) {
|
||||
if s == "" || s[len(s)-1] != '\n' { // We need a closing newline
|
||||
return k.ReadPrivateKey(strings.NewReader(s+"\n"), "")
|
||||
}
|
||||
return k.ReadPrivateKey(strings.NewReader(s), "")
|
||||
}
|
||||
|
||||
// ReadPrivateKey reads a private key from the io.Reader q. The string file is
|
||||
// only used in error reporting.
|
||||
// The public key must be known, because some cryptographic algorithms embed
|
||||
// the public inside the privatekey.
|
||||
func (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (crypto.PrivateKey, error) {
|
||||
m, err := parseKey(q, file)
|
||||
if m == nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, ok := m["private-key-format"]; !ok {
|
||||
return nil, ErrPrivKey
|
||||
}
|
||||
if m["private-key-format"] != "v1.2" && m["private-key-format"] != "v1.3" {
|
||||
return nil, ErrPrivKey
|
||||
}
|
||||
// TODO(mg): check if the pubkey matches the private key
|
||||
algo, err := strconv.ParseUint(strings.SplitN(m["algorithm"], " ", 2)[0], 10, 8)
|
||||
if err != nil {
|
||||
return nil, ErrPrivKey
|
||||
}
|
||||
switch uint8(algo) {
|
||||
case DSA:
|
||||
priv, err := readPrivateKeyDSA(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pub := k.publicKeyDSA()
|
||||
if pub == nil {
|
||||
return nil, ErrKey
|
||||
}
|
||||
priv.PublicKey = *pub
|
||||
return priv, nil
|
||||
case RSAMD5:
|
||||
fallthrough
|
||||
case RSASHA1:
|
||||
fallthrough
|
||||
case RSASHA1NSEC3SHA1:
|
||||
fallthrough
|
||||
case RSASHA256:
|
||||
fallthrough
|
||||
case RSASHA512:
|
||||
priv, err := readPrivateKeyRSA(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pub := k.publicKeyRSA()
|
||||
if pub == nil {
|
||||
return nil, ErrKey
|
||||
}
|
||||
priv.PublicKey = *pub
|
||||
return priv, nil
|
||||
case ECCGOST:
|
||||
return nil, ErrPrivKey
|
||||
case ECDSAP256SHA256:
|
||||
fallthrough
|
||||
case ECDSAP384SHA384:
|
||||
priv, err := readPrivateKeyECDSA(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pub := k.publicKeyECDSA()
|
||||
if pub == nil {
|
||||
return nil, ErrKey
|
||||
}
|
||||
priv.PublicKey = *pub
|
||||
return priv, nil
|
||||
case ED25519:
|
||||
return readPrivateKeyED25519(m)
|
||||
default:
|
||||
return nil, ErrPrivKey
|
||||
}
|
||||
}
|
||||
|
||||
// Read a private key (file) string and create a public key. Return the private key.
|
||||
func readPrivateKeyRSA(m map[string]string) (*rsa.PrivateKey, error) {
|
||||
p := new(rsa.PrivateKey)
|
||||
p.Primes = []*big.Int{nil, nil}
|
||||
for k, v := range m {
|
||||
switch k {
|
||||
case "modulus", "publicexponent", "privateexponent", "prime1", "prime2":
|
||||
v1, err := fromBase64([]byte(v))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch k {
|
||||
case "modulus":
|
||||
p.PublicKey.N = big.NewInt(0)
|
||||
p.PublicKey.N.SetBytes(v1)
|
||||
case "publicexponent":
|
||||
i := big.NewInt(0)
|
||||
i.SetBytes(v1)
|
||||
p.PublicKey.E = int(i.Int64()) // int64 should be large enough
|
||||
case "privateexponent":
|
||||
p.D = big.NewInt(0)
|
||||
p.D.SetBytes(v1)
|
||||
case "prime1":
|
||||
p.Primes[0] = big.NewInt(0)
|
||||
p.Primes[0].SetBytes(v1)
|
||||
case "prime2":
|
||||
p.Primes[1] = big.NewInt(0)
|
||||
p.Primes[1].SetBytes(v1)
|
||||
}
|
||||
case "exponent1", "exponent2", "coefficient":
|
||||
// not used in Go (yet)
|
||||
case "created", "publish", "activate":
|
||||
// not used in Go (yet)
|
||||
}
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func readPrivateKeyDSA(m map[string]string) (*dsa.PrivateKey, error) {
|
||||
p := new(dsa.PrivateKey)
|
||||
p.X = big.NewInt(0)
|
||||
for k, v := range m {
|
||||
switch k {
|
||||
case "private_value(x)":
|
||||
v1, err := fromBase64([]byte(v))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.X.SetBytes(v1)
|
||||
case "created", "publish", "activate":
|
||||
/* not used in Go (yet) */
|
||||
}
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func readPrivateKeyECDSA(m map[string]string) (*ecdsa.PrivateKey, error) {
|
||||
p := new(ecdsa.PrivateKey)
|
||||
p.D = big.NewInt(0)
|
||||
// TODO: validate that the required flags are present
|
||||
for k, v := range m {
|
||||
switch k {
|
||||
case "privatekey":
|
||||
v1, err := fromBase64([]byte(v))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.D.SetBytes(v1)
|
||||
case "created", "publish", "activate":
|
||||
/* not used in Go (yet) */
|
||||
}
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func readPrivateKeyED25519(m map[string]string) (ed25519.PrivateKey, error) {
|
||||
var p ed25519.PrivateKey
|
||||
// TODO: validate that the required flags are present
|
||||
for k, v := range m {
|
||||
switch k {
|
||||
case "privatekey":
|
||||
p1, err := fromBase64([]byte(v))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(p1) != 32 {
|
||||
return nil, ErrPrivKey
|
||||
}
|
||||
// RFC 8080 and Golang's x/crypto/ed25519 differ as to how the
|
||||
// private keys are represented. RFC 8080 specifies that private
|
||||
// keys be stored solely as the seed value (p1 above) while the
|
||||
// ed25519 package represents them as the seed value concatenated
|
||||
// to the public key, which is derived from the seed value.
|
||||
//
|
||||
// ed25519.GenerateKey reads exactly 32 bytes from the passed in
|
||||
// io.Reader and uses them as the seed. It also derives the
|
||||
// public key and produces a compatible private key.
|
||||
_, p, err = ed25519.GenerateKey(bytes.NewReader(p1))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "created", "publish", "activate":
|
||||
/* not used in Go (yet) */
|
||||
}
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// parseKey reads a private key from r. It returns a map[string]string,
|
||||
// with the key-value pairs, or an error when the file is not correct.
|
||||
func parseKey(r io.Reader, file string) (map[string]string, error) {
|
||||
s, cancel := scanInit(r)
|
||||
m := make(map[string]string)
|
||||
c := make(chan lex)
|
||||
k := ""
|
||||
defer func() {
|
||||
cancel()
|
||||
// zlexer can send up to two tokens, the next one and possibly 1 remainders.
|
||||
// Do a non-blocking read.
|
||||
_, ok := <-c
|
||||
_, ok = <-c
|
||||
if !ok {
|
||||
// too bad
|
||||
}
|
||||
}()
|
||||
// Start the lexer
|
||||
go klexer(s, c)
|
||||
for l := range c {
|
||||
// It should alternate
|
||||
switch l.value {
|
||||
case zKey:
|
||||
k = l.token
|
||||
case zValue:
|
||||
if k == "" {
|
||||
return nil, &ParseError{file, "no private key seen", l}
|
||||
}
|
||||
//println("Setting", strings.ToLower(k), "to", l.token, "b")
|
||||
m[strings.ToLower(k)] = l.token
|
||||
k = ""
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// klexer scans the sourcefile and returns tokens on the channel c.
|
||||
func klexer(s *scan, c chan lex) {
|
||||
var l lex
|
||||
str := "" // Hold the current read text
|
||||
commt := false
|
||||
key := true
|
||||
x, err := s.tokenText()
|
||||
defer close(c)
|
||||
for err == nil {
|
||||
l.column = s.position.Column
|
||||
l.line = s.position.Line
|
||||
switch x {
|
||||
case ':':
|
||||
if commt {
|
||||
break
|
||||
}
|
||||
l.token = str
|
||||
if key {
|
||||
l.value = zKey
|
||||
c <- l
|
||||
// Next token is a space, eat it
|
||||
s.tokenText()
|
||||
key = false
|
||||
str = ""
|
||||
} else {
|
||||
l.value = zValue
|
||||
}
|
||||
case ';':
|
||||
commt = true
|
||||
case '\n':
|
||||
if commt {
|
||||
// Reset a comment
|
||||
commt = false
|
||||
}
|
||||
l.value = zValue
|
||||
l.token = str
|
||||
c <- l
|
||||
str = ""
|
||||
commt = false
|
||||
key = true
|
||||
default:
|
||||
if commt {
|
||||
break
|
||||
}
|
||||
str += string(x)
|
||||
}
|
||||
x, err = s.tokenText()
|
||||
}
|
||||
if len(str) > 0 {
|
||||
// Send remainder
|
||||
l.token = str
|
||||
l.value = zValue
|
||||
c <- l
|
||||
}
|
||||
}
|
||||
93
vendor/github.com/miekg/dns/dnssec_privkey.go
generated
vendored
Normal file
93
vendor/github.com/miekg/dns/dnssec_privkey.go
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
)
|
||||
|
||||
const format = "Private-key-format: v1.3\n"
|
||||
|
||||
// PrivateKeyString converts a PrivateKey to a string. This string has the same
|
||||
// format as the private-key-file of BIND9 (Private-key-format: v1.3).
|
||||
// It needs some info from the key (the algorithm), so its a method of the DNSKEY
|
||||
// It supports rsa.PrivateKey, ecdsa.PrivateKey and dsa.PrivateKey
|
||||
func (r *DNSKEY) PrivateKeyString(p crypto.PrivateKey) string {
|
||||
algorithm := strconv.Itoa(int(r.Algorithm))
|
||||
algorithm += " (" + AlgorithmToString[r.Algorithm] + ")"
|
||||
|
||||
switch p := p.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
modulus := toBase64(p.PublicKey.N.Bytes())
|
||||
e := big.NewInt(int64(p.PublicKey.E))
|
||||
publicExponent := toBase64(e.Bytes())
|
||||
privateExponent := toBase64(p.D.Bytes())
|
||||
prime1 := toBase64(p.Primes[0].Bytes())
|
||||
prime2 := toBase64(p.Primes[1].Bytes())
|
||||
// Calculate Exponent1/2 and Coefficient as per: http://en.wikipedia.org/wiki/RSA#Using_the_Chinese_remainder_algorithm
|
||||
// and from: http://code.google.com/p/go/issues/detail?id=987
|
||||
one := big.NewInt(1)
|
||||
p1 := big.NewInt(0).Sub(p.Primes[0], one)
|
||||
q1 := big.NewInt(0).Sub(p.Primes[1], one)
|
||||
exp1 := big.NewInt(0).Mod(p.D, p1)
|
||||
exp2 := big.NewInt(0).Mod(p.D, q1)
|
||||
coeff := big.NewInt(0).ModInverse(p.Primes[1], p.Primes[0])
|
||||
|
||||
exponent1 := toBase64(exp1.Bytes())
|
||||
exponent2 := toBase64(exp2.Bytes())
|
||||
coefficient := toBase64(coeff.Bytes())
|
||||
|
||||
return format +
|
||||
"Algorithm: " + algorithm + "\n" +
|
||||
"Modulus: " + modulus + "\n" +
|
||||
"PublicExponent: " + publicExponent + "\n" +
|
||||
"PrivateExponent: " + privateExponent + "\n" +
|
||||
"Prime1: " + prime1 + "\n" +
|
||||
"Prime2: " + prime2 + "\n" +
|
||||
"Exponent1: " + exponent1 + "\n" +
|
||||
"Exponent2: " + exponent2 + "\n" +
|
||||
"Coefficient: " + coefficient + "\n"
|
||||
|
||||
case *ecdsa.PrivateKey:
|
||||
var intlen int
|
||||
switch r.Algorithm {
|
||||
case ECDSAP256SHA256:
|
||||
intlen = 32
|
||||
case ECDSAP384SHA384:
|
||||
intlen = 48
|
||||
}
|
||||
private := toBase64(intToBytes(p.D, intlen))
|
||||
return format +
|
||||
"Algorithm: " + algorithm + "\n" +
|
||||
"PrivateKey: " + private + "\n"
|
||||
|
||||
case *dsa.PrivateKey:
|
||||
T := divRoundUp(divRoundUp(p.PublicKey.Parameters.G.BitLen(), 8)-64, 8)
|
||||
prime := toBase64(intToBytes(p.PublicKey.Parameters.P, 64+T*8))
|
||||
subprime := toBase64(intToBytes(p.PublicKey.Parameters.Q, 20))
|
||||
base := toBase64(intToBytes(p.PublicKey.Parameters.G, 64+T*8))
|
||||
priv := toBase64(intToBytes(p.X, 20))
|
||||
pub := toBase64(intToBytes(p.PublicKey.Y, 64+T*8))
|
||||
return format +
|
||||
"Algorithm: " + algorithm + "\n" +
|
||||
"Prime(p): " + prime + "\n" +
|
||||
"Subprime(q): " + subprime + "\n" +
|
||||
"Base(g): " + base + "\n" +
|
||||
"Private_value(x): " + priv + "\n" +
|
||||
"Public_value(y): " + pub + "\n"
|
||||
|
||||
case ed25519.PrivateKey:
|
||||
private := toBase64(p[:32])
|
||||
return format +
|
||||
"Algorithm: " + algorithm + "\n" +
|
||||
"PrivateKey: " + private + "\n"
|
||||
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
860
vendor/github.com/miekg/dns/dnssec_test.go
generated
vendored
Normal file
860
vendor/github.com/miekg/dns/dnssec_test.go
generated
vendored
Normal file
@@ -0,0 +1,860 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
)
|
||||
|
||||
func getKey() *DNSKEY {
|
||||
key := new(DNSKEY)
|
||||
key.Hdr.Name = "miek.nl."
|
||||
key.Hdr.Class = ClassINET
|
||||
key.Hdr.Ttl = 14400
|
||||
key.Flags = 256
|
||||
key.Protocol = 3
|
||||
key.Algorithm = RSASHA256
|
||||
key.PublicKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz"
|
||||
return key
|
||||
}
|
||||
|
||||
func getSoa() *SOA {
|
||||
soa := new(SOA)
|
||||
soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0}
|
||||
soa.Ns = "open.nlnetlabs.nl."
|
||||
soa.Mbox = "miekg.atoom.net."
|
||||
soa.Serial = 1293945905
|
||||
soa.Refresh = 14400
|
||||
soa.Retry = 3600
|
||||
soa.Expire = 604800
|
||||
soa.Minttl = 86400
|
||||
return soa
|
||||
}
|
||||
|
||||
func TestSecure(t *testing.T) {
|
||||
soa := getSoa()
|
||||
|
||||
sig := new(RRSIG)
|
||||
sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
|
||||
sig.TypeCovered = TypeSOA
|
||||
sig.Algorithm = RSASHA256
|
||||
sig.Labels = 2
|
||||
sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
|
||||
sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
|
||||
sig.OrigTtl = 14400
|
||||
sig.KeyTag = 12051
|
||||
sig.SignerName = "miek.nl."
|
||||
sig.Signature = "oMCbslaAVIp/8kVtLSms3tDABpcPRUgHLrOR48OOplkYo+8TeEGWwkSwaz/MRo2fB4FxW0qj/hTlIjUGuACSd+b1wKdH5GvzRJc2pFmxtCbm55ygAh4EUL0F6U5cKtGJGSXxxg6UFCQ0doJCmiGFa78LolaUOXImJrk6AFrGa0M="
|
||||
|
||||
key := new(DNSKEY)
|
||||
key.Hdr.Name = "miek.nl."
|
||||
key.Hdr.Class = ClassINET
|
||||
key.Hdr.Ttl = 14400
|
||||
key.Flags = 256
|
||||
key.Protocol = 3
|
||||
key.Algorithm = RSASHA256
|
||||
key.PublicKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz"
|
||||
|
||||
// It should validate. Period is checked separately, so this will keep on working
|
||||
if sig.Verify(key, []RR{soa}) != nil {
|
||||
t.Error("failure to validate")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignature(t *testing.T) {
|
||||
sig := new(RRSIG)
|
||||
sig.Hdr.Name = "miek.nl."
|
||||
sig.Hdr.Class = ClassINET
|
||||
sig.Hdr.Ttl = 3600
|
||||
sig.TypeCovered = TypeDNSKEY
|
||||
sig.Algorithm = RSASHA1
|
||||
sig.Labels = 2
|
||||
sig.OrigTtl = 4000
|
||||
sig.Expiration = 1000 //Thu Jan 1 02:06:40 CET 1970
|
||||
sig.Inception = 800 //Thu Jan 1 01:13:20 CET 1970
|
||||
sig.KeyTag = 34641
|
||||
sig.SignerName = "miek.nl."
|
||||
sig.Signature = "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ"
|
||||
|
||||
// Should not be valid
|
||||
if sig.ValidityPeriod(time.Now()) {
|
||||
t.Error("should not be valid")
|
||||
}
|
||||
|
||||
sig.Inception = 315565800 //Tue Jan 1 10:10:00 CET 1980
|
||||
sig.Expiration = 4102477800 //Fri Jan 1 10:10:00 CET 2100
|
||||
if !sig.ValidityPeriod(time.Now()) {
|
||||
t.Error("should be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignVerify(t *testing.T) {
|
||||
// The record we want to sign
|
||||
soa := new(SOA)
|
||||
soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0}
|
||||
soa.Ns = "open.nlnetlabs.nl."
|
||||
soa.Mbox = "miekg.atoom.net."
|
||||
soa.Serial = 1293945905
|
||||
soa.Refresh = 14400
|
||||
soa.Retry = 3600
|
||||
soa.Expire = 604800
|
||||
soa.Minttl = 86400
|
||||
|
||||
soa1 := new(SOA)
|
||||
soa1.Hdr = RR_Header{"*.miek.nl.", TypeSOA, ClassINET, 14400, 0}
|
||||
soa1.Ns = "open.nlnetlabs.nl."
|
||||
soa1.Mbox = "miekg.atoom.net."
|
||||
soa1.Serial = 1293945905
|
||||
soa1.Refresh = 14400
|
||||
soa1.Retry = 3600
|
||||
soa1.Expire = 604800
|
||||
soa1.Minttl = 86400
|
||||
|
||||
srv := new(SRV)
|
||||
srv.Hdr = RR_Header{"srv.miek.nl.", TypeSRV, ClassINET, 14400, 0}
|
||||
srv.Port = 1000
|
||||
srv.Weight = 800
|
||||
srv.Target = "web1.miek.nl."
|
||||
|
||||
hinfo := &HINFO{
|
||||
Hdr: RR_Header{
|
||||
Name: "miek.nl.",
|
||||
Rrtype: TypeHINFO,
|
||||
Class: ClassINET,
|
||||
Ttl: 3789,
|
||||
},
|
||||
Cpu: "X",
|
||||
Os: "Y",
|
||||
}
|
||||
|
||||
// With this key
|
||||
key := new(DNSKEY)
|
||||
key.Hdr.Rrtype = TypeDNSKEY
|
||||
key.Hdr.Name = "miek.nl."
|
||||
key.Hdr.Class = ClassINET
|
||||
key.Hdr.Ttl = 14400
|
||||
key.Flags = 256
|
||||
key.Protocol = 3
|
||||
key.Algorithm = RSASHA256
|
||||
privkey, _ := key.Generate(512)
|
||||
|
||||
// Fill in the values of the Sig, before signing
|
||||
sig := new(RRSIG)
|
||||
sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
|
||||
sig.TypeCovered = soa.Hdr.Rrtype
|
||||
sig.Labels = uint8(CountLabel(soa.Hdr.Name)) // works for all 3
|
||||
sig.OrigTtl = soa.Hdr.Ttl
|
||||
sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
|
||||
sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
|
||||
sig.KeyTag = key.KeyTag() // Get the keyfrom the Key
|
||||
sig.SignerName = key.Hdr.Name
|
||||
sig.Algorithm = RSASHA256
|
||||
|
||||
for _, r := range []RR{soa, soa1, srv, hinfo} {
|
||||
if err := sig.Sign(privkey.(*rsa.PrivateKey), []RR{r}); err != nil {
|
||||
t.Error("failure to sign the record:", err)
|
||||
continue
|
||||
}
|
||||
if err := sig.Verify(key, []RR{r}); err != nil {
|
||||
t.Errorf("failure to validate: %s", r.Header().Name)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test65534(t *testing.T) {
|
||||
t6 := new(RFC3597)
|
||||
t6.Hdr = RR_Header{"miek.nl.", 65534, ClassINET, 14400, 0}
|
||||
t6.Rdata = "505D870001"
|
||||
key := new(DNSKEY)
|
||||
key.Hdr.Name = "miek.nl."
|
||||
key.Hdr.Rrtype = TypeDNSKEY
|
||||
key.Hdr.Class = ClassINET
|
||||
key.Hdr.Ttl = 14400
|
||||
key.Flags = 256
|
||||
key.Protocol = 3
|
||||
key.Algorithm = RSASHA256
|
||||
privkey, _ := key.Generate(1024)
|
||||
|
||||
sig := new(RRSIG)
|
||||
sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
|
||||
sig.TypeCovered = t6.Hdr.Rrtype
|
||||
sig.Labels = uint8(CountLabel(t6.Hdr.Name))
|
||||
sig.OrigTtl = t6.Hdr.Ttl
|
||||
sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
|
||||
sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
|
||||
sig.KeyTag = key.KeyTag()
|
||||
sig.SignerName = key.Hdr.Name
|
||||
sig.Algorithm = RSASHA256
|
||||
if err := sig.Sign(privkey.(*rsa.PrivateKey), []RR{t6}); err != nil {
|
||||
t.Error(err)
|
||||
t.Error("failure to sign the TYPE65534 record")
|
||||
}
|
||||
if err := sig.Verify(key, []RR{t6}); err != nil {
|
||||
t.Error(err)
|
||||
t.Errorf("failure to validate %s", t6.Header().Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDnskey(t *testing.T) {
|
||||
pubkey, err := ReadRR(strings.NewReader(`
|
||||
miek.nl. IN DNSKEY 256 3 10 AwEAAZuMCu2FdugHkTrXYgl5qixvcDw1aDDlvL46/xJKbHBAHY16fNUb2b65cwko2Js/aJxUYJbZk5dwCDZxYfrfbZVtDPQuc3o8QaChVxC7/JYz2AHc9qHvqQ1j4VrH71RWINlQo6VYjzN/BGpMhOZoZOEwzp1HfsOE3lNYcoWU1smL ;{id = 5240 (zsk), size = 1024b}
|
||||
`), "Kmiek.nl.+010+05240.key")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
privStr := `Private-key-format: v1.3
|
||||
Algorithm: 10 (RSASHA512)
|
||||
Modulus: m4wK7YV26AeROtdiCXmqLG9wPDVoMOW8vjr/EkpscEAdjXp81RvZvrlzCSjYmz9onFRgltmTl3AINnFh+t9tlW0M9C5zejxBoKFXELv8ljPYAdz2oe+pDWPhWsfvVFYg2VCjpViPM38EakyE5mhk4TDOnUd+w4TeU1hyhZTWyYs=
|
||||
PublicExponent: AQAB
|
||||
PrivateExponent: UfCoIQ/Z38l8vB6SSqOI/feGjHEl/fxIPX4euKf0D/32k30fHbSaNFrFOuIFmWMB3LimWVEs6u3dpbB9CQeCVg7hwU5puG7OtuiZJgDAhNeOnxvo5btp4XzPZrJSxR4WNQnwIiYWbl0aFlL1VGgHC/3By89ENZyWaZcMLW4KGWE=
|
||||
Prime1: yxwC6ogAu8aVcDx2wg1V0b5M5P6jP8qkRFVMxWNTw60Vkn+ECvw6YAZZBHZPaMyRYZLzPgUlyYRd0cjupy4+fQ==
|
||||
Prime2: xA1bF8M0RTIQ6+A11AoVG6GIR/aPGg5sogRkIZ7ID/sF6g9HMVU/CM2TqVEBJLRPp73cv6ZeC3bcqOCqZhz+pw==
|
||||
Exponent1: xzkblyZ96bGYxTVZm2/vHMOXswod4KWIyMoOepK6B/ZPcZoIT6omLCgtypWtwHLfqyCz3MK51Nc0G2EGzg8rFQ==
|
||||
Exponent2: Pu5+mCEb7T5F+kFNZhQadHUklt0JUHbi3hsEvVoHpEGSw3BGDQrtIflDde0/rbWHgDPM4WQY+hscd8UuTXrvLw==
|
||||
Coefficient: UuRoNqe7YHnKmQzE6iDWKTMIWTuoqqrFAmXPmKQnC+Y+BQzOVEHUo9bXdDnoI9hzXP1gf8zENMYwYLeWpuYlFQ==
|
||||
`
|
||||
privkey, err := pubkey.(*DNSKEY).ReadPrivateKey(strings.NewReader(privStr),
|
||||
"Kmiek.nl.+010+05240.private")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pubkey.(*DNSKEY).PublicKey != "AwEAAZuMCu2FdugHkTrXYgl5qixvcDw1aDDlvL46/xJKbHBAHY16fNUb2b65cwko2Js/aJxUYJbZk5dwCDZxYfrfbZVtDPQuc3o8QaChVxC7/JYz2AHc9qHvqQ1j4VrH71RWINlQo6VYjzN/BGpMhOZoZOEwzp1HfsOE3lNYcoWU1smL" {
|
||||
t.Error("pubkey is not what we've read")
|
||||
}
|
||||
if pubkey.(*DNSKEY).PrivateKeyString(privkey) != privStr {
|
||||
t.Error("privkey is not what we've read")
|
||||
t.Errorf("%v", pubkey.(*DNSKEY).PrivateKeyString(privkey))
|
||||
}
|
||||
}
|
||||
|
||||
func TestTag(t *testing.T) {
|
||||
key := new(DNSKEY)
|
||||
key.Hdr.Name = "miek.nl."
|
||||
key.Hdr.Rrtype = TypeDNSKEY
|
||||
key.Hdr.Class = ClassINET
|
||||
key.Hdr.Ttl = 3600
|
||||
key.Flags = 256
|
||||
key.Protocol = 3
|
||||
key.Algorithm = RSASHA256
|
||||
key.PublicKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz"
|
||||
|
||||
tag := key.KeyTag()
|
||||
if tag != 12051 {
|
||||
t.Errorf("wrong key tag: %d for key %v", tag, key)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyRSA(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping test in short mode.")
|
||||
}
|
||||
key := new(DNSKEY)
|
||||
key.Hdr.Name = "miek.nl."
|
||||
key.Hdr.Rrtype = TypeDNSKEY
|
||||
key.Hdr.Class = ClassINET
|
||||
key.Hdr.Ttl = 3600
|
||||
key.Flags = 256
|
||||
key.Protocol = 3
|
||||
key.Algorithm = RSASHA256
|
||||
priv, _ := key.Generate(2048)
|
||||
|
||||
soa := new(SOA)
|
||||
soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0}
|
||||
soa.Ns = "open.nlnetlabs.nl."
|
||||
soa.Mbox = "miekg.atoom.net."
|
||||
soa.Serial = 1293945905
|
||||
soa.Refresh = 14400
|
||||
soa.Retry = 3600
|
||||
soa.Expire = 604800
|
||||
soa.Minttl = 86400
|
||||
|
||||
sig := new(RRSIG)
|
||||
sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
|
||||
sig.TypeCovered = TypeSOA
|
||||
sig.Algorithm = RSASHA256
|
||||
sig.Labels = 2
|
||||
sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
|
||||
sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
|
||||
sig.OrigTtl = soa.Hdr.Ttl
|
||||
sig.KeyTag = key.KeyTag()
|
||||
sig.SignerName = key.Hdr.Name
|
||||
|
||||
if err := sig.Sign(priv.(*rsa.PrivateKey), []RR{soa}); err != nil {
|
||||
t.Error("failed to sign")
|
||||
return
|
||||
}
|
||||
if err := sig.Verify(key, []RR{soa}); err != nil {
|
||||
t.Error("failed to verify")
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyToDS(t *testing.T) {
|
||||
key := new(DNSKEY)
|
||||
key.Hdr.Name = "miek.nl."
|
||||
key.Hdr.Rrtype = TypeDNSKEY
|
||||
key.Hdr.Class = ClassINET
|
||||
key.Hdr.Ttl = 3600
|
||||
key.Flags = 256
|
||||
key.Protocol = 3
|
||||
key.Algorithm = RSASHA256
|
||||
key.PublicKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz"
|
||||
|
||||
ds := key.ToDS(SHA1)
|
||||
if strings.ToUpper(ds.Digest) != "B5121BDB5B8D86D0CC5FFAFBAAABE26C3E20BAC1" {
|
||||
t.Errorf("wrong DS digest for SHA1\n%v", ds)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignRSA(t *testing.T) {
|
||||
pub := "miek.nl. IN DNSKEY 256 3 5 AwEAAb+8lGNCxJgLS8rYVer6EnHVuIkQDghdjdtewDzU3G5R7PbMbKVRvH2Ma7pQyYceoaqWZQirSj72euPWfPxQnMy9ucCylA+FuH9cSjIcPf4PqJfdupHk9X6EBYjxrCLY4p1/yBwgyBIRJtZtAqM3ceAH2WovEJD6rTtOuHo5AluJ"
|
||||
|
||||
priv := `Private-key-format: v1.3
|
||||
Algorithm: 5 (RSASHA1)
|
||||
Modulus: v7yUY0LEmAtLythV6voScdW4iRAOCF2N217APNTcblHs9sxspVG8fYxrulDJhx6hqpZlCKtKPvZ649Z8/FCczL25wLKUD4W4f1xKMhw9/g+ol926keT1foQFiPGsItjinX/IHCDIEhEm1m0Cozdx4AfZai8QkPqtO064ejkCW4k=
|
||||
PublicExponent: AQAB
|
||||
PrivateExponent: YPwEmwjk5HuiROKU4xzHQ6l1hG8Iiha4cKRG3P5W2b66/EN/GUh07ZSf0UiYB67o257jUDVEgwCuPJz776zfApcCB4oGV+YDyEu7Hp/rL8KcSN0la0k2r9scKwxTp4BTJT23zyBFXsV/1wRDK1A5NxsHPDMYi2SoK63Enm/1ptk=
|
||||
Prime1: /wjOG+fD0ybNoSRn7nQ79udGeR1b0YhUA5mNjDx/x2fxtIXzygYk0Rhx9QFfDy6LOBvz92gbNQlzCLz3DJt5hw==
|
||||
Prime2: wHZsJ8OGhkp5p3mrJFZXMDc2mbYusDVTA+t+iRPdS797Tj0pjvU2HN4vTnTj8KBQp6hmnY7dLp9Y1qserySGbw==
|
||||
Exponent1: N0A7FsSRIg+IAN8YPQqlawoTtG1t1OkJ+nWrurPootScApX6iMvn8fyvw3p2k51rv84efnzpWAYiC8SUaQDNxQ==
|
||||
Exponent2: SvuYRaGyvo0zemE3oS+WRm2scxR8eiA8WJGeOc+obwOKCcBgeZblXzfdHGcEC1KaOcetOwNW/vwMA46lpLzJNw==
|
||||
Coefficient: 8+7ZN/JgByqv0NfULiFKTjtyegUcijRuyij7yNxYbCBneDvZGxJwKNi4YYXWx743pcAj4Oi4Oh86gcmxLs+hGw==
|
||||
Created: 20110302104537
|
||||
Publish: 20110302104537
|
||||
Activate: 20110302104537`
|
||||
|
||||
xk := testRR(pub)
|
||||
k := xk.(*DNSKEY)
|
||||
p, err := k.NewPrivateKey(priv)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
switch priv := p.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
if 65537 != priv.PublicKey.E {
|
||||
t.Error("exponenent should be 65537")
|
||||
}
|
||||
default:
|
||||
t.Errorf("we should have read an RSA key: %v", priv)
|
||||
}
|
||||
if k.KeyTag() != 37350 {
|
||||
t.Errorf("keytag should be 37350, got %d %v", k.KeyTag(), k)
|
||||
}
|
||||
|
||||
soa := new(SOA)
|
||||
soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0}
|
||||
soa.Ns = "open.nlnetlabs.nl."
|
||||
soa.Mbox = "miekg.atoom.net."
|
||||
soa.Serial = 1293945905
|
||||
soa.Refresh = 14400
|
||||
soa.Retry = 3600
|
||||
soa.Expire = 604800
|
||||
soa.Minttl = 86400
|
||||
|
||||
sig := new(RRSIG)
|
||||
sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
|
||||
sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
|
||||
sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
|
||||
sig.KeyTag = k.KeyTag()
|
||||
sig.SignerName = k.Hdr.Name
|
||||
sig.Algorithm = k.Algorithm
|
||||
|
||||
sig.Sign(p.(*rsa.PrivateKey), []RR{soa})
|
||||
if sig.Signature != "D5zsobpQcmMmYsUMLxCVEtgAdCvTu8V/IEeP4EyLBjqPJmjt96bwM9kqihsccofA5LIJ7DN91qkCORjWSTwNhzCv7bMyr2o5vBZElrlpnRzlvsFIoAZCD9xg6ZY7ZyzUJmU6IcTwG4v3xEYajcpbJJiyaw/RqR90MuRdKPiBzSo=" {
|
||||
t.Errorf("signature is not correct: %v", sig)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignVerifyECDSA(t *testing.T) {
|
||||
pub := `example.net. 3600 IN DNSKEY 257 3 14 (
|
||||
xKYaNhWdGOfJ+nPrL8/arkwf2EY3MDJ+SErKivBVSum1
|
||||
w/egsXvSADtNJhyem5RCOpgQ6K8X1DRSEkrbYQ+OB+v8
|
||||
/uX45NBwY8rp65F6Glur8I/mlVNgF6W/qTI37m40 )`
|
||||
priv := `Private-key-format: v1.2
|
||||
Algorithm: 14 (ECDSAP384SHA384)
|
||||
PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR`
|
||||
|
||||
eckey := testRR(pub)
|
||||
privkey, err := eckey.(*DNSKEY).NewPrivateKey(priv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// TODO: Create separate test for this
|
||||
ds := eckey.(*DNSKEY).ToDS(SHA384)
|
||||
if ds.KeyTag != 10771 {
|
||||
t.Fatal("wrong keytag on DS")
|
||||
}
|
||||
if ds.Digest != "72d7b62976ce06438e9c0bf319013cf801f09ecc84b8d7e9495f27e305c6a9b0563a9b5f4d288405c3008a946df983d6" {
|
||||
t.Fatal("wrong DS Digest")
|
||||
}
|
||||
a := testRR("www.example.net. 3600 IN A 192.0.2.1")
|
||||
sig := new(RRSIG)
|
||||
sig.Hdr = RR_Header{"example.net.", TypeRRSIG, ClassINET, 14400, 0}
|
||||
sig.Expiration, _ = StringToTime("20100909102025")
|
||||
sig.Inception, _ = StringToTime("20100812102025")
|
||||
sig.KeyTag = eckey.(*DNSKEY).KeyTag()
|
||||
sig.SignerName = eckey.(*DNSKEY).Hdr.Name
|
||||
sig.Algorithm = eckey.(*DNSKEY).Algorithm
|
||||
|
||||
if sig.Sign(privkey.(*ecdsa.PrivateKey), []RR{a}) != nil {
|
||||
t.Fatal("failure to sign the record")
|
||||
}
|
||||
|
||||
if err := sig.Verify(eckey.(*DNSKEY), []RR{a}); err != nil {
|
||||
t.Fatalf("failure to validate:\n%s\n%s\n%s\n\n%s\n\n%v",
|
||||
eckey.(*DNSKEY).String(),
|
||||
a.String(),
|
||||
sig.String(),
|
||||
eckey.(*DNSKEY).PrivateKeyString(privkey),
|
||||
err,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignVerifyECDSA2(t *testing.T) {
|
||||
srv1 := testRR("srv.miek.nl. IN SRV 1000 800 0 web1.miek.nl.")
|
||||
srv := srv1.(*SRV)
|
||||
|
||||
// With this key
|
||||
key := new(DNSKEY)
|
||||
key.Hdr.Rrtype = TypeDNSKEY
|
||||
key.Hdr.Name = "miek.nl."
|
||||
key.Hdr.Class = ClassINET
|
||||
key.Hdr.Ttl = 14400
|
||||
key.Flags = 256
|
||||
key.Protocol = 3
|
||||
key.Algorithm = ECDSAP256SHA256
|
||||
privkey, err := key.Generate(256)
|
||||
if err != nil {
|
||||
t.Fatal("failure to generate key")
|
||||
}
|
||||
|
||||
// Fill in the values of the Sig, before signing
|
||||
sig := new(RRSIG)
|
||||
sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
|
||||
sig.TypeCovered = srv.Hdr.Rrtype
|
||||
sig.Labels = uint8(CountLabel(srv.Hdr.Name)) // works for all 3
|
||||
sig.OrigTtl = srv.Hdr.Ttl
|
||||
sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
|
||||
sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
|
||||
sig.KeyTag = key.KeyTag() // Get the keyfrom the Key
|
||||
sig.SignerName = key.Hdr.Name
|
||||
sig.Algorithm = ECDSAP256SHA256
|
||||
|
||||
if sig.Sign(privkey.(*ecdsa.PrivateKey), []RR{srv}) != nil {
|
||||
t.Fatal("failure to sign the record")
|
||||
}
|
||||
|
||||
err = sig.Verify(key, []RR{srv})
|
||||
if err != nil {
|
||||
t.Errorf("failure to validate:\n%s\n%s\n%s\n\n%s\n\n%v",
|
||||
key.String(),
|
||||
srv.String(),
|
||||
sig.String(),
|
||||
key.PrivateKeyString(privkey),
|
||||
err,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignVerifyEd25519(t *testing.T) {
|
||||
srv1, err := NewRR("srv.miek.nl. IN SRV 1000 800 0 web1.miek.nl.")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
srv := srv1.(*SRV)
|
||||
|
||||
// With this key
|
||||
key := new(DNSKEY)
|
||||
key.Hdr.Rrtype = TypeDNSKEY
|
||||
key.Hdr.Name = "miek.nl."
|
||||
key.Hdr.Class = ClassINET
|
||||
key.Hdr.Ttl = 14400
|
||||
key.Flags = 256
|
||||
key.Protocol = 3
|
||||
key.Algorithm = ED25519
|
||||
privkey, err := key.Generate(256)
|
||||
if err != nil {
|
||||
t.Fatal("failure to generate key")
|
||||
}
|
||||
|
||||
// Fill in the values of the Sig, before signing
|
||||
sig := new(RRSIG)
|
||||
sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
|
||||
sig.TypeCovered = srv.Hdr.Rrtype
|
||||
sig.Labels = uint8(CountLabel(srv.Hdr.Name)) // works for all 3
|
||||
sig.OrigTtl = srv.Hdr.Ttl
|
||||
sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
|
||||
sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
|
||||
sig.KeyTag = key.KeyTag() // Get the keyfrom the Key
|
||||
sig.SignerName = key.Hdr.Name
|
||||
sig.Algorithm = ED25519
|
||||
|
||||
if sig.Sign(privkey.(ed25519.PrivateKey), []RR{srv}) != nil {
|
||||
t.Fatal("failure to sign the record")
|
||||
}
|
||||
|
||||
err = sig.Verify(key, []RR{srv})
|
||||
if err != nil {
|
||||
t.Logf("failure to validate:\n%s\n%s\n%s\n\n%s\n\n%v",
|
||||
key.String(),
|
||||
srv.String(),
|
||||
sig.String(),
|
||||
key.PrivateKeyString(privkey),
|
||||
err,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Here the test vectors from the relevant RFCs are checked.
|
||||
// rfc6605 6.1
|
||||
func TestRFC6605P256(t *testing.T) {
|
||||
exDNSKEY := `example.net. 3600 IN DNSKEY 257 3 13 (
|
||||
GojIhhXUN/u4v54ZQqGSnyhWJwaubCvTmeexv7bR6edb
|
||||
krSqQpF64cYbcB7wNcP+e+MAnLr+Wi9xMWyQLc8NAA== )`
|
||||
exPriv := `Private-key-format: v1.2
|
||||
Algorithm: 13 (ECDSAP256SHA256)
|
||||
PrivateKey: GU6SnQ/Ou+xC5RumuIUIuJZteXT2z0O/ok1s38Et6mQ=`
|
||||
rrDNSKEY := testRR(exDNSKEY)
|
||||
priv, err := rrDNSKEY.(*DNSKEY).NewPrivateKey(exPriv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
exDS := `example.net. 3600 IN DS 55648 13 2 (
|
||||
b4c8c1fe2e7477127b27115656ad6256f424625bf5c1
|
||||
e2770ce6d6e37df61d17 )`
|
||||
rrDS := testRR(exDS)
|
||||
ourDS := rrDNSKEY.(*DNSKEY).ToDS(SHA256)
|
||||
if !reflect.DeepEqual(ourDS, rrDS.(*DS)) {
|
||||
t.Errorf("DS record differs:\n%v\n%v", ourDS, rrDS.(*DS))
|
||||
}
|
||||
|
||||
exA := `www.example.net. 3600 IN A 192.0.2.1`
|
||||
exRRSIG := `www.example.net. 3600 IN RRSIG A 13 3 3600 (
|
||||
20100909100439 20100812100439 55648 example.net.
|
||||
qx6wLYqmh+l9oCKTN6qIc+bw6ya+KJ8oMz0YP107epXA
|
||||
yGmt+3SNruPFKG7tZoLBLlUzGGus7ZwmwWep666VCw== )`
|
||||
rrA := testRR(exA)
|
||||
rrRRSIG := testRR(exRRSIG)
|
||||
if err := rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {
|
||||
t.Errorf("failure to validate the spec RRSIG: %v", err)
|
||||
}
|
||||
|
||||
ourRRSIG := &RRSIG{
|
||||
Hdr: RR_Header{
|
||||
Ttl: rrA.Header().Ttl,
|
||||
},
|
||||
KeyTag: rrDNSKEY.(*DNSKEY).KeyTag(),
|
||||
SignerName: rrDNSKEY.(*DNSKEY).Hdr.Name,
|
||||
Algorithm: rrDNSKEY.(*DNSKEY).Algorithm,
|
||||
}
|
||||
ourRRSIG.Expiration, _ = StringToTime("20100909100439")
|
||||
ourRRSIG.Inception, _ = StringToTime("20100812100439")
|
||||
err = ourRRSIG.Sign(priv.(*ecdsa.PrivateKey), []RR{rrA})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err = ourRRSIG.Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {
|
||||
t.Errorf("failure to validate our RRSIG: %v", err)
|
||||
}
|
||||
|
||||
// Signatures are randomized
|
||||
rrRRSIG.(*RRSIG).Signature = ""
|
||||
ourRRSIG.Signature = ""
|
||||
if !reflect.DeepEqual(ourRRSIG, rrRRSIG.(*RRSIG)) {
|
||||
t.Fatalf("RRSIG record differs:\n%v\n%v", ourRRSIG, rrRRSIG.(*RRSIG))
|
||||
}
|
||||
}
|
||||
|
||||
// rfc6605 6.2
|
||||
func TestRFC6605P384(t *testing.T) {
|
||||
exDNSKEY := `example.net. 3600 IN DNSKEY 257 3 14 (
|
||||
xKYaNhWdGOfJ+nPrL8/arkwf2EY3MDJ+SErKivBVSum1
|
||||
w/egsXvSADtNJhyem5RCOpgQ6K8X1DRSEkrbYQ+OB+v8
|
||||
/uX45NBwY8rp65F6Glur8I/mlVNgF6W/qTI37m40 )`
|
||||
exPriv := `Private-key-format: v1.2
|
||||
Algorithm: 14 (ECDSAP384SHA384)
|
||||
PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR`
|
||||
rrDNSKEY := testRR(exDNSKEY)
|
||||
priv, err := rrDNSKEY.(*DNSKEY).NewPrivateKey(exPriv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
exDS := `example.net. 3600 IN DS 10771 14 4 (
|
||||
72d7b62976ce06438e9c0bf319013cf801f09ecc84b8
|
||||
d7e9495f27e305c6a9b0563a9b5f4d288405c3008a94
|
||||
6df983d6 )`
|
||||
rrDS := testRR(exDS)
|
||||
ourDS := rrDNSKEY.(*DNSKEY).ToDS(SHA384)
|
||||
if !reflect.DeepEqual(ourDS, rrDS.(*DS)) {
|
||||
t.Fatalf("DS record differs:\n%v\n%v", ourDS, rrDS.(*DS))
|
||||
}
|
||||
|
||||
exA := `www.example.net. 3600 IN A 192.0.2.1`
|
||||
exRRSIG := `www.example.net. 3600 IN RRSIG A 14 3 3600 (
|
||||
20100909102025 20100812102025 10771 example.net.
|
||||
/L5hDKIvGDyI1fcARX3z65qrmPsVz73QD1Mr5CEqOiLP
|
||||
95hxQouuroGCeZOvzFaxsT8Glr74hbavRKayJNuydCuz
|
||||
WTSSPdz7wnqXL5bdcJzusdnI0RSMROxxwGipWcJm )`
|
||||
rrA := testRR(exA)
|
||||
rrRRSIG := testRR(exRRSIG)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {
|
||||
t.Errorf("failure to validate the spec RRSIG: %v", err)
|
||||
}
|
||||
|
||||
ourRRSIG := &RRSIG{
|
||||
Hdr: RR_Header{
|
||||
Ttl: rrA.Header().Ttl,
|
||||
},
|
||||
KeyTag: rrDNSKEY.(*DNSKEY).KeyTag(),
|
||||
SignerName: rrDNSKEY.(*DNSKEY).Hdr.Name,
|
||||
Algorithm: rrDNSKEY.(*DNSKEY).Algorithm,
|
||||
}
|
||||
ourRRSIG.Expiration, _ = StringToTime("20100909102025")
|
||||
ourRRSIG.Inception, _ = StringToTime("20100812102025")
|
||||
err = ourRRSIG.Sign(priv.(*ecdsa.PrivateKey), []RR{rrA})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err = ourRRSIG.Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {
|
||||
t.Errorf("failure to validate our RRSIG: %v", err)
|
||||
}
|
||||
|
||||
// Signatures are randomized
|
||||
rrRRSIG.(*RRSIG).Signature = ""
|
||||
ourRRSIG.Signature = ""
|
||||
if !reflect.DeepEqual(ourRRSIG, rrRRSIG.(*RRSIG)) {
|
||||
t.Fatalf("RRSIG record differs:\n%v\n%v", ourRRSIG, rrRRSIG.(*RRSIG))
|
||||
}
|
||||
}
|
||||
|
||||
// rfc8080 6.1
|
||||
func TestRFC8080Ed25519Example1(t *testing.T) {
|
||||
exDNSKEY := `example.com. 3600 IN DNSKEY 257 3 15 (
|
||||
l02Woi0iS8Aa25FQkUd9RMzZHJpBoRQwAQEX1SxZJA4= )`
|
||||
exPriv := `Private-key-format: v1.2
|
||||
Algorithm: 15 (ED25519)
|
||||
PrivateKey: ODIyNjAzODQ2MjgwODAxMjI2NDUxOTAyMDQxNDIyNjI=`
|
||||
rrDNSKEY, err := NewRR(exDNSKEY)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
priv, err := rrDNSKEY.(*DNSKEY).NewPrivateKey(exPriv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
exDS := `example.com. 3600 IN DS 3613 15 2 (
|
||||
3aa5ab37efce57f737fc1627013fee07bdf241bd10f3b1964ab55c78e79
|
||||
a304b )`
|
||||
rrDS, err := NewRR(exDS)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ourDS := rrDNSKEY.(*DNSKEY).ToDS(SHA256)
|
||||
if !reflect.DeepEqual(ourDS, rrDS.(*DS)) {
|
||||
t.Fatalf("DS record differs:\n%v\n%v", ourDS, rrDS.(*DS))
|
||||
}
|
||||
|
||||
exMX := `example.com. 3600 IN MX 10 mail.example.com.`
|
||||
exRRSIG := `example.com. 3600 IN RRSIG MX 15 2 3600 (
|
||||
1440021600 1438207200 3613 example.com. (
|
||||
oL9krJun7xfBOIWcGHi7mag5/hdZrKWw15jPGrHpjQeRAvTdszaPD+QLs3f
|
||||
x8A4M3e23mRZ9VrbpMngwcrqNAg== ) )`
|
||||
rrMX, err := NewRR(exMX)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rrRRSIG, err := NewRR(exRRSIG)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrMX}); err != nil {
|
||||
t.Errorf("failure to validate the spec RRSIG: %v", err)
|
||||
}
|
||||
|
||||
ourRRSIG := &RRSIG{
|
||||
Hdr: RR_Header{
|
||||
Ttl: rrMX.Header().Ttl,
|
||||
},
|
||||
KeyTag: rrDNSKEY.(*DNSKEY).KeyTag(),
|
||||
SignerName: rrDNSKEY.(*DNSKEY).Hdr.Name,
|
||||
Algorithm: rrDNSKEY.(*DNSKEY).Algorithm,
|
||||
}
|
||||
ourRRSIG.Expiration, _ = StringToTime("20150819220000")
|
||||
ourRRSIG.Inception, _ = StringToTime("20150729220000")
|
||||
err = ourRRSIG.Sign(priv.(ed25519.PrivateKey), []RR{rrMX})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err = ourRRSIG.Verify(rrDNSKEY.(*DNSKEY), []RR{rrMX}); err != nil {
|
||||
t.Errorf("failure to validate our RRSIG: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(ourRRSIG, rrRRSIG.(*RRSIG)) {
|
||||
t.Fatalf("RRSIG record differs:\n%v\n%v", ourRRSIG, rrRRSIG.(*RRSIG))
|
||||
}
|
||||
}
|
||||
|
||||
// rfc8080 6.1
|
||||
func TestRFC8080Ed25519Example2(t *testing.T) {
|
||||
exDNSKEY := `example.com. 3600 IN DNSKEY 257 3 15 (
|
||||
zPnZ/QwEe7S8C5SPz2OfS5RR40ATk2/rYnE9xHIEijs= )`
|
||||
exPriv := `Private-key-format: v1.2
|
||||
Algorithm: 15 (ED25519)
|
||||
PrivateKey: DSSF3o0s0f+ElWzj9E/Osxw8hLpk55chkmx0LYN5WiY=`
|
||||
rrDNSKEY, err := NewRR(exDNSKEY)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
priv, err := rrDNSKEY.(*DNSKEY).NewPrivateKey(exPriv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
exDS := `example.com. 3600 IN DS 35217 15 2 (
|
||||
401781b934e392de492ec77ae2e15d70f6575a1c0bc59c5275c04ebe80c
|
||||
6614c )`
|
||||
rrDS, err := NewRR(exDS)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ourDS := rrDNSKEY.(*DNSKEY).ToDS(SHA256)
|
||||
if !reflect.DeepEqual(ourDS, rrDS.(*DS)) {
|
||||
t.Fatalf("DS record differs:\n%v\n%v", ourDS, rrDS.(*DS))
|
||||
}
|
||||
|
||||
exMX := `example.com. 3600 IN MX 10 mail.example.com.`
|
||||
exRRSIG := `example.com. 3600 IN RRSIG MX 15 2 3600 (
|
||||
1440021600 1438207200 35217 example.com. (
|
||||
zXQ0bkYgQTEFyfLyi9QoiY6D8ZdYo4wyUhVioYZXFdT410QPRITQSqJSnzQ
|
||||
oSm5poJ7gD7AQR0O7KuI5k2pcBg== ) )`
|
||||
rrMX, err := NewRR(exMX)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rrRRSIG, err := NewRR(exRRSIG)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrMX}); err != nil {
|
||||
t.Errorf("failure to validate the spec RRSIG: %v", err)
|
||||
}
|
||||
|
||||
ourRRSIG := &RRSIG{
|
||||
Hdr: RR_Header{
|
||||
Ttl: rrMX.Header().Ttl,
|
||||
},
|
||||
KeyTag: rrDNSKEY.(*DNSKEY).KeyTag(),
|
||||
SignerName: rrDNSKEY.(*DNSKEY).Hdr.Name,
|
||||
Algorithm: rrDNSKEY.(*DNSKEY).Algorithm,
|
||||
}
|
||||
ourRRSIG.Expiration, _ = StringToTime("20150819220000")
|
||||
ourRRSIG.Inception, _ = StringToTime("20150729220000")
|
||||
err = ourRRSIG.Sign(priv.(ed25519.PrivateKey), []RR{rrMX})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err = ourRRSIG.Verify(rrDNSKEY.(*DNSKEY), []RR{rrMX}); err != nil {
|
||||
t.Errorf("failure to validate our RRSIG: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(ourRRSIG, rrRRSIG.(*RRSIG)) {
|
||||
t.Fatalf("RRSIG record differs:\n%v\n%v", ourRRSIG, rrRRSIG.(*RRSIG))
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidRRSet(t *testing.T) {
|
||||
goodRecords := make([]RR, 2)
|
||||
goodRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
|
||||
goodRecords[1] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"_o/"}}
|
||||
|
||||
// Generate key
|
||||
keyname := "cloudflare.com."
|
||||
key := &DNSKEY{
|
||||
Hdr: RR_Header{Name: keyname, Rrtype: TypeDNSKEY, Class: ClassINET, Ttl: 0},
|
||||
Algorithm: ECDSAP256SHA256,
|
||||
Flags: ZONE,
|
||||
Protocol: 3,
|
||||
}
|
||||
privatekey, err := key.Generate(256)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
// Need to fill in: Inception, Expiration, KeyTag, SignerName and Algorithm
|
||||
curTime := time.Now()
|
||||
signature := &RRSIG{
|
||||
Inception: uint32(curTime.Unix()),
|
||||
Expiration: uint32(curTime.Add(time.Hour).Unix()),
|
||||
KeyTag: key.KeyTag(),
|
||||
SignerName: keyname,
|
||||
Algorithm: ECDSAP256SHA256,
|
||||
}
|
||||
|
||||
// Inconsistent name between records
|
||||
badRecords := make([]RR, 2)
|
||||
badRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
|
||||
badRecords[1] = &TXT{Hdr: RR_Header{Name: "nama.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"_o/"}}
|
||||
|
||||
if IsRRset(badRecords) {
|
||||
t.Fatal("Record set with inconsistent names considered valid")
|
||||
}
|
||||
|
||||
badRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
|
||||
badRecords[1] = &A{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeA, Class: ClassINET, Ttl: 0}}
|
||||
|
||||
if IsRRset(badRecords) {
|
||||
t.Fatal("Record set with inconsistent record types considered valid")
|
||||
}
|
||||
|
||||
badRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
|
||||
badRecords[1] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassCHAOS, Ttl: 0}, Txt: []string{"_o/"}}
|
||||
|
||||
if IsRRset(badRecords) {
|
||||
t.Fatal("Record set with inconsistent record class considered valid")
|
||||
}
|
||||
|
||||
// Sign the good record set and then make sure verification fails on the bad record set
|
||||
if err := signature.Sign(privatekey.(crypto.Signer), goodRecords); err != nil {
|
||||
t.Fatal("Signing good records failed")
|
||||
}
|
||||
|
||||
if err := signature.Verify(key, badRecords); err != ErrRRset {
|
||||
t.Fatal("Verification did not return ErrRRset with inconsistent records")
|
||||
}
|
||||
}
|
||||
|
||||
// Issue #688 - RSA exponent unpacked in reverse
|
||||
func TestRsaExponentUnpack(t *testing.T) {
|
||||
zskRrDnskey, _ := NewRR("isc.org. 7200 IN DNSKEY 256 3 5 AwEAAcdkaRUlsRD4gcF63PpPJJ1E6kOIb3yn/UHptVsPEQtEbgJ2y20O eix4unpwoQkz+bIAd2rrOU/95wgV530x0/qqKwBLWoGkxdcnNcvVT4hl 3SOTZy1VjwkAfyayHPU8VisXqJGbB3KWevBZlb6AtrXzFu8AHuBeeAAe /fOgreCh")
|
||||
kskRrDnskey, _ := NewRR("isc.org. 7200 IN DNSKEY 257 3 5 BEAAAAOhHQDBrhQbtphgq2wQUpEQ5t4DtUHxoMVFu2hWLDMvoOMRXjGr hhCeFvAZih7yJHf8ZGfW6hd38hXG/xylYCO6Krpbdojwx8YMXLA5/kA+ u50WIL8ZR1R6KTbsYVMf/Qx5RiNbPClw+vT+U8eXEJmO20jIS1ULgqy3 47cBB1zMnnz/4LJpA0da9CbKj3A254T515sNIMcwsB8/2+2E63/zZrQz Bkj0BrN/9Bexjpiks3jRhZatEsXn3dTy47R09Uix5WcJt+xzqZ7+ysyL KOOedS39Z7SDmsn2eA0FKtQpwA6LXeG2w+jxmw3oA8lVUgEf/rzeC/bB yBNsO70aEFTd")
|
||||
kskRrRrsig, _ := NewRR("isc.org. 7200 IN RRSIG DNSKEY 5 2 7200 20180627230244 20180528230244 12892 isc.org. ebKBlhYi1hPGTdPg6zSwvprOIkoFMs+WIhMSjoYW6/K5CS9lDDFdK4cu TgXJRT3etrltTuJiFe2HRpp+7t5cKLy+CeJZVzqrCz200MoHiFuLI9yI DJQGaS5YYCiFbw5+jUGU6aUhZ7Y5/YufeqATkRZzdrKwgK+zri8LPw9T WLoVJPAOW7GR0dgxl9WKmO7Fzi9P8BZR3NuwLV7329X94j+4zyswaw7q e5vif0ybzFveODLsEi/E0a2rTXc4QzzyM0fSVxRkVQyQ7ifIPP4ohnnT d5qpPUbE8xxBzTdWR/TaKADC5aCFkppG9lVAq5CPfClii2949X5RYzy1 rxhuSA==")
|
||||
zskRrRrsig, _ := NewRR("isc.org. 7200 IN RRSIG DNSKEY 5 2 7200 20180627230244 20180528230244 19923 isc.org. RgCfzUeq4RJPGoe9RRB6cWf6d/Du+tHK5SxI5QL1waA3O5qVtQKFkY1C dq/yyVjwzfjD9F62TObujOaktv8X80ZMcNPmgHbvK1xOqelMBWv5hxj3 xRe+QQObLZ5NPfHFsphQKXvwgO5Sjk8py2B2iCr3BHCZ8S38oIfuSrQx sn8=")
|
||||
|
||||
zsk, ksk := zskRrDnskey.(*DNSKEY), kskRrDnskey.(*DNSKEY)
|
||||
zskSig, kskSig := zskRrRrsig.(*RRSIG), kskRrRrsig.(*RRSIG)
|
||||
|
||||
if e := zskSig.Verify(zsk, []RR{zsk, ksk}); e != nil {
|
||||
t.Fatalf("cannot verify RRSIG with keytag [%d]. Cause [%s]", zsk.KeyTag(), e.Error())
|
||||
}
|
||||
|
||||
if e := kskSig.Verify(ksk, []RR{zsk, ksk}); e != nil {
|
||||
t.Fatalf("cannot verify RRSIG with keytag [%d]. Cause [%s]", ksk.KeyTag(), e.Error())
|
||||
}
|
||||
}
|
||||
83
vendor/github.com/miekg/dns/dnsutil/util.go
generated
vendored
Normal file
83
vendor/github.com/miekg/dns/dnsutil/util.go
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
// Package dnsutil contains higher-level methods useful with the dns
|
||||
// package. While package dns implements the DNS protocols itself,
|
||||
// these functions are related but not directly required for protocol
|
||||
// processing. They are often useful in preparing input/output of the
|
||||
// functions in package dns.
|
||||
package dnsutil
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// AddOrigin adds origin to s if s is not already a FQDN.
|
||||
// Note that the result may not be a FQDN. If origin does not end
|
||||
// with a ".", the result won't either.
|
||||
// This implements the zonefile convention (specified in RFC 1035,
|
||||
// Section "5.1. Format") that "@" represents the
|
||||
// apex (bare) domain. i.e. AddOrigin("@", "foo.com.") returns "foo.com.".
|
||||
func AddOrigin(s, origin string) string {
|
||||
// ("foo.", "origin.") -> "foo." (already a FQDN)
|
||||
// ("foo", "origin.") -> "foo.origin."
|
||||
// ("foo", "origin") -> "foo.origin"
|
||||
// ("foo", ".") -> "foo." (Same as dns.Fqdn())
|
||||
// ("foo.", ".") -> "foo." (Same as dns.Fqdn())
|
||||
// ("@", "origin.") -> "origin." (@ represents the apex (bare) domain)
|
||||
// ("", "origin.") -> "origin." (not obvious)
|
||||
// ("foo", "") -> "foo" (not obvious)
|
||||
|
||||
if dns.IsFqdn(s) {
|
||||
return s // s is already a FQDN, no need to mess with it.
|
||||
}
|
||||
if len(origin) == 0 {
|
||||
return s // Nothing to append.
|
||||
}
|
||||
if s == "@" || len(s) == 0 {
|
||||
return origin // Expand apex.
|
||||
}
|
||||
if origin == "." {
|
||||
return dns.Fqdn(s)
|
||||
}
|
||||
|
||||
return s + "." + origin // The simple case.
|
||||
}
|
||||
|
||||
// TrimDomainName trims origin from s if s is a subdomain.
|
||||
// This function will never return "", but returns "@" instead (@ represents the apex domain).
|
||||
func TrimDomainName(s, origin string) string {
|
||||
// An apex (bare) domain is always returned as "@".
|
||||
// If the return value ends in a ".", the domain was not the suffix.
|
||||
// origin can end in "." or not. Either way the results should be the same.
|
||||
|
||||
if len(s) == 0 {
|
||||
return "@"
|
||||
}
|
||||
// Someone is using TrimDomainName(s, ".") to remove a dot if it exists.
|
||||
if origin == "." {
|
||||
return strings.TrimSuffix(s, origin)
|
||||
}
|
||||
|
||||
original := s
|
||||
s = dns.Fqdn(s)
|
||||
origin = dns.Fqdn(origin)
|
||||
|
||||
if !dns.IsSubDomain(origin, s) {
|
||||
return original
|
||||
}
|
||||
|
||||
slabels := dns.Split(s)
|
||||
olabels := dns.Split(origin)
|
||||
m := dns.CompareDomainName(s, origin)
|
||||
if len(olabels) == m {
|
||||
if len(olabels) == len(slabels) {
|
||||
return "@" // origin == s
|
||||
}
|
||||
if (s[0] == '.') && (len(slabels) == (len(olabels) + 1)) {
|
||||
return "@" // TrimDomainName(".foo.", "foo.")
|
||||
}
|
||||
}
|
||||
|
||||
// Return the first (len-m) labels:
|
||||
return s[:slabels[len(slabels)-m]-1]
|
||||
}
|
||||
130
vendor/github.com/miekg/dns/dnsutil/util_test.go
generated
vendored
Normal file
130
vendor/github.com/miekg/dns/dnsutil/util_test.go
generated
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
package dnsutil
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestAddOrigin(t *testing.T) {
|
||||
var tests = []struct{ e1, e2, expected string }{
|
||||
{"@", "example.com", "example.com"},
|
||||
{"foo", "example.com", "foo.example.com"},
|
||||
{"foo.", "example.com", "foo."},
|
||||
{"@", "example.com.", "example.com."},
|
||||
{"foo", "example.com.", "foo.example.com."},
|
||||
{"foo.", "example.com.", "foo."},
|
||||
{"example.com", ".", "example.com."},
|
||||
{"example.com.", ".", "example.com."},
|
||||
// Oddball tests:
|
||||
// In general origin should not be "" or "." but at least
|
||||
// these tests verify we don't crash and will keep results
|
||||
// from changing unexpectedly.
|
||||
{"*.", "", "*."},
|
||||
{"@", "", "@"},
|
||||
{"foobar", "", "foobar"},
|
||||
{"foobar.", "", "foobar."},
|
||||
{"*.", ".", "*."},
|
||||
{"@", ".", "."},
|
||||
{"foobar", ".", "foobar."},
|
||||
{"foobar.", ".", "foobar."},
|
||||
}
|
||||
for _, test := range tests {
|
||||
actual := AddOrigin(test.e1, test.e2)
|
||||
if test.expected != actual {
|
||||
t.Errorf("AddOrigin(%#v, %#v) expected %#v, got %#v\n", test.e1, test.e2, test.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrimDomainName(t *testing.T) {
|
||||
// Basic tests.
|
||||
// Try trimming "example.com" and "example.com." from typical use cases.
|
||||
testsEx := []struct{ experiment, expected string }{
|
||||
{"foo.example.com", "foo"},
|
||||
{"foo.example.com.", "foo"},
|
||||
{".foo.example.com", ".foo"},
|
||||
{".foo.example.com.", ".foo"},
|
||||
{"*.example.com", "*"},
|
||||
{"example.com", "@"},
|
||||
{"example.com.", "@"},
|
||||
{"com.", "com."},
|
||||
{"foo.", "foo."},
|
||||
{"serverfault.com.", "serverfault.com."},
|
||||
{"serverfault.com", "serverfault.com"},
|
||||
{".foo.ronco.com", ".foo.ronco.com"},
|
||||
{".foo.ronco.com.", ".foo.ronco.com."},
|
||||
}
|
||||
for _, dom := range []string{"example.com", "example.com."} {
|
||||
for i, test := range testsEx {
|
||||
actual := TrimDomainName(test.experiment, dom)
|
||||
if test.expected != actual {
|
||||
t.Errorf("%d TrimDomainName(%#v, %#v): expected %v, got %v\n", i, test.experiment, dom, test.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Paranoid tests.
|
||||
// These test shouldn't be needed but I was weary of off-by-one errors.
|
||||
// In theory, these can't happen because there are no single-letter TLDs,
|
||||
// but it is good to exercize the code this way.
|
||||
tests := []struct{ experiment, expected string }{
|
||||
{"", "@"},
|
||||
{".", "."},
|
||||
{"a.b.c.d.e.f.", "a.b.c.d.e"},
|
||||
{"b.c.d.e.f.", "b.c.d.e"},
|
||||
{"c.d.e.f.", "c.d.e"},
|
||||
{"d.e.f.", "d.e"},
|
||||
{"e.f.", "e"},
|
||||
{"f.", "@"},
|
||||
{".a.b.c.d.e.f.", ".a.b.c.d.e"},
|
||||
{".b.c.d.e.f.", ".b.c.d.e"},
|
||||
{".c.d.e.f.", ".c.d.e"},
|
||||
{".d.e.f.", ".d.e"},
|
||||
{".e.f.", ".e"},
|
||||
{".f.", "@"},
|
||||
{"a.b.c.d.e.f", "a.b.c.d.e"},
|
||||
{"a.b.c.d.e.", "a.b.c.d.e."},
|
||||
{"a.b.c.d.e", "a.b.c.d.e"},
|
||||
{"a.b.c.d.", "a.b.c.d."},
|
||||
{"a.b.c.d", "a.b.c.d"},
|
||||
{"a.b.c.", "a.b.c."},
|
||||
{"a.b.c", "a.b.c"},
|
||||
{"a.b.", "a.b."},
|
||||
{"a.b", "a.b"},
|
||||
{"a.", "a."},
|
||||
{"a", "a"},
|
||||
{".a.b.c.d.e.f", ".a.b.c.d.e"},
|
||||
{".a.b.c.d.e.", ".a.b.c.d.e."},
|
||||
{".a.b.c.d.e", ".a.b.c.d.e"},
|
||||
{".a.b.c.d.", ".a.b.c.d."},
|
||||
{".a.b.c.d", ".a.b.c.d"},
|
||||
{".a.b.c.", ".a.b.c."},
|
||||
{".a.b.c", ".a.b.c"},
|
||||
{".a.b.", ".a.b."},
|
||||
{".a.b", ".a.b"},
|
||||
{".a.", ".a."},
|
||||
{".a", ".a"},
|
||||
}
|
||||
for _, dom := range []string{"f", "f."} {
|
||||
for i, test := range tests {
|
||||
actual := TrimDomainName(test.experiment, dom)
|
||||
if test.expected != actual {
|
||||
t.Errorf("%d TrimDomainName(%#v, %#v): expected %v, got %v\n", i, test.experiment, dom, test.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test cases for bugs found in the wild.
|
||||
// These test cases provide both origin, s, and the expected result.
|
||||
// If you find a bug in the while, this is probably the easiest place
|
||||
// to add it as a test case.
|
||||
var testsWild = []struct{ e1, e2, expected string }{
|
||||
{"mathoverflow.net.", ".", "mathoverflow.net"},
|
||||
{"mathoverflow.net", ".", "mathoverflow.net"},
|
||||
{"", ".", "@"},
|
||||
{"@", ".", "@"},
|
||||
}
|
||||
for i, test := range testsWild {
|
||||
actual := TrimDomainName(test.e1, test.e2)
|
||||
if test.expected != actual {
|
||||
t.Errorf("%d TrimDomainName(%#v, %#v): expected %v, got %v\n", i, test.e1, test.e2, test.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
272
vendor/github.com/miekg/dns/doc.go
generated
vendored
Normal file
272
vendor/github.com/miekg/dns/doc.go
generated
vendored
Normal file
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
Package dns implements a full featured interface to the Domain Name System.
|
||||
Server- and client-side programming is supported.
|
||||
The package allows complete control over what is sent out to the DNS. The package
|
||||
API follows the less-is-more principle, by presenting a small, clean interface.
|
||||
|
||||
The package dns supports (asynchronous) querying/replying, incoming/outgoing zone transfers,
|
||||
TSIG, EDNS0, dynamic updates, notifies and DNSSEC validation/signing.
|
||||
Note that domain names MUST be fully qualified, before sending them, unqualified
|
||||
names in a message will result in a packing failure.
|
||||
|
||||
Resource records are native types. They are not stored in wire format.
|
||||
Basic usage pattern for creating a new resource record:
|
||||
|
||||
r := new(dns.MX)
|
||||
r.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeMX,
|
||||
Class: dns.ClassINET, Ttl: 3600}
|
||||
r.Preference = 10
|
||||
r.Mx = "mx.miek.nl."
|
||||
|
||||
Or directly from a string:
|
||||
|
||||
mx, err := dns.NewRR("miek.nl. 3600 IN MX 10 mx.miek.nl.")
|
||||
|
||||
Or when the default origin (.) and TTL (3600) and class (IN) suit you:
|
||||
|
||||
mx, err := dns.NewRR("miek.nl MX 10 mx.miek.nl")
|
||||
|
||||
Or even:
|
||||
|
||||
mx, err := dns.NewRR("$ORIGIN nl.\nmiek 1H IN MX 10 mx.miek")
|
||||
|
||||
In the DNS messages are exchanged, these messages contain resource
|
||||
records (sets). Use pattern for creating a message:
|
||||
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion("miek.nl.", dns.TypeMX)
|
||||
|
||||
Or when not certain if the domain name is fully qualified:
|
||||
|
||||
m.SetQuestion(dns.Fqdn("miek.nl"), dns.TypeMX)
|
||||
|
||||
The message m is now a message with the question section set to ask
|
||||
the MX records for the miek.nl. zone.
|
||||
|
||||
The following is slightly more verbose, but more flexible:
|
||||
|
||||
m1 := new(dns.Msg)
|
||||
m1.Id = dns.Id()
|
||||
m1.RecursionDesired = true
|
||||
m1.Question = make([]dns.Question, 1)
|
||||
m1.Question[0] = dns.Question{"miek.nl.", dns.TypeMX, dns.ClassINET}
|
||||
|
||||
After creating a message it can be sent.
|
||||
Basic use pattern for synchronous querying the DNS at a
|
||||
server configured on 127.0.0.1 and port 53:
|
||||
|
||||
c := new(dns.Client)
|
||||
in, rtt, err := c.Exchange(m1, "127.0.0.1:53")
|
||||
|
||||
Suppressing multiple outstanding queries (with the same question, type and
|
||||
class) is as easy as setting:
|
||||
|
||||
c.SingleInflight = true
|
||||
|
||||
More advanced options are available using a net.Dialer and the corresponding API.
|
||||
For example it is possible to set a timeout, or to specify a source IP address
|
||||
and port to use for the connection:
|
||||
|
||||
c := new(dns.Client)
|
||||
laddr := net.UDPAddr{
|
||||
IP: net.ParseIP("[::1]"),
|
||||
Port: 12345,
|
||||
Zone: "",
|
||||
}
|
||||
c.Dialer := &net.Dialer{
|
||||
Timeout: 200 * time.Millisecond,
|
||||
LocalAddr: &laddr,
|
||||
}
|
||||
in, rtt, err := c.Exchange(m1, "8.8.8.8:53")
|
||||
|
||||
If these "advanced" features are not needed, a simple UDP query can be sent,
|
||||
with:
|
||||
|
||||
in, err := dns.Exchange(m1, "127.0.0.1:53")
|
||||
|
||||
When this functions returns you will get dns message. A dns message consists
|
||||
out of four sections.
|
||||
The question section: in.Question, the answer section: in.Answer,
|
||||
the authority section: in.Ns and the additional section: in.Extra.
|
||||
|
||||
Each of these sections (except the Question section) contain a []RR. Basic
|
||||
use pattern for accessing the rdata of a TXT RR as the first RR in
|
||||
the Answer section:
|
||||
|
||||
if t, ok := in.Answer[0].(*dns.TXT); ok {
|
||||
// do something with t.Txt
|
||||
}
|
||||
|
||||
Domain Name and TXT Character String Representations
|
||||
|
||||
Both domain names and TXT character strings are converted to presentation
|
||||
form both when unpacked and when converted to strings.
|
||||
|
||||
For TXT character strings, tabs, carriage returns and line feeds will be
|
||||
converted to \t, \r and \n respectively. Back slashes and quotations marks
|
||||
will be escaped. Bytes below 32 and above 127 will be converted to \DDD
|
||||
form.
|
||||
|
||||
For domain names, in addition to the above rules brackets, periods,
|
||||
spaces, semicolons and the at symbol are escaped.
|
||||
|
||||
DNSSEC
|
||||
|
||||
DNSSEC (DNS Security Extension) adds a layer of security to the DNS. It
|
||||
uses public key cryptography to sign resource records. The
|
||||
public keys are stored in DNSKEY records and the signatures in RRSIG records.
|
||||
|
||||
Requesting DNSSEC information for a zone is done by adding the DO (DNSSEC OK) bit
|
||||
to a request.
|
||||
|
||||
m := new(dns.Msg)
|
||||
m.SetEdns0(4096, true)
|
||||
|
||||
Signature generation, signature verification and key generation are all supported.
|
||||
|
||||
DYNAMIC UPDATES
|
||||
|
||||
Dynamic updates reuses the DNS message format, but renames three of
|
||||
the sections. Question is Zone, Answer is Prerequisite, Authority is
|
||||
Update, only the Additional is not renamed. See RFC 2136 for the gory details.
|
||||
|
||||
You can set a rather complex set of rules for the existence of absence of
|
||||
certain resource records or names in a zone to specify if resource records
|
||||
should be added or removed. The table from RFC 2136 supplemented with the Go
|
||||
DNS function shows which functions exist to specify the prerequisites.
|
||||
|
||||
3.2.4 - Table Of Metavalues Used In Prerequisite Section
|
||||
|
||||
CLASS TYPE RDATA Meaning Function
|
||||
--------------------------------------------------------------
|
||||
ANY ANY empty Name is in use dns.NameUsed
|
||||
ANY rrset empty RRset exists (value indep) dns.RRsetUsed
|
||||
NONE ANY empty Name is not in use dns.NameNotUsed
|
||||
NONE rrset empty RRset does not exist dns.RRsetNotUsed
|
||||
zone rrset rr RRset exists (value dep) dns.Used
|
||||
|
||||
The prerequisite section can also be left empty.
|
||||
If you have decided on the prerequisites you can tell what RRs should
|
||||
be added or deleted. The next table shows the options you have and
|
||||
what functions to call.
|
||||
|
||||
3.4.2.6 - Table Of Metavalues Used In Update Section
|
||||
|
||||
CLASS TYPE RDATA Meaning Function
|
||||
---------------------------------------------------------------
|
||||
ANY ANY empty Delete all RRsets from name dns.RemoveName
|
||||
ANY rrset empty Delete an RRset dns.RemoveRRset
|
||||
NONE rrset rr Delete an RR from RRset dns.Remove
|
||||
zone rrset rr Add to an RRset dns.Insert
|
||||
|
||||
TRANSACTION SIGNATURE
|
||||
|
||||
An TSIG or transaction signature adds a HMAC TSIG record to each message sent.
|
||||
The supported algorithms include: HmacMD5, HmacSHA1, HmacSHA256 and HmacSHA512.
|
||||
|
||||
Basic use pattern when querying with a TSIG name "axfr." (note that these key names
|
||||
must be fully qualified - as they are domain names) and the base64 secret
|
||||
"so6ZGir4GPAqINNh9U5c3A==":
|
||||
|
||||
If an incoming message contains a TSIG record it MUST be the last record in
|
||||
the additional section (RFC2845 3.2). This means that you should make the
|
||||
call to SetTsig last, right before executing the query. If you make any
|
||||
changes to the RRset after calling SetTsig() the signature will be incorrect.
|
||||
|
||||
c := new(dns.Client)
|
||||
c.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion("miek.nl.", dns.TypeMX)
|
||||
m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix())
|
||||
...
|
||||
// When sending the TSIG RR is calculated and filled in before sending
|
||||
|
||||
When requesting an zone transfer (almost all TSIG usage is when requesting zone transfers), with
|
||||
TSIG, this is the basic use pattern. In this example we request an AXFR for
|
||||
miek.nl. with TSIG key named "axfr." and secret "so6ZGir4GPAqINNh9U5c3A=="
|
||||
and using the server 176.58.119.54:
|
||||
|
||||
t := new(dns.Transfer)
|
||||
m := new(dns.Msg)
|
||||
t.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
|
||||
m.SetAxfr("miek.nl.")
|
||||
m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix())
|
||||
c, err := t.In(m, "176.58.119.54:53")
|
||||
for r := range c { ... }
|
||||
|
||||
You can now read the records from the transfer as they come in. Each envelope is checked with TSIG.
|
||||
If something is not correct an error is returned.
|
||||
|
||||
Basic use pattern validating and replying to a message that has TSIG set.
|
||||
|
||||
server := &dns.Server{Addr: ":53", Net: "udp"}
|
||||
server.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
|
||||
go server.ListenAndServe()
|
||||
dns.HandleFunc(".", handleRequest)
|
||||
|
||||
func handleRequest(w dns.ResponseWriter, r *dns.Msg) {
|
||||
m := new(dns.Msg)
|
||||
m.SetReply(r)
|
||||
if r.IsTsig() != nil {
|
||||
if w.TsigStatus() == nil {
|
||||
// *Msg r has an TSIG record and it was validated
|
||||
m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix())
|
||||
} else {
|
||||
// *Msg r has an TSIG records and it was not valided
|
||||
}
|
||||
}
|
||||
w.WriteMsg(m)
|
||||
}
|
||||
|
||||
PRIVATE RRS
|
||||
|
||||
RFC 6895 sets aside a range of type codes for private use. This range
|
||||
is 65,280 - 65,534 (0xFF00 - 0xFFFE). When experimenting with new Resource Records these
|
||||
can be used, before requesting an official type code from IANA.
|
||||
|
||||
see http://miek.nl/2014/September/21/idn-and-private-rr-in-go-dns/ for more
|
||||
information.
|
||||
|
||||
EDNS0
|
||||
|
||||
EDNS0 is an extension mechanism for the DNS defined in RFC 2671 and updated
|
||||
by RFC 6891. It defines an new RR type, the OPT RR, which is then completely
|
||||
abused.
|
||||
Basic use pattern for creating an (empty) OPT RR:
|
||||
|
||||
o := new(dns.OPT)
|
||||
o.Hdr.Name = "." // MUST be the root zone, per definition.
|
||||
o.Hdr.Rrtype = dns.TypeOPT
|
||||
|
||||
The rdata of an OPT RR consists out of a slice of EDNS0 (RFC 6891)
|
||||
interfaces. Currently only a few have been standardized: EDNS0_NSID
|
||||
(RFC 5001) and EDNS0_SUBNET (draft-vandergaast-edns-client-subnet-02). Note
|
||||
that these options may be combined in an OPT RR.
|
||||
Basic use pattern for a server to check if (and which) options are set:
|
||||
|
||||
// o is a dns.OPT
|
||||
for _, s := range o.Option {
|
||||
switch e := s.(type) {
|
||||
case *dns.EDNS0_NSID:
|
||||
// do stuff with e.Nsid
|
||||
case *dns.EDNS0_SUBNET:
|
||||
// access e.Family, e.Address, etc.
|
||||
}
|
||||
}
|
||||
|
||||
SIG(0)
|
||||
|
||||
From RFC 2931:
|
||||
|
||||
SIG(0) provides protection for DNS transactions and requests ....
|
||||
... protection for glue records, DNS requests, protection for message headers
|
||||
on requests and responses, and protection of the overall integrity of a response.
|
||||
|
||||
It works like TSIG, except that SIG(0) uses public key cryptography, instead of the shared
|
||||
secret approach in TSIG.
|
||||
Supported algorithms: DSA, ECDSAP256SHA256, ECDSAP384SHA384, RSASHA1, RSASHA256 and
|
||||
RSASHA512.
|
||||
|
||||
Signing subsequent messages in multi-message sessions is not implemented.
|
||||
*/
|
||||
package dns
|
||||
3
vendor/github.com/miekg/dns/dyn_test.go
generated
vendored
Normal file
3
vendor/github.com/miekg/dns/dyn_test.go
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
package dns
|
||||
|
||||
// Find better solution
|
||||
630
vendor/github.com/miekg/dns/edns.go
generated
vendored
Normal file
630
vendor/github.com/miekg/dns/edns.go
generated
vendored
Normal file
@@ -0,0 +1,630 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// EDNS0 Option codes.
|
||||
const (
|
||||
EDNS0LLQ = 0x1 // long lived queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01
|
||||
EDNS0UL = 0x2 // update lease draft: http://files.dns-sd.org/draft-sekar-dns-ul.txt
|
||||
EDNS0NSID = 0x3 // nsid (See RFC 5001)
|
||||
EDNS0DAU = 0x5 // DNSSEC Algorithm Understood
|
||||
EDNS0DHU = 0x6 // DS Hash Understood
|
||||
EDNS0N3U = 0x7 // NSEC3 Hash Understood
|
||||
EDNS0SUBNET = 0x8 // client-subnet (See RFC 7871)
|
||||
EDNS0EXPIRE = 0x9 // EDNS0 expire
|
||||
EDNS0COOKIE = 0xa // EDNS0 Cookie
|
||||
EDNS0TCPKEEPALIVE = 0xb // EDNS0 tcp keep alive (See RFC 7828)
|
||||
EDNS0PADDING = 0xc // EDNS0 padding (See RFC 7830)
|
||||
EDNS0LOCALSTART = 0xFDE9 // Beginning of range reserved for local/experimental use (See RFC 6891)
|
||||
EDNS0LOCALEND = 0xFFFE // End of range reserved for local/experimental use (See RFC 6891)
|
||||
_DO = 1 << 15 // DNSSEC OK
|
||||
)
|
||||
|
||||
// OPT is the EDNS0 RR appended to messages to convey extra (meta) information.
|
||||
// See RFC 6891.
|
||||
type OPT struct {
|
||||
Hdr RR_Header
|
||||
Option []EDNS0 `dns:"opt"`
|
||||
}
|
||||
|
||||
func (rr *OPT) String() string {
|
||||
s := "\n;; OPT PSEUDOSECTION:\n; EDNS: version " + strconv.Itoa(int(rr.Version())) + "; "
|
||||
if rr.Do() {
|
||||
s += "flags: do; "
|
||||
} else {
|
||||
s += "flags: ; "
|
||||
}
|
||||
s += "udp: " + strconv.Itoa(int(rr.UDPSize()))
|
||||
|
||||
for _, o := range rr.Option {
|
||||
switch o.(type) {
|
||||
case *EDNS0_NSID:
|
||||
s += "\n; NSID: " + o.String()
|
||||
h, e := o.pack()
|
||||
var r string
|
||||
if e == nil {
|
||||
for _, c := range h {
|
||||
r += "(" + string(c) + ")"
|
||||
}
|
||||
s += " " + r
|
||||
}
|
||||
case *EDNS0_SUBNET:
|
||||
s += "\n; SUBNET: " + o.String()
|
||||
case *EDNS0_COOKIE:
|
||||
s += "\n; COOKIE: " + o.String()
|
||||
case *EDNS0_UL:
|
||||
s += "\n; UPDATE LEASE: " + o.String()
|
||||
case *EDNS0_LLQ:
|
||||
s += "\n; LONG LIVED QUERIES: " + o.String()
|
||||
case *EDNS0_DAU:
|
||||
s += "\n; DNSSEC ALGORITHM UNDERSTOOD: " + o.String()
|
||||
case *EDNS0_DHU:
|
||||
s += "\n; DS HASH UNDERSTOOD: " + o.String()
|
||||
case *EDNS0_N3U:
|
||||
s += "\n; NSEC3 HASH UNDERSTOOD: " + o.String()
|
||||
case *EDNS0_LOCAL:
|
||||
s += "\n; LOCAL OPT: " + o.String()
|
||||
case *EDNS0_PADDING:
|
||||
s += "\n; PADDING: " + o.String()
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (rr *OPT) len() int {
|
||||
l := rr.Hdr.len()
|
||||
for i := 0; i < len(rr.Option); i++ {
|
||||
l += 4 // Account for 2-byte option code and 2-byte option length.
|
||||
lo, _ := rr.Option[i].pack()
|
||||
l += len(lo)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// return the old value -> delete SetVersion?
|
||||
|
||||
// Version returns the EDNS version used. Only zero is defined.
|
||||
func (rr *OPT) Version() uint8 {
|
||||
return uint8((rr.Hdr.Ttl & 0x00FF0000) >> 16)
|
||||
}
|
||||
|
||||
// SetVersion sets the version of EDNS. This is usually zero.
|
||||
func (rr *OPT) SetVersion(v uint8) {
|
||||
rr.Hdr.Ttl = rr.Hdr.Ttl&0xFF00FFFF | (uint32(v) << 16)
|
||||
}
|
||||
|
||||
// ExtendedRcode returns the EDNS extended RCODE field (the upper 8 bits of the TTL).
|
||||
func (rr *OPT) ExtendedRcode() int {
|
||||
return int((rr.Hdr.Ttl&0xFF000000)>>24) + 15
|
||||
}
|
||||
|
||||
// SetExtendedRcode sets the EDNS extended RCODE field.
|
||||
func (rr *OPT) SetExtendedRcode(v uint8) {
|
||||
if v < RcodeBadVers { // Smaller than 16.. Use the 4 bits you have!
|
||||
return
|
||||
}
|
||||
rr.Hdr.Ttl = rr.Hdr.Ttl&0x00FFFFFF | (uint32(v-15) << 24)
|
||||
}
|
||||
|
||||
// UDPSize returns the UDP buffer size.
|
||||
func (rr *OPT) UDPSize() uint16 {
|
||||
return rr.Hdr.Class
|
||||
}
|
||||
|
||||
// SetUDPSize sets the UDP buffer size.
|
||||
func (rr *OPT) SetUDPSize(size uint16) {
|
||||
rr.Hdr.Class = size
|
||||
}
|
||||
|
||||
// Do returns the value of the DO (DNSSEC OK) bit.
|
||||
func (rr *OPT) Do() bool {
|
||||
return rr.Hdr.Ttl&_DO == _DO
|
||||
}
|
||||
|
||||
// SetDo sets the DO (DNSSEC OK) bit.
|
||||
// If we pass an argument, set the DO bit to that value.
|
||||
// It is possible to pass 2 or more arguments. Any arguments after the 1st is silently ignored.
|
||||
func (rr *OPT) SetDo(do ...bool) {
|
||||
if len(do) == 1 {
|
||||
if do[0] {
|
||||
rr.Hdr.Ttl |= _DO
|
||||
} else {
|
||||
rr.Hdr.Ttl &^= _DO
|
||||
}
|
||||
} else {
|
||||
rr.Hdr.Ttl |= _DO
|
||||
}
|
||||
}
|
||||
|
||||
// EDNS0 defines an EDNS0 Option. An OPT RR can have multiple options appended to it.
|
||||
type EDNS0 interface {
|
||||
// Option returns the option code for the option.
|
||||
Option() uint16
|
||||
// pack returns the bytes of the option data.
|
||||
pack() ([]byte, error)
|
||||
// unpack sets the data as found in the buffer. Is also sets
|
||||
// the length of the slice as the length of the option data.
|
||||
unpack([]byte) error
|
||||
// String returns the string representation of the option.
|
||||
String() string
|
||||
}
|
||||
|
||||
// EDNS0_NSID option is used to retrieve a nameserver
|
||||
// identifier. When sending a request Nsid must be set to the empty string
|
||||
// The identifier is an opaque string encoded as hex.
|
||||
// Basic use pattern for creating an nsid option:
|
||||
//
|
||||
// o := new(dns.OPT)
|
||||
// o.Hdr.Name = "."
|
||||
// o.Hdr.Rrtype = dns.TypeOPT
|
||||
// e := new(dns.EDNS0_NSID)
|
||||
// e.Code = dns.EDNS0NSID
|
||||
// e.Nsid = "AA"
|
||||
// o.Option = append(o.Option, e)
|
||||
type EDNS0_NSID struct {
|
||||
Code uint16 // Always EDNS0NSID
|
||||
Nsid string // This string needs to be hex encoded
|
||||
}
|
||||
|
||||
func (e *EDNS0_NSID) pack() ([]byte, error) {
|
||||
h, err := hex.DecodeString(e.Nsid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// Option implements the EDNS0 interface.
|
||||
func (e *EDNS0_NSID) Option() uint16 { return EDNS0NSID } // Option returns the option code.
|
||||
func (e *EDNS0_NSID) unpack(b []byte) error { e.Nsid = hex.EncodeToString(b); return nil }
|
||||
func (e *EDNS0_NSID) String() string { return string(e.Nsid) }
|
||||
|
||||
// EDNS0_SUBNET is the subnet option that is used to give the remote nameserver
|
||||
// an idea of where the client lives. See RFC 7871. It can then give back a different
|
||||
// answer depending on the location or network topology.
|
||||
// Basic use pattern for creating an subnet option:
|
||||
//
|
||||
// o := new(dns.OPT)
|
||||
// o.Hdr.Name = "."
|
||||
// o.Hdr.Rrtype = dns.TypeOPT
|
||||
// e := new(dns.EDNS0_SUBNET)
|
||||
// e.Code = dns.EDNS0SUBNET
|
||||
// e.Family = 1 // 1 for IPv4 source address, 2 for IPv6
|
||||
// e.SourceNetmask = 32 // 32 for IPV4, 128 for IPv6
|
||||
// e.SourceScope = 0
|
||||
// e.Address = net.ParseIP("127.0.0.1").To4() // for IPv4
|
||||
// // e.Address = net.ParseIP("2001:7b8:32a::2") // for IPV6
|
||||
// o.Option = append(o.Option, e)
|
||||
//
|
||||
// This code will parse all the available bits when unpacking (up to optlen).
|
||||
// When packing it will apply SourceNetmask. If you need more advanced logic,
|
||||
// patches welcome and good luck.
|
||||
type EDNS0_SUBNET struct {
|
||||
Code uint16 // Always EDNS0SUBNET
|
||||
Family uint16 // 1 for IP, 2 for IP6
|
||||
SourceNetmask uint8
|
||||
SourceScope uint8
|
||||
Address net.IP
|
||||
}
|
||||
|
||||
// Option implements the EDNS0 interface.
|
||||
func (e *EDNS0_SUBNET) Option() uint16 { return EDNS0SUBNET }
|
||||
|
||||
func (e *EDNS0_SUBNET) pack() ([]byte, error) {
|
||||
b := make([]byte, 4)
|
||||
binary.BigEndian.PutUint16(b[0:], e.Family)
|
||||
b[2] = e.SourceNetmask
|
||||
b[3] = e.SourceScope
|
||||
switch e.Family {
|
||||
case 0:
|
||||
// "dig" sets AddressFamily to 0 if SourceNetmask is also 0
|
||||
// We might don't need to complain either
|
||||
if e.SourceNetmask != 0 {
|
||||
return nil, errors.New("dns: bad address family")
|
||||
}
|
||||
case 1:
|
||||
if e.SourceNetmask > net.IPv4len*8 {
|
||||
return nil, errors.New("dns: bad netmask")
|
||||
}
|
||||
if len(e.Address.To4()) != net.IPv4len {
|
||||
return nil, errors.New("dns: bad address")
|
||||
}
|
||||
ip := e.Address.To4().Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv4len*8))
|
||||
needLength := (e.SourceNetmask + 8 - 1) / 8 // division rounding up
|
||||
b = append(b, ip[:needLength]...)
|
||||
case 2:
|
||||
if e.SourceNetmask > net.IPv6len*8 {
|
||||
return nil, errors.New("dns: bad netmask")
|
||||
}
|
||||
if len(e.Address) != net.IPv6len {
|
||||
return nil, errors.New("dns: bad address")
|
||||
}
|
||||
ip := e.Address.Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv6len*8))
|
||||
needLength := (e.SourceNetmask + 8 - 1) / 8 // division rounding up
|
||||
b = append(b, ip[:needLength]...)
|
||||
default:
|
||||
return nil, errors.New("dns: bad address family")
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (e *EDNS0_SUBNET) unpack(b []byte) error {
|
||||
if len(b) < 4 {
|
||||
return ErrBuf
|
||||
}
|
||||
e.Family = binary.BigEndian.Uint16(b)
|
||||
e.SourceNetmask = b[2]
|
||||
e.SourceScope = b[3]
|
||||
switch e.Family {
|
||||
case 0:
|
||||
// "dig" sets AddressFamily to 0 if SourceNetmask is also 0
|
||||
// It's okay to accept such a packet
|
||||
if e.SourceNetmask != 0 {
|
||||
return errors.New("dns: bad address family")
|
||||
}
|
||||
e.Address = net.IPv4(0, 0, 0, 0)
|
||||
case 1:
|
||||
if e.SourceNetmask > net.IPv4len*8 || e.SourceScope > net.IPv4len*8 {
|
||||
return errors.New("dns: bad netmask")
|
||||
}
|
||||
addr := make([]byte, net.IPv4len)
|
||||
for i := 0; i < net.IPv4len && 4+i < len(b); i++ {
|
||||
addr[i] = b[4+i]
|
||||
}
|
||||
e.Address = net.IPv4(addr[0], addr[1], addr[2], addr[3])
|
||||
case 2:
|
||||
if e.SourceNetmask > net.IPv6len*8 || e.SourceScope > net.IPv6len*8 {
|
||||
return errors.New("dns: bad netmask")
|
||||
}
|
||||
addr := make([]byte, net.IPv6len)
|
||||
for i := 0; i < net.IPv6len && 4+i < len(b); i++ {
|
||||
addr[i] = b[4+i]
|
||||
}
|
||||
e.Address = net.IP{addr[0], addr[1], addr[2], addr[3], addr[4],
|
||||
addr[5], addr[6], addr[7], addr[8], addr[9], addr[10],
|
||||
addr[11], addr[12], addr[13], addr[14], addr[15]}
|
||||
default:
|
||||
return errors.New("dns: bad address family")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *EDNS0_SUBNET) String() (s string) {
|
||||
if e.Address == nil {
|
||||
s = "<nil>"
|
||||
} else if e.Address.To4() != nil {
|
||||
s = e.Address.String()
|
||||
} else {
|
||||
s = "[" + e.Address.String() + "]"
|
||||
}
|
||||
s += "/" + strconv.Itoa(int(e.SourceNetmask)) + "/" + strconv.Itoa(int(e.SourceScope))
|
||||
return
|
||||
}
|
||||
|
||||
// The EDNS0_COOKIE option is used to add a DNS Cookie to a message.
|
||||
//
|
||||
// o := new(dns.OPT)
|
||||
// o.Hdr.Name = "."
|
||||
// o.Hdr.Rrtype = dns.TypeOPT
|
||||
// e := new(dns.EDNS0_COOKIE)
|
||||
// e.Code = dns.EDNS0COOKIE
|
||||
// e.Cookie = "24a5ac.."
|
||||
// o.Option = append(o.Option, e)
|
||||
//
|
||||
// The Cookie field consists out of a client cookie (RFC 7873 Section 4), that is
|
||||
// always 8 bytes. It may then optionally be followed by the server cookie. The server
|
||||
// cookie is of variable length, 8 to a maximum of 32 bytes. In other words:
|
||||
//
|
||||
// cCookie := o.Cookie[:16]
|
||||
// sCookie := o.Cookie[16:]
|
||||
//
|
||||
// There is no guarantee that the Cookie string has a specific length.
|
||||
type EDNS0_COOKIE struct {
|
||||
Code uint16 // Always EDNS0COOKIE
|
||||
Cookie string // Hex-encoded cookie data
|
||||
}
|
||||
|
||||
func (e *EDNS0_COOKIE) pack() ([]byte, error) {
|
||||
h, err := hex.DecodeString(e.Cookie)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// Option implements the EDNS0 interface.
|
||||
func (e *EDNS0_COOKIE) Option() uint16 { return EDNS0COOKIE }
|
||||
func (e *EDNS0_COOKIE) unpack(b []byte) error { e.Cookie = hex.EncodeToString(b); return nil }
|
||||
func (e *EDNS0_COOKIE) String() string { return e.Cookie }
|
||||
|
||||
// The EDNS0_UL (Update Lease) (draft RFC) option is used to tell the server to set
|
||||
// an expiration on an update RR. This is helpful for clients that cannot clean
|
||||
// up after themselves. This is a draft RFC and more information can be found at
|
||||
// http://files.dns-sd.org/draft-sekar-dns-ul.txt
|
||||
//
|
||||
// o := new(dns.OPT)
|
||||
// o.Hdr.Name = "."
|
||||
// o.Hdr.Rrtype = dns.TypeOPT
|
||||
// e := new(dns.EDNS0_UL)
|
||||
// e.Code = dns.EDNS0UL
|
||||
// e.Lease = 120 // in seconds
|
||||
// o.Option = append(o.Option, e)
|
||||
type EDNS0_UL struct {
|
||||
Code uint16 // Always EDNS0UL
|
||||
Lease uint32
|
||||
}
|
||||
|
||||
// Option implements the EDNS0 interface.
|
||||
func (e *EDNS0_UL) Option() uint16 { return EDNS0UL }
|
||||
func (e *EDNS0_UL) String() string { return strconv.FormatUint(uint64(e.Lease), 10) }
|
||||
|
||||
// Copied: http://golang.org/src/pkg/net/dnsmsg.go
|
||||
func (e *EDNS0_UL) pack() ([]byte, error) {
|
||||
b := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(b, e.Lease)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (e *EDNS0_UL) unpack(b []byte) error {
|
||||
if len(b) < 4 {
|
||||
return ErrBuf
|
||||
}
|
||||
e.Lease = binary.BigEndian.Uint32(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
// EDNS0_LLQ stands for Long Lived Queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01
|
||||
// Implemented for completeness, as the EDNS0 type code is assigned.
|
||||
type EDNS0_LLQ struct {
|
||||
Code uint16 // Always EDNS0LLQ
|
||||
Version uint16
|
||||
Opcode uint16
|
||||
Error uint16
|
||||
Id uint64
|
||||
LeaseLife uint32
|
||||
}
|
||||
|
||||
// Option implements the EDNS0 interface.
|
||||
func (e *EDNS0_LLQ) Option() uint16 { return EDNS0LLQ }
|
||||
|
||||
func (e *EDNS0_LLQ) pack() ([]byte, error) {
|
||||
b := make([]byte, 18)
|
||||
binary.BigEndian.PutUint16(b[0:], e.Version)
|
||||
binary.BigEndian.PutUint16(b[2:], e.Opcode)
|
||||
binary.BigEndian.PutUint16(b[4:], e.Error)
|
||||
binary.BigEndian.PutUint64(b[6:], e.Id)
|
||||
binary.BigEndian.PutUint32(b[14:], e.LeaseLife)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (e *EDNS0_LLQ) unpack(b []byte) error {
|
||||
if len(b) < 18 {
|
||||
return ErrBuf
|
||||
}
|
||||
e.Version = binary.BigEndian.Uint16(b[0:])
|
||||
e.Opcode = binary.BigEndian.Uint16(b[2:])
|
||||
e.Error = binary.BigEndian.Uint16(b[4:])
|
||||
e.Id = binary.BigEndian.Uint64(b[6:])
|
||||
e.LeaseLife = binary.BigEndian.Uint32(b[14:])
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *EDNS0_LLQ) String() string {
|
||||
s := strconv.FormatUint(uint64(e.Version), 10) + " " + strconv.FormatUint(uint64(e.Opcode), 10) +
|
||||
" " + strconv.FormatUint(uint64(e.Error), 10) + " " + strconv.FormatUint(uint64(e.Id), 10) +
|
||||
" " + strconv.FormatUint(uint64(e.LeaseLife), 10)
|
||||
return s
|
||||
}
|
||||
|
||||
// EDNS0_DUA implements the EDNS0 "DNSSEC Algorithm Understood" option. See RFC 6975.
|
||||
type EDNS0_DAU struct {
|
||||
Code uint16 // Always EDNS0DAU
|
||||
AlgCode []uint8
|
||||
}
|
||||
|
||||
// Option implements the EDNS0 interface.
|
||||
func (e *EDNS0_DAU) Option() uint16 { return EDNS0DAU }
|
||||
func (e *EDNS0_DAU) pack() ([]byte, error) { return e.AlgCode, nil }
|
||||
func (e *EDNS0_DAU) unpack(b []byte) error { e.AlgCode = b; return nil }
|
||||
|
||||
func (e *EDNS0_DAU) String() string {
|
||||
s := ""
|
||||
for i := 0; i < len(e.AlgCode); i++ {
|
||||
if a, ok := AlgorithmToString[e.AlgCode[i]]; ok {
|
||||
s += " " + a
|
||||
} else {
|
||||
s += " " + strconv.Itoa(int(e.AlgCode[i]))
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// EDNS0_DHU implements the EDNS0 "DS Hash Understood" option. See RFC 6975.
|
||||
type EDNS0_DHU struct {
|
||||
Code uint16 // Always EDNS0DHU
|
||||
AlgCode []uint8
|
||||
}
|
||||
|
||||
// Option implements the EDNS0 interface.
|
||||
func (e *EDNS0_DHU) Option() uint16 { return EDNS0DHU }
|
||||
func (e *EDNS0_DHU) pack() ([]byte, error) { return e.AlgCode, nil }
|
||||
func (e *EDNS0_DHU) unpack(b []byte) error { e.AlgCode = b; return nil }
|
||||
|
||||
func (e *EDNS0_DHU) String() string {
|
||||
s := ""
|
||||
for i := 0; i < len(e.AlgCode); i++ {
|
||||
if a, ok := HashToString[e.AlgCode[i]]; ok {
|
||||
s += " " + a
|
||||
} else {
|
||||
s += " " + strconv.Itoa(int(e.AlgCode[i]))
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// EDNS0_N3U implements the EDNS0 "NSEC3 Hash Understood" option. See RFC 6975.
|
||||
type EDNS0_N3U struct {
|
||||
Code uint16 // Always EDNS0N3U
|
||||
AlgCode []uint8
|
||||
}
|
||||
|
||||
// Option implements the EDNS0 interface.
|
||||
func (e *EDNS0_N3U) Option() uint16 { return EDNS0N3U }
|
||||
func (e *EDNS0_N3U) pack() ([]byte, error) { return e.AlgCode, nil }
|
||||
func (e *EDNS0_N3U) unpack(b []byte) error { e.AlgCode = b; return nil }
|
||||
|
||||
func (e *EDNS0_N3U) String() string {
|
||||
// Re-use the hash map
|
||||
s := ""
|
||||
for i := 0; i < len(e.AlgCode); i++ {
|
||||
if a, ok := HashToString[e.AlgCode[i]]; ok {
|
||||
s += " " + a
|
||||
} else {
|
||||
s += " " + strconv.Itoa(int(e.AlgCode[i]))
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// EDNS0_EXPIRE implementes the EDNS0 option as described in RFC 7314.
|
||||
type EDNS0_EXPIRE struct {
|
||||
Code uint16 // Always EDNS0EXPIRE
|
||||
Expire uint32
|
||||
}
|
||||
|
||||
// Option implements the EDNS0 interface.
|
||||
func (e *EDNS0_EXPIRE) Option() uint16 { return EDNS0EXPIRE }
|
||||
func (e *EDNS0_EXPIRE) String() string { return strconv.FormatUint(uint64(e.Expire), 10) }
|
||||
|
||||
func (e *EDNS0_EXPIRE) pack() ([]byte, error) {
|
||||
b := make([]byte, 4)
|
||||
b[0] = byte(e.Expire >> 24)
|
||||
b[1] = byte(e.Expire >> 16)
|
||||
b[2] = byte(e.Expire >> 8)
|
||||
b[3] = byte(e.Expire)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (e *EDNS0_EXPIRE) unpack(b []byte) error {
|
||||
if len(b) < 4 {
|
||||
return ErrBuf
|
||||
}
|
||||
e.Expire = binary.BigEndian.Uint32(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
// The EDNS0_LOCAL option is used for local/experimental purposes. The option
|
||||
// code is recommended to be within the range [EDNS0LOCALSTART, EDNS0LOCALEND]
|
||||
// (RFC6891), although any unassigned code can actually be used. The content of
|
||||
// the option is made available in Data, unaltered.
|
||||
// Basic use pattern for creating a local option:
|
||||
//
|
||||
// o := new(dns.OPT)
|
||||
// o.Hdr.Name = "."
|
||||
// o.Hdr.Rrtype = dns.TypeOPT
|
||||
// e := new(dns.EDNS0_LOCAL)
|
||||
// e.Code = dns.EDNS0LOCALSTART
|
||||
// e.Data = []byte{72, 82, 74}
|
||||
// o.Option = append(o.Option, e)
|
||||
type EDNS0_LOCAL struct {
|
||||
Code uint16
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// Option implements the EDNS0 interface.
|
||||
func (e *EDNS0_LOCAL) Option() uint16 { return e.Code }
|
||||
func (e *EDNS0_LOCAL) String() string {
|
||||
return strconv.FormatInt(int64(e.Code), 10) + ":0x" + hex.EncodeToString(e.Data)
|
||||
}
|
||||
|
||||
func (e *EDNS0_LOCAL) pack() ([]byte, error) {
|
||||
b := make([]byte, len(e.Data))
|
||||
copied := copy(b, e.Data)
|
||||
if copied != len(e.Data) {
|
||||
return nil, ErrBuf
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (e *EDNS0_LOCAL) unpack(b []byte) error {
|
||||
e.Data = make([]byte, len(b))
|
||||
copied := copy(e.Data, b)
|
||||
if copied != len(b) {
|
||||
return ErrBuf
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EDNS0_TCP_KEEPALIVE is an EDNS0 option that instructs the server to keep
|
||||
// the TCP connection alive. See RFC 7828.
|
||||
type EDNS0_TCP_KEEPALIVE struct {
|
||||
Code uint16 // Always EDNSTCPKEEPALIVE
|
||||
Length uint16 // the value 0 if the TIMEOUT is omitted, the value 2 if it is present;
|
||||
Timeout uint16 // an idle timeout value for the TCP connection, specified in units of 100 milliseconds, encoded in network byte order.
|
||||
}
|
||||
|
||||
// Option implements the EDNS0 interface.
|
||||
func (e *EDNS0_TCP_KEEPALIVE) Option() uint16 { return EDNS0TCPKEEPALIVE }
|
||||
|
||||
func (e *EDNS0_TCP_KEEPALIVE) pack() ([]byte, error) {
|
||||
if e.Timeout != 0 && e.Length != 2 {
|
||||
return nil, errors.New("dns: timeout specified but length is not 2")
|
||||
}
|
||||
if e.Timeout == 0 && e.Length != 0 {
|
||||
return nil, errors.New("dns: timeout not specified but length is not 0")
|
||||
}
|
||||
b := make([]byte, 4+e.Length)
|
||||
binary.BigEndian.PutUint16(b[0:], e.Code)
|
||||
binary.BigEndian.PutUint16(b[2:], e.Length)
|
||||
if e.Length == 2 {
|
||||
binary.BigEndian.PutUint16(b[4:], e.Timeout)
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (e *EDNS0_TCP_KEEPALIVE) unpack(b []byte) error {
|
||||
if len(b) < 4 {
|
||||
return ErrBuf
|
||||
}
|
||||
e.Length = binary.BigEndian.Uint16(b[2:4])
|
||||
if e.Length != 0 && e.Length != 2 {
|
||||
return errors.New("dns: length mismatch, want 0/2 but got " + strconv.FormatUint(uint64(e.Length), 10))
|
||||
}
|
||||
if e.Length == 2 {
|
||||
if len(b) < 6 {
|
||||
return ErrBuf
|
||||
}
|
||||
e.Timeout = binary.BigEndian.Uint16(b[4:6])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *EDNS0_TCP_KEEPALIVE) String() (s string) {
|
||||
s = "use tcp keep-alive"
|
||||
if e.Length == 0 {
|
||||
s += ", timeout omitted"
|
||||
} else {
|
||||
s += fmt.Sprintf(", timeout %dms", e.Timeout*100)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EDNS0_PADDING option is used to add padding to a request/response. The default
|
||||
// value of padding SHOULD be 0x0 but other values MAY be used, for instance if
|
||||
// compression is applied before encryption which may break signatures.
|
||||
type EDNS0_PADDING struct {
|
||||
Padding []byte
|
||||
}
|
||||
|
||||
// Option implements the EDNS0 interface.
|
||||
func (e *EDNS0_PADDING) Option() uint16 { return EDNS0PADDING }
|
||||
func (e *EDNS0_PADDING) pack() ([]byte, error) { return e.Padding, nil }
|
||||
func (e *EDNS0_PADDING) unpack(b []byte) error { e.Padding = b; return nil }
|
||||
func (e *EDNS0_PADDING) String() string { return fmt.Sprintf("%0X", e.Padding) }
|
||||
68
vendor/github.com/miekg/dns/edns_test.go
generated
vendored
Normal file
68
vendor/github.com/miekg/dns/edns_test.go
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
package dns
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestOPTTtl(t *testing.T) {
|
||||
e := &OPT{}
|
||||
e.Hdr.Name = "."
|
||||
e.Hdr.Rrtype = TypeOPT
|
||||
|
||||
// verify the default setting of DO=0
|
||||
if e.Do() {
|
||||
t.Errorf("DO bit should be zero")
|
||||
}
|
||||
|
||||
// There are 6 possible invocations of SetDo():
|
||||
//
|
||||
// 1. Starting with DO=0, using SetDo()
|
||||
// 2. Starting with DO=0, using SetDo(true)
|
||||
// 3. Starting with DO=0, using SetDo(false)
|
||||
// 4. Starting with DO=1, using SetDo()
|
||||
// 5. Starting with DO=1, using SetDo(true)
|
||||
// 6. Starting with DO=1, using SetDo(false)
|
||||
|
||||
// verify that invoking SetDo() sets DO=1 (TEST #1)
|
||||
e.SetDo()
|
||||
if !e.Do() {
|
||||
t.Errorf("DO bit should be non-zero")
|
||||
}
|
||||
// verify that using SetDo(true) works when DO=1 (TEST #5)
|
||||
e.SetDo(true)
|
||||
if !e.Do() {
|
||||
t.Errorf("DO bit should still be non-zero")
|
||||
}
|
||||
// verify that we can use SetDo(false) to set DO=0 (TEST #6)
|
||||
e.SetDo(false)
|
||||
if e.Do() {
|
||||
t.Errorf("DO bit should be zero")
|
||||
}
|
||||
// verify that if we call SetDo(false) when DO=0 that it is unchanged (TEST #3)
|
||||
e.SetDo(false)
|
||||
if e.Do() {
|
||||
t.Errorf("DO bit should still be zero")
|
||||
}
|
||||
// verify that using SetDo(true) works for DO=0 (TEST #2)
|
||||
e.SetDo(true)
|
||||
if !e.Do() {
|
||||
t.Errorf("DO bit should be non-zero")
|
||||
}
|
||||
// verify that using SetDo() works for DO=1 (TEST #4)
|
||||
e.SetDo()
|
||||
if !e.Do() {
|
||||
t.Errorf("DO bit should be non-zero")
|
||||
}
|
||||
|
||||
if e.Version() != 0 {
|
||||
t.Errorf("version should be non-zero")
|
||||
}
|
||||
|
||||
e.SetVersion(42)
|
||||
if e.Version() != 42 {
|
||||
t.Errorf("set 42, expected %d, got %d", 42, e.Version())
|
||||
}
|
||||
|
||||
e.SetExtendedRcode(42)
|
||||
if e.ExtendedRcode() != 42 {
|
||||
t.Errorf("set 42, expected %d, got %d", 42, e.ExtendedRcode())
|
||||
}
|
||||
}
|
||||
146
vendor/github.com/miekg/dns/example_test.go
generated
vendored
Normal file
146
vendor/github.com/miekg/dns/example_test.go
generated
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
package dns_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// Retrieve the MX records for miek.nl.
|
||||
func ExampleMX() {
|
||||
config, _ := dns.ClientConfigFromFile("/etc/resolv.conf")
|
||||
c := new(dns.Client)
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion("miek.nl.", dns.TypeMX)
|
||||
m.RecursionDesired = true
|
||||
r, _, err := c.Exchange(m, config.Servers[0]+":"+config.Port)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
return
|
||||
}
|
||||
for _, a := range r.Answer {
|
||||
if mx, ok := a.(*dns.MX); ok {
|
||||
fmt.Printf("%s\n", mx.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve the DNSKEY records of a zone and convert them
|
||||
// to DS records for SHA1, SHA256 and SHA384.
|
||||
func ExampleDS() {
|
||||
config, _ := dns.ClientConfigFromFile("/etc/resolv.conf")
|
||||
c := new(dns.Client)
|
||||
m := new(dns.Msg)
|
||||
zone := "miek.nl"
|
||||
m.SetQuestion(dns.Fqdn(zone), dns.TypeDNSKEY)
|
||||
m.SetEdns0(4096, true)
|
||||
r, _, err := c.Exchange(m, config.Servers[0]+":"+config.Port)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
return
|
||||
}
|
||||
for _, k := range r.Answer {
|
||||
if key, ok := k.(*dns.DNSKEY); ok {
|
||||
for _, alg := range []uint8{dns.SHA1, dns.SHA256, dns.SHA384} {
|
||||
fmt.Printf("%s; %d\n", key.ToDS(alg).String(), key.Flags)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const TypeAPAIR = 0x0F99
|
||||
|
||||
type APAIR struct {
|
||||
addr [2]net.IP
|
||||
}
|
||||
|
||||
func NewAPAIR() dns.PrivateRdata { return new(APAIR) }
|
||||
|
||||
func (rd *APAIR) String() string { return rd.addr[0].String() + " " + rd.addr[1].String() }
|
||||
func (rd *APAIR) Parse(txt []string) error {
|
||||
if len(txt) != 2 {
|
||||
return errors.New("two addresses required for APAIR")
|
||||
}
|
||||
for i, s := range txt {
|
||||
ip := net.ParseIP(s)
|
||||
if ip == nil {
|
||||
return errors.New("invalid IP in APAIR text representation")
|
||||
}
|
||||
rd.addr[i] = ip
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rd *APAIR) Pack(buf []byte) (int, error) {
|
||||
b := append([]byte(rd.addr[0]), []byte(rd.addr[1])...)
|
||||
n := copy(buf, b)
|
||||
if n != len(b) {
|
||||
return n, dns.ErrBuf
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (rd *APAIR) Unpack(buf []byte) (int, error) {
|
||||
ln := net.IPv4len * 2
|
||||
if len(buf) != ln {
|
||||
return 0, errors.New("invalid length of APAIR rdata")
|
||||
}
|
||||
cp := make([]byte, ln)
|
||||
copy(cp, buf) // clone bytes to use them in IPs
|
||||
|
||||
rd.addr[0] = net.IP(cp[:3])
|
||||
rd.addr[1] = net.IP(cp[4:])
|
||||
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
func (rd *APAIR) Copy(dest dns.PrivateRdata) error {
|
||||
cp := make([]byte, rd.Len())
|
||||
_, err := rd.Pack(cp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d := dest.(*APAIR)
|
||||
d.addr[0] = net.IP(cp[:3])
|
||||
d.addr[1] = net.IP(cp[4:])
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rd *APAIR) Len() int {
|
||||
return net.IPv4len * 2
|
||||
}
|
||||
|
||||
func ExamplePrivateHandle() {
|
||||
dns.PrivateHandle("APAIR", TypeAPAIR, NewAPAIR)
|
||||
defer dns.PrivateHandleRemove(TypeAPAIR)
|
||||
|
||||
rr, err := dns.NewRR("miek.nl. APAIR (1.2.3.4 1.2.3.5)")
|
||||
if err != nil {
|
||||
log.Fatal("could not parse APAIR record: ", err)
|
||||
}
|
||||
fmt.Println(rr)
|
||||
// Output: miek.nl. 3600 IN APAIR 1.2.3.4 1.2.3.5
|
||||
|
||||
m := new(dns.Msg)
|
||||
m.Id = 12345
|
||||
m.SetQuestion("miek.nl.", TypeAPAIR)
|
||||
m.Answer = append(m.Answer, rr)
|
||||
|
||||
fmt.Println(m)
|
||||
// ;; opcode: QUERY, status: NOERROR, id: 12345
|
||||
// ;; flags: rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
|
||||
//
|
||||
// ;; QUESTION SECTION:
|
||||
// ;miek.nl. IN APAIR
|
||||
//
|
||||
// ;; ANSWER SECTION:
|
||||
// miek.nl. 3600 IN APAIR 1.2.3.4 1.2.3.5
|
||||
}
|
||||
87
vendor/github.com/miekg/dns/format.go
generated
vendored
Normal file
87
vendor/github.com/miekg/dns/format.go
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"net"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// NumField returns the number of rdata fields r has.
|
||||
func NumField(r RR) int {
|
||||
return reflect.ValueOf(r).Elem().NumField() - 1 // Remove RR_Header
|
||||
}
|
||||
|
||||
// Field returns the rdata field i as a string. Fields are indexed starting from 1.
|
||||
// RR types that holds slice data, for instance the NSEC type bitmap will return a single
|
||||
// string where the types are concatenated using a space.
|
||||
// Accessing non existing fields will cause a panic.
|
||||
func Field(r RR, i int) string {
|
||||
if i == 0 {
|
||||
return ""
|
||||
}
|
||||
d := reflect.ValueOf(r).Elem().Field(i)
|
||||
switch k := d.Kind(); k {
|
||||
case reflect.String:
|
||||
return d.String()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return strconv.FormatInt(d.Int(), 10)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return strconv.FormatUint(d.Uint(), 10)
|
||||
case reflect.Slice:
|
||||
switch reflect.ValueOf(r).Elem().Type().Field(i).Tag {
|
||||
case `dns:"a"`:
|
||||
// TODO(miek): Hmm store this as 16 bytes
|
||||
if d.Len() < net.IPv6len {
|
||||
return net.IPv4(byte(d.Index(0).Uint()),
|
||||
byte(d.Index(1).Uint()),
|
||||
byte(d.Index(2).Uint()),
|
||||
byte(d.Index(3).Uint())).String()
|
||||
}
|
||||
return net.IPv4(byte(d.Index(12).Uint()),
|
||||
byte(d.Index(13).Uint()),
|
||||
byte(d.Index(14).Uint()),
|
||||
byte(d.Index(15).Uint())).String()
|
||||
case `dns:"aaaa"`:
|
||||
return net.IP{
|
||||
byte(d.Index(0).Uint()),
|
||||
byte(d.Index(1).Uint()),
|
||||
byte(d.Index(2).Uint()),
|
||||
byte(d.Index(3).Uint()),
|
||||
byte(d.Index(4).Uint()),
|
||||
byte(d.Index(5).Uint()),
|
||||
byte(d.Index(6).Uint()),
|
||||
byte(d.Index(7).Uint()),
|
||||
byte(d.Index(8).Uint()),
|
||||
byte(d.Index(9).Uint()),
|
||||
byte(d.Index(10).Uint()),
|
||||
byte(d.Index(11).Uint()),
|
||||
byte(d.Index(12).Uint()),
|
||||
byte(d.Index(13).Uint()),
|
||||
byte(d.Index(14).Uint()),
|
||||
byte(d.Index(15).Uint()),
|
||||
}.String()
|
||||
case `dns:"nsec"`:
|
||||
if d.Len() == 0 {
|
||||
return ""
|
||||
}
|
||||
s := Type(d.Index(0).Uint()).String()
|
||||
for i := 1; i < d.Len(); i++ {
|
||||
s += " " + Type(d.Index(i).Uint()).String()
|
||||
}
|
||||
return s
|
||||
default:
|
||||
// if it does not have a tag its a string slice
|
||||
fallthrough
|
||||
case `dns:"txt"`:
|
||||
if d.Len() == 0 {
|
||||
return ""
|
||||
}
|
||||
s := d.Index(0).String()
|
||||
for i := 1; i < d.Len(); i++ {
|
||||
s += " " + d.Index(i).String()
|
||||
}
|
||||
return s
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
23
vendor/github.com/miekg/dns/fuzz.go
generated
vendored
Normal file
23
vendor/github.com/miekg/dns/fuzz.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
// +build fuzz
|
||||
|
||||
package dns
|
||||
|
||||
func Fuzz(data []byte) int {
|
||||
msg := new(Msg)
|
||||
|
||||
if err := msg.Unpack(data); err != nil {
|
||||
return 0
|
||||
}
|
||||
if _, err := msg.Pack(); err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func FuzzNewRR(data []byte) int {
|
||||
if _, err := NewRR(string(data)); err != nil {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
159
vendor/github.com/miekg/dns/generate.go
generated
vendored
Normal file
159
vendor/github.com/miekg/dns/generate.go
generated
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Parse the $GENERATE statement as used in BIND9 zones.
|
||||
// See http://www.zytrax.com/books/dns/ch8/generate.html for instance.
|
||||
// We are called after '$GENERATE '. After which we expect:
|
||||
// * the range (12-24/2)
|
||||
// * lhs (ownername)
|
||||
// * [[ttl][class]]
|
||||
// * type
|
||||
// * rhs (rdata)
|
||||
// But we are lazy here, only the range is parsed *all* occurrences
|
||||
// of $ after that are interpreted.
|
||||
// Any error are returned as a string value, the empty string signals
|
||||
// "no error".
|
||||
func generate(l lex, c chan lex, t chan *Token, o string) string {
|
||||
step := 1
|
||||
if i := strings.IndexAny(l.token, "/"); i != -1 {
|
||||
if i+1 == len(l.token) {
|
||||
return "bad step in $GENERATE range"
|
||||
}
|
||||
if s, err := strconv.Atoi(l.token[i+1:]); err == nil {
|
||||
if s < 0 {
|
||||
return "bad step in $GENERATE range"
|
||||
}
|
||||
step = s
|
||||
} else {
|
||||
return "bad step in $GENERATE range"
|
||||
}
|
||||
l.token = l.token[:i]
|
||||
}
|
||||
sx := strings.SplitN(l.token, "-", 2)
|
||||
if len(sx) != 2 {
|
||||
return "bad start-stop in $GENERATE range"
|
||||
}
|
||||
start, err := strconv.Atoi(sx[0])
|
||||
if err != nil {
|
||||
return "bad start in $GENERATE range"
|
||||
}
|
||||
end, err := strconv.Atoi(sx[1])
|
||||
if err != nil {
|
||||
return "bad stop in $GENERATE range"
|
||||
}
|
||||
if end < 0 || start < 0 || end < start {
|
||||
return "bad range in $GENERATE range"
|
||||
}
|
||||
|
||||
<-c // _BLANK
|
||||
// Create a complete new string, which we then parse again.
|
||||
s := ""
|
||||
BuildRR:
|
||||
l = <-c
|
||||
if l.value != zNewline && l.value != zEOF {
|
||||
s += l.token
|
||||
goto BuildRR
|
||||
}
|
||||
for i := start; i <= end; i += step {
|
||||
var (
|
||||
escape bool
|
||||
dom bytes.Buffer
|
||||
mod string
|
||||
err error
|
||||
offset int
|
||||
)
|
||||
|
||||
for j := 0; j < len(s); j++ { // No 'range' because we need to jump around
|
||||
switch s[j] {
|
||||
case '\\':
|
||||
if escape {
|
||||
dom.WriteByte('\\')
|
||||
escape = false
|
||||
continue
|
||||
}
|
||||
escape = true
|
||||
case '$':
|
||||
mod = "%d"
|
||||
offset = 0
|
||||
if escape {
|
||||
dom.WriteByte('$')
|
||||
escape = false
|
||||
continue
|
||||
}
|
||||
escape = false
|
||||
if j+1 >= len(s) { // End of the string
|
||||
dom.WriteString(fmt.Sprintf(mod, i+offset))
|
||||
continue
|
||||
} else {
|
||||
if s[j+1] == '$' {
|
||||
dom.WriteByte('$')
|
||||
j++
|
||||
continue
|
||||
}
|
||||
}
|
||||
// Search for { and }
|
||||
if s[j+1] == '{' { // Modifier block
|
||||
sep := strings.Index(s[j+2:], "}")
|
||||
if sep == -1 {
|
||||
return "bad modifier in $GENERATE"
|
||||
}
|
||||
mod, offset, err = modToPrintf(s[j+2 : j+2+sep])
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
j += 2 + sep // Jump to it
|
||||
}
|
||||
dom.WriteString(fmt.Sprintf(mod, i+offset))
|
||||
default:
|
||||
if escape { // Pretty useless here
|
||||
escape = false
|
||||
continue
|
||||
}
|
||||
dom.WriteByte(s[j])
|
||||
}
|
||||
}
|
||||
// Re-parse the RR and send it on the current channel t
|
||||
rx, err := NewRR("$ORIGIN " + o + "\n" + dom.String())
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
t <- &Token{RR: rx}
|
||||
// Its more efficient to first built the rrlist and then parse it in
|
||||
// one go! But is this a problem?
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Convert a $GENERATE modifier 0,0,d to something Printf can deal with.
|
||||
func modToPrintf(s string) (string, int, error) {
|
||||
xs := strings.SplitN(s, ",", 3)
|
||||
if len(xs) != 3 {
|
||||
return "", 0, errors.New("bad modifier in $GENERATE")
|
||||
}
|
||||
// xs[0] is offset, xs[1] is width, xs[2] is base
|
||||
if xs[2] != "o" && xs[2] != "d" && xs[2] != "x" && xs[2] != "X" {
|
||||
return "", 0, errors.New("bad base in $GENERATE")
|
||||
}
|
||||
offset, err := strconv.Atoi(xs[0])
|
||||
if err != nil || offset > 255 {
|
||||
return "", 0, errors.New("bad offset in $GENERATE")
|
||||
}
|
||||
width, err := strconv.Atoi(xs[1])
|
||||
if err != nil || width > 255 {
|
||||
return "", offset, errors.New("bad width in $GENERATE")
|
||||
}
|
||||
switch {
|
||||
case width < 0:
|
||||
return "", offset, errors.New("bad width in $GENERATE")
|
||||
case width == 0:
|
||||
return "%" + xs[1] + xs[2], offset, nil
|
||||
}
|
||||
return "%0" + xs[1] + xs[2], offset, nil
|
||||
}
|
||||
62
vendor/github.com/miekg/dns/issue_test.go
generated
vendored
Normal file
62
vendor/github.com/miekg/dns/issue_test.go
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
package dns
|
||||
|
||||
// Tests that solve that an specific issue.
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTCPRtt(t *testing.T) {
|
||||
m := new(Msg)
|
||||
m.RecursionDesired = true
|
||||
m.SetQuestion("example.org.", TypeA)
|
||||
|
||||
c := &Client{}
|
||||
for _, proto := range []string{"udp", "tcp"} {
|
||||
c.Net = proto
|
||||
_, rtt, err := c.Exchange(m, "8.8.4.4:53")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if rtt == 0 {
|
||||
t.Fatalf("expecting non zero rtt %s, got zero", c.Net)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNSEC3MissingSalt(t *testing.T) {
|
||||
rr := testRR("ji6neoaepv8b5o6k4ev33abha8ht9fgc.example. NSEC3 1 1 12 aabbccdd K8UDEMVP1J2F7EG6JEBPS17VP3N8I58H")
|
||||
m := new(Msg)
|
||||
m.Answer = []RR{rr}
|
||||
mb, err := m.Pack()
|
||||
if err != nil {
|
||||
t.Fatalf("expected to pack message. err: %s", err)
|
||||
}
|
||||
if err := m.Unpack(mb); err != nil {
|
||||
t.Fatalf("expected to unpack message. missing salt? err: %s", err)
|
||||
}
|
||||
in := rr.(*NSEC3).Salt
|
||||
out := m.Answer[0].(*NSEC3).Salt
|
||||
if in != out {
|
||||
t.Fatalf("expected salts to match. packed: `%s`. returned: `%s`", in, out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNSEC3MixedNextDomain(t *testing.T) {
|
||||
rr := testRR("ji6neoaepv8b5o6k4ev33abha8ht9fgc.example. NSEC3 1 1 12 - k8udemvp1j2f7eg6jebps17vp3n8i58h")
|
||||
m := new(Msg)
|
||||
m.Answer = []RR{rr}
|
||||
mb, err := m.Pack()
|
||||
if err != nil {
|
||||
t.Fatalf("expected to pack message. err: %s", err)
|
||||
}
|
||||
if err := m.Unpack(mb); err != nil {
|
||||
t.Fatalf("expected to unpack message. err: %s", err)
|
||||
}
|
||||
in := strings.ToUpper(rr.(*NSEC3).NextDomain)
|
||||
out := m.Answer[0].(*NSEC3).NextDomain
|
||||
if in != out {
|
||||
t.Fatalf("expected round trip to produce NextDomain `%s`, instead `%s`", in, out)
|
||||
}
|
||||
}
|
||||
191
vendor/github.com/miekg/dns/labels.go
generated
vendored
Normal file
191
vendor/github.com/miekg/dns/labels.go
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
package dns
|
||||
|
||||
// Holds a bunch of helper functions for dealing with labels.
|
||||
|
||||
// SplitDomainName splits a name string into it's labels.
|
||||
// www.miek.nl. returns []string{"www", "miek", "nl"}
|
||||
// .www.miek.nl. returns []string{"", "www", "miek", "nl"},
|
||||
// The root label (.) returns nil. Note that using
|
||||
// strings.Split(s) will work in most cases, but does not handle
|
||||
// escaped dots (\.) for instance.
|
||||
// s must be a syntactically valid domain name, see IsDomainName.
|
||||
func SplitDomainName(s string) (labels []string) {
|
||||
if len(s) == 0 {
|
||||
return nil
|
||||
}
|
||||
fqdnEnd := 0 // offset of the final '.' or the length of the name
|
||||
idx := Split(s)
|
||||
begin := 0
|
||||
if s[len(s)-1] == '.' {
|
||||
fqdnEnd = len(s) - 1
|
||||
} else {
|
||||
fqdnEnd = len(s)
|
||||
}
|
||||
|
||||
switch len(idx) {
|
||||
case 0:
|
||||
return nil
|
||||
case 1:
|
||||
// no-op
|
||||
default:
|
||||
end := 0
|
||||
for i := 1; i < len(idx); i++ {
|
||||
end = idx[i]
|
||||
labels = append(labels, s[begin:end-1])
|
||||
begin = end
|
||||
}
|
||||
}
|
||||
|
||||
labels = append(labels, s[begin:fqdnEnd])
|
||||
return labels
|
||||
}
|
||||
|
||||
// CompareDomainName compares the names s1 and s2 and
|
||||
// returns how many labels they have in common starting from the *right*.
|
||||
// The comparison stops at the first inequality. The names are downcased
|
||||
// before the comparison.
|
||||
//
|
||||
// www.miek.nl. and miek.nl. have two labels in common: miek and nl
|
||||
// www.miek.nl. and www.bla.nl. have one label in common: nl
|
||||
//
|
||||
// s1 and s2 must be syntactically valid domain names.
|
||||
func CompareDomainName(s1, s2 string) (n int) {
|
||||
// the first check: root label
|
||||
if s1 == "." || s2 == "." {
|
||||
return 0
|
||||
}
|
||||
|
||||
l1 := Split(s1)
|
||||
l2 := Split(s2)
|
||||
|
||||
j1 := len(l1) - 1 // end
|
||||
i1 := len(l1) - 2 // start
|
||||
j2 := len(l2) - 1
|
||||
i2 := len(l2) - 2
|
||||
// the second check can be done here: last/only label
|
||||
// before we fall through into the for-loop below
|
||||
if equal(s1[l1[j1]:], s2[l2[j2]:]) {
|
||||
n++
|
||||
} else {
|
||||
return
|
||||
}
|
||||
for {
|
||||
if i1 < 0 || i2 < 0 {
|
||||
break
|
||||
}
|
||||
if equal(s1[l1[i1]:l1[j1]], s2[l2[i2]:l2[j2]]) {
|
||||
n++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
j1--
|
||||
i1--
|
||||
j2--
|
||||
i2--
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CountLabel counts the the number of labels in the string s.
|
||||
// s must be a syntactically valid domain name.
|
||||
func CountLabel(s string) (labels int) {
|
||||
if s == "." {
|
||||
return
|
||||
}
|
||||
off := 0
|
||||
end := false
|
||||
for {
|
||||
off, end = NextLabel(s, off)
|
||||
labels++
|
||||
if end {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Split splits a name s into its label indexes.
|
||||
// www.miek.nl. returns []int{0, 4, 9}, www.miek.nl also returns []int{0, 4, 9}.
|
||||
// The root name (.) returns nil. Also see SplitDomainName.
|
||||
// s must be a syntactically valid domain name.
|
||||
func Split(s string) []int {
|
||||
if s == "." {
|
||||
return nil
|
||||
}
|
||||
idx := make([]int, 1, 3)
|
||||
off := 0
|
||||
end := false
|
||||
|
||||
for {
|
||||
off, end = NextLabel(s, off)
|
||||
if end {
|
||||
return idx
|
||||
}
|
||||
idx = append(idx, off)
|
||||
}
|
||||
}
|
||||
|
||||
// NextLabel returns the index of the start of the next label in the
|
||||
// string s starting at offset.
|
||||
// The bool end is true when the end of the string has been reached.
|
||||
// Also see PrevLabel.
|
||||
func NextLabel(s string, offset int) (i int, end bool) {
|
||||
quote := false
|
||||
for i = offset; i < len(s)-1; i++ {
|
||||
switch s[i] {
|
||||
case '\\':
|
||||
quote = !quote
|
||||
default:
|
||||
quote = false
|
||||
case '.':
|
||||
if quote {
|
||||
quote = !quote
|
||||
continue
|
||||
}
|
||||
return i + 1, false
|
||||
}
|
||||
}
|
||||
return i + 1, true
|
||||
}
|
||||
|
||||
// PrevLabel returns the index of the label when starting from the right and
|
||||
// jumping n labels to the left.
|
||||
// The bool start is true when the start of the string has been overshot.
|
||||
// Also see NextLabel.
|
||||
func PrevLabel(s string, n int) (i int, start bool) {
|
||||
if n == 0 {
|
||||
return len(s), false
|
||||
}
|
||||
lab := Split(s)
|
||||
if lab == nil {
|
||||
return 0, true
|
||||
}
|
||||
if n > len(lab) {
|
||||
return 0, true
|
||||
}
|
||||
return lab[len(lab)-n], false
|
||||
}
|
||||
|
||||
// equal compares a and b while ignoring case. It returns true when equal otherwise false.
|
||||
func equal(a, b string) bool {
|
||||
// might be lifted into API function.
|
||||
la := len(a)
|
||||
lb := len(b)
|
||||
if la != lb {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := la - 1; i >= 0; i-- {
|
||||
ai := a[i]
|
||||
bi := b[i]
|
||||
if ai >= 'A' && ai <= 'Z' {
|
||||
ai |= ('a' - 'A')
|
||||
}
|
||||
if bi >= 'A' && bi <= 'Z' {
|
||||
bi |= ('a' - 'A')
|
||||
}
|
||||
if ai != bi {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
201
vendor/github.com/miekg/dns/labels_test.go
generated
vendored
Normal file
201
vendor/github.com/miekg/dns/labels_test.go
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
package dns
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestCompareDomainName(t *testing.T) {
|
||||
s1 := "www.miek.nl."
|
||||
s2 := "miek.nl."
|
||||
s3 := "www.bla.nl."
|
||||
s4 := "nl.www.bla."
|
||||
s5 := "nl."
|
||||
s6 := "miek.nl."
|
||||
|
||||
if CompareDomainName(s1, s2) != 2 {
|
||||
t.Errorf("%s with %s should be %d", s1, s2, 2)
|
||||
}
|
||||
if CompareDomainName(s1, s3) != 1 {
|
||||
t.Errorf("%s with %s should be %d", s1, s3, 1)
|
||||
}
|
||||
if CompareDomainName(s3, s4) != 0 {
|
||||
t.Errorf("%s with %s should be %d", s3, s4, 0)
|
||||
}
|
||||
// Non qualified tests
|
||||
if CompareDomainName(s1, s5) != 1 {
|
||||
t.Errorf("%s with %s should be %d", s1, s5, 1)
|
||||
}
|
||||
if CompareDomainName(s1, s6) != 2 {
|
||||
t.Errorf("%s with %s should be %d", s1, s5, 2)
|
||||
}
|
||||
|
||||
if CompareDomainName(s1, ".") != 0 {
|
||||
t.Errorf("%s with %s should be %d", s1, s5, 0)
|
||||
}
|
||||
if CompareDomainName(".", ".") != 0 {
|
||||
t.Errorf("%s with %s should be %d", ".", ".", 0)
|
||||
}
|
||||
if CompareDomainName("test.com.", "TEST.COM.") != 2 {
|
||||
t.Errorf("test.com. and TEST.COM. should be an exact match")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplit(t *testing.T) {
|
||||
splitter := map[string]int{
|
||||
"www.miek.nl.": 3,
|
||||
"www.miek.nl": 3,
|
||||
"www..miek.nl": 4,
|
||||
`www\.miek.nl.`: 2,
|
||||
`www\\.miek.nl.`: 3,
|
||||
".": 0,
|
||||
"nl.": 1,
|
||||
"nl": 1,
|
||||
"com.": 1,
|
||||
".com.": 2,
|
||||
}
|
||||
for s, i := range splitter {
|
||||
if x := len(Split(s)); x != i {
|
||||
t.Errorf("labels should be %d, got %d: %s %v", i, x, s, Split(s))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplit2(t *testing.T) {
|
||||
splitter := map[string][]int{
|
||||
"www.miek.nl.": {0, 4, 9},
|
||||
"www.miek.nl": {0, 4, 9},
|
||||
"nl": {0},
|
||||
}
|
||||
for s, i := range splitter {
|
||||
x := Split(s)
|
||||
switch len(i) {
|
||||
case 1:
|
||||
if x[0] != i[0] {
|
||||
t.Errorf("labels should be %v, got %v: %s", i, x, s)
|
||||
}
|
||||
default:
|
||||
if x[0] != i[0] || x[1] != i[1] || x[2] != i[2] {
|
||||
t.Errorf("labels should be %v, got %v: %s", i, x, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrevLabel(t *testing.T) {
|
||||
type prev struct {
|
||||
string
|
||||
int
|
||||
}
|
||||
prever := map[prev]int{
|
||||
{"www.miek.nl.", 0}: 12,
|
||||
{"www.miek.nl.", 1}: 9,
|
||||
{"www.miek.nl.", 2}: 4,
|
||||
|
||||
{"www.miek.nl", 0}: 11,
|
||||
{"www.miek.nl", 1}: 9,
|
||||
{"www.miek.nl", 2}: 4,
|
||||
|
||||
{"www.miek.nl.", 5}: 0,
|
||||
{"www.miek.nl", 5}: 0,
|
||||
|
||||
{"www.miek.nl.", 3}: 0,
|
||||
{"www.miek.nl", 3}: 0,
|
||||
}
|
||||
for s, i := range prever {
|
||||
x, ok := PrevLabel(s.string, s.int)
|
||||
if i != x {
|
||||
t.Errorf("label should be %d, got %d, %t: preving %d, %s", i, x, ok, s.int, s.string)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCountLabel(t *testing.T) {
|
||||
splitter := map[string]int{
|
||||
"www.miek.nl.": 3,
|
||||
"www.miek.nl": 3,
|
||||
"nl": 1,
|
||||
".": 0,
|
||||
}
|
||||
for s, i := range splitter {
|
||||
x := CountLabel(s)
|
||||
if x != i {
|
||||
t.Errorf("CountLabel should have %d, got %d", i, x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitDomainName(t *testing.T) {
|
||||
labels := map[string][]string{
|
||||
"miek.nl": {"miek", "nl"},
|
||||
".": nil,
|
||||
"www.miek.nl.": {"www", "miek", "nl"},
|
||||
"www.miek.nl": {"www", "miek", "nl"},
|
||||
"www..miek.nl": {"www", "", "miek", "nl"},
|
||||
`www\.miek.nl`: {`www\.miek`, "nl"},
|
||||
`www\\.miek.nl`: {`www\\`, "miek", "nl"},
|
||||
".www.miek.nl.": {"", "www", "miek", "nl"},
|
||||
}
|
||||
domainLoop:
|
||||
for domain, splits := range labels {
|
||||
parts := SplitDomainName(domain)
|
||||
if len(parts) != len(splits) {
|
||||
t.Errorf("SplitDomainName returned %v for %s, expected %v", parts, domain, splits)
|
||||
continue domainLoop
|
||||
}
|
||||
for i := range parts {
|
||||
if parts[i] != splits[i] {
|
||||
t.Errorf("SplitDomainName returned %v for %s, expected %v", parts, domain, splits)
|
||||
continue domainLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsDomainName(t *testing.T) {
|
||||
type ret struct {
|
||||
ok bool
|
||||
lab int
|
||||
}
|
||||
names := map[string]*ret{
|
||||
"..": {false, 1},
|
||||
"@.": {true, 1},
|
||||
"www.example.com": {true, 3},
|
||||
"www.e%ample.com": {true, 3},
|
||||
"www.example.com.": {true, 3},
|
||||
"mi\\k.nl.": {true, 2},
|
||||
"mi\\k.nl": {true, 2},
|
||||
}
|
||||
for d, ok := range names {
|
||||
l, k := IsDomainName(d)
|
||||
if ok.ok != k || ok.lab != l {
|
||||
t.Errorf(" got %v %d for %s ", k, l, d)
|
||||
t.Errorf("have %v %d for %s ", ok.ok, ok.lab, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSplitLabels(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
Split("www.example.com.")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLenLabels(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
CountLabel("www.example.com.")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCompareDomainName(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
CompareDomainName("www.example.com.", "aa.example.com.")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIsSubDomain(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
IsSubDomain("www.example.com.", "aa.example.com.")
|
||||
IsSubDomain("example.com.", "aa.example.com.")
|
||||
IsSubDomain("miek.nl.", "aa.example.com.")
|
||||
}
|
||||
}
|
||||
72
vendor/github.com/miekg/dns/leak_test.go
generated
vendored
Normal file
72
vendor/github.com/miekg/dns/leak_test.go
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// copied from net/http/main_test.go
|
||||
|
||||
func interestingGoroutines() (gs []string) {
|
||||
buf := make([]byte, 2<<20)
|
||||
buf = buf[:runtime.Stack(buf, true)]
|
||||
for _, g := range strings.Split(string(buf), "\n\n") {
|
||||
sl := strings.SplitN(g, "\n", 2)
|
||||
if len(sl) != 2 {
|
||||
continue
|
||||
}
|
||||
stack := strings.TrimSpace(sl[1])
|
||||
if stack == "" ||
|
||||
strings.Contains(stack, "testing.(*M).before.func1") ||
|
||||
strings.Contains(stack, "os/signal.signal_recv") ||
|
||||
strings.Contains(stack, "created by net.startServer") ||
|
||||
strings.Contains(stack, "created by testing.RunTests") ||
|
||||
strings.Contains(stack, "closeWriteAndWait") ||
|
||||
strings.Contains(stack, "testing.Main(") ||
|
||||
strings.Contains(stack, "testing.(*T).Run(") ||
|
||||
strings.Contains(stack, "created by net/http.(*http2Transport).newClientConn") ||
|
||||
// These only show up with GOTRACEBACK=2; Issue 5005 (comment 28)
|
||||
strings.Contains(stack, "runtime.goexit") ||
|
||||
strings.Contains(stack, "created by runtime.gc") ||
|
||||
strings.Contains(stack, "dns.interestingGoroutines") ||
|
||||
strings.Contains(stack, "runtime.MHeap_Scavenger") {
|
||||
continue
|
||||
}
|
||||
gs = append(gs, stack)
|
||||
}
|
||||
sort.Strings(gs)
|
||||
return
|
||||
}
|
||||
|
||||
func goroutineLeaked() error {
|
||||
if testing.Short() {
|
||||
// Don't worry about goroutine leaks in -short mode or in
|
||||
// benchmark mode. Too distracting when there are false positives.
|
||||
return nil
|
||||
}
|
||||
|
||||
var stackCount map[string]int
|
||||
for i := 0; i < 5; i++ {
|
||||
n := 0
|
||||
stackCount = make(map[string]int)
|
||||
gs := interestingGoroutines()
|
||||
for _, g := range gs {
|
||||
stackCount[g]++
|
||||
n++
|
||||
}
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
// Wait for goroutines to schedule and die off:
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
for stack, count := range stackCount {
|
||||
fmt.Fprintf(os.Stderr, "%d instances of:\n%s\n", count, stack)
|
||||
}
|
||||
return fmt.Errorf("too many goroutines running after dns test(s)")
|
||||
}
|
||||
371
vendor/github.com/miekg/dns/length_test.go
generated
vendored
Normal file
371
vendor/github.com/miekg/dns/length_test.go
generated
vendored
Normal file
@@ -0,0 +1,371 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCompressLength(t *testing.T) {
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl", TypeMX)
|
||||
ul := m.Len()
|
||||
m.Compress = true
|
||||
if ul != m.Len() {
|
||||
t.Fatalf("should be equal")
|
||||
}
|
||||
}
|
||||
|
||||
// Does the predicted length match final packed length?
|
||||
func TestMsgCompressLength(t *testing.T) {
|
||||
makeMsg := func(question string, ans, ns, e []RR) *Msg {
|
||||
msg := new(Msg)
|
||||
msg.SetQuestion(Fqdn(question), TypeANY)
|
||||
msg.Answer = append(msg.Answer, ans...)
|
||||
msg.Ns = append(msg.Ns, ns...)
|
||||
msg.Extra = append(msg.Extra, e...)
|
||||
msg.Compress = true
|
||||
return msg
|
||||
}
|
||||
|
||||
name1 := "12345678901234567890123456789012345.12345678.123."
|
||||
rrA := testRR(name1 + " 3600 IN A 192.0.2.1")
|
||||
rrMx := testRR(name1 + " 3600 IN MX 10 " + name1)
|
||||
tests := []*Msg{
|
||||
makeMsg(name1, []RR{rrA}, nil, nil),
|
||||
makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)}
|
||||
|
||||
for _, msg := range tests {
|
||||
predicted := msg.Len()
|
||||
buf, err := msg.Pack()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if predicted < len(buf) {
|
||||
t.Errorf("predicted compressed length is wrong: predicted %s (len=%d) %d, actual %d",
|
||||
msg.Question[0].Name, len(msg.Answer), predicted, len(buf))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgLength(t *testing.T) {
|
||||
makeMsg := func(question string, ans, ns, e []RR) *Msg {
|
||||
msg := new(Msg)
|
||||
msg.Compress = true
|
||||
msg.SetQuestion(Fqdn(question), TypeANY)
|
||||
msg.Answer = append(msg.Answer, ans...)
|
||||
msg.Ns = append(msg.Ns, ns...)
|
||||
msg.Extra = append(msg.Extra, e...)
|
||||
return msg
|
||||
}
|
||||
|
||||
name1 := "12345678901234567890123456789012345.12345678.123."
|
||||
rrA := testRR(name1 + " 3600 IN A 192.0.2.1")
|
||||
rrMx := testRR(name1 + " 3600 IN MX 10 " + name1)
|
||||
tests := []*Msg{
|
||||
makeMsg(name1, []RR{rrA}, nil, nil),
|
||||
makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)}
|
||||
|
||||
for _, msg := range tests {
|
||||
predicted := msg.Len()
|
||||
buf, err := msg.Pack()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if predicted < len(buf) {
|
||||
t.Errorf("predicted length is wrong: predicted %s (len=%d), actual %d",
|
||||
msg.Question[0].Name, predicted, len(buf))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompressionLenHelper(t *testing.T) {
|
||||
c := make(map[string]int)
|
||||
compressionLenHelper(c, "example.com", 12)
|
||||
if c["example.com"] != 12 {
|
||||
t.Errorf("bad %d", c["example.com"])
|
||||
}
|
||||
if c["com"] != 20 {
|
||||
t.Errorf("bad %d", c["com"])
|
||||
}
|
||||
|
||||
// Test boundaries
|
||||
c = make(map[string]int)
|
||||
// foo label starts at 16379
|
||||
// com label starts at 16384
|
||||
compressionLenHelper(c, "foo.com", 16379)
|
||||
if c["foo.com"] != 16379 {
|
||||
t.Errorf("bad %d", c["foo.com"])
|
||||
}
|
||||
// com label is accessible
|
||||
if c["com"] != 16383 {
|
||||
t.Errorf("bad %d", c["com"])
|
||||
}
|
||||
|
||||
c = make(map[string]int)
|
||||
// foo label starts at 16379
|
||||
// com label starts at 16385 => outside range
|
||||
compressionLenHelper(c, "foo.com", 16380)
|
||||
if c["foo.com"] != 16380 {
|
||||
t.Errorf("bad %d", c["foo.com"])
|
||||
}
|
||||
// com label is NOT accessible
|
||||
if c["com"] != 0 {
|
||||
t.Errorf("bad %d", c["com"])
|
||||
}
|
||||
|
||||
c = make(map[string]int)
|
||||
compressionLenHelper(c, "example.com", 16375)
|
||||
if c["example.com"] != 16375 {
|
||||
t.Errorf("bad %d", c["example.com"])
|
||||
}
|
||||
// com starts AFTER 16384
|
||||
if c["com"] != 16383 {
|
||||
t.Errorf("bad %d", c["com"])
|
||||
}
|
||||
|
||||
c = make(map[string]int)
|
||||
compressionLenHelper(c, "example.com", 16376)
|
||||
if c["example.com"] != 16376 {
|
||||
t.Errorf("bad %d", c["example.com"])
|
||||
}
|
||||
// com starts AFTER 16384
|
||||
if c["com"] != 0 {
|
||||
t.Errorf("bad %d", c["com"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompressionLenSearch(t *testing.T) {
|
||||
c := make(map[string]int)
|
||||
compressed, ok, fullSize := compressionLenSearch(c, "a.b.org.")
|
||||
if compressed != 0 || ok || fullSize != 14 {
|
||||
panic(fmt.Errorf("Failed: compressed:=%d, ok:=%v, fullSize:=%d", compressed, ok, fullSize))
|
||||
}
|
||||
c["org."] = 3
|
||||
compressed, ok, fullSize = compressionLenSearch(c, "a.b.org.")
|
||||
if compressed != 4 || !ok || fullSize != 8 {
|
||||
panic(fmt.Errorf("Failed: compressed:=%d, ok:=%v, fullSize:=%d", compressed, ok, fullSize))
|
||||
}
|
||||
c["b.org."] = 5
|
||||
compressed, ok, fullSize = compressionLenSearch(c, "a.b.org.")
|
||||
if compressed != 6 || !ok || fullSize != 4 {
|
||||
panic(fmt.Errorf("Failed: compressed:=%d, ok:=%v, fullSize:=%d", compressed, ok, fullSize))
|
||||
}
|
||||
// Not found long compression
|
||||
c["x.b.org."] = 5
|
||||
compressed, ok, fullSize = compressionLenSearch(c, "a.b.org.")
|
||||
if compressed != 6 || !ok || fullSize != 4 {
|
||||
panic(fmt.Errorf("Failed: compressed:=%d, ok:=%v, fullSize:=%d", compressed, ok, fullSize))
|
||||
}
|
||||
// Found long compression
|
||||
c["a.b.org."] = 5
|
||||
compressed, ok, fullSize = compressionLenSearch(c, "a.b.org.")
|
||||
if compressed != 8 || !ok || fullSize != 0 {
|
||||
panic(fmt.Errorf("Failed: compressed:=%d, ok:=%v, fullSize:=%d", compressed, ok, fullSize))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgLength2(t *testing.T) {
|
||||
// Serialized replies
|
||||
var testMessages = []string{
|
||||
// google.com. IN A?
|
||||
"064e81800001000b0004000506676f6f676c6503636f6d0000010001c00c00010001000000050004adc22986c00c00010001000000050004adc22987c00c00010001000000050004adc22988c00c00010001000000050004adc22989c00c00010001000000050004adc2298ec00c00010001000000050004adc22980c00c00010001000000050004adc22981c00c00010001000000050004adc22982c00c00010001000000050004adc22983c00c00010001000000050004adc22984c00c00010001000000050004adc22985c00c00020001000000050006036e7331c00cc00c00020001000000050006036e7332c00cc00c00020001000000050006036e7333c00cc00c00020001000000050006036e7334c00cc0d800010001000000050004d8ef200ac0ea00010001000000050004d8ef220ac0fc00010001000000050004d8ef240ac10e00010001000000050004d8ef260a0000290500000000050000",
|
||||
// amazon.com. IN A? (reply has no EDNS0 record)
|
||||
// TODO(miek): this one is off-by-one, need to find out why
|
||||
//"6de1818000010004000a000806616d617a6f6e03636f6d0000010001c00c000100010000000500044815c2d4c00c000100010000000500044815d7e8c00c00010001000000050004b02062a6c00c00010001000000050004cdfbf236c00c000200010000000500140570646e733408756c747261646e73036f726700c00c000200010000000500150570646e733508756c747261646e7304696e666f00c00c000200010000000500160570646e733608756c747261646e7302636f02756b00c00c00020001000000050014036e7331037033310664796e656374036e657400c00c00020001000000050006036e7332c0cfc00c00020001000000050006036e7333c0cfc00c00020001000000050006036e7334c0cfc00c000200010000000500110570646e733108756c747261646e73c0dac00c000200010000000500080570646e7332c127c00c000200010000000500080570646e7333c06ec0cb00010001000000050004d04e461fc0eb00010001000000050004cc0dfa1fc0fd00010001000000050004d04e471fc10f00010001000000050004cc0dfb1fc12100010001000000050004cc4a6c01c121001c000100000005001020010502f3ff00000000000000000001c13e00010001000000050004cc4a6d01c13e001c0001000000050010261000a1101400000000000000000001",
|
||||
// yahoo.com. IN A?
|
||||
"fc2d81800001000300070008057961686f6f03636f6d0000010001c00c00010001000000050004628afd6dc00c00010001000000050004628bb718c00c00010001000000050004cebe242dc00c00020001000000050006036e7336c00cc00c00020001000000050006036e7338c00cc00c00020001000000050006036e7331c00cc00c00020001000000050006036e7332c00cc00c00020001000000050006036e7333c00cc00c00020001000000050006036e7334c00cc00c00020001000000050006036e7335c00cc07b0001000100000005000444b48310c08d00010001000000050004448eff10c09f00010001000000050004cb54dd35c0b100010001000000050004628a0b9dc0c30001000100000005000477a0f77cc05700010001000000050004ca2bdfaac06900010001000000050004caa568160000290500000000050000",
|
||||
// microsoft.com. IN A?
|
||||
"f4368180000100020005000b096d6963726f736f667403636f6d0000010001c00c0001000100000005000440040b25c00c0001000100000005000441373ac9c00c0002000100000005000e036e7331046d736674036e657400c00c00020001000000050006036e7332c04fc00c00020001000000050006036e7333c04fc00c00020001000000050006036e7334c04fc00c00020001000000050006036e7335c04fc04b000100010000000500044137253ec04b001c00010000000500102a010111200500000000000000010001c0650001000100000005000440043badc065001c00010000000500102a010111200600060000000000010001c07700010001000000050004d5c7b435c077001c00010000000500102a010111202000000000000000010001c08900010001000000050004cf2e4bfec089001c00010000000500102404f800200300000000000000010001c09b000100010000000500044137e28cc09b001c00010000000500102a010111200f000100000000000100010000290500000000050000",
|
||||
// google.com. IN MX?
|
||||
"724b8180000100050004000b06676f6f676c6503636f6d00000f0001c00c000f000100000005000c000a056173706d78016cc00cc00c000f0001000000050009001404616c7431c02ac00c000f0001000000050009001e04616c7432c02ac00c000f0001000000050009002804616c7433c02ac00c000f0001000000050009003204616c7434c02ac00c00020001000000050006036e7332c00cc00c00020001000000050006036e7333c00cc00c00020001000000050006036e7334c00cc00c00020001000000050006036e7331c00cc02a00010001000000050004adc2421bc02a001c00010000000500102a00145040080c01000000000000001bc04200010001000000050004adc2461bc05700010001000000050004adc2451bc06c000100010000000500044a7d8f1bc081000100010000000500044a7d191bc0ca00010001000000050004d8ef200ac09400010001000000050004d8ef220ac0a600010001000000050004d8ef240ac0b800010001000000050004d8ef260a0000290500000000050000",
|
||||
// reddit.com. IN A?
|
||||
"12b98180000100080000000c0672656464697403636f6d0000020001c00c0002000100000005000f046175733204616b616d036e657400c00c000200010000000500070475736534c02dc00c000200010000000500070475737733c02dc00c000200010000000500070475737735c02dc00c00020001000000050008056173696131c02dc00c00020001000000050008056173696139c02dc00c00020001000000050008056e73312d31c02dc00c0002000100000005000a076e73312d313935c02dc02800010001000000050004c30a242ec04300010001000000050004451f1d39c05600010001000000050004451f3bc7c0690001000100000005000460073240c07c000100010000000500046007fb81c090000100010000000500047c283484c090001c00010000000500102a0226f0006700000000000000000064c0a400010001000000050004c16c5b01c0a4001c000100000005001026001401000200000000000000000001c0b800010001000000050004c16c5bc3c0b8001c0001000000050010260014010002000000000000000000c30000290500000000050000",
|
||||
}
|
||||
|
||||
for i, hexData := range testMessages {
|
||||
// we won't fail the decoding of the hex
|
||||
input, _ := hex.DecodeString(hexData)
|
||||
|
||||
m := new(Msg)
|
||||
m.Unpack(input)
|
||||
m.Compress = true
|
||||
lenComp := m.Len()
|
||||
b, _ := m.Pack()
|
||||
pacComp := len(b)
|
||||
m.Compress = false
|
||||
lenUnComp := m.Len()
|
||||
b, _ = m.Pack()
|
||||
pacUnComp := len(b)
|
||||
if pacComp+1 != lenComp {
|
||||
t.Errorf("msg.Len(compressed)=%d actual=%d for test %d", lenComp, pacComp, i)
|
||||
}
|
||||
if pacUnComp+1 != lenUnComp {
|
||||
t.Errorf("msg.Len(uncompressed)=%d actual=%d for test %d", lenUnComp, pacUnComp, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgLengthCompressionMalformed(t *testing.T) {
|
||||
// SOA with empty hostmaster, which is illegal
|
||||
soa := &SOA{Hdr: RR_Header{Name: ".", Rrtype: TypeSOA, Class: ClassINET, Ttl: 12345},
|
||||
Ns: ".",
|
||||
Mbox: "",
|
||||
Serial: 0,
|
||||
Refresh: 28800,
|
||||
Retry: 7200,
|
||||
Expire: 604800,
|
||||
Minttl: 60}
|
||||
m := new(Msg)
|
||||
m.Compress = true
|
||||
m.Ns = []RR{soa}
|
||||
m.Len() // Should not crash.
|
||||
}
|
||||
|
||||
func TestMsgCompressLength2(t *testing.T) {
|
||||
msg := new(Msg)
|
||||
msg.Compress = true
|
||||
msg.SetQuestion(Fqdn("bliep."), TypeANY)
|
||||
msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "blaat.", Rrtype: 0x21, Class: 0x1, Ttl: 0x3c}, Port: 0x4c57, Target: "foo.bar."})
|
||||
msg.Extra = append(msg.Extra, &A{Hdr: RR_Header{Name: "foo.bar.", Rrtype: 0x1, Class: 0x1, Ttl: 0x3c}, A: net.IP{0xac, 0x11, 0x0, 0x3}})
|
||||
predicted := msg.Len()
|
||||
buf, err := msg.Pack()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if predicted != len(buf) {
|
||||
t.Errorf("predicted compressed length is wrong: predicted %s (len=%d) %d, actual %d",
|
||||
msg.Question[0].Name, len(msg.Answer), predicted, len(buf))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgCompressLengthLargeRecords(t *testing.T) {
|
||||
msg := new(Msg)
|
||||
msg.Compress = true
|
||||
msg.SetQuestion("my.service.acme.", TypeSRV)
|
||||
j := 1
|
||||
for i := 0; i < 250; i++ {
|
||||
target := fmt.Sprintf("host-redis-1-%d.test.acme.com.node.dc1.consul.", i)
|
||||
msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "redis.service.consul.", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target})
|
||||
msg.Extra = append(msg.Extra, &CNAME{Hdr: RR_Header{Name: target, Class: 1, Rrtype: TypeCNAME, Ttl: 0x3c}, Target: fmt.Sprintf("fx.168.%d.%d.", j, i)})
|
||||
}
|
||||
predicted := msg.Len()
|
||||
buf, err := msg.Pack()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if predicted != len(buf) {
|
||||
t.Fatalf("predicted compressed length is wrong: predicted %s (len=%d) %d, actual %d", msg.Question[0].Name, len(msg.Answer), predicted, len(buf))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompareCompressionMapsForANY(t *testing.T) {
|
||||
msg := new(Msg)
|
||||
msg.Compress = true
|
||||
msg.SetQuestion("a.service.acme.", TypeANY)
|
||||
// Be sure to have more than 14bits
|
||||
for i := 0; i < 2000; i++ {
|
||||
target := fmt.Sprintf("host.app-%d.x%d.test.acme.", i%250, i)
|
||||
msg.Answer = append(msg.Answer, &AAAA{Hdr: RR_Header{Name: target, Rrtype: TypeAAAA, Class: ClassINET, Ttl: 0x3c}, AAAA: net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, byte(i / 255), byte(i % 255)}})
|
||||
msg.Answer = append(msg.Answer, &A{Hdr: RR_Header{Name: target, Rrtype: TypeA, Class: ClassINET, Ttl: 0x3c}, A: net.IP{127, 0, byte(i / 255), byte(i % 255)}})
|
||||
if msg.Len() > 16384 {
|
||||
break
|
||||
}
|
||||
}
|
||||
for labelSize := 0; labelSize < 63; labelSize++ {
|
||||
msg.SetQuestion(fmt.Sprintf("a%s.service.acme.", strings.Repeat("x", labelSize)), TypeANY)
|
||||
|
||||
compressionFake := make(map[string]int)
|
||||
lenFake := compressedLenWithCompressionMap(msg, compressionFake)
|
||||
|
||||
compressionReal := make(map[string]int)
|
||||
buf, err := msg.packBufferWithCompressionMap(nil, compressionReal)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if lenFake != len(buf) {
|
||||
t.Fatalf("padding= %d ; Predicted len := %d != real:= %d", labelSize, lenFake, len(buf))
|
||||
}
|
||||
if !reflect.DeepEqual(compressionFake, compressionReal) {
|
||||
t.Fatalf("padding= %d ; Fake Compression Map != Real Compression Map\n*** Real:= %v\n\n***Fake:= %v", labelSize, compressionReal, compressionFake)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompareCompressionMapsForSRV(t *testing.T) {
|
||||
msg := new(Msg)
|
||||
msg.Compress = true
|
||||
msg.SetQuestion("a.service.acme.", TypeSRV)
|
||||
// Be sure to have more than 14bits
|
||||
for i := 0; i < 2000; i++ {
|
||||
target := fmt.Sprintf("host.app-%d.x%d.test.acme.", i%250, i)
|
||||
msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "redis.service.consul.", Class: ClassINET, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target})
|
||||
msg.Extra = append(msg.Extra, &A{Hdr: RR_Header{Name: target, Rrtype: TypeA, Class: ClassINET, Ttl: 0x3c}, A: net.IP{127, 0, byte(i / 255), byte(i % 255)}})
|
||||
if msg.Len() > 16384 {
|
||||
break
|
||||
}
|
||||
}
|
||||
for labelSize := 0; labelSize < 63; labelSize++ {
|
||||
msg.SetQuestion(fmt.Sprintf("a%s.service.acme.", strings.Repeat("x", labelSize)), TypeAAAA)
|
||||
|
||||
compressionFake := make(map[string]int)
|
||||
lenFake := compressedLenWithCompressionMap(msg, compressionFake)
|
||||
|
||||
compressionReal := make(map[string]int)
|
||||
buf, err := msg.packBufferWithCompressionMap(nil, compressionReal)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if lenFake != len(buf) {
|
||||
t.Fatalf("padding= %d ; Predicted len := %d != real:= %d", labelSize, lenFake, len(buf))
|
||||
}
|
||||
if !reflect.DeepEqual(compressionFake, compressionReal) {
|
||||
t.Fatalf("padding= %d ; Fake Compression Map != Real Compression Map\n*** Real:= %v\n\n***Fake:= %v", labelSize, compressionReal, compressionFake)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgCompressLengthLargeRecordsWithPaddingPermutation(t *testing.T) {
|
||||
msg := new(Msg)
|
||||
msg.Compress = true
|
||||
msg.SetQuestion("my.service.acme.", TypeSRV)
|
||||
|
||||
for i := 0; i < 250; i++ {
|
||||
target := fmt.Sprintf("host-redis-x-%d.test.acme.com.node.dc1.consul.", i)
|
||||
msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "redis.service.consul.", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target})
|
||||
msg.Extra = append(msg.Extra, &CNAME{Hdr: RR_Header{Name: target, Class: ClassINET, Rrtype: TypeCNAME, Ttl: 0x3c}, Target: fmt.Sprintf("fx.168.x.%d.", i)})
|
||||
}
|
||||
for labelSize := 1; labelSize < 63; labelSize++ {
|
||||
msg.SetQuestion(fmt.Sprintf("my.%s.service.acme.", strings.Repeat("x", labelSize)), TypeSRV)
|
||||
predicted := msg.Len()
|
||||
buf, err := msg.Pack()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if predicted != len(buf) {
|
||||
t.Fatalf("padding= %d ; predicted compressed length is wrong: predicted %s (len=%d) %d, actual %d", labelSize, msg.Question[0].Name, len(msg.Answer), predicted, len(buf))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgCompressLengthLargeRecordsAllValues(t *testing.T) {
|
||||
msg := new(Msg)
|
||||
msg.Compress = true
|
||||
msg.SetQuestion("redis.service.consul.", TypeSRV)
|
||||
for i := 0; i < 900; i++ {
|
||||
target := fmt.Sprintf("host-redis-%d-%d.test.acme.com.node.dc1.consul.", i/256, i%256)
|
||||
msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "redis.service.consul.", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target})
|
||||
msg.Extra = append(msg.Extra, &CNAME{Hdr: RR_Header{Name: target, Class: ClassINET, Rrtype: TypeCNAME, Ttl: 0x3c}, Target: fmt.Sprintf("fx.168.%d.%d.", i/256, i%256)})
|
||||
predicted := msg.Len()
|
||||
buf, err := msg.Pack()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if predicted != len(buf) {
|
||||
t.Fatalf("predicted compressed length is wrong for %d records: predicted %s (len=%d) %d, actual %d", i, msg.Question[0].Name, len(msg.Answer), predicted, len(buf))
|
||||
}
|
||||
}
|
||||
}
|
||||
1206
vendor/github.com/miekg/dns/msg.go
generated
vendored
Normal file
1206
vendor/github.com/miekg/dns/msg.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
348
vendor/github.com/miekg/dns/msg_generate.go
generated
vendored
Normal file
348
vendor/github.com/miekg/dns/msg_generate.go
generated
vendored
Normal file
@@ -0,0 +1,348 @@
|
||||
//+build ignore
|
||||
|
||||
// msg_generate.go is meant to run with go generate. It will use
|
||||
// go/{importer,types} to track down all the RR struct types. Then for each type
|
||||
// it will generate pack/unpack methods based on the struct tags. The generated source is
|
||||
// written to zmsg.go, and is meant to be checked into git.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"go/importer"
|
||||
"go/types"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var packageHdr = `
|
||||
// Code generated by "go run msg_generate.go"; DO NOT EDIT.
|
||||
|
||||
package dns
|
||||
|
||||
`
|
||||
|
||||
// getTypeStruct will take a type and the package scope, and return the
|
||||
// (innermost) struct if the type is considered a RR type (currently defined as
|
||||
// those structs beginning with a RR_Header, could be redefined as implementing
|
||||
// the RR interface). The bool return value indicates if embedded structs were
|
||||
// resolved.
|
||||
func getTypeStruct(t types.Type, scope *types.Scope) (*types.Struct, bool) {
|
||||
st, ok := t.Underlying().(*types.Struct)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
if st.Field(0).Type() == scope.Lookup("RR_Header").Type() {
|
||||
return st, false
|
||||
}
|
||||
if st.Field(0).Anonymous() {
|
||||
st, _ := getTypeStruct(st.Field(0).Type(), scope)
|
||||
return st, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Import and type-check the package
|
||||
pkg, err := importer.Default().Import("github.com/miekg/dns")
|
||||
fatalIfErr(err)
|
||||
scope := pkg.Scope()
|
||||
|
||||
// Collect actual types (*X)
|
||||
var namedTypes []string
|
||||
for _, name := range scope.Names() {
|
||||
o := scope.Lookup(name)
|
||||
if o == nil || !o.Exported() {
|
||||
continue
|
||||
}
|
||||
if st, _ := getTypeStruct(o.Type(), scope); st == nil {
|
||||
continue
|
||||
}
|
||||
if name == "PrivateRR" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if corresponding TypeX exists
|
||||
if scope.Lookup("Type"+o.Name()) == nil && o.Name() != "RFC3597" {
|
||||
log.Fatalf("Constant Type%s does not exist.", o.Name())
|
||||
}
|
||||
|
||||
namedTypes = append(namedTypes, o.Name())
|
||||
}
|
||||
|
||||
b := &bytes.Buffer{}
|
||||
b.WriteString(packageHdr)
|
||||
|
||||
fmt.Fprint(b, "// pack*() functions\n\n")
|
||||
for _, name := range namedTypes {
|
||||
o := scope.Lookup(name)
|
||||
st, _ := getTypeStruct(o.Type(), scope)
|
||||
|
||||
fmt.Fprintf(b, "func (rr *%s) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) {\n", name)
|
||||
fmt.Fprint(b, `off, err := rr.Hdr.pack(msg, off, compression, compress)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
headerEnd := off
|
||||
`)
|
||||
for i := 1; i < st.NumFields(); i++ {
|
||||
o := func(s string) {
|
||||
fmt.Fprintf(b, s, st.Field(i).Name())
|
||||
fmt.Fprint(b, `if err != nil {
|
||||
return off, err
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
||||
if _, ok := st.Field(i).Type().(*types.Slice); ok {
|
||||
switch st.Tag(i) {
|
||||
case `dns:"-"`: // ignored
|
||||
case `dns:"txt"`:
|
||||
o("off, err = packStringTxt(rr.%s, msg, off)\n")
|
||||
case `dns:"opt"`:
|
||||
o("off, err = packDataOpt(rr.%s, msg, off)\n")
|
||||
case `dns:"nsec"`:
|
||||
o("off, err = packDataNsec(rr.%s, msg, off)\n")
|
||||
case `dns:"domain-name"`:
|
||||
o("off, err = packDataDomainNames(rr.%s, msg, off, compression, compress)\n")
|
||||
default:
|
||||
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
switch {
|
||||
case st.Tag(i) == `dns:"-"`: // ignored
|
||||
case st.Tag(i) == `dns:"cdomain-name"`:
|
||||
o("off, err = PackDomainName(rr.%s, msg, off, compression, compress)\n")
|
||||
case st.Tag(i) == `dns:"domain-name"`:
|
||||
o("off, err = PackDomainName(rr.%s, msg, off, compression, false)\n")
|
||||
case st.Tag(i) == `dns:"a"`:
|
||||
o("off, err = packDataA(rr.%s, msg, off)\n")
|
||||
case st.Tag(i) == `dns:"aaaa"`:
|
||||
o("off, err = packDataAAAA(rr.%s, msg, off)\n")
|
||||
case st.Tag(i) == `dns:"uint48"`:
|
||||
o("off, err = packUint48(rr.%s, msg, off)\n")
|
||||
case st.Tag(i) == `dns:"txt"`:
|
||||
o("off, err = packString(rr.%s, msg, off)\n")
|
||||
|
||||
case strings.HasPrefix(st.Tag(i), `dns:"size-base32`): // size-base32 can be packed just like base32
|
||||
fallthrough
|
||||
case st.Tag(i) == `dns:"base32"`:
|
||||
o("off, err = packStringBase32(rr.%s, msg, off)\n")
|
||||
|
||||
case strings.HasPrefix(st.Tag(i), `dns:"size-base64`): // size-base64 can be packed just like base64
|
||||
fallthrough
|
||||
case st.Tag(i) == `dns:"base64"`:
|
||||
o("off, err = packStringBase64(rr.%s, msg, off)\n")
|
||||
|
||||
case strings.HasPrefix(st.Tag(i), `dns:"size-hex:SaltLength`):
|
||||
// directly write instead of using o() so we get the error check in the correct place
|
||||
field := st.Field(i).Name()
|
||||
fmt.Fprintf(b, `// Only pack salt if value is not "-", i.e. empty
|
||||
if rr.%s != "-" {
|
||||
off, err = packStringHex(rr.%s, msg, off)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
}
|
||||
`, field, field)
|
||||
continue
|
||||
case strings.HasPrefix(st.Tag(i), `dns:"size-hex`): // size-hex can be packed just like hex
|
||||
fallthrough
|
||||
case st.Tag(i) == `dns:"hex"`:
|
||||
o("off, err = packStringHex(rr.%s, msg, off)\n")
|
||||
|
||||
case st.Tag(i) == `dns:"octet"`:
|
||||
o("off, err = packStringOctet(rr.%s, msg, off)\n")
|
||||
case st.Tag(i) == "":
|
||||
switch st.Field(i).Type().(*types.Basic).Kind() {
|
||||
case types.Uint8:
|
||||
o("off, err = packUint8(rr.%s, msg, off)\n")
|
||||
case types.Uint16:
|
||||
o("off, err = packUint16(rr.%s, msg, off)\n")
|
||||
case types.Uint32:
|
||||
o("off, err = packUint32(rr.%s, msg, off)\n")
|
||||
case types.Uint64:
|
||||
o("off, err = packUint64(rr.%s, msg, off)\n")
|
||||
case types.String:
|
||||
o("off, err = packString(rr.%s, msg, off)\n")
|
||||
default:
|
||||
log.Fatalln(name, st.Field(i).Name())
|
||||
}
|
||||
default:
|
||||
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
|
||||
}
|
||||
}
|
||||
// We have packed everything, only now we know the rdlength of this RR
|
||||
fmt.Fprintln(b, "rr.Header().Rdlength = uint16(off-headerEnd)")
|
||||
fmt.Fprintln(b, "return off, nil }\n")
|
||||
}
|
||||
|
||||
fmt.Fprint(b, "// unpack*() functions\n\n")
|
||||
for _, name := range namedTypes {
|
||||
o := scope.Lookup(name)
|
||||
st, _ := getTypeStruct(o.Type(), scope)
|
||||
|
||||
fmt.Fprintf(b, "func unpack%s(h RR_Header, msg []byte, off int) (RR, int, error) {\n", name)
|
||||
fmt.Fprintf(b, "rr := new(%s)\n", name)
|
||||
fmt.Fprint(b, "rr.Hdr = h\n")
|
||||
fmt.Fprint(b, `if noRdata(h) {
|
||||
return rr, off, nil
|
||||
}
|
||||
var err error
|
||||
rdStart := off
|
||||
_ = rdStart
|
||||
|
||||
`)
|
||||
for i := 1; i < st.NumFields(); i++ {
|
||||
o := func(s string) {
|
||||
fmt.Fprintf(b, s, st.Field(i).Name())
|
||||
fmt.Fprint(b, `if err != nil {
|
||||
return rr, off, err
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
||||
// size-* are special, because they reference a struct member we should use for the length.
|
||||
if strings.HasPrefix(st.Tag(i), `dns:"size-`) {
|
||||
structMember := structMember(st.Tag(i))
|
||||
structTag := structTag(st.Tag(i))
|
||||
switch structTag {
|
||||
case "hex":
|
||||
fmt.Fprintf(b, "rr.%s, off, err = unpackStringHex(msg, off, off + int(rr.%s))\n", st.Field(i).Name(), structMember)
|
||||
case "base32":
|
||||
fmt.Fprintf(b, "rr.%s, off, err = unpackStringBase32(msg, off, off + int(rr.%s))\n", st.Field(i).Name(), structMember)
|
||||
case "base64":
|
||||
fmt.Fprintf(b, "rr.%s, off, err = unpackStringBase64(msg, off, off + int(rr.%s))\n", st.Field(i).Name(), structMember)
|
||||
default:
|
||||
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
|
||||
}
|
||||
fmt.Fprint(b, `if err != nil {
|
||||
return rr, off, err
|
||||
}
|
||||
`)
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := st.Field(i).Type().(*types.Slice); ok {
|
||||
switch st.Tag(i) {
|
||||
case `dns:"-"`: // ignored
|
||||
case `dns:"txt"`:
|
||||
o("rr.%s, off, err = unpackStringTxt(msg, off)\n")
|
||||
case `dns:"opt"`:
|
||||
o("rr.%s, off, err = unpackDataOpt(msg, off)\n")
|
||||
case `dns:"nsec"`:
|
||||
o("rr.%s, off, err = unpackDataNsec(msg, off)\n")
|
||||
case `dns:"domain-name"`:
|
||||
o("rr.%s, off, err = unpackDataDomainNames(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
|
||||
default:
|
||||
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
switch st.Tag(i) {
|
||||
case `dns:"-"`: // ignored
|
||||
case `dns:"cdomain-name"`:
|
||||
fallthrough
|
||||
case `dns:"domain-name"`:
|
||||
o("rr.%s, off, err = UnpackDomainName(msg, off)\n")
|
||||
case `dns:"a"`:
|
||||
o("rr.%s, off, err = unpackDataA(msg, off)\n")
|
||||
case `dns:"aaaa"`:
|
||||
o("rr.%s, off, err = unpackDataAAAA(msg, off)\n")
|
||||
case `dns:"uint48"`:
|
||||
o("rr.%s, off, err = unpackUint48(msg, off)\n")
|
||||
case `dns:"txt"`:
|
||||
o("rr.%s, off, err = unpackString(msg, off)\n")
|
||||
case `dns:"base32"`:
|
||||
o("rr.%s, off, err = unpackStringBase32(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
|
||||
case `dns:"base64"`:
|
||||
o("rr.%s, off, err = unpackStringBase64(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
|
||||
case `dns:"hex"`:
|
||||
o("rr.%s, off, err = unpackStringHex(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
|
||||
case `dns:"octet"`:
|
||||
o("rr.%s, off, err = unpackStringOctet(msg, off)\n")
|
||||
case "":
|
||||
switch st.Field(i).Type().(*types.Basic).Kind() {
|
||||
case types.Uint8:
|
||||
o("rr.%s, off, err = unpackUint8(msg, off)\n")
|
||||
case types.Uint16:
|
||||
o("rr.%s, off, err = unpackUint16(msg, off)\n")
|
||||
case types.Uint32:
|
||||
o("rr.%s, off, err = unpackUint32(msg, off)\n")
|
||||
case types.Uint64:
|
||||
o("rr.%s, off, err = unpackUint64(msg, off)\n")
|
||||
case types.String:
|
||||
o("rr.%s, off, err = unpackString(msg, off)\n")
|
||||
default:
|
||||
log.Fatalln(name, st.Field(i).Name())
|
||||
}
|
||||
default:
|
||||
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
|
||||
}
|
||||
// If we've hit len(msg) we return without error.
|
||||
if i < st.NumFields()-1 {
|
||||
fmt.Fprintf(b, `if off == len(msg) {
|
||||
return rr, off, nil
|
||||
}
|
||||
`)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(b, "return rr, off, err }\n\n")
|
||||
}
|
||||
// Generate typeToUnpack map
|
||||
fmt.Fprintln(b, "var typeToUnpack = map[uint16]func(RR_Header, []byte, int) (RR, int, error){")
|
||||
for _, name := range namedTypes {
|
||||
if name == "RFC3597" {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(b, "Type%s: unpack%s,\n", name, name)
|
||||
}
|
||||
fmt.Fprintln(b, "}\n")
|
||||
|
||||
// gofmt
|
||||
res, err := format.Source(b.Bytes())
|
||||
if err != nil {
|
||||
b.WriteTo(os.Stderr)
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// write result
|
||||
f, err := os.Create("zmsg.go")
|
||||
fatalIfErr(err)
|
||||
defer f.Close()
|
||||
f.Write(res)
|
||||
}
|
||||
|
||||
// structMember will take a tag like dns:"size-base32:SaltLength" and return the last part of this string.
|
||||
func structMember(s string) string {
|
||||
fields := strings.Split(s, ":")
|
||||
if len(fields) == 0 {
|
||||
return ""
|
||||
}
|
||||
f := fields[len(fields)-1]
|
||||
// f should have a closing "
|
||||
if len(f) > 1 {
|
||||
return f[:len(f)-1]
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// structTag will take a tag like dns:"size-base32:SaltLength" and return base32.
|
||||
func structTag(s string) string {
|
||||
fields := strings.Split(s, ":")
|
||||
if len(fields) < 2 {
|
||||
return ""
|
||||
}
|
||||
return fields[1][len("\"size-"):]
|
||||
}
|
||||
|
||||
func fatalIfErr(err error) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
641
vendor/github.com/miekg/dns/msg_helpers.go
generated
vendored
Normal file
641
vendor/github.com/miekg/dns/msg_helpers.go
generated
vendored
Normal file
@@ -0,0 +1,641 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"encoding/base32"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// helper functions called from the generated zmsg.go
|
||||
|
||||
// These function are named after the tag to help pack/unpack, if there is no tag it is the name
|
||||
// of the type they pack/unpack (string, int, etc). We prefix all with unpackData or packData, so packDataA or
|
||||
// packDataDomainName.
|
||||
|
||||
func unpackDataA(msg []byte, off int) (net.IP, int, error) {
|
||||
if off+net.IPv4len > len(msg) {
|
||||
return nil, len(msg), &Error{err: "overflow unpacking a"}
|
||||
}
|
||||
a := append(make(net.IP, 0, net.IPv4len), msg[off:off+net.IPv4len]...)
|
||||
off += net.IPv4len
|
||||
return a, off, nil
|
||||
}
|
||||
|
||||
func packDataA(a net.IP, msg []byte, off int) (int, error) {
|
||||
// It must be a slice of 4, even if it is 16, we encode only the first 4
|
||||
if off+net.IPv4len > len(msg) {
|
||||
return len(msg), &Error{err: "overflow packing a"}
|
||||
}
|
||||
switch len(a) {
|
||||
case net.IPv4len, net.IPv6len:
|
||||
copy(msg[off:], a.To4())
|
||||
off += net.IPv4len
|
||||
case 0:
|
||||
// Allowed, for dynamic updates.
|
||||
default:
|
||||
return len(msg), &Error{err: "overflow packing a"}
|
||||
}
|
||||
return off, nil
|
||||
}
|
||||
|
||||
func unpackDataAAAA(msg []byte, off int) (net.IP, int, error) {
|
||||
if off+net.IPv6len > len(msg) {
|
||||
return nil, len(msg), &Error{err: "overflow unpacking aaaa"}
|
||||
}
|
||||
aaaa := append(make(net.IP, 0, net.IPv6len), msg[off:off+net.IPv6len]...)
|
||||
off += net.IPv6len
|
||||
return aaaa, off, nil
|
||||
}
|
||||
|
||||
func packDataAAAA(aaaa net.IP, msg []byte, off int) (int, error) {
|
||||
if off+net.IPv6len > len(msg) {
|
||||
return len(msg), &Error{err: "overflow packing aaaa"}
|
||||
}
|
||||
|
||||
switch len(aaaa) {
|
||||
case net.IPv6len:
|
||||
copy(msg[off:], aaaa)
|
||||
off += net.IPv6len
|
||||
case 0:
|
||||
// Allowed, dynamic updates.
|
||||
default:
|
||||
return len(msg), &Error{err: "overflow packing aaaa"}
|
||||
}
|
||||
return off, nil
|
||||
}
|
||||
|
||||
// unpackHeader unpacks an RR header, returning the offset to the end of the header and a
|
||||
// re-sliced msg according to the expected length of the RR.
|
||||
func unpackHeader(msg []byte, off int) (rr RR_Header, off1 int, truncmsg []byte, err error) {
|
||||
hdr := RR_Header{}
|
||||
if off == len(msg) {
|
||||
return hdr, off, msg, nil
|
||||
}
|
||||
|
||||
hdr.Name, off, err = UnpackDomainName(msg, off)
|
||||
if err != nil {
|
||||
return hdr, len(msg), msg, err
|
||||
}
|
||||
hdr.Rrtype, off, err = unpackUint16(msg, off)
|
||||
if err != nil {
|
||||
return hdr, len(msg), msg, err
|
||||
}
|
||||
hdr.Class, off, err = unpackUint16(msg, off)
|
||||
if err != nil {
|
||||
return hdr, len(msg), msg, err
|
||||
}
|
||||
hdr.Ttl, off, err = unpackUint32(msg, off)
|
||||
if err != nil {
|
||||
return hdr, len(msg), msg, err
|
||||
}
|
||||
hdr.Rdlength, off, err = unpackUint16(msg, off)
|
||||
if err != nil {
|
||||
return hdr, len(msg), msg, err
|
||||
}
|
||||
msg, err = truncateMsgFromRdlength(msg, off, hdr.Rdlength)
|
||||
return hdr, off, msg, err
|
||||
}
|
||||
|
||||
// pack packs an RR header, returning the offset to the end of the header.
|
||||
// See PackDomainName for documentation about the compression.
|
||||
func (hdr RR_Header) pack(msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) {
|
||||
if off == len(msg) {
|
||||
return off, nil
|
||||
}
|
||||
|
||||
off, err = PackDomainName(hdr.Name, msg, off, compression, compress)
|
||||
if err != nil {
|
||||
return len(msg), err
|
||||
}
|
||||
off, err = packUint16(hdr.Rrtype, msg, off)
|
||||
if err != nil {
|
||||
return len(msg), err
|
||||
}
|
||||
off, err = packUint16(hdr.Class, msg, off)
|
||||
if err != nil {
|
||||
return len(msg), err
|
||||
}
|
||||
off, err = packUint32(hdr.Ttl, msg, off)
|
||||
if err != nil {
|
||||
return len(msg), err
|
||||
}
|
||||
off, err = packUint16(hdr.Rdlength, msg, off)
|
||||
if err != nil {
|
||||
return len(msg), err
|
||||
}
|
||||
return off, nil
|
||||
}
|
||||
|
||||
// helper helper functions.
|
||||
|
||||
// truncateMsgFromRdLength truncates msg to match the expected length of the RR.
|
||||
// Returns an error if msg is smaller than the expected size.
|
||||
func truncateMsgFromRdlength(msg []byte, off int, rdlength uint16) (truncmsg []byte, err error) {
|
||||
lenrd := off + int(rdlength)
|
||||
if lenrd > len(msg) {
|
||||
return msg, &Error{err: "overflowing header size"}
|
||||
}
|
||||
return msg[:lenrd], nil
|
||||
}
|
||||
|
||||
var base32HexNoPadEncoding = base32.HexEncoding.WithPadding(base32.NoPadding)
|
||||
|
||||
func fromBase32(s []byte) (buf []byte, err error) {
|
||||
for i, b := range s {
|
||||
if b >= 'a' && b <= 'z' {
|
||||
s[i] = b - 32
|
||||
}
|
||||
}
|
||||
buflen := base32HexNoPadEncoding.DecodedLen(len(s))
|
||||
buf = make([]byte, buflen)
|
||||
n, err := base32HexNoPadEncoding.Decode(buf, s)
|
||||
buf = buf[:n]
|
||||
return
|
||||
}
|
||||
|
||||
func toBase32(b []byte) string {
|
||||
return base32HexNoPadEncoding.EncodeToString(b)
|
||||
}
|
||||
|
||||
func fromBase64(s []byte) (buf []byte, err error) {
|
||||
buflen := base64.StdEncoding.DecodedLen(len(s))
|
||||
buf = make([]byte, buflen)
|
||||
n, err := base64.StdEncoding.Decode(buf, s)
|
||||
buf = buf[:n]
|
||||
return
|
||||
}
|
||||
|
||||
func toBase64(b []byte) string { return base64.StdEncoding.EncodeToString(b) }
|
||||
|
||||
// dynamicUpdate returns true if the Rdlength is zero.
|
||||
func noRdata(h RR_Header) bool { return h.Rdlength == 0 }
|
||||
|
||||
func unpackUint8(msg []byte, off int) (i uint8, off1 int, err error) {
|
||||
if off+1 > len(msg) {
|
||||
return 0, len(msg), &Error{err: "overflow unpacking uint8"}
|
||||
}
|
||||
return uint8(msg[off]), off + 1, nil
|
||||
}
|
||||
|
||||
func packUint8(i uint8, msg []byte, off int) (off1 int, err error) {
|
||||
if off+1 > len(msg) {
|
||||
return len(msg), &Error{err: "overflow packing uint8"}
|
||||
}
|
||||
msg[off] = byte(i)
|
||||
return off + 1, nil
|
||||
}
|
||||
|
||||
func unpackUint16(msg []byte, off int) (i uint16, off1 int, err error) {
|
||||
if off+2 > len(msg) {
|
||||
return 0, len(msg), &Error{err: "overflow unpacking uint16"}
|
||||
}
|
||||
return binary.BigEndian.Uint16(msg[off:]), off + 2, nil
|
||||
}
|
||||
|
||||
func packUint16(i uint16, msg []byte, off int) (off1 int, err error) {
|
||||
if off+2 > len(msg) {
|
||||
return len(msg), &Error{err: "overflow packing uint16"}
|
||||
}
|
||||
binary.BigEndian.PutUint16(msg[off:], i)
|
||||
return off + 2, nil
|
||||
}
|
||||
|
||||
func unpackUint32(msg []byte, off int) (i uint32, off1 int, err error) {
|
||||
if off+4 > len(msg) {
|
||||
return 0, len(msg), &Error{err: "overflow unpacking uint32"}
|
||||
}
|
||||
return binary.BigEndian.Uint32(msg[off:]), off + 4, nil
|
||||
}
|
||||
|
||||
func packUint32(i uint32, msg []byte, off int) (off1 int, err error) {
|
||||
if off+4 > len(msg) {
|
||||
return len(msg), &Error{err: "overflow packing uint32"}
|
||||
}
|
||||
binary.BigEndian.PutUint32(msg[off:], i)
|
||||
return off + 4, nil
|
||||
}
|
||||
|
||||
func unpackUint48(msg []byte, off int) (i uint64, off1 int, err error) {
|
||||
if off+6 > len(msg) {
|
||||
return 0, len(msg), &Error{err: "overflow unpacking uint64 as uint48"}
|
||||
}
|
||||
// Used in TSIG where the last 48 bits are occupied, so for now, assume a uint48 (6 bytes)
|
||||
i = (uint64(uint64(msg[off])<<40 | uint64(msg[off+1])<<32 | uint64(msg[off+2])<<24 | uint64(msg[off+3])<<16 |
|
||||
uint64(msg[off+4])<<8 | uint64(msg[off+5])))
|
||||
off += 6
|
||||
return i, off, nil
|
||||
}
|
||||
|
||||
func packUint48(i uint64, msg []byte, off int) (off1 int, err error) {
|
||||
if off+6 > len(msg) {
|
||||
return len(msg), &Error{err: "overflow packing uint64 as uint48"}
|
||||
}
|
||||
msg[off] = byte(i >> 40)
|
||||
msg[off+1] = byte(i >> 32)
|
||||
msg[off+2] = byte(i >> 24)
|
||||
msg[off+3] = byte(i >> 16)
|
||||
msg[off+4] = byte(i >> 8)
|
||||
msg[off+5] = byte(i)
|
||||
off += 6
|
||||
return off, nil
|
||||
}
|
||||
|
||||
func unpackUint64(msg []byte, off int) (i uint64, off1 int, err error) {
|
||||
if off+8 > len(msg) {
|
||||
return 0, len(msg), &Error{err: "overflow unpacking uint64"}
|
||||
}
|
||||
return binary.BigEndian.Uint64(msg[off:]), off + 8, nil
|
||||
}
|
||||
|
||||
func packUint64(i uint64, msg []byte, off int) (off1 int, err error) {
|
||||
if off+8 > len(msg) {
|
||||
return len(msg), &Error{err: "overflow packing uint64"}
|
||||
}
|
||||
binary.BigEndian.PutUint64(msg[off:], i)
|
||||
off += 8
|
||||
return off, nil
|
||||
}
|
||||
|
||||
func unpackString(msg []byte, off int) (string, int, error) {
|
||||
if off+1 > len(msg) {
|
||||
return "", off, &Error{err: "overflow unpacking txt"}
|
||||
}
|
||||
l := int(msg[off])
|
||||
if off+l+1 > len(msg) {
|
||||
return "", off, &Error{err: "overflow unpacking txt"}
|
||||
}
|
||||
s := make([]byte, 0, l)
|
||||
for _, b := range msg[off+1 : off+1+l] {
|
||||
switch b {
|
||||
case '"', '\\':
|
||||
s = append(s, '\\', b)
|
||||
default:
|
||||
if b < 32 || b > 127 { // unprintable
|
||||
var buf [3]byte
|
||||
bufs := strconv.AppendInt(buf[:0], int64(b), 10)
|
||||
s = append(s, '\\')
|
||||
for i := 0; i < 3-len(bufs); i++ {
|
||||
s = append(s, '0')
|
||||
}
|
||||
for _, r := range bufs {
|
||||
s = append(s, r)
|
||||
}
|
||||
} else {
|
||||
s = append(s, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
off += 1 + l
|
||||
return string(s), off, nil
|
||||
}
|
||||
|
||||
func packString(s string, msg []byte, off int) (int, error) {
|
||||
txtTmp := make([]byte, 256*4+1)
|
||||
off, err := packTxtString(s, msg, off, txtTmp)
|
||||
if err != nil {
|
||||
return len(msg), err
|
||||
}
|
||||
return off, nil
|
||||
}
|
||||
|
||||
func unpackStringBase32(msg []byte, off, end int) (string, int, error) {
|
||||
if end > len(msg) {
|
||||
return "", len(msg), &Error{err: "overflow unpacking base32"}
|
||||
}
|
||||
s := toBase32(msg[off:end])
|
||||
return s, end, nil
|
||||
}
|
||||
|
||||
func packStringBase32(s string, msg []byte, off int) (int, error) {
|
||||
b32, err := fromBase32([]byte(s))
|
||||
if err != nil {
|
||||
return len(msg), err
|
||||
}
|
||||
if off+len(b32) > len(msg) {
|
||||
return len(msg), &Error{err: "overflow packing base32"}
|
||||
}
|
||||
copy(msg[off:off+len(b32)], b32)
|
||||
off += len(b32)
|
||||
return off, nil
|
||||
}
|
||||
|
||||
func unpackStringBase64(msg []byte, off, end int) (string, int, error) {
|
||||
// Rest of the RR is base64 encoded value, so we don't need an explicit length
|
||||
// to be set. Thus far all RR's that have base64 encoded fields have those as their
|
||||
// last one. What we do need is the end of the RR!
|
||||
if end > len(msg) {
|
||||
return "", len(msg), &Error{err: "overflow unpacking base64"}
|
||||
}
|
||||
s := toBase64(msg[off:end])
|
||||
return s, end, nil
|
||||
}
|
||||
|
||||
func packStringBase64(s string, msg []byte, off int) (int, error) {
|
||||
b64, err := fromBase64([]byte(s))
|
||||
if err != nil {
|
||||
return len(msg), err
|
||||
}
|
||||
if off+len(b64) > len(msg) {
|
||||
return len(msg), &Error{err: "overflow packing base64"}
|
||||
}
|
||||
copy(msg[off:off+len(b64)], b64)
|
||||
off += len(b64)
|
||||
return off, nil
|
||||
}
|
||||
|
||||
func unpackStringHex(msg []byte, off, end int) (string, int, error) {
|
||||
// Rest of the RR is hex encoded value, so we don't need an explicit length
|
||||
// to be set. NSEC and TSIG have hex fields with a length field.
|
||||
// What we do need is the end of the RR!
|
||||
if end > len(msg) {
|
||||
return "", len(msg), &Error{err: "overflow unpacking hex"}
|
||||
}
|
||||
|
||||
s := hex.EncodeToString(msg[off:end])
|
||||
return s, end, nil
|
||||
}
|
||||
|
||||
func packStringHex(s string, msg []byte, off int) (int, error) {
|
||||
h, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
return len(msg), err
|
||||
}
|
||||
if off+(len(h)) > len(msg) {
|
||||
return len(msg), &Error{err: "overflow packing hex"}
|
||||
}
|
||||
copy(msg[off:off+len(h)], h)
|
||||
off += len(h)
|
||||
return off, nil
|
||||
}
|
||||
|
||||
func unpackStringTxt(msg []byte, off int) ([]string, int, error) {
|
||||
txt, off, err := unpackTxt(msg, off)
|
||||
if err != nil {
|
||||
return nil, len(msg), err
|
||||
}
|
||||
return txt, off, nil
|
||||
}
|
||||
|
||||
func packStringTxt(s []string, msg []byte, off int) (int, error) {
|
||||
txtTmp := make([]byte, 256*4+1) // If the whole string consists out of \DDD we need this many.
|
||||
off, err := packTxt(s, msg, off, txtTmp)
|
||||
if err != nil {
|
||||
return len(msg), err
|
||||
}
|
||||
return off, nil
|
||||
}
|
||||
|
||||
func unpackDataOpt(msg []byte, off int) ([]EDNS0, int, error) {
|
||||
var edns []EDNS0
|
||||
Option:
|
||||
code := uint16(0)
|
||||
if off+4 > len(msg) {
|
||||
return nil, len(msg), &Error{err: "overflow unpacking opt"}
|
||||
}
|
||||
code = binary.BigEndian.Uint16(msg[off:])
|
||||
off += 2
|
||||
optlen := binary.BigEndian.Uint16(msg[off:])
|
||||
off += 2
|
||||
if off+int(optlen) > len(msg) {
|
||||
return nil, len(msg), &Error{err: "overflow unpacking opt"}
|
||||
}
|
||||
switch code {
|
||||
case EDNS0NSID:
|
||||
e := new(EDNS0_NSID)
|
||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
||||
return nil, len(msg), err
|
||||
}
|
||||
edns = append(edns, e)
|
||||
off += int(optlen)
|
||||
case EDNS0SUBNET:
|
||||
e := new(EDNS0_SUBNET)
|
||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
||||
return nil, len(msg), err
|
||||
}
|
||||
edns = append(edns, e)
|
||||
off += int(optlen)
|
||||
case EDNS0COOKIE:
|
||||
e := new(EDNS0_COOKIE)
|
||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
||||
return nil, len(msg), err
|
||||
}
|
||||
edns = append(edns, e)
|
||||
off += int(optlen)
|
||||
case EDNS0UL:
|
||||
e := new(EDNS0_UL)
|
||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
||||
return nil, len(msg), err
|
||||
}
|
||||
edns = append(edns, e)
|
||||
off += int(optlen)
|
||||
case EDNS0LLQ:
|
||||
e := new(EDNS0_LLQ)
|
||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
||||
return nil, len(msg), err
|
||||
}
|
||||
edns = append(edns, e)
|
||||
off += int(optlen)
|
||||
case EDNS0DAU:
|
||||
e := new(EDNS0_DAU)
|
||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
||||
return nil, len(msg), err
|
||||
}
|
||||
edns = append(edns, e)
|
||||
off += int(optlen)
|
||||
case EDNS0DHU:
|
||||
e := new(EDNS0_DHU)
|
||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
||||
return nil, len(msg), err
|
||||
}
|
||||
edns = append(edns, e)
|
||||
off += int(optlen)
|
||||
case EDNS0N3U:
|
||||
e := new(EDNS0_N3U)
|
||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
||||
return nil, len(msg), err
|
||||
}
|
||||
edns = append(edns, e)
|
||||
off += int(optlen)
|
||||
case EDNS0PADDING:
|
||||
e := new(EDNS0_PADDING)
|
||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
||||
return nil, len(msg), err
|
||||
}
|
||||
edns = append(edns, e)
|
||||
off += int(optlen)
|
||||
default:
|
||||
e := new(EDNS0_LOCAL)
|
||||
e.Code = code
|
||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
||||
return nil, len(msg), err
|
||||
}
|
||||
edns = append(edns, e)
|
||||
off += int(optlen)
|
||||
}
|
||||
|
||||
if off < len(msg) {
|
||||
goto Option
|
||||
}
|
||||
|
||||
return edns, off, nil
|
||||
}
|
||||
|
||||
func packDataOpt(options []EDNS0, msg []byte, off int) (int, error) {
|
||||
for _, el := range options {
|
||||
b, err := el.pack()
|
||||
if err != nil || off+3 > len(msg) {
|
||||
return len(msg), &Error{err: "overflow packing opt"}
|
||||
}
|
||||
binary.BigEndian.PutUint16(msg[off:], el.Option()) // Option code
|
||||
binary.BigEndian.PutUint16(msg[off+2:], uint16(len(b))) // Length
|
||||
off += 4
|
||||
if off+len(b) > len(msg) {
|
||||
copy(msg[off:], b)
|
||||
off = len(msg)
|
||||
continue
|
||||
}
|
||||
// Actual data
|
||||
copy(msg[off:off+len(b)], b)
|
||||
off += len(b)
|
||||
}
|
||||
return off, nil
|
||||
}
|
||||
|
||||
func unpackStringOctet(msg []byte, off int) (string, int, error) {
|
||||
s := string(msg[off:])
|
||||
return s, len(msg), nil
|
||||
}
|
||||
|
||||
func packStringOctet(s string, msg []byte, off int) (int, error) {
|
||||
txtTmp := make([]byte, 256*4+1)
|
||||
off, err := packOctetString(s, msg, off, txtTmp)
|
||||
if err != nil {
|
||||
return len(msg), err
|
||||
}
|
||||
return off, nil
|
||||
}
|
||||
|
||||
func unpackDataNsec(msg []byte, off int) ([]uint16, int, error) {
|
||||
var nsec []uint16
|
||||
length, window, lastwindow := 0, 0, -1
|
||||
for off < len(msg) {
|
||||
if off+2 > len(msg) {
|
||||
return nsec, len(msg), &Error{err: "overflow unpacking nsecx"}
|
||||
}
|
||||
window = int(msg[off])
|
||||
length = int(msg[off+1])
|
||||
off += 2
|
||||
if window <= lastwindow {
|
||||
// RFC 4034: Blocks are present in the NSEC RR RDATA in
|
||||
// increasing numerical order.
|
||||
return nsec, len(msg), &Error{err: "out of order NSEC block"}
|
||||
}
|
||||
if length == 0 {
|
||||
// RFC 4034: Blocks with no types present MUST NOT be included.
|
||||
return nsec, len(msg), &Error{err: "empty NSEC block"}
|
||||
}
|
||||
if length > 32 {
|
||||
return nsec, len(msg), &Error{err: "NSEC block too long"}
|
||||
}
|
||||
if off+length > len(msg) {
|
||||
return nsec, len(msg), &Error{err: "overflowing NSEC block"}
|
||||
}
|
||||
|
||||
// Walk the bytes in the window and extract the type bits
|
||||
for j := 0; j < length; j++ {
|
||||
b := msg[off+j]
|
||||
// Check the bits one by one, and set the type
|
||||
if b&0x80 == 0x80 {
|
||||
nsec = append(nsec, uint16(window*256+j*8+0))
|
||||
}
|
||||
if b&0x40 == 0x40 {
|
||||
nsec = append(nsec, uint16(window*256+j*8+1))
|
||||
}
|
||||
if b&0x20 == 0x20 {
|
||||
nsec = append(nsec, uint16(window*256+j*8+2))
|
||||
}
|
||||
if b&0x10 == 0x10 {
|
||||
nsec = append(nsec, uint16(window*256+j*8+3))
|
||||
}
|
||||
if b&0x8 == 0x8 {
|
||||
nsec = append(nsec, uint16(window*256+j*8+4))
|
||||
}
|
||||
if b&0x4 == 0x4 {
|
||||
nsec = append(nsec, uint16(window*256+j*8+5))
|
||||
}
|
||||
if b&0x2 == 0x2 {
|
||||
nsec = append(nsec, uint16(window*256+j*8+6))
|
||||
}
|
||||
if b&0x1 == 0x1 {
|
||||
nsec = append(nsec, uint16(window*256+j*8+7))
|
||||
}
|
||||
}
|
||||
off += length
|
||||
lastwindow = window
|
||||
}
|
||||
return nsec, off, nil
|
||||
}
|
||||
|
||||
func packDataNsec(bitmap []uint16, msg []byte, off int) (int, error) {
|
||||
if len(bitmap) == 0 {
|
||||
return off, nil
|
||||
}
|
||||
var lastwindow, lastlength uint16
|
||||
for j := 0; j < len(bitmap); j++ {
|
||||
t := bitmap[j]
|
||||
window := t / 256
|
||||
length := (t-window*256)/8 + 1
|
||||
if window > lastwindow && lastlength != 0 { // New window, jump to the new offset
|
||||
off += int(lastlength) + 2
|
||||
lastlength = 0
|
||||
}
|
||||
if window < lastwindow || length < lastlength {
|
||||
return len(msg), &Error{err: "nsec bits out of order"}
|
||||
}
|
||||
if off+2+int(length) > len(msg) {
|
||||
return len(msg), &Error{err: "overflow packing nsec"}
|
||||
}
|
||||
// Setting the window #
|
||||
msg[off] = byte(window)
|
||||
// Setting the octets length
|
||||
msg[off+1] = byte(length)
|
||||
// Setting the bit value for the type in the right octet
|
||||
msg[off+1+int(length)] |= byte(1 << (7 - (t % 8)))
|
||||
lastwindow, lastlength = window, length
|
||||
}
|
||||
off += int(lastlength) + 2
|
||||
return off, nil
|
||||
}
|
||||
|
||||
func unpackDataDomainNames(msg []byte, off, end int) ([]string, int, error) {
|
||||
var (
|
||||
servers []string
|
||||
s string
|
||||
err error
|
||||
)
|
||||
if end > len(msg) {
|
||||
return nil, len(msg), &Error{err: "overflow unpacking domain names"}
|
||||
}
|
||||
for off < end {
|
||||
s, off, err = UnpackDomainName(msg, off)
|
||||
if err != nil {
|
||||
return servers, len(msg), err
|
||||
}
|
||||
servers = append(servers, s)
|
||||
}
|
||||
return servers, off, nil
|
||||
}
|
||||
|
||||
func packDataDomainNames(names []string, msg []byte, off int, compression map[string]int, compress bool) (int, error) {
|
||||
var err error
|
||||
for j := 0; j < len(names); j++ {
|
||||
off, err = PackDomainName(names[j], msg, off, compression, false && compress)
|
||||
if err != nil {
|
||||
return len(msg), err
|
||||
}
|
||||
}
|
||||
return off, nil
|
||||
}
|
||||
146
vendor/github.com/miekg/dns/msg_test.go
generated
vendored
Normal file
146
vendor/github.com/miekg/dns/msg_test.go
generated
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
maxPrintableLabel = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789x"
|
||||
tooLongLabel = maxPrintableLabel + "x"
|
||||
)
|
||||
|
||||
var (
|
||||
longDomain = maxPrintableLabel[:53] + strings.TrimSuffix(
|
||||
strings.Join([]string{".", ".", ".", ".", "."}, maxPrintableLabel[:49]), ".")
|
||||
reChar = regexp.MustCompile(`.`)
|
||||
i = -1
|
||||
maxUnprintableLabel = reChar.ReplaceAllStringFunc(maxPrintableLabel, func(ch string) string {
|
||||
if i++; i >= 32 {
|
||||
i = 0
|
||||
}
|
||||
return fmt.Sprintf("\\%03d", i)
|
||||
})
|
||||
)
|
||||
|
||||
func TestPackNoSideEffect(t *testing.T) {
|
||||
m := new(Msg)
|
||||
m.SetQuestion(Fqdn("example.com."), TypeNS)
|
||||
|
||||
a := new(Msg)
|
||||
o := &OPT{
|
||||
Hdr: RR_Header{
|
||||
Name: ".",
|
||||
Rrtype: TypeOPT,
|
||||
},
|
||||
}
|
||||
o.SetUDPSize(DefaultMsgSize)
|
||||
|
||||
a.Extra = append(a.Extra, o)
|
||||
a.SetRcode(m, RcodeBadVers)
|
||||
|
||||
a.Pack()
|
||||
if a.Rcode != RcodeBadVers {
|
||||
t.Errorf("after pack: Rcode is expected to be BADVERS")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnpackDomainName(t *testing.T) {
|
||||
var cases = []struct {
|
||||
label string
|
||||
input string
|
||||
expectedOutput string
|
||||
expectedError string
|
||||
}{
|
||||
{"empty domain",
|
||||
"\x00",
|
||||
".",
|
||||
""},
|
||||
{"long label",
|
||||
string(63) + maxPrintableLabel + "\x00",
|
||||
maxPrintableLabel + ".",
|
||||
""},
|
||||
{"unprintable label",
|
||||
string(63) + regexp.MustCompile(`\\[0-9]+`).ReplaceAllStringFunc(maxUnprintableLabel,
|
||||
func(escape string) string {
|
||||
n, _ := strconv.ParseInt(escape[1:], 10, 8)
|
||||
return string(n)
|
||||
}) + "\x00",
|
||||
maxUnprintableLabel + ".",
|
||||
""},
|
||||
{"long domain",
|
||||
string(53) + strings.Replace(longDomain, ".", string(49), -1) + "\x00",
|
||||
longDomain + ".",
|
||||
""},
|
||||
{"compression pointer",
|
||||
// an unrealistic but functional test referencing an offset _inside_ a label
|
||||
"\x03foo" + "\x05\x03com\x00" + "\x07example" + "\xC0\x05",
|
||||
"foo.\\003com\\000.example.com.",
|
||||
""},
|
||||
|
||||
{"too long domain",
|
||||
string(54) + "x" + strings.Replace(longDomain, ".", string(49), -1) + "\x00",
|
||||
"x" + longDomain + ".",
|
||||
ErrLongDomain.Error()},
|
||||
{"too long by pointer",
|
||||
// a matryoshka doll name to get over 255 octets after expansion via internal pointers
|
||||
string([]byte{
|
||||
// 11 length values, first to last
|
||||
40, 37, 34, 31, 28, 25, 22, 19, 16, 13, 0,
|
||||
// 12 filler values
|
||||
120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120,
|
||||
// 10 pointers, last to first
|
||||
192, 10, 192, 9, 192, 8, 192, 7, 192, 6, 192, 5, 192, 4, 192, 3, 192, 2, 192, 1,
|
||||
}),
|
||||
"",
|
||||
ErrLongDomain.Error()},
|
||||
{"long by pointer",
|
||||
// a matryoshka doll name _not_ exceeding 255 octets after expansion
|
||||
string([]byte{
|
||||
// 11 length values, first to last
|
||||
37, 34, 31, 28, 25, 22, 19, 16, 13, 10, 0,
|
||||
// 9 filler values
|
||||
120, 120, 120, 120, 120, 120, 120, 120, 120,
|
||||
// 10 pointers, last to first
|
||||
192, 10, 192, 9, 192, 8, 192, 7, 192, 6, 192, 5, 192, 4, 192, 3, 192, 2, 192, 1,
|
||||
}),
|
||||
"" +
|
||||
(`\"\031\028\025\022\019\016\013\010\000xxxxxxxxx` +
|
||||
`\192\010\192\009\192\008\192\007\192\006\192\005\192\004\192\003\192\002.`) +
|
||||
(`\031\028\025\022\019\016\013\010\000xxxxxxxxx` +
|
||||
`\192\010\192\009\192\008\192\007\192\006\192\005\192\004\192\003.`) +
|
||||
(`\028\025\022\019\016\013\010\000xxxxxxxxx` +
|
||||
`\192\010\192\009\192\008\192\007\192\006\192\005\192\004.`) +
|
||||
(`\025\022\019\016\013\010\000xxxxxxxxx` +
|
||||
`\192\010\192\009\192\008\192\007\192\006\192\005.`) +
|
||||
`\022\019\016\013\010\000xxxxxxxxx\192\010\192\009\192\008\192\007\192\006.` +
|
||||
`\019\016\013\010\000xxxxxxxxx\192\010\192\009\192\008\192\007.` +
|
||||
`\016\013\010\000xxxxxxxxx\192\010\192\009\192\008.` +
|
||||
`\013\010\000xxxxxxxxx\192\010\192\009.` +
|
||||
`\010\000xxxxxxxxx\192\010.` +
|
||||
`\000xxxxxxxxx.`,
|
||||
""},
|
||||
{"truncated name", "\x07example\x03", "", "dns: buffer size too small"},
|
||||
{"non-absolute name", "\x07example\x03com", "", "dns: buffer size too small"},
|
||||
{"compression pointer cycle",
|
||||
"\x03foo" + "\x03bar" + "\x07example" + "\xC0\x04",
|
||||
"",
|
||||
"dns: too many compression pointers"},
|
||||
{"reserved compression pointer 0b10", "\x07example\x80", "", "dns: bad rdata"},
|
||||
{"reserved compression pointer 0b01", "\x07example\x40", "", "dns: bad rdata"},
|
||||
}
|
||||
for _, test := range cases {
|
||||
output, idx, err := UnpackDomainName([]byte(test.input), 0)
|
||||
if test.expectedOutput != "" && output != test.expectedOutput {
|
||||
t.Errorf("%s: expected %s, got %s", test.label, test.expectedOutput, output)
|
||||
}
|
||||
if test.expectedError == "" && err != nil {
|
||||
t.Errorf("%s: expected no error, got %d %v", test.label, idx, err)
|
||||
} else if test.expectedError != "" && (err == nil || err.Error() != test.expectedError) {
|
||||
t.Errorf("%s: expected error %s, got %d %v", test.label, test.expectedError, idx, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
106
vendor/github.com/miekg/dns/nsecx.go
generated
vendored
Normal file
106
vendor/github.com/miekg/dns/nsecx.go
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"hash"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type saltWireFmt struct {
|
||||
Salt string `dns:"size-hex"`
|
||||
}
|
||||
|
||||
// HashName hashes a string (label) according to RFC 5155. It returns the hashed string in uppercase.
|
||||
func HashName(label string, ha uint8, iter uint16, salt string) string {
|
||||
saltwire := new(saltWireFmt)
|
||||
saltwire.Salt = salt
|
||||
wire := make([]byte, DefaultMsgSize)
|
||||
n, err := packSaltWire(saltwire, wire)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
wire = wire[:n]
|
||||
name := make([]byte, 255)
|
||||
off, err := PackDomainName(strings.ToLower(label), name, 0, nil, false)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
name = name[:off]
|
||||
var s hash.Hash
|
||||
switch ha {
|
||||
case SHA1:
|
||||
s = sha1.New()
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
||||
// k = 0
|
||||
s.Write(name)
|
||||
s.Write(wire)
|
||||
nsec3 := s.Sum(nil)
|
||||
// k > 0
|
||||
for k := uint16(0); k < iter; k++ {
|
||||
s.Reset()
|
||||
s.Write(nsec3)
|
||||
s.Write(wire)
|
||||
nsec3 = s.Sum(nsec3[:0])
|
||||
}
|
||||
return toBase32(nsec3)
|
||||
}
|
||||
|
||||
// Cover returns true if a name is covered by the NSEC3 record
|
||||
func (rr *NSEC3) Cover(name string) bool {
|
||||
nameHash := HashName(name, rr.Hash, rr.Iterations, rr.Salt)
|
||||
owner := strings.ToUpper(rr.Hdr.Name)
|
||||
labelIndices := Split(owner)
|
||||
if len(labelIndices) < 2 {
|
||||
return false
|
||||
}
|
||||
ownerHash := owner[:labelIndices[1]-1]
|
||||
ownerZone := owner[labelIndices[1]:]
|
||||
if !IsSubDomain(ownerZone, strings.ToUpper(name)) { // name is outside owner zone
|
||||
return false
|
||||
}
|
||||
|
||||
nextHash := rr.NextDomain
|
||||
if ownerHash == nextHash { // empty interval
|
||||
return false
|
||||
}
|
||||
if ownerHash > nextHash { // end of zone
|
||||
if nameHash > ownerHash { // covered since there is nothing after ownerHash
|
||||
return true
|
||||
}
|
||||
return nameHash < nextHash // if nameHash is before beginning of zone it is covered
|
||||
}
|
||||
if nameHash < ownerHash { // nameHash is before ownerHash, not covered
|
||||
return false
|
||||
}
|
||||
return nameHash < nextHash // if nameHash is before nextHash is it covered (between ownerHash and nextHash)
|
||||
}
|
||||
|
||||
// Match returns true if a name matches the NSEC3 record
|
||||
func (rr *NSEC3) Match(name string) bool {
|
||||
nameHash := HashName(name, rr.Hash, rr.Iterations, rr.Salt)
|
||||
owner := strings.ToUpper(rr.Hdr.Name)
|
||||
labelIndices := Split(owner)
|
||||
if len(labelIndices) < 2 {
|
||||
return false
|
||||
}
|
||||
ownerHash := owner[:labelIndices[1]-1]
|
||||
ownerZone := owner[labelIndices[1]:]
|
||||
if !IsSubDomain(ownerZone, strings.ToUpper(name)) { // name is outside owner zone
|
||||
return false
|
||||
}
|
||||
if ownerHash == nameHash {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func packSaltWire(sw *saltWireFmt, msg []byte) (int, error) {
|
||||
off, err := packStringHex(sw.Salt, msg, 0)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
return off, nil
|
||||
}
|
||||
141
vendor/github.com/miekg/dns/nsecx_test.go
generated
vendored
Normal file
141
vendor/github.com/miekg/dns/nsecx_test.go
generated
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
package dns
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestPackNsec3(t *testing.T) {
|
||||
nsec3 := HashName("dnsex.nl.", SHA1, 0, "DEAD")
|
||||
if nsec3 != "ROCCJAE8BJJU7HN6T7NG3TNM8ACRS87J" {
|
||||
t.Error(nsec3)
|
||||
}
|
||||
|
||||
nsec3 = HashName("a.b.c.example.org.", SHA1, 2, "DEAD")
|
||||
if nsec3 != "6LQ07OAHBTOOEU2R9ANI2AT70K5O0RCG" {
|
||||
t.Error(nsec3)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNsec3(t *testing.T) {
|
||||
nsec3 := testRR("sk4e8fj94u78smusb40o1n0oltbblu2r.nl. IN NSEC3 1 1 5 F10E9F7EA83FC8F3 SK4F38CQ0ATIEI8MH3RGD0P5I4II6QAN NS SOA TXT RRSIG DNSKEY NSEC3PARAM")
|
||||
if !nsec3.(*NSEC3).Match("nl.") { // name hash = sk4e8fj94u78smusb40o1n0oltbblu2r
|
||||
t.Fatal("sk4e8fj94u78smusb40o1n0oltbblu2r.nl. should match sk4e8fj94u78smusb40o1n0oltbblu2r.nl.")
|
||||
}
|
||||
if !nsec3.(*NSEC3).Match("NL.") { // name hash = sk4e8fj94u78smusb40o1n0oltbblu2r
|
||||
t.Fatal("sk4e8fj94u78smusb40o1n0oltbblu2r.NL. should match sk4e8fj94u78smusb40o1n0oltbblu2r.nl.")
|
||||
}
|
||||
if nsec3.(*NSEC3).Match("com.") { //
|
||||
t.Fatal("com. is not in the zone nl.")
|
||||
}
|
||||
if nsec3.(*NSEC3).Match("test.nl.") { // name hash = gd0ptr5bnfpimpu2d3v6gd4n0bai7s0q
|
||||
t.Fatal("gd0ptr5bnfpimpu2d3v6gd4n0bai7s0q.nl. should not match sk4e8fj94u78smusb40o1n0oltbblu2r.nl.")
|
||||
}
|
||||
nsec3 = testRR("nl. IN NSEC3 1 1 5 F10E9F7EA83FC8F3 SK4F38CQ0ATIEI8MH3RGD0P5I4II6QAN NS SOA TXT RRSIG DNSKEY NSEC3PARAM")
|
||||
if nsec3.(*NSEC3).Match("nl.") {
|
||||
t.Fatal("sk4e8fj94u78smusb40o1n0oltbblu2r.nl. should not match a record without a owner hash")
|
||||
}
|
||||
|
||||
for _, tc := range []struct {
|
||||
rr *NSEC3
|
||||
name string
|
||||
covers bool
|
||||
}{
|
||||
// positive tests
|
||||
{ // name hash between owner hash and next hash
|
||||
rr: &NSEC3{
|
||||
Hdr: RR_Header{Name: "2N1TB3VAIRUOBL6RKDVII42N9TFMIALP.com."},
|
||||
Hash: 1,
|
||||
Flags: 1,
|
||||
Iterations: 5,
|
||||
Salt: "F10E9F7EA83FC8F3",
|
||||
NextDomain: "PT3RON8N7PM3A0OE989IB84OOSADP7O8",
|
||||
},
|
||||
name: "bsd.com.",
|
||||
covers: true,
|
||||
},
|
||||
{ // end of zone, name hash is after owner hash
|
||||
rr: &NSEC3{
|
||||
Hdr: RR_Header{Name: "3v62ulr0nre83v0rja2vjgtlif9v6rab.com."},
|
||||
Hash: 1,
|
||||
Flags: 1,
|
||||
Iterations: 5,
|
||||
Salt: "F10E9F7EA83FC8F3",
|
||||
NextDomain: "2N1TB3VAIRUOBL6RKDVII42N9TFMIALP",
|
||||
},
|
||||
name: "csd.com.",
|
||||
covers: true,
|
||||
},
|
||||
{ // end of zone, name hash is before beginning of zone
|
||||
rr: &NSEC3{
|
||||
Hdr: RR_Header{Name: "PT3RON8N7PM3A0OE989IB84OOSADP7O8.com."},
|
||||
Hash: 1,
|
||||
Flags: 1,
|
||||
Iterations: 5,
|
||||
Salt: "F10E9F7EA83FC8F3",
|
||||
NextDomain: "3V62ULR0NRE83V0RJA2VJGTLIF9V6RAB",
|
||||
},
|
||||
name: "asd.com.",
|
||||
covers: true,
|
||||
},
|
||||
// negative tests
|
||||
{ // too short owner name
|
||||
rr: &NSEC3{
|
||||
Hdr: RR_Header{Name: "nl."},
|
||||
Hash: 1,
|
||||
Flags: 1,
|
||||
Iterations: 5,
|
||||
Salt: "F10E9F7EA83FC8F3",
|
||||
NextDomain: "39P99DCGG0MDLARTCRMCF6OFLLUL7PR6",
|
||||
},
|
||||
name: "asd.com.",
|
||||
covers: false,
|
||||
},
|
||||
{ // outside of zone
|
||||
rr: &NSEC3{
|
||||
Hdr: RR_Header{Name: "39p91242oslggest5e6a7cci4iaeqvnk.nl."},
|
||||
Hash: 1,
|
||||
Flags: 1,
|
||||
Iterations: 5,
|
||||
Salt: "F10E9F7EA83FC8F3",
|
||||
NextDomain: "39P99DCGG0MDLARTCRMCF6OFLLUL7PR6",
|
||||
},
|
||||
name: "asd.com.",
|
||||
covers: false,
|
||||
},
|
||||
{ // empty interval
|
||||
rr: &NSEC3{
|
||||
Hdr: RR_Header{Name: "2n1tb3vairuobl6rkdvii42n9tfmialp.com."},
|
||||
Hash: 1,
|
||||
Flags: 1,
|
||||
Iterations: 5,
|
||||
Salt: "F10E9F7EA83FC8F3",
|
||||
NextDomain: "2N1TB3VAIRUOBL6RKDVII42N9TFMIALP",
|
||||
},
|
||||
name: "asd.com.",
|
||||
covers: false,
|
||||
},
|
||||
{ // name hash is before owner hash, not covered
|
||||
rr: &NSEC3{
|
||||
Hdr: RR_Header{Name: "3V62ULR0NRE83V0RJA2VJGTLIF9V6RAB.com."},
|
||||
Hash: 1,
|
||||
Flags: 1,
|
||||
Iterations: 5,
|
||||
Salt: "F10E9F7EA83FC8F3",
|
||||
NextDomain: "PT3RON8N7PM3A0OE989IB84OOSADP7O8",
|
||||
},
|
||||
name: "asd.com.",
|
||||
covers: false,
|
||||
},
|
||||
} {
|
||||
covers := tc.rr.Cover(tc.name)
|
||||
if tc.covers != covers {
|
||||
t.Fatalf("cover failed for %s: expected %t, got %t [record: %s]", tc.name, tc.covers, covers, tc.rr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNsec3EmptySalt(t *testing.T) {
|
||||
rr, _ := NewRR("CK0POJMG874LJREF7EFN8430QVIT8BSM.com. 86400 IN NSEC3 1 1 0 - CK0Q1GIN43N1ARRC9OSM6QPQR81H5M9A NS SOA RRSIG DNSKEY NSEC3PARAM")
|
||||
|
||||
if !rr.(*NSEC3).Match("com.") {
|
||||
t.Fatalf("expected record to match com. label")
|
||||
}
|
||||
}
|
||||
1465
vendor/github.com/miekg/dns/parse_test.go
generated
vendored
Normal file
1465
vendor/github.com/miekg/dns/parse_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
148
vendor/github.com/miekg/dns/privaterr.go
generated
vendored
Normal file
148
vendor/github.com/miekg/dns/privaterr.go
generated
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PrivateRdata is an interface used for implementing "Private Use" RR types, see
|
||||
// RFC 6895. This allows one to experiment with new RR types, without requesting an
|
||||
// official type code. Also see dns.PrivateHandle and dns.PrivateHandleRemove.
|
||||
type PrivateRdata interface {
|
||||
// String returns the text presentaton of the Rdata of the Private RR.
|
||||
String() string
|
||||
// Parse parses the Rdata of the private RR.
|
||||
Parse([]string) error
|
||||
// Pack is used when packing a private RR into a buffer.
|
||||
Pack([]byte) (int, error)
|
||||
// Unpack is used when unpacking a private RR from a buffer.
|
||||
// TODO(miek): diff. signature than Pack, see edns0.go for instance.
|
||||
Unpack([]byte) (int, error)
|
||||
// Copy copies the Rdata.
|
||||
Copy(PrivateRdata) error
|
||||
// Len returns the length in octets of the Rdata.
|
||||
Len() int
|
||||
}
|
||||
|
||||
// PrivateRR represents an RR that uses a PrivateRdata user-defined type.
|
||||
// It mocks normal RRs and implements dns.RR interface.
|
||||
type PrivateRR struct {
|
||||
Hdr RR_Header
|
||||
Data PrivateRdata
|
||||
}
|
||||
|
||||
func mkPrivateRR(rrtype uint16) *PrivateRR {
|
||||
// Panics if RR is not an instance of PrivateRR.
|
||||
rrfunc, ok := TypeToRR[rrtype]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("dns: invalid operation with Private RR type %d", rrtype))
|
||||
}
|
||||
|
||||
anyrr := rrfunc()
|
||||
switch rr := anyrr.(type) {
|
||||
case *PrivateRR:
|
||||
return rr
|
||||
}
|
||||
panic(fmt.Sprintf("dns: RR is not a PrivateRR, TypeToRR[%d] generator returned %T", rrtype, anyrr))
|
||||
}
|
||||
|
||||
// Header return the RR header of r.
|
||||
func (r *PrivateRR) Header() *RR_Header { return &r.Hdr }
|
||||
|
||||
func (r *PrivateRR) String() string { return r.Hdr.String() + r.Data.String() }
|
||||
|
||||
// Private len and copy parts to satisfy RR interface.
|
||||
func (r *PrivateRR) len() int { return r.Hdr.len() + r.Data.Len() }
|
||||
func (r *PrivateRR) copy() RR {
|
||||
// make new RR like this:
|
||||
rr := mkPrivateRR(r.Hdr.Rrtype)
|
||||
rr.Hdr = r.Hdr
|
||||
|
||||
err := r.Data.Copy(rr.Data)
|
||||
if err != nil {
|
||||
panic("dns: got value that could not be used to copy Private rdata")
|
||||
}
|
||||
return rr
|
||||
}
|
||||
func (r *PrivateRR) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) {
|
||||
off, err := r.Hdr.pack(msg, off, compression, compress)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
headerEnd := off
|
||||
n, err := r.Data.Pack(msg[off:])
|
||||
if err != nil {
|
||||
return len(msg), err
|
||||
}
|
||||
off += n
|
||||
r.Header().Rdlength = uint16(off - headerEnd)
|
||||
return off, nil
|
||||
}
|
||||
|
||||
// PrivateHandle registers a private resource record type. It requires
|
||||
// string and numeric representation of private RR type and generator function as argument.
|
||||
func PrivateHandle(rtypestr string, rtype uint16, generator func() PrivateRdata) {
|
||||
rtypestr = strings.ToUpper(rtypestr)
|
||||
|
||||
TypeToRR[rtype] = func() RR { return &PrivateRR{RR_Header{}, generator()} }
|
||||
TypeToString[rtype] = rtypestr
|
||||
StringToType[rtypestr] = rtype
|
||||
|
||||
typeToUnpack[rtype] = func(h RR_Header, msg []byte, off int) (RR, int, error) {
|
||||
if noRdata(h) {
|
||||
return &h, off, nil
|
||||
}
|
||||
var err error
|
||||
|
||||
rr := mkPrivateRR(h.Rrtype)
|
||||
rr.Hdr = h
|
||||
|
||||
off1, err := rr.Data.Unpack(msg[off:])
|
||||
off += off1
|
||||
if err != nil {
|
||||
return rr, off, err
|
||||
}
|
||||
return rr, off, err
|
||||
}
|
||||
|
||||
setPrivateRR := func(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
||||
rr := mkPrivateRR(h.Rrtype)
|
||||
rr.Hdr = h
|
||||
|
||||
var l lex
|
||||
text := make([]string, 0, 2) // could be 0..N elements, median is probably 1
|
||||
Fetch:
|
||||
for {
|
||||
// TODO(miek): we could also be returning _QUOTE, this might or might not
|
||||
// be an issue (basically parsing TXT becomes hard)
|
||||
switch l = <-c; l.value {
|
||||
case zNewline, zEOF:
|
||||
break Fetch
|
||||
case zString:
|
||||
text = append(text, l.token)
|
||||
}
|
||||
}
|
||||
|
||||
err := rr.Data.Parse(text)
|
||||
if err != nil {
|
||||
return nil, &ParseError{f, err.Error(), l}, ""
|
||||
}
|
||||
|
||||
return rr, nil, ""
|
||||
}
|
||||
|
||||
typeToparserFunc[rtype] = parserFunc{setPrivateRR, true}
|
||||
}
|
||||
|
||||
// PrivateHandleRemove removes defenitions required to support private RR type.
|
||||
func PrivateHandleRemove(rtype uint16) {
|
||||
rtypestr, ok := TypeToString[rtype]
|
||||
if ok {
|
||||
delete(TypeToRR, rtype)
|
||||
delete(TypeToString, rtype)
|
||||
delete(typeToparserFunc, rtype)
|
||||
delete(StringToType, rtypestr)
|
||||
delete(typeToUnpack, rtype)
|
||||
}
|
||||
return
|
||||
}
|
||||
166
vendor/github.com/miekg/dns/privaterr_test.go
generated
vendored
Normal file
166
vendor/github.com/miekg/dns/privaterr_test.go
generated
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
package dns_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
const TypeISBN uint16 = 0xFF00
|
||||
|
||||
// A crazy new RR type :)
|
||||
type ISBN struct {
|
||||
x string // rdata with 10 or 13 numbers, dashes or spaces allowed
|
||||
}
|
||||
|
||||
func NewISBN() dns.PrivateRdata { return &ISBN{""} }
|
||||
|
||||
func (rd *ISBN) Len() int { return len([]byte(rd.x)) }
|
||||
func (rd *ISBN) String() string { return rd.x }
|
||||
|
||||
func (rd *ISBN) Parse(txt []string) error {
|
||||
rd.x = strings.TrimSpace(strings.Join(txt, " "))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rd *ISBN) Pack(buf []byte) (int, error) {
|
||||
b := []byte(rd.x)
|
||||
n := copy(buf, b)
|
||||
if n != len(b) {
|
||||
return n, dns.ErrBuf
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (rd *ISBN) Unpack(buf []byte) (int, error) {
|
||||
rd.x = string(buf)
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
func (rd *ISBN) Copy(dest dns.PrivateRdata) error {
|
||||
isbn, ok := dest.(*ISBN)
|
||||
if !ok {
|
||||
return dns.ErrRdata
|
||||
}
|
||||
isbn.x = rd.x
|
||||
return nil
|
||||
}
|
||||
|
||||
var testrecord = strings.Join([]string{"example.org.", "3600", "IN", "ISBN", "12-3 456789-0-123"}, "\t")
|
||||
|
||||
func TestPrivateText(t *testing.T) {
|
||||
dns.PrivateHandle("ISBN", TypeISBN, NewISBN)
|
||||
defer dns.PrivateHandleRemove(TypeISBN)
|
||||
|
||||
rr, err := dns.NewRR(testrecord)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if rr.String() != testrecord {
|
||||
t.Errorf("record string representation did not match original %#v != %#v", rr.String(), testrecord)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrivateByteSlice(t *testing.T) {
|
||||
dns.PrivateHandle("ISBN", TypeISBN, NewISBN)
|
||||
defer dns.PrivateHandleRemove(TypeISBN)
|
||||
|
||||
rr, err := dns.NewRR(testrecord)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
buf := make([]byte, 100)
|
||||
off, err := dns.PackRR(rr, buf, 0, nil, false)
|
||||
if err != nil {
|
||||
t.Errorf("got error packing ISBN: %v", err)
|
||||
}
|
||||
|
||||
custrr := rr.(*dns.PrivateRR)
|
||||
if ln := custrr.Data.Len() + len(custrr.Header().Name) + 11; ln != off {
|
||||
t.Errorf("offset is not matching to length of Private RR: %d!=%d", off, ln)
|
||||
}
|
||||
|
||||
rr1, off1, err := dns.UnpackRR(buf[:off], 0)
|
||||
if err != nil {
|
||||
t.Errorf("got error unpacking ISBN: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if off1 != off {
|
||||
t.Errorf("offset after unpacking differs: %d != %d", off1, off)
|
||||
}
|
||||
|
||||
if rr1.String() != testrecord {
|
||||
t.Errorf("record string representation did not match original %#v != %#v", rr1.String(), testrecord)
|
||||
}
|
||||
}
|
||||
|
||||
const TypeVERSION uint16 = 0xFF01
|
||||
|
||||
type VERSION struct {
|
||||
x string
|
||||
}
|
||||
|
||||
func NewVersion() dns.PrivateRdata { return &VERSION{""} }
|
||||
|
||||
func (rd *VERSION) String() string { return rd.x }
|
||||
func (rd *VERSION) Parse(txt []string) error {
|
||||
rd.x = strings.TrimSpace(strings.Join(txt, " "))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rd *VERSION) Pack(buf []byte) (int, error) {
|
||||
b := []byte(rd.x)
|
||||
n := copy(buf, b)
|
||||
if n != len(b) {
|
||||
return n, dns.ErrBuf
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (rd *VERSION) Unpack(buf []byte) (int, error) {
|
||||
rd.x = string(buf)
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
func (rd *VERSION) Copy(dest dns.PrivateRdata) error {
|
||||
isbn, ok := dest.(*VERSION)
|
||||
if !ok {
|
||||
return dns.ErrRdata
|
||||
}
|
||||
isbn.x = rd.x
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rd *VERSION) Len() int {
|
||||
return len([]byte(rd.x))
|
||||
}
|
||||
|
||||
var smallzone = `$ORIGIN example.org.
|
||||
@ 3600 IN SOA sns.dns.icann.org. noc.dns.icann.org. (
|
||||
2014091518 7200 3600 1209600 3600
|
||||
)
|
||||
A 1.2.3.4
|
||||
ok ISBN 1231-92110-12
|
||||
go VERSION (
|
||||
1.3.1 ; comment
|
||||
)
|
||||
www ISBN 1231-92110-16
|
||||
* CNAME @
|
||||
`
|
||||
|
||||
func TestPrivateZoneParser(t *testing.T) {
|
||||
dns.PrivateHandle("ISBN", TypeISBN, NewISBN)
|
||||
dns.PrivateHandle("VERSION", TypeVERSION, NewVersion)
|
||||
defer dns.PrivateHandleRemove(TypeISBN)
|
||||
defer dns.PrivateHandleRemove(TypeVERSION)
|
||||
|
||||
r := strings.NewReader(smallzone)
|
||||
for x := range dns.ParseZone(r, ".", "") {
|
||||
if err := x.Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
49
vendor/github.com/miekg/dns/rawmsg.go
generated
vendored
Normal file
49
vendor/github.com/miekg/dns/rawmsg.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
package dns
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
// rawSetRdlength sets the rdlength in the header of
|
||||
// the RR. The offset 'off' must be positioned at the
|
||||
// start of the header of the RR, 'end' must be the
|
||||
// end of the RR.
|
||||
func rawSetRdlength(msg []byte, off, end int) bool {
|
||||
l := len(msg)
|
||||
Loop:
|
||||
for {
|
||||
if off+1 > l {
|
||||
return false
|
||||
}
|
||||
c := int(msg[off])
|
||||
off++
|
||||
switch c & 0xC0 {
|
||||
case 0x00:
|
||||
if c == 0x00 {
|
||||
// End of the domainname
|
||||
break Loop
|
||||
}
|
||||
if off+c > l {
|
||||
return false
|
||||
}
|
||||
off += c
|
||||
|
||||
case 0xC0:
|
||||
// pointer, next byte included, ends domainname
|
||||
off++
|
||||
break Loop
|
||||
}
|
||||
}
|
||||
// The domainname has been seen, we at the start of the fixed part in the header.
|
||||
// Type is 2 bytes, class is 2 bytes, ttl 4 and then 2 bytes for the length.
|
||||
off += 2 + 2 + 4
|
||||
if off+2 > l {
|
||||
return false
|
||||
}
|
||||
//off+1 is the end of the header, 'end' is the end of the rr
|
||||
//so 'end' - 'off+2' is the length of the rdata
|
||||
rdatalen := end - (off + 2)
|
||||
if rdatalen > 0xFFFF {
|
||||
return false
|
||||
}
|
||||
binary.BigEndian.PutUint16(msg[off:], uint16(rdatalen))
|
||||
return true
|
||||
}
|
||||
19
vendor/github.com/miekg/dns/remote_test.go
generated
vendored
Normal file
19
vendor/github.com/miekg/dns/remote_test.go
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
package dns
|
||||
|
||||
import "testing"
|
||||
|
||||
const LinodeAddr = "176.58.119.54:53"
|
||||
|
||||
func TestClientRemote(t *testing.T) {
|
||||
m := new(Msg)
|
||||
m.SetQuestion("go.dns.miek.nl.", TypeTXT)
|
||||
|
||||
c := new(Client)
|
||||
r, _, err := c.Exchange(m, LinodeAddr)
|
||||
if err != nil {
|
||||
t.Errorf("failed to exchange: %v", err)
|
||||
}
|
||||
if r != nil && r.Rcode != RcodeSuccess {
|
||||
t.Errorf("failed to get an valid answer\n%v", r)
|
||||
}
|
||||
}
|
||||
38
vendor/github.com/miekg/dns/reverse.go
generated
vendored
Normal file
38
vendor/github.com/miekg/dns/reverse.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package dns
|
||||
|
||||
// StringToType is the reverse of TypeToString, needed for string parsing.
|
||||
var StringToType = reverseInt16(TypeToString)
|
||||
|
||||
// StringToClass is the reverse of ClassToString, needed for string parsing.
|
||||
var StringToClass = reverseInt16(ClassToString)
|
||||
|
||||
// StringToOpcode is a map of opcodes to strings.
|
||||
var StringToOpcode = reverseInt(OpcodeToString)
|
||||
|
||||
// StringToRcode is a map of rcodes to strings.
|
||||
var StringToRcode = reverseInt(RcodeToString)
|
||||
|
||||
// Reverse a map
|
||||
func reverseInt8(m map[uint8]string) map[string]uint8 {
|
||||
n := make(map[string]uint8, len(m))
|
||||
for u, s := range m {
|
||||
n[s] = u
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func reverseInt16(m map[uint16]string) map[string]uint16 {
|
||||
n := make(map[string]uint16, len(m))
|
||||
for u, s := range m {
|
||||
n[s] = u
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func reverseInt(m map[int]string) map[string]int {
|
||||
n := make(map[string]int, len(m))
|
||||
for u, s := range m {
|
||||
n[s] = u
|
||||
}
|
||||
return n
|
||||
}
|
||||
7
vendor/github.com/miekg/dns/rr_test.go
generated
vendored
Normal file
7
vendor/github.com/miekg/dns/rr_test.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
package dns
|
||||
|
||||
// testRR returns the RR from string s. The error is thrown away.
|
||||
func testRR(s string) RR {
|
||||
r, _ := NewRR(s)
|
||||
return r
|
||||
}
|
||||
84
vendor/github.com/miekg/dns/sanitize.go
generated
vendored
Normal file
84
vendor/github.com/miekg/dns/sanitize.go
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
package dns
|
||||
|
||||
// Dedup removes identical RRs from rrs. It preserves the original ordering.
|
||||
// The lowest TTL of any duplicates is used in the remaining one. Dedup modifies
|
||||
// rrs.
|
||||
// m is used to store the RRs temporary. If it is nil a new map will be allocated.
|
||||
func Dedup(rrs []RR, m map[string]RR) []RR {
|
||||
if m == nil {
|
||||
m = make(map[string]RR)
|
||||
}
|
||||
// Save the keys, so we don't have to call normalizedString twice.
|
||||
keys := make([]*string, 0, len(rrs))
|
||||
|
||||
for _, r := range rrs {
|
||||
key := normalizedString(r)
|
||||
keys = append(keys, &key)
|
||||
if _, ok := m[key]; ok {
|
||||
// Shortest TTL wins.
|
||||
if m[key].Header().Ttl > r.Header().Ttl {
|
||||
m[key].Header().Ttl = r.Header().Ttl
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
m[key] = r
|
||||
}
|
||||
// If the length of the result map equals the amount of RRs we got,
|
||||
// it means they were all different. We can then just return the original rrset.
|
||||
if len(m) == len(rrs) {
|
||||
return rrs
|
||||
}
|
||||
|
||||
j := 0
|
||||
for i, r := range rrs {
|
||||
// If keys[i] lives in the map, we should copy and remove it.
|
||||
if _, ok := m[*keys[i]]; ok {
|
||||
delete(m, *keys[i])
|
||||
rrs[j] = r
|
||||
j++
|
||||
}
|
||||
|
||||
if len(m) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return rrs[:j]
|
||||
}
|
||||
|
||||
// normalizedString returns a normalized string from r. The TTL
|
||||
// is removed and the domain name is lowercased. We go from this:
|
||||
// DomainName<TAB>TTL<TAB>CLASS<TAB>TYPE<TAB>RDATA to:
|
||||
// lowercasename<TAB>CLASS<TAB>TYPE...
|
||||
func normalizedString(r RR) string {
|
||||
// A string Go DNS makes has: domainname<TAB>TTL<TAB>...
|
||||
b := []byte(r.String())
|
||||
|
||||
// find the first non-escaped tab, then another, so we capture where the TTL lives.
|
||||
esc := false
|
||||
ttlStart, ttlEnd := 0, 0
|
||||
for i := 0; i < len(b) && ttlEnd == 0; i++ {
|
||||
switch {
|
||||
case b[i] == '\\':
|
||||
esc = !esc
|
||||
case b[i] == '\t' && !esc:
|
||||
if ttlStart == 0 {
|
||||
ttlStart = i
|
||||
continue
|
||||
}
|
||||
if ttlEnd == 0 {
|
||||
ttlEnd = i
|
||||
}
|
||||
case b[i] >= 'A' && b[i] <= 'Z' && !esc:
|
||||
b[i] += 32
|
||||
default:
|
||||
esc = false
|
||||
}
|
||||
}
|
||||
|
||||
// remove TTL.
|
||||
copy(b[ttlStart:], b[ttlEnd:])
|
||||
cut := ttlEnd - ttlStart
|
||||
return string(b[:len(b)-cut])
|
||||
}
|
||||
75
vendor/github.com/miekg/dns/sanitize_test.go
generated
vendored
Normal file
75
vendor/github.com/miekg/dns/sanitize_test.go
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
package dns
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestDedup(t *testing.T) {
|
||||
testcases := map[[3]RR][]string{
|
||||
[...]RR{
|
||||
testRR("mIek.nl. IN A 127.0.0.1"),
|
||||
testRR("mieK.nl. IN A 127.0.0.1"),
|
||||
testRR("miek.Nl. IN A 127.0.0.1"),
|
||||
}: {"mIek.nl.\t3600\tIN\tA\t127.0.0.1"},
|
||||
[...]RR{
|
||||
testRR("miEk.nl. 2000 IN A 127.0.0.1"),
|
||||
testRR("mieK.Nl. 1000 IN A 127.0.0.1"),
|
||||
testRR("Miek.nL. 500 IN A 127.0.0.1"),
|
||||
}: {"miEk.nl.\t500\tIN\tA\t127.0.0.1"},
|
||||
[...]RR{
|
||||
testRR("miek.nl. IN A 127.0.0.1"),
|
||||
testRR("miek.nl. CH A 127.0.0.1"),
|
||||
testRR("miek.nl. IN A 127.0.0.1"),
|
||||
}: {"miek.nl.\t3600\tIN\tA\t127.0.0.1",
|
||||
"miek.nl.\t3600\tCH\tA\t127.0.0.1",
|
||||
},
|
||||
[...]RR{
|
||||
testRR("miek.nl. CH A 127.0.0.1"),
|
||||
testRR("miek.nl. IN A 127.0.0.1"),
|
||||
testRR("miek.de. IN A 127.0.0.1"),
|
||||
}: {"miek.nl.\t3600\tCH\tA\t127.0.0.1",
|
||||
"miek.nl.\t3600\tIN\tA\t127.0.0.1",
|
||||
"miek.de.\t3600\tIN\tA\t127.0.0.1",
|
||||
},
|
||||
[...]RR{
|
||||
testRR("miek.de. IN A 127.0.0.1"),
|
||||
testRR("miek.nl. 200 IN A 127.0.0.1"),
|
||||
testRR("miek.nl. 300 IN A 127.0.0.1"),
|
||||
}: {"miek.de.\t3600\tIN\tA\t127.0.0.1",
|
||||
"miek.nl.\t200\tIN\tA\t127.0.0.1",
|
||||
},
|
||||
}
|
||||
|
||||
for rr, expected := range testcases {
|
||||
out := Dedup([]RR{rr[0], rr[1], rr[2]}, nil)
|
||||
for i, o := range out {
|
||||
if o.String() != expected[i] {
|
||||
t.Fatalf("expected %v, got %v", expected[i], o.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDedup(b *testing.B) {
|
||||
rrs := []RR{
|
||||
testRR("miEk.nl. 2000 IN A 127.0.0.1"),
|
||||
testRR("mieK.Nl. 1000 IN A 127.0.0.1"),
|
||||
testRR("Miek.nL. 500 IN A 127.0.0.1"),
|
||||
}
|
||||
m := make(map[string]RR)
|
||||
for i := 0; i < b.N; i++ {
|
||||
Dedup(rrs, m)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizedString(t *testing.T) {
|
||||
tests := map[RR]string{
|
||||
testRR("mIEk.Nl. 3600 IN A 127.0.0.1"): "miek.nl.\tIN\tA\t127.0.0.1",
|
||||
testRR("m\\ iek.nL. 3600 IN A 127.0.0.1"): "m\\ iek.nl.\tIN\tA\t127.0.0.1",
|
||||
testRR("m\\\tIeK.nl. 3600 in A 127.0.0.1"): "m\\009iek.nl.\tIN\tA\t127.0.0.1",
|
||||
}
|
||||
for tc, expected := range tests {
|
||||
n := normalizedString(tc)
|
||||
if n != expected {
|
||||
t.Errorf("expected %s, got %s", expected, n)
|
||||
}
|
||||
}
|
||||
}
|
||||
1007
vendor/github.com/miekg/dns/scan.go
generated
vendored
Normal file
1007
vendor/github.com/miekg/dns/scan.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2203
vendor/github.com/miekg/dns/scan_rr.go
generated
vendored
Normal file
2203
vendor/github.com/miekg/dns/scan_rr.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
48
vendor/github.com/miekg/dns/scan_test.go
generated
vendored
Normal file
48
vendor/github.com/miekg/dns/scan_test.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseZoneInclude(t *testing.T) {
|
||||
|
||||
tmpfile, err := ioutil.TempFile("", "dns")
|
||||
if err != nil {
|
||||
t.Fatalf("could not create tmpfile for test: %s", err)
|
||||
}
|
||||
|
||||
if _, err := tmpfile.WriteString("foo\tIN\tA\t127.0.0.1"); err != nil {
|
||||
t.Fatalf("unable to write content to tmpfile %q: %s", tmpfile.Name(), err)
|
||||
}
|
||||
if err := tmpfile.Close(); err != nil {
|
||||
t.Fatalf("could not close tmpfile %q: %s", tmpfile.Name(), err)
|
||||
}
|
||||
|
||||
zone := "$ORIGIN example.org.\n$INCLUDE " + tmpfile.Name()
|
||||
|
||||
tok := ParseZone(strings.NewReader(zone), "", "")
|
||||
for x := range tok {
|
||||
if x.Error != nil {
|
||||
t.Fatalf("expected no error, but got %s", x.Error)
|
||||
}
|
||||
if x.RR.Header().Name != "foo.example.org." {
|
||||
t.Fatalf("expected %s, but got %s", "foo.example.org.", x.RR.Header().Name)
|
||||
}
|
||||
}
|
||||
|
||||
os.Remove(tmpfile.Name())
|
||||
|
||||
tok = ParseZone(strings.NewReader(zone), "", "")
|
||||
for x := range tok {
|
||||
if x.Error == nil {
|
||||
t.Fatalf("expected first token to contain an error but it didn't")
|
||||
}
|
||||
if !strings.Contains(x.Error.Error(), "failed to open") ||
|
||||
!strings.Contains(x.Error.Error(), tmpfile.Name()) {
|
||||
t.Fatalf(`expected error to contain: "failed to open" and %q but got: %s`, tmpfile.Name(), x.Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
56
vendor/github.com/miekg/dns/scanner.go
generated
vendored
Normal file
56
vendor/github.com/miekg/dns/scanner.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
package dns
|
||||
|
||||
// Implement a simple scanner, return a byte stream from an io reader.
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"io"
|
||||
"text/scanner"
|
||||
)
|
||||
|
||||
type scan struct {
|
||||
src *bufio.Reader
|
||||
position scanner.Position
|
||||
eof bool // Have we just seen a eof
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func scanInit(r io.Reader) (*scan, context.CancelFunc) {
|
||||
s := new(scan)
|
||||
s.src = bufio.NewReader(r)
|
||||
s.position.Line = 1
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
s.ctx = ctx
|
||||
|
||||
return s, cancel
|
||||
}
|
||||
|
||||
// tokenText returns the next byte from the input
|
||||
func (s *scan) tokenText() (byte, error) {
|
||||
c, err := s.src.ReadByte()
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
select {
|
||||
case <-s.ctx.Done():
|
||||
return c, context.Canceled
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
// delay the newline handling until the next token is delivered,
|
||||
// fixes off-by-one errors when reporting a parse error.
|
||||
if s.eof == true {
|
||||
s.position.Line++
|
||||
s.position.Column = 0
|
||||
s.eof = false
|
||||
}
|
||||
if c == '\n' {
|
||||
s.eof = true
|
||||
return c, nil
|
||||
}
|
||||
s.position.Column++
|
||||
return c, nil
|
||||
}
|
||||
782
vendor/github.com/miekg/dns/server.go
generated
vendored
Normal file
782
vendor/github.com/miekg/dns/server.go
generated
vendored
Normal file
@@ -0,0 +1,782 @@
|
||||
// DNS server implementation.
|
||||
|
||||
package dns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Default maximum number of TCP queries before we close the socket.
|
||||
const maxTCPQueries = 128
|
||||
|
||||
// Interval for stop worker if no load
|
||||
const idleWorkerTimeout = 10 * time.Second
|
||||
|
||||
// Maximum number of workers
|
||||
const maxWorkersCount = 10000
|
||||
|
||||
// Handler is implemented by any value that implements ServeDNS.
|
||||
type Handler interface {
|
||||
ServeDNS(w ResponseWriter, r *Msg)
|
||||
}
|
||||
|
||||
// A ResponseWriter interface is used by an DNS handler to
|
||||
// construct an DNS response.
|
||||
type ResponseWriter interface {
|
||||
// LocalAddr returns the net.Addr of the server
|
||||
LocalAddr() net.Addr
|
||||
// RemoteAddr returns the net.Addr of the client that sent the current request.
|
||||
RemoteAddr() net.Addr
|
||||
// WriteMsg writes a reply back to the client.
|
||||
WriteMsg(*Msg) error
|
||||
// Write writes a raw buffer back to the client.
|
||||
Write([]byte) (int, error)
|
||||
// Close closes the connection.
|
||||
Close() error
|
||||
// TsigStatus returns the status of the Tsig.
|
||||
TsigStatus() error
|
||||
// TsigTimersOnly sets the tsig timers only boolean.
|
||||
TsigTimersOnly(bool)
|
||||
// Hijack lets the caller take over the connection.
|
||||
// After a call to Hijack(), the DNS package will not do anything with the connection.
|
||||
Hijack()
|
||||
}
|
||||
|
||||
type response struct {
|
||||
msg []byte
|
||||
hijacked bool // connection has been hijacked by handler
|
||||
tsigStatus error
|
||||
tsigTimersOnly bool
|
||||
tsigRequestMAC string
|
||||
tsigSecret map[string]string // the tsig secrets
|
||||
udp *net.UDPConn // i/o connection if UDP was used
|
||||
tcp net.Conn // i/o connection if TCP was used
|
||||
udpSession *SessionUDP // oob data to get egress interface right
|
||||
writer Writer // writer to output the raw DNS bits
|
||||
}
|
||||
|
||||
// ServeMux is an DNS request multiplexer. It matches the
|
||||
// zone name of each incoming request against a list of
|
||||
// registered patterns add calls the handler for the pattern
|
||||
// that most closely matches the zone name. ServeMux is DNSSEC aware, meaning
|
||||
// that queries for the DS record are redirected to the parent zone (if that
|
||||
// is also registered), otherwise the child gets the query.
|
||||
// ServeMux is also safe for concurrent access from multiple goroutines.
|
||||
type ServeMux struct {
|
||||
z map[string]Handler
|
||||
m *sync.RWMutex
|
||||
}
|
||||
|
||||
// NewServeMux allocates and returns a new ServeMux.
|
||||
func NewServeMux() *ServeMux { return &ServeMux{z: make(map[string]Handler), m: new(sync.RWMutex)} }
|
||||
|
||||
// DefaultServeMux is the default ServeMux used by Serve.
|
||||
var DefaultServeMux = NewServeMux()
|
||||
|
||||
// The HandlerFunc type is an adapter to allow the use of
|
||||
// ordinary functions as DNS handlers. If f is a function
|
||||
// with the appropriate signature, HandlerFunc(f) is a
|
||||
// Handler object that calls f.
|
||||
type HandlerFunc func(ResponseWriter, *Msg)
|
||||
|
||||
// ServeDNS calls f(w, r).
|
||||
func (f HandlerFunc) ServeDNS(w ResponseWriter, r *Msg) {
|
||||
f(w, r)
|
||||
}
|
||||
|
||||
// HandleFailed returns a HandlerFunc that returns SERVFAIL for every request it gets.
|
||||
func HandleFailed(w ResponseWriter, r *Msg) {
|
||||
m := new(Msg)
|
||||
m.SetRcode(r, RcodeServerFailure)
|
||||
// does not matter if this write fails
|
||||
w.WriteMsg(m)
|
||||
}
|
||||
|
||||
func failedHandler() Handler { return HandlerFunc(HandleFailed) }
|
||||
|
||||
// ListenAndServe Starts a server on address and network specified Invoke handler
|
||||
// for incoming queries.
|
||||
func ListenAndServe(addr string, network string, handler Handler) error {
|
||||
server := &Server{Addr: addr, Net: network, Handler: handler}
|
||||
return server.ListenAndServe()
|
||||
}
|
||||
|
||||
// ListenAndServeTLS acts like http.ListenAndServeTLS, more information in
|
||||
// http://golang.org/pkg/net/http/#ListenAndServeTLS
|
||||
func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error {
|
||||
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config := tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
}
|
||||
|
||||
server := &Server{
|
||||
Addr: addr,
|
||||
Net: "tcp-tls",
|
||||
TLSConfig: &config,
|
||||
Handler: handler,
|
||||
}
|
||||
|
||||
return server.ListenAndServe()
|
||||
}
|
||||
|
||||
// ActivateAndServe activates a server with a listener from systemd,
|
||||
// l and p should not both be non-nil.
|
||||
// If both l and p are not nil only p will be used.
|
||||
// Invoke handler for incoming queries.
|
||||
func ActivateAndServe(l net.Listener, p net.PacketConn, handler Handler) error {
|
||||
server := &Server{Listener: l, PacketConn: p, Handler: handler}
|
||||
return server.ActivateAndServe()
|
||||
}
|
||||
|
||||
func (mux *ServeMux) match(q string, t uint16) Handler {
|
||||
mux.m.RLock()
|
||||
defer mux.m.RUnlock()
|
||||
var handler Handler
|
||||
b := make([]byte, len(q)) // worst case, one label of length q
|
||||
off := 0
|
||||
end := false
|
||||
for {
|
||||
l := len(q[off:])
|
||||
for i := 0; i < l; i++ {
|
||||
b[i] = q[off+i]
|
||||
if b[i] >= 'A' && b[i] <= 'Z' {
|
||||
b[i] |= ('a' - 'A')
|
||||
}
|
||||
}
|
||||
if h, ok := mux.z[string(b[:l])]; ok { // causes garbage, might want to change the map key
|
||||
if t != TypeDS {
|
||||
return h
|
||||
}
|
||||
// Continue for DS to see if we have a parent too, if so delegeate to the parent
|
||||
handler = h
|
||||
}
|
||||
off, end = NextLabel(q, off)
|
||||
if end {
|
||||
break
|
||||
}
|
||||
}
|
||||
// Wildcard match, if we have found nothing try the root zone as a last resort.
|
||||
if h, ok := mux.z["."]; ok {
|
||||
return h
|
||||
}
|
||||
return handler
|
||||
}
|
||||
|
||||
// Handle adds a handler to the ServeMux for pattern.
|
||||
func (mux *ServeMux) Handle(pattern string, handler Handler) {
|
||||
if pattern == "" {
|
||||
panic("dns: invalid pattern " + pattern)
|
||||
}
|
||||
mux.m.Lock()
|
||||
mux.z[Fqdn(pattern)] = handler
|
||||
mux.m.Unlock()
|
||||
}
|
||||
|
||||
// HandleFunc adds a handler function to the ServeMux for pattern.
|
||||
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) {
|
||||
mux.Handle(pattern, HandlerFunc(handler))
|
||||
}
|
||||
|
||||
// HandleRemove deregistrars the handler specific for pattern from the ServeMux.
|
||||
func (mux *ServeMux) HandleRemove(pattern string) {
|
||||
if pattern == "" {
|
||||
panic("dns: invalid pattern " + pattern)
|
||||
}
|
||||
mux.m.Lock()
|
||||
delete(mux.z, Fqdn(pattern))
|
||||
mux.m.Unlock()
|
||||
}
|
||||
|
||||
// ServeDNS dispatches the request to the handler whose
|
||||
// pattern most closely matches the request message. If DefaultServeMux
|
||||
// is used the correct thing for DS queries is done: a possible parent
|
||||
// is sought.
|
||||
// If no handler is found a standard SERVFAIL message is returned
|
||||
// If the request message does not have exactly one question in the
|
||||
// question section a SERVFAIL is returned, unlesss Unsafe is true.
|
||||
func (mux *ServeMux) ServeDNS(w ResponseWriter, request *Msg) {
|
||||
var h Handler
|
||||
if len(request.Question) < 1 { // allow more than one question
|
||||
h = failedHandler()
|
||||
} else {
|
||||
if h = mux.match(request.Question[0].Name, request.Question[0].Qtype); h == nil {
|
||||
h = failedHandler()
|
||||
}
|
||||
}
|
||||
h.ServeDNS(w, request)
|
||||
}
|
||||
|
||||
// Handle registers the handler with the given pattern
|
||||
// in the DefaultServeMux. The documentation for
|
||||
// ServeMux explains how patterns are matched.
|
||||
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }
|
||||
|
||||
// HandleRemove deregisters the handle with the given pattern
|
||||
// in the DefaultServeMux.
|
||||
func HandleRemove(pattern string) { DefaultServeMux.HandleRemove(pattern) }
|
||||
|
||||
// HandleFunc registers the handler function with the given pattern
|
||||
// in the DefaultServeMux.
|
||||
func HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) {
|
||||
DefaultServeMux.HandleFunc(pattern, handler)
|
||||
}
|
||||
|
||||
// Writer writes raw DNS messages; each call to Write should send an entire message.
|
||||
type Writer interface {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
// Reader reads raw DNS messages; each call to ReadTCP or ReadUDP should return an entire message.
|
||||
type Reader interface {
|
||||
// ReadTCP reads a raw message from a TCP connection. Implementations may alter
|
||||
// connection properties, for example the read-deadline.
|
||||
ReadTCP(conn net.Conn, timeout time.Duration) ([]byte, error)
|
||||
// ReadUDP reads a raw message from a UDP connection. Implementations may alter
|
||||
// connection properties, for example the read-deadline.
|
||||
ReadUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error)
|
||||
}
|
||||
|
||||
// defaultReader is an adapter for the Server struct that implements the Reader interface
|
||||
// using the readTCP and readUDP func of the embedded Server.
|
||||
type defaultReader struct {
|
||||
*Server
|
||||
}
|
||||
|
||||
func (dr *defaultReader) ReadTCP(conn net.Conn, timeout time.Duration) ([]byte, error) {
|
||||
return dr.readTCP(conn, timeout)
|
||||
}
|
||||
|
||||
func (dr *defaultReader) ReadUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error) {
|
||||
return dr.readUDP(conn, timeout)
|
||||
}
|
||||
|
||||
// DecorateReader is a decorator hook for extending or supplanting the functionality of a Reader.
|
||||
// Implementations should never return a nil Reader.
|
||||
type DecorateReader func(Reader) Reader
|
||||
|
||||
// DecorateWriter is a decorator hook for extending or supplanting the functionality of a Writer.
|
||||
// Implementations should never return a nil Writer.
|
||||
type DecorateWriter func(Writer) Writer
|
||||
|
||||
// A Server defines parameters for running an DNS server.
|
||||
type Server struct {
|
||||
// Address to listen on, ":dns" if empty.
|
||||
Addr string
|
||||
// if "tcp" or "tcp-tls" (DNS over TLS) it will invoke a TCP listener, otherwise an UDP one
|
||||
Net string
|
||||
// TCP Listener to use, this is to aid in systemd's socket activation.
|
||||
Listener net.Listener
|
||||
// TLS connection configuration
|
||||
TLSConfig *tls.Config
|
||||
// UDP "Listener" to use, this is to aid in systemd's socket activation.
|
||||
PacketConn net.PacketConn
|
||||
// Handler to invoke, dns.DefaultServeMux if nil.
|
||||
Handler Handler
|
||||
// Default buffer size to use to read incoming UDP messages. If not set
|
||||
// it defaults to MinMsgSize (512 B).
|
||||
UDPSize int
|
||||
// The net.Conn.SetReadTimeout value for new connections, defaults to 2 * time.Second.
|
||||
ReadTimeout time.Duration
|
||||
// The net.Conn.SetWriteTimeout value for new connections, defaults to 2 * time.Second.
|
||||
WriteTimeout time.Duration
|
||||
// TCP idle timeout for multiple queries, if nil, defaults to 8 * time.Second (RFC 5966).
|
||||
IdleTimeout func() time.Duration
|
||||
// Secret(s) for Tsig map[<zonename>]<base64 secret>. The zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2).
|
||||
TsigSecret map[string]string
|
||||
// Unsafe instructs the server to disregard any sanity checks and directly hand the message to
|
||||
// the handler. It will specifically not check if the query has the QR bit not set.
|
||||
Unsafe bool
|
||||
// If NotifyStartedFunc is set it is called once the server has started listening.
|
||||
NotifyStartedFunc func()
|
||||
// DecorateReader is optional, allows customization of the process that reads raw DNS messages.
|
||||
DecorateReader DecorateReader
|
||||
// DecorateWriter is optional, allows customization of the process that writes raw DNS messages.
|
||||
DecorateWriter DecorateWriter
|
||||
// Maximum number of TCP queries before we close the socket. Default is maxTCPQueries (unlimited if -1).
|
||||
MaxTCPQueries int
|
||||
|
||||
// UDP packet or TCP connection queue
|
||||
queue chan *response
|
||||
// Workers count
|
||||
workersCount int32
|
||||
// Shutdown handling
|
||||
lock sync.RWMutex
|
||||
started bool
|
||||
}
|
||||
|
||||
func (srv *Server) worker(w *response) {
|
||||
srv.serve(w)
|
||||
|
||||
for {
|
||||
count := atomic.LoadInt32(&srv.workersCount)
|
||||
if count > maxWorkersCount {
|
||||
return
|
||||
}
|
||||
if atomic.CompareAndSwapInt32(&srv.workersCount, count, count+1) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
defer atomic.AddInt32(&srv.workersCount, -1)
|
||||
|
||||
inUse := false
|
||||
timeout := time.NewTimer(idleWorkerTimeout)
|
||||
defer timeout.Stop()
|
||||
LOOP:
|
||||
for {
|
||||
select {
|
||||
case w, ok := <-srv.queue:
|
||||
if !ok {
|
||||
break LOOP
|
||||
}
|
||||
inUse = true
|
||||
srv.serve(w)
|
||||
case <-timeout.C:
|
||||
if !inUse {
|
||||
break LOOP
|
||||
}
|
||||
inUse = false
|
||||
timeout.Reset(idleWorkerTimeout)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Server) spawnWorker(w *response) {
|
||||
select {
|
||||
case srv.queue <- w:
|
||||
default:
|
||||
go srv.worker(w)
|
||||
}
|
||||
}
|
||||
|
||||
// ListenAndServe starts a nameserver on the configured address in *Server.
|
||||
func (srv *Server) ListenAndServe() error {
|
||||
srv.lock.Lock()
|
||||
defer srv.lock.Unlock()
|
||||
if srv.started {
|
||||
return &Error{err: "server already started"}
|
||||
}
|
||||
|
||||
addr := srv.Addr
|
||||
if addr == "" {
|
||||
addr = ":domain"
|
||||
}
|
||||
if srv.UDPSize == 0 {
|
||||
srv.UDPSize = MinMsgSize
|
||||
}
|
||||
srv.queue = make(chan *response)
|
||||
defer close(srv.queue)
|
||||
switch srv.Net {
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
a, err := net.ResolveTCPAddr(srv.Net, addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l, err := net.ListenTCP(srv.Net, a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srv.Listener = l
|
||||
srv.started = true
|
||||
srv.lock.Unlock()
|
||||
err = srv.serveTCP(l)
|
||||
srv.lock.Lock() // to satisfy the defer at the top
|
||||
return err
|
||||
case "tcp-tls", "tcp4-tls", "tcp6-tls":
|
||||
network := "tcp"
|
||||
if srv.Net == "tcp4-tls" {
|
||||
network = "tcp4"
|
||||
} else if srv.Net == "tcp6-tls" {
|
||||
network = "tcp6"
|
||||
}
|
||||
|
||||
l, err := tls.Listen(network, addr, srv.TLSConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srv.Listener = l
|
||||
srv.started = true
|
||||
srv.lock.Unlock()
|
||||
err = srv.serveTCP(l)
|
||||
srv.lock.Lock() // to satisfy the defer at the top
|
||||
return err
|
||||
case "udp", "udp4", "udp6":
|
||||
a, err := net.ResolveUDPAddr(srv.Net, addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l, err := net.ListenUDP(srv.Net, a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if e := setUDPSocketOptions(l); e != nil {
|
||||
return e
|
||||
}
|
||||
srv.PacketConn = l
|
||||
srv.started = true
|
||||
srv.lock.Unlock()
|
||||
err = srv.serveUDP(l)
|
||||
srv.lock.Lock() // to satisfy the defer at the top
|
||||
return err
|
||||
}
|
||||
return &Error{err: "bad network"}
|
||||
}
|
||||
|
||||
// ActivateAndServe starts a nameserver with the PacketConn or Listener
|
||||
// configured in *Server. Its main use is to start a server from systemd.
|
||||
func (srv *Server) ActivateAndServe() error {
|
||||
srv.lock.Lock()
|
||||
defer srv.lock.Unlock()
|
||||
if srv.started {
|
||||
return &Error{err: "server already started"}
|
||||
}
|
||||
|
||||
pConn := srv.PacketConn
|
||||
l := srv.Listener
|
||||
srv.queue = make(chan *response)
|
||||
defer close(srv.queue)
|
||||
if pConn != nil {
|
||||
if srv.UDPSize == 0 {
|
||||
srv.UDPSize = MinMsgSize
|
||||
}
|
||||
// Check PacketConn interface's type is valid and value
|
||||
// is not nil
|
||||
if t, ok := pConn.(*net.UDPConn); ok && t != nil {
|
||||
if e := setUDPSocketOptions(t); e != nil {
|
||||
return e
|
||||
}
|
||||
srv.started = true
|
||||
srv.lock.Unlock()
|
||||
e := srv.serveUDP(t)
|
||||
srv.lock.Lock() // to satisfy the defer at the top
|
||||
return e
|
||||
}
|
||||
}
|
||||
if l != nil {
|
||||
srv.started = true
|
||||
srv.lock.Unlock()
|
||||
e := srv.serveTCP(l)
|
||||
srv.lock.Lock() // to satisfy the defer at the top
|
||||
return e
|
||||
}
|
||||
return &Error{err: "bad listeners"}
|
||||
}
|
||||
|
||||
// Shutdown shuts down a server. After a call to Shutdown, ListenAndServe and
|
||||
// ActivateAndServe will return.
|
||||
func (srv *Server) Shutdown() error {
|
||||
srv.lock.Lock()
|
||||
if !srv.started {
|
||||
srv.lock.Unlock()
|
||||
return &Error{err: "server not started"}
|
||||
}
|
||||
srv.started = false
|
||||
srv.lock.Unlock()
|
||||
|
||||
if srv.PacketConn != nil {
|
||||
srv.PacketConn.Close()
|
||||
}
|
||||
if srv.Listener != nil {
|
||||
srv.Listener.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getReadTimeout is a helper func to use system timeout if server did not intend to change it.
|
||||
func (srv *Server) getReadTimeout() time.Duration {
|
||||
rtimeout := dnsTimeout
|
||||
if srv.ReadTimeout != 0 {
|
||||
rtimeout = srv.ReadTimeout
|
||||
}
|
||||
return rtimeout
|
||||
}
|
||||
|
||||
// serveTCP starts a TCP listener for the server.
|
||||
func (srv *Server) serveTCP(l net.Listener) error {
|
||||
defer l.Close()
|
||||
|
||||
if srv.NotifyStartedFunc != nil {
|
||||
srv.NotifyStartedFunc()
|
||||
}
|
||||
|
||||
for {
|
||||
rw, err := l.Accept()
|
||||
srv.lock.RLock()
|
||||
if !srv.started {
|
||||
srv.lock.RUnlock()
|
||||
return nil
|
||||
}
|
||||
srv.lock.RUnlock()
|
||||
if err != nil {
|
||||
if neterr, ok := err.(net.Error); ok && neterr.Temporary() {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
srv.spawnWorker(&response{tsigSecret: srv.TsigSecret, tcp: rw})
|
||||
}
|
||||
}
|
||||
|
||||
// serveUDP starts a UDP listener for the server.
|
||||
func (srv *Server) serveUDP(l *net.UDPConn) error {
|
||||
defer l.Close()
|
||||
|
||||
if srv.NotifyStartedFunc != nil {
|
||||
srv.NotifyStartedFunc()
|
||||
}
|
||||
|
||||
reader := Reader(&defaultReader{srv})
|
||||
if srv.DecorateReader != nil {
|
||||
reader = srv.DecorateReader(reader)
|
||||
}
|
||||
|
||||
rtimeout := srv.getReadTimeout()
|
||||
// deadline is not used here
|
||||
for {
|
||||
m, s, err := reader.ReadUDP(l, rtimeout)
|
||||
srv.lock.RLock()
|
||||
if !srv.started {
|
||||
srv.lock.RUnlock()
|
||||
return nil
|
||||
}
|
||||
srv.lock.RUnlock()
|
||||
if err != nil {
|
||||
if netErr, ok := err.(net.Error); ok && netErr.Temporary() {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
if len(m) < headerSize {
|
||||
continue
|
||||
}
|
||||
srv.spawnWorker(&response{msg: m, tsigSecret: srv.TsigSecret, udp: l, udpSession: s})
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Server) serve(w *response) {
|
||||
if srv.DecorateWriter != nil {
|
||||
w.writer = srv.DecorateWriter(w)
|
||||
} else {
|
||||
w.writer = w
|
||||
}
|
||||
|
||||
if w.udp != nil {
|
||||
// serve UDP
|
||||
srv.serveDNS(w)
|
||||
return
|
||||
}
|
||||
|
||||
reader := Reader(&defaultReader{srv})
|
||||
if srv.DecorateReader != nil {
|
||||
reader = srv.DecorateReader(reader)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if !w.hijacked {
|
||||
w.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
idleTimeout := tcpIdleTimeout
|
||||
if srv.IdleTimeout != nil {
|
||||
idleTimeout = srv.IdleTimeout()
|
||||
}
|
||||
|
||||
timeout := srv.getReadTimeout()
|
||||
|
||||
limit := srv.MaxTCPQueries
|
||||
if limit == 0 {
|
||||
limit = maxTCPQueries
|
||||
}
|
||||
|
||||
for q := 0; q < limit || limit == -1; q++ {
|
||||
var err error
|
||||
w.msg, err = reader.ReadTCP(w.tcp, timeout)
|
||||
if err != nil {
|
||||
// TODO(tmthrgd): handle error
|
||||
break
|
||||
}
|
||||
srv.serveDNS(w)
|
||||
if w.tcp == nil {
|
||||
break // Close() was called
|
||||
}
|
||||
if w.hijacked {
|
||||
break // client will call Close() themselves
|
||||
}
|
||||
// The first read uses the read timeout, the rest use the
|
||||
// idle timeout.
|
||||
timeout = idleTimeout
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Server) serveDNS(w *response) {
|
||||
req := new(Msg)
|
||||
err := req.Unpack(w.msg)
|
||||
if err != nil { // Send a FormatError back
|
||||
x := new(Msg)
|
||||
x.SetRcodeFormatError(req)
|
||||
w.WriteMsg(x)
|
||||
return
|
||||
}
|
||||
if !srv.Unsafe && req.Response {
|
||||
return
|
||||
}
|
||||
|
||||
w.tsigStatus = nil
|
||||
if w.tsigSecret != nil {
|
||||
if t := req.IsTsig(); t != nil {
|
||||
if secret, ok := w.tsigSecret[t.Hdr.Name]; ok {
|
||||
w.tsigStatus = TsigVerify(w.msg, secret, "", false)
|
||||
} else {
|
||||
w.tsigStatus = ErrSecret
|
||||
}
|
||||
w.tsigTimersOnly = false
|
||||
w.tsigRequestMAC = req.Extra[len(req.Extra)-1].(*TSIG).MAC
|
||||
}
|
||||
}
|
||||
|
||||
handler := srv.Handler
|
||||
if handler == nil {
|
||||
handler = DefaultServeMux
|
||||
}
|
||||
|
||||
handler.ServeDNS(w, req) // Writes back to the client
|
||||
}
|
||||
|
||||
func (srv *Server) readTCP(conn net.Conn, timeout time.Duration) ([]byte, error) {
|
||||
conn.SetReadDeadline(time.Now().Add(timeout))
|
||||
l := make([]byte, 2)
|
||||
n, err := conn.Read(l)
|
||||
if err != nil || n != 2 {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, ErrShortRead
|
||||
}
|
||||
length := binary.BigEndian.Uint16(l)
|
||||
if length == 0 {
|
||||
return nil, ErrShortRead
|
||||
}
|
||||
m := make([]byte, int(length))
|
||||
n, err = conn.Read(m[:int(length)])
|
||||
if err != nil || n == 0 {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, ErrShortRead
|
||||
}
|
||||
i := n
|
||||
for i < int(length) {
|
||||
j, err := conn.Read(m[i:int(length)])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
i += j
|
||||
}
|
||||
n = i
|
||||
m = m[:n]
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (srv *Server) readUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error) {
|
||||
conn.SetReadDeadline(time.Now().Add(timeout))
|
||||
m := make([]byte, srv.UDPSize)
|
||||
n, s, err := ReadFromSessionUDP(conn, m)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
m = m[:n]
|
||||
return m, s, nil
|
||||
}
|
||||
|
||||
// WriteMsg implements the ResponseWriter.WriteMsg method.
|
||||
func (w *response) WriteMsg(m *Msg) (err error) {
|
||||
var data []byte
|
||||
if w.tsigSecret != nil { // if no secrets, dont check for the tsig (which is a longer check)
|
||||
if t := m.IsTsig(); t != nil {
|
||||
data, w.tsigRequestMAC, err = TsigGenerate(m, w.tsigSecret[t.Hdr.Name], w.tsigRequestMAC, w.tsigTimersOnly)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.writer.Write(data)
|
||||
return err
|
||||
}
|
||||
}
|
||||
data, err = m.Pack()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.writer.Write(data)
|
||||
return err
|
||||
}
|
||||
|
||||
// Write implements the ResponseWriter.Write method.
|
||||
func (w *response) Write(m []byte) (int, error) {
|
||||
switch {
|
||||
case w.udp != nil:
|
||||
n, err := WriteToSessionUDP(w.udp, m, w.udpSession)
|
||||
return n, err
|
||||
case w.tcp != nil:
|
||||
lm := len(m)
|
||||
if lm < 2 {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
if lm > MaxMsgSize {
|
||||
return 0, &Error{err: "message too large"}
|
||||
}
|
||||
l := make([]byte, 2, 2+lm)
|
||||
binary.BigEndian.PutUint16(l, uint16(lm))
|
||||
m = append(l, m...)
|
||||
|
||||
n, err := io.Copy(w.tcp, bytes.NewReader(m))
|
||||
return int(n), err
|
||||
}
|
||||
panic("not reached")
|
||||
}
|
||||
|
||||
// LocalAddr implements the ResponseWriter.LocalAddr method.
|
||||
func (w *response) LocalAddr() net.Addr {
|
||||
if w.tcp != nil {
|
||||
return w.tcp.LocalAddr()
|
||||
}
|
||||
return w.udp.LocalAddr()
|
||||
}
|
||||
|
||||
// RemoteAddr implements the ResponseWriter.RemoteAddr method.
|
||||
func (w *response) RemoteAddr() net.Addr {
|
||||
if w.tcp != nil {
|
||||
return w.tcp.RemoteAddr()
|
||||
}
|
||||
return w.udpSession.RemoteAddr()
|
||||
}
|
||||
|
||||
// TsigStatus implements the ResponseWriter.TsigStatus method.
|
||||
func (w *response) TsigStatus() error { return w.tsigStatus }
|
||||
|
||||
// TsigTimersOnly implements the ResponseWriter.TsigTimersOnly method.
|
||||
func (w *response) TsigTimersOnly(b bool) { w.tsigTimersOnly = b }
|
||||
|
||||
// Hijack implements the ResponseWriter.Hijack method.
|
||||
func (w *response) Hijack() { w.hijacked = true }
|
||||
|
||||
// Close implements the ResponseWriter.Close method
|
||||
func (w *response) Close() error {
|
||||
// Can't close the udp conn, as that is actually the listener.
|
||||
if w.tcp != nil {
|
||||
e := w.tcp.Close()
|
||||
w.tcp = nil
|
||||
return e
|
||||
}
|
||||
return nil
|
||||
}
|
||||
792
vendor/github.com/miekg/dns/server_test.go
generated
vendored
Normal file
792
vendor/github.com/miekg/dns/server_test.go
generated
vendored
Normal file
@@ -0,0 +1,792 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func HelloServer(w ResponseWriter, req *Msg) {
|
||||
m := new(Msg)
|
||||
m.SetReply(req)
|
||||
|
||||
m.Extra = make([]RR, 1)
|
||||
m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
|
||||
w.WriteMsg(m)
|
||||
}
|
||||
|
||||
func HelloServerBadID(w ResponseWriter, req *Msg) {
|
||||
m := new(Msg)
|
||||
m.SetReply(req)
|
||||
m.Id++
|
||||
|
||||
m.Extra = make([]RR, 1)
|
||||
m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
|
||||
w.WriteMsg(m)
|
||||
}
|
||||
|
||||
func HelloServerEchoAddrPort(w ResponseWriter, req *Msg) {
|
||||
m := new(Msg)
|
||||
m.SetReply(req)
|
||||
|
||||
remoteAddr := w.RemoteAddr().String()
|
||||
m.Extra = make([]RR, 1)
|
||||
m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{remoteAddr}}
|
||||
w.WriteMsg(m)
|
||||
}
|
||||
|
||||
func AnotherHelloServer(w ResponseWriter, req *Msg) {
|
||||
m := new(Msg)
|
||||
m.SetReply(req)
|
||||
|
||||
m.Extra = make([]RR, 1)
|
||||
m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello example"}}
|
||||
w.WriteMsg(m)
|
||||
}
|
||||
|
||||
func RunLocalUDPServer(laddr string) (*Server, string, error) {
|
||||
server, l, _, err := RunLocalUDPServerWithFinChan(laddr)
|
||||
|
||||
return server, l, err
|
||||
}
|
||||
|
||||
func RunLocalUDPServerWithFinChan(laddr string) (*Server, string, chan error, error) {
|
||||
pc, err := net.ListenPacket("udp", laddr)
|
||||
if err != nil {
|
||||
return nil, "", nil, err
|
||||
}
|
||||
server := &Server{PacketConn: pc, ReadTimeout: time.Hour, WriteTimeout: time.Hour}
|
||||
|
||||
waitLock := sync.Mutex{}
|
||||
waitLock.Lock()
|
||||
server.NotifyStartedFunc = waitLock.Unlock
|
||||
|
||||
// fin must be buffered so the goroutine below won't block
|
||||
// forever if fin is never read from. This always happens
|
||||
// in RunLocalUDPServer and can happen in TestShutdownUDP.
|
||||
fin := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
fin <- server.ActivateAndServe()
|
||||
pc.Close()
|
||||
}()
|
||||
|
||||
waitLock.Lock()
|
||||
return server, pc.LocalAddr().String(), fin, nil
|
||||
}
|
||||
|
||||
func RunLocalUDPServerUnsafe(laddr string) (*Server, string, error) {
|
||||
pc, err := net.ListenPacket("udp", laddr)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
server := &Server{PacketConn: pc, Unsafe: true,
|
||||
ReadTimeout: time.Hour, WriteTimeout: time.Hour}
|
||||
|
||||
waitLock := sync.Mutex{}
|
||||
waitLock.Lock()
|
||||
server.NotifyStartedFunc = waitLock.Unlock
|
||||
|
||||
go func() {
|
||||
server.ActivateAndServe()
|
||||
pc.Close()
|
||||
}()
|
||||
|
||||
waitLock.Lock()
|
||||
return server, pc.LocalAddr().String(), nil
|
||||
}
|
||||
|
||||
func RunLocalTCPServer(laddr string) (*Server, string, error) {
|
||||
server, l, _, err := RunLocalTCPServerWithFinChan(laddr)
|
||||
|
||||
return server, l, err
|
||||
}
|
||||
|
||||
func RunLocalTCPServerWithFinChan(laddr string) (*Server, string, chan error, error) {
|
||||
l, err := net.Listen("tcp", laddr)
|
||||
if err != nil {
|
||||
return nil, "", nil, err
|
||||
}
|
||||
|
||||
server := &Server{Listener: l, ReadTimeout: time.Hour, WriteTimeout: time.Hour}
|
||||
|
||||
waitLock := sync.Mutex{}
|
||||
waitLock.Lock()
|
||||
server.NotifyStartedFunc = waitLock.Unlock
|
||||
|
||||
// See the comment in RunLocalUDPServerWithFinChan as to
|
||||
// why fin must be buffered.
|
||||
fin := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
fin <- server.ActivateAndServe()
|
||||
l.Close()
|
||||
}()
|
||||
|
||||
waitLock.Lock()
|
||||
return server, l.Addr().String(), fin, nil
|
||||
}
|
||||
|
||||
func RunLocalTLSServer(laddr string, config *tls.Config) (*Server, string, error) {
|
||||
l, err := tls.Listen("tcp", laddr, config)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
server := &Server{Listener: l, ReadTimeout: time.Hour, WriteTimeout: time.Hour}
|
||||
|
||||
waitLock := sync.Mutex{}
|
||||
waitLock.Lock()
|
||||
server.NotifyStartedFunc = waitLock.Unlock
|
||||
|
||||
go func() {
|
||||
server.ActivateAndServe()
|
||||
l.Close()
|
||||
}()
|
||||
|
||||
waitLock.Lock()
|
||||
return server, l.Addr().String(), nil
|
||||
}
|
||||
|
||||
func TestServing(t *testing.T) {
|
||||
HandleFunc("miek.nl.", HelloServer)
|
||||
HandleFunc("example.com.", AnotherHelloServer)
|
||||
defer HandleRemove("miek.nl.")
|
||||
defer HandleRemove("example.com.")
|
||||
|
||||
s, addrstr, err := RunLocalUDPServer(":0")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to run test server: %v", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
c := new(Client)
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl.", TypeTXT)
|
||||
r, _, err := c.Exchange(m, addrstr)
|
||||
if err != nil || len(r.Extra) == 0 {
|
||||
t.Fatal("failed to exchange miek.nl", err)
|
||||
}
|
||||
txt := r.Extra[0].(*TXT).Txt[0]
|
||||
if txt != "Hello world" {
|
||||
t.Error("unexpected result for miek.nl", txt, "!= Hello world")
|
||||
}
|
||||
|
||||
m.SetQuestion("example.com.", TypeTXT)
|
||||
r, _, err = c.Exchange(m, addrstr)
|
||||
if err != nil {
|
||||
t.Fatal("failed to exchange example.com", err)
|
||||
}
|
||||
txt = r.Extra[0].(*TXT).Txt[0]
|
||||
if txt != "Hello example" {
|
||||
t.Error("unexpected result for example.com", txt, "!= Hello example")
|
||||
}
|
||||
|
||||
// Test Mixes cased as noticed by Ask.
|
||||
m.SetQuestion("eXaMplE.cOm.", TypeTXT)
|
||||
r, _, err = c.Exchange(m, addrstr)
|
||||
if err != nil {
|
||||
t.Error("failed to exchange eXaMplE.cOm", err)
|
||||
}
|
||||
txt = r.Extra[0].(*TXT).Txt[0]
|
||||
if txt != "Hello example" {
|
||||
t.Error("unexpected result for example.com", txt, "!= Hello example")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServingTLS(t *testing.T) {
|
||||
HandleFunc("miek.nl.", HelloServer)
|
||||
HandleFunc("example.com.", AnotherHelloServer)
|
||||
defer HandleRemove("miek.nl.")
|
||||
defer HandleRemove("example.com.")
|
||||
|
||||
cert, err := tls.X509KeyPair(CertPEMBlock, KeyPEMBlock)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to build certificate: %v", err)
|
||||
}
|
||||
|
||||
config := tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
}
|
||||
|
||||
s, addrstr, err := RunLocalTLSServer(":0", &config)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to run test server: %v", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
c := new(Client)
|
||||
c.Net = "tcp-tls"
|
||||
c.TLSConfig = &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl.", TypeTXT)
|
||||
r, _, err := c.Exchange(m, addrstr)
|
||||
if err != nil || len(r.Extra) == 0 {
|
||||
t.Fatal("failed to exchange miek.nl", err)
|
||||
}
|
||||
txt := r.Extra[0].(*TXT).Txt[0]
|
||||
if txt != "Hello world" {
|
||||
t.Error("unexpected result for miek.nl", txt, "!= Hello world")
|
||||
}
|
||||
|
||||
m.SetQuestion("example.com.", TypeTXT)
|
||||
r, _, err = c.Exchange(m, addrstr)
|
||||
if err != nil {
|
||||
t.Fatal("failed to exchange example.com", err)
|
||||
}
|
||||
txt = r.Extra[0].(*TXT).Txt[0]
|
||||
if txt != "Hello example" {
|
||||
t.Error("unexpected result for example.com", txt, "!= Hello example")
|
||||
}
|
||||
|
||||
// Test Mixes cased as noticed by Ask.
|
||||
m.SetQuestion("eXaMplE.cOm.", TypeTXT)
|
||||
r, _, err = c.Exchange(m, addrstr)
|
||||
if err != nil {
|
||||
t.Error("failed to exchange eXaMplE.cOm", err)
|
||||
}
|
||||
txt = r.Extra[0].(*TXT).Txt[0]
|
||||
if txt != "Hello example" {
|
||||
t.Error("unexpected result for example.com", txt, "!= Hello example")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServingListenAndServe(t *testing.T) {
|
||||
HandleFunc("example.com.", AnotherHelloServer)
|
||||
defer HandleRemove("example.com.")
|
||||
|
||||
waitLock := sync.Mutex{}
|
||||
server := &Server{Addr: ":0", Net: "udp", ReadTimeout: time.Hour, WriteTimeout: time.Hour, NotifyStartedFunc: waitLock.Unlock}
|
||||
waitLock.Lock()
|
||||
|
||||
go func() {
|
||||
server.ListenAndServe()
|
||||
}()
|
||||
waitLock.Lock()
|
||||
|
||||
c, m := new(Client), new(Msg)
|
||||
m.SetQuestion("example.com.", TypeTXT)
|
||||
addr := server.PacketConn.LocalAddr().String() // Get address via the PacketConn that gets set.
|
||||
r, _, err := c.Exchange(m, addr)
|
||||
if err != nil {
|
||||
t.Fatal("failed to exchange example.com", err)
|
||||
}
|
||||
txt := r.Extra[0].(*TXT).Txt[0]
|
||||
if txt != "Hello example" {
|
||||
t.Error("unexpected result for example.com", txt, "!= Hello example")
|
||||
}
|
||||
server.Shutdown()
|
||||
}
|
||||
|
||||
func TestServingListenAndServeTLS(t *testing.T) {
|
||||
HandleFunc("example.com.", AnotherHelloServer)
|
||||
defer HandleRemove("example.com.")
|
||||
|
||||
cert, err := tls.X509KeyPair(CertPEMBlock, KeyPEMBlock)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to build certificate: %v", err)
|
||||
}
|
||||
|
||||
config := &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
}
|
||||
|
||||
waitLock := sync.Mutex{}
|
||||
server := &Server{Addr: ":0", Net: "tcp", TLSConfig: config, ReadTimeout: time.Hour, WriteTimeout: time.Hour, NotifyStartedFunc: waitLock.Unlock}
|
||||
waitLock.Lock()
|
||||
|
||||
go func() {
|
||||
server.ListenAndServe()
|
||||
}()
|
||||
waitLock.Lock()
|
||||
|
||||
c, m := new(Client), new(Msg)
|
||||
c.Net = "tcp"
|
||||
m.SetQuestion("example.com.", TypeTXT)
|
||||
addr := server.Listener.Addr().String() // Get address via the Listener that gets set.
|
||||
r, _, err := c.Exchange(m, addr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
txt := r.Extra[0].(*TXT).Txt[0]
|
||||
if txt != "Hello example" {
|
||||
t.Error("unexpected result for example.com", txt, "!= Hello example")
|
||||
}
|
||||
server.Shutdown()
|
||||
}
|
||||
|
||||
func BenchmarkServe(b *testing.B) {
|
||||
b.StopTimer()
|
||||
HandleFunc("miek.nl.", HelloServer)
|
||||
defer HandleRemove("miek.nl.")
|
||||
a := runtime.GOMAXPROCS(4)
|
||||
|
||||
s, addrstr, err := RunLocalUDPServer(":0")
|
||||
if err != nil {
|
||||
b.Fatalf("unable to run test server: %v", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
c := new(Client)
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl", TypeSOA)
|
||||
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
c.Exchange(m, addrstr)
|
||||
}
|
||||
runtime.GOMAXPROCS(a)
|
||||
}
|
||||
|
||||
func benchmarkServe6(b *testing.B) {
|
||||
b.StopTimer()
|
||||
HandleFunc("miek.nl.", HelloServer)
|
||||
defer HandleRemove("miek.nl.")
|
||||
a := runtime.GOMAXPROCS(4)
|
||||
s, addrstr, err := RunLocalUDPServer("[::1]:0")
|
||||
if err != nil {
|
||||
b.Fatalf("unable to run test server: %v", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
c := new(Client)
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl", TypeSOA)
|
||||
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
c.Exchange(m, addrstr)
|
||||
}
|
||||
runtime.GOMAXPROCS(a)
|
||||
}
|
||||
|
||||
func HelloServerCompress(w ResponseWriter, req *Msg) {
|
||||
m := new(Msg)
|
||||
m.SetReply(req)
|
||||
m.Extra = make([]RR, 1)
|
||||
m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
|
||||
m.Compress = true
|
||||
w.WriteMsg(m)
|
||||
}
|
||||
|
||||
func BenchmarkServeCompress(b *testing.B) {
|
||||
b.StopTimer()
|
||||
HandleFunc("miek.nl.", HelloServerCompress)
|
||||
defer HandleRemove("miek.nl.")
|
||||
a := runtime.GOMAXPROCS(4)
|
||||
s, addrstr, err := RunLocalUDPServer(":0")
|
||||
if err != nil {
|
||||
b.Fatalf("unable to run test server: %v", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
c := new(Client)
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl", TypeSOA)
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
c.Exchange(m, addrstr)
|
||||
}
|
||||
runtime.GOMAXPROCS(a)
|
||||
}
|
||||
|
||||
func TestDotAsCatchAllWildcard(t *testing.T) {
|
||||
mux := NewServeMux()
|
||||
mux.Handle(".", HandlerFunc(HelloServer))
|
||||
mux.Handle("example.com.", HandlerFunc(AnotherHelloServer))
|
||||
|
||||
handler := mux.match("www.miek.nl.", TypeTXT)
|
||||
if handler == nil {
|
||||
t.Error("wildcard match failed")
|
||||
}
|
||||
|
||||
handler = mux.match("www.example.com.", TypeTXT)
|
||||
if handler == nil {
|
||||
t.Error("example.com match failed")
|
||||
}
|
||||
|
||||
handler = mux.match("a.www.example.com.", TypeTXT)
|
||||
if handler == nil {
|
||||
t.Error("a.www.example.com match failed")
|
||||
}
|
||||
|
||||
handler = mux.match("boe.", TypeTXT)
|
||||
if handler == nil {
|
||||
t.Error("boe. match failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCaseFolding(t *testing.T) {
|
||||
mux := NewServeMux()
|
||||
mux.Handle("_udp.example.com.", HandlerFunc(HelloServer))
|
||||
|
||||
handler := mux.match("_dns._udp.example.com.", TypeSRV)
|
||||
if handler == nil {
|
||||
t.Error("case sensitive characters folded")
|
||||
}
|
||||
|
||||
handler = mux.match("_DNS._UDP.EXAMPLE.COM.", TypeSRV)
|
||||
if handler == nil {
|
||||
t.Error("case insensitive characters not folded")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRootServer(t *testing.T) {
|
||||
mux := NewServeMux()
|
||||
mux.Handle(".", HandlerFunc(HelloServer))
|
||||
|
||||
handler := mux.match(".", TypeNS)
|
||||
if handler == nil {
|
||||
t.Error("root match failed")
|
||||
}
|
||||
}
|
||||
|
||||
type maxRec struct {
|
||||
max int
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
var M = new(maxRec)
|
||||
|
||||
func HelloServerLargeResponse(resp ResponseWriter, req *Msg) {
|
||||
m := new(Msg)
|
||||
m.SetReply(req)
|
||||
m.Authoritative = true
|
||||
m1 := 0
|
||||
M.RLock()
|
||||
m1 = M.max
|
||||
M.RUnlock()
|
||||
for i := 0; i < m1; i++ {
|
||||
aRec := &A{
|
||||
Hdr: RR_Header{
|
||||
Name: req.Question[0].Name,
|
||||
Rrtype: TypeA,
|
||||
Class: ClassINET,
|
||||
Ttl: 0,
|
||||
},
|
||||
A: net.ParseIP(fmt.Sprintf("127.0.0.%d", i+1)).To4(),
|
||||
}
|
||||
m.Answer = append(m.Answer, aRec)
|
||||
}
|
||||
resp.WriteMsg(m)
|
||||
}
|
||||
|
||||
func TestServingLargeResponses(t *testing.T) {
|
||||
HandleFunc("example.", HelloServerLargeResponse)
|
||||
defer HandleRemove("example.")
|
||||
|
||||
s, addrstr, err := RunLocalUDPServer(":0")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to run test server: %v", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
// Create request
|
||||
m := new(Msg)
|
||||
m.SetQuestion("web.service.example.", TypeANY)
|
||||
|
||||
c := new(Client)
|
||||
c.Net = "udp"
|
||||
M.Lock()
|
||||
M.max = 2
|
||||
M.Unlock()
|
||||
_, _, err = c.Exchange(m, addrstr)
|
||||
if err != nil {
|
||||
t.Errorf("failed to exchange: %v", err)
|
||||
}
|
||||
// This must fail
|
||||
M.Lock()
|
||||
M.max = 20
|
||||
M.Unlock()
|
||||
_, _, err = c.Exchange(m, addrstr)
|
||||
if err == nil {
|
||||
t.Error("failed to fail exchange, this should generate packet error")
|
||||
}
|
||||
// But this must work again
|
||||
c.UDPSize = 7000
|
||||
_, _, err = c.Exchange(m, addrstr)
|
||||
if err != nil {
|
||||
t.Errorf("failed to exchange: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServingResponse(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping test in short mode.")
|
||||
}
|
||||
HandleFunc("miek.nl.", HelloServer)
|
||||
s, addrstr, err := RunLocalUDPServer(":0")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to run test server: %v", err)
|
||||
}
|
||||
|
||||
c := new(Client)
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl.", TypeTXT)
|
||||
m.Response = false
|
||||
_, _, err = c.Exchange(m, addrstr)
|
||||
if err != nil {
|
||||
t.Fatal("failed to exchange", err)
|
||||
}
|
||||
m.Response = true
|
||||
_, _, err = c.Exchange(m, addrstr)
|
||||
if err == nil {
|
||||
t.Fatal("exchanged response message")
|
||||
}
|
||||
|
||||
s.Shutdown()
|
||||
s, addrstr, err = RunLocalUDPServerUnsafe(":0")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to run test server: %v", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
m.Response = true
|
||||
_, _, err = c.Exchange(m, addrstr)
|
||||
if err != nil {
|
||||
t.Fatal("could exchanged response message in Unsafe mode")
|
||||
}
|
||||
}
|
||||
|
||||
func TestShutdownTCP(t *testing.T) {
|
||||
s, _, fin, err := RunLocalTCPServerWithFinChan(":0")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to run test server: %v", err)
|
||||
}
|
||||
err = s.Shutdown()
|
||||
if err != nil {
|
||||
t.Fatalf("could not shutdown test TCP server, %v", err)
|
||||
}
|
||||
select {
|
||||
case err := <-fin:
|
||||
if err != nil {
|
||||
t.Errorf("error returned from ActivateAndServe, %v", err)
|
||||
}
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Error("could not shutdown test TCP server. Gave up waiting")
|
||||
}
|
||||
}
|
||||
|
||||
func TestShutdownTLS(t *testing.T) {
|
||||
cert, err := tls.X509KeyPair(CertPEMBlock, KeyPEMBlock)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to build certificate: %v", err)
|
||||
}
|
||||
|
||||
config := tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
}
|
||||
|
||||
s, _, err := RunLocalTLSServer(":0", &config)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to run test server: %v", err)
|
||||
}
|
||||
err = s.Shutdown()
|
||||
if err != nil {
|
||||
t.Errorf("could not shutdown test TLS server, %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
type trigger struct {
|
||||
done bool
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func (t *trigger) Set() {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
t.done = true
|
||||
}
|
||||
func (t *trigger) Get() bool {
|
||||
t.RLock()
|
||||
defer t.RUnlock()
|
||||
return t.done
|
||||
}
|
||||
|
||||
func TestHandlerCloseTCP(t *testing.T) {
|
||||
|
||||
ln, err := net.Listen("tcp", ":0")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
addr := ln.Addr().String()
|
||||
|
||||
server := &Server{Addr: addr, Net: "tcp", Listener: ln}
|
||||
|
||||
hname := "testhandlerclosetcp."
|
||||
triggered := &trigger{}
|
||||
HandleFunc(hname, func(w ResponseWriter, r *Msg) {
|
||||
triggered.Set()
|
||||
w.Close()
|
||||
})
|
||||
defer HandleRemove(hname)
|
||||
|
||||
go func() {
|
||||
defer server.Shutdown()
|
||||
c := &Client{Net: "tcp"}
|
||||
m := new(Msg).SetQuestion(hname, 1)
|
||||
tries := 0
|
||||
exchange:
|
||||
_, _, err := c.Exchange(m, addr)
|
||||
if err != nil && err != io.EOF {
|
||||
t.Errorf("exchange failed: %s\n", err)
|
||||
if tries == 3 {
|
||||
return
|
||||
}
|
||||
time.Sleep(time.Second / 10)
|
||||
tries++
|
||||
goto exchange
|
||||
}
|
||||
}()
|
||||
server.ActivateAndServe()
|
||||
if !triggered.Get() {
|
||||
t.Fatalf("handler never called")
|
||||
}
|
||||
}
|
||||
|
||||
func TestShutdownUDP(t *testing.T) {
|
||||
s, _, fin, err := RunLocalUDPServerWithFinChan(":0")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to run test server: %v", err)
|
||||
}
|
||||
err = s.Shutdown()
|
||||
if err != nil {
|
||||
t.Errorf("could not shutdown test UDP server, %v", err)
|
||||
}
|
||||
select {
|
||||
case err := <-fin:
|
||||
if err != nil {
|
||||
t.Errorf("error returned from ActivateAndServe, %v", err)
|
||||
}
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Error("could not shutdown test UDP server. Gave up waiting")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerStartStopRace(t *testing.T) {
|
||||
for i := 0; i < 10; i++ {
|
||||
var err error
|
||||
s := &Server{}
|
||||
s, _, _, err = RunLocalUDPServerWithFinChan(":0")
|
||||
if err != nil {
|
||||
t.Fatalf("could not start server: %s", err)
|
||||
}
|
||||
go func() {
|
||||
if err := s.Shutdown(); err != nil {
|
||||
t.Fatalf("could not stop server: %s", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
type ExampleFrameLengthWriter struct {
|
||||
Writer
|
||||
}
|
||||
|
||||
func (e *ExampleFrameLengthWriter) Write(m []byte) (int, error) {
|
||||
fmt.Println("writing raw DNS message of length", len(m))
|
||||
return e.Writer.Write(m)
|
||||
}
|
||||
|
||||
func ExampleDecorateWriter() {
|
||||
// instrument raw DNS message writing
|
||||
wf := DecorateWriter(func(w Writer) Writer {
|
||||
return &ExampleFrameLengthWriter{w}
|
||||
})
|
||||
|
||||
// simple UDP server
|
||||
pc, err := net.ListenPacket("udp", ":0")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
server := &Server{
|
||||
PacketConn: pc,
|
||||
DecorateWriter: wf,
|
||||
ReadTimeout: time.Hour, WriteTimeout: time.Hour,
|
||||
}
|
||||
|
||||
waitLock := sync.Mutex{}
|
||||
waitLock.Lock()
|
||||
server.NotifyStartedFunc = waitLock.Unlock
|
||||
defer server.Shutdown()
|
||||
|
||||
go func() {
|
||||
server.ActivateAndServe()
|
||||
pc.Close()
|
||||
}()
|
||||
|
||||
waitLock.Lock()
|
||||
|
||||
HandleFunc("miek.nl.", HelloServer)
|
||||
|
||||
c := new(Client)
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl.", TypeTXT)
|
||||
_, _, err = c.Exchange(m, pc.LocalAddr().String())
|
||||
if err != nil {
|
||||
fmt.Println("failed to exchange", err.Error())
|
||||
return
|
||||
}
|
||||
// Output: writing raw DNS message of length 56
|
||||
}
|
||||
|
||||
var (
|
||||
// CertPEMBlock is a X509 data used to test TLS servers (used with tls.X509KeyPair)
|
||||
CertPEMBlock = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIDAzCCAeugAwIBAgIRAJFYMkcn+b8dpU15wjf++GgwDQYJKoZIhvcNAQELBQAw
|
||||
EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNjAxMDgxMjAzNTNaFw0xNzAxMDcxMjAz
|
||||
NTNaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
|
||||
ggEKAoIBAQDXjqO6skvP03k58CNjQggd9G/mt+Wa+xRU+WXiKCCHttawM8x+slq5
|
||||
yfsHCwxlwsGn79HmJqecNqgHb2GWBXAvVVokFDTcC1hUP4+gp2gu9Ny27UHTjlLm
|
||||
O0l/xZ5MN8tfKyYlFw18tXu3fkaPyHj8v/D1RDkuo4ARdFvGSe8TqisbhLk2+9ow
|
||||
xfIGbEM9Fdiw8qByC2+d+FfvzIKz3GfQVwn0VoRom8L6NBIANq1IGrB5JefZB6nv
|
||||
DnfuxkBmY7F1513HKuEJ8KsLWWZWV9OPU4j4I4Rt+WJNlKjbD2srHxyrS2RDsr91
|
||||
8nCkNoWVNO3sZq0XkWKecdc921vL4ginAgMBAAGjVDBSMA4GA1UdDwEB/wQEAwIC
|
||||
pDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MBoGA1UdEQQT
|
||||
MBGCCWxvY2FsaG9zdIcEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEAGcU3iyLBIVZj
|
||||
aDzSvEDHUd1bnLBl1C58Xu/CyKlPqVU7mLfK0JcgEaYQTSX6fCJVNLbbCrcGLsPJ
|
||||
fbjlBbyeLjTV413fxPVuona62pBFjqdtbli2Qe8FRH2KBdm41JUJGdo+SdsFu7nc
|
||||
BFOcubdw6LLIXvsTvwndKcHWx1rMX709QU1Vn1GAIsbJV/DWI231Jyyb+lxAUx/C
|
||||
8vce5uVxiKcGS+g6OjsN3D3TtiEQGSXLh013W6Wsih8td8yMCMZ3w8LQ38br1GUe
|
||||
ahLIgUJ9l6HDguM17R7kGqxNvbElsMUHfTtXXP7UDQUiYXDakg8xDP6n9DCDhJ8Y
|
||||
bSt7OLB7NQ==
|
||||
-----END CERTIFICATE-----`)
|
||||
|
||||
// KeyPEMBlock is a X509 data used to test TLS servers (used with tls.X509KeyPair)
|
||||
KeyPEMBlock = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEA146jurJLz9N5OfAjY0IIHfRv5rflmvsUVPll4iggh7bWsDPM
|
||||
frJaucn7BwsMZcLBp+/R5iannDaoB29hlgVwL1VaJBQ03AtYVD+PoKdoLvTctu1B
|
||||
045S5jtJf8WeTDfLXysmJRcNfLV7t35Gj8h4/L/w9UQ5LqOAEXRbxknvE6orG4S5
|
||||
NvvaMMXyBmxDPRXYsPKgcgtvnfhX78yCs9xn0FcJ9FaEaJvC+jQSADatSBqweSXn
|
||||
2Qep7w537sZAZmOxdeddxyrhCfCrC1lmVlfTj1OI+COEbfliTZSo2w9rKx8cq0tk
|
||||
Q7K/dfJwpDaFlTTt7GatF5FinnHXPdtby+IIpwIDAQABAoIBAAJK4RDmPooqTJrC
|
||||
JA41MJLo+5uvjwCT9QZmVKAQHzByUFw1YNJkITTiognUI0CdzqNzmH7jIFs39ZeG
|
||||
proKusO2G6xQjrNcZ4cV2fgyb5g4QHStl0qhs94A+WojduiGm2IaumAgm6Mc5wDv
|
||||
ld6HmknN3Mku/ZCyanVFEIjOVn2WB7ZQLTBs6ZYaebTJG2Xv6p9t2YJW7pPQ9Xce
|
||||
s9ohAWohyM4X/OvfnfnLtQp2YLw/BxwehBsCR5SXM3ibTKpFNtxJC8hIfTuWtxZu
|
||||
2ywrmXShYBRB1WgtZt5k04bY/HFncvvcHK3YfI1+w4URKtwdaQgPUQRbVwDwuyBn
|
||||
flfkCJECgYEA/eWt01iEyE/lXkGn6V9lCocUU7lCU6yk5UT8VXVUc5If4KZKPfCk
|
||||
p4zJDOqwn2eM673aWz/mG9mtvAvmnugaGjcaVCyXOp/D/GDmKSoYcvW5B/yjfkLy
|
||||
dK6Yaa5LDRVYlYgyzcdCT5/9Qc626NzFwKCZNI4ncIU8g7ViATRxWJ8CgYEA2Ver
|
||||
vZ0M606sfgC0H3NtwNBxmuJ+lIF5LNp/wDi07lDfxRR1rnZMX5dnxjcpDr/zvm8J
|
||||
WtJJX3xMgqjtHuWKL3yKKony9J5ZPjichSbSbhrzfovgYIRZLxLLDy4MP9L3+CX/
|
||||
yBXnqMWuSnFX+M5fVGxdDWiYF3V+wmeOv9JvavkCgYEAiXAPDFzaY+R78O3xiu7M
|
||||
r0o3wqqCMPE/wav6O/hrYrQy9VSO08C0IM6g9pEEUwWmzuXSkZqhYWoQFb8Lc/GI
|
||||
T7CMXAxXQLDDUpbRgG79FR3Wr3AewHZU8LyiXHKwxcBMV4WGmsXGK3wbh8fyU1NO
|
||||
6NsGk+BvkQVOoK1LBAPzZ1kCgYEAsBSmD8U33T9s4dxiEYTrqyV0lH3g/SFz8ZHH
|
||||
pAyNEPI2iC1ONhyjPWKlcWHpAokiyOqeUpVBWnmSZtzC1qAydsxYB6ShT+sl9BHb
|
||||
RMix/QAauzBJhQhUVJ3OIys0Q1UBDmqCsjCE8SfOT4NKOUnA093C+YT+iyrmmktZ
|
||||
zDCJkckCgYEAndqM5KXGk5xYo+MAA1paZcbTUXwaWwjLU+XSRSSoyBEi5xMtfvUb
|
||||
7+a1OMhLwWbuz+pl64wFKrbSUyimMOYQpjVE/1vk/kb99pxbgol27hdKyTH1d+ov
|
||||
kFsxKCqxAnBVGEWAvVZAiiTOxleQFjz5RnL0BQp9Lg2cQe+dvuUmIAA=
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
)
|
||||
218
vendor/github.com/miekg/dns/sig0.go
generated
vendored
Normal file
218
vendor/github.com/miekg/dns/sig0.go
generated
vendored
Normal file
@@ -0,0 +1,218 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"encoding/binary"
|
||||
"math/big"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Sign signs a dns.Msg. It fills the signature with the appropriate data.
|
||||
// The SIG record should have the SignerName, KeyTag, Algorithm, Inception
|
||||
// and Expiration set.
|
||||
func (rr *SIG) Sign(k crypto.Signer, m *Msg) ([]byte, error) {
|
||||
if k == nil {
|
||||
return nil, ErrPrivKey
|
||||
}
|
||||
if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 {
|
||||
return nil, ErrKey
|
||||
}
|
||||
rr.Header().Rrtype = TypeSIG
|
||||
rr.Header().Class = ClassANY
|
||||
rr.Header().Ttl = 0
|
||||
rr.Header().Name = "."
|
||||
rr.OrigTtl = 0
|
||||
rr.TypeCovered = 0
|
||||
rr.Labels = 0
|
||||
|
||||
buf := make([]byte, m.Len()+rr.len())
|
||||
mbuf, err := m.PackBuffer(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if &buf[0] != &mbuf[0] {
|
||||
return nil, ErrBuf
|
||||
}
|
||||
off, err := PackRR(rr, buf, len(mbuf), nil, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf = buf[:off:cap(buf)]
|
||||
|
||||
hash, ok := AlgorithmToHash[rr.Algorithm]
|
||||
if !ok {
|
||||
return nil, ErrAlg
|
||||
}
|
||||
|
||||
hasher := hash.New()
|
||||
// Write SIG rdata
|
||||
hasher.Write(buf[len(mbuf)+1+2+2+4+2:])
|
||||
// Write message
|
||||
hasher.Write(buf[:len(mbuf)])
|
||||
|
||||
signature, err := sign(k, hasher.Sum(nil), hash, rr.Algorithm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rr.Signature = toBase64(signature)
|
||||
|
||||
buf = append(buf, signature...)
|
||||
if len(buf) > int(^uint16(0)) {
|
||||
return nil, ErrBuf
|
||||
}
|
||||
// Adjust sig data length
|
||||
rdoff := len(mbuf) + 1 + 2 + 2 + 4
|
||||
rdlen := binary.BigEndian.Uint16(buf[rdoff:])
|
||||
rdlen += uint16(len(signature))
|
||||
binary.BigEndian.PutUint16(buf[rdoff:], rdlen)
|
||||
// Adjust additional count
|
||||
adc := binary.BigEndian.Uint16(buf[10:])
|
||||
adc++
|
||||
binary.BigEndian.PutUint16(buf[10:], adc)
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Verify validates the message buf using the key k.
|
||||
// It's assumed that buf is a valid message from which rr was unpacked.
|
||||
func (rr *SIG) Verify(k *KEY, buf []byte) error {
|
||||
if k == nil {
|
||||
return ErrKey
|
||||
}
|
||||
if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 {
|
||||
return ErrKey
|
||||
}
|
||||
|
||||
var hash crypto.Hash
|
||||
switch rr.Algorithm {
|
||||
case DSA, RSASHA1:
|
||||
hash = crypto.SHA1
|
||||
case RSASHA256, ECDSAP256SHA256:
|
||||
hash = crypto.SHA256
|
||||
case ECDSAP384SHA384:
|
||||
hash = crypto.SHA384
|
||||
case RSASHA512:
|
||||
hash = crypto.SHA512
|
||||
default:
|
||||
return ErrAlg
|
||||
}
|
||||
hasher := hash.New()
|
||||
|
||||
buflen := len(buf)
|
||||
qdc := binary.BigEndian.Uint16(buf[4:])
|
||||
anc := binary.BigEndian.Uint16(buf[6:])
|
||||
auc := binary.BigEndian.Uint16(buf[8:])
|
||||
adc := binary.BigEndian.Uint16(buf[10:])
|
||||
offset := 12
|
||||
var err error
|
||||
for i := uint16(0); i < qdc && offset < buflen; i++ {
|
||||
_, offset, err = UnpackDomainName(buf, offset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Skip past Type and Class
|
||||
offset += 2 + 2
|
||||
}
|
||||
for i := uint16(1); i < anc+auc+adc && offset < buflen; i++ {
|
||||
_, offset, err = UnpackDomainName(buf, offset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Skip past Type, Class and TTL
|
||||
offset += 2 + 2 + 4
|
||||
if offset+1 >= buflen {
|
||||
continue
|
||||
}
|
||||
var rdlen uint16
|
||||
rdlen = binary.BigEndian.Uint16(buf[offset:])
|
||||
offset += 2
|
||||
offset += int(rdlen)
|
||||
}
|
||||
if offset >= buflen {
|
||||
return &Error{err: "overflowing unpacking signed message"}
|
||||
}
|
||||
|
||||
// offset should be just prior to SIG
|
||||
bodyend := offset
|
||||
// owner name SHOULD be root
|
||||
_, offset, err = UnpackDomainName(buf, offset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Skip Type, Class, TTL, RDLen
|
||||
offset += 2 + 2 + 4 + 2
|
||||
sigstart := offset
|
||||
// Skip Type Covered, Algorithm, Labels, Original TTL
|
||||
offset += 2 + 1 + 1 + 4
|
||||
if offset+4+4 >= buflen {
|
||||
return &Error{err: "overflow unpacking signed message"}
|
||||
}
|
||||
expire := binary.BigEndian.Uint32(buf[offset:])
|
||||
offset += 4
|
||||
incept := binary.BigEndian.Uint32(buf[offset:])
|
||||
offset += 4
|
||||
now := uint32(time.Now().Unix())
|
||||
if now < incept || now > expire {
|
||||
return ErrTime
|
||||
}
|
||||
// Skip key tag
|
||||
offset += 2
|
||||
var signername string
|
||||
signername, offset, err = UnpackDomainName(buf, offset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// If key has come from the DNS name compression might
|
||||
// have mangled the case of the name
|
||||
if strings.ToLower(signername) != strings.ToLower(k.Header().Name) {
|
||||
return &Error{err: "signer name doesn't match key name"}
|
||||
}
|
||||
sigend := offset
|
||||
hasher.Write(buf[sigstart:sigend])
|
||||
hasher.Write(buf[:10])
|
||||
hasher.Write([]byte{
|
||||
byte((adc - 1) << 8),
|
||||
byte(adc - 1),
|
||||
})
|
||||
hasher.Write(buf[12:bodyend])
|
||||
|
||||
hashed := hasher.Sum(nil)
|
||||
sig := buf[sigend:]
|
||||
switch k.Algorithm {
|
||||
case DSA:
|
||||
pk := k.publicKeyDSA()
|
||||
sig = sig[1:]
|
||||
r := big.NewInt(0)
|
||||
r.SetBytes(sig[:len(sig)/2])
|
||||
s := big.NewInt(0)
|
||||
s.SetBytes(sig[len(sig)/2:])
|
||||
if pk != nil {
|
||||
if dsa.Verify(pk, hashed, r, s) {
|
||||
return nil
|
||||
}
|
||||
return ErrSig
|
||||
}
|
||||
case RSASHA1, RSASHA256, RSASHA512:
|
||||
pk := k.publicKeyRSA()
|
||||
if pk != nil {
|
||||
return rsa.VerifyPKCS1v15(pk, hash, hashed, sig)
|
||||
}
|
||||
case ECDSAP256SHA256, ECDSAP384SHA384:
|
||||
pk := k.publicKeyECDSA()
|
||||
r := big.NewInt(0)
|
||||
r.SetBytes(sig[:len(sig)/2])
|
||||
s := big.NewInt(0)
|
||||
s.SetBytes(sig[len(sig)/2:])
|
||||
if pk != nil {
|
||||
if ecdsa.Verify(pk, hashed, r, s) {
|
||||
return nil
|
||||
}
|
||||
return ErrSig
|
||||
}
|
||||
}
|
||||
return ErrKeyAlg
|
||||
}
|
||||
89
vendor/github.com/miekg/dns/sig0_test.go
generated
vendored
Normal file
89
vendor/github.com/miekg/dns/sig0_test.go
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestSIG0(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping test in short mode.")
|
||||
}
|
||||
m := new(Msg)
|
||||
m.SetQuestion("example.org.", TypeSOA)
|
||||
for _, alg := range []uint8{ECDSAP256SHA256, ECDSAP384SHA384, RSASHA1, RSASHA256, RSASHA512} {
|
||||
algstr := AlgorithmToString[alg]
|
||||
keyrr := new(KEY)
|
||||
keyrr.Hdr.Name = algstr + "."
|
||||
keyrr.Hdr.Rrtype = TypeKEY
|
||||
keyrr.Hdr.Class = ClassINET
|
||||
keyrr.Algorithm = alg
|
||||
keysize := 1024
|
||||
switch alg {
|
||||
case ECDSAP256SHA256:
|
||||
keysize = 256
|
||||
case ECDSAP384SHA384:
|
||||
keysize = 384
|
||||
}
|
||||
pk, err := keyrr.Generate(keysize)
|
||||
if err != nil {
|
||||
t.Errorf("failed to generate key for “%s”: %v", algstr, err)
|
||||
continue
|
||||
}
|
||||
now := uint32(time.Now().Unix())
|
||||
sigrr := new(SIG)
|
||||
sigrr.Hdr.Name = "."
|
||||
sigrr.Hdr.Rrtype = TypeSIG
|
||||
sigrr.Hdr.Class = ClassANY
|
||||
sigrr.Algorithm = alg
|
||||
sigrr.Expiration = now + 300
|
||||
sigrr.Inception = now - 300
|
||||
sigrr.KeyTag = keyrr.KeyTag()
|
||||
sigrr.SignerName = keyrr.Hdr.Name
|
||||
mb, err := sigrr.Sign(pk.(crypto.Signer), m)
|
||||
if err != nil {
|
||||
t.Errorf("failed to sign message using “%s”: %v", algstr, err)
|
||||
continue
|
||||
}
|
||||
m := new(Msg)
|
||||
if err := m.Unpack(mb); err != nil {
|
||||
t.Errorf("failed to unpack message signed using “%s”: %v", algstr, err)
|
||||
continue
|
||||
}
|
||||
if len(m.Extra) != 1 {
|
||||
t.Errorf("missing SIG for message signed using “%s”", algstr)
|
||||
continue
|
||||
}
|
||||
var sigrrwire *SIG
|
||||
switch rr := m.Extra[0].(type) {
|
||||
case *SIG:
|
||||
sigrrwire = rr
|
||||
default:
|
||||
t.Errorf("expected SIG RR, instead: %v", rr)
|
||||
continue
|
||||
}
|
||||
for _, rr := range []*SIG{sigrr, sigrrwire} {
|
||||
id := "sigrr"
|
||||
if rr == sigrrwire {
|
||||
id = "sigrrwire"
|
||||
}
|
||||
if err := rr.Verify(keyrr, mb); err != nil {
|
||||
t.Errorf("failed to verify “%s” signed SIG(%s): %v", algstr, id, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
mb[13]++
|
||||
if err := sigrr.Verify(keyrr, mb); err == nil {
|
||||
t.Errorf("verify succeeded on an altered message using “%s”", algstr)
|
||||
continue
|
||||
}
|
||||
sigrr.Expiration = 2
|
||||
sigrr.Inception = 1
|
||||
mb, _ = sigrr.Sign(pk.(crypto.Signer), m)
|
||||
if err := sigrr.Verify(keyrr, mb); err == nil {
|
||||
t.Errorf("verify succeeded on an expired message using “%s”", algstr)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
57
vendor/github.com/miekg/dns/singleinflight.go
generated
vendored
Normal file
57
vendor/github.com/miekg/dns/singleinflight.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Adapted for dns package usage by Miek Gieben.
|
||||
|
||||
package dns
|
||||
|
||||
import "sync"
|
||||
import "time"
|
||||
|
||||
// call is an in-flight or completed singleflight.Do call
|
||||
type call struct {
|
||||
wg sync.WaitGroup
|
||||
val *Msg
|
||||
rtt time.Duration
|
||||
err error
|
||||
dups int
|
||||
}
|
||||
|
||||
// singleflight represents a class of work and forms a namespace in
|
||||
// which units of work can be executed with duplicate suppression.
|
||||
type singleflight struct {
|
||||
sync.Mutex // protects m
|
||||
m map[string]*call // lazily initialized
|
||||
}
|
||||
|
||||
// Do executes and returns the results of the given function, making
|
||||
// sure that only one execution is in-flight for a given key at a
|
||||
// time. If a duplicate comes in, the duplicate caller waits for the
|
||||
// original to complete and receives the same results.
|
||||
// The return value shared indicates whether v was given to multiple callers.
|
||||
func (g *singleflight) Do(key string, fn func() (*Msg, time.Duration, error)) (v *Msg, rtt time.Duration, err error, shared bool) {
|
||||
g.Lock()
|
||||
if g.m == nil {
|
||||
g.m = make(map[string]*call)
|
||||
}
|
||||
if c, ok := g.m[key]; ok {
|
||||
c.dups++
|
||||
g.Unlock()
|
||||
c.wg.Wait()
|
||||
return c.val, c.rtt, c.err, true
|
||||
}
|
||||
c := new(call)
|
||||
c.wg.Add(1)
|
||||
g.m[key] = c
|
||||
g.Unlock()
|
||||
|
||||
c.val, c.rtt, c.err = fn()
|
||||
c.wg.Done()
|
||||
|
||||
g.Lock()
|
||||
delete(g.m, key)
|
||||
g.Unlock()
|
||||
|
||||
return c.val, c.rtt, c.err, c.dups > 0
|
||||
}
|
||||
47
vendor/github.com/miekg/dns/smimea.go
generated
vendored
Normal file
47
vendor/github.com/miekg/dns/smimea.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
// Sign creates a SMIMEA record from an SSL certificate.
|
||||
func (r *SMIMEA) Sign(usage, selector, matchingType int, cert *x509.Certificate) (err error) {
|
||||
r.Hdr.Rrtype = TypeSMIMEA
|
||||
r.Usage = uint8(usage)
|
||||
r.Selector = uint8(selector)
|
||||
r.MatchingType = uint8(matchingType)
|
||||
|
||||
r.Certificate, err = CertificateToDANE(r.Selector, r.MatchingType, cert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Verify verifies a SMIMEA record against an SSL certificate. If it is OK
|
||||
// a nil error is returned.
|
||||
func (r *SMIMEA) Verify(cert *x509.Certificate) error {
|
||||
c, err := CertificateToDANE(r.Selector, r.MatchingType, cert)
|
||||
if err != nil {
|
||||
return err // Not also ErrSig?
|
||||
}
|
||||
if r.Certificate == c {
|
||||
return nil
|
||||
}
|
||||
return ErrSig // ErrSig, really?
|
||||
}
|
||||
|
||||
// SMIMEAName returns the ownername of a SMIMEA resource record as per the
|
||||
// format specified in RFC 'draft-ietf-dane-smime-12' Section 2 and 3
|
||||
func SMIMEAName(email, domain string) (string, error) {
|
||||
hasher := sha256.New()
|
||||
hasher.Write([]byte(email))
|
||||
|
||||
// RFC Section 3: "The local-part is hashed using the SHA2-256
|
||||
// algorithm with the hash truncated to 28 octets and
|
||||
// represented in its hexadecimal representation to become the
|
||||
// left-most label in the prepared domain name"
|
||||
return hex.EncodeToString(hasher.Sum(nil)[:28]) + "." + "_smimecert." + domain, nil
|
||||
}
|
||||
47
vendor/github.com/miekg/dns/tlsa.go
generated
vendored
Normal file
47
vendor/github.com/miekg/dns/tlsa.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Sign creates a TLSA record from an SSL certificate.
|
||||
func (r *TLSA) Sign(usage, selector, matchingType int, cert *x509.Certificate) (err error) {
|
||||
r.Hdr.Rrtype = TypeTLSA
|
||||
r.Usage = uint8(usage)
|
||||
r.Selector = uint8(selector)
|
||||
r.MatchingType = uint8(matchingType)
|
||||
|
||||
r.Certificate, err = CertificateToDANE(r.Selector, r.MatchingType, cert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Verify verifies a TLSA record against an SSL certificate. If it is OK
|
||||
// a nil error is returned.
|
||||
func (r *TLSA) Verify(cert *x509.Certificate) error {
|
||||
c, err := CertificateToDANE(r.Selector, r.MatchingType, cert)
|
||||
if err != nil {
|
||||
return err // Not also ErrSig?
|
||||
}
|
||||
if r.Certificate == c {
|
||||
return nil
|
||||
}
|
||||
return ErrSig // ErrSig, really?
|
||||
}
|
||||
|
||||
// TLSAName returns the ownername of a TLSA resource record as per the
|
||||
// rules specified in RFC 6698, Section 3.
|
||||
func TLSAName(name, service, network string) (string, error) {
|
||||
if !IsFqdn(name) {
|
||||
return "", ErrFqdn
|
||||
}
|
||||
p, err := net.LookupPort(network, service)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "_" + strconv.Itoa(p) + "._" + network + "." + name, nil
|
||||
}
|
||||
386
vendor/github.com/miekg/dns/tsig.go
generated
vendored
Normal file
386
vendor/github.com/miekg/dns/tsig.go
generated
vendored
Normal file
@@ -0,0 +1,386 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"hash"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// HMAC hashing codes. These are transmitted as domain names.
|
||||
const (
|
||||
HmacMD5 = "hmac-md5.sig-alg.reg.int."
|
||||
HmacSHA1 = "hmac-sha1."
|
||||
HmacSHA256 = "hmac-sha256."
|
||||
HmacSHA512 = "hmac-sha512."
|
||||
)
|
||||
|
||||
// TSIG is the RR the holds the transaction signature of a message.
|
||||
// See RFC 2845 and RFC 4635.
|
||||
type TSIG struct {
|
||||
Hdr RR_Header
|
||||
Algorithm string `dns:"domain-name"`
|
||||
TimeSigned uint64 `dns:"uint48"`
|
||||
Fudge uint16
|
||||
MACSize uint16
|
||||
MAC string `dns:"size-hex:MACSize"`
|
||||
OrigId uint16
|
||||
Error uint16
|
||||
OtherLen uint16
|
||||
OtherData string `dns:"size-hex:OtherLen"`
|
||||
}
|
||||
|
||||
// TSIG has no official presentation format, but this will suffice.
|
||||
|
||||
func (rr *TSIG) String() string {
|
||||
s := "\n;; TSIG PSEUDOSECTION:\n"
|
||||
s += rr.Hdr.String() +
|
||||
" " + rr.Algorithm +
|
||||
" " + tsigTimeToString(rr.TimeSigned) +
|
||||
" " + strconv.Itoa(int(rr.Fudge)) +
|
||||
" " + strconv.Itoa(int(rr.MACSize)) +
|
||||
" " + strings.ToUpper(rr.MAC) +
|
||||
" " + strconv.Itoa(int(rr.OrigId)) +
|
||||
" " + strconv.Itoa(int(rr.Error)) + // BIND prints NOERROR
|
||||
" " + strconv.Itoa(int(rr.OtherLen)) +
|
||||
" " + rr.OtherData
|
||||
return s
|
||||
}
|
||||
|
||||
// The following values must be put in wireformat, so that the MAC can be calculated.
|
||||
// RFC 2845, section 3.4.2. TSIG Variables.
|
||||
type tsigWireFmt struct {
|
||||
// From RR_Header
|
||||
Name string `dns:"domain-name"`
|
||||
Class uint16
|
||||
Ttl uint32
|
||||
// Rdata of the TSIG
|
||||
Algorithm string `dns:"domain-name"`
|
||||
TimeSigned uint64 `dns:"uint48"`
|
||||
Fudge uint16
|
||||
// MACSize, MAC and OrigId excluded
|
||||
Error uint16
|
||||
OtherLen uint16
|
||||
OtherData string `dns:"size-hex:OtherLen"`
|
||||
}
|
||||
|
||||
// If we have the MAC use this type to convert it to wiredata. Section 3.4.3. Request MAC
|
||||
type macWireFmt struct {
|
||||
MACSize uint16
|
||||
MAC string `dns:"size-hex:MACSize"`
|
||||
}
|
||||
|
||||
// 3.3. Time values used in TSIG calculations
|
||||
type timerWireFmt struct {
|
||||
TimeSigned uint64 `dns:"uint48"`
|
||||
Fudge uint16
|
||||
}
|
||||
|
||||
// TsigGenerate fills out the TSIG record attached to the message.
|
||||
// The message should contain
|
||||
// a "stub" TSIG RR with the algorithm, key name (owner name of the RR),
|
||||
// time fudge (defaults to 300 seconds) and the current time
|
||||
// The TSIG MAC is saved in that Tsig RR.
|
||||
// When TsigGenerate is called for the first time requestMAC is set to the empty string and
|
||||
// timersOnly is false.
|
||||
// If something goes wrong an error is returned, otherwise it is nil.
|
||||
func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, string, error) {
|
||||
if m.IsTsig() == nil {
|
||||
panic("dns: TSIG not last RR in additional")
|
||||
}
|
||||
// If we barf here, the caller is to blame
|
||||
rawsecret, err := fromBase64([]byte(secret))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
rr := m.Extra[len(m.Extra)-1].(*TSIG)
|
||||
m.Extra = m.Extra[0 : len(m.Extra)-1] // kill the TSIG from the msg
|
||||
mbuf, err := m.Pack()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
buf := tsigBuffer(mbuf, rr, requestMAC, timersOnly)
|
||||
|
||||
t := new(TSIG)
|
||||
var h hash.Hash
|
||||
switch strings.ToLower(rr.Algorithm) {
|
||||
case HmacMD5:
|
||||
h = hmac.New(md5.New, []byte(rawsecret))
|
||||
case HmacSHA1:
|
||||
h = hmac.New(sha1.New, []byte(rawsecret))
|
||||
case HmacSHA256:
|
||||
h = hmac.New(sha256.New, []byte(rawsecret))
|
||||
case HmacSHA512:
|
||||
h = hmac.New(sha512.New, []byte(rawsecret))
|
||||
default:
|
||||
return nil, "", ErrKeyAlg
|
||||
}
|
||||
h.Write(buf)
|
||||
t.MAC = hex.EncodeToString(h.Sum(nil))
|
||||
t.MACSize = uint16(len(t.MAC) / 2) // Size is half!
|
||||
|
||||
t.Hdr = RR_Header{Name: rr.Hdr.Name, Rrtype: TypeTSIG, Class: ClassANY, Ttl: 0}
|
||||
t.Fudge = rr.Fudge
|
||||
t.TimeSigned = rr.TimeSigned
|
||||
t.Algorithm = rr.Algorithm
|
||||
t.OrigId = m.Id
|
||||
|
||||
tbuf := make([]byte, t.len())
|
||||
if off, err := PackRR(t, tbuf, 0, nil, false); err == nil {
|
||||
tbuf = tbuf[:off] // reset to actual size used
|
||||
} else {
|
||||
return nil, "", err
|
||||
}
|
||||
mbuf = append(mbuf, tbuf...)
|
||||
// Update the ArCount directly in the buffer.
|
||||
binary.BigEndian.PutUint16(mbuf[10:], uint16(len(m.Extra)+1))
|
||||
|
||||
return mbuf, t.MAC, nil
|
||||
}
|
||||
|
||||
// TsigVerify verifies the TSIG on a message.
|
||||
// If the signature does not validate err contains the
|
||||
// error, otherwise it is nil.
|
||||
func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error {
|
||||
rawsecret, err := fromBase64([]byte(secret))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Strip the TSIG from the incoming msg
|
||||
stripped, tsig, err := stripTsig(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgMAC, err := hex.DecodeString(tsig.MAC)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf := tsigBuffer(stripped, tsig, requestMAC, timersOnly)
|
||||
|
||||
// Fudge factor works both ways. A message can arrive before it was signed because
|
||||
// of clock skew.
|
||||
now := uint64(time.Now().Unix())
|
||||
ti := now - tsig.TimeSigned
|
||||
if now < tsig.TimeSigned {
|
||||
ti = tsig.TimeSigned - now
|
||||
}
|
||||
if uint64(tsig.Fudge) < ti {
|
||||
return ErrTime
|
||||
}
|
||||
|
||||
var h hash.Hash
|
||||
switch strings.ToLower(tsig.Algorithm) {
|
||||
case HmacMD5:
|
||||
h = hmac.New(md5.New, rawsecret)
|
||||
case HmacSHA1:
|
||||
h = hmac.New(sha1.New, rawsecret)
|
||||
case HmacSHA256:
|
||||
h = hmac.New(sha256.New, rawsecret)
|
||||
case HmacSHA512:
|
||||
h = hmac.New(sha512.New, rawsecret)
|
||||
default:
|
||||
return ErrKeyAlg
|
||||
}
|
||||
h.Write(buf)
|
||||
if !hmac.Equal(h.Sum(nil), msgMAC) {
|
||||
return ErrSig
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a wiredata buffer for the MAC calculation.
|
||||
func tsigBuffer(msgbuf []byte, rr *TSIG, requestMAC string, timersOnly bool) []byte {
|
||||
var buf []byte
|
||||
if rr.TimeSigned == 0 {
|
||||
rr.TimeSigned = uint64(time.Now().Unix())
|
||||
}
|
||||
if rr.Fudge == 0 {
|
||||
rr.Fudge = 300 // Standard (RFC) default.
|
||||
}
|
||||
|
||||
// Replace message ID in header with original ID from TSIG
|
||||
binary.BigEndian.PutUint16(msgbuf[0:2], rr.OrigId)
|
||||
|
||||
if requestMAC != "" {
|
||||
m := new(macWireFmt)
|
||||
m.MACSize = uint16(len(requestMAC) / 2)
|
||||
m.MAC = requestMAC
|
||||
buf = make([]byte, len(requestMAC)) // long enough
|
||||
n, _ := packMacWire(m, buf)
|
||||
buf = buf[:n]
|
||||
}
|
||||
|
||||
tsigvar := make([]byte, DefaultMsgSize)
|
||||
if timersOnly {
|
||||
tsig := new(timerWireFmt)
|
||||
tsig.TimeSigned = rr.TimeSigned
|
||||
tsig.Fudge = rr.Fudge
|
||||
n, _ := packTimerWire(tsig, tsigvar)
|
||||
tsigvar = tsigvar[:n]
|
||||
} else {
|
||||
tsig := new(tsigWireFmt)
|
||||
tsig.Name = strings.ToLower(rr.Hdr.Name)
|
||||
tsig.Class = ClassANY
|
||||
tsig.Ttl = rr.Hdr.Ttl
|
||||
tsig.Algorithm = strings.ToLower(rr.Algorithm)
|
||||
tsig.TimeSigned = rr.TimeSigned
|
||||
tsig.Fudge = rr.Fudge
|
||||
tsig.Error = rr.Error
|
||||
tsig.OtherLen = rr.OtherLen
|
||||
tsig.OtherData = rr.OtherData
|
||||
n, _ := packTsigWire(tsig, tsigvar)
|
||||
tsigvar = tsigvar[:n]
|
||||
}
|
||||
|
||||
if requestMAC != "" {
|
||||
x := append(buf, msgbuf...)
|
||||
buf = append(x, tsigvar...)
|
||||
} else {
|
||||
buf = append(msgbuf, tsigvar...)
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
// Strip the TSIG from the raw message.
|
||||
func stripTsig(msg []byte) ([]byte, *TSIG, error) {
|
||||
// Copied from msg.go's Unpack() Header, but modified.
|
||||
var (
|
||||
dh Header
|
||||
err error
|
||||
)
|
||||
off, tsigoff := 0, 0
|
||||
|
||||
if dh, off, err = unpackMsgHdr(msg, off); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if dh.Arcount == 0 {
|
||||
return nil, nil, ErrNoSig
|
||||
}
|
||||
|
||||
// Rcode, see msg.go Unpack()
|
||||
if int(dh.Bits&0xF) == RcodeNotAuth {
|
||||
return nil, nil, ErrAuth
|
||||
}
|
||||
|
||||
for i := 0; i < int(dh.Qdcount); i++ {
|
||||
_, off, err = unpackQuestion(msg, off)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
_, off, err = unpackRRslice(int(dh.Ancount), msg, off)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
_, off, err = unpackRRslice(int(dh.Nscount), msg, off)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
rr := new(TSIG)
|
||||
var extra RR
|
||||
for i := 0; i < int(dh.Arcount); i++ {
|
||||
tsigoff = off
|
||||
extra, off, err = UnpackRR(msg, off)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if extra.Header().Rrtype == TypeTSIG {
|
||||
rr = extra.(*TSIG)
|
||||
// Adjust Arcount.
|
||||
arcount := binary.BigEndian.Uint16(msg[10:])
|
||||
binary.BigEndian.PutUint16(msg[10:], arcount-1)
|
||||
break
|
||||
}
|
||||
}
|
||||
if rr == nil {
|
||||
return nil, nil, ErrNoSig
|
||||
}
|
||||
return msg[:tsigoff], rr, nil
|
||||
}
|
||||
|
||||
// Translate the TSIG time signed into a date. There is no
|
||||
// need for RFC1982 calculations as this date is 48 bits.
|
||||
func tsigTimeToString(t uint64) string {
|
||||
ti := time.Unix(int64(t), 0).UTC()
|
||||
return ti.Format("20060102150405")
|
||||
}
|
||||
|
||||
func packTsigWire(tw *tsigWireFmt, msg []byte) (int, error) {
|
||||
// copied from zmsg.go TSIG packing
|
||||
// RR_Header
|
||||
off, err := PackDomainName(tw.Name, msg, 0, nil, false)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
off, err = packUint16(tw.Class, msg, off)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
off, err = packUint32(tw.Ttl, msg, off)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
|
||||
off, err = PackDomainName(tw.Algorithm, msg, off, nil, false)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
off, err = packUint48(tw.TimeSigned, msg, off)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
off, err = packUint16(tw.Fudge, msg, off)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
|
||||
off, err = packUint16(tw.Error, msg, off)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
off, err = packUint16(tw.OtherLen, msg, off)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
off, err = packStringHex(tw.OtherData, msg, off)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
return off, nil
|
||||
}
|
||||
|
||||
func packMacWire(mw *macWireFmt, msg []byte) (int, error) {
|
||||
off, err := packUint16(mw.MACSize, msg, 0)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
off, err = packStringHex(mw.MAC, msg, off)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
return off, nil
|
||||
}
|
||||
|
||||
func packTimerWire(tw *timerWireFmt, msg []byte) (int, error) {
|
||||
off, err := packUint48(tw.TimeSigned, msg, 0)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
off, err = packUint16(tw.Fudge, msg, off)
|
||||
if err != nil {
|
||||
return off, err
|
||||
}
|
||||
return off, nil
|
||||
}
|
||||
52
vendor/github.com/miekg/dns/tsig_test.go
generated
vendored
Normal file
52
vendor/github.com/miekg/dns/tsig_test.go
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func newTsig(algo string) *Msg {
|
||||
m := new(Msg)
|
||||
m.SetQuestion("example.org.", TypeA)
|
||||
m.SetTsig("example.", algo, 300, time.Now().Unix())
|
||||
return m
|
||||
}
|
||||
|
||||
func TestTsig(t *testing.T) {
|
||||
m := newTsig(HmacMD5)
|
||||
buf, _, err := TsigGenerate(m, "pRZgBrBvI4NAHZYhxmhs/Q==", "", false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = TsigVerify(buf, "pRZgBrBvI4NAHZYhxmhs/Q==", "", false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// TSIG accounts for ID substitution. This means if the message ID is
|
||||
// changed by a forwarder, we should still be able to verify the TSIG.
|
||||
m = newTsig(HmacMD5)
|
||||
buf, _, err = TsigGenerate(m, "pRZgBrBvI4NAHZYhxmhs/Q==", "", false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
binary.BigEndian.PutUint16(buf[0:2], uint16(42))
|
||||
err = TsigVerify(buf, "pRZgBrBvI4NAHZYhxmhs/Q==", "", false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTsigCase(t *testing.T) {
|
||||
m := newTsig("HmAc-mD5.sig-ALg.rEg.int.") // HmacMD5
|
||||
buf, _, err := TsigGenerate(m, "pRZgBrBvI4NAHZYhxmhs/Q==", "", false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = TsigVerify(buf, "pRZgBrBvI4NAHZYhxmhs/Q==", "", false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
1381
vendor/github.com/miekg/dns/types.go
generated
vendored
Normal file
1381
vendor/github.com/miekg/dns/types.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
272
vendor/github.com/miekg/dns/types_generate.go
generated
vendored
Normal file
272
vendor/github.com/miekg/dns/types_generate.go
generated
vendored
Normal file
@@ -0,0 +1,272 @@
|
||||
//+build ignore
|
||||
|
||||
// types_generate.go is meant to run with go generate. It will use
|
||||
// go/{importer,types} to track down all the RR struct types. Then for each type
|
||||
// it will generate conversion tables (TypeToRR and TypeToString) and banal
|
||||
// methods (len, Header, copy) based on the struct tags. The generated source is
|
||||
// written to ztypes.go, and is meant to be checked into git.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"go/importer"
|
||||
"go/types"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
var skipLen = map[string]struct{}{
|
||||
"NSEC": {},
|
||||
"NSEC3": {},
|
||||
"OPT": {},
|
||||
"CSYNC": {},
|
||||
}
|
||||
|
||||
var packageHdr = `
|
||||
// Code generated by "go run types_generate.go"; DO NOT EDIT.
|
||||
|
||||
package dns
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"net"
|
||||
)
|
||||
|
||||
`
|
||||
|
||||
var TypeToRR = template.Must(template.New("TypeToRR").Parse(`
|
||||
// TypeToRR is a map of constructors for each RR type.
|
||||
var TypeToRR = map[uint16]func() RR{
|
||||
{{range .}}{{if ne . "RFC3597"}} Type{{.}}: func() RR { return new({{.}}) },
|
||||
{{end}}{{end}} }
|
||||
|
||||
`))
|
||||
|
||||
var typeToString = template.Must(template.New("typeToString").Parse(`
|
||||
// TypeToString is a map of strings for each RR type.
|
||||
var TypeToString = map[uint16]string{
|
||||
{{range .}}{{if ne . "NSAPPTR"}} Type{{.}}: "{{.}}",
|
||||
{{end}}{{end}} TypeNSAPPTR: "NSAP-PTR",
|
||||
}
|
||||
|
||||
`))
|
||||
|
||||
var headerFunc = template.Must(template.New("headerFunc").Parse(`
|
||||
{{range .}} func (rr *{{.}}) Header() *RR_Header { return &rr.Hdr }
|
||||
{{end}}
|
||||
|
||||
`))
|
||||
|
||||
// getTypeStruct will take a type and the package scope, and return the
|
||||
// (innermost) struct if the type is considered a RR type (currently defined as
|
||||
// those structs beginning with a RR_Header, could be redefined as implementing
|
||||
// the RR interface). The bool return value indicates if embedded structs were
|
||||
// resolved.
|
||||
func getTypeStruct(t types.Type, scope *types.Scope) (*types.Struct, bool) {
|
||||
st, ok := t.Underlying().(*types.Struct)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
if st.Field(0).Type() == scope.Lookup("RR_Header").Type() {
|
||||
return st, false
|
||||
}
|
||||
if st.Field(0).Anonymous() {
|
||||
st, _ := getTypeStruct(st.Field(0).Type(), scope)
|
||||
return st, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Import and type-check the package
|
||||
pkg, err := importer.Default().Import("github.com/miekg/dns")
|
||||
fatalIfErr(err)
|
||||
scope := pkg.Scope()
|
||||
|
||||
// Collect constants like TypeX
|
||||
var numberedTypes []string
|
||||
for _, name := range scope.Names() {
|
||||
o := scope.Lookup(name)
|
||||
if o == nil || !o.Exported() {
|
||||
continue
|
||||
}
|
||||
b, ok := o.Type().(*types.Basic)
|
||||
if !ok || b.Kind() != types.Uint16 {
|
||||
continue
|
||||
}
|
||||
if !strings.HasPrefix(o.Name(), "Type") {
|
||||
continue
|
||||
}
|
||||
name := strings.TrimPrefix(o.Name(), "Type")
|
||||
if name == "PrivateRR" {
|
||||
continue
|
||||
}
|
||||
numberedTypes = append(numberedTypes, name)
|
||||
}
|
||||
|
||||
// Collect actual types (*X)
|
||||
var namedTypes []string
|
||||
for _, name := range scope.Names() {
|
||||
o := scope.Lookup(name)
|
||||
if o == nil || !o.Exported() {
|
||||
continue
|
||||
}
|
||||
if st, _ := getTypeStruct(o.Type(), scope); st == nil {
|
||||
continue
|
||||
}
|
||||
if name == "PrivateRR" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if corresponding TypeX exists
|
||||
if scope.Lookup("Type"+o.Name()) == nil && o.Name() != "RFC3597" {
|
||||
log.Fatalf("Constant Type%s does not exist.", o.Name())
|
||||
}
|
||||
|
||||
namedTypes = append(namedTypes, o.Name())
|
||||
}
|
||||
|
||||
b := &bytes.Buffer{}
|
||||
b.WriteString(packageHdr)
|
||||
|
||||
// Generate TypeToRR
|
||||
fatalIfErr(TypeToRR.Execute(b, namedTypes))
|
||||
|
||||
// Generate typeToString
|
||||
fatalIfErr(typeToString.Execute(b, numberedTypes))
|
||||
|
||||
// Generate headerFunc
|
||||
fatalIfErr(headerFunc.Execute(b, namedTypes))
|
||||
|
||||
// Generate len()
|
||||
fmt.Fprint(b, "// len() functions\n")
|
||||
for _, name := range namedTypes {
|
||||
if _, ok := skipLen[name]; ok {
|
||||
continue
|
||||
}
|
||||
o := scope.Lookup(name)
|
||||
st, isEmbedded := getTypeStruct(o.Type(), scope)
|
||||
if isEmbedded {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(b, "func (rr *%s) len() int {\n", name)
|
||||
fmt.Fprintf(b, "l := rr.Hdr.len()\n")
|
||||
for i := 1; i < st.NumFields(); i++ {
|
||||
o := func(s string) { fmt.Fprintf(b, s, st.Field(i).Name()) }
|
||||
|
||||
if _, ok := st.Field(i).Type().(*types.Slice); ok {
|
||||
switch st.Tag(i) {
|
||||
case `dns:"-"`:
|
||||
// ignored
|
||||
case `dns:"cdomain-name"`, `dns:"domain-name"`, `dns:"txt"`:
|
||||
o("for _, x := range rr.%s { l += len(x) + 1 }\n")
|
||||
default:
|
||||
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
switch {
|
||||
case st.Tag(i) == `dns:"-"`:
|
||||
// ignored
|
||||
case st.Tag(i) == `dns:"cdomain-name"`, st.Tag(i) == `dns:"domain-name"`:
|
||||
o("l += len(rr.%s) + 1\n")
|
||||
case st.Tag(i) == `dns:"octet"`:
|
||||
o("l += len(rr.%s)\n")
|
||||
case strings.HasPrefix(st.Tag(i), `dns:"size-base64`):
|
||||
fallthrough
|
||||
case st.Tag(i) == `dns:"base64"`:
|
||||
o("l += base64.StdEncoding.DecodedLen(len(rr.%s))\n")
|
||||
case strings.HasPrefix(st.Tag(i), `dns:"size-hex:`): // this has an extra field where the length is stored
|
||||
o("l += len(rr.%s)/2\n")
|
||||
case strings.HasPrefix(st.Tag(i), `dns:"size-hex`):
|
||||
fallthrough
|
||||
case st.Tag(i) == `dns:"hex"`:
|
||||
o("l += len(rr.%s)/2 + 1\n")
|
||||
case st.Tag(i) == `dns:"a"`:
|
||||
o("l += net.IPv4len // %s\n")
|
||||
case st.Tag(i) == `dns:"aaaa"`:
|
||||
o("l += net.IPv6len // %s\n")
|
||||
case st.Tag(i) == `dns:"txt"`:
|
||||
o("for _, t := range rr.%s { l += len(t) + 1 }\n")
|
||||
case st.Tag(i) == `dns:"uint48"`:
|
||||
o("l += 6 // %s\n")
|
||||
case st.Tag(i) == "":
|
||||
switch st.Field(i).Type().(*types.Basic).Kind() {
|
||||
case types.Uint8:
|
||||
o("l++ // %s\n")
|
||||
case types.Uint16:
|
||||
o("l += 2 // %s\n")
|
||||
case types.Uint32:
|
||||
o("l += 4 // %s\n")
|
||||
case types.Uint64:
|
||||
o("l += 8 // %s\n")
|
||||
case types.String:
|
||||
o("l += len(rr.%s) + 1\n")
|
||||
default:
|
||||
log.Fatalln(name, st.Field(i).Name())
|
||||
}
|
||||
default:
|
||||
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(b, "return l }\n")
|
||||
}
|
||||
|
||||
// Generate copy()
|
||||
fmt.Fprint(b, "// copy() functions\n")
|
||||
for _, name := range namedTypes {
|
||||
o := scope.Lookup(name)
|
||||
st, isEmbedded := getTypeStruct(o.Type(), scope)
|
||||
if isEmbedded {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(b, "func (rr *%s) copy() RR {\n", name)
|
||||
fields := []string{"rr.Hdr"}
|
||||
for i := 1; i < st.NumFields(); i++ {
|
||||
f := st.Field(i).Name()
|
||||
if sl, ok := st.Field(i).Type().(*types.Slice); ok {
|
||||
t := sl.Underlying().String()
|
||||
t = strings.TrimPrefix(t, "[]")
|
||||
if strings.Contains(t, ".") {
|
||||
splits := strings.Split(t, ".")
|
||||
t = splits[len(splits)-1]
|
||||
}
|
||||
fmt.Fprintf(b, "%s := make([]%s, len(rr.%s)); copy(%s, rr.%s)\n",
|
||||
f, t, f, f, f)
|
||||
fields = append(fields, f)
|
||||
continue
|
||||
}
|
||||
if st.Field(i).Type().String() == "net.IP" {
|
||||
fields = append(fields, "copyIP(rr."+f+")")
|
||||
continue
|
||||
}
|
||||
fields = append(fields, "rr."+f)
|
||||
}
|
||||
fmt.Fprintf(b, "return &%s{%s}\n", name, strings.Join(fields, ","))
|
||||
fmt.Fprintf(b, "}\n")
|
||||
}
|
||||
|
||||
// gofmt
|
||||
res, err := format.Source(b.Bytes())
|
||||
if err != nil {
|
||||
b.WriteTo(os.Stderr)
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// write result
|
||||
f, err := os.Create("ztypes.go")
|
||||
fatalIfErr(err)
|
||||
defer f.Close()
|
||||
f.Write(res)
|
||||
}
|
||||
|
||||
func fatalIfErr(err error) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
74
vendor/github.com/miekg/dns/types_test.go
generated
vendored
Normal file
74
vendor/github.com/miekg/dns/types_test.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCmToM(t *testing.T) {
|
||||
s := cmToM(0, 0)
|
||||
if s != "0.00" {
|
||||
t.Error("0, 0")
|
||||
}
|
||||
|
||||
s = cmToM(1, 0)
|
||||
if s != "0.01" {
|
||||
t.Error("1, 0")
|
||||
}
|
||||
|
||||
s = cmToM(3, 1)
|
||||
if s != "0.30" {
|
||||
t.Error("3, 1")
|
||||
}
|
||||
|
||||
s = cmToM(4, 2)
|
||||
if s != "4" {
|
||||
t.Error("4, 2")
|
||||
}
|
||||
|
||||
s = cmToM(5, 3)
|
||||
if s != "50" {
|
||||
t.Error("5, 3")
|
||||
}
|
||||
|
||||
s = cmToM(7, 5)
|
||||
if s != "7000" {
|
||||
t.Error("7, 5")
|
||||
}
|
||||
|
||||
s = cmToM(9, 9)
|
||||
if s != "90000000" {
|
||||
t.Error("9, 9")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitN(t *testing.T) {
|
||||
xs := splitN("abc", 5)
|
||||
if len(xs) != 1 && xs[0] != "abc" {
|
||||
t.Errorf("failure to split abc")
|
||||
}
|
||||
|
||||
s := ""
|
||||
for i := 0; i < 255; i++ {
|
||||
s += "a"
|
||||
}
|
||||
|
||||
xs = splitN(s, 255)
|
||||
if len(xs) != 1 && xs[0] != s {
|
||||
t.Errorf("failure to split 255 char long string")
|
||||
}
|
||||
|
||||
s += "b"
|
||||
xs = splitN(s, 255)
|
||||
if len(xs) != 2 || xs[1] != "b" {
|
||||
t.Errorf("failure to split 256 char long string: %d", len(xs))
|
||||
}
|
||||
|
||||
// Make s longer
|
||||
for i := 0; i < 255; i++ {
|
||||
s += "a"
|
||||
}
|
||||
xs = splitN(s, 255)
|
||||
if len(xs) != 3 || xs[2] != "a" {
|
||||
t.Errorf("failure to split 510 char long string: %d", len(xs))
|
||||
}
|
||||
}
|
||||
102
vendor/github.com/miekg/dns/udp.go
generated
vendored
Normal file
102
vendor/github.com/miekg/dns/udp.go
generated
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
// +build !windows
|
||||
|
||||
package dns
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
// This is the required size of the OOB buffer to pass to ReadMsgUDP.
|
||||
var udpOOBSize = func() int {
|
||||
// We can't know whether we'll get an IPv4 control message or an
|
||||
// IPv6 control message ahead of time. To get around this, we size
|
||||
// the buffer equal to the largest of the two.
|
||||
|
||||
oob4 := ipv4.NewControlMessage(ipv4.FlagDst | ipv4.FlagInterface)
|
||||
oob6 := ipv6.NewControlMessage(ipv6.FlagDst | ipv6.FlagInterface)
|
||||
|
||||
if len(oob4) > len(oob6) {
|
||||
return len(oob4)
|
||||
}
|
||||
|
||||
return len(oob6)
|
||||
}()
|
||||
|
||||
// SessionUDP holds the remote address and the associated
|
||||
// out-of-band data.
|
||||
type SessionUDP struct {
|
||||
raddr *net.UDPAddr
|
||||
context []byte
|
||||
}
|
||||
|
||||
// RemoteAddr returns the remote network address.
|
||||
func (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr }
|
||||
|
||||
// ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a
|
||||
// net.UDPAddr.
|
||||
func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) {
|
||||
oob := make([]byte, udpOOBSize)
|
||||
n, oobn, _, raddr, err := conn.ReadMsgUDP(b, oob)
|
||||
if err != nil {
|
||||
return n, nil, err
|
||||
}
|
||||
return n, &SessionUDP{raddr, oob[:oobn]}, err
|
||||
}
|
||||
|
||||
// WriteToSessionUDP acts just like net.UDPConn.WriteTo(), but uses a *SessionUDP instead of a net.Addr.
|
||||
func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) {
|
||||
oob := correctSource(session.context)
|
||||
n, _, err := conn.WriteMsgUDP(b, oob, session.raddr)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func setUDPSocketOptions(conn *net.UDPConn) error {
|
||||
// Try setting the flags for both families and ignore the errors unless they
|
||||
// both error.
|
||||
err6 := ipv6.NewPacketConn(conn).SetControlMessage(ipv6.FlagDst|ipv6.FlagInterface, true)
|
||||
err4 := ipv4.NewPacketConn(conn).SetControlMessage(ipv4.FlagDst|ipv4.FlagInterface, true)
|
||||
if err6 != nil && err4 != nil {
|
||||
return err4
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseDstFromOOB takes oob data and returns the destination IP.
|
||||
func parseDstFromOOB(oob []byte) net.IP {
|
||||
// Start with IPv6 and then fallback to IPv4
|
||||
// TODO(fastest963): Figure out a way to prefer one or the other. Looking at
|
||||
// the lvl of the header for a 0 or 41 isn't cross-platform.
|
||||
cm6 := new(ipv6.ControlMessage)
|
||||
if cm6.Parse(oob) == nil && cm6.Dst != nil {
|
||||
return cm6.Dst
|
||||
}
|
||||
cm4 := new(ipv4.ControlMessage)
|
||||
if cm4.Parse(oob) == nil && cm4.Dst != nil {
|
||||
return cm4.Dst
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// correctSource takes oob data and returns new oob data with the Src equal to the Dst
|
||||
func correctSource(oob []byte) []byte {
|
||||
dst := parseDstFromOOB(oob)
|
||||
if dst == nil {
|
||||
return nil
|
||||
}
|
||||
// If the dst is definitely an IPv6, then use ipv6's ControlMessage to
|
||||
// respond otherwise use ipv4's because ipv6's marshal ignores ipv4
|
||||
// addresses.
|
||||
if dst.To4() == nil {
|
||||
cm := new(ipv6.ControlMessage)
|
||||
cm.Src = dst
|
||||
oob = cm.Marshal()
|
||||
} else {
|
||||
cm := new(ipv4.ControlMessage)
|
||||
cm.Src = dst
|
||||
oob = cm.Marshal()
|
||||
}
|
||||
return oob
|
||||
}
|
||||
140
vendor/github.com/miekg/dns/udp_test.go
generated
vendored
Normal file
140
vendor/github.com/miekg/dns/udp_test.go
generated
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
// +build linux,!appengine
|
||||
|
||||
package dns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
func TestSetUDPSocketOptions(t *testing.T) {
|
||||
// returns an error if we cannot resolve that address
|
||||
testFamily := func(n, addr string) error {
|
||||
a, err := net.ResolveUDPAddr(n, addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c, err := net.ListenUDP(n, a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := setUDPSocketOptions(c); err != nil {
|
||||
t.Fatalf("failed to set socket options: %v", err)
|
||||
}
|
||||
ch := make(chan *SessionUDP)
|
||||
go func() {
|
||||
// Set some deadline so this goroutine doesn't hang forever
|
||||
c.SetReadDeadline(time.Now().Add(time.Minute))
|
||||
b := make([]byte, 1)
|
||||
_, sess, err := ReadFromSessionUDP(c, b)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read from conn: %v", err)
|
||||
}
|
||||
ch <- sess
|
||||
}()
|
||||
|
||||
c2, err := net.Dial("udp", c.LocalAddr().String())
|
||||
if err != nil {
|
||||
t.Fatalf("failed to dial udp: %v", err)
|
||||
}
|
||||
if _, err := c2.Write([]byte{1}); err != nil {
|
||||
t.Fatalf("failed to write to conn: %v", err)
|
||||
}
|
||||
sess := <-ch
|
||||
if len(sess.context) == 0 {
|
||||
t.Fatalf("empty session context: %v", sess)
|
||||
}
|
||||
ip := parseDstFromOOB(sess.context)
|
||||
if ip == nil {
|
||||
t.Fatalf("failed to parse dst: %v", sess)
|
||||
}
|
||||
if !strings.Contains(c.LocalAddr().String(), ip.String()) {
|
||||
t.Fatalf("dst was different than listen addr: %v != %v", ip.String(), c.LocalAddr().String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// we require that ipv4 be supported
|
||||
if err := testFamily("udp4", "127.0.0.1:0"); err != nil {
|
||||
t.Fatalf("failed to test socket options on IPv4: %v", err)
|
||||
}
|
||||
// IPv6 might not be supported so these will just log
|
||||
if err := testFamily("udp6", "[::1]:0"); err != nil {
|
||||
t.Logf("failed to test socket options on IPv6-only: %v", err)
|
||||
}
|
||||
if err := testFamily("udp", "[::1]:0"); err != nil {
|
||||
t.Logf("failed to test socket options on IPv6/IPv4: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseDstFromOOB(t *testing.T) {
|
||||
if runtime.GOARCH != "amd64" {
|
||||
// The cmsghdr struct differs in the width (32/64-bit) of
|
||||
// lengths and the struct padding between architectures.
|
||||
// The data below was only written with amd64 in mind, and
|
||||
// thus the test must be skipped on other architectures.
|
||||
t.Skip("skipping test on unsupported architecture")
|
||||
}
|
||||
|
||||
// dst is :ffff:100.100.100.100
|
||||
oob := []byte{36, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 100, 100, 100, 100, 2, 0, 0, 0}
|
||||
dst := parseDstFromOOB(oob)
|
||||
dst4 := dst.To4()
|
||||
if dst4 == nil {
|
||||
t.Errorf("failed to parse IPv4 in IPv6: %v", dst)
|
||||
} else if dst4.String() != "100.100.100.100" {
|
||||
t.Errorf("unexpected IPv4: %v", dst4)
|
||||
}
|
||||
|
||||
// dst is 2001:db8::1
|
||||
oob = []byte{36, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 50, 0, 0, 0, 32, 1, 13, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}
|
||||
dst = parseDstFromOOB(oob)
|
||||
dst6 := dst.To16()
|
||||
if dst6 == nil {
|
||||
t.Errorf("failed to parse IPv6: %v", dst)
|
||||
} else if dst6.String() != "2001:db8::1" {
|
||||
t.Errorf("unexpected IPv6: %v", dst4)
|
||||
}
|
||||
|
||||
// dst is 100.100.100.100 but was received on 10.10.10.10
|
||||
oob = []byte{28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 10, 10, 10, 10, 100, 100, 100, 100, 0, 0, 0, 0}
|
||||
dst = parseDstFromOOB(oob)
|
||||
dst4 = dst.To4()
|
||||
if dst4 == nil {
|
||||
t.Errorf("failed to parse IPv4: %v", dst)
|
||||
} else if dst4.String() != "100.100.100.100" {
|
||||
t.Errorf("unexpected IPv4: %v", dst4)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCorrectSource(t *testing.T) {
|
||||
if runtime.GOARCH != "amd64" {
|
||||
// See comment above in TestParseDstFromOOB.
|
||||
t.Skip("skipping test on unsupported architecture")
|
||||
}
|
||||
|
||||
// dst is :ffff:100.100.100.100 which should be counted as IPv4
|
||||
oob := []byte{36, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 100, 100, 100, 100, 2, 0, 0, 0}
|
||||
soob := correctSource(oob)
|
||||
cm4 := new(ipv4.ControlMessage)
|
||||
cm4.Src = net.ParseIP("100.100.100.100")
|
||||
if !bytes.Equal(soob, cm4.Marshal()) {
|
||||
t.Errorf("unexpected oob for ipv4 address: %v", soob)
|
||||
}
|
||||
|
||||
// dst is 2001:db8::1
|
||||
oob = []byte{36, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 50, 0, 0, 0, 32, 1, 13, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}
|
||||
soob = correctSource(oob)
|
||||
cm6 := new(ipv6.ControlMessage)
|
||||
cm6.Src = net.ParseIP("2001:db8::1")
|
||||
if !bytes.Equal(soob, cm6.Marshal()) {
|
||||
t.Errorf("unexpected oob for IPv6 address: %v", soob)
|
||||
}
|
||||
}
|
||||
37
vendor/github.com/miekg/dns/udp_windows.go
generated
vendored
Normal file
37
vendor/github.com/miekg/dns/udp_windows.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
// +build windows
|
||||
|
||||
package dns
|
||||
|
||||
import "net"
|
||||
|
||||
// SessionUDP holds the remote address
|
||||
type SessionUDP struct {
|
||||
raddr *net.UDPAddr
|
||||
}
|
||||
|
||||
// RemoteAddr returns the remote network address.
|
||||
func (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr }
|
||||
|
||||
// ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a
|
||||
// net.UDPAddr.
|
||||
// TODO(fastest963): Once go1.10 is released, use ReadMsgUDP.
|
||||
func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) {
|
||||
n, raddr, err := conn.ReadFrom(b)
|
||||
if err != nil {
|
||||
return n, nil, err
|
||||
}
|
||||
session := &SessionUDP{raddr.(*net.UDPAddr)}
|
||||
return n, session, err
|
||||
}
|
||||
|
||||
// WriteToSessionUDP acts just like net.UDPConn.WriteTo(), but uses a *SessionUDP instead of a net.Addr.
|
||||
// TODO(fastest963): Once go1.10 is released, use WriteMsgUDP.
|
||||
func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) {
|
||||
n, err := conn.WriteTo(b, session.raddr)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// TODO(fastest963): Once go1.10 is released and we can use *MsgUDP methods
|
||||
// use the standard method in udp.go for these.
|
||||
func setUDPSocketOptions(*net.UDPConn) error { return nil }
|
||||
func parseDstFromOOB([]byte, net.IP) net.IP { return nil }
|
||||
106
vendor/github.com/miekg/dns/update.go
generated
vendored
Normal file
106
vendor/github.com/miekg/dns/update.go
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
package dns
|
||||
|
||||
// NameUsed sets the RRs in the prereq section to
|
||||
// "Name is in use" RRs. RFC 2136 section 2.4.4.
|
||||
func (u *Msg) NameUsed(rr []RR) {
|
||||
if u.Answer == nil {
|
||||
u.Answer = make([]RR, 0, len(rr))
|
||||
}
|
||||
for _, r := range rr {
|
||||
u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassANY}})
|
||||
}
|
||||
}
|
||||
|
||||
// NameNotUsed sets the RRs in the prereq section to
|
||||
// "Name is in not use" RRs. RFC 2136 section 2.4.5.
|
||||
func (u *Msg) NameNotUsed(rr []RR) {
|
||||
if u.Answer == nil {
|
||||
u.Answer = make([]RR, 0, len(rr))
|
||||
}
|
||||
for _, r := range rr {
|
||||
u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassNONE}})
|
||||
}
|
||||
}
|
||||
|
||||
// Used sets the RRs in the prereq section to
|
||||
// "RRset exists (value dependent -- with rdata)" RRs. RFC 2136 section 2.4.2.
|
||||
func (u *Msg) Used(rr []RR) {
|
||||
if len(u.Question) == 0 {
|
||||
panic("dns: empty question section")
|
||||
}
|
||||
if u.Answer == nil {
|
||||
u.Answer = make([]RR, 0, len(rr))
|
||||
}
|
||||
for _, r := range rr {
|
||||
r.Header().Class = u.Question[0].Qclass
|
||||
u.Answer = append(u.Answer, r)
|
||||
}
|
||||
}
|
||||
|
||||
// RRsetUsed sets the RRs in the prereq section to
|
||||
// "RRset exists (value independent -- no rdata)" RRs. RFC 2136 section 2.4.1.
|
||||
func (u *Msg) RRsetUsed(rr []RR) {
|
||||
if u.Answer == nil {
|
||||
u.Answer = make([]RR, 0, len(rr))
|
||||
}
|
||||
for _, r := range rr {
|
||||
u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: r.Header().Rrtype, Class: ClassANY}})
|
||||
}
|
||||
}
|
||||
|
||||
// RRsetNotUsed sets the RRs in the prereq section to
|
||||
// "RRset does not exist" RRs. RFC 2136 section 2.4.3.
|
||||
func (u *Msg) RRsetNotUsed(rr []RR) {
|
||||
if u.Answer == nil {
|
||||
u.Answer = make([]RR, 0, len(rr))
|
||||
}
|
||||
for _, r := range rr {
|
||||
u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: r.Header().Rrtype, Class: ClassNONE}})
|
||||
}
|
||||
}
|
||||
|
||||
// Insert creates a dynamic update packet that adds an complete RRset, see RFC 2136 section 2.5.1.
|
||||
func (u *Msg) Insert(rr []RR) {
|
||||
if len(u.Question) == 0 {
|
||||
panic("dns: empty question section")
|
||||
}
|
||||
if u.Ns == nil {
|
||||
u.Ns = make([]RR, 0, len(rr))
|
||||
}
|
||||
for _, r := range rr {
|
||||
r.Header().Class = u.Question[0].Qclass
|
||||
u.Ns = append(u.Ns, r)
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveRRset creates a dynamic update packet that deletes an RRset, see RFC 2136 section 2.5.2.
|
||||
func (u *Msg) RemoveRRset(rr []RR) {
|
||||
if u.Ns == nil {
|
||||
u.Ns = make([]RR, 0, len(rr))
|
||||
}
|
||||
for _, r := range rr {
|
||||
u.Ns = append(u.Ns, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: r.Header().Rrtype, Class: ClassANY}})
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveName creates a dynamic update packet that deletes all RRsets of a name, see RFC 2136 section 2.5.3
|
||||
func (u *Msg) RemoveName(rr []RR) {
|
||||
if u.Ns == nil {
|
||||
u.Ns = make([]RR, 0, len(rr))
|
||||
}
|
||||
for _, r := range rr {
|
||||
u.Ns = append(u.Ns, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassANY}})
|
||||
}
|
||||
}
|
||||
|
||||
// Remove creates a dynamic update packet deletes RR from a RRSset, see RFC 2136 section 2.5.4
|
||||
func (u *Msg) Remove(rr []RR) {
|
||||
if u.Ns == nil {
|
||||
u.Ns = make([]RR, 0, len(rr))
|
||||
}
|
||||
for _, r := range rr {
|
||||
r.Header().Class = ClassNONE
|
||||
r.Header().Ttl = 0
|
||||
u.Ns = append(u.Ns, r)
|
||||
}
|
||||
}
|
||||
139
vendor/github.com/miekg/dns/update_test.go
generated
vendored
Normal file
139
vendor/github.com/miekg/dns/update_test.go
generated
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDynamicUpdateParsing(t *testing.T) {
|
||||
prefix := "example.com. IN "
|
||||
for _, typ := range TypeToString {
|
||||
if typ == "OPT" || typ == "AXFR" || typ == "IXFR" || typ == "ANY" || typ == "TKEY" ||
|
||||
typ == "TSIG" || typ == "ISDN" || typ == "UNSPEC" || typ == "NULL" || typ == "ATMA" ||
|
||||
typ == "Reserved" || typ == "None" || typ == "NXT" || typ == "MAILB" || typ == "MAILA" {
|
||||
continue
|
||||
}
|
||||
if _, err := NewRR(prefix + typ); err != nil {
|
||||
t.Errorf("failure to parse: %s %s: %v", prefix, typ, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDynamicUpdateUnpack(t *testing.T) {
|
||||
// From https://github.com/miekg/dns/issues/150#issuecomment-62296803
|
||||
// It should be an update message for the zone "example.",
|
||||
// deleting the A RRset "example." and then adding an A record at "example.".
|
||||
// class ANY, TYPE A
|
||||
buf := []byte{171, 68, 40, 0, 0, 1, 0, 0, 0, 2, 0, 0, 7, 101, 120, 97, 109, 112, 108, 101, 0, 0, 6, 0, 1, 192, 12, 0, 1, 0, 255, 0, 0, 0, 0, 0, 0, 192, 12, 0, 1, 0, 1, 0, 0, 0, 0, 0, 4, 127, 0, 0, 1}
|
||||
msg := new(Msg)
|
||||
err := msg.Unpack(buf)
|
||||
if err != nil {
|
||||
t.Errorf("failed to unpack: %v\n%s", err, msg.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDynamicUpdateZeroRdataUnpack(t *testing.T) {
|
||||
m := new(Msg)
|
||||
rr := &RR_Header{Name: ".", Rrtype: 0, Class: 1, Ttl: ^uint32(0), Rdlength: 0}
|
||||
m.Answer = []RR{rr, rr, rr, rr, rr}
|
||||
m.Ns = m.Answer
|
||||
for n, s := range TypeToString {
|
||||
rr.Rrtype = n
|
||||
bytes, err := m.Pack()
|
||||
if err != nil {
|
||||
t.Errorf("failed to pack %s: %v", s, err)
|
||||
continue
|
||||
}
|
||||
if err := new(Msg).Unpack(bytes); err != nil {
|
||||
t.Errorf("failed to unpack %s: %v", s, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveRRset(t *testing.T) {
|
||||
// Should add a zero data RR in Class ANY with a TTL of 0
|
||||
// for each set mentioned in the RRs provided to it.
|
||||
rr := testRR(". 100 IN A 127.0.0.1")
|
||||
m := new(Msg)
|
||||
m.Ns = []RR{&RR_Header{Name: ".", Rrtype: TypeA, Class: ClassANY, Ttl: 0, Rdlength: 0}}
|
||||
expectstr := m.String()
|
||||
expect, err := m.Pack()
|
||||
if err != nil {
|
||||
t.Fatalf("error packing expected msg: %v", err)
|
||||
}
|
||||
|
||||
m.Ns = nil
|
||||
m.RemoveRRset([]RR{rr})
|
||||
actual, err := m.Pack()
|
||||
if err != nil {
|
||||
t.Fatalf("error packing actual msg: %v", err)
|
||||
}
|
||||
if !bytes.Equal(actual, expect) {
|
||||
tmp := new(Msg)
|
||||
if err := tmp.Unpack(actual); err != nil {
|
||||
t.Fatalf("error unpacking actual msg: %v\nexpected: %v\ngot: %v\n", err, expect, actual)
|
||||
}
|
||||
t.Errorf("expected msg:\n%s", expectstr)
|
||||
t.Errorf("actual msg:\n%v", tmp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreReqAndRemovals(t *testing.T) {
|
||||
// Build a list of multiple prereqs and then somes removes followed by an insert.
|
||||
// We should be able to add multiple prereqs and updates.
|
||||
m := new(Msg)
|
||||
m.SetUpdate("example.org.")
|
||||
m.Id = 1234
|
||||
|
||||
// Use a full set of RRs each time, so we are sure the rdata is stripped.
|
||||
rrName1 := testRR("name_used. 3600 IN A 127.0.0.1")
|
||||
rrName2 := testRR("name_not_used. 3600 IN A 127.0.0.1")
|
||||
rrRemove1 := testRR("remove1. 3600 IN A 127.0.0.1")
|
||||
rrRemove2 := testRR("remove2. 3600 IN A 127.0.0.1")
|
||||
rrRemove3 := testRR("remove3. 3600 IN A 127.0.0.1")
|
||||
rrInsert := testRR("insert. 3600 IN A 127.0.0.1")
|
||||
rrRrset1 := testRR("rrset_used1. 3600 IN A 127.0.0.1")
|
||||
rrRrset2 := testRR("rrset_used2. 3600 IN A 127.0.0.1")
|
||||
rrRrset3 := testRR("rrset_not_used. 3600 IN A 127.0.0.1")
|
||||
|
||||
// Handle the prereqs.
|
||||
m.NameUsed([]RR{rrName1})
|
||||
m.NameNotUsed([]RR{rrName2})
|
||||
m.RRsetUsed([]RR{rrRrset1})
|
||||
m.Used([]RR{rrRrset2})
|
||||
m.RRsetNotUsed([]RR{rrRrset3})
|
||||
|
||||
// and now the updates.
|
||||
m.RemoveName([]RR{rrRemove1})
|
||||
m.RemoveRRset([]RR{rrRemove2})
|
||||
m.Remove([]RR{rrRemove3})
|
||||
m.Insert([]RR{rrInsert})
|
||||
|
||||
// This test function isn't a Example function because we print these RR with tabs at the
|
||||
// end and the Example function trim these, thus they never match.
|
||||
// TODO(miek): don't print these tabs and make this into an Example function.
|
||||
expect := `;; opcode: UPDATE, status: NOERROR, id: 1234
|
||||
;; flags:; QUERY: 1, ANSWER: 5, AUTHORITY: 4, ADDITIONAL: 0
|
||||
|
||||
;; QUESTION SECTION:
|
||||
;example.org. IN SOA
|
||||
|
||||
;; ANSWER SECTION:
|
||||
name_used. 0 CLASS255 ANY
|
||||
name_not_used. 0 NONE ANY
|
||||
rrset_used1. 0 CLASS255 A
|
||||
rrset_used2. 3600 IN A 127.0.0.1
|
||||
rrset_not_used. 0 NONE A
|
||||
|
||||
;; AUTHORITY SECTION:
|
||||
remove1. 0 CLASS255 ANY
|
||||
remove2. 0 CLASS255 A
|
||||
remove3. 0 NONE A 127.0.0.1
|
||||
insert. 3600 IN A 127.0.0.1
|
||||
`
|
||||
|
||||
if m.String() != expect {
|
||||
t.Errorf("expected msg:\n%s", expect)
|
||||
t.Errorf("actual msg:\n%v", m.String())
|
||||
}
|
||||
}
|
||||
15
vendor/github.com/miekg/dns/version.go
generated
vendored
Normal file
15
vendor/github.com/miekg/dns/version.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
package dns
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Version is current version of this library.
|
||||
var Version = V{1, 0, 8}
|
||||
|
||||
// V holds the version of this library.
|
||||
type V struct {
|
||||
Major, Minor, Patch int
|
||||
}
|
||||
|
||||
func (v V) String() string {
|
||||
return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)
|
||||
}
|
||||
10
vendor/github.com/miekg/dns/version_test.go
generated
vendored
Normal file
10
vendor/github.com/miekg/dns/version_test.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
package dns
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestVersion(t *testing.T) {
|
||||
v := V{1, 0, 0}
|
||||
if x := v.String(); x != "1.0.0" {
|
||||
t.Fatalf("Failed to convert version %v, got: %s", v, x)
|
||||
}
|
||||
}
|
||||
260
vendor/github.com/miekg/dns/xfr.go
generated
vendored
Normal file
260
vendor/github.com/miekg/dns/xfr.go
generated
vendored
Normal file
@@ -0,0 +1,260 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Envelope is used when doing a zone transfer with a remote server.
|
||||
type Envelope struct {
|
||||
RR []RR // The set of RRs in the answer section of the xfr reply message.
|
||||
Error error // If something went wrong, this contains the error.
|
||||
}
|
||||
|
||||
// A Transfer defines parameters that are used during a zone transfer.
|
||||
type Transfer struct {
|
||||
*Conn
|
||||
DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds
|
||||
ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds
|
||||
WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds
|
||||
TsigSecret map[string]string // Secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2)
|
||||
tsigTimersOnly bool
|
||||
}
|
||||
|
||||
// Think we need to away to stop the transfer
|
||||
|
||||
// In performs an incoming transfer with the server in a.
|
||||
// If you would like to set the source IP, or some other attribute
|
||||
// of a Dialer for a Transfer, you can do so by specifying the attributes
|
||||
// in the Transfer.Conn:
|
||||
//
|
||||
// d := net.Dialer{LocalAddr: transfer_source}
|
||||
// con, err := d.Dial("tcp", master)
|
||||
// dnscon := &dns.Conn{Conn:con}
|
||||
// transfer = &dns.Transfer{Conn: dnscon}
|
||||
// channel, err := transfer.In(message, master)
|
||||
//
|
||||
func (t *Transfer) In(q *Msg, a string) (env chan *Envelope, err error) {
|
||||
timeout := dnsTimeout
|
||||
if t.DialTimeout != 0 {
|
||||
timeout = t.DialTimeout
|
||||
}
|
||||
if t.Conn == nil {
|
||||
t.Conn, err = DialTimeout("tcp", a, timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err := t.WriteMsg(q); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
env = make(chan *Envelope)
|
||||
go func() {
|
||||
if q.Question[0].Qtype == TypeAXFR {
|
||||
go t.inAxfr(q, env)
|
||||
return
|
||||
}
|
||||
if q.Question[0].Qtype == TypeIXFR {
|
||||
go t.inIxfr(q, env)
|
||||
return
|
||||
}
|
||||
}()
|
||||
return env, nil
|
||||
}
|
||||
|
||||
func (t *Transfer) inAxfr(q *Msg, c chan *Envelope) {
|
||||
first := true
|
||||
defer t.Close()
|
||||
defer close(c)
|
||||
timeout := dnsTimeout
|
||||
if t.ReadTimeout != 0 {
|
||||
timeout = t.ReadTimeout
|
||||
}
|
||||
for {
|
||||
t.Conn.SetReadDeadline(time.Now().Add(timeout))
|
||||
in, err := t.ReadMsg()
|
||||
if err != nil {
|
||||
c <- &Envelope{nil, err}
|
||||
return
|
||||
}
|
||||
if q.Id != in.Id {
|
||||
c <- &Envelope{in.Answer, ErrId}
|
||||
return
|
||||
}
|
||||
if first {
|
||||
if in.Rcode != RcodeSuccess {
|
||||
c <- &Envelope{in.Answer, &Error{err: fmt.Sprintf(errXFR, in.Rcode)}}
|
||||
return
|
||||
}
|
||||
if !isSOAFirst(in) {
|
||||
c <- &Envelope{in.Answer, ErrSoa}
|
||||
return
|
||||
}
|
||||
first = !first
|
||||
// only one answer that is SOA, receive more
|
||||
if len(in.Answer) == 1 {
|
||||
t.tsigTimersOnly = true
|
||||
c <- &Envelope{in.Answer, nil}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if !first {
|
||||
t.tsigTimersOnly = true // Subsequent envelopes use this.
|
||||
if isSOALast(in) {
|
||||
c <- &Envelope{in.Answer, nil}
|
||||
return
|
||||
}
|
||||
c <- &Envelope{in.Answer, nil}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Transfer) inIxfr(q *Msg, c chan *Envelope) {
|
||||
serial := uint32(0) // The first serial seen is the current server serial
|
||||
axfr := true
|
||||
n := 0
|
||||
qser := q.Ns[0].(*SOA).Serial
|
||||
defer t.Close()
|
||||
defer close(c)
|
||||
timeout := dnsTimeout
|
||||
if t.ReadTimeout != 0 {
|
||||
timeout = t.ReadTimeout
|
||||
}
|
||||
for {
|
||||
t.SetReadDeadline(time.Now().Add(timeout))
|
||||
in, err := t.ReadMsg()
|
||||
if err != nil {
|
||||
c <- &Envelope{nil, err}
|
||||
return
|
||||
}
|
||||
if q.Id != in.Id {
|
||||
c <- &Envelope{in.Answer, ErrId}
|
||||
return
|
||||
}
|
||||
if in.Rcode != RcodeSuccess {
|
||||
c <- &Envelope{in.Answer, &Error{err: fmt.Sprintf(errXFR, in.Rcode)}}
|
||||
return
|
||||
}
|
||||
if n == 0 {
|
||||
// Check if the returned answer is ok
|
||||
if !isSOAFirst(in) {
|
||||
c <- &Envelope{in.Answer, ErrSoa}
|
||||
return
|
||||
}
|
||||
// This serial is important
|
||||
serial = in.Answer[0].(*SOA).Serial
|
||||
// Check if there are no changes in zone
|
||||
if qser >= serial {
|
||||
c <- &Envelope{in.Answer, nil}
|
||||
return
|
||||
}
|
||||
}
|
||||
// Now we need to check each message for SOA records, to see what we need to do
|
||||
t.tsigTimersOnly = true
|
||||
for _, rr := range in.Answer {
|
||||
if v, ok := rr.(*SOA); ok {
|
||||
if v.Serial == serial {
|
||||
n++
|
||||
// quit if it's a full axfr or the the servers' SOA is repeated the third time
|
||||
if axfr && n == 2 || n == 3 {
|
||||
c <- &Envelope{in.Answer, nil}
|
||||
return
|
||||
}
|
||||
} else if axfr {
|
||||
// it's an ixfr
|
||||
axfr = false
|
||||
}
|
||||
}
|
||||
}
|
||||
c <- &Envelope{in.Answer, nil}
|
||||
}
|
||||
}
|
||||
|
||||
// Out performs an outgoing transfer with the client connecting in w.
|
||||
// Basic use pattern:
|
||||
//
|
||||
// ch := make(chan *dns.Envelope)
|
||||
// tr := new(dns.Transfer)
|
||||
// go tr.Out(w, r, ch)
|
||||
// ch <- &dns.Envelope{RR: []dns.RR{soa, rr1, rr2, rr3, soa}}
|
||||
// close(ch)
|
||||
// w.Hijack()
|
||||
// // w.Close() // Client closes connection
|
||||
//
|
||||
// The server is responsible for sending the correct sequence of RRs through the
|
||||
// channel ch.
|
||||
func (t *Transfer) Out(w ResponseWriter, q *Msg, ch chan *Envelope) error {
|
||||
for x := range ch {
|
||||
r := new(Msg)
|
||||
// Compress?
|
||||
r.SetReply(q)
|
||||
r.Authoritative = true
|
||||
// assume it fits TODO(miek): fix
|
||||
r.Answer = append(r.Answer, x.RR...)
|
||||
if err := w.WriteMsg(r); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
w.TsigTimersOnly(true)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadMsg reads a message from the transfer connection t.
|
||||
func (t *Transfer) ReadMsg() (*Msg, error) {
|
||||
m := new(Msg)
|
||||
p := make([]byte, MaxMsgSize)
|
||||
n, err := t.Read(p)
|
||||
if err != nil && n == 0 {
|
||||
return nil, err
|
||||
}
|
||||
p = p[:n]
|
||||
if err := m.Unpack(p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ts := m.IsTsig(); ts != nil && t.TsigSecret != nil {
|
||||
if _, ok := t.TsigSecret[ts.Hdr.Name]; !ok {
|
||||
return m, ErrSecret
|
||||
}
|
||||
// Need to work on the original message p, as that was used to calculate the tsig.
|
||||
err = TsigVerify(p, t.TsigSecret[ts.Hdr.Name], t.tsigRequestMAC, t.tsigTimersOnly)
|
||||
t.tsigRequestMAC = ts.MAC
|
||||
}
|
||||
return m, err
|
||||
}
|
||||
|
||||
// WriteMsg writes a message through the transfer connection t.
|
||||
func (t *Transfer) WriteMsg(m *Msg) (err error) {
|
||||
var out []byte
|
||||
if ts := m.IsTsig(); ts != nil && t.TsigSecret != nil {
|
||||
if _, ok := t.TsigSecret[ts.Hdr.Name]; !ok {
|
||||
return ErrSecret
|
||||
}
|
||||
out, t.tsigRequestMAC, err = TsigGenerate(m, t.TsigSecret[ts.Hdr.Name], t.tsigRequestMAC, t.tsigTimersOnly)
|
||||
} else {
|
||||
out, err = m.Pack()
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = t.Write(out); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isSOAFirst(in *Msg) bool {
|
||||
if len(in.Answer) > 0 {
|
||||
return in.Answer[0].Header().Rrtype == TypeSOA
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isSOALast(in *Msg) bool {
|
||||
if len(in.Answer) > 0 {
|
||||
return in.Answer[len(in.Answer)-1].Header().Rrtype == TypeSOA
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const errXFR = "bad xfr rcode: %d"
|
||||
155
vendor/github.com/miekg/dns/zcompress.go
generated
vendored
Normal file
155
vendor/github.com/miekg/dns/zcompress.go
generated
vendored
Normal file
@@ -0,0 +1,155 @@
|
||||
// Code generated by "go run compress_generate.go"; DO NOT EDIT.
|
||||
|
||||
package dns
|
||||
|
||||
func compressionLenHelperType(c map[string]int, r RR, initLen int) int {
|
||||
currentLen := initLen
|
||||
switch x := r.(type) {
|
||||
case *AFSDB:
|
||||
currentLen -= len(x.Hostname) + 1
|
||||
currentLen += compressionLenHelper(c, x.Hostname, currentLen)
|
||||
case *CNAME:
|
||||
currentLen -= len(x.Target) + 1
|
||||
currentLen += compressionLenHelper(c, x.Target, currentLen)
|
||||
case *DNAME:
|
||||
currentLen -= len(x.Target) + 1
|
||||
currentLen += compressionLenHelper(c, x.Target, currentLen)
|
||||
case *HIP:
|
||||
for i := range x.RendezvousServers {
|
||||
currentLen -= len(x.RendezvousServers[i]) + 1
|
||||
}
|
||||
for i := range x.RendezvousServers {
|
||||
currentLen += compressionLenHelper(c, x.RendezvousServers[i], currentLen)
|
||||
}
|
||||
case *KX:
|
||||
currentLen -= len(x.Exchanger) + 1
|
||||
currentLen += compressionLenHelper(c, x.Exchanger, currentLen)
|
||||
case *LP:
|
||||
currentLen -= len(x.Fqdn) + 1
|
||||
currentLen += compressionLenHelper(c, x.Fqdn, currentLen)
|
||||
case *MB:
|
||||
currentLen -= len(x.Mb) + 1
|
||||
currentLen += compressionLenHelper(c, x.Mb, currentLen)
|
||||
case *MD:
|
||||
currentLen -= len(x.Md) + 1
|
||||
currentLen += compressionLenHelper(c, x.Md, currentLen)
|
||||
case *MF:
|
||||
currentLen -= len(x.Mf) + 1
|
||||
currentLen += compressionLenHelper(c, x.Mf, currentLen)
|
||||
case *MG:
|
||||
currentLen -= len(x.Mg) + 1
|
||||
currentLen += compressionLenHelper(c, x.Mg, currentLen)
|
||||
case *MINFO:
|
||||
currentLen -= len(x.Rmail) + 1
|
||||
currentLen += compressionLenHelper(c, x.Rmail, currentLen)
|
||||
currentLen -= len(x.Email) + 1
|
||||
currentLen += compressionLenHelper(c, x.Email, currentLen)
|
||||
case *MR:
|
||||
currentLen -= len(x.Mr) + 1
|
||||
currentLen += compressionLenHelper(c, x.Mr, currentLen)
|
||||
case *MX:
|
||||
currentLen -= len(x.Mx) + 1
|
||||
currentLen += compressionLenHelper(c, x.Mx, currentLen)
|
||||
case *NAPTR:
|
||||
currentLen -= len(x.Replacement) + 1
|
||||
currentLen += compressionLenHelper(c, x.Replacement, currentLen)
|
||||
case *NS:
|
||||
currentLen -= len(x.Ns) + 1
|
||||
currentLen += compressionLenHelper(c, x.Ns, currentLen)
|
||||
case *NSAPPTR:
|
||||
currentLen -= len(x.Ptr) + 1
|
||||
currentLen += compressionLenHelper(c, x.Ptr, currentLen)
|
||||
case *NSEC:
|
||||
currentLen -= len(x.NextDomain) + 1
|
||||
currentLen += compressionLenHelper(c, x.NextDomain, currentLen)
|
||||
case *PTR:
|
||||
currentLen -= len(x.Ptr) + 1
|
||||
currentLen += compressionLenHelper(c, x.Ptr, currentLen)
|
||||
case *PX:
|
||||
currentLen -= len(x.Map822) + 1
|
||||
currentLen += compressionLenHelper(c, x.Map822, currentLen)
|
||||
currentLen -= len(x.Mapx400) + 1
|
||||
currentLen += compressionLenHelper(c, x.Mapx400, currentLen)
|
||||
case *RP:
|
||||
currentLen -= len(x.Mbox) + 1
|
||||
currentLen += compressionLenHelper(c, x.Mbox, currentLen)
|
||||
currentLen -= len(x.Txt) + 1
|
||||
currentLen += compressionLenHelper(c, x.Txt, currentLen)
|
||||
case *RRSIG:
|
||||
currentLen -= len(x.SignerName) + 1
|
||||
currentLen += compressionLenHelper(c, x.SignerName, currentLen)
|
||||
case *RT:
|
||||
currentLen -= len(x.Host) + 1
|
||||
currentLen += compressionLenHelper(c, x.Host, currentLen)
|
||||
case *SIG:
|
||||
currentLen -= len(x.SignerName) + 1
|
||||
currentLen += compressionLenHelper(c, x.SignerName, currentLen)
|
||||
case *SOA:
|
||||
currentLen -= len(x.Ns) + 1
|
||||
currentLen += compressionLenHelper(c, x.Ns, currentLen)
|
||||
currentLen -= len(x.Mbox) + 1
|
||||
currentLen += compressionLenHelper(c, x.Mbox, currentLen)
|
||||
case *SRV:
|
||||
currentLen -= len(x.Target) + 1
|
||||
currentLen += compressionLenHelper(c, x.Target, currentLen)
|
||||
case *TALINK:
|
||||
currentLen -= len(x.PreviousName) + 1
|
||||
currentLen += compressionLenHelper(c, x.PreviousName, currentLen)
|
||||
currentLen -= len(x.NextName) + 1
|
||||
currentLen += compressionLenHelper(c, x.NextName, currentLen)
|
||||
case *TKEY:
|
||||
currentLen -= len(x.Algorithm) + 1
|
||||
currentLen += compressionLenHelper(c, x.Algorithm, currentLen)
|
||||
case *TSIG:
|
||||
currentLen -= len(x.Algorithm) + 1
|
||||
currentLen += compressionLenHelper(c, x.Algorithm, currentLen)
|
||||
}
|
||||
return currentLen - initLen
|
||||
}
|
||||
|
||||
func compressionLenSearchType(c map[string]int, r RR) (int, bool, int) {
|
||||
switch x := r.(type) {
|
||||
case *AFSDB:
|
||||
k1, ok1, sz1 := compressionLenSearch(c, x.Hostname)
|
||||
return k1, ok1, sz1
|
||||
case *CNAME:
|
||||
k1, ok1, sz1 := compressionLenSearch(c, x.Target)
|
||||
return k1, ok1, sz1
|
||||
case *MB:
|
||||
k1, ok1, sz1 := compressionLenSearch(c, x.Mb)
|
||||
return k1, ok1, sz1
|
||||
case *MD:
|
||||
k1, ok1, sz1 := compressionLenSearch(c, x.Md)
|
||||
return k1, ok1, sz1
|
||||
case *MF:
|
||||
k1, ok1, sz1 := compressionLenSearch(c, x.Mf)
|
||||
return k1, ok1, sz1
|
||||
case *MG:
|
||||
k1, ok1, sz1 := compressionLenSearch(c, x.Mg)
|
||||
return k1, ok1, sz1
|
||||
case *MINFO:
|
||||
k1, ok1, sz1 := compressionLenSearch(c, x.Rmail)
|
||||
k2, ok2, sz2 := compressionLenSearch(c, x.Email)
|
||||
return k1 + k2, ok1 && ok2, sz1 + sz2
|
||||
case *MR:
|
||||
k1, ok1, sz1 := compressionLenSearch(c, x.Mr)
|
||||
return k1, ok1, sz1
|
||||
case *MX:
|
||||
k1, ok1, sz1 := compressionLenSearch(c, x.Mx)
|
||||
return k1, ok1, sz1
|
||||
case *NS:
|
||||
k1, ok1, sz1 := compressionLenSearch(c, x.Ns)
|
||||
return k1, ok1, sz1
|
||||
case *PTR:
|
||||
k1, ok1, sz1 := compressionLenSearch(c, x.Ptr)
|
||||
return k1, ok1, sz1
|
||||
case *RT:
|
||||
k1, ok1, sz1 := compressionLenSearch(c, x.Host)
|
||||
return k1, ok1, sz1
|
||||
case *SOA:
|
||||
k1, ok1, sz1 := compressionLenSearch(c, x.Ns)
|
||||
k2, ok2, sz2 := compressionLenSearch(c, x.Mbox)
|
||||
return k1 + k2, ok1 && ok2, sz1 + sz2
|
||||
}
|
||||
return 0, false, 0
|
||||
}
|
||||
3615
vendor/github.com/miekg/dns/zmsg.go
generated
vendored
Normal file
3615
vendor/github.com/miekg/dns/zmsg.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
863
vendor/github.com/miekg/dns/ztypes.go
generated
vendored
Normal file
863
vendor/github.com/miekg/dns/ztypes.go
generated
vendored
Normal file
@@ -0,0 +1,863 @@
|
||||
// Code generated by "go run types_generate.go"; DO NOT EDIT.
|
||||
|
||||
package dns
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"net"
|
||||
)
|
||||
|
||||
// TypeToRR is a map of constructors for each RR type.
|
||||
var TypeToRR = map[uint16]func() RR{
|
||||
TypeA: func() RR { return new(A) },
|
||||
TypeAAAA: func() RR { return new(AAAA) },
|
||||
TypeAFSDB: func() RR { return new(AFSDB) },
|
||||
TypeANY: func() RR { return new(ANY) },
|
||||
TypeAVC: func() RR { return new(AVC) },
|
||||
TypeCAA: func() RR { return new(CAA) },
|
||||
TypeCDNSKEY: func() RR { return new(CDNSKEY) },
|
||||
TypeCDS: func() RR { return new(CDS) },
|
||||
TypeCERT: func() RR { return new(CERT) },
|
||||
TypeCNAME: func() RR { return new(CNAME) },
|
||||
TypeCSYNC: func() RR { return new(CSYNC) },
|
||||
TypeDHCID: func() RR { return new(DHCID) },
|
||||
TypeDLV: func() RR { return new(DLV) },
|
||||
TypeDNAME: func() RR { return new(DNAME) },
|
||||
TypeDNSKEY: func() RR { return new(DNSKEY) },
|
||||
TypeDS: func() RR { return new(DS) },
|
||||
TypeEID: func() RR { return new(EID) },
|
||||
TypeEUI48: func() RR { return new(EUI48) },
|
||||
TypeEUI64: func() RR { return new(EUI64) },
|
||||
TypeGID: func() RR { return new(GID) },
|
||||
TypeGPOS: func() RR { return new(GPOS) },
|
||||
TypeHINFO: func() RR { return new(HINFO) },
|
||||
TypeHIP: func() RR { return new(HIP) },
|
||||
TypeKEY: func() RR { return new(KEY) },
|
||||
TypeKX: func() RR { return new(KX) },
|
||||
TypeL32: func() RR { return new(L32) },
|
||||
TypeL64: func() RR { return new(L64) },
|
||||
TypeLOC: func() RR { return new(LOC) },
|
||||
TypeLP: func() RR { return new(LP) },
|
||||
TypeMB: func() RR { return new(MB) },
|
||||
TypeMD: func() RR { return new(MD) },
|
||||
TypeMF: func() RR { return new(MF) },
|
||||
TypeMG: func() RR { return new(MG) },
|
||||
TypeMINFO: func() RR { return new(MINFO) },
|
||||
TypeMR: func() RR { return new(MR) },
|
||||
TypeMX: func() RR { return new(MX) },
|
||||
TypeNAPTR: func() RR { return new(NAPTR) },
|
||||
TypeNID: func() RR { return new(NID) },
|
||||
TypeNIMLOC: func() RR { return new(NIMLOC) },
|
||||
TypeNINFO: func() RR { return new(NINFO) },
|
||||
TypeNS: func() RR { return new(NS) },
|
||||
TypeNSAPPTR: func() RR { return new(NSAPPTR) },
|
||||
TypeNSEC: func() RR { return new(NSEC) },
|
||||
TypeNSEC3: func() RR { return new(NSEC3) },
|
||||
TypeNSEC3PARAM: func() RR { return new(NSEC3PARAM) },
|
||||
TypeOPENPGPKEY: func() RR { return new(OPENPGPKEY) },
|
||||
TypeOPT: func() RR { return new(OPT) },
|
||||
TypePTR: func() RR { return new(PTR) },
|
||||
TypePX: func() RR { return new(PX) },
|
||||
TypeRKEY: func() RR { return new(RKEY) },
|
||||
TypeRP: func() RR { return new(RP) },
|
||||
TypeRRSIG: func() RR { return new(RRSIG) },
|
||||
TypeRT: func() RR { return new(RT) },
|
||||
TypeSIG: func() RR { return new(SIG) },
|
||||
TypeSMIMEA: func() RR { return new(SMIMEA) },
|
||||
TypeSOA: func() RR { return new(SOA) },
|
||||
TypeSPF: func() RR { return new(SPF) },
|
||||
TypeSRV: func() RR { return new(SRV) },
|
||||
TypeSSHFP: func() RR { return new(SSHFP) },
|
||||
TypeTA: func() RR { return new(TA) },
|
||||
TypeTALINK: func() RR { return new(TALINK) },
|
||||
TypeTKEY: func() RR { return new(TKEY) },
|
||||
TypeTLSA: func() RR { return new(TLSA) },
|
||||
TypeTSIG: func() RR { return new(TSIG) },
|
||||
TypeTXT: func() RR { return new(TXT) },
|
||||
TypeUID: func() RR { return new(UID) },
|
||||
TypeUINFO: func() RR { return new(UINFO) },
|
||||
TypeURI: func() RR { return new(URI) },
|
||||
TypeX25: func() RR { return new(X25) },
|
||||
}
|
||||
|
||||
// TypeToString is a map of strings for each RR type.
|
||||
var TypeToString = map[uint16]string{
|
||||
TypeA: "A",
|
||||
TypeAAAA: "AAAA",
|
||||
TypeAFSDB: "AFSDB",
|
||||
TypeANY: "ANY",
|
||||
TypeATMA: "ATMA",
|
||||
TypeAVC: "AVC",
|
||||
TypeAXFR: "AXFR",
|
||||
TypeCAA: "CAA",
|
||||
TypeCDNSKEY: "CDNSKEY",
|
||||
TypeCDS: "CDS",
|
||||
TypeCERT: "CERT",
|
||||
TypeCNAME: "CNAME",
|
||||
TypeCSYNC: "CSYNC",
|
||||
TypeDHCID: "DHCID",
|
||||
TypeDLV: "DLV",
|
||||
TypeDNAME: "DNAME",
|
||||
TypeDNSKEY: "DNSKEY",
|
||||
TypeDS: "DS",
|
||||
TypeEID: "EID",
|
||||
TypeEUI48: "EUI48",
|
||||
TypeEUI64: "EUI64",
|
||||
TypeGID: "GID",
|
||||
TypeGPOS: "GPOS",
|
||||
TypeHINFO: "HINFO",
|
||||
TypeHIP: "HIP",
|
||||
TypeISDN: "ISDN",
|
||||
TypeIXFR: "IXFR",
|
||||
TypeKEY: "KEY",
|
||||
TypeKX: "KX",
|
||||
TypeL32: "L32",
|
||||
TypeL64: "L64",
|
||||
TypeLOC: "LOC",
|
||||
TypeLP: "LP",
|
||||
TypeMAILA: "MAILA",
|
||||
TypeMAILB: "MAILB",
|
||||
TypeMB: "MB",
|
||||
TypeMD: "MD",
|
||||
TypeMF: "MF",
|
||||
TypeMG: "MG",
|
||||
TypeMINFO: "MINFO",
|
||||
TypeMR: "MR",
|
||||
TypeMX: "MX",
|
||||
TypeNAPTR: "NAPTR",
|
||||
TypeNID: "NID",
|
||||
TypeNIMLOC: "NIMLOC",
|
||||
TypeNINFO: "NINFO",
|
||||
TypeNS: "NS",
|
||||
TypeNSEC: "NSEC",
|
||||
TypeNSEC3: "NSEC3",
|
||||
TypeNSEC3PARAM: "NSEC3PARAM",
|
||||
TypeNULL: "NULL",
|
||||
TypeNXT: "NXT",
|
||||
TypeNone: "None",
|
||||
TypeOPENPGPKEY: "OPENPGPKEY",
|
||||
TypeOPT: "OPT",
|
||||
TypePTR: "PTR",
|
||||
TypePX: "PX",
|
||||
TypeRKEY: "RKEY",
|
||||
TypeRP: "RP",
|
||||
TypeRRSIG: "RRSIG",
|
||||
TypeRT: "RT",
|
||||
TypeReserved: "Reserved",
|
||||
TypeSIG: "SIG",
|
||||
TypeSMIMEA: "SMIMEA",
|
||||
TypeSOA: "SOA",
|
||||
TypeSPF: "SPF",
|
||||
TypeSRV: "SRV",
|
||||
TypeSSHFP: "SSHFP",
|
||||
TypeTA: "TA",
|
||||
TypeTALINK: "TALINK",
|
||||
TypeTKEY: "TKEY",
|
||||
TypeTLSA: "TLSA",
|
||||
TypeTSIG: "TSIG",
|
||||
TypeTXT: "TXT",
|
||||
TypeUID: "UID",
|
||||
TypeUINFO: "UINFO",
|
||||
TypeUNSPEC: "UNSPEC",
|
||||
TypeURI: "URI",
|
||||
TypeX25: "X25",
|
||||
TypeNSAPPTR: "NSAP-PTR",
|
||||
}
|
||||
|
||||
func (rr *A) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *AAAA) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *AFSDB) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *ANY) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *AVC) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *CAA) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *CDNSKEY) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *CDS) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *CERT) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *CNAME) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *CSYNC) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *DHCID) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *DLV) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *DNAME) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *DNSKEY) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *DS) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *EID) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *EUI48) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *EUI64) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *GID) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *GPOS) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *HINFO) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *HIP) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *KEY) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *KX) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *L32) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *L64) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *LOC) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *LP) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *MB) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *MD) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *MF) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *MG) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *MINFO) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *MR) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *MX) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *NAPTR) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *NID) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *NIMLOC) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *NINFO) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *NS) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *NSAPPTR) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *NSEC) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *NSEC3) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *NSEC3PARAM) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *OPENPGPKEY) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *OPT) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *PTR) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *PX) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *RFC3597) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *RKEY) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *RP) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *RRSIG) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *RT) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *SIG) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *SMIMEA) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *SOA) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *SPF) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *SRV) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *SSHFP) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *TA) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *TALINK) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *TKEY) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *TLSA) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *TSIG) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *TXT) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *UID) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *UINFO) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *URI) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *X25) Header() *RR_Header { return &rr.Hdr }
|
||||
|
||||
// len() functions
|
||||
func (rr *A) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += net.IPv4len // A
|
||||
return l
|
||||
}
|
||||
func (rr *AAAA) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += net.IPv6len // AAAA
|
||||
return l
|
||||
}
|
||||
func (rr *AFSDB) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += 2 // Subtype
|
||||
l += len(rr.Hostname) + 1
|
||||
return l
|
||||
}
|
||||
func (rr *ANY) len() int {
|
||||
l := rr.Hdr.len()
|
||||
return l
|
||||
}
|
||||
func (rr *AVC) len() int {
|
||||
l := rr.Hdr.len()
|
||||
for _, x := range rr.Txt {
|
||||
l += len(x) + 1
|
||||
}
|
||||
return l
|
||||
}
|
||||
func (rr *CAA) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l++ // Flag
|
||||
l += len(rr.Tag) + 1
|
||||
l += len(rr.Value)
|
||||
return l
|
||||
}
|
||||
func (rr *CERT) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += 2 // Type
|
||||
l += 2 // KeyTag
|
||||
l++ // Algorithm
|
||||
l += base64.StdEncoding.DecodedLen(len(rr.Certificate))
|
||||
return l
|
||||
}
|
||||
func (rr *CNAME) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += len(rr.Target) + 1
|
||||
return l
|
||||
}
|
||||
func (rr *DHCID) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += base64.StdEncoding.DecodedLen(len(rr.Digest))
|
||||
return l
|
||||
}
|
||||
func (rr *DNAME) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += len(rr.Target) + 1
|
||||
return l
|
||||
}
|
||||
func (rr *DNSKEY) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += 2 // Flags
|
||||
l++ // Protocol
|
||||
l++ // Algorithm
|
||||
l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
|
||||
return l
|
||||
}
|
||||
func (rr *DS) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += 2 // KeyTag
|
||||
l++ // Algorithm
|
||||
l++ // DigestType
|
||||
l += len(rr.Digest)/2 + 1
|
||||
return l
|
||||
}
|
||||
func (rr *EID) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += len(rr.Endpoint)/2 + 1
|
||||
return l
|
||||
}
|
||||
func (rr *EUI48) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += 6 // Address
|
||||
return l
|
||||
}
|
||||
func (rr *EUI64) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += 8 // Address
|
||||
return l
|
||||
}
|
||||
func (rr *GID) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += 4 // Gid
|
||||
return l
|
||||
}
|
||||
func (rr *GPOS) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += len(rr.Longitude) + 1
|
||||
l += len(rr.Latitude) + 1
|
||||
l += len(rr.Altitude) + 1
|
||||
return l
|
||||
}
|
||||
func (rr *HINFO) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += len(rr.Cpu) + 1
|
||||
l += len(rr.Os) + 1
|
||||
return l
|
||||
}
|
||||
func (rr *HIP) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l++ // HitLength
|
||||
l++ // PublicKeyAlgorithm
|
||||
l += 2 // PublicKeyLength
|
||||
l += len(rr.Hit) / 2
|
||||
l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
|
||||
for _, x := range rr.RendezvousServers {
|
||||
l += len(x) + 1
|
||||
}
|
||||
return l
|
||||
}
|
||||
func (rr *KX) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += 2 // Preference
|
||||
l += len(rr.Exchanger) + 1
|
||||
return l
|
||||
}
|
||||
func (rr *L32) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += 2 // Preference
|
||||
l += net.IPv4len // Locator32
|
||||
return l
|
||||
}
|
||||
func (rr *L64) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += 2 // Preference
|
||||
l += 8 // Locator64
|
||||
return l
|
||||
}
|
||||
func (rr *LOC) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l++ // Version
|
||||
l++ // Size
|
||||
l++ // HorizPre
|
||||
l++ // VertPre
|
||||
l += 4 // Latitude
|
||||
l += 4 // Longitude
|
||||
l += 4 // Altitude
|
||||
return l
|
||||
}
|
||||
func (rr *LP) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += 2 // Preference
|
||||
l += len(rr.Fqdn) + 1
|
||||
return l
|
||||
}
|
||||
func (rr *MB) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += len(rr.Mb) + 1
|
||||
return l
|
||||
}
|
||||
func (rr *MD) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += len(rr.Md) + 1
|
||||
return l
|
||||
}
|
||||
func (rr *MF) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += len(rr.Mf) + 1
|
||||
return l
|
||||
}
|
||||
func (rr *MG) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += len(rr.Mg) + 1
|
||||
return l
|
||||
}
|
||||
func (rr *MINFO) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += len(rr.Rmail) + 1
|
||||
l += len(rr.Email) + 1
|
||||
return l
|
||||
}
|
||||
func (rr *MR) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += len(rr.Mr) + 1
|
||||
return l
|
||||
}
|
||||
func (rr *MX) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += 2 // Preference
|
||||
l += len(rr.Mx) + 1
|
||||
return l
|
||||
}
|
||||
func (rr *NAPTR) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += 2 // Order
|
||||
l += 2 // Preference
|
||||
l += len(rr.Flags) + 1
|
||||
l += len(rr.Service) + 1
|
||||
l += len(rr.Regexp) + 1
|
||||
l += len(rr.Replacement) + 1
|
||||
return l
|
||||
}
|
||||
func (rr *NID) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += 2 // Preference
|
||||
l += 8 // NodeID
|
||||
return l
|
||||
}
|
||||
func (rr *NIMLOC) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += len(rr.Locator)/2 + 1
|
||||
return l
|
||||
}
|
||||
func (rr *NINFO) len() int {
|
||||
l := rr.Hdr.len()
|
||||
for _, x := range rr.ZSData {
|
||||
l += len(x) + 1
|
||||
}
|
||||
return l
|
||||
}
|
||||
func (rr *NS) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += len(rr.Ns) + 1
|
||||
return l
|
||||
}
|
||||
func (rr *NSAPPTR) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += len(rr.Ptr) + 1
|
||||
return l
|
||||
}
|
||||
func (rr *NSEC3PARAM) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l++ // Hash
|
||||
l++ // Flags
|
||||
l += 2 // Iterations
|
||||
l++ // SaltLength
|
||||
l += len(rr.Salt) / 2
|
||||
return l
|
||||
}
|
||||
func (rr *OPENPGPKEY) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
|
||||
return l
|
||||
}
|
||||
func (rr *PTR) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += len(rr.Ptr) + 1
|
||||
return l
|
||||
}
|
||||
func (rr *PX) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += 2 // Preference
|
||||
l += len(rr.Map822) + 1
|
||||
l += len(rr.Mapx400) + 1
|
||||
return l
|
||||
}
|
||||
func (rr *RFC3597) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += len(rr.Rdata)/2 + 1
|
||||
return l
|
||||
}
|
||||
func (rr *RKEY) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += 2 // Flags
|
||||
l++ // Protocol
|
||||
l++ // Algorithm
|
||||
l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
|
||||
return l
|
||||
}
|
||||
func (rr *RP) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += len(rr.Mbox) + 1
|
||||
l += len(rr.Txt) + 1
|
||||
return l
|
||||
}
|
||||
func (rr *RRSIG) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += 2 // TypeCovered
|
||||
l++ // Algorithm
|
||||
l++ // Labels
|
||||
l += 4 // OrigTtl
|
||||
l += 4 // Expiration
|
||||
l += 4 // Inception
|
||||
l += 2 // KeyTag
|
||||
l += len(rr.SignerName) + 1
|
||||
l += base64.StdEncoding.DecodedLen(len(rr.Signature))
|
||||
return l
|
||||
}
|
||||
func (rr *RT) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += 2 // Preference
|
||||
l += len(rr.Host) + 1
|
||||
return l
|
||||
}
|
||||
func (rr *SMIMEA) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l++ // Usage
|
||||
l++ // Selector
|
||||
l++ // MatchingType
|
||||
l += len(rr.Certificate)/2 + 1
|
||||
return l
|
||||
}
|
||||
func (rr *SOA) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += len(rr.Ns) + 1
|
||||
l += len(rr.Mbox) + 1
|
||||
l += 4 // Serial
|
||||
l += 4 // Refresh
|
||||
l += 4 // Retry
|
||||
l += 4 // Expire
|
||||
l += 4 // Minttl
|
||||
return l
|
||||
}
|
||||
func (rr *SPF) len() int {
|
||||
l := rr.Hdr.len()
|
||||
for _, x := range rr.Txt {
|
||||
l += len(x) + 1
|
||||
}
|
||||
return l
|
||||
}
|
||||
func (rr *SRV) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += 2 // Priority
|
||||
l += 2 // Weight
|
||||
l += 2 // Port
|
||||
l += len(rr.Target) + 1
|
||||
return l
|
||||
}
|
||||
func (rr *SSHFP) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l++ // Algorithm
|
||||
l++ // Type
|
||||
l += len(rr.FingerPrint)/2 + 1
|
||||
return l
|
||||
}
|
||||
func (rr *TA) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += 2 // KeyTag
|
||||
l++ // Algorithm
|
||||
l++ // DigestType
|
||||
l += len(rr.Digest)/2 + 1
|
||||
return l
|
||||
}
|
||||
func (rr *TALINK) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += len(rr.PreviousName) + 1
|
||||
l += len(rr.NextName) + 1
|
||||
return l
|
||||
}
|
||||
func (rr *TKEY) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += len(rr.Algorithm) + 1
|
||||
l += 4 // Inception
|
||||
l += 4 // Expiration
|
||||
l += 2 // Mode
|
||||
l += 2 // Error
|
||||
l += 2 // KeySize
|
||||
l += len(rr.Key) / 2
|
||||
l += 2 // OtherLen
|
||||
l += len(rr.OtherData) / 2
|
||||
return l
|
||||
}
|
||||
func (rr *TLSA) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l++ // Usage
|
||||
l++ // Selector
|
||||
l++ // MatchingType
|
||||
l += len(rr.Certificate)/2 + 1
|
||||
return l
|
||||
}
|
||||
func (rr *TSIG) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += len(rr.Algorithm) + 1
|
||||
l += 6 // TimeSigned
|
||||
l += 2 // Fudge
|
||||
l += 2 // MACSize
|
||||
l += len(rr.MAC) / 2
|
||||
l += 2 // OrigId
|
||||
l += 2 // Error
|
||||
l += 2 // OtherLen
|
||||
l += len(rr.OtherData) / 2
|
||||
return l
|
||||
}
|
||||
func (rr *TXT) len() int {
|
||||
l := rr.Hdr.len()
|
||||
for _, x := range rr.Txt {
|
||||
l += len(x) + 1
|
||||
}
|
||||
return l
|
||||
}
|
||||
func (rr *UID) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += 4 // Uid
|
||||
return l
|
||||
}
|
||||
func (rr *UINFO) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += len(rr.Uinfo) + 1
|
||||
return l
|
||||
}
|
||||
func (rr *URI) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += 2 // Priority
|
||||
l += 2 // Weight
|
||||
l += len(rr.Target)
|
||||
return l
|
||||
}
|
||||
func (rr *X25) len() int {
|
||||
l := rr.Hdr.len()
|
||||
l += len(rr.PSDNAddress) + 1
|
||||
return l
|
||||
}
|
||||
|
||||
// copy() functions
|
||||
func (rr *A) copy() RR {
|
||||
return &A{rr.Hdr, copyIP(rr.A)}
|
||||
}
|
||||
func (rr *AAAA) copy() RR {
|
||||
return &AAAA{rr.Hdr, copyIP(rr.AAAA)}
|
||||
}
|
||||
func (rr *AFSDB) copy() RR {
|
||||
return &AFSDB{rr.Hdr, rr.Subtype, rr.Hostname}
|
||||
}
|
||||
func (rr *ANY) copy() RR {
|
||||
return &ANY{rr.Hdr}
|
||||
}
|
||||
func (rr *AVC) copy() RR {
|
||||
Txt := make([]string, len(rr.Txt))
|
||||
copy(Txt, rr.Txt)
|
||||
return &AVC{rr.Hdr, Txt}
|
||||
}
|
||||
func (rr *CAA) copy() RR {
|
||||
return &CAA{rr.Hdr, rr.Flag, rr.Tag, rr.Value}
|
||||
}
|
||||
func (rr *CERT) copy() RR {
|
||||
return &CERT{rr.Hdr, rr.Type, rr.KeyTag, rr.Algorithm, rr.Certificate}
|
||||
}
|
||||
func (rr *CNAME) copy() RR {
|
||||
return &CNAME{rr.Hdr, rr.Target}
|
||||
}
|
||||
func (rr *CSYNC) copy() RR {
|
||||
TypeBitMap := make([]uint16, len(rr.TypeBitMap))
|
||||
copy(TypeBitMap, rr.TypeBitMap)
|
||||
return &CSYNC{rr.Hdr, rr.Serial, rr.Flags, TypeBitMap}
|
||||
}
|
||||
func (rr *DHCID) copy() RR {
|
||||
return &DHCID{rr.Hdr, rr.Digest}
|
||||
}
|
||||
func (rr *DNAME) copy() RR {
|
||||
return &DNAME{rr.Hdr, rr.Target}
|
||||
}
|
||||
func (rr *DNSKEY) copy() RR {
|
||||
return &DNSKEY{rr.Hdr, rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey}
|
||||
}
|
||||
func (rr *DS) copy() RR {
|
||||
return &DS{rr.Hdr, rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest}
|
||||
}
|
||||
func (rr *EID) copy() RR {
|
||||
return &EID{rr.Hdr, rr.Endpoint}
|
||||
}
|
||||
func (rr *EUI48) copy() RR {
|
||||
return &EUI48{rr.Hdr, rr.Address}
|
||||
}
|
||||
func (rr *EUI64) copy() RR {
|
||||
return &EUI64{rr.Hdr, rr.Address}
|
||||
}
|
||||
func (rr *GID) copy() RR {
|
||||
return &GID{rr.Hdr, rr.Gid}
|
||||
}
|
||||
func (rr *GPOS) copy() RR {
|
||||
return &GPOS{rr.Hdr, rr.Longitude, rr.Latitude, rr.Altitude}
|
||||
}
|
||||
func (rr *HINFO) copy() RR {
|
||||
return &HINFO{rr.Hdr, rr.Cpu, rr.Os}
|
||||
}
|
||||
func (rr *HIP) copy() RR {
|
||||
RendezvousServers := make([]string, len(rr.RendezvousServers))
|
||||
copy(RendezvousServers, rr.RendezvousServers)
|
||||
return &HIP{rr.Hdr, rr.HitLength, rr.PublicKeyAlgorithm, rr.PublicKeyLength, rr.Hit, rr.PublicKey, RendezvousServers}
|
||||
}
|
||||
func (rr *KX) copy() RR {
|
||||
return &KX{rr.Hdr, rr.Preference, rr.Exchanger}
|
||||
}
|
||||
func (rr *L32) copy() RR {
|
||||
return &L32{rr.Hdr, rr.Preference, copyIP(rr.Locator32)}
|
||||
}
|
||||
func (rr *L64) copy() RR {
|
||||
return &L64{rr.Hdr, rr.Preference, rr.Locator64}
|
||||
}
|
||||
func (rr *LOC) copy() RR {
|
||||
return &LOC{rr.Hdr, rr.Version, rr.Size, rr.HorizPre, rr.VertPre, rr.Latitude, rr.Longitude, rr.Altitude}
|
||||
}
|
||||
func (rr *LP) copy() RR {
|
||||
return &LP{rr.Hdr, rr.Preference, rr.Fqdn}
|
||||
}
|
||||
func (rr *MB) copy() RR {
|
||||
return &MB{rr.Hdr, rr.Mb}
|
||||
}
|
||||
func (rr *MD) copy() RR {
|
||||
return &MD{rr.Hdr, rr.Md}
|
||||
}
|
||||
func (rr *MF) copy() RR {
|
||||
return &MF{rr.Hdr, rr.Mf}
|
||||
}
|
||||
func (rr *MG) copy() RR {
|
||||
return &MG{rr.Hdr, rr.Mg}
|
||||
}
|
||||
func (rr *MINFO) copy() RR {
|
||||
return &MINFO{rr.Hdr, rr.Rmail, rr.Email}
|
||||
}
|
||||
func (rr *MR) copy() RR {
|
||||
return &MR{rr.Hdr, rr.Mr}
|
||||
}
|
||||
func (rr *MX) copy() RR {
|
||||
return &MX{rr.Hdr, rr.Preference, rr.Mx}
|
||||
}
|
||||
func (rr *NAPTR) copy() RR {
|
||||
return &NAPTR{rr.Hdr, rr.Order, rr.Preference, rr.Flags, rr.Service, rr.Regexp, rr.Replacement}
|
||||
}
|
||||
func (rr *NID) copy() RR {
|
||||
return &NID{rr.Hdr, rr.Preference, rr.NodeID}
|
||||
}
|
||||
func (rr *NIMLOC) copy() RR {
|
||||
return &NIMLOC{rr.Hdr, rr.Locator}
|
||||
}
|
||||
func (rr *NINFO) copy() RR {
|
||||
ZSData := make([]string, len(rr.ZSData))
|
||||
copy(ZSData, rr.ZSData)
|
||||
return &NINFO{rr.Hdr, ZSData}
|
||||
}
|
||||
func (rr *NS) copy() RR {
|
||||
return &NS{rr.Hdr, rr.Ns}
|
||||
}
|
||||
func (rr *NSAPPTR) copy() RR {
|
||||
return &NSAPPTR{rr.Hdr, rr.Ptr}
|
||||
}
|
||||
func (rr *NSEC) copy() RR {
|
||||
TypeBitMap := make([]uint16, len(rr.TypeBitMap))
|
||||
copy(TypeBitMap, rr.TypeBitMap)
|
||||
return &NSEC{rr.Hdr, rr.NextDomain, TypeBitMap}
|
||||
}
|
||||
func (rr *NSEC3) copy() RR {
|
||||
TypeBitMap := make([]uint16, len(rr.TypeBitMap))
|
||||
copy(TypeBitMap, rr.TypeBitMap)
|
||||
return &NSEC3{rr.Hdr, rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt, rr.HashLength, rr.NextDomain, TypeBitMap}
|
||||
}
|
||||
func (rr *NSEC3PARAM) copy() RR {
|
||||
return &NSEC3PARAM{rr.Hdr, rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt}
|
||||
}
|
||||
func (rr *OPENPGPKEY) copy() RR {
|
||||
return &OPENPGPKEY{rr.Hdr, rr.PublicKey}
|
||||
}
|
||||
func (rr *OPT) copy() RR {
|
||||
Option := make([]EDNS0, len(rr.Option))
|
||||
copy(Option, rr.Option)
|
||||
return &OPT{rr.Hdr, Option}
|
||||
}
|
||||
func (rr *PTR) copy() RR {
|
||||
return &PTR{rr.Hdr, rr.Ptr}
|
||||
}
|
||||
func (rr *PX) copy() RR {
|
||||
return &PX{rr.Hdr, rr.Preference, rr.Map822, rr.Mapx400}
|
||||
}
|
||||
func (rr *RFC3597) copy() RR {
|
||||
return &RFC3597{rr.Hdr, rr.Rdata}
|
||||
}
|
||||
func (rr *RKEY) copy() RR {
|
||||
return &RKEY{rr.Hdr, rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey}
|
||||
}
|
||||
func (rr *RP) copy() RR {
|
||||
return &RP{rr.Hdr, rr.Mbox, rr.Txt}
|
||||
}
|
||||
func (rr *RRSIG) copy() RR {
|
||||
return &RRSIG{rr.Hdr, rr.TypeCovered, rr.Algorithm, rr.Labels, rr.OrigTtl, rr.Expiration, rr.Inception, rr.KeyTag, rr.SignerName, rr.Signature}
|
||||
}
|
||||
func (rr *RT) copy() RR {
|
||||
return &RT{rr.Hdr, rr.Preference, rr.Host}
|
||||
}
|
||||
func (rr *SMIMEA) copy() RR {
|
||||
return &SMIMEA{rr.Hdr, rr.Usage, rr.Selector, rr.MatchingType, rr.Certificate}
|
||||
}
|
||||
func (rr *SOA) copy() RR {
|
||||
return &SOA{rr.Hdr, rr.Ns, rr.Mbox, rr.Serial, rr.Refresh, rr.Retry, rr.Expire, rr.Minttl}
|
||||
}
|
||||
func (rr *SPF) copy() RR {
|
||||
Txt := make([]string, len(rr.Txt))
|
||||
copy(Txt, rr.Txt)
|
||||
return &SPF{rr.Hdr, Txt}
|
||||
}
|
||||
func (rr *SRV) copy() RR {
|
||||
return &SRV{rr.Hdr, rr.Priority, rr.Weight, rr.Port, rr.Target}
|
||||
}
|
||||
func (rr *SSHFP) copy() RR {
|
||||
return &SSHFP{rr.Hdr, rr.Algorithm, rr.Type, rr.FingerPrint}
|
||||
}
|
||||
func (rr *TA) copy() RR {
|
||||
return &TA{rr.Hdr, rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest}
|
||||
}
|
||||
func (rr *TALINK) copy() RR {
|
||||
return &TALINK{rr.Hdr, rr.PreviousName, rr.NextName}
|
||||
}
|
||||
func (rr *TKEY) copy() RR {
|
||||
return &TKEY{rr.Hdr, rr.Algorithm, rr.Inception, rr.Expiration, rr.Mode, rr.Error, rr.KeySize, rr.Key, rr.OtherLen, rr.OtherData}
|
||||
}
|
||||
func (rr *TLSA) copy() RR {
|
||||
return &TLSA{rr.Hdr, rr.Usage, rr.Selector, rr.MatchingType, rr.Certificate}
|
||||
}
|
||||
func (rr *TSIG) copy() RR {
|
||||
return &TSIG{rr.Hdr, rr.Algorithm, rr.TimeSigned, rr.Fudge, rr.MACSize, rr.MAC, rr.OrigId, rr.Error, rr.OtherLen, rr.OtherData}
|
||||
}
|
||||
func (rr *TXT) copy() RR {
|
||||
Txt := make([]string, len(rr.Txt))
|
||||
copy(Txt, rr.Txt)
|
||||
return &TXT{rr.Hdr, Txt}
|
||||
}
|
||||
func (rr *UID) copy() RR {
|
||||
return &UID{rr.Hdr, rr.Uid}
|
||||
}
|
||||
func (rr *UINFO) copy() RR {
|
||||
return &UINFO{rr.Hdr, rr.Uinfo}
|
||||
}
|
||||
func (rr *URI) copy() RR {
|
||||
return &URI{rr.Hdr, rr.Priority, rr.Weight, rr.Target}
|
||||
}
|
||||
func (rr *X25) copy() RR {
|
||||
return &X25{rr.Hdr, rr.PSDNAddress}
|
||||
}
|
||||
Reference in New Issue
Block a user