mirror of https://github.com/ginuerzh/gost
427 changed files with 189909 additions and 0 deletions
@ -0,0 +1 @@ |
|||
Miek Gieben <[email protected]> |
|||
@ -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 |
|||
@ -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. |
|||
@ -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 = "b47b1587369238182299fe4dad77d05b8b461e06" |
|||
|
|||
[[projects]] |
|||
branch = "master" |
|||
name = "golang.org/x/net" |
|||
packages = ["bpf","internal/iana","internal/socket","ipv4","ipv6"] |
|||
revision = "1e491301e022f8f977054da4c2d852decd59571f" |
|||
|
|||
[solve-meta] |
|||
analyzer-name = "dep" |
|||
analyzer-version = 1 |
|||
inputs-digest = "c4abc38abaeeeeb9be92455c9c02cae32841122b8982aaa067ef25bb8e86ff9d" |
|||
solver-name = "gps-cdcl" |
|||
solver-version = 1 |
|||
@ -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" |
|||
@ -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. |
|||
|
|||
@ -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 |
|||
@ -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 |
|||
@ -0,0 +1,171 @@ |
|||
[](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 |
|||
* https://github.com/xor-gate/sshfp |
|||
* https://github.com/rs/dnstrace |
|||
* https://blitiri.com.ar/p/dnss ([github mirror](https://github.com/albertito/dnss)) |
|||
|
|||
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` |
|||
@ -0,0 +1,577 @@ |
|||
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 = *c.Dialer |
|||
} |
|||
|
|||
network := c.Net |
|||
if network == "" { |
|||
network = "udp" |
|||
} |
|||
|
|||
useTLS := strings.HasPrefix(network, "tcp") && strings.HasSuffix(network, "-tls") |
|||
|
|||
conn = new(Conn) |
|||
if useTLS { |
|||
network = strings.TrimSuffix(network, "-tls") |
|||
|
|||
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) |
|||
} |
|||
@ -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 |
|||
} |
|||
@ -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) |
|||
} |
|||
} |
|||
@ -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") |
|||
} |
|||
@ -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)) |
|||
} |
|||
@ -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 |
|||
} |
|||
@ -0,0 +1,801 @@ |
|||
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 |
|||
} |
|||
|
|||
if len(keybuf) < 1+1+64 { |
|||
// Exponent must be at least 1 byte and modulus at least 64
|
|||
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 || explen == 0 || keybuf[keyoff] == 0 { |
|||
// Exponent larger than supported by the crypto package,
|
|||
// empty, or contains prohibited leading zero.
|
|||
return nil |
|||
} |
|||
|
|||
modoff := keyoff + int(explen) |
|||
modlen := len(keybuf) - modoff |
|||
if modlen < 64 || modlen > 512 || keybuf[modoff] == 0 { |
|||
// Modulus is too small, large, or contains prohibited leading zero.
|
|||
return nil |
|||
} |
|||
|
|||
pubkey := new(rsa.PublicKey) |
|||
|
|||
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 = big.NewInt(0) |
|||
pubkey.N.SetBytes(keybuf[modoff:]) |
|||
|
|||
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 |
|||
} |
|||
@ -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 |
|||
} |
|||
@ -0,0 +1,284 @@ |
|||
package dns |
|||
|
|||
import ( |
|||
"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) != ed25519.SeedSize { |
|||
return nil, ErrPrivKey |
|||
} |
|||
p = ed25519.NewKeyFromSeed(p1) |
|||
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 |
|||
} |
|||
} |
|||
@ -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.Seed()) |
|||
return format + |
|||
"Algorithm: " + algorithm + "\n" + |
|||
"PrivateKey: " + private + "\n" |
|||
|
|||
default: |
|||
return "" |
|||
} |
|||
} |
|||
@ -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 |
|||
@ -0,0 +1,25 @@ |
|||
package dns |
|||
|
|||
//go:generate go run duplicate_generate.go
|
|||
|
|||
// IsDuplicate checks of r1 and r2 are duplicates of each other, excluding the TTL.
|
|||
// So this means the header data is equal *and* the RDATA is the same. Return true
|
|||
// is so, otherwise false.
|
|||
// It's is a protocol violation to have identical RRs in a message.
|
|||
func IsDuplicate(r1, r2 RR) bool { |
|||
if r1.Header().Class != r2.Header().Class { |
|||
return false |
|||
} |
|||
if r1.Header().Rrtype != r2.Header().Rrtype { |
|||
return false |
|||
} |
|||
if !isDulicateName(r1.Header().Name, r2.Header().Name) { |
|||
return false |
|||
} |
|||
// ignore TTL
|
|||
|
|||
return isDuplicateRdata(r1, r2) |
|||
} |
|||
|
|||
// isDulicateName checks if the domain names s1 and s2 are equal.
|
|||
func isDulicateName(s1, s2 string) bool { return equal(s1, s2) } |
|||
@ -0,0 +1,158 @@ |
|||
//+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" |
|||
) |
|||
|
|||
var packageHdr = ` |
|||
// Code generated by "go run duplicate_generate.go"; DO NOT EDIT.
|
|||
|
|||
package dns |
|||
|
|||
` |
|||
|
|||
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" || name == "RFC3597" { |
|||
continue |
|||
} |
|||
if name == "OPT" || name == "ANY" || name == "IXFR" || name == "AXFR" { |
|||
continue |
|||
} |
|||
|
|||
namedTypes = append(namedTypes, o.Name()) |
|||
} |
|||
|
|||
b := &bytes.Buffer{} |
|||
b.WriteString(packageHdr) |
|||
|
|||
// Generate the giant switch that calls the correct function for each type.
|
|||
fmt.Fprint(b, "// isDuplicateRdata calls the rdata specific functions\n") |
|||
fmt.Fprint(b, "func isDuplicateRdata(r1, r2 RR) bool {\n") |
|||
fmt.Fprint(b, "switch r1.Header().Rrtype {\n") |
|||
|
|||
for _, name := range namedTypes { |
|||
|
|||
o := scope.Lookup(name) |
|||
_, isEmbedded := getTypeStruct(o.Type(), scope) |
|||
if isEmbedded { |
|||
continue |
|||
} |
|||
fmt.Fprintf(b, "case Type%s:\nreturn isDuplicate%s(r1.(*%s), r2.(*%s))\n", name, name, name, name) |
|||
} |
|||
fmt.Fprintf(b, "}\nreturn false\n}\n") |
|||
|
|||
// Generate the duplicate check for each type.
|
|||
fmt.Fprint(b, "// isDuplicate() functions\n\n") |
|||
for _, name := range namedTypes { |
|||
|
|||
o := scope.Lookup(name) |
|||
st, isEmbedded := getTypeStruct(o.Type(), scope) |
|||
if isEmbedded { |
|||
continue |
|||
} |
|||
fmt.Fprintf(b, "func isDuplicate%s(r1, r2 *%s) bool {\n", name, name) |
|||
for i := 1; i < st.NumFields(); i++ { |
|||
field := st.Field(i).Name() |
|||
o2 := func(s string) { fmt.Fprintf(b, s+"\n", field, field) } |
|||
o3 := func(s string) { fmt.Fprintf(b, s+"\n", field, field, field) } |
|||
|
|||
// For some reason, a and aaaa don't pop up as *types.Slice here (mostly like because the are
|
|||
// *indirectly* defined as a slice in the net package).
|
|||
if _, ok := st.Field(i).Type().(*types.Slice); ok || st.Tag(i) == `dns:"a"` || st.Tag(i) == `dns:"aaaa"` { |
|||
o2("if len(r1.%s) != len(r2.%s) {\nreturn false\n}") |
|||
|
|||
if st.Tag(i) == `dns:"cdomain-name"` || st.Tag(i) == `dns:"domain-name"` { |
|||
o3(`for i := 0; i < len(r1.%s); i++ { |
|||
if !isDulicateName(r1.%s[i], r2.%s[i]) { |
|||
return false |
|||
} |
|||
}`) |
|||
|
|||
continue |
|||
} |
|||
|
|||
o3(`for i := 0; i < len(r1.%s); i++ { |
|||
if r1.%s[i] != r2.%s[i] { |
|||
return false |
|||
} |
|||
}`) |
|||
|
|||
continue |
|||
} |
|||
|
|||
switch st.Tag(i) { |
|||
case `dns:"-"`: |
|||
// ignored
|
|||
case `dns:"cdomain-name"`, `dns:"domain-name"`: |
|||
o2("if !isDulicateName(r1.%s, r2.%s) {\nreturn false\n}") |
|||
default: |
|||
o2("if r1.%s != r2.%s {\nreturn false\n}") |
|||
} |
|||
} |
|||
fmt.Fprintf(b, "return true\n}\n\n") |
|||
} |
|||
|
|||
// gofmt
|
|||
res, err := format.Source(b.Bytes()) |
|||
if err != nil { |
|||
b.WriteTo(os.Stderr) |
|||
log.Fatal(err) |
|||
} |
|||
|
|||
// write result
|
|||
f, err := os.Create("zduplicate.go") |
|||
fatalIfErr(err) |
|||
defer f.Close() |
|||
f.Write(res) |
|||
} |
|||
|
|||
func fatalIfErr(err error) { |
|||
if err != nil { |
|||
log.Fatal(err) |
|||
} |
|||
} |
|||
@ -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) } |
|||
@ -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 "" |
|||
} |
|||
@ -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 |
|||
} |
|||
@ -0,0 +1,169 @@ |
|||
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.Split(s, ",") |
|||
|
|||
// Modifier is { offset [ ,width [ ,base ] ] } - provide default
|
|||
// values for optional width and type, if necessary.
|
|||
switch len(xs) { |
|||
case 1: |
|||
xs = append(xs, "0", "d") |
|||
case 2: |
|||
xs = append(xs, "d") |
|||
case 3: |
|||
default: |
|||
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 |
|||
} |
|||
@ -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 |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
// +build go1.11,!windows
|
|||
|
|||
package dns |
|||
|
|||
import ( |
|||
"context" |
|||
"net" |
|||
"syscall" |
|||
|
|||
"golang.org/x/sys/unix" |
|||
) |
|||
|
|||
const supportsReusePort = true |
|||
|
|||
func reuseportControl(network, address string, c syscall.RawConn) error { |
|||
var opErr error |
|||
err := c.Control(func(fd uintptr) { |
|||
opErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1) |
|||
}) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
|
|||
return opErr |
|||
} |
|||
|
|||
func listenTCP(network, addr string, reuseport bool) (net.Listener, error) { |
|||
var lc net.ListenConfig |
|||
if reuseport { |
|||
lc.Control = reuseportControl |
|||
} |
|||
|
|||
return lc.Listen(context.Background(), network, addr) |
|||
} |
|||
|
|||
func listenUDP(network, addr string, reuseport bool) (net.PacketConn, error) { |
|||
var lc net.ListenConfig |
|||
if reuseport { |
|||
lc.Control = reuseportControl |
|||
} |
|||
|
|||
return lc.ListenPacket(context.Background(), network, addr) |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
// +build !go1.11 windows
|
|||
|
|||
package dns |
|||
|
|||
import "net" |
|||
|
|||
const supportsReusePort = false |
|||
|
|||
func listenTCP(network, addr string, reuseport bool) (net.Listener, error) { |
|||
if reuseport { |
|||
// TODO(tmthrgd): return an error?
|
|||
} |
|||
|
|||
return net.Listen(network, addr) |
|||
} |
|||
|
|||
func listenUDP(network, addr string, reuseport bool) (net.PacketConn, error) { |
|||
if reuseport { |
|||
// TODO(tmthrgd): return an error?
|
|||
} |
|||
|
|||
return net.ListenPacket(network, addr) |
|||
} |
|||
File diff suppressed because it is too large
@ -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) |
|||
} |
|||
} |
|||
@ -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 |
|||
} |
|||
@ -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 |
|||
} |
|||
@ -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 |
|||
} |
|||
@ -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 |
|||
} |
|||
@ -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 |
|||
} |
|||
@ -0,0 +1,85 @@ |
|||
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]) |
|||
} |
|||
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -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 |
|||
} |
|||
@ -0,0 +1,809 @@ |
|||
// DNS server implementation.
|
|||
|
|||
package dns |
|||
|
|||
import ( |
|||
"bytes" |
|||
"crypto/tls" |
|||
"encoding/binary" |
|||
"errors" |
|||
"io" |
|||
"net" |
|||
"strings" |
|||
"sync" |
|||
"sync/atomic" |
|||
"time" |
|||
) |
|||
|
|||
// Default maximum number of TCP queries before we close the socket.
|
|||
const maxTCPQueries = 128 |
|||
|
|||
// The maximum number of idle workers.
|
|||
//
|
|||
// This controls the maximum number of workers that are allowed to stay
|
|||
// idle waiting for incoming requests before being torn down.
|
|||
//
|
|||
// If this limit is reached, the server will just keep spawning new
|
|||
// workers (goroutines) for each incoming request. In this case, each
|
|||
// worker will only be used for a single request.
|
|||
const maxIdleWorkersCount = 10000 |
|||
|
|||
// The maximum length of time a worker may idle for before being destroyed.
|
|||
const idleWorkerTimeout = 10 * time.Second |
|||
|
|||
// 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 |
|||
// Whether to set the SO_REUSEPORT socket option, allowing multiple listeners to be bound to a single address.
|
|||
// It is only supported on go1.11+ and when using ListenAndServe.
|
|||
ReusePort bool |
|||
|
|||
// UDP packet or TCP connection queue
|
|||
queue chan *response |
|||
// Workers count
|
|||
workersCount int32 |
|||
// Shutdown handling
|
|||
lock sync.RWMutex |
|||
started bool |
|||
|
|||
// A pool for UDP message buffers.
|
|||
udpPool sync.Pool |
|||
} |
|||
|
|||
func (srv *Server) isStarted() bool { |
|||
srv.lock.RLock() |
|||
started := srv.started |
|||
srv.lock.RUnlock() |
|||
return started |
|||
} |
|||
|
|||
func (srv *Server) worker(w *response) { |
|||
srv.serve(w) |
|||
|
|||
for { |
|||
count := atomic.LoadInt32(&srv.workersCount) |
|||
if count > maxIdleWorkersCount { |
|||
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) |
|||
} |
|||
} |
|||
|
|||
func makeUDPBuffer(size int) func() interface{} { |
|||
return func() interface{} { |
|||
return make([]byte, size) |
|||
} |
|||
} |
|||
|
|||
func (srv *Server) init() { |
|||
srv.queue = make(chan *response) |
|||
|
|||
if srv.UDPSize == 0 { |
|||
srv.UDPSize = MinMsgSize |
|||
} |
|||
|
|||
srv.udpPool.New = makeUDPBuffer(srv.UDPSize) |
|||
} |
|||
|
|||
func unlockOnce(l sync.Locker) func() { |
|||
var once sync.Once |
|||
return func() { once.Do(l.Unlock) } |
|||
} |
|||
|
|||
// ListenAndServe starts a nameserver on the configured address in *Server.
|
|||
func (srv *Server) ListenAndServe() error { |
|||
unlock := unlockOnce(&srv.lock) |
|||
srv.lock.Lock() |
|||
defer unlock() |
|||
|
|||
if srv.started { |
|||
return &Error{err: "server already started"} |
|||
} |
|||
|
|||
addr := srv.Addr |
|||
if addr == "" { |
|||
addr = ":domain" |
|||
} |
|||
|
|||
srv.init() |
|||
defer close(srv.queue) |
|||
|
|||
switch srv.Net { |
|||
case "tcp", "tcp4", "tcp6": |
|||
l, err := listenTCP(srv.Net, addr, srv.ReusePort) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
srv.Listener = l |
|||
srv.started = true |
|||
unlock() |
|||
return srv.serveTCP(l) |
|||
case "tcp-tls", "tcp4-tls", "tcp6-tls": |
|||
if srv.TLSConfig == nil || (len(srv.TLSConfig.Certificates) == 0 && srv.TLSConfig.GetCertificate == nil) { |
|||
return errors.New("dns: neither Certificates nor GetCertificate set in Config") |
|||
} |
|||
network := strings.TrimSuffix(srv.Net, "-tls") |
|||
l, err := listenTCP(network, addr, srv.ReusePort) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
l = tls.NewListener(l, srv.TLSConfig) |
|||
srv.Listener = l |
|||
srv.started = true |
|||
unlock() |
|||
return srv.serveTCP(l) |
|||
case "udp", "udp4", "udp6": |
|||
l, err := listenUDP(srv.Net, addr, srv.ReusePort) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
u := l.(*net.UDPConn) |
|||
if e := setUDPSocketOptions(u); e != nil { |
|||
return e |
|||
} |
|||
srv.PacketConn = l |
|||
srv.started = true |
|||
unlock() |
|||
return srv.serveUDP(u) |
|||
} |
|||
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 { |
|||
unlock := unlockOnce(&srv.lock) |
|||
srv.lock.Lock() |
|||
defer unlock() |
|||
|
|||
if srv.started { |
|||
return &Error{err: "server already started"} |
|||
} |
|||
|
|||
srv.init() |
|||
defer close(srv.queue) |
|||
|
|||
pConn := srv.PacketConn |
|||
l := srv.Listener |
|||
if pConn != nil { |
|||
// 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 |
|||
unlock() |
|||
return srv.serveUDP(t) |
|||
} |
|||
} |
|||
if l != nil { |
|||
srv.started = true |
|||
unlock() |
|||
return srv.serveTCP(l) |
|||
} |
|||
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() |
|||
if !srv.isStarted() { |
|||
return nil |
|||
} |
|||
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) |
|||
if !srv.isStarted() { |
|||
return nil |
|||
} |
|||
if err != nil { |
|||
if netErr, ok := err.(net.Error); ok && netErr.Temporary() { |
|||
continue |
|||
} |
|||
return err |
|||
} |
|||
if len(m) < headerSize { |
|||
if cap(m) == srv.UDPSize { |
|||
srv.udpPool.Put(m[:srv.UDPSize]) |
|||
} |
|||
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 w.udp != nil && cap(w.msg) == srv.UDPSize { |
|||
srv.udpPool.Put(w.msg[:srv.UDPSize]) |
|||
} |
|||
w.msg = nil |
|||
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 := srv.udpPool.Get().([]byte) |
|||
n, s, err := ReadFromSessionUDP(conn, m) |
|||
if err != nil { |
|||
srv.udpPool.Put(m) |
|||
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 |
|||
} |
|||
@ -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 |
|||
} |
|||
@ -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 |
|||
} |
|||
@ -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 |
|||
} |
|||
@ -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 |
|||
} |
|||
@ -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 |
|||
} |
|||
File diff suppressed because it is too large
@ -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) |
|||
} |
|||
} |
|||
@ -0,0 +1,100 @@ |
|||
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 |
|||
} |
|||
@ -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) |
|||
} |
|||
} |
|||
@ -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) |
|||
} |
|||
@ -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" |
|||
@ -0,0 +1,152 @@ |
|||
// 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 *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 |
|||
} |
|||
@ -0,0 +1,943 @@ |
|||
// Code generated by "go run duplicate_generate.go"; DO NOT EDIT.
|
|||
|
|||
package dns |
|||
|
|||
// isDuplicateRdata calls the rdata specific functions
|
|||
func isDuplicateRdata(r1, r2 RR) bool { |
|||
switch r1.Header().Rrtype { |
|||
case TypeA: |
|||
return isDuplicateA(r1.(*A), r2.(*A)) |
|||
case TypeAAAA: |
|||
return isDuplicateAAAA(r1.(*AAAA), r2.(*AAAA)) |
|||
case TypeAFSDB: |
|||
return isDuplicateAFSDB(r1.(*AFSDB), r2.(*AFSDB)) |
|||
case TypeAVC: |
|||
return isDuplicateAVC(r1.(*AVC), r2.(*AVC)) |
|||
case TypeCAA: |
|||
return isDuplicateCAA(r1.(*CAA), r2.(*CAA)) |
|||
case TypeCERT: |
|||
return isDuplicateCERT(r1.(*CERT), r2.(*CERT)) |
|||
case TypeCNAME: |
|||
return isDuplicateCNAME(r1.(*CNAME), r2.(*CNAME)) |
|||
case TypeCSYNC: |
|||
return isDuplicateCSYNC(r1.(*CSYNC), r2.(*CSYNC)) |
|||
case TypeDHCID: |
|||
return isDuplicateDHCID(r1.(*DHCID), r2.(*DHCID)) |
|||
case TypeDNAME: |
|||
return isDuplicateDNAME(r1.(*DNAME), r2.(*DNAME)) |
|||
case TypeDNSKEY: |
|||
return isDuplicateDNSKEY(r1.(*DNSKEY), r2.(*DNSKEY)) |
|||
case TypeDS: |
|||
return isDuplicateDS(r1.(*DS), r2.(*DS)) |
|||
case TypeEID: |
|||
return isDuplicateEID(r1.(*EID), r2.(*EID)) |
|||
case TypeEUI48: |
|||
return isDuplicateEUI48(r1.(*EUI48), r2.(*EUI48)) |
|||
case TypeEUI64: |
|||
return isDuplicateEUI64(r1.(*EUI64), r2.(*EUI64)) |
|||
case TypeGID: |
|||
return isDuplicateGID(r1.(*GID), r2.(*GID)) |
|||
case TypeGPOS: |
|||
return isDuplicateGPOS(r1.(*GPOS), r2.(*GPOS)) |
|||
case TypeHINFO: |
|||
return isDuplicateHINFO(r1.(*HINFO), r2.(*HINFO)) |
|||
case TypeHIP: |
|||
return isDuplicateHIP(r1.(*HIP), r2.(*HIP)) |
|||
case TypeKX: |
|||
return isDuplicateKX(r1.(*KX), r2.(*KX)) |
|||
case TypeL32: |
|||
return isDuplicateL32(r1.(*L32), r2.(*L32)) |
|||
case TypeL64: |
|||
return isDuplicateL64(r1.(*L64), r2.(*L64)) |
|||
case TypeLOC: |
|||
return isDuplicateLOC(r1.(*LOC), r2.(*LOC)) |
|||
case TypeLP: |
|||
return isDuplicateLP(r1.(*LP), r2.(*LP)) |
|||
case TypeMB: |
|||
return isDuplicateMB(r1.(*MB), r2.(*MB)) |
|||
case TypeMD: |
|||
return isDuplicateMD(r1.(*MD), r2.(*MD)) |
|||
case TypeMF: |
|||
return isDuplicateMF(r1.(*MF), r2.(*MF)) |
|||
case TypeMG: |
|||
return isDuplicateMG(r1.(*MG), r2.(*MG)) |
|||
case TypeMINFO: |
|||
return isDuplicateMINFO(r1.(*MINFO), r2.(*MINFO)) |
|||
case TypeMR: |
|||
return isDuplicateMR(r1.(*MR), r2.(*MR)) |
|||
case TypeMX: |
|||
return isDuplicateMX(r1.(*MX), r2.(*MX)) |
|||
case TypeNAPTR: |
|||
return isDuplicateNAPTR(r1.(*NAPTR), r2.(*NAPTR)) |
|||
case TypeNID: |
|||
return isDuplicateNID(r1.(*NID), r2.(*NID)) |
|||
case TypeNIMLOC: |
|||
return isDuplicateNIMLOC(r1.(*NIMLOC), r2.(*NIMLOC)) |
|||
case TypeNINFO: |
|||
return isDuplicateNINFO(r1.(*NINFO), r2.(*NINFO)) |
|||
case TypeNS: |
|||
return isDuplicateNS(r1.(*NS), r2.(*NS)) |
|||
case TypeNSAPPTR: |
|||
return isDuplicateNSAPPTR(r1.(*NSAPPTR), r2.(*NSAPPTR)) |
|||
case TypeNSEC: |
|||
return isDuplicateNSEC(r1.(*NSEC), r2.(*NSEC)) |
|||
case TypeNSEC3: |
|||
return isDuplicateNSEC3(r1.(*NSEC3), r2.(*NSEC3)) |
|||
case TypeNSEC3PARAM: |
|||
return isDuplicateNSEC3PARAM(r1.(*NSEC3PARAM), r2.(*NSEC3PARAM)) |
|||
case TypeOPENPGPKEY: |
|||
return isDuplicateOPENPGPKEY(r1.(*OPENPGPKEY), r2.(*OPENPGPKEY)) |
|||
case TypePTR: |
|||
return isDuplicatePTR(r1.(*PTR), r2.(*PTR)) |
|||
case TypePX: |
|||
return isDuplicatePX(r1.(*PX), r2.(*PX)) |
|||
case TypeRKEY: |
|||
return isDuplicateRKEY(r1.(*RKEY), r2.(*RKEY)) |
|||
case TypeRP: |
|||
return isDuplicateRP(r1.(*RP), r2.(*RP)) |
|||
case TypeRRSIG: |
|||
return isDuplicateRRSIG(r1.(*RRSIG), r2.(*RRSIG)) |
|||
case TypeRT: |
|||
return isDuplicateRT(r1.(*RT), r2.(*RT)) |
|||
case TypeSMIMEA: |
|||
return isDuplicateSMIMEA(r1.(*SMIMEA), r2.(*SMIMEA)) |
|||
case TypeSOA: |
|||
return isDuplicateSOA(r1.(*SOA), r2.(*SOA)) |
|||
case TypeSPF: |
|||
return isDuplicateSPF(r1.(*SPF), r2.(*SPF)) |
|||
case TypeSRV: |
|||
return isDuplicateSRV(r1.(*SRV), r2.(*SRV)) |
|||
case TypeSSHFP: |
|||
return isDuplicateSSHFP(r1.(*SSHFP), r2.(*SSHFP)) |
|||
case TypeTA: |
|||
return isDuplicateTA(r1.(*TA), r2.(*TA)) |
|||
case TypeTALINK: |
|||
return isDuplicateTALINK(r1.(*TALINK), r2.(*TALINK)) |
|||
case TypeTKEY: |
|||
return isDuplicateTKEY(r1.(*TKEY), r2.(*TKEY)) |
|||
case TypeTLSA: |
|||
return isDuplicateTLSA(r1.(*TLSA), r2.(*TLSA)) |
|||
case TypeTSIG: |
|||
return isDuplicateTSIG(r1.(*TSIG), r2.(*TSIG)) |
|||
case TypeTXT: |
|||
return isDuplicateTXT(r1.(*TXT), r2.(*TXT)) |
|||
case TypeUID: |
|||
return isDuplicateUID(r1.(*UID), r2.(*UID)) |
|||
case TypeUINFO: |
|||
return isDuplicateUINFO(r1.(*UINFO), r2.(*UINFO)) |
|||
case TypeURI: |
|||
return isDuplicateURI(r1.(*URI), r2.(*URI)) |
|||
case TypeX25: |
|||
return isDuplicateX25(r1.(*X25), r2.(*X25)) |
|||
} |
|||
return false |
|||
} |
|||
|
|||
// isDuplicate() functions
|
|||
|
|||
func isDuplicateA(r1, r2 *A) bool { |
|||
if len(r1.A) != len(r2.A) { |
|||
return false |
|||
} |
|||
for i := 0; i < len(r1.A); i++ { |
|||
if r1.A[i] != r2.A[i] { |
|||
return false |
|||
} |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateAAAA(r1, r2 *AAAA) bool { |
|||
if len(r1.AAAA) != len(r2.AAAA) { |
|||
return false |
|||
} |
|||
for i := 0; i < len(r1.AAAA); i++ { |
|||
if r1.AAAA[i] != r2.AAAA[i] { |
|||
return false |
|||
} |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateAFSDB(r1, r2 *AFSDB) bool { |
|||
if r1.Subtype != r2.Subtype { |
|||
return false |
|||
} |
|||
if !isDulicateName(r1.Hostname, r2.Hostname) { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateAVC(r1, r2 *AVC) bool { |
|||
if len(r1.Txt) != len(r2.Txt) { |
|||
return false |
|||
} |
|||
for i := 0; i < len(r1.Txt); i++ { |
|||
if r1.Txt[i] != r2.Txt[i] { |
|||
return false |
|||
} |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateCAA(r1, r2 *CAA) bool { |
|||
if r1.Flag != r2.Flag { |
|||
return false |
|||
} |
|||
if r1.Tag != r2.Tag { |
|||
return false |
|||
} |
|||
if r1.Value != r2.Value { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateCERT(r1, r2 *CERT) bool { |
|||
if r1.Type != r2.Type { |
|||
return false |
|||
} |
|||
if r1.KeyTag != r2.KeyTag { |
|||
return false |
|||
} |
|||
if r1.Algorithm != r2.Algorithm { |
|||
return false |
|||
} |
|||
if r1.Certificate != r2.Certificate { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateCNAME(r1, r2 *CNAME) bool { |
|||
if !isDulicateName(r1.Target, r2.Target) { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateCSYNC(r1, r2 *CSYNC) bool { |
|||
if r1.Serial != r2.Serial { |
|||
return false |
|||
} |
|||
if r1.Flags != r2.Flags { |
|||
return false |
|||
} |
|||
if len(r1.TypeBitMap) != len(r2.TypeBitMap) { |
|||
return false |
|||
} |
|||
for i := 0; i < len(r1.TypeBitMap); i++ { |
|||
if r1.TypeBitMap[i] != r2.TypeBitMap[i] { |
|||
return false |
|||
} |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateDHCID(r1, r2 *DHCID) bool { |
|||
if r1.Digest != r2.Digest { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateDNAME(r1, r2 *DNAME) bool { |
|||
if !isDulicateName(r1.Target, r2.Target) { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateDNSKEY(r1, r2 *DNSKEY) bool { |
|||
if r1.Flags != r2.Flags { |
|||
return false |
|||
} |
|||
if r1.Protocol != r2.Protocol { |
|||
return false |
|||
} |
|||
if r1.Algorithm != r2.Algorithm { |
|||
return false |
|||
} |
|||
if r1.PublicKey != r2.PublicKey { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateDS(r1, r2 *DS) bool { |
|||
if r1.KeyTag != r2.KeyTag { |
|||
return false |
|||
} |
|||
if r1.Algorithm != r2.Algorithm { |
|||
return false |
|||
} |
|||
if r1.DigestType != r2.DigestType { |
|||
return false |
|||
} |
|||
if r1.Digest != r2.Digest { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateEID(r1, r2 *EID) bool { |
|||
if r1.Endpoint != r2.Endpoint { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateEUI48(r1, r2 *EUI48) bool { |
|||
if r1.Address != r2.Address { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateEUI64(r1, r2 *EUI64) bool { |
|||
if r1.Address != r2.Address { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateGID(r1, r2 *GID) bool { |
|||
if r1.Gid != r2.Gid { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateGPOS(r1, r2 *GPOS) bool { |
|||
if r1.Longitude != r2.Longitude { |
|||
return false |
|||
} |
|||
if r1.Latitude != r2.Latitude { |
|||
return false |
|||
} |
|||
if r1.Altitude != r2.Altitude { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateHINFO(r1, r2 *HINFO) bool { |
|||
if r1.Cpu != r2.Cpu { |
|||
return false |
|||
} |
|||
if r1.Os != r2.Os { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateHIP(r1, r2 *HIP) bool { |
|||
if r1.HitLength != r2.HitLength { |
|||
return false |
|||
} |
|||
if r1.PublicKeyAlgorithm != r2.PublicKeyAlgorithm { |
|||
return false |
|||
} |
|||
if r1.PublicKeyLength != r2.PublicKeyLength { |
|||
return false |
|||
} |
|||
if r1.Hit != r2.Hit { |
|||
return false |
|||
} |
|||
if r1.PublicKey != r2.PublicKey { |
|||
return false |
|||
} |
|||
if len(r1.RendezvousServers) != len(r2.RendezvousServers) { |
|||
return false |
|||
} |
|||
for i := 0; i < len(r1.RendezvousServers); i++ { |
|||
if !isDulicateName(r1.RendezvousServers[i], r2.RendezvousServers[i]) { |
|||
return false |
|||
} |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateKX(r1, r2 *KX) bool { |
|||
if r1.Preference != r2.Preference { |
|||
return false |
|||
} |
|||
if !isDulicateName(r1.Exchanger, r2.Exchanger) { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateL32(r1, r2 *L32) bool { |
|||
if r1.Preference != r2.Preference { |
|||
return false |
|||
} |
|||
if len(r1.Locator32) != len(r2.Locator32) { |
|||
return false |
|||
} |
|||
for i := 0; i < len(r1.Locator32); i++ { |
|||
if r1.Locator32[i] != r2.Locator32[i] { |
|||
return false |
|||
} |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateL64(r1, r2 *L64) bool { |
|||
if r1.Preference != r2.Preference { |
|||
return false |
|||
} |
|||
if r1.Locator64 != r2.Locator64 { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateLOC(r1, r2 *LOC) bool { |
|||
if r1.Version != r2.Version { |
|||
return false |
|||
} |
|||
if r1.Size != r2.Size { |
|||
return false |
|||
} |
|||
if r1.HorizPre != r2.HorizPre { |
|||
return false |
|||
} |
|||
if r1.VertPre != r2.VertPre { |
|||
return false |
|||
} |
|||
if r1.Latitude != r2.Latitude { |
|||
return false |
|||
} |
|||
if r1.Longitude != r2.Longitude { |
|||
return false |
|||
} |
|||
if r1.Altitude != r2.Altitude { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateLP(r1, r2 *LP) bool { |
|||
if r1.Preference != r2.Preference { |
|||
return false |
|||
} |
|||
if !isDulicateName(r1.Fqdn, r2.Fqdn) { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateMB(r1, r2 *MB) bool { |
|||
if !isDulicateName(r1.Mb, r2.Mb) { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateMD(r1, r2 *MD) bool { |
|||
if !isDulicateName(r1.Md, r2.Md) { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateMF(r1, r2 *MF) bool { |
|||
if !isDulicateName(r1.Mf, r2.Mf) { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateMG(r1, r2 *MG) bool { |
|||
if !isDulicateName(r1.Mg, r2.Mg) { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateMINFO(r1, r2 *MINFO) bool { |
|||
if !isDulicateName(r1.Rmail, r2.Rmail) { |
|||
return false |
|||
} |
|||
if !isDulicateName(r1.Email, r2.Email) { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateMR(r1, r2 *MR) bool { |
|||
if !isDulicateName(r1.Mr, r2.Mr) { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateMX(r1, r2 *MX) bool { |
|||
if r1.Preference != r2.Preference { |
|||
return false |
|||
} |
|||
if !isDulicateName(r1.Mx, r2.Mx) { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateNAPTR(r1, r2 *NAPTR) bool { |
|||
if r1.Order != r2.Order { |
|||
return false |
|||
} |
|||
if r1.Preference != r2.Preference { |
|||
return false |
|||
} |
|||
if r1.Flags != r2.Flags { |
|||
return false |
|||
} |
|||
if r1.Service != r2.Service { |
|||
return false |
|||
} |
|||
if r1.Regexp != r2.Regexp { |
|||
return false |
|||
} |
|||
if !isDulicateName(r1.Replacement, r2.Replacement) { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateNID(r1, r2 *NID) bool { |
|||
if r1.Preference != r2.Preference { |
|||
return false |
|||
} |
|||
if r1.NodeID != r2.NodeID { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateNIMLOC(r1, r2 *NIMLOC) bool { |
|||
if r1.Locator != r2.Locator { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateNINFO(r1, r2 *NINFO) bool { |
|||
if len(r1.ZSData) != len(r2.ZSData) { |
|||
return false |
|||
} |
|||
for i := 0; i < len(r1.ZSData); i++ { |
|||
if r1.ZSData[i] != r2.ZSData[i] { |
|||
return false |
|||
} |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateNS(r1, r2 *NS) bool { |
|||
if !isDulicateName(r1.Ns, r2.Ns) { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateNSAPPTR(r1, r2 *NSAPPTR) bool { |
|||
if !isDulicateName(r1.Ptr, r2.Ptr) { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateNSEC(r1, r2 *NSEC) bool { |
|||
if !isDulicateName(r1.NextDomain, r2.NextDomain) { |
|||
return false |
|||
} |
|||
if len(r1.TypeBitMap) != len(r2.TypeBitMap) { |
|||
return false |
|||
} |
|||
for i := 0; i < len(r1.TypeBitMap); i++ { |
|||
if r1.TypeBitMap[i] != r2.TypeBitMap[i] { |
|||
return false |
|||
} |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateNSEC3(r1, r2 *NSEC3) bool { |
|||
if r1.Hash != r2.Hash { |
|||
return false |
|||
} |
|||
if r1.Flags != r2.Flags { |
|||
return false |
|||
} |
|||
if r1.Iterations != r2.Iterations { |
|||
return false |
|||
} |
|||
if r1.SaltLength != r2.SaltLength { |
|||
return false |
|||
} |
|||
if r1.Salt != r2.Salt { |
|||
return false |
|||
} |
|||
if r1.HashLength != r2.HashLength { |
|||
return false |
|||
} |
|||
if r1.NextDomain != r2.NextDomain { |
|||
return false |
|||
} |
|||
if len(r1.TypeBitMap) != len(r2.TypeBitMap) { |
|||
return false |
|||
} |
|||
for i := 0; i < len(r1.TypeBitMap); i++ { |
|||
if r1.TypeBitMap[i] != r2.TypeBitMap[i] { |
|||
return false |
|||
} |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateNSEC3PARAM(r1, r2 *NSEC3PARAM) bool { |
|||
if r1.Hash != r2.Hash { |
|||
return false |
|||
} |
|||
if r1.Flags != r2.Flags { |
|||
return false |
|||
} |
|||
if r1.Iterations != r2.Iterations { |
|||
return false |
|||
} |
|||
if r1.SaltLength != r2.SaltLength { |
|||
return false |
|||
} |
|||
if r1.Salt != r2.Salt { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateOPENPGPKEY(r1, r2 *OPENPGPKEY) bool { |
|||
if r1.PublicKey != r2.PublicKey { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicatePTR(r1, r2 *PTR) bool { |
|||
if !isDulicateName(r1.Ptr, r2.Ptr) { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicatePX(r1, r2 *PX) bool { |
|||
if r1.Preference != r2.Preference { |
|||
return false |
|||
} |
|||
if !isDulicateName(r1.Map822, r2.Map822) { |
|||
return false |
|||
} |
|||
if !isDulicateName(r1.Mapx400, r2.Mapx400) { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateRKEY(r1, r2 *RKEY) bool { |
|||
if r1.Flags != r2.Flags { |
|||
return false |
|||
} |
|||
if r1.Protocol != r2.Protocol { |
|||
return false |
|||
} |
|||
if r1.Algorithm != r2.Algorithm { |
|||
return false |
|||
} |
|||
if r1.PublicKey != r2.PublicKey { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateRP(r1, r2 *RP) bool { |
|||
if !isDulicateName(r1.Mbox, r2.Mbox) { |
|||
return false |
|||
} |
|||
if !isDulicateName(r1.Txt, r2.Txt) { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateRRSIG(r1, r2 *RRSIG) bool { |
|||
if r1.TypeCovered != r2.TypeCovered { |
|||
return false |
|||
} |
|||
if r1.Algorithm != r2.Algorithm { |
|||
return false |
|||
} |
|||
if r1.Labels != r2.Labels { |
|||
return false |
|||
} |
|||
if r1.OrigTtl != r2.OrigTtl { |
|||
return false |
|||
} |
|||
if r1.Expiration != r2.Expiration { |
|||
return false |
|||
} |
|||
if r1.Inception != r2.Inception { |
|||
return false |
|||
} |
|||
if r1.KeyTag != r2.KeyTag { |
|||
return false |
|||
} |
|||
if !isDulicateName(r1.SignerName, r2.SignerName) { |
|||
return false |
|||
} |
|||
if r1.Signature != r2.Signature { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateRT(r1, r2 *RT) bool { |
|||
if r1.Preference != r2.Preference { |
|||
return false |
|||
} |
|||
if !isDulicateName(r1.Host, r2.Host) { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateSMIMEA(r1, r2 *SMIMEA) bool { |
|||
if r1.Usage != r2.Usage { |
|||
return false |
|||
} |
|||
if r1.Selector != r2.Selector { |
|||
return false |
|||
} |
|||
if r1.MatchingType != r2.MatchingType { |
|||
return false |
|||
} |
|||
if r1.Certificate != r2.Certificate { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateSOA(r1, r2 *SOA) bool { |
|||
if !isDulicateName(r1.Ns, r2.Ns) { |
|||
return false |
|||
} |
|||
if !isDulicateName(r1.Mbox, r2.Mbox) { |
|||
return false |
|||
} |
|||
if r1.Serial != r2.Serial { |
|||
return false |
|||
} |
|||
if r1.Refresh != r2.Refresh { |
|||
return false |
|||
} |
|||
if r1.Retry != r2.Retry { |
|||
return false |
|||
} |
|||
if r1.Expire != r2.Expire { |
|||
return false |
|||
} |
|||
if r1.Minttl != r2.Minttl { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateSPF(r1, r2 *SPF) bool { |
|||
if len(r1.Txt) != len(r2.Txt) { |
|||
return false |
|||
} |
|||
for i := 0; i < len(r1.Txt); i++ { |
|||
if r1.Txt[i] != r2.Txt[i] { |
|||
return false |
|||
} |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateSRV(r1, r2 *SRV) bool { |
|||
if r1.Priority != r2.Priority { |
|||
return false |
|||
} |
|||
if r1.Weight != r2.Weight { |
|||
return false |
|||
} |
|||
if r1.Port != r2.Port { |
|||
return false |
|||
} |
|||
if !isDulicateName(r1.Target, r2.Target) { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateSSHFP(r1, r2 *SSHFP) bool { |
|||
if r1.Algorithm != r2.Algorithm { |
|||
return false |
|||
} |
|||
if r1.Type != r2.Type { |
|||
return false |
|||
} |
|||
if r1.FingerPrint != r2.FingerPrint { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateTA(r1, r2 *TA) bool { |
|||
if r1.KeyTag != r2.KeyTag { |
|||
return false |
|||
} |
|||
if r1.Algorithm != r2.Algorithm { |
|||
return false |
|||
} |
|||
if r1.DigestType != r2.DigestType { |
|||
return false |
|||
} |
|||
if r1.Digest != r2.Digest { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateTALINK(r1, r2 *TALINK) bool { |
|||
if !isDulicateName(r1.PreviousName, r2.PreviousName) { |
|||
return false |
|||
} |
|||
if !isDulicateName(r1.NextName, r2.NextName) { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateTKEY(r1, r2 *TKEY) bool { |
|||
if !isDulicateName(r1.Algorithm, r2.Algorithm) { |
|||
return false |
|||
} |
|||
if r1.Inception != r2.Inception { |
|||
return false |
|||
} |
|||
if r1.Expiration != r2.Expiration { |
|||
return false |
|||
} |
|||
if r1.Mode != r2.Mode { |
|||
return false |
|||
} |
|||
if r1.Error != r2.Error { |
|||
return false |
|||
} |
|||
if r1.KeySize != r2.KeySize { |
|||
return false |
|||
} |
|||
if r1.Key != r2.Key { |
|||
return false |
|||
} |
|||
if r1.OtherLen != r2.OtherLen { |
|||
return false |
|||
} |
|||
if r1.OtherData != r2.OtherData { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateTLSA(r1, r2 *TLSA) bool { |
|||
if r1.Usage != r2.Usage { |
|||
return false |
|||
} |
|||
if r1.Selector != r2.Selector { |
|||
return false |
|||
} |
|||
if r1.MatchingType != r2.MatchingType { |
|||
return false |
|||
} |
|||
if r1.Certificate != r2.Certificate { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateTSIG(r1, r2 *TSIG) bool { |
|||
if !isDulicateName(r1.Algorithm, r2.Algorithm) { |
|||
return false |
|||
} |
|||
if r1.TimeSigned != r2.TimeSigned { |
|||
return false |
|||
} |
|||
if r1.Fudge != r2.Fudge { |
|||
return false |
|||
} |
|||
if r1.MACSize != r2.MACSize { |
|||
return false |
|||
} |
|||
if r1.MAC != r2.MAC { |
|||
return false |
|||
} |
|||
if r1.OrigId != r2.OrigId { |
|||
return false |
|||
} |
|||
if r1.Error != r2.Error { |
|||
return false |
|||
} |
|||
if r1.OtherLen != r2.OtherLen { |
|||
return false |
|||
} |
|||
if r1.OtherData != r2.OtherData { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateTXT(r1, r2 *TXT) bool { |
|||
if len(r1.Txt) != len(r2.Txt) { |
|||
return false |
|||
} |
|||
for i := 0; i < len(r1.Txt); i++ { |
|||
if r1.Txt[i] != r2.Txt[i] { |
|||
return false |
|||
} |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateUID(r1, r2 *UID) bool { |
|||
if r1.Uid != r2.Uid { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateUINFO(r1, r2 *UINFO) bool { |
|||
if r1.Uinfo != r2.Uinfo { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateURI(r1, r2 *URI) bool { |
|||
if r1.Priority != r2.Priority { |
|||
return false |
|||
} |
|||
if r1.Weight != r2.Weight { |
|||
return false |
|||
} |
|||
if r1.Target != r2.Target { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func isDuplicateX25(r1, r2 *X25) bool { |
|||
if r1.PSDNAddress != r2.PSDNAddress { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
File diff suppressed because it is too large
@ -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} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
|
|||
|
|||
package socket |
|||
|
|||
func (h *cmsghdr) len() int { return int(h.Len) } |
|||
func (h *cmsghdr) lvl() int { return int(h.Level) } |
|||
func (h *cmsghdr) typ() int { return int(h.Type) } |
|||
@ -0,0 +1,13 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build darwin dragonfly freebsd netbsd openbsd
|
|||
|
|||
package socket |
|||
|
|||
func (h *cmsghdr) set(l, lvl, typ int) { |
|||
h.Len = uint32(l) |
|||
h.Level = int32(lvl) |
|||
h.Type = int32(typ) |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build arm mips mipsle 386
|
|||
// +build linux
|
|||
|
|||
package socket |
|||
|
|||
func (h *cmsghdr) set(l, lvl, typ int) { |
|||
h.Len = uint32(l) |
|||
h.Level = int32(lvl) |
|||
h.Type = int32(typ) |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build arm64 amd64 ppc64 ppc64le mips64 mips64le s390x
|
|||
// +build linux
|
|||
|
|||
package socket |
|||
|
|||
func (h *cmsghdr) set(l, lvl, typ int) { |
|||
h.Len = uint64(l) |
|||
h.Level = int32(lvl) |
|||
h.Type = int32(typ) |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build amd64
|
|||
// +build solaris
|
|||
|
|||
package socket |
|||
|
|||
func (h *cmsghdr) set(l, lvl, typ int) { |
|||
h.Len = uint32(l) |
|||
h.Level = int32(lvl) |
|||
h.Type = int32(typ) |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
|
|||
|
|||
package socket |
|||
|
|||
type cmsghdr struct{} |
|||
|
|||
const sizeofCmsghdr = 0 |
|||
|
|||
func (h *cmsghdr) len() int { return 0 } |
|||
func (h *cmsghdr) lvl() int { return 0 } |
|||
func (h *cmsghdr) typ() int { return 0 } |
|||
|
|||
func (h *cmsghdr) set(l, lvl, typ int) {} |
|||
@ -0,0 +1,31 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
|
|||
|
|||
package socket |
|||
|
|||
import "syscall" |
|||
|
|||
var ( |
|||
errEAGAIN error = syscall.EAGAIN |
|||
errEINVAL error = syscall.EINVAL |
|||
errENOENT error = syscall.ENOENT |
|||
) |
|||
|
|||
// errnoErr returns common boxed Errno values, to prevent allocations
|
|||
// at runtime.
|
|||
func errnoErr(errno syscall.Errno) error { |
|||
switch errno { |
|||
case 0: |
|||
return nil |
|||
case syscall.EAGAIN: |
|||
return errEAGAIN |
|||
case syscall.EINVAL: |
|||
return errEINVAL |
|||
case syscall.ENOENT: |
|||
return errENOENT |
|||
} |
|||
return errno |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
package socket |
|||
|
|||
import "syscall" |
|||
|
|||
var ( |
|||
errERROR_IO_PENDING error = syscall.ERROR_IO_PENDING |
|||
errEINVAL error = syscall.EINVAL |
|||
) |
|||
|
|||
// errnoErr returns common boxed Errno values, to prevent allocations
|
|||
// at runtime.
|
|||
func errnoErr(errno syscall.Errno) error { |
|||
switch errno { |
|||
case 0: |
|||
return nil |
|||
case syscall.ERROR_IO_PENDING: |
|||
return errERROR_IO_PENDING |
|||
case syscall.EINVAL: |
|||
return errEINVAL |
|||
} |
|||
return errno |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build arm mips mipsle 386
|
|||
// +build darwin dragonfly freebsd linux netbsd openbsd
|
|||
|
|||
package socket |
|||
|
|||
import "unsafe" |
|||
|
|||
func (v *iovec) set(b []byte) { |
|||
l := len(b) |
|||
if l == 0 { |
|||
return |
|||
} |
|||
v.Base = (*byte)(unsafe.Pointer(&b[0])) |
|||
v.Len = uint32(l) |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build arm64 amd64 ppc64 ppc64le mips64 mips64le s390x
|
|||
// +build darwin dragonfly freebsd linux netbsd openbsd
|
|||
|
|||
package socket |
|||
|
|||
import "unsafe" |
|||
|
|||
func (v *iovec) set(b []byte) { |
|||
l := len(b) |
|||
if l == 0 { |
|||
return |
|||
} |
|||
v.Base = (*byte)(unsafe.Pointer(&b[0])) |
|||
v.Len = uint64(l) |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build amd64
|
|||
// +build solaris
|
|||
|
|||
package socket |
|||
|
|||
import "unsafe" |
|||
|
|||
func (v *iovec) set(b []byte) { |
|||
l := len(b) |
|||
if l == 0 { |
|||
return |
|||
} |
|||
v.Base = (*int8)(unsafe.Pointer(&b[0])) |
|||
v.Len = uint64(l) |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
|
|||
|
|||
package socket |
|||
|
|||
type iovec struct{} |
|||
|
|||
func (v *iovec) set(b []byte) {} |
|||
@ -0,0 +1,21 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build !linux,!netbsd
|
|||
|
|||
package socket |
|||
|
|||
import "net" |
|||
|
|||
type mmsghdr struct{} |
|||
|
|||
type mmsghdrs []mmsghdr |
|||
|
|||
func (hs mmsghdrs) pack(ms []Message, parseFn func([]byte, string) (net.Addr, error), marshalFn func(net.Addr) []byte) error { |
|||
return nil |
|||
} |
|||
|
|||
func (hs mmsghdrs) unpack(ms []Message, parseFn func([]byte, string) (net.Addr, error), hint string) error { |
|||
return nil |
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build linux netbsd
|
|||
|
|||
package socket |
|||
|
|||
import "net" |
|||
|
|||
type mmsghdrs []mmsghdr |
|||
|
|||
func (hs mmsghdrs) pack(ms []Message, parseFn func([]byte, string) (net.Addr, error), marshalFn func(net.Addr) []byte) error { |
|||
for i := range hs { |
|||
vs := make([]iovec, len(ms[i].Buffers)) |
|||
var sa []byte |
|||
if parseFn != nil { |
|||
sa = make([]byte, sizeofSockaddrInet6) |
|||
} |
|||
if marshalFn != nil { |
|||
sa = marshalFn(ms[i].Addr) |
|||
} |
|||
hs[i].Hdr.pack(vs, ms[i].Buffers, ms[i].OOB, sa) |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
func (hs mmsghdrs) unpack(ms []Message, parseFn func([]byte, string) (net.Addr, error), hint string) error { |
|||
for i := range hs { |
|||
ms[i].N = int(hs[i].Len) |
|||
ms[i].NN = hs[i].Hdr.controllen() |
|||
ms[i].Flags = hs[i].Hdr.flags() |
|||
if parseFn != nil { |
|||
var err error |
|||
ms[i].Addr, err = parseFn(hs[i].Hdr.name(), hint) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
} |
|||
} |
|||
return nil |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build darwin dragonfly freebsd netbsd openbsd
|
|||
|
|||
package socket |
|||
|
|||
import "unsafe" |
|||
|
|||
func (h *msghdr) pack(vs []iovec, bs [][]byte, oob []byte, sa []byte) { |
|||
for i := range vs { |
|||
vs[i].set(bs[i]) |
|||
} |
|||
h.setIov(vs) |
|||
if len(oob) > 0 { |
|||
h.Control = (*byte)(unsafe.Pointer(&oob[0])) |
|||
h.Controllen = uint32(len(oob)) |
|||
} |
|||
if sa != nil { |
|||
h.Name = (*byte)(unsafe.Pointer(&sa[0])) |
|||
h.Namelen = uint32(len(sa)) |
|||
} |
|||
} |
|||
|
|||
func (h *msghdr) name() []byte { |
|||
if h.Name != nil && h.Namelen > 0 { |
|||
return (*[sizeofSockaddrInet6]byte)(unsafe.Pointer(h.Name))[:h.Namelen] |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
func (h *msghdr) controllen() int { |
|||
return int(h.Controllen) |
|||
} |
|||
|
|||
func (h *msghdr) flags() int { |
|||
return int(h.Flags) |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build darwin dragonfly freebsd netbsd
|
|||
|
|||
package socket |
|||
|
|||
func (h *msghdr) setIov(vs []iovec) { |
|||
l := len(vs) |
|||
if l == 0 { |
|||
return |
|||
} |
|||
h.Iov = &vs[0] |
|||
h.Iovlen = int32(l) |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
package socket |
|||
|
|||
import "unsafe" |
|||
|
|||
func (h *msghdr) pack(vs []iovec, bs [][]byte, oob []byte, sa []byte) { |
|||
for i := range vs { |
|||
vs[i].set(bs[i]) |
|||
} |
|||
h.setIov(vs) |
|||
if len(oob) > 0 { |
|||
h.setControl(oob) |
|||
} |
|||
if sa != nil { |
|||
h.Name = (*byte)(unsafe.Pointer(&sa[0])) |
|||
h.Namelen = uint32(len(sa)) |
|||
} |
|||
} |
|||
|
|||
func (h *msghdr) name() []byte { |
|||
if h.Name != nil && h.Namelen > 0 { |
|||
return (*[sizeofSockaddrInet6]byte)(unsafe.Pointer(h.Name))[:h.Namelen] |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
func (h *msghdr) controllen() int { |
|||
return int(h.Controllen) |
|||
} |
|||
|
|||
func (h *msghdr) flags() int { |
|||
return int(h.Flags) |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build arm mips mipsle 386
|
|||
// +build linux
|
|||
|
|||
package socket |
|||
|
|||
import "unsafe" |
|||
|
|||
func (h *msghdr) setIov(vs []iovec) { |
|||
l := len(vs) |
|||
if l == 0 { |
|||
return |
|||
} |
|||
h.Iov = &vs[0] |
|||
h.Iovlen = uint32(l) |
|||
} |
|||
|
|||
func (h *msghdr) setControl(b []byte) { |
|||
h.Control = (*byte)(unsafe.Pointer(&b[0])) |
|||
h.Controllen = uint32(len(b)) |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build arm64 amd64 ppc64 ppc64le mips64 mips64le s390x
|
|||
// +build linux
|
|||
|
|||
package socket |
|||
|
|||
import "unsafe" |
|||
|
|||
func (h *msghdr) setIov(vs []iovec) { |
|||
l := len(vs) |
|||
if l == 0 { |
|||
return |
|||
} |
|||
h.Iov = &vs[0] |
|||
h.Iovlen = uint64(l) |
|||
} |
|||
|
|||
func (h *msghdr) setControl(b []byte) { |
|||
h.Control = (*byte)(unsafe.Pointer(&b[0])) |
|||
h.Controllen = uint64(len(b)) |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
package socket |
|||
|
|||
func (h *msghdr) setIov(vs []iovec) { |
|||
l := len(vs) |
|||
if l == 0 { |
|||
return |
|||
} |
|||
h.Iov = &vs[0] |
|||
h.Iovlen = uint32(l) |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build amd64
|
|||
// +build solaris
|
|||
|
|||
package socket |
|||
|
|||
import "unsafe" |
|||
|
|||
func (h *msghdr) pack(vs []iovec, bs [][]byte, oob []byte, sa []byte) { |
|||
for i := range vs { |
|||
vs[i].set(bs[i]) |
|||
} |
|||
if len(vs) > 0 { |
|||
h.Iov = &vs[0] |
|||
h.Iovlen = int32(len(vs)) |
|||
} |
|||
if len(oob) > 0 { |
|||
h.Accrights = (*int8)(unsafe.Pointer(&oob[0])) |
|||
h.Accrightslen = int32(len(oob)) |
|||
} |
|||
if sa != nil { |
|||
h.Name = (*byte)(unsafe.Pointer(&sa[0])) |
|||
h.Namelen = uint32(len(sa)) |
|||
} |
|||
} |
|||
|
|||
func (h *msghdr) controllen() int { |
|||
return int(h.Accrightslen) |
|||
} |
|||
|
|||
func (h *msghdr) flags() int { |
|||
return int(NativeEndian.Uint32(h.Pad_cgo_2[:])) |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
|
|||
|
|||
package socket |
|||
|
|||
type msghdr struct{} |
|||
|
|||
func (h *msghdr) pack(vs []iovec, bs [][]byte, oob []byte, sa []byte) {} |
|||
func (h *msghdr) name() []byte { return nil } |
|||
func (h *msghdr) controllen() int { return 0 } |
|||
func (h *msghdr) flags() int { return 0 } |
|||
@ -0,0 +1,66 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build go1.9
|
|||
|
|||
package socket |
|||
|
|||
import ( |
|||
"errors" |
|||
"net" |
|||
"os" |
|||
"syscall" |
|||
) |
|||
|
|||
// A Conn represents a raw connection.
|
|||
type Conn struct { |
|||
network string |
|||
c syscall.RawConn |
|||
} |
|||
|
|||
// NewConn returns a new raw connection.
|
|||
func NewConn(c net.Conn) (*Conn, error) { |
|||
var err error |
|||
var cc Conn |
|||
switch c := c.(type) { |
|||
case *net.TCPConn: |
|||
cc.network = "tcp" |
|||
cc.c, err = c.SyscallConn() |
|||
case *net.UDPConn: |
|||
cc.network = "udp" |
|||
cc.c, err = c.SyscallConn() |
|||
case *net.IPConn: |
|||
cc.network = "ip" |
|||
cc.c, err = c.SyscallConn() |
|||
default: |
|||
return nil, errors.New("unknown connection type") |
|||
} |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return &cc, nil |
|||
} |
|||
|
|||
func (o *Option) get(c *Conn, b []byte) (int, error) { |
|||
var operr error |
|||
var n int |
|||
fn := func(s uintptr) { |
|||
n, operr = getsockopt(s, o.Level, o.Name, b) |
|||
} |
|||
if err := c.c.Control(fn); err != nil { |
|||
return 0, err |
|||
} |
|||
return n, os.NewSyscallError("getsockopt", operr) |
|||
} |
|||
|
|||
func (o *Option) set(c *Conn, b []byte) error { |
|||
var operr error |
|||
fn := func(s uintptr) { |
|||
operr = setsockopt(s, o.Level, o.Name, b) |
|||
} |
|||
if err := c.c.Control(fn); err != nil { |
|||
return err |
|||
} |
|||
return os.NewSyscallError("setsockopt", operr) |
|||
} |
|||
@ -0,0 +1,74 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build go1.9
|
|||
// +build linux
|
|||
|
|||
package socket |
|||
|
|||
import ( |
|||
"net" |
|||
"os" |
|||
"syscall" |
|||
) |
|||
|
|||
func (c *Conn) recvMsgs(ms []Message, flags int) (int, error) { |
|||
hs := make(mmsghdrs, len(ms)) |
|||
var parseFn func([]byte, string) (net.Addr, error) |
|||
if c.network != "tcp" { |
|||
parseFn = parseInetAddr |
|||
} |
|||
if err := hs.pack(ms, parseFn, nil); err != nil { |
|||
return 0, err |
|||
} |
|||
var operr error |
|||
var n int |
|||
fn := func(s uintptr) bool { |
|||
n, operr = recvmmsg(s, hs, flags) |
|||
if operr == syscall.EAGAIN { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
if err := c.c.Read(fn); err != nil { |
|||
return n, err |
|||
} |
|||
if operr != nil { |
|||
return n, os.NewSyscallError("recvmmsg", operr) |
|||
} |
|||
if err := hs[:n].unpack(ms[:n], parseFn, c.network); err != nil { |
|||
return n, err |
|||
} |
|||
return n, nil |
|||
} |
|||
|
|||
func (c *Conn) sendMsgs(ms []Message, flags int) (int, error) { |
|||
hs := make(mmsghdrs, len(ms)) |
|||
var marshalFn func(net.Addr) []byte |
|||
if c.network != "tcp" { |
|||
marshalFn = marshalInetAddr |
|||
} |
|||
if err := hs.pack(ms, nil, marshalFn); err != nil { |
|||
return 0, err |
|||
} |
|||
var operr error |
|||
var n int |
|||
fn := func(s uintptr) bool { |
|||
n, operr = sendmmsg(s, hs, flags) |
|||
if operr == syscall.EAGAIN { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
if err := c.c.Write(fn); err != nil { |
|||
return n, err |
|||
} |
|||
if operr != nil { |
|||
return n, os.NewSyscallError("sendmmsg", operr) |
|||
} |
|||
if err := hs[:n].unpack(ms[:n], nil, ""); err != nil { |
|||
return n, err |
|||
} |
|||
return n, nil |
|||
} |
|||
@ -0,0 +1,77 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build go1.9
|
|||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
|
|||
|
|||
package socket |
|||
|
|||
import ( |
|||
"os" |
|||
"syscall" |
|||
) |
|||
|
|||
func (c *Conn) recvMsg(m *Message, flags int) error { |
|||
var h msghdr |
|||
vs := make([]iovec, len(m.Buffers)) |
|||
var sa []byte |
|||
if c.network != "tcp" { |
|||
sa = make([]byte, sizeofSockaddrInet6) |
|||
} |
|||
h.pack(vs, m.Buffers, m.OOB, sa) |
|||
var operr error |
|||
var n int |
|||
fn := func(s uintptr) bool { |
|||
n, operr = recvmsg(s, &h, flags) |
|||
if operr == syscall.EAGAIN { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
if err := c.c.Read(fn); err != nil { |
|||
return err |
|||
} |
|||
if operr != nil { |
|||
return os.NewSyscallError("recvmsg", operr) |
|||
} |
|||
if c.network != "tcp" { |
|||
var err error |
|||
m.Addr, err = parseInetAddr(sa[:], c.network) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
} |
|||
m.N = n |
|||
m.NN = h.controllen() |
|||
m.Flags = h.flags() |
|||
return nil |
|||
} |
|||
|
|||
func (c *Conn) sendMsg(m *Message, flags int) error { |
|||
var h msghdr |
|||
vs := make([]iovec, len(m.Buffers)) |
|||
var sa []byte |
|||
if m.Addr != nil { |
|||
sa = marshalInetAddr(m.Addr) |
|||
} |
|||
h.pack(vs, m.Buffers, m.OOB, sa) |
|||
var operr error |
|||
var n int |
|||
fn := func(s uintptr) bool { |
|||
n, operr = sendmsg(s, &h, flags) |
|||
if operr == syscall.EAGAIN { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
if err := c.c.Write(fn); err != nil { |
|||
return err |
|||
} |
|||
if operr != nil { |
|||
return os.NewSyscallError("sendmsg", operr) |
|||
} |
|||
m.N = n |
|||
m.NN = len(m.OOB) |
|||
return nil |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build go1.9
|
|||
// +build !linux
|
|||
|
|||
package socket |
|||
|
|||
import "errors" |
|||
|
|||
func (c *Conn) recvMsgs(ms []Message, flags int) (int, error) { |
|||
return 0, errors.New("not implemented") |
|||
} |
|||
|
|||
func (c *Conn) sendMsgs(ms []Message, flags int) (int, error) { |
|||
return 0, errors.New("not implemented") |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build go1.9
|
|||
// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows
|
|||
|
|||
package socket |
|||
|
|||
import "errors" |
|||
|
|||
func (c *Conn) recvMsg(m *Message, flags int) error { |
|||
return errors.New("not implemented") |
|||
} |
|||
|
|||
func (c *Conn) sendMsg(m *Message, flags int) error { |
|||
return errors.New("not implemented") |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build !go1.9
|
|||
|
|||
package socket |
|||
|
|||
import "errors" |
|||
|
|||
func (c *Conn) recvMsg(m *Message, flags int) error { |
|||
return errors.New("not implemented") |
|||
} |
|||
|
|||
func (c *Conn) sendMsg(m *Message, flags int) error { |
|||
return errors.New("not implemented") |
|||
} |
|||
|
|||
func (c *Conn) recvMsgs(ms []Message, flags int) (int, error) { |
|||
return 0, errors.New("not implemented") |
|||
} |
|||
|
|||
func (c *Conn) sendMsgs(ms []Message, flags int) (int, error) { |
|||
return 0, errors.New("not implemented") |
|||
} |
|||
@ -0,0 +1,62 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build !go1.9
|
|||
|
|||
package socket |
|||
|
|||
import ( |
|||
"errors" |
|||
"net" |
|||
"os" |
|||
"reflect" |
|||
"runtime" |
|||
) |
|||
|
|||
// A Conn represents a raw connection.
|
|||
type Conn struct { |
|||
c net.Conn |
|||
} |
|||
|
|||
// NewConn returns a new raw connection.
|
|||
func NewConn(c net.Conn) (*Conn, error) { |
|||
return &Conn{c: c}, nil |
|||
} |
|||
|
|||
func (o *Option) get(c *Conn, b []byte) (int, error) { |
|||
s, err := socketOf(c.c) |
|||
if err != nil { |
|||
return 0, err |
|||
} |
|||
n, err := getsockopt(s, o.Level, o.Name, b) |
|||
return n, os.NewSyscallError("getsockopt", err) |
|||
} |
|||
|
|||
func (o *Option) set(c *Conn, b []byte) error { |
|||
s, err := socketOf(c.c) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
return os.NewSyscallError("setsockopt", setsockopt(s, o.Level, o.Name, b)) |
|||
} |
|||
|
|||
func socketOf(c net.Conn) (uintptr, error) { |
|||
switch c.(type) { |
|||
case *net.TCPConn, *net.UDPConn, *net.IPConn: |
|||
v := reflect.ValueOf(c) |
|||
switch e := v.Elem(); e.Kind() { |
|||
case reflect.Struct: |
|||
fd := e.FieldByName("conn").FieldByName("fd") |
|||
switch e := fd.Elem(); e.Kind() { |
|||
case reflect.Struct: |
|||
sysfd := e.FieldByName("sysfd") |
|||
if runtime.GOOS == "windows" { |
|||
return uintptr(sysfd.Uint()), nil |
|||
} |
|||
return uintptr(sysfd.Int()), nil |
|||
} |
|||
} |
|||
} |
|||
return 0, errors.New("invalid type") |
|||
} |
|||
@ -0,0 +1,285 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// Package socket provides a portable interface for socket system
|
|||
// calls.
|
|||
package socket // import "golang.org/x/net/internal/socket"
|
|||
|
|||
import ( |
|||
"errors" |
|||
"net" |
|||
"unsafe" |
|||
) |
|||
|
|||
// An Option represents a sticky socket option.
|
|||
type Option struct { |
|||
Level int // level
|
|||
Name int // name; must be equal or greater than 1
|
|||
Len int // length of value in bytes; must be equal or greater than 1
|
|||
} |
|||
|
|||
// Get reads a value for the option from the kernel.
|
|||
// It returns the number of bytes written into b.
|
|||
func (o *Option) Get(c *Conn, b []byte) (int, error) { |
|||
if o.Name < 1 || o.Len < 1 { |
|||
return 0, errors.New("invalid option") |
|||
} |
|||
if len(b) < o.Len { |
|||
return 0, errors.New("short buffer") |
|||
} |
|||
return o.get(c, b) |
|||
} |
|||
|
|||
// GetInt returns an integer value for the option.
|
|||
//
|
|||
// The Len field of Option must be either 1 or 4.
|
|||
func (o *Option) GetInt(c *Conn) (int, error) { |
|||
if o.Len != 1 && o.Len != 4 { |
|||
return 0, errors.New("invalid option") |
|||
} |
|||
var b []byte |
|||
var bb [4]byte |
|||
if o.Len == 1 { |
|||
b = bb[:1] |
|||
} else { |
|||
b = bb[:4] |
|||
} |
|||
n, err := o.get(c, b) |
|||
if err != nil { |
|||
return 0, err |
|||
} |
|||
if n != o.Len { |
|||
return 0, errors.New("invalid option length") |
|||
} |
|||
if o.Len == 1 { |
|||
return int(b[0]), nil |
|||
} |
|||
return int(NativeEndian.Uint32(b[:4])), nil |
|||
} |
|||
|
|||
// Set writes the option and value to the kernel.
|
|||
func (o *Option) Set(c *Conn, b []byte) error { |
|||
if o.Name < 1 || o.Len < 1 { |
|||
return errors.New("invalid option") |
|||
} |
|||
if len(b) < o.Len { |
|||
return errors.New("short buffer") |
|||
} |
|||
return o.set(c, b) |
|||
} |
|||
|
|||
// SetInt writes the option and value to the kernel.
|
|||
//
|
|||
// The Len field of Option must be either 1 or 4.
|
|||
func (o *Option) SetInt(c *Conn, v int) error { |
|||
if o.Len != 1 && o.Len != 4 { |
|||
return errors.New("invalid option") |
|||
} |
|||
var b []byte |
|||
if o.Len == 1 { |
|||
b = []byte{byte(v)} |
|||
} else { |
|||
var bb [4]byte |
|||
NativeEndian.PutUint32(bb[:o.Len], uint32(v)) |
|||
b = bb[:4] |
|||
} |
|||
return o.set(c, b) |
|||
} |
|||
|
|||
func controlHeaderLen() int { |
|||
return roundup(sizeofCmsghdr) |
|||
} |
|||
|
|||
func controlMessageLen(dataLen int) int { |
|||
return roundup(sizeofCmsghdr) + dataLen |
|||
} |
|||
|
|||
// ControlMessageSpace returns the whole length of control message.
|
|||
func ControlMessageSpace(dataLen int) int { |
|||
return roundup(sizeofCmsghdr) + roundup(dataLen) |
|||
} |
|||
|
|||
// A ControlMessage represents the head message in a stream of control
|
|||
// messages.
|
|||
//
|
|||
// A control message comprises of a header, data and a few padding
|
|||
// fields to conform to the interface to the kernel.
|
|||
//
|
|||
// See RFC 3542 for further information.
|
|||
type ControlMessage []byte |
|||
|
|||
// Data returns the data field of the control message at the head on
|
|||
// m.
|
|||
func (m ControlMessage) Data(dataLen int) []byte { |
|||
l := controlHeaderLen() |
|||
if len(m) < l || len(m) < l+dataLen { |
|||
return nil |
|||
} |
|||
return m[l : l+dataLen] |
|||
} |
|||
|
|||
// Next returns the control message at the next on m.
|
|||
//
|
|||
// Next works only for standard control messages.
|
|||
func (m ControlMessage) Next(dataLen int) ControlMessage { |
|||
l := ControlMessageSpace(dataLen) |
|||
if len(m) < l { |
|||
return nil |
|||
} |
|||
return m[l:] |
|||
} |
|||
|
|||
// MarshalHeader marshals the header fields of the control message at
|
|||
// the head on m.
|
|||
func (m ControlMessage) MarshalHeader(lvl, typ, dataLen int) error { |
|||
if len(m) < controlHeaderLen() { |
|||
return errors.New("short message") |
|||
} |
|||
h := (*cmsghdr)(unsafe.Pointer(&m[0])) |
|||
h.set(controlMessageLen(dataLen), lvl, typ) |
|||
return nil |
|||
} |
|||
|
|||
// ParseHeader parses and returns the header fields of the control
|
|||
// message at the head on m.
|
|||
func (m ControlMessage) ParseHeader() (lvl, typ, dataLen int, err error) { |
|||
l := controlHeaderLen() |
|||
if len(m) < l { |
|||
return 0, 0, 0, errors.New("short message") |
|||
} |
|||
h := (*cmsghdr)(unsafe.Pointer(&m[0])) |
|||
return h.lvl(), h.typ(), int(uint64(h.len()) - uint64(l)), nil |
|||
} |
|||
|
|||
// Marshal marshals the control message at the head on m, and returns
|
|||
// the next control message.
|
|||
func (m ControlMessage) Marshal(lvl, typ int, data []byte) (ControlMessage, error) { |
|||
l := len(data) |
|||
if len(m) < ControlMessageSpace(l) { |
|||
return nil, errors.New("short message") |
|||
} |
|||
h := (*cmsghdr)(unsafe.Pointer(&m[0])) |
|||
h.set(controlMessageLen(l), lvl, typ) |
|||
if l > 0 { |
|||
copy(m.Data(l), data) |
|||
} |
|||
return m.Next(l), nil |
|||
} |
|||
|
|||
// Parse parses m as a single or multiple control messages.
|
|||
//
|
|||
// Parse works for both standard and compatible messages.
|
|||
func (m ControlMessage) Parse() ([]ControlMessage, error) { |
|||
var ms []ControlMessage |
|||
for len(m) >= controlHeaderLen() { |
|||
h := (*cmsghdr)(unsafe.Pointer(&m[0])) |
|||
l := h.len() |
|||
if l <= 0 { |
|||
return nil, errors.New("invalid header length") |
|||
} |
|||
if uint64(l) < uint64(controlHeaderLen()) { |
|||
return nil, errors.New("invalid message length") |
|||
} |
|||
if uint64(l) > uint64(len(m)) { |
|||
return nil, errors.New("short buffer") |
|||
} |
|||
// On message reception:
|
|||
//
|
|||
// |<- ControlMessageSpace --------------->|
|
|||
// |<- controlMessageLen ---------->| |
|
|||
// |<- controlHeaderLen ->| | |
|
|||
// +---------------+------+---------+------+
|
|||
// | Header | PadH | Data | PadD |
|
|||
// +---------------+------+---------+------+
|
|||
//
|
|||
// On compatible message reception:
|
|||
//
|
|||
// | ... |<- controlMessageLen ----------->|
|
|||
// | ... |<- controlHeaderLen ->| |
|
|||
// +-----+---------------+------+----------+
|
|||
// | ... | Header | PadH | Data |
|
|||
// +-----+---------------+------+----------+
|
|||
ms = append(ms, ControlMessage(m[:l])) |
|||
ll := l - controlHeaderLen() |
|||
if len(m) >= ControlMessageSpace(ll) { |
|||
m = m[ControlMessageSpace(ll):] |
|||
} else { |
|||
m = m[controlMessageLen(ll):] |
|||
} |
|||
} |
|||
return ms, nil |
|||
} |
|||
|
|||
// NewControlMessage returns a new stream of control messages.
|
|||
func NewControlMessage(dataLen []int) ControlMessage { |
|||
var l int |
|||
for i := range dataLen { |
|||
l += ControlMessageSpace(dataLen[i]) |
|||
} |
|||
return make([]byte, l) |
|||
} |
|||
|
|||
// A Message represents an IO message.
|
|||
type Message struct { |
|||
// When writing, the Buffers field must contain at least one
|
|||
// byte to write.
|
|||
// When reading, the Buffers field will always contain a byte
|
|||
// to read.
|
|||
Buffers [][]byte |
|||
|
|||
// OOB contains protocol-specific control or miscellaneous
|
|||
// ancillary data known as out-of-band data.
|
|||
OOB []byte |
|||
|
|||
// Addr specifies a destination address when writing.
|
|||
// It can be nil when the underlying protocol of the raw
|
|||
// connection uses connection-oriented communication.
|
|||
// After a successful read, it may contain the source address
|
|||
// on the received packet.
|
|||
Addr net.Addr |
|||
|
|||
N int // # of bytes read or written from/to Buffers
|
|||
NN int // # of bytes read or written from/to OOB
|
|||
Flags int // protocol-specific information on the received message
|
|||
} |
|||
|
|||
// RecvMsg wraps recvmsg system call.
|
|||
//
|
|||
// The provided flags is a set of platform-dependent flags, such as
|
|||
// syscall.MSG_PEEK.
|
|||
func (c *Conn) RecvMsg(m *Message, flags int) error { |
|||
return c.recvMsg(m, flags) |
|||
} |
|||
|
|||
// SendMsg wraps sendmsg system call.
|
|||
//
|
|||
// The provided flags is a set of platform-dependent flags, such as
|
|||
// syscall.MSG_DONTROUTE.
|
|||
func (c *Conn) SendMsg(m *Message, flags int) error { |
|||
return c.sendMsg(m, flags) |
|||
} |
|||
|
|||
// RecvMsgs wraps recvmmsg system call.
|
|||
//
|
|||
// It returns the number of processed messages.
|
|||
//
|
|||
// The provided flags is a set of platform-dependent flags, such as
|
|||
// syscall.MSG_PEEK.
|
|||
//
|
|||
// Only Linux supports this.
|
|||
func (c *Conn) RecvMsgs(ms []Message, flags int) (int, error) { |
|||
return c.recvMsgs(ms, flags) |
|||
} |
|||
|
|||
// SendMsgs wraps sendmmsg system call.
|
|||
//
|
|||
// It returns the number of processed messages.
|
|||
//
|
|||
// The provided flags is a set of platform-dependent flags, such as
|
|||
// syscall.MSG_DONTROUTE.
|
|||
//
|
|||
// Only Linux supports this.
|
|||
func (c *Conn) SendMsgs(ms []Message, flags int) (int, error) { |
|||
return c.sendMsgs(ms, flags) |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
package socket |
|||
|
|||
import ( |
|||
"encoding/binary" |
|||
"unsafe" |
|||
) |
|||
|
|||
var ( |
|||
// NativeEndian is the machine native endian implementation of
|
|||
// ByteOrder.
|
|||
NativeEndian binary.ByteOrder |
|||
|
|||
kernelAlign int |
|||
) |
|||
|
|||
func init() { |
|||
i := uint32(1) |
|||
b := (*[4]byte)(unsafe.Pointer(&i)) |
|||
if b[0] == 1 { |
|||
NativeEndian = binary.LittleEndian |
|||
} else { |
|||
NativeEndian = binary.BigEndian |
|||
} |
|||
kernelAlign = probeProtocolStack() |
|||
} |
|||
|
|||
func roundup(l int) int { |
|||
return (l + kernelAlign - 1) & ^(kernelAlign - 1) |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build darwin dragonfly freebsd openbsd
|
|||
|
|||
package socket |
|||
|
|||
import "errors" |
|||
|
|||
func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) { |
|||
return 0, errors.New("not implemented") |
|||
} |
|||
|
|||
func sendmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) { |
|||
return 0, errors.New("not implemented") |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build freebsd netbsd openbsd
|
|||
|
|||
package socket |
|||
|
|||
import "unsafe" |
|||
|
|||
func probeProtocolStack() int { |
|||
var p uintptr |
|||
return int(unsafe.Sizeof(p)) |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
package socket |
|||
|
|||
func probeProtocolStack() int { return 4 } |
|||
@ -0,0 +1,7 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
package socket |
|||
|
|||
func probeProtocolStack() int { return 4 } |
|||
@ -0,0 +1,27 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
// +build linux,!s390x,!386
|
|||
|
|||
package socket |
|||
|
|||
import ( |
|||
"syscall" |
|||
"unsafe" |
|||
) |
|||
|
|||
func probeProtocolStack() int { |
|||
var p uintptr |
|||
return int(unsafe.Sizeof(p)) |
|||
} |
|||
|
|||
func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) { |
|||
n, _, errno := syscall.Syscall6(sysRECVMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0) |
|||
return int(n), errnoErr(errno) |
|||
} |
|||
|
|||
func sendmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) { |
|||
n, _, errno := syscall.Syscall6(sysSENDMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0) |
|||
return int(n), errnoErr(errno) |
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
package socket |
|||
|
|||
import ( |
|||
"syscall" |
|||
"unsafe" |
|||
) |
|||
|
|||
func probeProtocolStack() int { return 4 } |
|||
|
|||
const ( |
|||
sysSETSOCKOPT = 0xe |
|||
sysGETSOCKOPT = 0xf |
|||
sysSENDMSG = 0x10 |
|||
sysRECVMSG = 0x11 |
|||
sysRECVMMSG = 0x13 |
|||
sysSENDMMSG = 0x14 |
|||
) |
|||
|
|||
func socketcall(call, a0, a1, a2, a3, a4, a5 uintptr) (uintptr, syscall.Errno) |
|||
func rawsocketcall(call, a0, a1, a2, a3, a4, a5 uintptr) (uintptr, syscall.Errno) |
|||
|
|||
func getsockopt(s uintptr, level, name int, b []byte) (int, error) { |
|||
l := uint32(len(b)) |
|||
_, errno := socketcall(sysGETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(unsafe.Pointer(&l)), 0) |
|||
return int(l), errnoErr(errno) |
|||
} |
|||
|
|||
func setsockopt(s uintptr, level, name int, b []byte) error { |
|||
_, errno := socketcall(sysSETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), 0) |
|||
return errnoErr(errno) |
|||
} |
|||
|
|||
func recvmsg(s uintptr, h *msghdr, flags int) (int, error) { |
|||
n, errno := socketcall(sysRECVMSG, s, uintptr(unsafe.Pointer(h)), uintptr(flags), 0, 0, 0) |
|||
return int(n), errnoErr(errno) |
|||
} |
|||
|
|||
func sendmsg(s uintptr, h *msghdr, flags int) (int, error) { |
|||
n, errno := socketcall(sysSENDMSG, s, uintptr(unsafe.Pointer(h)), uintptr(flags), 0, 0, 0) |
|||
return int(n), errnoErr(errno) |
|||
} |
|||
|
|||
func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) { |
|||
n, errno := socketcall(sysRECVMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0) |
|||
return int(n), errnoErr(errno) |
|||
} |
|||
|
|||
func sendmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) { |
|||
n, errno := socketcall(sysSENDMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0) |
|||
return int(n), errnoErr(errno) |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
// Copyright 2014 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. |
|||
|
|||
#include "textflag.h" |
|||
|
|||
TEXT ·socketcall(SB),NOSPLIT,$0-36 |
|||
JMP syscall·socketcall(SB) |
|||
|
|||
TEXT ·rawsocketcall(SB),NOSPLIT,$0-36 |
|||
JMP syscall·rawsocketcall(SB) |
|||
@ -0,0 +1,10 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
package socket |
|||
|
|||
const ( |
|||
sysRECVMMSG = 0x12b |
|||
sysSENDMMSG = 0x133 |
|||
) |
|||
@ -0,0 +1,10 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
package socket |
|||
|
|||
const ( |
|||
sysRECVMMSG = 0x16d |
|||
sysSENDMMSG = 0x176 |
|||
) |
|||
@ -0,0 +1,10 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
package socket |
|||
|
|||
const ( |
|||
sysRECVMMSG = 0xf3 |
|||
sysSENDMMSG = 0x10d |
|||
) |
|||
@ -0,0 +1,10 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
package socket |
|||
|
|||
const ( |
|||
sysRECVMMSG = 0x10ef |
|||
sysSENDMMSG = 0x10f7 |
|||
) |
|||
@ -0,0 +1,10 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
package socket |
|||
|
|||
const ( |
|||
sysRECVMMSG = 0x14ae |
|||
sysSENDMMSG = 0x14b6 |
|||
) |
|||
@ -0,0 +1,10 @@ |
|||
// Copyright 2017 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.
|
|||
|
|||
package socket |
|||
|
|||
const ( |
|||
sysRECVMMSG = 0x14ae |
|||
sysSENDMMSG = 0x14b6 |
|||
) |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue