Merge master

This commit is contained in:
Roland Shoemaker 2015-08-14 16:46:09 -07:00
commit 60478f13b0
36 changed files with 858 additions and 479 deletions

View File

@ -17,11 +17,13 @@ addons:
- libffi-dev
- ca-certificates
- rsyslog
mariadb: "10.0"
sudo: false
services:
- rabbitmq
- mysql
matrix:
fast_finish: true

View File

@ -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"]

5
Godeps/Godeps.json generated
View File

@ -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",

View File

@ -1,6 +1,6 @@
language: go
go:
- 1.3
- 1.4
- 1.5rc1
script:
- go test -short -bench=.

View File

@ -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.

View File

@ -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 {

View File

@ -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() {

View File

@ -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")
}
}

View File

@ -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
}

View File

@ -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())
}
}
}

View File

@ -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 {

View File

@ -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) {

View File

@ -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?

View File

@ -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())
}
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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) },

View File

@ -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 {

View File

@ -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
}

View File

@ -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},

View File

@ -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,

View File

@ -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.

View File

@ -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

View File

@ -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")

View File

@ -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")

View File

@ -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

View File

@ -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, ""))
}
}
}

View File

@ -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) {

View File

@ -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

View File

@ -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';"

View File

@ -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;

View File

@ -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()

View File

@ -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
View File

@ -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
#

View File

@ -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",

11
test/create_db.sh Executable file
View File

@ -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"