Merge master
This commit is contained in:
commit
60478f13b0
|
@ -17,11 +17,13 @@ addons:
|
|||
- libffi-dev
|
||||
- ca-certificates
|
||||
- rsyslog
|
||||
mariadb: "10.0"
|
||||
|
||||
sudo: false
|
||||
|
||||
services:
|
||||
- rabbitmq
|
||||
- mysql
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
|
14
Dockerfile
14
Dockerfile
|
@ -1,4 +1,4 @@
|
|||
FROM golang:1.4.2
|
||||
FROM golang:1.5rc1
|
||||
|
||||
MAINTAINER J.C. Jones "jjones@letsencrypt.org"
|
||||
MAINTAINER William Budington "bill@eff.org"
|
||||
|
@ -13,12 +13,13 @@ RUN apt-get update && \
|
|||
apt-transport-https && \
|
||||
echo deb https://deb.nodesource.com/node_0.12 jessie main > /etc/apt/sources.list.d/nodesource.list && \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
libltdl-dev \
|
||||
rsyslog \
|
||||
nodejs \
|
||||
lsb-release \
|
||||
rabbitmq-server \
|
||||
mariadb-server \
|
||||
git-core && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/* \
|
||||
|
@ -42,6 +43,8 @@ RUN ./bootstrap/debian.sh && \
|
|||
RUN virtualenv --no-site-packages -p python2 venv && \
|
||||
./venv/bin/pip install -r requirements.txt -e acme -e .[dev,docs,testing] -e letsencrypt-apache -e letsencrypt-nginx
|
||||
|
||||
ENV LETSENCRYPT_PATH /letsencrypt
|
||||
|
||||
# Copy in the Boulder sources
|
||||
COPY . /go/src/github.com/letsencrypt/boulder
|
||||
|
||||
|
@ -55,4 +58,9 @@ RUN go install \
|
|||
github.com/letsencrypt/boulder/cmd/boulder-wfe
|
||||
|
||||
WORKDIR /go/src/github.com/letsencrypt/boulder
|
||||
CMD ["bash", "-c", "rsyslogd && /go/bin/boulder"]
|
||||
CMD ["bash", "-c", "service mysql start && \
|
||||
service rsyslog start && \
|
||||
service rabbitmq-server start && \
|
||||
cd /go/src/github.com/letsencrypt/boulder/ && \
|
||||
./test/create_db.sh && \
|
||||
WFE_LISTEN_ADDR=0.0.0.0:4000 ./start.py"]
|
||||
|
|
|
@ -82,7 +82,7 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "github.com/miekg/dns",
|
||||
"Rev": "259969e797348d20e8c144a7573c23f06fa962f5"
|
||||
"Rev": "7ff8d29c8b70b10f383a11f03b7bf5b7408bf41a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/miekg/pkcs11",
|
||||
|
@ -98,8 +98,7 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/crypto/ocsp",
|
||||
"Comment": "ocsp-unauthorized.mailed",
|
||||
"Rev": "88aa4b6881d17665db91c95982b2feda28140f9d"
|
||||
"Rev": "173ce04bfaf66c7bb0fa9d5c0bfd93e773909dbd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "gopkg.in/gorp.v1",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
language: go
|
||||
go:
|
||||
- 1.3
|
||||
- 1.4
|
||||
- 1.5rc1
|
||||
script:
|
||||
- go test -short -bench=.
|
||||
|
|
|
@ -24,6 +24,7 @@ If you like this, you may also be interested in:
|
|||
|
||||
A not-so-up-to-date-list-that-may-be-actually-current:
|
||||
|
||||
* https://cloudflare.com
|
||||
* https://github.com/abh/geodns
|
||||
* http://www.statdns.com/
|
||||
* http://www.dnsinspect.com/
|
||||
|
@ -38,6 +39,7 @@ A not-so-up-to-date-list-that-may-be-actually-current:
|
|||
* https://github.com/tianon/rawdns
|
||||
* https://mesosphere.github.io/mesos-dns/
|
||||
* https://pulse.turbobytes.com/
|
||||
* https://play.google.com/store/apps/details?id=com.turbobytes.dig
|
||||
|
||||
Send pull request if you want to be listed here.
|
||||
|
||||
|
@ -81,7 +83,7 @@ Example programs can be found in the `github.com/miekg/exdns` repository.
|
|||
*all of them*
|
||||
|
||||
* 103{4,5} - DNS standard
|
||||
* 1348 - NSAP record
|
||||
* 1348 - NSAP record (removed the record)
|
||||
* 1982 - Serial Arithmetic
|
||||
* 1876 - LOC record
|
||||
* 1995 - IXFR
|
||||
|
@ -125,7 +127,7 @@ Example programs can be found in the `github.com/miekg/exdns` repository.
|
|||
* 6975 - Algorithm Understanding in DNSSEC
|
||||
* 7043 - EUI48/EUI64 records
|
||||
* 7314 - DNS (EDNS) EXPIRE Option
|
||||
* xxxx - URI record (draft)
|
||||
* 7553 - URI record
|
||||
* xxxx - EDNS0 DNS Update Lease (draft)
|
||||
|
||||
## Loosely based upon
|
||||
|
@ -138,7 +140,7 @@ Example programs can be found in the `github.com/miekg/exdns` repository.
|
|||
## TODO
|
||||
|
||||
* privatekey.Precompute() when signing?
|
||||
* Last remaining RRs: APL, ATMA, A6 and NXT and IPSECKEY;
|
||||
* Missing in parsing: ISDN, UNSPEC, ATMA;
|
||||
* NSEC(3) cover/match/closest enclose;
|
||||
* Replies with TC bit are not parsed to the end;
|
||||
* Last remaining RRs: APL, ATMA, A6, NSAP and NXT.
|
||||
* Missing in parsing: ISDN, UNSPEC, NSAP and ATMA.
|
||||
* NSEC(3) cover/match/closest enclose.
|
||||
* Replies with TC bit are not parsed to the end.
|
||||
|
|
|
@ -188,6 +188,33 @@ func IsFqdn(s string) bool {
|
|||
return s[l-1] == '.'
|
||||
}
|
||||
|
||||
// IsRRset checks if a set of RRs is a valid RRset as defined by RFC 2181.
|
||||
// This means the RRs need to have the same type, name, and class. Returns true
|
||||
// if the RR set is valid, otherwise false.
|
||||
func IsRRset(rrset []RR) bool {
|
||||
if len(rrset) == 0 {
|
||||
return false
|
||||
}
|
||||
if len(rrset) == 1 {
|
||||
return true
|
||||
}
|
||||
rrHeader := rrset[0].Header()
|
||||
rrType := rrHeader.Rrtype
|
||||
rrClass := rrHeader.Class
|
||||
rrName := rrHeader.Name
|
||||
|
||||
for _, rr := range rrset[1:] {
|
||||
curRRHeader := rr.Header()
|
||||
if curRRHeader.Rrtype != rrType || curRRHeader.Class != rrClass || curRRHeader.Name != rrName {
|
||||
// Mismatch between the records, so this is not a valid rrset for
|
||||
//signing/verifying
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Fqdn return the fully qualified domain name from s.
|
||||
// If s is already fully qualified, it behaves as the identity function.
|
||||
func Fqdn(s string) string {
|
||||
|
|
|
@ -296,7 +296,7 @@ func (rr *RRSIG) Sign(k PrivateKey, rrset []RR) error {
|
|||
// This function copies the rdata of some RRs (to lowercase domain names) for the validation to work.
|
||||
func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
|
||||
// First the easy checks
|
||||
if len(rrset) == 0 {
|
||||
if !IsRRset(rrset) {
|
||||
return ErrRRset
|
||||
}
|
||||
if rr.KeyTag != k.KeyTag() {
|
||||
|
@ -314,14 +314,17 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
|
|||
if k.Protocol != 3 {
|
||||
return ErrKey
|
||||
}
|
||||
for _, r := range rrset {
|
||||
if r.Header().Class != rr.Hdr.Class {
|
||||
return ErrRRset
|
||||
}
|
||||
if r.Header().Rrtype != rr.TypeCovered {
|
||||
return ErrRRset
|
||||
}
|
||||
|
||||
// IsRRset checked that we have at least one RR and that the RRs in
|
||||
// the set have consistent type, class, and name. Also check that type and
|
||||
// class matches the RRSIG record.
|
||||
if rrset[0].Header().Class != rr.Hdr.Class {
|
||||
return ErrRRset
|
||||
}
|
||||
if rrset[0].Header().Rrtype != rr.TypeCovered {
|
||||
return ErrRRset
|
||||
}
|
||||
|
||||
// RFC 4035 5.3.2. Reconstructing the Signed Data
|
||||
// Copy the sig, except the rrsig data
|
||||
sigwire := new(rrsigWireFmt)
|
||||
|
@ -409,7 +412,8 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
|
|||
|
||||
// ValidityPeriod uses RFC1982 serial arithmetic to calculate
|
||||
// if a signature period is valid. If t is the zero time, the
|
||||
// current time is taken other t is.
|
||||
// current time is taken other t is. Returns true if the signature
|
||||
// is valid at the given time, otherwise returns false.
|
||||
func (rr *RRSIG) ValidityPeriod(t time.Time) bool {
|
||||
var utc int64
|
||||
if t.IsZero() {
|
||||
|
|
|
@ -656,3 +656,64 @@ PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR`
|
|||
t.Fatalf("RRSIG record differs:\n%v\n%v", ourRRSIG, rrRRSIG.(*RRSIG))
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidRRSet(t *testing.T) {
|
||||
goodRecords := make([]RR, 2)
|
||||
goodRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
|
||||
goodRecords[1] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"_o/"}}
|
||||
|
||||
// Generate key
|
||||
keyname := "cloudflare.com."
|
||||
key := &DNSKEY{
|
||||
Hdr: RR_Header{Name: keyname, Rrtype: TypeDNSKEY, Class: ClassINET, Ttl: 0},
|
||||
Algorithm: ECDSAP256SHA256,
|
||||
Flags: ZONE,
|
||||
Protocol: 3,
|
||||
}
|
||||
privatekey, err := key.Generate(256)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
// Need to fill in: Inception, Expiration, KeyTag, SignerName and Algorithm
|
||||
curTime := time.Now()
|
||||
signature := &RRSIG{
|
||||
Inception: uint32(curTime.Unix()),
|
||||
Expiration: uint32(curTime.Add(time.Hour).Unix()),
|
||||
KeyTag: key.KeyTag(),
|
||||
SignerName: keyname,
|
||||
Algorithm: ECDSAP256SHA256,
|
||||
}
|
||||
|
||||
// Inconsistent name between records
|
||||
badRecords := make([]RR, 2)
|
||||
badRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
|
||||
badRecords[1] = &TXT{Hdr: RR_Header{Name: "nama.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"_o/"}}
|
||||
|
||||
if IsRRset(badRecords) {
|
||||
t.Fatal("Record set with inconsistent names considered valid")
|
||||
}
|
||||
|
||||
badRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
|
||||
badRecords[1] = &A{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeA, Class: ClassINET, Ttl: 0}}
|
||||
|
||||
if IsRRset(badRecords) {
|
||||
t.Fatal("Record set with inconsistent record types considered valid")
|
||||
}
|
||||
|
||||
badRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
|
||||
badRecords[1] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassCHAOS, Ttl: 0}, Txt: []string{"_o/"}}
|
||||
|
||||
if IsRRset(badRecords) {
|
||||
t.Fatal("Record set with inconsistent record class considered valid")
|
||||
}
|
||||
|
||||
// Sign the good record set and then make sure verification fails on the bad record set
|
||||
if err := signature.Sign(privatekey, goodRecords); err != nil {
|
||||
t.Fatal("Signing good records failed")
|
||||
}
|
||||
|
||||
if err := signature.Verify(key, badRecords); err != ErrRRset {
|
||||
t.Fatal("Verification did not return ErrRRset with inconsistent records")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -192,6 +192,11 @@ func (e *EDNS0_NSID) String() string { return string(e.Nsid) }
|
|||
// e.Address = net.ParseIP("127.0.0.1").To4() // for IPv4
|
||||
// // e.Address = net.ParseIP("2001:7b8:32a::2") // for IPV6
|
||||
// o.Option = append(o.Option, e)
|
||||
//
|
||||
// Note: the spec (draft-ietf-dnsop-edns-client-subnet-00) has some insane logic
|
||||
// for which netmask applies to the address. This code will parse all the
|
||||
// available bits when unpacking (up to optlen). When packing it will apply
|
||||
// SourceNetmask. If you need more advanced logic, patches welcome and good luck.
|
||||
type EDNS0_SUBNET struct {
|
||||
Code uint16 // Always EDNS0SUBNET
|
||||
Family uint16 // 1 for IP, 2 for IP6
|
||||
|
@ -218,38 +223,22 @@ func (e *EDNS0_SUBNET) pack() ([]byte, error) {
|
|||
if e.SourceNetmask > net.IPv4len*8 {
|
||||
return nil, errors.New("dns: bad netmask")
|
||||
}
|
||||
ip := make([]byte, net.IPv4len)
|
||||
a := e.Address.To4().Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv4len*8))
|
||||
for i := 0; i < net.IPv4len; i++ {
|
||||
if i+1 > len(e.Address) {
|
||||
break
|
||||
}
|
||||
ip[i] = a[i]
|
||||
if len(e.Address.To4()) != net.IPv4len {
|
||||
return nil, errors.New("dns: bad address")
|
||||
}
|
||||
needLength := e.SourceNetmask / 8
|
||||
if e.SourceNetmask%8 > 0 {
|
||||
needLength++
|
||||
}
|
||||
ip = ip[:needLength]
|
||||
b = append(b, ip...)
|
||||
ip := e.Address.To4().Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv4len*8))
|
||||
needLength := (e.SourceNetmask + 8 - 1) / 8 // division rounding up
|
||||
b = append(b, ip[:needLength]...)
|
||||
case 2:
|
||||
if e.SourceNetmask > net.IPv6len*8 {
|
||||
return nil, errors.New("dns: bad netmask")
|
||||
}
|
||||
ip := make([]byte, net.IPv6len)
|
||||
a := e.Address.Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv6len*8))
|
||||
for i := 0; i < net.IPv6len; i++ {
|
||||
if i+1 > len(e.Address) {
|
||||
break
|
||||
}
|
||||
ip[i] = a[i]
|
||||
if len(e.Address) != net.IPv6len {
|
||||
return nil, errors.New("dns: bad address")
|
||||
}
|
||||
needLength := e.SourceNetmask / 8
|
||||
if e.SourceNetmask%8 > 0 {
|
||||
needLength++
|
||||
}
|
||||
ip = ip[:needLength]
|
||||
b = append(b, ip...)
|
||||
ip := e.Address.Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv6len*8))
|
||||
needLength := (e.SourceNetmask + 8 - 1) / 8 // division rounding up
|
||||
b = append(b, ip[:needLength]...)
|
||||
default:
|
||||
return nil, errors.New("dns: bad address family")
|
||||
}
|
||||
|
@ -257,8 +246,7 @@ func (e *EDNS0_SUBNET) pack() ([]byte, error) {
|
|||
}
|
||||
|
||||
func (e *EDNS0_SUBNET) unpack(b []byte) error {
|
||||
lb := len(b)
|
||||
if lb < 4 {
|
||||
if len(b) < 4 {
|
||||
return ErrBuf
|
||||
}
|
||||
e.Family, _ = unpackUint16(b, 0)
|
||||
|
@ -266,25 +254,27 @@ func (e *EDNS0_SUBNET) unpack(b []byte) error {
|
|||
e.SourceScope = b[3]
|
||||
switch e.Family {
|
||||
case 1:
|
||||
addr := make([]byte, 4)
|
||||
for i := 0; i < int(e.SourceNetmask/8); i++ {
|
||||
if i >= len(addr) || 4+i >= len(b) {
|
||||
return ErrBuf
|
||||
}
|
||||
if e.SourceNetmask > net.IPv4len*8 || e.SourceScope > net.IPv4len*8 {
|
||||
return errors.New("dns: bad netmask")
|
||||
}
|
||||
addr := make([]byte, net.IPv4len)
|
||||
for i := 0; i < net.IPv4len && 4+i < len(b); i++ {
|
||||
addr[i] = b[4+i]
|
||||
}
|
||||
e.Address = net.IPv4(addr[0], addr[1], addr[2], addr[3])
|
||||
case 2:
|
||||
addr := make([]byte, 16)
|
||||
for i := 0; i < int(e.SourceNetmask/8); i++ {
|
||||
if i >= len(addr) || 4+i >= len(b) {
|
||||
return ErrBuf
|
||||
}
|
||||
if e.SourceNetmask > net.IPv6len*8 || e.SourceScope > net.IPv6len*8 {
|
||||
return errors.New("dns: bad netmask")
|
||||
}
|
||||
addr := make([]byte, net.IPv6len)
|
||||
for i := 0; i < net.IPv6len && 4+i < len(b); i++ {
|
||||
addr[i] = b[4+i]
|
||||
}
|
||||
e.Address = net.IP{addr[0], addr[1], addr[2], addr[3], addr[4],
|
||||
addr[5], addr[6], addr[7], addr[8], addr[9], addr[10],
|
||||
addr[11], addr[12], addr[13], addr[14], addr[15]}
|
||||
default:
|
||||
return errors.New("dns: bad address family")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package dns
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestFuzzString(t *testing.T) {
|
||||
testcases := []string{"", " MINFO ", " RP ", " NSEC 0 0", " \" NSEC 0 0\"", " \" MINFO \"",
|
||||
";a ", ";a<><61><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>",
|
||||
" NSAP O ", " NSAP N ",
|
||||
" TYPE4 TYPE6a789a3bc0045c8a5fb42c7d1bd998f5444 IN 9579b47d46817afbd17273e6",
|
||||
" TYPE45 3 3 4147994 TYPE\\(\\)\\)\\(\\)\\(\\(\\)\\(\\)\\)\\)\\(\\)\\(\\)\\(\\(\\R 948\"\")\\(\\)\\)\\)\\(\\ ",
|
||||
"$GENERATE 0-3 ${441189,5039418474430,o}",
|
||||
"$INCLUDE 00 TYPE00000000000n ",
|
||||
"$INCLUDE PE4 TYPE061463623/727071511 \\(\\)\\$GENERATE 6-462/0",
|
||||
}
|
||||
for i, tc := range testcases {
|
||||
rr, err := NewRR(tc)
|
||||
if err == nil {
|
||||
// rr can be nil because we can (for instance) just parse a comment
|
||||
if rr == nil {
|
||||
continue
|
||||
}
|
||||
t.Fatalf("parsed mailformed RR %d: %s", i, rr.String())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ import (
|
|||
"bytes"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/miekg/dns"
|
||||
)
|
||||
|
@ -27,9 +28,15 @@ const (
|
|||
)
|
||||
|
||||
// ToPunycode converts unicode domain names to DNS-appropriate punycode names.
|
||||
// This function would return an empty string result for domain names with
|
||||
// This function will return an empty string result for domain names with
|
||||
// invalid unicode strings. This function expects domain names in lowercase.
|
||||
func ToPunycode(s string) string {
|
||||
// Early check to see if encoding is needed.
|
||||
// This will prevent making heap allocations when not needed.
|
||||
if !needToPunycode(s) {
|
||||
return s
|
||||
}
|
||||
|
||||
tokens := dns.SplitDomainName(s)
|
||||
switch {
|
||||
case s == "":
|
||||
|
@ -51,7 +58,14 @@ func ToPunycode(s string) string {
|
|||
}
|
||||
|
||||
// FromPunycode returns unicode domain name from provided punycode string.
|
||||
// This function expects punycode strings in lowercase.
|
||||
func FromPunycode(s string) string {
|
||||
// Early check to see if decoding is needed.
|
||||
// This will prevent making heap allocations when not needed.
|
||||
if !needFromPunycode(s) {
|
||||
return s
|
||||
}
|
||||
|
||||
tokens := dns.SplitDomainName(s)
|
||||
switch {
|
||||
case s == "":
|
||||
|
@ -124,7 +138,7 @@ func next(b []rune, boundary rune) rune {
|
|||
}
|
||||
|
||||
// preprune converts unicode rune to lower case. At this time it's not
|
||||
// supporting all things described in RFCs
|
||||
// supporting all things described in RFCs.
|
||||
func preprune(r rune) rune {
|
||||
if unicode.IsUpper(r) {
|
||||
r = unicode.ToLower(r)
|
||||
|
@ -132,7 +146,7 @@ func preprune(r rune) rune {
|
|||
return r
|
||||
}
|
||||
|
||||
// tfunc is a function that helps calculate each character weight
|
||||
// tfunc is a function that helps calculate each character weight.
|
||||
func tfunc(k, bias rune) rune {
|
||||
switch {
|
||||
case k <= bias:
|
||||
|
@ -143,6 +157,51 @@ func tfunc(k, bias rune) rune {
|
|||
return k - bias
|
||||
}
|
||||
|
||||
// needToPunycode returns true for strings that require punycode encoding
|
||||
// (contain unicode characters).
|
||||
func needToPunycode(s string) bool {
|
||||
// This function is very similar to bytes.Runes. We don't use bytes.Runes
|
||||
// because it makes a heap allocation that's not needed here.
|
||||
for i := 0; len(s) > 0; i++ {
|
||||
r, l := utf8.DecodeRuneInString(s)
|
||||
if r > 0x7f {
|
||||
return true
|
||||
}
|
||||
s = s[l:]
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// needFromPunycode returns true for strings that require punycode decoding.
|
||||
func needFromPunycode(s string) bool {
|
||||
if s == "." {
|
||||
return false
|
||||
}
|
||||
|
||||
off := 0
|
||||
end := false
|
||||
pl := len(_PREFIX)
|
||||
sl := len(s)
|
||||
|
||||
// If s starts with _PREFIX.
|
||||
if sl > pl && s[off:off+pl] == _PREFIX {
|
||||
return true
|
||||
}
|
||||
|
||||
for {
|
||||
// Find the part after the next ".".
|
||||
off, end = dns.NextLabel(s, off)
|
||||
if end {
|
||||
return false
|
||||
}
|
||||
// If this parts starts with _PREFIX.
|
||||
if sl-off > pl && s[off:off+pl] == _PREFIX {
|
||||
return true
|
||||
}
|
||||
}
|
||||
panic("dns: not reached")
|
||||
}
|
||||
|
||||
// encode transforms Unicode input bytes (that represent DNS label) into
|
||||
// punycode bytestream. This function would return nil if there's an invalid
|
||||
// character in the label.
|
||||
|
@ -217,7 +276,7 @@ func encode(input []byte) []byte {
|
|||
return out.Bytes()
|
||||
}
|
||||
|
||||
// decode transforms punycode input bytes (that represent DNS label) into Unicode bytestream
|
||||
// decode transforms punycode input bytes (that represent DNS label) into Unicode bytestream.
|
||||
func decode(b []byte) []byte {
|
||||
src := b // b would move and we need to keep it
|
||||
|
||||
|
@ -254,6 +313,10 @@ func decode(b []byte) []byte {
|
|||
return src
|
||||
}
|
||||
i += digit * w
|
||||
if i < 0 {
|
||||
// safety check for rune overflow
|
||||
return src
|
||||
}
|
||||
|
||||
t = tfunc(k, bias)
|
||||
if digit < t {
|
||||
|
|
|
@ -8,9 +8,9 @@ import (
|
|||
var testcases = [][2]string{
|
||||
{"", ""},
|
||||
{"a", "a"},
|
||||
{"A-B", "a-b"},
|
||||
{"A-B-C", "a-b-c"},
|
||||
{"AbC", "abc"},
|
||||
{"a-b", "a-b"},
|
||||
{"a-b-c", "a-b-c"},
|
||||
{"abc", "abc"},
|
||||
{"я", "xn--41a"},
|
||||
{"zя", "xn--z-0ub"},
|
||||
{"яZ", "xn--z-zub"},
|
||||
|
@ -86,6 +86,7 @@ var invalidACEs = []string{
|
|||
"xn--*",
|
||||
"xn--",
|
||||
"xn---",
|
||||
"xn--a000000000",
|
||||
}
|
||||
|
||||
func TestInvalidPunycode(t *testing.T) {
|
||||
|
|
|
@ -29,8 +29,6 @@ var (
|
|||
ErrAuth error = &Error{err: "bad authentication"}
|
||||
// ErrBuf indicates that the buffer used it too small for the message.
|
||||
ErrBuf error = &Error{err: "buffer size too small"}
|
||||
// ErrConn indicates that a connection has both a TCP and UDP socket.
|
||||
ErrConn error = &Error{err: "conn holds both UDP and TCP connection"}
|
||||
// ErrConnEmpty indicates a connection is being uses before it is initialized.
|
||||
ErrConnEmpty error = &Error{err: "conn has no connection"}
|
||||
// ErrExtendedRcode ...
|
||||
|
@ -51,8 +49,6 @@ var (
|
|||
ErrShortRead error = &Error{err: "short read"}
|
||||
// ErrSig indicates that a signature can not be cryptographically validated.
|
||||
ErrSig error = &Error{err: "bad signature"}
|
||||
// ErrSigGen indicates a faulure to generate a signature.
|
||||
ErrSigGen error = &Error{err: "bad signature generation"}
|
||||
// ErrSOA indicates that no SOA RR was seen when doing zone transfers.
|
||||
ErrSoa error = &Error{err: "no SOA"}
|
||||
// ErrTime indicates a timing error in TSIG authentication.
|
||||
|
@ -138,7 +134,6 @@ var TypeToString = map[uint16]string{
|
|||
TypeNINFO: "NINFO",
|
||||
TypeNIMLOC: "NIMLOC",
|
||||
TypeNS: "NS",
|
||||
TypeNSAP: "NSAP",
|
||||
TypeNSAPPTR: "NSAP-PTR",
|
||||
TypeNSEC3: "NSEC3",
|
||||
TypeNSEC3PARAM: "NSEC3PARAM",
|
||||
|
@ -237,7 +232,7 @@ var RcodeToString = map[int]string{
|
|||
// PackDomainName packs a domain name s into msg[off:].
|
||||
// If compression is wanted compress must be true and the compression
|
||||
// map needs to hold a mapping between domain names and offsets
|
||||
// pointing into msg[].
|
||||
// pointing into msg.
|
||||
func PackDomainName(s string, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) {
|
||||
off1, _, err = packDomainName(s, msg, off, compression, compress)
|
||||
return
|
||||
|
@ -413,9 +408,6 @@ Loop:
|
|||
case 0x00:
|
||||
if c == 0x00 {
|
||||
// end of name
|
||||
if len(s) == 0 {
|
||||
return ".", off, nil
|
||||
}
|
||||
break Loop
|
||||
}
|
||||
// literal string
|
||||
|
@ -476,6 +468,9 @@ Loop:
|
|||
if ptr == 0 {
|
||||
off1 = off
|
||||
}
|
||||
if len(s) == 0 {
|
||||
s = []byte(".")
|
||||
}
|
||||
return string(s), off1, nil
|
||||
}
|
||||
|
||||
|
@ -573,17 +568,16 @@ func packOctetString(s string, msg []byte, offset int, tmp []byte) (int, error)
|
|||
return offset, nil
|
||||
}
|
||||
|
||||
func unpackTxt(msg []byte, offset, rdend int) ([]string, int, error) {
|
||||
var err error
|
||||
var ss []string
|
||||
func unpackTxt(msg []byte, off0 int) (ss []string, off int, err error) {
|
||||
off = off0
|
||||
var s string
|
||||
for offset < rdend && err == nil {
|
||||
s, offset, err = unpackTxtString(msg, offset)
|
||||
for off < len(msg) && err == nil {
|
||||
s, off, err = unpackTxtString(msg, off)
|
||||
if err == nil {
|
||||
ss = append(ss, s)
|
||||
}
|
||||
}
|
||||
return ss, offset, err
|
||||
return
|
||||
}
|
||||
|
||||
func unpackTxtString(msg []byte, offset int) (string, int, error) {
|
||||
|
@ -748,58 +742,49 @@ func packStructValue(val reflect.Value, msg []byte, off int, compression map[str
|
|||
if val.Field(i).Len() == 0 {
|
||||
break
|
||||
}
|
||||
var bitmapbyte uint16
|
||||
off1 := off
|
||||
for j := 0; j < val.Field(i).Len(); j++ {
|
||||
serv := uint16((fv.Index(j).Uint()))
|
||||
bitmapbyte = uint16(serv / 8)
|
||||
if int(bitmapbyte) > lenmsg {
|
||||
return lenmsg, &Error{err: "overflow packing wks"}
|
||||
serv := int(fv.Index(j).Uint())
|
||||
if off+serv/8+1 > len(msg) {
|
||||
return len(msg), &Error{err: "overflow packing wks"}
|
||||
}
|
||||
msg[off+serv/8] |= byte(1 << (7 - uint(serv%8)))
|
||||
if off+serv/8+1 > off1 {
|
||||
off1 = off + serv/8 + 1
|
||||
}
|
||||
bit := uint16(serv) - bitmapbyte*8
|
||||
msg[bitmapbyte] = byte(1 << (7 - bit))
|
||||
}
|
||||
off += int(bitmapbyte)
|
||||
off = off1
|
||||
case `dns:"nsec"`: // NSEC/NSEC3
|
||||
// This is the uint16 type bitmap
|
||||
if val.Field(i).Len() == 0 {
|
||||
// Do absolutely nothing
|
||||
break
|
||||
}
|
||||
|
||||
lastwindow := uint16(0)
|
||||
length := uint16(0)
|
||||
if off+2 > lenmsg {
|
||||
return lenmsg, &Error{err: "overflow packing nsecx"}
|
||||
}
|
||||
var lastwindow, lastlength uint16
|
||||
for j := 0; j < val.Field(i).Len(); j++ {
|
||||
t := uint16((fv.Index(j).Uint()))
|
||||
window := uint16(t / 256)
|
||||
if lastwindow != window {
|
||||
t := uint16(fv.Index(j).Uint())
|
||||
window := t / 256
|
||||
length := (t-window*256)/8 + 1
|
||||
if window > lastwindow && lastlength != 0 {
|
||||
// New window, jump to the new offset
|
||||
off += int(length) + 3
|
||||
if off > lenmsg {
|
||||
return lenmsg, &Error{err: "overflow packing nsecx bitmap"}
|
||||
}
|
||||
off += int(lastlength) + 2
|
||||
lastlength = 0
|
||||
}
|
||||
length = (t - window*256) / 8
|
||||
bit := t - (window * 256) - (length * 8)
|
||||
if off+2+int(length) > lenmsg {
|
||||
return lenmsg, &Error{err: "overflow packing nsecx bitmap"}
|
||||
if window < lastwindow || length < lastlength {
|
||||
return len(msg), &Error{err: "nsec bits out of order"}
|
||||
}
|
||||
if off+2+int(length) > len(msg) {
|
||||
return len(msg), &Error{err: "overflow packing nsec"}
|
||||
}
|
||||
|
||||
// Setting the window #
|
||||
msg[off] = byte(window)
|
||||
// Setting the octets length
|
||||
msg[off+1] = byte(length + 1)
|
||||
msg[off+1] = byte(length)
|
||||
// Setting the bit value for the type in the right octet
|
||||
msg[off+2+int(length)] |= byte(1 << (7 - bit))
|
||||
lastwindow = window
|
||||
}
|
||||
off += 2 + int(length)
|
||||
off++
|
||||
if off > lenmsg {
|
||||
return lenmsg, &Error{err: "overflow packing nsecx bitmap"}
|
||||
msg[off+1+int(length)] |= byte(1 << (7 - (t % 8)))
|
||||
lastwindow, lastlength = window, length
|
||||
}
|
||||
off += int(lastlength) + 2
|
||||
}
|
||||
case reflect.Struct:
|
||||
off, err = packStructValue(fv, msg, off, compression, compress)
|
||||
|
@ -957,17 +942,11 @@ func packStructCompress(any interface{}, msg []byte, off int, compression map[st
|
|||
return off, err
|
||||
}
|
||||
|
||||
// TODO(miek): Fix use of rdlength here
|
||||
|
||||
// Unpack a reflect.StructValue from msg.
|
||||
// Same restrictions as packStructValue.
|
||||
func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err error) {
|
||||
var lenrd int
|
||||
lenmsg := len(msg)
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
if lenrd != 0 && lenrd == off {
|
||||
break
|
||||
}
|
||||
if off > lenmsg {
|
||||
return lenmsg, &Error{"bad offset unpacking"}
|
||||
}
|
||||
|
@ -979,7 +958,7 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
|
|||
// therefore it's expected that this interface would be PrivateRdata
|
||||
switch data := fv.Interface().(type) {
|
||||
case PrivateRdata:
|
||||
n, err := data.Unpack(msg[off:lenrd])
|
||||
n, err := data.Unpack(msg[off:])
|
||||
if err != nil {
|
||||
return lenmsg, err
|
||||
}
|
||||
|
@ -995,7 +974,7 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
|
|||
// HIP record slice of name (or none)
|
||||
var servers []string
|
||||
var s string
|
||||
for off < lenrd {
|
||||
for off < lenmsg {
|
||||
s, off, err = UnpackDomainName(msg, off)
|
||||
if err != nil {
|
||||
return lenmsg, err
|
||||
|
@ -1004,17 +983,17 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
|
|||
}
|
||||
fv.Set(reflect.ValueOf(servers))
|
||||
case `dns:"txt"`:
|
||||
if off == lenmsg || lenrd == off {
|
||||
if off == lenmsg {
|
||||
break
|
||||
}
|
||||
var txt []string
|
||||
txt, off, err = unpackTxt(msg, off, lenrd)
|
||||
txt, off, err = unpackTxt(msg, off)
|
||||
if err != nil {
|
||||
return lenmsg, err
|
||||
}
|
||||
fv.Set(reflect.ValueOf(txt))
|
||||
case `dns:"opt"`: // edns0
|
||||
if off == lenrd {
|
||||
if off == lenmsg {
|
||||
// This is an EDNS0 (OPT Record) with no rdata
|
||||
// We can safely return here.
|
||||
break
|
||||
|
@ -1022,12 +1001,12 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
|
|||
var edns []EDNS0
|
||||
Option:
|
||||
code := uint16(0)
|
||||
if off+2 > lenmsg {
|
||||
if off+4 > lenmsg {
|
||||
return lenmsg, &Error{err: "overflow unpacking opt"}
|
||||
}
|
||||
code, off = unpackUint16(msg, off)
|
||||
optlen, off1 := unpackUint16(msg, off)
|
||||
if off1+int(optlen) > lenrd {
|
||||
if off1+int(optlen) > lenmsg {
|
||||
return lenmsg, &Error{err: "overflow unpacking opt"}
|
||||
}
|
||||
switch code {
|
||||
|
@ -1092,7 +1071,7 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
|
|||
edns = append(edns, e)
|
||||
off = off1 + int(optlen)
|
||||
}
|
||||
if off < lenrd {
|
||||
if off < lenmsg {
|
||||
goto Option
|
||||
}
|
||||
fv.Set(reflect.ValueOf(edns))
|
||||
|
@ -1103,10 +1082,10 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
|
|||
continue
|
||||
}
|
||||
}
|
||||
if off == lenrd {
|
||||
if off == lenmsg {
|
||||
break // dyn. update
|
||||
}
|
||||
if off+net.IPv4len > lenrd || off+net.IPv4len > lenmsg {
|
||||
if off+net.IPv4len > lenmsg {
|
||||
return lenmsg, &Error{err: "overflow unpacking a"}
|
||||
}
|
||||
fv.Set(reflect.ValueOf(net.IPv4(msg[off], msg[off+1], msg[off+2], msg[off+3])))
|
||||
|
@ -1118,10 +1097,10 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
|
|||
continue
|
||||
}
|
||||
}
|
||||
if off == lenrd {
|
||||
if off == lenmsg {
|
||||
break
|
||||
}
|
||||
if off+net.IPv6len > lenrd || off+net.IPv6len > lenmsg {
|
||||
if off+net.IPv6len > lenmsg {
|
||||
return lenmsg, &Error{err: "overflow unpacking aaaa"}
|
||||
}
|
||||
fv.Set(reflect.ValueOf(net.IP{msg[off], msg[off+1], msg[off+2], msg[off+3], msg[off+4],
|
||||
|
@ -1132,7 +1111,7 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
|
|||
// Rest of the record is the bitmap
|
||||
var serv []uint16
|
||||
j := 0
|
||||
for off < lenrd {
|
||||
for off < lenmsg {
|
||||
if off+1 > lenmsg {
|
||||
return lenmsg, &Error{err: "overflow unpacking wks"}
|
||||
}
|
||||
|
@ -1167,36 +1146,39 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
|
|||
}
|
||||
fv.Set(reflect.ValueOf(serv))
|
||||
case `dns:"nsec"`: // NSEC/NSEC3
|
||||
if off == lenrd {
|
||||
if off == len(msg) {
|
||||
break
|
||||
}
|
||||
// Rest of the record is the type bitmap
|
||||
if off+2 > lenrd || off+2 > lenmsg {
|
||||
return lenmsg, &Error{err: "overflow unpacking nsecx"}
|
||||
}
|
||||
var nsec []uint16
|
||||
length := 0
|
||||
window := 0
|
||||
for off+2 < lenrd {
|
||||
lastwindow := -1
|
||||
for off < len(msg) {
|
||||
if off+2 > len(msg) {
|
||||
return len(msg), &Error{err: "overflow unpacking nsecx"}
|
||||
}
|
||||
window = int(msg[off])
|
||||
length = int(msg[off+1])
|
||||
//println("off, windows, length, end", off, window, length, endrr)
|
||||
off += 2
|
||||
if window <= lastwindow {
|
||||
// RFC 4034: Blocks are present in the NSEC RR RDATA in
|
||||
// increasing numerical order.
|
||||
return len(msg), &Error{err: "out of order NSEC block"}
|
||||
}
|
||||
if length == 0 {
|
||||
// A length window of zero is strange. If there
|
||||
// the window should not have been specified. Bail out
|
||||
// println("dns: length == 0 when unpacking NSEC")
|
||||
return lenmsg, &Error{err: "overflow unpacking nsecx"}
|
||||
// RFC 4034: Blocks with no types present MUST NOT be included.
|
||||
return len(msg), &Error{err: "empty NSEC block"}
|
||||
}
|
||||
if length > 32 {
|
||||
return lenmsg, &Error{err: "overflow unpacking nsecx"}
|
||||
return len(msg), &Error{err: "NSEC block too long"}
|
||||
}
|
||||
if off+length > len(msg) {
|
||||
return len(msg), &Error{err: "overflowing NSEC block"}
|
||||
}
|
||||
|
||||
// Walk the bytes in the window - and check the bit settings...
|
||||
off += 2
|
||||
// Walk the bytes in the window and extract the type bits
|
||||
for j := 0; j < length; j++ {
|
||||
if off+j+1 > lenmsg {
|
||||
return lenmsg, &Error{err: "overflow unpacking nsecx"}
|
||||
}
|
||||
b := msg[off+j]
|
||||
// Check the bits one by one, and set the type
|
||||
if b&0x80 == 0x80 {
|
||||
|
@ -1225,6 +1207,7 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
|
|||
}
|
||||
}
|
||||
off += length
|
||||
lastwindow = window
|
||||
}
|
||||
fv.Set(reflect.ValueOf(nsec))
|
||||
}
|
||||
|
@ -1234,7 +1217,12 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
|
|||
return lenmsg, err
|
||||
}
|
||||
if val.Type().Field(i).Name == "Hdr" {
|
||||
lenrd = off + int(val.FieldByName("Hdr").FieldByName("Rdlength").Uint())
|
||||
lenrd := off + int(val.FieldByName("Hdr").FieldByName("Rdlength").Uint())
|
||||
if lenrd > lenmsg {
|
||||
return lenmsg, &Error{err: "overflowing header size"}
|
||||
}
|
||||
msg = msg[:lenrd]
|
||||
lenmsg = len(msg)
|
||||
}
|
||||
case reflect.Uint8:
|
||||
if off == lenmsg {
|
||||
|
@ -1265,6 +1253,9 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
|
|||
fv.SetUint(uint64(uint32(msg[off])<<24 | uint32(msg[off+1])<<16 | uint32(msg[off+2])<<8 | uint32(msg[off+3])))
|
||||
off += 4
|
||||
case reflect.Uint64:
|
||||
if off == lenmsg {
|
||||
break
|
||||
}
|
||||
switch val.Type().Field(i).Tag {
|
||||
default:
|
||||
if off+8 > lenmsg {
|
||||
|
@ -1291,30 +1282,26 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
|
|||
default:
|
||||
return lenmsg, &Error{"bad tag unpacking string: " + val.Type().Field(i).Tag.Get("dns")}
|
||||
case `dns:"octet"`:
|
||||
strend := lenrd
|
||||
if strend > lenmsg {
|
||||
return lenmsg, &Error{err: "overflow unpacking octet"}
|
||||
}
|
||||
s = string(msg[off:strend])
|
||||
off = strend
|
||||
s = string(msg[off:])
|
||||
off = lenmsg
|
||||
case `dns:"hex"`:
|
||||
hexend := lenrd
|
||||
hexend := lenmsg
|
||||
if val.FieldByName("Hdr").FieldByName("Rrtype").Uint() == uint64(TypeHIP) {
|
||||
hexend = off + int(val.FieldByName("HitLength").Uint())
|
||||
}
|
||||
if hexend > lenrd || hexend > lenmsg {
|
||||
return lenmsg, &Error{err: "overflow unpacking hex"}
|
||||
if hexend > lenmsg {
|
||||
return lenmsg, &Error{err: "overflow unpacking HIP hex"}
|
||||
}
|
||||
s = hex.EncodeToString(msg[off:hexend])
|
||||
off = hexend
|
||||
case `dns:"base64"`:
|
||||
// Rest of the RR is base64 encoded value
|
||||
b64end := lenrd
|
||||
b64end := lenmsg
|
||||
if val.FieldByName("Hdr").FieldByName("Rrtype").Uint() == uint64(TypeHIP) {
|
||||
b64end = off + int(val.FieldByName("PublicKeyLength").Uint())
|
||||
}
|
||||
if b64end > lenrd || b64end > lenmsg {
|
||||
return lenmsg, &Error{err: "overflow unpacking base64"}
|
||||
if b64end > lenmsg {
|
||||
return lenmsg, &Error{err: "overflow unpacking HIP base64"}
|
||||
}
|
||||
s = toBase64(msg[off:b64end])
|
||||
off = b64end
|
||||
|
@ -1684,45 +1671,77 @@ func (dns *Msg) Unpack(msg []byte) (err error) {
|
|||
dns.CheckingDisabled = (dh.Bits & _CD) != 0
|
||||
dns.Rcode = int(dh.Bits & 0xF)
|
||||
|
||||
// Arrays.
|
||||
dns.Question = make([]Question, dh.Qdcount)
|
||||
dns.Answer = make([]RR, dh.Ancount)
|
||||
dns.Ns = make([]RR, dh.Nscount)
|
||||
dns.Extra = make([]RR, dh.Arcount)
|
||||
// Don't pre-alloc these arrays, the incoming lengths are from the network.
|
||||
dns.Question = make([]Question, 0, 1)
|
||||
dns.Answer = make([]RR, 0, 10)
|
||||
dns.Ns = make([]RR, 0, 10)
|
||||
dns.Extra = make([]RR, 0, 10)
|
||||
|
||||
for i := 0; i < len(dns.Question); i++ {
|
||||
off, err = UnpackStruct(&dns.Question[i], msg, off)
|
||||
var q Question
|
||||
for i := 0; i < int(dh.Qdcount); i++ {
|
||||
off1 := off
|
||||
off, err = UnpackStruct(&q, msg, off)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if off1 == off { // Offset does not increase anymore, dh.Qdcount is a lie!
|
||||
dh.Qdcount = uint16(i)
|
||||
break
|
||||
}
|
||||
|
||||
dns.Question = append(dns.Question, q)
|
||||
|
||||
}
|
||||
// If we see a TC bit being set we return here, without
|
||||
// an error, because technically it isn't an error. So return
|
||||
// without parsing the potentially corrupt packet and hitting an error.
|
||||
// TODO(miek): this isn't the best strategy!
|
||||
// Better stragey would be: set boolean indicating truncated message, go forth and parse
|
||||
// until we hit an error, return the message without the latest parsed rr if this boolean
|
||||
// is true.
|
||||
if dns.Truncated {
|
||||
dns.Answer = nil
|
||||
dns.Ns = nil
|
||||
dns.Extra = nil
|
||||
return nil
|
||||
}
|
||||
for i := 0; i < len(dns.Answer); i++ {
|
||||
dns.Answer[i], off, err = UnpackRR(msg, off)
|
||||
|
||||
var r RR
|
||||
for i := 0; i < int(dh.Ancount); i++ {
|
||||
off1 := off
|
||||
r, off, err = UnpackRR(msg, off)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if off1 == off { // Offset does not increase anymore, dh.Ancount is a lie!
|
||||
dh.Ancount = uint16(i)
|
||||
break
|
||||
}
|
||||
dns.Answer = append(dns.Answer, r)
|
||||
}
|
||||
for i := 0; i < len(dns.Ns); i++ {
|
||||
dns.Ns[i], off, err = UnpackRR(msg, off)
|
||||
for i := 0; i < int(dh.Nscount); i++ {
|
||||
off1 := off
|
||||
r, off, err = UnpackRR(msg, off)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if off1 == off { // Offset does not increase anymore, dh.Nscount is a lie!
|
||||
dh.Nscount = uint16(i)
|
||||
break
|
||||
}
|
||||
dns.Ns = append(dns.Ns, r)
|
||||
}
|
||||
for i := 0; i < len(dns.Extra); i++ {
|
||||
dns.Extra[i], off, err = UnpackRR(msg, off)
|
||||
for i := 0; i < int(dh.Arcount); i++ {
|
||||
off1 := off
|
||||
r, off, err = UnpackRR(msg, off)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if off1 == off { // Offset does not increase anymore, dh.Arcount is a lie!
|
||||
dh.Arcount = uint16(i)
|
||||
break
|
||||
}
|
||||
dns.Extra = append(dns.Extra, r)
|
||||
}
|
||||
if off != len(msg) {
|
||||
// TODO(miek) make this an error?
|
||||
|
|
|
@ -905,11 +905,8 @@ func TestILNP(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestNsapGposEidNimloc(t *testing.T) {
|
||||
func TestGposEidNimloc(t *testing.T) {
|
||||
dt := map[string]string{
|
||||
"foo.bar.com. IN NSAP 21 47000580ffff000000321099991111222233334444": "foo.bar.com.\t3600\tIN\tNSAP\t0x47000580ffff000000321099991111222233334444",
|
||||
"foo.bar.com. IN NSAP 0x47000580ffff000000321099991111222233334444": "foo.bar.com.\t3600\tIN\tNSAP\t0x47000580ffff000000321099991111222233334444",
|
||||
"host.school.de IN NSAP 17 39276f3100111100002222333344449876": "host.school.de.\t3600\tIN\tNSAP\t0x39276f3100111100002222333344449876",
|
||||
"444433332222111199990123000000ff. NSAP-PTR foo.bar.com.": "444433332222111199990123000000ff.\t3600\tIN\tNSAP-PTR\tfoo.bar.com.",
|
||||
"lillee. IN GPOS -32.6882 116.8652 10.0": "lillee.\t3600\tIN\tGPOS\t-32.6882 116.8652 10.0",
|
||||
"hinault. IN GPOS -22.6882 116.8652 250.0": "hinault.\t3600\tIN\tGPOS\t-22.6882 116.8652 250.0",
|
||||
|
@ -1506,3 +1503,22 @@ func TestPackCAA(t *testing.T) {
|
|||
t.Fatalf("invalid flag for unpacked answer")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseURI(t *testing.T) {
|
||||
lt := map[string]string{
|
||||
"_http._tcp. IN URI 10 1 \"http://www.example.com/path\"": "_http._tcp.\t3600\tIN\tURI\t10 1 \"http://www.example.com/path\"",
|
||||
"_http._tcp. IN URI 10 1 \"\"": "_http._tcp.\t3600\tIN\tURI\t10 1 \"\"",
|
||||
}
|
||||
for i, o := range lt {
|
||||
rr, err := NewRR(i)
|
||||
if err != nil {
|
||||
t.Error("failed to parse RR: ", err)
|
||||
continue
|
||||
}
|
||||
if rr.String() != o {
|
||||
t.Errorf("`%s' should be equal to\n`%s', but is `%s'", i, o, rr.String())
|
||||
} else {
|
||||
t.Logf("RR is OK: `%s'", rr.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ type response struct {
|
|||
tcp *net.TCPConn // i/o connection if TCP was used
|
||||
udpSession *SessionUDP // oob data to get egress interface right
|
||||
remoteAddr net.Addr // address of the client
|
||||
writer Writer // writer to output the raw DNS bits
|
||||
}
|
||||
|
||||
// ServeMux is an DNS request multiplexer. It matches the
|
||||
|
@ -197,6 +198,43 @@ func HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) {
|
|||
DefaultServeMux.HandleFunc(pattern, handler)
|
||||
}
|
||||
|
||||
// Writer writes raw DNS messages; each call to Write should send an entire message.
|
||||
type Writer interface {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
// Reader reads raw DNS messages; each call to ReadTCP or ReadUDP should return an entire message.
|
||||
type Reader interface {
|
||||
// ReadTCP reads a raw message from a TCP connection. Implementations may alter
|
||||
// connection properties, for example the read-deadline.
|
||||
ReadTCP(conn *net.TCPConn, timeout time.Duration) ([]byte, error)
|
||||
// ReadUDP reads a raw message from a UDP connection. Implementations may alter
|
||||
// connection properties, for example the read-deadline.
|
||||
ReadUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error)
|
||||
}
|
||||
|
||||
// defaultReader is an adapter for the Server struct that implements the Reader interface
|
||||
// using the readTCP and readUDP func of the embedded Server.
|
||||
type defaultReader struct {
|
||||
*Server
|
||||
}
|
||||
|
||||
func (dr *defaultReader) ReadTCP(conn *net.TCPConn, timeout time.Duration) ([]byte, error) {
|
||||
return dr.readTCP(conn, timeout)
|
||||
}
|
||||
|
||||
func (dr *defaultReader) ReadUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error) {
|
||||
return dr.readUDP(conn, timeout)
|
||||
}
|
||||
|
||||
// DecorateReader is a decorator hook for extending or supplanting the functionality of a Reader.
|
||||
// Implementations should never return a nil Reader.
|
||||
type DecorateReader func(Reader) Reader
|
||||
|
||||
// DecorateWriter is a decorator hook for extending or supplanting the functionality of a Writer.
|
||||
// Implementations should never return a nil Writer.
|
||||
type DecorateWriter func(Writer) Writer
|
||||
|
||||
// A Server defines parameters for running an DNS server.
|
||||
type Server struct {
|
||||
// Address to listen on, ":dns" if empty.
|
||||
|
@ -223,8 +261,12 @@ type Server struct {
|
|||
// Unsafe instructs the server to disregard any sanity checks and directly hand the message to
|
||||
// the handler. It will specfically not check if the query has the QR bit not set.
|
||||
Unsafe bool
|
||||
// If NotifyStartedFunc is set is is called, once the server has started listening.
|
||||
// If NotifyStartedFunc is set it is called once the server has started listening.
|
||||
NotifyStartedFunc func()
|
||||
// DecorateReader is optional, allows customization of the process that reads raw DNS messages.
|
||||
DecorateReader DecorateReader
|
||||
// DecorateWriter is optional, allows customization of the process that writes raw DNS messages.
|
||||
DecorateWriter DecorateWriter
|
||||
|
||||
// For graceful shutdown.
|
||||
stopUDP chan bool
|
||||
|
@ -244,9 +286,9 @@ func (srv *Server) ListenAndServe() error {
|
|||
srv.lock.Unlock()
|
||||
return &Error{err: "server already started"}
|
||||
}
|
||||
defer srv.lock.Unlock()
|
||||
srv.stopUDP, srv.stopTCP = make(chan bool), make(chan bool)
|
||||
srv.started = true
|
||||
srv.lock.Unlock()
|
||||
addr := srv.Addr
|
||||
if addr == "" {
|
||||
addr = ":domain"
|
||||
|
@ -294,20 +336,22 @@ func (srv *Server) ActivateAndServe() error {
|
|||
}
|
||||
srv.stopUDP, srv.stopTCP = make(chan bool), make(chan bool)
|
||||
srv.started = true
|
||||
pConn := srv.PacketConn
|
||||
l := srv.Listener
|
||||
srv.lock.Unlock()
|
||||
if srv.PacketConn != nil {
|
||||
if pConn != nil {
|
||||
if srv.UDPSize == 0 {
|
||||
srv.UDPSize = MinMsgSize
|
||||
}
|
||||
if t, ok := srv.PacketConn.(*net.UDPConn); ok {
|
||||
if t, ok := pConn.(*net.UDPConn); ok {
|
||||
if e := setUDPSocketOptions(t); e != nil {
|
||||
return e
|
||||
}
|
||||
return srv.serveUDP(t)
|
||||
}
|
||||
}
|
||||
if srv.Listener != nil {
|
||||
if t, ok := srv.Listener.(*net.TCPListener); ok {
|
||||
if l != nil {
|
||||
if t, ok := l.(*net.TCPListener); ok {
|
||||
return srv.serveTCP(t)
|
||||
}
|
||||
}
|
||||
|
@ -316,7 +360,7 @@ func (srv *Server) ActivateAndServe() error {
|
|||
|
||||
// Shutdown gracefully shuts down a server. After a call to Shutdown, ListenAndServe and
|
||||
// ActivateAndServe will return. All in progress queries are completed before the server
|
||||
// is taken down. If the Shutdown is taking longer than the reading timeout and error
|
||||
// is taken down. If the Shutdown is taking longer than the reading timeout an error
|
||||
// is returned.
|
||||
func (srv *Server) Shutdown() error {
|
||||
srv.lock.Lock()
|
||||
|
@ -325,7 +369,6 @@ func (srv *Server) Shutdown() error {
|
|||
return &Error{err: "server not started"}
|
||||
}
|
||||
srv.started = false
|
||||
srv.lock.Unlock()
|
||||
net, addr := srv.Net, srv.Addr
|
||||
switch {
|
||||
case srv.Listener != nil:
|
||||
|
@ -335,6 +378,7 @@ func (srv *Server) Shutdown() error {
|
|||
a := srv.PacketConn.LocalAddr()
|
||||
net, addr = a.Network(), a.String()
|
||||
}
|
||||
srv.lock.Unlock()
|
||||
|
||||
fin := make(chan bool)
|
||||
switch net {
|
||||
|
@ -382,6 +426,11 @@ func (srv *Server) serveTCP(l *net.TCPListener) error {
|
|||
srv.NotifyStartedFunc()
|
||||
}
|
||||
|
||||
reader := Reader(&defaultReader{srv})
|
||||
if srv.DecorateReader != nil {
|
||||
reader = srv.DecorateReader(reader)
|
||||
}
|
||||
|
||||
handler := srv.Handler
|
||||
if handler == nil {
|
||||
handler = DefaultServeMux
|
||||
|
@ -393,7 +442,7 @@ func (srv *Server) serveTCP(l *net.TCPListener) error {
|
|||
if e != nil {
|
||||
continue
|
||||
}
|
||||
m, e := srv.readTCP(rw, rtimeout)
|
||||
m, e := reader.ReadTCP(rw, rtimeout)
|
||||
select {
|
||||
case <-srv.stopTCP:
|
||||
return nil
|
||||
|
@ -417,6 +466,11 @@ func (srv *Server) serveUDP(l *net.UDPConn) error {
|
|||
srv.NotifyStartedFunc()
|
||||
}
|
||||
|
||||
reader := Reader(&defaultReader{srv})
|
||||
if srv.DecorateReader != nil {
|
||||
reader = srv.DecorateReader(reader)
|
||||
}
|
||||
|
||||
handler := srv.Handler
|
||||
if handler == nil {
|
||||
handler = DefaultServeMux
|
||||
|
@ -424,7 +478,7 @@ func (srv *Server) serveUDP(l *net.UDPConn) error {
|
|||
rtimeout := srv.getReadTimeout()
|
||||
// deadline is not used here
|
||||
for {
|
||||
m, s, e := srv.readUDP(l, rtimeout)
|
||||
m, s, e := reader.ReadUDP(l, rtimeout)
|
||||
select {
|
||||
case <-srv.stopUDP:
|
||||
return nil
|
||||
|
@ -442,6 +496,12 @@ func (srv *Server) serveUDP(l *net.UDPConn) error {
|
|||
// Serve a new connection.
|
||||
func (srv *Server) serve(a net.Addr, h Handler, m []byte, u *net.UDPConn, s *SessionUDP, t *net.TCPConn) {
|
||||
w := &response{tsigSecret: srv.TsigSecret, udp: u, tcp: t, remoteAddr: a, udpSession: s}
|
||||
if srv.DecorateWriter != nil {
|
||||
w.writer = srv.DecorateWriter(w)
|
||||
} else {
|
||||
w.writer = w
|
||||
}
|
||||
|
||||
q := 0
|
||||
defer func() {
|
||||
if u != nil {
|
||||
|
@ -451,6 +511,11 @@ func (srv *Server) serve(a net.Addr, h Handler, m []byte, u *net.UDPConn, s *Ses
|
|||
srv.wgTCP.Done()
|
||||
}
|
||||
}()
|
||||
|
||||
reader := Reader(&defaultReader{srv})
|
||||
if srv.DecorateReader != nil {
|
||||
reader = srv.DecorateReader(reader)
|
||||
}
|
||||
Redo:
|
||||
req := new(Msg)
|
||||
err := req.Unpack(m)
|
||||
|
@ -490,7 +555,7 @@ Exit:
|
|||
if srv.IdleTimeout != nil {
|
||||
idleTimeout = srv.IdleTimeout()
|
||||
}
|
||||
m, e := srv.readTCP(w.tcp, idleTimeout)
|
||||
m, e := reader.ReadTCP(w.tcp, idleTimeout)
|
||||
if e == nil {
|
||||
q++
|
||||
// TODO(miek): make this number configurable?
|
||||
|
@ -562,7 +627,7 @@ func (w *response) WriteMsg(m *Msg) (err error) {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(data)
|
||||
_, err = w.writer.Write(data)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -570,7 +635,7 @@ func (w *response) WriteMsg(m *Msg) (err error) {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(data)
|
||||
_, err = w.writer.Write(data)
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -397,3 +397,54 @@ func TestShutdownUDP(t *testing.T) {
|
|||
t.Errorf("Could not shutdown test UDP server, %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
type ExampleFrameLengthWriter struct {
|
||||
Writer
|
||||
}
|
||||
|
||||
func (e *ExampleFrameLengthWriter) Write(m []byte) (int, error) {
|
||||
fmt.Println("writing raw DNS message of length", len(m))
|
||||
return e.Writer.Write(m)
|
||||
}
|
||||
|
||||
func ExampleDecorateWriter() {
|
||||
// instrument raw DNS message writing
|
||||
wf := DecorateWriter(func(w Writer) Writer {
|
||||
return &ExampleFrameLengthWriter{w}
|
||||
})
|
||||
|
||||
// simple UDP server
|
||||
pc, err := net.ListenPacket("udp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
server := &Server{
|
||||
PacketConn: pc,
|
||||
DecorateWriter: wf,
|
||||
}
|
||||
|
||||
waitLock := sync.Mutex{}
|
||||
waitLock.Lock()
|
||||
server.NotifyStartedFunc = waitLock.Unlock
|
||||
defer server.Shutdown()
|
||||
|
||||
go func() {
|
||||
server.ActivateAndServe()
|
||||
pc.Close()
|
||||
}()
|
||||
|
||||
waitLock.Lock()
|
||||
|
||||
HandleFunc("miek.nl.", HelloServer)
|
||||
|
||||
c := new(Client)
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl.", TypeTXT)
|
||||
_, _, err = c.Exchange(m, pc.LocalAddr().String())
|
||||
if err != nil {
|
||||
fmt.Println("failed to exchange", err.Error())
|
||||
return
|
||||
}
|
||||
// Output: writing raw DNS message of length 56
|
||||
}
|
||||
|
|
|
@ -46,7 +46,6 @@ const (
|
|||
TypeX25 uint16 = 19
|
||||
TypeISDN uint16 = 20
|
||||
TypeRT uint16 = 21
|
||||
TypeNSAP uint16 = 22
|
||||
TypeNSAPPTR uint16 = 23
|
||||
TypeSIG uint16 = 24
|
||||
TypeKEY uint16 = 25
|
||||
|
@ -503,7 +502,7 @@ func sprintName(s string) string {
|
|||
return string(dst)
|
||||
}
|
||||
|
||||
func sprintCAAValue(s string) string {
|
||||
func sprintTxtOctet(s string) string {
|
||||
src := []byte(s)
|
||||
dst := make([]byte, 0, len(src))
|
||||
dst = append(dst, '"')
|
||||
|
@ -850,7 +849,6 @@ func cmToM(m, e uint8) string {
|
|||
return s
|
||||
}
|
||||
|
||||
// String returns a string version of a LOC
|
||||
func (rr *LOC) String() string {
|
||||
s := rr.Hdr.String()
|
||||
|
||||
|
@ -1180,16 +1178,6 @@ func (rr *RKEY) String() string {
|
|||
" " + rr.PublicKey
|
||||
}
|
||||
|
||||
type NSAP struct {
|
||||
Hdr RR_Header
|
||||
Nsap string
|
||||
}
|
||||
|
||||
func (rr *NSAP) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *NSAP) copy() RR { return &NSAP{*rr.Hdr.copyHeader(), rr.Nsap} }
|
||||
func (rr *NSAP) String() string { return rr.Hdr.String() + "0x" + rr.Nsap }
|
||||
func (rr *NSAP) len() int { return rr.Hdr.len() + 1 + len(rr.Nsap) + 1 }
|
||||
|
||||
type NSAPPTR struct {
|
||||
Hdr RR_Header
|
||||
Ptr string `dns:"domain-name"`
|
||||
|
@ -1329,27 +1317,15 @@ type URI struct {
|
|||
Hdr RR_Header
|
||||
Priority uint16
|
||||
Weight uint16
|
||||
Target []string `dns:"txt"`
|
||||
Target string `dns:"octet"`
|
||||
}
|
||||
|
||||
func (rr *URI) Header() *RR_Header { return &rr.Hdr }
|
||||
func (rr *URI) copy() RR {
|
||||
cp := make([]string, len(rr.Target), cap(rr.Target))
|
||||
copy(cp, rr.Target)
|
||||
return &URI{*rr.Hdr.copyHeader(), rr.Weight, rr.Priority, cp}
|
||||
}
|
||||
|
||||
func (rr *URI) copy() RR { return &URI{*rr.Hdr.copyHeader(), rr.Weight, rr.Priority, rr.Target} }
|
||||
func (rr *URI) len() int { return rr.Hdr.len() + 4 + len(rr.Target) }
|
||||
func (rr *URI) String() string {
|
||||
return rr.Hdr.String() + strconv.Itoa(int(rr.Priority)) +
|
||||
" " + strconv.Itoa(int(rr.Weight)) + sprintTxt(rr.Target)
|
||||
}
|
||||
|
||||
func (rr *URI) len() int {
|
||||
l := rr.Hdr.len() + 4
|
||||
for _, t := range rr.Target {
|
||||
l += len(t) + 1
|
||||
}
|
||||
return l
|
||||
" " + strconv.Itoa(int(rr.Weight)) + " " + sprintTxtOctet(rr.Target)
|
||||
}
|
||||
|
||||
type DHCID struct {
|
||||
|
@ -1571,7 +1547,7 @@ func (rr *CAA) Header() *RR_Header { return &rr.Hdr }
|
|||
func (rr *CAA) copy() RR { return &CAA{*rr.Hdr.copyHeader(), rr.Flag, rr.Tag, rr.Value} }
|
||||
func (rr *CAA) len() int { return rr.Hdr.len() + 2 + len(rr.Tag) + len(rr.Value) }
|
||||
func (rr *CAA) String() string {
|
||||
return rr.Hdr.String() + strconv.Itoa(int(rr.Flag)) + " " + rr.Tag + " " + sprintCAAValue(rr.Value)
|
||||
return rr.Hdr.String() + strconv.Itoa(int(rr.Flag)) + " " + rr.Tag + " " + sprintTxtOctet(rr.Value)
|
||||
}
|
||||
|
||||
type UID struct {
|
||||
|
@ -1733,7 +1709,6 @@ var typeToRR = map[uint16]func() RR{
|
|||
TypeNINFO: func() RR { return new(NINFO) },
|
||||
TypeNIMLOC: func() RR { return new(NIMLOC) },
|
||||
TypeNS: func() RR { return new(NS) },
|
||||
TypeNSAP: func() RR { return new(NSAP) },
|
||||
TypeNSAPPTR: func() RR { return new(NSAPPTR) },
|
||||
TypeNSEC3: func() RR { return new(NSEC3) },
|
||||
TypeNSEC3PARAM: func() RR { return new(NSEC3PARAM) },
|
||||
|
|
|
@ -141,11 +141,11 @@ func modToPrintf(s string) (string, int, string) {
|
|||
return "", 0, "bad base in $GENERATE"
|
||||
}
|
||||
offset, err := strconv.Atoi(xs[0])
|
||||
if err != nil {
|
||||
if err != nil || offset > 255 {
|
||||
return "", 0, "bad offset in $GENERATE"
|
||||
}
|
||||
width, err := strconv.Atoi(xs[1])
|
||||
if err != nil {
|
||||
if err != nil || width > 255 {
|
||||
return "", offset, "bad width in $GENERATE"
|
||||
}
|
||||
switch {
|
||||
|
|
|
@ -131,11 +131,11 @@ func ReadRR(q io.Reader, filename string) (RR, error) {
|
|||
return r.RR, nil
|
||||
}
|
||||
|
||||
// ParseZone reads a RFC 1035 style one from r. It returns *Tokens on the
|
||||
// ParseZone reads a RFC 1035 style zonefile from r. It returns *Tokens on the
|
||||
// returned channel, which consist out the parsed RR, a potential comment or an error.
|
||||
// If there is an error the RR is nil. The string file is only used
|
||||
// in error reporting. The string origin is used as the initial origin, as
|
||||
// if the file would start with: $ORIGIN origin .
|
||||
// if the file would start with: $ORIGIN origin .
|
||||
// The directives $INCLUDE, $ORIGIN, $TTL and $GENERATE are supported.
|
||||
// The channel t is closed by ParseZone when the end of r is reached.
|
||||
//
|
||||
|
@ -152,7 +152,7 @@ func ReadRR(q io.Reader, filename string) (RR, error) {
|
|||
//
|
||||
// foo. IN A 10.0.0.1 ; this is a comment
|
||||
//
|
||||
// The text "; this is comment" is returned in Token.Comment . Comments inside the
|
||||
// The text "; this is comment" is returned in Token.Comment. Comments inside the
|
||||
// RR are discarded. Comments on a line by themselves are discarded too.
|
||||
func ParseZone(r io.Reader, origin, file string) chan *Token {
|
||||
return parseZoneHelper(r, origin, file, 10000)
|
||||
|
@ -281,7 +281,7 @@ func parseZone(r io.Reader, origin, f string, t chan *Token, include int) {
|
|||
case zBlank:
|
||||
l := <-c
|
||||
if l.value == zString {
|
||||
if _, ok := IsDomainName(l.token); !ok {
|
||||
if _, ok := IsDomainName(l.token); !ok || l.length == 0 || l.err {
|
||||
t <- &Token{Error: &ParseError{f, "bad origin name", l}}
|
||||
return
|
||||
}
|
||||
|
@ -806,7 +806,11 @@ func zlexer(s *scan, c chan lex) {
|
|||
|
||||
// Extract the class number from CLASSxx
|
||||
func classToInt(token string) (uint16, bool) {
|
||||
class, ok := strconv.Atoi(token[5:])
|
||||
offset := 5
|
||||
if len(token) < offset+1 {
|
||||
return 0, false
|
||||
}
|
||||
class, ok := strconv.Atoi(token[offset:])
|
||||
if ok != nil || class > maxUint16 {
|
||||
return 0, false
|
||||
}
|
||||
|
@ -815,7 +819,11 @@ func classToInt(token string) (uint16, bool) {
|
|||
|
||||
// Extract the rr number from TYPExxx
|
||||
func typeToInt(token string) (uint16, bool) {
|
||||
typ, ok := strconv.Atoi(token[4:])
|
||||
offset := 4
|
||||
if len(token) < offset+1 {
|
||||
return 0, false
|
||||
}
|
||||
typ, ok := strconv.Atoi(token[offset:])
|
||||
if ok != nil || typ > maxUint16 {
|
||||
return 0, false
|
||||
}
|
||||
|
|
|
@ -145,7 +145,7 @@ func setA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
rr.A = net.ParseIP(l.token)
|
||||
if rr.A == nil {
|
||||
if rr.A == nil || l.err {
|
||||
return nil, &ParseError{f, "bad A A", l}, ""
|
||||
}
|
||||
return rr, nil, ""
|
||||
|
@ -160,7 +160,7 @@ func setAAAA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
rr.AAAA = net.ParseIP(l.token)
|
||||
if rr.AAAA == nil {
|
||||
if rr.AAAA == nil || l.err {
|
||||
return nil, &ParseError{f, "bad AAAA AAAA", l}, ""
|
||||
}
|
||||
return rr, nil, ""
|
||||
|
@ -180,7 +180,7 @@ func setNS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad NS Ns", l}, ""
|
||||
}
|
||||
if rr.Ns[l.length-1] != '.' {
|
||||
|
@ -203,7 +203,7 @@ func setPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad PTR Ptr", l}, ""
|
||||
}
|
||||
if rr.Ptr[l.length-1] != '.' {
|
||||
|
@ -226,7 +226,7 @@ func setNSAPPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string)
|
|||
return rr, nil, ""
|
||||
}
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad NSAP-PTR Ptr", l}, ""
|
||||
}
|
||||
if rr.Ptr[l.length-1] != '.' {
|
||||
|
@ -248,7 +248,7 @@ func setRP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
rr.Mbox = o
|
||||
} else {
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad RP Mbox", l}, ""
|
||||
}
|
||||
if rr.Mbox[l.length-1] != '.' {
|
||||
|
@ -263,7 +263,7 @@ func setRP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad RP Txt", l}, ""
|
||||
}
|
||||
if rr.Txt[l.length-1] != '.' {
|
||||
|
@ -286,7 +286,7 @@ func setMR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad MR Mr", l}, ""
|
||||
}
|
||||
if rr.Mr[l.length-1] != '.' {
|
||||
|
@ -309,7 +309,7 @@ func setMB(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad MB Mb", l}, ""
|
||||
}
|
||||
if rr.Mb[l.length-1] != '.' {
|
||||
|
@ -332,7 +332,7 @@ func setMG(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad MG Mg", l}, ""
|
||||
}
|
||||
if rr.Mg[l.length-1] != '.' {
|
||||
|
@ -380,7 +380,7 @@ func setMINFO(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
rr.Rmail = o
|
||||
} else {
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad MINFO Rmail", l}, ""
|
||||
}
|
||||
if rr.Rmail[l.length-1] != '.' {
|
||||
|
@ -395,7 +395,7 @@ func setMINFO(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad MINFO Email", l}, ""
|
||||
}
|
||||
if rr.Email[l.length-1] != '.' {
|
||||
|
@ -418,7 +418,7 @@ func setMF(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad MF Mf", l}, ""
|
||||
}
|
||||
if rr.Mf[l.length-1] != '.' {
|
||||
|
@ -441,7 +441,7 @@ func setMD(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad MD Md", l}, ""
|
||||
}
|
||||
if rr.Md[l.length-1] != '.' {
|
||||
|
@ -459,7 +459,7 @@ func setMX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
i, e := strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad MX Pref", l}, ""
|
||||
}
|
||||
rr.Preference = uint16(i)
|
||||
|
@ -471,7 +471,7 @@ func setMX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok || l.length == 0 {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad MX Mx", l}, ""
|
||||
}
|
||||
if rr.Mx[l.length-1] != '.' {
|
||||
|
@ -500,7 +500,7 @@ func setRT(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok || l.length == 0 {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad RT Host", l}, ""
|
||||
}
|
||||
if rr.Host[l.length-1] != '.' {
|
||||
|
@ -518,7 +518,7 @@ func setAFSDB(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
i, e := strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad AFSDB Subtype", l}, ""
|
||||
}
|
||||
rr.Subtype = uint16(i)
|
||||
|
@ -530,7 +530,7 @@ func setAFSDB(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok || l.length == 0 {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad AFSDB Hostname", l}, ""
|
||||
}
|
||||
if rr.Hostname[l.length-1] != '.' {
|
||||
|
@ -544,6 +544,12 @@ func setX25(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
rr.Hdr = h
|
||||
|
||||
l := <-c
|
||||
if l.length == 0 {
|
||||
return rr, nil, ""
|
||||
}
|
||||
if l.err {
|
||||
return nil, &ParseError{f, "bad X25 PSDNAddress", l}, ""
|
||||
}
|
||||
rr.PSDNAddress = l.token
|
||||
return rr, nil, ""
|
||||
}
|
||||
|
@ -557,7 +563,7 @@ func setKX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
i, e := strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad KX Pref", l}, ""
|
||||
}
|
||||
rr.Preference = uint16(i)
|
||||
|
@ -569,7 +575,7 @@ func setKX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok || l.length == 0 {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad KX Exchanger", l}, ""
|
||||
}
|
||||
if rr.Exchanger[l.length-1] != '.' {
|
||||
|
@ -592,7 +598,7 @@ func setCNAME(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok || l.length == 0 {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad CNAME Target", l}, ""
|
||||
}
|
||||
if rr.Target[l.length-1] != '.' {
|
||||
|
@ -615,7 +621,7 @@ func setDNAME(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad CNAME Target", l}, ""
|
||||
}
|
||||
if rr.Target[l.length-1] != '.' {
|
||||
|
@ -638,7 +644,7 @@ func setSOA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
rr.Ns = o
|
||||
} else {
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad SOA Ns", l}, ""
|
||||
}
|
||||
if rr.Ns[l.length-1] != '.' {
|
||||
|
@ -652,7 +658,7 @@ func setSOA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
rr.Mbox = o
|
||||
} else {
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok || l.length == 0 {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad SOA Mbox", l}, ""
|
||||
}
|
||||
if rr.Mbox[l.length-1] != '.' {
|
||||
|
@ -667,6 +673,9 @@ func setSOA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
)
|
||||
for i := 0; i < 5; i++ {
|
||||
l = <-c
|
||||
if l.err {
|
||||
return nil, &ParseError{f, "bad SOA zone parameter", l}, ""
|
||||
}
|
||||
if j, e := strconv.Atoi(l.token); e != nil {
|
||||
if i == 0 {
|
||||
// Serial should be a number
|
||||
|
@ -708,21 +717,21 @@ func setSRV(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
i, e := strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad SRV Priority", l}, ""
|
||||
}
|
||||
rr.Priority = uint16(i)
|
||||
<-c // zBlank
|
||||
l = <-c // zString
|
||||
i, e = strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad SRV Weight", l}, ""
|
||||
}
|
||||
rr.Weight = uint16(i)
|
||||
<-c // zBlank
|
||||
l = <-c // zString
|
||||
i, e = strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad SRV Port", l}, ""
|
||||
}
|
||||
rr.Port = uint16(i)
|
||||
|
@ -734,7 +743,7 @@ func setSRV(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok || l.length == 0 {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad SRV Target", l}, ""
|
||||
}
|
||||
if rr.Target[l.length-1] != '.' {
|
||||
|
@ -752,14 +761,14 @@ func setNAPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
i, e := strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad NAPTR Order", l}, ""
|
||||
}
|
||||
rr.Order = uint16(i)
|
||||
<-c // zBlank
|
||||
l = <-c // zString
|
||||
i, e = strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad NAPTR Preference", l}, ""
|
||||
}
|
||||
rr.Preference = uint16(i)
|
||||
|
@ -828,7 +837,7 @@ func setNAPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok || l.length == 0 {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad NAPTR Replacement", l}, ""
|
||||
}
|
||||
if rr.Replacement[l.length-1] != '.' {
|
||||
|
@ -850,7 +859,7 @@ func setTALINK(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
rr.PreviousName = o
|
||||
} else {
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad TALINK PreviousName", l}, ""
|
||||
}
|
||||
if rr.PreviousName[l.length-1] != '.' {
|
||||
|
@ -865,7 +874,7 @@ func setTALINK(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok || l.length == 0 {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad TALINK NextName", l}, ""
|
||||
}
|
||||
if rr.NextName[l.length-1] != '.' {
|
||||
|
@ -887,7 +896,7 @@ func setLOC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
if l.length == 0 {
|
||||
return rr, nil, ""
|
||||
}
|
||||
if i, e := strconv.Atoi(l.token); e != nil {
|
||||
if i, e := strconv.Atoi(l.token); e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad LOC Latitude", l}, ""
|
||||
} else {
|
||||
rr.Latitude = 1000 * 60 * 60 * uint32(i)
|
||||
|
@ -898,14 +907,14 @@ func setLOC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
if rr.Latitude, ok = locCheckNorth(l.token, rr.Latitude); ok {
|
||||
goto East
|
||||
}
|
||||
if i, e := strconv.Atoi(l.token); e != nil {
|
||||
if i, e := strconv.Atoi(l.token); e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad LOC Latitude minutes", l}, ""
|
||||
} else {
|
||||
rr.Latitude += 1000 * 60 * uint32(i)
|
||||
}
|
||||
<-c // zBlank
|
||||
l = <-c
|
||||
if i, e := strconv.ParseFloat(l.token, 32); e != nil {
|
||||
if i, e := strconv.ParseFloat(l.token, 32); e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad LOC Latitude seconds", l}, ""
|
||||
} else {
|
||||
rr.Latitude += uint32(1000 * i)
|
||||
|
@ -923,7 +932,7 @@ East:
|
|||
// East
|
||||
<-c // zBlank
|
||||
l = <-c
|
||||
if i, e := strconv.Atoi(l.token); e != nil {
|
||||
if i, e := strconv.Atoi(l.token); e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad LOC Longitude", l}, ""
|
||||
} else {
|
||||
rr.Longitude = 1000 * 60 * 60 * uint32(i)
|
||||
|
@ -934,14 +943,14 @@ East:
|
|||
if rr.Longitude, ok = locCheckEast(l.token, rr.Longitude); ok {
|
||||
goto Altitude
|
||||
}
|
||||
if i, e := strconv.Atoi(l.token); e != nil {
|
||||
if i, e := strconv.Atoi(l.token); e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad LOC Longitude minutes", l}, ""
|
||||
} else {
|
||||
rr.Longitude += 1000 * 60 * uint32(i)
|
||||
}
|
||||
<-c // zBlank
|
||||
l = <-c
|
||||
if i, e := strconv.ParseFloat(l.token, 32); e != nil {
|
||||
if i, e := strconv.ParseFloat(l.token, 32); e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad LOC Longitude seconds", l}, ""
|
||||
} else {
|
||||
rr.Longitude += uint32(1000 * i)
|
||||
|
@ -958,6 +967,9 @@ East:
|
|||
Altitude:
|
||||
<-c // zBlank
|
||||
l = <-c
|
||||
if l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad LOC Altitude", l}, ""
|
||||
}
|
||||
if l.token[len(l.token)-1] == 'M' || l.token[len(l.token)-1] == 'm' {
|
||||
l.token = l.token[0 : len(l.token)-1]
|
||||
}
|
||||
|
@ -1014,17 +1026,23 @@ func setHIP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, l.comment
|
||||
}
|
||||
i, e := strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad HIP PublicKeyAlgorithm", l}, ""
|
||||
}
|
||||
rr.PublicKeyAlgorithm = uint8(i)
|
||||
<-c // zBlank
|
||||
l = <-c // zString
|
||||
<-c // zBlank
|
||||
l = <-c // zString
|
||||
if l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad HIP Hit", l}, ""
|
||||
}
|
||||
rr.Hit = l.token // This can not contain spaces, see RFC 5205 Section 6.
|
||||
rr.HitLength = uint8(len(rr.Hit)) / 2
|
||||
|
||||
<-c // zBlank
|
||||
l = <-c // zString
|
||||
<-c // zBlank
|
||||
l = <-c // zString
|
||||
if l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad HIP PublicKey", l}, ""
|
||||
}
|
||||
rr.PublicKey = l.token // This cannot contain spaces
|
||||
rr.PublicKeyLength = uint16(base64.StdEncoding.DecodedLen(len(rr.PublicKey)))
|
||||
|
||||
|
@ -1036,10 +1054,11 @@ func setHIP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
case zString:
|
||||
if l.token == "@" {
|
||||
xs = append(xs, o)
|
||||
l = <-c
|
||||
continue
|
||||
}
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok || l.length == 0 {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad HIP RendezvousServers", l}, ""
|
||||
}
|
||||
if l.token[l.length-1] != '.' {
|
||||
|
@ -1075,7 +1094,7 @@ func setCERT(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
<-c // zBlank
|
||||
l = <-c // zString
|
||||
i, e := strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad CERT KeyTag", l}, ""
|
||||
}
|
||||
rr.KeyTag = uint16(i)
|
||||
|
@ -1139,21 +1158,21 @@ func setRRSIG(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
<-c // zBlank
|
||||
l = <-c
|
||||
i, err := strconv.Atoi(l.token)
|
||||
if err != nil {
|
||||
if err != nil || l.err {
|
||||
return nil, &ParseError{f, "bad RRSIG Algorithm", l}, ""
|
||||
}
|
||||
rr.Algorithm = uint8(i)
|
||||
<-c // zBlank
|
||||
l = <-c
|
||||
i, err = strconv.Atoi(l.token)
|
||||
if err != nil {
|
||||
if err != nil || l.err {
|
||||
return nil, &ParseError{f, "bad RRSIG Labels", l}, ""
|
||||
}
|
||||
rr.Labels = uint8(i)
|
||||
<-c // zBlank
|
||||
l = <-c
|
||||
i, err = strconv.Atoi(l.token)
|
||||
if err != nil {
|
||||
if err != nil || l.err {
|
||||
return nil, &ParseError{f, "bad RRSIG OrigTtl", l}, ""
|
||||
}
|
||||
rr.OrigTtl = uint32(i)
|
||||
|
@ -1184,7 +1203,7 @@ func setRRSIG(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
<-c // zBlank
|
||||
l = <-c
|
||||
i, err = strconv.Atoi(l.token)
|
||||
if err != nil {
|
||||
if err != nil || l.err {
|
||||
return nil, &ParseError{f, "bad RRSIG KeyTag", l}, ""
|
||||
}
|
||||
rr.KeyTag = uint16(i)
|
||||
|
@ -1195,7 +1214,7 @@ func setRRSIG(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
rr.SignerName = o
|
||||
} else {
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok || l.length == 0 {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad RRSIG SignerName", l}, ""
|
||||
}
|
||||
if rr.SignerName[l.length-1] != '.' {
|
||||
|
@ -1223,7 +1242,7 @@ func setNSEC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
rr.NextDomain = o
|
||||
} else {
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad NSEC NextDomain", l}, ""
|
||||
}
|
||||
if rr.NextDomain[l.length-1] != '.' {
|
||||
|
@ -1265,27 +1284,27 @@ func setNSEC3(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, l.comment
|
||||
}
|
||||
i, e := strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad NSEC3 Hash", l}, ""
|
||||
}
|
||||
rr.Hash = uint8(i)
|
||||
<-c // zBlank
|
||||
l = <-c
|
||||
i, e = strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad NSEC3 Flags", l}, ""
|
||||
}
|
||||
rr.Flags = uint8(i)
|
||||
<-c // zBlank
|
||||
l = <-c
|
||||
i, e = strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad NSEC3 Iterations", l}, ""
|
||||
}
|
||||
rr.Iterations = uint16(i)
|
||||
<-c
|
||||
l = <-c
|
||||
if len(l.token) == 0 {
|
||||
if len(l.token) == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad NSEC3 Salt", l}, ""
|
||||
}
|
||||
rr.SaltLength = uint8(len(l.token)) / 2
|
||||
|
@ -1293,6 +1312,9 @@ func setNSEC3(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
|
||||
<-c
|
||||
l = <-c
|
||||
if len(l.token) == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad NSEC3 NextDomain", l}, ""
|
||||
}
|
||||
rr.HashLength = 20 // Fix for NSEC3 (sha1 160 bits)
|
||||
rr.NextDomain = l.token
|
||||
|
||||
|
@ -1330,21 +1352,21 @@ func setNSEC3PARAM(h RR_Header, c chan lex, o, f string) (RR, *ParseError, strin
|
|||
return rr, nil, ""
|
||||
}
|
||||
i, e := strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad NSEC3PARAM Hash", l}, ""
|
||||
}
|
||||
rr.Hash = uint8(i)
|
||||
<-c // zBlank
|
||||
l = <-c
|
||||
i, e = strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad NSEC3PARAM Flags", l}, ""
|
||||
}
|
||||
rr.Flags = uint8(i)
|
||||
<-c // zBlank
|
||||
l = <-c
|
||||
i, e = strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad NSEC3PARAM Iterations", l}, ""
|
||||
}
|
||||
rr.Iterations = uint16(i)
|
||||
|
@ -1363,7 +1385,7 @@ func setEUI48(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
if l.length == 0 {
|
||||
return rr, nil, ""
|
||||
}
|
||||
if l.length != 17 {
|
||||
if l.length != 17 || l.err {
|
||||
return nil, &ParseError{f, "bad EUI48 Address", l}, ""
|
||||
}
|
||||
addr := make([]byte, 12)
|
||||
|
@ -1395,7 +1417,7 @@ func setEUI64(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
if l.length == 0 {
|
||||
return rr, nil, ""
|
||||
}
|
||||
if l.length != 23 {
|
||||
if l.length != 23 || l.err {
|
||||
return nil, &ParseError{f, "bad EUI64 Address", l}, ""
|
||||
}
|
||||
addr := make([]byte, 16)
|
||||
|
@ -1428,7 +1450,7 @@ func setWKS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, l.comment
|
||||
}
|
||||
rr.Address = net.ParseIP(l.token)
|
||||
if rr.Address == nil {
|
||||
if rr.Address == nil || l.err {
|
||||
return nil, &ParseError{f, "bad WKS Address", l}, ""
|
||||
}
|
||||
|
||||
|
@ -1436,7 +1458,7 @@ func setWKS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
l = <-c
|
||||
proto := "tcp"
|
||||
i, e := strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad WKS Protocol", l}, ""
|
||||
}
|
||||
rr.Protocol = uint8(i)
|
||||
|
@ -1486,14 +1508,14 @@ func setSSHFP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
i, e := strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad SSHFP Algorithm", l}, ""
|
||||
}
|
||||
rr.Algorithm = uint8(i)
|
||||
<-c // zBlank
|
||||
l = <-c
|
||||
i, e = strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad SSHFP Type", l}, ""
|
||||
}
|
||||
rr.Type = uint8(i)
|
||||
|
@ -1515,21 +1537,21 @@ func setDNSKEYs(h RR_Header, c chan lex, o, f, typ string) (RR, *ParseError, str
|
|||
return rr, nil, l.comment
|
||||
}
|
||||
i, e := strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad " + typ + " Flags", l}, ""
|
||||
}
|
||||
rr.Flags = uint16(i)
|
||||
<-c // zBlank
|
||||
l = <-c // zString
|
||||
i, e = strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad " + typ + " Protocol", l}, ""
|
||||
}
|
||||
rr.Protocol = uint8(i)
|
||||
<-c // zBlank
|
||||
l = <-c // zString
|
||||
i, e = strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad " + typ + " Algorithm", l}, ""
|
||||
}
|
||||
rr.Algorithm = uint8(i)
|
||||
|
@ -1571,21 +1593,21 @@ func setRKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, l.comment
|
||||
}
|
||||
i, e := strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad RKEY Flags", l}, ""
|
||||
}
|
||||
rr.Flags = uint16(i)
|
||||
<-c // zBlank
|
||||
l = <-c // zString
|
||||
i, e = strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad RKEY Protocol", l}, ""
|
||||
}
|
||||
rr.Protocol = uint8(i)
|
||||
<-c // zBlank
|
||||
l = <-c // zString
|
||||
i, e = strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad RKEY Algorithm", l}, ""
|
||||
}
|
||||
rr.Algorithm = uint8(i)
|
||||
|
@ -1619,34 +1641,6 @@ func setNIMLOC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, c1
|
||||
}
|
||||
|
||||
func setNSAP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
||||
rr := new(NSAP)
|
||||
rr.Hdr = h
|
||||
chunks, e1, c1 := endingToTxtSlice(c, "bad NSAP Nsap", f)
|
||||
if e1 != nil {
|
||||
return nil, e1, c1
|
||||
}
|
||||
// data would come as one string or multiple... Just to ignore possible
|
||||
// variety let's merge things back together and split to actual "words"
|
||||
s := strings.Fields(strings.Join(chunks, " "))
|
||||
if len(s) == 0 {
|
||||
return rr, nil, c1
|
||||
}
|
||||
if len(s[0]) >= 2 && s[0][0:2] == "0x" || s[0][0:2] == "0X" {
|
||||
// although RFC only suggests 0x there is no clarification that X is not allowed
|
||||
rr.Nsap = strings.Join(s, "")[2:]
|
||||
} else {
|
||||
// since we do not know what to do with this data, and, we would not use original length
|
||||
// in formatting, it's moot to check correctness of the length
|
||||
_, err := strconv.Atoi(s[0])
|
||||
if err != nil {
|
||||
return nil, &ParseError{f, "bad NSAP Length", lex{token: s[0]}}, ""
|
||||
}
|
||||
rr.Nsap = strings.Join(s[1:], "")
|
||||
}
|
||||
return rr, nil, c1
|
||||
}
|
||||
|
||||
func setGPOS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
||||
rr := new(GPOS)
|
||||
rr.Hdr = h
|
||||
|
@ -1655,21 +1649,21 @@ func setGPOS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
_, e := strconv.ParseFloat(l.token, 64)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad GPOS Longitude", l}, ""
|
||||
}
|
||||
rr.Longitude = l.token
|
||||
<-c // zBlank
|
||||
l = <-c
|
||||
_, e = strconv.ParseFloat(l.token, 64)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad GPOS Latitude", l}, ""
|
||||
}
|
||||
rr.Latitude = l.token
|
||||
<-c // zBlank
|
||||
l = <-c
|
||||
_, e = strconv.ParseFloat(l.token, 64)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad GPOS Altitude", l}, ""
|
||||
}
|
||||
rr.Altitude = l.token
|
||||
|
@ -1684,7 +1678,7 @@ func setDSs(h RR_Header, c chan lex, o, f, typ string) (RR, *ParseError, string)
|
|||
return rr, nil, l.comment
|
||||
}
|
||||
i, e := strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad " + typ + " KeyTag", l}, ""
|
||||
}
|
||||
rr.KeyTag = uint16(i)
|
||||
|
@ -1692,7 +1686,7 @@ func setDSs(h RR_Header, c chan lex, o, f, typ string) (RR, *ParseError, string)
|
|||
l = <-c
|
||||
if i, e := strconv.Atoi(l.token); e != nil {
|
||||
i, ok := StringToAlgorithm[l.tokenUpper]
|
||||
if !ok {
|
||||
if !ok || l.err {
|
||||
return nil, &ParseError{f, "bad " + typ + " Algorithm", l}, ""
|
||||
}
|
||||
rr.Algorithm = i
|
||||
|
@ -1702,7 +1696,7 @@ func setDSs(h RR_Header, c chan lex, o, f, typ string) (RR, *ParseError, string)
|
|||
<-c // zBlank
|
||||
l = <-c
|
||||
i, e = strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad " + typ + " DigestType", l}, ""
|
||||
}
|
||||
rr.DigestType = uint8(i)
|
||||
|
@ -1743,7 +1737,7 @@ func setTA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, l.comment
|
||||
}
|
||||
i, e := strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad TA KeyTag", l}, ""
|
||||
}
|
||||
rr.KeyTag = uint16(i)
|
||||
|
@ -1751,7 +1745,7 @@ func setTA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
l = <-c
|
||||
if i, e := strconv.Atoi(l.token); e != nil {
|
||||
i, ok := StringToAlgorithm[l.tokenUpper]
|
||||
if !ok {
|
||||
if !ok || l.err {
|
||||
return nil, &ParseError{f, "bad TA Algorithm", l}, ""
|
||||
}
|
||||
rr.Algorithm = i
|
||||
|
@ -1761,7 +1755,7 @@ func setTA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
<-c // zBlank
|
||||
l = <-c
|
||||
i, e = strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad TA DigestType", l}, ""
|
||||
}
|
||||
rr.DigestType = uint8(i)
|
||||
|
@ -1781,21 +1775,21 @@ func setTLSA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, l.comment
|
||||
}
|
||||
i, e := strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad TLSA Usage", l}, ""
|
||||
}
|
||||
rr.Usage = uint8(i)
|
||||
<-c // zBlank
|
||||
l = <-c
|
||||
i, e = strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad TLSA Selector", l}, ""
|
||||
}
|
||||
rr.Selector = uint8(i)
|
||||
<-c // zBlank
|
||||
l = <-c
|
||||
i, e = strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad TLSA MatchingType", l}, ""
|
||||
}
|
||||
rr.MatchingType = uint8(i)
|
||||
|
@ -1818,7 +1812,7 @@ func setRFC3597(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string)
|
|||
<-c // zBlank
|
||||
l = <-c
|
||||
rdlength, e := strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad RFC3597 Rdata ", l}, ""
|
||||
}
|
||||
|
||||
|
@ -1876,28 +1870,32 @@ func setURI(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
rr.Hdr = h
|
||||
|
||||
l := <-c
|
||||
if l.length == 0 {
|
||||
return rr, nil, l.comment
|
||||
if l.length == 0 { // Dynamic updates.
|
||||
return rr, nil, ""
|
||||
}
|
||||
|
||||
i, e := strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad URI Priority", l}, ""
|
||||
}
|
||||
rr.Priority = uint16(i)
|
||||
<-c // zBlank
|
||||
l = <-c
|
||||
i, e = strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad URI Weight", l}, ""
|
||||
}
|
||||
rr.Weight = uint16(i)
|
||||
|
||||
<-c // zBlank
|
||||
s, e, c1 := endingToTxtSlice(c, "bad URI Target", f)
|
||||
if e != nil {
|
||||
return nil, e.(*ParseError), ""
|
||||
s, err, c1 := endingToTxtSlice(c, "bad URI Target", f)
|
||||
if err != nil {
|
||||
return nil, err, ""
|
||||
}
|
||||
rr.Target = s
|
||||
if len(s) > 1 {
|
||||
return nil, &ParseError{f, "bad URI Target", l}, ""
|
||||
}
|
||||
rr.Target = s[0]
|
||||
return rr, nil, c1
|
||||
}
|
||||
|
||||
|
@ -1923,14 +1921,14 @@ func setNID(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
i, e := strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad NID Preference", l}, ""
|
||||
}
|
||||
rr.Preference = uint16(i)
|
||||
<-c // zBlank
|
||||
l = <-c // zString
|
||||
u, err := stringToNodeID(l)
|
||||
if err != nil {
|
||||
if err != nil || l.err {
|
||||
return nil, err, ""
|
||||
}
|
||||
rr.NodeID = u
|
||||
|
@ -1946,14 +1944,14 @@ func setL32(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
i, e := strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad L32 Preference", l}, ""
|
||||
}
|
||||
rr.Preference = uint16(i)
|
||||
<-c // zBlank
|
||||
l = <-c // zString
|
||||
rr.Locator32 = net.ParseIP(l.token)
|
||||
if rr.Locator32 == nil {
|
||||
if rr.Locator32 == nil || l.err {
|
||||
return nil, &ParseError{f, "bad L32 Locator", l}, ""
|
||||
}
|
||||
return rr, nil, ""
|
||||
|
@ -1968,7 +1966,7 @@ func setLP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
i, e := strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad LP Preference", l}, ""
|
||||
}
|
||||
rr.Preference = uint16(i)
|
||||
|
@ -1983,7 +1981,7 @@ func setLP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok || l.length == 0 {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad LP Fqdn", l}, ""
|
||||
}
|
||||
if rr.Fqdn[l.length-1] != '.' {
|
||||
|
@ -2001,14 +1999,14 @@ func setL64(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
i, e := strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad L64 Preference", l}, ""
|
||||
}
|
||||
rr.Preference = uint16(i)
|
||||
<-c // zBlank
|
||||
l = <-c // zString
|
||||
u, err := stringToNodeID(l)
|
||||
if err != nil {
|
||||
if err != nil || l.err {
|
||||
return nil, err, ""
|
||||
}
|
||||
rr.Locator64 = u
|
||||
|
@ -2023,7 +2021,7 @@ func setUID(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
i, e := strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad UID Uid", l}, ""
|
||||
}
|
||||
rr.Uid = uint32(i)
|
||||
|
@ -2038,7 +2036,7 @@ func setGID(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
i, e := strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad GID Gid", l}, ""
|
||||
}
|
||||
rr.Gid = uint32(i)
|
||||
|
@ -2065,7 +2063,7 @@ func setPX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
i, e := strconv.Atoi(l.token)
|
||||
if e != nil {
|
||||
if e != nil || l.err {
|
||||
return nil, &ParseError{f, "bad PX Preference", l}, ""
|
||||
}
|
||||
rr.Preference = uint16(i)
|
||||
|
@ -2080,7 +2078,7 @@ func setPX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad PX Map822", l}, ""
|
||||
}
|
||||
if rr.Map822[l.length-1] != '.' {
|
||||
|
@ -2094,7 +2092,7 @@ func setPX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, ""
|
||||
}
|
||||
_, ok = IsDomainName(l.token)
|
||||
if !ok || l.length == 0 {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad PX Mapx400", l}, ""
|
||||
}
|
||||
if rr.Mapx400[l.length-1] != '.' {
|
||||
|
@ -2111,21 +2109,21 @@ func setIPSECKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string)
|
|||
return rr, nil, l.comment
|
||||
}
|
||||
i, err := strconv.Atoi(l.token)
|
||||
if err != nil {
|
||||
if err != nil || l.err {
|
||||
return nil, &ParseError{f, "bad IPSECKEY Precedence", l}, ""
|
||||
}
|
||||
rr.Precedence = uint8(i)
|
||||
<-c // zBlank
|
||||
l = <-c
|
||||
i, err = strconv.Atoi(l.token)
|
||||
if err != nil {
|
||||
if err != nil || l.err {
|
||||
return nil, &ParseError{f, "bad IPSECKEY GatewayType", l}, ""
|
||||
}
|
||||
rr.GatewayType = uint8(i)
|
||||
<-c // zBlank
|
||||
l = <-c
|
||||
i, err = strconv.Atoi(l.token)
|
||||
if err != nil {
|
||||
if err != nil || l.err {
|
||||
return nil, &ParseError{f, "bad IPSECKEY Algorithm", l}, ""
|
||||
}
|
||||
rr.Algorithm = uint8(i)
|
||||
|
@ -2142,7 +2140,7 @@ func setIPSECKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string)
|
|||
rr.GatewayName = o
|
||||
}
|
||||
_, ok := IsDomainName(l.token)
|
||||
if !ok {
|
||||
if !ok || l.length == 0 || l.err {
|
||||
return nil, &ParseError{f, "bad IPSECKEY GatewayName", l}, ""
|
||||
}
|
||||
if rr.GatewayName[l.length-1] != '.' {
|
||||
|
@ -2178,12 +2176,12 @@ func setCAA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
return rr, nil, l.comment
|
||||
}
|
||||
i, err := strconv.Atoi(l.token)
|
||||
if err != nil {
|
||||
if err != nil || l.err {
|
||||
return nil, &ParseError{f, "bad CAA Flag", l}, ""
|
||||
}
|
||||
rr.Flag = uint8(i)
|
||||
|
||||
<-c // zBlank
|
||||
<-c // zBlank
|
||||
l = <-c // zString
|
||||
if l.value != zString {
|
||||
return nil, &ParseError{f, "bad CAA Tag", l}, ""
|
||||
|
@ -2197,9 +2195,8 @@ func setCAA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
|||
}
|
||||
if len(s) > 1 {
|
||||
return nil, &ParseError{f, "bad CAA Value", l}, ""
|
||||
} else {
|
||||
rr.Value = s[0]
|
||||
}
|
||||
rr.Value = s[0]
|
||||
return rr, nil, c1
|
||||
}
|
||||
|
||||
|
@ -2242,7 +2239,6 @@ var typeToparserFunc = map[uint16]parserFunc{
|
|||
TypeNID: parserFunc{setNID, false},
|
||||
TypeNIMLOC: parserFunc{setNIMLOC, true},
|
||||
TypeNINFO: parserFunc{setNINFO, true},
|
||||
TypeNSAP: parserFunc{setNSAP, true},
|
||||
TypeNSAPPTR: parserFunc{setNSAPPTR, false},
|
||||
TypeNSEC3PARAM: parserFunc{setNSEC3PARAM, false},
|
||||
TypeNSEC3: parserFunc{setNSEC3, true},
|
||||
|
|
|
@ -68,13 +68,6 @@ type responseBytes struct {
|
|||
Response []byte
|
||||
}
|
||||
|
||||
type basicResponseForMarshal struct {
|
||||
TBSResponseData asn1.RawValue
|
||||
SignatureAlgorithm pkix.AlgorithmIdentifier
|
||||
Signature asn1.BitString
|
||||
Certificates []asn1.RawValue `asn1:"explicit,tag:0,optional"`
|
||||
}
|
||||
|
||||
type basicResponse struct {
|
||||
TBSResponseData responseData
|
||||
SignatureAlgorithm pkix.AlgorithmIdentifier
|
||||
|
@ -82,7 +75,7 @@ type basicResponse struct {
|
|||
Certificates []asn1.RawValue `asn1:"explicit,tag:0,optional"`
|
||||
}
|
||||
|
||||
type responseDataForMarshal struct {
|
||||
type responseData struct {
|
||||
Raw asn1.RawContent
|
||||
Version int `asn1:"optional,default:1,explicit,tag:0"`
|
||||
RawResponderName asn1.RawValue `asn1:"optional,explicit,tag:1"`
|
||||
|
@ -91,19 +84,10 @@ type responseDataForMarshal struct {
|
|||
Responses []singleResponse
|
||||
}
|
||||
|
||||
type responseData struct {
|
||||
Raw asn1.RawContent
|
||||
Version int `asn1:"optional,default:1,explicit,tag:0"`
|
||||
ResponderName pkix.RDNSequence `asn1:"optional,explicit,tag:1"`
|
||||
KeyHash []byte `asn1:"optional,explicit,tag:2"`
|
||||
ProducedAt time.Time `asn1:"generalized"`
|
||||
Responses []singleResponse
|
||||
}
|
||||
|
||||
type singleResponse struct {
|
||||
CertID certID
|
||||
Good asn1.Flag `asn1:"tag:0,optional"`
|
||||
Revoked revokedInfo `asn1:"explicit,tag:1,optional"`
|
||||
Revoked revokedInfo `asn1:"tag:1,optional"`
|
||||
Unknown asn1.Flag `asn1:"tag:2,optional"`
|
||||
ThisUpdate time.Time `asn1:"generalized"`
|
||||
NextUpdate time.Time `asn1:"generalized,explicit,tag:0,optional"`
|
||||
|
@ -414,13 +398,13 @@ func ParseResponse(bytes []byte, issuer *x509.Certificate) (*Response, error) {
|
|||
ret.Status = Unknown
|
||||
default:
|
||||
ret.Status = Revoked
|
||||
ret.RevokedAt = time.Time(r.Revoked.RevocationTime)
|
||||
ret.RevokedAt = r.Revoked.RevocationTime
|
||||
ret.RevocationReason = r.Revoked.Reason
|
||||
}
|
||||
|
||||
ret.ProducedAt = time.Time(basicResp.TBSResponseData.ProducedAt)
|
||||
ret.ThisUpdate = time.Time(r.ThisUpdate)
|
||||
ret.NextUpdate = time.Time(r.NextUpdate)
|
||||
ret.ProducedAt = basicResp.TBSResponseData.ProducedAt
|
||||
ret.ThisUpdate = r.ThisUpdate
|
||||
ret.NextUpdate = r.NextUpdate
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
@ -551,12 +535,12 @@ func CreateResponse(issuer, responderCert *x509.Certificate, template Response,
|
|||
}
|
||||
|
||||
responderName := asn1.RawValue{
|
||||
Class: 2,
|
||||
Tag: 1,
|
||||
Class: 2, // context-specific
|
||||
Tag: 1, // explicit tag
|
||||
IsCompound: true,
|
||||
Bytes: responderCert.RawSubject,
|
||||
}
|
||||
tbsResponseData := responseDataForMarshal{
|
||||
tbsResponseData := responseData{
|
||||
Version: 0,
|
||||
RawResponderName: responderName,
|
||||
ProducedAt: time.Now().Truncate(time.Minute).UTC(),
|
||||
|
@ -580,8 +564,8 @@ func CreateResponse(issuer, responderCert *x509.Certificate, template Response,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
response := basicResponseForMarshal{
|
||||
TBSResponseData: asn1.RawValue{FullBytes: tbsResponseDataDER},
|
||||
response := basicResponse{
|
||||
TBSResponseData: tbsResponseData,
|
||||
SignatureAlgorithm: signatureAlgorithm,
|
||||
Signature: asn1.BitString{
|
||||
Bytes: signature,
|
||||
|
|
15
README.md
15
README.md
|
@ -24,11 +24,8 @@ There are several tags available:
|
|||
|
||||
A quick-start method for running a Boulder instance is to use one of the example configurations:
|
||||
|
||||
```
|
||||
> mkdir .boulder-config
|
||||
> cp test/boulder-config.json .boulder-config/config.json
|
||||
> docker run --name=boulder --read-only=true --rm=true -v $(pwd)/.boulder-config:/boulder:ro -p 4000:4000 quay.io/letsencrypt/boulder:latest boulder
|
||||
```
|
||||
docker run -i --name=boulder --read-only=true --rm=true -p 4000:4000 quay.io/letsencrypt/boulder:latest
|
||||
|
||||
|
||||
Alternatively, to run all services locally, using AMQP to pass messages between them, you can use:
|
||||
|
||||
|
@ -119,13 +116,17 @@ The full details of how the various ACME operations happen in Boulder are laid o
|
|||
Dependencies
|
||||
------------
|
||||
|
||||
All dependencies are vendorized under the Godeps directory,
|
||||
All Go dependencies are vendorized under the Godeps directory,
|
||||
both to [make dependency management
|
||||
easier](https://groups.google.com/forum/m/#!topic/golang-dev/nMWoEAG55v8)
|
||||
and to [avoid insecure fallback in go
|
||||
get](https://github.com/golang/go/issues/9637).
|
||||
|
||||
To update dependencies:
|
||||
Local development also requires a RabbitMQ installation and MariaDB
|
||||
10 installation. MariaDB should be run on port 3306 for the
|
||||
default integration tests.
|
||||
|
||||
To update the Go dependencies:
|
||||
|
||||
```
|
||||
# Disable insecure fallback by blocking port 80.
|
||||
|
|
|
@ -84,7 +84,12 @@ func (cadb *CertificateAuthorityDatabaseImpl) IncrementAndGetSerial(tx *gorp.Tra
|
|||
return
|
||||
}
|
||||
|
||||
row := rowObj.(*SerialNumber)
|
||||
row, ok := rowObj.(*SerialNumber)
|
||||
if !ok {
|
||||
err = fmt.Errorf("No serial number found. This is a serious issue")
|
||||
return
|
||||
}
|
||||
|
||||
val = row.Number
|
||||
row.Number = val + 1
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cactus/go-statsd-client/statsd"
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/codegangsta/cli"
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/streadway/amqp"
|
||||
|
||||
"github.com/letsencrypt/boulder/cmd"
|
||||
|
@ -75,6 +76,19 @@ func HandlerTimer(handler http.Handler, stats statsd.Statter) http.Handler {
|
|||
|
||||
func main() {
|
||||
app := cmd.NewAppShell("boulder-wfe")
|
||||
addrFlag := cli.StringFlag{
|
||||
Name: "addr",
|
||||
Value: "",
|
||||
Usage: "if set, overrides the listenAddr setting in the WFE config",
|
||||
EnvVar: "WFE_LISTEN_ADDR",
|
||||
}
|
||||
app.App.Flags = append(app.App.Flags, addrFlag)
|
||||
app.Config = func(c *cli.Context, config cmd.Config) cmd.Config {
|
||||
if c.GlobalString("addr") != "" {
|
||||
config.WFE.ListenAddress = c.GlobalString("addr")
|
||||
}
|
||||
return config
|
||||
}
|
||||
app.Action = func(c cmd.Config) {
|
||||
// Set up logging
|
||||
stats, err := statsd.NewClient(c.Statsd.Server, c.Statsd.Prefix)
|
||||
|
@ -135,6 +149,7 @@ func main() {
|
|||
auditlogger.Info(app.VersionString())
|
||||
|
||||
// Add HandlerTimer to output resp time + success/failure stats to statsd
|
||||
|
||||
auditlogger.Info(fmt.Sprintf("Server running, listening on %s...\n", c.WFE.ListenAddress))
|
||||
err = http.ListenAndServe(c.WFE.ListenAddress, HandlerTimer(h, stats))
|
||||
cmd.FailOnError(err, "Error starting HTTP server")
|
||||
|
|
|
@ -159,7 +159,7 @@ func main() {
|
|||
blog.SetAuditLogger(auditlogger)
|
||||
|
||||
// Configure DB
|
||||
dbMap, err := sa.NewDbMap(c.Common.PolicyDBDriver, c.Common.PolicyDBConnect)
|
||||
dbMap, err := sa.NewDbMap(c.PA.DBDriver, c.PA.DBConnect)
|
||||
cmd.FailOnError(err, "Could not connect to database")
|
||||
|
||||
dbMap.AddTableWithName(core.ExternalCert{}, "externalCerts").SetKeys(false, "SHA1")
|
||||
|
|
30
cmd/shell.go
30
cmd/shell.go
|
@ -170,7 +170,21 @@ type Config struct {
|
|||
StatsdRate float32
|
||||
}
|
||||
|
||||
Common CommonConfig
|
||||
PA struct {
|
||||
DBDriver string
|
||||
DBConnect string
|
||||
EnforcePolicyWhitelist bool
|
||||
}
|
||||
|
||||
Common struct {
|
||||
BaseURL string
|
||||
// Path to a PEM-encoded copy of the issuer certificate.
|
||||
IssuerCert string
|
||||
MaxKeySize int
|
||||
|
||||
DNSResolver string
|
||||
DNSTimeout string
|
||||
}
|
||||
|
||||
SubscriberAgreementURL string
|
||||
}
|
||||
|
@ -211,20 +225,6 @@ type PKCS11Config struct {
|
|||
Label string
|
||||
}
|
||||
|
||||
type CommonConfig struct {
|
||||
BaseURL string
|
||||
// Path to a PEM-encoded copy of the issuer certificate.
|
||||
IssuerCert string
|
||||
MaxKeySize int
|
||||
|
||||
DNSResolver string
|
||||
DNSTimeout string
|
||||
|
||||
PolicyDBDriver string
|
||||
PolicyDBConnect string
|
||||
EnforcePolicyWhitelist bool
|
||||
}
|
||||
|
||||
// TLSConfig reprents certificates and a key for authenticated TLS.
|
||||
type TLSConfig struct {
|
||||
CertFile *string
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/miekg/dns"
|
||||
|
@ -69,9 +70,7 @@ func (dnsResolver *DNSResolverImpl) LookupTXT(hostname string) ([]string, time.D
|
|||
for _, answer := range r.Answer {
|
||||
if answer.Header().Rrtype == dns.TypeTXT {
|
||||
if txtRec, ok := answer.(*dns.TXT); ok {
|
||||
for _, field := range txtRec.Txt {
|
||||
txt = append(txt, field)
|
||||
}
|
||||
txt = append(txt, strings.Join(txtRec.Txt, ""))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,6 +91,13 @@ func mockDNSQuery(w dns.ResponseWriter, r *dns.Msg) {
|
|||
record.Flag = 1
|
||||
appendAnswer(record)
|
||||
}
|
||||
case dns.TypeTXT:
|
||||
if q.Name == "split-txt.letsencrypt.org." {
|
||||
record := new(dns.TXT)
|
||||
record.Hdr = dns.RR_Header{Name: "split-txt.letsencrypt.org.", Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 0}
|
||||
record.Txt = []string{"a", "b", "c"}
|
||||
appendAnswer(record)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -193,9 +200,14 @@ func TestDNSLookupTXT(t *testing.T) {
|
|||
obj := NewDNSResolverImpl(time.Second*10, []string{dnsLoopbackAddr})
|
||||
|
||||
a, rtt, err := obj.LookupTXT("letsencrypt.org")
|
||||
|
||||
t.Logf("A: %v RTT %s", a, rtt)
|
||||
test.AssertNotError(t, err, "No message")
|
||||
|
||||
a, rtt, err = obj.LookupTXT("split-txt.letsencrypt.org")
|
||||
t.Logf("A: %v RTT %s", a, rtt)
|
||||
test.AssertNotError(t, err, "No message")
|
||||
test.AssertEquals(t, len(a), 1)
|
||||
test.AssertEquals(t, a[0], "abc")
|
||||
}
|
||||
|
||||
func TestDNSLookupHost(t *testing.T) {
|
||||
|
|
|
@ -201,13 +201,13 @@ type Registration struct {
|
|||
ID int64 `json:"id" db:"id"`
|
||||
|
||||
// Account key to which the details are attached
|
||||
Key jose.JsonWebKey `json:"key" db:"jwk"`
|
||||
Key jose.JsonWebKey `json:"key"`
|
||||
|
||||
// Contact URIs
|
||||
Contact []*AcmeURL `json:"contact,omitempty" db:"contact"`
|
||||
Contact []*AcmeURL `json:"contact,omitempty"`
|
||||
|
||||
// Agreement with terms of service
|
||||
Agreement string `json:"agreement,omitempty" db:"agreement"`
|
||||
Agreement string `json:"agreement,omitempty"`
|
||||
}
|
||||
|
||||
// MergeUpdate copies a subset of information from the input Registration
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
These `.sql` files define the table layout, indicies, relationships, and users default to Boulder. Implementors should use these as starting points for their own configuration.
|
||||
|
||||
## Notes
|
||||
The currently supported database is MariaDB 10.
|
||||
|
||||
Currently, if you use MySQL / MariaDB with Boulder, you must manually append `?parseTime=true"` onto the end of the `dbConnect` configuration fields for each entry. This is related to [Issue #242](https://github.com/letsencrypt/boulder/issues/242).
|
||||
mysql -u root -e "create database boulder_test; create database boulder_development; grant all privileges on boulder_test.* to 'boulder'@'localhost';"
|
||||
|
|
|
@ -11,14 +11,14 @@
|
|||
|
||||
CREATE TABLE `registrations` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
`jwk` varchar(1024) NOT NULL,
|
||||
`recoveryToken` varchar(255) DEFAULT NULL,
|
||||
`jwk` mediumblob NOT NULL,
|
||||
`jwk_sha256` varchar(255) NOT NULL,
|
||||
`contact` varchar(255) DEFAULT NULL,
|
||||
`agreement` varchar(255) DEFAULT NULL,
|
||||
`LockCol` bigint(20) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_registrations_jwk` (`jwk`(255)) COMMENT 'Used by GetRegistrationByKey'
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=70 DEFAULT CHARSET=utf8;
|
||||
UNIQUE KEY `jwk_sha256` (`jwk_sha256`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE `authz` (
|
||||
`id` varchar(255) NOT NULL,
|
||||
|
@ -30,7 +30,7 @@ CREATE TABLE `authz` (
|
|||
`combinations` varchar(255) DEFAULT NULL,
|
||||
`sequence` bigint(20) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `regId_idx` (`registrationID`),
|
||||
KEY `regId_idx` (`registrationID`) COMMENT 'Common lookup',
|
||||
CONSTRAINT `regId_authz` FOREIGN KEY (`registrationID`) REFERENCES `registrations` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
@ -43,7 +43,7 @@ CREATE TABLE `certificates` (
|
|||
`issued` datetime DEFAULT NULL,
|
||||
`expires` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`serial`),
|
||||
KEY `regId_certificates_idx` (`registrationID`),
|
||||
KEY `regId_certificates_idx` (`registrationID`) COMMENT 'Common lookup',
|
||||
CONSTRAINT `regId_certificates` FOREIGN KEY (`registrationID`) REFERENCES `registrations` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
@ -54,31 +54,32 @@ CREATE TABLE `certificateStatus` (
|
|||
`ocspLastUpdated` datetime DEFAULT NULL,
|
||||
`revokedDate` datetime DEFAULT NULL,
|
||||
`revokedReason` int(11) DEFAULT NULL,
|
||||
`lastExpirationNagSent` datetime DEFAULT NULL,
|
||||
`LockCol` bigint(20) DEFAULT NULL,
|
||||
PRIMARY KEY (`serial`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE `crls` (
|
||||
`serial` varchar(255) NOT NULL,
|
||||
`createdAt` datetime DEFAULT NULL,
|
||||
`crl` varchar(255) DEFAULT NULL,
|
||||
`createdAt` datetime NOT NULL,
|
||||
`crl` varchar(255) NOT NULL,
|
||||
PRIMARY KEY (`serial`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE `deniedCSRs` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`names` varchar(255) DEFAULT NULL,
|
||||
`names` varchar(255) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE `ocspResponses` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`serial` varchar(255) NOT NULL,
|
||||
`createdAt` datetime DEFAULT NULL,
|
||||
`createdAt` datetime NOT NULL,
|
||||
`response` mediumblob,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `SERIAL` (`serial`) COMMENT 'Actual lookup mechanism'
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE `pending_authz` (
|
||||
`id` varchar(255) NOT NULL,
|
||||
|
@ -94,6 +95,7 @@ CREATE TABLE `pending_authz` (
|
|||
CONSTRAINT `regId_pending_authz` FOREIGN KEY (`registrationID`) REFERENCES `registrations` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
CREATE TABLE `identifierData` (
|
||||
`reversedName` varchar(255) NOT NULL,
|
||||
`certSHA1` varchar(40) NOT NULL,
|
||||
|
@ -107,7 +109,7 @@ CREATE TABLE `externalCerts` (
|
|||
`notAfter` datetime DEFAULT NULL,
|
||||
`spki` blob DEFAULT NULL,
|
||||
`valid` tinyint(1) DEFAULT NULL,
|
||||
`ev` tinyint(1) DEFAULT NULL,
|
||||
`ev` tinyint(1) DEFAULT NULL,
|
||||
`rawDERCert` blob DEFAULT NULL,
|
||||
UNIQUE INDEX (sha1)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"database/sql"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
// Load both drivers to allow configuring either
|
||||
_ "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/go-sql-driver/mysql"
|
||||
|
@ -35,20 +36,11 @@ func NewDbMap(driver string, dbConnect string) (*gorp.DbMap, error) {
|
|||
logger := blog.GetAuditLogger()
|
||||
|
||||
if driver == "mysql" {
|
||||
// Check the parseTime=true DSN is present
|
||||
dbURI, err := url.Parse(dbConnect)
|
||||
var err error
|
||||
dbConnect, err = recombineURLForDB(dbConnect)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dsnVals, err := url.ParseQuery(dbURI.RawQuery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if k := dsnVals.Get("parseTime"); k != "true" {
|
||||
dsnVals.Set("parseTime", "true")
|
||||
dbURI.RawQuery = dsnVals.Encode()
|
||||
}
|
||||
dbConnect = dbURI.String()
|
||||
}
|
||||
|
||||
db, err := sql.Open(driver, dbConnect)
|
||||
|
@ -59,7 +51,7 @@ func NewDbMap(driver string, dbConnect string) (*gorp.DbMap, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
logger.Debug(fmt.Sprintf("Connecting to database %s %s", driver, dbConnect))
|
||||
logger.Debug("Connecting to database")
|
||||
|
||||
dialect, ok := dialectMap[driver].(gorp.Dialect)
|
||||
if !ok {
|
||||
|
@ -67,7 +59,7 @@ func NewDbMap(driver string, dbConnect string) (*gorp.DbMap, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
logger.Info(fmt.Sprintf("Connected to database %s %s", driver, dbConnect))
|
||||
logger.Info("Connected to database")
|
||||
|
||||
dbmap := &gorp.DbMap{Db: db, Dialect: dialect, TypeConverter: BoulderTypeConverter{}}
|
||||
|
||||
|
@ -76,6 +68,50 @@ func NewDbMap(driver string, dbConnect string) (*gorp.DbMap, error) {
|
|||
return dbmap, err
|
||||
}
|
||||
|
||||
// recombineURLForDB transforms a database URL to a URL-like string
|
||||
// that the mysql driver can use. The mysql driver needs the Host data
|
||||
// to be wrapped in "tcp()" but url.Parse will escape the parentheses
|
||||
// and the mysql driver doesn't understand them. So, we can't have
|
||||
// "tcp()" in the configs, but can't leave it out before passing it to
|
||||
// the mysql driver. Similarly, the driver needs the password and
|
||||
// username unescaped. Compromise by doing the leg work if the config
|
||||
// says the database URL's scheme is a fake one called
|
||||
// "mysqltcp://". See
|
||||
// https://github.com/go-sql-driver/mysql/issues/362 for why we have
|
||||
// to futz around and avoid URL.String.
|
||||
func recombineURLForDB(dbConnect string) (string, error) {
|
||||
dbConnect = strings.TrimSpace(dbConnect)
|
||||
dbURL, err := url.Parse(dbConnect)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if dbURL.Scheme != "mysql+tcp" {
|
||||
format := "given database connection string was not a mysql+tcp:// URL, was %#v"
|
||||
return "", fmt.Errorf(format, dbURL.Scheme)
|
||||
}
|
||||
|
||||
dsnVals, err := url.ParseQuery(dbURL.RawQuery)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
dsnVals.Set("parseTime", "true")
|
||||
|
||||
user := dbURL.User.Username()
|
||||
passwd, hasPass := dbURL.User.Password()
|
||||
dbConn := ""
|
||||
if user != "" {
|
||||
dbConn = url.QueryEscape(user)
|
||||
}
|
||||
if hasPass {
|
||||
dbConn += ":" + passwd
|
||||
}
|
||||
dbConn += "@tcp(" + dbURL.Host + ")"
|
||||
// TODO(jmhodges): should be dbURL.EscapedPath() but Travis doesn't have 1.5
|
||||
return dbConn + dbURL.Path + "?" + dsnVals.Encode(), nil
|
||||
}
|
||||
|
||||
// SetSQLDebug enables/disables GORP SQL-level Debugging
|
||||
func SetSQLDebug(dbMap *gorp.DbMap, state bool) {
|
||||
dbMap.TraceOff()
|
||||
|
|
|
@ -38,8 +38,6 @@ func (tc BoulderTypeConverter) ToDb(val interface{}) (interface{}, error) {
|
|||
return string(t), nil
|
||||
case core.OCSPStatus:
|
||||
return string(t), nil
|
||||
case core.JSONBuffer:
|
||||
return []byte(t), nil
|
||||
default:
|
||||
return val, nil
|
||||
}
|
||||
|
@ -48,7 +46,7 @@ func (tc BoulderTypeConverter) ToDb(val interface{}) (interface{}, error) {
|
|||
// FromDb converts a DB representation back into a Boulder object.
|
||||
func (tc BoulderTypeConverter) FromDb(target interface{}) (gorp.CustomScanner, bool) {
|
||||
switch target.(type) {
|
||||
case *core.AcmeIdentifier, *[]core.Challenge, *[]*core.AcmeURL, *[][]int, core.JSONBuffer:
|
||||
case *core.AcmeIdentifier, *[]core.Challenge, *[]*core.AcmeURL, *[][]int:
|
||||
binder := func(holder, target interface{}) error {
|
||||
s, ok := holder.(*string)
|
||||
if !ok {
|
||||
|
|
10
test.sh
10
test.sh
|
@ -53,7 +53,7 @@ update_status() {
|
|||
fi
|
||||
}
|
||||
|
||||
run() {
|
||||
function run() {
|
||||
echo "$@"
|
||||
"$@" 2>&1
|
||||
local status=$?
|
||||
|
@ -70,7 +70,7 @@ run() {
|
|||
return ${status}
|
||||
}
|
||||
|
||||
run_and_comment() {
|
||||
function run_and_comment() {
|
||||
if [ "x${TRAVIS}" = "x" ] || [ "${TRAVIS_PULL_REQUEST}" == "false" ] || [ ! -f "${GITHUB_SECRET_FILE}" ] ; then
|
||||
run "$@"
|
||||
else
|
||||
|
@ -118,7 +118,7 @@ function run_unit_tests() {
|
|||
if [ "${TRAVIS}" == "true" ]; then
|
||||
# Run each test by itself for Travis, so we can get coverage
|
||||
for dir in ${TESTDIRS}; do
|
||||
run go test -race -covermode=count -coverprofile=${dir}.coverprofile ./${dir}/
|
||||
run go test -race -cover -coverprofile=${dir}.coverprofile ./${dir}/
|
||||
done
|
||||
|
||||
# Gather all the coverprofiles
|
||||
|
@ -208,6 +208,10 @@ if [ "${SKIP_INTEGRATION_TESTS}" = "1" ]; then
|
|||
exit ${FAILURE}
|
||||
fi
|
||||
|
||||
if [ "${TRAVIS}" == "true" ]; then
|
||||
./test/create_db.sh || die "unable to create the boulder database with test/create_db.sh"
|
||||
fi
|
||||
|
||||
#
|
||||
# Integration tests
|
||||
#
|
||||
|
|
|
@ -48,8 +48,8 @@
|
|||
"ca": {
|
||||
"serialPrefix": 255,
|
||||
"profile": "ee",
|
||||
"dbDriver": "sqlite3",
|
||||
"dbConnect": ":memory:",
|
||||
"dbDriver": "mysql",
|
||||
"dbConnect": "mysql+tcp://boulder@localhost:3306/boulder_test",
|
||||
"debugAddr": "localhost:8001",
|
||||
"testMode": true,
|
||||
"_comment": "This should only be present in testMode. In prod use an HSM.",
|
||||
|
@ -120,8 +120,8 @@
|
|||
},
|
||||
|
||||
"sa": {
|
||||
"dbDriver": "sqlite3",
|
||||
"dbConnect": ":memory:",
|
||||
"dbDriver": "mysql",
|
||||
"dbConnect": "mysql+tcp://boulder@localhost:3306/boulder_test",
|
||||
"debugAddr": "localhost:8003"
|
||||
},
|
||||
|
||||
|
@ -136,21 +136,21 @@
|
|||
},
|
||||
|
||||
"revoker": {
|
||||
"dbDriver": "sqlite3",
|
||||
"dbConnect": ":memory:"
|
||||
"dbDriver": "mysql",
|
||||
"dbConnect": "mysql+tcp://boulder@localhost:3306/boulder_test"
|
||||
},
|
||||
|
||||
"ocspResponder": {
|
||||
"dbDriver": "sqlite3",
|
||||
"dbConnect": ":memory:",
|
||||
"dbDriver": "mysql",
|
||||
"dbConnect": "mysql+tcp://boulder@localhost:3306/boulder_test",
|
||||
"path": "/",
|
||||
"listenAddress": "localhost:4001",
|
||||
"debugAddr": "localhost:8005"
|
||||
},
|
||||
|
||||
"ocspUpdater": {
|
||||
"dbDriver": "sqlite3",
|
||||
"dbConnect": ":memory:",
|
||||
"dbDriver": "mysql",
|
||||
"dbConnect": "mysql+tcp://boulder@localhost:3306/boulder_test",
|
||||
"minTimeToExpiry": "72h",
|
||||
"debugAddr": "localhost:8006"
|
||||
},
|
||||
|
@ -164,8 +164,8 @@
|
|||
"port": "25",
|
||||
"username": "cert-master@example.com",
|
||||
"password": "password",
|
||||
"dbDriver": "sqlite3",
|
||||
"dbConnect": ":memory:",
|
||||
"dbDriver": "mysql",
|
||||
"dbConnect": "mysql+tcp://boulder@localhost:3306/boulder_test",
|
||||
"messageLimit": 0,
|
||||
"nagTimes": ["24h", "72h", "168h", "336h"],
|
||||
"emailTemplate": "test/example-expiration-template",
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
#!/bin/bash
|
||||
|
||||
function die() {
|
||||
if [ ! -z "$1" ]; then
|
||||
echo $1 > /dev/stderr
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
|
||||
mysql -u root -e "create database boulder_test; grant all privileges on boulder_test.* to 'boulder'@'localhost'" || die "unable to create boulder_test"
|
||||
echo "created boulder_test database"
|
Loading…
Reference in New Issue