cleaned dependencies

This commit is contained in:
Paul 2021-11-16 19:19:12 +01:00
parent 5734c3019a
commit 38e095d380
424 changed files with 35959 additions and 18998 deletions

View File

@ -1,7 +1,11 @@
package main package main
import ( import (
"github.com/micro/mdns" "net"
"github.com/hashicorp/mdns"
"github.com/paulbsd/go-aptproxy/src/server"
"flag" "flag"
"fmt" "fmt"
@ -21,7 +25,7 @@ func main() {
// Create the HTTP server and initialize the cache // Create the HTTP server and initialize the cache
addr := fmt.Sprintf("%s:%d", *host, *port) addr := fmt.Sprintf("%s:%d", *host, *port)
httpServer, err := NewServer(addr, *directory) httpServer, err := server.NewServer(addr, *directory)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
os.Exit(1) os.Exit(1)
@ -36,19 +40,13 @@ func main() {
log.Println("Stopping HTTP server...") log.Println("Stopping HTTP server...")
}() }()
// Respond to mDNS queries
h, err := os.Hostname()
if err != nil {
log.Println(err)
os.Exit(1)
}
service, err := mdns.NewMDNSService( service, err := mdns.NewMDNSService(
h, "thinkpad",
"_apt_proxy._tcp", "_apt_proxy._tcp",
"", "",
"", "",
*port, *port,
nil, []net.IP{GetOutboundIP()},
[]string{"go-aptproxy - Smarter APT Proxy"}, []string{"go-aptproxy - Smarter APT Proxy"},
) )
if err != nil { if err != nil {
@ -72,3 +70,16 @@ func main() {
<-c <-c
log.Println("Caught SIGINT") log.Println("Caught SIGINT")
} }
// Get preferred outbound ip of this machine
func GetOutboundIP() net.IP {
conn, err := net.Dial("udp", "8.8.8.8:53")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
localAddr := conn.LocalAddr().(*net.UDPAddr)
return localAddr.IP
}

13
go.mod
View File

@ -1,19 +1,16 @@
module go-aptproxy module github.com/paulbsd/go-aptproxy
go 1.17 go 1.17
require ( require (
github.com/fsnotify/fsnotify v1.5.1 github.com/fsnotify/fsnotify v1.5.1
github.com/hashicorp/mdns v1.0.4
github.com/hectane/go-asyncserver v0.1.0 github.com/hectane/go-asyncserver v0.1.0
github.com/micro/mdns v0.3.0
github.com/nathan-osman/go-aptproxy v0.0.0-20160507053712-02fe4d04b5fb
github.com/pquerna/cachecontrol v0.1.0 github.com/pquerna/cachecontrol v0.1.0
) )
require ( require (
github.com/miekg/dns v1.1.3 // indirect github.com/miekg/dns v1.1.43 // indirect
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664 // indirect golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 // indirect golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
) )

31
go.sum
View File

@ -2,14 +2,13 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/hashicorp/mdns v1.0.4 h1:sY0CMhFmjIPDMlTB+HfymFHCaYLhgifZ0QhjaYKD/UQ=
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
github.com/hectane/go-asyncserver v0.1.0 h1:miQMVjLchB0fklo4nKoUp3votMOCSJhddVlPSmyiieM= github.com/hectane/go-asyncserver v0.1.0 h1:miQMVjLchB0fklo4nKoUp3votMOCSJhddVlPSmyiieM=
github.com/hectane/go-asyncserver v0.1.0/go.mod h1:waBaOSU0nKsRCHYMISp/+MSakn6C5HyDSF3y0NUknu0= github.com/hectane/go-asyncserver v0.1.0/go.mod h1:waBaOSU0nKsRCHYMISp/+MSakn6C5HyDSF3y0NUknu0=
github.com/micro/mdns v0.3.0 h1:bYycYe+98AXR3s8Nq5qvt6C573uFTDPIYzJemWON0QE= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
github.com/micro/mdns v0.3.0/go.mod h1:KJ0dW7KmicXU2BV++qkLlmHYcVv7/hHnbtguSWt9Aoc= github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
github.com/miekg/dns v1.1.3 h1:1g0r1IvskvgL8rR+AcHzUA+oFmGcQlaIm4IqakufeMM= github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/nathan-osman/go-aptproxy v0.0.0-20160507053712-02fe4d04b5fb h1:wyxHQadQph4aRZhnsIP04eLgVP5GodIUYcto+TLITCE=
github.com/nathan-osman/go-aptproxy v0.0.0-20160507053712-02fe4d04b5fb/go.mod h1:5Ix064b/k8CMKEnpquzB2sDr5D9Wm1pf+DVQi9Q+UVg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8cTqKc= github.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8cTqKc=
@ -17,15 +16,23 @@ github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQ
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664 h1:YbZJ76lQ1BqNhVe7dKTSB67wDrc2VPRR75IyGyyPDX8= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 h1:ulvT7fqt0yHWzpJwI57MezWnYDVpCAYBVuYst/L+fAY= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c h1:DHcbWVXeY+0Y8HHKR+rbLwnoh2F4tNCY7rTiHJ30RmA=
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -1,7 +1,7 @@
package main package server
import ( import (
"go-aptproxy/cache" "github.com/paulbsd/go-aptproxy/src/cache"
server "github.com/hectane/go-asyncserver" server "github.com/hectane/go-asyncserver"
"github.com/pquerna/cachecontrol/cacheobject" "github.com/pquerna/cachecontrol/cacheobject"

View File

@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) {} Copyright (c) 2014 Armon Dadgar
Permission is hereby granted, free of charge, to any person obtaining a copy of Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in this software and associated documentation files (the "Software"), to deal in

37
vendor/github.com/hashicorp/mdns/README.md generated vendored Normal file
View File

@ -0,0 +1,37 @@
mdns
====
Simple mDNS client/server library in Golang. mDNS or Multicast DNS can be
used to discover services on the local network without the use of an authoritative
DNS server. This enables peer-to-peer discovery. It is important to note that many
networks restrict the use of multicasting, which prevents mDNS from functioning.
Notably, multicast cannot be used in any sort of cloud, or shared infrastructure
environment. However it works well in most office, home, or private infrastructure
environments.
Using the library is very simple, here is an example of publishing a service entry:
// Setup our service export
host, _ := os.Hostname()
info := []string{"My awesome service"}
service, _ := mdns.NewMDNSService(host, "_foobar._tcp", "", "", 8000, nil, info)
// Create the mDNS server, defer shutdown
server, _ := mdns.NewServer(&mdns.Config{Zone: service})
defer server.Shutdown()
Doing a lookup for service providers is also very simple:
// Make a channel for results and start listening
entriesCh := make(chan *mdns.ServiceEntry, 4)
go func() {
for entry := range entriesCh {
fmt.Printf("Got new entry: %v\n", entry)
}
}()
// Start the lookup
mdns.Lookup("_foobar._tcp", entriesCh)
close(entriesCh)

View File

@ -1,12 +1,11 @@
package mdns package mdns
import ( import (
"context"
"fmt" "fmt"
"log" "log"
"net" "net"
"strings" "strings"
"sync" "sync/atomic"
"time" "time"
"github.com/miekg/dns" "github.com/miekg/dns"
@ -23,7 +22,6 @@ type ServiceEntry struct {
Port int Port int
Info string Info string
InfoFields []string InfoFields []string
TTL int
Addr net.IP // @Deprecated Addr net.IP // @Deprecated
@ -40,8 +38,7 @@ func (s *ServiceEntry) complete() bool {
type QueryParam struct { type QueryParam struct {
Service string // Service to lookup Service string // Service to lookup
Domain string // Lookup domain, default "local" Domain string // Lookup domain, default "local"
Context context.Context // Context Timeout time.Duration // Lookup timeout, default 1 second
Timeout time.Duration // Lookup timeout, default 1 second. Ignored if Context is provided
Interface *net.Interface // Multicast interface to use Interface *net.Interface // Multicast interface to use
Entries chan<- *ServiceEntry // Entries Channel Entries chan<- *ServiceEntry // Entries Channel
WantUnicastResponse bool // Unicast response desired, as per 5.4 in RFC WantUnicastResponse bool // Unicast response desired, as per 5.4 in RFC
@ -72,7 +69,7 @@ func Query(params *QueryParam) error {
// Set the multicast interface // Set the multicast interface
if params.Interface != nil { if params.Interface != nil {
if err := client.setInterface(params.Interface, false); err != nil { if err := client.setInterface(params.Interface); err != nil {
return err return err
} }
} }
@ -81,77 +78,14 @@ func Query(params *QueryParam) error {
if params.Domain == "" { if params.Domain == "" {
params.Domain = "local" params.Domain = "local"
} }
if params.Context == nil {
if params.Timeout == 0 { if params.Timeout == 0 {
params.Timeout = time.Second params.Timeout = time.Second
} }
params.Context, _ = context.WithTimeout(context.Background(), params.Timeout)
if err != nil {
return err
}
}
// Run the query // Run the query
return client.query(params) return client.query(params)
} }
// Listen listens indefinitely for multicast updates
func Listen(entries chan<- *ServiceEntry, exit chan struct{}) error {
// Create a new client
client, err := newClient()
if err != nil {
return err
}
defer client.Close()
client.setInterface(nil, true)
// Start listening for response packets
msgCh := make(chan *dns.Msg, 32)
go client.recv(client.ipv4UnicastConn, msgCh)
go client.recv(client.ipv6UnicastConn, msgCh)
go client.recv(client.ipv4MulticastConn, msgCh)
go client.recv(client.ipv6MulticastConn, msgCh)
ip := make(map[string]*ServiceEntry)
for {
select {
case <-exit:
return nil
case <-client.closedCh:
return nil
case m := <-msgCh:
e := messageToEntry(m, ip)
if e == nil {
continue
}
// Check if this entry is complete
if e.complete() {
if e.sent {
continue
}
e.sent = true
entries <- e
ip = make(map[string]*ServiceEntry)
} else {
// Fire off a node specific query
m := new(dns.Msg)
m.SetQuestion(e.Name, dns.TypePTR)
m.RecursionDesired = false
if err := client.sendQuery(m); err != nil {
log.Printf("[ERR] mdns: Failed to query instance %s: %v", e.Name, err)
}
}
}
}
return nil
}
// Lookup is the same as Query, however it uses all the default parameters // Lookup is the same as Query, however it uses all the default parameters
func Lookup(service string, entries chan<- *ServiceEntry) error { func Lookup(service string, entries chan<- *ServiceEntry) error {
params := DefaultParams(service) params := DefaultParams(service)
@ -168,9 +102,8 @@ type client struct {
ipv4MulticastConn *net.UDPConn ipv4MulticastConn *net.UDPConn
ipv6MulticastConn *net.UDPConn ipv6MulticastConn *net.UDPConn
closed bool closed int32
closedCh chan struct{} // TODO(reddaly): This doesn't appear to be used. closedCh chan struct{} // TODO(reddaly): This doesn't appear to be used.
closeLock sync.Mutex
} }
// NewClient creates a new mdns Client that can be used to query // NewClient creates a new mdns Client that can be used to query
@ -178,67 +111,32 @@ type client struct {
func newClient() (*client, error) { func newClient() (*client, error) {
// TODO(reddaly): At least attempt to bind to the port required in the spec. // TODO(reddaly): At least attempt to bind to the port required in the spec.
// Create a IPv4 listener // Create a IPv4 listener
uconn4, err4 := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0}) uconn4, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
uconn6, err6 := net.ListenUDP("udp6", &net.UDPAddr{IP: net.IPv6zero, Port: 0}) if err != nil {
if err4 != nil && err6 != nil { log.Printf("[ERR] mdns: Failed to bind to udp4 port: %v", err)
log.Printf("[ERR] mdns: Failed to bind to udp port: %v %v", err4, err6) }
uconn6, err := net.ListenUDP("udp6", &net.UDPAddr{IP: net.IPv6zero, Port: 0})
if err != nil {
log.Printf("[ERR] mdns: Failed to bind to udp6 port: %v", err)
} }
if uconn4 == nil && uconn6 == nil { if uconn4 == nil && uconn6 == nil {
return nil, fmt.Errorf("failed to bind to any unicast udp port") return nil, fmt.Errorf("failed to bind to any unicast udp port")
} }
if uconn4 == nil { mconn4, err := net.ListenMulticastUDP("udp4", nil, ipv4Addr)
uconn4 = &net.UDPConn{} if err != nil {
log.Printf("[ERR] mdns: Failed to bind to udp4 port: %v", err)
} }
mconn6, err := net.ListenMulticastUDP("udp6", nil, ipv6Addr)
if uconn6 == nil { if err != nil {
uconn6 = &net.UDPConn{} log.Printf("[ERR] mdns: Failed to bind to udp6 port: %v", err)
}
mconn4, err4 := net.ListenUDP("udp4", mdnsWildcardAddrIPv4)
mconn6, err6 := net.ListenUDP("udp6", mdnsWildcardAddrIPv6)
if err4 != nil && err6 != nil {
log.Printf("[ERR] mdns: Failed to bind to udp port: %v %v", err4, err6)
} }
if mconn4 == nil && mconn6 == nil { if mconn4 == nil && mconn6 == nil {
return nil, fmt.Errorf("failed to bind to any multicast udp port") return nil, fmt.Errorf("failed to bind to any multicast udp port")
} }
if mconn4 == nil {
mconn4 = &net.UDPConn{}
}
if mconn6 == nil {
mconn6 = &net.UDPConn{}
}
p1 := ipv4.NewPacketConn(mconn4)
p2 := ipv6.NewPacketConn(mconn6)
p1.SetMulticastLoopback(true)
p2.SetMulticastLoopback(true)
ifaces, err := net.Interfaces()
if err != nil {
return nil, err
}
var errCount1, errCount2 int
for _, iface := range ifaces {
if err := p1.JoinGroup(&iface, &net.UDPAddr{IP: mdnsGroupIPv4}); err != nil {
errCount1++
}
if err := p2.JoinGroup(&iface, &net.UDPAddr{IP: mdnsGroupIPv6}); err != nil {
errCount2++
}
}
if len(ifaces) == errCount1 && len(ifaces) == errCount2 {
return nil, fmt.Errorf("Failed to join multicast group on all interfaces!")
}
c := &client{ c := &client{
ipv4MulticastConn: mconn4, ipv4MulticastConn: mconn4,
ipv6MulticastConn: mconn6, ipv6MulticastConn: mconn6,
@ -251,14 +149,12 @@ func newClient() (*client, error) {
// Close is used to cleanup the client // Close is used to cleanup the client
func (c *client) Close() error { func (c *client) Close() error {
c.closeLock.Lock() if !atomic.CompareAndSwapInt32(&c.closed, 0, 1) {
defer c.closeLock.Unlock() // something else already closed it
if c.closed {
return nil return nil
} }
c.closed = true
log.Printf("[INFO] mdns: Closing client %v", *c)
close(c.closedCh) close(c.closedCh)
if c.ipv4UnicastConn != nil { if c.ipv4UnicastConn != nil {
@ -277,31 +173,25 @@ func (c *client) Close() error {
return nil return nil
} }
// setInterface is used to set the query interface, uses sytem // setInterface is used to set the query interface, uses system
// default if not provided // default if not provided
func (c *client) setInterface(iface *net.Interface, loopback bool) error { func (c *client) setInterface(iface *net.Interface) error {
p := ipv4.NewPacketConn(c.ipv4UnicastConn) p := ipv4.NewPacketConn(c.ipv4UnicastConn)
if err := p.JoinGroup(iface, &net.UDPAddr{IP: mdnsGroupIPv4}); err != nil { if err := p.SetMulticastInterface(iface); err != nil {
return err return err
} }
p2 := ipv6.NewPacketConn(c.ipv6UnicastConn) p2 := ipv6.NewPacketConn(c.ipv6UnicastConn)
if err := p2.JoinGroup(iface, &net.UDPAddr{IP: mdnsGroupIPv6}); err != nil { if err := p2.SetMulticastInterface(iface); err != nil {
return err return err
} }
p = ipv4.NewPacketConn(c.ipv4MulticastConn) p = ipv4.NewPacketConn(c.ipv4MulticastConn)
if err := p.JoinGroup(iface, &net.UDPAddr{IP: mdnsGroupIPv4}); err != nil { if err := p.SetMulticastInterface(iface); err != nil {
return err return err
} }
p2 = ipv6.NewPacketConn(c.ipv6MulticastConn) p2 = ipv6.NewPacketConn(c.ipv6MulticastConn)
if err := p2.JoinGroup(iface, &net.UDPAddr{IP: mdnsGroupIPv6}); err != nil { if err := p2.SetMulticastInterface(iface); err != nil {
return err return err
} }
if loopback {
p.SetMulticastLoopback(true)
p2.SetMulticastLoopback(true)
}
return nil return nil
} }
@ -337,10 +227,51 @@ func (c *client) query(params *QueryParam) error {
// Map the in-progress responses // Map the in-progress responses
inprogress := make(map[string]*ServiceEntry) inprogress := make(map[string]*ServiceEntry)
// Listen until we reach the timeout
finish := time.After(params.Timeout)
for { for {
select { select {
case resp := <-msgCh: case resp := <-msgCh:
inp := messageToEntry(resp, inprogress) var inp *ServiceEntry
for _, answer := range append(resp.Answer, resp.Extra...) {
// TODO(reddaly): Check that response corresponds to serviceAddr?
switch rr := answer.(type) {
case *dns.PTR:
// Create new entry for this
inp = ensureName(inprogress, rr.Ptr)
case *dns.SRV:
// Check for a target mismatch
if rr.Target != rr.Hdr.Name {
alias(inprogress, rr.Hdr.Name, rr.Target)
}
// Get the port
inp = ensureName(inprogress, rr.Hdr.Name)
inp.Host = rr.Target
inp.Port = int(rr.Port)
case *dns.TXT:
// Pull out the txt
inp = ensureName(inprogress, rr.Hdr.Name)
inp.Info = strings.Join(rr.Txt, "|")
inp.InfoFields = rr.Txt
inp.hasTXT = true
case *dns.A:
// Pull out the IP
inp = ensureName(inprogress, rr.Hdr.Name)
inp.Addr = rr.A // @Deprecated
inp.AddrV4 = rr.A
case *dns.AAAA:
// Pull out the IP
inp = ensureName(inprogress, rr.Hdr.Name)
inp.Addr = rr.AAAA // @Deprecated
inp.AddrV6 = rr.AAAA
}
}
if inp == nil { if inp == nil {
continue continue
} }
@ -353,8 +284,7 @@ func (c *client) query(params *QueryParam) error {
inp.sent = true inp.sent = true
select { select {
case params.Entries <- inp: case params.Entries <- inp:
case <-params.Context.Done(): default:
return nil
} }
} else { } else {
// Fire off a node specific query // Fire off a node specific query
@ -365,7 +295,7 @@ func (c *client) query(params *QueryParam) error {
log.Printf("[ERR] mdns: Failed to query instance %s: %v", inp.Name, err) log.Printf("[ERR] mdns: Failed to query instance %s: %v", inp.Name, err)
} }
} }
case <-params.Context.Done(): case <-finish:
return nil return nil
} }
} }
@ -378,10 +308,16 @@ func (c *client) sendQuery(q *dns.Msg) error {
return err return err
} }
if c.ipv4UnicastConn != nil { if c.ipv4UnicastConn != nil {
c.ipv4UnicastConn.WriteToUDP(buf, ipv4Addr) _, err = c.ipv4UnicastConn.WriteToUDP(buf, ipv4Addr)
if err != nil {
return err
}
} }
if c.ipv6UnicastConn != nil { if c.ipv6UnicastConn != nil {
c.ipv6UnicastConn.WriteToUDP(buf, ipv6Addr) _, err = c.ipv6UnicastConn.WriteToUDP(buf, ipv6Addr)
if err != nil {
return err
}
} }
return nil return nil
} }
@ -392,19 +328,20 @@ func (c *client) recv(l *net.UDPConn, msgCh chan *dns.Msg) {
return return
} }
buf := make([]byte, 65536) buf := make([]byte, 65536)
for { for atomic.LoadInt32(&c.closed) == 0 {
c.closeLock.Lock() n, err := l.Read(buf)
if c.closed {
c.closeLock.Unlock() if atomic.LoadInt32(&c.closed) == 1 {
return return
} }
c.closeLock.Unlock()
n, err := l.Read(buf)
if err != nil { if err != nil {
log.Printf("[ERR] mdns: Failed to read packet: %v", err)
continue continue
} }
msg := new(dns.Msg) msg := new(dns.Msg)
if err := msg.Unpack(buf[:n]); err != nil { if err := msg.Unpack(buf[:n]); err != nil {
log.Printf("[ERR] mdns: Failed to unpack packet: %v", err)
continue continue
} }
select { select {
@ -432,63 +369,3 @@ func alias(inprogress map[string]*ServiceEntry, src, dst string) {
srcEntry := ensureName(inprogress, src) srcEntry := ensureName(inprogress, src)
inprogress[dst] = srcEntry inprogress[dst] = srcEntry
} }
func messageToEntry(m *dns.Msg, inprogress map[string]*ServiceEntry) *ServiceEntry {
var inp *ServiceEntry
for _, answer := range append(m.Answer, m.Extra...) {
// TODO(reddaly): Check that response corresponds to serviceAddr?
switch rr := answer.(type) {
case *dns.PTR:
// Create new entry for this
inp = ensureName(inprogress, rr.Ptr)
if inp.complete() {
continue
}
case *dns.SRV:
// Check for a target mismatch
if rr.Target != rr.Hdr.Name {
alias(inprogress, rr.Hdr.Name, rr.Target)
}
// Get the port
inp = ensureName(inprogress, rr.Hdr.Name)
if inp.complete() {
continue
}
inp.Host = rr.Target
inp.Port = int(rr.Port)
case *dns.TXT:
// Pull out the txt
inp = ensureName(inprogress, rr.Hdr.Name)
if inp.complete() {
continue
}
inp.Info = strings.Join(rr.Txt, "|")
inp.InfoFields = rr.Txt
inp.hasTXT = true
case *dns.A:
// Pull out the IP
inp = ensureName(inprogress, rr.Hdr.Name)
if inp.complete() {
continue
}
inp.Addr = rr.A // @Deprecated
inp.AddrV4 = rr.A
case *dns.AAAA:
// Pull out the IP
inp = ensureName(inprogress, rr.Hdr.Name)
if inp.complete() {
continue
}
inp.Addr = rr.AAAA // @Deprecated
inp.AddrV6 = rr.AAAA
}
if inp != nil {
inp.TTL = int(answer.Header().Ttl)
}
}
return inp
}

View File

@ -3,38 +3,28 @@ package mdns
import ( import (
"fmt" "fmt"
"log" "log"
"math/rand"
"net" "net"
"sync" "strings"
"time" "sync/atomic"
"github.com/miekg/dns" "github.com/miekg/dns"
"golang.org/x/net/ipv4" )
"golang.org/x/net/ipv6"
const (
ipv4mdns = "224.0.0.251"
ipv6mdns = "ff02::fb"
mdnsPort = 5353
forceUnicastResponses = false
) )
var ( var (
mdnsGroupIPv4 = net.ParseIP("224.0.0.251")
mdnsGroupIPv6 = net.ParseIP("ff02::fb")
// mDNS wildcard addresses
mdnsWildcardAddrIPv4 = &net.UDPAddr{
IP: net.ParseIP("224.0.0.0"),
Port: 5353,
}
mdnsWildcardAddrIPv6 = &net.UDPAddr{
IP: net.ParseIP("ff02::"),
Port: 5353,
}
// mDNS endpoint addresses
ipv4Addr = &net.UDPAddr{ ipv4Addr = &net.UDPAddr{
IP: mdnsGroupIPv4, IP: net.ParseIP(ipv4mdns),
Port: 5353, Port: mdnsPort,
} }
ipv6Addr = &net.UDPAddr{ ipv6Addr = &net.UDPAddr{
IP: mdnsGroupIPv6, IP: net.ParseIP(ipv6mdns),
Port: 5353, Port: mdnsPort,
} }
) )
@ -48,8 +38,9 @@ type Config struct {
// is used. // is used.
Iface *net.Interface Iface *net.Interface
// Port If it is not 0, replace the port 5353 with this port number. // LogEmptyResponses indicates the server should print an informative message
Port int // when there is an mDNS query for which the server has no response.
LogEmptyResponses bool
} }
// mDNS server is used to listen for mDNS queries and respond if we // mDNS server is used to listen for mDNS queries and respond if we
@ -60,66 +51,19 @@ type Server struct {
ipv4List *net.UDPConn ipv4List *net.UDPConn
ipv6List *net.UDPConn ipv6List *net.UDPConn
shutdown bool shutdown int32
shutdownCh chan struct{} shutdownCh chan struct{}
shutdownLock sync.Mutex
wg sync.WaitGroup
} }
// NewServer is used to create a new mDNS server from a config // NewServer is used to create a new mDNS server from a config
func NewServer(config *Config) (*Server, error) { func NewServer(config *Config) (*Server, error) {
if config.Port != 0 {
mdnsWildcardAddrIPv4.Port = config.Port
mdnsWildcardAddrIPv6.Port = config.Port
ipv4Addr.Port = config.Port
ipv6Addr.Port = config.Port
}
// Create the listeners // Create the listeners
// Create wildcard connections (because :5353 can be already taken by other apps) ipv4List, _ := net.ListenMulticastUDP("udp4", config.Iface, ipv4Addr)
ipv4List, _ := net.ListenUDP("udp4", mdnsWildcardAddrIPv4) ipv6List, _ := net.ListenMulticastUDP("udp6", config.Iface, ipv6Addr)
ipv6List, _ := net.ListenUDP("udp6", mdnsWildcardAddrIPv6)
// Check if we have any listener
if ipv4List == nil && ipv6List == nil { if ipv4List == nil && ipv6List == nil {
return nil, fmt.Errorf("[ERR] mdns: Failed to bind to any udp port!") return nil, fmt.Errorf("No multicast listeners could be started")
}
if ipv4List == nil {
ipv4List = &net.UDPConn{}
}
if ipv6List == nil {
ipv6List = &net.UDPConn{}
}
// Join multicast groups to receive announcements
p1 := ipv4.NewPacketConn(ipv4List)
p2 := ipv6.NewPacketConn(ipv6List)
p1.SetMulticastLoopback(true)
p2.SetMulticastLoopback(true)
if config.Iface != nil {
if err := p1.JoinGroup(config.Iface, &net.UDPAddr{IP: mdnsGroupIPv4}); err != nil {
return nil, err
}
if err := p2.JoinGroup(config.Iface, &net.UDPAddr{IP: mdnsGroupIPv6}); err != nil {
return nil, err
}
} else {
ifaces, err := net.Interfaces()
if err != nil {
return nil, err
}
errCount1, errCount2 := 0, 0
for _, iface := range ifaces {
if err := p1.JoinGroup(&iface, &net.UDPAddr{IP: mdnsGroupIPv4}); err != nil {
errCount1++
}
if err := p2.JoinGroup(&iface, &net.UDPAddr{IP: mdnsGroupIPv6}); err != nil {
errCount2++
}
}
if len(ifaces) == errCount1 && len(ifaces) == errCount2 {
return nil, fmt.Errorf("Failed to join multicast group on all interfaces!")
}
} }
s := &Server{ s := &Server{
@ -129,27 +73,25 @@ func NewServer(config *Config) (*Server, error) {
shutdownCh: make(chan struct{}), shutdownCh: make(chan struct{}),
} }
if ipv4List != nil {
go s.recv(s.ipv4List) go s.recv(s.ipv4List)
go s.recv(s.ipv6List) }
s.wg.Add(1) if ipv6List != nil {
go s.probe() go s.recv(s.ipv6List)
}
return s, nil return s, nil
} }
// Shutdown is used to shutdown the listener // Shutdown is used to shutdown the listener
func (s *Server) Shutdown() error { func (s *Server) Shutdown() error {
s.shutdownLock.Lock() if !atomic.CompareAndSwapInt32(&s.shutdown, 0, 1) {
defer s.shutdownLock.Unlock() // something else already closed us
if s.shutdown {
return nil return nil
} }
s.shutdown = true
close(s.shutdownCh) close(s.shutdownCh)
s.unregister()
if s.ipv4List != nil { if s.ipv4List != nil {
s.ipv4List.Close() s.ipv4List.Close()
@ -157,8 +99,6 @@ func (s *Server) Shutdown() error {
if s.ipv6List != nil { if s.ipv6List != nil {
s.ipv6List.Close() s.ipv6List.Close()
} }
s.wg.Wait()
return nil return nil
} }
@ -168,14 +108,9 @@ func (s *Server) recv(c *net.UDPConn) {
return return
} }
buf := make([]byte, 65536) buf := make([]byte, 65536)
for { for atomic.LoadInt32(&s.shutdown) == 0 {
s.shutdownLock.Lock()
if s.shutdown {
s.shutdownLock.Unlock()
return
}
s.shutdownLock.Unlock()
n, from, err := c.ReadFrom(buf) n, from, err := c.ReadFrom(buf)
if err != nil { if err != nil {
continue continue
} }
@ -192,10 +127,6 @@ func (s *Server) parsePacket(packet []byte, from net.Addr) error {
log.Printf("[ERR] mdns: Failed to unpack packet: %v", err) log.Printf("[ERR] mdns: Failed to unpack packet: %v", err)
return err return err
} }
// TODO: This is a bit of a hack
// We decided to ignore some mDNS answers for the time being
// See: https://tools.ietf.org/html/rfc6762#section-7.2
msg.Truncated = false
return s.handleQuery(&msg, from) return s.handleQuery(&msg, from)
} }
@ -287,13 +218,21 @@ func (s *Server) handleQuery(query *dns.Msg, from net.Addr) error {
} }
} }
if s.config.LogEmptyResponses && len(multicastAnswer) == 0 && len(unicastAnswer) == 0 {
questions := make([]string, len(query.Question))
for i, q := range query.Question {
questions[i] = q.Name
}
log.Printf("no responses for query with questions: %s", strings.Join(questions, ", "))
}
if mresp := resp(false); mresp != nil { if mresp := resp(false); mresp != nil {
if err := s.sendResponse(mresp, from); err != nil { if err := s.sendResponse(mresp, from, false); err != nil {
return fmt.Errorf("mdns: error sending multicast response: %v", err) return fmt.Errorf("mdns: error sending multicast response: %v", err)
} }
} }
if uresp := resp(true); uresp != nil { if uresp := resp(true); uresp != nil {
if err := s.sendResponse(uresp, from); err != nil { if err := s.sendResponse(uresp, from, true); err != nil {
return fmt.Errorf("mdns: error sending unicast response: %v", err) return fmt.Errorf("mdns: error sending unicast response: %v", err)
} }
} }
@ -322,109 +261,14 @@ func (s *Server) handleQuestion(q dns.Question) (multicastRecs, unicastRecs []dn
// In the Question Section of a Multicast DNS query, the top bit of the // In the Question Section of a Multicast DNS query, the top bit of the
// qclass field is used to indicate that unicast responses are preferred // qclass field is used to indicate that unicast responses are preferred
// for this particular question. (See Section 5.4.) // for this particular question. (See Section 5.4.)
if q.Qclass&(1<<15) != 0 { if q.Qclass&(1<<15) != 0 || forceUnicastResponses {
return nil, records return nil, records
} }
return records, nil return records, nil
} }
func (s *Server) probe() {
defer s.wg.Done()
sd, ok := s.config.Zone.(*MDNSService)
if !ok {
return
}
name := fmt.Sprintf("%s.%s.%s.", sd.Instance, trimDot(sd.Service), trimDot(sd.Domain))
q := new(dns.Msg)
q.SetQuestion(name, dns.TypePTR)
q.RecursionDesired = false
srv := &dns.SRV{
Hdr: dns.RR_Header{
Name: name,
Rrtype: dns.TypeSRV,
Class: dns.ClassINET,
Ttl: defaultTTL,
},
Priority: 0,
Weight: 0,
Port: uint16(sd.Port),
Target: sd.HostName,
}
txt := &dns.TXT{
Hdr: dns.RR_Header{
Name: name,
Rrtype: dns.TypeTXT,
Class: dns.ClassINET,
Ttl: defaultTTL,
},
Txt: sd.TXT,
}
q.Ns = []dns.RR{srv, txt}
randomizer := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := 0; i < 3; i++ {
if err := s.SendMulticast(q); err != nil {
log.Println("[ERR] mdns: failed to send probe:", err.Error())
}
time.Sleep(time.Duration(randomizer.Intn(250)) * time.Millisecond)
}
resp := new(dns.Msg)
resp.MsgHdr.Response = true
// set for query
q.SetQuestion(name, dns.TypeANY)
resp.Answer = append(resp.Answer, s.config.Zone.Records(q.Question[0])...)
// reset
q.SetQuestion(name, dns.TypePTR)
// From RFC6762
// The Multicast DNS responder MUST send at least two unsolicited
// responses, one second apart. To provide increased robustness against
// packet loss, a responder MAY send up to eight unsolicited responses,
// provided that the interval between unsolicited responses increases by
// at least a factor of two with every response sent.
timeout := 1 * time.Second
timer := time.NewTimer(timeout)
for i := 0; i < 3; i++ {
if err := s.SendMulticast(resp); err != nil {
log.Println("[ERR] mdns: failed to send announcement:", err.Error())
}
select {
case <-timer.C:
timeout *= 2
timer.Reset(timeout)
case <-s.shutdownCh:
timer.Stop()
return
}
}
}
// multicastResponse us used to send a multicast response packet
func (s *Server) SendMulticast(msg *dns.Msg) error {
buf, err := msg.Pack()
if err != nil {
return err
}
if s.ipv4List != nil {
s.ipv4List.WriteToUDP(buf, ipv4Addr)
}
if s.ipv6List != nil {
s.ipv6List.WriteToUDP(buf, ipv6Addr)
}
return nil
}
// sendResponse is used to send a response packet // sendResponse is used to send a response packet
func (s *Server) sendResponse(resp *dns.Msg, from net.Addr) error { func (s *Server) sendResponse(resp *dns.Msg, from net.Addr, unicast bool) error {
// TODO(reddaly): Respect the unicast argument, and allow sending responses // TODO(reddaly): Respect the unicast argument, and allow sending responses
// over multicast. // over multicast.
buf, err := resp.Pack() buf, err := resp.Pack()
@ -442,22 +286,3 @@ func (s *Server) sendResponse(resp *dns.Msg, from net.Addr) error {
return err return err
} }
} }
func (s *Server) unregister() error {
sd, ok := s.config.Zone.(*MDNSService)
if !ok {
return nil
}
sd.TTL = 0
name := fmt.Sprintf("%s.%s.%s.", sd.Instance, trimDot(sd.Service), trimDot(sd.Domain))
q := new(dns.Msg)
q.SetQuestion(name, dns.TypeANY)
resp := new(dns.Msg)
resp.MsgHdr.Response = true
resp.Answer = append(resp.Answer, s.config.Zone.Records(q.Question[0])...)
return s.SendMulticast(resp)
}

View File

@ -30,7 +30,7 @@ type MDNSService struct {
Port int // Service Port Port int // Service Port
IPs []net.IP // IP addresses for the service's host IPs []net.IP // IP addresses for the service's host
TXT []string // Service TXT records TXT []string // Service TXT records
TTL uint32
serviceAddr string // Fully qualified service address serviceAddr string // Fully qualified service address
instanceAddr string // Fully qualified instance address instanceAddr string // Fully qualified instance address
enumAddr string // _services._dns-sd._udp.<domain> enumAddr string // _services._dns-sd._udp.<domain>
@ -95,13 +95,13 @@ func NewMDNSService(instance, service, domain, hostName string, port int, ips []
if len(ips) == 0 { if len(ips) == 0 {
var err error var err error
ips, err = net.LookupIP(trimDot(hostName)) ips, err = net.LookupIP(hostName)
if err != nil { if err != nil {
// Try appending the host domain suffix and lookup again // Try appending the host domain suffix and lookup again
// (required for Linux-based hosts) // (required for Linux-based hosts)
tmpHostName := fmt.Sprintf("%s%s", hostName, domain) tmpHostName := fmt.Sprintf("%s%s", hostName, domain)
ips, err = net.LookupIP(trimDot(tmpHostName)) ips, err = net.LookupIP(tmpHostName)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not determine host IP addresses for %s", hostName) return nil, fmt.Errorf("could not determine host IP addresses for %s", hostName)
@ -122,7 +122,6 @@ func NewMDNSService(instance, service, domain, hostName string, port int, ips []
Port: port, Port: port,
IPs: ips, IPs: ips,
TXT: txt, TXT: txt,
TTL: defaultTTL,
serviceAddr: fmt.Sprintf("%s.%s.", trimDot(service), trimDot(domain)), serviceAddr: fmt.Sprintf("%s.%s.", trimDot(service), trimDot(domain)),
instanceAddr: fmt.Sprintf("%s.%s.%s.", instance, trimDot(service), trimDot(domain)), instanceAddr: fmt.Sprintf("%s.%s.%s.", instance, trimDot(service), trimDot(domain)),
enumAddr: fmt.Sprintf("_services._dns-sd._udp.%s.", trimDot(domain)), enumAddr: fmt.Sprintf("_services._dns-sd._udp.%s.", trimDot(domain)),
@ -163,7 +162,7 @@ func (m *MDNSService) serviceEnum(q dns.Question) []dns.RR {
Name: q.Name, Name: q.Name,
Rrtype: dns.TypePTR, Rrtype: dns.TypePTR,
Class: dns.ClassINET, Class: dns.ClassINET,
Ttl: m.TTL, Ttl: defaultTTL,
}, },
Ptr: m.serviceAddr, Ptr: m.serviceAddr,
} }
@ -185,7 +184,7 @@ func (m *MDNSService) serviceRecords(q dns.Question) []dns.RR {
Name: q.Name, Name: q.Name,
Rrtype: dns.TypePTR, Rrtype: dns.TypePTR,
Class: dns.ClassINET, Class: dns.ClassINET,
Ttl: m.TTL, Ttl: defaultTTL,
}, },
Ptr: m.instanceAddr, Ptr: m.instanceAddr,
} }
@ -230,7 +229,7 @@ func (m *MDNSService) instanceRecords(q dns.Question) []dns.RR {
Name: m.HostName, Name: m.HostName,
Rrtype: dns.TypeA, Rrtype: dns.TypeA,
Class: dns.ClassINET, Class: dns.ClassINET,
Ttl: m.TTL, Ttl: defaultTTL,
}, },
A: ip4, A: ip4,
}) })
@ -255,7 +254,7 @@ func (m *MDNSService) instanceRecords(q dns.Question) []dns.RR {
Name: m.HostName, Name: m.HostName,
Rrtype: dns.TypeAAAA, Rrtype: dns.TypeAAAA,
Class: dns.ClassINET, Class: dns.ClassINET,
Ttl: m.TTL, Ttl: defaultTTL,
}, },
AAAA: ip16, AAAA: ip16,
}) })
@ -270,7 +269,7 @@ func (m *MDNSService) instanceRecords(q dns.Question) []dns.RR {
Name: q.Name, Name: q.Name,
Rrtype: dns.TypeSRV, Rrtype: dns.TypeSRV,
Class: dns.ClassINET, Class: dns.ClassINET,
Ttl: m.TTL, Ttl: defaultTTL,
}, },
Priority: 10, Priority: 10,
Weight: 1, Weight: 1,
@ -298,7 +297,7 @@ func (m *MDNSService) instanceRecords(q dns.Question) []dns.RR {
Name: q.Name, Name: q.Name,
Rrtype: dns.TypeTXT, Rrtype: dns.TypeTXT,
Class: dns.ClassINET, Class: dns.ClassINET,
Ttl: m.TTL, Ttl: defaultTTL,
}, },
Txt: m.TXT, Txt: m.TXT,
} }

View File

@ -1,70 +0,0 @@
# MDNS [![GoDoc](https://godoc.org/github.com/micro/mdns?status.svg)](https://godoc.org/github.com/micro/mdns)
MDNS is a simple mdns client/server library by Hashicorp.
We maintain a fork with updates for PRs and issues they have not merged or addressed.
## Overview
MDNS or Multicast DNS can be used to discover services on the local network without the use of an authoritative
DNS server. This enables peer-to-peer discovery. It is important to note that many
networks restrict the use of multicasting, which prevents mDNS from functioning.
Notably, multicast cannot be used in any sort of cloud, or shared infrastructure
environment. However it works well in most office, home, or private infrastructure
environments.
## Usage
Using the library is very simple, here is an example of publishing a service entry:
```go
package main
import (
"github.com/micro/mdns"
"os"
)
func main() {
// Setup our service export
host, _ := os.Hostname()
info := []string{"My awesome service"}
service, _ := mdns.NewMDNSService(host, "_foobar._tcp", "", "", 8000, nil, info)
// Create the mDNS server, defer shutdown
server, _ := mdns.NewServer(&mdns.Config{Zone: service})
defer server.Shutdown()
}
```
Doing a lookup for service providers is also very simple:
```go
package main
import (
"fmt"
"github.com/micro/mdns"
)
func main() {
// Make a channel for results and start listening
entriesCh := make(chan *mdns.ServiceEntry, 8)
go func() {
for entry := range entriesCh {
fmt.Printf("Got new entry: %v\n", entry)
}
}()
// Start the lookup
err := mdns.Lookup("_foobar._tcp", entriesCh)
if err != nil {
fmt.Println(err)
}
close(entriesCh)
}
```

View File

@ -1,84 +0,0 @@
package mdns
import "github.com/miekg/dns"
// DNSSDService is a service that complies with the DNS-SD (RFC 6762) and MDNS
// (RFC 6762) specs for local, multicast-DNS-based discovery.
//
// DNSSDService implements the Zone interface and wraps an MDNSService instance.
// To deploy an mDNS service that is compliant with DNS-SD, it's recommended to
// register only the wrapped instance with the server.
//
// Example usage:
// service := &mdns.DNSSDService{
// MDNSService: &mdns.MDNSService{
// Instance: "My Foobar Service",
// Service: "_foobar._tcp",
// Port: 8000,
// }
// }
// server, err := mdns.NewServer(&mdns.Config{Zone: service})
// if err != nil {
// log.Fatalf("Error creating server: %v", err)
// }
// defer server.Shutdown()
type DNSSDService struct {
MDNSService *MDNSService
}
// Records returns DNS records in response to a DNS question.
//
// This function returns the DNS response of the underlying MDNSService
// instance. It also returns a PTR record for a request for "
// _services._dns-sd._udp.<Domain>", as described in section 9 of RFC 6763
// ("Service Type Enumeration"), to allow browsing of the underlying MDNSService
// instance.
func (s *DNSSDService) Records(q dns.Question) []dns.RR {
var recs []dns.RR
if q.Name == "_services._dns-sd._udp."+s.MDNSService.Domain+"." {
recs = s.dnssdMetaQueryRecords(q)
}
return append(recs, s.MDNSService.Records(q)...)
}
// dnssdMetaQueryRecords returns the DNS records in response to a "meta-query"
// issued to browse for DNS-SD services, as per section 9. of RFC6763.
//
// A meta-query has a name of the form "_services._dns-sd._udp.<Domain>" where
// Domain is a fully-qualified domain, such as "local."
func (s *DNSSDService) dnssdMetaQueryRecords(q dns.Question) []dns.RR {
// Intended behavior, as described in the RFC:
// ...it may be useful for network administrators to find the list of
// advertised service types on the network, even if those Service Names
// are just opaque identifiers and not particularly informative in
// isolation.
//
// For this purpose, a special meta-query is defined. A DNS query for PTR
// records with the name "_services._dns-sd._udp.<Domain>" yields a set of
// PTR records, where the rdata of each PTR record is the two-abel
// <Service> name, plus the same domain, e.g., "_http._tcp.<Domain>".
// Including the domain in the PTR rdata allows for slightly better name
// compression in Unicast DNS responses, but only the first two labels are
// relevant for the purposes of service type enumeration. These two-label
// service types can then be used to construct subsequent Service Instance
// Enumeration PTR queries, in this <Domain> or others, to discover
// instances of that service type.
return []dns.RR{
&dns.PTR{
Hdr: dns.RR_Header{
Name: q.Name,
Rrtype: dns.TypePTR,
Class: dns.ClassINET,
Ttl: defaultTTL,
},
Ptr: s.MDNSService.serviceAddr,
},
}
}
// Announcement returns DNS records that should be broadcast during the initial
// availability of the service, as described in section 8.3 of RFC 6762.
// TODO(reddaly): Add this when Announcement is added to the mdns.Zone interface.
//func (s *DNSSDService) Announcement() []dns.RR {
// return s.MDNSService.Announcement()
//}

View File

@ -1,18 +0,0 @@
language: go
sudo: false
go:
- 1.10.x
- 1.11.x
- tip
before_install:
# don't use the miekg/dns when testing forks
- mkdir -p $GOPATH/src/github.com/miekg
- ln -s $TRAVIS_BUILD_DIR $GOPATH/src/github.com/miekg/ || true
script:
- go test -race -v -bench=. -coverprofile=coverage.txt -covermode=atomic ./...
after_success:
- bash <(curl -s https://codecov.io/bash)

1
vendor/github.com/miekg/dns/CODEOWNERS generated vendored Normal file
View File

@ -0,0 +1 @@
* @miekg @tmthrgd

View File

@ -1,57 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
digest = "1:6914c49eed986dfb8dffb33516fa129c49929d4d873f41e073c83c11c372b870"
name = "golang.org/x/crypto"
packages = [
"ed25519",
"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]
analyzer-name = "dep"
analyzer-version = 1
input-imports = [
"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-version = 1

View File

@ -1,38 +0,0 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
[[constraint]]
branch = "master"
name = "golang.org/x/crypto"
[[constraint]]
branch = "master"
name = "golang.org/x/net"
[[constraint]]
branch = "master"
name = "golang.org/x/sys"
[[constraint]]
branch = "master"
name = "golang.org/x/sync"

View File

@ -1,7 +1,3 @@
Extensions of the original work are copyright (c) 2011 Miek Gieben
As this is fork of the official Go code the same license applies:
Copyright (c) 2009 The Go Authors. All rights reserved. Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@ -30,3 +26,5 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
As this is fork of the official Go code the same license applies.
Extensions of the original work are copyright (c) 2011 Miek Gieben

View File

@ -1,7 +1,7 @@
# Makefile for releasing. # Makefile for releasing.
# #
# The release is controlled from version.go. The version found there is # 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 # used to tag the git repo, we're not building any artifacts so there is nothing
# to upload to github. # to upload to github.
# #
# * Up the version in version.go # * Up the version in version.go

View File

@ -26,8 +26,8 @@ avoiding breaking changes wherever reasonable. We support the last two versions
A not-so-up-to-date-list-that-may-be-actually-current: A not-so-up-to-date-list-that-may-be-actually-current:
* https://github.com/coredns/coredns * https://github.com/coredns/coredns
* https://cloudflare.com
* https://github.com/abh/geodns * https://github.com/abh/geodns
* https://github.com/baidu/bfe
* http://www.statdns.com/ * http://www.statdns.com/
* http://www.dnsinspect.com/ * http://www.dnsinspect.com/
* https://github.com/chuangbo/jianbing-dictionary-dns * https://github.com/chuangbo/jianbing-dictionary-dns
@ -41,11 +41,9 @@ A not-so-up-to-date-list-that-may-be-actually-current:
* https://github.com/StalkR/dns-reverse-proxy * https://github.com/StalkR/dns-reverse-proxy
* 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://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://github.com/miekg/unbound * https://github.com/miekg/unbound
* https://github.com/miekg/exdns * https://github.com/miekg/exdns
* https://dnslookup.org * https://dnslookup.org
@ -54,19 +52,28 @@ A not-so-up-to-date-list-that-may-be-actually-current:
* https://github.com/mehrdadrad/mylg * https://github.com/mehrdadrad/mylg
* https://github.com/bamarni/dockness * https://github.com/bamarni/dockness
* https://github.com/fffaraz/microdns * https://github.com/fffaraz/microdns
* http://kelda.io
* https://github.com/ipdcode/hades <https://jd.com> * https://github.com/ipdcode/hades <https://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://github.com/oif/apex * https://github.com/oif/apex
* https://github.com/jedisct1/dnscrypt-proxy * https://github.com/jedisct1/dnscrypt-proxy
* https://github.com/jedisct1/rpdns * https://github.com/jedisct1/rpdns
* https://github.com/xor-gate/sshfp * https://github.com/xor-gate/sshfp
* https://github.com/rs/dnstrace * https://github.com/rs/dnstrace
* https://blitiri.com.ar/p/dnss ([github mirror](https://github.com/albertito/dnss)) * 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
* https://github.com/folbricht/routedns
* https://domainr.com/
* https://zonedb.org/
* https://router7.org/
* https://github.com/fortio/dnsping
* https://github.com/Luzilla/dnsbl_exporter
* https://github.com/bodgit/tsig
* https://github.com/v2fly/v2ray-core (test only)
* https://kuma.io/
Send pull request if you want to be listed here. Send pull request if you want to be listed here.
@ -91,8 +98,8 @@ DNS Authors 2012-
# Building # Building
Building is done with the `go` tool. If you have setup your GOPATH correctly, the following should This library uses Go modules and uses semantic versioning. Building is done with the `go` tool, so
work: the following should work:
go get github.com/miekg/dns go get github.com/miekg/dns
go build github.com/miekg/dns go build github.com/miekg/dns
@ -124,6 +131,7 @@ Example programs can be found in the `github.com/miekg/exdns` repository.
* 2915 - NAPTR record * 2915 - NAPTR record
* 2929 - DNS IANA Considerations * 2929 - DNS IANA Considerations
* 3110 - RSASHA1 DNS keys * 3110 - RSASHA1 DNS keys
* 3123 - APL record
* 3225 - DO bit (DNSSEC OK) * 3225 - DO bit (DNSSEC OK)
* 340{1,2,3} - NAPTR record * 340{1,2,3} - NAPTR record
* 3445 - Limiting the scope of (DNS)KEY * 3445 - Limiting the scope of (DNS)KEY
@ -150,6 +158,7 @@ Example programs can be found in the `github.com/miekg/exdns` repository.
* 6844 - CAA record * 6844 - CAA record
* 6891 - EDNS0 update * 6891 - EDNS0 update
* 6895 - DNS IANA considerations * 6895 - DNS IANA considerations
* 6944 - DNSSEC DNSKEY Algorithm Status
* 6975 - Algorithm Understanding in DNSSEC * 6975 - Algorithm Understanding in DNSSEC
* 7043 - EUI48/EUI64 records * 7043 - EUI48/EUI64 records
* 7314 - DNS (EDNS) EXPIRE Option * 7314 - DNS (EDNS) EXPIRE Option
@ -161,6 +170,9 @@ Example programs can be found in the `github.com/miekg/exdns` repository.
* 7873 - Domain Name System (DNS) Cookies * 7873 - Domain Name System (DNS) Cookies
* 8080 - EdDSA for DNSSEC * 8080 - EdDSA for DNSSEC
* 8499 - DNS Terminology * 8499 - DNS Terminology
* 8659 - DNS Certification Authority Authorization (CAA) Resource Record
* 8914 - Extended DNS Errors
* 8976 - Message Digest for DNS Zones (ZONEMD RR)
## Loosely Based Upon ## Loosely Based Upon

View File

@ -6,22 +6,31 @@ type MsgAcceptFunc func(dh Header) MsgAcceptAction
// DefaultMsgAcceptFunc checks the request and will reject if: // DefaultMsgAcceptFunc checks the request and will reject if:
// //
// * isn't a request (don't respond in that case). // * isn't a request (don't respond in that case)
//
// * opcode isn't OpcodeQuery or OpcodeNotify // * opcode isn't OpcodeQuery or OpcodeNotify
//
// * Zero bit isn't zero // * Zero bit isn't zero
//
// * has more than 1 question in the question section // * has more than 1 question in the question section
//
// * has more than 1 RR in the Answer section // * has more than 1 RR in the Answer section
//
// * has more than 0 RRs in the Authority section // * has more than 0 RRs in the Authority section
//
// * has more than 2 RRs in the Additional section // * has more than 2 RRs in the Additional section
//
var DefaultMsgAcceptFunc MsgAcceptFunc = defaultMsgAcceptFunc var DefaultMsgAcceptFunc MsgAcceptFunc = defaultMsgAcceptFunc
// MsgAcceptAction represents the action to be taken. // MsgAcceptAction represents the action to be taken.
type MsgAcceptAction int type MsgAcceptAction int
// Allowed returned values from a MsgAcceptFunc.
const ( const (
MsgAccept MsgAcceptAction = iota // Accept the message MsgAccept MsgAcceptAction = iota // Accept the message
MsgReject // Reject the message with a RcodeFormatError MsgReject // Reject the message with a RcodeFormatError
MsgIgnore // Ignore the error and send nothing back. MsgIgnore // Ignore the error and send nothing back.
MsgRejectNotImplemented // Reject the message with a RcodeNotImplemented
) )
func defaultMsgAcceptFunc(dh Header) MsgAcceptAction { func defaultMsgAcceptFunc(dh Header) MsgAcceptAction {
@ -32,12 +41,9 @@ func defaultMsgAcceptFunc(dh Header) MsgAcceptAction {
// Don't allow dynamic updates, because then the sections can contain a whole bunch of RRs. // Don't allow dynamic updates, because then the sections can contain a whole bunch of RRs.
opcode := int(dh.Bits>>11) & 0xF opcode := int(dh.Bits>>11) & 0xF
if opcode != OpcodeQuery && opcode != OpcodeNotify { if opcode != OpcodeQuery && opcode != OpcodeNotify {
return MsgReject return MsgRejectNotImplemented
} }
if isZero := dh.Bits&_Z != 0; isZero {
return MsgReject
}
if dh.Qdcount != 1 { if dh.Qdcount != 1 {
return MsgReject return MsgReject
} }

211
vendor/github.com/miekg/dns/client.go generated vendored
View File

@ -3,10 +3,10 @@ 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"
@ -23,6 +23,7 @@ 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)
TsigProvider TsigProvider // An implementation of the TsigProvider interface. If defined it replaces TsigSecret and is used for all TSIG operations.
tsigRequestMAC string tsigRequestMAC string
} }
@ -34,12 +35,13 @@ type Client struct {
Dialer *net.Dialer // a net.Dialer used to set local address, timeouts and more Dialer *net.Dialer // a net.Dialer used to set local address, timeouts and more
// Timeout is a cumulative timeout for dial, write and read, defaults to 0 (disabled) - overrides DialTimeout, ReadTimeout, // Timeout is a cumulative timeout for dial, write and read, defaults to 0 (disabled) - overrides DialTimeout, ReadTimeout,
// WriteTimeout when non-zero. Can be overridden with net.Dialer.Timeout (see Client.ExchangeWithDialer and // WriteTimeout when non-zero. Can be overridden with net.Dialer.Timeout (see Client.ExchangeWithDialer and
// Client.Dialer) or context.Context.Deadline (see the deprecated ExchangeContext) // Client.Dialer) or context.Context.Deadline (see ExchangeContext)
Timeout time.Duration Timeout time.Duration
DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds, or net.Dialer.Timeout if expiring earlier - overridden by Timeout when that value is non-zero DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds, or net.Dialer.Timeout if expiring earlier - overridden by Timeout when that value is non-zero
ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
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)
TsigProvider TsigProvider // An implementation of the TsigProvider interface. If defined it replaces TsigSecret and is used for all TSIG operations.
SingleInflight bool // if true suppress multiple outstanding queries for the same Qname, Qtype and Qclass SingleInflight bool // if true suppress multiple outstanding queries for the same Qname, Qtype and Qclass
group singleflight group singleflight
} }
@ -106,7 +108,7 @@ func (c *Client) Dial(address string) (conn *Conn, err error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
conn.UDPSize = c.UDPSize
return conn, nil return conn, nil
} }
@ -125,36 +127,45 @@ func (c *Client) Dial(address string) (conn *Conn, err error) {
// To specify a local address or a timeout, the caller has to set the `Client.Dialer` // To specify a local address or a timeout, the caller has to set the `Client.Dialer`
// attribute appropriately // attribute appropriately
func (c *Client) Exchange(m *Msg, address string) (r *Msg, rtt time.Duration, err error) { func (c *Client) Exchange(m *Msg, address string) (r *Msg, rtt time.Duration, err error) {
if !c.SingleInflight { co, err := c.Dial(address)
return c.exchange(m, address)
}
t := "nop"
if t1, ok := TypeToString[m.Question[0].Qtype]; ok {
t = t1
}
cl := "nop"
if cl1, ok := ClassToString[m.Question[0].Qclass]; ok {
cl = cl1
}
r, rtt, err, shared := c.group.Do(m.Question[0].Name+t+cl, func() (*Msg, time.Duration, error) {
return c.exchange(m, address)
})
if r != nil && shared {
r = r.Copy()
}
return r, rtt, err
}
func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
var co *Conn
co, err = c.Dial(a)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
defer co.Close() defer co.Close()
return c.ExchangeWithConn(m, co)
}
// ExchangeWithConn has the same behavior as Exchange, just with a predetermined connection
// that will be used instead of creating a new one.
// Usage pattern with a *dns.Client:
// c := new(dns.Client)
// // connection management logic goes here
//
// conn := c.Dial(address)
// in, rtt, err := c.ExchangeWithConn(message, conn)
//
// This allows users of the library to implement their own connection management,
// as opposed to Exchange, which will always use new connections and incur the added overhead
// that entails when using "tcp" and especially "tcp-tls" clients.
func (c *Client) ExchangeWithConn(m *Msg, conn *Conn) (r *Msg, rtt time.Duration, err error) {
if !c.SingleInflight {
return c.exchange(m, conn)
}
q := m.Question[0]
key := fmt.Sprintf("%s:%d:%d", q.Name, q.Qtype, q.Qclass)
r, rtt, err, shared := c.group.Do(key, func() (*Msg, time.Duration, error) {
return c.exchange(m, conn)
})
if r != nil && shared {
r = r.Copy()
}
return r, rtt, err
}
func (c *Client) exchange(m *Msg, co *Conn) (r *Msg, rtt time.Duration, err error) {
opt := m.IsEdns0() opt := m.IsEdns0()
// If EDNS0 is used use that for size. // If EDNS0 is used use that for size.
@ -166,7 +177,7 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro
co.UDPSize = c.UDPSize co.UDPSize = c.UDPSize
} }
co.TsigSecret = c.TsigSecret co.TsigSecret, co.TsigProvider = c.TsigSecret, c.TsigProvider
t := time.Now() 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(t.Add(c.getTimeoutForRequest(c.writeTimeout())))
@ -175,10 +186,21 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro
} }
co.SetReadDeadline(time.Now().Add(c.getTimeoutForRequest(c.readTimeout()))) co.SetReadDeadline(time.Now().Add(c.getTimeoutForRequest(c.readTimeout())))
if _, ok := co.Conn.(net.PacketConn); ok {
for {
r, err = co.ReadMsg()
// Ignore replies with mismatched IDs because they might be
// responses to earlier queries that timed out.
if err != nil || r.Id == m.Id {
break
}
}
} else {
r, err = co.ReadMsg() r, err = co.ReadMsg()
if err == nil && r.Id != m.Id { if err == nil && r.Id != m.Id {
err = ErrId err = ErrId
} }
}
rtt = time.Since(t) rtt = time.Since(t)
return r, rtt, err return r, rtt, err
} }
@ -202,12 +224,16 @@ func (co *Conn) ReadMsg() (*Msg, error) {
return m, err return m, err
} }
if t := m.IsTsig(); t != nil { if t := m.IsTsig(); t != nil {
if co.TsigProvider != nil {
err = tsigVerifyProvider(p, co.TsigProvider, co.tsigRequestMAC, false)
} else {
if _, ok := co.TsigSecret[t.Hdr.Name]; !ok { if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
return m, ErrSecret return m, ErrSecret
} }
// Need to work on the original message p, as that was used to calculate the tsig. // Need to work on the original message p, as that was used to calculate the tsig.
err = TsigVerify(p, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false) err = TsigVerify(p, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false)
} }
}
return m, err return m, err
} }
@ -221,24 +247,21 @@ func (co *Conn) ReadMsgHeader(hdr *Header) ([]byte, error) {
err error err error
) )
switch t := co.Conn.(type) { if _, ok := co.Conn.(net.PacketConn); ok {
case *net.TCPConn, *tls.Conn:
r := t.(io.Reader)
// First two bytes specify the length of the entire message.
l, err := tcpMsgLen(r)
if err != nil {
return nil, err
}
p = make([]byte, l)
n, err = tcpRead(r, p)
default:
if co.UDPSize > MinMsgSize { if co.UDPSize > MinMsgSize {
p = make([]byte, co.UDPSize) p = make([]byte, co.UDPSize)
} else { } else {
p = make([]byte, MinMsgSize) p = make([]byte, MinMsgSize)
} }
n, err = co.Read(p) n, err = co.Read(p)
} else {
var length uint16
if err := binary.Read(co.Conn, binary.BigEndian, &length); err != nil {
return nil, err
}
p = make([]byte, length)
n, err = io.ReadFull(co.Conn, p)
} }
if err != nil { if err != nil {
@ -258,76 +281,28 @@ 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 {
return 0, io.ErrShortBuffer
}
switch t := co.Conn.(type) {
case *net.TCPConn, *tls.Conn:
r := t.(io.Reader)
l, err := tcpMsgLen(r) if _, ok := co.Conn.(net.PacketConn); ok {
if err != nil {
return 0, err
}
if l > len(p) {
return l, io.ErrShortBuffer
}
return tcpRead(r, p[:l])
}
// UDP connection // UDP connection
return co.Conn.Read(p) return co.Conn.Read(p)
} }
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 io.ReadFull(co.Conn, p[:length])
}
// WriteMsg sends a message through the connection co. // WriteMsg sends a message through the connection co.
// If the message m contains a TSIG record the transaction // If the message m contains a TSIG record the transaction
// signature is calculated. // signature is calculated.
@ -335,10 +310,14 @@ func (co *Conn) WriteMsg(m *Msg) (err error) {
var out []byte var out []byte
if t := m.IsTsig(); t != nil { if t := m.IsTsig(); t != nil {
mac := "" mac := ""
if co.TsigProvider != nil {
out, mac, err = tsigGenerateProvider(m, co.TsigProvider, co.tsigRequestMAC, false)
} else {
if _, ok := co.TsigSecret[t.Hdr.Name]; !ok { if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
return ErrSecret return ErrSecret
} }
out, mac, err = TsigGenerate(m, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false) out, mac, err = TsigGenerate(m, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false)
}
// Set for the next read, although only used in zone transfers // Set for the next read, although only used in zone transfers
co.tsigRequestMAC = mac co.tsigRequestMAC = mac
} else { } else {
@ -352,27 +331,21 @@ func (co *Conn) WriteMsg(m *Msg) (err error) {
} }
// 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) (int, error) {
switch t := co.Conn.(type) { if len(p) > MaxMsgSize {
case *net.TCPConn, *tls.Conn:
w := t.(io.Writer)
lp := len(p)
if lp < 2 {
return 0, io.ErrShortBuffer
}
if lp > MaxMsgSize {
return 0, &Error{err: "message too large"} return 0, &Error{err: "message too large"}
} }
l := make([]byte, 2, lp+2)
binary.BigEndian.PutUint16(l, uint16(lp)) if _, ok := co.Conn.(net.PacketConn); ok {
p = append(l, p...)
n, err := io.Copy(w, bytes.NewReader(p))
return int(n), err
}
return co.Conn.Write(p) return co.Conn.Write(p)
} }
msg := make([]byte, 2+len(p))
binary.BigEndian.PutUint16(msg, uint16(len(p)))
copy(msg[2:], p)
return co.Conn.Write(msg)
}
// Return the appropriate timeout for a specific request // Return the appropriate timeout for a specific request
func (c *Client) getTimeoutForRequest(timeout time.Duration) time.Duration { func (c *Client) getTimeoutForRequest(timeout time.Duration) time.Duration {
var requestTimeout time.Duration var requestTimeout time.Duration
@ -406,14 +379,14 @@ func Dial(network, address string) (conn *Conn, err error) {
func ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, err error) { func ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, err error) {
client := Client{Net: "udp"} client := Client{Net: "udp"}
r, _, err = client.ExchangeContext(ctx, m, a) r, _, err = client.ExchangeContext(ctx, m, a)
// ignorint rtt to leave the original ExchangeContext API unchanged, but // ignoring rtt to leave the original ExchangeContext API unchanged, but
// this function will go away // this function will go away
return r, err return r, err
} }
// 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.
// This function is going away, but can easily be mimicked: // Deprecated: 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)

View File

@ -68,14 +68,10 @@ 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 = make([]string, len(f)-1) c.Search = append([]string(nil), 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 i := 1; i < len(f); i++ { for _, s := range f[1:] {
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:])

View File

@ -105,7 +105,7 @@ func (dns *Msg) SetAxfr(z string) *Msg {
// SetTsig appends a TSIG RR to the message. // SetTsig appends a TSIG RR to the message.
// This is only a skeleton TSIG RR that is added as the last RR in the // This is only a skeleton TSIG RR that is added as the last RR in the
// additional section. The Tsig is calculated when the message is being send. // additional section. The TSIG is calculated when the message is being send.
func (dns *Msg) SetTsig(z, algo string, fudge uint16, timesigned int64) *Msg { func (dns *Msg) SetTsig(z, algo string, fudge uint16, timesigned int64) *Msg {
t := new(TSIG) t := new(TSIG)
t.Hdr = RR_Header{z, TypeTSIG, ClassANY, 0, 0} t.Hdr = RR_Header{z, TypeTSIG, ClassANY, 0, 0}
@ -146,10 +146,9 @@ 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 {
// EDNS0 is at the end of the additional section, start there. // RFC 6891, Section 6.1.1 allows the OPT record to appear
// We might want to change this to *only* look at the last two // anywhere in the additional record section, but it's usually at
// records. So we see TSIG and/or OPT - this a slightly bigger // the end so start there.
// 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)
@ -158,6 +157,21 @@ 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
@ -303,6 +317,12 @@ func Fqdn(s string) string {
return s + "." return s + "."
} }
// CanonicalName returns the domain name in canonical form. A name in canonical
// form is lowercase and fully qualified. See Section 6.2 in RFC 4034.
func CanonicalName(s string) string {
return strings.ToLower(Fqdn(s))
}
// Copied from the official Go code. // Copied from the official Go code.
// ReverseAddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP // ReverseAddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP
@ -329,10 +349,7 @@ func ReverseAddr(addr string) (arpa string, err error) {
// 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]
buf = append(buf, hexDigit[v&0xF]) buf = append(buf, hexDigit[v&0xF], '.', hexDigit[v>>4], '.')
buf = append(buf, '.')
buf = append(buf, hexDigit[v>>4])
buf = append(buf, '.')
} }
// Append "ip6.arpa." and return (buf already has the final .) // Append "ip6.arpa." and return (buf already has the final .)
buf = append(buf, "ip6.arpa."...) buf = append(buf, "ip6.arpa."...)
@ -350,7 +367,7 @@ 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 s, ok := ClassToString[uint16(c)]; ok {
// Only emit mnemonics when they are unambiguous, specically ANY is in both. // Only emit mnemonics when they are unambiguous, specially ANY is in both.
if _, ok := StringToType[s]; !ok { if _, ok := StringToType[s]; !ok {
return s return s
} }

34
vendor/github.com/miekg/dns/dns.go generated vendored
View File

@ -1,6 +1,9 @@
package dns package dns
import "strconv" import (
"encoding/hex"
"strconv"
)
const ( const (
year68 = 1 << 31 // For RFC1982 (Serial Arithmetic) calculations in 32 bits. year68 = 1 << 31 // For RFC1982 (Serial Arithmetic) calculations in 32 bits.
@ -54,7 +57,7 @@ type RR interface {
// parse parses an RR from zone file format. // 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. // This will only be called on a new and empty RR type with only the header populated.
parse(c *zlexer, origin, file string) *ParseError parse(c *zlexer, origin string) *ParseError
// isDuplicate returns whether the two RRs are duplicates. // isDuplicate returns whether the two RRs are duplicates.
isDuplicate(r2 RR) bool isDuplicate(r2 RR) bool
@ -105,13 +108,13 @@ func (h *RR_Header) unpack(msg []byte, off int) (int, error) {
panic("dns: internal error: unpack should never be called on RR_Header") panic("dns: internal error: unpack should never be called on RR_Header")
} }
func (h *RR_Header) parse(c *zlexer, origin, file string) *ParseError { func (h *RR_Header) parse(c *zlexer, origin string) *ParseError {
panic("dns: internal error: parse should never be called on RR_Header") 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, Len(r))
headerEnd, off, err := packRR(r, buf, 0, compressionMap{}, false) headerEnd, off, err := packRR(r, buf, 0, compressionMap{}, false)
if err != nil { if err != nil {
return err return err
@ -126,9 +129,30 @@ func (rr *RFC3597) ToRFC3597(r RR) error {
} }
_, err = rr.unpack(buf, headerEnd) _, err = rr.unpack(buf, headerEnd)
return err
}
// fromRFC3597 converts an unknown RR representation from RFC 3597 to the known RR type.
func (rr *RFC3597) fromRFC3597(r RR) error {
hdr := r.Header()
*hdr = rr.Hdr
// Can't overflow uint16 as the length of Rdata is validated in (*RFC3597).parse.
// We can only get here when rr was constructed with that method.
hdr.Rdlength = uint16(hex.DecodedLen(len(rr.Rdata)))
if noRdata(*hdr) {
// Dynamic update.
return nil
}
// rr.pack requires an extra allocation and a copy so we just decode Rdata
// manually, it's simpler anyway.
msg, err := hex.DecodeString(rr.Rdata)
if err != nil { if err != nil {
return err return err
} }
return nil _, err = r.unpack(msg, 0)
return err
} }

145
vendor/github.com/miekg/dns/dnssec.go generated vendored
View File

@ -3,15 +3,14 @@ package dns
import ( import (
"bytes" "bytes"
"crypto" "crypto"
"crypto/dsa"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic" "crypto/elliptic"
_ "crypto/md5"
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
_ "crypto/sha1" _ "crypto/sha1" // need its init function
_ "crypto/sha256" _ "crypto/sha256" // need its init function
_ "crypto/sha512" _ "crypto/sha512" // need its init function
"encoding/asn1" "encoding/asn1"
"encoding/binary" "encoding/binary"
"encoding/hex" "encoding/hex"
@ -19,8 +18,6 @@ import (
"sort" "sort"
"strings" "strings"
"time" "time"
"golang.org/x/crypto/ed25519"
) )
// DNSSEC encryption algorithm codes. // DNSSEC encryption algorithm codes.
@ -141,8 +138,8 @@ func (k *DNSKEY) KeyTag() uint16 {
switch k.Algorithm { switch k.Algorithm {
case RSAMD5: case RSAMD5:
// Look at the bottom two bytes of the modules, which the last // Look at the bottom two bytes of the modules, which the last
// item in the pubkey. We could do this faster by looking directly // item in the pubkey.
// at the base64 values. But I'm lazy. // This algorithm has been deprecated, but keep this key-tag calculation.
modulus, _ := fromBase64([]byte(k.PublicKey)) modulus, _ := fromBase64([]byte(k.PublicKey))
if len(modulus) > 1 { if len(modulus) > 1 {
x := binary.BigEndian.Uint16(modulus[len(modulus)-2:]) x := binary.BigEndian.Uint16(modulus[len(modulus)-2:])
@ -200,7 +197,7 @@ func (k *DNSKEY) ToDS(h uint8) *DS {
wire = wire[:n] wire = wire[:n]
owner := make([]byte, 255) owner := make([]byte, 255)
off, err1 := PackDomainName(strings.ToLower(k.Hdr.Name), owner, 0, nil, false) off, err1 := PackDomainName(CanonicalName(k.Hdr.Name), owner, 0, nil, false)
if err1 != nil { if err1 != nil {
return nil return nil
} }
@ -285,7 +282,7 @@ func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error {
sigwire.Inception = rr.Inception sigwire.Inception = rr.Inception
sigwire.KeyTag = rr.KeyTag sigwire.KeyTag = rr.KeyTag
// For signing, lowercase this name // For signing, lowercase this name
sigwire.SignerName = strings.ToLower(rr.SignerName) sigwire.SignerName = CanonicalName(rr.SignerName)
// Create the desired binary blob // Create the desired binary blob
signdata := make([]byte, DefaultMsgSize) signdata := make([]byte, DefaultMsgSize)
@ -318,6 +315,10 @@ func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error {
} }
rr.Signature = toBase64(signature) rr.Signature = toBase64(signature)
return nil
case RSAMD5, DSA, DSANSEC3SHA1:
// See RFC 6944.
return ErrAlg
default: default:
h := hash.New() h := hash.New()
h.Write(signdata) h.Write(signdata)
@ -329,10 +330,9 @@ func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error {
} }
rr.Signature = toBase64(signature) rr.Signature = toBase64(signature)
}
return nil return nil
} }
}
func sign(k crypto.Signer, hashed []byte, hash crypto.Hash, alg uint8) ([]byte, error) { func sign(k crypto.Signer, hashed []byte, hash crypto.Hash, alg uint8) ([]byte, error) {
signature, err := k.Sign(rand.Reader, hashed, hash) signature, err := k.Sign(rand.Reader, hashed, hash)
@ -343,7 +343,6 @@ func sign(k crypto.Signer, hashed []byte, hash crypto.Hash, alg uint8) ([]byte,
switch alg { switch alg {
case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512: case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512:
return signature, nil return signature, nil
case ECDSAP256SHA256, ECDSAP384SHA384: case ECDSAP256SHA256, ECDSAP384SHA384:
ecdsaSignature := &struct { ecdsaSignature := &struct {
R, S *big.Int R, S *big.Int
@ -363,25 +362,18 @@ func sign(k crypto.Signer, hashed []byte, hash crypto.Hash, alg uint8) ([]byte,
signature := intToBytes(ecdsaSignature.R, intlen) signature := intToBytes(ecdsaSignature.R, intlen)
signature = append(signature, intToBytes(ecdsaSignature.S, intlen)...) signature = append(signature, intToBytes(ecdsaSignature.S, intlen)...)
return signature, nil return signature, nil
// There is no defined interface for what a DSA backed crypto.Signer returns
case DSA, DSANSEC3SHA1:
// t := divRoundUp(divRoundUp(p.PublicKey.Y.BitLen(), 8)-64, 8)
// signature := []byte{byte(t)}
// signature = append(signature, intToBytes(r1, 20)...)
// signature = append(signature, intToBytes(s1, 20)...)
// rr.Signature = signature
case ED25519: case ED25519:
return signature, nil return signature, nil
} default:
return nil, ErrAlg return nil, ErrAlg
} }
}
// Verify validates an RRSet with the signature and key. This is only the // Verify validates an RRSet with the signature and key. This is only the
// cryptographic test, the signature validity period must be checked separately. // cryptographic test, the signature validity period must be checked separately.
// This function copies the rdata of some RRs (to lowercase domain names) for the validation to work. // This function copies the rdata of some RRs (to lowercase domain names) for the validation to work.
// It also checks that the Zone Key bit (RFC 4034 2.1.1) is set on the DNSKEY
// and that the Protocol field is set to 3 (RFC 4034 2.1.2).
func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error { func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
// First the easy checks // First the easy checks
if !IsRRset(rrset) { if !IsRRset(rrset) {
@ -402,6 +394,12 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
if k.Protocol != 3 { if k.Protocol != 3 {
return ErrKey return ErrKey
} }
// RFC 4034 2.1.1 If bit 7 has value 0, then the DNSKEY record holds some
// other type of DNS public key and MUST NOT be used to verify RRSIGs that
// cover RRsets.
if k.Flags&ZONE == 0 {
return ErrKey
}
// 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
@ -420,7 +418,7 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
sigwire.Expiration = rr.Expiration sigwire.Expiration = rr.Expiration
sigwire.Inception = rr.Inception sigwire.Inception = rr.Inception
sigwire.KeyTag = rr.KeyTag sigwire.KeyTag = rr.KeyTag
sigwire.SignerName = strings.ToLower(rr.SignerName) sigwire.SignerName = CanonicalName(rr.SignerName)
// Create the desired binary blob // Create the desired binary blob
signeddata := make([]byte, DefaultMsgSize) signeddata := make([]byte, DefaultMsgSize)
n, err := packSigWire(sigwire, signeddata) n, err := packSigWire(sigwire, signeddata)
@ -445,7 +443,7 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
} }
switch rr.Algorithm { switch rr.Algorithm {
case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512, RSAMD5: case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512:
// TODO(mg): this can be done quicker, ie. cache the pubkey data somewhere?? // TODO(mg): this can be done quicker, ie. cache the pubkey data somewhere??
pubkey := k.publicKeyRSA() // Get the key pubkey := k.publicKeyRSA() // Get the key
if pubkey == nil { if pubkey == nil {
@ -509,7 +507,7 @@ func (rr *RRSIG) ValidityPeriod(t time.Time) bool {
return ti <= utc && utc <= te return ti <= utc && utc <= te
} }
// Return the signatures base64 encodedig sigdata as a byte slice. // Return the signatures base64 encoding sigdata as a byte slice.
func (rr *RRSIG) sigBuf() []byte { func (rr *RRSIG) sigBuf() []byte {
sigbuf, err := fromBase64([]byte(rr.Signature)) sigbuf, err := fromBase64([]byte(rr.Signature))
if err != nil { if err != nil {
@ -556,19 +554,18 @@ func (k *DNSKEY) publicKeyRSA() *rsa.PublicKey {
pubkey := new(rsa.PublicKey) pubkey := new(rsa.PublicKey)
var expo uint64 var expo uint64
for i := 0; i < int(explen); i++ { // The exponent of length explen is between keyoff and modoff.
for _, v := range keybuf[keyoff:modoff] {
expo <<= 8 expo <<= 8
expo |= uint64(keybuf[keyoff+i]) expo |= uint64(v)
} }
if expo > 1<<31-1 { if expo > 1<<31-1 {
// Larger exponent than supported by the crypto package. // Larger exponent than supported by the crypto package.
return nil return nil
} }
pubkey.E = int(expo) pubkey.E = int(expo)
pubkey.N = new(big.Int).SetBytes(keybuf[modoff:])
pubkey.N = big.NewInt(0)
pubkey.N.SetBytes(keybuf[modoff:])
return pubkey return pubkey
} }
@ -593,34 +590,8 @@ func (k *DNSKEY) publicKeyECDSA() *ecdsa.PublicKey {
return nil return nil
} }
} }
pubkey.X = big.NewInt(0) pubkey.X = new(big.Int).SetBytes(keybuf[:len(keybuf)/2])
pubkey.X.SetBytes(keybuf[:len(keybuf)/2]) pubkey.Y = new(big.Int).SetBytes(keybuf[len(keybuf)/2:])
pubkey.Y = big.NewInt(0)
pubkey.Y.SetBytes(keybuf[len(keybuf)/2:])
return pubkey
}
func (k *DNSKEY) publicKeyDSA() *dsa.PublicKey {
keybuf, err := fromBase64([]byte(k.PublicKey))
if err != nil {
return nil
}
if len(keybuf) < 22 {
return nil
}
t, keybuf := int(keybuf[0]), keybuf[1:]
size := 64 + t*8
q, keybuf := keybuf[:20], keybuf[20:]
if len(keybuf) != 3*size {
return nil
}
p, keybuf := keybuf[:size], keybuf[size:]
g, y := keybuf[:size], keybuf[size:]
pubkey := new(dsa.PublicKey)
pubkey.Parameters.Q = big.NewInt(0).SetBytes(q)
pubkey.Parameters.P = big.NewInt(0).SetBytes(p)
pubkey.Parameters.G = big.NewInt(0).SetBytes(g)
pubkey.Y = big.NewInt(0).SetBytes(y)
return pubkey return pubkey
} }
@ -659,7 +630,7 @@ func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) {
h.Name = "*." + strings.Join(labels[len(labels)-int(s.Labels):], ".") + "." h.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) h.Name = CanonicalName(h.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,
@ -672,49 +643,49 @@ func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) {
// conversion. // conversion.
switch x := r1.(type) { switch x := r1.(type) {
case *NS: case *NS:
x.Ns = strings.ToLower(x.Ns) x.Ns = CanonicalName(x.Ns)
case *MD: case *MD:
x.Md = strings.ToLower(x.Md) x.Md = CanonicalName(x.Md)
case *MF: case *MF:
x.Mf = strings.ToLower(x.Mf) x.Mf = CanonicalName(x.Mf)
case *CNAME: case *CNAME:
x.Target = strings.ToLower(x.Target) x.Target = CanonicalName(x.Target)
case *SOA: case *SOA:
x.Ns = strings.ToLower(x.Ns) x.Ns = CanonicalName(x.Ns)
x.Mbox = strings.ToLower(x.Mbox) x.Mbox = CanonicalName(x.Mbox)
case *MB: case *MB:
x.Mb = strings.ToLower(x.Mb) x.Mb = CanonicalName(x.Mb)
case *MG: case *MG:
x.Mg = strings.ToLower(x.Mg) x.Mg = CanonicalName(x.Mg)
case *MR: case *MR:
x.Mr = strings.ToLower(x.Mr) x.Mr = CanonicalName(x.Mr)
case *PTR: case *PTR:
x.Ptr = strings.ToLower(x.Ptr) x.Ptr = CanonicalName(x.Ptr)
case *MINFO: case *MINFO:
x.Rmail = strings.ToLower(x.Rmail) x.Rmail = CanonicalName(x.Rmail)
x.Email = strings.ToLower(x.Email) x.Email = CanonicalName(x.Email)
case *MX: case *MX:
x.Mx = strings.ToLower(x.Mx) x.Mx = CanonicalName(x.Mx)
case *RP: case *RP:
x.Mbox = strings.ToLower(x.Mbox) x.Mbox = CanonicalName(x.Mbox)
x.Txt = strings.ToLower(x.Txt) x.Txt = CanonicalName(x.Txt)
case *AFSDB: case *AFSDB:
x.Hostname = strings.ToLower(x.Hostname) x.Hostname = CanonicalName(x.Hostname)
case *RT: case *RT:
x.Host = strings.ToLower(x.Host) x.Host = CanonicalName(x.Host)
case *SIG: case *SIG:
x.SignerName = strings.ToLower(x.SignerName) x.SignerName = CanonicalName(x.SignerName)
case *PX: case *PX:
x.Map822 = strings.ToLower(x.Map822) x.Map822 = CanonicalName(x.Map822)
x.Mapx400 = strings.ToLower(x.Mapx400) x.Mapx400 = CanonicalName(x.Mapx400)
case *NAPTR: case *NAPTR:
x.Replacement = strings.ToLower(x.Replacement) x.Replacement = CanonicalName(x.Replacement)
case *KX: case *KX:
x.Exchanger = strings.ToLower(x.Exchanger) x.Exchanger = CanonicalName(x.Exchanger)
case *SRV: case *SRV:
x.Target = strings.ToLower(x.Target) x.Target = CanonicalName(x.Target)
case *DNAME: case *DNAME:
x.Target = strings.ToLower(x.Target) x.Target = CanonicalName(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, Len(r1)+1) // +1 to be safe(r)

View File

@ -2,14 +2,12 @@ package dns
import ( import (
"crypto" "crypto"
"crypto/dsa"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic" "crypto/elliptic"
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
"math/big" "math/big"
"golang.org/x/crypto/ed25519"
) )
// Generate generates a DNSKEY of the given bit size. // Generate generates a DNSKEY of the given bit size.
@ -20,11 +18,7 @@ import (
// bits should be set to the size of the algorithm. // bits should be set to the size of the algorithm.
func (k *DNSKEY) Generate(bits int) (crypto.PrivateKey, error) { func (k *DNSKEY) Generate(bits int) (crypto.PrivateKey, error) {
switch k.Algorithm { switch k.Algorithm {
case DSA, DSANSEC3SHA1: case RSASHA1, RSASHA256, RSASHA1NSEC3SHA1:
if bits != 1024 {
return nil, ErrKeySize
}
case RSAMD5, RSASHA1, RSASHA256, RSASHA1NSEC3SHA1:
if bits < 512 || bits > 4096 { if bits < 512 || bits > 4096 {
return nil, ErrKeySize return nil, ErrKeySize
} }
@ -44,23 +38,12 @@ func (k *DNSKEY) Generate(bits int) (crypto.PrivateKey, error) {
if bits != 256 { if bits != 256 {
return nil, ErrKeySize return nil, ErrKeySize
} }
default:
return nil, ErrAlg
} }
switch k.Algorithm { switch k.Algorithm {
case DSA, DSANSEC3SHA1: case RSASHA1, RSASHA256, RSASHA512, RSASHA1NSEC3SHA1:
params := new(dsa.Parameters)
if err := dsa.GenerateParameters(params, rand.Reader, dsa.L1024N160); err != nil {
return nil, err
}
priv := new(dsa.PrivateKey)
priv.PublicKey.Parameters = *params
err := dsa.GenerateKey(priv, rand.Reader)
if err != nil {
return nil, err
}
k.setPublicKeyDSA(params.Q, params.P, params.G, priv.PublicKey.Y)
return priv, nil
case RSAMD5, RSASHA1, RSASHA256, RSASHA512, RSASHA1NSEC3SHA1:
priv, err := rsa.GenerateKey(rand.Reader, bits) priv, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil { if err != nil {
return nil, err return nil, err
@ -120,16 +103,6 @@ func (k *DNSKEY) setPublicKeyECDSA(_X, _Y *big.Int) bool {
return true return true
} }
// Set the public key for DSA
func (k *DNSKEY) setPublicKeyDSA(_Q, _P, _G, _Y *big.Int) bool {
if _Q == nil || _P == nil || _G == nil || _Y == nil {
return false
}
buf := dsaToBuf(_Q, _P, _G, _Y)
k.PublicKey = toBase64(buf)
return true
}
// Set the public key for Ed25519 // Set the public key for Ed25519
func (k *DNSKEY) setPublicKeyED25519(_K ed25519.PublicKey) bool { func (k *DNSKEY) setPublicKeyED25519(_K ed25519.PublicKey) bool {
if _K == nil { if _K == nil {
@ -164,15 +137,3 @@ func curveToBuf(_X, _Y *big.Int, intlen int) []byte {
buf = append(buf, intToBytes(_Y, intlen)...) buf = append(buf, intToBytes(_Y, intlen)...)
return buf return buf
} }
// Set the public key for X and Y for Curve. The two
// values are just concatenated.
func dsaToBuf(_Q, _P, _G, _Y *big.Int) []byte {
t := divRoundUp(divRoundUp(_G.BitLen(), 8)-64, 8)
buf := []byte{byte(t)}
buf = append(buf, intToBytes(_Q, 20)...)
buf = append(buf, intToBytes(_P, 64+t*8)...)
buf = append(buf, intToBytes(_G, 64+t*8)...)
buf = append(buf, intToBytes(_Y, 64+t*8)...)
return buf
}

View File

@ -3,15 +3,13 @@ package dns
import ( import (
"bufio" "bufio"
"crypto" "crypto"
"crypto/dsa"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa" "crypto/rsa"
"io" "io"
"math/big" "math/big"
"strconv" "strconv"
"strings" "strings"
"golang.org/x/crypto/ed25519"
) )
// NewPrivateKey returns a PrivateKey by parsing the string s. // NewPrivateKey returns a PrivateKey by parsing the string s.
@ -44,26 +42,7 @@ func (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (crypto.PrivateKey, er
return nil, ErrPrivKey return nil, ErrPrivKey
} }
switch uint8(algo) { switch uint8(algo) {
case DSA: case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512:
priv, err := readPrivateKeyDSA(m)
if err != nil {
return nil, err
}
pub := k.publicKeyDSA()
if pub == nil {
return nil, ErrKey
}
priv.PublicKey = *pub
return priv, nil
case RSAMD5:
fallthrough
case RSASHA1:
fallthrough
case RSASHA1NSEC3SHA1:
fallthrough
case RSASHA256:
fallthrough
case RSASHA512:
priv, err := readPrivateKeyRSA(m) priv, err := readPrivateKeyRSA(m)
if err != nil { if err != nil {
return nil, err return nil, err
@ -74,11 +53,7 @@ func (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (crypto.PrivateKey, er
} }
priv.PublicKey = *pub priv.PublicKey = *pub
return priv, nil return priv, nil
case ECCGOST: case ECDSAP256SHA256, ECDSAP384SHA384:
return nil, ErrPrivKey
case ECDSAP256SHA256:
fallthrough
case ECDSAP384SHA384:
priv, err := readPrivateKeyECDSA(m) priv, err := readPrivateKeyECDSA(m)
if err != nil { if err != nil {
return nil, err return nil, err
@ -92,7 +67,7 @@ func (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (crypto.PrivateKey, er
case ED25519: case ED25519:
return readPrivateKeyED25519(m) return readPrivateKeyED25519(m)
default: default:
return nil, ErrPrivKey return nil, ErrAlg
} }
} }
@ -109,21 +84,16 @@ func readPrivateKeyRSA(m map[string]string) (*rsa.PrivateKey, error) {
} }
switch k { switch k {
case "modulus": case "modulus":
p.PublicKey.N = big.NewInt(0) p.PublicKey.N = new(big.Int).SetBytes(v1)
p.PublicKey.N.SetBytes(v1)
case "publicexponent": case "publicexponent":
i := big.NewInt(0) i := new(big.Int).SetBytes(v1)
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 = big.NewInt(0) p.D = new(big.Int).SetBytes(v1)
p.D.SetBytes(v1)
case "prime1": case "prime1":
p.Primes[0] = big.NewInt(0) p.Primes[0] = new(big.Int).SetBytes(v1)
p.Primes[0].SetBytes(v1)
case "prime2": case "prime2":
p.Primes[1] = big.NewInt(0) p.Primes[1] = new(big.Int).SetBytes(v1)
p.Primes[1].SetBytes(v1)
} }
case "exponent1", "exponent2", "coefficient": case "exponent1", "exponent2", "coefficient":
// not used in Go (yet) // not used in Go (yet)
@ -134,27 +104,9 @@ func readPrivateKeyRSA(m map[string]string) (*rsa.PrivateKey, error) {
return p, nil return p, nil
} }
func readPrivateKeyDSA(m map[string]string) (*dsa.PrivateKey, error) {
p := new(dsa.PrivateKey)
p.X = big.NewInt(0)
for k, v := range m {
switch k {
case "private_value(x)":
v1, err := fromBase64([]byte(v))
if err != nil {
return nil, err
}
p.X.SetBytes(v1)
case "created", "publish", "activate":
/* not used in Go (yet) */
}
}
return p, nil
}
func readPrivateKeyECDSA(m map[string]string) (*ecdsa.PrivateKey, error) { func readPrivateKeyECDSA(m map[string]string) (*ecdsa.PrivateKey, error) {
p := new(ecdsa.PrivateKey) p := new(ecdsa.PrivateKey)
p.D = big.NewInt(0) p.D = new(big.Int)
// 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 {
@ -322,6 +274,11 @@ func (kl *klexer) Next() (lex, bool) {
commt = false commt = false
} }
if kl.key && str.Len() == 0 {
// ignore empty lines
break
}
kl.key = true kl.key = true
l.value = zValue l.value = zValue

View File

@ -2,21 +2,21 @@ package dns
import ( import (
"crypto" "crypto"
"crypto/dsa"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa" "crypto/rsa"
"math/big" "math/big"
"strconv" "strconv"
"golang.org/x/crypto/ed25519"
) )
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.
// It supports rsa.PrivateKey, ecdsa.PrivateKey and dsa.PrivateKey // It supports *rsa.PrivateKey, *ecdsa.PrivateKey and ed25519.PrivateKey.
func (r *DNSKEY) PrivateKeyString(p crypto.PrivateKey) string { func (r *DNSKEY) PrivateKeyString(p crypto.PrivateKey) string {
algorithm := strconv.Itoa(int(r.Algorithm)) algorithm := strconv.Itoa(int(r.Algorithm))
algorithm += " (" + AlgorithmToString[r.Algorithm] + ")" algorithm += " (" + AlgorithmToString[r.Algorithm] + ")"
@ -31,12 +31,11 @@ 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
one := big.NewInt(1) p1 := new(big.Int).Sub(p.Primes[0], bigIntOne)
p1 := big.NewInt(0).Sub(p.Primes[0], one) q1 := new(big.Int).Sub(p.Primes[1], bigIntOne)
q1 := big.NewInt(0).Sub(p.Primes[1], one) exp1 := new(big.Int).Mod(p.D, p1)
exp1 := big.NewInt(0).Mod(p.D, p1) exp2 := new(big.Int).Mod(p.D, q1)
exp2 := big.NewInt(0).Mod(p.D, q1) coeff := new(big.Int).ModInverse(p.Primes[1], p.Primes[0])
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())
@ -66,21 +65,6 @@ func (r *DNSKEY) PrivateKeyString(p crypto.PrivateKey) string {
"Algorithm: " + algorithm + "\n" + "Algorithm: " + algorithm + "\n" +
"PrivateKey: " + private + "\n" "PrivateKey: " + private + "\n"
case *dsa.PrivateKey:
T := divRoundUp(divRoundUp(p.PublicKey.Parameters.G.BitLen(), 8)-64, 8)
prime := toBase64(intToBytes(p.PublicKey.Parameters.P, 64+T*8))
subprime := toBase64(intToBytes(p.PublicKey.Parameters.Q, 20))
base := toBase64(intToBytes(p.PublicKey.Parameters.G, 64+T*8))
priv := toBase64(intToBytes(p.X, 20))
pub := toBase64(intToBytes(p.PublicKey.Y, 64+T*8))
return format +
"Algorithm: " + algorithm + "\n" +
"Prime(p): " + prime + "\n" +
"Subprime(q): " + subprime + "\n" +
"Base(g): " + base + "\n" +
"Private_value(x): " + priv + "\n" +
"Public_value(y): " + pub + "\n"
case ed25519.PrivateKey: case ed25519.PrivateKey:
private := toBase64(p.Seed()) private := toBase64(p.Seed())
return format + return format +

45
vendor/github.com/miekg/dns/doc.go generated vendored
View File

@ -83,7 +83,7 @@ with:
in, err := dns.Exchange(m1, "127.0.0.1:53") in, err := dns.Exchange(m1, "127.0.0.1:53")
When this functions returns you will get dns message. A dns message consists When this functions returns you will get DNS message. A DNS message consists
out of four sections. out of four sections.
The question section: in.Question, the answer section: in.Answer, The question section: in.Question, the answer section: in.Answer,
the authority section: in.Ns and the additional section: in.Extra. the authority section: in.Ns and the additional section: in.Extra.
@ -159,7 +159,7 @@ shows the options you have and what functions to call.
TRANSACTION SIGNATURE TRANSACTION SIGNATURE
An TSIG or transaction signature adds a HMAC TSIG record to each message sent. An TSIG or transaction signature adds a HMAC TSIG record to each message sent.
The supported algorithms include: HmacMD5, HmacSHA1, HmacSHA256 and HmacSHA512. The supported algorithms include: HmacSHA1, HmacSHA256 and HmacSHA512.
Basic use pattern when querying with a TSIG name "axfr." (note that these key names Basic use pattern when querying with a TSIG name "axfr." (note that these key names
must be fully qualified - as they are domain names) and the base64 secret must be fully qualified - as they are domain names) and the base64 secret
@ -174,7 +174,7 @@ changes to the RRset after calling SetTsig() the signature will be incorrect.
c.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="} c.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
m := new(dns.Msg) m := new(dns.Msg)
m.SetQuestion("miek.nl.", dns.TypeMX) m.SetQuestion("miek.nl.", dns.TypeMX)
m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix()) m.SetTsig("axfr.", dns.HmacSHA256, 300, time.Now().Unix())
... ...
// When sending the TSIG RR is calculated and filled in before sending // When sending the TSIG RR is calculated and filled in before sending
@ -187,13 +187,37 @@ request an AXFR for miek.nl. with TSIG key named "axfr." and secret
m := new(dns.Msg) m := new(dns.Msg)
t.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="} t.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
m.SetAxfr("miek.nl.") m.SetAxfr("miek.nl.")
m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix()) m.SetTsig("axfr.", dns.HmacSHA256, 300, time.Now().Unix())
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. If something is not correct an error is returned. is checked with TSIG. If something is not correct an error is returned.
A custom TSIG implementation can be used. This requires additional code to
perform any session establishment and signature generation/verification. The
client must be configured with an implementation of the TsigProvider interface:
type Provider struct{}
func (*Provider) Generate(msg []byte, tsig *dns.TSIG) ([]byte, error) {
// Use tsig.Hdr.Name and tsig.Algorithm in your code to
// generate the MAC using msg as the payload.
}
func (*Provider) Verify(msg []byte, tsig *dns.TSIG) error {
// Use tsig.Hdr.Name and tsig.Algorithm in your code to verify
// that msg matches the value in tsig.MAC.
}
c := new(dns.Client)
c.TsigProvider = new(Provider)
m := new(dns.Msg)
m.SetQuestion("miek.nl.", dns.TypeMX)
m.SetTsig(keyname, dns.HmacSHA256, 300, time.Now().Unix())
...
// TSIG RR is calculated by calling your Generate method
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.
server := &dns.Server{Addr: ":53", Net: "udp"} server := &dns.Server{Addr: ":53", Net: "udp"}
@ -207,9 +231,9 @@ Basic use pattern validating and replying to a message that has TSIG set.
if r.IsTsig() != nil { if r.IsTsig() != nil {
if w.TsigStatus() == nil { if w.TsigStatus() == nil {
// *Msg r has an TSIG record and it was validated // *Msg r has an TSIG record and it was validated
m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix()) m.SetTsig("axfr.", dns.HmacSHA256, 300, time.Now().Unix())
} else { } else {
// *Msg r has an TSIG records and it was not valided // *Msg r has an TSIG records and it was not validated
} }
} }
w.WriteMsg(m) w.WriteMsg(m)
@ -221,7 +245,7 @@ RFC 6895 sets aside a range of type codes for private use. This range is 65,280
- 65,534 (0xFF00 - 0xFFFE). When experimenting with new Resource Records these - 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 https://miek.nl/2014/september/21/idn-and-private-rr-in-go-dns/ for more
information. information.
EDNS0 EDNS0
@ -238,9 +262,8 @@ Basic use pattern for creating an (empty) OPT RR:
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) interfaces.
Currently only a few have been standardized: EDNS0_NSID (RFC 5001) and Currently only a few have been standardized: EDNS0_NSID (RFC 5001) and
EDNS0_SUBNET (draft-vandergaast-edns-client-subnet-02). Note that these options EDNS0_SUBNET (RFC 7871). Note that these options may be combined in an OPT RR.
may be combined in an OPT RR. Basic use pattern for a server to check if (and Basic use pattern for a server to check if (and which) options are set:
which) options are set:
// o is a dns.OPT // o is a dns.OPT
for _, s := range o.Option { for _, s := range o.Option {
@ -261,7 +284,7 @@ From RFC 2931:
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 secret approach in TSIG. Supported algorithms: DSA, ECDSAP256SHA256, the shared secret approach in TSIG. Supported algorithms: ECDSAP256SHA256,
ECDSAP384SHA384, RSASHA1, RSASHA256 and RSASHA512. 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.

View File

@ -3,9 +3,8 @@ package dns
//go:generate go run duplicate_generate.go //go:generate go run duplicate_generate.go
// IsDuplicate checks of r1 and r2 are duplicates of each other, excluding the TTL. // 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 // So this means the header data is equal *and* the RDATA is the same. Returns true
// is so, otherwise false. // if so, otherwise false. It's a protocol violation to have identical RRs in a message.
// It's is a protocol violation to have identical RRs in a message.
func IsDuplicate(r1, r2 RR) bool { func IsDuplicate(r1, r2 RR) bool {
// Check whether the record header is identical. // Check whether the record header is identical.
if !r1.Header().isDuplicate(r2.Header()) { if !r1.Header().isDuplicate(r2.Header()) {
@ -27,12 +26,12 @@ func (r1 *RR_Header) isDuplicate(_r2 RR) bool {
if r1.Rrtype != r2.Rrtype { if r1.Rrtype != r2.Rrtype {
return false return false
} }
if !isDulicateName(r1.Name, r2.Name) { if !isDuplicateName(r1.Name, r2.Name) {
return false return false
} }
// ignore TTL // ignore TTL
return true return true
} }
// isDulicateName checks if the domain names s1 and s2 are equal. // isDuplicateName checks if the domain names s1 and s2 are equal.
func isDulicateName(s1, s2 string) bool { return equal(s1, s2) } func isDuplicateName(s1, s2 string) bool { return equal(s1, s2) }

235
vendor/github.com/miekg/dns/edns.go generated vendored
View File

@ -22,11 +22,47 @@ const (
EDNS0COOKIE = 0xa // EDNS0 Cookie EDNS0COOKIE = 0xa // EDNS0 Cookie
EDNS0TCPKEEPALIVE = 0xb // EDNS0 tcp keep alive (See RFC 7828) EDNS0TCPKEEPALIVE = 0xb // EDNS0 tcp keep alive (See RFC 7828)
EDNS0PADDING = 0xc // EDNS0 padding (See RFC 7830) EDNS0PADDING = 0xc // EDNS0 padding (See RFC 7830)
EDNS0EDE = 0xf // EDNS0 extended DNS errors (See RFC 8914)
EDNS0LOCALSTART = 0xFDE9 // Beginning of range reserved for local/experimental use (See RFC 6891) EDNS0LOCALSTART = 0xFDE9 // Beginning of range reserved for local/experimental use (See RFC 6891)
EDNS0LOCALEND = 0xFFFE // End of range reserved for local/experimental use (See RFC 6891) EDNS0LOCALEND = 0xFFFE // End of range reserved for local/experimental use (See RFC 6891)
_DO = 1 << 15 // DNSSEC OK _DO = 1 << 15 // DNSSEC OK
) )
// makeDataOpt is used to unpack the EDNS0 option(s) from a message.
func makeDataOpt(code uint16) EDNS0 {
// All the EDNS0.* constants above need to be in this switch.
switch code {
case EDNS0LLQ:
return new(EDNS0_LLQ)
case EDNS0UL:
return new(EDNS0_UL)
case EDNS0NSID:
return new(EDNS0_NSID)
case EDNS0DAU:
return new(EDNS0_DAU)
case EDNS0DHU:
return new(EDNS0_DHU)
case EDNS0N3U:
return new(EDNS0_N3U)
case EDNS0SUBNET:
return new(EDNS0_SUBNET)
case EDNS0EXPIRE:
return new(EDNS0_EXPIRE)
case EDNS0COOKIE:
return new(EDNS0_COOKIE)
case EDNS0TCPKEEPALIVE:
return new(EDNS0_TCP_KEEPALIVE)
case EDNS0PADDING:
return new(EDNS0_PADDING)
case EDNS0EDE:
return new(EDNS0_EDE)
default:
e := new(EDNS0_LOCAL)
e.Code = code
return e
}
}
// OPT is the EDNS0 RR appended to messages to convey extra (meta) information. // OPT is the EDNS0 RR appended to messages to convey extra (meta) information.
// See RFC 6891. // See RFC 6891.
type OPT struct { type OPT struct {
@ -73,6 +109,8 @@ func (rr *OPT) String() string {
s += "\n; LOCAL OPT: " + o.String() s += "\n; LOCAL OPT: " + o.String()
case *EDNS0_PADDING: case *EDNS0_PADDING:
s += "\n; PADDING: " + o.String() s += "\n; PADDING: " + o.String()
case *EDNS0_EDE:
s += "\n; EDE: " + o.String()
} }
} }
return s return s
@ -80,19 +118,19 @@ func (rr *OPT) String() string {
func (rr *OPT) len(off int, compression map[string]struct{}) int { func (rr *OPT) len(off int, compression map[string]struct{}) int {
l := rr.Hdr.len(off, compression) l := rr.Hdr.len(off, compression)
for i := 0; i < len(rr.Option); i++ { for _, o := range rr.Option {
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, _ := rr.Option[i].pack() lo, _ := o.pack()
l += len(lo) l += len(lo)
} }
return l return l
} }
func (rr *OPT) parse(c *zlexer, origin, file string) *ParseError { func (*OPT) parse(c *zlexer, origin string) *ParseError {
panic("dns: internal error: parse should never be called on OPT") return &ParseError{err: "OPT records do not have a presentation format"}
} }
func (r1 *OPT) isDuplicate(r2 RR) bool { return false } func (rr *OPT) isDuplicate(r2 RR) bool { return false }
// return the old value -> delete SetVersion? // return the old value -> delete SetVersion?
@ -148,6 +186,16 @@ func (rr *OPT) SetDo(do ...bool) {
} }
} }
// Z returns the Z part of the OPT RR as a uint16 with only the 15 least significant bits used.
func (rr *OPT) Z() uint16 {
return uint16(rr.Hdr.Ttl & 0x7FFF)
}
// SetZ sets the Z part of the OPT RR, note only the 15 least significant bits of z are used.
func (rr *OPT) SetZ(z uint16) {
rr.Hdr.Ttl = rr.Hdr.Ttl&^0x7FFF | uint32(z&0x7FFF)
}
// EDNS0 defines an EDNS0 Option. An OPT RR can have multiple options appended to it. // EDNS0 defines an EDNS0 Option. An OPT RR can have multiple options appended to it.
type EDNS0 interface { type EDNS0 interface {
// Option returns the option code for the option. // Option returns the option code for the option.
@ -159,6 +207,8 @@ 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
@ -190,6 +240,7 @@ func (e *EDNS0_NSID) pack() ([]byte, error) {
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 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
@ -307,6 +358,16 @@ 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)
@ -342,11 +403,12 @@ 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
// up after themselves. This is a draft RFC and more information can be found at // up after themselves. This is a draft RFC and more information can be found at
// http://files.dns-sd.org/draft-sekar-dns-ul.txt // https://tools.ietf.org/html/draft-sekar-dns-ul-02
// //
// o := new(dns.OPT) // o := new(dns.OPT)
// o.Hdr.Name = "." // o.Hdr.Name = "."
@ -358,21 +420,34 @@ func (e *EDNS0_COOKIE) String() string { return e.Cookie }
type EDNS0_UL struct { type EDNS0_UL struct {
Code uint16 // Always EDNS0UL Code uint16 // Always EDNS0UL
Lease uint32 Lease uint32
KeyLease uint32
} }
// 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 fmt.Sprintf("%d %d", e.Lease, e.KeyLease) }
func (e *EDNS0_UL) copy() EDNS0 { return &EDNS0_UL{e.Code, e.Lease, e.KeyLease} }
// 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) {
b := make([]byte, 4) var b []byte
if e.KeyLease == 0 {
b = make([]byte, 4)
} else {
b = make([]byte, 8)
binary.BigEndian.PutUint32(b[4:], e.KeyLease)
}
binary.BigEndian.PutUint32(b, e.Lease) binary.BigEndian.PutUint32(b, e.Lease)
return b, nil return b, nil
} }
func (e *EDNS0_UL) unpack(b []byte) error { func (e *EDNS0_UL) unpack(b []byte) error {
if len(b) < 4 { switch len(b) {
case 4:
e.KeyLease = 0
case 8:
e.KeyLease = binary.BigEndian.Uint32(b[4:])
default:
return ErrBuf return ErrBuf
} }
e.Lease = binary.BigEndian.Uint32(b) e.Lease = binary.BigEndian.Uint32(b)
@ -421,8 +496,11 @@ func (e *EDNS0_LLQ) String() string {
" " + 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. // EDNS0_DAU 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
@ -435,15 +513,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 i := 0; i < len(e.AlgCode); i++ { for _, alg := range e.AlgCode {
if a, ok := AlgorithmToString[e.AlgCode[i]]; ok { if a, ok := AlgorithmToString[alg]; ok {
s += " " + a s += " " + a
} else { } else {
s += " " + strconv.Itoa(int(e.AlgCode[i])) s += " " + strconv.Itoa(int(alg))
} }
} }
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. // EDNS0_DHU implements the EDNS0 "DS Hash Understood" option. See RFC 6975.
type EDNS0_DHU struct { type EDNS0_DHU struct {
@ -458,15 +537,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 i := 0; i < len(e.AlgCode); i++ { for _, alg := range e.AlgCode {
if a, ok := HashToString[e.AlgCode[i]]; ok { if a, ok := HashToString[alg]; ok {
s += " " + a s += " " + a
} else { } else {
s += " " + strconv.Itoa(int(e.AlgCode[i])) s += " " + strconv.Itoa(int(alg))
} }
} }
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. // EDNS0_N3U implements the EDNS0 "NSEC3 Hash Understood" option. See RFC 6975.
type EDNS0_N3U struct { type EDNS0_N3U struct {
@ -482,17 +562,18 @@ 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 i := 0; i < len(e.AlgCode); i++ { for _, alg := range e.AlgCode {
if a, ok := HashToString[e.AlgCode[i]]; ok { if a, ok := HashToString[alg]; ok {
s += " " + a s += " " + a
} else { } else {
s += " " + strconv.Itoa(int(e.AlgCode[i])) s += " " + strconv.Itoa(int(alg))
} }
} }
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. // EDNS0_EXPIRE implements 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
@ -501,6 +582,7 @@ 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)
@ -509,6 +591,10 @@ func (e *EDNS0_EXPIRE) pack() ([]byte, error) {
} }
func (e *EDNS0_EXPIRE) unpack(b []byte) error { func (e *EDNS0_EXPIRE) unpack(b []byte) error {
if len(b) == 0 {
// zero-length EXPIRE query, see RFC 7314 Section 2
return nil
}
if len(b) < 4 { if len(b) < 4 {
return ErrBuf return ErrBuf
} }
@ -539,6 +625,11 @@ 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))
@ -611,6 +702,7 @@ 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
@ -624,3 +716,106 @@ 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}
}
// Extended DNS Error Codes (RFC 8914).
const (
ExtendedErrorCodeOther uint16 = iota
ExtendedErrorCodeUnsupportedDNSKEYAlgorithm
ExtendedErrorCodeUnsupportedDSDigestType
ExtendedErrorCodeStaleAnswer
ExtendedErrorCodeForgedAnswer
ExtendedErrorCodeDNSSECIndeterminate
ExtendedErrorCodeDNSBogus
ExtendedErrorCodeSignatureExpired
ExtendedErrorCodeSignatureNotYetValid
ExtendedErrorCodeDNSKEYMissing
ExtendedErrorCodeRRSIGsMissing
ExtendedErrorCodeNoZoneKeyBitSet
ExtendedErrorCodeNSECMissing
ExtendedErrorCodeCachedError
ExtendedErrorCodeNotReady
ExtendedErrorCodeBlocked
ExtendedErrorCodeCensored
ExtendedErrorCodeFiltered
ExtendedErrorCodeProhibited
ExtendedErrorCodeStaleNXDOMAINAnswer
ExtendedErrorCodeNotAuthoritative
ExtendedErrorCodeNotSupported
ExtendedErrorCodeNoReachableAuthority
ExtendedErrorCodeNetworkError
ExtendedErrorCodeInvalidData
)
// ExtendedErrorCodeToString maps extended error info codes to a human readable
// description.
var ExtendedErrorCodeToString = map[uint16]string{
ExtendedErrorCodeOther: "Other",
ExtendedErrorCodeUnsupportedDNSKEYAlgorithm: "Unsupported DNSKEY Algorithm",
ExtendedErrorCodeUnsupportedDSDigestType: "Unsupported DS Digest Type",
ExtendedErrorCodeStaleAnswer: "Stale Answer",
ExtendedErrorCodeForgedAnswer: "Forged Answer",
ExtendedErrorCodeDNSSECIndeterminate: "DNSSEC Indeterminate",
ExtendedErrorCodeDNSBogus: "DNSSEC Bogus",
ExtendedErrorCodeSignatureExpired: "Signature Expired",
ExtendedErrorCodeSignatureNotYetValid: "Signature Not Yet Valid",
ExtendedErrorCodeDNSKEYMissing: "DNSKEY Missing",
ExtendedErrorCodeRRSIGsMissing: "RRSIGs Missing",
ExtendedErrorCodeNoZoneKeyBitSet: "No Zone Key Bit Set",
ExtendedErrorCodeNSECMissing: "NSEC Missing",
ExtendedErrorCodeCachedError: "Cached Error",
ExtendedErrorCodeNotReady: "Not Ready",
ExtendedErrorCodeBlocked: "Blocked",
ExtendedErrorCodeCensored: "Censored",
ExtendedErrorCodeFiltered: "Filtered",
ExtendedErrorCodeProhibited: "Prohibited",
ExtendedErrorCodeStaleNXDOMAINAnswer: "Stale NXDOMAIN Answer",
ExtendedErrorCodeNotAuthoritative: "Not Authoritative",
ExtendedErrorCodeNotSupported: "Not Supported",
ExtendedErrorCodeNoReachableAuthority: "No Reachable Authority",
ExtendedErrorCodeNetworkError: "Network Error",
ExtendedErrorCodeInvalidData: "Invalid Data",
}
// StringToExtendedErrorCode is a map from human readable descriptions to
// extended error info codes.
var StringToExtendedErrorCode = reverseInt16(ExtendedErrorCodeToString)
// EDNS0_EDE option is used to return additional information about the cause of
// DNS errors.
type EDNS0_EDE struct {
InfoCode uint16
ExtraText string
}
// Option implements the EDNS0 interface.
func (e *EDNS0_EDE) Option() uint16 { return EDNS0EDE }
func (e *EDNS0_EDE) copy() EDNS0 { return &EDNS0_EDE{e.InfoCode, e.ExtraText} }
func (e *EDNS0_EDE) String() string {
info := strconv.FormatUint(uint64(e.InfoCode), 10)
if s, ok := ExtendedErrorCodeToString[e.InfoCode]; ok {
info += fmt.Sprintf(" (%s)", s)
}
return fmt.Sprintf("%s: (%s)", info, e.ExtraText)
}
func (e *EDNS0_EDE) pack() ([]byte, error) {
b := make([]byte, 2+len(e.ExtraText))
binary.BigEndian.PutUint16(b[0:], e.InfoCode)
copy(b[2:], []byte(e.ExtraText))
return b, nil
}
func (e *EDNS0_EDE) unpack(b []byte) error {
if len(b) < 2 {
return ErrBuf
}
e.InfoCode = binary.BigEndian.Uint16(b[0:])
e.ExtraText = string(b[2:])
return nil
}

View File

@ -31,6 +31,9 @@ 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()),
@ -42,6 +45,9 @@ 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()),

11
vendor/github.com/miekg/dns/fuzz.go generated vendored
View File

@ -2,6 +2,8 @@
package dns package dns
import "strings"
func Fuzz(data []byte) int { func Fuzz(data []byte) int {
msg := new(Msg) msg := new(Msg)
@ -16,7 +18,14 @@ func Fuzz(data []byte) int {
} }
func FuzzNewRR(data []byte) int { func FuzzNewRR(data []byte) int {
if _, err := NewRR(string(data)); err != nil { str := string(data)
// Do not fuzz lines that include the $INCLUDE keyword and hint the fuzzer
// at avoiding them.
// See GH#1025 for context.
if strings.Contains(strings.ToUpper(str), "$INCLUDE") {
return -1
}
if _, err := NewRR(str); err != nil {
return 0 return 0
} }
return 1 return 1

View File

@ -20,13 +20,13 @@ import (
// of $ after that are interpreted. // of $ after that are interpreted.
func (zp *ZoneParser) generate(l lex) (RR, bool) { func (zp *ZoneParser) generate(l lex) (RR, bool) {
token := l.token token := l.token
step := 1 step := int64(1)
if i := strings.IndexByte(token, '/'); i >= 0 { if i := strings.IndexByte(token, '/'); i >= 0 {
if i+1 == len(token) { if i+1 == len(token) {
return zp.setParseError("bad step in $GENERATE range", l) return zp.setParseError("bad step in $GENERATE range", l)
} }
s, err := strconv.Atoi(token[i+1:]) s, err := strconv.ParseInt(token[i+1:], 10, 64)
if err != nil || s <= 0 { if err != nil || s <= 0 {
return zp.setParseError("bad step in $GENERATE range", l) return zp.setParseError("bad step in $GENERATE range", l)
} }
@ -40,20 +40,24 @@ func (zp *ZoneParser) generate(l lex) (RR, bool) {
return zp.setParseError("bad start-stop in $GENERATE range", l) return zp.setParseError("bad start-stop in $GENERATE range", l)
} }
start, err := strconv.Atoi(sx[0]) start, err := strconv.ParseInt(sx[0], 10, 64)
if err != nil { if err != nil {
return zp.setParseError("bad start in $GENERATE range", l) return zp.setParseError("bad start in $GENERATE range", l)
} }
end, err := strconv.Atoi(sx[1]) end, err := strconv.ParseInt(sx[1], 10, 64)
if err != nil { if err != nil {
return zp.setParseError("bad stop in $GENERATE range", l) return zp.setParseError("bad stop in $GENERATE range", l)
} }
if end < 0 || start < 0 || end < start { if end < 0 || start < 0 || end < start || (end-start)/step > 65535 {
return zp.setParseError("bad range in $GENERATE range", l) return zp.setParseError("bad range in $GENERATE range", l)
} }
zp.c.Next() // _BLANK // _BLANK
l, ok := zp.c.Next()
if !ok || l.value != zBlank {
return zp.setParseError("garbage after $GENERATE range", l)
}
// Create a complete new string, which we then parse again. // Create a complete new string, which we then parse again.
var s string var s string
@ -81,6 +85,7 @@ func (zp *ZoneParser) generate(l lex) (RR, bool) {
} }
zp.sub = NewZoneParser(r, zp.origin, zp.file) zp.sub = NewZoneParser(r, zp.origin, zp.file)
zp.sub.includeDepth, zp.sub.includeAllowed = zp.includeDepth, zp.includeAllowed zp.sub.includeDepth, zp.sub.includeAllowed = zp.includeDepth, zp.includeAllowed
zp.sub.generateDisallowed = true
zp.sub.SetDefaultTTL(defaultTtl) zp.sub.SetDefaultTTL(defaultTtl)
return zp.subNext() return zp.subNext()
} }
@ -89,10 +94,10 @@ type generateReader struct {
s string s string
si int si int
cur int cur int64
start int start int64
end int end int64
step int step int64
mod bytes.Buffer mod bytes.Buffer
@ -168,7 +173,7 @@ func (r *generateReader) ReadByte() (byte, error) {
return '$', nil return '$', nil
} }
var offset int var offset int64
// Search for { and } // Search for { and }
if r.s[si+1] == '{' { if r.s[si+1] == '{' {
@ -203,7 +208,7 @@ func (r *generateReader) ReadByte() (byte, error) {
} }
// 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, int64, string) {
// Modifier is { offset [ ,width [ ,base ] ] } - provide default // Modifier is { offset [ ,width [ ,base ] ] } - provide default
// values for optional width and type, if necessary. // values for optional width and type, if necessary.
var offStr, widthStr, base string var offStr, widthStr, base string
@ -224,12 +229,12 @@ func modToPrintf(s string) (string, int, string) {
return "", 0, "bad base in $GENERATE" return "", 0, "bad base in $GENERATE"
} }
offset, err := strconv.Atoi(offStr) offset, err := strconv.ParseInt(offStr, 10, 64)
if err != nil { if err != nil {
return "", 0, "bad offset in $GENERATE" return "", 0, "bad offset in $GENERATE"
} }
width, err := strconv.Atoi(widthStr) width, err := strconv.ParseInt(widthStr, 10, 64)
if err != nil || width < 0 || width > 255 { if err != nil || width < 0 || width > 255 {
return "", 0, "bad width in $GENERATE" return "", 0, "bad width in $GENERATE"
} }

View File

@ -10,7 +10,7 @@ package dns
// escaped dots (\.) for instance. // escaped dots (\.) for instance.
// s must be a syntactically valid domain name, see IsDomainName. // s must be a syntactically valid domain name, see IsDomainName.
func SplitDomainName(s string) (labels []string) { func SplitDomainName(s string) (labels []string) {
if len(s) == 0 { if s == "" {
return nil return nil
} }
fqdnEnd := 0 // offset of the final '.' or the length of the name fqdnEnd := 0 // offset of the final '.' or the length of the name
@ -28,9 +28,7 @@ func SplitDomainName(s string) (labels []string) {
case 1: case 1:
// no-op // no-op
default: default:
end := 0 for _, end := range idx[1:] {
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
} }
@ -85,7 +83,7 @@ func CompareDomainName(s1, s2 string) (n int) {
return return
} }
// CountLabel counts the the number of labels in the string s. // CountLabel counts the number of labels in the string s.
// s must be a syntactically valid domain name. // s must be a syntactically valid domain name.
func CountLabel(s string) (labels int) { func CountLabel(s string) (labels int) {
if s == "." { if s == "." {
@ -128,20 +126,23 @@ func Split(s string) []int {
// The bool end is true when the end of the string has been reached. // The bool end is true when the end of the string has been reached.
// Also see PrevLabel. // Also see PrevLabel.
func NextLabel(s string, offset int) (i int, end bool) { func NextLabel(s string, offset int) (i int, end bool) {
quote := false if s == "" {
return 0, true
}
for i = offset; i < len(s)-1; i++ { for i = offset; i < len(s)-1; i++ {
switch s[i] { if s[i] != '.' {
case '\\':
quote = !quote
default:
quote = false
case '.':
if quote {
quote = !quote
continue continue
} }
return i + 1, false j := i - 1
for j >= 0 && s[j] == '\\' {
j--
} }
if (j-i)%2 == 0 {
continue
}
return i + 1, false
} }
return i + 1, true return i + 1, true
} }
@ -151,17 +152,38 @@ func NextLabel(s string, offset int) (i int, end bool) {
// The bool start is true when the start of the string has been overshot. // The bool start is true when the start of the string has been overshot.
// Also see NextLabel. // Also see NextLabel.
func PrevLabel(s string, n int) (i int, start bool) { func PrevLabel(s string, n int) (i int, start bool) {
if s == "" {
return 0, true
}
if n == 0 { if n == 0 {
return len(s), false return len(s), false
} }
lab := Split(s)
if lab == nil { l := len(s) - 1
return 0, true if s[l] == '.' {
l--
} }
if n > len(lab) {
return 0, true for ; l >= 0 && n > 0; l-- {
if s[l] != '.' {
continue
} }
return lab[len(lab)-n], false j := l - 1
for j >= 0 && s[j] == '\\' {
j--
}
if (j-l)%2 == 0 {
continue
}
n--
if n == 0 {
return l + 1, false
}
}
return 0, n > 1
} }
// equal compares a and b while ignoring case. It returns true when equal otherwise false. // equal compares a and b while ignoring case. It returns true when equal otherwise false.

136
vendor/github.com/miekg/dns/msg.go generated vendored
View File

@ -11,14 +11,12 @@ package dns
//go:generate go run msg_generate.go //go:generate go run msg_generate.go
import ( import (
crand "crypto/rand" "crypto/rand"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"math/big" "math/big"
"math/rand"
"strconv" "strconv"
"strings" "strings"
"sync"
) )
const ( const (
@ -73,53 +71,23 @@ var (
ErrTime error = &Error{err: "bad time"} // ErrTime indicates a timing error in TSIG authentication. ErrTime error = &Error{err: "bad time"} // ErrTime indicates a timing error in TSIG authentication.
) )
// Id by default, returns a 16 bits random number to be used as a // Id by default returns a 16-bit random number to be used as a message id. The
// message id. The random provided should be good enough. This being a // number is drawn from a cryptographically secure random number generator.
// variable the function can be reassigned to a custom function. // This being a variable the function can be reassigned to a custom function.
// For instance, to make it return a static value: // For instance, to make it return a static value for testing:
// //
// dns.Id = func() uint16 { return 3 } // dns.Id = func() uint16 { return 3 }
var Id = id var Id = id
var (
idLock sync.Mutex
idRand *rand.Rand
)
// id returns a 16 bits random number to be used as a // id returns a 16 bits random number to be used as a
// message id. The random provided should be good enough. // message id. The random provided should be good enough.
func id() uint16 { func id() uint16 {
idLock.Lock() var output uint16
err := binary.Read(rand.Reader, binary.BigEndian, &output)
if idRand == nil { if err != nil {
// This (partially) works around panic("dns: reading random id failed: " + err.Error())
// https://github.com/golang/go/issues/11833 by only
// seeding idRand upon the first call to id.
var seed int64
var buf [8]byte
if _, err := crand.Read(buf[:]); err == nil {
seed = int64(binary.LittleEndian.Uint64(buf[:]))
} else {
seed = rand.Int63()
} }
return output
idRand = rand.New(rand.NewSource(seed))
}
// The call to idRand.Uint32 must be within the
// mutex lock because *rand.Rand is not safe for
// concurrent use.
//
// There is no added performance overhead to calling
// idRand.Uint32 inside a mutex lock over just
// calling rand.Uint32 as the global math/rand rng
// is internally protected by a sync.Mutex.
id := uint16(idRand.Uint32())
idLock.Unlock()
return id
} }
// MsgHdr is a a manually-unpacked version of (id, bits). // MsgHdr is a a manually-unpacked version of (id, bits).
@ -429,20 +397,15 @@ Loop:
if budget <= 0 { if budget <= 0 {
return "", lenmsg, ErrLongDomain return "", lenmsg, ErrLongDomain
} }
for j := off; j < off+c; j++ { for _, b := range msg[off : off+c] {
switch b := msg[j]; b { if isDomainNameLabelSpecial(b) {
case '.', '(', ')', ';', ' ', '@':
fallthrough
case '"', '\\':
s = append(s, '\\', b) s = append(s, '\\', b)
default: } else if b < ' ' || b > '~' {
if b < ' ' || b > '~' { // unprintable, use \DDD
s = append(s, escapeByte(b)...) s = append(s, escapeByte(b)...)
} else { } else {
s = append(s, b) s = append(s, b)
} }
} }
}
s = append(s, '.') s = append(s, '.')
off += c off += c
case 0xC0: case 0xC0:
@ -489,11 +452,11 @@ func packTxt(txt []string, msg []byte, offset int, tmp []byte) (int, error) {
return offset, nil return offset, nil
} }
var err error var err error
for i := range txt { for _, s := range txt {
if len(txt[i]) > len(tmp) { if len(s) > len(tmp) {
return offset, ErrBuf return offset, ErrBuf
} }
offset, err = packTxtString(txt[i], msg, offset, tmp) offset, err = packTxtString(s, msg, offset, tmp)
if err != nil { if err != nil {
return offset, err return offset, err
} }
@ -661,11 +624,18 @@ func UnpackRRWithHeader(h RR_Header, msg []byte, off int) (rr RR, off1 int, err
rr = &RFC3597{Hdr: h} rr = &RFC3597{Hdr: h}
} }
if noRdata(h) { if off < 0 || off > len(msg) {
return rr, off, nil return &h, off, &Error{err: "bad off"}
} }
end := off + int(h.Rdlength) end := off + int(h.Rdlength)
if end < off || end > len(msg) {
return &h, end, &Error{err: "bad rdlength"}
}
if noRdata(h) {
return rr, off, nil
}
off, err = rr.unpack(msg, off) off, err = rr.unpack(msg, off)
if err != nil { if err != nil {
@ -693,7 +663,6 @@ func unpackRRslice(l int, msg []byte, off int) (dst1 []RR, off1 int, err error)
} }
// If offset does not increase anymore, l is a lie // If offset does not increase anymore, l is a lie
if off1 == off { if off1 == off {
l = i
break break
} }
dst = append(dst, r) dst = append(dst, r)
@ -773,7 +742,7 @@ func (dns *Msg) packBufferWithCompressionMap(buf []byte, compression compression
} }
// Set extended rcode unconditionally if we have an opt, this will allow // Set extended rcode unconditionally if we have an opt, this will allow
// reseting the extended rcode bits if they need to. // resetting the extended rcode bits if they need to.
if opt := dns.IsEdns0(); opt != nil { if opt := dns.IsEdns0(); opt != nil {
opt.SetExtendedRcode(uint16(dns.Rcode)) opt.SetExtendedRcode(uint16(dns.Rcode))
} else if dns.Rcode > 0xF { } else if dns.Rcode > 0xF {
@ -934,31 +903,31 @@ func (dns *Msg) String() string {
s += "ADDITIONAL: " + strconv.Itoa(len(dns.Extra)) + "\n" s += "ADDITIONAL: " + strconv.Itoa(len(dns.Extra)) + "\n"
if len(dns.Question) > 0 { if len(dns.Question) > 0 {
s += "\n;; QUESTION SECTION:\n" s += "\n;; QUESTION SECTION:\n"
for i := 0; i < len(dns.Question); i++ { for _, r := range dns.Question {
s += dns.Question[i].String() + "\n" s += r.String() + "\n"
} }
} }
if len(dns.Answer) > 0 { if len(dns.Answer) > 0 {
s += "\n;; ANSWER SECTION:\n" s += "\n;; ANSWER SECTION:\n"
for i := 0; i < len(dns.Answer); i++ { for _, r := range dns.Answer {
if dns.Answer[i] != nil { if r != nil {
s += dns.Answer[i].String() + "\n" s += r.String() + "\n"
} }
} }
} }
if len(dns.Ns) > 0 { if len(dns.Ns) > 0 {
s += "\n;; AUTHORITY SECTION:\n" s += "\n;; AUTHORITY SECTION:\n"
for i := 0; i < len(dns.Ns); i++ { for _, r := range dns.Ns {
if dns.Ns[i] != nil { if r != nil {
s += dns.Ns[i].String() + "\n" s += r.String() + "\n"
} }
} }
} }
if len(dns.Extra) > 0 { if len(dns.Extra) > 0 {
s += "\n;; ADDITIONAL SECTION:\n" s += "\n;; ADDITIONAL SECTION:\n"
for i := 0; i < len(dns.Extra); i++ { for _, r := range dns.Extra {
if dns.Extra[i] != nil { if r != nil {
s += dns.Extra[i].String() + "\n" s += r.String() + "\n"
} }
} }
} }
@ -1091,33 +1060,20 @@ func (dns *Msg) CopyTo(r1 *Msg) *Msg {
} }
rrArr := make([]RR, len(dns.Answer)+len(dns.Ns)+len(dns.Extra)) rrArr := make([]RR, len(dns.Answer)+len(dns.Ns)+len(dns.Extra))
var rri int r1.Answer, rrArr = rrArr[:0:len(dns.Answer)], rrArr[len(dns.Answer):]
r1.Ns, rrArr = rrArr[:0:len(dns.Ns)], rrArr[len(dns.Ns):]
r1.Extra = rrArr[:0:len(dns.Extra)]
if len(dns.Answer) > 0 { for _, r := range dns.Answer {
rrbegin := rri r1.Answer = append(r1.Answer, r.copy())
for i := 0; i < len(dns.Answer); i++ {
rrArr[rri] = dns.Answer[i].copy()
rri++
}
r1.Answer = rrArr[rrbegin:rri:rri]
} }
if len(dns.Ns) > 0 { for _, r := range dns.Ns {
rrbegin := rri r1.Ns = append(r1.Ns, r.copy())
for i := 0; i < len(dns.Ns); i++ {
rrArr[rri] = dns.Ns[i].copy()
rri++
}
r1.Ns = rrArr[rrbegin:rri:rri]
} }
if len(dns.Extra) > 0 { for _, r := range dns.Extra {
rrbegin := rri r1.Extra = append(r1.Extra, r.copy())
for i := 0; i < len(dns.Extra); i++ {
rrArr[rri] = dns.Extra[i].copy()
rri++
}
r1.Extra = rrArr[rrbegin:rri:rri]
} }
return r1 return r1

View File

@ -6,6 +6,7 @@ import (
"encoding/binary" "encoding/binary"
"encoding/hex" "encoding/hex"
"net" "net"
"sort"
"strings" "strings"
) )
@ -25,12 +26,13 @@ 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:
@ -51,12 +53,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:
@ -264,24 +266,36 @@ func unpackString(msg []byte, off int) (string, int, error) {
return "", off, &Error{err: "overflow unpacking txt"} return "", off, &Error{err: "overflow unpacking txt"}
} }
l := int(msg[off]) l := int(msg[off])
if off+l+1 > len(msg) { off++
if off+l > len(msg) {
return "", off, &Error{err: "overflow unpacking txt"} return "", off, &Error{err: "overflow unpacking txt"}
} }
var s strings.Builder var s strings.Builder
s.Grow(l) consumed := 0
for _, b := range msg[off+1 : off+1+l] { for i, b := range msg[off : off+l] {
switch { switch {
case b == '"' || b == '\\': case b == '"' || b == '\\':
if consumed == 0 {
s.Grow(l * 2)
}
s.Write(msg[off+consumed : off+i])
s.WriteByte('\\') s.WriteByte('\\')
s.WriteByte(b) s.WriteByte(b)
consumed = i + 1
case b < ' ' || b > '~': // unprintable case b < ' ' || b > '~': // unprintable
if consumed == 0 {
s.Grow(l * 2)
}
s.Write(msg[off+consumed : off+i])
s.WriteString(escapeByte(b)) s.WriteString(escapeByte(b))
default: consumed = i + 1
s.WriteByte(b)
} }
} }
off += 1 + l if consumed == 0 { // no escaping needed
return s.String(), off, nil return string(msg[off : off+l]), off + l, nil
}
s.Write(msg[off+consumed : off+l])
return s.String(), off + l, nil
} }
func packString(s string, msg []byte, off int) (int, error) { func packString(s string, msg []byte, off int) (int, error) {
@ -410,79 +424,12 @@ Option:
if off+int(optlen) > len(msg) { if off+int(optlen) > len(msg) {
return nil, len(msg), &Error{err: "overflow unpacking opt"} return nil, len(msg), &Error{err: "overflow unpacking opt"}
} }
switch code { e := makeDataOpt(code)
case EDNS0NSID:
e := new(EDNS0_NSID)
if err := e.unpack(msg[off : off+int(optlen)]); err != nil { if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
return nil, len(msg), err return nil, len(msg), err
} }
edns = append(edns, e) edns = append(edns, e)
off += int(optlen) off += int(optlen)
case EDNS0SUBNET:
e := new(EDNS0_SUBNET)
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
return nil, len(msg), err
}
edns = append(edns, e)
off += int(optlen)
case EDNS0COOKIE:
e := new(EDNS0_COOKIE)
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
return nil, len(msg), err
}
edns = append(edns, e)
off += int(optlen)
case EDNS0UL:
e := new(EDNS0_UL)
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
return nil, len(msg), err
}
edns = append(edns, e)
off += int(optlen)
case EDNS0LLQ:
e := new(EDNS0_LLQ)
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
return nil, len(msg), err
}
edns = append(edns, e)
off += int(optlen)
case EDNS0DAU:
e := new(EDNS0_DAU)
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
return nil, len(msg), err
}
edns = append(edns, e)
off += int(optlen)
case EDNS0DHU:
e := new(EDNS0_DHU)
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
return nil, len(msg), err
}
edns = append(edns, e)
off += int(optlen)
case EDNS0N3U:
e := new(EDNS0_N3U)
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
return nil, len(msg), err
}
edns = append(edns, e)
off += int(optlen)
case EDNS0PADDING:
e := new(EDNS0_PADDING)
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
return nil, len(msg), err
}
edns = append(edns, e)
off += int(optlen)
default:
e := new(EDNS0_LOCAL)
e.Code = code
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
return nil, len(msg), err
}
edns = append(edns, e)
off += int(optlen)
}
if off < len(msg) { if off < len(msg) {
goto Option goto Option
@ -494,16 +441,14 @@ Option:
func packDataOpt(options []EDNS0, msg []byte, off int) (int, error) { func packDataOpt(options []EDNS0, msg []byte, off int) (int, error) {
for _, el := range options { for _, el := range options {
b, err := el.pack() b, err := el.pack()
if err != nil || off+3 > len(msg) { if err != nil || off+4 > len(msg) {
return len(msg), &Error{err: "overflow packing opt"} return len(msg), &Error{err: "overflow packing opt"}
} }
binary.BigEndian.PutUint16(msg[off:], el.Option()) // Option code binary.BigEndian.PutUint16(msg[off:], el.Option()) // Option code
binary.BigEndian.PutUint16(msg[off+2:], uint16(len(b))) // Length binary.BigEndian.PutUint16(msg[off+2:], uint16(len(b))) // Length
off += 4 off += 4
if off+len(b) > len(msg) { if off+len(b) > len(msg) {
copy(msg[off:], b) return len(msg), &Error{err: "overflow packing opt"}
off = len(msg)
continue
} }
// Actual data // Actual data
copy(msg[off:off+len(b)], b) copy(msg[off:off+len(b)], b)
@ -553,8 +498,7 @@ 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 := 0; j < length; j++ { for j, b := range msg[off : off+length] {
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))
@ -587,13 +531,35 @@ func unpackDataNsec(msg []byte, off int) ([]uint16, int, error) {
return nsec, off, nil return nsec, off, nil
} }
// typeBitMapLen is a helper function which computes the "maximum" length of
// a the NSEC Type BitMap field.
func typeBitMapLen(bitmap []uint16) int {
var l int
var lastwindow, lastlength uint16
for _, t := range bitmap {
window := t / 256
length := (t-window*256)/8 + 1
if window > lastwindow && lastlength != 0 { // New window, jump to the new offset
l += int(lastlength) + 2
lastlength = 0
}
if window < lastwindow || length < lastlength {
// packDataNsec would return Error{err: "nsec bits out of order"} here, but
// when computing the length, we want do be liberal.
continue
}
lastwindow, lastlength = window, length
}
l += int(lastlength) + 2
return l
}
func packDataNsec(bitmap []uint16, msg []byte, off int) (int, error) { func packDataNsec(bitmap []uint16, msg []byte, off int) (int, error) {
if len(bitmap) == 0 { if len(bitmap) == 0 {
return off, nil return off, nil
} }
var lastwindow, lastlength uint16 var lastwindow, lastlength uint16
for j := 0; j < len(bitmap); j++ { for _, t := range bitmap {
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
@ -618,6 +584,65 @@ func packDataNsec(bitmap []uint16, msg []byte, off int) (int, error) {
return off, nil return off, nil
} }
func unpackDataSVCB(msg []byte, off int) ([]SVCBKeyValue, int, error) {
var xs []SVCBKeyValue
var code uint16
var length uint16
var err error
for off < len(msg) {
code, off, err = unpackUint16(msg, off)
if err != nil {
return nil, len(msg), &Error{err: "overflow unpacking SVCB"}
}
length, off, err = unpackUint16(msg, off)
if err != nil || off+int(length) > len(msg) {
return nil, len(msg), &Error{err: "overflow unpacking SVCB"}
}
e := makeSVCBKeyValue(SVCBKey(code))
if e == nil {
return nil, len(msg), &Error{err: "bad SVCB key"}
}
if err := e.unpack(msg[off : off+int(length)]); err != nil {
return nil, len(msg), err
}
if len(xs) > 0 && e.Key() <= xs[len(xs)-1].Key() {
return nil, len(msg), &Error{err: "SVCB keys not in strictly increasing order"}
}
xs = append(xs, e)
off += int(length)
}
return xs, off, nil
}
func packDataSVCB(pairs []SVCBKeyValue, msg []byte, off int) (int, error) {
pairs = append([]SVCBKeyValue(nil), pairs...)
sort.Slice(pairs, func(i, j int) bool {
return pairs[i].Key() < pairs[j].Key()
})
prev := svcb_RESERVED
for _, el := range pairs {
if el.Key() == prev {
return len(msg), &Error{err: "repeated SVCB keys are not allowed"}
}
prev = el.Key()
packed, err := el.pack()
if err != nil {
return len(msg), err
}
off, err = packUint16(uint16(el.Key()), msg, off)
if err != nil {
return len(msg), &Error{err: "overflow packing SVCB"}
}
off, err = packUint16(uint16(len(packed)), msg, off)
if err != nil || off+len(packed) > len(msg) {
return len(msg), &Error{err: "overflow packing SVCB"}
}
copy(msg[off:off+len(packed)], packed)
off += len(packed)
}
return off, nil
}
func unpackDataDomainNames(msg []byte, off, end int) ([]string, int, error) { func unpackDataDomainNames(msg []byte, off, end int) ([]string, int, error) {
var ( var (
servers []string servers []string
@ -639,11 +664,141 @@ func unpackDataDomainNames(msg []byte, off, end int) ([]string, int, error) {
func packDataDomainNames(names []string, msg []byte, off int, compression compressionMap, compress bool) (int, error) { func packDataDomainNames(names []string, msg []byte, off int, compression compressionMap, compress bool) (int, error) {
var err error var err error
for j := 0; j < len(names); j++ { for _, name := range names {
off, err = packDomainName(names[j], msg, off, compression, compress) off, err = packDomainName(name, msg, off, compression, compress)
if err != nil { if err != nil {
return len(msg), err return len(msg), err
} }
} }
return off, nil return off, nil
} }
func packDataApl(data []APLPrefix, msg []byte, off int) (int, error) {
var err error
for i := range data {
off, err = packDataAplPrefix(&data[i], msg, off)
if err != nil {
return len(msg), err
}
}
return off, nil
}
func packDataAplPrefix(p *APLPrefix, msg []byte, off int) (int, error) {
if len(p.Network.IP) != len(p.Network.Mask) {
return len(msg), &Error{err: "address and mask lengths don't match"}
}
var err error
prefix, _ := p.Network.Mask.Size()
addr := p.Network.IP.Mask(p.Network.Mask)[:(prefix+7)/8]
switch len(p.Network.IP) {
case net.IPv4len:
off, err = packUint16(1, msg, off)
case net.IPv6len:
off, err = packUint16(2, msg, off)
default:
err = &Error{err: "unrecognized address family"}
}
if err != nil {
return len(msg), err
}
off, err = packUint8(uint8(prefix), msg, off)
if err != nil {
return len(msg), err
}
var n uint8
if p.Negation {
n = 0x80
}
// trim trailing zero bytes as specified in RFC3123 Sections 4.1 and 4.2.
i := len(addr) - 1
for ; i >= 0 && addr[i] == 0; i-- {
}
addr = addr[:i+1]
adflen := uint8(len(addr)) & 0x7f
off, err = packUint8(n|adflen, msg, off)
if err != nil {
return len(msg), err
}
if off+len(addr) > len(msg) {
return len(msg), &Error{err: "overflow packing APL prefix"}
}
off += copy(msg[off:], addr)
return off, nil
}
func unpackDataApl(msg []byte, off int) ([]APLPrefix, int, error) {
var result []APLPrefix
for off < len(msg) {
prefix, end, err := unpackDataAplPrefix(msg, off)
if err != nil {
return nil, len(msg), err
}
off = end
result = append(result, prefix)
}
return result, off, nil
}
func unpackDataAplPrefix(msg []byte, off int) (APLPrefix, int, error) {
family, off, err := unpackUint16(msg, off)
if err != nil {
return APLPrefix{}, len(msg), &Error{err: "overflow unpacking APL prefix"}
}
prefix, off, err := unpackUint8(msg, off)
if err != nil {
return APLPrefix{}, len(msg), &Error{err: "overflow unpacking APL prefix"}
}
nlen, off, err := unpackUint8(msg, off)
if err != nil {
return APLPrefix{}, len(msg), &Error{err: "overflow unpacking APL prefix"}
}
var ip []byte
switch family {
case 1:
ip = make([]byte, net.IPv4len)
case 2:
ip = make([]byte, net.IPv6len)
default:
return APLPrefix{}, len(msg), &Error{err: "unrecognized APL address family"}
}
if int(prefix) > 8*len(ip) {
return APLPrefix{}, len(msg), &Error{err: "APL prefix too long"}
}
afdlen := int(nlen & 0x7f)
if afdlen > len(ip) {
return APLPrefix{}, len(msg), &Error{err: "APL length too long"}
}
if off+afdlen > len(msg) {
return APLPrefix{}, len(msg), &Error{err: "overflow unpacking APL address"}
}
off += copy(ip, msg[off:off+afdlen])
if afdlen > 0 {
last := ip[afdlen-1]
if last == 0 {
return APLPrefix{}, len(msg), &Error{err: "extra APL address bits"}
}
}
ipnet := net.IPNet{
IP: ip,
Mask: net.CIDRMask(int(prefix), 8*len(ip)),
}
network := ipnet.IP.Mask(ipnet.Mask)
if !network.Equal(ipnet.IP) {
return APLPrefix{}, len(msg), &Error{err: "invalid APL address length"}
}
return APLPrefix{
Negation: (nlen & 0x80) != 0,
Network: ipnet,
}, off, nil
}

117
vendor/github.com/miekg/dns/msg_truncate.go generated vendored Normal file
View File

@ -0,0 +1,117 @@
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, Truncate then walks the
// record adding as many records as possible without exceeding the
// requested buffer size.
//
// If the message fits within the requested size without compression,
// Truncate will set the message's Compress attribute to false. It is
// the caller's responsibility to set it back to true if they wish to
// compress the payload regardless of size.
//
// The TC bit will be set if any records were excluded from the message.
// If the TC bit is already set on the message it will be retained.
// TC indicates that the client should retry over TCP.
//
// According to RFC 2181, the TC bit should only be set if not all of the
// "required" RRs can be included in the response. Unfortunately, we have
// no way of knowing which RRs are required so we set the TC bit if any RR
// had to be omitted from the response.
//
// 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 (MinMsgSize) bytes must be treated as equal to 512 bytes.
//
// For ease of use, we impose that restriction here.
if size < MinMsgSize {
size = MinMsgSize
}
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 {
_, numExtra = truncateLoop(dns.Extra, size, l, compression)
}
// See the function documentation for when we set this.
dns.Truncated = dns.Truncated || len(dns.Answer) > numAnswer ||
len(dns.Ns) > numNS || len(dns.Extra) > numExtra
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)
}

View File

@ -43,7 +43,7 @@ func HashName(label string, ha uint8, iter uint16, salt string) string {
return toBase32(nsec3) return toBase32(nsec3)
} }
// Cover returns true if a name is covered by the NSEC3 record // Cover returns true if a name is covered by the NSEC3 record.
func (rr *NSEC3) Cover(name string) bool { func (rr *NSEC3) Cover(name string) bool {
nameHash := HashName(name, rr.Hash, rr.Iterations, rr.Salt) nameHash := HashName(name, rr.Hash, rr.Iterations, rr.Salt)
owner := strings.ToUpper(rr.Hdr.Name) owner := strings.ToUpper(rr.Hdr.Name)

View File

@ -1,24 +1,20 @@
package dns package dns
import ( import "strings"
"fmt"
"strings"
)
// PrivateRdata is an interface used for implementing "Private Use" RR types, see // PrivateRdata is an interface used for implementing "Private Use" RR types, see
// RFC 6895. This allows one to experiment with new RR types, without requesting an // RFC 6895. This allows one to experiment with new RR types, without requesting an
// official type code. Also see dns.PrivateHandle and dns.PrivateHandleRemove. // official type code. Also see dns.PrivateHandle and dns.PrivateHandleRemove.
type PrivateRdata interface { type PrivateRdata interface {
// String returns the text presentaton of the Rdata of the Private RR. // String returns the text presentation of the Rdata of the Private RR.
String() string String() string
// Parse parses the Rdata of the private RR. // Parse parses the Rdata of the private RR.
Parse([]string) error Parse([]string) error
// Pack is used when packing a private RR into a buffer. // Pack is used when packing a private RR into a buffer.
Pack([]byte) (int, error) Pack([]byte) (int, error)
// Unpack is used when unpacking a private RR from a buffer. // Unpack is used when unpacking a private RR from a buffer.
// TODO(miek): diff. signature than Pack, see edns0.go for instance.
Unpack([]byte) (int, error) Unpack([]byte) (int, error)
// Copy copies the Rdata. // Copy copies the Rdata into the PrivateRdata argument.
Copy(PrivateRdata) error Copy(PrivateRdata) error
// Len returns the length in octets of the Rdata. // Len returns the length in octets of the Rdata.
Len() int Len() int
@ -29,22 +25,8 @@ type PrivateRdata interface {
type PrivateRR struct { type PrivateRR struct {
Hdr RR_Header Hdr RR_Header
Data PrivateRdata Data PrivateRdata
}
func mkPrivateRR(rrtype uint16) *PrivateRR { generator func() PrivateRdata // for copy
// Panics if RR is not an instance of PrivateRR.
rrfunc, ok := TypeToRR[rrtype]
if !ok {
panic(fmt.Sprintf("dns: invalid operation with Private RR type %d", rrtype))
}
anyrr := rrfunc()
rr, ok := anyrr.(*PrivateRR)
if !ok {
panic(fmt.Sprintf("dns: RR is not a PrivateRR, TypeToRR[%d] generator returned %T", rrtype, anyrr))
}
return rr
} }
// Header return the RR header of r. // Header return the RR header of r.
@ -61,13 +43,12 @@ func (r *PrivateRR) len(off int, compression map[string]struct{}) int {
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 := &PrivateRR{r.Hdr, r.generator(), r.generator}
rr.Hdr = r.Hdr
err := r.Data.Copy(rr.Data) if err := r.Data.Copy(rr.Data); err != nil {
if err != nil { panic("dns: got value that could not be used to copy Private rdata: " + err.Error())
panic("dns: got value that could not be used to copy Private rdata")
} }
return rr return rr
} }
@ -86,7 +67,7 @@ func (r *PrivateRR) unpack(msg []byte, off int) (int, error) {
return off, err return off, err
} }
func (r *PrivateRR) parse(c *zlexer, origin, file string) *ParseError { func (r *PrivateRR) parse(c *zlexer, origin string) *ParseError {
var l lex var l lex
text := make([]string, 0, 2) // could be 0..N elements, median is probably 1 text := make([]string, 0, 2) // could be 0..N elements, median is probably 1
Fetch: Fetch:
@ -103,20 +84,20 @@ Fetch:
err := r.Data.Parse(text) err := r.Data.Parse(text)
if err != nil { if err != nil {
return &ParseError{file, err.Error(), l} return &ParseError{"", err.Error(), l}
} }
return nil return nil
} }
func (r1 *PrivateRR) isDuplicate(r2 RR) bool { return false } func (r *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) {
rtypestr = strings.ToUpper(rtypestr) rtypestr = strings.ToUpper(rtypestr)
TypeToRR[rtype] = func() RR { return &PrivateRR{RR_Header{}, generator()} } TypeToRR[rtype] = func() RR { return &PrivateRR{RR_Header{}, generator(), generator} }
TypeToString[rtype] = rtypestr TypeToString[rtype] = rtypestr
StringToType[rtypestr] = rtype StringToType[rtypestr] = rtype
} }

245
vendor/github.com/miekg/dns/scan.go generated vendored
View File

@ -87,31 +87,18 @@ type lex struct {
column int // column in the file column int // column in the file
} }
// Token holds the token that are returned when a zone file is parsed.
type Token struct {
// The scanned resource record when error is not nil.
RR
// When an error occurred, this has the error specifics.
Error *ParseError
// A potential comment positioned after the RR and on the same line.
Comment string
}
// ttlState describes the state necessary to fill in an omitted RR TTL // ttlState describes the state necessary to fill in an omitted RR TTL
type ttlState struct { type ttlState struct {
ttl uint32 // ttl is the current default TTL ttl uint32 // ttl is the current default TTL
isByDirective bool // isByDirective indicates whether ttl was set by a $TTL directive isByDirective bool // isByDirective indicates whether ttl was set by a $TTL directive
} }
// NewRR reads the RR contained in the string s. Only the first RR is // NewRR reads the RR contained in the string s. Only the first RR is returned.
// returned. If s contains no records, NewRR will return nil with no // If s contains no records, NewRR will return nil with no error.
// error.
// //
// The class defaults to IN and TTL defaults to 3600. The full zone // The class defaults to IN and TTL defaults to 3600. The full zone file syntax
// file syntax like $TTL, $ORIGIN, etc. is supported. // like $TTL, $ORIGIN, etc. is supported. All fields of the returned RR are
// // set, except RR.Header().Rdlength which is set to 0.
// All fields of the returned RR are set, except RR.Header().Rdlength
// which is set to 0.
func NewRR(s string) (RR, error) { func NewRR(s string) (RR, error) {
if len(s) > 0 && s[len(s)-1] != '\n' { // We need a closing newline if len(s) > 0 && s[len(s)-1] != '\n' { // We need a closing newline
return ReadRR(strings.NewReader(s+"\n"), "") return ReadRR(strings.NewReader(s+"\n"), "")
@ -133,69 +120,6 @@ func ReadRR(r io.Reader, file string) (RR, error) {
return rr, zp.Err() return rr, zp.Err()
} }
// ParseZone reads a RFC 1035 style zonefile from r. It returns
// *Tokens on the returned channel, each consisting of either a
// parsed RR and optional comment or a nil RR and an error. The
// channel is closed by ParseZone when the end of r is reached.
//
// The string file is used in error reporting and to resolve relative
// $INCLUDE directives. The string origin is used as the initial
// origin, as if the file would start with an $ORIGIN directive.
//
// The directives $INCLUDE, $ORIGIN, $TTL and $GENERATE are all
// supported.
//
// Basic usage pattern when reading from a string (z) containing the
// zone data:
//
// for x := range dns.ParseZone(strings.NewReader(z), "", "") {
// if x.Error != nil {
// // log.Println(x.Error)
// } else {
// // Do something with x.RR
// }
// }
//
// Comments specified after an RR (and on the same line!) are
// returned too:
//
// foo. IN A 10.0.0.1 ; this is a comment
//
// The text "; this is comment" is returned in Token.Comment.
// Comments inside the RR are returned concatenated along with the
// RR. Comments on a line by themselves are discarded.
//
// To prevent memory leaks it is important to always fully drain the
// returned channel. If an error occurs, it will always be the last
// Token sent on the channel.
//
// Deprecated: New users should prefer the ZoneParser API.
func ParseZone(r io.Reader, origin, file string) chan *Token {
t := make(chan *Token, 10000)
go parseZone(r, origin, file, t)
return t
}
func parseZone(r io.Reader, origin, file string, t chan *Token) {
defer close(t)
zp := NewZoneParser(r, origin, file)
zp.SetIncludeAllowed(true)
for rr, ok := zp.Next(); ok; rr, ok = zp.Next() {
t <- &Token{RR: rr, Comment: zp.Comment()}
}
if err := zp.Err(); err != nil {
pe, ok := err.(*ParseError)
if !ok {
pe = &ParseError{file: file, err: err.Error()}
}
t <- &Token{Error: pe}
}
}
// ZoneParser is a parser for an RFC 1035 style zonefile. // ZoneParser is a parser for an RFC 1035 style zonefile.
// //
// Each parsed RR in the zone is returned sequentially from Next. An // Each parsed RR in the zone is returned sequentially from Next. An
@ -203,6 +127,7 @@ func parseZone(r io.Reader, origin, file string, t chan *Token) {
// //
// The directives $INCLUDE, $ORIGIN, $TTL and $GENERATE are all // The directives $INCLUDE, $ORIGIN, $TTL and $GENERATE are all
// supported. Although $INCLUDE is disabled by default. // supported. Although $INCLUDE is disabled by default.
// Note that $GENERATE's range support up to a maximum of 65535 steps.
// //
// Basic usage pattern when reading from a string (z) containing the // Basic usage pattern when reading from a string (z) containing the
// zone data: // zone data:
@ -225,6 +150,9 @@ func parseZone(r io.Reader, origin, file string, t chan *Token) {
// The text "; this is comment" is returned from Comment. Comments inside // The text "; this is comment" is returned from Comment. Comments inside
// the RR are returned concatenated along with the RR. Comments on a line // the RR are returned concatenated along with the RR. Comments on a line
// by themselves are discarded. // by themselves are discarded.
//
// Callers should not assume all returned data in an Resource Record is
// syntactically correct, e.g. illegal base64 in RRSIGs will be returned as-is.
type ZoneParser struct { type ZoneParser struct {
c *zlexer c *zlexer
@ -246,6 +174,7 @@ type ZoneParser struct {
includeDepth uint8 includeDepth uint8
includeAllowed bool includeAllowed bool
generateDisallowed bool
} }
// NewZoneParser returns an RFC 1035 style zonefile parser that reads // NewZoneParser returns an RFC 1035 style zonefile parser that reads
@ -503,9 +432,8 @@ func (zp *ZoneParser) Next() (RR, bool) {
return zp.setParseError("expecting $TTL value, not this...", l) return zp.setParseError("expecting $TTL value, not this...", l)
} }
if e := slurpRemainder(zp.c, zp.file); e != nil { if err := slurpRemainder(zp.c); err != nil {
zp.parseErr = e return zp.setParseError(err.err, err.lex)
return nil, false
} }
ttl, ok := stringToTTL(l.token) ttl, ok := stringToTTL(l.token)
@ -527,9 +455,8 @@ func (zp *ZoneParser) Next() (RR, bool) {
return zp.setParseError("expecting $ORIGIN value, not this...", l) return zp.setParseError("expecting $ORIGIN value, not this...", l)
} }
if e := slurpRemainder(zp.c, zp.file); e != nil { if err := slurpRemainder(zp.c); err != nil {
zp.parseErr = e return zp.setParseError(err.err, err.lex)
return nil, false
} }
name, ok := toAbsoluteName(l.token, zp.origin) name, ok := toAbsoluteName(l.token, zp.origin)
@ -547,6 +474,9 @@ func (zp *ZoneParser) Next() (RR, bool) {
st = zExpectDirGenerate st = zExpectDirGenerate
case zExpectDirGenerate: case zExpectDirGenerate:
if zp.generateDisallowed {
return zp.setParseError("nested $GENERATE directive not allowed", l)
}
if l.value != zString { if l.value != zString {
return zp.setParseError("expecting $GENERATE value, not this...", l) return zp.setParseError("expecting $GENERATE value, not this...", l)
} }
@ -650,19 +580,69 @@ func (zp *ZoneParser) Next() (RR, bool) {
st = zExpectRdata st = zExpectRdata
case zExpectRdata: case zExpectRdata:
r, e := setRR(*h, zp.c, zp.origin, zp.file) var (
if e != nil { rr RR
// If e.lex is nil than we have encounter a unknown RR type parseAsRFC3597 bool
// in that case we substitute our current lex token )
if e.lex.token == "" && e.lex.value == 0 { if newFn, ok := TypeToRR[h.Rrtype]; ok {
e.lex = l // Uh, dirty rr = newFn()
*rr.Header() = *h
// We may be parsing a known RR type using the RFC3597 format.
// If so, we handle that here in a generic way.
//
// This is also true for PrivateRR types which will have the
// RFC3597 parsing done for them and the Unpack method called
// to populate the RR instead of simply deferring to Parse.
if zp.c.Peek().token == "\\#" {
parseAsRFC3597 = true
}
} else {
rr = &RFC3597{Hdr: *h}
} }
zp.parseErr = e _, isPrivate := rr.(*PrivateRR)
return nil, false if !isPrivate && zp.c.Peek().token == "" {
// This is a dynamic update rr.
// TODO(tmthrgd): Previously slurpRemainder was only called
// for certain RR types, which may have been important.
if err := slurpRemainder(zp.c); err != nil {
return zp.setParseError(err.err, err.lex)
} }
return r, true return rr, true
} else if l.value == zNewline {
return zp.setParseError("unexpected newline", l)
}
parseAsRR := rr
if parseAsRFC3597 {
parseAsRR = &RFC3597{Hdr: *h}
}
if err := parseAsRR.parse(zp.c, zp.origin); err != nil {
// err is a concrete *ParseError without the file field set.
// The setParseError call below will construct a new
// *ParseError with file set to zp.file.
// err.lex may be nil in which case we substitute our current
// lex token.
if err.lex == (lex{}) {
return zp.setParseError(err.err, l)
}
return zp.setParseError(err.err, err.lex)
}
if parseAsRFC3597 {
err := parseAsRR.(*RFC3597).fromRFC3597(rr)
if err != nil {
return zp.setParseError(err.Error(), l)
}
}
return rr, true
} }
} }
@ -683,6 +663,7 @@ type zlexer struct {
comment string comment string
l lex l lex
cachedL *lex
brace int brace int
quote bool quote bool
@ -748,13 +729,37 @@ func (zl *zlexer) readByte() (byte, bool) {
return c, true return c, true
} }
func (zl *zlexer) Peek() lex {
if zl.nextL {
return zl.l
}
l, ok := zl.Next()
if !ok {
return l
}
if zl.nextL {
// Cache l. Next returns zl.cachedL then zl.l.
zl.cachedL = &l
} else {
// In this case l == zl.l, so we just tell Next to return zl.l.
zl.nextL = true
}
return l
}
func (zl *zlexer) Next() (lex, bool) { func (zl *zlexer) Next() (lex, bool) {
l := &zl.l l := &zl.l
if zl.nextL { switch {
case zl.cachedL != nil:
l, zl.cachedL = zl.cachedL, nil
return *l, true
case zl.nextL:
zl.nextL = false zl.nextL = false
return *l, true return *l, true
} case l.err:
if l.err {
// Parsing errors should be sticky. // Parsing errors should be sticky.
return lex{value: zEOF}, false return lex{value: zEOF}, false
} }
@ -908,6 +913,11 @@ func (zl *zlexer) Next() (lex, bool) {
// was inside braces and we delayed adding it until now. // was inside braces and we delayed adding it until now.
com[comi] = ' ' // convert newline to space com[comi] = ' ' // convert newline to space
comi++ comi++
if comi >= len(com) {
l.token = "comment length insufficient for parsing"
l.err = true
return *l, true
}
} }
com[comi] = ';' com[comi] = ';'
@ -1216,11 +1226,29 @@ func stringToCm(token string) (e, m uint8, ok bool) {
if cmeters, err = strconv.Atoi(s[1]); err != nil { if cmeters, err = strconv.Atoi(s[1]); err != nil {
return return
} }
// There's no point in having more than 2 digits in this part, and would rather make the implementation complicated ('123' should be treated as '12').
// So we simply reject it.
// We also make sure the first character is a digit to reject '+-' signs.
if len(s[1]) > 2 || s[1][0] < '0' || s[1][0] > '9' {
return
}
if len(s[1]) == 1 {
// 'nn.1' must be treated as 'nn-meters and 10cm, not 1cm.
cmeters *= 10
}
if s[0] == "" {
// This will allow omitting the 'meter' part, like .01 (meaning 0.01m = 1cm).
break
}
fallthrough fallthrough
case 1: case 1:
if meters, err = strconv.Atoi(s[0]); err != nil { if meters, err = strconv.Atoi(s[0]); err != nil {
return return
} }
// RFC1876 states the max value is 90000000.00. The latter two conditions enforce it.
if s[0][0] < '0' || s[0][0] > '9' || meters > 90000000 || (meters == 90000000 && cmeters != 0) {
return
}
case 0: case 0:
// huh? // huh?
return 0, 0, false return 0, 0, false
@ -1233,13 +1261,10 @@ func stringToCm(token string) (e, m uint8, ok bool) {
e = 0 e = 0
val = cmeters val = cmeters
} }
for val > 10 { for val >= 10 {
e++ e++
val /= 10 val /= 10
} }
if e > 9 {
ok = false
}
m = uint8(val) m = uint8(val)
return return
} }
@ -1281,6 +1306,9 @@ func appendOrigin(name, origin string) string {
// LOC record helper function // LOC record helper function
func locCheckNorth(token string, latitude uint32) (uint32, bool) { func locCheckNorth(token string, latitude uint32) (uint32, bool) {
if latitude > 90*1000*60*60 {
return latitude, false
}
switch token { switch token {
case "n", "N": case "n", "N":
return LOC_EQUATOR + latitude, true return LOC_EQUATOR + latitude, true
@ -1292,6 +1320,9 @@ func locCheckNorth(token string, latitude uint32) (uint32, bool) {
// LOC record helper function // LOC record helper function
func locCheckEast(token string, longitude uint32) (uint32, bool) { func locCheckEast(token string, longitude uint32) (uint32, bool) {
if longitude > 180*1000*60*60 {
return longitude, false
}
switch token { switch token {
case "e", "E": case "e", "E":
return LOC_EQUATOR + longitude, true return LOC_EQUATOR + longitude, true
@ -1302,18 +1333,18 @@ func locCheckEast(token string, longitude uint32) (uint32, bool) {
} }
// "Eat" the rest of the "line" // "Eat" the rest of the "line"
func slurpRemainder(c *zlexer, f string) *ParseError { func slurpRemainder(c *zlexer) *ParseError {
l, _ := c.Next() l, _ := c.Next()
switch l.value { switch l.value {
case zBlank: case zBlank:
l, _ = c.Next() l, _ = c.Next()
if l.value != zNewline && l.value != zEOF { if l.value != zNewline && l.value != zEOF {
return &ParseError{f, "garbage after rdata", l} return &ParseError{"", "garbage after rdata", l}
} }
case zNewline: case zNewline:
case zEOF: case zEOF:
default: default:
return &ParseError{f, "garbage after rdata", l} return &ParseError{"", "garbage after rdata", l}
} }
return nil return nil
} }
@ -1324,7 +1355,7 @@ func stringToNodeID(l lex) (uint64, *ParseError) {
if len(l.token) < 19 { if len(l.token) < 19 {
return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l} return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l}
} }
// There must be three colons at fixes postitions, if not its a parse error // There must be three colons at fixes positions, if not its a parse error
if l.token[4] != ':' && l.token[9] != ':' && l.token[14] != ':' { if l.token[4] != ':' && l.token[9] != ':' && l.token[14] != ':' {
return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l} return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l}
} }

1211
vendor/github.com/miekg/dns/scan_rr.go generated vendored

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,6 @@
package dns package dns
import ( import (
"strings"
"sync" "sync"
) )
@ -36,33 +35,9 @@ func (mux *ServeMux) match(q string, t uint16) Handler {
return nil return nil
} }
q = CanonicalName(q)
var handler Handler 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) { for off, end := 0, false; !end; off, end = NextLabel(q, off) {
if h, ok := mux.z[q[off:]]; ok { if h, ok := mux.z[q[off:]]; ok {
if t != TypeDS { if t != TypeDS {
@ -90,7 +65,7 @@ func (mux *ServeMux) Handle(pattern string, handler Handler) {
if mux.z == nil { if mux.z == nil {
mux.z = make(map[string]Handler) mux.z = make(map[string]Handler)
} }
mux.z[Fqdn(pattern)] = handler mux.z[CanonicalName(pattern)] = handler
mux.m.Unlock() mux.m.Unlock()
} }
@ -105,7 +80,7 @@ func (mux *ServeMux) HandleRemove(pattern string) {
panic("dns: invalid pattern " + pattern) panic("dns: invalid pattern " + pattern)
} }
mux.m.Lock() mux.m.Lock()
delete(mux.z, Fqdn(pattern)) delete(mux.z, CanonicalName(pattern))
mux.m.Unlock() mux.m.Unlock()
} }
@ -116,7 +91,7 @@ func (mux *ServeMux) HandleRemove(pattern string) {
// are redirected to the parent zone (if that is also registered), // are redirected to the parent zone (if that is also registered),
// otherwise the child gets the query. // otherwise the child gets the query.
// //
// If no handler is found, or there is no question, a standard SERVFAIL // If no handler is found, or there is no question, a standard REFUSED
// message is returned // message is returned
func (mux *ServeMux) ServeDNS(w ResponseWriter, req *Msg) { func (mux *ServeMux) ServeDNS(w ResponseWriter, req *Msg) {
var h Handler var h Handler
@ -127,7 +102,7 @@ func (mux *ServeMux) ServeDNS(w ResponseWriter, req *Msg) {
if h != nil { if h != nil {
h.ServeDNS(w, req) h.ServeDNS(w, req)
} else { } else {
HandleFailed(w, req) handleRefused(w, req)
} }
} }

342
vendor/github.com/miekg/dns/server.go generated vendored
View File

@ -3,7 +3,6 @@
package dns package dns
import ( import (
"bytes"
"context" "context"
"crypto/tls" "crypto/tls"
"encoding/binary" "encoding/binary"
@ -12,26 +11,12 @@ import (
"net" "net"
"strings" "strings"
"sync" "sync"
"sync/atomic"
"time" "time"
) )
// Default maximum number of TCP queries before we close the socket. // Default maximum number of TCP queries before we close the socket.
const maxTCPQueries = 128 const maxTCPQueries = 128
// The maximum number of idle workers.
//
// This controls the maximum number of workers that are allowed to stay
// idle waiting for incoming requests before being torn down.
//
// If this limit is reached, the server will just keep spawning new
// workers (goroutines) for each incoming request. In this case, each
// worker will only be used for a single request.
const maxIdleWorkersCount = 10000
// The maximum length of time a worker may idle for before being destroyed.
const idleWorkerTimeout = 10 * time.Second
// aLongTimeAgo is a non-zero time, far in the past, used for // aLongTimeAgo is a non-zero time, far in the past, used for
// immediate cancelation of network operations. // immediate cancelation of network operations.
var aLongTimeAgo = time.Unix(1, 0) var aLongTimeAgo = time.Unix(1, 0)
@ -81,21 +66,28 @@ type ConnectionStater interface {
} }
type response struct { type response struct {
msg []byte
closed bool // connection has been closed closed bool // connection has been closed
hijacked bool // connection has been hijacked by handler hijacked bool // connection has been hijacked by handler
tsigTimersOnly bool tsigTimersOnly bool
tsigStatus error tsigStatus error
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.PacketConn // 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
pcSession net.Addr // address to use when writing to a generic net.PacketConn
writer Writer // writer to output the raw DNS bits writer Writer // writer to output the raw DNS bits
wg *sync.WaitGroup // for gracefull shutdown }
// handleRefused returns a HandlerFunc that returns REFUSED for every request it gets.
func handleRefused(w ResponseWriter, r *Msg) {
m := new(Msg)
m.SetRcode(r, RcodeRefused)
w.WriteMsg(m)
} }
// HandleFailed returns a HandlerFunc that returns SERVFAIL for every request it gets. // HandleFailed returns a HandlerFunc that returns SERVFAIL for every request it gets.
// Deprecated: This function is going away.
func HandleFailed(w ResponseWriter, r *Msg) { func HandleFailed(w ResponseWriter, r *Msg) {
m := new(Msg) m := new(Msg)
m.SetRcode(r, RcodeServerFailure) m.SetRcode(r, RcodeServerFailure)
@ -156,12 +148,24 @@ type Reader interface {
ReadUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error) ReadUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error)
} }
// defaultReader is an adapter for the Server struct that implements the Reader interface // PacketConnReader is an optional interface that Readers can implement to support using generic net.PacketConns.
// using the readTCP and readUDP func of the embedded Server. type PacketConnReader interface {
Reader
// ReadPacketConn reads a raw message from a generic net.PacketConn UDP connection. Implementations may
// alter connection properties, for example the read-deadline.
ReadPacketConn(conn net.PacketConn, timeout time.Duration) ([]byte, net.Addr, error)
}
// defaultReader is an adapter for the Server struct that implements the Reader and
// PacketConnReader interfaces using the readTCP, readUDP and readPacketConn funcs
// of the embedded Server.
type defaultReader struct { type defaultReader struct {
*Server *Server
} }
var _ PacketConnReader = defaultReader{}
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)
} }
@ -170,8 +174,14 @@ func (dr defaultReader) ReadUDP(conn *net.UDPConn, timeout time.Duration) ([]byt
return dr.readUDP(conn, timeout) return dr.readUDP(conn, timeout)
} }
func (dr defaultReader) ReadPacketConn(conn net.PacketConn, timeout time.Duration) ([]byte, net.Addr, error) {
return dr.readPacketConn(conn, timeout)
}
// DecorateReader is a decorator hook for extending or supplanting the functionality of a Reader. // DecorateReader is a decorator hook for extending or supplanting the functionality of a Reader.
// Implementations should never return a nil Reader. // Implementations should never return a nil Reader.
// Readers should also implement the optional PacketConnReader interface.
// PacketConnReader is required to use a generic net.PacketConn.
type DecorateReader func(Reader) Reader type DecorateReader func(Reader) Reader
// DecorateWriter is a decorator hook for extending or supplanting the functionality of a Writer. // DecorateWriter is a decorator hook for extending or supplanting the functionality of a Writer.
@ -218,11 +228,6 @@ type Server struct {
// By default DefaultMsgAcceptFunc will be used. // By default DefaultMsgAcceptFunc will be used.
MsgAcceptFunc MsgAcceptFunc MsgAcceptFunc MsgAcceptFunc
// UDP packet or TCP connection queue
queue chan *response
// Workers count
workersCount int32
// Shutdown handling // Shutdown handling
lock sync.RWMutex lock sync.RWMutex
started bool started bool
@ -240,51 +245,6 @@ func (srv *Server) isStarted() bool {
return started return started
} }
func (srv *Server) worker(w *response) {
srv.serve(w)
for {
count := atomic.LoadInt32(&srv.workersCount)
if count > maxIdleWorkersCount {
return
}
if atomic.CompareAndSwapInt32(&srv.workersCount, count, count+1) {
break
}
}
defer atomic.AddInt32(&srv.workersCount, -1)
inUse := false
timeout := time.NewTimer(idleWorkerTimeout)
defer timeout.Stop()
LOOP:
for {
select {
case w, ok := <-srv.queue:
if !ok {
break LOOP
}
inUse = true
srv.serve(w)
case <-timeout.C:
if !inUse {
break LOOP
}
inUse = false
timeout.Reset(idleWorkerTimeout)
}
}
}
func (srv *Server) spawnWorker(w *response) {
select {
case srv.queue <- w:
default:
go srv.worker(w)
}
}
func makeUDPBuffer(size int) func() interface{} { func makeUDPBuffer(size int) func() interface{} {
return func() interface{} { return func() interface{} {
return make([]byte, size) return make([]byte, size)
@ -292,8 +252,6 @@ func makeUDPBuffer(size int) func() interface{} {
} }
func (srv *Server) init() { func (srv *Server) init() {
srv.queue = make(chan *response)
srv.shutdown = make(chan struct{}) srv.shutdown = make(chan struct{})
srv.conns = make(map[net.Conn]struct{}) srv.conns = make(map[net.Conn]struct{})
@ -301,7 +259,10 @@ func (srv *Server) init() {
srv.UDPSize = MinMsgSize srv.UDPSize = MinMsgSize
} }
if srv.MsgAcceptFunc == nil { if srv.MsgAcceptFunc == nil {
srv.MsgAcceptFunc = defaultMsgAcceptFunc srv.MsgAcceptFunc = DefaultMsgAcceptFunc
}
if srv.Handler == nil {
srv.Handler = DefaultServeMux
} }
srv.udpPool.New = makeUDPBuffer(srv.UDPSize) srv.udpPool.New = makeUDPBuffer(srv.UDPSize)
@ -328,7 +289,6 @@ func (srv *Server) ListenAndServe() error {
} }
srv.init() srv.init()
defer close(srv.queue)
switch srv.Net { switch srv.Net {
case "tcp", "tcp4", "tcp6": case "tcp", "tcp4", "tcp6":
@ -361,6 +321,7 @@ func (srv *Server) ListenAndServe() error {
} }
u := l.(*net.UDPConn) u := l.(*net.UDPConn)
if e := setUDPSocketOptions(u); e != nil { if e := setUDPSocketOptions(u); e != nil {
u.Close()
return e return e
} }
srv.PacketConn = l srv.PacketConn = l
@ -383,26 +344,23 @@ func (srv *Server) ActivateAndServe() error {
} }
srv.init() srv.init()
defer close(srv.queue)
pConn := srv.PacketConn if srv.PacketConn != nil {
l := srv.Listener
if pConn != nil {
// 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 := srv.PacketConn.(*net.UDPConn); ok && t != nil {
if e := setUDPSocketOptions(t); e != nil { if e := setUDPSocketOptions(t); e != nil {
return e return e
} }
}
srv.started = true srv.started = true
unlock() unlock()
return srv.serveUDP(t) return srv.serveUDP(srv.PacketConn)
} }
} if srv.Listener != nil {
if l != nil {
srv.started = true srv.started = true
unlock() unlock()
return srv.serveTCP(l) return srv.serveTCP(srv.Listener)
} }
return &Error{err: "bad listeners"} return &Error{err: "bad listeners"}
} }
@ -499,29 +457,31 @@ func (srv *Server) serveTCP(l net.Listener) error {
srv.conns[rw] = struct{}{} srv.conns[rw] = struct{}{}
srv.lock.Unlock() srv.lock.Unlock()
wg.Add(1) wg.Add(1)
srv.spawnWorker(&response{ go srv.serveTCPConn(&wg, rw)
tsigSecret: srv.TsigSecret,
tcp: rw,
wg: &wg,
})
} }
return nil return nil
} }
// serveUDP starts a UDP listener for the server. // serveUDP starts a UDP listener for the server.
func (srv *Server) serveUDP(l *net.UDPConn) error { func (srv *Server) serveUDP(l net.PacketConn) error {
defer l.Close() defer l.Close()
if srv.NotifyStartedFunc != nil {
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)
} }
lUDP, isUDP := l.(*net.UDPConn)
readerPC, canPacketConn := reader.(PacketConnReader)
if !isUDP && !canPacketConn {
return &Error{err: "PacketConnReader was not implemented on Reader returned from DecorateReader but is required for net.PacketConn"}
}
if srv.NotifyStartedFunc != nil {
srv.NotifyStartedFunc()
}
var wg sync.WaitGroup var wg sync.WaitGroup
defer func() { defer func() {
wg.Wait() wg.Wait()
@ -531,7 +491,17 @@ func (srv *Server) serveUDP(l *net.UDPConn) error {
rtimeout := srv.getReadTimeout() rtimeout := srv.getReadTimeout()
// deadline is not used here // deadline is not used here
for srv.isStarted() { for srv.isStarted() {
m, s, err := reader.ReadUDP(l, rtimeout) var (
m []byte
sPC net.Addr
sUDP *SessionUDP
err error
)
if isUDP {
m, sUDP, err = reader.ReadUDP(lUDP, rtimeout)
} else {
m, sPC, err = readerPC.ReadPacketConn(l, rtimeout)
}
if err != nil { if err != nil {
if !srv.isStarted() { if !srv.isStarted() {
return nil return nil
@ -548,45 +518,21 @@ func (srv *Server) serveUDP(l *net.UDPConn) error {
continue continue
} }
wg.Add(1) wg.Add(1)
srv.spawnWorker(&response{ go srv.serveUDPPacket(&wg, m, l, sUDP, sPC)
msg: m,
tsigSecret: srv.TsigSecret,
udp: l,
udpSession: s,
wg: &wg,
})
} }
return nil return nil
} }
func (srv *Server) serve(w *response) { // Serve a new TCP connection.
func (srv *Server) serveTCPConn(wg *sync.WaitGroup, rw net.Conn) {
w := &response{tsigSecret: srv.TsigSecret, tcp: rw}
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
} }
if w.udp != nil {
// serve UDP
srv.serveDNS(w)
w.wg.Done()
return
}
defer func() {
if !w.hijacked {
w.Close()
}
srv.lock.Lock()
delete(srv.conns, w.tcp)
srv.lock.Unlock()
w.wg.Done()
}()
reader := Reader(defaultReader{srv}) reader := Reader(defaultReader{srv})
if srv.DecorateReader != nil { if srv.DecorateReader != nil {
reader = srv.DecorateReader(reader) reader = srv.DecorateReader(reader)
@ -605,14 +551,13 @@ func (srv *Server) serve(w *response) {
} }
for q := 0; (q < limit || limit == -1) && srv.isStarted(); q++ { for q := 0; (q < limit || limit == -1) && srv.isStarted(); q++ {
var err error m, err := reader.ReadTCP(w.tcp, timeout)
w.msg, err = reader.ReadTCP(w.tcp, timeout)
if err != nil { if err != nil {
// TODO(tmthrgd): handle error // TODO(tmthrgd): handle error
break break
} }
srv.serveDNS(w) srv.serveDNS(m, w)
if w.tcp == nil { if w.closed {
break // Close() was called break // Close() was called
} }
if w.hijacked { if w.hijacked {
@ -622,17 +567,33 @@ func (srv *Server) serve(w *response) {
// idle timeout. // idle timeout.
timeout = idleTimeout timeout = idleTimeout
} }
if !w.hijacked {
w.Close()
} }
func (srv *Server) disposeBuffer(w *response) { srv.lock.Lock()
if w.udp != nil && cap(w.msg) == srv.UDPSize { delete(srv.conns, w.tcp)
srv.udpPool.Put(w.msg[:srv.UDPSize]) srv.lock.Unlock()
}
w.msg = nil wg.Done()
} }
func (srv *Server) serveDNS(w *response) { // Serve a new UDP request.
dh, off, err := unpackMsgHdr(w.msg, 0) func (srv *Server) serveUDPPacket(wg *sync.WaitGroup, m []byte, u net.PacketConn, udpSession *SessionUDP, pcSession net.Addr) {
w := &response{tsigSecret: srv.TsigSecret, udp: u, udpSession: udpSession, pcSession: pcSession}
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 { if err != nil {
// Let client hang, they are sending crap; any reply can be used to amplify. // Let client hang, they are sending crap; any reply can be used to amplify.
return return
@ -641,26 +602,32 @@ func (srv *Server) serveDNS(w *response) {
req := new(Msg) req := new(Msg)
req.setHdr(dh) req.setHdr(dh)
switch srv.MsgAcceptFunc(dh) { switch action := srv.MsgAcceptFunc(dh); action {
case MsgAccept: case MsgAccept:
case MsgIgnore: if req.unpack(dh, m, off) == nil {
return break
case MsgReject: }
fallthrough
case MsgReject, MsgRejectNotImplemented:
opcode := req.Opcode
req.SetRcodeFormatError(req) req.SetRcodeFormatError(req)
req.Zero = false
if action == MsgRejectNotImplemented {
req.Opcode = opcode
req.Rcode = RcodeNotImplemented
}
// Are we allowed to delete any OPT records here? // Are we allowed to delete any OPT records here?
req.Ns, req.Answer, req.Extra = nil, nil, nil req.Ns, req.Answer, req.Extra = nil, nil, nil
w.WriteMsg(req) w.WriteMsg(req)
srv.disposeBuffer(w) fallthrough
return case MsgIgnore:
if w.udp != nil && cap(m) == srv.UDPSize {
srv.udpPool.Put(m[:srv.UDPSize])
} }
if err := req.unpack(dh, w.msg, off); err != nil {
req.SetRcodeFormatError(req)
req.Ns, req.Answer, req.Extra = nil, nil, nil
w.WriteMsg(req)
srv.disposeBuffer(w)
return return
} }
@ -668,7 +635,7 @@ func (srv *Server) serveDNS(w *response) {
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 { if secret, ok := w.tsigSecret[t.Hdr.Name]; ok {
w.tsigStatus = TsigVerify(w.msg, secret, "", false) w.tsigStatus = TsigVerify(m, secret, "", false)
} else { } else {
w.tsigStatus = ErrSecret w.tsigStatus = ErrSecret
} }
@ -677,14 +644,11 @@ func (srv *Server) serveDNS(w *response) {
} }
} }
srv.disposeBuffer(w) if w.udp != nil && cap(m) == srv.UDPSize {
srv.udpPool.Put(m[:srv.UDPSize])
handler := srv.Handler
if handler == nil {
handler = DefaultServeMux
} }
handler.ServeDNS(w, req) // Writes back to the client srv.Handler.ServeDNS(w, req) // Writes back to the client
} }
func (srv *Server) readTCP(conn net.Conn, timeout time.Duration) ([]byte, error) { func (srv *Server) readTCP(conn net.Conn, timeout time.Duration) ([]byte, error) {
@ -698,36 +662,16 @@ func (srv *Server) readTCP(conn net.Conn, timeout time.Duration) ([]byte, error)
} }
srv.lock.RUnlock() srv.lock.RUnlock()
l := make([]byte, 2) var length uint16
n, err := conn.Read(l) if err := binary.Read(conn, binary.BigEndian, &length); err != nil {
if err != nil || n != 2 {
if err != nil {
return nil, err return nil, err
} }
return nil, ErrShortRead
} m := make([]byte, length)
length := binary.BigEndian.Uint16(l) if _, err := io.ReadFull(conn, m); err != nil {
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
} }
@ -749,6 +693,24 @@ func (srv *Server) readUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *S
return m, s, nil return m, s, nil
} }
func (srv *Server) readPacketConn(conn net.PacketConn, timeout time.Duration) ([]byte, net.Addr, error) {
srv.lock.RLock()
if srv.started {
// See the comment in readTCP above.
conn.SetReadDeadline(time.Now().Add(timeout))
}
srv.lock.RUnlock()
m := srv.udpPool.Get().([]byte)
n, addr, err := conn.ReadFrom(m)
if err != nil {
srv.udpPool.Put(m)
return nil, nil, err
}
m = m[:n]
return m, addr, 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 { if w.closed {
@ -782,21 +744,19 @@ func (w *response) Write(m []byte) (int, error) {
switch { switch {
case w.udp != nil: case w.udp != nil:
return WriteToSessionUDP(w.udp, m, w.udpSession) if u, ok := w.udp.(*net.UDPConn); ok {
case w.tcp != nil: return WriteToSessionUDP(u, m, w.udpSession)
lm := len(m)
if lm < 2 {
return 0, io.ErrShortBuffer
} }
if lm > MaxMsgSize { return w.udp.WriteTo(m, w.pcSession)
case w.tcp != nil:
if len(m) > 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...)
n, err := io.Copy(w.tcp, bytes.NewReader(m)) msg := make([]byte, 2+len(m))
return int(n), err binary.BigEndian.PutUint16(msg, uint16(len(m)))
copy(msg[2:], m)
return w.tcp.Write(msg)
default: default:
panic("dns: internal error: udp and tcp both nil") panic("dns: internal error: udp and tcp both nil")
} }
@ -819,10 +779,12 @@ func (w *response) RemoteAddr() net.Addr {
switch { switch {
case w.udpSession != nil: case w.udpSession != nil:
return w.udpSession.RemoteAddr() return w.udpSession.RemoteAddr()
case w.pcSession != nil:
return w.pcSession
case w.tcp != nil: case w.tcp != nil:
return w.tcp.RemoteAddr() return w.tcp.RemoteAddr()
default: default:
panic("dns: internal error: udpSession and tcp both nil") panic("dns: internal error: udpSession, pcSession and tcp are all nil")
} }
} }

26
vendor/github.com/miekg/dns/sig0.go generated vendored
View File

@ -2,7 +2,6 @@ package dns
import ( import (
"crypto" "crypto"
"crypto/dsa"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/rsa" "crypto/rsa"
"encoding/binary" "encoding/binary"
@ -18,7 +17,7 @@ func (rr *SIG) Sign(k crypto.Signer, m *Msg) ([]byte, error) {
if k == nil { if k == nil {
return nil, ErrPrivKey return nil, ErrPrivKey
} }
if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 { if rr.KeyTag == 0 || rr.SignerName == "" || rr.Algorithm == 0 {
return nil, ErrKey return nil, ErrKey
} }
@ -79,13 +78,13 @@ func (rr *SIG) Verify(k *KEY, buf []byte) error {
if k == nil { if k == nil {
return ErrKey return ErrKey
} }
if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 { if rr.KeyTag == 0 || rr.SignerName == "" || rr.Algorithm == 0 {
return ErrKey return ErrKey
} }
var hash crypto.Hash var hash crypto.Hash
switch rr.Algorithm { switch rr.Algorithm {
case DSA, RSASHA1: case RSASHA1:
hash = crypto.SHA1 hash = crypto.SHA1
case RSASHA256, ECDSAP256SHA256: case RSASHA256, ECDSAP256SHA256:
hash = crypto.SHA256 hash = crypto.SHA256
@ -178,19 +177,6 @@ func (rr *SIG) Verify(k *KEY, buf []byte) error {
hashed := hasher.Sum(nil) hashed := hasher.Sum(nil)
sig := buf[sigend:] sig := buf[sigend:]
switch k.Algorithm { switch k.Algorithm {
case DSA:
pk := k.publicKeyDSA()
sig = sig[1:]
r := big.NewInt(0)
r.SetBytes(sig[:len(sig)/2])
s := big.NewInt(0)
s.SetBytes(sig[len(sig)/2:])
if pk != nil {
if dsa.Verify(pk, hashed, r, s) {
return nil
}
return ErrSig
}
case RSASHA1, RSASHA256, RSASHA512: case RSASHA1, RSASHA256, RSASHA512:
pk := k.publicKeyRSA() pk := k.publicKeyRSA()
if pk != nil { if pk != nil {
@ -198,10 +184,8 @@ func (rr *SIG) Verify(k *KEY, buf []byte) error {
} }
case ECDSAP256SHA256, ECDSAP384SHA384: case ECDSAP256SHA256, ECDSAP384SHA384:
pk := k.publicKeyECDSA() pk := k.publicKeyECDSA()
r := big.NewInt(0) r := new(big.Int).SetBytes(sig[:len(sig)/2])
r.SetBytes(sig[:len(sig)/2]) s := new(big.Int).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

755
vendor/github.com/miekg/dns/svcb.go generated vendored Normal file
View File

@ -0,0 +1,755 @@
package dns
import (
"bytes"
"encoding/binary"
"errors"
"net"
"sort"
"strconv"
"strings"
)
// SVCBKey is the type of the keys used in the SVCB RR.
type SVCBKey uint16
// Keys defined in draft-ietf-dnsop-svcb-https-01 Section 12.3.2.
const (
SVCB_MANDATORY SVCBKey = 0
SVCB_ALPN SVCBKey = 1
SVCB_NO_DEFAULT_ALPN SVCBKey = 2
SVCB_PORT SVCBKey = 3
SVCB_IPV4HINT SVCBKey = 4
SVCB_ECHCONFIG SVCBKey = 5
SVCB_IPV6HINT SVCBKey = 6
svcb_RESERVED SVCBKey = 65535
)
var svcbKeyToStringMap = map[SVCBKey]string{
SVCB_MANDATORY: "mandatory",
SVCB_ALPN: "alpn",
SVCB_NO_DEFAULT_ALPN: "no-default-alpn",
SVCB_PORT: "port",
SVCB_IPV4HINT: "ipv4hint",
SVCB_ECHCONFIG: "echconfig",
SVCB_IPV6HINT: "ipv6hint",
}
var svcbStringToKeyMap = reverseSVCBKeyMap(svcbKeyToStringMap)
func reverseSVCBKeyMap(m map[SVCBKey]string) map[string]SVCBKey {
n := make(map[string]SVCBKey, len(m))
for u, s := range m {
n[s] = u
}
return n
}
// String takes the numerical code of an SVCB key and returns its name.
// Returns an empty string for reserved keys.
// Accepts unassigned keys as well as experimental/private keys.
func (key SVCBKey) String() string {
if x := svcbKeyToStringMap[key]; x != "" {
return x
}
if key == svcb_RESERVED {
return ""
}
return "key" + strconv.FormatUint(uint64(key), 10)
}
// svcbStringToKey returns the numerical code of an SVCB key.
// Returns svcb_RESERVED for reserved/invalid keys.
// Accepts unassigned keys as well as experimental/private keys.
func svcbStringToKey(s string) SVCBKey {
if strings.HasPrefix(s, "key") {
a, err := strconv.ParseUint(s[3:], 10, 16)
// no leading zeros
// key shouldn't be registered
if err != nil || a == 65535 || s[3] == '0' || svcbKeyToStringMap[SVCBKey(a)] != "" {
return svcb_RESERVED
}
return SVCBKey(a)
}
if key, ok := svcbStringToKeyMap[s]; ok {
return key
}
return svcb_RESERVED
}
func (rr *SVCB) parse(c *zlexer, o string) *ParseError {
l, _ := c.Next()
i, e := strconv.ParseUint(l.token, 10, 16)
if e != nil || l.err {
return &ParseError{l.token, "bad SVCB priority", l}
}
rr.Priority = uint16(i)
c.Next() // zBlank
l, _ = c.Next() // zString
rr.Target = l.token
name, nameOk := toAbsoluteName(l.token, o)
if l.err || !nameOk {
return &ParseError{l.token, "bad SVCB Target", l}
}
rr.Target = name
// Values (if any)
l, _ = c.Next()
var xs []SVCBKeyValue
// Helps require whitespace between pairs.
// Prevents key1000="a"key1001=...
canHaveNextKey := true
for l.value != zNewline && l.value != zEOF {
switch l.value {
case zString:
if !canHaveNextKey {
// The key we can now read was probably meant to be
// a part of the last value.
return &ParseError{l.token, "bad SVCB value quotation", l}
}
// In key=value pairs, value does not have to be quoted unless value
// contains whitespace. And keys don't need to have values.
// Similarly, keys with an equality signs after them don't need values.
// l.token includes at least up to the first equality sign.
idx := strings.IndexByte(l.token, '=')
var key, value string
if idx < 0 {
// Key with no value and no equality sign
key = l.token
} else if idx == 0 {
return &ParseError{l.token, "bad SVCB key", l}
} else {
key, value = l.token[:idx], l.token[idx+1:]
if value == "" {
// We have a key and an equality sign. Maybe we have nothing
// after "=" or we have a double quote.
l, _ = c.Next()
if l.value == zQuote {
// Only needed when value ends with double quotes.
// Any value starting with zQuote ends with it.
canHaveNextKey = false
l, _ = c.Next()
switch l.value {
case zString:
// We have a value in double quotes.
value = l.token
l, _ = c.Next()
if l.value != zQuote {
return &ParseError{l.token, "SVCB unterminated value", l}
}
case zQuote:
// There's nothing in double quotes.
default:
return &ParseError{l.token, "bad SVCB value", l}
}
}
}
}
kv := makeSVCBKeyValue(svcbStringToKey(key))
if kv == nil {
return &ParseError{l.token, "bad SVCB key", l}
}
if err := kv.parse(value); err != nil {
return &ParseError{l.token, err.Error(), l}
}
xs = append(xs, kv)
case zQuote:
return &ParseError{l.token, "SVCB key can't contain double quotes", l}
case zBlank:
canHaveNextKey = true
default:
return &ParseError{l.token, "bad SVCB values", l}
}
l, _ = c.Next()
}
rr.Value = xs
if rr.Priority == 0 && len(xs) > 0 {
return &ParseError{l.token, "SVCB aliasform can't have values", l}
}
return nil
}
// makeSVCBKeyValue returns an SVCBKeyValue struct with the key or nil for reserved keys.
func makeSVCBKeyValue(key SVCBKey) SVCBKeyValue {
switch key {
case SVCB_MANDATORY:
return new(SVCBMandatory)
case SVCB_ALPN:
return new(SVCBAlpn)
case SVCB_NO_DEFAULT_ALPN:
return new(SVCBNoDefaultAlpn)
case SVCB_PORT:
return new(SVCBPort)
case SVCB_IPV4HINT:
return new(SVCBIPv4Hint)
case SVCB_ECHCONFIG:
return new(SVCBECHConfig)
case SVCB_IPV6HINT:
return new(SVCBIPv6Hint)
case svcb_RESERVED:
return nil
default:
e := new(SVCBLocal)
e.KeyCode = key
return e
}
}
// SVCB RR. See RFC xxxx (https://tools.ietf.org/html/draft-ietf-dnsop-svcb-https-01).
type SVCB struct {
Hdr RR_Header
Priority uint16
Target string `dns:"domain-name"`
Value []SVCBKeyValue `dns:"pairs"` // Value must be empty if Priority is zero.
}
// HTTPS RR. Everything valid for SVCB applies to HTTPS as well.
// Except that the HTTPS record is intended for use with the HTTP and HTTPS protocols.
type HTTPS struct {
SVCB
}
func (rr *HTTPS) String() string {
return rr.SVCB.String()
}
func (rr *HTTPS) parse(c *zlexer, o string) *ParseError {
return rr.SVCB.parse(c, o)
}
// SVCBKeyValue defines a key=value pair for the SVCB RR type.
// An SVCB RR can have multiple SVCBKeyValues appended to it.
type SVCBKeyValue interface {
Key() SVCBKey // Key returns the numerical key code.
pack() ([]byte, error) // pack returns the encoded value.
unpack([]byte) error // unpack sets the value.
String() string // String returns the string representation of the value.
parse(string) error // parse sets the value to the given string representation of the value.
copy() SVCBKeyValue // copy returns a deep-copy of the pair.
len() int // len returns the length of value in the wire format.
}
// SVCBMandatory pair adds to required keys that must be interpreted for the RR
// to be functional.
// Basic use pattern for creating a mandatory option:
//
// s := &dns.SVCB{Hdr: dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}}
// e := new(dns.SVCBMandatory)
// e.Code = []uint16{65403}
// s.Value = append(s.Value, e)
type SVCBMandatory struct {
Code []SVCBKey // Must not include mandatory
}
func (*SVCBMandatory) Key() SVCBKey { return SVCB_MANDATORY }
func (s *SVCBMandatory) String() string {
str := make([]string, len(s.Code))
for i, e := range s.Code {
str[i] = e.String()
}
return strings.Join(str, ",")
}
func (s *SVCBMandatory) pack() ([]byte, error) {
codes := append([]SVCBKey(nil), s.Code...)
sort.Slice(codes, func(i, j int) bool {
return codes[i] < codes[j]
})
b := make([]byte, 2*len(codes))
for i, e := range codes {
binary.BigEndian.PutUint16(b[2*i:], uint16(e))
}
return b, nil
}
func (s *SVCBMandatory) unpack(b []byte) error {
if len(b)%2 != 0 {
return errors.New("dns: svcbmandatory: value length is not a multiple of 2")
}
codes := make([]SVCBKey, 0, len(b)/2)
for i := 0; i < len(b); i += 2 {
// We assume strictly increasing order.
codes = append(codes, SVCBKey(binary.BigEndian.Uint16(b[i:])))
}
s.Code = codes
return nil
}
func (s *SVCBMandatory) parse(b string) error {
str := strings.Split(b, ",")
codes := make([]SVCBKey, 0, len(str))
for _, e := range str {
codes = append(codes, svcbStringToKey(e))
}
s.Code = codes
return nil
}
func (s *SVCBMandatory) len() int {
return 2 * len(s.Code)
}
func (s *SVCBMandatory) copy() SVCBKeyValue {
return &SVCBMandatory{
append([]SVCBKey(nil), s.Code...),
}
}
// SVCBAlpn pair is used to list supported connection protocols.
// Protocol ids can be found at:
// https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
// Basic use pattern for creating an alpn option:
//
// h := new(dns.HTTPS)
// h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}
// e := new(dns.SVCBAlpn)
// e.Alpn = []string{"h2", "http/1.1"}
// h.Value = append(o.Value, e)
type SVCBAlpn struct {
Alpn []string
}
func (*SVCBAlpn) Key() SVCBKey { return SVCB_ALPN }
func (s *SVCBAlpn) String() string { return strings.Join(s.Alpn, ",") }
func (s *SVCBAlpn) pack() ([]byte, error) {
// Liberally estimate the size of an alpn as 10 octets
b := make([]byte, 0, 10*len(s.Alpn))
for _, e := range s.Alpn {
if e == "" {
return nil, errors.New("dns: svcbalpn: empty alpn-id")
}
if len(e) > 255 {
return nil, errors.New("dns: svcbalpn: alpn-id too long")
}
b = append(b, byte(len(e)))
b = append(b, e...)
}
return b, nil
}
func (s *SVCBAlpn) unpack(b []byte) error {
// Estimate the size of the smallest alpn as 4 bytes
alpn := make([]string, 0, len(b)/4)
for i := 0; i < len(b); {
length := int(b[i])
i++
if i+length > len(b) {
return errors.New("dns: svcbalpn: alpn array overflowing")
}
alpn = append(alpn, string(b[i:i+length]))
i += length
}
s.Alpn = alpn
return nil
}
func (s *SVCBAlpn) parse(b string) error {
s.Alpn = strings.Split(b, ",")
return nil
}
func (s *SVCBAlpn) len() int {
var l int
for _, e := range s.Alpn {
l += 1 + len(e)
}
return l
}
func (s *SVCBAlpn) copy() SVCBKeyValue {
return &SVCBAlpn{
append([]string(nil), s.Alpn...),
}
}
// SVCBNoDefaultAlpn pair signifies no support for default connection protocols.
// Basic use pattern for creating a no-default-alpn option:
//
// s := &dns.SVCB{Hdr: dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}}
// e := new(dns.SVCBNoDefaultAlpn)
// s.Value = append(s.Value, e)
type SVCBNoDefaultAlpn struct{}
func (*SVCBNoDefaultAlpn) Key() SVCBKey { return SVCB_NO_DEFAULT_ALPN }
func (*SVCBNoDefaultAlpn) copy() SVCBKeyValue { return &SVCBNoDefaultAlpn{} }
func (*SVCBNoDefaultAlpn) pack() ([]byte, error) { return []byte{}, nil }
func (*SVCBNoDefaultAlpn) String() string { return "" }
func (*SVCBNoDefaultAlpn) len() int { return 0 }
func (*SVCBNoDefaultAlpn) unpack(b []byte) error {
if len(b) != 0 {
return errors.New("dns: svcbnodefaultalpn: no_default_alpn must have no value")
}
return nil
}
func (*SVCBNoDefaultAlpn) parse(b string) error {
if b != "" {
return errors.New("dns: svcbnodefaultalpn: no_default_alpn must have no value")
}
return nil
}
// SVCBPort pair defines the port for connection.
// Basic use pattern for creating a port option:
//
// s := &dns.SVCB{Hdr: dns.RR_Header{Name: ".", Rrtype: dns.TypeSVCB, Class: dns.ClassINET}}
// e := new(dns.SVCBPort)
// e.Port = 80
// s.Value = append(s.Value, e)
type SVCBPort struct {
Port uint16
}
func (*SVCBPort) Key() SVCBKey { return SVCB_PORT }
func (*SVCBPort) len() int { return 2 }
func (s *SVCBPort) String() string { return strconv.FormatUint(uint64(s.Port), 10) }
func (s *SVCBPort) copy() SVCBKeyValue { return &SVCBPort{s.Port} }
func (s *SVCBPort) unpack(b []byte) error {
if len(b) != 2 {
return errors.New("dns: svcbport: port length is not exactly 2 octets")
}
s.Port = binary.BigEndian.Uint16(b)
return nil
}
func (s *SVCBPort) pack() ([]byte, error) {
b := make([]byte, 2)
binary.BigEndian.PutUint16(b, s.Port)
return b, nil
}
func (s *SVCBPort) parse(b string) error {
port, err := strconv.ParseUint(b, 10, 16)
if err != nil {
return errors.New("dns: svcbport: port out of range")
}
s.Port = uint16(port)
return nil
}
// SVCBIPv4Hint pair suggests an IPv4 address which may be used to open connections
// if A and AAAA record responses for SVCB's Target domain haven't been received.
// In that case, optionally, A and AAAA requests can be made, after which the connection
// to the hinted IP address may be terminated and a new connection may be opened.
// Basic use pattern for creating an ipv4hint option:
//
// h := new(dns.HTTPS)
// h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}
// e := new(dns.SVCBIPv4Hint)
// e.Hint = []net.IP{net.IPv4(1,1,1,1).To4()}
//
// Or
//
// e.Hint = []net.IP{net.ParseIP("1.1.1.1").To4()}
// h.Value = append(h.Value, e)
type SVCBIPv4Hint struct {
Hint []net.IP
}
func (*SVCBIPv4Hint) Key() SVCBKey { return SVCB_IPV4HINT }
func (s *SVCBIPv4Hint) len() int { return 4 * len(s.Hint) }
func (s *SVCBIPv4Hint) pack() ([]byte, error) {
b := make([]byte, 0, 4*len(s.Hint))
for _, e := range s.Hint {
x := e.To4()
if x == nil {
return nil, errors.New("dns: svcbipv4hint: expected ipv4, hint is ipv6")
}
b = append(b, x...)
}
return b, nil
}
func (s *SVCBIPv4Hint) unpack(b []byte) error {
if len(b) == 0 || len(b)%4 != 0 {
return errors.New("dns: svcbipv4hint: ipv4 address byte array length is not a multiple of 4")
}
x := make([]net.IP, 0, len(b)/4)
for i := 0; i < len(b); i += 4 {
x = append(x, net.IP(b[i:i+4]))
}
s.Hint = x
return nil
}
func (s *SVCBIPv4Hint) String() string {
str := make([]string, len(s.Hint))
for i, e := range s.Hint {
x := e.To4()
if x == nil {
return "<nil>"
}
str[i] = x.String()
}
return strings.Join(str, ",")
}
func (s *SVCBIPv4Hint) parse(b string) error {
if strings.Contains(b, ":") {
return errors.New("dns: svcbipv4hint: expected ipv4, got ipv6")
}
str := strings.Split(b, ",")
dst := make([]net.IP, len(str))
for i, e := range str {
ip := net.ParseIP(e).To4()
if ip == nil {
return errors.New("dns: svcbipv4hint: bad ip")
}
dst[i] = ip
}
s.Hint = dst
return nil
}
func (s *SVCBIPv4Hint) copy() SVCBKeyValue {
hint := make([]net.IP, len(s.Hint))
for i, ip := range s.Hint {
hint[i] = copyIP(ip)
}
return &SVCBIPv4Hint{
Hint: hint,
}
}
// SVCBECHConfig pair contains the ECHConfig structure defined in draft-ietf-tls-esni [RFC xxxx].
// Basic use pattern for creating an echconfig option:
//
// h := new(dns.HTTPS)
// h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}
// e := new(dns.SVCBECHConfig)
// e.ECH = []byte{0xfe, 0x08, ...}
// h.Value = append(h.Value, e)
type SVCBECHConfig struct {
ECH []byte
}
func (*SVCBECHConfig) Key() SVCBKey { return SVCB_ECHCONFIG }
func (s *SVCBECHConfig) String() string { return toBase64(s.ECH) }
func (s *SVCBECHConfig) len() int { return len(s.ECH) }
func (s *SVCBECHConfig) pack() ([]byte, error) {
return append([]byte(nil), s.ECH...), nil
}
func (s *SVCBECHConfig) copy() SVCBKeyValue {
return &SVCBECHConfig{
append([]byte(nil), s.ECH...),
}
}
func (s *SVCBECHConfig) unpack(b []byte) error {
s.ECH = append([]byte(nil), b...)
return nil
}
func (s *SVCBECHConfig) parse(b string) error {
x, err := fromBase64([]byte(b))
if err != nil {
return errors.New("dns: svcbechconfig: bad base64 echconfig")
}
s.ECH = x
return nil
}
// SVCBIPv6Hint pair suggests an IPv6 address which may be used to open connections
// if A and AAAA record responses for SVCB's Target domain haven't been received.
// In that case, optionally, A and AAAA requests can be made, after which the
// connection to the hinted IP address may be terminated and a new connection may be opened.
// Basic use pattern for creating an ipv6hint option:
//
// h := new(dns.HTTPS)
// h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}
// e := new(dns.SVCBIPv6Hint)
// e.Hint = []net.IP{net.ParseIP("2001:db8::1")}
// h.Value = append(h.Value, e)
type SVCBIPv6Hint struct {
Hint []net.IP
}
func (*SVCBIPv6Hint) Key() SVCBKey { return SVCB_IPV6HINT }
func (s *SVCBIPv6Hint) len() int { return 16 * len(s.Hint) }
func (s *SVCBIPv6Hint) pack() ([]byte, error) {
b := make([]byte, 0, 16*len(s.Hint))
for _, e := range s.Hint {
if len(e) != net.IPv6len || e.To4() != nil {
return nil, errors.New("dns: svcbipv6hint: expected ipv6, hint is ipv4")
}
b = append(b, e...)
}
return b, nil
}
func (s *SVCBIPv6Hint) unpack(b []byte) error {
if len(b) == 0 || len(b)%16 != 0 {
return errors.New("dns: svcbipv6hint: ipv6 address byte array length not a multiple of 16")
}
x := make([]net.IP, 0, len(b)/16)
for i := 0; i < len(b); i += 16 {
ip := net.IP(b[i : i+16])
if ip.To4() != nil {
return errors.New("dns: svcbipv6hint: expected ipv6, got ipv4")
}
x = append(x, ip)
}
s.Hint = x
return nil
}
func (s *SVCBIPv6Hint) String() string {
str := make([]string, len(s.Hint))
for i, e := range s.Hint {
if x := e.To4(); x != nil {
return "<nil>"
}
str[i] = e.String()
}
return strings.Join(str, ",")
}
func (s *SVCBIPv6Hint) parse(b string) error {
if strings.Contains(b, ".") {
return errors.New("dns: svcbipv6hint: expected ipv6, got ipv4")
}
str := strings.Split(b, ",")
dst := make([]net.IP, len(str))
for i, e := range str {
ip := net.ParseIP(e)
if ip == nil {
return errors.New("dns: svcbipv6hint: bad ip")
}
dst[i] = ip
}
s.Hint = dst
return nil
}
func (s *SVCBIPv6Hint) copy() SVCBKeyValue {
hint := make([]net.IP, len(s.Hint))
for i, ip := range s.Hint {
hint[i] = copyIP(ip)
}
return &SVCBIPv6Hint{
Hint: hint,
}
}
// SVCBLocal pair is intended for experimental/private use. The key is recommended
// to be in the range [SVCB_PRIVATE_LOWER, SVCB_PRIVATE_UPPER].
// Basic use pattern for creating a keyNNNNN option:
//
// h := new(dns.HTTPS)
// h.Hdr = dns.RR_Header{Name: ".", Rrtype: dns.TypeHTTPS, Class: dns.ClassINET}
// e := new(dns.SVCBLocal)
// e.KeyCode = 65400
// e.Data = []byte("abc")
// h.Value = append(h.Value, e)
type SVCBLocal struct {
KeyCode SVCBKey // Never 65535 or any assigned keys.
Data []byte // All byte sequences are allowed.
}
func (s *SVCBLocal) Key() SVCBKey { return s.KeyCode }
func (s *SVCBLocal) pack() ([]byte, error) { return append([]byte(nil), s.Data...), nil }
func (s *SVCBLocal) len() int { return len(s.Data) }
func (s *SVCBLocal) unpack(b []byte) error {
s.Data = append([]byte(nil), b...)
return nil
}
func (s *SVCBLocal) String() string {
var str strings.Builder
str.Grow(4 * len(s.Data))
for _, e := range s.Data {
if ' ' <= e && e <= '~' {
switch e {
case '"', ';', ' ', '\\':
str.WriteByte('\\')
str.WriteByte(e)
default:
str.WriteByte(e)
}
} else {
str.WriteString(escapeByte(e))
}
}
return str.String()
}
func (s *SVCBLocal) parse(b string) error {
data := make([]byte, 0, len(b))
for i := 0; i < len(b); {
if b[i] != '\\' {
data = append(data, b[i])
i++
continue
}
if i+1 == len(b) {
return errors.New("dns: svcblocal: svcb private/experimental key escape unterminated")
}
if isDigit(b[i+1]) {
if i+3 < len(b) && isDigit(b[i+2]) && isDigit(b[i+3]) {
a, err := strconv.ParseUint(b[i+1:i+4], 10, 8)
if err == nil {
i += 4
data = append(data, byte(a))
continue
}
}
return errors.New("dns: svcblocal: svcb private/experimental key bad escaped octet")
} else {
data = append(data, b[i+1])
i += 2
}
}
s.Data = data
return nil
}
func (s *SVCBLocal) copy() SVCBKeyValue {
return &SVCBLocal{s.KeyCode,
append([]byte(nil), s.Data...),
}
}
func (rr *SVCB) String() string {
s := rr.Hdr.String() +
strconv.Itoa(int(rr.Priority)) + " " +
sprintName(rr.Target)
for _, e := range rr.Value {
s += " " + e.Key().String() + "=\"" + e.String() + "\""
}
return s
}
// areSVCBPairArraysEqual checks if SVCBKeyValue arrays are equal after sorting their
// copies. arrA and arrB have equal lengths, otherwise zduplicate.go wouldn't call this function.
func areSVCBPairArraysEqual(a []SVCBKeyValue, b []SVCBKeyValue) bool {
a = append([]SVCBKeyValue(nil), a...)
b = append([]SVCBKeyValue(nil), b...)
sort.Slice(a, func(i, j int) bool { return a[i].Key() < a[j].Key() })
sort.Slice(b, func(i, j int) bool { return b[i].Key() < b[j].Key() })
for i, e := range a {
if e.Key() != b[i].Key() {
return false
}
b1, err1 := e.pack()
b2, err2 := b[i].pack()
if err1 != nil || err2 != nil || !bytes.Equal(b1, b2) {
return false
}
}
return true
}

162
vendor/github.com/miekg/dns/tsig.go generated vendored
View File

@ -2,7 +2,6 @@ package dns
import ( import (
"crypto/hmac" "crypto/hmac"
"crypto/md5"
"crypto/sha1" "crypto/sha1"
"crypto/sha256" "crypto/sha256"
"crypto/sha512" "crypto/sha512"
@ -16,12 +15,65 @@ import (
// HMAC hashing codes. These are transmitted as domain names. // HMAC hashing codes. These are transmitted as domain names.
const ( const (
HmacMD5 = "hmac-md5.sig-alg.reg.int."
HmacSHA1 = "hmac-sha1." HmacSHA1 = "hmac-sha1."
HmacSHA224 = "hmac-sha224."
HmacSHA256 = "hmac-sha256." HmacSHA256 = "hmac-sha256."
HmacSHA384 = "hmac-sha384."
HmacSHA512 = "hmac-sha512." HmacSHA512 = "hmac-sha512."
HmacMD5 = "hmac-md5.sig-alg.reg.int." // Deprecated: HmacMD5 is no longer supported.
) )
// TsigProvider provides the API to plug-in a custom TSIG implementation.
type TsigProvider interface {
// Generate is passed the DNS message to be signed and the partial TSIG RR. It returns the signature and nil, otherwise an error.
Generate(msg []byte, t *TSIG) ([]byte, error)
// Verify is passed the DNS message to be verified and the TSIG RR. If the signature is valid it will return nil, otherwise an error.
Verify(msg []byte, t *TSIG) error
}
type tsigHMACProvider string
func (key tsigHMACProvider) Generate(msg []byte, t *TSIG) ([]byte, error) {
// If we barf here, the caller is to blame
rawsecret, err := fromBase64([]byte(key))
if err != nil {
return nil, err
}
var h hash.Hash
switch CanonicalName(t.Algorithm) {
case HmacSHA1:
h = hmac.New(sha1.New, rawsecret)
case HmacSHA224:
h = hmac.New(sha256.New224, rawsecret)
case HmacSHA256:
h = hmac.New(sha256.New, rawsecret)
case HmacSHA384:
h = hmac.New(sha512.New384, rawsecret)
case HmacSHA512:
h = hmac.New(sha512.New, rawsecret)
default:
return nil, ErrKeyAlg
}
h.Write(msg)
return h.Sum(nil), nil
}
func (key tsigHMACProvider) Verify(msg []byte, t *TSIG) error {
b, err := key.Generate(msg, t)
if err != nil {
return err
}
mac, err := hex.DecodeString(t.MAC)
if err != nil {
return err
}
if !hmac.Equal(b, mac) {
return ErrSig
}
return nil
}
// TSIG is the RR the holds the transaction signature of a message. // TSIG is the RR the holds the transaction signature of a message.
// See RFC 2845 and RFC 4635. // See RFC 2845 and RFC 4635.
type TSIG struct { type TSIG struct {
@ -40,7 +92,7 @@ type TSIG struct {
// TSIG has no official presentation format, but this will suffice. // TSIG has no official presentation format, but this will suffice.
func (rr *TSIG) String() string { func (rr *TSIG) String() string {
s := "\n;; TSIG PSEUDOSECTION:\n" s := "\n;; TSIG PSEUDOSECTION:\n; " // add another semi-colon to signify TSIG does not have a presentation format
s += rr.Hdr.String() + s += rr.Hdr.String() +
" " + rr.Algorithm + " " + rr.Algorithm +
" " + tsigTimeToString(rr.TimeSigned) + " " + tsigTimeToString(rr.TimeSigned) +
@ -54,8 +106,8 @@ func (rr *TSIG) String() string {
return s return s
} }
func (rr *TSIG) parse(c *zlexer, origin, file string) *ParseError { func (*TSIG) parse(c *zlexer, origin string) *ParseError {
panic("dns: internal error: parse should never be called on TSIG") return &ParseError{err: "TSIG records do not have a presentation format"}
} }
// 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.
@ -96,14 +148,13 @@ type timerWireFmt struct {
// timersOnly is false. // timersOnly is false.
// If something goes wrong an error is returned, otherwise it is nil. // If something goes wrong an error is returned, otherwise it is nil.
func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, string, error) { func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, string, error) {
return tsigGenerateProvider(m, tsigHMACProvider(secret), requestMAC, timersOnly)
}
func tsigGenerateProvider(m *Msg, provider TsigProvider, requestMAC string, timersOnly bool) ([]byte, string, error) {
if m.IsTsig() == nil { if m.IsTsig() == nil {
panic("dns: TSIG not last RR in additional") panic("dns: TSIG not last RR in additional")
} }
// If we barf here, the caller is to blame
rawsecret, err := fromBase64([]byte(secret))
if err != nil {
return nil, "", err
}
rr := m.Extra[len(m.Extra)-1].(*TSIG) rr := m.Extra[len(m.Extra)-1].(*TSIG)
m.Extra = m.Extra[0 : len(m.Extra)-1] // kill the TSIG from the msg m.Extra = m.Extra[0 : len(m.Extra)-1] // kill the TSIG from the msg
@ -111,32 +162,21 @@ func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, s
if err != nil { if err != nil {
return nil, "", err return nil, "", err
} }
buf := tsigBuffer(mbuf, rr, requestMAC, timersOnly) buf, err := tsigBuffer(mbuf, rr, requestMAC, timersOnly)
if err != nil {
return nil, "", err
}
t := new(TSIG) t := new(TSIG)
var h hash.Hash // Copy all TSIG fields except MAC and its size, which are filled using the computed digest.
switch strings.ToLower(rr.Algorithm) { *t = *rr
case HmacMD5: mac, err := provider.Generate(buf, rr)
h = hmac.New(md5.New, rawsecret) if err != nil {
case HmacSHA1: return nil, "", err
h = hmac.New(sha1.New, rawsecret)
case HmacSHA256:
h = hmac.New(sha256.New, rawsecret)
case HmacSHA512:
h = hmac.New(sha512.New, rawsecret)
default:
return nil, "", ErrKeyAlg
} }
h.Write(buf) t.MAC = hex.EncodeToString(mac)
t.MAC = hex.EncodeToString(h.Sum(nil))
t.MACSize = uint16(len(t.MAC) / 2) // Size is half! t.MACSize = uint16(len(t.MAC) / 2) // Size is half!
t.Hdr = RR_Header{Name: rr.Hdr.Name, Rrtype: TypeTSIG, Class: ClassANY, Ttl: 0}
t.Fudge = rr.Fudge
t.TimeSigned = rr.TimeSigned
t.Algorithm = rr.Algorithm
t.OrigId = m.Id
tbuf := make([]byte, Len(t)) tbuf := make([]byte, Len(t))
off, err := PackRR(t, tbuf, 0, nil, false) off, err := PackRR(t, tbuf, 0, nil, false)
if err != nil { if err != nil {
@ -153,26 +193,34 @@ func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, s
// If the signature does not validate err contains the // If the signature does not validate err contains the
// error, otherwise it is nil. // error, otherwise it is nil.
func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error { func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error {
rawsecret, err := fromBase64([]byte(secret)) return tsigVerify(msg, tsigHMACProvider(secret), requestMAC, timersOnly, uint64(time.Now().Unix()))
if err != nil {
return err
} }
func tsigVerifyProvider(msg []byte, provider TsigProvider, requestMAC string, timersOnly bool) error {
return tsigVerify(msg, provider, requestMAC, timersOnly, uint64(time.Now().Unix()))
}
// actual implementation of TsigVerify, taking the current time ('now') as a parameter for the convenience of tests.
func tsigVerify(msg []byte, provider TsigProvider, requestMAC string, timersOnly bool, now uint64) error {
// Strip the TSIG from the incoming msg // Strip the TSIG from the incoming msg
stripped, tsig, err := stripTsig(msg) stripped, tsig, err := stripTsig(msg)
if err != nil { if err != nil {
return err return err
} }
msgMAC, err := hex.DecodeString(tsig.MAC) buf, err := tsigBuffer(stripped, tsig, requestMAC, timersOnly)
if err != nil { if err != nil {
return err return err
} }
buf := tsigBuffer(stripped, tsig, requestMAC, timersOnly) if err := provider.Verify(buf, tsig); err != nil {
return err
}
// Fudge factor works both ways. A message can arrive before it was signed because // Fudge factor works both ways. A message can arrive before it was signed because
// of clock skew. // of clock skew.
now := uint64(time.Now().Unix()) // We check this after verifying the signature, following draft-ietf-dnsop-rfc2845bis
// instead of RFC2845, in order to prevent a security vulnerability as reported in CVE-2017-3142/3143.
ti := now - tsig.TimeSigned ti := now - tsig.TimeSigned
if now < tsig.TimeSigned { if now < tsig.TimeSigned {
ti = tsig.TimeSigned - now ti = tsig.TimeSigned - now
@ -181,28 +229,11 @@ func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error {
return ErrTime return ErrTime
} }
var h hash.Hash
switch strings.ToLower(tsig.Algorithm) {
case HmacMD5:
h = hmac.New(md5.New, rawsecret)
case HmacSHA1:
h = hmac.New(sha1.New, rawsecret)
case HmacSHA256:
h = hmac.New(sha256.New, rawsecret)
case HmacSHA512:
h = hmac.New(sha512.New, rawsecret)
default:
return ErrKeyAlg
}
h.Write(buf)
if !hmac.Equal(h.Sum(nil), msgMAC) {
return ErrSig
}
return nil return nil
} }
// Create a wiredata buffer for the MAC calculation. // Create a wiredata buffer for the MAC calculation.
func tsigBuffer(msgbuf []byte, rr *TSIG, requestMAC string, timersOnly bool) []byte { func tsigBuffer(msgbuf []byte, rr *TSIG, requestMAC string, timersOnly bool) ([]byte, error) {
var buf []byte var buf []byte
if rr.TimeSigned == 0 { if rr.TimeSigned == 0 {
rr.TimeSigned = uint64(time.Now().Unix()) rr.TimeSigned = uint64(time.Now().Unix())
@ -219,7 +250,10 @@ func tsigBuffer(msgbuf []byte, rr *TSIG, requestMAC string, timersOnly bool) []b
m.MACSize = uint16(len(requestMAC) / 2) m.MACSize = uint16(len(requestMAC) / 2)
m.MAC = requestMAC m.MAC = requestMAC
buf = make([]byte, len(requestMAC)) // long enough buf = make([]byte, len(requestMAC)) // long enough
n, _ := packMacWire(m, buf) n, err := packMacWire(m, buf)
if err != nil {
return nil, err
}
buf = buf[:n] buf = buf[:n]
} }
@ -228,20 +262,26 @@ func tsigBuffer(msgbuf []byte, rr *TSIG, requestMAC string, timersOnly bool) []b
tsig := new(timerWireFmt) tsig := new(timerWireFmt)
tsig.TimeSigned = rr.TimeSigned tsig.TimeSigned = rr.TimeSigned
tsig.Fudge = rr.Fudge tsig.Fudge = rr.Fudge
n, _ := packTimerWire(tsig, tsigvar) n, err := packTimerWire(tsig, tsigvar)
if err != nil {
return nil, err
}
tsigvar = tsigvar[:n] tsigvar = tsigvar[:n]
} else { } else {
tsig := new(tsigWireFmt) tsig := new(tsigWireFmt)
tsig.Name = strings.ToLower(rr.Hdr.Name) tsig.Name = CanonicalName(rr.Hdr.Name)
tsig.Class = ClassANY tsig.Class = ClassANY
tsig.Ttl = rr.Hdr.Ttl tsig.Ttl = rr.Hdr.Ttl
tsig.Algorithm = strings.ToLower(rr.Algorithm) tsig.Algorithm = CanonicalName(rr.Algorithm)
tsig.TimeSigned = rr.TimeSigned tsig.TimeSigned = rr.TimeSigned
tsig.Fudge = rr.Fudge tsig.Fudge = rr.Fudge
tsig.Error = rr.Error tsig.Error = rr.Error
tsig.OtherLen = rr.OtherLen tsig.OtherLen = rr.OtherLen
tsig.OtherData = rr.OtherData tsig.OtherData = rr.OtherData
n, _ := packTsigWire(tsig, tsigvar) n, err := packTsigWire(tsig, tsigvar)
if err != nil {
return nil, err
}
tsigvar = tsigvar[:n] tsigvar = tsigvar[:n]
} }
@ -251,7 +291,7 @@ func tsigBuffer(msgbuf []byte, rr *TSIG, requestMAC string, timersOnly bool) []b
} else { } else {
buf = append(msgbuf, tsigvar...) buf = append(msgbuf, tsigvar...)
} }
return buf return buf, nil
} }
// Strip the TSIG from the raw message. // Strip the TSIG from the raw message.

265
vendor/github.com/miekg/dns/types.go generated vendored
View File

@ -1,6 +1,7 @@
package dns package dns
import ( import (
"bytes"
"fmt" "fmt"
"net" "net"
"strconv" "strconv"
@ -61,6 +62,7 @@ const (
TypeCERT uint16 = 37 TypeCERT uint16 = 37
TypeDNAME uint16 = 39 TypeDNAME uint16 = 39
TypeOPT uint16 = 41 // EDNS TypeOPT uint16 = 41 // EDNS
TypeAPL uint16 = 42
TypeDS uint16 = 43 TypeDS uint16 = 43
TypeSSHFP uint16 = 44 TypeSSHFP uint16 = 44
TypeRRSIG uint16 = 46 TypeRRSIG uint16 = 46
@ -79,6 +81,9 @@ const (
TypeCDNSKEY uint16 = 60 TypeCDNSKEY uint16 = 60
TypeOPENPGPKEY uint16 = 61 TypeOPENPGPKEY uint16 = 61
TypeCSYNC uint16 = 62 TypeCSYNC uint16 = 62
TypeZONEMD uint16 = 63
TypeSVCB uint16 = 64
TypeHTTPS uint16 = 65
TypeSPF uint16 = 99 TypeSPF uint16 = 99
TypeUINFO uint16 = 100 TypeUINFO uint16 = 100
TypeUID uint16 = 101 TypeUID uint16 = 101
@ -146,6 +151,14 @@ const (
OpcodeUpdate = 5 OpcodeUpdate = 5
) )
// Used in ZONEMD https://tools.ietf.org/html/rfc8976
const (
ZoneMDSchemeSimple = 1
ZoneMDHashAlgSHA384 = 1
ZoneMDHashAlgSHA512 = 2
)
// Header is the wire format for the DNS packet header. // Header is the wire format for the DNS packet header.
type Header struct { type Header struct {
Id uint16 Id uint16
@ -163,11 +176,11 @@ const (
_RD = 1 << 8 // recursion desired _RD = 1 << 8 // recursion desired
_RA = 1 << 7 // recursion available _RA = 1 << 7 // recursion available
_Z = 1 << 6 // Z _Z = 1 << 6 // Z
_AD = 1 << 5 // authticated data _AD = 1 << 5 // authenticated data
_CD = 1 << 4 // checking disabled _CD = 1 << 4 // checking disabled
) )
// Various constants used in the LOC RR, See RFC 1887. // Various constants used in the LOC RR. See RFC 1887.
const ( const (
LOC_EQUATOR = 1 << 31 // RFC 1876, Section 2. LOC_EQUATOR = 1 << 31 // RFC 1876, Section 2.
LOC_PRIMEMERIDIAN = 1 << 31 // RFC 1876, Section 2. LOC_PRIMEMERIDIAN = 1 << 31 // RFC 1876, Section 2.
@ -207,8 +220,11 @@ var CertTypeToString = map[uint16]string{
//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. Usually there is just one. While the
// question section of a message. Usually there is just one. // original DNS RFCs allow multiple questions in the question section of a
// message, in practice it never works. Because most DNS servers see multiple
// questions as an error, it is recommended to only have one question per
// message.
type Question struct { type Question struct {
Name string `dns:"cdomain-name"` // "cdomain-name" specifies encoding (and may be compressed) Name string `dns:"cdomain-name"` // "cdomain-name" specifies encoding (and may be compressed)
Qtype uint16 Qtype uint16
@ -238,8 +254,8 @@ 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 { func (*ANY) parse(c *zlexer, origin string) *ParseError {
panic("dns: internal error: parse should never be called on ANY") return &ParseError{err: "ANY records do not have a presentation format"}
} }
// NULL RR. See RFC 1035. // NULL RR. See RFC 1035.
@ -253,8 +269,8 @@ func (rr *NULL) String() string {
return ";" + rr.Hdr.String() + rr.Data return ";" + rr.Hdr.String() + rr.Data
} }
func (rr *NULL) parse(c *zlexer, origin, file string) *ParseError { func (*NULL) parse(c *zlexer, origin string) *ParseError {
panic("dns: internal error: parse should never be called on NULL") return &ParseError{err: "NULL records do not have a presentation format"}
} }
// CNAME RR. See RFC 1034. // CNAME RR. See RFC 1034.
@ -404,7 +420,7 @@ type RP struct {
} }
func (rr *RP) String() string { func (rr *RP) String() string {
return rr.Hdr.String() + rr.Mbox + " " + sprintTxt([]string{rr.Txt}) return rr.Hdr.String() + sprintName(rr.Mbox) + " " + sprintName(rr.Txt)
} }
// SOA RR. See RFC 1035. // SOA RR. See RFC 1035.
@ -438,25 +454,47 @@ 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 var dst strings.Builder
dst.Grow(len(s))
for i := 0; i < len(s); { for i := 0; i < len(s); {
if i+1 < len(s) && s[i] == '\\' && s[i+1] == '.' { if s[i] == '.' {
dst.WriteString(s[i : i+2]) if dst.Len() != 0 {
i += 2 dst.WriteByte('.')
}
i++
continue continue
} }
b, n := nextByte(s, i) b, n := nextByte(s, i)
switch { if n == 0 {
case n == 0: // Drop "dangling" incomplete escapes.
i++ // dangling back slash if dst.Len() == 0 {
case b == '.': return s[:i]
dst.WriteByte('.') }
default: break
writeDomainNameByte(&dst, b) }
if isDomainNameLabelSpecial(b) {
if dst.Len() == 0 {
dst.Grow(len(s) * 2)
dst.WriteString(s[:i])
}
dst.WriteByte('\\')
dst.WriteByte(b)
} else if b < ' ' || b > '~' { // unprintable, use \DDD
if dst.Len() == 0 {
dst.Grow(len(s) * 2)
dst.WriteString(s[:i])
}
dst.WriteString(escapeByte(b))
} else {
if dst.Len() != 0 {
dst.WriteByte(b)
}
} }
i += n i += n
} }
if dst.Len() == 0 {
return s
}
return dst.String() return dst.String()
} }
@ -472,15 +510,10 @@ func sprintTxtOctet(s string) string {
} }
b, n := nextByte(s, i) b, n := nextByte(s, i)
switch { if n == 0 {
case n == 0:
i++ // dangling back slash i++ // dangling back slash
case b == '.': } else {
dst.WriteByte('.') writeTXTStringByte(&dst, b)
case b < ' ' || b > '~':
dst.WriteString(escapeByte(b))
default:
dst.WriteByte(b)
} }
i += n i += n
} }
@ -510,16 +543,6 @@ func sprintTxt(txt []string) string {
return out.String() return out.String()
} }
func writeDomainNameByte(s *strings.Builder, b byte) {
switch b {
case '.', ' ', '\'', '@', ';', '(', ')': // additional chars to escape
s.WriteByte('\\')
s.WriteByte(b)
default:
writeTXTStringByte(s, b)
}
}
func writeTXTStringByte(s *strings.Builder, b byte) { func writeTXTStringByte(s *strings.Builder, b byte) {
switch { switch {
case b == '"' || b == '\\': case b == '"' || b == '\\':
@ -566,6 +589,17 @@ func escapeByte(b byte) string {
return escapedByteLarge[int(b)*4 : int(b)*4+4] return escapedByteLarge[int(b)*4 : int(b)*4+4]
} }
// isDomainNameLabelSpecial returns true if
// a domain name label byte should be prefixed
// with an escaping backslash.
func isDomainNameLabelSpecial(b byte) bool {
switch b {
case '.', ' ', '\'', '@', ';', '(', ')', '"', '\\':
return true
}
return false
}
func nextByte(s string, offset int) (byte, int) { func nextByte(s string, offset int) (byte, int) {
if offset >= len(s) { if offset >= len(s) {
return 0, 0 return 0, 0
@ -739,7 +773,7 @@ type LOC struct {
} }
// cmToM takes a cm value expressed in RFC 1876 SIZE mantissa/exponent // cmToM takes a cm value expressed in RFC 1876 SIZE mantissa/exponent
// format and returns a string in m (two decimals for the cm) // format and returns a string in m (two decimals for the cm).
func cmToM(m, e uint8) string { func cmToM(m, e uint8) string {
if e < 2 { if e < 2 {
if e == 1 { if e == 1 {
@ -845,8 +879,8 @@ 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 i := 0; i < len(rr.TypeBitMap); i++ { for _, t := range rr.TypeBitMap {
s += " " + Type(rr.TypeBitMap[i]).String() s += " " + Type(t).String()
} }
return s return s
} }
@ -854,14 +888,7 @@ func (rr *NSEC) String() string {
func (rr *NSEC) len(off int, compression map[string]struct{}) int { func (rr *NSEC) len(off int, compression map[string]struct{}) int {
l := rr.Hdr.len(off, compression) l := rr.Hdr.len(off, compression)
l += domainNameLen(rr.NextDomain, off+l, compression, false) l += domainNameLen(rr.NextDomain, off+l, compression, false)
lastwindow := uint32(2 ^ 32 + 1) l += typeBitMapLen(rr.TypeBitMap)
for _, t := range rr.TypeBitMap {
window := t / 256
if uint32(window) != lastwindow {
l += 1 + 32
}
lastwindow = uint32(window)
}
return l return l
} }
@ -1011,8 +1038,8 @@ 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 i := 0; i < len(rr.TypeBitMap); i++ { for _, t := range rr.TypeBitMap {
s += " " + Type(rr.TypeBitMap[i]).String() s += " " + Type(t).String()
} }
return s return s
} }
@ -1020,14 +1047,7 @@ func (rr *NSEC3) String() string {
func (rr *NSEC3) len(off int, compression map[string]struct{}) int { func (rr *NSEC3) len(off int, compression map[string]struct{}) int {
l := rr.Hdr.len(off, compression) l := rr.Hdr.len(off, compression)
l += 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) l += typeBitMapLen(rr.TypeBitMap)
for _, t := range rr.TypeBitMap {
window := t / 256
if uint32(window) != lastwindow {
l += 1 + 32
}
lastwindow = uint32(window)
}
return l return l
} }
@ -1111,6 +1131,7 @@ type URI struct {
Target string `dns:"octet"` Target string `dns:"octet"`
} }
// rr.Target to be parsed as a sequence of character encoded octets according to RFC 3986
func (rr *URI) String() string { func (rr *URI) String() string {
return rr.Hdr.String() + strconv.Itoa(int(rr.Priority)) + return rr.Hdr.String() + strconv.Itoa(int(rr.Priority)) +
" " + strconv.Itoa(int(rr.Weight)) + " " + sprintTxtOctet(rr.Target) " " + strconv.Itoa(int(rr.Weight)) + " " + sprintTxtOctet(rr.Target)
@ -1272,6 +1293,7 @@ type CAA struct {
Value string `dns:"octet"` Value string `dns:"octet"`
} }
// rr.Value Is the character-string encoding of the value field as specified in RFC 1035, Section 5.1.
func (rr *CAA) String() string { func (rr *CAA) String() string {
return rr.Hdr.String() + strconv.Itoa(int(rr.Flag)) + " " + rr.Tag + " " + sprintTxtOctet(rr.Value) return rr.Hdr.String() + strconv.Itoa(int(rr.Flag)) + " " + rr.Tag + " " + sprintTxtOctet(rr.Value)
} }
@ -1335,8 +1357,8 @@ 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 i := 0; i < len(rr.TypeBitMap); i++ { for _, t := range rr.TypeBitMap {
s += " " + Type(rr.TypeBitMap[i]).String() s += " " + Type(t).String()
} }
return s return s
} }
@ -1344,17 +1366,109 @@ func (rr *CSYNC) String() string {
func (rr *CSYNC) len(off int, compression map[string]struct{}) int { func (rr *CSYNC) len(off int, compression map[string]struct{}) int {
l := rr.Hdr.len(off, compression) l := rr.Hdr.len(off, compression)
l += 4 + 2 l += 4 + 2
lastwindow := uint32(2 ^ 32 + 1) l += typeBitMapLen(rr.TypeBitMap)
for _, t := range rr.TypeBitMap {
window := t / 256
if uint32(window) != lastwindow {
l += 1 + 32
}
lastwindow = uint32(window)
}
return l return l
} }
// ZONEMD RR, from draft-ietf-dnsop-dns-zone-digest
type ZONEMD struct {
Hdr RR_Header
Serial uint32
Scheme uint8
Hash uint8
Digest string `dns:"hex"`
}
func (rr *ZONEMD) String() string {
return rr.Hdr.String() +
strconv.Itoa(int(rr.Serial)) +
" " + strconv.Itoa(int(rr.Scheme)) +
" " + strconv.Itoa(int(rr.Hash)) +
" " + rr.Digest
}
// APL RR. See RFC 3123.
type APL struct {
Hdr RR_Header
Prefixes []APLPrefix `dns:"apl"`
}
// APLPrefix is an address prefix hold by an APL record.
type APLPrefix struct {
Negation bool
Network net.IPNet
}
// String returns presentation form of the APL record.
func (rr *APL) String() string {
var sb strings.Builder
sb.WriteString(rr.Hdr.String())
for i, p := range rr.Prefixes {
if i > 0 {
sb.WriteByte(' ')
}
sb.WriteString(p.str())
}
return sb.String()
}
// str returns presentation form of the APL prefix.
func (a *APLPrefix) str() string {
var sb strings.Builder
if a.Negation {
sb.WriteByte('!')
}
switch len(a.Network.IP) {
case net.IPv4len:
sb.WriteByte('1')
case net.IPv6len:
sb.WriteByte('2')
}
sb.WriteByte(':')
switch len(a.Network.IP) {
case net.IPv4len:
sb.WriteString(a.Network.IP.String())
case net.IPv6len:
// add prefix for IPv4-mapped IPv6
if v4 := a.Network.IP.To4(); v4 != nil {
sb.WriteString("::ffff:")
}
sb.WriteString(a.Network.IP.String())
}
sb.WriteByte('/')
prefix, _ := a.Network.Mask.Size()
sb.WriteString(strconv.Itoa(prefix))
return sb.String()
}
// equals reports whether two APL prefixes are identical.
func (a *APLPrefix) equals(b *APLPrefix) bool {
return a.Negation == b.Negation &&
bytes.Equal(a.Network.IP, b.Network.IP) &&
bytes.Equal(a.Network.Mask, b.Network.Mask)
}
// copy returns a copy of the APL prefix.
func (a *APLPrefix) copy() APLPrefix {
return APLPrefix{
Negation: a.Negation,
Network: copyNet(a.Network),
}
}
// len returns size of the prefix in wire format.
func (a *APLPrefix) len() int {
// 4-byte header and the network address prefix (see Section 4 of RFC 3123)
prefix, _ := a.Network.Mask.Size()
return 4 + (prefix+7)/8
}
// TimeToString translates the RRSIG's incep. and expir. times to the // TimeToString translates the RRSIG's incep. and expir. times to the
// 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.
@ -1384,7 +1498,7 @@ func StringToTime(s string) (uint32, error) {
// 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.
func saltToString(s string) string { func saltToString(s string) string {
if len(s) == 0 { if s == "" {
return "-" return "-"
} }
return strings.ToUpper(s) return strings.ToUpper(s)
@ -1411,6 +1525,17 @@ func copyIP(ip net.IP) net.IP {
return p return p
} }
// copyNet returns a copy of a subnet.
func copyNet(n net.IPNet) net.IPNet {
m := make(net.IPMask, len(n.Mask))
copy(m, n.Mask)
return net.IPNet{
IP: copyIP(n.IP),
Mask: m,
}
}
// SplitN splits a string into N sized string chunks. // SplitN splits a string into N sized string chunks.
// This might become an exported function once. // This might become an exported function once.
func splitN(s string, n int) []string { func splitN(s string, n int) []string {

View File

@ -3,13 +3,13 @@ package dns
import "fmt" import "fmt"
// Version is current version of this library. // Version is current version of this library.
var Version = V{1, 1, 3} var Version = v{1, 1, 43}
// V holds the version of this library. // v holds the version of this library.
type V struct { type v struct {
Major, Minor, Patch int Major, Minor, Patch int
} }
func (v V) String() string { func (v v) String() string {
return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch) return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)
} }

18
vendor/github.com/miekg/dns/xfr.go generated vendored
View File

@ -182,14 +182,17 @@ func (t *Transfer) inIxfr(q *Msg, c chan *Envelope) {
// //
// ch := make(chan *dns.Envelope) // ch := make(chan *dns.Envelope)
// tr := new(dns.Transfer) // tr := new(dns.Transfer)
// go tr.Out(w, r, ch) // var wg sync.WaitGroup
// go func() {
// tr.Out(w, r, ch)
// wg.Done()
// }()
// ch <- &dns.Envelope{RR: []dns.RR{soa, rr1, rr2, rr3, soa}} // ch <- &dns.Envelope{RR: []dns.RR{soa, rr1, rr2, rr3, soa}}
// close(ch) // close(ch)
// w.Hijack() // wg.Wait() // wait until everything is written out
// // w.Close() // Client closes connection // w.Close() // close connection
// //
// The server is responsible for sending the correct sequence of RRs through the // The server is responsible for sending the correct sequence of RRs through the channel ch.
// channel ch.
func (t *Transfer) Out(w ResponseWriter, q *Msg, ch chan *Envelope) error { func (t *Transfer) Out(w ResponseWriter, q *Msg, ch chan *Envelope) error {
for x := range ch { for x := range ch {
r := new(Msg) r := new(Msg)
@ -198,11 +201,14 @@ func (t *Transfer) Out(w ResponseWriter, q *Msg, ch chan *Envelope) error {
r.Authoritative = true r.Authoritative = true
// assume it fits TODO(miek): fix // assume it fits TODO(miek): fix
r.Answer = append(r.Answer, x.RR...) r.Answer = append(r.Answer, x.RR...)
if tsig := q.IsTsig(); tsig != nil && w.TsigStatus() == nil {
r.SetTsig(tsig.Hdr.Name, tsig.Algorithm, tsig.Fudge, time.Now().Unix())
}
if err := w.WriteMsg(r); err != nil { if err := w.WriteMsg(r); err != nil {
return err return err
} }
}
w.TsigTimersOnly(true) w.TsigTimersOnly(true)
}
return nil return nil
} }

View File

@ -37,7 +37,7 @@ func (r1 *AFSDB) isDuplicate(_r2 RR) bool {
if r1.Subtype != r2.Subtype { if r1.Subtype != r2.Subtype {
return false return false
} }
if !isDulicateName(r1.Hostname, r2.Hostname) { if !isDuplicateName(r1.Hostname, r2.Hostname) {
return false return false
} }
return true return true
@ -52,6 +52,23 @@ func (r1 *ANY) isDuplicate(_r2 RR) bool {
return true return true
} }
func (r1 *APL) isDuplicate(_r2 RR) bool {
r2, ok := _r2.(*APL)
if !ok {
return false
}
_ = r2
if len(r1.Prefixes) != len(r2.Prefixes) {
return false
}
for i := 0; i < len(r1.Prefixes); i++ {
if !r1.Prefixes[i].equals(&r2.Prefixes[i]) {
return false
}
}
return true
}
func (r1 *AVC) isDuplicate(_r2 RR) bool { func (r1 *AVC) isDuplicate(_r2 RR) bool {
r2, ok := _r2.(*AVC) r2, ok := _r2.(*AVC)
if !ok { if !ok {
@ -87,6 +104,48 @@ func (r1 *CAA) isDuplicate(_r2 RR) bool {
return true return true
} }
func (r1 *CDNSKEY) isDuplicate(_r2 RR) bool {
r2, ok := _r2.(*CDNSKEY)
if !ok {
return false
}
_ = r2
if r1.Flags != r2.Flags {
return false
}
if r1.Protocol != r2.Protocol {
return false
}
if r1.Algorithm != r2.Algorithm {
return false
}
if r1.PublicKey != r2.PublicKey {
return false
}
return true
}
func (r1 *CDS) isDuplicate(_r2 RR) bool {
r2, ok := _r2.(*CDS)
if !ok {
return false
}
_ = r2
if r1.KeyTag != r2.KeyTag {
return false
}
if r1.Algorithm != r2.Algorithm {
return false
}
if r1.DigestType != r2.DigestType {
return false
}
if r1.Digest != r2.Digest {
return false
}
return true
}
func (r1 *CERT) isDuplicate(_r2 RR) bool { func (r1 *CERT) isDuplicate(_r2 RR) bool {
r2, ok := _r2.(*CERT) r2, ok := _r2.(*CERT)
if !ok { if !ok {
@ -114,7 +173,7 @@ func (r1 *CNAME) isDuplicate(_r2 RR) bool {
return false return false
} }
_ = r2 _ = r2
if !isDulicateName(r1.Target, r2.Target) { if !isDuplicateName(r1.Target, r2.Target) {
return false return false
} }
return true return true
@ -155,13 +214,34 @@ func (r1 *DHCID) isDuplicate(_r2 RR) bool {
return true return true
} }
func (r1 *DLV) isDuplicate(_r2 RR) bool {
r2, ok := _r2.(*DLV)
if !ok {
return false
}
_ = r2
if r1.KeyTag != r2.KeyTag {
return false
}
if r1.Algorithm != r2.Algorithm {
return false
}
if r1.DigestType != r2.DigestType {
return false
}
if r1.Digest != r2.Digest {
return false
}
return true
}
func (r1 *DNAME) isDuplicate(_r2 RR) bool { func (r1 *DNAME) isDuplicate(_r2 RR) bool {
r2, ok := _r2.(*DNAME) r2, ok := _r2.(*DNAME)
if !ok { if !ok {
return false return false
} }
_ = r2 _ = r2
if !isDulicateName(r1.Target, r2.Target) { if !isDuplicateName(r1.Target, r2.Target) {
return false return false
} }
return true return true
@ -315,13 +395,55 @@ func (r1 *HIP) isDuplicate(_r2 RR) bool {
return false return false
} }
for i := 0; i < len(r1.RendezvousServers); i++ { for i := 0; i < len(r1.RendezvousServers); i++ {
if !isDulicateName(r1.RendezvousServers[i], r2.RendezvousServers[i]) { if !isDuplicateName(r1.RendezvousServers[i], r2.RendezvousServers[i]) {
return false return false
} }
} }
return true return true
} }
func (r1 *HTTPS) isDuplicate(_r2 RR) bool {
r2, ok := _r2.(*HTTPS)
if !ok {
return false
}
_ = r2
if r1.Priority != r2.Priority {
return false
}
if !isDuplicateName(r1.Target, r2.Target) {
return false
}
if len(r1.Value) != len(r2.Value) {
return false
}
if !areSVCBPairArraysEqual(r1.Value, r2.Value) {
return false
}
return true
}
func (r1 *KEY) isDuplicate(_r2 RR) bool {
r2, ok := _r2.(*KEY)
if !ok {
return false
}
_ = r2
if r1.Flags != r2.Flags {
return false
}
if r1.Protocol != r2.Protocol {
return false
}
if r1.Algorithm != r2.Algorithm {
return false
}
if r1.PublicKey != r2.PublicKey {
return false
}
return true
}
func (r1 *KX) isDuplicate(_r2 RR) bool { func (r1 *KX) isDuplicate(_r2 RR) bool {
r2, ok := _r2.(*KX) r2, ok := _r2.(*KX)
if !ok { if !ok {
@ -331,7 +453,7 @@ func (r1 *KX) isDuplicate(_r2 RR) bool {
if r1.Preference != r2.Preference { if r1.Preference != r2.Preference {
return false return false
} }
if !isDulicateName(r1.Exchanger, r2.Exchanger) { if !isDuplicateName(r1.Exchanger, r2.Exchanger) {
return false return false
} }
return true return true
@ -406,7 +528,7 @@ func (r1 *LP) isDuplicate(_r2 RR) bool {
if r1.Preference != r2.Preference { if r1.Preference != r2.Preference {
return false return false
} }
if !isDulicateName(r1.Fqdn, r2.Fqdn) { if !isDuplicateName(r1.Fqdn, r2.Fqdn) {
return false return false
} }
return true return true
@ -418,7 +540,7 @@ func (r1 *MB) isDuplicate(_r2 RR) bool {
return false return false
} }
_ = r2 _ = r2
if !isDulicateName(r1.Mb, r2.Mb) { if !isDuplicateName(r1.Mb, r2.Mb) {
return false return false
} }
return true return true
@ -430,7 +552,7 @@ func (r1 *MD) isDuplicate(_r2 RR) bool {
return false return false
} }
_ = r2 _ = r2
if !isDulicateName(r1.Md, r2.Md) { if !isDuplicateName(r1.Md, r2.Md) {
return false return false
} }
return true return true
@ -442,7 +564,7 @@ func (r1 *MF) isDuplicate(_r2 RR) bool {
return false return false
} }
_ = r2 _ = r2
if !isDulicateName(r1.Mf, r2.Mf) { if !isDuplicateName(r1.Mf, r2.Mf) {
return false return false
} }
return true return true
@ -454,7 +576,7 @@ func (r1 *MG) isDuplicate(_r2 RR) bool {
return false return false
} }
_ = r2 _ = r2
if !isDulicateName(r1.Mg, r2.Mg) { if !isDuplicateName(r1.Mg, r2.Mg) {
return false return false
} }
return true return true
@ -466,10 +588,10 @@ func (r1 *MINFO) isDuplicate(_r2 RR) bool {
return false return false
} }
_ = r2 _ = r2
if !isDulicateName(r1.Rmail, r2.Rmail) { if !isDuplicateName(r1.Rmail, r2.Rmail) {
return false return false
} }
if !isDulicateName(r1.Email, r2.Email) { if !isDuplicateName(r1.Email, r2.Email) {
return false return false
} }
return true return true
@ -481,7 +603,7 @@ func (r1 *MR) isDuplicate(_r2 RR) bool {
return false return false
} }
_ = r2 _ = r2
if !isDulicateName(r1.Mr, r2.Mr) { if !isDuplicateName(r1.Mr, r2.Mr) {
return false return false
} }
return true return true
@ -496,7 +618,7 @@ func (r1 *MX) isDuplicate(_r2 RR) bool {
if r1.Preference != r2.Preference { if r1.Preference != r2.Preference {
return false return false
} }
if !isDulicateName(r1.Mx, r2.Mx) { if !isDuplicateName(r1.Mx, r2.Mx) {
return false return false
} }
return true return true
@ -523,7 +645,7 @@ func (r1 *NAPTR) isDuplicate(_r2 RR) bool {
if r1.Regexp != r2.Regexp { if r1.Regexp != r2.Regexp {
return false return false
} }
if !isDulicateName(r1.Replacement, r2.Replacement) { if !isDuplicateName(r1.Replacement, r2.Replacement) {
return false return false
} }
return true return true
@ -579,7 +701,7 @@ func (r1 *NS) isDuplicate(_r2 RR) bool {
return false return false
} }
_ = r2 _ = r2
if !isDulicateName(r1.Ns, r2.Ns) { if !isDuplicateName(r1.Ns, r2.Ns) {
return false return false
} }
return true return true
@ -591,7 +713,7 @@ func (r1 *NSAPPTR) isDuplicate(_r2 RR) bool {
return false return false
} }
_ = r2 _ = r2
if !isDulicateName(r1.Ptr, r2.Ptr) { if !isDuplicateName(r1.Ptr, r2.Ptr) {
return false return false
} }
return true return true
@ -603,7 +725,7 @@ func (r1 *NSEC) isDuplicate(_r2 RR) bool {
return false return false
} }
_ = r2 _ = r2
if !isDulicateName(r1.NextDomain, r2.NextDomain) { if !isDuplicateName(r1.NextDomain, r2.NextDomain) {
return false return false
} }
if len(r1.TypeBitMap) != len(r2.TypeBitMap) { if len(r1.TypeBitMap) != len(r2.TypeBitMap) {
@ -709,7 +831,7 @@ func (r1 *PTR) isDuplicate(_r2 RR) bool {
return false return false
} }
_ = r2 _ = r2
if !isDulicateName(r1.Ptr, r2.Ptr) { if !isDuplicateName(r1.Ptr, r2.Ptr) {
return false return false
} }
return true return true
@ -724,10 +846,10 @@ func (r1 *PX) isDuplicate(_r2 RR) bool {
if r1.Preference != r2.Preference { if r1.Preference != r2.Preference {
return false return false
} }
if !isDulicateName(r1.Map822, r2.Map822) { if !isDuplicateName(r1.Map822, r2.Map822) {
return false return false
} }
if !isDulicateName(r1.Mapx400, r2.Mapx400) { if !isDuplicateName(r1.Mapx400, r2.Mapx400) {
return false return false
} }
return true return true
@ -772,10 +894,10 @@ func (r1 *RP) isDuplicate(_r2 RR) bool {
return false return false
} }
_ = r2 _ = r2
if !isDulicateName(r1.Mbox, r2.Mbox) { if !isDuplicateName(r1.Mbox, r2.Mbox) {
return false return false
} }
if !isDulicateName(r1.Txt, r2.Txt) { if !isDuplicateName(r1.Txt, r2.Txt) {
return false return false
} }
return true return true
@ -808,7 +930,7 @@ func (r1 *RRSIG) isDuplicate(_r2 RR) bool {
if r1.KeyTag != r2.KeyTag { if r1.KeyTag != r2.KeyTag {
return false return false
} }
if !isDulicateName(r1.SignerName, r2.SignerName) { if !isDuplicateName(r1.SignerName, r2.SignerName) {
return false return false
} }
if r1.Signature != r2.Signature { if r1.Signature != r2.Signature {
@ -826,7 +948,43 @@ func (r1 *RT) isDuplicate(_r2 RR) bool {
if r1.Preference != r2.Preference { if r1.Preference != r2.Preference {
return false return false
} }
if !isDulicateName(r1.Host, r2.Host) { if !isDuplicateName(r1.Host, r2.Host) {
return false
}
return true
}
func (r1 *SIG) isDuplicate(_r2 RR) bool {
r2, ok := _r2.(*SIG)
if !ok {
return false
}
_ = r2
if r1.TypeCovered != r2.TypeCovered {
return false
}
if r1.Algorithm != r2.Algorithm {
return false
}
if r1.Labels != r2.Labels {
return false
}
if r1.OrigTtl != r2.OrigTtl {
return false
}
if r1.Expiration != r2.Expiration {
return false
}
if r1.Inception != r2.Inception {
return false
}
if r1.KeyTag != r2.KeyTag {
return false
}
if !isDuplicateName(r1.SignerName, r2.SignerName) {
return false
}
if r1.Signature != r2.Signature {
return false return false
} }
return true return true
@ -859,10 +1017,10 @@ func (r1 *SOA) isDuplicate(_r2 RR) bool {
return false return false
} }
_ = r2 _ = r2
if !isDulicateName(r1.Ns, r2.Ns) { if !isDuplicateName(r1.Ns, r2.Ns) {
return false return false
} }
if !isDulicateName(r1.Mbox, r2.Mbox) { if !isDuplicateName(r1.Mbox, r2.Mbox) {
return false return false
} }
if r1.Serial != r2.Serial { if r1.Serial != r2.Serial {
@ -915,7 +1073,7 @@ func (r1 *SRV) isDuplicate(_r2 RR) bool {
if r1.Port != r2.Port { if r1.Port != r2.Port {
return false return false
} }
if !isDulicateName(r1.Target, r2.Target) { if !isDuplicateName(r1.Target, r2.Target) {
return false return false
} }
return true return true
@ -939,6 +1097,27 @@ func (r1 *SSHFP) isDuplicate(_r2 RR) bool {
return true return true
} }
func (r1 *SVCB) isDuplicate(_r2 RR) bool {
r2, ok := _r2.(*SVCB)
if !ok {
return false
}
_ = r2
if r1.Priority != r2.Priority {
return false
}
if !isDuplicateName(r1.Target, r2.Target) {
return false
}
if len(r1.Value) != len(r2.Value) {
return false
}
if !areSVCBPairArraysEqual(r1.Value, r2.Value) {
return false
}
return true
}
func (r1 *TA) isDuplicate(_r2 RR) bool { func (r1 *TA) isDuplicate(_r2 RR) bool {
r2, ok := _r2.(*TA) r2, ok := _r2.(*TA)
if !ok { if !ok {
@ -966,10 +1145,10 @@ func (r1 *TALINK) isDuplicate(_r2 RR) bool {
return false return false
} }
_ = r2 _ = r2
if !isDulicateName(r1.PreviousName, r2.PreviousName) { if !isDuplicateName(r1.PreviousName, r2.PreviousName) {
return false return false
} }
if !isDulicateName(r1.NextName, r2.NextName) { if !isDuplicateName(r1.NextName, r2.NextName) {
return false return false
} }
return true return true
@ -981,7 +1160,7 @@ func (r1 *TKEY) isDuplicate(_r2 RR) bool {
return false return false
} }
_ = r2 _ = r2
if !isDulicateName(r1.Algorithm, r2.Algorithm) { if !isDuplicateName(r1.Algorithm, r2.Algorithm) {
return false return false
} }
if r1.Inception != r2.Inception { if r1.Inception != r2.Inception {
@ -1038,7 +1217,7 @@ func (r1 *TSIG) isDuplicate(_r2 RR) bool {
return false return false
} }
_ = r2 _ = r2
if !isDulicateName(r1.Algorithm, r2.Algorithm) { if !isDuplicateName(r1.Algorithm, r2.Algorithm) {
return false return false
} }
if r1.TimeSigned != r2.TimeSigned { if r1.TimeSigned != r2.TimeSigned {
@ -1138,3 +1317,24 @@ func (r1 *X25) isDuplicate(_r2 RR) bool {
} }
return true return true
} }
func (r1 *ZONEMD) isDuplicate(_r2 RR) bool {
r2, ok := _r2.(*ZONEMD)
if !ok {
return false
}
_ = r2
if r1.Serial != r2.Serial {
return false
}
if r1.Scheme != r2.Scheme {
return false
}
if r1.Hash != r2.Hash {
return false
}
if r1.Digest != r2.Digest {
return false
}
return true
}

153
vendor/github.com/miekg/dns/zmsg.go generated vendored
View File

@ -36,6 +36,14 @@ func (rr *ANY) pack(msg []byte, off int, compression compressionMap, compress bo
return off, nil return off, nil
} }
func (rr *APL) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
off, err = packDataApl(rr.Prefixes, msg, off)
if err != nil {
return off, err
}
return off, nil
}
func (rr *AVC) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { func (rr *AVC) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
off, err = packStringTxt(rr.Txt, msg, off) off, err = packStringTxt(rr.Txt, msg, off)
if err != nil { if err != nil {
@ -308,6 +316,22 @@ func (rr *HIP) pack(msg []byte, off int, compression compressionMap, compress bo
return off, nil return off, nil
} }
func (rr *HTTPS) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
off, err = packUint16(rr.Priority, msg, off)
if err != nil {
return off, err
}
off, err = packDomainName(rr.Target, msg, off, compression, false)
if err != nil {
return off, err
}
off, err = packDataSVCB(rr.Value, msg, off)
if err != nil {
return off, err
}
return off, nil
}
func (rr *KEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { func (rr *KEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
off, err = packUint16(rr.Flags, msg, off) off, err = packUint16(rr.Flags, msg, off)
if err != nil { if err != nil {
@ -898,6 +922,22 @@ func (rr *SSHFP) pack(msg []byte, off int, compression compressionMap, compress
return off, nil return off, nil
} }
func (rr *SVCB) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
off, err = packUint16(rr.Priority, msg, off)
if err != nil {
return off, err
}
off, err = packDomainName(rr.Target, msg, off, compression, false)
if err != nil {
return off, err
}
off, err = packDataSVCB(rr.Value, msg, off)
if err != nil {
return off, err
}
return off, nil
}
func (rr *TA) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { func (rr *TA) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
off, err = packUint16(rr.KeyTag, msg, off) off, err = packUint16(rr.KeyTag, msg, off)
if err != nil { if err != nil {
@ -1078,6 +1118,26 @@ func (rr *X25) pack(msg []byte, off int, compression compressionMap, compress bo
return off, nil return off, nil
} }
func (rr *ZONEMD) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
off, err = packUint32(rr.Serial, msg, off)
if err != nil {
return off, err
}
off, err = packUint8(rr.Scheme, msg, off)
if err != nil {
return off, err
}
off, err = packUint8(rr.Hash, msg, off)
if err != nil {
return off, err
}
off, err = packStringHex(rr.Digest, msg, off)
if err != nil {
return off, err
}
return off, nil
}
// unpack*() functions // unpack*() functions
func (rr *A) unpack(msg []byte, off int) (off1 int, err error) { func (rr *A) unpack(msg []byte, off int) (off1 int, err error) {
@ -1127,6 +1187,17 @@ func (rr *ANY) unpack(msg []byte, off int) (off1 int, err error) {
return off, nil return off, nil
} }
func (rr *APL) unpack(msg []byte, off int) (off1 int, err error) {
rdStart := off
_ = rdStart
rr.Prefixes, off, err = unpackDataApl(msg, off)
if err != nil {
return off, err
}
return off, nil
}
func (rr *AVC) unpack(msg []byte, off int) (off1 int, err error) { func (rr *AVC) unpack(msg []byte, off int) (off1 int, err error) {
rdStart := off rdStart := off
_ = rdStart _ = rdStart
@ -1540,6 +1611,31 @@ func (rr *HIP) unpack(msg []byte, off int) (off1 int, err error) {
return off, nil return off, nil
} }
func (rr *HTTPS) unpack(msg []byte, off int) (off1 int, err error) {
rdStart := off
_ = rdStart
rr.Priority, off, err = unpackUint16(msg, off)
if err != nil {
return off, err
}
if off == len(msg) {
return off, nil
}
rr.Target, off, err = UnpackDomainName(msg, off)
if err != nil {
return off, err
}
if off == len(msg) {
return off, nil
}
rr.Value, off, err = unpackDataSVCB(msg, off)
if err != nil {
return off, err
}
return off, nil
}
func (rr *KEY) unpack(msg []byte, off int) (off1 int, err error) { func (rr *KEY) unpack(msg []byte, off int) (off1 int, err error) {
rdStart := off rdStart := off
_ = rdStart _ = rdStart
@ -2442,6 +2538,31 @@ func (rr *SSHFP) unpack(msg []byte, off int) (off1 int, err error) {
return off, nil return off, nil
} }
func (rr *SVCB) unpack(msg []byte, off int) (off1 int, err error) {
rdStart := off
_ = rdStart
rr.Priority, off, err = unpackUint16(msg, off)
if err != nil {
return off, err
}
if off == len(msg) {
return off, nil
}
rr.Target, off, err = UnpackDomainName(msg, off)
if err != nil {
return off, err
}
if off == len(msg) {
return off, nil
}
rr.Value, off, err = unpackDataSVCB(msg, off)
if err != nil {
return off, err
}
return off, nil
}
func (rr *TA) unpack(msg []byte, off int) (off1 int, err error) { func (rr *TA) unpack(msg []byte, off int) (off1 int, err error) {
rdStart := off rdStart := off
_ = rdStart _ = rdStart
@ -2720,3 +2841,35 @@ func (rr *X25) unpack(msg []byte, off int) (off1 int, err error) {
} }
return off, nil return off, nil
} }
func (rr *ZONEMD) unpack(msg []byte, off int) (off1 int, err error) {
rdStart := off
_ = rdStart
rr.Serial, off, err = unpackUint32(msg, off)
if err != nil {
return off, err
}
if off == len(msg) {
return off, nil
}
rr.Scheme, off, err = unpackUint8(msg, off)
if err != nil {
return off, err
}
if off == len(msg) {
return off, nil
}
rr.Hash, off, err = unpackUint8(msg, off)
if err != nil {
return off, err
}
if off == len(msg) {
return off, nil
}
rr.Digest, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength))
if err != nil {
return off, err
}
return off, nil
}

103
vendor/github.com/miekg/dns/ztypes.go generated vendored
View File

@ -13,6 +13,7 @@ var TypeToRR = map[uint16]func() RR{
TypeAAAA: func() RR { return new(AAAA) }, TypeAAAA: func() RR { return new(AAAA) },
TypeAFSDB: func() RR { return new(AFSDB) }, TypeAFSDB: func() RR { return new(AFSDB) },
TypeANY: func() RR { return new(ANY) }, TypeANY: func() RR { return new(ANY) },
TypeAPL: func() RR { return new(APL) },
TypeAVC: func() RR { return new(AVC) }, TypeAVC: func() RR { return new(AVC) },
TypeCAA: func() RR { return new(CAA) }, TypeCAA: func() RR { return new(CAA) },
TypeCDNSKEY: func() RR { return new(CDNSKEY) }, TypeCDNSKEY: func() RR { return new(CDNSKEY) },
@ -32,6 +33,7 @@ var TypeToRR = map[uint16]func() RR{
TypeGPOS: func() RR { return new(GPOS) }, TypeGPOS: func() RR { return new(GPOS) },
TypeHINFO: func() RR { return new(HINFO) }, TypeHINFO: func() RR { return new(HINFO) },
TypeHIP: func() RR { return new(HIP) }, TypeHIP: func() RR { return new(HIP) },
TypeHTTPS: func() RR { return new(HTTPS) },
TypeKEY: func() RR { return new(KEY) }, TypeKEY: func() RR { return new(KEY) },
TypeKX: func() RR { return new(KX) }, TypeKX: func() RR { return new(KX) },
TypeL32: func() RR { return new(L32) }, TypeL32: func() RR { return new(L32) },
@ -69,6 +71,7 @@ var TypeToRR = map[uint16]func() RR{
TypeSPF: func() RR { return new(SPF) }, TypeSPF: func() RR { return new(SPF) },
TypeSRV: func() RR { return new(SRV) }, TypeSRV: func() RR { return new(SRV) },
TypeSSHFP: func() RR { return new(SSHFP) }, TypeSSHFP: func() RR { return new(SSHFP) },
TypeSVCB: func() RR { return new(SVCB) },
TypeTA: func() RR { return new(TA) }, TypeTA: func() RR { return new(TA) },
TypeTALINK: func() RR { return new(TALINK) }, TypeTALINK: func() RR { return new(TALINK) },
TypeTKEY: func() RR { return new(TKEY) }, TypeTKEY: func() RR { return new(TKEY) },
@ -79,6 +82,7 @@ var TypeToRR = map[uint16]func() RR{
TypeUINFO: func() RR { return new(UINFO) }, TypeUINFO: func() RR { return new(UINFO) },
TypeURI: func() RR { return new(URI) }, TypeURI: func() RR { return new(URI) },
TypeX25: func() RR { return new(X25) }, TypeX25: func() RR { return new(X25) },
TypeZONEMD: func() RR { return new(ZONEMD) },
} }
// TypeToString is a map of strings for each RR type. // TypeToString is a map of strings for each RR type.
@ -87,6 +91,7 @@ var TypeToString = map[uint16]string{
TypeAAAA: "AAAA", TypeAAAA: "AAAA",
TypeAFSDB: "AFSDB", TypeAFSDB: "AFSDB",
TypeANY: "ANY", TypeANY: "ANY",
TypeAPL: "APL",
TypeATMA: "ATMA", TypeATMA: "ATMA",
TypeAVC: "AVC", TypeAVC: "AVC",
TypeAXFR: "AXFR", TypeAXFR: "AXFR",
@ -108,6 +113,7 @@ var TypeToString = map[uint16]string{
TypeGPOS: "GPOS", TypeGPOS: "GPOS",
TypeHINFO: "HINFO", TypeHINFO: "HINFO",
TypeHIP: "HIP", TypeHIP: "HIP",
TypeHTTPS: "HTTPS",
TypeISDN: "ISDN", TypeISDN: "ISDN",
TypeIXFR: "IXFR", TypeIXFR: "IXFR",
TypeKEY: "KEY", TypeKEY: "KEY",
@ -151,6 +157,7 @@ var TypeToString = map[uint16]string{
TypeSPF: "SPF", TypeSPF: "SPF",
TypeSRV: "SRV", TypeSRV: "SRV",
TypeSSHFP: "SSHFP", TypeSSHFP: "SSHFP",
TypeSVCB: "SVCB",
TypeTA: "TA", TypeTA: "TA",
TypeTALINK: "TALINK", TypeTALINK: "TALINK",
TypeTKEY: "TKEY", TypeTKEY: "TKEY",
@ -162,6 +169,7 @@ var TypeToString = map[uint16]string{
TypeUNSPEC: "UNSPEC", TypeUNSPEC: "UNSPEC",
TypeURI: "URI", TypeURI: "URI",
TypeX25: "X25", TypeX25: "X25",
TypeZONEMD: "ZONEMD",
TypeNSAPPTR: "NSAP-PTR", TypeNSAPPTR: "NSAP-PTR",
} }
@ -169,6 +177,7 @@ func (rr *A) Header() *RR_Header { return &rr.Hdr }
func (rr *AAAA) Header() *RR_Header { return &rr.Hdr } func (rr *AAAA) Header() *RR_Header { return &rr.Hdr }
func (rr *AFSDB) Header() *RR_Header { return &rr.Hdr } func (rr *AFSDB) Header() *RR_Header { return &rr.Hdr }
func (rr *ANY) Header() *RR_Header { return &rr.Hdr } func (rr *ANY) Header() *RR_Header { return &rr.Hdr }
func (rr *APL) Header() *RR_Header { return &rr.Hdr }
func (rr *AVC) Header() *RR_Header { return &rr.Hdr } func (rr *AVC) Header() *RR_Header { return &rr.Hdr }
func (rr *CAA) Header() *RR_Header { return &rr.Hdr } func (rr *CAA) Header() *RR_Header { return &rr.Hdr }
func (rr *CDNSKEY) Header() *RR_Header { return &rr.Hdr } func (rr *CDNSKEY) Header() *RR_Header { return &rr.Hdr }
@ -188,6 +197,7 @@ func (rr *GID) Header() *RR_Header { return &rr.Hdr }
func (rr *GPOS) Header() *RR_Header { return &rr.Hdr } func (rr *GPOS) Header() *RR_Header { return &rr.Hdr }
func (rr *HINFO) Header() *RR_Header { return &rr.Hdr } func (rr *HINFO) Header() *RR_Header { return &rr.Hdr }
func (rr *HIP) Header() *RR_Header { return &rr.Hdr } func (rr *HIP) Header() *RR_Header { return &rr.Hdr }
func (rr *HTTPS) Header() *RR_Header { return &rr.Hdr }
func (rr *KEY) Header() *RR_Header { return &rr.Hdr } func (rr *KEY) Header() *RR_Header { return &rr.Hdr }
func (rr *KX) Header() *RR_Header { return &rr.Hdr } func (rr *KX) Header() *RR_Header { return &rr.Hdr }
func (rr *L32) Header() *RR_Header { return &rr.Hdr } func (rr *L32) Header() *RR_Header { return &rr.Hdr }
@ -226,6 +236,7 @@ func (rr *SOA) Header() *RR_Header { return &rr.Hdr }
func (rr *SPF) Header() *RR_Header { return &rr.Hdr } func (rr *SPF) Header() *RR_Header { return &rr.Hdr }
func (rr *SRV) Header() *RR_Header { return &rr.Hdr } func (rr *SRV) Header() *RR_Header { return &rr.Hdr }
func (rr *SSHFP) Header() *RR_Header { return &rr.Hdr } func (rr *SSHFP) Header() *RR_Header { return &rr.Hdr }
func (rr *SVCB) Header() *RR_Header { return &rr.Hdr }
func (rr *TA) Header() *RR_Header { return &rr.Hdr } func (rr *TA) Header() *RR_Header { return &rr.Hdr }
func (rr *TALINK) Header() *RR_Header { return &rr.Hdr } func (rr *TALINK) Header() *RR_Header { return &rr.Hdr }
func (rr *TKEY) Header() *RR_Header { return &rr.Hdr } func (rr *TKEY) Header() *RR_Header { return &rr.Hdr }
@ -236,16 +247,21 @@ func (rr *UID) Header() *RR_Header { return &rr.Hdr }
func (rr *UINFO) Header() *RR_Header { return &rr.Hdr } func (rr *UINFO) Header() *RR_Header { return &rr.Hdr }
func (rr *URI) Header() *RR_Header { return &rr.Hdr } func (rr *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 }
func (rr *ZONEMD) 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(off int, compression map[string]struct{}) int {
l := rr.Hdr.len(off, compression) l := rr.Hdr.len(off, compression)
l += net.IPv4len // A if len(rr.A) != 0 {
l += net.IPv4len
}
return l return l
} }
func (rr *AAAA) len(off int, compression map[string]struct{}) int { func (rr *AAAA) len(off int, compression map[string]struct{}) int {
l := rr.Hdr.len(off, compression) l := rr.Hdr.len(off, compression)
l += net.IPv6len // AAAA if len(rr.AAAA) != 0 {
l += net.IPv6len
}
return l return l
} }
func (rr *AFSDB) len(off int, compression map[string]struct{}) int { func (rr *AFSDB) len(off int, compression map[string]struct{}) int {
@ -258,6 +274,13 @@ func (rr *ANY) len(off int, compression map[string]struct{}) int {
l := rr.Hdr.len(off, compression) l := rr.Hdr.len(off, compression)
return l return l
} }
func (rr *APL) len(off int, compression map[string]struct{}) int {
l := rr.Hdr.len(off, compression)
for _, x := range rr.Prefixes {
l += x.len()
}
return l
}
func (rr *AVC) len(off int, compression map[string]struct{}) int { func (rr *AVC) len(off int, compression map[string]struct{}) int {
l := rr.Hdr.len(off, compression) l := rr.Hdr.len(off, compression)
for _, x := range rr.Txt { for _, x := range rr.Txt {
@ -308,12 +331,12 @@ func (rr *DS) len(off int, compression map[string]struct{}) int {
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
return l return l
} }
func (rr *EID) len(off int, compression map[string]struct{}) int { func (rr *EID) len(off int, compression map[string]struct{}) int {
l := rr.Hdr.len(off, compression) l := rr.Hdr.len(off, compression)
l += len(rr.Endpoint)/2 + 1 l += len(rr.Endpoint) / 2
return l return l
} }
func (rr *EUI48) len(off int, compression map[string]struct{}) int { func (rr *EUI48) len(off int, compression map[string]struct{}) int {
@ -365,7 +388,9 @@ func (rr *KX) len(off int, compression map[string]struct{}) int {
func (rr *L32) len(off int, compression map[string]struct{}) int { func (rr *L32) len(off int, compression map[string]struct{}) int {
l := rr.Hdr.len(off, compression) l := rr.Hdr.len(off, compression)
l += 2 // Preference l += 2 // Preference
l += net.IPv4len // Locator32 if len(rr.Locator32) != 0 {
l += net.IPv4len
}
return l return l
} }
func (rr *L64) len(off int, compression map[string]struct{}) int { func (rr *L64) len(off int, compression map[string]struct{}) int {
@ -446,7 +471,7 @@ func (rr *NID) len(off int, compression map[string]struct{}) int {
} }
func (rr *NIMLOC) len(off int, compression map[string]struct{}) int { func (rr *NIMLOC) len(off int, compression map[string]struct{}) int {
l := rr.Hdr.len(off, compression) l := rr.Hdr.len(off, compression)
l += len(rr.Locator)/2 + 1 l += len(rr.Locator) / 2
return l return l
} }
func (rr *NINFO) len(off int, compression map[string]struct{}) int { func (rr *NINFO) len(off int, compression map[string]struct{}) int {
@ -499,7 +524,7 @@ func (rr *PX) len(off int, compression map[string]struct{}) int {
} }
func (rr *RFC3597) len(off int, compression map[string]struct{}) int { func (rr *RFC3597) len(off int, compression map[string]struct{}) int {
l := rr.Hdr.len(off, compression) l := rr.Hdr.len(off, compression)
l += len(rr.Rdata)/2 + 1 l += len(rr.Rdata) / 2
return l return l
} }
func (rr *RKEY) len(off int, compression map[string]struct{}) int { func (rr *RKEY) len(off int, compression map[string]struct{}) int {
@ -540,7 +565,7 @@ func (rr *SMIMEA) len(off int, compression map[string]struct{}) int {
l++ // Usage l++ // Usage
l++ // Selector l++ // Selector
l++ // MatchingType l++ // MatchingType
l += len(rr.Certificate)/2 + 1 l += len(rr.Certificate) / 2
return l return l
} }
func (rr *SOA) len(off int, compression map[string]struct{}) int { func (rr *SOA) len(off int, compression map[string]struct{}) int {
@ -573,7 +598,16 @@ func (rr *SSHFP) len(off int, compression map[string]struct{}) int {
l := rr.Hdr.len(off, compression) l := rr.Hdr.len(off, compression)
l++ // Algorithm l++ // Algorithm
l++ // Type l++ // Type
l += len(rr.FingerPrint)/2 + 1 l += len(rr.FingerPrint) / 2
return l
}
func (rr *SVCB) len(off int, compression map[string]struct{}) int {
l := rr.Hdr.len(off, compression)
l += 2 // Priority
l += domainNameLen(rr.Target, off+l, compression, false)
for _, x := range rr.Value {
l += 4 + int(x.len())
}
return l return l
} }
func (rr *TA) len(off int, compression map[string]struct{}) int { func (rr *TA) len(off int, compression map[string]struct{}) int {
@ -581,7 +615,7 @@ func (rr *TA) len(off int, compression map[string]struct{}) int {
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
return l return l
} }
func (rr *TALINK) len(off int, compression map[string]struct{}) int { func (rr *TALINK) len(off int, compression map[string]struct{}) int {
@ -608,7 +642,7 @@ func (rr *TLSA) len(off int, compression map[string]struct{}) int {
l++ // Usage l++ // Usage
l++ // Selector l++ // Selector
l++ // MatchingType l++ // MatchingType
l += len(rr.Certificate)/2 + 1 l += len(rr.Certificate) / 2
return l return l
} }
func (rr *TSIG) len(off int, compression map[string]struct{}) int { func (rr *TSIG) len(off int, compression map[string]struct{}) int {
@ -653,6 +687,14 @@ func (rr *X25) len(off int, compression map[string]struct{}) int {
l += len(rr.PSDNAddress) + 1 l += len(rr.PSDNAddress) + 1
return l return l
} }
func (rr *ZONEMD) len(off int, compression map[string]struct{}) int {
l := rr.Hdr.len(off, compression)
l += 4 // Serial
l++ // Scheme
l++ // Hash
l += len(rr.Digest) / 2
return l
}
// copy() functions // copy() functions
func (rr *A) copy() RR { func (rr *A) copy() RR {
@ -667,6 +709,13 @@ func (rr *AFSDB) copy() RR {
func (rr *ANY) copy() RR { func (rr *ANY) copy() RR {
return &ANY{rr.Hdr} return &ANY{rr.Hdr}
} }
func (rr *APL) copy() RR {
Prefixes := make([]APLPrefix, len(rr.Prefixes))
for i, e := range rr.Prefixes {
Prefixes[i] = e.copy()
}
return &APL{rr.Hdr, Prefixes}
}
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)
@ -675,6 +724,12 @@ func (rr *AVC) copy() RR {
func (rr *CAA) copy() RR { func (rr *CAA) copy() RR {
return &CAA{rr.Hdr, rr.Flag, rr.Tag, rr.Value} return &CAA{rr.Hdr, rr.Flag, rr.Tag, rr.Value}
} }
func (rr *CDNSKEY) copy() RR {
return &CDNSKEY{*rr.DNSKEY.copy().(*DNSKEY)}
}
func (rr *CDS) copy() RR {
return &CDS{*rr.DS.copy().(*DS)}
}
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, rr.Type, rr.KeyTag, rr.Algorithm, rr.Certificate}
} }
@ -689,6 +744,9 @@ func (rr *CSYNC) copy() RR {
func (rr *DHCID) copy() RR { func (rr *DHCID) copy() RR {
return &DHCID{rr.Hdr, rr.Digest} return &DHCID{rr.Hdr, rr.Digest}
} }
func (rr *DLV) copy() RR {
return &DLV{*rr.DS.copy().(*DS)}
}
func (rr *DNAME) copy() RR { func (rr *DNAME) copy() RR {
return &DNAME{rr.Hdr, rr.Target} return &DNAME{rr.Hdr, rr.Target}
} }
@ -721,6 +779,12 @@ func (rr *HIP) copy() RR {
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, rr.HitLength, rr.PublicKeyAlgorithm, rr.PublicKeyLength, rr.Hit, rr.PublicKey, RendezvousServers}
} }
func (rr *HTTPS) copy() RR {
return &HTTPS{*rr.SVCB.copy().(*SVCB)}
}
func (rr *KEY) copy() RR {
return &KEY{*rr.DNSKEY.copy().(*DNSKEY)}
}
func (rr *KX) copy() RR { func (rr *KX) copy() RR {
return &KX{rr.Hdr, rr.Preference, rr.Exchanger} return &KX{rr.Hdr, rr.Preference, rr.Exchanger}
} }
@ -798,7 +862,9 @@ func (rr *OPENPGPKEY) copy() RR {
} }
func (rr *OPT) copy() RR { func (rr *OPT) copy() RR {
Option := make([]EDNS0, len(rr.Option)) Option := make([]EDNS0, len(rr.Option))
copy(Option, rr.Option) for i, e := range rr.Option {
Option[i] = e.copy()
}
return &OPT{rr.Hdr, Option} return &OPT{rr.Hdr, Option}
} }
func (rr *PTR) copy() RR { func (rr *PTR) copy() RR {
@ -822,6 +888,9 @@ func (rr *RRSIG) copy() RR {
func (rr *RT) copy() RR { func (rr *RT) copy() RR {
return &RT{rr.Hdr, rr.Preference, rr.Host} return &RT{rr.Hdr, rr.Preference, rr.Host}
} }
func (rr *SIG) copy() RR {
return &SIG{*rr.RRSIG.copy().(*RRSIG)}
}
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, rr.Usage, rr.Selector, rr.MatchingType, rr.Certificate}
} }
@ -839,6 +908,13 @@ func (rr *SRV) copy() RR {
func (rr *SSHFP) copy() RR { func (rr *SSHFP) copy() RR {
return &SSHFP{rr.Hdr, rr.Algorithm, rr.Type, rr.FingerPrint} return &SSHFP{rr.Hdr, rr.Algorithm, rr.Type, rr.FingerPrint}
} }
func (rr *SVCB) copy() RR {
Value := make([]SVCBKeyValue, len(rr.Value))
for i, e := range rr.Value {
Value[i] = e.copy()
}
return &SVCB{rr.Hdr, rr.Priority, rr.Target, Value}
}
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, rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest}
} }
@ -871,3 +947,6 @@ func (rr *URI) copy() RR {
func (rr *X25) copy() RR { func (rr *X25) copy() RR {
return &X25{rr.Hdr, rr.PSDNAddress} return &X25{rr.Hdr, rr.PSDNAddress}
} }
func (rr *ZONEMD) copy() RR {
return &ZONEMD{rr.Hdr, rr.Serial, rr.Scheme, rr.Hash, rr.Digest}
}

View File

@ -1,9 +0,0 @@
The MIT License (MIT)
Copyright (c) 2016 Nathan Osman
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,125 +0,0 @@
package cache
import (
"crypto/md5"
"encoding/hex"
"fmt"
"io"
"log"
"mime"
"net/http"
"os"
"path"
"strconv"
"sync"
"time"
)
// Reader is a generic interface for reading cache entries either from disk or
// directly attached to a downloader.
type Reader interface {
io.ReadCloser
GetEntry() (*Entry, error)
}
// Cache provides access to entries in the cache.
type Cache struct {
mutex sync.Mutex
directory string
downloaders map[string]*downloader
waitGroup sync.WaitGroup
}
// NewCache creates a new cache in the specified directory.
func NewCache(directory string) (*Cache, error) {
if err := os.MkdirAll(directory, 0775); err != nil {
return nil, err
}
return &Cache{
directory: directory,
downloaders: make(map[string]*downloader),
}, nil
}
// getFilenames returns the filenames for the JSON and data files from a URL.
func (c *Cache) getFilenames(rawurl string) (hash, jsonFilename, dataFilename string) {
b := md5.Sum([]byte(rawurl))
hash = hex.EncodeToString(b[:])
jsonFilename = path.Join(c.directory, fmt.Sprintf("%s.json", hash))
dataFilename = path.Join(c.directory, fmt.Sprintf("%s.data", hash))
return
}
// GetReader obtains a Reader for the specified rawurl. If a downloader
// currently exists for the URL, a live reader is created and connected to it.
// If the URL exists in the cache, it is read using the standard file API. If
// not, a downloader and live reader are created.
func (c *Cache) GetReader(rawurl string, maxAge time.Duration) (Reader, error) {
hash, jsonFilename, dataFilename := c.getFilenames(rawurl)
c.mutex.Lock()
defer c.mutex.Unlock()
d, ok := c.downloaders[hash]
if !ok {
_, err := os.Stat(jsonFilename)
if err != nil {
if !os.IsNotExist(err) {
return nil, err
}
} else {
r, err := newDiskReader(jsonFilename, dataFilename)
if err != nil {
return nil, err
}
e, _ := r.GetEntry()
lastModified, _ := time.Parse(http.TimeFormat, e.LastModified)
if e.Complete &&
(maxAge == -1 ||
lastModified.Before(time.Now().Add(maxAge))) {
log.Println("[HIT]", rawurl)
return r, nil
}
}
d = newDownloader(rawurl, jsonFilename, dataFilename)
go func() {
d.WaitForDone()
c.mutex.Lock()
defer c.mutex.Unlock()
delete(c.downloaders, hash)
c.waitGroup.Done()
}()
c.downloaders[hash] = d
c.waitGroup.Add(1)
}
log.Println("[MISS]", rawurl)
return newLiveReader(d, dataFilename)
}
// Insert adds an item into the cache.
func (c *Cache) Insert(rawurl string, r io.Reader) error {
_, jsonFilename, dataFilename := c.getFilenames(rawurl)
f, err := os.Open(dataFilename)
if err != nil {
return err
}
defer f.Close()
n, err := io.Copy(f, r)
if err != nil {
return err
}
e := &Entry{
URL: rawurl,
Complete: true,
ContentLength: strconv.FormatInt(n, 10),
ContentType: mime.TypeByExtension(rawurl),
LastModified: time.Now().Format(http.TimeFormat),
}
return e.Save(jsonFilename)
}
// TODO: implement some form of "safe abort" for downloads so that the entire
// application doesn't end up spinning its tires waiting for downloads to end.
// Close waits for all downloaders to complete before shutting down.
func (c *Cache) Close() {
c.waitGroup.Wait()
}

View File

@ -1,43 +0,0 @@
package cache
import (
"os"
)
// diskReader reads a file from the cache on disk.
type diskReader struct {
entry *Entry
file *os.File
}
// newDiskReader creates a reader from the provided JSON and data filenames.
// Failure to open either of these results in an immediate error.
func newDiskReader(jsonFilename, dataFilename string) (*diskReader, error) {
e := &Entry{}
if err := e.Load(jsonFilename); err != nil {
return nil, err
}
f, err := os.Open(dataFilename)
if err != nil {
return nil, err
}
return &diskReader{
entry: e,
file: f,
}, nil
}
// Read attempts to read as much data as possible into the provided buffer.
func (d *diskReader) Read(p []byte) (int, error) {
return d.file.Read(p)
}
// Close attempts to close the data file.
func (d *diskReader) Close() error {
return d.file.Close()
}
// GetEntry returns the Entry associated with the file.
func (d *diskReader) GetEntry() (*Entry, error) {
return d.entry, nil
}

View File

@ -1,105 +0,0 @@
package cache
import (
"io"
"net/http"
"os"
"strconv"
"sync"
"time"
)
// DownloadError conveys information about a download request that failed.
type DownloadError struct {
Status string
}
// Error returns a description of the error.
func (d *DownloadError) Error() string {
return d.Status
}
// downloader attempts to download a file from a remote URL.
type downloader struct {
doneMutex sync.Mutex
err error
entry *Entry
entryMutex sync.Mutex
}
// newDownloader creates a new downloader.
func newDownloader(rawurl, jsonFilename, dataFilename string) *downloader {
d := &downloader{}
d.doneMutex.Lock()
d.entryMutex.Lock()
go func() {
defer func() {
d.doneMutex.Unlock()
}()
once := &sync.Once{}
trigger := func() {
once.Do(func() {
d.entryMutex.Unlock()
})
}
defer trigger()
resp, err := http.Get(rawurl)
if err != nil {
d.err = err
return
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
d.err = &DownloadError{
Status: resp.Status,
}
return
}
f, err := os.Create(dataFilename)
if err != nil {
d.err = err
return
}
defer f.Close()
d.entry = &Entry{
URL: rawurl,
ContentLength: strconv.FormatInt(resp.ContentLength, 10),
ContentType: resp.Header.Get("Content-Type"),
LastModified: resp.Header.Get("Last-Modified"),
}
if d.entry.ContentType == "" {
d.entry.ContentType = "application/octet-stream"
}
if d.entry.LastModified == "" {
d.entry.LastModified = time.Now().Format(http.TimeFormat)
}
if err = d.entry.Save(jsonFilename); err != nil {
d.err = err
return
}
trigger()
n, err := io.Copy(f, resp.Body)
if err != nil {
d.err = err
return
}
d.entry.ContentLength = strconv.FormatInt(n, 10)
d.entry.Complete = true
d.entry.Save(jsonFilename)
}()
return d
}
// GetEntry retrieves the entry associated with the download.
func (d *downloader) GetEntry() (*Entry, error) {
d.entryMutex.Lock()
defer d.entryMutex.Unlock()
return d.entry, d.err
}
// WaitForDone will block until the download completes.
func (d *downloader) WaitForDone() error {
d.doneMutex.Lock()
defer d.doneMutex.Unlock()
return d.err
}

View File

@ -1,35 +0,0 @@
package cache
import (
"encoding/json"
"os"
)
// Entry represents an individual item in the cache.
type Entry struct {
URL string `json:"url"`
Complete bool `json:"complete"`
ContentLength string `json:"content_length"`
ContentType string `json:"content_type"`
LastModified string `json:"last_modified"`
}
// Load reads the entry from disk.
func (e *Entry) Load(filename string) error {
f, err := os.Open(filename)
if err != nil {
return err
}
defer f.Close()
return json.NewDecoder(f).Decode(e)
}
// Save writes the entry to disk.
func (e *Entry) Save(filename string) error {
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()
return json.NewEncoder(f).Encode(e)
}

View File

@ -1,102 +0,0 @@
package cache
import (
"github.com/fsnotify/fsnotify"
"io"
"os"
)
// liveReader reads a file from disk, synchronizing reads with a downloader.
type liveReader struct {
downloader *downloader
dataFilename string
file *os.File
entry *Entry
done chan error
err error
eof bool
}
// newLiveReader creates a reader from the provided downloader and data
// file. fsnotify is used to watch for writes to the file to avoid using a
// spinloop. Invoking this function assumes the existence of the data file.
func newLiveReader(d *downloader, dataFilename string) (*liveReader, error) {
l := &liveReader{
downloader: d,
dataFilename: dataFilename,
done: make(chan error),
}
go func() {
defer close(l.done)
l.done <- d.WaitForDone()
}()
return l, nil
}
// Read attempts to read as much data as possible into the provided buffer.
// Since data is being downloaded as data is being read, fsnotify is used to
// monitor writes to the file. This function blocks until the requested amount
// of data is read, an error occurs, or EOF is encountered.
func (l *liveReader) Read(p []byte) (int, error) {
if l.err != nil {
return 0, l.err
}
if l.file == nil {
f, err := os.Open(l.dataFilename)
if err != nil {
l.err = err
return 0, l.err
}
l.file = f
}
bytesRead := 0
watcher, err := fsnotify.NewWatcher()
if err != nil {
l.err = err
return 0, l.err
}
defer watcher.Close()
if err := watcher.Add(l.dataFilename); err != nil {
l.err = err
return 0, l.err
}
loop:
for bytesRead < len(p) {
n, err := l.file.Read(p[bytesRead:])
bytesRead += n
if err != nil {
if err != io.EOF || l.eof {
l.err = err
break loop
}
for {
select {
case e := <-watcher.Events:
if e.Op&fsnotify.Write != fsnotify.Write {
continue
}
case err = <-l.done:
l.err = err
l.eof = true
}
continue loop
}
}
}
return bytesRead, l.err
}
// Close attempts to close the data file (if opened).
func (l *liveReader) Close() error {
if l.file != nil {
return l.file.Close()
}
return nil
}
// GetEntry returns the Entry associated with the file, blocking until either
// the data is available or an error occurs.
func (l *liveReader) GetEntry() (*Entry, error) {
return l.downloader.GetEntry()
}

3
vendor/golang.org/x/crypto/AUTHORS generated vendored
View File

@ -1,3 +0,0 @@
# This source code refers to The Go Authors for copyright purposes.
# The master list of authors is in the main Go distribution,
# visible at https://tip.golang.org/AUTHORS.

View File

@ -1,3 +0,0 @@
# This source code was written by the Go contributors.
# The master list of contributors is in the main Go distribution,
# visible at https://tip.golang.org/CONTRIBUTORS.

27
vendor/golang.org/x/crypto/LICENSE generated vendored
View File

@ -1,27 +0,0 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

22
vendor/golang.org/x/crypto/PATENTS generated vendored
View File

@ -1,22 +0,0 @@
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.

View File

@ -1,217 +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 ed25519 implements the Ed25519 signature algorithm. See
// https://ed25519.cr.yp.to/.
//
// These functions are also compatible with the “Ed25519” function defined in
// RFC 8032. However, unlike RFC 8032's formulation, this package's private key
// representation includes a public key suffix to make multiple signing
// operations with the same key more efficient. This package refers to the RFC
// 8032 private key as the “seed”.
package ed25519
// This code is a port of the public domain, “ref10” implementation of ed25519
// from SUPERCOP.
import (
"bytes"
"crypto"
cryptorand "crypto/rand"
"crypto/sha512"
"errors"
"io"
"strconv"
"golang.org/x/crypto/ed25519/internal/edwards25519"
)
const (
// PublicKeySize is the size, in bytes, of public keys as used in this package.
PublicKeySize = 32
// PrivateKeySize is the size, in bytes, of private keys as used in this package.
PrivateKeySize = 64
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
SignatureSize = 64
// SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032.
SeedSize = 32
)
// PublicKey is the type of Ed25519 public keys.
type PublicKey []byte
// PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer.
type PrivateKey []byte
// Public returns the PublicKey corresponding to priv.
func (priv PrivateKey) Public() crypto.PublicKey {
publicKey := make([]byte, PublicKeySize)
copy(publicKey, priv[32:])
return PublicKey(publicKey)
}
// Seed returns the private key seed corresponding to priv. It is provided for
// interoperability with RFC 8032. RFC 8032's private keys correspond to seeds
// in this package.
func (priv PrivateKey) Seed() []byte {
seed := make([]byte, SeedSize)
copy(seed, priv[:32])
return seed
}
// Sign signs the given message with priv.
// Ed25519 performs two passes over messages to be signed and therefore cannot
// handle pre-hashed messages. Thus opts.HashFunc() must return zero to
// indicate the message hasn't been hashed. This can be achieved by passing
// crypto.Hash(0) as the value for opts.
func (priv PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) {
if opts.HashFunc() != crypto.Hash(0) {
return nil, errors.New("ed25519: cannot sign hashed message")
}
return Sign(priv, message), nil
}
// GenerateKey generates a public/private key pair using entropy from rand.
// If rand is nil, crypto/rand.Reader will be used.
func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) {
if rand == nil {
rand = cryptorand.Reader
}
seed := make([]byte, SeedSize)
if _, err := io.ReadFull(rand, seed); err != nil {
return nil, nil, err
}
privateKey := NewKeyFromSeed(seed)
publicKey := make([]byte, PublicKeySize)
copy(publicKey, privateKey[32:])
return publicKey, privateKey, nil
}
// NewKeyFromSeed calculates a private key from a seed. It will panic if
// len(seed) is not SeedSize. This function is provided for interoperability
// with RFC 8032. RFC 8032's private keys correspond to seeds in this
// package.
func NewKeyFromSeed(seed []byte) PrivateKey {
if l := len(seed); l != SeedSize {
panic("ed25519: bad seed length: " + strconv.Itoa(l))
}
digest := sha512.Sum512(seed)
digest[0] &= 248
digest[31] &= 127
digest[31] |= 64
var A edwards25519.ExtendedGroupElement
var hBytes [32]byte
copy(hBytes[:], digest[:])
edwards25519.GeScalarMultBase(&A, &hBytes)
var publicKeyBytes [32]byte
A.ToBytes(&publicKeyBytes)
privateKey := make([]byte, PrivateKeySize)
copy(privateKey, seed)
copy(privateKey[32:], publicKeyBytes[:])
return privateKey
}
// Sign signs the message with privateKey and returns a signature. It will
// panic if len(privateKey) is not PrivateKeySize.
func Sign(privateKey PrivateKey, message []byte) []byte {
if l := len(privateKey); l != PrivateKeySize {
panic("ed25519: bad private key length: " + strconv.Itoa(l))
}
h := sha512.New()
h.Write(privateKey[:32])
var digest1, messageDigest, hramDigest [64]byte
var expandedSecretKey [32]byte
h.Sum(digest1[:0])
copy(expandedSecretKey[:], digest1[:])
expandedSecretKey[0] &= 248
expandedSecretKey[31] &= 63
expandedSecretKey[31] |= 64
h.Reset()
h.Write(digest1[32:])
h.Write(message)
h.Sum(messageDigest[:0])
var messageDigestReduced [32]byte
edwards25519.ScReduce(&messageDigestReduced, &messageDigest)
var R edwards25519.ExtendedGroupElement
edwards25519.GeScalarMultBase(&R, &messageDigestReduced)
var encodedR [32]byte
R.ToBytes(&encodedR)
h.Reset()
h.Write(encodedR[:])
h.Write(privateKey[32:])
h.Write(message)
h.Sum(hramDigest[:0])
var hramDigestReduced [32]byte
edwards25519.ScReduce(&hramDigestReduced, &hramDigest)
var s [32]byte
edwards25519.ScMulAdd(&s, &hramDigestReduced, &expandedSecretKey, &messageDigestReduced)
signature := make([]byte, SignatureSize)
copy(signature[:], encodedR[:])
copy(signature[32:], s[:])
return signature
}
// Verify reports whether sig is a valid signature of message by publicKey. It
// will panic if len(publicKey) is not PublicKeySize.
func Verify(publicKey PublicKey, message, sig []byte) bool {
if l := len(publicKey); l != PublicKeySize {
panic("ed25519: bad public key length: " + strconv.Itoa(l))
}
if len(sig) != SignatureSize || sig[63]&224 != 0 {
return false
}
var A edwards25519.ExtendedGroupElement
var publicKeyBytes [32]byte
copy(publicKeyBytes[:], publicKey)
if !A.FromBytes(&publicKeyBytes) {
return false
}
edwards25519.FeNeg(&A.X, &A.X)
edwards25519.FeNeg(&A.T, &A.T)
h := sha512.New()
h.Write(sig[:32])
h.Write(publicKey[:])
h.Write(message)
var digest [64]byte
h.Sum(digest[:0])
var hReduced [32]byte
edwards25519.ScReduce(&hReduced, &digest)
var R edwards25519.ProjectiveGroupElement
var s [32]byte
copy(s[:], sig[32:])
// https://tools.ietf.org/html/rfc8032#section-5.1.7 requires that s be in
// the range [0, order) in order to prevent signature malleability.
if !edwards25519.ScMinimal(&s) {
return false
}
edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &s)
var checkR [32]byte
R.ToBytes(&checkR)
return bytes.Equal(sig[:32], checkR[:])
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -129,7 +129,8 @@ func loadIndirect(ins LoadIndirect, in []byte, regX uint32) (uint32, bool) {
func loadMemShift(ins LoadMemShift, in []byte) (uint32, bool) { func loadMemShift(ins LoadMemShift, in []byte) (uint32, bool) {
offset := int(ins.Off) offset := int(ins.Off)
if !inBounds(len(in), offset, 0) { // Size of LoadMemShift is always 1 byte
if !inBounds(len(in), offset, 1) {
return 0, false return 0, false
} }

View File

@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build darwin dragonfly freebsd linux netbsd openbsd solaris //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
package socket package socket

View File

@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build darwin dragonfly freebsd netbsd openbsd //go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd
// +build aix darwin dragonfly freebsd netbsd openbsd
package socket package socket

View File

@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build arm mips mipsle 386 //go:build (arm || mips || mipsle || 386 || ppc) && linux
// +build arm mips mipsle 386 ppc
// +build linux // +build linux
package socket package socket

View File

@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build arm64 amd64 ppc64 ppc64le mips64 mips64le s390x //go:build (arm64 || amd64 || ppc64 || ppc64le || mips64 || mips64le || riscv64 || s390x) && linux
// +build arm64 amd64 ppc64 ppc64le mips64 mips64le riscv64 s390x
// +build linux // +build linux
package socket package socket

View File

@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build amd64 //go:build amd64 && solaris
// +build solaris // +build amd64,solaris
package socket package socket

View File

@ -2,13 +2,24 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !zos
// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos
package socket package socket
type cmsghdr struct{} func controlHeaderLen() int {
return 0
}
const sizeofCmsghdr = 0 func controlMessageLen(dataLen int) int {
return 0
}
func controlMessageSpace(dataLen int) int {
return 0
}
type cmsghdr struct{}
func (h *cmsghdr) len() int { return 0 } func (h *cmsghdr) len() int { return 0 }
func (h *cmsghdr) lvl() int { return 0 } func (h *cmsghdr) lvl() int { return 0 }

View File

@ -0,0 +1,22 @@
// Copyright 2020 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.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
package socket
import "golang.org/x/sys/unix"
func controlHeaderLen() int {
return unix.CmsgLen(0)
}
func controlMessageLen(dataLen int) int {
return unix.CmsgLen(dataLen)
}
func controlMessageSpace(dataLen int) int {
return unix.CmsgSpace(dataLen)
}

View File

@ -0,0 +1,25 @@
// Copyright 2020 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"
func (h *cmsghdr) set(l, lvl, typ int) {
h.Len = int32(l)
h.Level = int32(lvl)
h.Type = int32(typ)
}
func controlHeaderLen() int {
return syscall.CmsgLen(0)
}
func controlMessageLen(dataLen int) int {
return syscall.CmsgLen(dataLen)
}
func controlMessageSpace(dataLen int) int {
return syscall.CmsgSpace(dataLen)
}

View File

@ -0,0 +1,26 @@
// Copyright 2021 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.
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
package socket
import (
"syscall"
)
// ioComplete checks the flags and result of a syscall, to be used as return
// value in a syscall.RawConn.Read or Write callback.
func ioComplete(flags int, operr error) bool {
if flags&syscall.MSG_DONTWAIT != 0 {
// Caller explicitly said don't wait, so always return immediately.
return true
}
if operr == syscall.EAGAIN || operr == syscall.EWOULDBLOCK {
// No data available, block for I/O and try again.
return false
}
return true
}

View File

@ -0,0 +1,22 @@
// Copyright 2021 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.
//go:build aix || windows || zos
// +build aix windows zos
package socket
import (
"syscall"
)
// ioComplete checks the flags and result of a syscall, to be used as return
// value in a syscall.RawConn.Read or Write callback.
func ioComplete(flags int, operr error) bool {
if operr == syscall.EAGAIN || operr == syscall.EWOULDBLOCK {
// No data available, block for I/O and try again.
return false
}
return true
}

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build darwin && go1.12
// +build darwin,go1.12 // +build darwin,go1.12
// This exists solely so we can linkname in symbols from syscall. // This exists solely so we can linkname in symbols from syscall.

View File

@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build darwin dragonfly freebsd linux netbsd openbsd solaris //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
package socket package socket

View File

@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build arm mips mipsle 386 //go:build (arm || mips || mipsle || 386 || ppc) && (darwin || dragonfly || freebsd || linux || netbsd || openbsd)
// +build arm mips mipsle 386 ppc
// +build darwin dragonfly freebsd linux netbsd openbsd // +build darwin dragonfly freebsd linux netbsd openbsd
package socket package socket

View File

@ -2,8 +2,9 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build arm64 amd64 ppc64 ppc64le mips64 mips64le s390x //go:build (arm64 || amd64 || ppc64 || ppc64le || mips64 || mips64le || riscv64 || s390x) && (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || zos)
// +build darwin dragonfly freebsd linux netbsd openbsd // +build arm64 amd64 ppc64 ppc64le mips64 mips64le riscv64 s390x
// +build aix darwin dragonfly freebsd linux netbsd openbsd zos
package socket package socket

View File

@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build amd64 //go:build amd64 && solaris
// +build solaris // +build amd64,solaris
package socket package socket

View File

@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !zos
// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos
package socket package socket

View File

@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build !linux,!netbsd //go:build !aix && !linux && !netbsd
// +build !aix,!linux,!netbsd
package socket package socket

View File

@ -2,29 +2,18 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build linux netbsd //go:build aix || linux || netbsd
// +build aix linux netbsd
package socket package socket
import "net" import (
"net"
"sync"
)
type mmsghdrs []mmsghdr 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 { func (hs mmsghdrs) unpack(ms []Message, parseFn func([]byte, string) (net.Addr, error), hint string) error {
for i := range hs { for i := range hs {
ms[i].N = int(hs[i].Len) ms[i].N = int(hs[i].Len)
@ -40,3 +29,86 @@ func (hs mmsghdrs) unpack(ms []Message, parseFn func([]byte, string) (net.Addr,
} }
return nil return nil
} }
// mmsghdrsPacker packs Message-slices into mmsghdrs (re-)using pre-allocated buffers.
type mmsghdrsPacker struct {
// hs are the pre-allocated mmsghdrs.
hs mmsghdrs
// sockaddrs is the pre-allocated buffer for the Hdr.Name buffers.
// We use one large buffer for all messages and slice it up.
sockaddrs []byte
// vs are the pre-allocated iovecs.
// We allocate one large buffer for all messages and slice it up. This allows to reuse the buffer
// if the number of buffers per message is distributed differently between calls.
vs []iovec
}
func (p *mmsghdrsPacker) prepare(ms []Message) {
n := len(ms)
if n <= cap(p.hs) {
p.hs = p.hs[:n]
} else {
p.hs = make(mmsghdrs, n)
}
if n*sizeofSockaddrInet6 <= cap(p.sockaddrs) {
p.sockaddrs = p.sockaddrs[:n*sizeofSockaddrInet6]
} else {
p.sockaddrs = make([]byte, n*sizeofSockaddrInet6)
}
nb := 0
for _, m := range ms {
nb += len(m.Buffers)
}
if nb <= cap(p.vs) {
p.vs = p.vs[:nb]
} else {
p.vs = make([]iovec, nb)
}
}
func (p *mmsghdrsPacker) pack(ms []Message, parseFn func([]byte, string) (net.Addr, error), marshalFn func(net.Addr, []byte) int) mmsghdrs {
p.prepare(ms)
hs := p.hs
vsRest := p.vs
saRest := p.sockaddrs
for i := range hs {
nvs := len(ms[i].Buffers)
vs := vsRest[:nvs]
vsRest = vsRest[nvs:]
var sa []byte
if parseFn != nil {
sa = saRest[:sizeofSockaddrInet6]
saRest = saRest[sizeofSockaddrInet6:]
} else if marshalFn != nil {
n := marshalFn(ms[i].Addr, saRest)
if n > 0 {
sa = saRest[:n]
saRest = saRest[n:]
}
}
hs[i].Hdr.pack(vs, ms[i].Buffers, ms[i].OOB, sa)
}
return hs
}
var defaultMmsghdrsPool = mmsghdrsPool{
p: sync.Pool{
New: func() interface{} {
return new(mmsghdrsPacker)
},
},
}
type mmsghdrsPool struct {
p sync.Pool
}
func (p *mmsghdrsPool) Get() *mmsghdrsPacker {
return p.p.Get().(*mmsghdrsPacker)
}
func (p *mmsghdrsPool) Put(packer *mmsghdrsPacker) {
p.p.Put(packer)
}

View File

@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build darwin dragonfly freebsd netbsd openbsd //go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd
// +build aix darwin dragonfly freebsd netbsd openbsd
package socket package socket

View File

@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build darwin dragonfly freebsd netbsd //go:build aix || darwin || dragonfly || freebsd || netbsd
// +build aix darwin dragonfly freebsd netbsd
package socket package socket

View File

@ -17,6 +17,9 @@ func (h *msghdr) pack(vs []iovec, bs [][]byte, oob []byte, sa []byte) {
if sa != nil { if sa != nil {
h.Name = (*byte)(unsafe.Pointer(&sa[0])) h.Name = (*byte)(unsafe.Pointer(&sa[0]))
h.Namelen = uint32(len(sa)) h.Namelen = uint32(len(sa))
} else {
h.Name = nil
h.Namelen = 0
} }
} }

View File

@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build arm mips mipsle 386 //go:build (arm || mips || mipsle || 386 || ppc) && linux
// +build arm mips mipsle 386 ppc
// +build linux // +build linux
package socket package socket

View File

@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build arm64 amd64 ppc64 ppc64le mips64 mips64le s390x //go:build (arm64 || amd64 || ppc64 || ppc64le || mips64 || mips64le || riscv64 || s390x) && linux
// +build arm64 amd64 ppc64 ppc64le mips64 mips64le riscv64 s390x
// +build linux // +build linux
package socket package socket

View File

@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build amd64 //go:build amd64 && solaris
// +build solaris // +build amd64,solaris
package socket package socket

View File

@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !zos
// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos
package socket package socket

View File

@ -0,0 +1,36 @@
// Copyright 2020 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.
//go:build s390x && zos
// +build s390x,zos
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.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) controllen() int {
return int(h.Controllen)
}
func (h *msghdr) flags() int {
return int(h.Flags)
}

Some files were not shown because too many files have changed in this diff Show More