Rebuild vendor
This commit is contained in:
parent
9a3a9fc4a0
commit
5785bb4620
|
@ -1,8 +0,0 @@
|
||||||
coverage:
|
|
||||||
status:
|
|
||||||
project:
|
|
||||||
default:
|
|
||||||
target: 40%
|
|
||||||
threshold: null
|
|
||||||
patch: false
|
|
||||||
changes: false
|
|
|
@ -1,19 +1,20 @@
|
||||||
language: go
|
language: go
|
||||||
sudo: false
|
sudo: false
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- 1.10.x
|
- 1.9.x
|
||||||
- 1.11.x
|
|
||||||
- 1.12.x
|
|
||||||
- tip
|
- tip
|
||||||
|
|
||||||
|
env:
|
||||||
|
- TESTS="-race -v -bench=. -coverprofile=coverage.txt -covermode=atomic"
|
||||||
|
- TESTS="-race -v ./..."
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
# don't use the miekg/dns when testing forks
|
# don't use the miekg/dns when testing forks
|
||||||
- mkdir -p $GOPATH/src/github.com/miekg
|
- mkdir -p $GOPATH/src/github.com/miekg
|
||||||
- ln -s $TRAVIS_BUILD_DIR $GOPATH/src/github.com/miekg/ || true
|
- ln -s $TRAVIS_BUILD_DIR $GOPATH/src/github.com/miekg/ || true
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- go test -race -v -bench=. -coverprofile=coverage.txt -covermode=atomic ./...
|
- go test $TESTS
|
||||||
|
|
||||||
after_success:
|
after_success:
|
||||||
- bash <(curl -s https://codecov.io/bash)
|
- bash <(curl -s https://codecov.io/bash)
|
||||||
|
|
|
@ -3,55 +3,13 @@
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:6914c49eed986dfb8dffb33516fa129c49929d4d873f41e073c83c11c372b870"
|
|
||||||
name = "golang.org/x/crypto"
|
name = "golang.org/x/crypto"
|
||||||
packages = [
|
packages = ["ed25519","ed25519/internal/edwards25519"]
|
||||||
"ed25519",
|
revision = "b080dc9a8c480b08e698fb1219160d598526310f"
|
||||||
"ed25519/internal/edwards25519",
|
|
||||||
]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "e3636079e1a4c1f337f212cc5cd2aca108f6c900"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:08e41d63f8dac84d83797368b56cf0b339e42d0224e5e56668963c28aec95685"
|
|
||||||
name = "golang.org/x/net"
|
|
||||||
packages = [
|
|
||||||
"bpf",
|
|
||||||
"context",
|
|
||||||
"internal/iana",
|
|
||||||
"internal/socket",
|
|
||||||
"ipv4",
|
|
||||||
"ipv6",
|
|
||||||
]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "4dfa2610cdf3b287375bbba5b8f2a14d3b01d8de"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:b2ea75de0ccb2db2ac79356407f8a4cd8f798fe15d41b381c00abf3ae8e55ed1"
|
|
||||||
name = "golang.org/x/sync"
|
|
||||||
packages = ["errgroup"]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "1d60e4601c6fd243af51cc01ddf169918a5407ca"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:149a432fabebb8221a80f77731b1cd63597197ded4f14af606ebe3a0959004ec"
|
|
||||||
name = "golang.org/x/sys"
|
|
||||||
packages = ["unix"]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "e4b3c5e9061176387e7cea65e4dc5853801f3fb7"
|
|
||||||
|
|
||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
input-imports = [
|
inputs-digest = "5046e265393bd5e54f570ce29ae8bc6fa3f30ef5110e922996540400f287c64a"
|
||||||
"golang.org/x/crypto/ed25519",
|
|
||||||
"golang.org/x/net/ipv4",
|
|
||||||
"golang.org/x/net/ipv6",
|
|
||||||
"golang.org/x/sync/errgroup",
|
|
||||||
"golang.org/x/sys/unix",
|
|
||||||
]
|
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
|
|
@ -24,15 +24,3 @@
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "golang.org/x/crypto"
|
name = "golang.org/x/crypto"
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
branch = "master"
|
|
||||||
name = "golang.org/x/net"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
branch = "master"
|
|
||||||
name = "golang.org/x/sys"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
branch = "master"
|
|
||||||
name = "golang.org/x/sync"
|
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
# 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
|
|
|
@ -7,10 +7,10 @@
|
||||||
|
|
||||||
> Less is more.
|
> Less is more.
|
||||||
|
|
||||||
Complete and usable DNS library. All Resource Records are supported, including the DNSSEC types.
|
Complete and usable DNS library. All widely used Resource Records are supported, including the
|
||||||
It follows a lean and mean philosophy. If there is stuff you should know as a DNS programmer there
|
DNSSEC types. It follows a lean and mean philosophy. If there is stuff you should know as a DNS
|
||||||
isn't a convenience function for it. Server side and client side programming is supported, i.e. you
|
programmer there isn't a convenience function for it. Server side and client side programming is
|
||||||
can build servers and resolvers with it.
|
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,
|
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.
|
avoiding breaking changes wherever reasonable. We support the last two versions of Go.
|
||||||
|
@ -42,9 +42,10 @@ A not-so-up-to-date-list-that-may-be-actually-current:
|
||||||
* https://github.com/tianon/rawdns
|
* https://github.com/tianon/rawdns
|
||||||
* https://mesosphere.github.io/mesos-dns/
|
* https://mesosphere.github.io/mesos-dns/
|
||||||
* https://pulse.turbobytes.com/
|
* https://pulse.turbobytes.com/
|
||||||
|
* https://play.google.com/store/apps/details?id=com.turbobytes.dig
|
||||||
* https://github.com/fcambus/statzone
|
* https://github.com/fcambus/statzone
|
||||||
* https://github.com/benschw/dns-clb-go
|
* https://github.com/benschw/dns-clb-go
|
||||||
* https://github.com/corny/dnscheck for <http://public-dns.info/>
|
* https://github.com/corny/dnscheck for http://public-dns.info/
|
||||||
* https://namesmith.io
|
* https://namesmith.io
|
||||||
* https://github.com/miekg/unbound
|
* https://github.com/miekg/unbound
|
||||||
* https://github.com/miekg/exdns
|
* https://github.com/miekg/exdns
|
||||||
|
@ -55,41 +56,35 @@ A not-so-up-to-date-list-that-may-be-actually-current:
|
||||||
* https://github.com/bamarni/dockness
|
* https://github.com/bamarni/dockness
|
||||||
* https://github.com/fffaraz/microdns
|
* https://github.com/fffaraz/microdns
|
||||||
* http://kelda.io
|
* http://kelda.io
|
||||||
* https://github.com/ipdcode/hades <https://jd.com>
|
* https://github.com/ipdcode/hades (JD.COM)
|
||||||
* https://github.com/StackExchange/dnscontrol/
|
* https://github.com/StackExchange/dnscontrol/
|
||||||
* https://www.dnsperf.com/
|
* https://www.dnsperf.com/
|
||||||
* https://dnssectest.net/
|
* https://dnssectest.net/
|
||||||
* https://dns.apebits.com
|
* https://dns.apebits.com
|
||||||
* https://github.com/oif/apex
|
* 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))
|
|
||||||
* https://github.com/semihalev/sdns
|
|
||||||
* https://render.com
|
|
||||||
* https://github.com/peterzen/goresolver
|
|
||||||
|
|
||||||
Send pull request if you want to be listed here.
|
Send pull request if you want to be listed here.
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
|
|
||||||
* UDP/TCP queries, IPv4 and IPv6
|
* UDP/TCP queries, IPv4 and IPv6;
|
||||||
* RFC 1035 zone file parsing ($INCLUDE, $ORIGIN, $TTL and $GENERATE (for all record types) are supported
|
* RFC 1035 zone file parsing ($INCLUDE, $ORIGIN, $TTL and $GENERATE (for all record types) are supported;
|
||||||
* Fast
|
* Fast:
|
||||||
* Server side programming (mimicking the net/http package)
|
* Reply speed around ~ 80K qps (faster hardware results in more qps);
|
||||||
* Client side programming
|
* Parsing RRs ~ 100K RR/s, that's 5M records in about 50 seconds;
|
||||||
* DNSSEC: signing, validating and key generation for DSA, RSA, ECDSA and Ed25519
|
* Server side programming (mimicking the net/http package);
|
||||||
* EDNS0, NSID, Cookies
|
* Client side programming;
|
||||||
* AXFR/IXFR
|
* DNSSEC: signing, validating and key generation for DSA, RSA, ECDSA and Ed25519;
|
||||||
* TSIG, SIG(0)
|
* EDNS0, NSID, Cookies;
|
||||||
* DNS over TLS (DoT): encrypted connection between client and server over TCP
|
* AXFR/IXFR;
|
||||||
* DNS name compression
|
* TSIG, SIG(0);
|
||||||
|
* DNS over TLS: optional encrypted connection between client and server;
|
||||||
|
* DNS name compression;
|
||||||
|
* Depends only on the standard library.
|
||||||
|
|
||||||
Have fun!
|
Have fun!
|
||||||
|
|
||||||
Miek Gieben - 2010-2012 - <miek@miek.nl>
|
Miek Gieben - 2010-2012 - <miek@miek.nl>
|
||||||
DNS Authors 2012-
|
|
||||||
|
|
||||||
# Building
|
# Building
|
||||||
|
|
||||||
|
@ -101,8 +96,8 @@ work:
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
A short "how to use the API" is at the beginning of doc.go (this also will show when you call `godoc
|
A short "how to use the API" is at the beginning of doc.go (this also will show
|
||||||
github.com/miekg/dns`).
|
when you call `godoc github.com/miekg/dns`).
|
||||||
|
|
||||||
Example programs can be found in the `github.com/miekg/exdns` repository.
|
Example programs can be found in the `github.com/miekg/exdns` repository.
|
||||||
|
|
||||||
|
@ -160,13 +155,12 @@ Example programs can be found in the `github.com/miekg/exdns` repository.
|
||||||
* 7553 - URI record
|
* 7553 - URI record
|
||||||
* 7858 - DNS over TLS: Initiation and Performance Considerations
|
* 7858 - DNS over TLS: Initiation and Performance Considerations
|
||||||
* 7871 - EDNS0 Client Subnet
|
* 7871 - EDNS0 Client Subnet
|
||||||
* 7873 - Domain Name System (DNS) Cookies
|
* 7873 - Domain Name System (DNS) Cookies (draft-ietf-dnsop-cookies)
|
||||||
* 8080 - EdDSA for DNSSEC
|
* 8080 - EdDSA for DNSSEC
|
||||||
* 8499 - DNS Terminology
|
|
||||||
|
|
||||||
## Loosely Based Upon
|
## Loosely based upon
|
||||||
|
|
||||||
* ldns - <https://nlnetlabs.nl/projects/ldns/about/>
|
* `ldns`
|
||||||
* NSD - <https://nlnetlabs.nl/projects/nsd/about/>
|
* `NSD`
|
||||||
* Net::DNS - <http://www.net-dns.org/>
|
* `Net::DNS`
|
||||||
* GRONG - <https://github.com/bortzmeyer/grong>
|
* `GRONG`
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
// MsgAcceptFunc is used early in the server code to accept or reject a message with RcodeFormatError.
|
|
||||||
// It returns a MsgAcceptAction to indicate what should happen with the message.
|
|
||||||
type MsgAcceptFunc func(dh Header) MsgAcceptAction
|
|
||||||
|
|
||||||
// DefaultMsgAcceptFunc checks the request and will reject if:
|
|
||||||
//
|
|
||||||
// * isn't a request (don't respond in that case).
|
|
||||||
// * opcode isn't OpcodeQuery or OpcodeNotify
|
|
||||||
// * Zero bit isn't zero
|
|
||||||
// * has more than 1 question in the question section
|
|
||||||
// * has more than 1 RR in the Answer section
|
|
||||||
// * has more than 0 RRs in the Authority section
|
|
||||||
// * has more than 2 RRs in the Additional section
|
|
||||||
var DefaultMsgAcceptFunc MsgAcceptFunc = defaultMsgAcceptFunc
|
|
||||||
|
|
||||||
// MsgAcceptAction represents the action to be taken.
|
|
||||||
type MsgAcceptAction int
|
|
||||||
|
|
||||||
const (
|
|
||||||
MsgAccept MsgAcceptAction = iota // Accept the message
|
|
||||||
MsgReject // Reject the message with a RcodeFormatError
|
|
||||||
MsgIgnore // Ignore the error and send nothing back.
|
|
||||||
)
|
|
||||||
|
|
||||||
func defaultMsgAcceptFunc(dh Header) MsgAcceptAction {
|
|
||||||
if isResponse := dh.Bits&_QR != 0; isResponse {
|
|
||||||
return MsgIgnore
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't allow dynamic updates, because then the sections can contain a whole bunch of RRs.
|
|
||||||
opcode := int(dh.Bits>>11) & 0xF
|
|
||||||
if opcode != OpcodeQuery && opcode != OpcodeNotify {
|
|
||||||
return MsgReject
|
|
||||||
}
|
|
||||||
|
|
||||||
if isZero := dh.Bits&_Z != 0; isZero {
|
|
||||||
return MsgReject
|
|
||||||
}
|
|
||||||
if dh.Qdcount != 1 {
|
|
||||||
return MsgReject
|
|
||||||
}
|
|
||||||
// NOTIFY requests can have a SOA in the ANSWER section. See RFC 1996 Section 3.7 and 3.11.
|
|
||||||
if dh.Ancount > 1 {
|
|
||||||
return MsgReject
|
|
||||||
}
|
|
||||||
// IXFR request could have one SOA RR in the NS section. See RFC 1995, section 3.
|
|
||||||
if dh.Nscount > 1 {
|
|
||||||
return MsgReject
|
|
||||||
}
|
|
||||||
if dh.Arcount > 2 {
|
|
||||||
return MsgReject
|
|
||||||
}
|
|
||||||
return MsgAccept
|
|
||||||
}
|
|
|
@ -3,26 +3,26 @@ package dns
|
||||||
// A client implementation.
|
// A client implementation.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const dnsTimeout time.Duration = 2 * time.Second
|
||||||
dnsTimeout time.Duration = 2 * time.Second
|
const tcpIdleTimeout time.Duration = 8 * time.Second
|
||||||
tcpIdleTimeout time.Duration = 8 * time.Second
|
|
||||||
)
|
|
||||||
|
|
||||||
// A Conn represents a connection to a DNS server.
|
// A Conn represents a connection to a DNS server.
|
||||||
type Conn struct {
|
type Conn struct {
|
||||||
net.Conn // a net.Conn holding the connection
|
net.Conn // a net.Conn holding the connection
|
||||||
UDPSize uint16 // minimum receive buffer for UDP messages
|
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)
|
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)
|
||||||
|
rtt time.Duration
|
||||||
|
t time.Time
|
||||||
tsigRequestMAC string
|
tsigRequestMAC string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,22 +83,33 @@ func (c *Client) Dial(address string) (conn *Conn, err error) {
|
||||||
// create a new dialer with the appropriate timeout
|
// create a new dialer with the appropriate timeout
|
||||||
var d net.Dialer
|
var d net.Dialer
|
||||||
if c.Dialer == nil {
|
if c.Dialer == nil {
|
||||||
d = net.Dialer{Timeout: c.getTimeoutForRequest(c.dialTimeout())}
|
d = net.Dialer{}
|
||||||
} else {
|
} else {
|
||||||
d = *c.Dialer
|
d = net.Dialer(*c.Dialer)
|
||||||
}
|
}
|
||||||
|
d.Timeout = c.getTimeoutForRequest(c.writeTimeout())
|
||||||
|
|
||||||
network := c.Net
|
network := "udp"
|
||||||
if network == "" {
|
useTLS := false
|
||||||
network = "udp"
|
|
||||||
|
switch c.Net {
|
||||||
|
case "tcp-tls":
|
||||||
|
network = "tcp"
|
||||||
|
useTLS = true
|
||||||
|
case "tcp4-tls":
|
||||||
|
network = "tcp4"
|
||||||
|
useTLS = true
|
||||||
|
case "tcp6-tls":
|
||||||
|
network = "tcp6"
|
||||||
|
useTLS = true
|
||||||
|
default:
|
||||||
|
if c.Net != "" {
|
||||||
|
network = c.Net
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
useTLS := strings.HasPrefix(network, "tcp") && strings.HasSuffix(network, "-tls")
|
|
||||||
|
|
||||||
conn = new(Conn)
|
conn = new(Conn)
|
||||||
if useTLS {
|
if useTLS {
|
||||||
network = strings.TrimSuffix(network, "-tls")
|
|
||||||
|
|
||||||
conn.Conn, err = tls.DialWithDialer(&d, network, address, c.TLSConfig)
|
conn.Conn, err = tls.DialWithDialer(&d, network, address, c.TLSConfig)
|
||||||
} else {
|
} else {
|
||||||
conn.Conn, err = d.Dial(network, address)
|
conn.Conn, err = d.Dial(network, address)
|
||||||
|
@ -106,7 +117,6 @@ func (c *Client) Dial(address string) (conn *Conn, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,15 +139,20 @@ func (c *Client) Exchange(m *Msg, address string) (r *Msg, rtt time.Duration, er
|
||||||
return c.exchange(m, address)
|
return c.exchange(m, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
q := m.Question[0]
|
t := "nop"
|
||||||
key := fmt.Sprintf("%s:%d:%d", q.Name, q.Qtype, q.Qclass)
|
if t1, ok := TypeToString[m.Question[0].Qtype]; ok {
|
||||||
r, rtt, err, shared := c.group.Do(key, func() (*Msg, time.Duration, error) {
|
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) {
|
||||||
return c.exchange(m, address)
|
return c.exchange(m, address)
|
||||||
})
|
})
|
||||||
if r != nil && shared {
|
if r != nil && shared {
|
||||||
r = r.Copy()
|
r = r.Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
return r, rtt, err
|
return r, rtt, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,9 +177,8 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro
|
||||||
}
|
}
|
||||||
|
|
||||||
co.TsigSecret = c.TsigSecret
|
co.TsigSecret = c.TsigSecret
|
||||||
t := time.Now()
|
|
||||||
// write with the appropriate write timeout
|
// write with the appropriate write timeout
|
||||||
co.SetWriteDeadline(t.Add(c.getTimeoutForRequest(c.writeTimeout())))
|
co.SetWriteDeadline(time.Now().Add(c.getTimeoutForRequest(c.writeTimeout())))
|
||||||
if err = co.WriteMsg(m); err != nil {
|
if err = co.WriteMsg(m); err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
@ -174,15 +188,12 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro
|
||||||
if err == nil && r.Id != m.Id {
|
if err == nil && r.Id != m.Id {
|
||||||
err = ErrId
|
err = ErrId
|
||||||
}
|
}
|
||||||
rtt = time.Since(t)
|
return r, co.rtt, err
|
||||||
return r, rtt, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadMsg reads a message from the connection co.
|
// ReadMsg reads a message from the connection co.
|
||||||
// If the received message contains a TSIG record the transaction signature
|
// If the received message contains a TSIG record the transaction
|
||||||
// is verified. This method always tries to return the message, however if an
|
// signature is verified.
|
||||||
// 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) {
|
func (co *Conn) ReadMsg() (*Msg, error) {
|
||||||
p, err := co.ReadMsgHeader(nil)
|
p, err := co.ReadMsgHeader(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -191,11 +202,14 @@ func (co *Conn) ReadMsg() (*Msg, error) {
|
||||||
|
|
||||||
m := new(Msg)
|
m := new(Msg)
|
||||||
if err := m.Unpack(p); err != nil {
|
if err := m.Unpack(p); err != nil {
|
||||||
// If an error was returned, we still want to allow the user to use
|
// If ErrTruncated 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
|
// the message, but naively they can just check err if they don't want
|
||||||
// to use an erroneous message
|
// to use a truncated message
|
||||||
|
if err == ErrTruncated {
|
||||||
return m, err
|
return m, err
|
||||||
}
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
if t := m.IsTsig(); t != nil {
|
if t := m.IsTsig(); t != nil {
|
||||||
if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
|
if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
|
||||||
return m, ErrSecret
|
return m, ErrSecret
|
||||||
|
@ -215,15 +229,19 @@ func (co *Conn) ReadMsgHeader(hdr *Header) ([]byte, error) {
|
||||||
n int
|
n int
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
switch co.Conn.(type) {
|
|
||||||
|
switch t := co.Conn.(type) {
|
||||||
case *net.TCPConn, *tls.Conn:
|
case *net.TCPConn, *tls.Conn:
|
||||||
var length uint16
|
r := t.(io.Reader)
|
||||||
if err := binary.Read(co.Conn, binary.BigEndian, &length); err != nil {
|
|
||||||
|
// First two bytes specify the length of the entire message.
|
||||||
|
l, err := tcpMsgLen(r)
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
p = make([]byte, l)
|
||||||
p = make([]byte, length)
|
n, err = tcpRead(r, p)
|
||||||
n, err = io.ReadFull(co.Conn, p)
|
co.rtt = time.Since(co.t)
|
||||||
default:
|
default:
|
||||||
if co.UDPSize > MinMsgSize {
|
if co.UDPSize > MinMsgSize {
|
||||||
p = make([]byte, co.UDPSize)
|
p = make([]byte, co.UDPSize)
|
||||||
|
@ -231,6 +249,7 @@ func (co *Conn) ReadMsgHeader(hdr *Header) ([]byte, error) {
|
||||||
p = make([]byte, MinMsgSize)
|
p = make([]byte, MinMsgSize)
|
||||||
}
|
}
|
||||||
n, err = co.Read(p)
|
n, err = co.Read(p)
|
||||||
|
co.rtt = time.Since(co.t)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -250,27 +269,78 @@ func (co *Conn) ReadMsgHeader(hdr *Header) ([]byte, error) {
|
||||||
return p, err
|
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.
|
// Read implements the net.Conn read method.
|
||||||
func (co *Conn) Read(p []byte) (n int, err error) {
|
func (co *Conn) Read(p []byte) (n int, err error) {
|
||||||
if co.Conn == nil {
|
if co.Conn == nil {
|
||||||
return 0, ErrConnEmpty
|
return 0, ErrConnEmpty
|
||||||
}
|
}
|
||||||
|
if len(p) < 2 {
|
||||||
switch co.Conn.(type) {
|
|
||||||
case *net.TCPConn, *tls.Conn:
|
|
||||||
var length uint16
|
|
||||||
if err := binary.Read(co.Conn, binary.BigEndian, &length); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
if int(length) > len(p) {
|
|
||||||
return 0, io.ErrShortBuffer
|
return 0, io.ErrShortBuffer
|
||||||
}
|
}
|
||||||
|
switch t := co.Conn.(type) {
|
||||||
|
case *net.TCPConn, *tls.Conn:
|
||||||
|
r := t.(io.Reader)
|
||||||
|
|
||||||
return io.ReadFull(co.Conn, p[:length])
|
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
|
// UDP connection
|
||||||
return co.Conn.Read(p)
|
n, err = co.Conn.Read(p)
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteMsg sends a message through the connection co.
|
// WriteMsg sends a message through the connection co.
|
||||||
|
@ -292,26 +362,34 @@ func (co *Conn) WriteMsg(m *Msg) (err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = co.Write(out)
|
co.t = time.Now()
|
||||||
|
if _, err = co.Write(out); err != nil {
|
||||||
return err
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write implements the net.Conn Write method.
|
// Write implements the net.Conn Write method.
|
||||||
func (co *Conn) Write(p []byte) (n int, err error) {
|
func (co *Conn) Write(p []byte) (n int, err error) {
|
||||||
switch co.Conn.(type) {
|
switch t := co.Conn.(type) {
|
||||||
case *net.TCPConn, *tls.Conn:
|
case *net.TCPConn, *tls.Conn:
|
||||||
if len(p) > MaxMsgSize {
|
w := t.(io.Writer)
|
||||||
|
|
||||||
|
lp := len(p)
|
||||||
|
if lp < 2 {
|
||||||
|
return 0, io.ErrShortBuffer
|
||||||
|
}
|
||||||
|
if lp > MaxMsgSize {
|
||||||
return 0, &Error{err: "message too large"}
|
return 0, &Error{err: "message too large"}
|
||||||
}
|
}
|
||||||
|
l := make([]byte, 2, lp+2)
|
||||||
l := make([]byte, 2)
|
binary.BigEndian.PutUint16(l, uint16(lp))
|
||||||
binary.BigEndian.PutUint16(l, uint16(len(p)))
|
p = append(l, p...)
|
||||||
|
n, err := io.Copy(w, bytes.NewReader(p))
|
||||||
n, err := (&net.Buffers{l, p}).WriteTo(co.Conn)
|
|
||||||
return int(n), err
|
return int(n), err
|
||||||
}
|
}
|
||||||
|
n, err = co.Conn.Write(p)
|
||||||
return co.Conn.Write(p)
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the appropriate timeout for a specific request
|
// Return the appropriate timeout for a specific request
|
||||||
|
@ -354,7 +432,7 @@ func ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, err error)
|
||||||
|
|
||||||
// ExchangeConn performs a synchronous query. It sends the message m via the connection
|
// 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.
|
// c and waits for a reply. The connection c is not closed by ExchangeConn.
|
||||||
// Deprecated: This function is going away, but can easily be mimicked:
|
// This function is going away, but can easily be mimicked:
|
||||||
//
|
//
|
||||||
// co := &dns.Conn{Conn: c} // c is your net.Conn
|
// co := &dns.Conn{Conn: c} // c is your net.Conn
|
||||||
// co.WriteMsg(m)
|
// co.WriteMsg(m)
|
||||||
|
@ -378,7 +456,11 @@ func ExchangeConn(c net.Conn, m *Msg) (r *Msg, err error) {
|
||||||
// DialTimeout acts like Dial but takes a timeout.
|
// DialTimeout acts like Dial but takes a timeout.
|
||||||
func DialTimeout(network, address string, timeout time.Duration) (conn *Conn, err error) {
|
func DialTimeout(network, address string, timeout time.Duration) (conn *Conn, err error) {
|
||||||
client := Client{Net: network, Dialer: &net.Dialer{Timeout: timeout}}
|
client := Client{Net: network, Dialer: &net.Dialer{Timeout: timeout}}
|
||||||
return client.Dial(address)
|
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.
|
// DialWithTLS connects to the address on the named network with TLS.
|
||||||
|
@ -387,7 +469,12 @@ func DialWithTLS(network, address string, tlsConfig *tls.Config) (conn *Conn, er
|
||||||
network += "-tls"
|
network += "-tls"
|
||||||
}
|
}
|
||||||
client := Client{Net: network, TLSConfig: tlsConfig}
|
client := Client{Net: network, TLSConfig: tlsConfig}
|
||||||
return client.Dial(address)
|
conn, err = client.Dial(address)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialTimeoutWithTLS acts like DialWithTLS but takes a timeout.
|
// DialTimeoutWithTLS acts like DialWithTLS but takes a timeout.
|
||||||
|
@ -396,7 +483,11 @@ func DialTimeoutWithTLS(network, address string, tlsConfig *tls.Config, timeout
|
||||||
network += "-tls"
|
network += "-tls"
|
||||||
}
|
}
|
||||||
client := Client{Net: network, Dialer: &net.Dialer{Timeout: timeout}, TLSConfig: tlsConfig}
|
client := Client{Net: network, Dialer: &net.Dialer{Timeout: timeout}, TLSConfig: tlsConfig}
|
||||||
return client.Dial(address)
|
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
|
// ExchangeContext acts like Exchange, but honors the deadline on the provided
|
||||||
|
@ -407,11 +498,10 @@ func (c *Client) ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg,
|
||||||
if deadline, ok := ctx.Deadline(); !ok {
|
if deadline, ok := ctx.Deadline(); !ok {
|
||||||
timeout = 0
|
timeout = 0
|
||||||
} else {
|
} else {
|
||||||
timeout = time.Until(deadline)
|
timeout = deadline.Sub(time.Now())
|
||||||
}
|
}
|
||||||
// not passing the context to the underlying calls, as the API does not support
|
// 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.
|
// context. For timeouts you should set up Client.Dialer and call Client.Exchange.
|
||||||
// TODO(tmthrgd,miekg): this is a race condition.
|
|
||||||
c.Dialer = &net.Dialer{Timeout: timeout}
|
c.Dialer = &net.Dialer{Timeout: timeout}
|
||||||
return c.Exchange(m, a)
|
return c.Exchange(m, a)
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,10 +68,14 @@ func ClientConfigFromReader(resolvconf io.Reader) (*ClientConfig, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case "search": // set search path to given servers
|
case "search": // set search path to given servers
|
||||||
c.Search = append([]string(nil), f[1:]...)
|
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
|
case "options": // magic options
|
||||||
for _, s := range f[1:] {
|
for i := 1; i < len(f); i++ {
|
||||||
|
s := f[i]
|
||||||
switch {
|
switch {
|
||||||
case len(s) >= 6 && s[:6] == "ndots:":
|
case len(s) >= 6 && s[:6] == "ndots:":
|
||||||
n, _ := strconv.Atoi(s[6:])
|
n, _ := strconv.Atoi(s[6:])
|
||||||
|
@ -87,7 +91,7 @@ func ClientConfigFromReader(resolvconf io.Reader) (*ClientConfig, error) {
|
||||||
n = 1
|
n = 1
|
||||||
}
|
}
|
||||||
c.Timeout = n
|
c.Timeout = n
|
||||||
case len(s) >= 9 && s[:9] == "attempts:":
|
case len(s) >= 8 && s[:9] == "attempts:":
|
||||||
n, _ := strconv.Atoi(s[9:])
|
n, _ := strconv.Atoi(s[9:])
|
||||||
if n < 1 {
|
if n < 1 {
|
||||||
n = 1
|
n = 1
|
||||||
|
|
|
@ -0,0 +1,189 @@
|
||||||
|
//+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 = `
|
||||||
|
// *** DO NOT MODIFY ***
|
||||||
|
// AUTOGENERATED BY go generate from compress_generate.go
|
||||||
|
|
||||||
|
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) {\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, "compressionLenHelper(c, x.%s)\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 {
|
||||||
|
compressionLenHelper(c, x.%s[i])
|
||||||
|
}
|
||||||
|
`, 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, "}\n}\n\n")
|
||||||
|
|
||||||
|
// compressionLenSearchType - search cdomain-tags types for compressible names.
|
||||||
|
|
||||||
|
fmt.Fprint(b, "func compressionLenSearchType(c map[string]int, r RR) (int, bool) {\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 := compressionLenSearch(c, x.%s)\n", 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"
|
||||||
|
for i := 2; i < j; i++ {
|
||||||
|
k += fmt.Sprintf(" + k%d", i)
|
||||||
|
ok += fmt.Sprintf(" && ok%d", i)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(b, "return %s, %s\n", k, ok)
|
||||||
|
}
|
||||||
|
fmt.Fprintln(b, "}\nreturn 0, false\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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const hexDigit = "0123456789abcdef"
|
const hexDigit = "0123456789abcdef"
|
||||||
|
@ -146,9 +145,10 @@ func (dns *Msg) IsTsig() *TSIG {
|
||||||
// record in the additional section will do. It returns the OPT record
|
// record in the additional section will do. It returns the OPT record
|
||||||
// found or nil.
|
// found or nil.
|
||||||
func (dns *Msg) IsEdns0() *OPT {
|
func (dns *Msg) IsEdns0() *OPT {
|
||||||
// RFC 6891, Section 6.1.1 allows the OPT record to appear
|
// EDNS0 is at the end of the additional section, start there.
|
||||||
// anywhere in the additional record section, but it's usually at
|
// We might want to change this to *only* look at the last two
|
||||||
// the end so start there.
|
// records. So we see TSIG and/or OPT - this a slightly bigger
|
||||||
|
// change though.
|
||||||
for i := len(dns.Extra) - 1; i >= 0; i-- {
|
for i := len(dns.Extra) - 1; i >= 0; i-- {
|
||||||
if dns.Extra[i].Header().Rrtype == TypeOPT {
|
if dns.Extra[i].Header().Rrtype == TypeOPT {
|
||||||
return dns.Extra[i].(*OPT)
|
return dns.Extra[i].(*OPT)
|
||||||
|
@ -157,93 +157,17 @@ func (dns *Msg) IsEdns0() *OPT {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// popEdns0 is like IsEdns0, but it removes the record from the message.
|
|
||||||
func (dns *Msg) popEdns0() *OPT {
|
|
||||||
// RFC 6891, Section 6.1.1 allows the OPT record to appear
|
|
||||||
// anywhere in the additional record section, but it's usually at
|
|
||||||
// the end so start there.
|
|
||||||
for i := len(dns.Extra) - 1; i >= 0; i-- {
|
|
||||||
if dns.Extra[i].Header().Rrtype == TypeOPT {
|
|
||||||
opt := dns.Extra[i].(*OPT)
|
|
||||||
dns.Extra = append(dns.Extra[:i], dns.Extra[i+1:]...)
|
|
||||||
return opt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsDomainName checks if s is a valid domain name, it returns the number of
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// string is a valid domain name as the DNS is 8 bit protocol. It checks if each
|
||||||
// label fits in 63 characters and that the entire name will fit into the 255
|
// label fits in 63 characters, but there is no length check for the entire
|
||||||
// octet wire format limit.
|
// string s. I.e. a domain name longer than 255 characters is considered valid.
|
||||||
func IsDomainName(s string) (labels int, ok bool) {
|
func IsDomainName(s string) (labels int, ok bool) {
|
||||||
// XXX: The logic in this function was copied from packDomainName and
|
_, labels, err := packDomainName(s, nil, 0, nil, false)
|
||||||
// should be kept in sync with that function.
|
return labels, err == nil
|
||||||
|
|
||||||
const lenmsg = 256
|
|
||||||
|
|
||||||
if len(s) == 0 { // Ok, for instance when dealing with update RR without any rdata.
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
s = Fqdn(s)
|
|
||||||
|
|
||||||
// Each dot ends a segment of the name. Except for escaped dots (\.), which
|
|
||||||
// are normal dots.
|
|
||||||
|
|
||||||
var (
|
|
||||||
off int
|
|
||||||
begin int
|
|
||||||
wasDot bool
|
|
||||||
)
|
|
||||||
for i := 0; i < len(s); i++ {
|
|
||||||
switch s[i] {
|
|
||||||
case '\\':
|
|
||||||
if off+1 > lenmsg {
|
|
||||||
return labels, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for \DDD
|
|
||||||
if i+3 < len(s) && isDigit(s[i+1]) && isDigit(s[i+2]) && isDigit(s[i+3]) {
|
|
||||||
i += 3
|
|
||||||
begin += 3
|
|
||||||
} else {
|
|
||||||
i++
|
|
||||||
begin++
|
|
||||||
}
|
|
||||||
|
|
||||||
wasDot = false
|
|
||||||
case '.':
|
|
||||||
if wasDot {
|
|
||||||
// two dots back to back is not legal
|
|
||||||
return labels, false
|
|
||||||
}
|
|
||||||
wasDot = true
|
|
||||||
|
|
||||||
labelLen := i - begin
|
|
||||||
if labelLen >= 1<<6 { // top two bits of length must be clear
|
|
||||||
return labels, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// off can already (we're in a loop) be bigger than lenmsg
|
|
||||||
// this happens when a name isn't fully qualified
|
|
||||||
off += 1 + labelLen
|
|
||||||
if off > lenmsg {
|
|
||||||
return labels, false
|
|
||||||
}
|
|
||||||
|
|
||||||
labels++
|
|
||||||
begin = i + 1
|
|
||||||
default:
|
|
||||||
wasDot = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return labels, true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsSubDomain checks if child is indeed a child of the parent. If child and parent
|
// IsSubDomain checks if child is indeed a child of the parent. If child and parent
|
||||||
|
@ -257,7 +181,7 @@ func IsSubDomain(parent, child string) bool {
|
||||||
// The checking is performed on the binary payload.
|
// The checking is performed on the binary payload.
|
||||||
func IsMsg(buf []byte) error {
|
func IsMsg(buf []byte) error {
|
||||||
// Header
|
// Header
|
||||||
if len(buf) < headerSize {
|
if len(buf) < 12 {
|
||||||
return errors.New("dns: bad message header")
|
return errors.New("dns: bad message header")
|
||||||
}
|
}
|
||||||
// Header: Opcode
|
// Header: Opcode
|
||||||
|
@ -267,18 +191,11 @@ func IsMsg(buf []byte) error {
|
||||||
|
|
||||||
// IsFqdn checks if a domain name is fully qualified.
|
// IsFqdn checks if a domain name is fully qualified.
|
||||||
func IsFqdn(s string) bool {
|
func IsFqdn(s string) bool {
|
||||||
s2 := strings.TrimSuffix(s, ".")
|
l := len(s)
|
||||||
if s == s2 {
|
if l == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
return s[l-1] == '.'
|
||||||
i := strings.LastIndexFunc(s2, func(r rune) bool {
|
|
||||||
return r != '\\'
|
|
||||||
})
|
|
||||||
|
|
||||||
// Test whether we have an even number of escape sequences before
|
|
||||||
// the dot or none.
|
|
||||||
return (len(s2)-i)%2 != 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsRRset checks if a set of RRs is a valid RRset as defined by RFC 2181.
|
// IsRRset checks if a set of RRs is a valid RRset as defined by RFC 2181.
|
||||||
|
@ -327,19 +244,12 @@ func ReverseAddr(addr string) (arpa string, err error) {
|
||||||
if ip == nil {
|
if ip == nil {
|
||||||
return "", &Error{err: "unrecognized address: " + addr}
|
return "", &Error{err: "unrecognized address: " + addr}
|
||||||
}
|
}
|
||||||
if v4 := ip.To4(); v4 != nil {
|
if ip.To4() != nil {
|
||||||
buf := make([]byte, 0, net.IPv4len*4+len("in-addr.arpa."))
|
return strconv.Itoa(int(ip[15])) + "." + strconv.Itoa(int(ip[14])) + "." + strconv.Itoa(int(ip[13])) + "." +
|
||||||
// Add it, in reverse, to the buffer
|
strconv.Itoa(int(ip[12])) + ".in-addr.arpa.", nil
|
||||||
for i := len(v4) - 1; i >= 0; i-- {
|
|
||||||
buf = strconv.AppendInt(buf, int64(v4[i]), 10)
|
|
||||||
buf = append(buf, '.')
|
|
||||||
}
|
|
||||||
// Append "in-addr.arpa." and return (buf already has the final .)
|
|
||||||
buf = append(buf, "in-addr.arpa."...)
|
|
||||||
return string(buf), nil
|
|
||||||
}
|
}
|
||||||
// Must be IPv6
|
// Must be IPv6
|
||||||
buf := make([]byte, 0, net.IPv6len*4+len("ip6.arpa."))
|
buf := make([]byte, 0, len(ip)*4+len("ip6.arpa."))
|
||||||
// Add it, in reverse, to the buffer
|
// Add it, in reverse, to the buffer
|
||||||
for i := len(ip) - 1; i >= 0; i-- {
|
for i := len(ip) - 1; i >= 0; i-- {
|
||||||
v := ip[i]
|
v := ip[i]
|
||||||
|
@ -363,11 +273,8 @@ func (t Type) String() string {
|
||||||
|
|
||||||
// String returns the string representation for the class c.
|
// String returns the string representation for the class c.
|
||||||
func (c Class) String() string {
|
func (c Class) String() string {
|
||||||
if s, ok := ClassToString[uint16(c)]; ok {
|
if c1, ok := ClassToString[uint16(c)]; ok {
|
||||||
// Only emit mnemonics when they are unambiguous, specically ANY is in both.
|
return c1
|
||||||
if _, ok := StringToType[s]; !ok {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return "CLASS" + strconv.Itoa(int(c))
|
return "CLASS" + strconv.Itoa(int(c))
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,30 +34,10 @@ type RR interface {
|
||||||
|
|
||||||
// copy returns a copy of the RR
|
// copy returns a copy of the RR
|
||||||
copy() RR
|
copy() RR
|
||||||
|
// len returns the length (in octets) of the uncompressed RR in wire format.
|
||||||
// len returns the length (in octets) of the compressed or uncompressed RR in wire format.
|
len() int
|
||||||
//
|
// pack packs an RR into wire format.
|
||||||
// If compression is nil, the uncompressed size will be returned, otherwise the compressed
|
pack([]byte, int, map[string]int, bool) (int, error)
|
||||||
// size will be returned and domain names will be added to the map for future compression.
|
|
||||||
len(off int, compression map[string]struct{}) int
|
|
||||||
|
|
||||||
// pack packs the records RDATA into wire format. The header will
|
|
||||||
// already have been packed into msg.
|
|
||||||
pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error)
|
|
||||||
|
|
||||||
// unpack unpacks an RR from wire format.
|
|
||||||
//
|
|
||||||
// This will only be called on a new and empty RR type with only the header populated. It
|
|
||||||
// will only be called if the record's RDATA is non-empty.
|
|
||||||
unpack(msg []byte, off int) (off1 int, err error)
|
|
||||||
|
|
||||||
// parse parses an RR from zone file format.
|
|
||||||
//
|
|
||||||
// This will only be called on a new and empty RR type with only the header populated.
|
|
||||||
parse(c *zlexer, origin, file string) *ParseError
|
|
||||||
|
|
||||||
// isDuplicate returns whether the two RRs are duplicates.
|
|
||||||
isDuplicate(r2 RR) bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RR_Header is the header all DNS resource records share.
|
// RR_Header is the header all DNS resource records share.
|
||||||
|
@ -75,6 +55,16 @@ func (h *RR_Header) Header() *RR_Header { return h }
|
||||||
// Just to implement the RR interface.
|
// Just to implement the RR interface.
|
||||||
func (h *RR_Header) copy() RR { return nil }
|
func (h *RR_Header) copy() RR { return nil }
|
||||||
|
|
||||||
|
func (h *RR_Header) copyHeader() *RR_Header {
|
||||||
|
r := new(RR_Header)
|
||||||
|
r.Name = h.Name
|
||||||
|
r.Rrtype = h.Rrtype
|
||||||
|
r.Class = h.Class
|
||||||
|
r.Ttl = h.Ttl
|
||||||
|
r.Rdlength = h.Rdlength
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
func (h *RR_Header) String() string {
|
func (h *RR_Header) String() string {
|
||||||
var s string
|
var s string
|
||||||
|
|
||||||
|
@ -90,45 +80,28 @@ func (h *RR_Header) String() string {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *RR_Header) len(off int, compression map[string]struct{}) int {
|
func (h *RR_Header) len() int {
|
||||||
l := domainNameLen(h.Name, off, compression, true)
|
l := len(h.Name) + 1
|
||||||
l += 10 // rrtype(2) + class(2) + ttl(4) + rdlength(2)
|
l += 10 // rrtype(2) + class(2) + ttl(4) + rdlength(2)
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *RR_Header) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
|
|
||||||
// RR_Header has no RDATA to pack.
|
|
||||||
return off, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *RR_Header) unpack(msg []byte, off int) (int, error) {
|
|
||||||
panic("dns: internal error: unpack should never be called on RR_Header")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *RR_Header) parse(c *zlexer, origin, file string) *ParseError {
|
|
||||||
panic("dns: internal error: parse should never be called on RR_Header")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToRFC3597 converts a known RR to the unknown RR representation from RFC 3597.
|
// ToRFC3597 converts a known RR to the unknown RR representation from RFC 3597.
|
||||||
func (rr *RFC3597) ToRFC3597(r RR) error {
|
func (rr *RFC3597) ToRFC3597(r RR) error {
|
||||||
buf := make([]byte, Len(r)*2)
|
buf := make([]byte, r.len()*2)
|
||||||
headerEnd, off, err := packRR(r, buf, 0, compressionMap{}, false)
|
off, err := PackRR(r, buf, 0, nil, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
buf = buf[:off]
|
buf = buf[:off]
|
||||||
|
if int(r.Header().Rdlength) > off {
|
||||||
*rr = RFC3597{Hdr: *r.Header()}
|
return ErrBuf
|
||||||
rr.Hdr.Rdlength = uint16(off - headerEnd)
|
|
||||||
|
|
||||||
if noRdata(rr.Hdr) {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = rr.unpack(buf, headerEnd)
|
rfc3597, _, err := unpackRFC3597(*r.Header(), buf, off-int(r.Header().Rdlength))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
*rr = *rfc3597.(*RFC3597)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,10 +67,12 @@ var AlgorithmToString = map[uint8]string{
|
||||||
PRIVATEOID: "PRIVATEOID",
|
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.
|
// AlgorithmToHash is a map of algorithm crypto hash IDs to crypto.Hash's.
|
||||||
var AlgorithmToHash = map[uint8]crypto.Hash{
|
var AlgorithmToHash = map[uint8]crypto.Hash{
|
||||||
RSAMD5: crypto.MD5, // Deprecated in RFC 6725
|
RSAMD5: crypto.MD5, // Deprecated in RFC 6725
|
||||||
DSA: crypto.SHA1,
|
|
||||||
RSASHA1: crypto.SHA1,
|
RSASHA1: crypto.SHA1,
|
||||||
RSASHA1NSEC3SHA1: crypto.SHA1,
|
RSASHA1NSEC3SHA1: crypto.SHA1,
|
||||||
RSASHA256: crypto.SHA256,
|
RSASHA256: crypto.SHA256,
|
||||||
|
@ -99,6 +101,9 @@ var HashToString = map[uint8]string{
|
||||||
SHA512: "SHA512",
|
SHA512: "SHA512",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StringToHash is a map of names to hash IDs.
|
||||||
|
var StringToHash = reverseInt8(HashToString)
|
||||||
|
|
||||||
// DNSKEY flag values.
|
// DNSKEY flag values.
|
||||||
const (
|
const (
|
||||||
SEP = 1
|
SEP = 1
|
||||||
|
@ -167,7 +172,7 @@ func (k *DNSKEY) KeyTag() uint16 {
|
||||||
keytag += int(v) << 8
|
keytag += int(v) << 8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
keytag += keytag >> 16 & 0xFFFF
|
keytag += (keytag >> 16) & 0xFFFF
|
||||||
keytag &= 0xFFFF
|
keytag &= 0xFFFF
|
||||||
}
|
}
|
||||||
return uint16(keytag)
|
return uint16(keytag)
|
||||||
|
@ -234,7 +239,7 @@ func (k *DNSKEY) ToDS(h uint8) *DS {
|
||||||
// ToCDNSKEY converts a DNSKEY record to a CDNSKEY record.
|
// ToCDNSKEY converts a DNSKEY record to a CDNSKEY record.
|
||||||
func (k *DNSKEY) ToCDNSKEY() *CDNSKEY {
|
func (k *DNSKEY) ToCDNSKEY() *CDNSKEY {
|
||||||
c := &CDNSKEY{DNSKEY: *k}
|
c := &CDNSKEY{DNSKEY: *k}
|
||||||
c.Hdr = k.Hdr
|
c.Hdr = *k.Hdr.copyHeader()
|
||||||
c.Hdr.Rrtype = TypeCDNSKEY
|
c.Hdr.Rrtype = TypeCDNSKEY
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
@ -242,7 +247,7 @@ func (k *DNSKEY) ToCDNSKEY() *CDNSKEY {
|
||||||
// ToCDS converts a DS record to a CDS record.
|
// ToCDS converts a DS record to a CDS record.
|
||||||
func (d *DS) ToCDS() *CDS {
|
func (d *DS) ToCDS() *CDS {
|
||||||
c := &CDS{DS: *d}
|
c := &CDS{DS: *d}
|
||||||
c.Hdr = d.Hdr
|
c.Hdr = *d.Hdr.copyHeader()
|
||||||
c.Hdr.Rrtype = TypeCDS
|
c.Hdr.Rrtype = TypeCDS
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
@ -262,17 +267,16 @@ func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error {
|
||||||
return ErrKey
|
return ErrKey
|
||||||
}
|
}
|
||||||
|
|
||||||
h0 := rrset[0].Header()
|
|
||||||
rr.Hdr.Rrtype = TypeRRSIG
|
rr.Hdr.Rrtype = TypeRRSIG
|
||||||
rr.Hdr.Name = h0.Name
|
rr.Hdr.Name = rrset[0].Header().Name
|
||||||
rr.Hdr.Class = h0.Class
|
rr.Hdr.Class = rrset[0].Header().Class
|
||||||
if rr.OrigTtl == 0 { // If set don't override
|
if rr.OrigTtl == 0 { // If set don't override
|
||||||
rr.OrigTtl = h0.Ttl
|
rr.OrigTtl = rrset[0].Header().Ttl
|
||||||
}
|
}
|
||||||
rr.TypeCovered = h0.Rrtype
|
rr.TypeCovered = rrset[0].Header().Rrtype
|
||||||
rr.Labels = uint8(CountLabel(h0.Name))
|
rr.Labels = uint8(CountLabel(rrset[0].Header().Name))
|
||||||
|
|
||||||
if strings.HasPrefix(h0.Name, "*") {
|
if strings.HasPrefix(rrset[0].Header().Name, "*") {
|
||||||
rr.Labels-- // wildcard, remove from label count
|
rr.Labels-- // wildcard, remove from label count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -396,7 +400,7 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
|
||||||
if rr.Algorithm != k.Algorithm {
|
if rr.Algorithm != k.Algorithm {
|
||||||
return ErrKey
|
return ErrKey
|
||||||
}
|
}
|
||||||
if !strings.EqualFold(rr.SignerName, k.Hdr.Name) {
|
if strings.ToLower(rr.SignerName) != strings.ToLower(k.Hdr.Name) {
|
||||||
return ErrKey
|
return ErrKey
|
||||||
}
|
}
|
||||||
if k.Protocol != 3 {
|
if k.Protocol != 3 {
|
||||||
|
@ -406,7 +410,10 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
|
||||||
// IsRRset checked that we have at least one RR and that the RRs in
|
// 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
|
// the set have consistent type, class, and name. Also check that type and
|
||||||
// class matches the RRSIG record.
|
// class matches the RRSIG record.
|
||||||
if h0 := rrset[0].Header(); h0.Class != rr.Hdr.Class || h0.Rrtype != rr.TypeCovered {
|
if rrset[0].Header().Class != rr.Hdr.Class {
|
||||||
|
return ErrRRset
|
||||||
|
}
|
||||||
|
if rrset[0].Header().Rrtype != rr.TypeCovered {
|
||||||
return ErrRRset
|
return ErrRRset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -504,8 +511,8 @@ func (rr *RRSIG) ValidityPeriod(t time.Time) bool {
|
||||||
}
|
}
|
||||||
modi := (int64(rr.Inception) - utc) / year68
|
modi := (int64(rr.Inception) - utc) / year68
|
||||||
mode := (int64(rr.Expiration) - utc) / year68
|
mode := (int64(rr.Expiration) - utc) / year68
|
||||||
ti := int64(rr.Inception) + modi*year68
|
ti := int64(rr.Inception) + (modi * year68)
|
||||||
te := int64(rr.Expiration) + mode*year68
|
te := int64(rr.Expiration) + (mode * year68)
|
||||||
return ti <= utc && utc <= te
|
return ti <= utc && utc <= te
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -525,11 +532,6 @@ func (k *DNSKEY) publicKeyRSA() *rsa.PublicKey {
|
||||||
return 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
|
// RFC 2537/3110, section 2. RSA Public KEY Resource Records
|
||||||
// Length is in the 0th byte, unless its zero, then it
|
// Length is in the 0th byte, unless its zero, then it
|
||||||
// it in bytes 1 and 2 and its a 16 bit number
|
// it in bytes 1 and 2 and its a 16 bit number
|
||||||
|
@ -539,35 +541,25 @@ func (k *DNSKEY) publicKeyRSA() *rsa.PublicKey {
|
||||||
explen = uint16(keybuf[1])<<8 | uint16(keybuf[2])
|
explen = uint16(keybuf[1])<<8 | uint16(keybuf[2])
|
||||||
keyoff = 3
|
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)
|
pubkey := new(rsa.PublicKey)
|
||||||
|
|
||||||
var expo uint64
|
pubkey.N = big.NewInt(0)
|
||||||
// The exponent of length explen is between keyoff and modoff.
|
shift := uint64((explen - 1) * 8)
|
||||||
for _, v := range keybuf[keyoff:modoff] {
|
expo := uint64(0)
|
||||||
expo <<= 8
|
for i := int(explen - 1); i > 0; i-- {
|
||||||
expo |= uint64(v)
|
expo += uint64(keybuf[keyoff+i]) << shift
|
||||||
|
shift -= 8
|
||||||
}
|
}
|
||||||
if expo > 1<<31-1 {
|
// Remainder
|
||||||
// Larger exponent than supported by the crypto package.
|
expo += uint64(keybuf[keyoff])
|
||||||
|
if expo > (2<<31)+1 {
|
||||||
|
// Larger expo than supported.
|
||||||
|
// println("dns: F5 primes (or larger) are not supported")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
pubkey.E = int(expo)
|
pubkey.E = int(expo)
|
||||||
pubkey.N = new(big.Int).SetBytes(keybuf[modoff:])
|
|
||||||
|
pubkey.N.SetBytes(keybuf[keyoff+int(explen):])
|
||||||
return pubkey
|
return pubkey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -592,8 +584,10 @@ func (k *DNSKEY) publicKeyECDSA() *ecdsa.PublicKey {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pubkey.X = new(big.Int).SetBytes(keybuf[:len(keybuf)/2])
|
pubkey.X = big.NewInt(0)
|
||||||
pubkey.Y = new(big.Int).SetBytes(keybuf[len(keybuf)/2:])
|
pubkey.X.SetBytes(keybuf[:len(keybuf)/2])
|
||||||
|
pubkey.Y = big.NewInt(0)
|
||||||
|
pubkey.Y.SetBytes(keybuf[len(keybuf)/2:])
|
||||||
return pubkey
|
return pubkey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -614,10 +608,10 @@ func (k *DNSKEY) publicKeyDSA() *dsa.PublicKey {
|
||||||
p, keybuf := keybuf[:size], keybuf[size:]
|
p, keybuf := keybuf[:size], keybuf[size:]
|
||||||
g, y := keybuf[:size], keybuf[size:]
|
g, y := keybuf[:size], keybuf[size:]
|
||||||
pubkey := new(dsa.PublicKey)
|
pubkey := new(dsa.PublicKey)
|
||||||
pubkey.Parameters.Q = new(big.Int).SetBytes(q)
|
pubkey.Parameters.Q = big.NewInt(0).SetBytes(q)
|
||||||
pubkey.Parameters.P = new(big.Int).SetBytes(p)
|
pubkey.Parameters.P = big.NewInt(0).SetBytes(p)
|
||||||
pubkey.Parameters.G = new(big.Int).SetBytes(g)
|
pubkey.Parameters.G = big.NewInt(0).SetBytes(g)
|
||||||
pubkey.Y = new(big.Int).SetBytes(y)
|
pubkey.Y = big.NewInt(0).SetBytes(y)
|
||||||
return pubkey
|
return pubkey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -647,16 +641,15 @@ func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) {
|
||||||
wires := make(wireSlice, len(rrset))
|
wires := make(wireSlice, len(rrset))
|
||||||
for i, r := range rrset {
|
for i, r := range rrset {
|
||||||
r1 := r.copy()
|
r1 := r.copy()
|
||||||
h := r1.Header()
|
r1.Header().Ttl = s.OrigTtl
|
||||||
h.Ttl = s.OrigTtl
|
labels := SplitDomainName(r1.Header().Name)
|
||||||
labels := SplitDomainName(h.Name)
|
|
||||||
// 6.2. Canonical RR Form. (4) - wildcards
|
// 6.2. Canonical RR Form. (4) - wildcards
|
||||||
if len(labels) > int(s.Labels) {
|
if len(labels) > int(s.Labels) {
|
||||||
// Wildcard
|
// Wildcard
|
||||||
h.Name = "*." + strings.Join(labels[len(labels)-int(s.Labels):], ".") + "."
|
r1.Header().Name = "*." + strings.Join(labels[len(labels)-int(s.Labels):], ".") + "."
|
||||||
}
|
}
|
||||||
// RFC 4034: 6.2. Canonical RR Form. (2) - domain name to lowercase
|
// RFC 4034: 6.2. Canonical RR Form. (2) - domain name to lowercase
|
||||||
h.Name = strings.ToLower(h.Name)
|
r1.Header().Name = strings.ToLower(r1.Header().Name)
|
||||||
// 6.2. Canonical RR Form. (3) - domain rdata to lowercase.
|
// 6.2. Canonical RR Form. (3) - domain rdata to lowercase.
|
||||||
// NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR,
|
// NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR,
|
||||||
// HINFO, MINFO, MX, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX,
|
// HINFO, MINFO, MX, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX,
|
||||||
|
@ -714,7 +707,7 @@ func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) {
|
||||||
x.Target = strings.ToLower(x.Target)
|
x.Target = strings.ToLower(x.Target)
|
||||||
}
|
}
|
||||||
// 6.2. Canonical RR Form. (5) - origTTL
|
// 6.2. Canonical RR Form. (5) - origTTL
|
||||||
wire := make([]byte, Len(r1)+1) // +1 to be safe(r)
|
wire := make([]byte, r1.len()+1) // +1 to be safe(r)
|
||||||
off, err1 := PackRR(r1, wire, 0, nil, false)
|
off, err1 := PackRR(r1, wire, 0, nil, false)
|
||||||
if err1 != nil {
|
if err1 != nil {
|
||||||
return nil, err1
|
return nil, err1
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bytes"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/dsa"
|
"crypto/dsa"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
@ -109,16 +109,21 @@ func readPrivateKeyRSA(m map[string]string) (*rsa.PrivateKey, error) {
|
||||||
}
|
}
|
||||||
switch k {
|
switch k {
|
||||||
case "modulus":
|
case "modulus":
|
||||||
p.PublicKey.N = new(big.Int).SetBytes(v1)
|
p.PublicKey.N = big.NewInt(0)
|
||||||
|
p.PublicKey.N.SetBytes(v1)
|
||||||
case "publicexponent":
|
case "publicexponent":
|
||||||
i := new(big.Int).SetBytes(v1)
|
i := big.NewInt(0)
|
||||||
|
i.SetBytes(v1)
|
||||||
p.PublicKey.E = int(i.Int64()) // int64 should be large enough
|
p.PublicKey.E = int(i.Int64()) // int64 should be large enough
|
||||||
case "privateexponent":
|
case "privateexponent":
|
||||||
p.D = new(big.Int).SetBytes(v1)
|
p.D = big.NewInt(0)
|
||||||
|
p.D.SetBytes(v1)
|
||||||
case "prime1":
|
case "prime1":
|
||||||
p.Primes[0] = new(big.Int).SetBytes(v1)
|
p.Primes[0] = big.NewInt(0)
|
||||||
|
p.Primes[0].SetBytes(v1)
|
||||||
case "prime2":
|
case "prime2":
|
||||||
p.Primes[1] = new(big.Int).SetBytes(v1)
|
p.Primes[1] = big.NewInt(0)
|
||||||
|
p.Primes[1].SetBytes(v1)
|
||||||
}
|
}
|
||||||
case "exponent1", "exponent2", "coefficient":
|
case "exponent1", "exponent2", "coefficient":
|
||||||
// not used in Go (yet)
|
// not used in Go (yet)
|
||||||
|
@ -131,7 +136,7 @@ func readPrivateKeyRSA(m map[string]string) (*rsa.PrivateKey, error) {
|
||||||
|
|
||||||
func readPrivateKeyDSA(m map[string]string) (*dsa.PrivateKey, error) {
|
func readPrivateKeyDSA(m map[string]string) (*dsa.PrivateKey, error) {
|
||||||
p := new(dsa.PrivateKey)
|
p := new(dsa.PrivateKey)
|
||||||
p.X = new(big.Int)
|
p.X = big.NewInt(0)
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
switch k {
|
switch k {
|
||||||
case "private_value(x)":
|
case "private_value(x)":
|
||||||
|
@ -149,7 +154,7 @@ func readPrivateKeyDSA(m map[string]string) (*dsa.PrivateKey, error) {
|
||||||
|
|
||||||
func readPrivateKeyECDSA(m map[string]string) (*ecdsa.PrivateKey, error) {
|
func readPrivateKeyECDSA(m map[string]string) (*ecdsa.PrivateKey, error) {
|
||||||
p := new(ecdsa.PrivateKey)
|
p := new(ecdsa.PrivateKey)
|
||||||
p.D = new(big.Int)
|
p.D = big.NewInt(0)
|
||||||
// TODO: validate that the required flags are present
|
// TODO: validate that the required flags are present
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
switch k {
|
switch k {
|
||||||
|
@ -176,10 +181,22 @@ func readPrivateKeyED25519(m map[string]string) (ed25519.PrivateKey, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(p1) != ed25519.SeedSize {
|
if len(p1) != 32 {
|
||||||
return nil, ErrPrivKey
|
return nil, ErrPrivKey
|
||||||
}
|
}
|
||||||
p = ed25519.NewKeyFromSeed(p1)
|
// RFC 8080 and Golang's x/crypto/ed25519 differ as to how the
|
||||||
|
// private keys are represented. RFC 8080 specifies that private
|
||||||
|
// keys be stored solely as the seed value (p1 above) while the
|
||||||
|
// ed25519 package represents them as the seed value concatenated
|
||||||
|
// to the public key, which is derived from the seed value.
|
||||||
|
//
|
||||||
|
// ed25519.GenerateKey reads exactly 32 bytes from the passed in
|
||||||
|
// io.Reader and uses them as the seed. It also derives the
|
||||||
|
// public key and produces a compatible private key.
|
||||||
|
_, p, err = ed25519.GenerateKey(bytes.NewReader(p1))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
case "created", "publish", "activate":
|
case "created", "publish", "activate":
|
||||||
/* not used in Go (yet) */
|
/* not used in Go (yet) */
|
||||||
}
|
}
|
||||||
|
@ -190,12 +207,23 @@ func readPrivateKeyED25519(m map[string]string) (ed25519.PrivateKey, error) {
|
||||||
// parseKey reads a private key from r. It returns a map[string]string,
|
// 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.
|
// 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) {
|
func parseKey(r io.Reader, file string) (map[string]string, error) {
|
||||||
|
s, cancel := scanInit(r)
|
||||||
m := make(map[string]string)
|
m := make(map[string]string)
|
||||||
var k string
|
c := make(chan lex)
|
||||||
|
k := ""
|
||||||
c := newKLexer(r)
|
defer func() {
|
||||||
|
cancel()
|
||||||
for l, ok := c.Next(); ok; l, ok = c.Next() {
|
// 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
|
// It should alternate
|
||||||
switch l.value {
|
switch l.value {
|
||||||
case zKey:
|
case zKey:
|
||||||
|
@ -204,111 +232,41 @@ func parseKey(r io.Reader, file string) (map[string]string, error) {
|
||||||
if k == "" {
|
if k == "" {
|
||||||
return nil, &ParseError{file, "no private key seen", l}
|
return nil, &ParseError{file, "no private key seen", l}
|
||||||
}
|
}
|
||||||
|
//println("Setting", strings.ToLower(k), "to", l.token, "b")
|
||||||
m[strings.ToLower(k)] = l.token
|
m[strings.ToLower(k)] = l.token
|
||||||
k = ""
|
k = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Surface any read errors from r.
|
|
||||||
if err := c.Err(); err != nil {
|
|
||||||
return nil, &ParseError{file: file, err: err.Error()}
|
|
||||||
}
|
|
||||||
|
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type klexer struct {
|
// klexer scans the sourcefile and returns tokens on the channel c.
|
||||||
br io.ByteReader
|
func klexer(s *scan, c chan lex) {
|
||||||
|
var l lex
|
||||||
readErr error
|
str := "" // Hold the current read text
|
||||||
|
commt := false
|
||||||
line int
|
key := true
|
||||||
column int
|
x, err := s.tokenText()
|
||||||
|
defer close(c)
|
||||||
key bool
|
for err == nil {
|
||||||
|
l.column = s.position.Column
|
||||||
eol bool // end-of-line
|
l.line = s.position.Line
|
||||||
}
|
|
||||||
|
|
||||||
func newKLexer(r io.Reader) *klexer {
|
|
||||||
br, ok := r.(io.ByteReader)
|
|
||||||
if !ok {
|
|
||||||
br = bufio.NewReaderSize(r, 1024)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &klexer{
|
|
||||||
br: br,
|
|
||||||
|
|
||||||
line: 1,
|
|
||||||
|
|
||||||
key: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (kl *klexer) Err() error {
|
|
||||||
if kl.readErr == io.EOF {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return kl.readErr
|
|
||||||
}
|
|
||||||
|
|
||||||
// readByte returns the next byte from the input
|
|
||||||
func (kl *klexer) readByte() (byte, bool) {
|
|
||||||
if kl.readErr != nil {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
c, err := kl.br.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
kl.readErr = err
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// delay the newline handling until the next token is delivered,
|
|
||||||
// fixes off-by-one errors when reporting a parse error.
|
|
||||||
if kl.eol {
|
|
||||||
kl.line++
|
|
||||||
kl.column = 0
|
|
||||||
kl.eol = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if c == '\n' {
|
|
||||||
kl.eol = true
|
|
||||||
} else {
|
|
||||||
kl.column++
|
|
||||||
}
|
|
||||||
|
|
||||||
return c, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (kl *klexer) Next() (lex, bool) {
|
|
||||||
var (
|
|
||||||
l lex
|
|
||||||
|
|
||||||
str strings.Builder
|
|
||||||
|
|
||||||
commt bool
|
|
||||||
)
|
|
||||||
|
|
||||||
for x, ok := kl.readByte(); ok; x, ok = kl.readByte() {
|
|
||||||
l.line, l.column = kl.line, kl.column
|
|
||||||
|
|
||||||
switch x {
|
switch x {
|
||||||
case ':':
|
case ':':
|
||||||
if commt || !kl.key {
|
if commt {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
l.token = str
|
||||||
kl.key = false
|
if key {
|
||||||
|
|
||||||
// Next token is a space, eat it
|
|
||||||
kl.readByte()
|
|
||||||
|
|
||||||
l.value = zKey
|
l.value = zKey
|
||||||
l.token = str.String()
|
c <- l
|
||||||
return l, true
|
// Next token is a space, eat it
|
||||||
|
s.tokenText()
|
||||||
|
key = false
|
||||||
|
str = ""
|
||||||
|
} else {
|
||||||
|
l.value = zValue
|
||||||
|
}
|
||||||
case ';':
|
case ';':
|
||||||
commt = true
|
commt = true
|
||||||
case '\n':
|
case '\n':
|
||||||
|
@ -316,37 +274,24 @@ func (kl *klexer) Next() (lex, bool) {
|
||||||
// Reset a comment
|
// Reset a comment
|
||||||
commt = false
|
commt = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if kl.key && str.Len() == 0 {
|
|
||||||
// ignore empty lines
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
kl.key = true
|
|
||||||
|
|
||||||
l.value = zValue
|
l.value = zValue
|
||||||
l.token = str.String()
|
l.token = str
|
||||||
return l, true
|
c <- l
|
||||||
|
str = ""
|
||||||
|
commt = false
|
||||||
|
key = true
|
||||||
default:
|
default:
|
||||||
if commt {
|
if commt {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
str += string(x)
|
||||||
str.WriteByte(x)
|
|
||||||
}
|
}
|
||||||
|
x, err = s.tokenText()
|
||||||
}
|
}
|
||||||
|
if len(str) > 0 {
|
||||||
if kl.readErr != nil && kl.readErr != io.EOF {
|
|
||||||
// Don't return any tokens after a read error occurs.
|
|
||||||
return lex{value: zEOF}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
if str.Len() > 0 {
|
|
||||||
// Send remainder
|
// Send remainder
|
||||||
|
l.token = str
|
||||||
l.value = zValue
|
l.value = zValue
|
||||||
l.token = str.String()
|
c <- l
|
||||||
return l, true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return lex{value: zEOF}, false
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,6 @@ import (
|
||||||
|
|
||||||
const format = "Private-key-format: v1.3\n"
|
const format = "Private-key-format: v1.3\n"
|
||||||
|
|
||||||
var bigIntOne = big.NewInt(1)
|
|
||||||
|
|
||||||
// PrivateKeyString converts a PrivateKey to a string. This string has the same
|
// 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).
|
// 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 needs some info from the key (the algorithm), so its a method of the DNSKEY
|
||||||
|
@ -33,11 +31,12 @@ func (r *DNSKEY) PrivateKeyString(p crypto.PrivateKey) string {
|
||||||
prime2 := toBase64(p.Primes[1].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
|
// 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
|
// and from: http://code.google.com/p/go/issues/detail?id=987
|
||||||
p1 := new(big.Int).Sub(p.Primes[0], bigIntOne)
|
one := big.NewInt(1)
|
||||||
q1 := new(big.Int).Sub(p.Primes[1], bigIntOne)
|
p1 := big.NewInt(0).Sub(p.Primes[0], one)
|
||||||
exp1 := new(big.Int).Mod(p.D, p1)
|
q1 := big.NewInt(0).Sub(p.Primes[1], one)
|
||||||
exp2 := new(big.Int).Mod(p.D, q1)
|
exp1 := big.NewInt(0).Mod(p.D, p1)
|
||||||
coeff := new(big.Int).ModInverse(p.Primes[1], p.Primes[0])
|
exp2 := big.NewInt(0).Mod(p.D, q1)
|
||||||
|
coeff := big.NewInt(0).ModInverse(p.Primes[1], p.Primes[0])
|
||||||
|
|
||||||
exponent1 := toBase64(exp1.Bytes())
|
exponent1 := toBase64(exp1.Bytes())
|
||||||
exponent2 := toBase64(exp2.Bytes())
|
exponent2 := toBase64(exp2.Bytes())
|
||||||
|
@ -83,7 +82,7 @@ func (r *DNSKEY) PrivateKeyString(p crypto.PrivateKey) string {
|
||||||
"Public_value(y): " + pub + "\n"
|
"Public_value(y): " + pub + "\n"
|
||||||
|
|
||||||
case ed25519.PrivateKey:
|
case ed25519.PrivateKey:
|
||||||
private := toBase64(p.Seed())
|
private := toBase64(p[:32])
|
||||||
return format +
|
return format +
|
||||||
"Algorithm: " + algorithm + "\n" +
|
"Algorithm: " + algorithm + "\n" +
|
||||||
"PrivateKey: " + private + "\n"
|
"PrivateKey: " + private + "\n"
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
/*
|
/*
|
||||||
Package dns implements a full featured interface to the Domain Name System.
|
Package dns implements a full featured interface to the Domain Name System.
|
||||||
Both server- and client-side programming is supported. The package allows
|
Server- and client-side programming is supported.
|
||||||
complete control over what is sent out to the DNS. The API follows the
|
The package allows complete control over what is sent out to the DNS. The package
|
||||||
less-is-more principle, by presenting a small, clean interface.
|
API follows the less-is-more principle, by presenting a small, clean interface.
|
||||||
|
|
||||||
It supports (asynchronous) querying/replying, incoming/outgoing zone transfers,
|
The package dns supports (asynchronous) querying/replying, incoming/outgoing zone transfers,
|
||||||
TSIG, EDNS0, dynamic updates, notifies and DNSSEC validation/signing.
|
TSIG, EDNS0, dynamic updates, notifies and DNSSEC validation/signing.
|
||||||
|
Note that domain names MUST be fully qualified, before sending them, unqualified
|
||||||
Note that domain names MUST be fully qualified before sending them, unqualified
|
|
||||||
names in a message will result in a packing failure.
|
names in a message will result in a packing failure.
|
||||||
|
|
||||||
Resource records are native types. They are not stored in wire format. Basic
|
Resource records are native types. They are not stored in wire format.
|
||||||
usage pattern for creating a new resource record:
|
Basic usage pattern for creating a new resource record:
|
||||||
|
|
||||||
r := new(dns.MX)
|
r := new(dns.MX)
|
||||||
r.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 3600}
|
r.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeMX,
|
||||||
|
Class: dns.ClassINET, Ttl: 3600}
|
||||||
r.Preference = 10
|
r.Preference = 10
|
||||||
r.Mx = "mx.miek.nl."
|
r.Mx = "mx.miek.nl."
|
||||||
|
|
||||||
|
@ -30,8 +30,8 @@ Or even:
|
||||||
|
|
||||||
mx, err := dns.NewRR("$ORIGIN nl.\nmiek 1H IN MX 10 mx.miek")
|
mx, err := dns.NewRR("$ORIGIN nl.\nmiek 1H IN MX 10 mx.miek")
|
||||||
|
|
||||||
In the DNS messages are exchanged, these messages contain resource records
|
In the DNS messages are exchanged, these messages contain resource
|
||||||
(sets). Use pattern for creating a message:
|
records (sets). Use pattern for creating a message:
|
||||||
|
|
||||||
m := new(dns.Msg)
|
m := new(dns.Msg)
|
||||||
m.SetQuestion("miek.nl.", dns.TypeMX)
|
m.SetQuestion("miek.nl.", dns.TypeMX)
|
||||||
|
@ -40,8 +40,8 @@ Or when not certain if the domain name is fully qualified:
|
||||||
|
|
||||||
m.SetQuestion(dns.Fqdn("miek.nl"), dns.TypeMX)
|
m.SetQuestion(dns.Fqdn("miek.nl"), dns.TypeMX)
|
||||||
|
|
||||||
The message m is now a message with the question section set to ask the MX
|
The message m is now a message with the question section set to ask
|
||||||
records for the miek.nl. zone.
|
the MX records for the miek.nl. zone.
|
||||||
|
|
||||||
The following is slightly more verbose, but more flexible:
|
The following is slightly more verbose, but more flexible:
|
||||||
|
|
||||||
|
@ -51,8 +51,9 @@ The following is slightly more verbose, but more flexible:
|
||||||
m1.Question = make([]dns.Question, 1)
|
m1.Question = make([]dns.Question, 1)
|
||||||
m1.Question[0] = dns.Question{"miek.nl.", dns.TypeMX, dns.ClassINET}
|
m1.Question[0] = dns.Question{"miek.nl.", dns.TypeMX, dns.ClassINET}
|
||||||
|
|
||||||
After creating a message it can be sent. Basic use pattern for synchronous
|
After creating a message it can be sent.
|
||||||
querying the DNS at a server configured on 127.0.0.1 and port 53:
|
Basic use pattern for synchronous querying the DNS at a
|
||||||
|
server configured on 127.0.0.1 and port 53:
|
||||||
|
|
||||||
c := new(dns.Client)
|
c := new(dns.Client)
|
||||||
in, rtt, err := c.Exchange(m1, "127.0.0.1:53")
|
in, rtt, err := c.Exchange(m1, "127.0.0.1:53")
|
||||||
|
@ -72,11 +73,11 @@ and port to use for the connection:
|
||||||
Port: 12345,
|
Port: 12345,
|
||||||
Zone: "",
|
Zone: "",
|
||||||
}
|
}
|
||||||
c.Dialer := &net.Dialer{
|
d := net.Dialer{
|
||||||
Timeout: 200 * time.Millisecond,
|
Timeout: 200 * time.Millisecond,
|
||||||
LocalAddr: &laddr,
|
LocalAddr: &laddr,
|
||||||
}
|
}
|
||||||
in, rtt, err := c.Exchange(m1, "8.8.8.8:53")
|
in, rtt, err := c.ExchangeWithDialer(&d, m1, "8.8.8.8:53")
|
||||||
|
|
||||||
If these "advanced" features are not needed, a simple UDP query can be sent,
|
If these "advanced" features are not needed, a simple UDP query can be sent,
|
||||||
with:
|
with:
|
||||||
|
@ -98,24 +99,25 @@ the Answer section:
|
||||||
|
|
||||||
Domain Name and TXT Character String Representations
|
Domain Name and TXT Character String Representations
|
||||||
|
|
||||||
Both domain names and TXT character strings are converted to presentation form
|
Both domain names and TXT character strings are converted to presentation
|
||||||
both when unpacked and when converted to strings.
|
form both when unpacked and when converted to strings.
|
||||||
|
|
||||||
For TXT character strings, tabs, carriage returns and line feeds will be
|
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
|
converted to \t, \r and \n respectively. Back slashes and quotations marks
|
||||||
be escaped. Bytes below 32 and above 127 will be converted to \DDD form.
|
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,
|
For domain names, in addition to the above rules brackets, periods,
|
||||||
semicolons and the at symbol are escaped.
|
spaces, semicolons and the at symbol are escaped.
|
||||||
|
|
||||||
DNSSEC
|
DNSSEC
|
||||||
|
|
||||||
DNSSEC (DNS Security Extension) adds a layer of security to the DNS. It uses
|
DNSSEC (DNS Security Extension) adds a layer of security to the DNS. It
|
||||||
public key cryptography to sign resource records. The public keys are stored in
|
uses public key cryptography to sign resource records. The
|
||||||
DNSKEY records and the signatures in RRSIG records.
|
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)
|
Requesting DNSSEC information for a zone is done by adding the DO (DNSSEC OK) bit
|
||||||
bit to a request.
|
to a request.
|
||||||
|
|
||||||
m := new(dns.Msg)
|
m := new(dns.Msg)
|
||||||
m.SetEdns0(4096, true)
|
m.SetEdns0(4096, true)
|
||||||
|
@ -124,9 +126,9 @@ Signature generation, signature verification and key generation are all supporte
|
||||||
|
|
||||||
DYNAMIC UPDATES
|
DYNAMIC UPDATES
|
||||||
|
|
||||||
Dynamic updates reuses the DNS message format, but renames three of the
|
Dynamic updates reuses the DNS message format, but renames three of
|
||||||
sections. Question is Zone, Answer is Prerequisite, Authority is Update, only
|
the sections. Question is Zone, Answer is Prerequisite, Authority is
|
||||||
the Additional is not renamed. See RFC 2136 for the gory details.
|
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
|
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
|
certain resource records or names in a zone to specify if resource records
|
||||||
|
@ -143,9 +145,10 @@ DNS function shows which functions exist to specify the prerequisites.
|
||||||
NONE rrset empty RRset does not exist dns.RRsetNotUsed
|
NONE rrset empty RRset does not exist dns.RRsetNotUsed
|
||||||
zone rrset rr RRset exists (value dep) dns.Used
|
zone rrset rr RRset exists (value dep) dns.Used
|
||||||
|
|
||||||
The prerequisite section can also be left empty. If you have decided on the
|
The prerequisite section can also be left empty.
|
||||||
prerequisites you can tell what RRs should be added or deleted. The next table
|
If you have decided on the prerequisites you can tell what RRs should
|
||||||
shows the options you have and what functions to call.
|
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
|
3.4.2.6 - Table Of Metavalues Used In Update Section
|
||||||
|
|
||||||
|
@ -178,10 +181,10 @@ changes to the RRset after calling SetTsig() the signature will be incorrect.
|
||||||
...
|
...
|
||||||
// When sending the TSIG RR is calculated and filled in before sending
|
// 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
|
When requesting an zone transfer (almost all TSIG usage is when requesting zone transfers), with
|
||||||
transfers), with TSIG, this is the basic use pattern. In this example we
|
TSIG, this is the basic use pattern. In this example we request an AXFR for
|
||||||
request an AXFR for miek.nl. with TSIG key named "axfr." and secret
|
miek.nl. with TSIG key named "axfr." and secret "so6ZGir4GPAqINNh9U5c3A=="
|
||||||
"so6ZGir4GPAqINNh9U5c3A==" and using the server 176.58.119.54:
|
and using the server 176.58.119.54:
|
||||||
|
|
||||||
t := new(dns.Transfer)
|
t := new(dns.Transfer)
|
||||||
m := new(dns.Msg)
|
m := new(dns.Msg)
|
||||||
|
@ -191,8 +194,8 @@ request an AXFR for miek.nl. with TSIG key named "axfr." and secret
|
||||||
c, err := t.In(m, "176.58.119.54:53")
|
c, err := t.In(m, "176.58.119.54:53")
|
||||||
for r := range c { ... }
|
for r := range c { ... }
|
||||||
|
|
||||||
You can now read the records from the transfer as they come in. Each envelope
|
You can now read the records from the transfer as they come in. Each envelope is checked with TSIG.
|
||||||
is checked with TSIG. If something is not correct an error is returned.
|
If something is not correct an error is returned.
|
||||||
|
|
||||||
Basic use pattern validating and replying to a message that has TSIG set.
|
Basic use pattern validating and replying to a message that has TSIG set.
|
||||||
|
|
||||||
|
@ -217,30 +220,29 @@ Basic use pattern validating and replying to a message that has TSIG set.
|
||||||
|
|
||||||
PRIVATE RRS
|
PRIVATE RRS
|
||||||
|
|
||||||
RFC 6895 sets aside a range of type codes for private use. This range is 65,280
|
RFC 6895 sets aside a range of type codes for private use. This range
|
||||||
- 65,534 (0xFF00 - 0xFFFE). When experimenting with new Resource Records these
|
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.
|
can be used, before requesting an official type code from IANA.
|
||||||
|
|
||||||
See https://miek.nl/2014/September/21/idn-and-private-rr-in-go-dns/ for more
|
see http://miek.nl/2014/September/21/idn-and-private-rr-in-go-dns/ for more
|
||||||
information.
|
information.
|
||||||
|
|
||||||
EDNS0
|
EDNS0
|
||||||
|
|
||||||
EDNS0 is an extension mechanism for the DNS defined in RFC 2671 and updated by
|
EDNS0 is an extension mechanism for the DNS defined in RFC 2671 and updated
|
||||||
RFC 6891. It defines an new RR type, the OPT RR, which is then completely
|
by RFC 6891. It defines an new RR type, the OPT RR, which is then completely
|
||||||
abused.
|
abused.
|
||||||
|
|
||||||
Basic use pattern for creating an (empty) OPT RR:
|
Basic use pattern for creating an (empty) OPT RR:
|
||||||
|
|
||||||
o := new(dns.OPT)
|
o := new(dns.OPT)
|
||||||
o.Hdr.Name = "." // MUST be the root zone, per definition.
|
o.Hdr.Name = "." // MUST be the root zone, per definition.
|
||||||
o.Hdr.Rrtype = dns.TypeOPT
|
o.Hdr.Rrtype = dns.TypeOPT
|
||||||
|
|
||||||
The rdata of an OPT RR consists out of a slice of EDNS0 (RFC 6891) interfaces.
|
The rdata of an OPT RR consists out of a slice of EDNS0 (RFC 6891)
|
||||||
Currently only a few have been standardized: EDNS0_NSID (RFC 5001) and
|
interfaces. Currently only a few have been standardized: EDNS0_NSID
|
||||||
EDNS0_SUBNET (draft-vandergaast-edns-client-subnet-02). Note that these options
|
(RFC 5001) and EDNS0_SUBNET (draft-vandergaast-edns-client-subnet-02). Note
|
||||||
may be combined in an OPT RR. Basic use pattern for a server to check if (and
|
that these options may be combined in an OPT RR.
|
||||||
which) options are set:
|
Basic use pattern for a server to check if (and which) options are set:
|
||||||
|
|
||||||
// o is a dns.OPT
|
// o is a dns.OPT
|
||||||
for _, s := range o.Option {
|
for _, s := range o.Option {
|
||||||
|
@ -260,9 +262,10 @@ From RFC 2931:
|
||||||
... protection for glue records, DNS requests, protection for message headers
|
... protection for glue records, DNS requests, protection for message headers
|
||||||
on requests and responses, and protection of the overall integrity of a response.
|
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
|
It works like TSIG, except that SIG(0) uses public key cryptography, instead of the shared
|
||||||
the shared secret approach in TSIG. Supported algorithms: DSA, ECDSAP256SHA256,
|
secret approach in TSIG.
|
||||||
ECDSAP384SHA384, RSASHA1, RSASHA256 and RSASHA512.
|
Supported algorithms: DSA, ECDSAP256SHA256, ECDSAP384SHA384, RSASHA1, RSASHA256 and
|
||||||
|
RSASHA512.
|
||||||
|
|
||||||
Signing subsequent messages in multi-message sessions is not implemented.
|
Signing subsequent messages in multi-message sessions is not implemented.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
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 {
|
|
||||||
// Check whether the record header is identical.
|
|
||||||
if !r1.Header().isDuplicate(r2.Header()) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether the RDATA is identical.
|
|
||||||
return r1.isDuplicate(r2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r1 *RR_Header) isDuplicate(_r2 RR) bool {
|
|
||||||
r2, ok := _r2.(*RR_Header)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if r1.Class != r2.Class {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if r1.Rrtype != r2.Rrtype {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if !isDuplicateName(r1.Name, r2.Name) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// ignore TTL
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// isDuplicateName checks if the domain names s1 and s2 are equal.
|
|
||||||
func isDuplicateName(s1, s2 string) bool { return equal(s1, s2) }
|
|
|
@ -1,144 +0,0 @@
|
||||||
//+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 == "OPT" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
namedTypes = append(namedTypes, o.Name())
|
|
||||||
}
|
|
||||||
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
b.WriteString(packageHdr)
|
|
||||||
|
|
||||||
// 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 (r1 *%s) isDuplicate(_r2 RR) bool {\n", name)
|
|
||||||
fmt.Fprintf(b, "r2, ok := _r2.(*%s)\n", name)
|
|
||||||
fmt.Fprint(b, "if !ok { return false }\n")
|
|
||||||
fmt.Fprint(b, "_ = r2\n")
|
|
||||||
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 {
|
|
||||||
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 !isDuplicateName(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:"a"`, `dns:"aaaa"`:
|
|
||||||
o2("if !r1.%s.Equal(r2.%s) {\nreturn false\n}")
|
|
||||||
case `dns:"cdomain-name"`, `dns:"domain-name"`:
|
|
||||||
o2("if !isDuplicateName(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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -78,44 +78,36 @@ func (rr *OPT) String() string {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rr *OPT) len(off int, compression map[string]struct{}) int {
|
func (rr *OPT) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
for _, o := range rr.Option {
|
for i := 0; i < len(rr.Option); i++ {
|
||||||
l += 4 // Account for 2-byte option code and 2-byte option length.
|
l += 4 // Account for 2-byte option code and 2-byte option length.
|
||||||
lo, _ := o.pack()
|
lo, _ := rr.Option[i].pack()
|
||||||
l += len(lo)
|
l += len(lo)
|
||||||
}
|
}
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rr *OPT) parse(c *zlexer, origin, file string) *ParseError {
|
|
||||||
panic("dns: internal error: parse should never be called on OPT")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r1 *OPT) isDuplicate(r2 RR) bool { return false }
|
|
||||||
|
|
||||||
// return the old value -> delete SetVersion?
|
// return the old value -> delete SetVersion?
|
||||||
|
|
||||||
// Version returns the EDNS version used. Only zero is defined.
|
// Version returns the EDNS version used. Only zero is defined.
|
||||||
func (rr *OPT) Version() uint8 {
|
func (rr *OPT) Version() uint8 {
|
||||||
return uint8(rr.Hdr.Ttl & 0x00FF0000 >> 16)
|
return uint8((rr.Hdr.Ttl & 0x00FF0000) >> 16)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetVersion sets the version of EDNS. This is usually zero.
|
// SetVersion sets the version of EDNS. This is usually zero.
|
||||||
func (rr *OPT) SetVersion(v uint8) {
|
func (rr *OPT) SetVersion(v uint8) {
|
||||||
rr.Hdr.Ttl = rr.Hdr.Ttl&0xFF00FFFF | uint32(v)<<16
|
rr.Hdr.Ttl = rr.Hdr.Ttl&0xFF00FFFF | (uint32(v) << 16)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtendedRcode returns the EDNS extended RCODE field (the upper 8 bits of the TTL).
|
// ExtendedRcode returns the EDNS extended RCODE field (the upper 8 bits of the TTL).
|
||||||
func (rr *OPT) ExtendedRcode() int {
|
func (rr *OPT) ExtendedRcode() int {
|
||||||
return int(rr.Hdr.Ttl&0xFF000000>>24) << 4
|
return int((rr.Hdr.Ttl & 0xFF000000) >> 24)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetExtendedRcode sets the EDNS extended RCODE field.
|
// SetExtendedRcode sets the EDNS extended RCODE field.
|
||||||
//
|
func (rr *OPT) SetExtendedRcode(v uint8) {
|
||||||
// If the RCODE is not an extended RCODE, will reset the extended RCODE field to 0.
|
rr.Hdr.Ttl = rr.Hdr.Ttl&0x00FFFFFF | (uint32(v) << 24)
|
||||||
func (rr *OPT) SetExtendedRcode(v uint16) {
|
|
||||||
rr.Hdr.Ttl = rr.Hdr.Ttl&0x00FFFFFF | uint32(v>>4)<<24
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UDPSize returns the UDP buffer size.
|
// UDPSize returns the UDP buffer size.
|
||||||
|
@ -159,8 +151,6 @@ type EDNS0 interface {
|
||||||
unpack([]byte) error
|
unpack([]byte) error
|
||||||
// String returns the string representation of the option.
|
// String returns the string representation of the option.
|
||||||
String() string
|
String() string
|
||||||
// copy returns a deep-copy of the option.
|
|
||||||
copy() EDNS0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EDNS0_NSID option is used to retrieve a nameserver
|
// EDNS0_NSID option is used to retrieve a nameserver
|
||||||
|
@ -191,8 +181,7 @@ func (e *EDNS0_NSID) pack() ([]byte, error) {
|
||||||
// Option implements the EDNS0 interface.
|
// Option implements the EDNS0 interface.
|
||||||
func (e *EDNS0_NSID) Option() uint16 { return EDNS0NSID } // Option returns the option code.
|
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) unpack(b []byte) error { e.Nsid = hex.EncodeToString(b); return nil }
|
||||||
func (e *EDNS0_NSID) String() string { return e.Nsid }
|
func (e *EDNS0_NSID) String() string { return string(e.Nsid) }
|
||||||
func (e *EDNS0_NSID) copy() EDNS0 { return &EDNS0_NSID{e.Code, e.Nsid} }
|
|
||||||
|
|
||||||
// EDNS0_SUBNET is the subnet option that is used to give the remote nameserver
|
// 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
|
// an idea of where the client lives. See RFC 7871. It can then give back a different
|
||||||
|
@ -282,16 +271,22 @@ func (e *EDNS0_SUBNET) unpack(b []byte) error {
|
||||||
if e.SourceNetmask > net.IPv4len*8 || e.SourceScope > net.IPv4len*8 {
|
if e.SourceNetmask > net.IPv4len*8 || e.SourceScope > net.IPv4len*8 {
|
||||||
return errors.New("dns: bad netmask")
|
return errors.New("dns: bad netmask")
|
||||||
}
|
}
|
||||||
addr := make(net.IP, net.IPv4len)
|
addr := make([]byte, net.IPv4len)
|
||||||
copy(addr, b[4:])
|
for i := 0; i < net.IPv4len && 4+i < len(b); i++ {
|
||||||
e.Address = addr.To16()
|
addr[i] = b[4+i]
|
||||||
|
}
|
||||||
|
e.Address = net.IPv4(addr[0], addr[1], addr[2], addr[3])
|
||||||
case 2:
|
case 2:
|
||||||
if e.SourceNetmask > net.IPv6len*8 || e.SourceScope > net.IPv6len*8 {
|
if e.SourceNetmask > net.IPv6len*8 || e.SourceScope > net.IPv6len*8 {
|
||||||
return errors.New("dns: bad netmask")
|
return errors.New("dns: bad netmask")
|
||||||
}
|
}
|
||||||
addr := make(net.IP, net.IPv6len)
|
addr := make([]byte, net.IPv6len)
|
||||||
copy(addr, b[4:])
|
for i := 0; i < net.IPv6len && 4+i < len(b); i++ {
|
||||||
e.Address = addr
|
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:
|
default:
|
||||||
return errors.New("dns: bad address family")
|
return errors.New("dns: bad address family")
|
||||||
}
|
}
|
||||||
|
@ -310,16 +305,6 @@ func (e *EDNS0_SUBNET) String() (s string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EDNS0_SUBNET) copy() EDNS0 {
|
|
||||||
return &EDNS0_SUBNET{
|
|
||||||
e.Code,
|
|
||||||
e.Family,
|
|
||||||
e.SourceNetmask,
|
|
||||||
e.SourceScope,
|
|
||||||
e.Address,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The EDNS0_COOKIE option is used to add a DNS Cookie to a message.
|
// The EDNS0_COOKIE option is used to add a DNS Cookie to a message.
|
||||||
//
|
//
|
||||||
// o := new(dns.OPT)
|
// o := new(dns.OPT)
|
||||||
|
@ -355,7 +340,6 @@ func (e *EDNS0_COOKIE) pack() ([]byte, error) {
|
||||||
func (e *EDNS0_COOKIE) Option() uint16 { return EDNS0COOKIE }
|
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) unpack(b []byte) error { e.Cookie = hex.EncodeToString(b); return nil }
|
||||||
func (e *EDNS0_COOKIE) String() string { return e.Cookie }
|
func (e *EDNS0_COOKIE) String() string { return e.Cookie }
|
||||||
func (e *EDNS0_COOKIE) copy() EDNS0 { return &EDNS0_COOKIE{e.Code, e.Cookie} }
|
|
||||||
|
|
||||||
// The EDNS0_UL (Update Lease) (draft RFC) option is used to tell the server to set
|
// 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
|
// an expiration on an update RR. This is helpful for clients that cannot clean
|
||||||
|
@ -377,7 +361,6 @@ type EDNS0_UL struct {
|
||||||
// Option implements the EDNS0 interface.
|
// Option implements the EDNS0 interface.
|
||||||
func (e *EDNS0_UL) Option() uint16 { return EDNS0UL }
|
func (e *EDNS0_UL) Option() uint16 { return EDNS0UL }
|
||||||
func (e *EDNS0_UL) String() string { return strconv.FormatUint(uint64(e.Lease), 10) }
|
func (e *EDNS0_UL) String() string { return strconv.FormatUint(uint64(e.Lease), 10) }
|
||||||
func (e *EDNS0_UL) copy() EDNS0 { return &EDNS0_UL{e.Code, e.Lease} }
|
|
||||||
|
|
||||||
// Copied: http://golang.org/src/pkg/net/dnsmsg.go
|
// Copied: http://golang.org/src/pkg/net/dnsmsg.go
|
||||||
func (e *EDNS0_UL) pack() ([]byte, error) {
|
func (e *EDNS0_UL) pack() ([]byte, error) {
|
||||||
|
@ -432,15 +415,11 @@ func (e *EDNS0_LLQ) unpack(b []byte) error {
|
||||||
|
|
||||||
func (e *EDNS0_LLQ) String() string {
|
func (e *EDNS0_LLQ) String() string {
|
||||||
s := strconv.FormatUint(uint64(e.Version), 10) + " " + strconv.FormatUint(uint64(e.Opcode), 10) +
|
s := strconv.FormatUint(uint64(e.Version), 10) + " " + strconv.FormatUint(uint64(e.Opcode), 10) +
|
||||||
" " + strconv.FormatUint(uint64(e.Error), 10) + " " + strconv.FormatUint(e.Id, 10) +
|
" " + strconv.FormatUint(uint64(e.Error), 10) + " " + strconv.FormatUint(uint64(e.Id), 10) +
|
||||||
" " + strconv.FormatUint(uint64(e.LeaseLife), 10)
|
" " + strconv.FormatUint(uint64(e.LeaseLife), 10)
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
func (e *EDNS0_LLQ) copy() EDNS0 {
|
|
||||||
return &EDNS0_LLQ{e.Code, e.Version, e.Opcode, e.Error, e.Id, e.LeaseLife}
|
|
||||||
}
|
|
||||||
|
|
||||||
// EDNS0_DUA implements the EDNS0 "DNSSEC Algorithm Understood" option. See RFC 6975.
|
|
||||||
type EDNS0_DAU struct {
|
type EDNS0_DAU struct {
|
||||||
Code uint16 // Always EDNS0DAU
|
Code uint16 // Always EDNS0DAU
|
||||||
AlgCode []uint8
|
AlgCode []uint8
|
||||||
|
@ -453,18 +432,16 @@ func (e *EDNS0_DAU) unpack(b []byte) error { e.AlgCode = b; return nil }
|
||||||
|
|
||||||
func (e *EDNS0_DAU) String() string {
|
func (e *EDNS0_DAU) String() string {
|
||||||
s := ""
|
s := ""
|
||||||
for _, alg := range e.AlgCode {
|
for i := 0; i < len(e.AlgCode); i++ {
|
||||||
if a, ok := AlgorithmToString[alg]; ok {
|
if a, ok := AlgorithmToString[e.AlgCode[i]]; ok {
|
||||||
s += " " + a
|
s += " " + a
|
||||||
} else {
|
} else {
|
||||||
s += " " + strconv.Itoa(int(alg))
|
s += " " + strconv.Itoa(int(e.AlgCode[i]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
func (e *EDNS0_DAU) copy() EDNS0 { return &EDNS0_DAU{e.Code, e.AlgCode} }
|
|
||||||
|
|
||||||
// EDNS0_DHU implements the EDNS0 "DS Hash Understood" option. See RFC 6975.
|
|
||||||
type EDNS0_DHU struct {
|
type EDNS0_DHU struct {
|
||||||
Code uint16 // Always EDNS0DHU
|
Code uint16 // Always EDNS0DHU
|
||||||
AlgCode []uint8
|
AlgCode []uint8
|
||||||
|
@ -477,18 +454,16 @@ func (e *EDNS0_DHU) unpack(b []byte) error { e.AlgCode = b; return nil }
|
||||||
|
|
||||||
func (e *EDNS0_DHU) String() string {
|
func (e *EDNS0_DHU) String() string {
|
||||||
s := ""
|
s := ""
|
||||||
for _, alg := range e.AlgCode {
|
for i := 0; i < len(e.AlgCode); i++ {
|
||||||
if a, ok := HashToString[alg]; ok {
|
if a, ok := HashToString[e.AlgCode[i]]; ok {
|
||||||
s += " " + a
|
s += " " + a
|
||||||
} else {
|
} else {
|
||||||
s += " " + strconv.Itoa(int(alg))
|
s += " " + strconv.Itoa(int(e.AlgCode[i]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
func (e *EDNS0_DHU) copy() EDNS0 { return &EDNS0_DHU{e.Code, e.AlgCode} }
|
|
||||||
|
|
||||||
// EDNS0_N3U implements the EDNS0 "NSEC3 Hash Understood" option. See RFC 6975.
|
|
||||||
type EDNS0_N3U struct {
|
type EDNS0_N3U struct {
|
||||||
Code uint16 // Always EDNS0N3U
|
Code uint16 // Always EDNS0N3U
|
||||||
AlgCode []uint8
|
AlgCode []uint8
|
||||||
|
@ -502,18 +477,16 @@ func (e *EDNS0_N3U) unpack(b []byte) error { e.AlgCode = b; return nil }
|
||||||
func (e *EDNS0_N3U) String() string {
|
func (e *EDNS0_N3U) String() string {
|
||||||
// Re-use the hash map
|
// Re-use the hash map
|
||||||
s := ""
|
s := ""
|
||||||
for _, alg := range e.AlgCode {
|
for i := 0; i < len(e.AlgCode); i++ {
|
||||||
if a, ok := HashToString[alg]; ok {
|
if a, ok := HashToString[e.AlgCode[i]]; ok {
|
||||||
s += " " + a
|
s += " " + a
|
||||||
} else {
|
} else {
|
||||||
s += " " + strconv.Itoa(int(alg))
|
s += " " + strconv.Itoa(int(e.AlgCode[i]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
func (e *EDNS0_N3U) copy() EDNS0 { return &EDNS0_N3U{e.Code, e.AlgCode} }
|
|
||||||
|
|
||||||
// EDNS0_EXPIRE implementes the EDNS0 option as described in RFC 7314.
|
|
||||||
type EDNS0_EXPIRE struct {
|
type EDNS0_EXPIRE struct {
|
||||||
Code uint16 // Always EDNS0EXPIRE
|
Code uint16 // Always EDNS0EXPIRE
|
||||||
Expire uint32
|
Expire uint32
|
||||||
|
@ -522,11 +495,13 @@ type EDNS0_EXPIRE struct {
|
||||||
// Option implements the EDNS0 interface.
|
// Option implements the EDNS0 interface.
|
||||||
func (e *EDNS0_EXPIRE) Option() uint16 { return EDNS0EXPIRE }
|
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) String() string { return strconv.FormatUint(uint64(e.Expire), 10) }
|
||||||
func (e *EDNS0_EXPIRE) copy() EDNS0 { return &EDNS0_EXPIRE{e.Code, e.Expire} }
|
|
||||||
|
|
||||||
func (e *EDNS0_EXPIRE) pack() ([]byte, error) {
|
func (e *EDNS0_EXPIRE) pack() ([]byte, error) {
|
||||||
b := make([]byte, 4)
|
b := make([]byte, 4)
|
||||||
binary.BigEndian.PutUint32(b, e.Expire)
|
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
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -561,11 +536,6 @@ func (e *EDNS0_LOCAL) Option() uint16 { return e.Code }
|
||||||
func (e *EDNS0_LOCAL) String() string {
|
func (e *EDNS0_LOCAL) String() string {
|
||||||
return strconv.FormatInt(int64(e.Code), 10) + ":0x" + hex.EncodeToString(e.Data)
|
return strconv.FormatInt(int64(e.Code), 10) + ":0x" + hex.EncodeToString(e.Data)
|
||||||
}
|
}
|
||||||
func (e *EDNS0_LOCAL) copy() EDNS0 {
|
|
||||||
b := make([]byte, len(e.Data))
|
|
||||||
copy(b, e.Data)
|
|
||||||
return &EDNS0_LOCAL{e.Code, b}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *EDNS0_LOCAL) pack() ([]byte, error) {
|
func (e *EDNS0_LOCAL) pack() ([]byte, error) {
|
||||||
b := make([]byte, len(e.Data))
|
b := make([]byte, len(e.Data))
|
||||||
|
@ -638,7 +608,6 @@ func (e *EDNS0_TCP_KEEPALIVE) String() (s string) {
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
func (e *EDNS0_TCP_KEEPALIVE) copy() EDNS0 { return &EDNS0_TCP_KEEPALIVE{e.Code, e.Length, e.Timeout} }
|
|
||||||
|
|
||||||
// EDNS0_PADDING option is used to add padding to a request/response. The default
|
// 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
|
// value of padding SHOULD be 0x0 but other values MAY be used, for instance if
|
||||||
|
@ -652,8 +621,3 @@ func (e *EDNS0_PADDING) Option() uint16 { return EDNS0PADDING }
|
||||||
func (e *EDNS0_PADDING) pack() ([]byte, error) { return e.Padding, nil }
|
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) unpack(b []byte) error { e.Padding = b; return nil }
|
||||||
func (e *EDNS0_PADDING) String() string { return fmt.Sprintf("%0X", e.Padding) }
|
func (e *EDNS0_PADDING) String() string { return fmt.Sprintf("%0X", e.Padding) }
|
||||||
func (e *EDNS0_PADDING) copy() EDNS0 {
|
|
||||||
b := make([]byte, len(e.Padding))
|
|
||||||
copy(b, e.Padding)
|
|
||||||
return &EDNS0_PADDING{b}
|
|
||||||
}
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ func Field(r RR, i int) string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
d := reflect.ValueOf(r).Elem().Field(i)
|
d := reflect.ValueOf(r).Elem().Field(i)
|
||||||
switch d.Kind() {
|
switch k := d.Kind(); k {
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
return d.String()
|
return d.String()
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
@ -31,9 +31,6 @@ func Field(r RR, i int) string {
|
||||||
switch reflect.ValueOf(r).Elem().Type().Field(i).Tag {
|
switch reflect.ValueOf(r).Elem().Type().Field(i).Tag {
|
||||||
case `dns:"a"`:
|
case `dns:"a"`:
|
||||||
// TODO(miek): Hmm store this as 16 bytes
|
// TODO(miek): Hmm store this as 16 bytes
|
||||||
if d.Len() < net.IPv4len {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
if d.Len() < net.IPv6len {
|
if d.Len() < net.IPv6len {
|
||||||
return net.IPv4(byte(d.Index(0).Uint()),
|
return net.IPv4(byte(d.Index(0).Uint()),
|
||||||
byte(d.Index(1).Uint()),
|
byte(d.Index(1).Uint()),
|
||||||
|
@ -45,9 +42,6 @@ func Field(r RR, i int) string {
|
||||||
byte(d.Index(14).Uint()),
|
byte(d.Index(14).Uint()),
|
||||||
byte(d.Index(15).Uint())).String()
|
byte(d.Index(15).Uint())).String()
|
||||||
case `dns:"aaaa"`:
|
case `dns:"aaaa"`:
|
||||||
if d.Len() < net.IPv6len {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return net.IP{
|
return net.IP{
|
||||||
byte(d.Index(0).Uint()),
|
byte(d.Index(0).Uint()),
|
||||||
byte(d.Index(1).Uint()),
|
byte(d.Index(1).Uint()),
|
||||||
|
|
|
@ -2,8 +2,8 @@ package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
@ -18,225 +18,142 @@ import (
|
||||||
// * rhs (rdata)
|
// * rhs (rdata)
|
||||||
// But we are lazy here, only the range is parsed *all* occurrences
|
// But we are lazy here, only the range is parsed *all* occurrences
|
||||||
// of $ after that are interpreted.
|
// of $ after that are interpreted.
|
||||||
func (zp *ZoneParser) generate(l lex) (RR, bool) {
|
// Any error are returned as a string value, the empty string signals
|
||||||
token := l.token
|
// "no error".
|
||||||
|
func generate(l lex, c chan lex, t chan *Token, o string) string {
|
||||||
step := 1
|
step := 1
|
||||||
if i := strings.IndexByte(token, '/'); i >= 0 {
|
if i := strings.IndexAny(l.token, "/"); i != -1 {
|
||||||
if i+1 == len(token) {
|
if i+1 == len(l.token) {
|
||||||
return zp.setParseError("bad step in $GENERATE range", l)
|
return "bad step in $GENERATE range"
|
||||||
}
|
}
|
||||||
|
if s, err := strconv.Atoi(l.token[i+1:]); err == nil {
|
||||||
s, err := strconv.Atoi(token[i+1:])
|
if s < 0 {
|
||||||
if err != nil || s <= 0 {
|
return "bad step in $GENERATE range"
|
||||||
return zp.setParseError("bad step in $GENERATE range", l)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
step = s
|
step = s
|
||||||
token = token[:i]
|
} else {
|
||||||
|
return "bad step in $GENERATE range"
|
||||||
}
|
}
|
||||||
|
l.token = l.token[:i]
|
||||||
sx := strings.SplitN(token, "-", 2)
|
}
|
||||||
|
sx := strings.SplitN(l.token, "-", 2)
|
||||||
if len(sx) != 2 {
|
if len(sx) != 2 {
|
||||||
return zp.setParseError("bad start-stop in $GENERATE range", l)
|
return "bad start-stop in $GENERATE range"
|
||||||
}
|
}
|
||||||
|
|
||||||
start, err := strconv.Atoi(sx[0])
|
start, err := strconv.Atoi(sx[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return zp.setParseError("bad start in $GENERATE range", l)
|
return "bad start in $GENERATE range"
|
||||||
}
|
}
|
||||||
|
|
||||||
end, err := strconv.Atoi(sx[1])
|
end, err := strconv.Atoi(sx[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return zp.setParseError("bad stop in $GENERATE range", l)
|
return "bad stop in $GENERATE range"
|
||||||
}
|
}
|
||||||
if end < 0 || start < 0 || end < start {
|
if end < 0 || start < 0 || end < start {
|
||||||
return zp.setParseError("bad range in $GENERATE range", l)
|
return "bad range in $GENERATE range"
|
||||||
}
|
}
|
||||||
|
|
||||||
zp.c.Next() // _BLANK
|
<-c // _BLANK
|
||||||
|
|
||||||
// Create a complete new string, which we then parse again.
|
// Create a complete new string, which we then parse again.
|
||||||
var s string
|
s := ""
|
||||||
for l, ok := zp.c.Next(); ok; l, ok = zp.c.Next() {
|
BuildRR:
|
||||||
if l.err {
|
l = <-c
|
||||||
return zp.setParseError("bad data in $GENERATE directive", l)
|
if l.value != zNewline && l.value != zEOF {
|
||||||
}
|
|
||||||
if l.value == zNewline {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
s += l.token
|
s += l.token
|
||||||
|
goto BuildRR
|
||||||
}
|
}
|
||||||
|
for i := start; i <= end; i += step {
|
||||||
r := &generateReader{
|
var (
|
||||||
s: s,
|
|
||||||
|
|
||||||
cur: start,
|
|
||||||
start: start,
|
|
||||||
end: end,
|
|
||||||
step: step,
|
|
||||||
|
|
||||||
file: zp.file,
|
|
||||||
lex: &l,
|
|
||||||
}
|
|
||||||
zp.sub = NewZoneParser(r, zp.origin, zp.file)
|
|
||||||
zp.sub.includeDepth, zp.sub.includeAllowed = zp.includeDepth, zp.includeAllowed
|
|
||||||
zp.sub.SetDefaultTTL(defaultTtl)
|
|
||||||
return zp.subNext()
|
|
||||||
}
|
|
||||||
|
|
||||||
type generateReader struct {
|
|
||||||
s string
|
|
||||||
si int
|
|
||||||
|
|
||||||
cur int
|
|
||||||
start int
|
|
||||||
end int
|
|
||||||
step int
|
|
||||||
|
|
||||||
mod bytes.Buffer
|
|
||||||
|
|
||||||
escape bool
|
escape bool
|
||||||
|
dom bytes.Buffer
|
||||||
|
mod string
|
||||||
|
err error
|
||||||
|
offset int
|
||||||
|
)
|
||||||
|
|
||||||
eof bool
|
for j := 0; j < len(s); j++ { // No 'range' because we need to jump around
|
||||||
|
switch s[j] {
|
||||||
file string
|
|
||||||
lex *lex
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *generateReader) parseError(msg string, end int) *ParseError {
|
|
||||||
r.eof = true // Make errors sticky.
|
|
||||||
|
|
||||||
l := *r.lex
|
|
||||||
l.token = r.s[r.si-1 : end]
|
|
||||||
l.column += r.si // l.column starts one zBLANK before r.s
|
|
||||||
|
|
||||||
return &ParseError{r.file, msg, l}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *generateReader) Read(p []byte) (int, error) {
|
|
||||||
// NewZLexer, through NewZoneParser, should use ReadByte and
|
|
||||||
// not end up here.
|
|
||||||
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *generateReader) ReadByte() (byte, error) {
|
|
||||||
if r.eof {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
if r.mod.Len() > 0 {
|
|
||||||
return r.mod.ReadByte()
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.si >= len(r.s) {
|
|
||||||
r.si = 0
|
|
||||||
r.cur += r.step
|
|
||||||
|
|
||||||
r.eof = r.cur > r.end || r.cur < 0
|
|
||||||
return '\n', nil
|
|
||||||
}
|
|
||||||
|
|
||||||
si := r.si
|
|
||||||
r.si++
|
|
||||||
|
|
||||||
switch r.s[si] {
|
|
||||||
case '\\':
|
case '\\':
|
||||||
if r.escape {
|
if escape {
|
||||||
r.escape = false
|
dom.WriteByte('\\')
|
||||||
return '\\', nil
|
escape = false
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
escape = true
|
||||||
r.escape = true
|
|
||||||
return r.ReadByte()
|
|
||||||
case '$':
|
case '$':
|
||||||
if r.escape {
|
mod = "%d"
|
||||||
r.escape = false
|
offset = 0
|
||||||
return '$', nil
|
if escape {
|
||||||
|
dom.WriteByte('$')
|
||||||
|
escape = false
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
escape = false
|
||||||
mod := "%d"
|
if j+1 >= len(s) { // End of the string
|
||||||
|
dom.WriteString(fmt.Sprintf(mod, i+offset))
|
||||||
if si >= len(r.s)-1 {
|
continue
|
||||||
// End of the string
|
} else {
|
||||||
fmt.Fprintf(&r.mod, mod, r.cur)
|
if s[j+1] == '$' {
|
||||||
return r.mod.ReadByte()
|
dom.WriteByte('$')
|
||||||
|
j++
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.s[si+1] == '$' {
|
|
||||||
r.si++
|
|
||||||
return '$', nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var offset int
|
|
||||||
|
|
||||||
// Search for { and }
|
// Search for { and }
|
||||||
if r.s[si+1] == '{' {
|
if s[j+1] == '{' { // Modifier block
|
||||||
// Modifier block
|
sep := strings.Index(s[j+2:], "}")
|
||||||
sep := strings.Index(r.s[si+2:], "}")
|
if sep == -1 {
|
||||||
if sep < 0 {
|
return "bad modifier in $GENERATE"
|
||||||
return 0, r.parseError("bad modifier in $GENERATE", len(r.s))
|
|
||||||
}
|
}
|
||||||
|
mod, offset, err = modToPrintf(s[j+2 : j+2+sep])
|
||||||
var errMsg string
|
if err != nil {
|
||||||
mod, offset, errMsg = modToPrintf(r.s[si+2 : si+2+sep])
|
return err.Error()
|
||||||
if errMsg != "" {
|
|
||||||
return 0, r.parseError(errMsg, si+3+sep)
|
|
||||||
}
|
}
|
||||||
if r.start+offset < 0 || r.end+offset > 1<<31-1 {
|
j += 2 + sep // Jump to it
|
||||||
return 0, r.parseError("bad offset in $GENERATE", si+3+sep)
|
|
||||||
}
|
}
|
||||||
|
dom.WriteString(fmt.Sprintf(mod, i+offset))
|
||||||
r.si += 2 + sep // Jump to it
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(&r.mod, mod, r.cur+offset)
|
|
||||||
return r.mod.ReadByte()
|
|
||||||
default:
|
default:
|
||||||
if r.escape { // Pretty useless here
|
if escape { // Pretty useless here
|
||||||
r.escape = false
|
escape = false
|
||||||
return r.ReadByte()
|
continue
|
||||||
}
|
}
|
||||||
|
dom.WriteByte(s[j])
|
||||||
return r.s[si], nil
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// 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.
|
// Convert a $GENERATE modifier 0,0,d to something Printf can deal with.
|
||||||
func modToPrintf(s string) (string, int, string) {
|
func modToPrintf(s string) (string, int, error) {
|
||||||
// Modifier is { offset [ ,width [ ,base ] ] } - provide default
|
xs := strings.SplitN(s, ",", 3)
|
||||||
// values for optional width and type, if necessary.
|
if len(xs) != 3 {
|
||||||
var offStr, widthStr, base string
|
return "", 0, errors.New("bad modifier in $GENERATE")
|
||||||
switch xs := strings.Split(s, ","); len(xs) {
|
|
||||||
case 1:
|
|
||||||
offStr, widthStr, base = xs[0], "0", "d"
|
|
||||||
case 2:
|
|
||||||
offStr, widthStr, base = xs[0], xs[1], "d"
|
|
||||||
case 3:
|
|
||||||
offStr, widthStr, base = xs[0], xs[1], xs[2]
|
|
||||||
default:
|
|
||||||
return "", 0, "bad modifier in $GENERATE"
|
|
||||||
}
|
}
|
||||||
|
// xs[0] is offset, xs[1] is width, xs[2] is base
|
||||||
switch base {
|
if xs[2] != "o" && xs[2] != "d" && xs[2] != "x" && xs[2] != "X" {
|
||||||
case "o", "d", "x", "X":
|
return "", 0, errors.New("bad base in $GENERATE")
|
||||||
default:
|
|
||||||
return "", 0, "bad base in $GENERATE"
|
|
||||||
}
|
}
|
||||||
|
offset, err := strconv.Atoi(xs[0])
|
||||||
offset, err := strconv.Atoi(offStr)
|
if err != nil || offset > 255 {
|
||||||
if err != nil {
|
return "", 0, errors.New("bad offset in $GENERATE")
|
||||||
return "", 0, "bad offset in $GENERATE"
|
|
||||||
}
|
}
|
||||||
|
width, err := strconv.Atoi(xs[1])
|
||||||
width, err := strconv.Atoi(widthStr)
|
if err != nil || width > 255 {
|
||||||
if err != nil || width < 0 || width > 255 {
|
return "", offset, errors.New("bad width in $GENERATE")
|
||||||
return "", 0, "bad width in $GENERATE"
|
|
||||||
}
|
}
|
||||||
|
switch {
|
||||||
if width == 0 {
|
case width < 0:
|
||||||
return "%" + base, offset, ""
|
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
|
||||||
return "%0" + widthStr + base, offset, ""
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
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) }
|
|
@ -1,12 +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 arm mips mipsle 386
|
// +build arm mips mipsle 386
|
||||||
// +build linux
|
// +build linux
|
||||||
|
|
||||||
package socket
|
package socket
|
||||||
|
|
||||||
|
type cmsghdr struct {
|
||||||
|
Len uint32
|
||||||
|
Level int32
|
||||||
|
Type int32
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
sizeofCmsghdr = 0xc
|
||||||
|
)
|
||||||
|
|
||||||
func (h *cmsghdr) set(l, lvl, typ int) {
|
func (h *cmsghdr) set(l, lvl, typ int) {
|
||||||
h.Len = uint32(l)
|
h.Len = uint32(l)
|
||||||
h.Level = int32(lvl)
|
h.Level = int32(lvl)
|
|
@ -1,12 +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 arm64 amd64 ppc64 ppc64le mips64 mips64le s390x
|
// +build arm64 amd64 ppc64 ppc64le mips64 mips64le s390x
|
||||||
// +build linux
|
// +build linux
|
||||||
|
|
||||||
package socket
|
package socket
|
||||||
|
|
||||||
|
type cmsghdr struct {
|
||||||
|
Len uint64
|
||||||
|
Level int32
|
||||||
|
Type int32
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
sizeofCmsghdr = 0x10
|
||||||
|
)
|
||||||
|
|
||||||
func (h *cmsghdr) set(l, lvl, typ int) {
|
func (h *cmsghdr) set(l, lvl, typ int) {
|
||||||
h.Len = uint64(l)
|
h.Len = uint64(l)
|
||||||
h.Level = int32(lvl)
|
h.Level = int32(lvl)
|
|
@ -0,0 +1,13 @@
|
||||||
|
// +build !linux
|
||||||
|
|
||||||
|
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,118 @@
|
||||||
|
package socket
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func controlHeaderLen() int {
|
||||||
|
return roundup(sizeofCmsghdr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func controlMessageLen(dataLen int) int {
|
||||||
|
return roundup(sizeofCmsghdr) + dataLen
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
func (m ControlMessage) Data(dataLen int) []byte {
|
||||||
|
l := controlHeaderLen()
|
||||||
|
if len(m) < l || len(m) < l+dataLen {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return m[l : l+dataLen]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseHeader parses and returns the header fields of the control
|
||||||
|
// message at the head.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next returns the control message at the next.
|
||||||
|
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.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal marshals the control message at the head, 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 as a single or multiple control 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")
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
// Package socket contains ControlMessage parsing code from
|
||||||
|
// golang.org/x/net/internal/socket. Instead of supporting all possible
|
||||||
|
// architectures, we're only supporting linux 32/64 bit.
|
||||||
|
package socket
|
|
@ -0,0 +1,14 @@
|
||||||
|
package socket
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
var (
|
||||||
|
kernelAlign = func() int {
|
||||||
|
var p uintptr
|
||||||
|
return int(unsafe.Sizeof(p))
|
||||||
|
}()
|
||||||
|
)
|
||||||
|
|
||||||
|
func roundup(l int) int {
|
||||||
|
return (l + kernelAlign - 1) & ^(kernelAlign - 1)
|
||||||
|
}
|
|
@ -16,7 +16,7 @@ func SplitDomainName(s string) (labels []string) {
|
||||||
fqdnEnd := 0 // offset of the final '.' or the length of the name
|
fqdnEnd := 0 // offset of the final '.' or the length of the name
|
||||||
idx := Split(s)
|
idx := Split(s)
|
||||||
begin := 0
|
begin := 0
|
||||||
if IsFqdn(s) {
|
if s[len(s)-1] == '.' {
|
||||||
fqdnEnd = len(s) - 1
|
fqdnEnd = len(s) - 1
|
||||||
} else {
|
} else {
|
||||||
fqdnEnd = len(s)
|
fqdnEnd = len(s)
|
||||||
|
@ -28,13 +28,16 @@ func SplitDomainName(s string) (labels []string) {
|
||||||
case 1:
|
case 1:
|
||||||
// no-op
|
// no-op
|
||||||
default:
|
default:
|
||||||
for _, end := range idx[1:] {
|
end := 0
|
||||||
|
for i := 1; i < len(idx); i++ {
|
||||||
|
end = idx[i]
|
||||||
labels = append(labels, s[begin:end-1])
|
labels = append(labels, s[begin:end-1])
|
||||||
begin = end
|
begin = end
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return append(labels, s[begin:fqdnEnd])
|
labels = append(labels, s[begin:fqdnEnd])
|
||||||
|
return labels
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompareDomainName compares the names s1 and s2 and
|
// CompareDomainName compares the names s1 and s2 and
|
||||||
|
@ -175,10 +178,10 @@ func equal(a, b string) bool {
|
||||||
ai := a[i]
|
ai := a[i]
|
||||||
bi := b[i]
|
bi := b[i]
|
||||||
if ai >= 'A' && ai <= 'Z' {
|
if ai >= 'A' && ai <= 'Z' {
|
||||||
ai |= 'a' - 'A'
|
ai |= ('a' - 'A')
|
||||||
}
|
}
|
||||||
if bi >= 'A' && bi <= 'Z' {
|
if bi >= 'A' && bi <= 'Z' {
|
||||||
bi |= 'a' - 'A'
|
bi |= ('a' - 'A')
|
||||||
}
|
}
|
||||||
if ai != bi {
|
if ai != bi {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
// +build go1.11
|
|
||||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
// +build !go1.11 !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd
|
|
||||||
|
|
||||||
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
Load Diff
|
@ -18,7 +18,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var packageHdr = `
|
var packageHdr = `
|
||||||
// Code generated by "go run msg_generate.go"; DO NOT EDIT.
|
// *** DO NOT MODIFY ***
|
||||||
|
// AUTOGENERATED BY go generate from msg_generate.go
|
||||||
|
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
|
@ -80,7 +81,13 @@ func main() {
|
||||||
o := scope.Lookup(name)
|
o := scope.Lookup(name)
|
||||||
st, _ := getTypeStruct(o.Type(), scope)
|
st, _ := getTypeStruct(o.Type(), scope)
|
||||||
|
|
||||||
fmt.Fprintf(b, "func (rr *%s) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n", name)
|
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++ {
|
for i := 1; i < st.NumFields(); i++ {
|
||||||
o := func(s string) {
|
o := func(s string) {
|
||||||
fmt.Fprintf(b, s, st.Field(i).Name())
|
fmt.Fprintf(b, s, st.Field(i).Name())
|
||||||
|
@ -100,7 +107,7 @@ return off, err
|
||||||
case `dns:"nsec"`:
|
case `dns:"nsec"`:
|
||||||
o("off, err = packDataNsec(rr.%s, msg, off)\n")
|
o("off, err = packDataNsec(rr.%s, msg, off)\n")
|
||||||
case `dns:"domain-name"`:
|
case `dns:"domain-name"`:
|
||||||
o("off, err = packDataDomainNames(rr.%s, msg, off, compression, false)\n")
|
o("off, err = packDataDomainNames(rr.%s, msg, off, compression, compress)\n")
|
||||||
default:
|
default:
|
||||||
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
|
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
|
||||||
}
|
}
|
||||||
|
@ -110,9 +117,9 @@ return off, err
|
||||||
switch {
|
switch {
|
||||||
case st.Tag(i) == `dns:"-"`: // ignored
|
case st.Tag(i) == `dns:"-"`: // ignored
|
||||||
case st.Tag(i) == `dns:"cdomain-name"`:
|
case st.Tag(i) == `dns:"cdomain-name"`:
|
||||||
o("off, err = packDomainName(rr.%s, msg, off, compression, compress)\n")
|
o("off, err = PackDomainName(rr.%s, msg, off, compression, compress)\n")
|
||||||
case st.Tag(i) == `dns:"domain-name"`:
|
case st.Tag(i) == `dns:"domain-name"`:
|
||||||
o("off, err = packDomainName(rr.%s, msg, off, compression, false)\n")
|
o("off, err = PackDomainName(rr.%s, msg, off, compression, false)\n")
|
||||||
case st.Tag(i) == `dns:"a"`:
|
case st.Tag(i) == `dns:"a"`:
|
||||||
o("off, err = packDataA(rr.%s, msg, off)\n")
|
o("off, err = packDataA(rr.%s, msg, off)\n")
|
||||||
case st.Tag(i) == `dns:"aaaa"`:
|
case st.Tag(i) == `dns:"aaaa"`:
|
||||||
|
@ -148,8 +155,7 @@ if rr.%s != "-" {
|
||||||
fallthrough
|
fallthrough
|
||||||
case st.Tag(i) == `dns:"hex"`:
|
case st.Tag(i) == `dns:"hex"`:
|
||||||
o("off, err = packStringHex(rr.%s, msg, off)\n")
|
o("off, err = packStringHex(rr.%s, msg, off)\n")
|
||||||
case st.Tag(i) == `dns:"any"`:
|
|
||||||
o("off, err = packStringAny(rr.%s, msg, off)\n")
|
|
||||||
case st.Tag(i) == `dns:"octet"`:
|
case st.Tag(i) == `dns:"octet"`:
|
||||||
o("off, err = packStringOctet(rr.%s, msg, off)\n")
|
o("off, err = packStringOctet(rr.%s, msg, off)\n")
|
||||||
case st.Tag(i) == "":
|
case st.Tag(i) == "":
|
||||||
|
@ -171,6 +177,8 @@ if rr.%s != "-" {
|
||||||
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
|
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.Fprintln(b, "return off, nil }\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,8 +187,14 @@ if rr.%s != "-" {
|
||||||
o := scope.Lookup(name)
|
o := scope.Lookup(name)
|
||||||
st, _ := getTypeStruct(o.Type(), scope)
|
st, _ := getTypeStruct(o.Type(), scope)
|
||||||
|
|
||||||
fmt.Fprintf(b, "func (rr *%s) unpack(msg []byte, off int) (off1 int, err error) {\n", name)
|
fmt.Fprintf(b, "func unpack%s(h RR_Header, msg []byte, off int) (RR, int, error) {\n", name)
|
||||||
fmt.Fprint(b, `rdStart := off
|
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
|
_ = rdStart
|
||||||
|
|
||||||
`)
|
`)
|
||||||
|
@ -188,7 +202,7 @@ _ = rdStart
|
||||||
o := func(s string) {
|
o := func(s string) {
|
||||||
fmt.Fprintf(b, s, st.Field(i).Name())
|
fmt.Fprintf(b, s, st.Field(i).Name())
|
||||||
fmt.Fprint(b, `if err != nil {
|
fmt.Fprint(b, `if err != nil {
|
||||||
return off, err
|
return rr, off, err
|
||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
@ -208,7 +222,7 @@ return off, err
|
||||||
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
|
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
|
||||||
}
|
}
|
||||||
fmt.Fprint(b, `if err != nil {
|
fmt.Fprint(b, `if err != nil {
|
||||||
return off, err
|
return rr, off, err
|
||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
continue
|
continue
|
||||||
|
@ -251,8 +265,6 @@ return off, err
|
||||||
o("rr.%s, off, err = unpackStringBase64(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
|
o("rr.%s, off, err = unpackStringBase64(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
|
||||||
case `dns:"hex"`:
|
case `dns:"hex"`:
|
||||||
o("rr.%s, off, err = unpackStringHex(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
|
o("rr.%s, off, err = unpackStringHex(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
|
||||||
case `dns:"any"`:
|
|
||||||
o("rr.%s, off, err = unpackStringAny(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
|
|
||||||
case `dns:"octet"`:
|
case `dns:"octet"`:
|
||||||
o("rr.%s, off, err = unpackStringOctet(msg, off)\n")
|
o("rr.%s, off, err = unpackStringOctet(msg, off)\n")
|
||||||
case "":
|
case "":
|
||||||
|
@ -276,13 +288,22 @@ return off, err
|
||||||
// If we've hit len(msg) we return without error.
|
// If we've hit len(msg) we return without error.
|
||||||
if i < st.NumFields()-1 {
|
if i < st.NumFields()-1 {
|
||||||
fmt.Fprintf(b, `if off == len(msg) {
|
fmt.Fprintf(b, `if off == len(msg) {
|
||||||
return off, nil
|
return rr, off, nil
|
||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Fprintf(b, "return off, nil }\n\n")
|
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
|
// gofmt
|
||||||
res, err := format.Source(b.Bytes())
|
res, err := format.Source(b.Bytes())
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// helper functions called from the generated zmsg.go
|
// helper functions called from the generated zmsg.go
|
||||||
|
@ -25,13 +25,12 @@ func unpackDataA(msg []byte, off int) (net.IP, int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func packDataA(a net.IP, msg []byte, off int) (int, error) {
|
func packDataA(a net.IP, msg []byte, off int) (int, error) {
|
||||||
switch len(a) {
|
|
||||||
case net.IPv4len, net.IPv6len:
|
|
||||||
// It must be a slice of 4, even if it is 16, we encode only the first 4
|
// It must be a slice of 4, even if it is 16, we encode only the first 4
|
||||||
if off+net.IPv4len > len(msg) {
|
if off+net.IPv4len > len(msg) {
|
||||||
return len(msg), &Error{err: "overflow packing a"}
|
return len(msg), &Error{err: "overflow packing a"}
|
||||||
}
|
}
|
||||||
|
switch len(a) {
|
||||||
|
case net.IPv4len, net.IPv6len:
|
||||||
copy(msg[off:], a.To4())
|
copy(msg[off:], a.To4())
|
||||||
off += net.IPv4len
|
off += net.IPv4len
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -52,12 +51,12 @@ func unpackDataAAAA(msg []byte, off int) (net.IP, int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func packDataAAAA(aaaa net.IP, msg []byte, off int) (int, error) {
|
func packDataAAAA(aaaa net.IP, msg []byte, off int) (int, error) {
|
||||||
switch len(aaaa) {
|
|
||||||
case net.IPv6len:
|
|
||||||
if off+net.IPv6len > len(msg) {
|
if off+net.IPv6len > len(msg) {
|
||||||
return len(msg), &Error{err: "overflow packing aaaa"}
|
return len(msg), &Error{err: "overflow packing aaaa"}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch len(aaaa) {
|
||||||
|
case net.IPv6len:
|
||||||
copy(msg[off:], aaaa)
|
copy(msg[off:], aaaa)
|
||||||
off += net.IPv6len
|
off += net.IPv6len
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -100,14 +99,14 @@ func unpackHeader(msg []byte, off int) (rr RR_Header, off1 int, truncmsg []byte,
|
||||||
return hdr, off, msg, err
|
return hdr, off, msg, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// packHeader packs an RR header, returning the offset to the end of the header.
|
// pack packs an RR header, returning the offset to the end of the header.
|
||||||
// See PackDomainName for documentation about the compression.
|
// See PackDomainName for documentation about the compression.
|
||||||
func (hdr RR_Header) packHeader(msg []byte, off int, compression compressionMap, compress bool) (int, error) {
|
func (hdr RR_Header) pack(msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) {
|
||||||
if off == len(msg) {
|
if off == len(msg) {
|
||||||
return off, nil
|
return off, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
off, err := packDomainName(hdr.Name, msg, off, compression, compress)
|
off, err = PackDomainName(hdr.Name, msg, off, compression, compress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return len(msg), err
|
return len(msg), err
|
||||||
}
|
}
|
||||||
|
@ -123,7 +122,7 @@ func (hdr RR_Header) packHeader(msg []byte, off int, compression compressionMap,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return len(msg), err
|
return len(msg), err
|
||||||
}
|
}
|
||||||
off, err = packUint16(0, msg, off) // The RDLENGTH field will be set later in packRR.
|
off, err = packUint16(hdr.Rdlength, msg, off)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return len(msg), err
|
return len(msg), err
|
||||||
}
|
}
|
||||||
|
@ -142,24 +141,20 @@ func truncateMsgFromRdlength(msg []byte, off int, rdlength uint16) (truncmsg []b
|
||||||
return msg[:lenrd], nil
|
return msg[:lenrd], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var base32HexNoPadEncoding = base32.HexEncoding.WithPadding(base32.NoPadding)
|
|
||||||
|
|
||||||
func fromBase32(s []byte) (buf []byte, err error) {
|
func fromBase32(s []byte) (buf []byte, err error) {
|
||||||
for i, b := range s {
|
for i, b := range s {
|
||||||
if b >= 'a' && b <= 'z' {
|
if b >= 'a' && b <= 'z' {
|
||||||
s[i] = b - 32
|
s[i] = b - 32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buflen := base32HexNoPadEncoding.DecodedLen(len(s))
|
buflen := base32.HexEncoding.DecodedLen(len(s))
|
||||||
buf = make([]byte, buflen)
|
buf = make([]byte, buflen)
|
||||||
n, err := base32HexNoPadEncoding.Decode(buf, s)
|
n, err := base32.HexEncoding.Decode(buf, s)
|
||||||
buf = buf[:n]
|
buf = buf[:n]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func toBase32(b []byte) string {
|
func toBase32(b []byte) string { return base32.HexEncoding.EncodeToString(b) }
|
||||||
return base32HexNoPadEncoding.EncodeToString(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func fromBase64(s []byte) (buf []byte, err error) {
|
func fromBase64(s []byte) (buf []byte, err error) {
|
||||||
buflen := base64.StdEncoding.DecodedLen(len(s))
|
buflen := base64.StdEncoding.DecodedLen(len(s))
|
||||||
|
@ -178,14 +173,14 @@ func unpackUint8(msg []byte, off int) (i uint8, off1 int, err error) {
|
||||||
if off+1 > len(msg) {
|
if off+1 > len(msg) {
|
||||||
return 0, len(msg), &Error{err: "overflow unpacking uint8"}
|
return 0, len(msg), &Error{err: "overflow unpacking uint8"}
|
||||||
}
|
}
|
||||||
return msg[off], off + 1, nil
|
return uint8(msg[off]), off + 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func packUint8(i uint8, msg []byte, off int) (off1 int, err error) {
|
func packUint8(i uint8, msg []byte, off int) (off1 int, err error) {
|
||||||
if off+1 > len(msg) {
|
if off+1 > len(msg) {
|
||||||
return len(msg), &Error{err: "overflow packing uint8"}
|
return len(msg), &Error{err: "overflow packing uint8"}
|
||||||
}
|
}
|
||||||
msg[off] = i
|
msg[off] = byte(i)
|
||||||
return off + 1, nil
|
return off + 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,8 +219,8 @@ func unpackUint48(msg []byte, off int) (i uint64, off1 int, err error) {
|
||||||
return 0, len(msg), &Error{err: "overflow unpacking uint64 as uint48"}
|
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)
|
// Used in TSIG where the last 48 bits are occupied, so for now, assume a uint48 (6 bytes)
|
||||||
i = uint64(msg[off])<<40 | uint64(msg[off+1])<<32 | uint64(msg[off+2])<<24 | uint64(msg[off+3])<<16 |
|
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])
|
uint64(msg[off+4])<<8 | uint64(msg[off+5])))
|
||||||
off += 6
|
off += 6
|
||||||
return i, off, nil
|
return i, off, nil
|
||||||
}
|
}
|
||||||
|
@ -268,21 +263,29 @@ func unpackString(msg []byte, off int) (string, int, error) {
|
||||||
if off+l+1 > len(msg) {
|
if off+l+1 > len(msg) {
|
||||||
return "", off, &Error{err: "overflow unpacking txt"}
|
return "", off, &Error{err: "overflow unpacking txt"}
|
||||||
}
|
}
|
||||||
var s strings.Builder
|
s := make([]byte, 0, l)
|
||||||
s.Grow(l)
|
|
||||||
for _, b := range msg[off+1 : off+1+l] {
|
for _, b := range msg[off+1 : off+1+l] {
|
||||||
switch {
|
switch b {
|
||||||
case b == '"' || b == '\\':
|
case '"', '\\':
|
||||||
s.WriteByte('\\')
|
s = append(s, '\\', b)
|
||||||
s.WriteByte(b)
|
|
||||||
case b < ' ' || b > '~': // unprintable
|
|
||||||
s.WriteString(escapeByte(b))
|
|
||||||
default:
|
default:
|
||||||
s.WriteByte(b)
|
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
|
off += 1 + l
|
||||||
return s.String(), off, nil
|
return string(s), off, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func packString(s string, msg []byte, off int) (int, error) {
|
func packString(s string, msg []byte, off int) (int, error) {
|
||||||
|
@ -356,7 +359,7 @@ func packStringHex(s string, msg []byte, off int) (int, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return len(msg), err
|
return len(msg), err
|
||||||
}
|
}
|
||||||
if off+len(h) > len(msg) {
|
if off+(len(h)) > len(msg) {
|
||||||
return len(msg), &Error{err: "overflow packing hex"}
|
return len(msg), &Error{err: "overflow packing hex"}
|
||||||
}
|
}
|
||||||
copy(msg[off:off+len(h)], h)
|
copy(msg[off:off+len(h)], h)
|
||||||
|
@ -364,22 +367,6 @@ func packStringHex(s string, msg []byte, off int) (int, error) {
|
||||||
return off, nil
|
return off, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func unpackStringAny(msg []byte, off, end int) (string, int, error) {
|
|
||||||
if end > len(msg) {
|
|
||||||
return "", len(msg), &Error{err: "overflow unpacking anything"}
|
|
||||||
}
|
|
||||||
return string(msg[off:end]), end, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func packStringAny(s string, msg []byte, off int) (int, error) {
|
|
||||||
if off+len(s) > len(msg) {
|
|
||||||
return len(msg), &Error{err: "overflow packing anything"}
|
|
||||||
}
|
|
||||||
copy(msg[off:off+len(s)], s)
|
|
||||||
off += len(s)
|
|
||||||
return off, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unpackStringTxt(msg []byte, off int) ([]string, int, error) {
|
func unpackStringTxt(msg []byte, off int) ([]string, int, error) {
|
||||||
txt, off, err := unpackTxt(msg, off)
|
txt, off, err := unpackTxt(msg, off)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -400,7 +387,7 @@ func packStringTxt(s []string, msg []byte, off int) (int, error) {
|
||||||
func unpackDataOpt(msg []byte, off int) ([]EDNS0, int, error) {
|
func unpackDataOpt(msg []byte, off int) ([]EDNS0, int, error) {
|
||||||
var edns []EDNS0
|
var edns []EDNS0
|
||||||
Option:
|
Option:
|
||||||
var code uint16
|
code := uint16(0)
|
||||||
if off+4 > len(msg) {
|
if off+4 > len(msg) {
|
||||||
return nil, len(msg), &Error{err: "overflow unpacking opt"}
|
return nil, len(msg), &Error{err: "overflow unpacking opt"}
|
||||||
}
|
}
|
||||||
|
@ -554,7 +541,8 @@ func unpackDataNsec(msg []byte, off int) ([]uint16, int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Walk the bytes in the window and extract the type bits
|
// Walk the bytes in the window and extract the type bits
|
||||||
for j, b := range msg[off : off+length] {
|
for j := 0; j < length; j++ {
|
||||||
|
b := msg[off+j]
|
||||||
// Check the bits one by one, and set the type
|
// Check the bits one by one, and set the type
|
||||||
if b&0x80 == 0x80 {
|
if b&0x80 == 0x80 {
|
||||||
nsec = append(nsec, uint16(window*256+j*8+0))
|
nsec = append(nsec, uint16(window*256+j*8+0))
|
||||||
|
@ -592,7 +580,8 @@ func packDataNsec(bitmap []uint16, msg []byte, off int) (int, error) {
|
||||||
return off, nil
|
return off, nil
|
||||||
}
|
}
|
||||||
var lastwindow, lastlength uint16
|
var lastwindow, lastlength uint16
|
||||||
for _, t := range bitmap {
|
for j := 0; j < len(bitmap); j++ {
|
||||||
|
t := bitmap[j]
|
||||||
window := t / 256
|
window := t / 256
|
||||||
length := (t-window*256)/8 + 1
|
length := (t-window*256)/8 + 1
|
||||||
if window > lastwindow && lastlength != 0 { // New window, jump to the new offset
|
if window > lastwindow && lastlength != 0 { // New window, jump to the new offset
|
||||||
|
@ -610,7 +599,7 @@ func packDataNsec(bitmap []uint16, msg []byte, off int) (int, error) {
|
||||||
// Setting the octets length
|
// Setting the octets length
|
||||||
msg[off+1] = byte(length)
|
msg[off+1] = byte(length)
|
||||||
// Setting the bit value for the type in the right octet
|
// Setting the bit value for the type in the right octet
|
||||||
msg[off+1+int(length)] |= byte(1 << (7 - t%8))
|
msg[off+1+int(length)] |= byte(1 << (7 - (t % 8)))
|
||||||
lastwindow, lastlength = window, length
|
lastwindow, lastlength = window, length
|
||||||
}
|
}
|
||||||
off += int(lastlength) + 2
|
off += int(lastlength) + 2
|
||||||
|
@ -636,10 +625,10 @@ func unpackDataDomainNames(msg []byte, off, end int) ([]string, int, error) {
|
||||||
return servers, off, nil
|
return servers, off, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func packDataDomainNames(names []string, msg []byte, off int, compression compressionMap, compress bool) (int, error) {
|
func packDataDomainNames(names []string, msg []byte, off int, compression map[string]int, compress bool) (int, error) {
|
||||||
var err error
|
var err error
|
||||||
for _, name := range names {
|
for j := 0; j < len(names); j++ {
|
||||||
off, err = packDomainName(name, msg, off, compression, compress)
|
off, err = PackDomainName(names[j], msg, off, compression, false && compress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return len(msg), err
|
return len(msg), err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,106 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
// Truncate ensures the reply message will fit into the requested buffer
|
|
||||||
// size by removing records that exceed the requested size.
|
|
||||||
//
|
|
||||||
// It will first check if the reply fits without compression and then with
|
|
||||||
// compression. If it won't fit with compression, Scrub then walks the
|
|
||||||
// record adding as many records as possible without exceeding the
|
|
||||||
// requested buffer size.
|
|
||||||
//
|
|
||||||
// The TC bit will be set if any answer records were excluded from the
|
|
||||||
// message. This indicates to that the client should retry over TCP.
|
|
||||||
//
|
|
||||||
// The appropriate buffer size can be retrieved from the requests OPT
|
|
||||||
// record, if present, and is transport specific otherwise. dns.MinMsgSize
|
|
||||||
// should be used for UDP requests without an OPT record, and
|
|
||||||
// dns.MaxMsgSize for TCP requests without an OPT record.
|
|
||||||
func (dns *Msg) Truncate(size int) {
|
|
||||||
if dns.IsTsig() != nil {
|
|
||||||
// To simplify this implementation, we don't perform
|
|
||||||
// truncation on responses with a TSIG record.
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// RFC 6891 mandates that the payload size in an OPT record
|
|
||||||
// less than 512 bytes must be treated as equal to 512 bytes.
|
|
||||||
//
|
|
||||||
// For ease of use, we impose that restriction here.
|
|
||||||
if size < 512 {
|
|
||||||
size = 512
|
|
||||||
}
|
|
||||||
|
|
||||||
l := msgLenWithCompressionMap(dns, nil) // uncompressed length
|
|
||||||
if l <= size {
|
|
||||||
// Don't waste effort compressing this message.
|
|
||||||
dns.Compress = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
dns.Compress = true
|
|
||||||
|
|
||||||
edns0 := dns.popEdns0()
|
|
||||||
if edns0 != nil {
|
|
||||||
// Account for the OPT record that gets added at the end,
|
|
||||||
// by subtracting that length from our budget.
|
|
||||||
//
|
|
||||||
// The EDNS(0) OPT record must have the root domain and
|
|
||||||
// it's length is thus unaffected by compression.
|
|
||||||
size -= Len(edns0)
|
|
||||||
}
|
|
||||||
|
|
||||||
compression := make(map[string]struct{})
|
|
||||||
|
|
||||||
l = headerSize
|
|
||||||
for _, r := range dns.Question {
|
|
||||||
l += r.len(l, compression)
|
|
||||||
}
|
|
||||||
|
|
||||||
var numAnswer int
|
|
||||||
if l < size {
|
|
||||||
l, numAnswer = truncateLoop(dns.Answer, size, l, compression)
|
|
||||||
}
|
|
||||||
|
|
||||||
var numNS int
|
|
||||||
if l < size {
|
|
||||||
l, numNS = truncateLoop(dns.Ns, size, l, compression)
|
|
||||||
}
|
|
||||||
|
|
||||||
var numExtra int
|
|
||||||
if l < size {
|
|
||||||
l, numExtra = truncateLoop(dns.Extra, size, l, compression)
|
|
||||||
}
|
|
||||||
|
|
||||||
// According to RFC 2181, the TC bit should only be set if not all
|
|
||||||
// of the answer RRs can be included in the response.
|
|
||||||
dns.Truncated = len(dns.Answer) > numAnswer
|
|
||||||
|
|
||||||
dns.Answer = dns.Answer[:numAnswer]
|
|
||||||
dns.Ns = dns.Ns[:numNS]
|
|
||||||
dns.Extra = dns.Extra[:numExtra]
|
|
||||||
|
|
||||||
if edns0 != nil {
|
|
||||||
// Add the OPT record back onto the additional section.
|
|
||||||
dns.Extra = append(dns.Extra, edns0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func truncateLoop(rrs []RR, size, l int, compression map[string]struct{}) (int, int) {
|
|
||||||
for i, r := range rrs {
|
|
||||||
if r == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
l += r.len(l, compression)
|
|
||||||
if l > size {
|
|
||||||
// Return size, rather than l prior to this record,
|
|
||||||
// to prevent any further records being added.
|
|
||||||
return size, i
|
|
||||||
}
|
|
||||||
if l == size {
|
|
||||||
return l, i + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return l, len(rrs)
|
|
||||||
}
|
|
|
@ -2,44 +2,49 @@ package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"encoding/hex"
|
"hash"
|
||||||
"strings"
|
"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.
|
// 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 {
|
func HashName(label string, ha uint8, iter uint16, salt string) string {
|
||||||
if ha != SHA1 {
|
saltwire := new(saltWireFmt)
|
||||||
return ""
|
saltwire.Salt = salt
|
||||||
}
|
wire := make([]byte, DefaultMsgSize)
|
||||||
|
n, err := packSaltWire(saltwire, wire)
|
||||||
wireSalt := make([]byte, hex.DecodedLen(len(salt)))
|
|
||||||
n, err := packStringHex(salt, wireSalt, 0)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
wireSalt = wireSalt[:n]
|
wire = wire[:n]
|
||||||
|
|
||||||
name := make([]byte, 255)
|
name := make([]byte, 255)
|
||||||
off, err := PackDomainName(strings.ToLower(label), name, 0, nil, false)
|
off, err := PackDomainName(strings.ToLower(label), name, 0, nil, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
name = name[:off]
|
name = name[:off]
|
||||||
|
var s hash.Hash
|
||||||
|
switch ha {
|
||||||
|
case SHA1:
|
||||||
|
s = sha1.New()
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
s := sha1.New()
|
|
||||||
// k = 0
|
// k = 0
|
||||||
s.Write(name)
|
s.Write(name)
|
||||||
s.Write(wireSalt)
|
s.Write(wire)
|
||||||
nsec3 := s.Sum(nil)
|
nsec3 := s.Sum(nil)
|
||||||
|
|
||||||
// k > 0
|
// k > 0
|
||||||
for k := uint16(0); k < iter; k++ {
|
for k := uint16(0); k < iter; k++ {
|
||||||
s.Reset()
|
s.Reset()
|
||||||
s.Write(nsec3)
|
s.Write(nsec3)
|
||||||
s.Write(wireSalt)
|
s.Write(wire)
|
||||||
nsec3 = s.Sum(nsec3[:0])
|
nsec3 = s.Sum(nsec3[:0])
|
||||||
}
|
}
|
||||||
|
|
||||||
return toBase32(nsec3)
|
return toBase32(nsec3)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,10 +63,8 @@ func (rr *NSEC3) Cover(name string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
nextHash := rr.NextDomain
|
nextHash := rr.NextDomain
|
||||||
|
if ownerHash == nextHash { // empty interval
|
||||||
// if empty interval found, try cover wildcard hashes so nameHash shouldn't match with ownerHash
|
return false
|
||||||
if ownerHash == nextHash && nameHash != ownerHash { // empty interval
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
if ownerHash > nextHash { // end of zone
|
if ownerHash > nextHash { // end of zone
|
||||||
if nameHash > ownerHash { // covered since there is nothing after ownerHash
|
if nameHash > ownerHash { // covered since there is nothing after ownerHash
|
||||||
|
@ -93,3 +96,11 @@ func (rr *NSEC3) Match(name string) bool {
|
||||||
}
|
}
|
||||||
return false
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -39,12 +39,11 @@ func mkPrivateRR(rrtype uint16) *PrivateRR {
|
||||||
}
|
}
|
||||||
|
|
||||||
anyrr := rrfunc()
|
anyrr := rrfunc()
|
||||||
rr, ok := anyrr.(*PrivateRR)
|
switch rr := anyrr.(type) {
|
||||||
if !ok {
|
case *PrivateRR:
|
||||||
panic(fmt.Sprintf("dns: RR is not a PrivateRR, TypeToRR[%d] generator returned %T", rrtype, anyrr))
|
|
||||||
}
|
|
||||||
|
|
||||||
return rr
|
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.
|
// Header return the RR header of r.
|
||||||
|
@ -53,16 +52,12 @@ func (r *PrivateRR) Header() *RR_Header { return &r.Hdr }
|
||||||
func (r *PrivateRR) String() string { return r.Hdr.String() + r.Data.String() }
|
func (r *PrivateRR) String() string { return r.Hdr.String() + r.Data.String() }
|
||||||
|
|
||||||
// Private len and copy parts to satisfy RR interface.
|
// Private len and copy parts to satisfy RR interface.
|
||||||
func (r *PrivateRR) len(off int, compression map[string]struct{}) int {
|
func (r *PrivateRR) len() int { return r.Hdr.len() + r.Data.Len() }
|
||||||
l := r.Hdr.len(off, compression)
|
|
||||||
l += r.Data.Len()
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *PrivateRR) copy() RR {
|
func (r *PrivateRR) copy() RR {
|
||||||
// make new RR like this:
|
// make new RR like this:
|
||||||
rr := mkPrivateRR(r.Hdr.Rrtype)
|
rr := mkPrivateRR(r.Hdr.Rrtype)
|
||||||
rr.Hdr = r.Hdr
|
newh := r.Hdr.copyHeader()
|
||||||
|
rr.Hdr = *newh
|
||||||
|
|
||||||
err := r.Data.Copy(rr.Data)
|
err := r.Data.Copy(rr.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -70,47 +65,21 @@ func (r *PrivateRR) copy() RR {
|
||||||
}
|
}
|
||||||
return rr
|
return rr
|
||||||
}
|
}
|
||||||
|
func (r *PrivateRR) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) {
|
||||||
func (r *PrivateRR) pack(msg []byte, off int, compression compressionMap, 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:])
|
n, err := r.Data.Pack(msg[off:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return len(msg), err
|
return len(msg), err
|
||||||
}
|
}
|
||||||
off += n
|
off += n
|
||||||
|
r.Header().Rdlength = uint16(off - headerEnd)
|
||||||
return off, nil
|
return off, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *PrivateRR) unpack(msg []byte, off int) (int, error) {
|
|
||||||
off1, err := r.Data.Unpack(msg[off:])
|
|
||||||
off += off1
|
|
||||||
return off, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *PrivateRR) parse(c *zlexer, origin, file string) *ParseError {
|
|
||||||
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.Next(); l.value {
|
|
||||||
case zNewline, zEOF:
|
|
||||||
break Fetch
|
|
||||||
case zString:
|
|
||||||
text = append(text, l.token)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := r.Data.Parse(text)
|
|
||||||
if err != nil {
|
|
||||||
return &ParseError{file, err.Error(), l}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r1 *PrivateRR) isDuplicate(r2 RR) bool { return false }
|
|
||||||
|
|
||||||
// PrivateHandle registers a private resource record type. It requires
|
// PrivateHandle registers a private resource record type. It requires
|
||||||
// string and numeric representation of private RR type and generator function as argument.
|
// string and numeric representation of private RR type and generator function as argument.
|
||||||
func PrivateHandle(rtypestr string, rtype uint16, generator func() PrivateRdata) {
|
func PrivateHandle(rtypestr string, rtype uint16, generator func() PrivateRdata) {
|
||||||
|
@ -119,14 +88,62 @@ func PrivateHandle(rtypestr string, rtype uint16, generator func() PrivateRdata)
|
||||||
TypeToRR[rtype] = func() RR { return &PrivateRR{RR_Header{}, generator()} }
|
TypeToRR[rtype] = func() RR { return &PrivateRR{RR_Header{}, generator()} }
|
||||||
TypeToString[rtype] = rtypestr
|
TypeToString[rtype] = rtypestr
|
||||||
StringToType[rtypestr] = rtype
|
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 definitions required to support private RR type.
|
// PrivateHandleRemove removes defenitions required to support private RR type.
|
||||||
func PrivateHandleRemove(rtype uint16) {
|
func PrivateHandleRemove(rtype uint16) {
|
||||||
rtypestr, ok := TypeToString[rtype]
|
rtypestr, ok := TypeToString[rtype]
|
||||||
if ok {
|
if ok {
|
||||||
delete(TypeToRR, rtype)
|
delete(TypeToRR, rtype)
|
||||||
delete(TypeToString, rtype)
|
delete(TypeToString, rtype)
|
||||||
|
delete(typeToparserFunc, rtype)
|
||||||
delete(StringToType, rtypestr)
|
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
|
||||||
|
}
|
|
@ -12,20 +12,6 @@ var StringToOpcode = reverseInt(OpcodeToString)
|
||||||
// StringToRcode is a map of rcodes to strings.
|
// StringToRcode is a map of rcodes to strings.
|
||||||
var StringToRcode = reverseInt(RcodeToString)
|
var StringToRcode = reverseInt(RcodeToString)
|
||||||
|
|
||||||
func init() {
|
|
||||||
// Preserve previous NOTIMP typo, see github.com/miekg/dns/issues/733.
|
|
||||||
StringToRcode["NOTIMPL"] = RcodeNotImplemented
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringToAlgorithm is the reverse of AlgorithmToString.
|
|
||||||
var StringToAlgorithm = reverseInt8(AlgorithmToString)
|
|
||||||
|
|
||||||
// StringToHash is a map of names to hash IDs.
|
|
||||||
var StringToHash = reverseInt8(HashToString)
|
|
||||||
|
|
||||||
// StringToCertType is the reverseof CertTypeToString.
|
|
||||||
var StringToCertType = reverseInt16(CertTypeToString)
|
|
||||||
|
|
||||||
// Reverse a map
|
// Reverse a map
|
||||||
func reverseInt8(m map[uint8]string) map[string]uint8 {
|
func reverseInt8(m map[uint8]string) map[string]uint8 {
|
||||||
n := make(map[string]uint8, len(m))
|
n := make(map[string]uint8, len(m))
|
||||||
|
|
|
@ -3,9 +3,8 @@ package dns
|
||||||
// Dedup removes identical RRs from rrs. It preserves the original ordering.
|
// 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
|
// The lowest TTL of any duplicates is used in the remaining one. Dedup modifies
|
||||||
// rrs.
|
// rrs.
|
||||||
// m is used to store the RRs temporary. If it is nil a new map will be allocated.
|
// m is used to store the RRs temporay. If it is nil a new map will be allocated.
|
||||||
func Dedup(rrs []RR, m map[string]RR) []RR {
|
func Dedup(rrs []RR, m map[string]RR) []RR {
|
||||||
|
|
||||||
if m == nil {
|
if m == nil {
|
||||||
m = make(map[string]RR)
|
m = make(map[string]RR)
|
||||||
}
|
}
|
||||||
|
@ -15,11 +14,10 @@ func Dedup(rrs []RR, m map[string]RR) []RR {
|
||||||
for _, r := range rrs {
|
for _, r := range rrs {
|
||||||
key := normalizedString(r)
|
key := normalizedString(r)
|
||||||
keys = append(keys, &key)
|
keys = append(keys, &key)
|
||||||
if mr, ok := m[key]; ok {
|
if _, ok := m[key]; ok {
|
||||||
// Shortest TTL wins.
|
// Shortest TTL wins.
|
||||||
rh, mrh := r.Header(), mr.Header()
|
if m[key].Header().Ttl > r.Header().Ttl {
|
||||||
if mrh.Ttl > rh.Ttl {
|
m[key].Header().Ttl = r.Header().Ttl
|
||||||
mrh.Ttl = rh.Ttl
|
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||||
|
}
|
|
@ -1,147 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
// The zero ServeMux is empty and ready for use.
|
|
||||||
type ServeMux struct {
|
|
||||||
z map[string]Handler
|
|
||||||
m sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewServeMux allocates and returns a new ServeMux.
|
|
||||||
func NewServeMux() *ServeMux {
|
|
||||||
return new(ServeMux)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultServeMux is the default ServeMux used by Serve.
|
|
||||||
var DefaultServeMux = NewServeMux()
|
|
||||||
|
|
||||||
func (mux *ServeMux) match(q string, t uint16) Handler {
|
|
||||||
mux.m.RLock()
|
|
||||||
defer mux.m.RUnlock()
|
|
||||||
if mux.z == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var handler Handler
|
|
||||||
|
|
||||||
// TODO(tmthrgd): Once https://go-review.googlesource.com/c/go/+/137575
|
|
||||||
// lands in a go release, replace the following with strings.ToLower.
|
|
||||||
var sb strings.Builder
|
|
||||||
for i := 0; i < len(q); i++ {
|
|
||||||
c := q[i]
|
|
||||||
if !(c >= 'A' && c <= 'Z') {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.Grow(len(q))
|
|
||||||
sb.WriteString(q[:i])
|
|
||||||
|
|
||||||
for ; i < len(q); i++ {
|
|
||||||
c := q[i]
|
|
||||||
if c >= 'A' && c <= 'Z' {
|
|
||||||
c += 'a' - 'A'
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.WriteByte(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
q = sb.String()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
for off, end := 0, false; !end; off, end = NextLabel(q, off) {
|
|
||||||
if h, ok := mux.z[q[off:]]; ok {
|
|
||||||
if t != TypeDS {
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
// Continue for DS to see if we have a parent too, if so delegate to the parent
|
|
||||||
handler = h
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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()
|
|
||||||
if mux.z == nil {
|
|
||||||
mux.z = make(map[string]Handler)
|
|
||||||
}
|
|
||||||
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 deregisters 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.
|
|
||||||
//
|
|
||||||
// ServeDNS 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.
|
|
||||||
//
|
|
||||||
// If no handler is found, or there is no question, a standard SERVFAIL
|
|
||||||
// message is returned
|
|
||||||
func (mux *ServeMux) ServeDNS(w ResponseWriter, req *Msg) {
|
|
||||||
var h Handler
|
|
||||||
if len(req.Question) >= 1 { // allow more than one question
|
|
||||||
h = mux.match(req.Question[0].Name, req.Question[0].Qtype)
|
|
||||||
}
|
|
||||||
|
|
||||||
if h != nil {
|
|
||||||
h.ServeDNS(w, req)
|
|
||||||
} else {
|
|
||||||
HandleFailed(w, req)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
|
|
@ -3,40 +3,23 @@
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"bytes"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Default maximum number of TCP queries before we close the socket.
|
// Maximum number of TCP queries before we close the socket.
|
||||||
const maxTCPQueries = 128
|
const maxTCPQueries = 128
|
||||||
|
|
||||||
// aLongTimeAgo is a non-zero time, far in the past, used for
|
|
||||||
// immediate cancelation of network operations.
|
|
||||||
var aLongTimeAgo = time.Unix(1, 0)
|
|
||||||
|
|
||||||
// Handler is implemented by any value that implements ServeDNS.
|
// Handler is implemented by any value that implements ServeDNS.
|
||||||
type Handler interface {
|
type Handler interface {
|
||||||
ServeDNS(w ResponseWriter, r *Msg)
|
ServeDNS(w ResponseWriter, r *Msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A ResponseWriter interface is used by an DNS handler to
|
// A ResponseWriter interface is used by an DNS handler to
|
||||||
// construct an DNS response.
|
// construct an DNS response.
|
||||||
type ResponseWriter interface {
|
type ResponseWriter interface {
|
||||||
|
@ -59,25 +42,48 @@ type ResponseWriter interface {
|
||||||
Hijack()
|
Hijack()
|
||||||
}
|
}
|
||||||
|
|
||||||
// A ConnectionStater interface is used by a DNS Handler to access TLS connection state
|
|
||||||
// when available.
|
|
||||||
type ConnectionStater interface {
|
|
||||||
ConnectionState() *tls.ConnectionState
|
|
||||||
}
|
|
||||||
|
|
||||||
type response struct {
|
type response struct {
|
||||||
closed bool // connection has been closed
|
|
||||||
hijacked bool // connection has been hijacked by handler
|
hijacked bool // connection has been hijacked by handler
|
||||||
tsigTimersOnly bool
|
|
||||||
tsigStatus error
|
tsigStatus error
|
||||||
|
tsigTimersOnly bool
|
||||||
tsigRequestMAC string
|
tsigRequestMAC string
|
||||||
tsigSecret map[string]string // the tsig secrets
|
tsigSecret map[string]string // the tsig secrets
|
||||||
udp *net.UDPConn // i/o connection if UDP was used
|
udp *net.UDPConn // i/o connection if UDP was used
|
||||||
tcp net.Conn // i/o connection if TCP was used
|
tcp net.Conn // i/o connection if TCP was used
|
||||||
udpSession *SessionUDP // oob data to get egress interface right
|
udpSession *SessionUDP // oob data to get egress interface right
|
||||||
|
remoteAddr net.Addr // address of the client
|
||||||
writer Writer // writer to output the raw DNS bits
|
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.
|
// HandleFailed returns a HandlerFunc that returns SERVFAIL for every request it gets.
|
||||||
func HandleFailed(w ResponseWriter, r *Msg) {
|
func HandleFailed(w ResponseWriter, r *Msg) {
|
||||||
m := new(Msg)
|
m := new(Msg)
|
||||||
|
@ -86,6 +92,8 @@ func HandleFailed(w ResponseWriter, r *Msg) {
|
||||||
w.WriteMsg(m)
|
w.WriteMsg(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func failedHandler() Handler { return HandlerFunc(HandleFailed) }
|
||||||
|
|
||||||
// ListenAndServe Starts a server on address and network specified Invoke handler
|
// ListenAndServe Starts a server on address and network specified Invoke handler
|
||||||
// for incoming queries.
|
// for incoming queries.
|
||||||
func ListenAndServe(addr string, network string, handler Handler) error {
|
func ListenAndServe(addr string, network string, handler Handler) error {
|
||||||
|
@ -124,6 +132,99 @@ func ActivateAndServe(l net.Listener, p net.PacketConn, handler Handler) error {
|
||||||
return server.ActivateAndServe()
|
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.
|
// Writer writes raw DNS messages; each call to Write should send an entire message.
|
||||||
type Writer interface {
|
type Writer interface {
|
||||||
io.Writer
|
io.Writer
|
||||||
|
@ -145,11 +246,11 @@ type defaultReader struct {
|
||||||
*Server
|
*Server
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dr defaultReader) ReadTCP(conn net.Conn, timeout time.Duration) ([]byte, error) {
|
func (dr *defaultReader) ReadTCP(conn net.Conn, timeout time.Duration) ([]byte, error) {
|
||||||
return dr.readTCP(conn, timeout)
|
return dr.readTCP(conn, timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dr defaultReader) ReadUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error) {
|
func (dr *defaultReader) ReadUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error) {
|
||||||
return dr.readUDP(conn, timeout)
|
return dr.readUDP(conn, timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,120 +287,87 @@ type Server struct {
|
||||||
IdleTimeout func() time.Duration
|
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).
|
// 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
|
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.
|
// If NotifyStartedFunc is set it is called once the server has started listening.
|
||||||
NotifyStartedFunc func()
|
NotifyStartedFunc func()
|
||||||
// DecorateReader is optional, allows customization of the process that reads raw DNS messages.
|
// DecorateReader is optional, allows customization of the process that reads raw DNS messages.
|
||||||
DecorateReader DecorateReader
|
DecorateReader DecorateReader
|
||||||
// DecorateWriter is optional, allows customization of the process that writes raw DNS messages.
|
// DecorateWriter is optional, allows customization of the process that writes raw DNS messages.
|
||||||
DecorateWriter DecorateWriter
|
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
|
|
||||||
// AcceptMsgFunc will check the incoming message and will reject it early in the process.
|
|
||||||
// By default DefaultMsgAcceptFunc will be used.
|
|
||||||
MsgAcceptFunc MsgAcceptFunc
|
|
||||||
|
|
||||||
// Shutdown handling
|
// Shutdown handling
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
started bool
|
started bool
|
||||||
shutdown chan struct{}
|
|
||||||
conns map[net.Conn]struct{}
|
|
||||||
|
|
||||||
// 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 makeUDPBuffer(size int) func() interface{} {
|
|
||||||
return func() interface{} {
|
|
||||||
return make([]byte, size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (srv *Server) init() {
|
|
||||||
srv.shutdown = make(chan struct{})
|
|
||||||
srv.conns = make(map[net.Conn]struct{})
|
|
||||||
|
|
||||||
if srv.UDPSize == 0 {
|
|
||||||
srv.UDPSize = MinMsgSize
|
|
||||||
}
|
|
||||||
if srv.MsgAcceptFunc == nil {
|
|
||||||
srv.MsgAcceptFunc = DefaultMsgAcceptFunc
|
|
||||||
}
|
|
||||||
if srv.Handler == nil {
|
|
||||||
srv.Handler = DefaultServeMux
|
|
||||||
}
|
|
||||||
|
|
||||||
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.
|
// ListenAndServe starts a nameserver on the configured address in *Server.
|
||||||
func (srv *Server) ListenAndServe() error {
|
func (srv *Server) ListenAndServe() error {
|
||||||
unlock := unlockOnce(&srv.lock)
|
|
||||||
srv.lock.Lock()
|
srv.lock.Lock()
|
||||||
defer unlock()
|
defer srv.lock.Unlock()
|
||||||
|
|
||||||
if srv.started {
|
if srv.started {
|
||||||
return &Error{err: "server already started"}
|
return &Error{err: "server already started"}
|
||||||
}
|
}
|
||||||
|
|
||||||
addr := srv.Addr
|
addr := srv.Addr
|
||||||
if addr == "" {
|
if addr == "" {
|
||||||
addr = ":domain"
|
addr = ":domain"
|
||||||
}
|
}
|
||||||
|
if srv.UDPSize == 0 {
|
||||||
srv.init()
|
srv.UDPSize = MinMsgSize
|
||||||
|
}
|
||||||
switch srv.Net {
|
switch srv.Net {
|
||||||
case "tcp", "tcp4", "tcp6":
|
case "tcp", "tcp4", "tcp6":
|
||||||
l, err := listenTCP(srv.Net, addr, srv.ReusePort)
|
a, err := net.ResolveTCPAddr(srv.Net, addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
l, err := net.ListenTCP(srv.Net, a)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
srv.Listener = l
|
srv.Listener = l
|
||||||
srv.started = true
|
srv.started = true
|
||||||
unlock()
|
srv.lock.Unlock()
|
||||||
return srv.serveTCP(l)
|
err = srv.serveTCP(l)
|
||||||
|
srv.lock.Lock() // to satisfy the defer at the top
|
||||||
|
return err
|
||||||
case "tcp-tls", "tcp4-tls", "tcp6-tls":
|
case "tcp-tls", "tcp4-tls", "tcp6-tls":
|
||||||
if srv.TLSConfig == nil || (len(srv.TLSConfig.Certificates) == 0 && srv.TLSConfig.GetCertificate == nil) {
|
network := "tcp"
|
||||||
return errors.New("dns: neither Certificates nor GetCertificate set in Config")
|
if srv.Net == "tcp4-tls" {
|
||||||
|
network = "tcp4"
|
||||||
|
} else if srv.Net == "tcp6-tls" {
|
||||||
|
network = "tcp6"
|
||||||
}
|
}
|
||||||
network := strings.TrimSuffix(srv.Net, "-tls")
|
|
||||||
l, err := listenTCP(network, addr, srv.ReusePort)
|
l, err := tls.Listen(network, addr, srv.TLSConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
l = tls.NewListener(l, srv.TLSConfig)
|
|
||||||
srv.Listener = l
|
srv.Listener = l
|
||||||
srv.started = true
|
srv.started = true
|
||||||
unlock()
|
srv.lock.Unlock()
|
||||||
return srv.serveTCP(l)
|
err = srv.serveTCP(l)
|
||||||
|
srv.lock.Lock() // to satisfy the defer at the top
|
||||||
|
return err
|
||||||
case "udp", "udp4", "udp6":
|
case "udp", "udp4", "udp6":
|
||||||
l, err := listenUDP(srv.Net, addr, srv.ReusePort)
|
a, err := net.ResolveUDPAddr(srv.Net, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
u := l.(*net.UDPConn)
|
l, err := net.ListenUDP(srv.Net, a)
|
||||||
if e := setUDPSocketOptions(u); e != nil {
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if e := setUDPSocketOptions(l); e != nil {
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
srv.PacketConn = l
|
srv.PacketConn = l
|
||||||
srv.started = true
|
srv.started = true
|
||||||
unlock()
|
srv.lock.Unlock()
|
||||||
return srv.serveUDP(u)
|
err = srv.serveUDP(l)
|
||||||
|
srv.lock.Lock() // to satisfy the defer at the top
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return &Error{err: "bad network"}
|
return &Error{err: "bad network"}
|
||||||
}
|
}
|
||||||
|
@ -307,19 +375,17 @@ func (srv *Server) ListenAndServe() error {
|
||||||
// ActivateAndServe starts a nameserver with the PacketConn or Listener
|
// ActivateAndServe starts a nameserver with the PacketConn or Listener
|
||||||
// configured in *Server. Its main use is to start a server from systemd.
|
// configured in *Server. Its main use is to start a server from systemd.
|
||||||
func (srv *Server) ActivateAndServe() error {
|
func (srv *Server) ActivateAndServe() error {
|
||||||
unlock := unlockOnce(&srv.lock)
|
|
||||||
srv.lock.Lock()
|
srv.lock.Lock()
|
||||||
defer unlock()
|
defer srv.lock.Unlock()
|
||||||
|
|
||||||
if srv.started {
|
if srv.started {
|
||||||
return &Error{err: "server already started"}
|
return &Error{err: "server already started"}
|
||||||
}
|
}
|
||||||
|
|
||||||
srv.init()
|
|
||||||
|
|
||||||
pConn := srv.PacketConn
|
pConn := srv.PacketConn
|
||||||
l := srv.Listener
|
l := srv.Listener
|
||||||
if pConn != nil {
|
if pConn != nil {
|
||||||
|
if srv.UDPSize == 0 {
|
||||||
|
srv.UDPSize = MinMsgSize
|
||||||
|
}
|
||||||
// Check PacketConn interface's type is valid and value
|
// Check PacketConn interface's type is valid and value
|
||||||
// is not nil
|
// is not nil
|
||||||
if t, ok := pConn.(*net.UDPConn); ok && t != nil {
|
if t, ok := pConn.(*net.UDPConn); ok && t != nil {
|
||||||
|
@ -327,14 +393,18 @@ func (srv *Server) ActivateAndServe() error {
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
srv.started = true
|
srv.started = true
|
||||||
unlock()
|
srv.lock.Unlock()
|
||||||
return srv.serveUDP(t)
|
e := srv.serveUDP(t)
|
||||||
|
srv.lock.Lock() // to satisfy the defer at the top
|
||||||
|
return e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if l != nil {
|
if l != nil {
|
||||||
srv.started = true
|
srv.started = true
|
||||||
unlock()
|
srv.lock.Unlock()
|
||||||
return srv.serveTCP(l)
|
e := srv.serveTCP(l)
|
||||||
|
srv.lock.Lock() // to satisfy the defer at the top
|
||||||
|
return e
|
||||||
}
|
}
|
||||||
return &Error{err: "bad listeners"}
|
return &Error{err: "bad listeners"}
|
||||||
}
|
}
|
||||||
|
@ -342,66 +412,34 @@ func (srv *Server) ActivateAndServe() error {
|
||||||
// Shutdown shuts down a server. After a call to Shutdown, ListenAndServe and
|
// Shutdown shuts down a server. After a call to Shutdown, ListenAndServe and
|
||||||
// ActivateAndServe will return.
|
// ActivateAndServe will return.
|
||||||
func (srv *Server) Shutdown() error {
|
func (srv *Server) Shutdown() error {
|
||||||
return srv.ShutdownContext(context.Background())
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShutdownContext shuts down a server. After a call to ShutdownContext,
|
|
||||||
// ListenAndServe and ActivateAndServe will return.
|
|
||||||
//
|
|
||||||
// A context.Context may be passed to limit how long to wait for connections
|
|
||||||
// to terminate.
|
|
||||||
func (srv *Server) ShutdownContext(ctx context.Context) error {
|
|
||||||
srv.lock.Lock()
|
srv.lock.Lock()
|
||||||
if !srv.started {
|
if !srv.started {
|
||||||
srv.lock.Unlock()
|
srv.lock.Unlock()
|
||||||
return &Error{err: "server not started"}
|
return &Error{err: "server not started"}
|
||||||
}
|
}
|
||||||
|
|
||||||
srv.started = false
|
srv.started = false
|
||||||
|
|
||||||
if srv.PacketConn != nil {
|
|
||||||
srv.PacketConn.SetReadDeadline(aLongTimeAgo) // Unblock reads
|
|
||||||
}
|
|
||||||
|
|
||||||
if srv.Listener != nil {
|
|
||||||
srv.Listener.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
for rw := range srv.conns {
|
|
||||||
rw.SetReadDeadline(aLongTimeAgo) // Unblock reads
|
|
||||||
}
|
|
||||||
|
|
||||||
srv.lock.Unlock()
|
srv.lock.Unlock()
|
||||||
|
|
||||||
if testShutdownNotify != nil {
|
|
||||||
testShutdownNotify.Broadcast()
|
|
||||||
}
|
|
||||||
|
|
||||||
var ctxErr error
|
|
||||||
select {
|
|
||||||
case <-srv.shutdown:
|
|
||||||
case <-ctx.Done():
|
|
||||||
ctxErr = ctx.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
if srv.PacketConn != nil {
|
if srv.PacketConn != nil {
|
||||||
srv.PacketConn.Close()
|
srv.PacketConn.Close()
|
||||||
}
|
}
|
||||||
|
if srv.Listener != nil {
|
||||||
return ctxErr
|
srv.Listener.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var testShutdownNotify *sync.Cond
|
|
||||||
|
|
||||||
// getReadTimeout is a helper func to use system timeout if server did not intend to change it.
|
// getReadTimeout is a helper func to use system timeout if server did not intend to change it.
|
||||||
func (srv *Server) getReadTimeout() time.Duration {
|
func (srv *Server) getReadTimeout() time.Duration {
|
||||||
|
rtimeout := dnsTimeout
|
||||||
if srv.ReadTimeout != 0 {
|
if srv.ReadTimeout != 0 {
|
||||||
return srv.ReadTimeout
|
rtimeout = srv.ReadTimeout
|
||||||
}
|
}
|
||||||
return dnsTimeout
|
return rtimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
// serveTCP starts a TCP listener for the server.
|
// serveTCP starts a TCP listener for the server.
|
||||||
|
// Each request is handled in a separate goroutine.
|
||||||
func (srv *Server) serveTCP(l net.Listener) error {
|
func (srv *Server) serveTCP(l net.Listener) error {
|
||||||
defer l.Close()
|
defer l.Close()
|
||||||
|
|
||||||
|
@ -409,35 +447,41 @@ func (srv *Server) serveTCP(l net.Listener) error {
|
||||||
srv.NotifyStartedFunc()
|
srv.NotifyStartedFunc()
|
||||||
}
|
}
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
reader := Reader(&defaultReader{srv})
|
||||||
defer func() {
|
if srv.DecorateReader != nil {
|
||||||
wg.Wait()
|
reader = srv.DecorateReader(reader)
|
||||||
close(srv.shutdown)
|
}
|
||||||
}()
|
|
||||||
|
|
||||||
for srv.isStarted() {
|
handler := srv.Handler
|
||||||
|
if handler == nil {
|
||||||
|
handler = DefaultServeMux
|
||||||
|
}
|
||||||
|
rtimeout := srv.getReadTimeout()
|
||||||
|
// deadline is not used here
|
||||||
|
for {
|
||||||
rw, err := l.Accept()
|
rw, err := l.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !srv.isStarted() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if neterr, ok := err.(net.Error); ok && neterr.Temporary() {
|
if neterr, ok := err.(net.Error); ok && neterr.Temporary() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
srv.lock.Lock()
|
m, err := reader.ReadTCP(rw, rtimeout)
|
||||||
// Track the connection to allow unblocking reads on shutdown.
|
srv.lock.RLock()
|
||||||
srv.conns[rw] = struct{}{}
|
if !srv.started {
|
||||||
srv.lock.Unlock()
|
srv.lock.RUnlock()
|
||||||
wg.Add(1)
|
|
||||||
go srv.serveTCPConn(&wg, rw)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
}
|
||||||
|
srv.lock.RUnlock()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
go srv.serve(rw.RemoteAddr(), handler, m, nil, nil, rw)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// serveUDP starts a UDP listener for the server.
|
// serveUDP starts a UDP listener for the server.
|
||||||
|
// Each request is handled in a separate goroutine.
|
||||||
func (srv *Server) serveUDP(l *net.UDPConn) error {
|
func (srv *Server) serveUDP(l *net.UDPConn) error {
|
||||||
defer l.Close()
|
defer l.Close()
|
||||||
|
|
||||||
|
@ -445,211 +489,155 @@ func (srv *Server) serveUDP(l *net.UDPConn) error {
|
||||||
srv.NotifyStartedFunc()
|
srv.NotifyStartedFunc()
|
||||||
}
|
}
|
||||||
|
|
||||||
reader := Reader(defaultReader{srv})
|
reader := Reader(&defaultReader{srv})
|
||||||
if srv.DecorateReader != nil {
|
if srv.DecorateReader != nil {
|
||||||
reader = srv.DecorateReader(reader)
|
reader = srv.DecorateReader(reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
handler := srv.Handler
|
||||||
defer func() {
|
if handler == nil {
|
||||||
wg.Wait()
|
handler = DefaultServeMux
|
||||||
close(srv.shutdown)
|
}
|
||||||
}()
|
|
||||||
|
|
||||||
rtimeout := srv.getReadTimeout()
|
rtimeout := srv.getReadTimeout()
|
||||||
// deadline is not used here
|
// deadline is not used here
|
||||||
for srv.isStarted() {
|
for {
|
||||||
m, s, err := reader.ReadUDP(l, rtimeout)
|
m, s, err := reader.ReadUDP(l, rtimeout)
|
||||||
|
srv.lock.RLock()
|
||||||
|
if !srv.started {
|
||||||
|
srv.lock.RUnlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
srv.lock.RUnlock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !srv.isStarted() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if netErr, ok := err.(net.Error); ok && netErr.Temporary() {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return err
|
go srv.serve(s.RemoteAddr(), handler, m, l, s, nil)
|
||||||
}
|
}
|
||||||
if len(m) < headerSize {
|
|
||||||
if cap(m) == srv.UDPSize {
|
|
||||||
srv.udpPool.Put(m[:srv.UDPSize])
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
wg.Add(1)
|
|
||||||
go srv.serveUDPPacket(&wg, m, l, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serve a new TCP connection.
|
// Serve a new connection.
|
||||||
func (srv *Server) serveTCPConn(wg *sync.WaitGroup, rw net.Conn) {
|
func (srv *Server) serve(a net.Addr, h Handler, m []byte, u *net.UDPConn, s *SessionUDP, t net.Conn) {
|
||||||
w := &response{tsigSecret: srv.TsigSecret, tcp: rw}
|
w := &response{tsigSecret: srv.TsigSecret, udp: u, tcp: t, remoteAddr: a, udpSession: s}
|
||||||
if srv.DecorateWriter != nil {
|
if srv.DecorateWriter != nil {
|
||||||
w.writer = srv.DecorateWriter(w)
|
w.writer = srv.DecorateWriter(w)
|
||||||
} else {
|
} else {
|
||||||
w.writer = w
|
w.writer = w
|
||||||
}
|
}
|
||||||
|
|
||||||
reader := Reader(defaultReader{srv})
|
q := 0 // counter for the amount of TCP queries we get
|
||||||
|
|
||||||
|
reader := Reader(&defaultReader{srv})
|
||||||
if srv.DecorateReader != nil {
|
if srv.DecorateReader != nil {
|
||||||
reader = srv.DecorateReader(reader)
|
reader = srv.DecorateReader(reader)
|
||||||
}
|
}
|
||||||
|
Redo:
|
||||||
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) && srv.isStarted(); q++ {
|
|
||||||
m, err := reader.ReadTCP(w.tcp, timeout)
|
|
||||||
if err != nil {
|
|
||||||
// TODO(tmthrgd): handle error
|
|
||||||
break
|
|
||||||
}
|
|
||||||
srv.serveDNS(m, w)
|
|
||||||
if w.closed {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
if !w.hijacked {
|
|
||||||
w.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
srv.lock.Lock()
|
|
||||||
delete(srv.conns, w.tcp)
|
|
||||||
srv.lock.Unlock()
|
|
||||||
|
|
||||||
wg.Done()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serve a new UDP request.
|
|
||||||
func (srv *Server) serveUDPPacket(wg *sync.WaitGroup, m []byte, u *net.UDPConn, s *SessionUDP) {
|
|
||||||
w := &response{tsigSecret: srv.TsigSecret, udp: u, udpSession: s}
|
|
||||||
if srv.DecorateWriter != nil {
|
|
||||||
w.writer = srv.DecorateWriter(w)
|
|
||||||
} else {
|
|
||||||
w.writer = w
|
|
||||||
}
|
|
||||||
|
|
||||||
srv.serveDNS(m, w)
|
|
||||||
wg.Done()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (srv *Server) serveDNS(m []byte, w *response) {
|
|
||||||
dh, off, err := unpackMsgHdr(m, 0)
|
|
||||||
if err != nil {
|
|
||||||
// Let client hang, they are sending crap; any reply can be used to amplify.
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
req := new(Msg)
|
req := new(Msg)
|
||||||
req.setHdr(dh)
|
err := req.Unpack(m)
|
||||||
|
if err != nil { // Send a FormatError back
|
||||||
switch srv.MsgAcceptFunc(dh) {
|
x := new(Msg)
|
||||||
case MsgAccept:
|
x.SetRcodeFormatError(req)
|
||||||
if req.unpack(dh, m, off) == nil {
|
w.WriteMsg(x)
|
||||||
break
|
goto Exit
|
||||||
}
|
}
|
||||||
|
if !srv.Unsafe && req.Response {
|
||||||
fallthrough
|
goto Exit
|
||||||
case MsgReject:
|
|
||||||
req.SetRcodeFormatError(req)
|
|
||||||
// Are we allowed to delete any OPT records here?
|
|
||||||
req.Ns, req.Answer, req.Extra = nil, nil, nil
|
|
||||||
|
|
||||||
w.WriteMsg(req)
|
|
||||||
fallthrough
|
|
||||||
case MsgIgnore:
|
|
||||||
if w.udp != nil && cap(m) == srv.UDPSize {
|
|
||||||
srv.udpPool.Put(m[:srv.UDPSize])
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
w.tsigStatus = nil
|
w.tsigStatus = nil
|
||||||
if w.tsigSecret != nil {
|
if w.tsigSecret != nil {
|
||||||
if t := req.IsTsig(); t != nil {
|
if t := req.IsTsig(); t != nil {
|
||||||
if secret, ok := w.tsigSecret[t.Hdr.Name]; ok {
|
secret := t.Hdr.Name
|
||||||
w.tsigStatus = TsigVerify(m, secret, "", false)
|
if _, ok := w.tsigSecret[secret]; !ok {
|
||||||
} else {
|
w.tsigStatus = ErrKeyAlg
|
||||||
w.tsigStatus = ErrSecret
|
|
||||||
}
|
}
|
||||||
|
w.tsigStatus = TsigVerify(m, w.tsigSecret[secret], "", false)
|
||||||
w.tsigTimersOnly = false
|
w.tsigTimersOnly = false
|
||||||
w.tsigRequestMAC = req.Extra[len(req.Extra)-1].(*TSIG).MAC
|
w.tsigRequestMAC = req.Extra[len(req.Extra)-1].(*TSIG).MAC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
h.ServeDNS(w, req) // Writes back to the client
|
||||||
|
|
||||||
if w.udp != nil && cap(m) == srv.UDPSize {
|
Exit:
|
||||||
srv.udpPool.Put(m[:srv.UDPSize])
|
if w.tcp == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// TODO(miek): make this number configurable?
|
||||||
|
if q > maxTCPQueries { // close socket after this many queries
|
||||||
|
w.Close()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
srv.Handler.ServeDNS(w, req) // Writes back to the client
|
if w.hijacked {
|
||||||
|
return // client calls Close()
|
||||||
|
}
|
||||||
|
if u != nil { // UDP, "close" and return
|
||||||
|
w.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
idleTimeout := tcpIdleTimeout
|
||||||
|
if srv.IdleTimeout != nil {
|
||||||
|
idleTimeout = srv.IdleTimeout()
|
||||||
|
}
|
||||||
|
m, err = reader.ReadTCP(w.tcp, idleTimeout)
|
||||||
|
if err == nil {
|
||||||
|
q++
|
||||||
|
goto Redo
|
||||||
|
}
|
||||||
|
w.Close()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) readTCP(conn net.Conn, timeout time.Duration) ([]byte, error) {
|
func (srv *Server) readTCP(conn net.Conn, timeout time.Duration) ([]byte, error) {
|
||||||
// If we race with ShutdownContext, the read deadline may
|
|
||||||
// have been set in the distant past to unblock the read
|
|
||||||
// below. We must not override it, otherwise we may block
|
|
||||||
// ShutdownContext.
|
|
||||||
srv.lock.RLock()
|
|
||||||
if srv.started {
|
|
||||||
conn.SetReadDeadline(time.Now().Add(timeout))
|
conn.SetReadDeadline(time.Now().Add(timeout))
|
||||||
}
|
l := make([]byte, 2)
|
||||||
srv.lock.RUnlock()
|
n, err := conn.Read(l)
|
||||||
|
if err != nil || n != 2 {
|
||||||
var length uint16
|
if err != nil {
|
||||||
if err := binary.Read(conn, binary.BigEndian, &length); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return nil, ErrShortRead
|
||||||
m := make([]byte, length)
|
}
|
||||||
if _, err := io.ReadFull(conn, m); err != nil {
|
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, 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
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) readUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error) {
|
func (srv *Server) readUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error) {
|
||||||
srv.lock.RLock()
|
|
||||||
if srv.started {
|
|
||||||
// See the comment in readTCP above.
|
|
||||||
conn.SetReadDeadline(time.Now().Add(timeout))
|
conn.SetReadDeadline(time.Now().Add(timeout))
|
||||||
}
|
m := make([]byte, srv.UDPSize)
|
||||||
srv.lock.RUnlock()
|
|
||||||
|
|
||||||
m := srv.udpPool.Get().([]byte)
|
|
||||||
n, s, err := ReadFromSessionUDP(conn, m)
|
n, s, err := ReadFromSessionUDP(conn, m)
|
||||||
|
if err != nil || n == 0 {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
srv.udpPool.Put(m)
|
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
return nil, nil, ErrShortRead
|
||||||
|
}
|
||||||
m = m[:n]
|
m = m[:n]
|
||||||
return m, s, nil
|
return m, s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteMsg implements the ResponseWriter.WriteMsg method.
|
// WriteMsg implements the ResponseWriter.WriteMsg method.
|
||||||
func (w *response) WriteMsg(m *Msg) (err error) {
|
func (w *response) WriteMsg(m *Msg) (err error) {
|
||||||
if w.closed {
|
|
||||||
return &Error{err: "WriteMsg called after Close"}
|
|
||||||
}
|
|
||||||
|
|
||||||
var data []byte
|
var data []byte
|
||||||
if w.tsigSecret != nil { // if no secrets, dont check for the tsig (which is a longer check)
|
if w.tsigSecret != nil { // if no secrets, dont check for the tsig (which is a longer check)
|
||||||
if t := m.IsTsig(); t != nil {
|
if t := m.IsTsig(); t != nil {
|
||||||
|
@ -671,51 +659,38 @@ func (w *response) WriteMsg(m *Msg) (err error) {
|
||||||
|
|
||||||
// Write implements the ResponseWriter.Write method.
|
// Write implements the ResponseWriter.Write method.
|
||||||
func (w *response) Write(m []byte) (int, error) {
|
func (w *response) Write(m []byte) (int, error) {
|
||||||
if w.closed {
|
|
||||||
return 0, &Error{err: "Write called after Close"}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case w.udp != nil:
|
case w.udp != nil:
|
||||||
return WriteToSessionUDP(w.udp, m, w.udpSession)
|
n, err := WriteToSessionUDP(w.udp, m, w.udpSession)
|
||||||
|
return n, err
|
||||||
case w.tcp != nil:
|
case w.tcp != nil:
|
||||||
if len(m) > MaxMsgSize {
|
lm := len(m)
|
||||||
|
if lm < 2 {
|
||||||
|
return 0, io.ErrShortBuffer
|
||||||
|
}
|
||||||
|
if lm > MaxMsgSize {
|
||||||
return 0, &Error{err: "message too large"}
|
return 0, &Error{err: "message too large"}
|
||||||
}
|
}
|
||||||
|
l := make([]byte, 2, 2+lm)
|
||||||
|
binary.BigEndian.PutUint16(l, uint16(lm))
|
||||||
|
m = append(l, m...)
|
||||||
|
|
||||||
l := make([]byte, 2)
|
n, err := io.Copy(w.tcp, bytes.NewReader(m))
|
||||||
binary.BigEndian.PutUint16(l, uint16(len(m)))
|
|
||||||
|
|
||||||
n, err := (&net.Buffers{l, m}).WriteTo(w.tcp)
|
|
||||||
return int(n), err
|
return int(n), err
|
||||||
default:
|
|
||||||
panic("dns: internal error: udp and tcp both nil")
|
|
||||||
}
|
}
|
||||||
|
panic("not reached")
|
||||||
}
|
}
|
||||||
|
|
||||||
// LocalAddr implements the ResponseWriter.LocalAddr method.
|
// LocalAddr implements the ResponseWriter.LocalAddr method.
|
||||||
func (w *response) LocalAddr() net.Addr {
|
func (w *response) LocalAddr() net.Addr {
|
||||||
switch {
|
if w.tcp != nil {
|
||||||
case w.udp != nil:
|
|
||||||
return w.udp.LocalAddr()
|
|
||||||
case w.tcp != nil:
|
|
||||||
return w.tcp.LocalAddr()
|
return w.tcp.LocalAddr()
|
||||||
default:
|
|
||||||
panic("dns: internal error: udp and tcp both nil")
|
|
||||||
}
|
}
|
||||||
|
return w.udp.LocalAddr()
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoteAddr implements the ResponseWriter.RemoteAddr method.
|
// RemoteAddr implements the ResponseWriter.RemoteAddr method.
|
||||||
func (w *response) RemoteAddr() net.Addr {
|
func (w *response) RemoteAddr() net.Addr { return w.remoteAddr }
|
||||||
switch {
|
|
||||||
case w.udpSession != nil:
|
|
||||||
return w.udpSession.RemoteAddr()
|
|
||||||
case w.tcp != nil:
|
|
||||||
return w.tcp.RemoteAddr()
|
|
||||||
default:
|
|
||||||
panic("dns: internal error: udpSession and tcp both nil")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TsigStatus implements the ResponseWriter.TsigStatus method.
|
// TsigStatus implements the ResponseWriter.TsigStatus method.
|
||||||
func (w *response) TsigStatus() error { return w.tsigStatus }
|
func (w *response) TsigStatus() error { return w.tsigStatus }
|
||||||
|
@ -728,30 +703,11 @@ func (w *response) Hijack() { w.hijacked = true }
|
||||||
|
|
||||||
// Close implements the ResponseWriter.Close method
|
// Close implements the ResponseWriter.Close method
|
||||||
func (w *response) Close() error {
|
func (w *response) Close() error {
|
||||||
if w.closed {
|
|
||||||
return &Error{err: "connection already closed"}
|
|
||||||
}
|
|
||||||
w.closed = true
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case w.udp != nil:
|
|
||||||
// Can't close the udp conn, as that is actually the listener.
|
// Can't close the udp conn, as that is actually the listener.
|
||||||
return nil
|
if w.tcp != nil {
|
||||||
case w.tcp != nil:
|
e := w.tcp.Close()
|
||||||
return w.tcp.Close()
|
w.tcp = nil
|
||||||
default:
|
return e
|
||||||
panic("dns: internal error: udp and tcp both nil")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConnectionState() implements the ConnectionStater.ConnectionState() interface.
|
|
||||||
func (w *response) ConnectionState() *tls.ConnectionState {
|
|
||||||
type tlsConnectionStater interface {
|
|
||||||
ConnectionState() tls.ConnectionState
|
|
||||||
}
|
|
||||||
if v, ok := w.tcp.(tlsConnectionStater); ok {
|
|
||||||
t := v.ConnectionState()
|
|
||||||
return &t
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,11 +21,15 @@ func (rr *SIG) Sign(k crypto.Signer, m *Msg) ([]byte, error) {
|
||||||
if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 {
|
if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 {
|
||||||
return nil, ErrKey
|
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
|
||||||
|
|
||||||
rr.Hdr = RR_Header{Name: ".", Rrtype: TypeSIG, Class: ClassANY, Ttl: 0}
|
buf := make([]byte, m.Len()+rr.len())
|
||||||
rr.OrigTtl, rr.TypeCovered, rr.Labels = 0, 0, 0
|
|
||||||
|
|
||||||
buf := make([]byte, m.Len()+Len(rr))
|
|
||||||
mbuf, err := m.PackBuffer(buf)
|
mbuf, err := m.PackBuffer(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -103,7 +107,7 @@ func (rr *SIG) Verify(k *KEY, buf []byte) error {
|
||||||
anc := binary.BigEndian.Uint16(buf[6:])
|
anc := binary.BigEndian.Uint16(buf[6:])
|
||||||
auc := binary.BigEndian.Uint16(buf[8:])
|
auc := binary.BigEndian.Uint16(buf[8:])
|
||||||
adc := binary.BigEndian.Uint16(buf[10:])
|
adc := binary.BigEndian.Uint16(buf[10:])
|
||||||
offset := headerSize
|
offset := 12
|
||||||
var err error
|
var err error
|
||||||
for i := uint16(0); i < qdc && offset < buflen; i++ {
|
for i := uint16(0); i < qdc && offset < buflen; i++ {
|
||||||
_, offset, err = UnpackDomainName(buf, offset)
|
_, offset, err = UnpackDomainName(buf, offset)
|
||||||
|
@ -123,7 +127,8 @@ func (rr *SIG) Verify(k *KEY, buf []byte) error {
|
||||||
if offset+1 >= buflen {
|
if offset+1 >= buflen {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
rdlen := binary.BigEndian.Uint16(buf[offset:])
|
var rdlen uint16
|
||||||
|
rdlen = binary.BigEndian.Uint16(buf[offset:])
|
||||||
offset += 2
|
offset += 2
|
||||||
offset += int(rdlen)
|
offset += int(rdlen)
|
||||||
}
|
}
|
||||||
|
@ -163,7 +168,7 @@ func (rr *SIG) Verify(k *KEY, buf []byte) error {
|
||||||
}
|
}
|
||||||
// If key has come from the DNS name compression might
|
// If key has come from the DNS name compression might
|
||||||
// have mangled the case of the name
|
// have mangled the case of the name
|
||||||
if !strings.EqualFold(signername, k.Header().Name) {
|
if strings.ToLower(signername) != strings.ToLower(k.Header().Name) {
|
||||||
return &Error{err: "signer name doesn't match key name"}
|
return &Error{err: "signer name doesn't match key name"}
|
||||||
}
|
}
|
||||||
sigend := offset
|
sigend := offset
|
||||||
|
@ -181,8 +186,10 @@ func (rr *SIG) Verify(k *KEY, buf []byte) error {
|
||||||
case DSA:
|
case DSA:
|
||||||
pk := k.publicKeyDSA()
|
pk := k.publicKeyDSA()
|
||||||
sig = sig[1:]
|
sig = sig[1:]
|
||||||
r := new(big.Int).SetBytes(sig[:len(sig)/2])
|
r := big.NewInt(0)
|
||||||
s := new(big.Int).SetBytes(sig[len(sig)/2:])
|
r.SetBytes(sig[:len(sig)/2])
|
||||||
|
s := big.NewInt(0)
|
||||||
|
s.SetBytes(sig[len(sig)/2:])
|
||||||
if pk != nil {
|
if pk != nil {
|
||||||
if dsa.Verify(pk, hashed, r, s) {
|
if dsa.Verify(pk, hashed, r, s) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -196,8 +203,10 @@ func (rr *SIG) Verify(k *KEY, buf []byte) error {
|
||||||
}
|
}
|
||||||
case ECDSAP256SHA256, ECDSAP384SHA384:
|
case ECDSAP256SHA256, ECDSAP384SHA384:
|
||||||
pk := k.publicKeyECDSA()
|
pk := k.publicKeyECDSA()
|
||||||
r := new(big.Int).SetBytes(sig[:len(sig)/2])
|
r := big.NewInt(0)
|
||||||
s := new(big.Int).SetBytes(sig[len(sig)/2:])
|
r.SetBytes(sig[:len(sig)/2])
|
||||||
|
s := big.NewInt(0)
|
||||||
|
s.SetBytes(sig[len(sig)/2:])
|
||||||
if pk != nil {
|
if pk != nil {
|
||||||
if ecdsa.Verify(pk, hashed, r, s) {
|
if ecdsa.Verify(pk, hashed, r, s) {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -23,8 +23,6 @@ type call struct {
|
||||||
type singleflight struct {
|
type singleflight struct {
|
||||||
sync.Mutex // protects m
|
sync.Mutex // protects m
|
||||||
m map[string]*call // lazily initialized
|
m map[string]*call // lazily initialized
|
||||||
|
|
||||||
dontDeleteForTesting bool // this is only to be used by TestConcurrentExchanges
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do executes and returns the results of the given function, making
|
// Do executes and returns the results of the given function, making
|
||||||
|
@ -51,11 +49,9 @@ func (g *singleflight) Do(key string, fn func() (*Msg, time.Duration, error)) (v
|
||||||
c.val, c.rtt, c.err = fn()
|
c.val, c.rtt, c.err = fn()
|
||||||
c.wg.Done()
|
c.wg.Done()
|
||||||
|
|
||||||
if !g.dontDeleteForTesting {
|
|
||||||
g.Lock()
|
g.Lock()
|
||||||
delete(g.m, key)
|
delete(g.m, key)
|
||||||
g.Unlock()
|
g.Unlock()
|
||||||
}
|
|
||||||
|
|
||||||
return c.val, c.rtt, c.err, c.dups > 0
|
return c.val, c.rtt, c.err, c.dups > 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,10 @@ func (r *SMIMEA) Sign(usage, selector, matchingType int, cert *x509.Certificate)
|
||||||
r.MatchingType = uint8(matchingType)
|
r.MatchingType = uint8(matchingType)
|
||||||
|
|
||||||
r.Certificate, err = CertificateToDANE(r.Selector, r.MatchingType, cert)
|
r.Certificate, err = CertificateToDANE(r.Selector, r.MatchingType, cert)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify verifies a SMIMEA record against an SSL certificate. If it is OK
|
// Verify verifies a SMIMEA record against an SSL certificate. If it is OK
|
||||||
|
|
|
@ -14,7 +14,10 @@ func (r *TLSA) Sign(usage, selector, matchingType int, cert *x509.Certificate) (
|
||||||
r.MatchingType = uint8(matchingType)
|
r.MatchingType = uint8(matchingType)
|
||||||
|
|
||||||
r.Certificate, err = CertificateToDANE(r.Selector, r.MatchingType, cert)
|
r.Certificate, err = CertificateToDANE(r.Selector, r.MatchingType, cert)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify verifies a TLSA record against an SSL certificate. If it is OK
|
// Verify verifies a TLSA record against an SSL certificate. If it is OK
|
||||||
|
|
|
@ -54,10 +54,6 @@ func (rr *TSIG) String() string {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rr *TSIG) parse(c *zlexer, origin, file string) *ParseError {
|
|
||||||
panic("dns: internal error: parse should never be called on TSIG")
|
|
||||||
}
|
|
||||||
|
|
||||||
// The following values must be put in wireformat, so that the MAC can be calculated.
|
// The following values must be put in wireformat, so that the MAC can be calculated.
|
||||||
// RFC 2845, section 3.4.2. TSIG Variables.
|
// RFC 2845, section 3.4.2. TSIG Variables.
|
||||||
type tsigWireFmt struct {
|
type tsigWireFmt struct {
|
||||||
|
@ -117,13 +113,13 @@ func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, s
|
||||||
var h hash.Hash
|
var h hash.Hash
|
||||||
switch strings.ToLower(rr.Algorithm) {
|
switch strings.ToLower(rr.Algorithm) {
|
||||||
case HmacMD5:
|
case HmacMD5:
|
||||||
h = hmac.New(md5.New, rawsecret)
|
h = hmac.New(md5.New, []byte(rawsecret))
|
||||||
case HmacSHA1:
|
case HmacSHA1:
|
||||||
h = hmac.New(sha1.New, rawsecret)
|
h = hmac.New(sha1.New, []byte(rawsecret))
|
||||||
case HmacSHA256:
|
case HmacSHA256:
|
||||||
h = hmac.New(sha256.New, rawsecret)
|
h = hmac.New(sha256.New, []byte(rawsecret))
|
||||||
case HmacSHA512:
|
case HmacSHA512:
|
||||||
h = hmac.New(sha512.New, rawsecret)
|
h = hmac.New(sha512.New, []byte(rawsecret))
|
||||||
default:
|
default:
|
||||||
return nil, "", ErrKeyAlg
|
return nil, "", ErrKeyAlg
|
||||||
}
|
}
|
||||||
|
@ -137,12 +133,13 @@ func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, s
|
||||||
t.Algorithm = rr.Algorithm
|
t.Algorithm = rr.Algorithm
|
||||||
t.OrigId = m.Id
|
t.OrigId = m.Id
|
||||||
|
|
||||||
tbuf := make([]byte, Len(t))
|
tbuf := make([]byte, t.len())
|
||||||
off, err := PackRR(t, tbuf, 0, nil, false)
|
if off, err := PackRR(t, tbuf, 0, nil, false); err == nil {
|
||||||
if err != nil {
|
tbuf = tbuf[:off] // reset to actual size used
|
||||||
|
} else {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
mbuf = append(mbuf, tbuf[:off]...)
|
mbuf = append(mbuf, tbuf...)
|
||||||
// Update the ArCount directly in the buffer.
|
// Update the ArCount directly in the buffer.
|
||||||
binary.BigEndian.PutUint16(mbuf[10:], uint16(len(m.Extra)+1))
|
binary.BigEndian.PutUint16(mbuf[10:], uint16(len(m.Extra)+1))
|
||||||
|
|
||||||
|
|
|
@ -205,6 +205,9 @@ var CertTypeToString = map[uint16]string{
|
||||||
CertOID: "OID",
|
CertOID: "OID",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StringToCertType is the reverseof CertTypeToString.
|
||||||
|
var StringToCertType = reverseInt16(CertTypeToString)
|
||||||
|
|
||||||
//go:generate go run types_generate.go
|
//go:generate go run types_generate.go
|
||||||
|
|
||||||
// Question holds a DNS question. There can be multiple questions in the
|
// Question holds a DNS question. There can be multiple questions in the
|
||||||
|
@ -215,10 +218,8 @@ type Question struct {
|
||||||
Qclass uint16
|
Qclass uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Question) len(off int, compression map[string]struct{}) int {
|
func (q *Question) len() int {
|
||||||
l := domainNameLen(q.Name, off, compression, true)
|
return len(q.Name) + 1 + 2 + 2
|
||||||
l += 2 + 2
|
|
||||||
return l
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Question) String() (s string) {
|
func (q *Question) String() (s string) {
|
||||||
|
@ -238,25 +239,6 @@ type ANY struct {
|
||||||
|
|
||||||
func (rr *ANY) String() string { return rr.Hdr.String() }
|
func (rr *ANY) String() string { return rr.Hdr.String() }
|
||||||
|
|
||||||
func (rr *ANY) parse(c *zlexer, origin, file string) *ParseError {
|
|
||||||
panic("dns: internal error: parse should never be called on ANY")
|
|
||||||
}
|
|
||||||
|
|
||||||
// NULL RR. See RFC 1035.
|
|
||||||
type NULL struct {
|
|
||||||
Hdr RR_Header
|
|
||||||
Data string `dns:"any"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rr *NULL) String() string {
|
|
||||||
// There is no presentation format; prefix string with a comment.
|
|
||||||
return ";" + rr.Hdr.String() + rr.Data
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rr *NULL) parse(c *zlexer, origin, file string) *ParseError {
|
|
||||||
panic("dns: internal error: parse should never be called on NULL")
|
|
||||||
}
|
|
||||||
|
|
||||||
// CNAME RR. See RFC 1034.
|
// CNAME RR. See RFC 1034.
|
||||||
type CNAME struct {
|
type CNAME struct {
|
||||||
Hdr RR_Header
|
Hdr RR_Header
|
||||||
|
@ -348,7 +330,7 @@ func (rr *MX) String() string {
|
||||||
type AFSDB struct {
|
type AFSDB struct {
|
||||||
Hdr RR_Header
|
Hdr RR_Header
|
||||||
Subtype uint16
|
Subtype uint16
|
||||||
Hostname string `dns:"domain-name"`
|
Hostname string `dns:"cdomain-name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rr *AFSDB) String() string {
|
func (rr *AFSDB) String() string {
|
||||||
|
@ -369,7 +351,7 @@ func (rr *X25) String() string {
|
||||||
type RT struct {
|
type RT struct {
|
||||||
Hdr RR_Header
|
Hdr RR_Header
|
||||||
Preference uint16
|
Preference uint16
|
||||||
Host string `dns:"domain-name"` // RFC 3597 prohibits compressing records not defined in RFC 1035.
|
Host string `dns:"cdomain-name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rr *RT) String() string {
|
func (rr *RT) String() string {
|
||||||
|
@ -404,7 +386,7 @@ type RP struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rr *RP) String() string {
|
func (rr *RP) String() string {
|
||||||
return rr.Hdr.String() + sprintName(rr.Mbox) + " " + sprintName(rr.Txt)
|
return rr.Hdr.String() + rr.Mbox + " " + sprintTxt([]string{rr.Txt})
|
||||||
}
|
}
|
||||||
|
|
||||||
// SOA RR. See RFC 1035.
|
// SOA RR. See RFC 1035.
|
||||||
|
@ -437,154 +419,128 @@ type TXT struct {
|
||||||
func (rr *TXT) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) }
|
func (rr *TXT) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) }
|
||||||
|
|
||||||
func sprintName(s string) string {
|
func sprintName(s string) string {
|
||||||
var dst strings.Builder
|
src := []byte(s)
|
||||||
dst.Grow(len(s))
|
dst := make([]byte, 0, len(src))
|
||||||
for i := 0; i < len(s); {
|
for i := 0; i < len(src); {
|
||||||
if i+1 < len(s) && s[i] == '\\' && s[i+1] == '.' {
|
if i+1 < len(src) && src[i] == '\\' && src[i+1] == '.' {
|
||||||
dst.WriteString(s[i : i+2])
|
dst = append(dst, src[i:i+2]...)
|
||||||
i += 2
|
i += 2
|
||||||
continue
|
} else {
|
||||||
}
|
b, n := nextByte(src, i)
|
||||||
|
if n == 0 {
|
||||||
b, n := nextByte(s, i)
|
|
||||||
switch {
|
|
||||||
case n == 0:
|
|
||||||
i++ // dangling back slash
|
i++ // dangling back slash
|
||||||
case b == '.':
|
} else if b == '.' {
|
||||||
dst.WriteByte('.')
|
dst = append(dst, b)
|
||||||
default:
|
} else {
|
||||||
writeDomainNameByte(&dst, b)
|
dst = appendDomainNameByte(dst, b)
|
||||||
}
|
}
|
||||||
i += n
|
i += n
|
||||||
}
|
}
|
||||||
return dst.String()
|
}
|
||||||
|
return string(dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sprintTxtOctet(s string) string {
|
func sprintTxtOctet(s string) string {
|
||||||
var dst strings.Builder
|
src := []byte(s)
|
||||||
dst.Grow(2 + len(s))
|
dst := make([]byte, 0, len(src))
|
||||||
dst.WriteByte('"')
|
dst = append(dst, '"')
|
||||||
for i := 0; i < len(s); {
|
for i := 0; i < len(src); {
|
||||||
if i+1 < len(s) && s[i] == '\\' && s[i+1] == '.' {
|
if i+1 < len(src) && src[i] == '\\' && src[i+1] == '.' {
|
||||||
dst.WriteString(s[i : i+2])
|
dst = append(dst, src[i:i+2]...)
|
||||||
i += 2
|
i += 2
|
||||||
continue
|
} else {
|
||||||
}
|
b, n := nextByte(src, i)
|
||||||
|
if n == 0 {
|
||||||
b, n := nextByte(s, i)
|
|
||||||
switch {
|
|
||||||
case n == 0:
|
|
||||||
i++ // dangling back slash
|
i++ // dangling back slash
|
||||||
case b == '.':
|
} else if b == '.' {
|
||||||
dst.WriteByte('.')
|
dst = append(dst, b)
|
||||||
case b < ' ' || b > '~':
|
} else {
|
||||||
dst.WriteString(escapeByte(b))
|
if b < ' ' || b > '~' {
|
||||||
default:
|
dst = appendByte(dst, b)
|
||||||
dst.WriteByte(b)
|
} else {
|
||||||
|
dst = append(dst, b)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
i += n
|
i += n
|
||||||
}
|
}
|
||||||
dst.WriteByte('"')
|
}
|
||||||
return dst.String()
|
dst = append(dst, '"')
|
||||||
|
return string(dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sprintTxt(txt []string) string {
|
func sprintTxt(txt []string) string {
|
||||||
var out strings.Builder
|
var out []byte
|
||||||
for i, s := range txt {
|
for i, s := range txt {
|
||||||
out.Grow(3 + len(s))
|
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
out.WriteString(` "`)
|
out = append(out, ` "`...)
|
||||||
} else {
|
} else {
|
||||||
out.WriteByte('"')
|
out = append(out, '"')
|
||||||
}
|
}
|
||||||
for j := 0; j < len(s); {
|
bs := []byte(s)
|
||||||
b, n := nextByte(s, j)
|
for j := 0; j < len(bs); {
|
||||||
|
b, n := nextByte(bs, j)
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
writeTXTStringByte(&out, b)
|
out = appendTXTStringByte(out, b)
|
||||||
j += n
|
j += n
|
||||||
}
|
}
|
||||||
out.WriteByte('"')
|
out = append(out, '"')
|
||||||
}
|
}
|
||||||
return out.String()
|
return string(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeDomainNameByte(s *strings.Builder, b byte) {
|
func appendDomainNameByte(s []byte, b byte) []byte {
|
||||||
switch b {
|
switch b {
|
||||||
case '.', ' ', '\'', '@', ';', '(', ')': // additional chars to escape
|
case '.', ' ', '\'', '@', ';', '(', ')': // additional chars to escape
|
||||||
s.WriteByte('\\')
|
return append(s, '\\', b)
|
||||||
s.WriteByte(b)
|
|
||||||
default:
|
|
||||||
writeTXTStringByte(s, b)
|
|
||||||
}
|
}
|
||||||
|
return appendTXTStringByte(s, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeTXTStringByte(s *strings.Builder, b byte) {
|
func appendTXTStringByte(s []byte, b byte) []byte {
|
||||||
switch {
|
switch b {
|
||||||
case b == '"' || b == '\\':
|
case '"', '\\':
|
||||||
s.WriteByte('\\')
|
return append(s, '\\', b)
|
||||||
s.WriteByte(b)
|
|
||||||
case b < ' ' || b > '~':
|
|
||||||
s.WriteString(escapeByte(b))
|
|
||||||
default:
|
|
||||||
s.WriteByte(b)
|
|
||||||
}
|
}
|
||||||
|
if b < ' ' || b > '~' {
|
||||||
|
return appendByte(s, b)
|
||||||
|
}
|
||||||
|
return append(s, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
func appendByte(s []byte, b byte) []byte {
|
||||||
escapedByteSmall = "" +
|
var buf [3]byte
|
||||||
`\000\001\002\003\004\005\006\007\008\009` +
|
bufs := strconv.AppendInt(buf[:0], int64(b), 10)
|
||||||
`\010\011\012\013\014\015\016\017\018\019` +
|
s = append(s, '\\')
|
||||||
`\020\021\022\023\024\025\026\027\028\029` +
|
for i := 0; i < 3-len(bufs); i++ {
|
||||||
`\030\031`
|
s = append(s, '0')
|
||||||
escapedByteLarge = `\127\128\129` +
|
|
||||||
`\130\131\132\133\134\135\136\137\138\139` +
|
|
||||||
`\140\141\142\143\144\145\146\147\148\149` +
|
|
||||||
`\150\151\152\153\154\155\156\157\158\159` +
|
|
||||||
`\160\161\162\163\164\165\166\167\168\169` +
|
|
||||||
`\170\171\172\173\174\175\176\177\178\179` +
|
|
||||||
`\180\181\182\183\184\185\186\187\188\189` +
|
|
||||||
`\190\191\192\193\194\195\196\197\198\199` +
|
|
||||||
`\200\201\202\203\204\205\206\207\208\209` +
|
|
||||||
`\210\211\212\213\214\215\216\217\218\219` +
|
|
||||||
`\220\221\222\223\224\225\226\227\228\229` +
|
|
||||||
`\230\231\232\233\234\235\236\237\238\239` +
|
|
||||||
`\240\241\242\243\244\245\246\247\248\249` +
|
|
||||||
`\250\251\252\253\254\255`
|
|
||||||
)
|
|
||||||
|
|
||||||
// escapeByte returns the \DDD escaping of b which must
|
|
||||||
// satisfy b < ' ' || b > '~'.
|
|
||||||
func escapeByte(b byte) string {
|
|
||||||
if b < ' ' {
|
|
||||||
return escapedByteSmall[b*4 : b*4+4]
|
|
||||||
}
|
}
|
||||||
|
for _, r := range bufs {
|
||||||
b -= '~' + 1
|
s = append(s, r)
|
||||||
// The cast here is needed as b*4 may overflow byte.
|
}
|
||||||
return escapedByteLarge[int(b)*4 : int(b)*4+4]
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func nextByte(s string, offset int) (byte, int) {
|
func nextByte(b []byte, offset int) (byte, int) {
|
||||||
if offset >= len(s) {
|
if offset >= len(b) {
|
||||||
return 0, 0
|
return 0, 0
|
||||||
}
|
}
|
||||||
if s[offset] != '\\' {
|
if b[offset] != '\\' {
|
||||||
// not an escape sequence
|
// not an escape sequence
|
||||||
return s[offset], 1
|
return b[offset], 1
|
||||||
}
|
}
|
||||||
switch len(s) - offset {
|
switch len(b) - offset {
|
||||||
case 1: // dangling escape
|
case 1: // dangling escape
|
||||||
return 0, 0
|
return 0, 0
|
||||||
case 2, 3: // too short to be \ddd
|
case 2, 3: // too short to be \ddd
|
||||||
default: // maybe \ddd
|
default: // maybe \ddd
|
||||||
if isDigit(s[offset+1]) && isDigit(s[offset+2]) && isDigit(s[offset+3]) {
|
if isDigit(b[offset+1]) && isDigit(b[offset+2]) && isDigit(b[offset+3]) {
|
||||||
return dddStringToByte(s[offset+1:]), 4
|
return dddToByte(b[offset+1:]), 4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// not \ddd, just an RFC 1035 "quoted" character
|
// not \ddd, just an RFC 1035 "quoted" character
|
||||||
return s[offset+1], 2
|
return b[offset+1], 2
|
||||||
}
|
}
|
||||||
|
|
||||||
// SPF RR. See RFC 4408, Section 3.1.1.
|
// SPF RR. See RFC 4408, Section 3.1.1.
|
||||||
|
@ -772,7 +728,7 @@ func (rr *LOC) String() string {
|
||||||
lat = lat % LOC_DEGREES
|
lat = lat % LOC_DEGREES
|
||||||
m := lat / LOC_HOURS
|
m := lat / LOC_HOURS
|
||||||
lat = lat % LOC_HOURS
|
lat = lat % LOC_HOURS
|
||||||
s += fmt.Sprintf("%02d %02d %0.3f %s ", h, m, float64(lat)/1000, ns)
|
s += fmt.Sprintf("%02d %02d %0.3f %s ", h, m, (float64(lat) / 1000), ns)
|
||||||
|
|
||||||
lon := rr.Longitude
|
lon := rr.Longitude
|
||||||
ew := "E"
|
ew := "E"
|
||||||
|
@ -786,7 +742,7 @@ func (rr *LOC) String() string {
|
||||||
lon = lon % LOC_DEGREES
|
lon = lon % LOC_DEGREES
|
||||||
m = lon / LOC_HOURS
|
m = lon / LOC_HOURS
|
||||||
lon = lon % LOC_HOURS
|
lon = lon % LOC_HOURS
|
||||||
s += fmt.Sprintf("%02d %02d %0.3f %s ", h, m, float64(lon)/1000, ew)
|
s += fmt.Sprintf("%02d %02d %0.3f %s ", h, m, (float64(lon) / 1000), ew)
|
||||||
|
|
||||||
var alt = float64(rr.Altitude) / 100
|
var alt = float64(rr.Altitude) / 100
|
||||||
alt -= LOC_ALTITUDEBASE
|
alt -= LOC_ALTITUDEBASE
|
||||||
|
@ -796,9 +752,9 @@ func (rr *LOC) String() string {
|
||||||
s += fmt.Sprintf("%.0fm ", alt)
|
s += fmt.Sprintf("%.0fm ", alt)
|
||||||
}
|
}
|
||||||
|
|
||||||
s += cmToM(rr.Size&0xf0>>4, rr.Size&0x0f) + "m "
|
s += cmToM((rr.Size&0xf0)>>4, rr.Size&0x0f) + "m "
|
||||||
s += cmToM(rr.HorizPre&0xf0>>4, rr.HorizPre&0x0f) + "m "
|
s += cmToM((rr.HorizPre&0xf0)>>4, rr.HorizPre&0x0f) + "m "
|
||||||
s += cmToM(rr.VertPre&0xf0>>4, rr.VertPre&0x0f) + "m"
|
s += cmToM((rr.VertPre&0xf0)>>4, rr.VertPre&0x0f) + "m"
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
@ -845,15 +801,14 @@ type NSEC struct {
|
||||||
|
|
||||||
func (rr *NSEC) String() string {
|
func (rr *NSEC) String() string {
|
||||||
s := rr.Hdr.String() + sprintName(rr.NextDomain)
|
s := rr.Hdr.String() + sprintName(rr.NextDomain)
|
||||||
for _, t := range rr.TypeBitMap {
|
for i := 0; i < len(rr.TypeBitMap); i++ {
|
||||||
s += " " + Type(t).String()
|
s += " " + Type(rr.TypeBitMap[i]).String()
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rr *NSEC) len(off int, compression map[string]struct{}) int {
|
func (rr *NSEC) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len() + len(rr.NextDomain) + 1
|
||||||
l += domainNameLen(rr.NextDomain, off+l, compression, false)
|
|
||||||
lastwindow := uint32(2 ^ 32 + 1)
|
lastwindow := uint32(2 ^ 32 + 1)
|
||||||
for _, t := range rr.TypeBitMap {
|
for _, t := range rr.TypeBitMap {
|
||||||
window := t / 256
|
window := t / 256
|
||||||
|
@ -1011,15 +966,14 @@ func (rr *NSEC3) String() string {
|
||||||
" " + strconv.Itoa(int(rr.Iterations)) +
|
" " + strconv.Itoa(int(rr.Iterations)) +
|
||||||
" " + saltToString(rr.Salt) +
|
" " + saltToString(rr.Salt) +
|
||||||
" " + rr.NextDomain
|
" " + rr.NextDomain
|
||||||
for _, t := range rr.TypeBitMap {
|
for i := 0; i < len(rr.TypeBitMap); i++ {
|
||||||
s += " " + Type(t).String()
|
s += " " + Type(rr.TypeBitMap[i]).String()
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rr *NSEC3) len(off int, compression map[string]struct{}) int {
|
func (rr *NSEC3) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len() + 6 + len(rr.Salt)/2 + 1 + len(rr.NextDomain) + 1
|
||||||
l += 6 + len(rr.Salt)/2 + 1 + len(rr.NextDomain) + 1
|
|
||||||
lastwindow := uint32(2 ^ 32 + 1)
|
lastwindow := uint32(2 ^ 32 + 1)
|
||||||
for _, t := range rr.TypeBitMap {
|
for _, t := range rr.TypeBitMap {
|
||||||
window := t / 256
|
window := t / 256
|
||||||
|
@ -1066,16 +1020,10 @@ type TKEY struct {
|
||||||
|
|
||||||
// TKEY has no official presentation format, but this will suffice.
|
// TKEY has no official presentation format, but this will suffice.
|
||||||
func (rr *TKEY) String() string {
|
func (rr *TKEY) String() string {
|
||||||
s := ";" + rr.Hdr.String() +
|
s := "\n;; TKEY PSEUDOSECTION:\n"
|
||||||
" " + rr.Algorithm +
|
s += rr.Hdr.String() + " " + rr.Algorithm + " " +
|
||||||
" " + TimeToString(rr.Inception) +
|
strconv.Itoa(int(rr.KeySize)) + " " + rr.Key + " " +
|
||||||
" " + TimeToString(rr.Expiration) +
|
strconv.Itoa(int(rr.OtherLen)) + " " + rr.OtherData
|
||||||
" " + strconv.Itoa(int(rr.Mode)) +
|
|
||||||
" " + strconv.Itoa(int(rr.Error)) +
|
|
||||||
" " + strconv.Itoa(int(rr.KeySize)) +
|
|
||||||
" " + rr.Key +
|
|
||||||
" " + strconv.Itoa(int(rr.OtherLen)) +
|
|
||||||
" " + rr.OtherData
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1335,15 +1283,14 @@ type CSYNC struct {
|
||||||
func (rr *CSYNC) String() string {
|
func (rr *CSYNC) String() string {
|
||||||
s := rr.Hdr.String() + strconv.FormatInt(int64(rr.Serial), 10) + " " + strconv.Itoa(int(rr.Flags))
|
s := rr.Hdr.String() + strconv.FormatInt(int64(rr.Serial), 10) + " " + strconv.Itoa(int(rr.Flags))
|
||||||
|
|
||||||
for _, t := range rr.TypeBitMap {
|
for i := 0; i < len(rr.TypeBitMap); i++ {
|
||||||
s += " " + Type(t).String()
|
s += " " + Type(rr.TypeBitMap[i]).String()
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rr *CSYNC) len(off int, compression map[string]struct{}) int {
|
func (rr *CSYNC) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len() + 4 + 2
|
||||||
l += 4 + 2
|
|
||||||
lastwindow := uint32(2 ^ 32 + 1)
|
lastwindow := uint32(2 ^ 32 + 1)
|
||||||
for _, t := range rr.TypeBitMap {
|
for _, t := range rr.TypeBitMap {
|
||||||
window := t / 256
|
window := t / 256
|
||||||
|
@ -1359,11 +1306,11 @@ func (rr *CSYNC) len(off int, compression map[string]struct{}) int {
|
||||||
// string representation used when printing the record.
|
// string representation used when printing the record.
|
||||||
// It takes serial arithmetic (RFC 1982) into account.
|
// It takes serial arithmetic (RFC 1982) into account.
|
||||||
func TimeToString(t uint32) string {
|
func TimeToString(t uint32) string {
|
||||||
mod := (int64(t)-time.Now().Unix())/year68 - 1
|
mod := ((int64(t) - time.Now().Unix()) / year68) - 1
|
||||||
if mod < 0 {
|
if mod < 0 {
|
||||||
mod = 0
|
mod = 0
|
||||||
}
|
}
|
||||||
ti := time.Unix(int64(t)-mod*year68, 0).UTC()
|
ti := time.Unix(int64(t)-(mod*year68), 0).UTC()
|
||||||
return ti.Format("20060102150405")
|
return ti.Format("20060102150405")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1375,11 +1322,11 @@ func StringToTime(s string) (uint32, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
mod := t.Unix()/year68 - 1
|
mod := (t.Unix() / year68) - 1
|
||||||
if mod < 0 {
|
if mod < 0 {
|
||||||
mod = 0
|
mod = 0
|
||||||
}
|
}
|
||||||
return uint32(t.Unix() - mod*year68), nil
|
return uint32(t.Unix() - (mod * year68)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// saltToString converts a NSECX salt to uppercase and returns "-" when it is empty.
|
// saltToString converts a NSECX salt to uppercase and returns "-" when it is empty.
|
||||||
|
|
|
@ -27,7 +27,8 @@ var skipLen = map[string]struct{}{
|
||||||
}
|
}
|
||||||
|
|
||||||
var packageHdr = `
|
var packageHdr = `
|
||||||
// Code generated by "go run types_generate.go"; DO NOT EDIT.
|
// *** DO NOT MODIFY ***
|
||||||
|
// AUTOGENERATED BY go generate from types_generate.go
|
||||||
|
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
|
@ -153,8 +154,8 @@ func main() {
|
||||||
if isEmbedded {
|
if isEmbedded {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fmt.Fprintf(b, "func (rr *%s) len(off int, compression map[string]struct{}) int {\n", name)
|
fmt.Fprintf(b, "func (rr *%s) len() int {\n", name)
|
||||||
fmt.Fprintf(b, "l := rr.Hdr.len(off, compression)\n")
|
fmt.Fprintf(b, "l := rr.Hdr.len()\n")
|
||||||
for i := 1; i < st.NumFields(); i++ {
|
for i := 1; i < st.NumFields(); i++ {
|
||||||
o := func(s string) { fmt.Fprintf(b, s, st.Field(i).Name()) }
|
o := func(s string) { fmt.Fprintf(b, s, st.Field(i).Name()) }
|
||||||
|
|
||||||
|
@ -162,11 +163,7 @@ func main() {
|
||||||
switch st.Tag(i) {
|
switch st.Tag(i) {
|
||||||
case `dns:"-"`:
|
case `dns:"-"`:
|
||||||
// ignored
|
// ignored
|
||||||
case `dns:"cdomain-name"`:
|
case `dns:"cdomain-name"`, `dns:"domain-name"`, `dns:"txt"`:
|
||||||
o("for _, x := range rr.%s { l += domainNameLen(x, off+l, compression, true) }\n")
|
|
||||||
case `dns:"domain-name"`:
|
|
||||||
o("for _, x := range rr.%s { l += domainNameLen(x, off+l, compression, false) }\n")
|
|
||||||
case `dns:"txt"`:
|
|
||||||
o("for _, x := range rr.%s { l += len(x) + 1 }\n")
|
o("for _, x := range rr.%s { l += len(x) + 1 }\n")
|
||||||
default:
|
default:
|
||||||
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
|
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
|
||||||
|
@ -177,28 +174,22 @@ func main() {
|
||||||
switch {
|
switch {
|
||||||
case st.Tag(i) == `dns:"-"`:
|
case st.Tag(i) == `dns:"-"`:
|
||||||
// ignored
|
// ignored
|
||||||
case st.Tag(i) == `dns:"cdomain-name"`:
|
case st.Tag(i) == `dns:"cdomain-name"`, st.Tag(i) == `dns:"domain-name"`:
|
||||||
o("l += domainNameLen(rr.%s, off+l, compression, true)\n")
|
o("l += len(rr.%s) + 1\n")
|
||||||
case st.Tag(i) == `dns:"domain-name"`:
|
|
||||||
o("l += domainNameLen(rr.%s, off+l, compression, false)\n")
|
|
||||||
case st.Tag(i) == `dns:"octet"`:
|
case st.Tag(i) == `dns:"octet"`:
|
||||||
o("l += len(rr.%s)\n")
|
o("l += len(rr.%s)\n")
|
||||||
case strings.HasPrefix(st.Tag(i), `dns:"size-base64`):
|
case strings.HasPrefix(st.Tag(i), `dns:"size-base64`):
|
||||||
fallthrough
|
fallthrough
|
||||||
case st.Tag(i) == `dns:"base64"`:
|
case st.Tag(i) == `dns:"base64"`:
|
||||||
o("l += base64.StdEncoding.DecodedLen(len(rr.%s))\n")
|
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`):
|
case strings.HasPrefix(st.Tag(i), `dns:"size-hex`):
|
||||||
fallthrough
|
fallthrough
|
||||||
case st.Tag(i) == `dns:"hex"`:
|
case st.Tag(i) == `dns:"hex"`:
|
||||||
o("l += len(rr.%s)/2 + 1\n")
|
o("l += len(rr.%s)/2 + 1\n")
|
||||||
case st.Tag(i) == `dns:"any"`:
|
|
||||||
o("l += len(rr.%s)\n")
|
|
||||||
case st.Tag(i) == `dns:"a"`:
|
case st.Tag(i) == `dns:"a"`:
|
||||||
o("if len(rr.%s) != 0 { l += net.IPv4len }\n")
|
o("l += net.IPv4len // %s\n")
|
||||||
case st.Tag(i) == `dns:"aaaa"`:
|
case st.Tag(i) == `dns:"aaaa"`:
|
||||||
o("if len(rr.%s) != 0 { l += net.IPv6len }\n")
|
o("l += net.IPv6len // %s\n")
|
||||||
case st.Tag(i) == `dns:"txt"`:
|
case st.Tag(i) == `dns:"txt"`:
|
||||||
o("for _, t := range rr.%s { l += len(t) + 1 }\n")
|
o("for _, t := range rr.%s { l += len(t) + 1 }\n")
|
||||||
case st.Tag(i) == `dns:"uint48"`:
|
case st.Tag(i) == `dns:"uint48"`:
|
||||||
|
@ -234,7 +225,7 @@ func main() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fmt.Fprintf(b, "func (rr *%s) copy() RR {\n", name)
|
fmt.Fprintf(b, "func (rr *%s) copy() RR {\n", name)
|
||||||
fields := []string{"rr.Hdr"}
|
fields := []string{"*rr.Hdr.copyHeader()"}
|
||||||
for i := 1; i < st.NumFields(); i++ {
|
for i := 1; i < st.NumFields(); i++ {
|
||||||
f := st.Field(i).Name()
|
f := st.Field(i).Name()
|
||||||
if sl, ok := st.Field(i).Type().(*types.Slice); ok {
|
if sl, ok := st.Field(i).Type().(*types.Slice); ok {
|
||||||
|
@ -244,13 +235,6 @@ func main() {
|
||||||
splits := strings.Split(t, ".")
|
splits := strings.Split(t, ".")
|
||||||
t = splits[len(splits)-1]
|
t = splits[len(splits)-1]
|
||||||
}
|
}
|
||||||
// For the EDNS0 interface (used in the OPT RR), we need to call the copy method on each element.
|
|
||||||
if t == "EDNS0" {
|
|
||||||
fmt.Fprintf(b, "%s := make([]%s, len(rr.%s));\nfor i,e := range rr.%s {\n %s[i] = e.copy()\n}\n",
|
|
||||||
f, t, f, f, f)
|
|
||||||
fields = append(fields, f)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fmt.Fprintf(b, "%s := make([]%s, len(rr.%s)); copy(%s, rr.%s)\n",
|
fmt.Fprintf(b, "%s := make([]%s, len(rr.%s)); copy(%s, rr.%s)\n",
|
||||||
f, t, f, f, f)
|
f, t, f, f, f)
|
||||||
fields = append(fields, f)
|
fields = append(fields, f)
|
||||||
|
|
|
@ -4,27 +4,8 @@ package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"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
|
// SessionUDP holds the remote address and the associated
|
||||||
// out-of-band data.
|
// out-of-band data.
|
||||||
type SessionUDP struct {
|
type SessionUDP struct {
|
||||||
|
@ -38,7 +19,7 @@ func (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr }
|
||||||
// ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a
|
// ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a
|
||||||
// net.UDPAddr.
|
// net.UDPAddr.
|
||||||
func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) {
|
func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) {
|
||||||
oob := make([]byte, udpOOBSize)
|
oob := make([]byte, 40)
|
||||||
n, oobn, _, raddr, err := conn.ReadMsgUDP(b, oob)
|
n, oobn, _, raddr, err := conn.ReadMsgUDP(b, oob)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return n, nil, err
|
return n, nil, err
|
||||||
|
@ -53,50 +34,12 @@ func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, e
|
||||||
return n, err
|
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
|
// correctSource takes oob data and returns new oob data with the Src equal to the Dst
|
||||||
func correctSource(oob []byte) []byte {
|
func correctSource(oob []byte) []byte {
|
||||||
dst := parseDstFromOOB(oob)
|
dst, err := parseUDPSocketDst(oob)
|
||||||
if dst == nil {
|
// If the destination could not be determined, ignore.
|
||||||
|
if err != nil || dst == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// If the dst is definitely an IPv6, then use ipv6's ControlMessage to
|
return marshalUDPSocketSrc(dst)
|
||||||
// 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,220 @@
|
||||||
|
// +build linux,!appengine
|
||||||
|
|
||||||
|
package dns
|
||||||
|
|
||||||
|
// See:
|
||||||
|
// * http://stackoverflow.com/questions/3062205/setting-the-source-ip-for-a-udp-socket and
|
||||||
|
// * http://blog.powerdns.com/2012/10/08/on-binding-datagram-udp-sockets-to-the-any-addresses/
|
||||||
|
//
|
||||||
|
// Why do we need this: When listening on 0.0.0.0 with UDP so kernel decides what is the outgoing
|
||||||
|
// interface, this might not always be the correct one. This code will make sure the egress
|
||||||
|
// packet's interface matched the ingress' one.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/miekg/dns/internal/socket"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
sizeofInet6Pktinfo = 0x14
|
||||||
|
sizeofInetPktinfo = 0xc
|
||||||
|
protocolIP = 0
|
||||||
|
protocolIPv6 = 41
|
||||||
|
)
|
||||||
|
|
||||||
|
type inetPktinfo struct {
|
||||||
|
Ifindex int32
|
||||||
|
Spec_dst [4]byte /* in_addr */
|
||||||
|
Addr [4]byte /* in_addr */
|
||||||
|
}
|
||||||
|
|
||||||
|
type inet6Pktinfo struct {
|
||||||
|
Addr [16]byte /* in6_addr */
|
||||||
|
Ifindex int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type inetControlMessage struct {
|
||||||
|
Src net.IP // source address, specifying only
|
||||||
|
Dst net.IP // destination address, receiving only
|
||||||
|
}
|
||||||
|
|
||||||
|
// setUDPSocketOptions sets the UDP socket options.
|
||||||
|
// This function is implemented on a per platform basis. See udp_*.go for more details
|
||||||
|
func setUDPSocketOptions(conn *net.UDPConn) error {
|
||||||
|
sa, err := getUDPSocketName(conn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch sa.(type) {
|
||||||
|
case *syscall.SockaddrInet6:
|
||||||
|
v6only, err := getUDPSocketOptions6Only(conn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
setUDPSocketOptions6(conn)
|
||||||
|
if !v6only {
|
||||||
|
setUDPSocketOptions4(conn)
|
||||||
|
}
|
||||||
|
case *syscall.SockaddrInet4:
|
||||||
|
setUDPSocketOptions4(conn)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// setUDPSocketOptions4 prepares the v4 socket for sessions.
|
||||||
|
func setUDPSocketOptions4(conn *net.UDPConn) error {
|
||||||
|
file, err := conn.File()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IP, syscall.IP_PKTINFO, 1); err != nil {
|
||||||
|
file.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Calling File() above results in the connection becoming blocking, we must fix that.
|
||||||
|
// See https://github.com/miekg/dns/issues/279
|
||||||
|
err = syscall.SetNonblock(int(file.Fd()), true)
|
||||||
|
if err != nil {
|
||||||
|
file.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
file.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// setUDPSocketOptions6 prepares the v6 socket for sessions.
|
||||||
|
func setUDPSocketOptions6(conn *net.UDPConn) error {
|
||||||
|
file, err := conn.File()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_RECVPKTINFO, 1); err != nil {
|
||||||
|
file.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = syscall.SetNonblock(int(file.Fd()), true)
|
||||||
|
if err != nil {
|
||||||
|
file.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
file.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getUDPSocketOption6Only return true if the socket is v6 only and false when it is v4/v6 combined
|
||||||
|
// (dualstack).
|
||||||
|
func getUDPSocketOptions6Only(conn *net.UDPConn) (bool, error) {
|
||||||
|
file, err := conn.File()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
// dual stack. See http://stackoverflow.com/questions/1618240/how-to-support-both-ipv4-and-ipv6-connections
|
||||||
|
v6only, err := syscall.GetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY)
|
||||||
|
if err != nil {
|
||||||
|
file.Close()
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
file.Close()
|
||||||
|
return v6only == 1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUDPSocketName(conn *net.UDPConn) (syscall.Sockaddr, error) {
|
||||||
|
file, err := conn.File()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
return syscall.Getsockname(int(file.Fd()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// marshalInetPacketInfo marshals a ipv4 control message, returning
|
||||||
|
// the byte slice for the next marshal, if any
|
||||||
|
func marshalInetPacketInfo(b []byte, cm *inetControlMessage) []byte {
|
||||||
|
m := socket.ControlMessage(b)
|
||||||
|
m.MarshalHeader(protocolIP, syscall.IP_PKTINFO, sizeofInetPktinfo)
|
||||||
|
if cm != nil {
|
||||||
|
pi := (*inetPktinfo)(unsafe.Pointer(&m.Data(sizeofInetPktinfo)[0]))
|
||||||
|
if ip := cm.Src.To4(); ip != nil {
|
||||||
|
copy(pi.Spec_dst[:], ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m.Next(sizeofInetPktinfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// marshalInet6PacketInfo marshals a ipv6 control message, returning
|
||||||
|
// the byte slice for the next marshal, if any
|
||||||
|
func marshalInet6PacketInfo(b []byte, cm *inetControlMessage) []byte {
|
||||||
|
m := socket.ControlMessage(b)
|
||||||
|
m.MarshalHeader(protocolIPv6, syscall.IPV6_PKTINFO, sizeofInet6Pktinfo)
|
||||||
|
if cm != nil {
|
||||||
|
pi := (*inet6Pktinfo)(unsafe.Pointer(&m.Data(sizeofInet6Pktinfo)[0]))
|
||||||
|
if ip := cm.Src.To16(); ip != nil && ip.To4() == nil {
|
||||||
|
copy(pi.Addr[:], ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m.Next(sizeofInet6Pktinfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseInetPacketInfo(cm *inetControlMessage, b []byte) {
|
||||||
|
pi := (*inetPktinfo)(unsafe.Pointer(&b[0]))
|
||||||
|
if len(cm.Dst) < net.IPv4len {
|
||||||
|
cm.Dst = make(net.IP, net.IPv4len)
|
||||||
|
}
|
||||||
|
copy(cm.Dst, pi.Addr[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseInet6PacketInfo(cm *inetControlMessage, b []byte) {
|
||||||
|
pi := (*inet6Pktinfo)(unsafe.Pointer(&b[0]))
|
||||||
|
if len(cm.Dst) < net.IPv6len {
|
||||||
|
cm.Dst = make(net.IP, net.IPv6len)
|
||||||
|
}
|
||||||
|
copy(cm.Dst, pi.Addr[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseUDPSocketDst takes out-of-band data from ReadMsgUDP and parses it for
|
||||||
|
// the Dst address
|
||||||
|
func parseUDPSocketDst(oob []byte) (net.IP, error) {
|
||||||
|
cm := new(inetControlMessage)
|
||||||
|
ms, err := socket.ControlMessage(oob).Parse()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, m := range ms {
|
||||||
|
lvl, typ, l, err := m.ParseHeader()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if lvl == protocolIPv6 { // IPv6
|
||||||
|
if typ == syscall.IPV6_PKTINFO && l >= sizeofInet6Pktinfo {
|
||||||
|
parseInet6PacketInfo(cm, m.Data(l))
|
||||||
|
}
|
||||||
|
} else if lvl == protocolIP { // IPv4
|
||||||
|
if typ == syscall.IP_PKTINFO && l >= sizeofInetPktinfo {
|
||||||
|
parseInetPacketInfo(cm, m.Data(l))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cm.Dst, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// marshalUDPSocketSrc takes the given src address and returns out-of-band data
|
||||||
|
// to give to WriteMsgUDP
|
||||||
|
func marshalUDPSocketSrc(src net.IP) []byte {
|
||||||
|
var oob []byte
|
||||||
|
// If the dst is definitely an ipv6, then use ipv6 control to respond
|
||||||
|
// otherwise use ipv4 because the ipv6 marshal ignores ipv4 messages.
|
||||||
|
// See marshalInet6PacketInfo
|
||||||
|
cm := new(inetControlMessage)
|
||||||
|
cm.Src = src
|
||||||
|
if src.To4() == nil {
|
||||||
|
oob = make([]byte, socket.ControlMessageSpace(sizeofInet6Pktinfo))
|
||||||
|
marshalInet6PacketInfo(oob, cm)
|
||||||
|
} else {
|
||||||
|
oob = make([]byte, socket.ControlMessageSpace(sizeofInetPktinfo))
|
||||||
|
marshalInetPacketInfo(oob, cm)
|
||||||
|
}
|
||||||
|
return oob
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
// +build !linux appengine
|
||||||
|
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// These do nothing. See udp_linux.go for an example of how to implement this.
|
||||||
|
|
||||||
|
// We tried to adhire to some kind of naming scheme.
|
||||||
|
func setUDPSocketOptions(conn *net.UDPConn) error { return nil }
|
||||||
|
func setUDPSocketOptions4(conn *net.UDPConn) error { return nil }
|
||||||
|
func setUDPSocketOptions6(conn *net.UDPConn) error { return nil }
|
||||||
|
func getUDPSocketOptions6Only(conn *net.UDPConn) (bool, error) { return false, nil }
|
||||||
|
func parseUDPSocketDst(oob []byte) (net.IP, error) { return nil, nil }
|
||||||
|
func marshalUDPSocketSrc(src net.IP) []byte { return nil }
|
|
@ -14,22 +14,17 @@ func (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr }
|
||||||
|
|
||||||
// ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a
|
// ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a
|
||||||
// net.UDPAddr.
|
// net.UDPAddr.
|
||||||
// TODO(fastest963): Once go1.10 is released, use ReadMsgUDP.
|
|
||||||
func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) {
|
func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) {
|
||||||
n, raddr, err := conn.ReadFrom(b)
|
n, raddr, err := conn.ReadFrom(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return n, nil, err
|
return n, nil, err
|
||||||
}
|
}
|
||||||
return n, &SessionUDP{raddr.(*net.UDPAddr)}, err
|
session := &SessionUDP{raddr.(*net.UDPAddr)}
|
||||||
|
return n, session, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteToSessionUDP acts just like net.UDPConn.WriteTo(), but uses a *SessionUDP instead of a net.Addr.
|
// WriteToSessionUDP acts just like net.UDPConn.WriteTo(), but uses a *SessionUDP instead of a net.Addr.
|
||||||
// TODO(fastest963): Once go1.10 is released, use WriteMsgUDP.
|
|
||||||
func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) {
|
func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) {
|
||||||
return conn.WriteTo(b, session.raddr)
|
n, err := conn.WriteTo(b, session.raddr)
|
||||||
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(fastest963): Once go1.10 is released and we can use *MsgUDP methods
|
|
||||||
// use the standard method in udp.go for these.
|
|
||||||
func setUDPSocketOptions(*net.UDPConn) error { return nil }
|
|
||||||
func parseDstFromOOB([]byte, net.IP) net.IP { return nil }
|
|
||||||
|
|
|
@ -44,8 +44,7 @@ func (u *Msg) RRsetUsed(rr []RR) {
|
||||||
u.Answer = make([]RR, 0, len(rr))
|
u.Answer = make([]RR, 0, len(rr))
|
||||||
}
|
}
|
||||||
for _, r := range rr {
|
for _, r := range rr {
|
||||||
h := r.Header()
|
u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: r.Header().Rrtype, Class: ClassANY}})
|
||||||
u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: h.Name, Ttl: 0, Rrtype: h.Rrtype, Class: ClassANY}})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,8 +55,7 @@ func (u *Msg) RRsetNotUsed(rr []RR) {
|
||||||
u.Answer = make([]RR, 0, len(rr))
|
u.Answer = make([]RR, 0, len(rr))
|
||||||
}
|
}
|
||||||
for _, r := range rr {
|
for _, r := range rr {
|
||||||
h := r.Header()
|
u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: r.Header().Rrtype, Class: ClassNONE}})
|
||||||
u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: h.Name, Ttl: 0, Rrtype: h.Rrtype, Class: ClassNONE}})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,8 +79,7 @@ func (u *Msg) RemoveRRset(rr []RR) {
|
||||||
u.Ns = make([]RR, 0, len(rr))
|
u.Ns = make([]RR, 0, len(rr))
|
||||||
}
|
}
|
||||||
for _, r := range rr {
|
for _, r := range rr {
|
||||||
h := r.Header()
|
u.Ns = append(u.Ns, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: r.Header().Rrtype, Class: ClassANY}})
|
||||||
u.Ns = append(u.Ns, &ANY{Hdr: RR_Header{Name: h.Name, Ttl: 0, Rrtype: h.Rrtype, Class: ClassANY}})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,9 +99,8 @@ func (u *Msg) Remove(rr []RR) {
|
||||||
u.Ns = make([]RR, 0, len(rr))
|
u.Ns = make([]RR, 0, len(rr))
|
||||||
}
|
}
|
||||||
for _, r := range rr {
|
for _, r := range rr {
|
||||||
h := r.Header()
|
r.Header().Class = ClassNONE
|
||||||
h.Class = ClassNONE
|
r.Header().Ttl = 0
|
||||||
h.Ttl = 0
|
|
||||||
u.Ns = append(u.Ns, r)
|
u.Ns = append(u.Ns, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
package dns
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
// Version is current version of this library.
|
|
||||||
var Version = V{1, 1, 9}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
|
|
@ -35,36 +35,30 @@ type Transfer struct {
|
||||||
// channel, err := transfer.In(message, master)
|
// channel, err := transfer.In(message, master)
|
||||||
//
|
//
|
||||||
func (t *Transfer) In(q *Msg, a string) (env chan *Envelope, err error) {
|
func (t *Transfer) In(q *Msg, a string) (env chan *Envelope, err error) {
|
||||||
switch q.Question[0].Qtype {
|
|
||||||
case TypeAXFR, TypeIXFR:
|
|
||||||
default:
|
|
||||||
return nil, &Error{"unsupported question type"}
|
|
||||||
}
|
|
||||||
|
|
||||||
timeout := dnsTimeout
|
timeout := dnsTimeout
|
||||||
if t.DialTimeout != 0 {
|
if t.DialTimeout != 0 {
|
||||||
timeout = t.DialTimeout
|
timeout = t.DialTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.Conn == nil {
|
if t.Conn == nil {
|
||||||
t.Conn, err = DialTimeout("tcp", a, timeout)
|
t.Conn, err = DialTimeout("tcp", a, timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := t.WriteMsg(q); err != nil {
|
if err := t.WriteMsg(q); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
env = make(chan *Envelope)
|
env = make(chan *Envelope)
|
||||||
switch q.Question[0].Qtype {
|
go func() {
|
||||||
case TypeAXFR:
|
if q.Question[0].Qtype == TypeAXFR {
|
||||||
go t.inAxfr(q, env)
|
go t.inAxfr(q, env)
|
||||||
case TypeIXFR:
|
return
|
||||||
go t.inIxfr(q, env)
|
|
||||||
}
|
}
|
||||||
|
if q.Question[0].Qtype == TypeIXFR {
|
||||||
|
go t.inIxfr(q, env)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
return env, nil
|
return env, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +111,7 @@ func (t *Transfer) inAxfr(q *Msg, c chan *Envelope) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transfer) inIxfr(q *Msg, c chan *Envelope) {
|
func (t *Transfer) inIxfr(q *Msg, c chan *Envelope) {
|
||||||
var serial uint32 // The first serial seen is the current server serial
|
serial := uint32(0) // The first serial seen is the current server serial
|
||||||
axfr := true
|
axfr := true
|
||||||
n := 0
|
n := 0
|
||||||
qser := q.Ns[0].(*SOA).Serial
|
qser := q.Ns[0].(*SOA).Serial
|
||||||
|
@ -243,18 +237,24 @@ func (t *Transfer) WriteMsg(m *Msg) (err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = t.Write(out)
|
if _, err = t.Write(out); err != nil {
|
||||||
return err
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isSOAFirst(in *Msg) bool {
|
func isSOAFirst(in *Msg) bool {
|
||||||
return len(in.Answer) > 0 &&
|
if len(in.Answer) > 0 {
|
||||||
in.Answer[0].Header().Rrtype == TypeSOA
|
return in.Answer[0].Header().Rrtype == TypeSOA
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func isSOALast(in *Msg) bool {
|
func isSOALast(in *Msg) bool {
|
||||||
return len(in.Answer) > 0 &&
|
if len(in.Answer) > 0 {
|
||||||
in.Answer[len(in.Answer)-1].Header().Rrtype == TypeSOA
|
return in.Answer[len(in.Answer)-1].Header().Rrtype == TypeSOA
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const errXFR = "bad xfr rcode: %d"
|
const errXFR = "bad xfr rcode: %d"
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
// *** DO NOT MODIFY ***
|
||||||
|
// AUTOGENERATED BY go generate from compress_generate.go
|
||||||
|
|
||||||
|
package dns
|
||||||
|
|
||||||
|
func compressionLenHelperType(c map[string]int, r RR) {
|
||||||
|
switch x := r.(type) {
|
||||||
|
case *AFSDB:
|
||||||
|
compressionLenHelper(c, x.Hostname)
|
||||||
|
case *CNAME:
|
||||||
|
compressionLenHelper(c, x.Target)
|
||||||
|
case *DNAME:
|
||||||
|
compressionLenHelper(c, x.Target)
|
||||||
|
case *HIP:
|
||||||
|
for i := range x.RendezvousServers {
|
||||||
|
compressionLenHelper(c, x.RendezvousServers[i])
|
||||||
|
}
|
||||||
|
case *KX:
|
||||||
|
compressionLenHelper(c, x.Exchanger)
|
||||||
|
case *LP:
|
||||||
|
compressionLenHelper(c, x.Fqdn)
|
||||||
|
case *MB:
|
||||||
|
compressionLenHelper(c, x.Mb)
|
||||||
|
case *MD:
|
||||||
|
compressionLenHelper(c, x.Md)
|
||||||
|
case *MF:
|
||||||
|
compressionLenHelper(c, x.Mf)
|
||||||
|
case *MG:
|
||||||
|
compressionLenHelper(c, x.Mg)
|
||||||
|
case *MINFO:
|
||||||
|
compressionLenHelper(c, x.Rmail)
|
||||||
|
compressionLenHelper(c, x.Email)
|
||||||
|
case *MR:
|
||||||
|
compressionLenHelper(c, x.Mr)
|
||||||
|
case *MX:
|
||||||
|
compressionLenHelper(c, x.Mx)
|
||||||
|
case *NAPTR:
|
||||||
|
compressionLenHelper(c, x.Replacement)
|
||||||
|
case *NS:
|
||||||
|
compressionLenHelper(c, x.Ns)
|
||||||
|
case *NSAPPTR:
|
||||||
|
compressionLenHelper(c, x.Ptr)
|
||||||
|
case *NSEC:
|
||||||
|
compressionLenHelper(c, x.NextDomain)
|
||||||
|
case *PTR:
|
||||||
|
compressionLenHelper(c, x.Ptr)
|
||||||
|
case *PX:
|
||||||
|
compressionLenHelper(c, x.Map822)
|
||||||
|
compressionLenHelper(c, x.Mapx400)
|
||||||
|
case *RP:
|
||||||
|
compressionLenHelper(c, x.Mbox)
|
||||||
|
compressionLenHelper(c, x.Txt)
|
||||||
|
case *RRSIG:
|
||||||
|
compressionLenHelper(c, x.SignerName)
|
||||||
|
case *RT:
|
||||||
|
compressionLenHelper(c, x.Host)
|
||||||
|
case *SIG:
|
||||||
|
compressionLenHelper(c, x.SignerName)
|
||||||
|
case *SOA:
|
||||||
|
compressionLenHelper(c, x.Ns)
|
||||||
|
compressionLenHelper(c, x.Mbox)
|
||||||
|
case *SRV:
|
||||||
|
compressionLenHelper(c, x.Target)
|
||||||
|
case *TALINK:
|
||||||
|
compressionLenHelper(c, x.PreviousName)
|
||||||
|
compressionLenHelper(c, x.NextName)
|
||||||
|
case *TKEY:
|
||||||
|
compressionLenHelper(c, x.Algorithm)
|
||||||
|
case *TSIG:
|
||||||
|
compressionLenHelper(c, x.Algorithm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func compressionLenSearchType(c map[string]int, r RR) (int, bool) {
|
||||||
|
switch x := r.(type) {
|
||||||
|
case *AFSDB:
|
||||||
|
k1, ok1 := compressionLenSearch(c, x.Hostname)
|
||||||
|
return k1, ok1
|
||||||
|
case *CNAME:
|
||||||
|
k1, ok1 := compressionLenSearch(c, x.Target)
|
||||||
|
return k1, ok1
|
||||||
|
case *MB:
|
||||||
|
k1, ok1 := compressionLenSearch(c, x.Mb)
|
||||||
|
return k1, ok1
|
||||||
|
case *MD:
|
||||||
|
k1, ok1 := compressionLenSearch(c, x.Md)
|
||||||
|
return k1, ok1
|
||||||
|
case *MF:
|
||||||
|
k1, ok1 := compressionLenSearch(c, x.Mf)
|
||||||
|
return k1, ok1
|
||||||
|
case *MG:
|
||||||
|
k1, ok1 := compressionLenSearch(c, x.Mg)
|
||||||
|
return k1, ok1
|
||||||
|
case *MINFO:
|
||||||
|
k1, ok1 := compressionLenSearch(c, x.Rmail)
|
||||||
|
k2, ok2 := compressionLenSearch(c, x.Email)
|
||||||
|
return k1 + k2, ok1 && ok2
|
||||||
|
case *MR:
|
||||||
|
k1, ok1 := compressionLenSearch(c, x.Mr)
|
||||||
|
return k1, ok1
|
||||||
|
case *MX:
|
||||||
|
k1, ok1 := compressionLenSearch(c, x.Mx)
|
||||||
|
return k1, ok1
|
||||||
|
case *NS:
|
||||||
|
k1, ok1 := compressionLenSearch(c, x.Ns)
|
||||||
|
return k1, ok1
|
||||||
|
case *PTR:
|
||||||
|
k1, ok1 := compressionLenSearch(c, x.Ptr)
|
||||||
|
return k1, ok1
|
||||||
|
case *RT:
|
||||||
|
k1, ok1 := compressionLenSearch(c, x.Host)
|
||||||
|
return k1, ok1
|
||||||
|
case *SOA:
|
||||||
|
k1, ok1 := compressionLenSearch(c, x.Ns)
|
||||||
|
k2, ok2 := compressionLenSearch(c, x.Mbox)
|
||||||
|
return k1 + k2, ok1 && ok2
|
||||||
|
}
|
||||||
|
return 0, false
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,5 @@
|
||||||
// Code generated by "go run types_generate.go"; DO NOT EDIT.
|
// *** DO NOT MODIFY ***
|
||||||
|
// AUTOGENERATED BY go generate from types_generate.go
|
||||||
|
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
|
@ -54,7 +55,6 @@ var TypeToRR = map[uint16]func() RR{
|
||||||
TypeNSEC: func() RR { return new(NSEC) },
|
TypeNSEC: func() RR { return new(NSEC) },
|
||||||
TypeNSEC3: func() RR { return new(NSEC3) },
|
TypeNSEC3: func() RR { return new(NSEC3) },
|
||||||
TypeNSEC3PARAM: func() RR { return new(NSEC3PARAM) },
|
TypeNSEC3PARAM: func() RR { return new(NSEC3PARAM) },
|
||||||
TypeNULL: func() RR { return new(NULL) },
|
|
||||||
TypeOPENPGPKEY: func() RR { return new(OPENPGPKEY) },
|
TypeOPENPGPKEY: func() RR { return new(OPENPGPKEY) },
|
||||||
TypeOPT: func() RR { return new(OPT) },
|
TypeOPT: func() RR { return new(OPT) },
|
||||||
TypePTR: func() RR { return new(PTR) },
|
TypePTR: func() RR { return new(PTR) },
|
||||||
|
@ -210,7 +210,6 @@ func (rr *NSAPPTR) Header() *RR_Header { return &rr.Hdr }
|
||||||
func (rr *NSEC) 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 *NSEC3) Header() *RR_Header { return &rr.Hdr }
|
||||||
func (rr *NSEC3PARAM) Header() *RR_Header { return &rr.Hdr }
|
func (rr *NSEC3PARAM) Header() *RR_Header { return &rr.Hdr }
|
||||||
func (rr *NULL) Header() *RR_Header { return &rr.Hdr }
|
|
||||||
func (rr *OPENPGPKEY) 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 *OPT) Header() *RR_Header { return &rr.Hdr }
|
||||||
func (rr *PTR) Header() *RR_Header { return &rr.Hdr }
|
func (rr *PTR) Header() *RR_Header { return &rr.Hdr }
|
||||||
|
@ -238,150 +237,144 @@ func (rr *URI) Header() *RR_Header { return &rr.Hdr }
|
||||||
func (rr *X25) Header() *RR_Header { return &rr.Hdr }
|
func (rr *X25) Header() *RR_Header { return &rr.Hdr }
|
||||||
|
|
||||||
// len() functions
|
// len() functions
|
||||||
func (rr *A) len(off int, compression map[string]struct{}) int {
|
func (rr *A) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
if len(rr.A) != 0 {
|
l += net.IPv4len // A
|
||||||
l += net.IPv4len
|
|
||||||
}
|
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *AAAA) len(off int, compression map[string]struct{}) int {
|
func (rr *AAAA) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
if len(rr.AAAA) != 0 {
|
l += net.IPv6len // AAAA
|
||||||
l += net.IPv6len
|
|
||||||
}
|
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *AFSDB) len(off int, compression map[string]struct{}) int {
|
func (rr *AFSDB) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += 2 // Subtype
|
l += 2 // Subtype
|
||||||
l += domainNameLen(rr.Hostname, off+l, compression, false)
|
l += len(rr.Hostname) + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *ANY) len(off int, compression map[string]struct{}) int {
|
func (rr *ANY) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *AVC) len(off int, compression map[string]struct{}) int {
|
func (rr *AVC) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
for _, x := range rr.Txt {
|
for _, x := range rr.Txt {
|
||||||
l += len(x) + 1
|
l += len(x) + 1
|
||||||
}
|
}
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *CAA) len(off int, compression map[string]struct{}) int {
|
func (rr *CAA) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l++ // Flag
|
l++ // Flag
|
||||||
l += len(rr.Tag) + 1
|
l += len(rr.Tag) + 1
|
||||||
l += len(rr.Value)
|
l += len(rr.Value)
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *CERT) len(off int, compression map[string]struct{}) int {
|
func (rr *CERT) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += 2 // Type
|
l += 2 // Type
|
||||||
l += 2 // KeyTag
|
l += 2 // KeyTag
|
||||||
l++ // Algorithm
|
l++ // Algorithm
|
||||||
l += base64.StdEncoding.DecodedLen(len(rr.Certificate))
|
l += base64.StdEncoding.DecodedLen(len(rr.Certificate))
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *CNAME) len(off int, compression map[string]struct{}) int {
|
func (rr *CNAME) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += domainNameLen(rr.Target, off+l, compression, true)
|
l += len(rr.Target) + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *DHCID) len(off int, compression map[string]struct{}) int {
|
func (rr *DHCID) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += base64.StdEncoding.DecodedLen(len(rr.Digest))
|
l += base64.StdEncoding.DecodedLen(len(rr.Digest))
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *DNAME) len(off int, compression map[string]struct{}) int {
|
func (rr *DNAME) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += domainNameLen(rr.Target, off+l, compression, false)
|
l += len(rr.Target) + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *DNSKEY) len(off int, compression map[string]struct{}) int {
|
func (rr *DNSKEY) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += 2 // Flags
|
l += 2 // Flags
|
||||||
l++ // Protocol
|
l++ // Protocol
|
||||||
l++ // Algorithm
|
l++ // Algorithm
|
||||||
l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
|
l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *DS) len(off int, compression map[string]struct{}) int {
|
func (rr *DS) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += 2 // KeyTag
|
l += 2 // KeyTag
|
||||||
l++ // Algorithm
|
l++ // Algorithm
|
||||||
l++ // DigestType
|
l++ // DigestType
|
||||||
l += len(rr.Digest)/2 + 1
|
l += len(rr.Digest)/2 + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *EID) len(off int, compression map[string]struct{}) int {
|
func (rr *EID) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += len(rr.Endpoint)/2 + 1
|
l += len(rr.Endpoint)/2 + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *EUI48) len(off int, compression map[string]struct{}) int {
|
func (rr *EUI48) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += 6 // Address
|
l += 6 // Address
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *EUI64) len(off int, compression map[string]struct{}) int {
|
func (rr *EUI64) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += 8 // Address
|
l += 8 // Address
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *GID) len(off int, compression map[string]struct{}) int {
|
func (rr *GID) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += 4 // Gid
|
l += 4 // Gid
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *GPOS) len(off int, compression map[string]struct{}) int {
|
func (rr *GPOS) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += len(rr.Longitude) + 1
|
l += len(rr.Longitude) + 1
|
||||||
l += len(rr.Latitude) + 1
|
l += len(rr.Latitude) + 1
|
||||||
l += len(rr.Altitude) + 1
|
l += len(rr.Altitude) + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *HINFO) len(off int, compression map[string]struct{}) int {
|
func (rr *HINFO) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += len(rr.Cpu) + 1
|
l += len(rr.Cpu) + 1
|
||||||
l += len(rr.Os) + 1
|
l += len(rr.Os) + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *HIP) len(off int, compression map[string]struct{}) int {
|
func (rr *HIP) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l++ // HitLength
|
l++ // HitLength
|
||||||
l++ // PublicKeyAlgorithm
|
l++ // PublicKeyAlgorithm
|
||||||
l += 2 // PublicKeyLength
|
l += 2 // PublicKeyLength
|
||||||
l += len(rr.Hit) / 2
|
l += len(rr.Hit)/2 + 1
|
||||||
l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
|
l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
|
||||||
for _, x := range rr.RendezvousServers {
|
for _, x := range rr.RendezvousServers {
|
||||||
l += domainNameLen(x, off+l, compression, false)
|
l += len(x) + 1
|
||||||
}
|
}
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *KX) len(off int, compression map[string]struct{}) int {
|
func (rr *KX) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += 2 // Preference
|
l += 2 // Preference
|
||||||
l += domainNameLen(rr.Exchanger, off+l, compression, false)
|
l += len(rr.Exchanger) + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *L32) len(off int, compression map[string]struct{}) int {
|
func (rr *L32) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += 2 // Preference
|
l += 2 // Preference
|
||||||
if len(rr.Locator32) != 0 {
|
l += net.IPv4len // Locator32
|
||||||
l += net.IPv4len
|
|
||||||
}
|
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *L64) len(off int, compression map[string]struct{}) int {
|
func (rr *L64) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += 2 // Preference
|
l += 2 // Preference
|
||||||
l += 8 // Locator64
|
l += 8 // Locator64
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *LOC) len(off int, compression map[string]struct{}) int {
|
func (rr *LOC) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l++ // Version
|
l++ // Version
|
||||||
l++ // Size
|
l++ // Size
|
||||||
l++ // HorizPre
|
l++ // HorizPre
|
||||||
|
@ -391,139 +384,134 @@ func (rr *LOC) len(off int, compression map[string]struct{}) int {
|
||||||
l += 4 // Altitude
|
l += 4 // Altitude
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *LP) len(off int, compression map[string]struct{}) int {
|
func (rr *LP) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += 2 // Preference
|
l += 2 // Preference
|
||||||
l += domainNameLen(rr.Fqdn, off+l, compression, false)
|
l += len(rr.Fqdn) + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *MB) len(off int, compression map[string]struct{}) int {
|
func (rr *MB) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += domainNameLen(rr.Mb, off+l, compression, true)
|
l += len(rr.Mb) + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *MD) len(off int, compression map[string]struct{}) int {
|
func (rr *MD) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += domainNameLen(rr.Md, off+l, compression, true)
|
l += len(rr.Md) + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *MF) len(off int, compression map[string]struct{}) int {
|
func (rr *MF) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += domainNameLen(rr.Mf, off+l, compression, true)
|
l += len(rr.Mf) + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *MG) len(off int, compression map[string]struct{}) int {
|
func (rr *MG) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += domainNameLen(rr.Mg, off+l, compression, true)
|
l += len(rr.Mg) + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *MINFO) len(off int, compression map[string]struct{}) int {
|
func (rr *MINFO) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += domainNameLen(rr.Rmail, off+l, compression, true)
|
l += len(rr.Rmail) + 1
|
||||||
l += domainNameLen(rr.Email, off+l, compression, true)
|
l += len(rr.Email) + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *MR) len(off int, compression map[string]struct{}) int {
|
func (rr *MR) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += domainNameLen(rr.Mr, off+l, compression, true)
|
l += len(rr.Mr) + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *MX) len(off int, compression map[string]struct{}) int {
|
func (rr *MX) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += 2 // Preference
|
l += 2 // Preference
|
||||||
l += domainNameLen(rr.Mx, off+l, compression, true)
|
l += len(rr.Mx) + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *NAPTR) len(off int, compression map[string]struct{}) int {
|
func (rr *NAPTR) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += 2 // Order
|
l += 2 // Order
|
||||||
l += 2 // Preference
|
l += 2 // Preference
|
||||||
l += len(rr.Flags) + 1
|
l += len(rr.Flags) + 1
|
||||||
l += len(rr.Service) + 1
|
l += len(rr.Service) + 1
|
||||||
l += len(rr.Regexp) + 1
|
l += len(rr.Regexp) + 1
|
||||||
l += domainNameLen(rr.Replacement, off+l, compression, false)
|
l += len(rr.Replacement) + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *NID) len(off int, compression map[string]struct{}) int {
|
func (rr *NID) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += 2 // Preference
|
l += 2 // Preference
|
||||||
l += 8 // NodeID
|
l += 8 // NodeID
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *NIMLOC) len(off int, compression map[string]struct{}) int {
|
func (rr *NIMLOC) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += len(rr.Locator)/2 + 1
|
l += len(rr.Locator)/2 + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *NINFO) len(off int, compression map[string]struct{}) int {
|
func (rr *NINFO) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
for _, x := range rr.ZSData {
|
for _, x := range rr.ZSData {
|
||||||
l += len(x) + 1
|
l += len(x) + 1
|
||||||
}
|
}
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *NS) len(off int, compression map[string]struct{}) int {
|
func (rr *NS) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += domainNameLen(rr.Ns, off+l, compression, true)
|
l += len(rr.Ns) + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *NSAPPTR) len(off int, compression map[string]struct{}) int {
|
func (rr *NSAPPTR) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += domainNameLen(rr.Ptr, off+l, compression, false)
|
l += len(rr.Ptr) + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *NSEC3PARAM) len(off int, compression map[string]struct{}) int {
|
func (rr *NSEC3PARAM) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l++ // Hash
|
l++ // Hash
|
||||||
l++ // Flags
|
l++ // Flags
|
||||||
l += 2 // Iterations
|
l += 2 // Iterations
|
||||||
l++ // SaltLength
|
l++ // SaltLength
|
||||||
l += len(rr.Salt) / 2
|
l += len(rr.Salt)/2 + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *NULL) len(off int, compression map[string]struct{}) int {
|
func (rr *OPENPGPKEY) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += len(rr.Data)
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
func (rr *OPENPGPKEY) len(off int, compression map[string]struct{}) int {
|
|
||||||
l := rr.Hdr.len(off, compression)
|
|
||||||
l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
|
l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *PTR) len(off int, compression map[string]struct{}) int {
|
func (rr *PTR) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += domainNameLen(rr.Ptr, off+l, compression, true)
|
l += len(rr.Ptr) + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *PX) len(off int, compression map[string]struct{}) int {
|
func (rr *PX) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += 2 // Preference
|
l += 2 // Preference
|
||||||
l += domainNameLen(rr.Map822, off+l, compression, false)
|
l += len(rr.Map822) + 1
|
||||||
l += domainNameLen(rr.Mapx400, off+l, compression, false)
|
l += len(rr.Mapx400) + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *RFC3597) len(off int, compression map[string]struct{}) int {
|
func (rr *RFC3597) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += len(rr.Rdata)/2 + 1
|
l += len(rr.Rdata)/2 + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *RKEY) len(off int, compression map[string]struct{}) int {
|
func (rr *RKEY) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += 2 // Flags
|
l += 2 // Flags
|
||||||
l++ // Protocol
|
l++ // Protocol
|
||||||
l++ // Algorithm
|
l++ // Algorithm
|
||||||
l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
|
l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *RP) len(off int, compression map[string]struct{}) int {
|
func (rr *RP) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += domainNameLen(rr.Mbox, off+l, compression, false)
|
l += len(rr.Mbox) + 1
|
||||||
l += domainNameLen(rr.Txt, off+l, compression, false)
|
l += len(rr.Txt) + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *RRSIG) len(off int, compression map[string]struct{}) int {
|
func (rr *RRSIG) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += 2 // TypeCovered
|
l += 2 // TypeCovered
|
||||||
l++ // Algorithm
|
l++ // Algorithm
|
||||||
l++ // Labels
|
l++ // Labels
|
||||||
|
@ -531,28 +519,28 @@ func (rr *RRSIG) len(off int, compression map[string]struct{}) int {
|
||||||
l += 4 // Expiration
|
l += 4 // Expiration
|
||||||
l += 4 // Inception
|
l += 4 // Inception
|
||||||
l += 2 // KeyTag
|
l += 2 // KeyTag
|
||||||
l += domainNameLen(rr.SignerName, off+l, compression, false)
|
l += len(rr.SignerName) + 1
|
||||||
l += base64.StdEncoding.DecodedLen(len(rr.Signature))
|
l += base64.StdEncoding.DecodedLen(len(rr.Signature))
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *RT) len(off int, compression map[string]struct{}) int {
|
func (rr *RT) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += 2 // Preference
|
l += 2 // Preference
|
||||||
l += domainNameLen(rr.Host, off+l, compression, false)
|
l += len(rr.Host) + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *SMIMEA) len(off int, compression map[string]struct{}) int {
|
func (rr *SMIMEA) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l++ // Usage
|
l++ // Usage
|
||||||
l++ // Selector
|
l++ // Selector
|
||||||
l++ // MatchingType
|
l++ // MatchingType
|
||||||
l += len(rr.Certificate)/2 + 1
|
l += len(rr.Certificate)/2 + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *SOA) len(off int, compression map[string]struct{}) int {
|
func (rr *SOA) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += domainNameLen(rr.Ns, off+l, compression, true)
|
l += len(rr.Ns) + 1
|
||||||
l += domainNameLen(rr.Mbox, off+l, compression, true)
|
l += len(rr.Mbox) + 1
|
||||||
l += 4 // Serial
|
l += 4 // Serial
|
||||||
l += 4 // Refresh
|
l += 4 // Refresh
|
||||||
l += 4 // Retry
|
l += 4 // Retry
|
||||||
|
@ -560,322 +548,317 @@ func (rr *SOA) len(off int, compression map[string]struct{}) int {
|
||||||
l += 4 // Minttl
|
l += 4 // Minttl
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *SPF) len(off int, compression map[string]struct{}) int {
|
func (rr *SPF) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
for _, x := range rr.Txt {
|
for _, x := range rr.Txt {
|
||||||
l += len(x) + 1
|
l += len(x) + 1
|
||||||
}
|
}
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *SRV) len(off int, compression map[string]struct{}) int {
|
func (rr *SRV) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += 2 // Priority
|
l += 2 // Priority
|
||||||
l += 2 // Weight
|
l += 2 // Weight
|
||||||
l += 2 // Port
|
l += 2 // Port
|
||||||
l += domainNameLen(rr.Target, off+l, compression, false)
|
l += len(rr.Target) + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *SSHFP) len(off int, compression map[string]struct{}) int {
|
func (rr *SSHFP) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l++ // Algorithm
|
l++ // Algorithm
|
||||||
l++ // Type
|
l++ // Type
|
||||||
l += len(rr.FingerPrint)/2 + 1
|
l += len(rr.FingerPrint)/2 + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *TA) len(off int, compression map[string]struct{}) int {
|
func (rr *TA) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += 2 // KeyTag
|
l += 2 // KeyTag
|
||||||
l++ // Algorithm
|
l++ // Algorithm
|
||||||
l++ // DigestType
|
l++ // DigestType
|
||||||
l += len(rr.Digest)/2 + 1
|
l += len(rr.Digest)/2 + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *TALINK) len(off int, compression map[string]struct{}) int {
|
func (rr *TALINK) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += domainNameLen(rr.PreviousName, off+l, compression, false)
|
l += len(rr.PreviousName) + 1
|
||||||
l += domainNameLen(rr.NextName, off+l, compression, false)
|
l += len(rr.NextName) + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *TKEY) len(off int, compression map[string]struct{}) int {
|
func (rr *TKEY) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += domainNameLen(rr.Algorithm, off+l, compression, false)
|
l += len(rr.Algorithm) + 1
|
||||||
l += 4 // Inception
|
l += 4 // Inception
|
||||||
l += 4 // Expiration
|
l += 4 // Expiration
|
||||||
l += 2 // Mode
|
l += 2 // Mode
|
||||||
l += 2 // Error
|
l += 2 // Error
|
||||||
l += 2 // KeySize
|
l += 2 // KeySize
|
||||||
l += len(rr.Key) / 2
|
l += len(rr.Key) + 1
|
||||||
l += 2 // OtherLen
|
l += 2 // OtherLen
|
||||||
l += len(rr.OtherData) / 2
|
l += len(rr.OtherData) + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *TLSA) len(off int, compression map[string]struct{}) int {
|
func (rr *TLSA) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l++ // Usage
|
l++ // Usage
|
||||||
l++ // Selector
|
l++ // Selector
|
||||||
l++ // MatchingType
|
l++ // MatchingType
|
||||||
l += len(rr.Certificate)/2 + 1
|
l += len(rr.Certificate)/2 + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *TSIG) len(off int, compression map[string]struct{}) int {
|
func (rr *TSIG) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += domainNameLen(rr.Algorithm, off+l, compression, false)
|
l += len(rr.Algorithm) + 1
|
||||||
l += 6 // TimeSigned
|
l += 6 // TimeSigned
|
||||||
l += 2 // Fudge
|
l += 2 // Fudge
|
||||||
l += 2 // MACSize
|
l += 2 // MACSize
|
||||||
l += len(rr.MAC) / 2
|
l += len(rr.MAC)/2 + 1
|
||||||
l += 2 // OrigId
|
l += 2 // OrigId
|
||||||
l += 2 // Error
|
l += 2 // Error
|
||||||
l += 2 // OtherLen
|
l += 2 // OtherLen
|
||||||
l += len(rr.OtherData) / 2
|
l += len(rr.OtherData)/2 + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *TXT) len(off int, compression map[string]struct{}) int {
|
func (rr *TXT) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
for _, x := range rr.Txt {
|
for _, x := range rr.Txt {
|
||||||
l += len(x) + 1
|
l += len(x) + 1
|
||||||
}
|
}
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *UID) len(off int, compression map[string]struct{}) int {
|
func (rr *UID) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += 4 // Uid
|
l += 4 // Uid
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *UINFO) len(off int, compression map[string]struct{}) int {
|
func (rr *UINFO) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += len(rr.Uinfo) + 1
|
l += len(rr.Uinfo) + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *URI) len(off int, compression map[string]struct{}) int {
|
func (rr *URI) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += 2 // Priority
|
l += 2 // Priority
|
||||||
l += 2 // Weight
|
l += 2 // Weight
|
||||||
l += len(rr.Target)
|
l += len(rr.Target)
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
func (rr *X25) len(off int, compression map[string]struct{}) int {
|
func (rr *X25) len() int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len()
|
||||||
l += len(rr.PSDNAddress) + 1
|
l += len(rr.PSDNAddress) + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy() functions
|
// copy() functions
|
||||||
func (rr *A) copy() RR {
|
func (rr *A) copy() RR {
|
||||||
return &A{rr.Hdr, copyIP(rr.A)}
|
return &A{*rr.Hdr.copyHeader(), copyIP(rr.A)}
|
||||||
}
|
}
|
||||||
func (rr *AAAA) copy() RR {
|
func (rr *AAAA) copy() RR {
|
||||||
return &AAAA{rr.Hdr, copyIP(rr.AAAA)}
|
return &AAAA{*rr.Hdr.copyHeader(), copyIP(rr.AAAA)}
|
||||||
}
|
}
|
||||||
func (rr *AFSDB) copy() RR {
|
func (rr *AFSDB) copy() RR {
|
||||||
return &AFSDB{rr.Hdr, rr.Subtype, rr.Hostname}
|
return &AFSDB{*rr.Hdr.copyHeader(), rr.Subtype, rr.Hostname}
|
||||||
}
|
}
|
||||||
func (rr *ANY) copy() RR {
|
func (rr *ANY) copy() RR {
|
||||||
return &ANY{rr.Hdr}
|
return &ANY{*rr.Hdr.copyHeader()}
|
||||||
}
|
}
|
||||||
func (rr *AVC) copy() RR {
|
func (rr *AVC) copy() RR {
|
||||||
Txt := make([]string, len(rr.Txt))
|
Txt := make([]string, len(rr.Txt))
|
||||||
copy(Txt, rr.Txt)
|
copy(Txt, rr.Txt)
|
||||||
return &AVC{rr.Hdr, Txt}
|
return &AVC{*rr.Hdr.copyHeader(), Txt}
|
||||||
}
|
}
|
||||||
func (rr *CAA) copy() RR {
|
func (rr *CAA) copy() RR {
|
||||||
return &CAA{rr.Hdr, rr.Flag, rr.Tag, rr.Value}
|
return &CAA{*rr.Hdr.copyHeader(), rr.Flag, rr.Tag, rr.Value}
|
||||||
}
|
}
|
||||||
func (rr *CERT) copy() RR {
|
func (rr *CERT) copy() RR {
|
||||||
return &CERT{rr.Hdr, rr.Type, rr.KeyTag, rr.Algorithm, rr.Certificate}
|
return &CERT{*rr.Hdr.copyHeader(), rr.Type, rr.KeyTag, rr.Algorithm, rr.Certificate}
|
||||||
}
|
}
|
||||||
func (rr *CNAME) copy() RR {
|
func (rr *CNAME) copy() RR {
|
||||||
return &CNAME{rr.Hdr, rr.Target}
|
return &CNAME{*rr.Hdr.copyHeader(), rr.Target}
|
||||||
}
|
}
|
||||||
func (rr *CSYNC) copy() RR {
|
func (rr *CSYNC) copy() RR {
|
||||||
TypeBitMap := make([]uint16, len(rr.TypeBitMap))
|
TypeBitMap := make([]uint16, len(rr.TypeBitMap))
|
||||||
copy(TypeBitMap, rr.TypeBitMap)
|
copy(TypeBitMap, rr.TypeBitMap)
|
||||||
return &CSYNC{rr.Hdr, rr.Serial, rr.Flags, TypeBitMap}
|
return &CSYNC{*rr.Hdr.copyHeader(), rr.Serial, rr.Flags, TypeBitMap}
|
||||||
}
|
}
|
||||||
func (rr *DHCID) copy() RR {
|
func (rr *DHCID) copy() RR {
|
||||||
return &DHCID{rr.Hdr, rr.Digest}
|
return &DHCID{*rr.Hdr.copyHeader(), rr.Digest}
|
||||||
}
|
}
|
||||||
func (rr *DNAME) copy() RR {
|
func (rr *DNAME) copy() RR {
|
||||||
return &DNAME{rr.Hdr, rr.Target}
|
return &DNAME{*rr.Hdr.copyHeader(), rr.Target}
|
||||||
}
|
}
|
||||||
func (rr *DNSKEY) copy() RR {
|
func (rr *DNSKEY) copy() RR {
|
||||||
return &DNSKEY{rr.Hdr, rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey}
|
return &DNSKEY{*rr.Hdr.copyHeader(), rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey}
|
||||||
}
|
}
|
||||||
func (rr *DS) copy() RR {
|
func (rr *DS) copy() RR {
|
||||||
return &DS{rr.Hdr, rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest}
|
return &DS{*rr.Hdr.copyHeader(), rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest}
|
||||||
}
|
}
|
||||||
func (rr *EID) copy() RR {
|
func (rr *EID) copy() RR {
|
||||||
return &EID{rr.Hdr, rr.Endpoint}
|
return &EID{*rr.Hdr.copyHeader(), rr.Endpoint}
|
||||||
}
|
}
|
||||||
func (rr *EUI48) copy() RR {
|
func (rr *EUI48) copy() RR {
|
||||||
return &EUI48{rr.Hdr, rr.Address}
|
return &EUI48{*rr.Hdr.copyHeader(), rr.Address}
|
||||||
}
|
}
|
||||||
func (rr *EUI64) copy() RR {
|
func (rr *EUI64) copy() RR {
|
||||||
return &EUI64{rr.Hdr, rr.Address}
|
return &EUI64{*rr.Hdr.copyHeader(), rr.Address}
|
||||||
}
|
}
|
||||||
func (rr *GID) copy() RR {
|
func (rr *GID) copy() RR {
|
||||||
return &GID{rr.Hdr, rr.Gid}
|
return &GID{*rr.Hdr.copyHeader(), rr.Gid}
|
||||||
}
|
}
|
||||||
func (rr *GPOS) copy() RR {
|
func (rr *GPOS) copy() RR {
|
||||||
return &GPOS{rr.Hdr, rr.Longitude, rr.Latitude, rr.Altitude}
|
return &GPOS{*rr.Hdr.copyHeader(), rr.Longitude, rr.Latitude, rr.Altitude}
|
||||||
}
|
}
|
||||||
func (rr *HINFO) copy() RR {
|
func (rr *HINFO) copy() RR {
|
||||||
return &HINFO{rr.Hdr, rr.Cpu, rr.Os}
|
return &HINFO{*rr.Hdr.copyHeader(), rr.Cpu, rr.Os}
|
||||||
}
|
}
|
||||||
func (rr *HIP) copy() RR {
|
func (rr *HIP) copy() RR {
|
||||||
RendezvousServers := make([]string, len(rr.RendezvousServers))
|
RendezvousServers := make([]string, len(rr.RendezvousServers))
|
||||||
copy(RendezvousServers, rr.RendezvousServers)
|
copy(RendezvousServers, rr.RendezvousServers)
|
||||||
return &HIP{rr.Hdr, rr.HitLength, rr.PublicKeyAlgorithm, rr.PublicKeyLength, rr.Hit, rr.PublicKey, RendezvousServers}
|
return &HIP{*rr.Hdr.copyHeader(), rr.HitLength, rr.PublicKeyAlgorithm, rr.PublicKeyLength, rr.Hit, rr.PublicKey, RendezvousServers}
|
||||||
}
|
}
|
||||||
func (rr *KX) copy() RR {
|
func (rr *KX) copy() RR {
|
||||||
return &KX{rr.Hdr, rr.Preference, rr.Exchanger}
|
return &KX{*rr.Hdr.copyHeader(), rr.Preference, rr.Exchanger}
|
||||||
}
|
}
|
||||||
func (rr *L32) copy() RR {
|
func (rr *L32) copy() RR {
|
||||||
return &L32{rr.Hdr, rr.Preference, copyIP(rr.Locator32)}
|
return &L32{*rr.Hdr.copyHeader(), rr.Preference, copyIP(rr.Locator32)}
|
||||||
}
|
}
|
||||||
func (rr *L64) copy() RR {
|
func (rr *L64) copy() RR {
|
||||||
return &L64{rr.Hdr, rr.Preference, rr.Locator64}
|
return &L64{*rr.Hdr.copyHeader(), rr.Preference, rr.Locator64}
|
||||||
}
|
}
|
||||||
func (rr *LOC) copy() RR {
|
func (rr *LOC) copy() RR {
|
||||||
return &LOC{rr.Hdr, rr.Version, rr.Size, rr.HorizPre, rr.VertPre, rr.Latitude, rr.Longitude, rr.Altitude}
|
return &LOC{*rr.Hdr.copyHeader(), rr.Version, rr.Size, rr.HorizPre, rr.VertPre, rr.Latitude, rr.Longitude, rr.Altitude}
|
||||||
}
|
}
|
||||||
func (rr *LP) copy() RR {
|
func (rr *LP) copy() RR {
|
||||||
return &LP{rr.Hdr, rr.Preference, rr.Fqdn}
|
return &LP{*rr.Hdr.copyHeader(), rr.Preference, rr.Fqdn}
|
||||||
}
|
}
|
||||||
func (rr *MB) copy() RR {
|
func (rr *MB) copy() RR {
|
||||||
return &MB{rr.Hdr, rr.Mb}
|
return &MB{*rr.Hdr.copyHeader(), rr.Mb}
|
||||||
}
|
}
|
||||||
func (rr *MD) copy() RR {
|
func (rr *MD) copy() RR {
|
||||||
return &MD{rr.Hdr, rr.Md}
|
return &MD{*rr.Hdr.copyHeader(), rr.Md}
|
||||||
}
|
}
|
||||||
func (rr *MF) copy() RR {
|
func (rr *MF) copy() RR {
|
||||||
return &MF{rr.Hdr, rr.Mf}
|
return &MF{*rr.Hdr.copyHeader(), rr.Mf}
|
||||||
}
|
}
|
||||||
func (rr *MG) copy() RR {
|
func (rr *MG) copy() RR {
|
||||||
return &MG{rr.Hdr, rr.Mg}
|
return &MG{*rr.Hdr.copyHeader(), rr.Mg}
|
||||||
}
|
}
|
||||||
func (rr *MINFO) copy() RR {
|
func (rr *MINFO) copy() RR {
|
||||||
return &MINFO{rr.Hdr, rr.Rmail, rr.Email}
|
return &MINFO{*rr.Hdr.copyHeader(), rr.Rmail, rr.Email}
|
||||||
}
|
}
|
||||||
func (rr *MR) copy() RR {
|
func (rr *MR) copy() RR {
|
||||||
return &MR{rr.Hdr, rr.Mr}
|
return &MR{*rr.Hdr.copyHeader(), rr.Mr}
|
||||||
}
|
}
|
||||||
func (rr *MX) copy() RR {
|
func (rr *MX) copy() RR {
|
||||||
return &MX{rr.Hdr, rr.Preference, rr.Mx}
|
return &MX{*rr.Hdr.copyHeader(), rr.Preference, rr.Mx}
|
||||||
}
|
}
|
||||||
func (rr *NAPTR) copy() RR {
|
func (rr *NAPTR) copy() RR {
|
||||||
return &NAPTR{rr.Hdr, rr.Order, rr.Preference, rr.Flags, rr.Service, rr.Regexp, rr.Replacement}
|
return &NAPTR{*rr.Hdr.copyHeader(), rr.Order, rr.Preference, rr.Flags, rr.Service, rr.Regexp, rr.Replacement}
|
||||||
}
|
}
|
||||||
func (rr *NID) copy() RR {
|
func (rr *NID) copy() RR {
|
||||||
return &NID{rr.Hdr, rr.Preference, rr.NodeID}
|
return &NID{*rr.Hdr.copyHeader(), rr.Preference, rr.NodeID}
|
||||||
}
|
}
|
||||||
func (rr *NIMLOC) copy() RR {
|
func (rr *NIMLOC) copy() RR {
|
||||||
return &NIMLOC{rr.Hdr, rr.Locator}
|
return &NIMLOC{*rr.Hdr.copyHeader(), rr.Locator}
|
||||||
}
|
}
|
||||||
func (rr *NINFO) copy() RR {
|
func (rr *NINFO) copy() RR {
|
||||||
ZSData := make([]string, len(rr.ZSData))
|
ZSData := make([]string, len(rr.ZSData))
|
||||||
copy(ZSData, rr.ZSData)
|
copy(ZSData, rr.ZSData)
|
||||||
return &NINFO{rr.Hdr, ZSData}
|
return &NINFO{*rr.Hdr.copyHeader(), ZSData}
|
||||||
}
|
}
|
||||||
func (rr *NS) copy() RR {
|
func (rr *NS) copy() RR {
|
||||||
return &NS{rr.Hdr, rr.Ns}
|
return &NS{*rr.Hdr.copyHeader(), rr.Ns}
|
||||||
}
|
}
|
||||||
func (rr *NSAPPTR) copy() RR {
|
func (rr *NSAPPTR) copy() RR {
|
||||||
return &NSAPPTR{rr.Hdr, rr.Ptr}
|
return &NSAPPTR{*rr.Hdr.copyHeader(), rr.Ptr}
|
||||||
}
|
}
|
||||||
func (rr *NSEC) copy() RR {
|
func (rr *NSEC) copy() RR {
|
||||||
TypeBitMap := make([]uint16, len(rr.TypeBitMap))
|
TypeBitMap := make([]uint16, len(rr.TypeBitMap))
|
||||||
copy(TypeBitMap, rr.TypeBitMap)
|
copy(TypeBitMap, rr.TypeBitMap)
|
||||||
return &NSEC{rr.Hdr, rr.NextDomain, TypeBitMap}
|
return &NSEC{*rr.Hdr.copyHeader(), rr.NextDomain, TypeBitMap}
|
||||||
}
|
}
|
||||||
func (rr *NSEC3) copy() RR {
|
func (rr *NSEC3) copy() RR {
|
||||||
TypeBitMap := make([]uint16, len(rr.TypeBitMap))
|
TypeBitMap := make([]uint16, len(rr.TypeBitMap))
|
||||||
copy(TypeBitMap, 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}
|
return &NSEC3{*rr.Hdr.copyHeader(), rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt, rr.HashLength, rr.NextDomain, TypeBitMap}
|
||||||
}
|
}
|
||||||
func (rr *NSEC3PARAM) copy() RR {
|
func (rr *NSEC3PARAM) copy() RR {
|
||||||
return &NSEC3PARAM{rr.Hdr, rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt}
|
return &NSEC3PARAM{*rr.Hdr.copyHeader(), rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt}
|
||||||
}
|
|
||||||
func (rr *NULL) copy() RR {
|
|
||||||
return &NULL{rr.Hdr, rr.Data}
|
|
||||||
}
|
}
|
||||||
func (rr *OPENPGPKEY) copy() RR {
|
func (rr *OPENPGPKEY) copy() RR {
|
||||||
return &OPENPGPKEY{rr.Hdr, rr.PublicKey}
|
return &OPENPGPKEY{*rr.Hdr.copyHeader(), rr.PublicKey}
|
||||||
}
|
}
|
||||||
func (rr *OPT) copy() RR {
|
func (rr *OPT) copy() RR {
|
||||||
Option := make([]EDNS0, len(rr.Option))
|
Option := make([]EDNS0, len(rr.Option))
|
||||||
for i, e := range rr.Option {
|
copy(Option, rr.Option)
|
||||||
Option[i] = e.copy()
|
return &OPT{*rr.Hdr.copyHeader(), Option}
|
||||||
}
|
|
||||||
return &OPT{rr.Hdr, Option}
|
|
||||||
}
|
}
|
||||||
func (rr *PTR) copy() RR {
|
func (rr *PTR) copy() RR {
|
||||||
return &PTR{rr.Hdr, rr.Ptr}
|
return &PTR{*rr.Hdr.copyHeader(), rr.Ptr}
|
||||||
}
|
}
|
||||||
func (rr *PX) copy() RR {
|
func (rr *PX) copy() RR {
|
||||||
return &PX{rr.Hdr, rr.Preference, rr.Map822, rr.Mapx400}
|
return &PX{*rr.Hdr.copyHeader(), rr.Preference, rr.Map822, rr.Mapx400}
|
||||||
}
|
}
|
||||||
func (rr *RFC3597) copy() RR {
|
func (rr *RFC3597) copy() RR {
|
||||||
return &RFC3597{rr.Hdr, rr.Rdata}
|
return &RFC3597{*rr.Hdr.copyHeader(), rr.Rdata}
|
||||||
}
|
}
|
||||||
func (rr *RKEY) copy() RR {
|
func (rr *RKEY) copy() RR {
|
||||||
return &RKEY{rr.Hdr, rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey}
|
return &RKEY{*rr.Hdr.copyHeader(), rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey}
|
||||||
}
|
}
|
||||||
func (rr *RP) copy() RR {
|
func (rr *RP) copy() RR {
|
||||||
return &RP{rr.Hdr, rr.Mbox, rr.Txt}
|
return &RP{*rr.Hdr.copyHeader(), rr.Mbox, rr.Txt}
|
||||||
}
|
}
|
||||||
func (rr *RRSIG) copy() RR {
|
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}
|
return &RRSIG{*rr.Hdr.copyHeader(), rr.TypeCovered, rr.Algorithm, rr.Labels, rr.OrigTtl, rr.Expiration, rr.Inception, rr.KeyTag, rr.SignerName, rr.Signature}
|
||||||
}
|
}
|
||||||
func (rr *RT) copy() RR {
|
func (rr *RT) copy() RR {
|
||||||
return &RT{rr.Hdr, rr.Preference, rr.Host}
|
return &RT{*rr.Hdr.copyHeader(), rr.Preference, rr.Host}
|
||||||
}
|
}
|
||||||
func (rr *SMIMEA) copy() RR {
|
func (rr *SMIMEA) copy() RR {
|
||||||
return &SMIMEA{rr.Hdr, rr.Usage, rr.Selector, rr.MatchingType, rr.Certificate}
|
return &SMIMEA{*rr.Hdr.copyHeader(), rr.Usage, rr.Selector, rr.MatchingType, rr.Certificate}
|
||||||
}
|
}
|
||||||
func (rr *SOA) copy() RR {
|
func (rr *SOA) copy() RR {
|
||||||
return &SOA{rr.Hdr, rr.Ns, rr.Mbox, rr.Serial, rr.Refresh, rr.Retry, rr.Expire, rr.Minttl}
|
return &SOA{*rr.Hdr.copyHeader(), rr.Ns, rr.Mbox, rr.Serial, rr.Refresh, rr.Retry, rr.Expire, rr.Minttl}
|
||||||
}
|
}
|
||||||
func (rr *SPF) copy() RR {
|
func (rr *SPF) copy() RR {
|
||||||
Txt := make([]string, len(rr.Txt))
|
Txt := make([]string, len(rr.Txt))
|
||||||
copy(Txt, rr.Txt)
|
copy(Txt, rr.Txt)
|
||||||
return &SPF{rr.Hdr, Txt}
|
return &SPF{*rr.Hdr.copyHeader(), Txt}
|
||||||
}
|
}
|
||||||
func (rr *SRV) copy() RR {
|
func (rr *SRV) copy() RR {
|
||||||
return &SRV{rr.Hdr, rr.Priority, rr.Weight, rr.Port, rr.Target}
|
return &SRV{*rr.Hdr.copyHeader(), rr.Priority, rr.Weight, rr.Port, rr.Target}
|
||||||
}
|
}
|
||||||
func (rr *SSHFP) copy() RR {
|
func (rr *SSHFP) copy() RR {
|
||||||
return &SSHFP{rr.Hdr, rr.Algorithm, rr.Type, rr.FingerPrint}
|
return &SSHFP{*rr.Hdr.copyHeader(), rr.Algorithm, rr.Type, rr.FingerPrint}
|
||||||
}
|
}
|
||||||
func (rr *TA) copy() RR {
|
func (rr *TA) copy() RR {
|
||||||
return &TA{rr.Hdr, rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest}
|
return &TA{*rr.Hdr.copyHeader(), rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest}
|
||||||
}
|
}
|
||||||
func (rr *TALINK) copy() RR {
|
func (rr *TALINK) copy() RR {
|
||||||
return &TALINK{rr.Hdr, rr.PreviousName, rr.NextName}
|
return &TALINK{*rr.Hdr.copyHeader(), rr.PreviousName, rr.NextName}
|
||||||
}
|
}
|
||||||
func (rr *TKEY) copy() RR {
|
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}
|
return &TKEY{*rr.Hdr.copyHeader(), rr.Algorithm, rr.Inception, rr.Expiration, rr.Mode, rr.Error, rr.KeySize, rr.Key, rr.OtherLen, rr.OtherData}
|
||||||
}
|
}
|
||||||
func (rr *TLSA) copy() RR {
|
func (rr *TLSA) copy() RR {
|
||||||
return &TLSA{rr.Hdr, rr.Usage, rr.Selector, rr.MatchingType, rr.Certificate}
|
return &TLSA{*rr.Hdr.copyHeader(), rr.Usage, rr.Selector, rr.MatchingType, rr.Certificate}
|
||||||
}
|
}
|
||||||
func (rr *TSIG) copy() RR {
|
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}
|
return &TSIG{*rr.Hdr.copyHeader(), rr.Algorithm, rr.TimeSigned, rr.Fudge, rr.MACSize, rr.MAC, rr.OrigId, rr.Error, rr.OtherLen, rr.OtherData}
|
||||||
}
|
}
|
||||||
func (rr *TXT) copy() RR {
|
func (rr *TXT) copy() RR {
|
||||||
Txt := make([]string, len(rr.Txt))
|
Txt := make([]string, len(rr.Txt))
|
||||||
copy(Txt, rr.Txt)
|
copy(Txt, rr.Txt)
|
||||||
return &TXT{rr.Hdr, Txt}
|
return &TXT{*rr.Hdr.copyHeader(), Txt}
|
||||||
}
|
}
|
||||||
func (rr *UID) copy() RR {
|
func (rr *UID) copy() RR {
|
||||||
return &UID{rr.Hdr, rr.Uid}
|
return &UID{*rr.Hdr.copyHeader(), rr.Uid}
|
||||||
}
|
}
|
||||||
func (rr *UINFO) copy() RR {
|
func (rr *UINFO) copy() RR {
|
||||||
return &UINFO{rr.Hdr, rr.Uinfo}
|
return &UINFO{*rr.Hdr.copyHeader(), rr.Uinfo}
|
||||||
}
|
}
|
||||||
func (rr *URI) copy() RR {
|
func (rr *URI) copy() RR {
|
||||||
return &URI{rr.Hdr, rr.Priority, rr.Weight, rr.Target}
|
return &URI{*rr.Hdr.copyHeader(), rr.Priority, rr.Weight, rr.Target}
|
||||||
}
|
}
|
||||||
func (rr *X25) copy() RR {
|
func (rr *X25) copy() RR {
|
||||||
return &X25{rr.Hdr, rr.PSDNAddress}
|
return &X25{*rr.Hdr.copyHeader(), rr.PSDNAddress}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
// Copyright 2016 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 bpf
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
// Assemble converts insts into raw instructions suitable for loading
|
|
||||||
// into a BPF virtual machine.
|
|
||||||
//
|
|
||||||
// Currently, no optimization is attempted, the assembled program flow
|
|
||||||
// is exactly as provided.
|
|
||||||
func Assemble(insts []Instruction) ([]RawInstruction, error) {
|
|
||||||
ret := make([]RawInstruction, len(insts))
|
|
||||||
var err error
|
|
||||||
for i, inst := range insts {
|
|
||||||
ret[i], err = inst.Assemble()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("assembling instruction %d: %s", i+1, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disassemble attempts to parse raw back into
|
|
||||||
// Instructions. Unrecognized RawInstructions are assumed to be an
|
|
||||||
// extension not implemented by this package, and are passed through
|
|
||||||
// unchanged to the output. The allDecoded value reports whether insts
|
|
||||||
// contains no RawInstructions.
|
|
||||||
func Disassemble(raw []RawInstruction) (insts []Instruction, allDecoded bool) {
|
|
||||||
insts = make([]Instruction, len(raw))
|
|
||||||
allDecoded = true
|
|
||||||
for i, r := range raw {
|
|
||||||
insts[i] = r.Disassemble()
|
|
||||||
if _, ok := insts[i].(RawInstruction); ok {
|
|
||||||
allDecoded = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return insts, allDecoded
|
|
||||||
}
|
|
|
@ -1,222 +0,0 @@
|
||||||
// Copyright 2016 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 bpf
|
|
||||||
|
|
||||||
// A Register is a register of the BPF virtual machine.
|
|
||||||
type Register uint16
|
|
||||||
|
|
||||||
const (
|
|
||||||
// RegA is the accumulator register. RegA is always the
|
|
||||||
// destination register of ALU operations.
|
|
||||||
RegA Register = iota
|
|
||||||
// RegX is the indirection register, used by LoadIndirect
|
|
||||||
// operations.
|
|
||||||
RegX
|
|
||||||
)
|
|
||||||
|
|
||||||
// An ALUOp is an arithmetic or logic operation.
|
|
||||||
type ALUOp uint16
|
|
||||||
|
|
||||||
// ALU binary operation types.
|
|
||||||
const (
|
|
||||||
ALUOpAdd ALUOp = iota << 4
|
|
||||||
ALUOpSub
|
|
||||||
ALUOpMul
|
|
||||||
ALUOpDiv
|
|
||||||
ALUOpOr
|
|
||||||
ALUOpAnd
|
|
||||||
ALUOpShiftLeft
|
|
||||||
ALUOpShiftRight
|
|
||||||
aluOpNeg // Not exported because it's the only unary ALU operation, and gets its own instruction type.
|
|
||||||
ALUOpMod
|
|
||||||
ALUOpXor
|
|
||||||
)
|
|
||||||
|
|
||||||
// A JumpTest is a comparison operator used in conditional jumps.
|
|
||||||
type JumpTest uint16
|
|
||||||
|
|
||||||
// Supported operators for conditional jumps.
|
|
||||||
// K can be RegX for JumpIfX
|
|
||||||
const (
|
|
||||||
// K == A
|
|
||||||
JumpEqual JumpTest = iota
|
|
||||||
// K != A
|
|
||||||
JumpNotEqual
|
|
||||||
// K > A
|
|
||||||
JumpGreaterThan
|
|
||||||
// K < A
|
|
||||||
JumpLessThan
|
|
||||||
// K >= A
|
|
||||||
JumpGreaterOrEqual
|
|
||||||
// K <= A
|
|
||||||
JumpLessOrEqual
|
|
||||||
// K & A != 0
|
|
||||||
JumpBitsSet
|
|
||||||
// K & A == 0
|
|
||||||
JumpBitsNotSet
|
|
||||||
)
|
|
||||||
|
|
||||||
// An Extension is a function call provided by the kernel that
|
|
||||||
// performs advanced operations that are expensive or impossible
|
|
||||||
// within the BPF virtual machine.
|
|
||||||
//
|
|
||||||
// Extensions are only implemented by the Linux kernel.
|
|
||||||
//
|
|
||||||
// TODO: should we prune this list? Some of these extensions seem
|
|
||||||
// either broken or near-impossible to use correctly, whereas other
|
|
||||||
// (len, random, ifindex) are quite useful.
|
|
||||||
type Extension int
|
|
||||||
|
|
||||||
// Extension functions available in the Linux kernel.
|
|
||||||
const (
|
|
||||||
// extOffset is the negative maximum number of instructions used
|
|
||||||
// to load instructions by overloading the K argument.
|
|
||||||
extOffset = -0x1000
|
|
||||||
// ExtLen returns the length of the packet.
|
|
||||||
ExtLen Extension = 1
|
|
||||||
// ExtProto returns the packet's L3 protocol type.
|
|
||||||
ExtProto Extension = 0
|
|
||||||
// ExtType returns the packet's type (skb->pkt_type in the kernel)
|
|
||||||
//
|
|
||||||
// TODO: better documentation. How nice an API do we want to
|
|
||||||
// provide for these esoteric extensions?
|
|
||||||
ExtType Extension = 4
|
|
||||||
// ExtPayloadOffset returns the offset of the packet payload, or
|
|
||||||
// the first protocol header that the kernel does not know how to
|
|
||||||
// parse.
|
|
||||||
ExtPayloadOffset Extension = 52
|
|
||||||
// ExtInterfaceIndex returns the index of the interface on which
|
|
||||||
// the packet was received.
|
|
||||||
ExtInterfaceIndex Extension = 8
|
|
||||||
// ExtNetlinkAttr returns the netlink attribute of type X at
|
|
||||||
// offset A.
|
|
||||||
ExtNetlinkAttr Extension = 12
|
|
||||||
// ExtNetlinkAttrNested returns the nested netlink attribute of
|
|
||||||
// type X at offset A.
|
|
||||||
ExtNetlinkAttrNested Extension = 16
|
|
||||||
// ExtMark returns the packet's mark value.
|
|
||||||
ExtMark Extension = 20
|
|
||||||
// ExtQueue returns the packet's assigned hardware queue.
|
|
||||||
ExtQueue Extension = 24
|
|
||||||
// ExtLinkLayerType returns the packet's hardware address type
|
|
||||||
// (e.g. Ethernet, Infiniband).
|
|
||||||
ExtLinkLayerType Extension = 28
|
|
||||||
// ExtRXHash returns the packets receive hash.
|
|
||||||
//
|
|
||||||
// TODO: figure out what this rxhash actually is.
|
|
||||||
ExtRXHash Extension = 32
|
|
||||||
// ExtCPUID returns the ID of the CPU processing the current
|
|
||||||
// packet.
|
|
||||||
ExtCPUID Extension = 36
|
|
||||||
// ExtVLANTag returns the packet's VLAN tag.
|
|
||||||
ExtVLANTag Extension = 44
|
|
||||||
// ExtVLANTagPresent returns non-zero if the packet has a VLAN
|
|
||||||
// tag.
|
|
||||||
//
|
|
||||||
// TODO: I think this might be a lie: it reads bit 0x1000 of the
|
|
||||||
// VLAN header, which changed meaning in recent revisions of the
|
|
||||||
// spec - this extension may now return meaningless information.
|
|
||||||
ExtVLANTagPresent Extension = 48
|
|
||||||
// ExtVLANProto returns 0x8100 if the frame has a VLAN header,
|
|
||||||
// 0x88a8 if the frame has a "Q-in-Q" double VLAN header, or some
|
|
||||||
// other value if no VLAN information is present.
|
|
||||||
ExtVLANProto Extension = 60
|
|
||||||
// ExtRand returns a uniformly random uint32.
|
|
||||||
ExtRand Extension = 56
|
|
||||||
)
|
|
||||||
|
|
||||||
// The following gives names to various bit patterns used in opcode construction.
|
|
||||||
|
|
||||||
const (
|
|
||||||
opMaskCls uint16 = 0x7
|
|
||||||
// opClsLoad masks
|
|
||||||
opMaskLoadDest = 0x01
|
|
||||||
opMaskLoadWidth = 0x18
|
|
||||||
opMaskLoadMode = 0xe0
|
|
||||||
// opClsALU & opClsJump
|
|
||||||
opMaskOperand = 0x08
|
|
||||||
opMaskOperator = 0xf0
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// +---------------+-----------------+---+---+---+
|
|
||||||
// | AddrMode (3b) | LoadWidth (2b) | 0 | 0 | 0 |
|
|
||||||
// +---------------+-----------------+---+---+---+
|
|
||||||
opClsLoadA uint16 = iota
|
|
||||||
// +---------------+-----------------+---+---+---+
|
|
||||||
// | AddrMode (3b) | LoadWidth (2b) | 0 | 0 | 1 |
|
|
||||||
// +---------------+-----------------+---+---+---+
|
|
||||||
opClsLoadX
|
|
||||||
// +---+---+---+---+---+---+---+---+
|
|
||||||
// | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
|
|
||||||
// +---+---+---+---+---+---+---+---+
|
|
||||||
opClsStoreA
|
|
||||||
// +---+---+---+---+---+---+---+---+
|
|
||||||
// | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
|
|
||||||
// +---+---+---+---+---+---+---+---+
|
|
||||||
opClsStoreX
|
|
||||||
// +---------------+-----------------+---+---+---+
|
|
||||||
// | Operator (4b) | OperandSrc (1b) | 1 | 0 | 0 |
|
|
||||||
// +---------------+-----------------+---+---+---+
|
|
||||||
opClsALU
|
|
||||||
// +-----------------------------+---+---+---+---+
|
|
||||||
// | TestOperator (4b) | 0 | 1 | 0 | 1 |
|
|
||||||
// +-----------------------------+---+---+---+---+
|
|
||||||
opClsJump
|
|
||||||
// +---+-------------------------+---+---+---+---+
|
|
||||||
// | 0 | 0 | 0 | RetSrc (1b) | 0 | 1 | 1 | 0 |
|
|
||||||
// +---+-------------------------+---+---+---+---+
|
|
||||||
opClsReturn
|
|
||||||
// +---+-------------------------+---+---+---+---+
|
|
||||||
// | 0 | 0 | 0 | TXAorTAX (1b) | 0 | 1 | 1 | 1 |
|
|
||||||
// +---+-------------------------+---+---+---+---+
|
|
||||||
opClsMisc
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
opAddrModeImmediate uint16 = iota << 5
|
|
||||||
opAddrModeAbsolute
|
|
||||||
opAddrModeIndirect
|
|
||||||
opAddrModeScratch
|
|
||||||
opAddrModePacketLen // actually an extension, not an addressing mode.
|
|
||||||
opAddrModeMemShift
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
opLoadWidth4 uint16 = iota << 3
|
|
||||||
opLoadWidth2
|
|
||||||
opLoadWidth1
|
|
||||||
)
|
|
||||||
|
|
||||||
// Operand for ALU and Jump instructions
|
|
||||||
type opOperand uint16
|
|
||||||
|
|
||||||
// Supported operand sources.
|
|
||||||
const (
|
|
||||||
opOperandConstant opOperand = iota << 3
|
|
||||||
opOperandX
|
|
||||||
)
|
|
||||||
|
|
||||||
// An jumpOp is a conditional jump condition.
|
|
||||||
type jumpOp uint16
|
|
||||||
|
|
||||||
// Supported jump conditions.
|
|
||||||
const (
|
|
||||||
opJumpAlways jumpOp = iota << 4
|
|
||||||
opJumpEqual
|
|
||||||
opJumpGT
|
|
||||||
opJumpGE
|
|
||||||
opJumpSet
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
opRetSrcConstant uint16 = iota << 4
|
|
||||||
opRetSrcA
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
opMiscTAX = 0x00
|
|
||||||
opMiscTXA = 0x80
|
|
||||||
)
|
|
|
@ -1,82 +0,0 @@
|
||||||
// Copyright 2016 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 bpf implements marshaling and unmarshaling of programs for the
|
|
||||||
Berkeley Packet Filter virtual machine, and provides a Go implementation
|
|
||||||
of the virtual machine.
|
|
||||||
|
|
||||||
BPF's main use is to specify a packet filter for network taps, so that
|
|
||||||
the kernel doesn't have to expensively copy every packet it sees to
|
|
||||||
userspace. However, it's been repurposed to other areas where running
|
|
||||||
user code in-kernel is needed. For example, Linux's seccomp uses BPF
|
|
||||||
to apply security policies to system calls. For simplicity, this
|
|
||||||
documentation refers only to packets, but other uses of BPF have their
|
|
||||||
own data payloads.
|
|
||||||
|
|
||||||
BPF programs run in a restricted virtual machine. It has almost no
|
|
||||||
access to kernel functions, and while conditional branches are
|
|
||||||
allowed, they can only jump forwards, to guarantee that there are no
|
|
||||||
infinite loops.
|
|
||||||
|
|
||||||
The virtual machine
|
|
||||||
|
|
||||||
The BPF VM is an accumulator machine. Its main register, called
|
|
||||||
register A, is an implicit source and destination in all arithmetic
|
|
||||||
and logic operations. The machine also has 16 scratch registers for
|
|
||||||
temporary storage, and an indirection register (register X) for
|
|
||||||
indirect memory access. All registers are 32 bits wide.
|
|
||||||
|
|
||||||
Each run of a BPF program is given one packet, which is placed in the
|
|
||||||
VM's read-only "main memory". LoadAbsolute and LoadIndirect
|
|
||||||
instructions can fetch up to 32 bits at a time into register A for
|
|
||||||
examination.
|
|
||||||
|
|
||||||
The goal of a BPF program is to produce and return a verdict (uint32),
|
|
||||||
which tells the kernel what to do with the packet. In the context of
|
|
||||||
packet filtering, the returned value is the number of bytes of the
|
|
||||||
packet to forward to userspace, or 0 to ignore the packet. Other
|
|
||||||
contexts like seccomp define their own return values.
|
|
||||||
|
|
||||||
In order to simplify programs, attempts to read past the end of the
|
|
||||||
packet terminate the program execution with a verdict of 0 (ignore
|
|
||||||
packet). This means that the vast majority of BPF programs don't need
|
|
||||||
to do any explicit bounds checking.
|
|
||||||
|
|
||||||
In addition to the bytes of the packet, some BPF programs have access
|
|
||||||
to extensions, which are essentially calls to kernel utility
|
|
||||||
functions. Currently, the only extensions supported by this package
|
|
||||||
are the Linux packet filter extensions.
|
|
||||||
|
|
||||||
Examples
|
|
||||||
|
|
||||||
This packet filter selects all ARP packets.
|
|
||||||
|
|
||||||
bpf.Assemble([]bpf.Instruction{
|
|
||||||
// Load "EtherType" field from the ethernet header.
|
|
||||||
bpf.LoadAbsolute{Off: 12, Size: 2},
|
|
||||||
// Skip over the next instruction if EtherType is not ARP.
|
|
||||||
bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 0x0806, SkipTrue: 1},
|
|
||||||
// Verdict is "send up to 4k of the packet to userspace."
|
|
||||||
bpf.RetConstant{Val: 4096},
|
|
||||||
// Verdict is "ignore packet."
|
|
||||||
bpf.RetConstant{Val: 0},
|
|
||||||
})
|
|
||||||
|
|
||||||
This packet filter captures a random 1% sample of traffic.
|
|
||||||
|
|
||||||
bpf.Assemble([]bpf.Instruction{
|
|
||||||
// Get a 32-bit random number from the Linux kernel.
|
|
||||||
bpf.LoadExtension{Num: bpf.ExtRand},
|
|
||||||
// 1% dice roll?
|
|
||||||
bpf.JumpIf{Cond: bpf.JumpLessThan, Val: 2^32/100, SkipFalse: 1},
|
|
||||||
// Capture.
|
|
||||||
bpf.RetConstant{Val: 4096},
|
|
||||||
// Ignore.
|
|
||||||
bpf.RetConstant{Val: 0},
|
|
||||||
})
|
|
||||||
|
|
||||||
*/
|
|
||||||
package bpf // import "golang.org/x/net/bpf"
|
|
|
@ -1,726 +0,0 @@
|
||||||
// Copyright 2016 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 bpf
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
// An Instruction is one instruction executed by the BPF virtual
|
|
||||||
// machine.
|
|
||||||
type Instruction interface {
|
|
||||||
// Assemble assembles the Instruction into a RawInstruction.
|
|
||||||
Assemble() (RawInstruction, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A RawInstruction is a raw BPF virtual machine instruction.
|
|
||||||
type RawInstruction struct {
|
|
||||||
// Operation to execute.
|
|
||||||
Op uint16
|
|
||||||
// For conditional jump instructions, the number of instructions
|
|
||||||
// to skip if the condition is true/false.
|
|
||||||
Jt uint8
|
|
||||||
Jf uint8
|
|
||||||
// Constant parameter. The meaning depends on the Op.
|
|
||||||
K uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assemble implements the Instruction Assemble method.
|
|
||||||
func (ri RawInstruction) Assemble() (RawInstruction, error) { return ri, nil }
|
|
||||||
|
|
||||||
// Disassemble parses ri into an Instruction and returns it. If ri is
|
|
||||||
// not recognized by this package, ri itself is returned.
|
|
||||||
func (ri RawInstruction) Disassemble() Instruction {
|
|
||||||
switch ri.Op & opMaskCls {
|
|
||||||
case opClsLoadA, opClsLoadX:
|
|
||||||
reg := Register(ri.Op & opMaskLoadDest)
|
|
||||||
sz := 0
|
|
||||||
switch ri.Op & opMaskLoadWidth {
|
|
||||||
case opLoadWidth4:
|
|
||||||
sz = 4
|
|
||||||
case opLoadWidth2:
|
|
||||||
sz = 2
|
|
||||||
case opLoadWidth1:
|
|
||||||
sz = 1
|
|
||||||
default:
|
|
||||||
return ri
|
|
||||||
}
|
|
||||||
switch ri.Op & opMaskLoadMode {
|
|
||||||
case opAddrModeImmediate:
|
|
||||||
if sz != 4 {
|
|
||||||
return ri
|
|
||||||
}
|
|
||||||
return LoadConstant{Dst: reg, Val: ri.K}
|
|
||||||
case opAddrModeScratch:
|
|
||||||
if sz != 4 || ri.K > 15 {
|
|
||||||
return ri
|
|
||||||
}
|
|
||||||
return LoadScratch{Dst: reg, N: int(ri.K)}
|
|
||||||
case opAddrModeAbsolute:
|
|
||||||
if ri.K > extOffset+0xffffffff {
|
|
||||||
return LoadExtension{Num: Extension(-extOffset + ri.K)}
|
|
||||||
}
|
|
||||||
return LoadAbsolute{Size: sz, Off: ri.K}
|
|
||||||
case opAddrModeIndirect:
|
|
||||||
return LoadIndirect{Size: sz, Off: ri.K}
|
|
||||||
case opAddrModePacketLen:
|
|
||||||
if sz != 4 {
|
|
||||||
return ri
|
|
||||||
}
|
|
||||||
return LoadExtension{Num: ExtLen}
|
|
||||||
case opAddrModeMemShift:
|
|
||||||
return LoadMemShift{Off: ri.K}
|
|
||||||
default:
|
|
||||||
return ri
|
|
||||||
}
|
|
||||||
|
|
||||||
case opClsStoreA:
|
|
||||||
if ri.Op != opClsStoreA || ri.K > 15 {
|
|
||||||
return ri
|
|
||||||
}
|
|
||||||
return StoreScratch{Src: RegA, N: int(ri.K)}
|
|
||||||
|
|
||||||
case opClsStoreX:
|
|
||||||
if ri.Op != opClsStoreX || ri.K > 15 {
|
|
||||||
return ri
|
|
||||||
}
|
|
||||||
return StoreScratch{Src: RegX, N: int(ri.K)}
|
|
||||||
|
|
||||||
case opClsALU:
|
|
||||||
switch op := ALUOp(ri.Op & opMaskOperator); op {
|
|
||||||
case ALUOpAdd, ALUOpSub, ALUOpMul, ALUOpDiv, ALUOpOr, ALUOpAnd, ALUOpShiftLeft, ALUOpShiftRight, ALUOpMod, ALUOpXor:
|
|
||||||
switch operand := opOperand(ri.Op & opMaskOperand); operand {
|
|
||||||
case opOperandX:
|
|
||||||
return ALUOpX{Op: op}
|
|
||||||
case opOperandConstant:
|
|
||||||
return ALUOpConstant{Op: op, Val: ri.K}
|
|
||||||
default:
|
|
||||||
return ri
|
|
||||||
}
|
|
||||||
case aluOpNeg:
|
|
||||||
return NegateA{}
|
|
||||||
default:
|
|
||||||
return ri
|
|
||||||
}
|
|
||||||
|
|
||||||
case opClsJump:
|
|
||||||
switch op := jumpOp(ri.Op & opMaskOperator); op {
|
|
||||||
case opJumpAlways:
|
|
||||||
return Jump{Skip: ri.K}
|
|
||||||
case opJumpEqual, opJumpGT, opJumpGE, opJumpSet:
|
|
||||||
cond, skipTrue, skipFalse := jumpOpToTest(op, ri.Jt, ri.Jf)
|
|
||||||
switch operand := opOperand(ri.Op & opMaskOperand); operand {
|
|
||||||
case opOperandX:
|
|
||||||
return JumpIfX{Cond: cond, SkipTrue: skipTrue, SkipFalse: skipFalse}
|
|
||||||
case opOperandConstant:
|
|
||||||
return JumpIf{Cond: cond, Val: ri.K, SkipTrue: skipTrue, SkipFalse: skipFalse}
|
|
||||||
default:
|
|
||||||
return ri
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return ri
|
|
||||||
}
|
|
||||||
|
|
||||||
case opClsReturn:
|
|
||||||
switch ri.Op {
|
|
||||||
case opClsReturn | opRetSrcA:
|
|
||||||
return RetA{}
|
|
||||||
case opClsReturn | opRetSrcConstant:
|
|
||||||
return RetConstant{Val: ri.K}
|
|
||||||
default:
|
|
||||||
return ri
|
|
||||||
}
|
|
||||||
|
|
||||||
case opClsMisc:
|
|
||||||
switch ri.Op {
|
|
||||||
case opClsMisc | opMiscTAX:
|
|
||||||
return TAX{}
|
|
||||||
case opClsMisc | opMiscTXA:
|
|
||||||
return TXA{}
|
|
||||||
default:
|
|
||||||
return ri
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
panic("unreachable") // switch is exhaustive on the bit pattern
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func jumpOpToTest(op jumpOp, skipTrue uint8, skipFalse uint8) (JumpTest, uint8, uint8) {
|
|
||||||
var test JumpTest
|
|
||||||
|
|
||||||
// Decode "fake" jump conditions that don't appear in machine code
|
|
||||||
// Ensures the Assemble -> Disassemble stage recreates the same instructions
|
|
||||||
// See https://github.com/golang/go/issues/18470
|
|
||||||
if skipTrue == 0 {
|
|
||||||
switch op {
|
|
||||||
case opJumpEqual:
|
|
||||||
test = JumpNotEqual
|
|
||||||
case opJumpGT:
|
|
||||||
test = JumpLessOrEqual
|
|
||||||
case opJumpGE:
|
|
||||||
test = JumpLessThan
|
|
||||||
case opJumpSet:
|
|
||||||
test = JumpBitsNotSet
|
|
||||||
}
|
|
||||||
|
|
||||||
return test, skipFalse, 0
|
|
||||||
}
|
|
||||||
|
|
||||||
switch op {
|
|
||||||
case opJumpEqual:
|
|
||||||
test = JumpEqual
|
|
||||||
case opJumpGT:
|
|
||||||
test = JumpGreaterThan
|
|
||||||
case opJumpGE:
|
|
||||||
test = JumpGreaterOrEqual
|
|
||||||
case opJumpSet:
|
|
||||||
test = JumpBitsSet
|
|
||||||
}
|
|
||||||
|
|
||||||
return test, skipTrue, skipFalse
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadConstant loads Val into register Dst.
|
|
||||||
type LoadConstant struct {
|
|
||||||
Dst Register
|
|
||||||
Val uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assemble implements the Instruction Assemble method.
|
|
||||||
func (a LoadConstant) Assemble() (RawInstruction, error) {
|
|
||||||
return assembleLoad(a.Dst, 4, opAddrModeImmediate, a.Val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the instruction in assembler notation.
|
|
||||||
func (a LoadConstant) String() string {
|
|
||||||
switch a.Dst {
|
|
||||||
case RegA:
|
|
||||||
return fmt.Sprintf("ld #%d", a.Val)
|
|
||||||
case RegX:
|
|
||||||
return fmt.Sprintf("ldx #%d", a.Val)
|
|
||||||
default:
|
|
||||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadScratch loads scratch[N] into register Dst.
|
|
||||||
type LoadScratch struct {
|
|
||||||
Dst Register
|
|
||||||
N int // 0-15
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assemble implements the Instruction Assemble method.
|
|
||||||
func (a LoadScratch) Assemble() (RawInstruction, error) {
|
|
||||||
if a.N < 0 || a.N > 15 {
|
|
||||||
return RawInstruction{}, fmt.Errorf("invalid scratch slot %d", a.N)
|
|
||||||
}
|
|
||||||
return assembleLoad(a.Dst, 4, opAddrModeScratch, uint32(a.N))
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the instruction in assembler notation.
|
|
||||||
func (a LoadScratch) String() string {
|
|
||||||
switch a.Dst {
|
|
||||||
case RegA:
|
|
||||||
return fmt.Sprintf("ld M[%d]", a.N)
|
|
||||||
case RegX:
|
|
||||||
return fmt.Sprintf("ldx M[%d]", a.N)
|
|
||||||
default:
|
|
||||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadAbsolute loads packet[Off:Off+Size] as an integer value into
|
|
||||||
// register A.
|
|
||||||
type LoadAbsolute struct {
|
|
||||||
Off uint32
|
|
||||||
Size int // 1, 2 or 4
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assemble implements the Instruction Assemble method.
|
|
||||||
func (a LoadAbsolute) Assemble() (RawInstruction, error) {
|
|
||||||
return assembleLoad(RegA, a.Size, opAddrModeAbsolute, a.Off)
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the instruction in assembler notation.
|
|
||||||
func (a LoadAbsolute) String() string {
|
|
||||||
switch a.Size {
|
|
||||||
case 1: // byte
|
|
||||||
return fmt.Sprintf("ldb [%d]", a.Off)
|
|
||||||
case 2: // half word
|
|
||||||
return fmt.Sprintf("ldh [%d]", a.Off)
|
|
||||||
case 4: // word
|
|
||||||
if a.Off > extOffset+0xffffffff {
|
|
||||||
return LoadExtension{Num: Extension(a.Off + 0x1000)}.String()
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("ld [%d]", a.Off)
|
|
||||||
default:
|
|
||||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadIndirect loads packet[X+Off:X+Off+Size] as an integer value
|
|
||||||
// into register A.
|
|
||||||
type LoadIndirect struct {
|
|
||||||
Off uint32
|
|
||||||
Size int // 1, 2 or 4
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assemble implements the Instruction Assemble method.
|
|
||||||
func (a LoadIndirect) Assemble() (RawInstruction, error) {
|
|
||||||
return assembleLoad(RegA, a.Size, opAddrModeIndirect, a.Off)
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the instruction in assembler notation.
|
|
||||||
func (a LoadIndirect) String() string {
|
|
||||||
switch a.Size {
|
|
||||||
case 1: // byte
|
|
||||||
return fmt.Sprintf("ldb [x + %d]", a.Off)
|
|
||||||
case 2: // half word
|
|
||||||
return fmt.Sprintf("ldh [x + %d]", a.Off)
|
|
||||||
case 4: // word
|
|
||||||
return fmt.Sprintf("ld [x + %d]", a.Off)
|
|
||||||
default:
|
|
||||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadMemShift multiplies the first 4 bits of the byte at packet[Off]
|
|
||||||
// by 4 and stores the result in register X.
|
|
||||||
//
|
|
||||||
// This instruction is mainly useful to load into X the length of an
|
|
||||||
// IPv4 packet header in a single instruction, rather than have to do
|
|
||||||
// the arithmetic on the header's first byte by hand.
|
|
||||||
type LoadMemShift struct {
|
|
||||||
Off uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assemble implements the Instruction Assemble method.
|
|
||||||
func (a LoadMemShift) Assemble() (RawInstruction, error) {
|
|
||||||
return assembleLoad(RegX, 1, opAddrModeMemShift, a.Off)
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the instruction in assembler notation.
|
|
||||||
func (a LoadMemShift) String() string {
|
|
||||||
return fmt.Sprintf("ldx 4*([%d]&0xf)", a.Off)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadExtension invokes a linux-specific extension and stores the
|
|
||||||
// result in register A.
|
|
||||||
type LoadExtension struct {
|
|
||||||
Num Extension
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assemble implements the Instruction Assemble method.
|
|
||||||
func (a LoadExtension) Assemble() (RawInstruction, error) {
|
|
||||||
if a.Num == ExtLen {
|
|
||||||
return assembleLoad(RegA, 4, opAddrModePacketLen, 0)
|
|
||||||
}
|
|
||||||
return assembleLoad(RegA, 4, opAddrModeAbsolute, uint32(extOffset+a.Num))
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the instruction in assembler notation.
|
|
||||||
func (a LoadExtension) String() string {
|
|
||||||
switch a.Num {
|
|
||||||
case ExtLen:
|
|
||||||
return "ld #len"
|
|
||||||
case ExtProto:
|
|
||||||
return "ld #proto"
|
|
||||||
case ExtType:
|
|
||||||
return "ld #type"
|
|
||||||
case ExtPayloadOffset:
|
|
||||||
return "ld #poff"
|
|
||||||
case ExtInterfaceIndex:
|
|
||||||
return "ld #ifidx"
|
|
||||||
case ExtNetlinkAttr:
|
|
||||||
return "ld #nla"
|
|
||||||
case ExtNetlinkAttrNested:
|
|
||||||
return "ld #nlan"
|
|
||||||
case ExtMark:
|
|
||||||
return "ld #mark"
|
|
||||||
case ExtQueue:
|
|
||||||
return "ld #queue"
|
|
||||||
case ExtLinkLayerType:
|
|
||||||
return "ld #hatype"
|
|
||||||
case ExtRXHash:
|
|
||||||
return "ld #rxhash"
|
|
||||||
case ExtCPUID:
|
|
||||||
return "ld #cpu"
|
|
||||||
case ExtVLANTag:
|
|
||||||
return "ld #vlan_tci"
|
|
||||||
case ExtVLANTagPresent:
|
|
||||||
return "ld #vlan_avail"
|
|
||||||
case ExtVLANProto:
|
|
||||||
return "ld #vlan_tpid"
|
|
||||||
case ExtRand:
|
|
||||||
return "ld #rand"
|
|
||||||
default:
|
|
||||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// StoreScratch stores register Src into scratch[N].
|
|
||||||
type StoreScratch struct {
|
|
||||||
Src Register
|
|
||||||
N int // 0-15
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assemble implements the Instruction Assemble method.
|
|
||||||
func (a StoreScratch) Assemble() (RawInstruction, error) {
|
|
||||||
if a.N < 0 || a.N > 15 {
|
|
||||||
return RawInstruction{}, fmt.Errorf("invalid scratch slot %d", a.N)
|
|
||||||
}
|
|
||||||
var op uint16
|
|
||||||
switch a.Src {
|
|
||||||
case RegA:
|
|
||||||
op = opClsStoreA
|
|
||||||
case RegX:
|
|
||||||
op = opClsStoreX
|
|
||||||
default:
|
|
||||||
return RawInstruction{}, fmt.Errorf("invalid source register %v", a.Src)
|
|
||||||
}
|
|
||||||
|
|
||||||
return RawInstruction{
|
|
||||||
Op: op,
|
|
||||||
K: uint32(a.N),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the instruction in assembler notation.
|
|
||||||
func (a StoreScratch) String() string {
|
|
||||||
switch a.Src {
|
|
||||||
case RegA:
|
|
||||||
return fmt.Sprintf("st M[%d]", a.N)
|
|
||||||
case RegX:
|
|
||||||
return fmt.Sprintf("stx M[%d]", a.N)
|
|
||||||
default:
|
|
||||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ALUOpConstant executes A = A <Op> Val.
|
|
||||||
type ALUOpConstant struct {
|
|
||||||
Op ALUOp
|
|
||||||
Val uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assemble implements the Instruction Assemble method.
|
|
||||||
func (a ALUOpConstant) Assemble() (RawInstruction, error) {
|
|
||||||
return RawInstruction{
|
|
||||||
Op: opClsALU | uint16(opOperandConstant) | uint16(a.Op),
|
|
||||||
K: a.Val,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the instruction in assembler notation.
|
|
||||||
func (a ALUOpConstant) String() string {
|
|
||||||
switch a.Op {
|
|
||||||
case ALUOpAdd:
|
|
||||||
return fmt.Sprintf("add #%d", a.Val)
|
|
||||||
case ALUOpSub:
|
|
||||||
return fmt.Sprintf("sub #%d", a.Val)
|
|
||||||
case ALUOpMul:
|
|
||||||
return fmt.Sprintf("mul #%d", a.Val)
|
|
||||||
case ALUOpDiv:
|
|
||||||
return fmt.Sprintf("div #%d", a.Val)
|
|
||||||
case ALUOpMod:
|
|
||||||
return fmt.Sprintf("mod #%d", a.Val)
|
|
||||||
case ALUOpAnd:
|
|
||||||
return fmt.Sprintf("and #%d", a.Val)
|
|
||||||
case ALUOpOr:
|
|
||||||
return fmt.Sprintf("or #%d", a.Val)
|
|
||||||
case ALUOpXor:
|
|
||||||
return fmt.Sprintf("xor #%d", a.Val)
|
|
||||||
case ALUOpShiftLeft:
|
|
||||||
return fmt.Sprintf("lsh #%d", a.Val)
|
|
||||||
case ALUOpShiftRight:
|
|
||||||
return fmt.Sprintf("rsh #%d", a.Val)
|
|
||||||
default:
|
|
||||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ALUOpX executes A = A <Op> X
|
|
||||||
type ALUOpX struct {
|
|
||||||
Op ALUOp
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assemble implements the Instruction Assemble method.
|
|
||||||
func (a ALUOpX) Assemble() (RawInstruction, error) {
|
|
||||||
return RawInstruction{
|
|
||||||
Op: opClsALU | uint16(opOperandX) | uint16(a.Op),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the instruction in assembler notation.
|
|
||||||
func (a ALUOpX) String() string {
|
|
||||||
switch a.Op {
|
|
||||||
case ALUOpAdd:
|
|
||||||
return "add x"
|
|
||||||
case ALUOpSub:
|
|
||||||
return "sub x"
|
|
||||||
case ALUOpMul:
|
|
||||||
return "mul x"
|
|
||||||
case ALUOpDiv:
|
|
||||||
return "div x"
|
|
||||||
case ALUOpMod:
|
|
||||||
return "mod x"
|
|
||||||
case ALUOpAnd:
|
|
||||||
return "and x"
|
|
||||||
case ALUOpOr:
|
|
||||||
return "or x"
|
|
||||||
case ALUOpXor:
|
|
||||||
return "xor x"
|
|
||||||
case ALUOpShiftLeft:
|
|
||||||
return "lsh x"
|
|
||||||
case ALUOpShiftRight:
|
|
||||||
return "rsh x"
|
|
||||||
default:
|
|
||||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NegateA executes A = -A.
|
|
||||||
type NegateA struct{}
|
|
||||||
|
|
||||||
// Assemble implements the Instruction Assemble method.
|
|
||||||
func (a NegateA) Assemble() (RawInstruction, error) {
|
|
||||||
return RawInstruction{
|
|
||||||
Op: opClsALU | uint16(aluOpNeg),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the instruction in assembler notation.
|
|
||||||
func (a NegateA) String() string {
|
|
||||||
return fmt.Sprintf("neg")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Jump skips the following Skip instructions in the program.
|
|
||||||
type Jump struct {
|
|
||||||
Skip uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assemble implements the Instruction Assemble method.
|
|
||||||
func (a Jump) Assemble() (RawInstruction, error) {
|
|
||||||
return RawInstruction{
|
|
||||||
Op: opClsJump | uint16(opJumpAlways),
|
|
||||||
K: a.Skip,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the instruction in assembler notation.
|
|
||||||
func (a Jump) String() string {
|
|
||||||
return fmt.Sprintf("ja %d", a.Skip)
|
|
||||||
}
|
|
||||||
|
|
||||||
// JumpIf skips the following Skip instructions in the program if A
|
|
||||||
// <Cond> Val is true.
|
|
||||||
type JumpIf struct {
|
|
||||||
Cond JumpTest
|
|
||||||
Val uint32
|
|
||||||
SkipTrue uint8
|
|
||||||
SkipFalse uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assemble implements the Instruction Assemble method.
|
|
||||||
func (a JumpIf) Assemble() (RawInstruction, error) {
|
|
||||||
return jumpToRaw(a.Cond, opOperandConstant, a.Val, a.SkipTrue, a.SkipFalse)
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the instruction in assembler notation.
|
|
||||||
func (a JumpIf) String() string {
|
|
||||||
return jumpToString(a.Cond, fmt.Sprintf("#%d", a.Val), a.SkipTrue, a.SkipFalse)
|
|
||||||
}
|
|
||||||
|
|
||||||
// JumpIfX skips the following Skip instructions in the program if A
|
|
||||||
// <Cond> X is true.
|
|
||||||
type JumpIfX struct {
|
|
||||||
Cond JumpTest
|
|
||||||
SkipTrue uint8
|
|
||||||
SkipFalse uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assemble implements the Instruction Assemble method.
|
|
||||||
func (a JumpIfX) Assemble() (RawInstruction, error) {
|
|
||||||
return jumpToRaw(a.Cond, opOperandX, 0, a.SkipTrue, a.SkipFalse)
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the instruction in assembler notation.
|
|
||||||
func (a JumpIfX) String() string {
|
|
||||||
return jumpToString(a.Cond, "x", a.SkipTrue, a.SkipFalse)
|
|
||||||
}
|
|
||||||
|
|
||||||
// jumpToRaw assembles a jump instruction into a RawInstruction
|
|
||||||
func jumpToRaw(test JumpTest, operand opOperand, k uint32, skipTrue, skipFalse uint8) (RawInstruction, error) {
|
|
||||||
var (
|
|
||||||
cond jumpOp
|
|
||||||
flip bool
|
|
||||||
)
|
|
||||||
switch test {
|
|
||||||
case JumpEqual:
|
|
||||||
cond = opJumpEqual
|
|
||||||
case JumpNotEqual:
|
|
||||||
cond, flip = opJumpEqual, true
|
|
||||||
case JumpGreaterThan:
|
|
||||||
cond = opJumpGT
|
|
||||||
case JumpLessThan:
|
|
||||||
cond, flip = opJumpGE, true
|
|
||||||
case JumpGreaterOrEqual:
|
|
||||||
cond = opJumpGE
|
|
||||||
case JumpLessOrEqual:
|
|
||||||
cond, flip = opJumpGT, true
|
|
||||||
case JumpBitsSet:
|
|
||||||
cond = opJumpSet
|
|
||||||
case JumpBitsNotSet:
|
|
||||||
cond, flip = opJumpSet, true
|
|
||||||
default:
|
|
||||||
return RawInstruction{}, fmt.Errorf("unknown JumpTest %v", test)
|
|
||||||
}
|
|
||||||
jt, jf := skipTrue, skipFalse
|
|
||||||
if flip {
|
|
||||||
jt, jf = jf, jt
|
|
||||||
}
|
|
||||||
return RawInstruction{
|
|
||||||
Op: opClsJump | uint16(cond) | uint16(operand),
|
|
||||||
Jt: jt,
|
|
||||||
Jf: jf,
|
|
||||||
K: k,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// jumpToString converts a jump instruction to assembler notation
|
|
||||||
func jumpToString(cond JumpTest, operand string, skipTrue, skipFalse uint8) string {
|
|
||||||
switch cond {
|
|
||||||
// K == A
|
|
||||||
case JumpEqual:
|
|
||||||
return conditionalJump(operand, skipTrue, skipFalse, "jeq", "jneq")
|
|
||||||
// K != A
|
|
||||||
case JumpNotEqual:
|
|
||||||
return fmt.Sprintf("jneq %s,%d", operand, skipTrue)
|
|
||||||
// K > A
|
|
||||||
case JumpGreaterThan:
|
|
||||||
return conditionalJump(operand, skipTrue, skipFalse, "jgt", "jle")
|
|
||||||
// K < A
|
|
||||||
case JumpLessThan:
|
|
||||||
return fmt.Sprintf("jlt %s,%d", operand, skipTrue)
|
|
||||||
// K >= A
|
|
||||||
case JumpGreaterOrEqual:
|
|
||||||
return conditionalJump(operand, skipTrue, skipFalse, "jge", "jlt")
|
|
||||||
// K <= A
|
|
||||||
case JumpLessOrEqual:
|
|
||||||
return fmt.Sprintf("jle %s,%d", operand, skipTrue)
|
|
||||||
// K & A != 0
|
|
||||||
case JumpBitsSet:
|
|
||||||
if skipFalse > 0 {
|
|
||||||
return fmt.Sprintf("jset %s,%d,%d", operand, skipTrue, skipFalse)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("jset %s,%d", operand, skipTrue)
|
|
||||||
// K & A == 0, there is no assembler instruction for JumpBitNotSet, use JumpBitSet and invert skips
|
|
||||||
case JumpBitsNotSet:
|
|
||||||
return jumpToString(JumpBitsSet, operand, skipFalse, skipTrue)
|
|
||||||
default:
|
|
||||||
return fmt.Sprintf("unknown JumpTest %#v", cond)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func conditionalJump(operand string, skipTrue, skipFalse uint8, positiveJump, negativeJump string) string {
|
|
||||||
if skipTrue > 0 {
|
|
||||||
if skipFalse > 0 {
|
|
||||||
return fmt.Sprintf("%s %s,%d,%d", positiveJump, operand, skipTrue, skipFalse)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s %s,%d", positiveJump, operand, skipTrue)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s %s,%d", negativeJump, operand, skipFalse)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RetA exits the BPF program, returning the value of register A.
|
|
||||||
type RetA struct{}
|
|
||||||
|
|
||||||
// Assemble implements the Instruction Assemble method.
|
|
||||||
func (a RetA) Assemble() (RawInstruction, error) {
|
|
||||||
return RawInstruction{
|
|
||||||
Op: opClsReturn | opRetSrcA,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the instruction in assembler notation.
|
|
||||||
func (a RetA) String() string {
|
|
||||||
return fmt.Sprintf("ret a")
|
|
||||||
}
|
|
||||||
|
|
||||||
// RetConstant exits the BPF program, returning a constant value.
|
|
||||||
type RetConstant struct {
|
|
||||||
Val uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assemble implements the Instruction Assemble method.
|
|
||||||
func (a RetConstant) Assemble() (RawInstruction, error) {
|
|
||||||
return RawInstruction{
|
|
||||||
Op: opClsReturn | opRetSrcConstant,
|
|
||||||
K: a.Val,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the instruction in assembler notation.
|
|
||||||
func (a RetConstant) String() string {
|
|
||||||
return fmt.Sprintf("ret #%d", a.Val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TXA copies the value of register X to register A.
|
|
||||||
type TXA struct{}
|
|
||||||
|
|
||||||
// Assemble implements the Instruction Assemble method.
|
|
||||||
func (a TXA) Assemble() (RawInstruction, error) {
|
|
||||||
return RawInstruction{
|
|
||||||
Op: opClsMisc | opMiscTXA,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the instruction in assembler notation.
|
|
||||||
func (a TXA) String() string {
|
|
||||||
return fmt.Sprintf("txa")
|
|
||||||
}
|
|
||||||
|
|
||||||
// TAX copies the value of register A to register X.
|
|
||||||
type TAX struct{}
|
|
||||||
|
|
||||||
// Assemble implements the Instruction Assemble method.
|
|
||||||
func (a TAX) Assemble() (RawInstruction, error) {
|
|
||||||
return RawInstruction{
|
|
||||||
Op: opClsMisc | opMiscTAX,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the instruction in assembler notation.
|
|
||||||
func (a TAX) String() string {
|
|
||||||
return fmt.Sprintf("tax")
|
|
||||||
}
|
|
||||||
|
|
||||||
func assembleLoad(dst Register, loadSize int, mode uint16, k uint32) (RawInstruction, error) {
|
|
||||||
var (
|
|
||||||
cls uint16
|
|
||||||
sz uint16
|
|
||||||
)
|
|
||||||
switch dst {
|
|
||||||
case RegA:
|
|
||||||
cls = opClsLoadA
|
|
||||||
case RegX:
|
|
||||||
cls = opClsLoadX
|
|
||||||
default:
|
|
||||||
return RawInstruction{}, fmt.Errorf("invalid target register %v", dst)
|
|
||||||
}
|
|
||||||
switch loadSize {
|
|
||||||
case 1:
|
|
||||||
sz = opLoadWidth1
|
|
||||||
case 2:
|
|
||||||
sz = opLoadWidth2
|
|
||||||
case 4:
|
|
||||||
sz = opLoadWidth4
|
|
||||||
default:
|
|
||||||
return RawInstruction{}, fmt.Errorf("invalid load byte length %d", sz)
|
|
||||||
}
|
|
||||||
return RawInstruction{
|
|
||||||
Op: cls | sz | mode,
|
|
||||||
K: k,
|
|
||||||
}, nil
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
// 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 bpf
|
|
||||||
|
|
||||||
// A Setter is a type which can attach a compiled BPF filter to itself.
|
|
||||||
type Setter interface {
|
|
||||||
SetBPF(filter []RawInstruction) error
|
|
||||||
}
|
|
|
@ -1,150 +0,0 @@
|
||||||
// Copyright 2016 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 bpf
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A VM is an emulated BPF virtual machine.
|
|
||||||
type VM struct {
|
|
||||||
filter []Instruction
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewVM returns a new VM using the input BPF program.
|
|
||||||
func NewVM(filter []Instruction) (*VM, error) {
|
|
||||||
if len(filter) == 0 {
|
|
||||||
return nil, errors.New("one or more Instructions must be specified")
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, ins := range filter {
|
|
||||||
check := len(filter) - (i + 1)
|
|
||||||
switch ins := ins.(type) {
|
|
||||||
// Check for out-of-bounds jumps in instructions
|
|
||||||
case Jump:
|
|
||||||
if check <= int(ins.Skip) {
|
|
||||||
return nil, fmt.Errorf("cannot jump %d instructions; jumping past program bounds", ins.Skip)
|
|
||||||
}
|
|
||||||
case JumpIf:
|
|
||||||
if check <= int(ins.SkipTrue) {
|
|
||||||
return nil, fmt.Errorf("cannot jump %d instructions in true case; jumping past program bounds", ins.SkipTrue)
|
|
||||||
}
|
|
||||||
if check <= int(ins.SkipFalse) {
|
|
||||||
return nil, fmt.Errorf("cannot jump %d instructions in false case; jumping past program bounds", ins.SkipFalse)
|
|
||||||
}
|
|
||||||
case JumpIfX:
|
|
||||||
if check <= int(ins.SkipTrue) {
|
|
||||||
return nil, fmt.Errorf("cannot jump %d instructions in true case; jumping past program bounds", ins.SkipTrue)
|
|
||||||
}
|
|
||||||
if check <= int(ins.SkipFalse) {
|
|
||||||
return nil, fmt.Errorf("cannot jump %d instructions in false case; jumping past program bounds", ins.SkipFalse)
|
|
||||||
}
|
|
||||||
// Check for division or modulus by zero
|
|
||||||
case ALUOpConstant:
|
|
||||||
if ins.Val != 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ins.Op {
|
|
||||||
case ALUOpDiv, ALUOpMod:
|
|
||||||
return nil, errors.New("cannot divide by zero using ALUOpConstant")
|
|
||||||
}
|
|
||||||
// Check for unknown extensions
|
|
||||||
case LoadExtension:
|
|
||||||
switch ins.Num {
|
|
||||||
case ExtLen:
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("extension %d not implemented", ins.Num)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure last instruction is a return instruction
|
|
||||||
switch filter[len(filter)-1].(type) {
|
|
||||||
case RetA, RetConstant:
|
|
||||||
default:
|
|
||||||
return nil, errors.New("BPF program must end with RetA or RetConstant")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Though our VM works using disassembled instructions, we
|
|
||||||
// attempt to assemble the input filter anyway to ensure it is compatible
|
|
||||||
// with an operating system VM.
|
|
||||||
_, err := Assemble(filter)
|
|
||||||
|
|
||||||
return &VM{
|
|
||||||
filter: filter,
|
|
||||||
}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run runs the VM's BPF program against the input bytes.
|
|
||||||
// Run returns the number of bytes accepted by the BPF program, and any errors
|
|
||||||
// which occurred while processing the program.
|
|
||||||
func (v *VM) Run(in []byte) (int, error) {
|
|
||||||
var (
|
|
||||||
// Registers of the virtual machine
|
|
||||||
regA uint32
|
|
||||||
regX uint32
|
|
||||||
regScratch [16]uint32
|
|
||||||
|
|
||||||
// OK is true if the program should continue processing the next
|
|
||||||
// instruction, or false if not, causing the loop to break
|
|
||||||
ok = true
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO(mdlayher): implement:
|
|
||||||
// - NegateA:
|
|
||||||
// - would require a change from uint32 registers to int32
|
|
||||||
// registers
|
|
||||||
|
|
||||||
// TODO(mdlayher): add interop tests that check signedness of ALU
|
|
||||||
// operations against kernel implementation, and make sure Go
|
|
||||||
// implementation matches behavior
|
|
||||||
|
|
||||||
for i := 0; i < len(v.filter) && ok; i++ {
|
|
||||||
ins := v.filter[i]
|
|
||||||
|
|
||||||
switch ins := ins.(type) {
|
|
||||||
case ALUOpConstant:
|
|
||||||
regA = aluOpConstant(ins, regA)
|
|
||||||
case ALUOpX:
|
|
||||||
regA, ok = aluOpX(ins, regA, regX)
|
|
||||||
case Jump:
|
|
||||||
i += int(ins.Skip)
|
|
||||||
case JumpIf:
|
|
||||||
jump := jumpIf(ins, regA)
|
|
||||||
i += jump
|
|
||||||
case JumpIfX:
|
|
||||||
jump := jumpIfX(ins, regA, regX)
|
|
||||||
i += jump
|
|
||||||
case LoadAbsolute:
|
|
||||||
regA, ok = loadAbsolute(ins, in)
|
|
||||||
case LoadConstant:
|
|
||||||
regA, regX = loadConstant(ins, regA, regX)
|
|
||||||
case LoadExtension:
|
|
||||||
regA = loadExtension(ins, in)
|
|
||||||
case LoadIndirect:
|
|
||||||
regA, ok = loadIndirect(ins, in, regX)
|
|
||||||
case LoadMemShift:
|
|
||||||
regX, ok = loadMemShift(ins, in)
|
|
||||||
case LoadScratch:
|
|
||||||
regA, regX = loadScratch(ins, regScratch, regA, regX)
|
|
||||||
case RetA:
|
|
||||||
return int(regA), nil
|
|
||||||
case RetConstant:
|
|
||||||
return int(ins.Val), nil
|
|
||||||
case StoreScratch:
|
|
||||||
regScratch = storeScratch(ins, regScratch, regA, regX)
|
|
||||||
case TAX:
|
|
||||||
regX = regA
|
|
||||||
case TXA:
|
|
||||||
regA = regX
|
|
||||||
default:
|
|
||||||
return 0, fmt.Errorf("unknown Instruction at index %d: %T", i, ins)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
|
@ -1,181 +0,0 @@
|
||||||
// Copyright 2016 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 bpf
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func aluOpConstant(ins ALUOpConstant, regA uint32) uint32 {
|
|
||||||
return aluOpCommon(ins.Op, regA, ins.Val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func aluOpX(ins ALUOpX, regA uint32, regX uint32) (uint32, bool) {
|
|
||||||
// Guard against division or modulus by zero by terminating
|
|
||||||
// the program, as the OS BPF VM does
|
|
||||||
if regX == 0 {
|
|
||||||
switch ins.Op {
|
|
||||||
case ALUOpDiv, ALUOpMod:
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return aluOpCommon(ins.Op, regA, regX), true
|
|
||||||
}
|
|
||||||
|
|
||||||
func aluOpCommon(op ALUOp, regA uint32, value uint32) uint32 {
|
|
||||||
switch op {
|
|
||||||
case ALUOpAdd:
|
|
||||||
return regA + value
|
|
||||||
case ALUOpSub:
|
|
||||||
return regA - value
|
|
||||||
case ALUOpMul:
|
|
||||||
return regA * value
|
|
||||||
case ALUOpDiv:
|
|
||||||
// Division by zero not permitted by NewVM and aluOpX checks
|
|
||||||
return regA / value
|
|
||||||
case ALUOpOr:
|
|
||||||
return regA | value
|
|
||||||
case ALUOpAnd:
|
|
||||||
return regA & value
|
|
||||||
case ALUOpShiftLeft:
|
|
||||||
return regA << value
|
|
||||||
case ALUOpShiftRight:
|
|
||||||
return regA >> value
|
|
||||||
case ALUOpMod:
|
|
||||||
// Modulus by zero not permitted by NewVM and aluOpX checks
|
|
||||||
return regA % value
|
|
||||||
case ALUOpXor:
|
|
||||||
return regA ^ value
|
|
||||||
default:
|
|
||||||
return regA
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func jumpIf(ins JumpIf, regA uint32) int {
|
|
||||||
return jumpIfCommon(ins.Cond, ins.SkipTrue, ins.SkipFalse, regA, ins.Val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func jumpIfX(ins JumpIfX, regA uint32, regX uint32) int {
|
|
||||||
return jumpIfCommon(ins.Cond, ins.SkipTrue, ins.SkipFalse, regA, regX)
|
|
||||||
}
|
|
||||||
|
|
||||||
func jumpIfCommon(cond JumpTest, skipTrue, skipFalse uint8, regA uint32, value uint32) int {
|
|
||||||
var ok bool
|
|
||||||
|
|
||||||
switch cond {
|
|
||||||
case JumpEqual:
|
|
||||||
ok = regA == value
|
|
||||||
case JumpNotEqual:
|
|
||||||
ok = regA != value
|
|
||||||
case JumpGreaterThan:
|
|
||||||
ok = regA > value
|
|
||||||
case JumpLessThan:
|
|
||||||
ok = regA < value
|
|
||||||
case JumpGreaterOrEqual:
|
|
||||||
ok = regA >= value
|
|
||||||
case JumpLessOrEqual:
|
|
||||||
ok = regA <= value
|
|
||||||
case JumpBitsSet:
|
|
||||||
ok = (regA & value) != 0
|
|
||||||
case JumpBitsNotSet:
|
|
||||||
ok = (regA & value) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if ok {
|
|
||||||
return int(skipTrue)
|
|
||||||
}
|
|
||||||
|
|
||||||
return int(skipFalse)
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadAbsolute(ins LoadAbsolute, in []byte) (uint32, bool) {
|
|
||||||
offset := int(ins.Off)
|
|
||||||
size := int(ins.Size)
|
|
||||||
|
|
||||||
return loadCommon(in, offset, size)
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadConstant(ins LoadConstant, regA uint32, regX uint32) (uint32, uint32) {
|
|
||||||
switch ins.Dst {
|
|
||||||
case RegA:
|
|
||||||
regA = ins.Val
|
|
||||||
case RegX:
|
|
||||||
regX = ins.Val
|
|
||||||
}
|
|
||||||
|
|
||||||
return regA, regX
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadExtension(ins LoadExtension, in []byte) uint32 {
|
|
||||||
switch ins.Num {
|
|
||||||
case ExtLen:
|
|
||||||
return uint32(len(in))
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("unimplemented extension: %d", ins.Num))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadIndirect(ins LoadIndirect, in []byte, regX uint32) (uint32, bool) {
|
|
||||||
offset := int(ins.Off) + int(regX)
|
|
||||||
size := int(ins.Size)
|
|
||||||
|
|
||||||
return loadCommon(in, offset, size)
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadMemShift(ins LoadMemShift, in []byte) (uint32, bool) {
|
|
||||||
offset := int(ins.Off)
|
|
||||||
|
|
||||||
if !inBounds(len(in), offset, 0) {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mask off high 4 bits and multiply low 4 bits by 4
|
|
||||||
return uint32(in[offset]&0x0f) * 4, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func inBounds(inLen int, offset int, size int) bool {
|
|
||||||
return offset+size <= inLen
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadCommon(in []byte, offset int, size int) (uint32, bool) {
|
|
||||||
if !inBounds(len(in), offset, size) {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
switch size {
|
|
||||||
case 1:
|
|
||||||
return uint32(in[offset]), true
|
|
||||||
case 2:
|
|
||||||
return uint32(binary.BigEndian.Uint16(in[offset : offset+size])), true
|
|
||||||
case 4:
|
|
||||||
return uint32(binary.BigEndian.Uint32(in[offset : offset+size])), true
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("invalid load size: %d", size))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadScratch(ins LoadScratch, regScratch [16]uint32, regA uint32, regX uint32) (uint32, uint32) {
|
|
||||||
switch ins.Dst {
|
|
||||||
case RegA:
|
|
||||||
regA = regScratch[ins.N]
|
|
||||||
case RegX:
|
|
||||||
regX = regScratch[ins.N]
|
|
||||||
}
|
|
||||||
|
|
||||||
return regA, regX
|
|
||||||
}
|
|
||||||
|
|
||||||
func storeScratch(ins StoreScratch, regScratch [16]uint32, regA uint32, regX uint32) [16]uint32 {
|
|
||||||
switch ins.Src {
|
|
||||||
case RegA:
|
|
||||||
regScratch[ins.N] = regA
|
|
||||||
case RegX:
|
|
||||||
regScratch[ins.N] = regX
|
|
||||||
}
|
|
||||||
|
|
||||||
return regScratch
|
|
||||||
}
|
|
|
@ -1,223 +0,0 @@
|
||||||
// go generate gen.go
|
|
||||||
// Code generated by the command above; DO NOT EDIT.
|
|
||||||
|
|
||||||
// Package iana provides protocol number resources managed by the Internet Assigned Numbers Authority (IANA).
|
|
||||||
package iana // import "golang.org/x/net/internal/iana"
|
|
||||||
|
|
||||||
// Differentiated Services Field Codepoints (DSCP), Updated: 2018-05-04
|
|
||||||
const (
|
|
||||||
DiffServCS0 = 0x00 // CS0
|
|
||||||
DiffServCS1 = 0x20 // CS1
|
|
||||||
DiffServCS2 = 0x40 // CS2
|
|
||||||
DiffServCS3 = 0x60 // CS3
|
|
||||||
DiffServCS4 = 0x80 // CS4
|
|
||||||
DiffServCS5 = 0xa0 // CS5
|
|
||||||
DiffServCS6 = 0xc0 // CS6
|
|
||||||
DiffServCS7 = 0xe0 // CS7
|
|
||||||
DiffServAF11 = 0x28 // AF11
|
|
||||||
DiffServAF12 = 0x30 // AF12
|
|
||||||
DiffServAF13 = 0x38 // AF13
|
|
||||||
DiffServAF21 = 0x48 // AF21
|
|
||||||
DiffServAF22 = 0x50 // AF22
|
|
||||||
DiffServAF23 = 0x58 // AF23
|
|
||||||
DiffServAF31 = 0x68 // AF31
|
|
||||||
DiffServAF32 = 0x70 // AF32
|
|
||||||
DiffServAF33 = 0x78 // AF33
|
|
||||||
DiffServAF41 = 0x88 // AF41
|
|
||||||
DiffServAF42 = 0x90 // AF42
|
|
||||||
DiffServAF43 = 0x98 // AF43
|
|
||||||
DiffServEF = 0xb8 // EF
|
|
||||||
DiffServVOICEADMIT = 0xb0 // VOICE-ADMIT
|
|
||||||
NotECNTransport = 0x00 // Not-ECT (Not ECN-Capable Transport)
|
|
||||||
ECNTransport1 = 0x01 // ECT(1) (ECN-Capable Transport(1))
|
|
||||||
ECNTransport0 = 0x02 // ECT(0) (ECN-Capable Transport(0))
|
|
||||||
CongestionExperienced = 0x03 // CE (Congestion Experienced)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Protocol Numbers, Updated: 2017-10-13
|
|
||||||
const (
|
|
||||||
ProtocolIP = 0 // IPv4 encapsulation, pseudo protocol number
|
|
||||||
ProtocolHOPOPT = 0 // IPv6 Hop-by-Hop Option
|
|
||||||
ProtocolICMP = 1 // Internet Control Message
|
|
||||||
ProtocolIGMP = 2 // Internet Group Management
|
|
||||||
ProtocolGGP = 3 // Gateway-to-Gateway
|
|
||||||
ProtocolIPv4 = 4 // IPv4 encapsulation
|
|
||||||
ProtocolST = 5 // Stream
|
|
||||||
ProtocolTCP = 6 // Transmission Control
|
|
||||||
ProtocolCBT = 7 // CBT
|
|
||||||
ProtocolEGP = 8 // Exterior Gateway Protocol
|
|
||||||
ProtocolIGP = 9 // any private interior gateway (used by Cisco for their IGRP)
|
|
||||||
ProtocolBBNRCCMON = 10 // BBN RCC Monitoring
|
|
||||||
ProtocolNVPII = 11 // Network Voice Protocol
|
|
||||||
ProtocolPUP = 12 // PUP
|
|
||||||
ProtocolEMCON = 14 // EMCON
|
|
||||||
ProtocolXNET = 15 // Cross Net Debugger
|
|
||||||
ProtocolCHAOS = 16 // Chaos
|
|
||||||
ProtocolUDP = 17 // User Datagram
|
|
||||||
ProtocolMUX = 18 // Multiplexing
|
|
||||||
ProtocolDCNMEAS = 19 // DCN Measurement Subsystems
|
|
||||||
ProtocolHMP = 20 // Host Monitoring
|
|
||||||
ProtocolPRM = 21 // Packet Radio Measurement
|
|
||||||
ProtocolXNSIDP = 22 // XEROX NS IDP
|
|
||||||
ProtocolTRUNK1 = 23 // Trunk-1
|
|
||||||
ProtocolTRUNK2 = 24 // Trunk-2
|
|
||||||
ProtocolLEAF1 = 25 // Leaf-1
|
|
||||||
ProtocolLEAF2 = 26 // Leaf-2
|
|
||||||
ProtocolRDP = 27 // Reliable Data Protocol
|
|
||||||
ProtocolIRTP = 28 // Internet Reliable Transaction
|
|
||||||
ProtocolISOTP4 = 29 // ISO Transport Protocol Class 4
|
|
||||||
ProtocolNETBLT = 30 // Bulk Data Transfer Protocol
|
|
||||||
ProtocolMFENSP = 31 // MFE Network Services Protocol
|
|
||||||
ProtocolMERITINP = 32 // MERIT Internodal Protocol
|
|
||||||
ProtocolDCCP = 33 // Datagram Congestion Control Protocol
|
|
||||||
Protocol3PC = 34 // Third Party Connect Protocol
|
|
||||||
ProtocolIDPR = 35 // Inter-Domain Policy Routing Protocol
|
|
||||||
ProtocolXTP = 36 // XTP
|
|
||||||
ProtocolDDP = 37 // Datagram Delivery Protocol
|
|
||||||
ProtocolIDPRCMTP = 38 // IDPR Control Message Transport Proto
|
|
||||||
ProtocolTPPP = 39 // TP++ Transport Protocol
|
|
||||||
ProtocolIL = 40 // IL Transport Protocol
|
|
||||||
ProtocolIPv6 = 41 // IPv6 encapsulation
|
|
||||||
ProtocolSDRP = 42 // Source Demand Routing Protocol
|
|
||||||
ProtocolIPv6Route = 43 // Routing Header for IPv6
|
|
||||||
ProtocolIPv6Frag = 44 // Fragment Header for IPv6
|
|
||||||
ProtocolIDRP = 45 // Inter-Domain Routing Protocol
|
|
||||||
ProtocolRSVP = 46 // Reservation Protocol
|
|
||||||
ProtocolGRE = 47 // Generic Routing Encapsulation
|
|
||||||
ProtocolDSR = 48 // Dynamic Source Routing Protocol
|
|
||||||
ProtocolBNA = 49 // BNA
|
|
||||||
ProtocolESP = 50 // Encap Security Payload
|
|
||||||
ProtocolAH = 51 // Authentication Header
|
|
||||||
ProtocolINLSP = 52 // Integrated Net Layer Security TUBA
|
|
||||||
ProtocolNARP = 54 // NBMA Address Resolution Protocol
|
|
||||||
ProtocolMOBILE = 55 // IP Mobility
|
|
||||||
ProtocolTLSP = 56 // Transport Layer Security Protocol using Kryptonet key management
|
|
||||||
ProtocolSKIP = 57 // SKIP
|
|
||||||
ProtocolIPv6ICMP = 58 // ICMP for IPv6
|
|
||||||
ProtocolIPv6NoNxt = 59 // No Next Header for IPv6
|
|
||||||
ProtocolIPv6Opts = 60 // Destination Options for IPv6
|
|
||||||
ProtocolCFTP = 62 // CFTP
|
|
||||||
ProtocolSATEXPAK = 64 // SATNET and Backroom EXPAK
|
|
||||||
ProtocolKRYPTOLAN = 65 // Kryptolan
|
|
||||||
ProtocolRVD = 66 // MIT Remote Virtual Disk Protocol
|
|
||||||
ProtocolIPPC = 67 // Internet Pluribus Packet Core
|
|
||||||
ProtocolSATMON = 69 // SATNET Monitoring
|
|
||||||
ProtocolVISA = 70 // VISA Protocol
|
|
||||||
ProtocolIPCV = 71 // Internet Packet Core Utility
|
|
||||||
ProtocolCPNX = 72 // Computer Protocol Network Executive
|
|
||||||
ProtocolCPHB = 73 // Computer Protocol Heart Beat
|
|
||||||
ProtocolWSN = 74 // Wang Span Network
|
|
||||||
ProtocolPVP = 75 // Packet Video Protocol
|
|
||||||
ProtocolBRSATMON = 76 // Backroom SATNET Monitoring
|
|
||||||
ProtocolSUNND = 77 // SUN ND PROTOCOL-Temporary
|
|
||||||
ProtocolWBMON = 78 // WIDEBAND Monitoring
|
|
||||||
ProtocolWBEXPAK = 79 // WIDEBAND EXPAK
|
|
||||||
ProtocolISOIP = 80 // ISO Internet Protocol
|
|
||||||
ProtocolVMTP = 81 // VMTP
|
|
||||||
ProtocolSECUREVMTP = 82 // SECURE-VMTP
|
|
||||||
ProtocolVINES = 83 // VINES
|
|
||||||
ProtocolTTP = 84 // Transaction Transport Protocol
|
|
||||||
ProtocolIPTM = 84 // Internet Protocol Traffic Manager
|
|
||||||
ProtocolNSFNETIGP = 85 // NSFNET-IGP
|
|
||||||
ProtocolDGP = 86 // Dissimilar Gateway Protocol
|
|
||||||
ProtocolTCF = 87 // TCF
|
|
||||||
ProtocolEIGRP = 88 // EIGRP
|
|
||||||
ProtocolOSPFIGP = 89 // OSPFIGP
|
|
||||||
ProtocolSpriteRPC = 90 // Sprite RPC Protocol
|
|
||||||
ProtocolLARP = 91 // Locus Address Resolution Protocol
|
|
||||||
ProtocolMTP = 92 // Multicast Transport Protocol
|
|
||||||
ProtocolAX25 = 93 // AX.25 Frames
|
|
||||||
ProtocolIPIP = 94 // IP-within-IP Encapsulation Protocol
|
|
||||||
ProtocolSCCSP = 96 // Semaphore Communications Sec. Pro.
|
|
||||||
ProtocolETHERIP = 97 // Ethernet-within-IP Encapsulation
|
|
||||||
ProtocolENCAP = 98 // Encapsulation Header
|
|
||||||
ProtocolGMTP = 100 // GMTP
|
|
||||||
ProtocolIFMP = 101 // Ipsilon Flow Management Protocol
|
|
||||||
ProtocolPNNI = 102 // PNNI over IP
|
|
||||||
ProtocolPIM = 103 // Protocol Independent Multicast
|
|
||||||
ProtocolARIS = 104 // ARIS
|
|
||||||
ProtocolSCPS = 105 // SCPS
|
|
||||||
ProtocolQNX = 106 // QNX
|
|
||||||
ProtocolAN = 107 // Active Networks
|
|
||||||
ProtocolIPComp = 108 // IP Payload Compression Protocol
|
|
||||||
ProtocolSNP = 109 // Sitara Networks Protocol
|
|
||||||
ProtocolCompaqPeer = 110 // Compaq Peer Protocol
|
|
||||||
ProtocolIPXinIP = 111 // IPX in IP
|
|
||||||
ProtocolVRRP = 112 // Virtual Router Redundancy Protocol
|
|
||||||
ProtocolPGM = 113 // PGM Reliable Transport Protocol
|
|
||||||
ProtocolL2TP = 115 // Layer Two Tunneling Protocol
|
|
||||||
ProtocolDDX = 116 // D-II Data Exchange (DDX)
|
|
||||||
ProtocolIATP = 117 // Interactive Agent Transfer Protocol
|
|
||||||
ProtocolSTP = 118 // Schedule Transfer Protocol
|
|
||||||
ProtocolSRP = 119 // SpectraLink Radio Protocol
|
|
||||||
ProtocolUTI = 120 // UTI
|
|
||||||
ProtocolSMP = 121 // Simple Message Protocol
|
|
||||||
ProtocolPTP = 123 // Performance Transparency Protocol
|
|
||||||
ProtocolISIS = 124 // ISIS over IPv4
|
|
||||||
ProtocolFIRE = 125 // FIRE
|
|
||||||
ProtocolCRTP = 126 // Combat Radio Transport Protocol
|
|
||||||
ProtocolCRUDP = 127 // Combat Radio User Datagram
|
|
||||||
ProtocolSSCOPMCE = 128 // SSCOPMCE
|
|
||||||
ProtocolIPLT = 129 // IPLT
|
|
||||||
ProtocolSPS = 130 // Secure Packet Shield
|
|
||||||
ProtocolPIPE = 131 // Private IP Encapsulation within IP
|
|
||||||
ProtocolSCTP = 132 // Stream Control Transmission Protocol
|
|
||||||
ProtocolFC = 133 // Fibre Channel
|
|
||||||
ProtocolRSVPE2EIGNORE = 134 // RSVP-E2E-IGNORE
|
|
||||||
ProtocolMobilityHeader = 135 // Mobility Header
|
|
||||||
ProtocolUDPLite = 136 // UDPLite
|
|
||||||
ProtocolMPLSinIP = 137 // MPLS-in-IP
|
|
||||||
ProtocolMANET = 138 // MANET Protocols
|
|
||||||
ProtocolHIP = 139 // Host Identity Protocol
|
|
||||||
ProtocolShim6 = 140 // Shim6 Protocol
|
|
||||||
ProtocolWESP = 141 // Wrapped Encapsulating Security Payload
|
|
||||||
ProtocolROHC = 142 // Robust Header Compression
|
|
||||||
ProtocolReserved = 255 // Reserved
|
|
||||||
)
|
|
||||||
|
|
||||||
// Address Family Numbers, Updated: 2018-04-02
|
|
||||||
const (
|
|
||||||
AddrFamilyIPv4 = 1 // IP (IP version 4)
|
|
||||||
AddrFamilyIPv6 = 2 // IP6 (IP version 6)
|
|
||||||
AddrFamilyNSAP = 3 // NSAP
|
|
||||||
AddrFamilyHDLC = 4 // HDLC (8-bit multidrop)
|
|
||||||
AddrFamilyBBN1822 = 5 // BBN 1822
|
|
||||||
AddrFamily802 = 6 // 802 (includes all 802 media plus Ethernet "canonical format")
|
|
||||||
AddrFamilyE163 = 7 // E.163
|
|
||||||
AddrFamilyE164 = 8 // E.164 (SMDS, Frame Relay, ATM)
|
|
||||||
AddrFamilyF69 = 9 // F.69 (Telex)
|
|
||||||
AddrFamilyX121 = 10 // X.121 (X.25, Frame Relay)
|
|
||||||
AddrFamilyIPX = 11 // IPX
|
|
||||||
AddrFamilyAppletalk = 12 // Appletalk
|
|
||||||
AddrFamilyDecnetIV = 13 // Decnet IV
|
|
||||||
AddrFamilyBanyanVines = 14 // Banyan Vines
|
|
||||||
AddrFamilyE164withSubaddress = 15 // E.164 with NSAP format subaddress
|
|
||||||
AddrFamilyDNS = 16 // DNS (Domain Name System)
|
|
||||||
AddrFamilyDistinguishedName = 17 // Distinguished Name
|
|
||||||
AddrFamilyASNumber = 18 // AS Number
|
|
||||||
AddrFamilyXTPoverIPv4 = 19 // XTP over IP version 4
|
|
||||||
AddrFamilyXTPoverIPv6 = 20 // XTP over IP version 6
|
|
||||||
AddrFamilyXTPnativemodeXTP = 21 // XTP native mode XTP
|
|
||||||
AddrFamilyFibreChannelWorldWidePortName = 22 // Fibre Channel World-Wide Port Name
|
|
||||||
AddrFamilyFibreChannelWorldWideNodeName = 23 // Fibre Channel World-Wide Node Name
|
|
||||||
AddrFamilyGWID = 24 // GWID
|
|
||||||
AddrFamilyL2VPN = 25 // AFI for L2VPN information
|
|
||||||
AddrFamilyMPLSTPSectionEndpointID = 26 // MPLS-TP Section Endpoint Identifier
|
|
||||||
AddrFamilyMPLSTPLSPEndpointID = 27 // MPLS-TP LSP Endpoint Identifier
|
|
||||||
AddrFamilyMPLSTPPseudowireEndpointID = 28 // MPLS-TP Pseudowire Endpoint Identifier
|
|
||||||
AddrFamilyMTIPv4 = 29 // MT IP: Multi-Topology IP version 4
|
|
||||||
AddrFamilyMTIPv6 = 30 // MT IPv6: Multi-Topology IP version 6
|
|
||||||
AddrFamilyEIGRPCommonServiceFamily = 16384 // EIGRP Common Service Family
|
|
||||||
AddrFamilyEIGRPIPv4ServiceFamily = 16385 // EIGRP IPv4 Service Family
|
|
||||||
AddrFamilyEIGRPIPv6ServiceFamily = 16386 // EIGRP IPv6 Service Family
|
|
||||||
AddrFamilyLISPCanonicalAddressFormat = 16387 // LISP Canonical Address Format (LCAF)
|
|
||||||
AddrFamilyBGPLS = 16388 // BGP-LS
|
|
||||||
AddrFamily48bitMAC = 16389 // 48-bit MAC
|
|
||||||
AddrFamily64bitMAC = 16390 // 64-bit MAC
|
|
||||||
AddrFamilyOUI = 16391 // OUI
|
|
||||||
AddrFamilyMACFinal24bits = 16392 // MAC/24
|
|
||||||
AddrFamilyMACFinal40bits = 16393 // MAC/40
|
|
||||||
AddrFamilyIPv6Initial64bits = 16394 // IPv6/64
|
|
||||||
AddrFamilyRBridgePortID = 16395 // RBridge Port ID
|
|
||||||
AddrFamilyTRILLNickname = 16396 // TRILL Nickname
|
|
||||||
)
|
|
|
@ -1,383 +0,0 @@
|
||||||
// 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.
|
|
||||||
|
|
||||||
// +build ignore
|
|
||||||
|
|
||||||
//go:generate go run gen.go
|
|
||||||
|
|
||||||
// This program generates internet protocol constants and tables by
|
|
||||||
// reading IANA protocol registries.
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/xml"
|
|
||||||
"fmt"
|
|
||||||
"go/format"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var registries = []struct {
|
|
||||||
url string
|
|
||||||
parse func(io.Writer, io.Reader) error
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"https://www.iana.org/assignments/dscp-registry/dscp-registry.xml",
|
|
||||||
parseDSCPRegistry,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml",
|
|
||||||
parseProtocolNumbers,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"https://www.iana.org/assignments/address-family-numbers/address-family-numbers.xml",
|
|
||||||
parseAddrFamilyNumbers,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
var bb bytes.Buffer
|
|
||||||
fmt.Fprintf(&bb, "// go generate gen.go\n")
|
|
||||||
fmt.Fprintf(&bb, "// Code generated by the command above; DO NOT EDIT.\n\n")
|
|
||||||
fmt.Fprintf(&bb, "// Package iana provides protocol number resources managed by the Internet Assigned Numbers Authority (IANA).\n")
|
|
||||||
fmt.Fprintf(&bb, `package iana // import "golang.org/x/net/internal/iana"`+"\n\n")
|
|
||||||
for _, r := range registries {
|
|
||||||
resp, err := http.Get(r.url)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
fmt.Fprintf(os.Stderr, "got HTTP status code %v for %v\n", resp.StatusCode, r.url)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
if err := r.parse(&bb, resp.Body); err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(&bb, "\n")
|
|
||||||
}
|
|
||||||
b, err := format.Source(bb.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
if err := ioutil.WriteFile("const.go", b, 0644); err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseDSCPRegistry(w io.Writer, r io.Reader) error {
|
|
||||||
dec := xml.NewDecoder(r)
|
|
||||||
var dr dscpRegistry
|
|
||||||
if err := dec.Decode(&dr); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "// %s, Updated: %s\n", dr.Title, dr.Updated)
|
|
||||||
fmt.Fprintf(w, "const (\n")
|
|
||||||
for _, dr := range dr.escapeDSCP() {
|
|
||||||
fmt.Fprintf(w, "DiffServ%s = %#02x", dr.Name, dr.Value)
|
|
||||||
fmt.Fprintf(w, "// %s\n", dr.OrigName)
|
|
||||||
}
|
|
||||||
for _, er := range dr.escapeECN() {
|
|
||||||
fmt.Fprintf(w, "%s = %#02x", er.Descr, er.Value)
|
|
||||||
fmt.Fprintf(w, "// %s\n", er.OrigDescr)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, ")\n")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type dscpRegistry struct {
|
|
||||||
XMLName xml.Name `xml:"registry"`
|
|
||||||
Title string `xml:"title"`
|
|
||||||
Updated string `xml:"updated"`
|
|
||||||
Note string `xml:"note"`
|
|
||||||
Registries []struct {
|
|
||||||
Title string `xml:"title"`
|
|
||||||
Registries []struct {
|
|
||||||
Title string `xml:"title"`
|
|
||||||
Records []struct {
|
|
||||||
Name string `xml:"name"`
|
|
||||||
Space string `xml:"space"`
|
|
||||||
} `xml:"record"`
|
|
||||||
} `xml:"registry"`
|
|
||||||
Records []struct {
|
|
||||||
Value string `xml:"value"`
|
|
||||||
Descr string `xml:"description"`
|
|
||||||
} `xml:"record"`
|
|
||||||
} `xml:"registry"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type canonDSCPRecord struct {
|
|
||||||
OrigName string
|
|
||||||
Name string
|
|
||||||
Value int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (drr *dscpRegistry) escapeDSCP() []canonDSCPRecord {
|
|
||||||
var drs []canonDSCPRecord
|
|
||||||
for _, preg := range drr.Registries {
|
|
||||||
if !strings.Contains(preg.Title, "Differentiated Services Field Codepoints") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, reg := range preg.Registries {
|
|
||||||
if !strings.Contains(reg.Title, "Pool 1 Codepoints") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
drs = make([]canonDSCPRecord, len(reg.Records))
|
|
||||||
sr := strings.NewReplacer(
|
|
||||||
"+", "",
|
|
||||||
"-", "",
|
|
||||||
"/", "",
|
|
||||||
".", "",
|
|
||||||
" ", "",
|
|
||||||
)
|
|
||||||
for i, dr := range reg.Records {
|
|
||||||
s := strings.TrimSpace(dr.Name)
|
|
||||||
drs[i].OrigName = s
|
|
||||||
drs[i].Name = sr.Replace(s)
|
|
||||||
n, err := strconv.ParseUint(dr.Space, 2, 8)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
drs[i].Value = int(n) << 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return drs
|
|
||||||
}
|
|
||||||
|
|
||||||
type canonECNRecord struct {
|
|
||||||
OrigDescr string
|
|
||||||
Descr string
|
|
||||||
Value int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (drr *dscpRegistry) escapeECN() []canonECNRecord {
|
|
||||||
var ers []canonECNRecord
|
|
||||||
for _, reg := range drr.Registries {
|
|
||||||
if !strings.Contains(reg.Title, "ECN Field") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ers = make([]canonECNRecord, len(reg.Records))
|
|
||||||
sr := strings.NewReplacer(
|
|
||||||
"Capable", "",
|
|
||||||
"Not-ECT", "",
|
|
||||||
"ECT(1)", "",
|
|
||||||
"ECT(0)", "",
|
|
||||||
"CE", "",
|
|
||||||
"(", "",
|
|
||||||
")", "",
|
|
||||||
"+", "",
|
|
||||||
"-", "",
|
|
||||||
"/", "",
|
|
||||||
".", "",
|
|
||||||
" ", "",
|
|
||||||
)
|
|
||||||
for i, er := range reg.Records {
|
|
||||||
s := strings.TrimSpace(er.Descr)
|
|
||||||
ers[i].OrigDescr = s
|
|
||||||
ss := strings.Split(s, " ")
|
|
||||||
if len(ss) > 1 {
|
|
||||||
ers[i].Descr = strings.Join(ss[1:], " ")
|
|
||||||
} else {
|
|
||||||
ers[i].Descr = ss[0]
|
|
||||||
}
|
|
||||||
ers[i].Descr = sr.Replace(er.Descr)
|
|
||||||
n, err := strconv.ParseUint(er.Value, 2, 8)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ers[i].Value = int(n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ers
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseProtocolNumbers(w io.Writer, r io.Reader) error {
|
|
||||||
dec := xml.NewDecoder(r)
|
|
||||||
var pn protocolNumbers
|
|
||||||
if err := dec.Decode(&pn); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
prs := pn.escape()
|
|
||||||
prs = append([]canonProtocolRecord{{
|
|
||||||
Name: "IP",
|
|
||||||
Descr: "IPv4 encapsulation, pseudo protocol number",
|
|
||||||
Value: 0,
|
|
||||||
}}, prs...)
|
|
||||||
fmt.Fprintf(w, "// %s, Updated: %s\n", pn.Title, pn.Updated)
|
|
||||||
fmt.Fprintf(w, "const (\n")
|
|
||||||
for _, pr := range prs {
|
|
||||||
if pr.Name == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "Protocol%s = %d", pr.Name, pr.Value)
|
|
||||||
s := pr.Descr
|
|
||||||
if s == "" {
|
|
||||||
s = pr.OrigName
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "// %s\n", s)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, ")\n")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type protocolNumbers struct {
|
|
||||||
XMLName xml.Name `xml:"registry"`
|
|
||||||
Title string `xml:"title"`
|
|
||||||
Updated string `xml:"updated"`
|
|
||||||
RegTitle string `xml:"registry>title"`
|
|
||||||
Note string `xml:"registry>note"`
|
|
||||||
Records []struct {
|
|
||||||
Value string `xml:"value"`
|
|
||||||
Name string `xml:"name"`
|
|
||||||
Descr string `xml:"description"`
|
|
||||||
} `xml:"registry>record"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type canonProtocolRecord struct {
|
|
||||||
OrigName string
|
|
||||||
Name string
|
|
||||||
Descr string
|
|
||||||
Value int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pn *protocolNumbers) escape() []canonProtocolRecord {
|
|
||||||
prs := make([]canonProtocolRecord, len(pn.Records))
|
|
||||||
sr := strings.NewReplacer(
|
|
||||||
"-in-", "in",
|
|
||||||
"-within-", "within",
|
|
||||||
"-over-", "over",
|
|
||||||
"+", "P",
|
|
||||||
"-", "",
|
|
||||||
"/", "",
|
|
||||||
".", "",
|
|
||||||
" ", "",
|
|
||||||
)
|
|
||||||
for i, pr := range pn.Records {
|
|
||||||
if strings.Contains(pr.Name, "Deprecated") ||
|
|
||||||
strings.Contains(pr.Name, "deprecated") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
prs[i].OrigName = pr.Name
|
|
||||||
s := strings.TrimSpace(pr.Name)
|
|
||||||
switch pr.Name {
|
|
||||||
case "ISIS over IPv4":
|
|
||||||
prs[i].Name = "ISIS"
|
|
||||||
case "manet":
|
|
||||||
prs[i].Name = "MANET"
|
|
||||||
default:
|
|
||||||
prs[i].Name = sr.Replace(s)
|
|
||||||
}
|
|
||||||
ss := strings.Split(pr.Descr, "\n")
|
|
||||||
for i := range ss {
|
|
||||||
ss[i] = strings.TrimSpace(ss[i])
|
|
||||||
}
|
|
||||||
if len(ss) > 1 {
|
|
||||||
prs[i].Descr = strings.Join(ss, " ")
|
|
||||||
} else {
|
|
||||||
prs[i].Descr = ss[0]
|
|
||||||
}
|
|
||||||
prs[i].Value, _ = strconv.Atoi(pr.Value)
|
|
||||||
}
|
|
||||||
return prs
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseAddrFamilyNumbers(w io.Writer, r io.Reader) error {
|
|
||||||
dec := xml.NewDecoder(r)
|
|
||||||
var afn addrFamilylNumbers
|
|
||||||
if err := dec.Decode(&afn); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
afrs := afn.escape()
|
|
||||||
fmt.Fprintf(w, "// %s, Updated: %s\n", afn.Title, afn.Updated)
|
|
||||||
fmt.Fprintf(w, "const (\n")
|
|
||||||
for _, afr := range afrs {
|
|
||||||
if afr.Name == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "AddrFamily%s = %d", afr.Name, afr.Value)
|
|
||||||
fmt.Fprintf(w, "// %s\n", afr.Descr)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, ")\n")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type addrFamilylNumbers struct {
|
|
||||||
XMLName xml.Name `xml:"registry"`
|
|
||||||
Title string `xml:"title"`
|
|
||||||
Updated string `xml:"updated"`
|
|
||||||
RegTitle string `xml:"registry>title"`
|
|
||||||
Note string `xml:"registry>note"`
|
|
||||||
Records []struct {
|
|
||||||
Value string `xml:"value"`
|
|
||||||
Descr string `xml:"description"`
|
|
||||||
} `xml:"registry>record"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type canonAddrFamilyRecord struct {
|
|
||||||
Name string
|
|
||||||
Descr string
|
|
||||||
Value int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (afn *addrFamilylNumbers) escape() []canonAddrFamilyRecord {
|
|
||||||
afrs := make([]canonAddrFamilyRecord, len(afn.Records))
|
|
||||||
sr := strings.NewReplacer(
|
|
||||||
"IP version 4", "IPv4",
|
|
||||||
"IP version 6", "IPv6",
|
|
||||||
"Identifier", "ID",
|
|
||||||
"-", "",
|
|
||||||
"-", "",
|
|
||||||
"/", "",
|
|
||||||
".", "",
|
|
||||||
" ", "",
|
|
||||||
)
|
|
||||||
for i, afr := range afn.Records {
|
|
||||||
if strings.Contains(afr.Descr, "Unassigned") ||
|
|
||||||
strings.Contains(afr.Descr, "Reserved") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
afrs[i].Descr = afr.Descr
|
|
||||||
s := strings.TrimSpace(afr.Descr)
|
|
||||||
switch s {
|
|
||||||
case "IP (IP version 4)":
|
|
||||||
afrs[i].Name = "IPv4"
|
|
||||||
case "IP6 (IP version 6)":
|
|
||||||
afrs[i].Name = "IPv6"
|
|
||||||
case "AFI for L2VPN information":
|
|
||||||
afrs[i].Name = "L2VPN"
|
|
||||||
case "E.164 with NSAP format subaddress":
|
|
||||||
afrs[i].Name = "E164withSubaddress"
|
|
||||||
case "MT IP: Multi-Topology IP version 4":
|
|
||||||
afrs[i].Name = "MTIPv4"
|
|
||||||
case "MAC/24":
|
|
||||||
afrs[i].Name = "MACFinal24bits"
|
|
||||||
case "MAC/40":
|
|
||||||
afrs[i].Name = "MACFinal40bits"
|
|
||||||
case "IPv6/64":
|
|
||||||
afrs[i].Name = "IPv6Initial64bits"
|
|
||||||
default:
|
|
||||||
n := strings.Index(s, "(")
|
|
||||||
if n > 0 {
|
|
||||||
s = s[:n]
|
|
||||||
}
|
|
||||||
n = strings.Index(s, ":")
|
|
||||||
if n > 0 {
|
|
||||||
s = s[:n]
|
|
||||||
}
|
|
||||||
afrs[i].Name = sr.Replace(s)
|
|
||||||
}
|
|
||||||
afrs[i].Value, _ = strconv.Atoi(afr.Value)
|
|
||||||
}
|
|
||||||
return afrs
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
// 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 aix 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) }
|
|
|
@ -1,13 +0,0 @@
|
||||||
// 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 aix 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)
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
// 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)
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
// 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 !aix,!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) {}
|
|
|
@ -1,47 +0,0 @@
|
||||||
// Copyright 2019 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 ignore
|
|
||||||
|
|
||||||
// +godefs map struct_in_addr [4]byte /* in_addr */
|
|
||||||
// +godefs map struct_in6_addr [16]byte /* in6_addr */
|
|
||||||
|
|
||||||
package socket
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include <sys/socket.h>
|
|
||||||
|
|
||||||
#include <netinet/in.h>
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
const (
|
|
||||||
sysAF_UNSPEC = C.AF_UNSPEC
|
|
||||||
sysAF_INET = C.AF_INET
|
|
||||||
sysAF_INET6 = C.AF_INET6
|
|
||||||
|
|
||||||
sysSOCK_RAW = C.SOCK_RAW
|
|
||||||
)
|
|
||||||
|
|
||||||
type iovec C.struct_iovec
|
|
||||||
|
|
||||||
type msghdr C.struct_msghdr
|
|
||||||
|
|
||||||
type mmsghdr C.struct_mmsghdr
|
|
||||||
|
|
||||||
type cmsghdr C.struct_cmsghdr
|
|
||||||
|
|
||||||
type sockaddrInet C.struct_sockaddr_in
|
|
||||||
|
|
||||||
type sockaddrInet6 C.struct_sockaddr_in6
|
|
||||||
|
|
||||||
const (
|
|
||||||
sizeofIovec = C.sizeof_struct_iovec
|
|
||||||
sizeofMsghdr = C.sizeof_struct_msghdr
|
|
||||||
sizeofMmsghdr = C.sizeof_struct_mmsghdr
|
|
||||||
sizeofCmsghdr = C.sizeof_struct_cmsghdr
|
|
||||||
|
|
||||||
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
|
|
||||||
sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
|
|
||||||
)
|
|
|
@ -1,44 +0,0 @@
|
||||||
// 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 ignore
|
|
||||||
|
|
||||||
// +godefs map struct_in_addr [4]byte /* in_addr */
|
|
||||||
// +godefs map struct_in6_addr [16]byte /* in6_addr */
|
|
||||||
|
|
||||||
package socket
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include <sys/socket.h>
|
|
||||||
|
|
||||||
#include <netinet/in.h>
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
const (
|
|
||||||
sysAF_UNSPEC = C.AF_UNSPEC
|
|
||||||
sysAF_INET = C.AF_INET
|
|
||||||
sysAF_INET6 = C.AF_INET6
|
|
||||||
|
|
||||||
sysSOCK_RAW = C.SOCK_RAW
|
|
||||||
)
|
|
||||||
|
|
||||||
type iovec C.struct_iovec
|
|
||||||
|
|
||||||
type msghdr C.struct_msghdr
|
|
||||||
|
|
||||||
type cmsghdr C.struct_cmsghdr
|
|
||||||
|
|
||||||
type sockaddrInet C.struct_sockaddr_in
|
|
||||||
|
|
||||||
type sockaddrInet6 C.struct_sockaddr_in6
|
|
||||||
|
|
||||||
const (
|
|
||||||
sizeofIovec = C.sizeof_struct_iovec
|
|
||||||
sizeofMsghdr = C.sizeof_struct_msghdr
|
|
||||||
sizeofCmsghdr = C.sizeof_struct_cmsghdr
|
|
||||||
|
|
||||||
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
|
|
||||||
sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
|
|
||||||
)
|
|
|
@ -1,44 +0,0 @@
|
||||||
// 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 ignore
|
|
||||||
|
|
||||||
// +godefs map struct_in_addr [4]byte /* in_addr */
|
|
||||||
// +godefs map struct_in6_addr [16]byte /* in6_addr */
|
|
||||||
|
|
||||||
package socket
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include <sys/socket.h>
|
|
||||||
|
|
||||||
#include <netinet/in.h>
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
const (
|
|
||||||
sysAF_UNSPEC = C.AF_UNSPEC
|
|
||||||
sysAF_INET = C.AF_INET
|
|
||||||
sysAF_INET6 = C.AF_INET6
|
|
||||||
|
|
||||||
sysSOCK_RAW = C.SOCK_RAW
|
|
||||||
)
|
|
||||||
|
|
||||||
type iovec C.struct_iovec
|
|
||||||
|
|
||||||
type msghdr C.struct_msghdr
|
|
||||||
|
|
||||||
type cmsghdr C.struct_cmsghdr
|
|
||||||
|
|
||||||
type sockaddrInet C.struct_sockaddr_in
|
|
||||||
|
|
||||||
type sockaddrInet6 C.struct_sockaddr_in6
|
|
||||||
|
|
||||||
const (
|
|
||||||
sizeofIovec = C.sizeof_struct_iovec
|
|
||||||
sizeofMsghdr = C.sizeof_struct_msghdr
|
|
||||||
sizeofCmsghdr = C.sizeof_struct_cmsghdr
|
|
||||||
|
|
||||||
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
|
|
||||||
sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
|
|
||||||
)
|
|
|
@ -1,44 +0,0 @@
|
||||||
// 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 ignore
|
|
||||||
|
|
||||||
// +godefs map struct_in_addr [4]byte /* in_addr */
|
|
||||||
// +godefs map struct_in6_addr [16]byte /* in6_addr */
|
|
||||||
|
|
||||||
package socket
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include <sys/socket.h>
|
|
||||||
|
|
||||||
#include <netinet/in.h>
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
const (
|
|
||||||
sysAF_UNSPEC = C.AF_UNSPEC
|
|
||||||
sysAF_INET = C.AF_INET
|
|
||||||
sysAF_INET6 = C.AF_INET6
|
|
||||||
|
|
||||||
sysSOCK_RAW = C.SOCK_RAW
|
|
||||||
)
|
|
||||||
|
|
||||||
type iovec C.struct_iovec
|
|
||||||
|
|
||||||
type msghdr C.struct_msghdr
|
|
||||||
|
|
||||||
type cmsghdr C.struct_cmsghdr
|
|
||||||
|
|
||||||
type sockaddrInet C.struct_sockaddr_in
|
|
||||||
|
|
||||||
type sockaddrInet6 C.struct_sockaddr_in6
|
|
||||||
|
|
||||||
const (
|
|
||||||
sizeofIovec = C.sizeof_struct_iovec
|
|
||||||
sizeofMsghdr = C.sizeof_struct_msghdr
|
|
||||||
sizeofCmsghdr = C.sizeof_struct_cmsghdr
|
|
||||||
|
|
||||||
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
|
|
||||||
sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
|
|
||||||
)
|
|
|
@ -1,49 +0,0 @@
|
||||||
// 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 ignore
|
|
||||||
|
|
||||||
// +godefs map struct_in_addr [4]byte /* in_addr */
|
|
||||||
// +godefs map struct_in6_addr [16]byte /* in6_addr */
|
|
||||||
|
|
||||||
package socket
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include <linux/in.h>
|
|
||||||
#include <linux/in6.h>
|
|
||||||
|
|
||||||
#define _GNU_SOURCE
|
|
||||||
#include <sys/socket.h>
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
const (
|
|
||||||
sysAF_UNSPEC = C.AF_UNSPEC
|
|
||||||
sysAF_INET = C.AF_INET
|
|
||||||
sysAF_INET6 = C.AF_INET6
|
|
||||||
|
|
||||||
sysSOCK_RAW = C.SOCK_RAW
|
|
||||||
)
|
|
||||||
|
|
||||||
type iovec C.struct_iovec
|
|
||||||
|
|
||||||
type msghdr C.struct_msghdr
|
|
||||||
|
|
||||||
type mmsghdr C.struct_mmsghdr
|
|
||||||
|
|
||||||
type cmsghdr C.struct_cmsghdr
|
|
||||||
|
|
||||||
type sockaddrInet C.struct_sockaddr_in
|
|
||||||
|
|
||||||
type sockaddrInet6 C.struct_sockaddr_in6
|
|
||||||
|
|
||||||
const (
|
|
||||||
sizeofIovec = C.sizeof_struct_iovec
|
|
||||||
sizeofMsghdr = C.sizeof_struct_msghdr
|
|
||||||
sizeofMmsghdr = C.sizeof_struct_mmsghdr
|
|
||||||
sizeofCmsghdr = C.sizeof_struct_cmsghdr
|
|
||||||
|
|
||||||
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
|
|
||||||
sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
|
|
||||||
)
|
|
|
@ -1,47 +0,0 @@
|
||||||
// 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 ignore
|
|
||||||
|
|
||||||
// +godefs map struct_in_addr [4]byte /* in_addr */
|
|
||||||
// +godefs map struct_in6_addr [16]byte /* in6_addr */
|
|
||||||
|
|
||||||
package socket
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include <sys/socket.h>
|
|
||||||
|
|
||||||
#include <netinet/in.h>
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
const (
|
|
||||||
sysAF_UNSPEC = C.AF_UNSPEC
|
|
||||||
sysAF_INET = C.AF_INET
|
|
||||||
sysAF_INET6 = C.AF_INET6
|
|
||||||
|
|
||||||
sysSOCK_RAW = C.SOCK_RAW
|
|
||||||
)
|
|
||||||
|
|
||||||
type iovec C.struct_iovec
|
|
||||||
|
|
||||||
type msghdr C.struct_msghdr
|
|
||||||
|
|
||||||
type mmsghdr C.struct_mmsghdr
|
|
||||||
|
|
||||||
type cmsghdr C.struct_cmsghdr
|
|
||||||
|
|
||||||
type sockaddrInet C.struct_sockaddr_in
|
|
||||||
|
|
||||||
type sockaddrInet6 C.struct_sockaddr_in6
|
|
||||||
|
|
||||||
const (
|
|
||||||
sizeofIovec = C.sizeof_struct_iovec
|
|
||||||
sizeofMsghdr = C.sizeof_struct_msghdr
|
|
||||||
sizeofMmsghdr = C.sizeof_struct_mmsghdr
|
|
||||||
sizeofCmsghdr = C.sizeof_struct_cmsghdr
|
|
||||||
|
|
||||||
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
|
|
||||||
sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
|
|
||||||
)
|
|
|
@ -1,44 +0,0 @@
|
||||||
// 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 ignore
|
|
||||||
|
|
||||||
// +godefs map struct_in_addr [4]byte /* in_addr */
|
|
||||||
// +godefs map struct_in6_addr [16]byte /* in6_addr */
|
|
||||||
|
|
||||||
package socket
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include <sys/socket.h>
|
|
||||||
|
|
||||||
#include <netinet/in.h>
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
const (
|
|
||||||
sysAF_UNSPEC = C.AF_UNSPEC
|
|
||||||
sysAF_INET = C.AF_INET
|
|
||||||
sysAF_INET6 = C.AF_INET6
|
|
||||||
|
|
||||||
sysSOCK_RAW = C.SOCK_RAW
|
|
||||||
)
|
|
||||||
|
|
||||||
type iovec C.struct_iovec
|
|
||||||
|
|
||||||
type msghdr C.struct_msghdr
|
|
||||||
|
|
||||||
type cmsghdr C.struct_cmsghdr
|
|
||||||
|
|
||||||
type sockaddrInet C.struct_sockaddr_in
|
|
||||||
|
|
||||||
type sockaddrInet6 C.struct_sockaddr_in6
|
|
||||||
|
|
||||||
const (
|
|
||||||
sizeofIovec = C.sizeof_struct_iovec
|
|
||||||
sizeofMsghdr = C.sizeof_struct_msghdr
|
|
||||||
sizeofCmsghdr = C.sizeof_struct_cmsghdr
|
|
||||||
|
|
||||||
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
|
|
||||||
sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
|
|
||||||
)
|
|
|
@ -1,44 +0,0 @@
|
||||||
// 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 ignore
|
|
||||||
|
|
||||||
// +godefs map struct_in_addr [4]byte /* in_addr */
|
|
||||||
// +godefs map struct_in6_addr [16]byte /* in6_addr */
|
|
||||||
|
|
||||||
package socket
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include <sys/socket.h>
|
|
||||||
|
|
||||||
#include <netinet/in.h>
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
const (
|
|
||||||
sysAF_UNSPEC = C.AF_UNSPEC
|
|
||||||
sysAF_INET = C.AF_INET
|
|
||||||
sysAF_INET6 = C.AF_INET6
|
|
||||||
|
|
||||||
sysSOCK_RAW = C.SOCK_RAW
|
|
||||||
)
|
|
||||||
|
|
||||||
type iovec C.struct_iovec
|
|
||||||
|
|
||||||
type msghdr C.struct_msghdr
|
|
||||||
|
|
||||||
type cmsghdr C.struct_cmsghdr
|
|
||||||
|
|
||||||
type sockaddrInet C.struct_sockaddr_in
|
|
||||||
|
|
||||||
type sockaddrInet6 C.struct_sockaddr_in6
|
|
||||||
|
|
||||||
const (
|
|
||||||
sizeofIovec = C.sizeof_struct_iovec
|
|
||||||
sizeofMsghdr = C.sizeof_struct_msghdr
|
|
||||||
sizeofCmsghdr = C.sizeof_struct_cmsghdr
|
|
||||||
|
|
||||||
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
|
|
||||||
sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
|
|
||||||
)
|
|
|
@ -1,7 +0,0 @@
|
||||||
// Copyright 2018 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,go1.12
|
|
||||||
|
|
||||||
// This exists solely so we can linkname in symbols from syscall.
|
|
|
@ -1,31 +0,0 @@
|
||||||
// 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 aix 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
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
// 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
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
// 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)
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
// 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 aix 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)
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
// 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)
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
// 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 !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
|
|
||||||
|
|
||||||
package socket
|
|
||||||
|
|
||||||
type iovec struct{}
|
|
||||||
|
|
||||||
func (v *iovec) set(b []byte) {}
|
|
|
@ -1,21 +0,0 @@
|
||||||
// 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 !aix,!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
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
// 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 aix 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
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
// 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 aix 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)
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
// 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 aix 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)
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
// 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)
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
// 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))
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
// 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))
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
// 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)
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
// 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[:]))
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue