forked from loweel/zabov
Compare commits
2 Commits
2fe0b7b0c2
...
bc2470e344
Author | SHA1 | Date |
---|---|---|
bloved | bc2470e344 | |
bloved | e2a625a92e |
19
02.cache.go
19
02.cache.go
|
@ -13,7 +13,7 @@ import (
|
||||||
|
|
||||||
type cacheItem struct {
|
type cacheItem struct {
|
||||||
Query []byte
|
Query []byte
|
||||||
Date time.Time
|
ExpireDate time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
//DomainCache stores a domain name inside the cache
|
//DomainCache stores a domain name inside the cache
|
||||||
|
@ -28,7 +28,17 @@ func DomainCache(s string, resp *dns.Msg) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Problems packing the response: ", err.Error())
|
fmt.Println("Problems packing the response: ", err.Error())
|
||||||
}
|
}
|
||||||
domain2cache.Date = time.Now()
|
if resp.Rcode == dns.RcodeSuccess{
|
||||||
|
// on success stores response normally
|
||||||
|
domain2cache.ExpireDate = time.Now().Add((time.Duration(ZabovCacheTTL) * time.Hour))
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
// on failure stores response for a very short time
|
||||||
|
if ZabovDebug {
|
||||||
|
fmt.Println("DomainCache(): DNS error Rcode: ", resp.Rcode, s, "cache time reduced to 10 seconds...")
|
||||||
|
}
|
||||||
|
domain2cache.ExpireDate = time.Now().Add((time.Duration(10) * time.Second))
|
||||||
|
}
|
||||||
|
|
||||||
err = enc.Encode(domain2cache)
|
err = enc.Encode(domain2cache)
|
||||||
|
|
||||||
|
@ -77,7 +87,10 @@ func GetDomainFromCache(s string) *dns.Msg {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if time.Since(record.Date) > (time.Duration(ZabovCacheTTL) * time.Hour) {
|
if time.Now().After(record.ExpireDate) {
|
||||||
|
if ZabovDebug {
|
||||||
|
fmt.Println("GetDomainFromCache(): entry expired:", s)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
7
go.mod
7
go.mod
|
@ -1,8 +1,11 @@
|
||||||
module zabov
|
module zabov
|
||||||
|
|
||||||
go 1.13
|
go 1.15
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/miekg/dns v1.1.27
|
github.com/golang/snappy v0.0.4
|
||||||
|
github.com/miekg/dns v1.1.43
|
||||||
github.com/syndtr/goleveldb v1.0.0
|
github.com/syndtr/goleveldb v1.0.0
|
||||||
|
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8
|
||||||
|
golang.org/x/sys v0.0.0-20210915083310-ed5796bab164
|
||||||
)
|
)
|
||||||
|
|
36
go.sum
36
go.sum
|
@ -1,35 +1,33 @@
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
|
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||||
|
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM=
|
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
|
||||||
github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8 h1:/6y1LfuqNuQdHAm0jjtPtgRcxIxjVZgm5OTu8/QhZvk=
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g=
|
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210915083310-ed5796bab164 h1:7ZDGnxgHAMw7thfC5bEos0RDAccZKxioiWBhfIe+tvw=
|
||||||
|
golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
|
|
@ -8,8 +8,11 @@
|
||||||
|
|
||||||
# Please keep the list sorted.
|
# Please keep the list sorted.
|
||||||
|
|
||||||
|
Amazon.com, Inc
|
||||||
Damian Gryski <dgryski@gmail.com>
|
Damian Gryski <dgryski@gmail.com>
|
||||||
|
Eric Buth <eric@topos.com>
|
||||||
Google Inc.
|
Google Inc.
|
||||||
Jan Mercl <0xjnml@gmail.com>
|
Jan Mercl <0xjnml@gmail.com>
|
||||||
|
Klaus Post <klauspost@gmail.com>
|
||||||
Rodolfo Carvalho <rhcarvalho@gmail.com>
|
Rodolfo Carvalho <rhcarvalho@gmail.com>
|
||||||
Sebastien Binet <seb.binet@gmail.com>
|
Sebastien Binet <seb.binet@gmail.com>
|
||||||
|
|
|
@ -26,9 +26,13 @@
|
||||||
|
|
||||||
# Please keep the list sorted.
|
# Please keep the list sorted.
|
||||||
|
|
||||||
|
Alex Legg <alexlegg@google.com>
|
||||||
Damian Gryski <dgryski@gmail.com>
|
Damian Gryski <dgryski@gmail.com>
|
||||||
|
Eric Buth <eric@topos.com>
|
||||||
Jan Mercl <0xjnml@gmail.com>
|
Jan Mercl <0xjnml@gmail.com>
|
||||||
|
Jonathan Swinney <jswinney@amazon.com>
|
||||||
Kai Backman <kaib@golang.org>
|
Kai Backman <kaib@golang.org>
|
||||||
|
Klaus Post <klauspost@gmail.com>
|
||||||
Marc-Antoine Ruel <maruel@chromium.org>
|
Marc-Antoine Ruel <maruel@chromium.org>
|
||||||
Nigel Tao <nigeltao@golang.org>
|
Nigel Tao <nigeltao@golang.org>
|
||||||
Rob Pike <r@golang.org>
|
Rob Pike <r@golang.org>
|
||||||
|
|
|
@ -52,6 +52,8 @@ const (
|
||||||
// Otherwise, a newly allocated slice will be returned.
|
// Otherwise, a newly allocated slice will be returned.
|
||||||
//
|
//
|
||||||
// The dst and src must not overlap. It is valid to pass a nil dst.
|
// The dst and src must not overlap. It is valid to pass a nil dst.
|
||||||
|
//
|
||||||
|
// Decode handles the Snappy block format, not the Snappy stream format.
|
||||||
func Decode(dst, src []byte) ([]byte, error) {
|
func Decode(dst, src []byte) ([]byte, error) {
|
||||||
dLen, s, err := decodedLen(src)
|
dLen, s, err := decodedLen(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -83,6 +85,8 @@ func NewReader(r io.Reader) *Reader {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reader is an io.Reader that can read Snappy-compressed bytes.
|
// Reader is an io.Reader that can read Snappy-compressed bytes.
|
||||||
|
//
|
||||||
|
// Reader handles the Snappy stream format, not the Snappy block format.
|
||||||
type Reader struct {
|
type Reader struct {
|
||||||
r io.Reader
|
r io.Reader
|
||||||
err error
|
err error
|
||||||
|
@ -114,32 +118,23 @@ func (r *Reader) readFull(p []byte, allowEOF bool) (ok bool) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read satisfies the io.Reader interface.
|
func (r *Reader) fill() error {
|
||||||
func (r *Reader) Read(p []byte) (int, error) {
|
for r.i >= r.j {
|
||||||
if r.err != nil {
|
|
||||||
return 0, r.err
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
if r.i < r.j {
|
|
||||||
n := copy(p, r.decoded[r.i:r.j])
|
|
||||||
r.i += n
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
if !r.readFull(r.buf[:4], true) {
|
if !r.readFull(r.buf[:4], true) {
|
||||||
return 0, r.err
|
return r.err
|
||||||
}
|
}
|
||||||
chunkType := r.buf[0]
|
chunkType := r.buf[0]
|
||||||
if !r.readHeader {
|
if !r.readHeader {
|
||||||
if chunkType != chunkTypeStreamIdentifier {
|
if chunkType != chunkTypeStreamIdentifier {
|
||||||
r.err = ErrCorrupt
|
r.err = ErrCorrupt
|
||||||
return 0, r.err
|
return r.err
|
||||||
}
|
}
|
||||||
r.readHeader = true
|
r.readHeader = true
|
||||||
}
|
}
|
||||||
chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16
|
chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16
|
||||||
if chunkLen > len(r.buf) {
|
if chunkLen > len(r.buf) {
|
||||||
r.err = ErrUnsupported
|
r.err = ErrUnsupported
|
||||||
return 0, r.err
|
return r.err
|
||||||
}
|
}
|
||||||
|
|
||||||
// The chunk types are specified at
|
// The chunk types are specified at
|
||||||
|
@ -149,11 +144,11 @@ func (r *Reader) Read(p []byte) (int, error) {
|
||||||
// Section 4.2. Compressed data (chunk type 0x00).
|
// Section 4.2. Compressed data (chunk type 0x00).
|
||||||
if chunkLen < checksumSize {
|
if chunkLen < checksumSize {
|
||||||
r.err = ErrCorrupt
|
r.err = ErrCorrupt
|
||||||
return 0, r.err
|
return r.err
|
||||||
}
|
}
|
||||||
buf := r.buf[:chunkLen]
|
buf := r.buf[:chunkLen]
|
||||||
if !r.readFull(buf, false) {
|
if !r.readFull(buf, false) {
|
||||||
return 0, r.err
|
return r.err
|
||||||
}
|
}
|
||||||
checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
|
checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
|
||||||
buf = buf[checksumSize:]
|
buf = buf[checksumSize:]
|
||||||
|
@ -161,19 +156,19 @@ func (r *Reader) Read(p []byte) (int, error) {
|
||||||
n, err := DecodedLen(buf)
|
n, err := DecodedLen(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.err = err
|
r.err = err
|
||||||
return 0, r.err
|
return r.err
|
||||||
}
|
}
|
||||||
if n > len(r.decoded) {
|
if n > len(r.decoded) {
|
||||||
r.err = ErrCorrupt
|
r.err = ErrCorrupt
|
||||||
return 0, r.err
|
return r.err
|
||||||
}
|
}
|
||||||
if _, err := Decode(r.decoded, buf); err != nil {
|
if _, err := Decode(r.decoded, buf); err != nil {
|
||||||
r.err = err
|
r.err = err
|
||||||
return 0, r.err
|
return r.err
|
||||||
}
|
}
|
||||||
if crc(r.decoded[:n]) != checksum {
|
if crc(r.decoded[:n]) != checksum {
|
||||||
r.err = ErrCorrupt
|
r.err = ErrCorrupt
|
||||||
return 0, r.err
|
return r.err
|
||||||
}
|
}
|
||||||
r.i, r.j = 0, n
|
r.i, r.j = 0, n
|
||||||
continue
|
continue
|
||||||
|
@ -182,25 +177,25 @@ func (r *Reader) Read(p []byte) (int, error) {
|
||||||
// Section 4.3. Uncompressed data (chunk type 0x01).
|
// Section 4.3. Uncompressed data (chunk type 0x01).
|
||||||
if chunkLen < checksumSize {
|
if chunkLen < checksumSize {
|
||||||
r.err = ErrCorrupt
|
r.err = ErrCorrupt
|
||||||
return 0, r.err
|
return r.err
|
||||||
}
|
}
|
||||||
buf := r.buf[:checksumSize]
|
buf := r.buf[:checksumSize]
|
||||||
if !r.readFull(buf, false) {
|
if !r.readFull(buf, false) {
|
||||||
return 0, r.err
|
return r.err
|
||||||
}
|
}
|
||||||
checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
|
checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
|
||||||
// Read directly into r.decoded instead of via r.buf.
|
// Read directly into r.decoded instead of via r.buf.
|
||||||
n := chunkLen - checksumSize
|
n := chunkLen - checksumSize
|
||||||
if n > len(r.decoded) {
|
if n > len(r.decoded) {
|
||||||
r.err = ErrCorrupt
|
r.err = ErrCorrupt
|
||||||
return 0, r.err
|
return r.err
|
||||||
}
|
}
|
||||||
if !r.readFull(r.decoded[:n], false) {
|
if !r.readFull(r.decoded[:n], false) {
|
||||||
return 0, r.err
|
return r.err
|
||||||
}
|
}
|
||||||
if crc(r.decoded[:n]) != checksum {
|
if crc(r.decoded[:n]) != checksum {
|
||||||
r.err = ErrCorrupt
|
r.err = ErrCorrupt
|
||||||
return 0, r.err
|
return r.err
|
||||||
}
|
}
|
||||||
r.i, r.j = 0, n
|
r.i, r.j = 0, n
|
||||||
continue
|
continue
|
||||||
|
@ -209,15 +204,15 @@ func (r *Reader) Read(p []byte) (int, error) {
|
||||||
// Section 4.1. Stream identifier (chunk type 0xff).
|
// Section 4.1. Stream identifier (chunk type 0xff).
|
||||||
if chunkLen != len(magicBody) {
|
if chunkLen != len(magicBody) {
|
||||||
r.err = ErrCorrupt
|
r.err = ErrCorrupt
|
||||||
return 0, r.err
|
return r.err
|
||||||
}
|
}
|
||||||
if !r.readFull(r.buf[:len(magicBody)], false) {
|
if !r.readFull(r.buf[:len(magicBody)], false) {
|
||||||
return 0, r.err
|
return r.err
|
||||||
}
|
}
|
||||||
for i := 0; i < len(magicBody); i++ {
|
for i := 0; i < len(magicBody); i++ {
|
||||||
if r.buf[i] != magicBody[i] {
|
if r.buf[i] != magicBody[i] {
|
||||||
r.err = ErrCorrupt
|
r.err = ErrCorrupt
|
||||||
return 0, r.err
|
return r.err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
|
@ -226,12 +221,44 @@ func (r *Reader) Read(p []byte) (int, error) {
|
||||||
if chunkType <= 0x7f {
|
if chunkType <= 0x7f {
|
||||||
// Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f).
|
// Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f).
|
||||||
r.err = ErrUnsupported
|
r.err = ErrUnsupported
|
||||||
return 0, r.err
|
return r.err
|
||||||
}
|
}
|
||||||
// Section 4.4 Padding (chunk type 0xfe).
|
// Section 4.4 Padding (chunk type 0xfe).
|
||||||
// Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd).
|
// Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd).
|
||||||
if !r.readFull(r.buf[:chunkLen], false) {
|
if !r.readFull(r.buf[:chunkLen], false) {
|
||||||
|
return r.err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read satisfies the io.Reader interface.
|
||||||
|
func (r *Reader) Read(p []byte) (int, error) {
|
||||||
|
if r.err != nil {
|
||||||
return 0, r.err
|
return 0, r.err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := r.fill(); err != nil {
|
||||||
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
n := copy(p, r.decoded[r.i:r.j])
|
||||||
|
r.i += n
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadByte satisfies the io.ByteReader interface.
|
||||||
|
func (r *Reader) ReadByte() (byte, error) {
|
||||||
|
if r.err != nil {
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := r.fill(); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c := r.decoded[r.i]
|
||||||
|
r.i++
|
||||||
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,494 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// +build !appengine
|
||||||
|
// +build gc
|
||||||
|
// +build !noasm
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// The asm code generally follows the pure Go code in decode_other.go, except
|
||||||
|
// where marked with a "!!!".
|
||||||
|
|
||||||
|
// func decode(dst, src []byte) int
|
||||||
|
//
|
||||||
|
// All local variables fit into registers. The non-zero stack size is only to
|
||||||
|
// spill registers and push args when issuing a CALL. The register allocation:
|
||||||
|
// - R2 scratch
|
||||||
|
// - R3 scratch
|
||||||
|
// - R4 length or x
|
||||||
|
// - R5 offset
|
||||||
|
// - R6 &src[s]
|
||||||
|
// - R7 &dst[d]
|
||||||
|
// + R8 dst_base
|
||||||
|
// + R9 dst_len
|
||||||
|
// + R10 dst_base + dst_len
|
||||||
|
// + R11 src_base
|
||||||
|
// + R12 src_len
|
||||||
|
// + R13 src_base + src_len
|
||||||
|
// - R14 used by doCopy
|
||||||
|
// - R15 used by doCopy
|
||||||
|
//
|
||||||
|
// The registers R8-R13 (marked with a "+") are set at the start of the
|
||||||
|
// function, and after a CALL returns, and are not otherwise modified.
|
||||||
|
//
|
||||||
|
// The d variable is implicitly R7 - R8, and len(dst)-d is R10 - R7.
|
||||||
|
// The s variable is implicitly R6 - R11, and len(src)-s is R13 - R6.
|
||||||
|
TEXT ·decode(SB), NOSPLIT, $56-56
|
||||||
|
// Initialize R6, R7 and R8-R13.
|
||||||
|
MOVD dst_base+0(FP), R8
|
||||||
|
MOVD dst_len+8(FP), R9
|
||||||
|
MOVD R8, R7
|
||||||
|
MOVD R8, R10
|
||||||
|
ADD R9, R10, R10
|
||||||
|
MOVD src_base+24(FP), R11
|
||||||
|
MOVD src_len+32(FP), R12
|
||||||
|
MOVD R11, R6
|
||||||
|
MOVD R11, R13
|
||||||
|
ADD R12, R13, R13
|
||||||
|
|
||||||
|
loop:
|
||||||
|
// for s < len(src)
|
||||||
|
CMP R13, R6
|
||||||
|
BEQ end
|
||||||
|
|
||||||
|
// R4 = uint32(src[s])
|
||||||
|
//
|
||||||
|
// switch src[s] & 0x03
|
||||||
|
MOVBU (R6), R4
|
||||||
|
MOVW R4, R3
|
||||||
|
ANDW $3, R3
|
||||||
|
MOVW $1, R1
|
||||||
|
CMPW R1, R3
|
||||||
|
BGE tagCopy
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// The code below handles literal tags.
|
||||||
|
|
||||||
|
// case tagLiteral:
|
||||||
|
// x := uint32(src[s] >> 2)
|
||||||
|
// switch
|
||||||
|
MOVW $60, R1
|
||||||
|
LSRW $2, R4, R4
|
||||||
|
CMPW R4, R1
|
||||||
|
BLS tagLit60Plus
|
||||||
|
|
||||||
|
// case x < 60:
|
||||||
|
// s++
|
||||||
|
ADD $1, R6, R6
|
||||||
|
|
||||||
|
doLit:
|
||||||
|
// This is the end of the inner "switch", when we have a literal tag.
|
||||||
|
//
|
||||||
|
// We assume that R4 == x and x fits in a uint32, where x is the variable
|
||||||
|
// used in the pure Go decode_other.go code.
|
||||||
|
|
||||||
|
// length = int(x) + 1
|
||||||
|
//
|
||||||
|
// Unlike the pure Go code, we don't need to check if length <= 0 because
|
||||||
|
// R4 can hold 64 bits, so the increment cannot overflow.
|
||||||
|
ADD $1, R4, R4
|
||||||
|
|
||||||
|
// Prepare to check if copying length bytes will run past the end of dst or
|
||||||
|
// src.
|
||||||
|
//
|
||||||
|
// R2 = len(dst) - d
|
||||||
|
// R3 = len(src) - s
|
||||||
|
MOVD R10, R2
|
||||||
|
SUB R7, R2, R2
|
||||||
|
MOVD R13, R3
|
||||||
|
SUB R6, R3, R3
|
||||||
|
|
||||||
|
// !!! Try a faster technique for short (16 or fewer bytes) copies.
|
||||||
|
//
|
||||||
|
// if length > 16 || len(dst)-d < 16 || len(src)-s < 16 {
|
||||||
|
// goto callMemmove // Fall back on calling runtime·memmove.
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// The C++ snappy code calls this TryFastAppend. It also checks len(src)-s
|
||||||
|
// against 21 instead of 16, because it cannot assume that all of its input
|
||||||
|
// is contiguous in memory and so it needs to leave enough source bytes to
|
||||||
|
// read the next tag without refilling buffers, but Go's Decode assumes
|
||||||
|
// contiguousness (the src argument is a []byte).
|
||||||
|
CMP $16, R4
|
||||||
|
BGT callMemmove
|
||||||
|
CMP $16, R2
|
||||||
|
BLT callMemmove
|
||||||
|
CMP $16, R3
|
||||||
|
BLT callMemmove
|
||||||
|
|
||||||
|
// !!! Implement the copy from src to dst as a 16-byte load and store.
|
||||||
|
// (Decode's documentation says that dst and src must not overlap.)
|
||||||
|
//
|
||||||
|
// This always copies 16 bytes, instead of only length bytes, but that's
|
||||||
|
// OK. If the input is a valid Snappy encoding then subsequent iterations
|
||||||
|
// will fix up the overrun. Otherwise, Decode returns a nil []byte (and a
|
||||||
|
// non-nil error), so the overrun will be ignored.
|
||||||
|
//
|
||||||
|
// Note that on arm64, it is legal and cheap to issue unaligned 8-byte or
|
||||||
|
// 16-byte loads and stores. This technique probably wouldn't be as
|
||||||
|
// effective on architectures that are fussier about alignment.
|
||||||
|
LDP 0(R6), (R14, R15)
|
||||||
|
STP (R14, R15), 0(R7)
|
||||||
|
|
||||||
|
// d += length
|
||||||
|
// s += length
|
||||||
|
ADD R4, R7, R7
|
||||||
|
ADD R4, R6, R6
|
||||||
|
B loop
|
||||||
|
|
||||||
|
callMemmove:
|
||||||
|
// if length > len(dst)-d || length > len(src)-s { etc }
|
||||||
|
CMP R2, R4
|
||||||
|
BGT errCorrupt
|
||||||
|
CMP R3, R4
|
||||||
|
BGT errCorrupt
|
||||||
|
|
||||||
|
// copy(dst[d:], src[s:s+length])
|
||||||
|
//
|
||||||
|
// This means calling runtime·memmove(&dst[d], &src[s], length), so we push
|
||||||
|
// R7, R6 and R4 as arguments. Coincidentally, we also need to spill those
|
||||||
|
// three registers to the stack, to save local variables across the CALL.
|
||||||
|
MOVD R7, 8(RSP)
|
||||||
|
MOVD R6, 16(RSP)
|
||||||
|
MOVD R4, 24(RSP)
|
||||||
|
MOVD R7, 32(RSP)
|
||||||
|
MOVD R6, 40(RSP)
|
||||||
|
MOVD R4, 48(RSP)
|
||||||
|
CALL runtime·memmove(SB)
|
||||||
|
|
||||||
|
// Restore local variables: unspill registers from the stack and
|
||||||
|
// re-calculate R8-R13.
|
||||||
|
MOVD 32(RSP), R7
|
||||||
|
MOVD 40(RSP), R6
|
||||||
|
MOVD 48(RSP), R4
|
||||||
|
MOVD dst_base+0(FP), R8
|
||||||
|
MOVD dst_len+8(FP), R9
|
||||||
|
MOVD R8, R10
|
||||||
|
ADD R9, R10, R10
|
||||||
|
MOVD src_base+24(FP), R11
|
||||||
|
MOVD src_len+32(FP), R12
|
||||||
|
MOVD R11, R13
|
||||||
|
ADD R12, R13, R13
|
||||||
|
|
||||||
|
// d += length
|
||||||
|
// s += length
|
||||||
|
ADD R4, R7, R7
|
||||||
|
ADD R4, R6, R6
|
||||||
|
B loop
|
||||||
|
|
||||||
|
tagLit60Plus:
|
||||||
|
// !!! This fragment does the
|
||||||
|
//
|
||||||
|
// s += x - 58; if uint(s) > uint(len(src)) { etc }
|
||||||
|
//
|
||||||
|
// checks. In the asm version, we code it once instead of once per switch case.
|
||||||
|
ADD R4, R6, R6
|
||||||
|
SUB $58, R6, R6
|
||||||
|
MOVD R6, R3
|
||||||
|
SUB R11, R3, R3
|
||||||
|
CMP R12, R3
|
||||||
|
BGT errCorrupt
|
||||||
|
|
||||||
|
// case x == 60:
|
||||||
|
MOVW $61, R1
|
||||||
|
CMPW R1, R4
|
||||||
|
BEQ tagLit61
|
||||||
|
BGT tagLit62Plus
|
||||||
|
|
||||||
|
// x = uint32(src[s-1])
|
||||||
|
MOVBU -1(R6), R4
|
||||||
|
B doLit
|
||||||
|
|
||||||
|
tagLit61:
|
||||||
|
// case x == 61:
|
||||||
|
// x = uint32(src[s-2]) | uint32(src[s-1])<<8
|
||||||
|
MOVHU -2(R6), R4
|
||||||
|
B doLit
|
||||||
|
|
||||||
|
tagLit62Plus:
|
||||||
|
CMPW $62, R4
|
||||||
|
BHI tagLit63
|
||||||
|
|
||||||
|
// case x == 62:
|
||||||
|
// x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16
|
||||||
|
MOVHU -3(R6), R4
|
||||||
|
MOVBU -1(R6), R3
|
||||||
|
ORR R3<<16, R4
|
||||||
|
B doLit
|
||||||
|
|
||||||
|
tagLit63:
|
||||||
|
// case x == 63:
|
||||||
|
// x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24
|
||||||
|
MOVWU -4(R6), R4
|
||||||
|
B doLit
|
||||||
|
|
||||||
|
// The code above handles literal tags.
|
||||||
|
// ----------------------------------------
|
||||||
|
// The code below handles copy tags.
|
||||||
|
|
||||||
|
tagCopy4:
|
||||||
|
// case tagCopy4:
|
||||||
|
// s += 5
|
||||||
|
ADD $5, R6, R6
|
||||||
|
|
||||||
|
// if uint(s) > uint(len(src)) { etc }
|
||||||
|
MOVD R6, R3
|
||||||
|
SUB R11, R3, R3
|
||||||
|
CMP R12, R3
|
||||||
|
BGT errCorrupt
|
||||||
|
|
||||||
|
// length = 1 + int(src[s-5])>>2
|
||||||
|
MOVD $1, R1
|
||||||
|
ADD R4>>2, R1, R4
|
||||||
|
|
||||||
|
// offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24)
|
||||||
|
MOVWU -4(R6), R5
|
||||||
|
B doCopy
|
||||||
|
|
||||||
|
tagCopy2:
|
||||||
|
// case tagCopy2:
|
||||||
|
// s += 3
|
||||||
|
ADD $3, R6, R6
|
||||||
|
|
||||||
|
// if uint(s) > uint(len(src)) { etc }
|
||||||
|
MOVD R6, R3
|
||||||
|
SUB R11, R3, R3
|
||||||
|
CMP R12, R3
|
||||||
|
BGT errCorrupt
|
||||||
|
|
||||||
|
// length = 1 + int(src[s-3])>>2
|
||||||
|
MOVD $1, R1
|
||||||
|
ADD R4>>2, R1, R4
|
||||||
|
|
||||||
|
// offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8)
|
||||||
|
MOVHU -2(R6), R5
|
||||||
|
B doCopy
|
||||||
|
|
||||||
|
tagCopy:
|
||||||
|
// We have a copy tag. We assume that:
|
||||||
|
// - R3 == src[s] & 0x03
|
||||||
|
// - R4 == src[s]
|
||||||
|
CMP $2, R3
|
||||||
|
BEQ tagCopy2
|
||||||
|
BGT tagCopy4
|
||||||
|
|
||||||
|
// case tagCopy1:
|
||||||
|
// s += 2
|
||||||
|
ADD $2, R6, R6
|
||||||
|
|
||||||
|
// if uint(s) > uint(len(src)) { etc }
|
||||||
|
MOVD R6, R3
|
||||||
|
SUB R11, R3, R3
|
||||||
|
CMP R12, R3
|
||||||
|
BGT errCorrupt
|
||||||
|
|
||||||
|
// offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1]))
|
||||||
|
MOVD R4, R5
|
||||||
|
AND $0xe0, R5
|
||||||
|
MOVBU -1(R6), R3
|
||||||
|
ORR R5<<3, R3, R5
|
||||||
|
|
||||||
|
// length = 4 + int(src[s-2])>>2&0x7
|
||||||
|
MOVD $7, R1
|
||||||
|
AND R4>>2, R1, R4
|
||||||
|
ADD $4, R4, R4
|
||||||
|
|
||||||
|
doCopy:
|
||||||
|
// This is the end of the outer "switch", when we have a copy tag.
|
||||||
|
//
|
||||||
|
// We assume that:
|
||||||
|
// - R4 == length && R4 > 0
|
||||||
|
// - R5 == offset
|
||||||
|
|
||||||
|
// if offset <= 0 { etc }
|
||||||
|
MOVD $0, R1
|
||||||
|
CMP R1, R5
|
||||||
|
BLE errCorrupt
|
||||||
|
|
||||||
|
// if d < offset { etc }
|
||||||
|
MOVD R7, R3
|
||||||
|
SUB R8, R3, R3
|
||||||
|
CMP R5, R3
|
||||||
|
BLT errCorrupt
|
||||||
|
|
||||||
|
// if length > len(dst)-d { etc }
|
||||||
|
MOVD R10, R3
|
||||||
|
SUB R7, R3, R3
|
||||||
|
CMP R3, R4
|
||||||
|
BGT errCorrupt
|
||||||
|
|
||||||
|
// forwardCopy(dst[d:d+length], dst[d-offset:]); d += length
|
||||||
|
//
|
||||||
|
// Set:
|
||||||
|
// - R14 = len(dst)-d
|
||||||
|
// - R15 = &dst[d-offset]
|
||||||
|
MOVD R10, R14
|
||||||
|
SUB R7, R14, R14
|
||||||
|
MOVD R7, R15
|
||||||
|
SUB R5, R15, R15
|
||||||
|
|
||||||
|
// !!! Try a faster technique for short (16 or fewer bytes) forward copies.
|
||||||
|
//
|
||||||
|
// First, try using two 8-byte load/stores, similar to the doLit technique
|
||||||
|
// above. Even if dst[d:d+length] and dst[d-offset:] can overlap, this is
|
||||||
|
// still OK if offset >= 8. Note that this has to be two 8-byte load/stores
|
||||||
|
// and not one 16-byte load/store, and the first store has to be before the
|
||||||
|
// second load, due to the overlap if offset is in the range [8, 16).
|
||||||
|
//
|
||||||
|
// if length > 16 || offset < 8 || len(dst)-d < 16 {
|
||||||
|
// goto slowForwardCopy
|
||||||
|
// }
|
||||||
|
// copy 16 bytes
|
||||||
|
// d += length
|
||||||
|
CMP $16, R4
|
||||||
|
BGT slowForwardCopy
|
||||||
|
CMP $8, R5
|
||||||
|
BLT slowForwardCopy
|
||||||
|
CMP $16, R14
|
||||||
|
BLT slowForwardCopy
|
||||||
|
MOVD 0(R15), R2
|
||||||
|
MOVD R2, 0(R7)
|
||||||
|
MOVD 8(R15), R3
|
||||||
|
MOVD R3, 8(R7)
|
||||||
|
ADD R4, R7, R7
|
||||||
|
B loop
|
||||||
|
|
||||||
|
slowForwardCopy:
|
||||||
|
// !!! If the forward copy is longer than 16 bytes, or if offset < 8, we
|
||||||
|
// can still try 8-byte load stores, provided we can overrun up to 10 extra
|
||||||
|
// bytes. As above, the overrun will be fixed up by subsequent iterations
|
||||||
|
// of the outermost loop.
|
||||||
|
//
|
||||||
|
// The C++ snappy code calls this technique IncrementalCopyFastPath. Its
|
||||||
|
// commentary says:
|
||||||
|
//
|
||||||
|
// ----
|
||||||
|
//
|
||||||
|
// The main part of this loop is a simple copy of eight bytes at a time
|
||||||
|
// until we've copied (at least) the requested amount of bytes. However,
|
||||||
|
// if d and d-offset are less than eight bytes apart (indicating a
|
||||||
|
// repeating pattern of length < 8), we first need to expand the pattern in
|
||||||
|
// order to get the correct results. For instance, if the buffer looks like
|
||||||
|
// this, with the eight-byte <d-offset> and <d> patterns marked as
|
||||||
|
// intervals:
|
||||||
|
//
|
||||||
|
// abxxxxxxxxxxxx
|
||||||
|
// [------] d-offset
|
||||||
|
// [------] d
|
||||||
|
//
|
||||||
|
// a single eight-byte copy from <d-offset> to <d> will repeat the pattern
|
||||||
|
// once, after which we can move <d> two bytes without moving <d-offset>:
|
||||||
|
//
|
||||||
|
// ababxxxxxxxxxx
|
||||||
|
// [------] d-offset
|
||||||
|
// [------] d
|
||||||
|
//
|
||||||
|
// and repeat the exercise until the two no longer overlap.
|
||||||
|
//
|
||||||
|
// This allows us to do very well in the special case of one single byte
|
||||||
|
// repeated many times, without taking a big hit for more general cases.
|
||||||
|
//
|
||||||
|
// The worst case of extra writing past the end of the match occurs when
|
||||||
|
// offset == 1 and length == 1; the last copy will read from byte positions
|
||||||
|
// [0..7] and write to [4..11], whereas it was only supposed to write to
|
||||||
|
// position 1. Thus, ten excess bytes.
|
||||||
|
//
|
||||||
|
// ----
|
||||||
|
//
|
||||||
|
// That "10 byte overrun" worst case is confirmed by Go's
|
||||||
|
// TestSlowForwardCopyOverrun, which also tests the fixUpSlowForwardCopy
|
||||||
|
// and finishSlowForwardCopy algorithm.
|
||||||
|
//
|
||||||
|
// if length > len(dst)-d-10 {
|
||||||
|
// goto verySlowForwardCopy
|
||||||
|
// }
|
||||||
|
SUB $10, R14, R14
|
||||||
|
CMP R14, R4
|
||||||
|
BGT verySlowForwardCopy
|
||||||
|
|
||||||
|
makeOffsetAtLeast8:
|
||||||
|
// !!! As above, expand the pattern so that offset >= 8 and we can use
|
||||||
|
// 8-byte load/stores.
|
||||||
|
//
|
||||||
|
// for offset < 8 {
|
||||||
|
// copy 8 bytes from dst[d-offset:] to dst[d:]
|
||||||
|
// length -= offset
|
||||||
|
// d += offset
|
||||||
|
// offset += offset
|
||||||
|
// // The two previous lines together means that d-offset, and therefore
|
||||||
|
// // R15, is unchanged.
|
||||||
|
// }
|
||||||
|
CMP $8, R5
|
||||||
|
BGE fixUpSlowForwardCopy
|
||||||
|
MOVD (R15), R3
|
||||||
|
MOVD R3, (R7)
|
||||||
|
SUB R5, R4, R4
|
||||||
|
ADD R5, R7, R7
|
||||||
|
ADD R5, R5, R5
|
||||||
|
B makeOffsetAtLeast8
|
||||||
|
|
||||||
|
fixUpSlowForwardCopy:
|
||||||
|
// !!! Add length (which might be negative now) to d (implied by R7 being
|
||||||
|
// &dst[d]) so that d ends up at the right place when we jump back to the
|
||||||
|
// top of the loop. Before we do that, though, we save R7 to R2 so that, if
|
||||||
|
// length is positive, copying the remaining length bytes will write to the
|
||||||
|
// right place.
|
||||||
|
MOVD R7, R2
|
||||||
|
ADD R4, R7, R7
|
||||||
|
|
||||||
|
finishSlowForwardCopy:
|
||||||
|
// !!! Repeat 8-byte load/stores until length <= 0. Ending with a negative
|
||||||
|
// length means that we overrun, but as above, that will be fixed up by
|
||||||
|
// subsequent iterations of the outermost loop.
|
||||||
|
MOVD $0, R1
|
||||||
|
CMP R1, R4
|
||||||
|
BLE loop
|
||||||
|
MOVD (R15), R3
|
||||||
|
MOVD R3, (R2)
|
||||||
|
ADD $8, R15, R15
|
||||||
|
ADD $8, R2, R2
|
||||||
|
SUB $8, R4, R4
|
||||||
|
B finishSlowForwardCopy
|
||||||
|
|
||||||
|
verySlowForwardCopy:
|
||||||
|
// verySlowForwardCopy is a simple implementation of forward copy. In C
|
||||||
|
// parlance, this is a do/while loop instead of a while loop, since we know
|
||||||
|
// that length > 0. In Go syntax:
|
||||||
|
//
|
||||||
|
// for {
|
||||||
|
// dst[d] = dst[d - offset]
|
||||||
|
// d++
|
||||||
|
// length--
|
||||||
|
// if length == 0 {
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
MOVB (R15), R3
|
||||||
|
MOVB R3, (R7)
|
||||||
|
ADD $1, R15, R15
|
||||||
|
ADD $1, R7, R7
|
||||||
|
SUB $1, R4, R4
|
||||||
|
CBNZ R4, verySlowForwardCopy
|
||||||
|
B loop
|
||||||
|
|
||||||
|
// The code above handles copy tags.
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
end:
|
||||||
|
// This is the end of the "for s < len(src)".
|
||||||
|
//
|
||||||
|
// if d != len(dst) { etc }
|
||||||
|
CMP R10, R7
|
||||||
|
BNE errCorrupt
|
||||||
|
|
||||||
|
// return 0
|
||||||
|
MOVD $0, ret+48(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
errCorrupt:
|
||||||
|
// return decodeErrCodeCorrupt
|
||||||
|
MOVD $1, R2
|
||||||
|
MOVD R2, ret+48(FP)
|
||||||
|
RET
|
|
@ -5,6 +5,7 @@
|
||||||
// +build !appengine
|
// +build !appengine
|
||||||
// +build gc
|
// +build gc
|
||||||
// +build !noasm
|
// +build !noasm
|
||||||
|
// +build amd64 arm64
|
||||||
|
|
||||||
package snappy
|
package snappy
|
||||||
|
|
|
@ -2,7 +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.
|
||||||
|
|
||||||
// +build !amd64 appengine !gc noasm
|
// +build !amd64,!arm64 appengine !gc noasm
|
||||||
|
|
||||||
package snappy
|
package snappy
|
||||||
|
|
||||||
|
@ -85,14 +85,28 @@ func decode(dst, src []byte) int {
|
||||||
if offset <= 0 || d < offset || length > len(dst)-d {
|
if offset <= 0 || d < offset || length > len(dst)-d {
|
||||||
return decodeErrCodeCorrupt
|
return decodeErrCodeCorrupt
|
||||||
}
|
}
|
||||||
// Copy from an earlier sub-slice of dst to a later sub-slice. Unlike
|
// Copy from an earlier sub-slice of dst to a later sub-slice.
|
||||||
// the built-in copy function, this byte-by-byte copy always runs
|
// If no overlap, use the built-in copy:
|
||||||
|
if offset >= length {
|
||||||
|
copy(dst[d:d+length], dst[d-offset:])
|
||||||
|
d += length
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlike the built-in copy function, this byte-by-byte copy always runs
|
||||||
// forwards, even if the slices overlap. Conceptually, this is:
|
// forwards, even if the slices overlap. Conceptually, this is:
|
||||||
//
|
//
|
||||||
// d += forwardCopy(dst[d:d+length], dst[d-offset:])
|
// d += forwardCopy(dst[d:d+length], dst[d-offset:])
|
||||||
for end := d + length; d != end; d++ {
|
//
|
||||||
dst[d] = dst[d-offset]
|
// We align the slices into a and b and show the compiler they are the same size.
|
||||||
|
// This allows the loop to run without bounds checks.
|
||||||
|
a := dst[d : d+length]
|
||||||
|
b := dst[d-offset:]
|
||||||
|
b = b[:len(a)]
|
||||||
|
for i := range a {
|
||||||
|
a[i] = b[i]
|
||||||
}
|
}
|
||||||
|
d += length
|
||||||
}
|
}
|
||||||
if d != len(dst) {
|
if d != len(dst) {
|
||||||
return decodeErrCodeCorrupt
|
return decodeErrCodeCorrupt
|
||||||
|
|
|
@ -15,6 +15,8 @@ import (
|
||||||
// Otherwise, a newly allocated slice will be returned.
|
// Otherwise, a newly allocated slice will be returned.
|
||||||
//
|
//
|
||||||
// The dst and src must not overlap. It is valid to pass a nil dst.
|
// The dst and src must not overlap. It is valid to pass a nil dst.
|
||||||
|
//
|
||||||
|
// Encode handles the Snappy block format, not the Snappy stream format.
|
||||||
func Encode(dst, src []byte) []byte {
|
func Encode(dst, src []byte) []byte {
|
||||||
if n := MaxEncodedLen(len(src)); n < 0 {
|
if n := MaxEncodedLen(len(src)); n < 0 {
|
||||||
panic(ErrTooLarge)
|
panic(ErrTooLarge)
|
||||||
|
@ -139,6 +141,8 @@ func NewBufferedWriter(w io.Writer) *Writer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Writer is an io.Writer that can write Snappy-compressed bytes.
|
// Writer is an io.Writer that can write Snappy-compressed bytes.
|
||||||
|
//
|
||||||
|
// Writer handles the Snappy stream format, not the Snappy block format.
|
||||||
type Writer struct {
|
type Writer struct {
|
||||||
w io.Writer
|
w io.Writer
|
||||||
err error
|
err error
|
||||||
|
|
|
@ -0,0 +1,722 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// +build !appengine
|
||||||
|
// +build gc
|
||||||
|
// +build !noasm
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// The asm code generally follows the pure Go code in encode_other.go, except
|
||||||
|
// where marked with a "!!!".
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// func emitLiteral(dst, lit []byte) int
|
||||||
|
//
|
||||||
|
// All local variables fit into registers. The register allocation:
|
||||||
|
// - R3 len(lit)
|
||||||
|
// - R4 n
|
||||||
|
// - R6 return value
|
||||||
|
// - R8 &dst[i]
|
||||||
|
// - R10 &lit[0]
|
||||||
|
//
|
||||||
|
// The 32 bytes of stack space is to call runtime·memmove.
|
||||||
|
//
|
||||||
|
// The unusual register allocation of local variables, such as R10 for the
|
||||||
|
// source pointer, matches the allocation used at the call site in encodeBlock,
|
||||||
|
// which makes it easier to manually inline this function.
|
||||||
|
TEXT ·emitLiteral(SB), NOSPLIT, $32-56
|
||||||
|
MOVD dst_base+0(FP), R8
|
||||||
|
MOVD lit_base+24(FP), R10
|
||||||
|
MOVD lit_len+32(FP), R3
|
||||||
|
MOVD R3, R6
|
||||||
|
MOVW R3, R4
|
||||||
|
SUBW $1, R4, R4
|
||||||
|
|
||||||
|
CMPW $60, R4
|
||||||
|
BLT oneByte
|
||||||
|
CMPW $256, R4
|
||||||
|
BLT twoBytes
|
||||||
|
|
||||||
|
threeBytes:
|
||||||
|
MOVD $0xf4, R2
|
||||||
|
MOVB R2, 0(R8)
|
||||||
|
MOVW R4, 1(R8)
|
||||||
|
ADD $3, R8, R8
|
||||||
|
ADD $3, R6, R6
|
||||||
|
B memmove
|
||||||
|
|
||||||
|
twoBytes:
|
||||||
|
MOVD $0xf0, R2
|
||||||
|
MOVB R2, 0(R8)
|
||||||
|
MOVB R4, 1(R8)
|
||||||
|
ADD $2, R8, R8
|
||||||
|
ADD $2, R6, R6
|
||||||
|
B memmove
|
||||||
|
|
||||||
|
oneByte:
|
||||||
|
LSLW $2, R4, R4
|
||||||
|
MOVB R4, 0(R8)
|
||||||
|
ADD $1, R8, R8
|
||||||
|
ADD $1, R6, R6
|
||||||
|
|
||||||
|
memmove:
|
||||||
|
MOVD R6, ret+48(FP)
|
||||||
|
|
||||||
|
// copy(dst[i:], lit)
|
||||||
|
//
|
||||||
|
// This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push
|
||||||
|
// R8, R10 and R3 as arguments.
|
||||||
|
MOVD R8, 8(RSP)
|
||||||
|
MOVD R10, 16(RSP)
|
||||||
|
MOVD R3, 24(RSP)
|
||||||
|
CALL runtime·memmove(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// func emitCopy(dst []byte, offset, length int) int
|
||||||
|
//
|
||||||
|
// All local variables fit into registers. The register allocation:
|
||||||
|
// - R3 length
|
||||||
|
// - R7 &dst[0]
|
||||||
|
// - R8 &dst[i]
|
||||||
|
// - R11 offset
|
||||||
|
//
|
||||||
|
// The unusual register allocation of local variables, such as R11 for the
|
||||||
|
// offset, matches the allocation used at the call site in encodeBlock, which
|
||||||
|
// makes it easier to manually inline this function.
|
||||||
|
TEXT ·emitCopy(SB), NOSPLIT, $0-48
|
||||||
|
MOVD dst_base+0(FP), R8
|
||||||
|
MOVD R8, R7
|
||||||
|
MOVD offset+24(FP), R11
|
||||||
|
MOVD length+32(FP), R3
|
||||||
|
|
||||||
|
loop0:
|
||||||
|
// for length >= 68 { etc }
|
||||||
|
CMPW $68, R3
|
||||||
|
BLT step1
|
||||||
|
|
||||||
|
// Emit a length 64 copy, encoded as 3 bytes.
|
||||||
|
MOVD $0xfe, R2
|
||||||
|
MOVB R2, 0(R8)
|
||||||
|
MOVW R11, 1(R8)
|
||||||
|
ADD $3, R8, R8
|
||||||
|
SUB $64, R3, R3
|
||||||
|
B loop0
|
||||||
|
|
||||||
|
step1:
|
||||||
|
// if length > 64 { etc }
|
||||||
|
CMP $64, R3
|
||||||
|
BLE step2
|
||||||
|
|
||||||
|
// Emit a length 60 copy, encoded as 3 bytes.
|
||||||
|
MOVD $0xee, R2
|
||||||
|
MOVB R2, 0(R8)
|
||||||
|
MOVW R11, 1(R8)
|
||||||
|
ADD $3, R8, R8
|
||||||
|
SUB $60, R3, R3
|
||||||
|
|
||||||
|
step2:
|
||||||
|
// if length >= 12 || offset >= 2048 { goto step3 }
|
||||||
|
CMP $12, R3
|
||||||
|
BGE step3
|
||||||
|
CMPW $2048, R11
|
||||||
|
BGE step3
|
||||||
|
|
||||||
|
// Emit the remaining copy, encoded as 2 bytes.
|
||||||
|
MOVB R11, 1(R8)
|
||||||
|
LSRW $3, R11, R11
|
||||||
|
AND $0xe0, R11, R11
|
||||||
|
SUB $4, R3, R3
|
||||||
|
LSLW $2, R3
|
||||||
|
AND $0xff, R3, R3
|
||||||
|
ORRW R3, R11, R11
|
||||||
|
ORRW $1, R11, R11
|
||||||
|
MOVB R11, 0(R8)
|
||||||
|
ADD $2, R8, R8
|
||||||
|
|
||||||
|
// Return the number of bytes written.
|
||||||
|
SUB R7, R8, R8
|
||||||
|
MOVD R8, ret+40(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
step3:
|
||||||
|
// Emit the remaining copy, encoded as 3 bytes.
|
||||||
|
SUB $1, R3, R3
|
||||||
|
AND $0xff, R3, R3
|
||||||
|
LSLW $2, R3, R3
|
||||||
|
ORRW $2, R3, R3
|
||||||
|
MOVB R3, 0(R8)
|
||||||
|
MOVW R11, 1(R8)
|
||||||
|
ADD $3, R8, R8
|
||||||
|
|
||||||
|
// Return the number of bytes written.
|
||||||
|
SUB R7, R8, R8
|
||||||
|
MOVD R8, ret+40(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// func extendMatch(src []byte, i, j int) int
|
||||||
|
//
|
||||||
|
// All local variables fit into registers. The register allocation:
|
||||||
|
// - R6 &src[0]
|
||||||
|
// - R7 &src[j]
|
||||||
|
// - R13 &src[len(src) - 8]
|
||||||
|
// - R14 &src[len(src)]
|
||||||
|
// - R15 &src[i]
|
||||||
|
//
|
||||||
|
// The unusual register allocation of local variables, such as R15 for a source
|
||||||
|
// pointer, matches the allocation used at the call site in encodeBlock, which
|
||||||
|
// makes it easier to manually inline this function.
|
||||||
|
TEXT ·extendMatch(SB), NOSPLIT, $0-48
|
||||||
|
MOVD src_base+0(FP), R6
|
||||||
|
MOVD src_len+8(FP), R14
|
||||||
|
MOVD i+24(FP), R15
|
||||||
|
MOVD j+32(FP), R7
|
||||||
|
ADD R6, R14, R14
|
||||||
|
ADD R6, R15, R15
|
||||||
|
ADD R6, R7, R7
|
||||||
|
MOVD R14, R13
|
||||||
|
SUB $8, R13, R13
|
||||||
|
|
||||||
|
cmp8:
|
||||||
|
// As long as we are 8 or more bytes before the end of src, we can load and
|
||||||
|
// compare 8 bytes at a time. If those 8 bytes are equal, repeat.
|
||||||
|
CMP R13, R7
|
||||||
|
BHI cmp1
|
||||||
|
MOVD (R15), R3
|
||||||
|
MOVD (R7), R4
|
||||||
|
CMP R4, R3
|
||||||
|
BNE bsf
|
||||||
|
ADD $8, R15, R15
|
||||||
|
ADD $8, R7, R7
|
||||||
|
B cmp8
|
||||||
|
|
||||||
|
bsf:
|
||||||
|
// If those 8 bytes were not equal, XOR the two 8 byte values, and return
|
||||||
|
// the index of the first byte that differs.
|
||||||
|
// RBIT reverses the bit order, then CLZ counts the leading zeros, the
|
||||||
|
// combination of which finds the least significant bit which is set.
|
||||||
|
// The arm64 architecture is little-endian, and the shift by 3 converts
|
||||||
|
// a bit index to a byte index.
|
||||||
|
EOR R3, R4, R4
|
||||||
|
RBIT R4, R4
|
||||||
|
CLZ R4, R4
|
||||||
|
ADD R4>>3, R7, R7
|
||||||
|
|
||||||
|
// Convert from &src[ret] to ret.
|
||||||
|
SUB R6, R7, R7
|
||||||
|
MOVD R7, ret+40(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
cmp1:
|
||||||
|
// In src's tail, compare 1 byte at a time.
|
||||||
|
CMP R7, R14
|
||||||
|
BLS extendMatchEnd
|
||||||
|
MOVB (R15), R3
|
||||||
|
MOVB (R7), R4
|
||||||
|
CMP R4, R3
|
||||||
|
BNE extendMatchEnd
|
||||||
|
ADD $1, R15, R15
|
||||||
|
ADD $1, R7, R7
|
||||||
|
B cmp1
|
||||||
|
|
||||||
|
extendMatchEnd:
|
||||||
|
// Convert from &src[ret] to ret.
|
||||||
|
SUB R6, R7, R7
|
||||||
|
MOVD R7, ret+40(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// func encodeBlock(dst, src []byte) (d int)
|
||||||
|
//
|
||||||
|
// All local variables fit into registers, other than "var table". The register
|
||||||
|
// allocation:
|
||||||
|
// - R3 . .
|
||||||
|
// - R4 . .
|
||||||
|
// - R5 64 shift
|
||||||
|
// - R6 72 &src[0], tableSize
|
||||||
|
// - R7 80 &src[s]
|
||||||
|
// - R8 88 &dst[d]
|
||||||
|
// - R9 96 sLimit
|
||||||
|
// - R10 . &src[nextEmit]
|
||||||
|
// - R11 104 prevHash, currHash, nextHash, offset
|
||||||
|
// - R12 112 &src[base], skip
|
||||||
|
// - R13 . &src[nextS], &src[len(src) - 8]
|
||||||
|
// - R14 . len(src), bytesBetweenHashLookups, &src[len(src)], x
|
||||||
|
// - R15 120 candidate
|
||||||
|
// - R16 . hash constant, 0x1e35a7bd
|
||||||
|
// - R17 . &table
|
||||||
|
// - . 128 table
|
||||||
|
//
|
||||||
|
// The second column (64, 72, etc) is the stack offset to spill the registers
|
||||||
|
// when calling other functions. We could pack this slightly tighter, but it's
|
||||||
|
// simpler to have a dedicated spill map independent of the function called.
|
||||||
|
//
|
||||||
|
// "var table [maxTableSize]uint16" takes up 32768 bytes of stack space. An
|
||||||
|
// extra 64 bytes, to call other functions, and an extra 64 bytes, to spill
|
||||||
|
// local variables (registers) during calls gives 32768 + 64 + 64 = 32896.
|
||||||
|
TEXT ·encodeBlock(SB), 0, $32896-56
|
||||||
|
MOVD dst_base+0(FP), R8
|
||||||
|
MOVD src_base+24(FP), R7
|
||||||
|
MOVD src_len+32(FP), R14
|
||||||
|
|
||||||
|
// shift, tableSize := uint32(32-8), 1<<8
|
||||||
|
MOVD $24, R5
|
||||||
|
MOVD $256, R6
|
||||||
|
MOVW $0xa7bd, R16
|
||||||
|
MOVKW $(0x1e35<<16), R16
|
||||||
|
|
||||||
|
calcShift:
|
||||||
|
// for ; tableSize < maxTableSize && tableSize < len(src); tableSize *= 2 {
|
||||||
|
// shift--
|
||||||
|
// }
|
||||||
|
MOVD $16384, R2
|
||||||
|
CMP R2, R6
|
||||||
|
BGE varTable
|
||||||
|
CMP R14, R6
|
||||||
|
BGE varTable
|
||||||
|
SUB $1, R5, R5
|
||||||
|
LSL $1, R6, R6
|
||||||
|
B calcShift
|
||||||
|
|
||||||
|
varTable:
|
||||||
|
// var table [maxTableSize]uint16
|
||||||
|
//
|
||||||
|
// In the asm code, unlike the Go code, we can zero-initialize only the
|
||||||
|
// first tableSize elements. Each uint16 element is 2 bytes and each
|
||||||
|
// iterations writes 64 bytes, so we can do only tableSize/32 writes
|
||||||
|
// instead of the 2048 writes that would zero-initialize all of table's
|
||||||
|
// 32768 bytes. This clear could overrun the first tableSize elements, but
|
||||||
|
// it won't overrun the allocated stack size.
|
||||||
|
ADD $128, RSP, R17
|
||||||
|
MOVD R17, R4
|
||||||
|
|
||||||
|
// !!! R6 = &src[tableSize]
|
||||||
|
ADD R6<<1, R17, R6
|
||||||
|
|
||||||
|
memclr:
|
||||||
|
STP.P (ZR, ZR), 64(R4)
|
||||||
|
STP (ZR, ZR), -48(R4)
|
||||||
|
STP (ZR, ZR), -32(R4)
|
||||||
|
STP (ZR, ZR), -16(R4)
|
||||||
|
CMP R4, R6
|
||||||
|
BHI memclr
|
||||||
|
|
||||||
|
// !!! R6 = &src[0]
|
||||||
|
MOVD R7, R6
|
||||||
|
|
||||||
|
// sLimit := len(src) - inputMargin
|
||||||
|
MOVD R14, R9
|
||||||
|
SUB $15, R9, R9
|
||||||
|
|
||||||
|
// !!! Pre-emptively spill R5, R6 and R9 to the stack. Their values don't
|
||||||
|
// change for the rest of the function.
|
||||||
|
MOVD R5, 64(RSP)
|
||||||
|
MOVD R6, 72(RSP)
|
||||||
|
MOVD R9, 96(RSP)
|
||||||
|
|
||||||
|
// nextEmit := 0
|
||||||
|
MOVD R6, R10
|
||||||
|
|
||||||
|
// s := 1
|
||||||
|
ADD $1, R7, R7
|
||||||
|
|
||||||
|
// nextHash := hash(load32(src, s), shift)
|
||||||
|
MOVW 0(R7), R11
|
||||||
|
MULW R16, R11, R11
|
||||||
|
LSRW R5, R11, R11
|
||||||
|
|
||||||
|
outer:
|
||||||
|
// for { etc }
|
||||||
|
|
||||||
|
// skip := 32
|
||||||
|
MOVD $32, R12
|
||||||
|
|
||||||
|
// nextS := s
|
||||||
|
MOVD R7, R13
|
||||||
|
|
||||||
|
// candidate := 0
|
||||||
|
MOVD $0, R15
|
||||||
|
|
||||||
|
inner0:
|
||||||
|
// for { etc }
|
||||||
|
|
||||||
|
// s := nextS
|
||||||
|
MOVD R13, R7
|
||||||
|
|
||||||
|
// bytesBetweenHashLookups := skip >> 5
|
||||||
|
MOVD R12, R14
|
||||||
|
LSR $5, R14, R14
|
||||||
|
|
||||||
|
// nextS = s + bytesBetweenHashLookups
|
||||||
|
ADD R14, R13, R13
|
||||||
|
|
||||||
|
// skip += bytesBetweenHashLookups
|
||||||
|
ADD R14, R12, R12
|
||||||
|
|
||||||
|
// if nextS > sLimit { goto emitRemainder }
|
||||||
|
MOVD R13, R3
|
||||||
|
SUB R6, R3, R3
|
||||||
|
CMP R9, R3
|
||||||
|
BHI emitRemainder
|
||||||
|
|
||||||
|
// candidate = int(table[nextHash])
|
||||||
|
MOVHU 0(R17)(R11<<1), R15
|
||||||
|
|
||||||
|
// table[nextHash] = uint16(s)
|
||||||
|
MOVD R7, R3
|
||||||
|
SUB R6, R3, R3
|
||||||
|
|
||||||
|
MOVH R3, 0(R17)(R11<<1)
|
||||||
|
|
||||||
|
// nextHash = hash(load32(src, nextS), shift)
|
||||||
|
MOVW 0(R13), R11
|
||||||
|
MULW R16, R11
|
||||||
|
LSRW R5, R11, R11
|
||||||
|
|
||||||
|
// if load32(src, s) != load32(src, candidate) { continue } break
|
||||||
|
MOVW 0(R7), R3
|
||||||
|
MOVW (R6)(R15), R4
|
||||||
|
CMPW R4, R3
|
||||||
|
BNE inner0
|
||||||
|
|
||||||
|
fourByteMatch:
|
||||||
|
// As per the encode_other.go code:
|
||||||
|
//
|
||||||
|
// A 4-byte match has been found. We'll later see etc.
|
||||||
|
|
||||||
|
// !!! Jump to a fast path for short (<= 16 byte) literals. See the comment
|
||||||
|
// on inputMargin in encode.go.
|
||||||
|
MOVD R7, R3
|
||||||
|
SUB R10, R3, R3
|
||||||
|
CMP $16, R3
|
||||||
|
BLE emitLiteralFastPath
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// Begin inline of the emitLiteral call.
|
||||||
|
//
|
||||||
|
// d += emitLiteral(dst[d:], src[nextEmit:s])
|
||||||
|
|
||||||
|
MOVW R3, R4
|
||||||
|
SUBW $1, R4, R4
|
||||||
|
|
||||||
|
MOVW $60, R2
|
||||||
|
CMPW R2, R4
|
||||||
|
BLT inlineEmitLiteralOneByte
|
||||||
|
MOVW $256, R2
|
||||||
|
CMPW R2, R4
|
||||||
|
BLT inlineEmitLiteralTwoBytes
|
||||||
|
|
||||||
|
inlineEmitLiteralThreeBytes:
|
||||||
|
MOVD $0xf4, R1
|
||||||
|
MOVB R1, 0(R8)
|
||||||
|
MOVW R4, 1(R8)
|
||||||
|
ADD $3, R8, R8
|
||||||
|
B inlineEmitLiteralMemmove
|
||||||
|
|
||||||
|
inlineEmitLiteralTwoBytes:
|
||||||
|
MOVD $0xf0, R1
|
||||||
|
MOVB R1, 0(R8)
|
||||||
|
MOVB R4, 1(R8)
|
||||||
|
ADD $2, R8, R8
|
||||||
|
B inlineEmitLiteralMemmove
|
||||||
|
|
||||||
|
inlineEmitLiteralOneByte:
|
||||||
|
LSLW $2, R4, R4
|
||||||
|
MOVB R4, 0(R8)
|
||||||
|
ADD $1, R8, R8
|
||||||
|
|
||||||
|
inlineEmitLiteralMemmove:
|
||||||
|
// Spill local variables (registers) onto the stack; call; unspill.
|
||||||
|
//
|
||||||
|
// copy(dst[i:], lit)
|
||||||
|
//
|
||||||
|
// This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push
|
||||||
|
// R8, R10 and R3 as arguments.
|
||||||
|
MOVD R8, 8(RSP)
|
||||||
|
MOVD R10, 16(RSP)
|
||||||
|
MOVD R3, 24(RSP)
|
||||||
|
|
||||||
|
// Finish the "d +=" part of "d += emitLiteral(etc)".
|
||||||
|
ADD R3, R8, R8
|
||||||
|
MOVD R7, 80(RSP)
|
||||||
|
MOVD R8, 88(RSP)
|
||||||
|
MOVD R15, 120(RSP)
|
||||||
|
CALL runtime·memmove(SB)
|
||||||
|
MOVD 64(RSP), R5
|
||||||
|
MOVD 72(RSP), R6
|
||||||
|
MOVD 80(RSP), R7
|
||||||
|
MOVD 88(RSP), R8
|
||||||
|
MOVD 96(RSP), R9
|
||||||
|
MOVD 120(RSP), R15
|
||||||
|
ADD $128, RSP, R17
|
||||||
|
MOVW $0xa7bd, R16
|
||||||
|
MOVKW $(0x1e35<<16), R16
|
||||||
|
B inner1
|
||||||
|
|
||||||
|
inlineEmitLiteralEnd:
|
||||||
|
// End inline of the emitLiteral call.
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
emitLiteralFastPath:
|
||||||
|
// !!! Emit the 1-byte encoding "uint8(len(lit)-1)<<2".
|
||||||
|
MOVB R3, R4
|
||||||
|
SUBW $1, R4, R4
|
||||||
|
AND $0xff, R4, R4
|
||||||
|
LSLW $2, R4, R4
|
||||||
|
MOVB R4, (R8)
|
||||||
|
ADD $1, R8, R8
|
||||||
|
|
||||||
|
// !!! Implement the copy from lit to dst as a 16-byte load and store.
|
||||||
|
// (Encode's documentation says that dst and src must not overlap.)
|
||||||
|
//
|
||||||
|
// This always copies 16 bytes, instead of only len(lit) bytes, but that's
|
||||||
|
// OK. Subsequent iterations will fix up the overrun.
|
||||||
|
//
|
||||||
|
// Note that on arm64, it is legal and cheap to issue unaligned 8-byte or
|
||||||
|
// 16-byte loads and stores. This technique probably wouldn't be as
|
||||||
|
// effective on architectures that are fussier about alignment.
|
||||||
|
LDP 0(R10), (R0, R1)
|
||||||
|
STP (R0, R1), 0(R8)
|
||||||
|
ADD R3, R8, R8
|
||||||
|
|
||||||
|
inner1:
|
||||||
|
// for { etc }
|
||||||
|
|
||||||
|
// base := s
|
||||||
|
MOVD R7, R12
|
||||||
|
|
||||||
|
// !!! offset := base - candidate
|
||||||
|
MOVD R12, R11
|
||||||
|
SUB R15, R11, R11
|
||||||
|
SUB R6, R11, R11
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// Begin inline of the extendMatch call.
|
||||||
|
//
|
||||||
|
// s = extendMatch(src, candidate+4, s+4)
|
||||||
|
|
||||||
|
// !!! R14 = &src[len(src)]
|
||||||
|
MOVD src_len+32(FP), R14
|
||||||
|
ADD R6, R14, R14
|
||||||
|
|
||||||
|
// !!! R13 = &src[len(src) - 8]
|
||||||
|
MOVD R14, R13
|
||||||
|
SUB $8, R13, R13
|
||||||
|
|
||||||
|
// !!! R15 = &src[candidate + 4]
|
||||||
|
ADD $4, R15, R15
|
||||||
|
ADD R6, R15, R15
|
||||||
|
|
||||||
|
// !!! s += 4
|
||||||
|
ADD $4, R7, R7
|
||||||
|
|
||||||
|
inlineExtendMatchCmp8:
|
||||||
|
// As long as we are 8 or more bytes before the end of src, we can load and
|
||||||
|
// compare 8 bytes at a time. If those 8 bytes are equal, repeat.
|
||||||
|
CMP R13, R7
|
||||||
|
BHI inlineExtendMatchCmp1
|
||||||
|
MOVD (R15), R3
|
||||||
|
MOVD (R7), R4
|
||||||
|
CMP R4, R3
|
||||||
|
BNE inlineExtendMatchBSF
|
||||||
|
ADD $8, R15, R15
|
||||||
|
ADD $8, R7, R7
|
||||||
|
B inlineExtendMatchCmp8
|
||||||
|
|
||||||
|
inlineExtendMatchBSF:
|
||||||
|
// If those 8 bytes were not equal, XOR the two 8 byte values, and return
|
||||||
|
// the index of the first byte that differs.
|
||||||
|
// RBIT reverses the bit order, then CLZ counts the leading zeros, the
|
||||||
|
// combination of which finds the least significant bit which is set.
|
||||||
|
// The arm64 architecture is little-endian, and the shift by 3 converts
|
||||||
|
// a bit index to a byte index.
|
||||||
|
EOR R3, R4, R4
|
||||||
|
RBIT R4, R4
|
||||||
|
CLZ R4, R4
|
||||||
|
ADD R4>>3, R7, R7
|
||||||
|
B inlineExtendMatchEnd
|
||||||
|
|
||||||
|
inlineExtendMatchCmp1:
|
||||||
|
// In src's tail, compare 1 byte at a time.
|
||||||
|
CMP R7, R14
|
||||||
|
BLS inlineExtendMatchEnd
|
||||||
|
MOVB (R15), R3
|
||||||
|
MOVB (R7), R4
|
||||||
|
CMP R4, R3
|
||||||
|
BNE inlineExtendMatchEnd
|
||||||
|
ADD $1, R15, R15
|
||||||
|
ADD $1, R7, R7
|
||||||
|
B inlineExtendMatchCmp1
|
||||||
|
|
||||||
|
inlineExtendMatchEnd:
|
||||||
|
// End inline of the extendMatch call.
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// Begin inline of the emitCopy call.
|
||||||
|
//
|
||||||
|
// d += emitCopy(dst[d:], base-candidate, s-base)
|
||||||
|
|
||||||
|
// !!! length := s - base
|
||||||
|
MOVD R7, R3
|
||||||
|
SUB R12, R3, R3
|
||||||
|
|
||||||
|
inlineEmitCopyLoop0:
|
||||||
|
// for length >= 68 { etc }
|
||||||
|
MOVW $68, R2
|
||||||
|
CMPW R2, R3
|
||||||
|
BLT inlineEmitCopyStep1
|
||||||
|
|
||||||
|
// Emit a length 64 copy, encoded as 3 bytes.
|
||||||
|
MOVD $0xfe, R1
|
||||||
|
MOVB R1, 0(R8)
|
||||||
|
MOVW R11, 1(R8)
|
||||||
|
ADD $3, R8, R8
|
||||||
|
SUBW $64, R3, R3
|
||||||
|
B inlineEmitCopyLoop0
|
||||||
|
|
||||||
|
inlineEmitCopyStep1:
|
||||||
|
// if length > 64 { etc }
|
||||||
|
MOVW $64, R2
|
||||||
|
CMPW R2, R3
|
||||||
|
BLE inlineEmitCopyStep2
|
||||||
|
|
||||||
|
// Emit a length 60 copy, encoded as 3 bytes.
|
||||||
|
MOVD $0xee, R1
|
||||||
|
MOVB R1, 0(R8)
|
||||||
|
MOVW R11, 1(R8)
|
||||||
|
ADD $3, R8, R8
|
||||||
|
SUBW $60, R3, R3
|
||||||
|
|
||||||
|
inlineEmitCopyStep2:
|
||||||
|
// if length >= 12 || offset >= 2048 { goto inlineEmitCopyStep3 }
|
||||||
|
MOVW $12, R2
|
||||||
|
CMPW R2, R3
|
||||||
|
BGE inlineEmitCopyStep3
|
||||||
|
MOVW $2048, R2
|
||||||
|
CMPW R2, R11
|
||||||
|
BGE inlineEmitCopyStep3
|
||||||
|
|
||||||
|
// Emit the remaining copy, encoded as 2 bytes.
|
||||||
|
MOVB R11, 1(R8)
|
||||||
|
LSRW $8, R11, R11
|
||||||
|
LSLW $5, R11, R11
|
||||||
|
SUBW $4, R3, R3
|
||||||
|
AND $0xff, R3, R3
|
||||||
|
LSLW $2, R3, R3
|
||||||
|
ORRW R3, R11, R11
|
||||||
|
ORRW $1, R11, R11
|
||||||
|
MOVB R11, 0(R8)
|
||||||
|
ADD $2, R8, R8
|
||||||
|
B inlineEmitCopyEnd
|
||||||
|
|
||||||
|
inlineEmitCopyStep3:
|
||||||
|
// Emit the remaining copy, encoded as 3 bytes.
|
||||||
|
SUBW $1, R3, R3
|
||||||
|
LSLW $2, R3, R3
|
||||||
|
ORRW $2, R3, R3
|
||||||
|
MOVB R3, 0(R8)
|
||||||
|
MOVW R11, 1(R8)
|
||||||
|
ADD $3, R8, R8
|
||||||
|
|
||||||
|
inlineEmitCopyEnd:
|
||||||
|
// End inline of the emitCopy call.
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
// nextEmit = s
|
||||||
|
MOVD R7, R10
|
||||||
|
|
||||||
|
// if s >= sLimit { goto emitRemainder }
|
||||||
|
MOVD R7, R3
|
||||||
|
SUB R6, R3, R3
|
||||||
|
CMP R3, R9
|
||||||
|
BLS emitRemainder
|
||||||
|
|
||||||
|
// As per the encode_other.go code:
|
||||||
|
//
|
||||||
|
// We could immediately etc.
|
||||||
|
|
||||||
|
// x := load64(src, s-1)
|
||||||
|
MOVD -1(R7), R14
|
||||||
|
|
||||||
|
// prevHash := hash(uint32(x>>0), shift)
|
||||||
|
MOVW R14, R11
|
||||||
|
MULW R16, R11, R11
|
||||||
|
LSRW R5, R11, R11
|
||||||
|
|
||||||
|
// table[prevHash] = uint16(s-1)
|
||||||
|
MOVD R7, R3
|
||||||
|
SUB R6, R3, R3
|
||||||
|
SUB $1, R3, R3
|
||||||
|
|
||||||
|
MOVHU R3, 0(R17)(R11<<1)
|
||||||
|
|
||||||
|
// currHash := hash(uint32(x>>8), shift)
|
||||||
|
LSR $8, R14, R14
|
||||||
|
MOVW R14, R11
|
||||||
|
MULW R16, R11, R11
|
||||||
|
LSRW R5, R11, R11
|
||||||
|
|
||||||
|
// candidate = int(table[currHash])
|
||||||
|
MOVHU 0(R17)(R11<<1), R15
|
||||||
|
|
||||||
|
// table[currHash] = uint16(s)
|
||||||
|
ADD $1, R3, R3
|
||||||
|
MOVHU R3, 0(R17)(R11<<1)
|
||||||
|
|
||||||
|
// if uint32(x>>8) == load32(src, candidate) { continue }
|
||||||
|
MOVW (R6)(R15), R4
|
||||||
|
CMPW R4, R14
|
||||||
|
BEQ inner1
|
||||||
|
|
||||||
|
// nextHash = hash(uint32(x>>16), shift)
|
||||||
|
LSR $8, R14, R14
|
||||||
|
MOVW R14, R11
|
||||||
|
MULW R16, R11, R11
|
||||||
|
LSRW R5, R11, R11
|
||||||
|
|
||||||
|
// s++
|
||||||
|
ADD $1, R7, R7
|
||||||
|
|
||||||
|
// break out of the inner1 for loop, i.e. continue the outer loop.
|
||||||
|
B outer
|
||||||
|
|
||||||
|
emitRemainder:
|
||||||
|
// if nextEmit < len(src) { etc }
|
||||||
|
MOVD src_len+32(FP), R3
|
||||||
|
ADD R6, R3, R3
|
||||||
|
CMP R3, R10
|
||||||
|
BEQ encodeBlockEnd
|
||||||
|
|
||||||
|
// d += emitLiteral(dst[d:], src[nextEmit:])
|
||||||
|
//
|
||||||
|
// Push args.
|
||||||
|
MOVD R8, 8(RSP)
|
||||||
|
MOVD $0, 16(RSP) // Unnecessary, as the callee ignores it, but conservative.
|
||||||
|
MOVD $0, 24(RSP) // Unnecessary, as the callee ignores it, but conservative.
|
||||||
|
MOVD R10, 32(RSP)
|
||||||
|
SUB R10, R3, R3
|
||||||
|
MOVD R3, 40(RSP)
|
||||||
|
MOVD R3, 48(RSP) // Unnecessary, as the callee ignores it, but conservative.
|
||||||
|
|
||||||
|
// Spill local variables (registers) onto the stack; call; unspill.
|
||||||
|
MOVD R8, 88(RSP)
|
||||||
|
CALL ·emitLiteral(SB)
|
||||||
|
MOVD 88(RSP), R8
|
||||||
|
|
||||||
|
// Finish the "d +=" part of "d += emitLiteral(etc)".
|
||||||
|
MOVD 56(RSP), R1
|
||||||
|
ADD R1, R8, R8
|
||||||
|
|
||||||
|
encodeBlockEnd:
|
||||||
|
MOVD dst_base+0(FP), R3
|
||||||
|
SUB R3, R8, R8
|
||||||
|
MOVD R8, d+48(FP)
|
||||||
|
RET
|
|
@ -5,6 +5,7 @@
|
||||||
// +build !appengine
|
// +build !appengine
|
||||||
// +build gc
|
// +build gc
|
||||||
// +build !noasm
|
// +build !noasm
|
||||||
|
// +build amd64 arm64
|
||||||
|
|
||||||
package snappy
|
package snappy
|
||||||
|
|
|
@ -2,7 +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.
|
||||||
|
|
||||||
// +build !amd64 appengine !gc noasm
|
// +build !amd64,!arm64 appengine !gc noasm
|
||||||
|
|
||||||
package snappy
|
package snappy
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
module github.com/golang/snappy
|
|
@ -1,17 +0,0 @@
|
||||||
language: go
|
|
||||||
sudo: false
|
|
||||||
|
|
||||||
go:
|
|
||||||
- "1.12.x"
|
|
||||||
- "1.13.x"
|
|
||||||
- tip
|
|
||||||
|
|
||||||
env:
|
|
||||||
- GO111MODULE=on
|
|
||||||
|
|
||||||
script:
|
|
||||||
- go generate ./... && test `git ls-files --modified | wc -l` = 0
|
|
||||||
- go test -race -v -bench=. -coverprofile=coverage.txt -covermode=atomic ./...
|
|
||||||
|
|
||||||
after_success:
|
|
||||||
- bash <(curl -s https://codecov.io/bash)
|
|
|
@ -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
|
||||||
|
|
|
@ -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,22 +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://render.com
|
||||||
* https://github.com/peterzen/goresolver
|
* https://github.com/peterzen/goresolver
|
||||||
* https://github.com/folbricht/routedns
|
* 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.
|
||||||
|
|
||||||
|
@ -166,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
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ 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
|
||||||
|
|
|
@ -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,14 +127,36 @@ 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) {
|
||||||
|
co, err := c.Dial(address)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
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 {
|
if !c.SingleInflight {
|
||||||
return c.exchange(m, address)
|
return c.exchange(m, conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
q := m.Question[0]
|
q := m.Question[0]
|
||||||
key := fmt.Sprintf("%s:%d:%d", q.Name, q.Qtype, q.Qclass)
|
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) {
|
r, rtt, err, shared := c.group.Do(key, func() (*Msg, time.Duration, error) {
|
||||||
return c.exchange(m, address)
|
return c.exchange(m, conn)
|
||||||
})
|
})
|
||||||
if r != nil && shared {
|
if r != nil && shared {
|
||||||
r = r.Copy()
|
r = r.Copy()
|
||||||
|
@ -141,15 +165,7 @@ func (c *Client) Exchange(m *Msg, address string) (r *Msg, rtt time.Duration, er
|
||||||
return r, rtt, err
|
return r, rtt, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
|
func (c *Client) exchange(m *Msg, co *Conn) (r *Msg, rtt time.Duration, err error) {
|
||||||
var co *Conn
|
|
||||||
|
|
||||||
co, err = c.Dial(a)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, err
|
|
||||||
}
|
|
||||||
defer co.Close()
|
|
||||||
|
|
||||||
opt := m.IsEdns0()
|
opt := m.IsEdns0()
|
||||||
// If EDNS0 is used use that for size.
|
// If EDNS0 is used use that for size.
|
||||||
|
@ -161,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())))
|
||||||
|
@ -170,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
|
||||||
}
|
}
|
||||||
|
@ -197,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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,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 {
|
||||||
|
@ -305,11 +340,10 @@ func (co *Conn) Write(p []byte) (int, error) {
|
||||||
return co.Conn.Write(p)
|
return co.Conn.Write(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
l := make([]byte, 2)
|
msg := make([]byte, 2+len(p))
|
||||||
binary.BigEndian.PutUint16(l, uint16(len(p)))
|
binary.BigEndian.PutUint16(msg, uint16(len(p)))
|
||||||
|
copy(msg[2:], p)
|
||||||
n, err := (&net.Buffers{l, p}).WriteTo(co.Conn)
|
return co.Conn.Write(msg)
|
||||||
return int(n), err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the appropriate timeout for a specific request
|
// Return the appropriate timeout for a specific request
|
||||||
|
@ -345,7 +379,7 @@ 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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}
|
||||||
|
@ -317,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
|
||||||
|
@ -343,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."...)
|
||||||
|
@ -364,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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -111,7 +114,7 @@ func (h *RR_Header) parse(c *zlexer, origin string) *ParseError {
|
||||||
|
|
||||||
// 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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -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,7 @@ func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
rr.Signature = toBase64(signature)
|
rr.Signature = toBase64(signature)
|
||||||
|
return nil
|
||||||
case RSAMD5, DSA, DSANSEC3SHA1:
|
case RSAMD5, DSA, DSANSEC3SHA1:
|
||||||
// See RFC 6944.
|
// See RFC 6944.
|
||||||
return ErrAlg
|
return ErrAlg
|
||||||
|
@ -332,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)
|
||||||
|
@ -346,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
|
||||||
|
@ -366,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) {
|
||||||
|
@ -405,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
|
||||||
|
@ -423,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)
|
||||||
|
@ -448,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 {
|
||||||
|
@ -512,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 {
|
||||||
|
@ -600,30 +595,6 @@ func (k *DNSKEY) publicKeyECDSA() *ecdsa.PublicKey {
|
||||||
return pubkey
|
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 = new(big.Int).SetBytes(q)
|
|
||||||
pubkey.Parameters.P = new(big.Int).SetBytes(p)
|
|
||||||
pubkey.Parameters.G = new(big.Int).SetBytes(g)
|
|
||||||
pubkey.Y = new(big.Int).SetBytes(y)
|
|
||||||
return pubkey
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *DNSKEY) publicKeyED25519() ed25519.PublicKey {
|
func (k *DNSKEY) publicKeyED25519() ed25519.PublicKey {
|
||||||
keybuf, err := fromBase64([]byte(k.PublicKey))
|
keybuf, err := fromBase64([]byte(k.PublicKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -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)
|
||||||
|
|
|
@ -3,12 +3,11 @@ package dns
|
||||||
import (
|
import (
|
||||||
"crypto"
|
"crypto"
|
||||||
"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.
|
||||||
|
@ -19,8 +18,6 @@ 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 RSAMD5, DSA, DSANSEC3SHA1:
|
|
||||||
return nil, ErrAlg
|
|
||||||
case RSASHA1, RSASHA256, RSASHA1NSEC3SHA1:
|
case RSASHA1, RSASHA256, RSASHA1NSEC3SHA1:
|
||||||
if bits < 512 || bits > 4096 {
|
if bits < 512 || bits > 4096 {
|
||||||
return nil, ErrKeySize
|
return nil, ErrKeySize
|
||||||
|
@ -41,6 +38,8 @@ 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 {
|
||||||
|
|
|
@ -4,13 +4,12 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"crypto"
|
"crypto"
|
||||||
"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.
|
||||||
|
@ -43,15 +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 RSAMD5, DSA, DSANSEC3SHA1:
|
case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512:
|
||||||
return nil, ErrAlg
|
|
||||||
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
|
||||||
|
@ -62,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
|
||||||
|
@ -80,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,13 +2,11 @@ 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"
|
||||||
|
@ -17,8 +15,8 @@ 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] + ")"
|
||||||
|
@ -67,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 +
|
||||||
|
|
|
@ -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)
|
||||||
|
@ -260,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.
|
||||||
|
|
|
@ -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 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()) {
|
||||||
|
|
|
@ -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
|
||||||
|
@ -88,11 +126,11 @@ func (rr *OPT) len(off int, compression map[string]struct{}) int {
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rr *OPT) parse(c *zlexer, origin 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.
|
||||||
|
@ -452,7 +500,7 @@ func (e *EDNS0_LLQ) copy() EDNS0 {
|
||||||
return &EDNS0_LLQ{e.Code, e.Version, e.Opcode, e.Error, e.Id, e.LeaseLife}
|
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
|
||||||
|
@ -525,7 +573,7 @@ func (e *EDNS0_N3U) String() string {
|
||||||
}
|
}
|
||||||
func (e *EDNS0_N3U) copy() EDNS0 { return &EDNS0_N3U{e.Code, e.AlgCode} }
|
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
|
||||||
|
@ -673,3 +721,101 @@ func (e *EDNS0_PADDING) copy() EDNS0 {
|
||||||
copy(b, e.Padding)
|
copy(b, e.Padding)
|
||||||
return &EDNS0_PADDING{b}
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -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,12 +40,12 @@ 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)
|
||||||
}
|
}
|
||||||
|
@ -94,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
|
||||||
|
|
||||||
|
@ -173,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] == '{' {
|
||||||
|
@ -208,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
|
||||||
|
@ -229,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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
module github.com/miekg/dns
|
module github.com/miekg/dns
|
||||||
|
|
||||||
go 1.12
|
go 1.14
|
||||||
|
|
||||||
require (
|
require (
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58
|
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04
|
||||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe
|
|
||||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 // indirect
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,39 +1,10 @@
|
||||||
golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4 h1:Vk3wNqEZwyGyei9yq5ekj7frek2u7HUfffJ1/opblzc=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
|
||||||
golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||||
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472 h1:Gv7RPwsi3eZ2Fgewe3CBsuOebPwO27PoXzRpJPsvSSM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392 h1:ACG4HJsFiNMf47Y4PeRoebLNy/2lXT9EtprMuTFWt1M=
|
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04 h1:cEhElsAv9LUt9ZUUocxzWe05oFLVd+AA2nstydTeI8g=
|
||||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
|
||||||
golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3 h1:dgd4x4kJt7G4k4m93AYLzM8Ni6h2qLTfh9n9vXJT3/0=
|
|
||||||
golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
|
|
||||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g=
|
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20180928133829-e4b3c5e90611 h1:O33LKL7WyJgjN9CvxfTIomjIClbd/Kq86/iipowHQU0=
|
|
||||||
golang.org/x/sys v0.0.0-20180928133829-e4b3c5e90611/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd h1:DBH9mDw0zluJT/R+nGuV3jWFWLFaHyYZWD4tOT+cjn0=
|
|
||||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M=
|
|
||||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA=
|
|
||||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
|
|
|
@ -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
|
||||||
|
@ -83,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 == "." {
|
||||||
|
|
|
@ -398,19 +398,14 @@ Loop:
|
||||||
return "", lenmsg, ErrLongDomain
|
return "", lenmsg, ErrLongDomain
|
||||||
}
|
}
|
||||||
for _, b := range msg[off : off+c] {
|
for _, b := range msg[off : off+c] {
|
||||||
switch 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:
|
||||||
|
@ -629,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 {
|
||||||
|
@ -661,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)
|
||||||
|
@ -741,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 {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"net"
|
"net"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -423,86 +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 EDNS0EXPIRE:
|
|
||||||
e := new(EDNS0_EXPIRE)
|
|
||||||
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
|
||||||
|
@ -521,9 +448,7 @@ func packDataOpt(options []EDNS0, msg []byte, off int) (int, error) {
|
||||||
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)
|
||||||
|
@ -659,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
|
||||||
|
@ -730,6 +714,13 @@ func packDataAplPrefix(p *APLPrefix, msg []byte, off int) (int, error) {
|
||||||
if p.Negation {
|
if p.Negation {
|
||||||
n = 0x80
|
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
|
adflen := uint8(len(addr)) & 0x7f
|
||||||
off, err = packUint8(n|adflen, msg, off)
|
off, err = packUint8(n|adflen, msg, off)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -783,28 +774,31 @@ func unpackDataAplPrefix(msg []byte, off int) (APLPrefix, int, error) {
|
||||||
if int(prefix) > 8*len(ip) {
|
if int(prefix) > 8*len(ip) {
|
||||||
return APLPrefix{}, len(msg), &Error{err: "APL prefix too long"}
|
return APLPrefix{}, len(msg), &Error{err: "APL prefix too long"}
|
||||||
}
|
}
|
||||||
|
|
||||||
afdlen := int(nlen & 0x7f)
|
afdlen := int(nlen & 0x7f)
|
||||||
if (int(prefix)+7)/8 != afdlen {
|
if afdlen > len(ip) {
|
||||||
return APLPrefix{}, len(msg), &Error{err: "invalid APL address length"}
|
return APLPrefix{}, len(msg), &Error{err: "APL length too long"}
|
||||||
}
|
}
|
||||||
if off+afdlen > len(msg) {
|
if off+afdlen > len(msg) {
|
||||||
return APLPrefix{}, len(msg), &Error{err: "overflow unpacking APL address"}
|
return APLPrefix{}, len(msg), &Error{err: "overflow unpacking APL address"}
|
||||||
}
|
}
|
||||||
off += copy(ip, msg[off:off+afdlen])
|
off += copy(ip, msg[off:off+afdlen])
|
||||||
if prefix%8 > 0 {
|
if afdlen > 0 {
|
||||||
last := ip[afdlen-1]
|
last := ip[afdlen-1]
|
||||||
zero := uint8(0xff) >> (prefix % 8)
|
if last == 0 {
|
||||||
if last&zero > 0 {
|
|
||||||
return APLPrefix{}, len(msg), &Error{err: "extra APL address bits"}
|
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{
|
return APLPrefix{
|
||||||
Negation: (nlen & 0x80) != 0,
|
Negation: (nlen & 0x80) != 0,
|
||||||
Network: net.IPNet{
|
Network: ipnet,
|
||||||
IP: ip,
|
|
||||||
Mask: net.CIDRMask(int(prefix), 8*len(ip)),
|
|
||||||
},
|
|
||||||
}, off, nil
|
}, off, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,14 @@ package dns
|
||||||
// record adding as many records as possible without exceeding the
|
// record adding as many records as possible without exceeding the
|
||||||
// requested buffer size.
|
// 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.
|
// The TC bit will be set if any records were excluded from the message.
|
||||||
// This indicates to that the client should retry over TCP.
|
// 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
|
// 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
|
// "required" RRs can be included in the response. Unfortunately, we have
|
||||||
|
@ -28,11 +34,11 @@ func (dns *Msg) Truncate(size int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RFC 6891 mandates that the payload size in an OPT record
|
// RFC 6891 mandates that the payload size in an OPT record
|
||||||
// less than 512 bytes must be treated as equal to 512 bytes.
|
// less than 512 (MinMsgSize) bytes must be treated as equal to 512 bytes.
|
||||||
//
|
//
|
||||||
// For ease of use, we impose that restriction here.
|
// For ease of use, we impose that restriction here.
|
||||||
if size < 512 {
|
if size < MinMsgSize {
|
||||||
size = 512
|
size = MinMsgSize
|
||||||
}
|
}
|
||||||
|
|
||||||
l := msgLenWithCompressionMap(dns, nil) // uncompressed length
|
l := msgLenWithCompressionMap(dns, nil) // uncompressed length
|
||||||
|
@ -73,11 +79,11 @@ func (dns *Msg) Truncate(size int) {
|
||||||
|
|
||||||
var numExtra int
|
var numExtra int
|
||||||
if l < size {
|
if l < size {
|
||||||
l, numExtra = truncateLoop(dns.Extra, size, l, compression)
|
_, numExtra = truncateLoop(dns.Extra, size, l, compression)
|
||||||
}
|
}
|
||||||
|
|
||||||
// See the function documentation for when we set this.
|
// See the function documentation for when we set this.
|
||||||
dns.Truncated = len(dns.Answer) > numAnswer ||
|
dns.Truncated = dns.Truncated || len(dns.Answer) > numAnswer ||
|
||||||
len(dns.Ns) > numNS || len(dns.Extra) > numExtra
|
len(dns.Ns) > numNS || len(dns.Extra) > numExtra
|
||||||
|
|
||||||
dns.Answer = dns.Answer[:numAnswer]
|
dns.Answer = dns.Answer[:numAnswer]
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -6,14 +6,13 @@ import "strings"
|
||||||
// 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 into the PrivateRdata argument.
|
// Copy copies the Rdata into the PrivateRdata argument.
|
||||||
Copy(PrivateRdata) error
|
Copy(PrivateRdata) error
|
||||||
|
@ -91,7 +90,7 @@ Fetch:
|
||||||
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.
|
||||||
|
|
|
@ -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,70 +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. Note that $GENERATE's range support up to a maximum of
|
|
||||||
// of 65535 steps.
|
|
||||||
//
|
|
||||||
// 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
|
||||||
|
@ -227,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
|
||||||
|
|
||||||
|
@ -654,10 +580,23 @@ func (zp *ZoneParser) Next() (RR, bool) {
|
||||||
|
|
||||||
st = zExpectRdata
|
st = zExpectRdata
|
||||||
case zExpectRdata:
|
case zExpectRdata:
|
||||||
var rr RR
|
var (
|
||||||
if newFn, ok := TypeToRR[h.Rrtype]; ok && canParseAsRR(h.Rrtype) {
|
rr RR
|
||||||
|
parseAsRFC3597 bool
|
||||||
|
)
|
||||||
|
if newFn, ok := TypeToRR[h.Rrtype]; ok {
|
||||||
rr = newFn()
|
rr = newFn()
|
||||||
*rr.Header() = *h
|
*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 {
|
} else {
|
||||||
rr = &RFC3597{Hdr: *h}
|
rr = &RFC3597{Hdr: *h}
|
||||||
}
|
}
|
||||||
|
@ -677,13 +616,18 @@ func (zp *ZoneParser) Next() (RR, bool) {
|
||||||
return zp.setParseError("unexpected newline", l)
|
return zp.setParseError("unexpected newline", l)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := rr.parse(zp.c, zp.origin); err != nil {
|
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.
|
// err is a concrete *ParseError without the file field set.
|
||||||
// The setParseError call below will construct a new
|
// The setParseError call below will construct a new
|
||||||
// *ParseError with file set to zp.file.
|
// *ParseError with file set to zp.file.
|
||||||
|
|
||||||
// If err.lex is nil than we have encounter an unknown RR type
|
// err.lex may be nil in which case we substitute our current
|
||||||
// in that case we substitute our current lex token.
|
// lex token.
|
||||||
if err.lex == (lex{}) {
|
if err.lex == (lex{}) {
|
||||||
return zp.setParseError(err.err, l)
|
return zp.setParseError(err.err, l)
|
||||||
}
|
}
|
||||||
|
@ -691,6 +635,13 @@ func (zp *ZoneParser) Next() (RR, bool) {
|
||||||
return zp.setParseError(err.err, err.lex)
|
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
|
return rr, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -700,18 +651,6 @@ func (zp *ZoneParser) Next() (RR, bool) {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// canParseAsRR returns true if the record type can be parsed as a
|
|
||||||
// concrete RR. It blacklists certain record types that must be parsed
|
|
||||||
// according to RFC 3597 because they lack a presentation format.
|
|
||||||
func canParseAsRR(rrtype uint16) bool {
|
|
||||||
switch rrtype {
|
|
||||||
case TypeANY, TypeNULL, TypeOPT, TypeTSIG:
|
|
||||||
return false
|
|
||||||
default:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type zlexer struct {
|
type zlexer struct {
|
||||||
br io.ByteReader
|
br io.ByteReader
|
||||||
|
|
||||||
|
@ -1287,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
|
||||||
|
@ -1304,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
|
||||||
}
|
}
|
||||||
|
@ -1352,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
|
||||||
|
@ -1363,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
|
||||||
|
@ -1395,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}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -10,15 +11,15 @@ import (
|
||||||
// A remainder of the rdata with embedded spaces, return the parsed string (sans the spaces)
|
// A remainder of the rdata with embedded spaces, return the parsed string (sans the spaces)
|
||||||
// or an error
|
// or an error
|
||||||
func endingToString(c *zlexer, errstr string) (string, *ParseError) {
|
func endingToString(c *zlexer, errstr string) (string, *ParseError) {
|
||||||
var s string
|
var buffer bytes.Buffer
|
||||||
l, _ := c.Next() // zString
|
l, _ := c.Next() // zString
|
||||||
for l.value != zNewline && l.value != zEOF {
|
for l.value != zNewline && l.value != zEOF {
|
||||||
if l.err {
|
if l.err {
|
||||||
return s, &ParseError{"", errstr, l}
|
return buffer.String(), &ParseError{"", errstr, l}
|
||||||
}
|
}
|
||||||
switch l.value {
|
switch l.value {
|
||||||
case zString:
|
case zString:
|
||||||
s += l.token
|
buffer.WriteString(l.token)
|
||||||
case zBlank: // Ok
|
case zBlank: // Ok
|
||||||
default:
|
default:
|
||||||
return "", &ParseError{"", errstr, l}
|
return "", &ParseError{"", errstr, l}
|
||||||
|
@ -26,7 +27,7 @@ func endingToString(c *zlexer, errstr string) (string, *ParseError) {
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
return s, nil
|
return buffer.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// A remainder of the rdata with embedded spaces, split on unquoted whitespace
|
// A remainder of the rdata with embedded spaces, split on unquoted whitespace
|
||||||
|
@ -403,7 +404,7 @@ func (rr *SOA) parse(c *zlexer, o string) *ParseError {
|
||||||
if l.err {
|
if l.err {
|
||||||
return &ParseError{"", "bad SOA zone parameter", l}
|
return &ParseError{"", "bad SOA zone parameter", l}
|
||||||
}
|
}
|
||||||
if j, e := strconv.ParseUint(l.token, 10, 32); e != nil {
|
if j, err := strconv.ParseUint(l.token, 10, 32); err != nil {
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
// Serial must be a number
|
// Serial must be a number
|
||||||
return &ParseError{"", "bad SOA zone parameter", l}
|
return &ParseError{"", "bad SOA zone parameter", l}
|
||||||
|
@ -446,16 +447,16 @@ func (rr *SRV) parse(c *zlexer, o string) *ParseError {
|
||||||
|
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next() // zString
|
l, _ = c.Next() // zString
|
||||||
i, e = strconv.ParseUint(l.token, 10, 16)
|
i, e1 := strconv.ParseUint(l.token, 10, 16)
|
||||||
if e != nil || l.err {
|
if e1 != nil || l.err {
|
||||||
return &ParseError{"", "bad SRV Weight", l}
|
return &ParseError{"", "bad SRV Weight", l}
|
||||||
}
|
}
|
||||||
rr.Weight = uint16(i)
|
rr.Weight = uint16(i)
|
||||||
|
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next() // zString
|
l, _ = c.Next() // zString
|
||||||
i, e = strconv.ParseUint(l.token, 10, 16)
|
i, e2 := strconv.ParseUint(l.token, 10, 16)
|
||||||
if e != nil || l.err {
|
if e2 != nil || l.err {
|
||||||
return &ParseError{"", "bad SRV Port", l}
|
return &ParseError{"", "bad SRV Port", l}
|
||||||
}
|
}
|
||||||
rr.Port = uint16(i)
|
rr.Port = uint16(i)
|
||||||
|
@ -482,8 +483,8 @@ func (rr *NAPTR) parse(c *zlexer, o string) *ParseError {
|
||||||
|
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next() // zString
|
l, _ = c.Next() // zString
|
||||||
i, e = strconv.ParseUint(l.token, 10, 16)
|
i, e1 := strconv.ParseUint(l.token, 10, 16)
|
||||||
if e != nil || l.err {
|
if e1 != nil || l.err {
|
||||||
return &ParseError{"", "bad NAPTR Preference", l}
|
return &ParseError{"", "bad NAPTR Preference", l}
|
||||||
}
|
}
|
||||||
rr.Preference = uint16(i)
|
rr.Preference = uint16(i)
|
||||||
|
@ -581,15 +582,15 @@ func (rr *TALINK) parse(c *zlexer, o string) *ParseError {
|
||||||
|
|
||||||
func (rr *LOC) parse(c *zlexer, o string) *ParseError {
|
func (rr *LOC) parse(c *zlexer, o string) *ParseError {
|
||||||
// Non zero defaults for LOC record, see RFC 1876, Section 3.
|
// Non zero defaults for LOC record, see RFC 1876, Section 3.
|
||||||
rr.HorizPre = 165 // 10000
|
rr.Size = 0x12 // 1e2 cm (1m)
|
||||||
rr.VertPre = 162 // 10
|
rr.HorizPre = 0x16 // 1e6 cm (10000m)
|
||||||
rr.Size = 18 // 1
|
rr.VertPre = 0x13 // 1e3 cm (10m)
|
||||||
ok := false
|
ok := false
|
||||||
|
|
||||||
// North
|
// North
|
||||||
l, _ := c.Next()
|
l, _ := c.Next()
|
||||||
i, e := strconv.ParseUint(l.token, 10, 32)
|
i, e := strconv.ParseUint(l.token, 10, 32)
|
||||||
if e != nil || l.err {
|
if e != nil || l.err || i > 90 {
|
||||||
return &ParseError{"", "bad LOC Latitude", l}
|
return &ParseError{"", "bad LOC Latitude", l}
|
||||||
}
|
}
|
||||||
rr.Latitude = 1000 * 60 * 60 * uint32(i)
|
rr.Latitude = 1000 * 60 * 60 * uint32(i)
|
||||||
|
@ -600,15 +601,15 @@ func (rr *LOC) parse(c *zlexer, o string) *ParseError {
|
||||||
if rr.Latitude, ok = locCheckNorth(l.token, rr.Latitude); ok {
|
if rr.Latitude, ok = locCheckNorth(l.token, rr.Latitude); ok {
|
||||||
goto East
|
goto East
|
||||||
}
|
}
|
||||||
i, e = strconv.ParseUint(l.token, 10, 32)
|
if i, err := strconv.ParseUint(l.token, 10, 32); err != nil || l.err || i > 59 {
|
||||||
if e != nil || l.err {
|
|
||||||
return &ParseError{"", "bad LOC Latitude minutes", l}
|
return &ParseError{"", "bad LOC Latitude minutes", l}
|
||||||
}
|
} else {
|
||||||
rr.Latitude += 1000 * 60 * uint32(i)
|
rr.Latitude += 1000 * 60 * uint32(i)
|
||||||
|
}
|
||||||
|
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
if i, e := strconv.ParseFloat(l.token, 32); e != nil || l.err {
|
if i, err := strconv.ParseFloat(l.token, 64); err != nil || l.err || i < 0 || i >= 60 {
|
||||||
return &ParseError{"", "bad LOC Latitude seconds", l}
|
return &ParseError{"", "bad LOC Latitude seconds", l}
|
||||||
} else {
|
} else {
|
||||||
rr.Latitude += uint32(1000 * i)
|
rr.Latitude += uint32(1000 * i)
|
||||||
|
@ -626,7 +627,7 @@ East:
|
||||||
// East
|
// East
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
if i, e := strconv.ParseUint(l.token, 10, 32); e != nil || l.err {
|
if i, err := strconv.ParseUint(l.token, 10, 32); err != nil || l.err || i > 180 {
|
||||||
return &ParseError{"", "bad LOC Longitude", l}
|
return &ParseError{"", "bad LOC Longitude", l}
|
||||||
} else {
|
} else {
|
||||||
rr.Longitude = 1000 * 60 * 60 * uint32(i)
|
rr.Longitude = 1000 * 60 * 60 * uint32(i)
|
||||||
|
@ -637,14 +638,14 @@ East:
|
||||||
if rr.Longitude, ok = locCheckEast(l.token, rr.Longitude); ok {
|
if rr.Longitude, ok = locCheckEast(l.token, rr.Longitude); ok {
|
||||||
goto Altitude
|
goto Altitude
|
||||||
}
|
}
|
||||||
if i, e := strconv.ParseUint(l.token, 10, 32); e != nil || l.err {
|
if i, err := strconv.ParseUint(l.token, 10, 32); err != nil || l.err || i > 59 {
|
||||||
return &ParseError{"", "bad LOC Longitude minutes", l}
|
return &ParseError{"", "bad LOC Longitude minutes", l}
|
||||||
} else {
|
} else {
|
||||||
rr.Longitude += 1000 * 60 * uint32(i)
|
rr.Longitude += 1000 * 60 * uint32(i)
|
||||||
}
|
}
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
if i, e := strconv.ParseFloat(l.token, 32); e != nil || l.err {
|
if i, err := strconv.ParseFloat(l.token, 64); err != nil || l.err || i < 0 || i >= 60 {
|
||||||
return &ParseError{"", "bad LOC Longitude seconds", l}
|
return &ParseError{"", "bad LOC Longitude seconds", l}
|
||||||
} else {
|
} else {
|
||||||
rr.Longitude += uint32(1000 * i)
|
rr.Longitude += uint32(1000 * i)
|
||||||
|
@ -661,13 +662,13 @@ East:
|
||||||
Altitude:
|
Altitude:
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
if len(l.token) == 0 || l.err {
|
if l.token == "" || l.err {
|
||||||
return &ParseError{"", "bad LOC Altitude", l}
|
return &ParseError{"", "bad LOC Altitude", l}
|
||||||
}
|
}
|
||||||
if l.token[len(l.token)-1] == 'M' || l.token[len(l.token)-1] == 'm' {
|
if l.token[len(l.token)-1] == 'M' || l.token[len(l.token)-1] == 'm' {
|
||||||
l.token = l.token[0 : len(l.token)-1]
|
l.token = l.token[0 : len(l.token)-1]
|
||||||
}
|
}
|
||||||
if i, e := strconv.ParseFloat(l.token, 32); e != nil {
|
if i, err := strconv.ParseFloat(l.token, 64); err != nil {
|
||||||
return &ParseError{"", "bad LOC Altitude", l}
|
return &ParseError{"", "bad LOC Altitude", l}
|
||||||
} else {
|
} else {
|
||||||
rr.Altitude = uint32(i*100.0 + 10000000.0 + 0.5)
|
rr.Altitude = uint32(i*100.0 + 10000000.0 + 0.5)
|
||||||
|
@ -681,23 +682,23 @@ Altitude:
|
||||||
case zString:
|
case zString:
|
||||||
switch count {
|
switch count {
|
||||||
case 0: // Size
|
case 0: // Size
|
||||||
e, m, ok := stringToCm(l.token)
|
exp, m, ok := stringToCm(l.token)
|
||||||
if !ok {
|
if !ok {
|
||||||
return &ParseError{"", "bad LOC Size", l}
|
return &ParseError{"", "bad LOC Size", l}
|
||||||
}
|
}
|
||||||
rr.Size = e&0x0f | m<<4&0xf0
|
rr.Size = exp&0x0f | m<<4&0xf0
|
||||||
case 1: // HorizPre
|
case 1: // HorizPre
|
||||||
e, m, ok := stringToCm(l.token)
|
exp, m, ok := stringToCm(l.token)
|
||||||
if !ok {
|
if !ok {
|
||||||
return &ParseError{"", "bad LOC HorizPre", l}
|
return &ParseError{"", "bad LOC HorizPre", l}
|
||||||
}
|
}
|
||||||
rr.HorizPre = e&0x0f | m<<4&0xf0
|
rr.HorizPre = exp&0x0f | m<<4&0xf0
|
||||||
case 2: // VertPre
|
case 2: // VertPre
|
||||||
e, m, ok := stringToCm(l.token)
|
exp, m, ok := stringToCm(l.token)
|
||||||
if !ok {
|
if !ok {
|
||||||
return &ParseError{"", "bad LOC VertPre", l}
|
return &ParseError{"", "bad LOC VertPre", l}
|
||||||
}
|
}
|
||||||
rr.VertPre = e&0x0f | m<<4&0xf0
|
rr.VertPre = exp&0x0f | m<<4&0xf0
|
||||||
}
|
}
|
||||||
count++
|
count++
|
||||||
case zBlank:
|
case zBlank:
|
||||||
|
@ -721,7 +722,7 @@ func (rr *HIP) parse(c *zlexer, o string) *ParseError {
|
||||||
|
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next() // zString
|
l, _ = c.Next() // zString
|
||||||
if len(l.token) == 0 || l.err {
|
if l.token == "" || l.err {
|
||||||
return &ParseError{"", "bad HIP Hit", l}
|
return &ParseError{"", "bad HIP Hit", l}
|
||||||
}
|
}
|
||||||
rr.Hit = l.token // This can not contain spaces, see RFC 5205 Section 6.
|
rr.Hit = l.token // This can not contain spaces, see RFC 5205 Section 6.
|
||||||
|
@ -729,11 +730,15 @@ func (rr *HIP) parse(c *zlexer, o string) *ParseError {
|
||||||
|
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next() // zString
|
l, _ = c.Next() // zString
|
||||||
if len(l.token) == 0 || l.err {
|
if l.token == "" || l.err {
|
||||||
return &ParseError{"", "bad HIP PublicKey", l}
|
return &ParseError{"", "bad HIP PublicKey", l}
|
||||||
}
|
}
|
||||||
rr.PublicKey = l.token // This cannot contain spaces
|
rr.PublicKey = l.token // This cannot contain spaces
|
||||||
rr.PublicKeyLength = uint16(base64.StdEncoding.DecodedLen(len(rr.PublicKey)))
|
decodedPK, decodedPKerr := base64.StdEncoding.DecodeString(rr.PublicKey)
|
||||||
|
if decodedPKerr != nil {
|
||||||
|
return &ParseError{"", "bad HIP PublicKey", l}
|
||||||
|
}
|
||||||
|
rr.PublicKeyLength = uint16(len(decodedPK))
|
||||||
|
|
||||||
// RendezvousServers (if any)
|
// RendezvousServers (if any)
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
|
@ -762,7 +767,7 @@ func (rr *CERT) parse(c *zlexer, o string) *ParseError {
|
||||||
l, _ := c.Next()
|
l, _ := c.Next()
|
||||||
if v, ok := StringToCertType[l.token]; ok {
|
if v, ok := StringToCertType[l.token]; ok {
|
||||||
rr.Type = v
|
rr.Type = v
|
||||||
} else if i, e := strconv.ParseUint(l.token, 10, 16); e != nil {
|
} else if i, err := strconv.ParseUint(l.token, 10, 16); err != nil {
|
||||||
return &ParseError{"", "bad CERT Type", l}
|
return &ParseError{"", "bad CERT Type", l}
|
||||||
} else {
|
} else {
|
||||||
rr.Type = uint16(i)
|
rr.Type = uint16(i)
|
||||||
|
@ -778,7 +783,7 @@ func (rr *CERT) parse(c *zlexer, o string) *ParseError {
|
||||||
l, _ = c.Next() // zString
|
l, _ = c.Next() // zString
|
||||||
if v, ok := StringToAlgorithm[l.token]; ok {
|
if v, ok := StringToAlgorithm[l.token]; ok {
|
||||||
rr.Algorithm = v
|
rr.Algorithm = v
|
||||||
} else if i, e := strconv.ParseUint(l.token, 10, 8); e != nil {
|
} else if i, err := strconv.ParseUint(l.token, 10, 8); err != nil {
|
||||||
return &ParseError{"", "bad CERT Algorithm", l}
|
return &ParseError{"", "bad CERT Algorithm", l}
|
||||||
} else {
|
} else {
|
||||||
rr.Algorithm = uint8(i)
|
rr.Algorithm = uint8(i)
|
||||||
|
@ -812,8 +817,8 @@ func (rr *CSYNC) parse(c *zlexer, o string) *ParseError {
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
|
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
j, e = strconv.ParseUint(l.token, 10, 16)
|
j, e1 := strconv.ParseUint(l.token, 10, 16)
|
||||||
if e != nil {
|
if e1 != nil {
|
||||||
// Serial must be a number
|
// Serial must be a number
|
||||||
return &ParseError{"", "bad CSYNC flags", l}
|
return &ParseError{"", "bad CSYNC flags", l}
|
||||||
}
|
}
|
||||||
|
@ -845,9 +850,39 @@ func (rr *CSYNC) parse(c *zlexer, o string) *ParseError {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rr *SIG) parse(c *zlexer, o string) *ParseError {
|
func (rr *ZONEMD) parse(c *zlexer, o string) *ParseError {
|
||||||
return rr.RRSIG.parse(c, o)
|
l, _ := c.Next()
|
||||||
|
i, e := strconv.ParseUint(l.token, 10, 32)
|
||||||
|
if e != nil || l.err {
|
||||||
|
return &ParseError{"", "bad ZONEMD Serial", l}
|
||||||
}
|
}
|
||||||
|
rr.Serial = uint32(i)
|
||||||
|
|
||||||
|
c.Next() // zBlank
|
||||||
|
l, _ = c.Next()
|
||||||
|
i, e1 := strconv.ParseUint(l.token, 10, 8)
|
||||||
|
if e1 != nil || l.err {
|
||||||
|
return &ParseError{"", "bad ZONEMD Scheme", l}
|
||||||
|
}
|
||||||
|
rr.Scheme = uint8(i)
|
||||||
|
|
||||||
|
c.Next() // zBlank
|
||||||
|
l, _ = c.Next()
|
||||||
|
i, err := strconv.ParseUint(l.token, 10, 8)
|
||||||
|
if err != nil || l.err {
|
||||||
|
return &ParseError{"", "bad ZONEMD Hash Algorithm", l}
|
||||||
|
}
|
||||||
|
rr.Hash = uint8(i)
|
||||||
|
|
||||||
|
s, e2 := endingToString(c, "bad ZONEMD Digest")
|
||||||
|
if e2 != nil {
|
||||||
|
return e2
|
||||||
|
}
|
||||||
|
rr.Digest = s
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *SIG) parse(c *zlexer, o string) *ParseError { return rr.RRSIG.parse(c, o) }
|
||||||
|
|
||||||
func (rr *RRSIG) parse(c *zlexer, o string) *ParseError {
|
func (rr *RRSIG) parse(c *zlexer, o string) *ParseError {
|
||||||
l, _ := c.Next()
|
l, _ := c.Next()
|
||||||
|
@ -868,24 +903,24 @@ func (rr *RRSIG) parse(c *zlexer, o string) *ParseError {
|
||||||
|
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
i, err := strconv.ParseUint(l.token, 10, 8)
|
i, e := strconv.ParseUint(l.token, 10, 8)
|
||||||
if err != nil || l.err {
|
if e != nil || l.err {
|
||||||
return &ParseError{"", "bad RRSIG Algorithm", l}
|
return &ParseError{"", "bad RRSIG Algorithm", l}
|
||||||
}
|
}
|
||||||
rr.Algorithm = uint8(i)
|
rr.Algorithm = uint8(i)
|
||||||
|
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
i, err = strconv.ParseUint(l.token, 10, 8)
|
i, e1 := strconv.ParseUint(l.token, 10, 8)
|
||||||
if err != nil || l.err {
|
if e1 != nil || l.err {
|
||||||
return &ParseError{"", "bad RRSIG Labels", l}
|
return &ParseError{"", "bad RRSIG Labels", l}
|
||||||
}
|
}
|
||||||
rr.Labels = uint8(i)
|
rr.Labels = uint8(i)
|
||||||
|
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
i, err = strconv.ParseUint(l.token, 10, 32)
|
i, e2 := strconv.ParseUint(l.token, 10, 32)
|
||||||
if err != nil || l.err {
|
if e2 != nil || l.err {
|
||||||
return &ParseError{"", "bad RRSIG OrigTtl", l}
|
return &ParseError{"", "bad RRSIG OrigTtl", l}
|
||||||
}
|
}
|
||||||
rr.OrigTtl = uint32(i)
|
rr.OrigTtl = uint32(i)
|
||||||
|
@ -894,8 +929,7 @@ func (rr *RRSIG) parse(c *zlexer, o string) *ParseError {
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
if i, err := StringToTime(l.token); err != nil {
|
if i, err := StringToTime(l.token); err != nil {
|
||||||
// Try to see if all numeric and use it as epoch
|
// Try to see if all numeric and use it as epoch
|
||||||
if i, err := strconv.ParseInt(l.token, 10, 64); err == nil {
|
if i, err := strconv.ParseUint(l.token, 10, 32); err == nil {
|
||||||
// TODO(miek): error out on > MAX_UINT32, same below
|
|
||||||
rr.Expiration = uint32(i)
|
rr.Expiration = uint32(i)
|
||||||
} else {
|
} else {
|
||||||
return &ParseError{"", "bad RRSIG Expiration", l}
|
return &ParseError{"", "bad RRSIG Expiration", l}
|
||||||
|
@ -907,7 +941,7 @@ func (rr *RRSIG) parse(c *zlexer, o string) *ParseError {
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
if i, err := StringToTime(l.token); err != nil {
|
if i, err := StringToTime(l.token); err != nil {
|
||||||
if i, err := strconv.ParseInt(l.token, 10, 64); err == nil {
|
if i, err := strconv.ParseUint(l.token, 10, 32); err == nil {
|
||||||
rr.Inception = uint32(i)
|
rr.Inception = uint32(i)
|
||||||
} else {
|
} else {
|
||||||
return &ParseError{"", "bad RRSIG Inception", l}
|
return &ParseError{"", "bad RRSIG Inception", l}
|
||||||
|
@ -918,8 +952,8 @@ func (rr *RRSIG) parse(c *zlexer, o string) *ParseError {
|
||||||
|
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
i, err = strconv.ParseUint(l.token, 10, 16)
|
i, e3 := strconv.ParseUint(l.token, 10, 16)
|
||||||
if err != nil || l.err {
|
if e3 != nil || l.err {
|
||||||
return &ParseError{"", "bad RRSIG KeyTag", l}
|
return &ParseError{"", "bad RRSIG KeyTag", l}
|
||||||
}
|
}
|
||||||
rr.KeyTag = uint16(i)
|
rr.KeyTag = uint16(i)
|
||||||
|
@ -933,9 +967,9 @@ func (rr *RRSIG) parse(c *zlexer, o string) *ParseError {
|
||||||
}
|
}
|
||||||
rr.SignerName = name
|
rr.SignerName = name
|
||||||
|
|
||||||
s, e := endingToString(c, "bad RRSIG Signature")
|
s, e4 := endingToString(c, "bad RRSIG Signature")
|
||||||
if e != nil {
|
if e4 != nil {
|
||||||
return e
|
return e4
|
||||||
}
|
}
|
||||||
rr.Signature = s
|
rr.Signature = s
|
||||||
|
|
||||||
|
@ -985,21 +1019,21 @@ func (rr *NSEC3) parse(c *zlexer, o string) *ParseError {
|
||||||
rr.Hash = uint8(i)
|
rr.Hash = uint8(i)
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
i, e = strconv.ParseUint(l.token, 10, 8)
|
i, e1 := strconv.ParseUint(l.token, 10, 8)
|
||||||
if e != nil || l.err {
|
if e1 != nil || l.err {
|
||||||
return &ParseError{"", "bad NSEC3 Flags", l}
|
return &ParseError{"", "bad NSEC3 Flags", l}
|
||||||
}
|
}
|
||||||
rr.Flags = uint8(i)
|
rr.Flags = uint8(i)
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
i, e = strconv.ParseUint(l.token, 10, 16)
|
i, e2 := strconv.ParseUint(l.token, 10, 16)
|
||||||
if e != nil || l.err {
|
if e2 != nil || l.err {
|
||||||
return &ParseError{"", "bad NSEC3 Iterations", l}
|
return &ParseError{"", "bad NSEC3 Iterations", l}
|
||||||
}
|
}
|
||||||
rr.Iterations = uint16(i)
|
rr.Iterations = uint16(i)
|
||||||
c.Next()
|
c.Next()
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
if len(l.token) == 0 || l.err {
|
if l.token == "" || l.err {
|
||||||
return &ParseError{"", "bad NSEC3 Salt", l}
|
return &ParseError{"", "bad NSEC3 Salt", l}
|
||||||
}
|
}
|
||||||
if l.token != "-" {
|
if l.token != "-" {
|
||||||
|
@ -1009,7 +1043,7 @@ func (rr *NSEC3) parse(c *zlexer, o string) *ParseError {
|
||||||
|
|
||||||
c.Next()
|
c.Next()
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
if len(l.token) == 0 || l.err {
|
if l.token == "" || l.err {
|
||||||
return &ParseError{"", "bad NSEC3 NextDomain", l}
|
return &ParseError{"", "bad NSEC3 NextDomain", l}
|
||||||
}
|
}
|
||||||
rr.HashLength = 20 // Fix for NSEC3 (sha1 160 bits)
|
rr.HashLength = 20 // Fix for NSEC3 (sha1 160 bits)
|
||||||
|
@ -1050,22 +1084,22 @@ func (rr *NSEC3PARAM) parse(c *zlexer, o string) *ParseError {
|
||||||
rr.Hash = uint8(i)
|
rr.Hash = uint8(i)
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
i, e = strconv.ParseUint(l.token, 10, 8)
|
i, e1 := strconv.ParseUint(l.token, 10, 8)
|
||||||
if e != nil || l.err {
|
if e1 != nil || l.err {
|
||||||
return &ParseError{"", "bad NSEC3PARAM Flags", l}
|
return &ParseError{"", "bad NSEC3PARAM Flags", l}
|
||||||
}
|
}
|
||||||
rr.Flags = uint8(i)
|
rr.Flags = uint8(i)
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
i, e = strconv.ParseUint(l.token, 10, 16)
|
i, e2 := strconv.ParseUint(l.token, 10, 16)
|
||||||
if e != nil || l.err {
|
if e2 != nil || l.err {
|
||||||
return &ParseError{"", "bad NSEC3PARAM Iterations", l}
|
return &ParseError{"", "bad NSEC3PARAM Iterations", l}
|
||||||
}
|
}
|
||||||
rr.Iterations = uint16(i)
|
rr.Iterations = uint16(i)
|
||||||
c.Next()
|
c.Next()
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
if l.token != "-" {
|
if l.token != "-" {
|
||||||
rr.SaltLength = uint8(len(l.token))
|
rr.SaltLength = uint8(len(l.token) / 2)
|
||||||
rr.Salt = l.token
|
rr.Salt = l.token
|
||||||
}
|
}
|
||||||
return slurpRemainder(c)
|
return slurpRemainder(c)
|
||||||
|
@ -1132,15 +1166,15 @@ func (rr *SSHFP) parse(c *zlexer, o string) *ParseError {
|
||||||
rr.Algorithm = uint8(i)
|
rr.Algorithm = uint8(i)
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
i, e = strconv.ParseUint(l.token, 10, 8)
|
i, e1 := strconv.ParseUint(l.token, 10, 8)
|
||||||
if e != nil || l.err {
|
if e1 != nil || l.err {
|
||||||
return &ParseError{"", "bad SSHFP Type", l}
|
return &ParseError{"", "bad SSHFP Type", l}
|
||||||
}
|
}
|
||||||
rr.Type = uint8(i)
|
rr.Type = uint8(i)
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
s, e1 := endingToString(c, "bad SSHFP Fingerprint")
|
s, e2 := endingToString(c, "bad SSHFP Fingerprint")
|
||||||
if e1 != nil {
|
if e2 != nil {
|
||||||
return e1
|
return e2
|
||||||
}
|
}
|
||||||
rr.FingerPrint = s
|
rr.FingerPrint = s
|
||||||
return nil
|
return nil
|
||||||
|
@ -1155,37 +1189,32 @@ func (rr *DNSKEY) parseDNSKEY(c *zlexer, o, typ string) *ParseError {
|
||||||
rr.Flags = uint16(i)
|
rr.Flags = uint16(i)
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next() // zString
|
l, _ = c.Next() // zString
|
||||||
i, e = strconv.ParseUint(l.token, 10, 8)
|
i, e1 := strconv.ParseUint(l.token, 10, 8)
|
||||||
if e != nil || l.err {
|
if e1 != nil || l.err {
|
||||||
return &ParseError{"", "bad " + typ + " Protocol", l}
|
return &ParseError{"", "bad " + typ + " Protocol", l}
|
||||||
}
|
}
|
||||||
rr.Protocol = uint8(i)
|
rr.Protocol = uint8(i)
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next() // zString
|
l, _ = c.Next() // zString
|
||||||
i, e = strconv.ParseUint(l.token, 10, 8)
|
i, e2 := strconv.ParseUint(l.token, 10, 8)
|
||||||
if e != nil || l.err {
|
if e2 != nil || l.err {
|
||||||
return &ParseError{"", "bad " + typ + " Algorithm", l}
|
return &ParseError{"", "bad " + typ + " Algorithm", l}
|
||||||
}
|
}
|
||||||
rr.Algorithm = uint8(i)
|
rr.Algorithm = uint8(i)
|
||||||
s, e1 := endingToString(c, "bad "+typ+" PublicKey")
|
s, e3 := endingToString(c, "bad "+typ+" PublicKey")
|
||||||
if e1 != nil {
|
if e3 != nil {
|
||||||
return e1
|
return e3
|
||||||
}
|
}
|
||||||
rr.PublicKey = s
|
rr.PublicKey = s
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rr *DNSKEY) parse(c *zlexer, o string) *ParseError {
|
func (rr *DNSKEY) parse(c *zlexer, o string) *ParseError { return rr.parseDNSKEY(c, o, "DNSKEY") }
|
||||||
return rr.parseDNSKEY(c, o, "DNSKEY")
|
func (rr *KEY) parse(c *zlexer, o string) *ParseError { return rr.parseDNSKEY(c, o, "KEY") }
|
||||||
}
|
func (rr *CDNSKEY) parse(c *zlexer, o string) *ParseError { return rr.parseDNSKEY(c, o, "CDNSKEY") }
|
||||||
|
func (rr *DS) parse(c *zlexer, o string) *ParseError { return rr.parseDS(c, o, "DS") }
|
||||||
func (rr *KEY) parse(c *zlexer, o string) *ParseError {
|
func (rr *DLV) parse(c *zlexer, o string) *ParseError { return rr.parseDS(c, o, "DLV") }
|
||||||
return rr.parseDNSKEY(c, o, "KEY")
|
func (rr *CDS) parse(c *zlexer, o string) *ParseError { return rr.parseDS(c, o, "CDS") }
|
||||||
}
|
|
||||||
|
|
||||||
func (rr *CDNSKEY) parse(c *zlexer, o string) *ParseError {
|
|
||||||
return rr.parseDNSKEY(c, o, "CDNSKEY")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rr *RKEY) parse(c *zlexer, o string) *ParseError {
|
func (rr *RKEY) parse(c *zlexer, o string) *ParseError {
|
||||||
l, _ := c.Next()
|
l, _ := c.Next()
|
||||||
|
@ -1196,21 +1225,21 @@ func (rr *RKEY) parse(c *zlexer, o string) *ParseError {
|
||||||
rr.Flags = uint16(i)
|
rr.Flags = uint16(i)
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next() // zString
|
l, _ = c.Next() // zString
|
||||||
i, e = strconv.ParseUint(l.token, 10, 8)
|
i, e1 := strconv.ParseUint(l.token, 10, 8)
|
||||||
if e != nil || l.err {
|
if e1 != nil || l.err {
|
||||||
return &ParseError{"", "bad RKEY Protocol", l}
|
return &ParseError{"", "bad RKEY Protocol", l}
|
||||||
}
|
}
|
||||||
rr.Protocol = uint8(i)
|
rr.Protocol = uint8(i)
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next() // zString
|
l, _ = c.Next() // zString
|
||||||
i, e = strconv.ParseUint(l.token, 10, 8)
|
i, e2 := strconv.ParseUint(l.token, 10, 8)
|
||||||
if e != nil || l.err {
|
if e2 != nil || l.err {
|
||||||
return &ParseError{"", "bad RKEY Algorithm", l}
|
return &ParseError{"", "bad RKEY Algorithm", l}
|
||||||
}
|
}
|
||||||
rr.Algorithm = uint8(i)
|
rr.Algorithm = uint8(i)
|
||||||
s, e1 := endingToString(c, "bad RKEY PublicKey")
|
s, e3 := endingToString(c, "bad RKEY PublicKey")
|
||||||
if e1 != nil {
|
if e3 != nil {
|
||||||
return e1
|
return e3
|
||||||
}
|
}
|
||||||
rr.PublicKey = s
|
rr.PublicKey = s
|
||||||
return nil
|
return nil
|
||||||
|
@ -1243,15 +1272,15 @@ func (rr *GPOS) parse(c *zlexer, o string) *ParseError {
|
||||||
rr.Longitude = l.token
|
rr.Longitude = l.token
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
_, e = strconv.ParseFloat(l.token, 64)
|
_, e1 := strconv.ParseFloat(l.token, 64)
|
||||||
if e != nil || l.err {
|
if e1 != nil || l.err {
|
||||||
return &ParseError{"", "bad GPOS Latitude", l}
|
return &ParseError{"", "bad GPOS Latitude", l}
|
||||||
}
|
}
|
||||||
rr.Latitude = l.token
|
rr.Latitude = l.token
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
_, e = strconv.ParseFloat(l.token, 64)
|
_, e2 := strconv.ParseFloat(l.token, 64)
|
||||||
if e != nil || l.err {
|
if e2 != nil || l.err {
|
||||||
return &ParseError{"", "bad GPOS Altitude", l}
|
return &ParseError{"", "bad GPOS Altitude", l}
|
||||||
}
|
}
|
||||||
rr.Altitude = l.token
|
rr.Altitude = l.token
|
||||||
|
@ -1267,7 +1296,7 @@ func (rr *DS) parseDS(c *zlexer, o, typ string) *ParseError {
|
||||||
rr.KeyTag = uint16(i)
|
rr.KeyTag = uint16(i)
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
if i, e = strconv.ParseUint(l.token, 10, 8); e != nil {
|
if i, err := strconv.ParseUint(l.token, 10, 8); err != nil {
|
||||||
tokenUpper := strings.ToUpper(l.token)
|
tokenUpper := strings.ToUpper(l.token)
|
||||||
i, ok := StringToAlgorithm[tokenUpper]
|
i, ok := StringToAlgorithm[tokenUpper]
|
||||||
if !ok || l.err {
|
if !ok || l.err {
|
||||||
|
@ -1279,31 +1308,19 @@ func (rr *DS) parseDS(c *zlexer, o, typ string) *ParseError {
|
||||||
}
|
}
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
i, e = strconv.ParseUint(l.token, 10, 8)
|
i, e1 := strconv.ParseUint(l.token, 10, 8)
|
||||||
if e != nil || l.err {
|
if e1 != nil || l.err {
|
||||||
return &ParseError{"", "bad " + typ + " DigestType", l}
|
return &ParseError{"", "bad " + typ + " DigestType", l}
|
||||||
}
|
}
|
||||||
rr.DigestType = uint8(i)
|
rr.DigestType = uint8(i)
|
||||||
s, e1 := endingToString(c, "bad "+typ+" Digest")
|
s, e2 := endingToString(c, "bad "+typ+" Digest")
|
||||||
if e1 != nil {
|
if e2 != nil {
|
||||||
return e1
|
return e2
|
||||||
}
|
}
|
||||||
rr.Digest = s
|
rr.Digest = s
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rr *DS) parse(c *zlexer, o string) *ParseError {
|
|
||||||
return rr.parseDS(c, o, "DS")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rr *DLV) parse(c *zlexer, o string) *ParseError {
|
|
||||||
return rr.parseDS(c, o, "DLV")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rr *CDS) parse(c *zlexer, o string) *ParseError {
|
|
||||||
return rr.parseDS(c, o, "CDS")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rr *TA) parse(c *zlexer, o string) *ParseError {
|
func (rr *TA) parse(c *zlexer, o string) *ParseError {
|
||||||
l, _ := c.Next()
|
l, _ := c.Next()
|
||||||
i, e := strconv.ParseUint(l.token, 10, 16)
|
i, e := strconv.ParseUint(l.token, 10, 16)
|
||||||
|
@ -1313,7 +1330,7 @@ func (rr *TA) parse(c *zlexer, o string) *ParseError {
|
||||||
rr.KeyTag = uint16(i)
|
rr.KeyTag = uint16(i)
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
if i, e := strconv.ParseUint(l.token, 10, 8); e != nil {
|
if i, err := strconv.ParseUint(l.token, 10, 8); err != nil {
|
||||||
tokenUpper := strings.ToUpper(l.token)
|
tokenUpper := strings.ToUpper(l.token)
|
||||||
i, ok := StringToAlgorithm[tokenUpper]
|
i, ok := StringToAlgorithm[tokenUpper]
|
||||||
if !ok || l.err {
|
if !ok || l.err {
|
||||||
|
@ -1325,14 +1342,14 @@ func (rr *TA) parse(c *zlexer, o string) *ParseError {
|
||||||
}
|
}
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
i, e = strconv.ParseUint(l.token, 10, 8)
|
i, e1 := strconv.ParseUint(l.token, 10, 8)
|
||||||
if e != nil || l.err {
|
if e1 != nil || l.err {
|
||||||
return &ParseError{"", "bad TA DigestType", l}
|
return &ParseError{"", "bad TA DigestType", l}
|
||||||
}
|
}
|
||||||
rr.DigestType = uint8(i)
|
rr.DigestType = uint8(i)
|
||||||
s, err := endingToString(c, "bad TA Digest")
|
s, e2 := endingToString(c, "bad TA Digest")
|
||||||
if err != nil {
|
if e2 != nil {
|
||||||
return err
|
return e2
|
||||||
}
|
}
|
||||||
rr.Digest = s
|
rr.Digest = s
|
||||||
return nil
|
return nil
|
||||||
|
@ -1347,22 +1364,22 @@ func (rr *TLSA) parse(c *zlexer, o string) *ParseError {
|
||||||
rr.Usage = uint8(i)
|
rr.Usage = uint8(i)
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
i, e = strconv.ParseUint(l.token, 10, 8)
|
i, e1 := strconv.ParseUint(l.token, 10, 8)
|
||||||
if e != nil || l.err {
|
if e1 != nil || l.err {
|
||||||
return &ParseError{"", "bad TLSA Selector", l}
|
return &ParseError{"", "bad TLSA Selector", l}
|
||||||
}
|
}
|
||||||
rr.Selector = uint8(i)
|
rr.Selector = uint8(i)
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
i, e = strconv.ParseUint(l.token, 10, 8)
|
i, e2 := strconv.ParseUint(l.token, 10, 8)
|
||||||
if e != nil || l.err {
|
if e2 != nil || l.err {
|
||||||
return &ParseError{"", "bad TLSA MatchingType", l}
|
return &ParseError{"", "bad TLSA MatchingType", l}
|
||||||
}
|
}
|
||||||
rr.MatchingType = uint8(i)
|
rr.MatchingType = uint8(i)
|
||||||
// So this needs be e2 (i.e. different than e), because...??t
|
// So this needs be e2 (i.e. different than e), because...??t
|
||||||
s, e2 := endingToString(c, "bad TLSA Certificate")
|
s, e3 := endingToString(c, "bad TLSA Certificate")
|
||||||
if e2 != nil {
|
if e3 != nil {
|
||||||
return e2
|
return e3
|
||||||
}
|
}
|
||||||
rr.Certificate = s
|
rr.Certificate = s
|
||||||
return nil
|
return nil
|
||||||
|
@ -1377,22 +1394,22 @@ func (rr *SMIMEA) parse(c *zlexer, o string) *ParseError {
|
||||||
rr.Usage = uint8(i)
|
rr.Usage = uint8(i)
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
i, e = strconv.ParseUint(l.token, 10, 8)
|
i, e1 := strconv.ParseUint(l.token, 10, 8)
|
||||||
if e != nil || l.err {
|
if e1 != nil || l.err {
|
||||||
return &ParseError{"", "bad SMIMEA Selector", l}
|
return &ParseError{"", "bad SMIMEA Selector", l}
|
||||||
}
|
}
|
||||||
rr.Selector = uint8(i)
|
rr.Selector = uint8(i)
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
i, e = strconv.ParseUint(l.token, 10, 8)
|
i, e2 := strconv.ParseUint(l.token, 10, 8)
|
||||||
if e != nil || l.err {
|
if e2 != nil || l.err {
|
||||||
return &ParseError{"", "bad SMIMEA MatchingType", l}
|
return &ParseError{"", "bad SMIMEA MatchingType", l}
|
||||||
}
|
}
|
||||||
rr.MatchingType = uint8(i)
|
rr.MatchingType = uint8(i)
|
||||||
// So this needs be e2 (i.e. different than e), because...??t
|
// So this needs be e2 (i.e. different than e), because...??t
|
||||||
s, e2 := endingToString(c, "bad SMIMEA Certificate")
|
s, e3 := endingToString(c, "bad SMIMEA Certificate")
|
||||||
if e2 != nil {
|
if e3 != nil {
|
||||||
return e2
|
return e3
|
||||||
}
|
}
|
||||||
rr.Certificate = s
|
rr.Certificate = s
|
||||||
return nil
|
return nil
|
||||||
|
@ -1406,7 +1423,7 @@ func (rr *RFC3597) parse(c *zlexer, o string) *ParseError {
|
||||||
|
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
rdlength, e := strconv.Atoi(l.token)
|
rdlength, e := strconv.ParseUint(l.token, 10, 16)
|
||||||
if e != nil || l.err {
|
if e != nil || l.err {
|
||||||
return &ParseError{"", "bad RFC3597 Rdata ", l}
|
return &ParseError{"", "bad RFC3597 Rdata ", l}
|
||||||
}
|
}
|
||||||
|
@ -1415,7 +1432,7 @@ func (rr *RFC3597) parse(c *zlexer, o string) *ParseError {
|
||||||
if e1 != nil {
|
if e1 != nil {
|
||||||
return e1
|
return e1
|
||||||
}
|
}
|
||||||
if rdlength*2 != len(s) {
|
if int(rdlength)*2 != len(s) {
|
||||||
return &ParseError{"", "bad RFC3597 Rdata", l}
|
return &ParseError{"", "bad RFC3597 Rdata", l}
|
||||||
}
|
}
|
||||||
rr.Rdata = s
|
rr.Rdata = s
|
||||||
|
@ -1469,16 +1486,16 @@ func (rr *URI) parse(c *zlexer, o string) *ParseError {
|
||||||
rr.Priority = uint16(i)
|
rr.Priority = uint16(i)
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
i, e = strconv.ParseUint(l.token, 10, 16)
|
i, e1 := strconv.ParseUint(l.token, 10, 16)
|
||||||
if e != nil || l.err {
|
if e1 != nil || l.err {
|
||||||
return &ParseError{"", "bad URI Weight", l}
|
return &ParseError{"", "bad URI Weight", l}
|
||||||
}
|
}
|
||||||
rr.Weight = uint16(i)
|
rr.Weight = uint16(i)
|
||||||
|
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
s, err := endingToTxtSlice(c, "bad URI Target")
|
s, e2 := endingToTxtSlice(c, "bad URI Target")
|
||||||
if err != nil {
|
if e2 != nil {
|
||||||
return err
|
return e2
|
||||||
}
|
}
|
||||||
if len(s) != 1 {
|
if len(s) != 1 {
|
||||||
return &ParseError{"", "bad URI Target", l}
|
return &ParseError{"", "bad URI Target", l}
|
||||||
|
@ -1506,9 +1523,9 @@ func (rr *NID) parse(c *zlexer, o string) *ParseError {
|
||||||
rr.Preference = uint16(i)
|
rr.Preference = uint16(i)
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next() // zString
|
l, _ = c.Next() // zString
|
||||||
u, err := stringToNodeID(l)
|
u, e1 := stringToNodeID(l)
|
||||||
if err != nil || l.err {
|
if e1 != nil || l.err {
|
||||||
return err
|
return e1
|
||||||
}
|
}
|
||||||
rr.NodeID = u
|
rr.NodeID = u
|
||||||
return slurpRemainder(c)
|
return slurpRemainder(c)
|
||||||
|
@ -1546,7 +1563,6 @@ func (rr *LP) parse(c *zlexer, o string) *ParseError {
|
||||||
return &ParseError{"", "bad LP Fqdn", l}
|
return &ParseError{"", "bad LP Fqdn", l}
|
||||||
}
|
}
|
||||||
rr.Fqdn = name
|
rr.Fqdn = name
|
||||||
|
|
||||||
return slurpRemainder(c)
|
return slurpRemainder(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1559,9 +1575,9 @@ func (rr *L64) parse(c *zlexer, o string) *ParseError {
|
||||||
rr.Preference = uint16(i)
|
rr.Preference = uint16(i)
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next() // zString
|
l, _ = c.Next() // zString
|
||||||
u, err := stringToNodeID(l)
|
u, e1 := stringToNodeID(l)
|
||||||
if err != nil || l.err {
|
if e1 != nil || l.err {
|
||||||
return err
|
return e1
|
||||||
}
|
}
|
||||||
rr.Locator64 = u
|
rr.Locator64 = u
|
||||||
return slurpRemainder(c)
|
return slurpRemainder(c)
|
||||||
|
@ -1624,14 +1640,13 @@ func (rr *PX) parse(c *zlexer, o string) *ParseError {
|
||||||
return &ParseError{"", "bad PX Mapx400", l}
|
return &ParseError{"", "bad PX Mapx400", l}
|
||||||
}
|
}
|
||||||
rr.Mapx400 = mapx400
|
rr.Mapx400 = mapx400
|
||||||
|
|
||||||
return slurpRemainder(c)
|
return slurpRemainder(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rr *CAA) parse(c *zlexer, o string) *ParseError {
|
func (rr *CAA) parse(c *zlexer, o string) *ParseError {
|
||||||
l, _ := c.Next()
|
l, _ := c.Next()
|
||||||
i, err := strconv.ParseUint(l.token, 10, 8)
|
i, e := strconv.ParseUint(l.token, 10, 8)
|
||||||
if err != nil || l.err {
|
if e != nil || l.err {
|
||||||
return &ParseError{"", "bad CAA Flag", l}
|
return &ParseError{"", "bad CAA Flag", l}
|
||||||
}
|
}
|
||||||
rr.Flag = uint8(i)
|
rr.Flag = uint8(i)
|
||||||
|
@ -1644,9 +1659,9 @@ func (rr *CAA) parse(c *zlexer, o string) *ParseError {
|
||||||
rr.Tag = l.token
|
rr.Tag = l.token
|
||||||
|
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
s, e := endingToTxtSlice(c, "bad CAA Value")
|
s, e1 := endingToTxtSlice(c, "bad CAA Value")
|
||||||
if e != nil {
|
if e1 != nil {
|
||||||
return e
|
return e1
|
||||||
}
|
}
|
||||||
if len(s) != 1 {
|
if len(s) != 1 {
|
||||||
return &ParseError{"", "bad CAA Value", l}
|
return &ParseError{"", "bad CAA Value", l}
|
||||||
|
@ -1667,8 +1682,8 @@ func (rr *TKEY) parse(c *zlexer, o string) *ParseError {
|
||||||
|
|
||||||
// Get the key length and key values
|
// Get the key length and key values
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
i, err := strconv.ParseUint(l.token, 10, 8)
|
i, e := strconv.ParseUint(l.token, 10, 8)
|
||||||
if err != nil || l.err {
|
if e != nil || l.err {
|
||||||
return &ParseError{"", "bad TKEY key length", l}
|
return &ParseError{"", "bad TKEY key length", l}
|
||||||
}
|
}
|
||||||
rr.KeySize = uint16(i)
|
rr.KeySize = uint16(i)
|
||||||
|
@ -1682,8 +1697,8 @@ func (rr *TKEY) parse(c *zlexer, o string) *ParseError {
|
||||||
|
|
||||||
// Get the otherdata length and string data
|
// Get the otherdata length and string data
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
i, err = strconv.ParseUint(l.token, 10, 8)
|
i, e1 := strconv.ParseUint(l.token, 10, 8)
|
||||||
if err != nil || l.err {
|
if e1 != nil || l.err {
|
||||||
return &ParseError{"", "bad TKEY otherdata length", l}
|
return &ParseError{"", "bad TKEY otherdata length", l}
|
||||||
}
|
}
|
||||||
rr.OtherLen = uint16(i)
|
rr.OtherLen = uint16(i)
|
||||||
|
@ -1693,7 +1708,6 @@ func (rr *TKEY) parse(c *zlexer, o string) *ParseError {
|
||||||
return &ParseError{"", "bad TKEY otherday", l}
|
return &ParseError{"", "bad TKEY otherday", l}
|
||||||
}
|
}
|
||||||
rr.OtherData = l.token
|
rr.OtherData = l.token
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1727,9 +1741,9 @@ func (rr *APL) parse(c *zlexer, o string) *ParseError {
|
||||||
family = family[1:]
|
family = family[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
afi, err := strconv.ParseUint(family, 10, 16)
|
afi, e := strconv.ParseUint(family, 10, 16)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return &ParseError{"", "failed to parse APL family: " + err.Error(), l}
|
return &ParseError{"", "failed to parse APL family: " + e.Error(), l}
|
||||||
}
|
}
|
||||||
var addrLen int
|
var addrLen int
|
||||||
switch afi {
|
switch afi {
|
||||||
|
@ -1741,9 +1755,9 @@ func (rr *APL) parse(c *zlexer, o string) *ParseError {
|
||||||
return &ParseError{"", "unrecognized APL family", l}
|
return &ParseError{"", "unrecognized APL family", l}
|
||||||
}
|
}
|
||||||
|
|
||||||
ip, subnet, err := net.ParseCIDR(cidr)
|
ip, subnet, e1 := net.ParseCIDR(cidr)
|
||||||
if err != nil {
|
if e1 != nil {
|
||||||
return &ParseError{"", "failed to parse APL address: " + err.Error(), l}
|
return &ParseError{"", "failed to parse APL address: " + e1.Error(), l}
|
||||||
}
|
}
|
||||||
if !ip.Equal(subnet.IP) {
|
if !ip.Equal(subnet.IP) {
|
||||||
return &ParseError{"", "extra bits in APL address", l}
|
return &ParseError{"", "extra bits in APL address", l}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -36,7 +35,7 @@ func (mux *ServeMux) match(q string, t uint16) Handler {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
q = strings.ToLower(q)
|
q = CanonicalName(q)
|
||||||
|
|
||||||
var handler Handler
|
var handler Handler
|
||||||
for off, end := 0, false; !end; off, end = NextLabel(q, off) {
|
for off, end := 0, false; !end; off, end = NextLabel(q, off) {
|
||||||
|
@ -66,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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,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
|
||||||
|
@ -103,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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,13 +72,22 @@ type response struct {
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
@ -139,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)
|
||||||
}
|
}
|
||||||
|
@ -153,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.
|
||||||
|
@ -294,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
|
||||||
|
@ -317,24 +345,22 @@ func (srv *Server) ActivateAndServe() error {
|
||||||
|
|
||||||
srv.init()
|
srv.init()
|
||||||
|
|
||||||
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"}
|
||||||
}
|
}
|
||||||
|
@ -438,18 +464,24 @@ func (srv *Server) serveTCP(l net.Listener) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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()
|
||||||
|
@ -459,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
|
||||||
|
@ -476,7 +518,7 @@ func (srv *Server) serveUDP(l *net.UDPConn) error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go srv.serveUDPPacket(&wg, m, l, s)
|
go srv.serveUDPPacket(&wg, m, l, sUDP, sPC)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -538,8 +580,8 @@ func (srv *Server) serveTCPConn(wg *sync.WaitGroup, rw net.Conn) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serve a new UDP request.
|
// Serve a new UDP request.
|
||||||
func (srv *Server) serveUDPPacket(wg *sync.WaitGroup, m []byte, u *net.UDPConn, s *SessionUDP) {
|
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: s}
|
w := &response{tsigSecret: srv.TsigSecret, udp: u, udpSession: udpSession, pcSession: pcSession}
|
||||||
if srv.DecorateWriter != nil {
|
if srv.DecorateWriter != nil {
|
||||||
w.writer = srv.DecorateWriter(w)
|
w.writer = srv.DecorateWriter(w)
|
||||||
} else {
|
} else {
|
||||||
|
@ -651,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 {
|
||||||
|
@ -684,17 +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 {
|
||||||
|
return WriteToSessionUDP(u, m, w.udpSession)
|
||||||
|
}
|
||||||
|
return w.udp.WriteTo(m, w.pcSession)
|
||||||
case w.tcp != nil:
|
case w.tcp != nil:
|
||||||
if len(m) > MaxMsgSize {
|
if len(m) > MaxMsgSize {
|
||||||
return 0, &Error{err: "message too large"}
|
return 0, &Error{err: "message too large"}
|
||||||
}
|
}
|
||||||
|
|
||||||
l := make([]byte, 2)
|
msg := make([]byte, 2+len(m))
|
||||||
binary.BigEndian.PutUint16(l, uint16(len(m)))
|
binary.BigEndian.PutUint16(msg, uint16(len(m)))
|
||||||
|
copy(msg[2:], m)
|
||||||
n, err := (&net.Buffers{l, m}).WriteTo(w.tcp)
|
return w.tcp.Write(msg)
|
||||||
return int(n), err
|
|
||||||
default:
|
default:
|
||||||
panic("dns: internal error: udp and tcp both nil")
|
panic("dns: internal error: udp and tcp both nil")
|
||||||
}
|
}
|
||||||
|
@ -717,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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,17 +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 := new(big.Int).SetBytes(sig[:len(sig)/2])
|
|
||||||
s := new(big.Int).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 {
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
@ -54,8 +106,8 @@ func (rr *TSIG) String() string {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rr *TSIG) parse(c *zlexer, origin 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.
|
||||||
|
|
|
@ -81,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
|
||||||
|
@ -148,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
|
||||||
|
@ -165,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.
|
||||||
|
@ -209,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
|
||||||
|
@ -240,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 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.
|
||||||
|
@ -255,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 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.
|
||||||
|
@ -442,45 +456,38 @@ func sprintName(s string) string {
|
||||||
var dst strings.Builder
|
var dst strings.Builder
|
||||||
|
|
||||||
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] == '.' {
|
||||||
if dst.Len() != 0 {
|
if dst.Len() != 0 {
|
||||||
dst.WriteString(s[i : i+2])
|
dst.WriteByte('.')
|
||||||
}
|
}
|
||||||
i += 2
|
i++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
b, n := nextByte(s, i)
|
b, n := nextByte(s, i)
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
i++
|
// Drop "dangling" incomplete escapes.
|
||||||
continue
|
if dst.Len() == 0 {
|
||||||
|
return s[:i]
|
||||||
}
|
}
|
||||||
if b == '.' {
|
break
|
||||||
if dst.Len() != 0 {
|
|
||||||
dst.WriteByte('.')
|
|
||||||
}
|
}
|
||||||
i += n
|
if isDomainNameLabelSpecial(b) {
|
||||||
continue
|
|
||||||
}
|
|
||||||
switch b {
|
|
||||||
case ' ', '\'', '@', ';', '(', ')', '"', '\\': // additional chars to escape
|
|
||||||
if dst.Len() == 0 {
|
if dst.Len() == 0 {
|
||||||
dst.Grow(len(s) * 2)
|
dst.Grow(len(s) * 2)
|
||||||
dst.WriteString(s[:i])
|
dst.WriteString(s[:i])
|
||||||
}
|
}
|
||||||
dst.WriteByte('\\')
|
dst.WriteByte('\\')
|
||||||
dst.WriteByte(b)
|
dst.WriteByte(b)
|
||||||
default:
|
} else if b < ' ' || b > '~' { // unprintable, use \DDD
|
||||||
if ' ' <= b && b <= '~' {
|
|
||||||
if dst.Len() != 0 {
|
|
||||||
dst.WriteByte(b)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if dst.Len() == 0 {
|
if dst.Len() == 0 {
|
||||||
dst.Grow(len(s) * 2)
|
dst.Grow(len(s) * 2)
|
||||||
dst.WriteString(s[:i])
|
dst.WriteString(s[:i])
|
||||||
}
|
}
|
||||||
dst.WriteString(escapeByte(b))
|
dst.WriteString(escapeByte(b))
|
||||||
|
} else {
|
||||||
|
if dst.Len() != 0 {
|
||||||
|
dst.WriteByte(b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i += n
|
i += n
|
||||||
|
@ -503,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
|
||||||
}
|
}
|
||||||
|
@ -587,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
|
||||||
|
@ -760,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 {
|
||||||
|
@ -1118,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)
|
||||||
|
@ -1279,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)
|
||||||
}
|
}
|
||||||
|
@ -1355,6 +1370,23 @@ func (rr *CSYNC) len(off int, compression map[string]struct{}) int {
|
||||||
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.
|
// APL RR. See RFC 3123.
|
||||||
type APL struct {
|
type APL struct {
|
||||||
Hdr RR_Header
|
Hdr RR_Header
|
||||||
|
@ -1381,13 +1413,13 @@ func (rr *APL) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// str returns presentation form of the APL prefix.
|
// str returns presentation form of the APL prefix.
|
||||||
func (p *APLPrefix) str() string {
|
func (a *APLPrefix) str() string {
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
if p.Negation {
|
if a.Negation {
|
||||||
sb.WriteByte('!')
|
sb.WriteByte('!')
|
||||||
}
|
}
|
||||||
|
|
||||||
switch len(p.Network.IP) {
|
switch len(a.Network.IP) {
|
||||||
case net.IPv4len:
|
case net.IPv4len:
|
||||||
sb.WriteByte('1')
|
sb.WriteByte('1')
|
||||||
case net.IPv6len:
|
case net.IPv6len:
|
||||||
|
@ -1396,20 +1428,20 @@ func (p *APLPrefix) str() string {
|
||||||
|
|
||||||
sb.WriteByte(':')
|
sb.WriteByte(':')
|
||||||
|
|
||||||
switch len(p.Network.IP) {
|
switch len(a.Network.IP) {
|
||||||
case net.IPv4len:
|
case net.IPv4len:
|
||||||
sb.WriteString(p.Network.IP.String())
|
sb.WriteString(a.Network.IP.String())
|
||||||
case net.IPv6len:
|
case net.IPv6len:
|
||||||
// add prefix for IPv4-mapped IPv6
|
// add prefix for IPv4-mapped IPv6
|
||||||
if v4 := p.Network.IP.To4(); v4 != nil {
|
if v4 := a.Network.IP.To4(); v4 != nil {
|
||||||
sb.WriteString("::ffff:")
|
sb.WriteString("::ffff:")
|
||||||
}
|
}
|
||||||
sb.WriteString(p.Network.IP.String())
|
sb.WriteString(a.Network.IP.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.WriteByte('/')
|
sb.WriteByte('/')
|
||||||
|
|
||||||
prefix, _ := p.Network.Mask.Size()
|
prefix, _ := a.Network.Mask.Size()
|
||||||
sb.WriteString(strconv.Itoa(prefix))
|
sb.WriteString(strconv.Itoa(prefix))
|
||||||
|
|
||||||
return sb.String()
|
return sb.String()
|
||||||
|
@ -1423,17 +1455,17 @@ func (a *APLPrefix) equals(b *APLPrefix) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy returns a copy of the APL prefix.
|
// copy returns a copy of the APL prefix.
|
||||||
func (p *APLPrefix) copy() APLPrefix {
|
func (a *APLPrefix) copy() APLPrefix {
|
||||||
return APLPrefix{
|
return APLPrefix{
|
||||||
Negation: p.Negation,
|
Negation: a.Negation,
|
||||||
Network: copyNet(p.Network),
|
Network: copyNet(a.Network),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// len returns size of the prefix in wire format.
|
// len returns size of the prefix in wire format.
|
||||||
func (p *APLPrefix) len() int {
|
func (a *APLPrefix) len() int {
|
||||||
// 4-byte header and the network address prefix (see Section 4 of RFC 3123)
|
// 4-byte header and the network address prefix (see Section 4 of RFC 3123)
|
||||||
prefix, _ := p.Network.Mask.Size()
|
prefix, _ := a.Network.Mask.Size()
|
||||||
return 4 + (prefix+7)/8
|
return 4 + (prefix+7)/8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1466,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)
|
||||||
|
|
|
@ -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, 27}
|
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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,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 {
|
||||||
|
@ -172,6 +214,27 @@ 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 {
|
||||||
|
@ -339,6 +402,48 @@ func (r1 *HIP) isDuplicate(_r2 RR) bool {
|
||||||
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 {
|
||||||
|
@ -849,6 +954,42 @@ func (r1 *RT) isDuplicate(_r2 RR) bool {
|
||||||
return true
|
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 true
|
||||||
|
}
|
||||||
|
|
||||||
func (r1 *SMIMEA) isDuplicate(_r2 RR) bool {
|
func (r1 *SMIMEA) isDuplicate(_r2 RR) bool {
|
||||||
r2, ok := _r2.(*SMIMEA)
|
r2, ok := _r2.(*SMIMEA)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -956,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 {
|
||||||
|
@ -1155,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
|
||||||
|
}
|
||||||
|
|
|
@ -316,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 {
|
||||||
|
@ -906,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 {
|
||||||
|
@ -1086,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) {
|
||||||
|
@ -1559,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
|
||||||
|
@ -2461,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
|
||||||
|
@ -2739,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
|
||||||
|
}
|
||||||
|
|
|
@ -33,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) },
|
||||||
|
@ -70,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) },
|
||||||
|
@ -80,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.
|
||||||
|
@ -110,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",
|
||||||
|
@ -153,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",
|
||||||
|
@ -164,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",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,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 }
|
||||||
|
@ -229,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 }
|
||||||
|
@ -239,6 +247,7 @@ 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 {
|
||||||
|
@ -592,6 +601,15 @@ func (rr *SSHFP) len(off int, compression map[string]struct{}) int {
|
||||||
l += len(rr.FingerPrint) / 2
|
l += len(rr.FingerPrint) / 2
|
||||||
return l
|
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
|
||||||
|
}
|
||||||
func (rr *TA) len(off int, compression map[string]struct{}) int {
|
func (rr *TA) len(off int, compression map[string]struct{}) int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len(off, compression)
|
||||||
l += 2 // KeyTag
|
l += 2 // KeyTag
|
||||||
|
@ -669,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 {
|
||||||
|
@ -685,8 +711,8 @@ func (rr *ANY) copy() RR {
|
||||||
}
|
}
|
||||||
func (rr *APL) copy() RR {
|
func (rr *APL) copy() RR {
|
||||||
Prefixes := make([]APLPrefix, len(rr.Prefixes))
|
Prefixes := make([]APLPrefix, len(rr.Prefixes))
|
||||||
for i := range rr.Prefixes {
|
for i, e := range rr.Prefixes {
|
||||||
Prefixes[i] = rr.Prefixes[i].copy()
|
Prefixes[i] = e.copy()
|
||||||
}
|
}
|
||||||
return &APL{rr.Hdr, Prefixes}
|
return &APL{rr.Hdr, Prefixes}
|
||||||
}
|
}
|
||||||
|
@ -698,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}
|
||||||
}
|
}
|
||||||
|
@ -712,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}
|
||||||
}
|
}
|
||||||
|
@ -744,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}
|
||||||
}
|
}
|
||||||
|
@ -847,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}
|
||||||
}
|
}
|
||||||
|
@ -864,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}
|
||||||
}
|
}
|
||||||
|
@ -896,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}
|
||||||
|
}
|
||||||
|
|
|
@ -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.
|
|
|
@ -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.
|
|
|
@ -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.
|
|
|
@ -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.
|
|
|
@ -1,222 +0,0 @@
|
||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// In Go 1.13, the ed25519 package was promoted to the standard library as
|
|
||||||
// crypto/ed25519, and this package became a wrapper for the standard library one.
|
|
||||||
//
|
|
||||||
// +build !go1.13
|
|
||||||
|
|
||||||
// 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[:])
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
// Copyright 2019 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build go1.13
|
|
||||||
|
|
||||||
// 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”.
|
|
||||||
//
|
|
||||||
// Beginning with Go 1.13, the functionality of this package was moved to the
|
|
||||||
// standard library as crypto/ed25519. This package only acts as a compatibility
|
|
||||||
// wrapper.
|
|
||||||
package ed25519
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/ed25519"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
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.
|
|
||||||
//
|
|
||||||
// This type is an alias for crypto/ed25519's PublicKey type.
|
|
||||||
// See the crypto/ed25519 package for the methods on this type.
|
|
||||||
type PublicKey = ed25519.PublicKey
|
|
||||||
|
|
||||||
// PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer.
|
|
||||||
//
|
|
||||||
// This type is an alias for crypto/ed25519's PrivateKey type.
|
|
||||||
// See the crypto/ed25519 package for the methods on this type.
|
|
||||||
type PrivateKey = ed25519.PrivateKey
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
return ed25519.GenerateKey(rand)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 {
|
|
||||||
return ed25519.NewKeyFromSeed(seed)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 {
|
|
||||||
return ed25519.Sign(privateKey, message)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 {
|
|
||||||
return ed25519.Verify(publicKey, message, sig)
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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 aix 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
|
||||||
|
|
||||||
|
|
|
@ -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 aix || darwin || dragonfly || freebsd || netbsd || openbsd
|
||||||
// +build aix darwin dragonfly freebsd netbsd openbsd
|
// +build aix darwin dragonfly freebsd netbsd openbsd
|
||||||
|
|
||||||
package socket
|
package socket
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 (arm64 || amd64 || ppc64 || ppc64le || mips64 || mips64le || riscv64 || s390x) && linux
|
||||||
// +build arm64 amd64 ppc64 ppc64le mips64 mips64le riscv64 s390x
|
// +build arm64 amd64 ppc64 ppc64le mips64 mips64le riscv64 s390x
|
||||||
// +build linux
|
// +build linux
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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 !aix,!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 }
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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 aix 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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
//go:build (arm64 || amd64 || ppc64 || ppc64le || mips64 || mips64le || riscv64 || s390x) && (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || zos)
|
||||||
// +build arm64 amd64 ppc64 ppc64le mips64 mips64le riscv64 s390x
|
// +build arm64 amd64 ppc64 ppc64le mips64 mips64le riscv64 s390x
|
||||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd
|
// +build aix darwin dragonfly freebsd linux netbsd openbsd zos
|
||||||
|
|
||||||
package socket
|
package socket
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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 !aix,!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
|
||||||
|
|
||||||
|
|
|
@ -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 !aix && !linux && !netbsd
|
||||||
// +build !aix,!linux,!netbsd
|
// +build !aix,!linux,!netbsd
|
||||||
|
|
||||||
package socket
|
package socket
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
//go:build aix || linux || netbsd
|
||||||
// +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)
|
||||||
|
}
|
||||||
|
|
|
@ -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 aix || darwin || dragonfly || freebsd || netbsd || openbsd
|
||||||
// +build aix darwin dragonfly freebsd netbsd openbsd
|
// +build aix darwin dragonfly freebsd netbsd openbsd
|
||||||
|
|
||||||
package socket
|
package socket
|
||||||
|
|
|
@ -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 aix || darwin || dragonfly || freebsd || netbsd
|
||||||
// +build aix darwin dragonfly freebsd netbsd
|
// +build aix darwin dragonfly freebsd netbsd
|
||||||
|
|
||||||
package socket
|
package socket
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 (arm64 || amd64 || ppc64 || ppc64le || mips64 || mips64le || riscv64 || s390x) && linux
|
||||||
// +build arm64 amd64 ppc64 ppc64le mips64 mips64le riscv64 s390x
|
// +build arm64 amd64 ppc64 ppc64le mips64 mips64le riscv64 s390x
|
||||||
// +build linux
|
// +build linux
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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 !aix,!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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !race
|
||||||
|
// +build !race
|
||||||
|
|
||||||
|
package socket
|
||||||
|
|
||||||
|
func (m *Message) raceRead() {
|
||||||
|
}
|
||||||
|
func (m *Message) raceWrite() {
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build race
|
||||||
|
// +build race
|
||||||
|
|
||||||
|
package socket
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This package reads and writes the Message buffers using a
|
||||||
|
// direct system call, which the race detector can't see.
|
||||||
|
// These functions tell the race detector what is going on during the syscall.
|
||||||
|
|
||||||
|
func (m *Message) raceRead() {
|
||||||
|
for _, b := range m.Buffers {
|
||||||
|
if len(b) > 0 {
|
||||||
|
runtime.RaceReadRange(unsafe.Pointer(&b[0]), len(b))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if b := m.OOB; len(b) > 0 {
|
||||||
|
runtime.RaceReadRange(unsafe.Pointer(&b[0]), len(b))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (m *Message) raceWrite() {
|
||||||
|
for _, b := range m.Buffers {
|
||||||
|
if len(b) > 0 {
|
||||||
|
runtime.RaceWriteRange(unsafe.Pointer(&b[0]), len(b))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if b := m.OOB; len(b) > 0 {
|
||||||
|
runtime.RaceWriteRange(unsafe.Pointer(&b[0]), len(b))
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,18 +17,45 @@ type Conn struct {
|
||||||
c syscall.RawConn
|
c syscall.RawConn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tcpConn is an interface implemented by net.TCPConn.
|
||||||
|
// It can be used for interface assertions to check if a net.Conn is a TCP connection.
|
||||||
|
type tcpConn interface {
|
||||||
|
SyscallConn() (syscall.RawConn, error)
|
||||||
|
SetLinger(int) error
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ tcpConn = (*net.TCPConn)(nil)
|
||||||
|
|
||||||
|
// udpConn is an interface implemented by net.UDPConn.
|
||||||
|
// It can be used for interface assertions to check if a net.Conn is a UDP connection.
|
||||||
|
type udpConn interface {
|
||||||
|
SyscallConn() (syscall.RawConn, error)
|
||||||
|
ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *net.UDPAddr, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ udpConn = (*net.UDPConn)(nil)
|
||||||
|
|
||||||
|
// ipConn is an interface implemented by net.IPConn.
|
||||||
|
// It can be used for interface assertions to check if a net.Conn is an IP connection.
|
||||||
|
type ipConn interface {
|
||||||
|
SyscallConn() (syscall.RawConn, error)
|
||||||
|
ReadMsgIP(b, oob []byte) (n, oobn, flags int, addr *net.IPAddr, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ipConn = (*net.IPConn)(nil)
|
||||||
|
|
||||||
// NewConn returns a new raw connection.
|
// NewConn returns a new raw connection.
|
||||||
func NewConn(c net.Conn) (*Conn, error) {
|
func NewConn(c net.Conn) (*Conn, error) {
|
||||||
var err error
|
var err error
|
||||||
var cc Conn
|
var cc Conn
|
||||||
switch c := c.(type) {
|
switch c := c.(type) {
|
||||||
case *net.TCPConn:
|
case tcpConn:
|
||||||
cc.network = "tcp"
|
cc.network = "tcp"
|
||||||
cc.c, err = c.SyscallConn()
|
cc.c, err = c.SyscallConn()
|
||||||
case *net.UDPConn:
|
case udpConn:
|
||||||
cc.network = "udp"
|
cc.network = "udp"
|
||||||
cc.c, err = c.SyscallConn()
|
cc.c, err = c.SyscallConn()
|
||||||
case *net.IPConn:
|
case ipConn:
|
||||||
cc.network = "ip"
|
cc.network = "ip"
|
||||||
cc.c, err = c.SyscallConn()
|
cc.c, err = c.SyscallConn()
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -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 linux
|
||||||
// +build linux
|
// +build linux
|
||||||
|
|
||||||
package socket
|
package socket
|
||||||
|
@ -9,26 +10,24 @@ package socket
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Conn) recvMsgs(ms []Message, flags int) (int, error) {
|
func (c *Conn) recvMsgs(ms []Message, flags int) (int, error) {
|
||||||
hs := make(mmsghdrs, len(ms))
|
for i := range ms {
|
||||||
|
ms[i].raceWrite()
|
||||||
|
}
|
||||||
|
packer := defaultMmsghdrsPool.Get()
|
||||||
|
defer defaultMmsghdrsPool.Put(packer)
|
||||||
var parseFn func([]byte, string) (net.Addr, error)
|
var parseFn func([]byte, string) (net.Addr, error)
|
||||||
if c.network != "tcp" {
|
if c.network != "tcp" {
|
||||||
parseFn = parseInetAddr
|
parseFn = parseInetAddr
|
||||||
}
|
}
|
||||||
if err := hs.pack(ms, parseFn, nil); err != nil {
|
hs := packer.pack(ms, parseFn, nil)
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
var operr error
|
var operr error
|
||||||
var n int
|
var n int
|
||||||
fn := func(s uintptr) bool {
|
fn := func(s uintptr) bool {
|
||||||
n, operr = recvmmsg(s, hs, flags)
|
n, operr = recvmmsg(s, hs, flags)
|
||||||
if operr == syscall.EAGAIN {
|
return ioComplete(flags, operr)
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
if err := c.c.Read(fn); err != nil {
|
if err := c.c.Read(fn); err != nil {
|
||||||
return n, err
|
return n, err
|
||||||
|
@ -43,22 +42,21 @@ func (c *Conn) recvMsgs(ms []Message, flags int) (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) sendMsgs(ms []Message, flags int) (int, error) {
|
func (c *Conn) sendMsgs(ms []Message, flags int) (int, error) {
|
||||||
hs := make(mmsghdrs, len(ms))
|
for i := range ms {
|
||||||
var marshalFn func(net.Addr) []byte
|
ms[i].raceRead()
|
||||||
|
}
|
||||||
|
packer := defaultMmsghdrsPool.Get()
|
||||||
|
defer defaultMmsghdrsPool.Put(packer)
|
||||||
|
var marshalFn func(net.Addr, []byte) int
|
||||||
if c.network != "tcp" {
|
if c.network != "tcp" {
|
||||||
marshalFn = marshalInetAddr
|
marshalFn = marshalInetAddr
|
||||||
}
|
}
|
||||||
if err := hs.pack(ms, nil, marshalFn); err != nil {
|
hs := packer.pack(ms, nil, marshalFn)
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
var operr error
|
var operr error
|
||||||
var n int
|
var n int
|
||||||
fn := func(s uintptr) bool {
|
fn := func(s uintptr) bool {
|
||||||
n, operr = sendmmsg(s, hs, flags)
|
n, operr = sendmmsg(s, hs, flags)
|
||||||
if operr == syscall.EAGAIN {
|
return ioComplete(flags, operr)
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
if err := c.c.Write(fn); err != nil {
|
if err := c.c.Write(fn); err != nil {
|
||||||
return n, err
|
return n, err
|
||||||
|
|
|
@ -2,16 +2,17 @@
|
||||||
// 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 aix darwin dragonfly freebsd linux netbsd openbsd solaris windows
|
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows || zos
|
||||||
|
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows zos
|
||||||
|
|
||||||
package socket
|
package socket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Conn) recvMsg(m *Message, flags int) error {
|
func (c *Conn) recvMsg(m *Message, flags int) error {
|
||||||
|
m.raceWrite()
|
||||||
var h msghdr
|
var h msghdr
|
||||||
vs := make([]iovec, len(m.Buffers))
|
vs := make([]iovec, len(m.Buffers))
|
||||||
var sa []byte
|
var sa []byte
|
||||||
|
@ -23,10 +24,7 @@ func (c *Conn) recvMsg(m *Message, flags int) error {
|
||||||
var n int
|
var n int
|
||||||
fn := func(s uintptr) bool {
|
fn := func(s uintptr) bool {
|
||||||
n, operr = recvmsg(s, &h, flags)
|
n, operr = recvmsg(s, &h, flags)
|
||||||
if operr == syscall.EAGAIN {
|
return ioComplete(flags, operr)
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
if err := c.c.Read(fn); err != nil {
|
if err := c.c.Read(fn); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -48,21 +46,21 @@ func (c *Conn) recvMsg(m *Message, flags int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) sendMsg(m *Message, flags int) error {
|
func (c *Conn) sendMsg(m *Message, flags int) error {
|
||||||
|
m.raceRead()
|
||||||
var h msghdr
|
var h msghdr
|
||||||
vs := make([]iovec, len(m.Buffers))
|
vs := make([]iovec, len(m.Buffers))
|
||||||
var sa []byte
|
var sa []byte
|
||||||
if m.Addr != nil {
|
if m.Addr != nil {
|
||||||
sa = marshalInetAddr(m.Addr)
|
var a [sizeofSockaddrInet6]byte
|
||||||
|
n := marshalInetAddr(m.Addr, a[:])
|
||||||
|
sa = a[:n]
|
||||||
}
|
}
|
||||||
h.pack(vs, m.Buffers, m.OOB, sa)
|
h.pack(vs, m.Buffers, m.OOB, sa)
|
||||||
var operr error
|
var operr error
|
||||||
var n int
|
var n int
|
||||||
fn := func(s uintptr) bool {
|
fn := func(s uintptr) bool {
|
||||||
n, operr = sendmsg(s, &h, flags)
|
n, operr = sendmsg(s, &h, flags)
|
||||||
if operr == syscall.EAGAIN {
|
return ioComplete(flags, operr)
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
if err := c.c.Write(fn); err != nil {
|
if err := c.c.Write(fn); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -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 !linux
|
||||||
// +build !linux
|
// +build !linux
|
||||||
|
|
||||||
package socket
|
package socket
|
||||||
|
|
|
@ -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 !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows
|
//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos
|
||||||
|
// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos
|
||||||
|
|
||||||
package socket
|
package socket
|
||||||
|
|
||||||
|
|
|
@ -90,17 +90,9 @@ func (o *Option) SetInt(c *Conn, v int) error {
|
||||||
return o.set(c, b)
|
return o.set(c, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func controlHeaderLen() int {
|
|
||||||
return roundup(sizeofCmsghdr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func controlMessageLen(dataLen int) int {
|
|
||||||
return roundup(sizeofCmsghdr) + dataLen
|
|
||||||
}
|
|
||||||
|
|
||||||
// ControlMessageSpace returns the whole length of control message.
|
// ControlMessageSpace returns the whole length of control message.
|
||||||
func ControlMessageSpace(dataLen int) int {
|
func ControlMessageSpace(dataLen int) int {
|
||||||
return roundup(sizeofCmsghdr) + roundup(dataLen)
|
return controlMessageSpace(dataLen)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A ControlMessage represents the head message in a stream of control
|
// A ControlMessage represents the head message in a stream of control
|
||||||
|
|
|
@ -9,13 +9,8 @@ import (
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
// NativeEndian is the machine native endian implementation of ByteOrder.
|
||||||
// NativeEndian is the machine native endian implementation of
|
var NativeEndian binary.ByteOrder
|
||||||
// ByteOrder.
|
|
||||||
NativeEndian binary.ByteOrder
|
|
||||||
|
|
||||||
kernelAlign int
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
i := uint32(1)
|
i := uint32(1)
|
||||||
|
@ -25,9 +20,4 @@ func init() {
|
||||||
} else {
|
} else {
|
||||||
NativeEndian = binary.BigEndian
|
NativeEndian = binary.BigEndian
|
||||||
}
|
}
|
||||||
kernelAlign = probeProtocolStack()
|
|
||||||
}
|
|
||||||
|
|
||||||
func roundup(l int) int {
|
|
||||||
return (l + kernelAlign - 1) &^ (kernelAlign - 1)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 aix || darwin || dragonfly || freebsd || openbsd
|
||||||
// +build aix darwin dragonfly freebsd openbsd
|
// +build aix darwin dragonfly freebsd openbsd
|
||||||
|
|
||||||
package socket
|
package socket
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
// Copyright 2017 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build aix freebsd netbsd openbsd
|
|
||||||
|
|
||||||
package socket
|
|
||||||
|
|
||||||
import (
|
|
||||||
"runtime"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
func probeProtocolStack() int {
|
|
||||||
if (runtime.GOOS == "netbsd" || runtime.GOOS == "openbsd") && runtime.GOARCH == "arm" {
|
|
||||||
return 8
|
|
||||||
}
|
|
||||||
if runtime.GOOS == "aix" {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
var p uintptr
|
|
||||||
return int(unsafe.Sizeof(p))
|
|
||||||
}
|
|
|
@ -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 aix 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
|
||||||
|
|
||||||
|
@ -14,4 +15,7 @@ const (
|
||||||
sysAF_INET6 = unix.AF_INET6
|
sysAF_INET6 = unix.AF_INET6
|
||||||
|
|
||||||
sysSOCK_RAW = unix.SOCK_RAW
|
sysSOCK_RAW = unix.SOCK_RAW
|
||||||
|
|
||||||
|
sizeofSockaddrInet4 = unix.SizeofSockaddrInet4
|
||||||
|
sizeofSockaddrInet6 = unix.SizeofSockaddrInet6
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
// Copyright 2017 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package socket
|
|
||||||
|
|
||||||
func probeProtocolStack() int { return 4 }
|
|
|
@ -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 aix || (go1.12 && darwin)
|
||||||
// +build aix go1.12,darwin
|
// +build aix go1.12,darwin
|
||||||
|
|
||||||
package socket
|
package socket
|
||||||
|
|
|
@ -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 linux && !s390x && !386
|
||||||
// +build linux,!s390x,!386
|
// +build linux,!s390x,!386
|
||||||
|
|
||||||
package socket
|
package socket
|
||||||
|
@ -11,11 +12,6 @@ import (
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
func probeProtocolStack() int {
|
|
||||||
var p uintptr
|
|
||||||
return int(unsafe.Sizeof(p))
|
|
||||||
}
|
|
||||||
|
|
||||||
func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
|
func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
|
||||||
n, _, errno := syscall.Syscall6(sysRECVMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0)
|
n, _, errno := syscall.Syscall6(sysRECVMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0)
|
||||||
return int(n), errnoErr(errno)
|
return int(n), errnoErr(errno)
|
||||||
|
|
|
@ -9,8 +9,6 @@ import (
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
func probeProtocolStack() int { return 4 }
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sysSETSOCKOPT = 0xe
|
sysSETSOCKOPT = 0xe
|
||||||
sysGETSOCKOPT = 0xf
|
sysGETSOCKOPT = 0xf
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue